From ba5f84cdb2cdbaab1aebdd569ac4ee8c5cd0a83c Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sun, 15 Aug 2021 11:17:55 +0900 Subject: [PATCH 1/9] Add support for ESLint v8. --- .circleci/config.yml | 18 ++++++++++++++++-- docs/user-guide/README.md | 20 +++++++++----------- lib/utils/indent-common.js | 2 +- package.json | 5 ++--- tests/lib/rules-without-vue-eslint-parser.js | 5 +---- tests/lib/rules/jsx-uses-vars.js | 4 +++- tests/lib/rules/script-setup-uses-vars.js | 4 +++- 7 files changed, 35 insertions(+), 23 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index c10b2139e..f4550d9cf 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -50,10 +50,24 @@ jobs: - run: name: Test command: npm test - node-v10: - <<: *node-base + eslint-v7: docker: - image: node:10 + steps: + - run: + name: Versions + command: npm version + - checkout + - run: + name: Install eslint@7 + command: | + npm install --save-exact eslint@7 + - run: + name: Install dependencies + command: npm install + - run: + name: Test + command: npm test node-v12: <<: *node-base docker: diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md index a056c912b..343b11c22 100644 --- a/docs/user-guide/README.md +++ b/docs/user-guide/README.md @@ -93,13 +93,13 @@ If you installed [@vue/cli-plugin-eslint](https://github.com/vuejs/vue-cli/tree/ ### How to use a custom parser? -If you want to use custom parsers such as [babel-eslint](https://www.npmjs.com/package/babel-eslint) or [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser), you have to use the `parserOptions.parser` option instead of the `parser` option. Because this plugin requires [vue-eslint-parser](https://www.npmjs.com/package/vue-eslint-parser) to parse `.vue` files, this plugin doesn't work if you overwrite the `parser` option. +If you want to use custom parsers such as [@babel/eslint-parser](https://www.npmjs.com/package/@babel/eslint-parser) or [@typescript-eslint/parser](https://www.npmjs.com/package/@typescript-eslint/parser), you have to use the `parserOptions.parser` option instead of the `parser` option. Because this plugin requires [vue-eslint-parser](https://www.npmjs.com/package/vue-eslint-parser) to parse `.vue` files, this plugin doesn't work if you overwrite the `parser` option. ```diff -- "parser": "babel-eslint", +- "parser": "@typescript-eslint/parser", + "parser": "vue-eslint-parser", "parserOptions": { -+ "parser": "babel-eslint", ++ "parser": "@typescript-eslint/parser", "sourceType": "module" } ``` @@ -238,13 +238,13 @@ Make sure you have one of the following settings in your **.eslintrc**: - `"extends": ["plugin:vue/vue3-recommended"]` - `"extends": ["plugin:vue/base"]` -If you already use another parser (e.g. `"parser": "babel-eslint"`), please move it into `parserOptions`, so it doesn't collide with the `vue-eslint-parser` used by this plugin's configuration: +If you already use another parser (e.g. `"parser": "@typescript-eslint/parser"`), please move it into `parserOptions`, so it doesn't collide with the `vue-eslint-parser` used by this plugin's configuration: ```diff -- "parser": "babel-eslint", +- "parser": "@typescript-eslint/parser", + "parser": "vue-eslint-parser", "parserOptions": { -+ "parser": "babel-eslint", ++ "parser": "@typescript-eslint/parser", "ecmaVersion": 2020, "sourceType": "module" } @@ -331,7 +331,7 @@ Note that you cannot use angle-bracket type assertion style (`var x = bar;` - Turning off the rule in the ESLint configuration file does not ignore the warning. - Using the `` comment does not suppress warnings. - Duplicate warnings are displayed. -- Used `babel-eslint`, but the template still show `vue/no-parsing-error` warnings. +- Used `@babel/eslint-parser`, but the template still show `vue/no-parsing-error` warnings. You need to turn off Vetur's template validation by adding `vetur.validation.template: false` to your `.vscode/settings.json`. @@ -398,20 +398,18 @@ module.exports = { However, note that the AST generated by `espree` v8+ may not work well with some rules of `ESLint` v7.x. - #### Other Problems diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js index 5d80b83bb..a4d0dbcfd 100644 --- a/lib/utils/indent-common.js +++ b/lib/utils/indent-common.js @@ -1331,7 +1331,7 @@ module.exports.defineVisitor = function create( setOffset([fromToken, nameToken], 1, exportToken) } } else { - // maybe babel-eslint + // maybe babel parser } } }, diff --git a/package.json b/package.json index 9c5ff64ef..71851e9c1 100644 --- a/package.json +++ b/package.json @@ -50,7 +50,7 @@ "node": ">=8.10" }, "peerDependencies": { - "eslint": "^6.2.0 || ^7.0.0" + "eslint": "^6.2.0 || ^7.0.0 || ^8.0.0-0" }, "dependencies": { "eslint-utils": "^2.1.0", @@ -66,9 +66,8 @@ "@types/semver": "^7.2.0", "@typescript-eslint/parser": "^4.28.0", "@vuepress/plugin-pwa": "^1.4.1", - "babel-eslint": "^10.1.0", "env-cmd": "^10.1.0", - "eslint": "^7.0.0", + "eslint": "^8.0.0-0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-eslint-plugin": "^3.5.3", "eslint-plugin-import": "^2.20.2", diff --git a/tests/lib/rules-without-vue-eslint-parser.js b/tests/lib/rules-without-vue-eslint-parser.js index b7c585134..a0c8a80a0 100644 --- a/tests/lib/rules-without-vue-eslint-parser.js +++ b/tests/lib/rules-without-vue-eslint-parser.js @@ -5,7 +5,6 @@ 'use strict' const Linter = require('eslint').Linter -const parser = require('babel-eslint') const rules = require('../..').rules const assert = require('assert') @@ -18,13 +17,11 @@ describe("Don't crash even if without vue-eslint-parser.", () => { it(ruleId, () => { const linter = new Linter() const config = { - parser: 'babel-eslint', - parserOptions: { ecmaVersion: 2015 }, + parserOptions: { ecmaVersion: 2015, ecmaFeatures: { jsx: true } }, rules: { [ruleId]: 'error' } } - linter.defineParser('babel-eslint', parser) linter.defineRule(ruleId, rules[key]) const resultVue = linter.verifyAndFix(code, config, 'test.vue') for (const { message } of resultVue.messages) { diff --git a/tests/lib/rules/jsx-uses-vars.js b/tests/lib/rules/jsx-uses-vars.js index ea00e836c..f8b700bd6 100644 --- a/tests/lib/rules/jsx-uses-vars.js +++ b/tests/lib/rules/jsx-uses-vars.js @@ -10,7 +10,9 @@ const eslint = require('eslint') const rule = require('../../../lib/rules/jsx-uses-vars') -const ruleNoUnusedVars = require('eslint/lib/rules/no-unused-vars') +const ruleNoUnusedVars = new (require('eslint').Linter)() + .getRules() + .get('no-unused-vars') const RuleTester = eslint.RuleTester const ruleTester = new RuleTester({ diff --git a/tests/lib/rules/script-setup-uses-vars.js b/tests/lib/rules/script-setup-uses-vars.js index 3cf9cde0c..f8287c0c7 100644 --- a/tests/lib/rules/script-setup-uses-vars.js +++ b/tests/lib/rules/script-setup-uses-vars.js @@ -11,7 +11,9 @@ const eslint = require('eslint') const rule = require('../../../lib/rules/script-setup-uses-vars') -const ruleNoUnusedVars = require('eslint/lib/rules/no-unused-vars') +const ruleNoUnusedVars = new (require('eslint').Linter)() + .getRules() + .get('no-unused-vars') const RuleTester = eslint.RuleTester const ruleTester = new RuleTester({ From 078bf7eee1f66af64b821903d39e7a6dd47c994c Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sun, 15 Aug 2021 11:19:58 +0900 Subject: [PATCH 2/9] fix CI --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f4550d9cf..0e2f4015f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,7 +3,7 @@ workflows: node-multi-build: jobs: - node-v8 - - node-v10 + - eslint-v7 - node-v12 - node-v14 - lint From e3ad2fa535d249f967668722ba9d8aeec2c2ac27 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 14:08:42 +0900 Subject: [PATCH 3/9] update --- lib/utils/index.js | 118 ++++++++++++++++++++++++++++++++++- tests/eslint-compat.js | 33 +++++++--- tests/lib/rules/camelcase.js | 14 +++++ 3 files changed, 155 insertions(+), 10 deletions(-) diff --git a/lib/utils/index.js b/lib/utils/index.js index e309dc579..7b2dfd6bb 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -50,7 +50,7 @@ const VUE3_BUILTIN_COMPONENT_NAMES = new Set( ) const path = require('path') const vueEslintParser = require('vue-eslint-parser') -const traverseNodes = vueEslintParser.AST.traverseNodes +const { traverseNodes, getFallbackKeys } = vueEslintParser.AST const { findVariable } = require('eslint-utils') const { getComponentPropsFromTypeDefine, @@ -155,11 +155,84 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) { } }) + const containerScopes = new WeakMap() + + /** + * @param {ASTNode} node + */ + function getContainerScope(node) { + const exprContainer = getVExpressionContainer(node) + if (!exprContainer) { + return null + } + const cache = containerScopes.get(exprContainer) + if (cache) { + return cache + } + const programNode = eslintSourceCode.ast + const parserOptions = context.parserOptions || {} + const ecmaFeatures = parserOptions.ecmaFeatures || {} + const ecmaVersion = parserOptions.ecmaVersion || 2020 + const sourceType = programNode.sourceType + try { + const eslintScope = createRequire(require.resolve('eslint'))( + 'eslint-scope' + ) + const expStmt = new Proxy(exprContainer, { + get(_object, key) { + if (key === 'type') { + return 'ExpressionStatement' + } + // @ts-expect-error + return exprContainer[key] + } + }) + const scopeProgram = new Proxy(programNode, { + get(_object, key) { + if (key === 'body') { + return [expStmt] + } + // @ts-expect-error + return programNode[key] + } + }) + const scope = eslintScope.analyze(scopeProgram, { + ignoreEval: true, + nodejsScope: false, + impliedStrict: ecmaFeatures.impliedStrict, + ecmaVersion, + sourceType, + fallback: getFallbackKeys + }) + containerScopes.set(exprContainer, scope) + return scope + } catch (e) { + // ignore + // console.log(e) + } + + return null + } + const declaredVariables = new WeakMap() return { // @ts-expect-error __proto__: context, getSourceCode() { return sourceCode + }, + getDeclaredVariables(node) { + const cacheVars = declaredVariables.get(node) + if (cacheVars) { + return cacheVars + } + const scope = getContainerScope(node) + if (scope) { + const vars = scope.getDeclaredVariables(node) + declaredVariables.set(node, vars) + return vars + } + + return context.getDeclaredVariables(node) } } } @@ -1806,6 +1879,36 @@ function isDef(v) { return v != null } +// ------------------------------------------------------------------------------ +// Nodejs Helpers +// ------------------------------------------------------------------------------ +/** + * @param {String} filename + */ +function createRequire(filename) { + const Module = require('module') + const moduleCreateRequire = + // Added in v12.2.0 + Module.createRequire || + // Added in v10.12.0, but deprecated in v12.2.0. + Module.createRequireFromPath || + // Polyfill - This is not executed on the tests on node@>=10. + /** + * @param {string} filename + */ + function (filename) { + const mod = new Module(filename) + + mod.filename = filename + // @ts-ignore + mod.paths = Module._nodeModulePaths(path.dirname(filename)) + // @ts-ignore + mod._compile('module.exports = require;', filename) + return mod.exports + } + return moduleCreateRequire(filename) +} + // ------------------------------------------------------------------------------ // Rule Helpers // ------------------------------------------------------------------------------ @@ -2121,6 +2224,19 @@ function getStringLiteralValue(node, stringOnly) { } return null } +/** + * Gets the VExpressionContainer of a given node. + * @param {ASTNode} node - The node to get. + * @return {VExpressionContainer|null} + */ +function getVExpressionContainer(node) { + /** @type {ASTNode | null} */ + let n = node + while (n && n.type !== 'VExpressionContainer') { + n = n.parent + } + return n +} // ------------------------------------------------------------------------------ // Vue Helpers diff --git a/tests/eslint-compat.js b/tests/eslint-compat.js index 5b846bdfe..8da19ae3f 100644 --- a/tests/eslint-compat.js +++ b/tests/eslint-compat.js @@ -16,24 +16,39 @@ function getESLintClassForV6() { /** @param {eslint.ESLint.Options} options */ constructor(options) { const { - overrideConfig: { plugins, globals, ...overrideConfig }, + overrideConfig: { plugins, globals, rules, ...overrideConfig } = { + plugins: [], + globals: {}, + rules: {} + }, fix, reportUnusedDisableDirectives, plugins: pluginsMap, ...otherOptions - } = options - this.engine = new eslint.CLIEngine({ + } = options || {} + /** @type {eslint.CLIEngine.Options} */ + const newOptions = { fix: Boolean(fix), + reportUnusedDisableDirectives: reportUnusedDisableDirectives + ? reportUnusedDisableDirectives !== 'off' + : undefined, + ...otherOptions, + globals: globals ? Object.keys(globals).filter((n) => globals[n]) : undefined, - ...otherOptions, - ...overrideConfig, plugins: plugins || [], - reportUnusedDisableDirectives: reportUnusedDisableDirectives - ? reportUnusedDisableDirectives !== 'off' - : undefined - }) + rules: rules + ? Object.entries(rules).reduce((o, [ruleId, opt]) => { + if (opt) { + o[ruleId] = opt + } + return o + }, /** @type {NonNullable}*/ ({})) + : undefined, + ...overrideConfig + } + this.engine = new eslint.CLIEngine(newOptions) for (const [name, plugin] of Object.entries(pluginsMap || {})) { this.engine.addPlugin(name, plugin) diff --git a/tests/lib/rules/camelcase.js b/tests/lib/rules/camelcase.js index 31d5b6beb..2470f63f1 100644 --- a/tests/lib/rules/camelcase.js +++ b/tests/lib/rules/camelcase.js @@ -52,6 +52,20 @@ tester.run('camelcase', rule, { line: 4 } ] + }, + { + code: ` + `, + errors: [ + { + message: "Identifier 'my_pref' is not in camel case.", + line: 4 + } + ] } ] }) From 5d06be41979b97ad0ae3f2fffaf201de7f965f44 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 14:10:29 +0900 Subject: [PATCH 4/9] update --- lib/utils/index.js | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/lib/utils/index.js b/lib/utils/index.js index 7b2dfd6bb..dfd5610d0 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -213,7 +213,6 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) { return null } - const declaredVariables = new WeakMap() return { // @ts-expect-error __proto__: context, @@ -221,15 +220,9 @@ function wrapContextToOverrideTokenMethods(context, tokenStore, options) { return sourceCode }, getDeclaredVariables(node) { - const cacheVars = declaredVariables.get(node) - if (cacheVars) { - return cacheVars - } const scope = getContainerScope(node) if (scope) { - const vars = scope.getDeclaredVariables(node) - declaredVariables.set(node, vars) - return vars + return scope.getDeclaredVariables(node) } return context.getDeclaredVariables(node) From ffd94f9461f65119a69856e7330a2ba254795801 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 14:18:04 +0900 Subject: [PATCH 5/9] update --- typings/eslint/index.d.ts | 3 --- 1 file changed, 3 deletions(-) diff --git a/typings/eslint/index.d.ts b/typings/eslint/index.d.ts index fdbca8503..0a1cd36c4 100644 --- a/typings/eslint/index.d.ts +++ b/typings/eslint/index.d.ts @@ -123,9 +123,6 @@ export class SourceCode /*extends ESLintSourceCode*/ { ): string getLines(): string[] getAllComments(): VNODE.Comment[] - getComments( - node: VAST.ESNode - ): { leading: VNODE.Comment[]; trailing: VNODE.Comment[] } getJSDocComment(node: VAST.ESNode): AST.Token | null getNodeByRangeIndex(index: number): VAST.ESNode | VAST.JSXNode isSpaceBetweenTokens(first: AST.Token, second: AST.Token): boolean From 36b0fdadf1c9c22432930c880409d51886f832d5 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 19:45:29 +0900 Subject: [PATCH 6/9] update --- .circleci/config.yml | 18 ++++++++++++++++++ package.json | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 0e2f4015f..e5b488663 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -68,6 +68,24 @@ jobs: - run: name: Test command: npm test + eslint-v8: + docker: + - image: node:14 + steps: + - run: + name: Versions + command: npm version + - checkout + - run: + name: Install eslint@8 + command: | + npm install --save-exact eslint@^8.0.0-0 + - run: + name: Install dependencies + command: npm install + - run: + name: Test + command: npm test node-v12: <<: *node-base docker: diff --git a/package.json b/package.json index 71851e9c1..4ba55e5c2 100644 --- a/package.json +++ b/package.json @@ -67,7 +67,7 @@ "@typescript-eslint/parser": "^4.28.0", "@vuepress/plugin-pwa": "^1.4.1", "env-cmd": "^10.1.0", - "eslint": "^8.0.0-0", + "eslint": "^7.0.0", "eslint-config-prettier": "^6.11.0", "eslint-plugin-eslint-plugin": "^3.5.3", "eslint-plugin-import": "^2.20.2", From 6edfb9632137062da9a7bf515e63b9028705de1d Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 19:47:16 +0900 Subject: [PATCH 7/9] fix --- .circleci/config.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index e5b488663..df482132b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,7 @@ workflows: jobs: - node-v8 - eslint-v7 + - eslint-v8 - node-v12 - node-v14 - lint From c24bfa7c5b223b26074fd8805870550a2a89d184 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 18 Aug 2021 19:48:32 +0900 Subject: [PATCH 8/9] update --- .circleci/config.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index df482132b..b80032ae8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,6 +3,7 @@ workflows: node-multi-build: jobs: - node-v8 + - node-v10 - eslint-v7 - eslint-v8 - node-v12 @@ -51,6 +52,10 @@ jobs: - run: name: Test command: npm test + node-v10: + <<: *node-base + docker: + - image: node:10 eslint-v7: docker: - image: node:10 From ab0d003e0e1827defae81764a527914f83eeaad5 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 25 Aug 2021 19:37:34 +0900 Subject: [PATCH 9/9] update doc --- docs/user-guide/README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md index 343b11c22..39e44d9ab 100644 --- a/docs/user-guide/README.md +++ b/docs/user-guide/README.md @@ -25,6 +25,8 @@ yarn add -D eslint eslint-plugin-vue - ESLint v6.2.0 and above - Node.js v8.10.0 and above +We have started supporting ESLint v8.0.0 beta, but note that beta support will be dropped once the stable version is released. + ::: ## :book: Usage