# @vue/cli-plugin-eslint
对于这个插件的基本内容,可以看下我翻译的 README ,相信这样你应该对这个插件有个基本的了解了。
# Service
Service 服务中主要是增加 webpack 的配置和 注册了 lint
命令:
module.exports = (api, options) => {
if (options.lintOnSave) {
const extensions = require('./eslintOptions').extensions(api)
// 这里使用 loadModule 方法,允许用户自定义 ESLint 依赖版本。
const { resolveModule, loadModule } = require('@vue/cli-shared-utils')
const cwd = api.getCwd()
const eslintPkg =
// 这里在下文分析下
loadModule('eslint/package.json', cwd, true) ||
loadModule('eslint/package.json', __dirname, true)
// eslint-loader 在 eslint 配置改变时不会破会缓存,所以我们需要手动地生成一个缓存标志将配置考虑在内。
const { cacheIdentifier } = api.genCacheConfig(
'eslint-loader',
{
'eslint-loader': require('eslint-loader/package.json').version,
eslint: eslintPkg.version
},
[
'.eslintrc.js',
'.eslintrc.yaml',
'.eslintrc.yml',
'.eslintrc.json',
'.eslintrc',
'package.json'
]
)
...
// 接下来是 webpack 的配置部分,暂时省略。
}
// 这里注册了新的命令 `vue-cli-service lint`
api.registerCommand(
'lint',
{
description: 'lint and fix source files',
usage: 'vue-cli-service lint [options] [...files]',
options: {
'--format [formatter]': 'specify formatter (default: codeframe)',
'--no-fix': 'do not fix errors or warnings',
'--no-fix-warnings': 'fix errors, but do not fix warnings',
'--max-errors [limit]':
'specify number of errors to make build failed (default: 0)',
'--max-warnings [limit]':
'specify number of warnings to make build failed (default: Infinity)'
},
details:
'For more options, see https://eslint.org/docs/user-guide/command-line-interface#options'
},
args => {
require('./lint')(args, api)
}
)
这里看下如果实现了 eslint 自定义和预置同时存在的:
const cwd = api.getCwd()
const eslintPkg =
loadModule('eslint/package.json', cwd, true) ||
loadModule('eslint/package.json', __dirname, true)
再看下 api.getCwd()
这个方法:
/**
* 当前工作目录(其实也就是项目目录,因为 Service 脚本执行是在项目根目录)
*/
getCwd () {
return this.service.context
}
loadModule 的区别主要是第二个参数不同,这里有个关于 __dirname 和 process.cwd() 区别的文档可以看下,所以这里会先在 项目目录下找用户定义的 ESLint 如果没有找到的情况下,再在 "源代码所在目录" -- 也就是 @vue/cli-plugin-eslint
这个插件中的 eslint。
const { cacheIdentifier } = api.genCacheConfig(
...
缓存标志的生成和我们在 @vue/cli-plugin-babel
的 Service 部分生成标志用的同一个方法。
关于命令注册,它是引用了 lint.js
,
...
require('./lint')(args, api)
再看先 lint.js
中部分内容:
// 这里可以看到,还是兼容前面提到用户自定义 eslint 的原则
const { CLIEngine } = loadModule('eslint', cwd, true) || require('eslint')
...
// 这里进行了 eslint 的初始化
const engine = new CLIEngine(config)
...
// 这里应该是进行了 lint 操作
const report = engine.executeOnFiles(files)
...
// 控制自动修复的逻辑
if (config.fix) {
CLIEngine.outputFixes(report)
}
到此,主要的 Service 逻辑我们就过了一遍。
# Pormpts
module.exports = [
{
name: 'config',
type: 'list',
message: `Pick an ESLint config:`,
choices: [
{
name: 'Error prevention only',
value: 'base',
short: 'Basic'
},
...
},
{
name: 'lintOn',
type: 'checkbox',
message: 'Pick additional lint features:',
choices: [
{
name: 'Lint on save',
value: 'save',
checked: true
},
{
name: 'Lint and fix on commit' + (hasGit() ? '' : chalk.red(' (requires Git)')),
value: 'commit'
}
]
}
对话一共两个,第一个是选择一种 ESLint 配置,第二个是选择 lint 的执行时机,默认是在文件保存的时候执行 lint。
# Generator
直接看代码:
// 这里的 config、lintOn 参数,其实就是 对话中的两个问题
module.exports = (api, { config, lintOn = [] }, _, invoking) => {
// 这里载入了默认的 eslint 配置
const eslintConfig = require('../eslintOptions').config(api, config)
// 这里的依赖是根据 config 选项决定的,不同的 eslint 规则对应不同的依赖
const devDependencies = require('../eslintDeps').getDeps(api, config)
const pkg = {
scripts: {
lint: 'vue-cli-service lint'
},
eslintConfig,
devDependencies
}
...
// lint & fix after create to ensure files adhere to chosen config
// for older versions that do not support the `hooks` feature
// lint 和 修复了 vue-cli 老版本不支持 `hooks` 功能
try {
// 这里的这个写法,对于我们自己做版本处理相关,也很有用
api.assertCliVersion('^4.0.0-beta.0')
} catch (e) {
if (config && config !== 'base') {
api.onCreateComplete(() => {
require('../lint')({ silent: true }, api)
})
}
}
# Migrator
这里来看下 eslint 插件升级的逻辑:
module.exports = async (api) => {
// 首先获取项目的 package.json 文件
const pkg = require(api.resolve('package.json'))
// 取得本地 eslint 版本
let localESLintRange = pkg.devDependencies.eslint
// 如果项目是通过 Vue CLI 3.0 或者更早版本构建的,ESLint 依赖(ESLint v4)将在 @vue/cli-plugin-eslint 插件内部;
// 在 Vue CLI v4 中他应该被提取到项目依赖中
// 这里判断如果项目当前 Vue CLI 是3.x版本,并且项目没有单独安装 ESLint 时
if (api.fromVersion('^3') && !localESLintRange) {
localESLintRange = '^4.19.1'
// 这里 针对你从 增加了相应的依赖
api.extendPackage({
devDependencies: {
eslint: localESLintRange,
'babel-eslint': '^8.2.5',
'eslint-plugin-vue': '^4.5.0'
}
})
}
// 这里获得 eslint 的主版本号
const localESLintMajor = semver.major(
semver.maxSatisfying(
['4.99.0', '5.99.0', '6.99.0'],
localESLintRange
)
)
// 如果 主版本 已经是 6,说明是最新的,则直接返回
if (localESLintMajor === 6) {
return
}
// 如果 主版本 不是 6,则进行对话
const { confirmUpgrade } = await inquirer.prompt([{
name: 'confirmUpgrade',
type: 'confirm',
message:
`Your current ESLint version is v${localESLintMajor}.\n` +
`The lastest major version is v6.\n` +
`Do you want to upgrade? (May contain breaking changes)\n`
}])
// 如果用户的答案是 true
if (confirmUpgrade) {
const { getDeps } = require('../eslintDeps')
const newDeps = getDeps(api)
// 这里根据用户已经选择 eslint 规则来设置
if (pkg.devDependencies['@vue/eslint-config-airbnb']) {
Object.assign(newDeps, getDeps(api, 'airbnb'))
}
if (pkg.devDependencies['@vue/eslint-config-standard']) {
Object.assign(newDeps, getDeps(api, 'standard'))
}
if (pkg.devDependencies['@vue/eslint-config-prettier']) {
Object.assign(newDeps, getDeps(api, 'prettier'))
}
api.extendPackage({ devDependencies: newDeps }, { warnIncompatibleVersions: false })
...
}
}