Skip to content

Commit

Permalink
feat(eslint): support eslint7 and @babel/eslint-parser (#6059)
Browse files Browse the repository at this point in the history
closes #5734 
closes #5981
  • Loading branch information
fangbinwei committed Nov 20, 2020
1 parent 482ef10 commit 9136696
Show file tree
Hide file tree
Showing 11 changed files with 359 additions and 33 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
},
"devDependencies": {
"@babel/core": "^7.11.6",
"@babel/eslint-parser": "^7.12.1",
"@typescript-eslint/eslint-plugin": "^2.33.0",
"@typescript-eslint/parser": "^2.33.0",
"@vue/eslint-config-airbnb": "^5.0.2",
Expand All @@ -55,11 +56,10 @@
"@vuepress/plugin-pwa": "^1.5.4",
"@vuepress/theme-vue": "^1.5.4",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "^10.1.0",
"babel-jest": "^24.9.0",
"chromedriver": "^87.0.0",
"debug": "^4.1.0",
"eslint": "^6.7.2",
"eslint": "^7.13.0",
"eslint-plugin-graphql": "^3.1.0",
"eslint-plugin-import": "^2.20.2",
"eslint-plugin-node": "^11.1.0",
Expand Down
21 changes: 19 additions & 2 deletions packages/@vue/cli-plugin-eslint/__tests__/eslintGenerator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,26 @@ test('babel', async () => {
])

expect(pkg.scripts.lint).toBeTruthy()
expect(pkg.devDependencies).toHaveProperty('babel-eslint')
expect(pkg.devDependencies).toHaveProperty('@babel/eslint-parser')
expect(pkg.devDependencies).toHaveProperty('@babel/core')
expect(pkg.eslintConfig.parserOptions).toEqual({
parser: 'babel-eslint'
parser: '@babel/eslint-parser'
})
})

test('no-@babel/eslint-parser', async () => {
const { pkg } = await generateWithPlugin([
{
id: 'eslint',
apply: require('../generator'),
options: {}
}
])

expect(pkg.devDependencies).not.toHaveProperty('@babel/eslint-parser')
expect(pkg.devDependencies).not.toHaveProperty('@babel/core')
expect(pkg.eslintConfig.parserOptions).not.toMatchObject({
parser: '@babel/eslint-parser'
})
})

Expand Down
146 changes: 143 additions & 3 deletions packages/@vue/cli-plugin-eslint/__tests__/eslintMigrator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ test('upgrade: should add eslint to devDependencies', async () => {
await project.upgrade('eslint')

const updatedPkg = JSON.parse(await project.read('package.json'))
expect(updatedPkg.devDependencies.eslint).toMatch('^6')
expect(updatedPkg.devDependencies.eslint).toMatch('^7')
})

test('upgrade: should upgrade eslint from v5 to v6', async () => {
test('upgrade: should upgrade eslint from v5 to v7', async () => {
const project = await create('plugin-eslint-with-eslint-5', {
plugins: {
'@vue/cli-plugin-eslint': {
Expand All @@ -43,6 +43,146 @@ test('upgrade: should upgrade eslint from v5 to v6', async () => {
}

const updatedPkg = JSON.parse(await project.read('package.json'))
expect(updatedPkg.devDependencies.eslint).toMatch('^6')
expect(updatedPkg.devDependencies.eslint).toMatch('^7')
expect(updatedPkg.devDependencies).toHaveProperty('eslint-plugin-import')
})

test.each([['3.12.1'], ['4.5.8']])('upgrade: replace babel-eslint with @babel/eslint-parser', async (version) => {
const project = await create('plugin-eslint-replace-babel-eslint' + version[0], {
plugins: {
'@vue/cli-plugin-eslint': {
version,
config: 'airbnb'
},
'@vue/cli-plugin-babel': {
version,
config: 'airbnb'
}
}
})

const pkg = JSON.parse(await project.read('package.json'))
expect(pkg.devDependencies).toHaveProperty('babel-eslint')
expect(pkg.devDependencies).not.toHaveProperty('@babel/core')
expect(pkg.eslintConfig).toMatchObject({
parserOptions: {
parser: 'babel-eslint'
}
})
pkg.eslintConfig.parserOptions.testMerge = 'testMerge'
await project.write('package.json', JSON.stringify(pkg, null, 2))
const eslintConfigBefore = pkg.eslintConfig

await project.upgrade('eslint')

const updatedPkg = JSON.parse(await project.read('package.json'))
expect(updatedPkg.eslintConfig).toMatchObject({
...eslintConfigBefore,
parserOptions: {
testMerge: 'testMerge',
parser: '@babel/eslint-parser'
}
})
expect(updatedPkg.devDependencies['@babel/eslint-parser']).toMatch('^7')
expect(updatedPkg.devDependencies).not.toHaveProperty('babel-eslint')
expect(updatedPkg.devDependencies).toHaveProperty('@babel/core')
})

test('upgrade: not add @babel/eslint-parser without babel', async () => {
const project = await create('plugin-eslint-not-replace-babel-eslint', {
plugins: {
'@vue/cli-plugin-eslint': {
version: '3.12.1',
config: 'airbnb'
}
}
})

await project.upgrade('eslint')

const updatedPkg = await project.read('package.json')
expect(updatedPkg).not.toMatch('@babel/eslint-parser')
expect(updatedPkg).not.toMatch('@babel/core')
expect(JSON.parse(updatedPkg).devDependencies.eslint).toMatch('^7')
})

test('upgrade: not add @babel/eslint-parser with ts', async () => {
const project = await create('plugin-eslint-not-add-babel-eslint-ts', {
plugins: {
'@vue/cli-plugin-eslint': {
version: '3.12.1',
config: 'airbnb'
},
'@vue/cli-plugin-typescript': {
version: '3.12.1',
config: 'airbnb'
}
}
})

await project.upgrade('eslint')

const updatedPkg = await project.read('package.json')
expect(updatedPkg).not.toMatch('@babel/eslint-parser')
expect(updatedPkg).not.toMatch('@babel/core')
expect(updatedPkg).toMatch('@typescript-eslint/parser')
expect(JSON.parse(updatedPkg).devDependencies.eslint).toMatch('^7')
})

test('upgrade: replace babel-eslint with @babel/eslint-parser in eslintrc.js', async () => {
const project = await create('plugin-eslint-replace-babel-eslint-eslintrc', {
useConfigFiles: true,
plugins: {
'@vue/cli-plugin-eslint': {
version: '3.12.1',
config: 'airbnb'
},
'@vue/cli-plugin-babel': {
version: '3.12.1',
config: 'airbnb'
}
}
})

const eslintrc = await project.read('.eslintrc.js')
expect(eslintrc).toMatch('babel-eslint')
const eslintrcMerge = eslintrc.replace(`parser: 'babel-eslint'`, `testMerge: 'testMerge', parser: 'babel-eslint'`)
await project.write('.eslintrc.js', eslintrcMerge)

await project.upgrade('eslint')

const updatedEslintrc = await project.read('.eslintrc.js')
expect(updatedEslintrc).toMatch('@babel/eslint-parser')
expect(updatedEslintrc).toMatch('testMerge')

const updatedPkg = JSON.parse(await project.read('package.json'))
expect(updatedPkg.devDependencies).not.toHaveProperty('babel-eslint')
expect(updatedPkg.devDependencies.eslint).toMatch('^7')
})

test.each([
['7.1.0', true],
['7.2.0', false]
])('upgrade: local @babel/core exists', async (localBabelCoreVersion, bumpVersion) => {
const project = await create('plugin-eslint-core-' + String(bumpVersion), {
plugins: {
'@vue/cli-plugin-eslint': {
version: '3.12.1',
config: 'airbnb'
},
'@vue/cli-plugin-babel': {
version: '3.12.1',
config: 'airbnb'
}
}
})

const pkg = JSON.parse(await project.read('package.json'))
pkg.devDependencies['@babel/core'] = localBabelCoreVersion
await project.write('package.json', JSON.stringify(pkg, null, 2))

await project.upgrade('eslint')

const updatedPkg = JSON.parse(await project.read('package.json'))
expect(updatedPkg.devDependencies['@babel/core'] !== localBabelCoreVersion).toBe(bumpVersion)
})
11 changes: 6 additions & 5 deletions packages/@vue/cli-plugin-eslint/eslintDeps.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const DEPS_MAP = {
base: {
eslint: '^6.7.2',
eslint: '^7.13.0',
'eslint-plugin-vue': '^6.2.2'
},
airbnb: {
Expand All @@ -20,9 +20,9 @@ const DEPS_MAP = {
'eslint-plugin-standard': '^4.0.0'
},
typescript: {
'@vue/eslint-config-typescript': '^5.1.0',
'@typescript-eslint/eslint-plugin': '^2.33.0',
'@typescript-eslint/parser': '^2.33.0'
'@vue/eslint-config-typescript': '^7.0.0',
'@typescript-eslint/eslint-plugin': '^4.4.0',
'@typescript-eslint/parser': '^4.4.0'
}
}

Expand All @@ -41,7 +41,8 @@ exports.getDeps = function (api, preset, rootOptions = {}) {

if (api.hasPlugin('babel') && !api.hasPlugin('typescript')) {
Object.assign(deps, {
'babel-eslint': '^10.1.0'
'@babel/eslint-parser': '^7.12.1',
'@babel/core': '^7.12.3'
})
}

Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-eslint/eslintOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ exports.config = (api, preset, rootOptions = {}) => {

if (api.hasPlugin('babel') && !api.hasPlugin('typescript')) {
config.parserOptions = {
parser: 'babel-eslint'
parser: '@babel/eslint-parser'
}
}

Expand Down
33 changes: 29 additions & 4 deletions packages/@vue/cli-plugin-eslint/migrator/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const { semver } = require('@vue/cli-shared-utils')

/** @param {import('@vue/cli/lib/MigratorAPI')} api MigratorAPI */
module.exports = async (api) => {
const pkg = require(api.resolve('package.json'))

Expand All @@ -13,7 +14,7 @@ module.exports = async (api) => {
api.extendPackage({
devDependencies: {
eslint: localESLintRange,
'babel-eslint': '^8.2.5',
'@babel/eslint-parser': '^7.12.1',
'eslint-plugin-vue': '^4.5.0'
}
})
Expand All @@ -24,10 +25,11 @@ module.exports = async (api) => {
// in case the user does not specify a typical caret range;
// it is used as **fallback** because the user may have not previously
// installed eslint yet, such as in the case that they are from v3.0.x
// eslint-disable-next-line node/no-extraneous-require
require('eslint/package.json').version
)

if (localESLintMajor >= 6) {
if (localESLintMajor > 6) {
return
}

Expand All @@ -44,7 +46,30 @@ module.exports = async (api) => {
Object.assign(newDeps, getDeps(api, 'prettier'))
}

api.extendPackage({ devDependencies: newDeps }, { warnIncompatibleVersions: false })
const fields = { devDependencies: newDeps }

if (newDeps['@babel/core'] && newDeps['@babel/eslint-parser']) {
Reflect.deleteProperty(api.generator.pkg.devDependencies, 'babel-eslint')

const minSupportedBabelCoreVersion = '>=7.2.0'
const localBabelCoreVersion = pkg.devDependencies['@babel/core']

if (localBabelCoreVersion &&
semver.satisfies(
localBabelCoreVersion,
minSupportedBabelCoreVersion
)) {
Reflect.deleteProperty(newDeps, '@babel/core')
}

fields.eslintConfig = {
parserOptions: {
parser: '@babel/eslint-parser'
}
}
}

api.extendPackage(fields, { warnIncompatibleVersions: false })

// in case anyone's upgrading from the legacy `typescript-eslint-parser`
if (api.hasPlugin('typescript')) {
Expand All @@ -57,7 +82,7 @@ module.exports = async (api) => {
})
}

api.exitLog(`ESLint upgraded from v${localESLintMajor}. to v6\n`)
api.exitLog(`ESLint upgraded from v${localESLintMajor}. to v7\n`)

// TODO:
// transform `@vue/prettier` to `eslint:recommended` + `@vue/prettier`
Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli-plugin-eslint/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,6 @@
},
"peerDependencies": {
"@vue/cli-service": "^3.0.0 || ^4.0.0-0",
"eslint": ">= 6.0.0"
"eslint": ">=7.5.0"
}
}
10 changes: 8 additions & 2 deletions packages/@vue/cli-test-utils/createUpgradableProject.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,19 @@ const Upgrader = require('@vue/cli/lib/Upgrader')

const outsideTestFolder = path.resolve(__dirname, '../../../../vue-upgrade-tests')

module.exports = async function createUpgradableProject (...args) {
/**
* create upgradable project
* @param {string} name
* @param {import('@vue/cli').Preset} preset
* @returns {ReturnType<createTestProject> & Promise<{upgrade: Upgrader['upgrade']}>}
*/
module.exports = async function createUpgradableProject (name, preset) {
if (!fs.existsSync(outsideTestFolder)) {
fs.mkdirSync(outsideTestFolder)
}
process.env.VUE_CLI_TEST_DO_INSTALL_PLUGIN = true

const project = await createTestProject(...args, outsideTestFolder)
const project = await createTestProject(name, preset, outsideTestFolder)
const upgrade = async function upgrade (pluginName, options) {
return (new Upgrader(project.dir)).upgrade(pluginName, options || {})
}
Expand Down
2 changes: 1 addition & 1 deletion packages/@vue/cli-ui/.eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = {
'vue/html-self-closing': 'error',
'vue/no-use-v-if-with-v-for': 'warn',
'vue/no-unused-vars': 'warn',
'vue/return-in-computed-property': 'warn',
'vue/return-in-computed-property': 'warn'
},

parserOptions: {
Expand Down
8 changes: 8 additions & 0 deletions packages/@vue/cli/__tests__/invoke.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,16 @@ test('invoke with prompts', async () => {
])
// need to be in the same process to have inquirer mocked
// so calling directly
const cwd = process.cwd()
// By default, @babel/eslint-parser will load babel.config.js in `path.resolve('.')`(equals `process.cwd()`) through @babel/core.
// chdir, and let @babel/eslint-parser find the babel.config.js in our test project
process.chdir(project.dir)

await invoke(`eslint`, {}, project.dir)
await assertUpdates(project)

// restore
process.chdir(cwd)
})

test('invoke with ts', async () => {
Expand Down
Loading

0 comments on commit 9136696

Please sign in to comment.