From b2dcb5202a5416d7fa364370b8e420e41eff0231 Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Wed, 21 Apr 2021 20:19:36 +0900 Subject: [PATCH 1/2] Supports ES2022 (Class Fileds) --- .vscode/settings.json | 6 +- lib/rules/require-slots-as-functions.js | 4 + lib/utils/indent-common.js | 19 ++- lib/utils/index.js | 7 +- .../class-fields-private-methods-01.vue | 64 ++++++++++ .../class-fields-private-properties-01.vue | 22 ++++ .../class-fields-properties-01.vue | 16 +++ .../class-fields-properties-02.vue | 12 ++ tests/lib/rules/script-indent.js | 2 +- typings/eslint-plugin-vue/global.d.ts | 2 + .../eslint-plugin-vue/util-types/ast/ast.ts | 4 + .../util-types/ast/es-ast.ts | 117 ++++++++++++++++-- 12 files changed, 252 insertions(+), 23 deletions(-) create mode 100644 tests/fixtures/script-indent/class-fields-private-methods-01.vue create mode 100644 tests/fixtures/script-indent/class-fields-private-properties-01.vue create mode 100644 tests/fixtures/script-indent/class-fields-properties-01.vue create mode 100644 tests/fixtures/script-indent/class-fields-properties-02.vue diff --git a/.vscode/settings.json b/.vscode/settings.json index e8e944bef..2c99a4943 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -9,5 +9,9 @@ "vue" ], "typescript.tsdk": "node_modules/typescript/lib", - "vetur.validation.script": false + "vetur.validation.script": false, + "[typescript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, } diff --git a/lib/rules/require-slots-as-functions.js b/lib/rules/require-slots-as-functions.js index 658071d22..0e83eeda7 100644 --- a/lib/rules/require-slots-as-functions.js +++ b/lib/rules/require-slots-as-functions.js @@ -113,6 +113,10 @@ module.exports = { if (!utils.isThis(object.object, context)) { return } + if (node.property.type === 'PrivateIdentifier') { + // Unreachable + return + } verify(node, node.property) } }) diff --git a/lib/utils/indent-common.js b/lib/utils/indent-common.js index 9e6bfdeff..bc764b999 100644 --- a/lib/utils/indent-common.js +++ b/lib/utils/indent-common.js @@ -60,8 +60,10 @@ const KNOWN_NODES = new Set([ 'NewExpression', 'ObjectExpression', 'ObjectPattern', + 'PrivateIdentifier', 'Program', 'Property', + 'PropertyDefinition', 'RestElement', 'ReturnStatement', 'SequenceExpression', @@ -667,7 +669,7 @@ module.exports.defineVisitor = function create( /** * Collect prefix tokens of the given property. * The prefix includes `async`, `get`, `set`, `static`, and `*`. - * @param {Property|MethodDefinition} node The property node to collect prefix tokens. + * @param {Property|MethodDefinition|PropertyDefinition} node The property node to collect prefix tokens. */ function getPrefixTokens(node) { const prefixes = [] @@ -1759,9 +1761,8 @@ module.exports.defineVisitor = function create( setOffset([dotToken, propertyToken], 1, objectToken) } }, - /** @param {MethodDefinition | Property} node */ - 'MethodDefinition, Property'(node) { - const isMethod = node.type === 'MethodDefinition' || node.method === true + /** @param {MethodDefinition | Property | PropertyDefinition} node */ + 'MethodDefinition, Property, PropertyDefinition'(node) { const prefixTokens = getPrefixTokens(node) const hasPrefix = prefixTokens.length >= 1 @@ -1795,7 +1796,10 @@ module.exports.defineVisitor = function create( } } - if (isMethod) { + if ( + node.type === 'MethodDefinition' || + (node.type === 'Property' && node.method === true) + ) { const leftParenToken = tokenStore.getTokenAfter(lastKeyToken) setOffset(leftParenToken, 1, lastKeyToken) @@ -1804,6 +1808,11 @@ module.exports.defineVisitor = function create( const valueToken = tokenStore.getTokenAfter(colonToken) setOffset([colonToken, valueToken], 1, lastKeyToken) + } else if (node.type === 'PropertyDefinition' && node.value != null) { + const eqToken = tokenStore.getTokenAfter(lastKeyToken) + const initToken = tokenStore.getTokenAfter(eqToken) + + setOffset([eqToken, initToken], 1, lastKeyToken) } }, /** @param {NewExpression} node */ diff --git a/lib/utils/index.js b/lib/utils/index.js index 0bcf80169..4dc69c6c7 100644 --- a/lib/utils/index.js +++ b/lib/utils/index.js @@ -1798,23 +1798,24 @@ function skipChainExpression(node) { */ function getStaticPropertyName(node) { if (node.type === 'Property' || node.type === 'MethodDefinition') { - const key = node.key - if (!node.computed) { + const key = node.key if (key.type === 'Identifier') { return key.name } } + const key = node.key // @ts-expect-error return getStringLiteralValue(key) } else if (node.type === 'MemberExpression') { - const property = node.property if (!node.computed) { + const property = node.property if (property.type === 'Identifier') { return property.name } return null } + const property = node.property // @ts-expect-error return getStringLiteralValue(property) } diff --git a/tests/fixtures/script-indent/class-fields-private-methods-01.vue b/tests/fixtures/script-indent/class-fields-private-methods-01.vue new file mode 100644 index 000000000..46c6ae67c --- /dev/null +++ b/tests/fixtures/script-indent/class-fields-private-methods-01.vue @@ -0,0 +1,64 @@ + + diff --git a/tests/fixtures/script-indent/class-fields-private-properties-01.vue b/tests/fixtures/script-indent/class-fields-private-properties-01.vue new file mode 100644 index 000000000..c935f1904 --- /dev/null +++ b/tests/fixtures/script-indent/class-fields-private-properties-01.vue @@ -0,0 +1,22 @@ + + diff --git a/tests/fixtures/script-indent/class-fields-properties-01.vue b/tests/fixtures/script-indent/class-fields-properties-01.vue new file mode 100644 index 000000000..ef193d1fe --- /dev/null +++ b/tests/fixtures/script-indent/class-fields-properties-01.vue @@ -0,0 +1,16 @@ + + diff --git a/tests/fixtures/script-indent/class-fields-properties-02.vue b/tests/fixtures/script-indent/class-fields-properties-02.vue new file mode 100644 index 000000000..3efe3bd25 --- /dev/null +++ b/tests/fixtures/script-indent/class-fields-properties-02.vue @@ -0,0 +1,12 @@ + + diff --git a/tests/lib/rules/script-indent.js b/tests/lib/rules/script-indent.js index 865e73141..e2ad23e99 100644 --- a/tests/lib/rules/script-indent.js +++ b/tests/lib/rules/script-indent.js @@ -114,7 +114,7 @@ function unIndent(strings) { const tester = new RuleTester({ parser: require.resolve('vue-eslint-parser'), parserOptions: { - ecmaVersion: 2020, + ecmaVersion: 2022, sourceType: 'module' } }) diff --git a/typings/eslint-plugin-vue/global.d.ts b/typings/eslint-plugin-vue/global.d.ts index aa23a1a21..5b185bb24 100644 --- a/typings/eslint-plugin-vue/global.d.ts +++ b/typings/eslint-plugin-vue/global.d.ts @@ -71,6 +71,7 @@ declare global { // ---- ES Nodes ---- type Identifier = VAST.Identifier + type PrivateIdentifier = VAST.PrivateIdentifier type Literal = VAST.Literal type Program = VAST.Program type SwitchCase = VAST.SwitchCase @@ -135,6 +136,7 @@ declare global { type AssignmentPattern = VAST.AssignmentPattern type ClassBody = VAST.ClassBody type MethodDefinition = VAST.MethodDefinition + type PropertyDefinition = VAST.PropertyDefinition type ModuleDeclaration = VAST.ModuleDeclaration type ImportDeclaration = VAST.ImportDeclaration type ExportNamedDeclaration = VAST.ExportNamedDeclaration diff --git a/typings/eslint-plugin-vue/util-types/ast/ast.ts b/typings/eslint-plugin-vue/util-types/ast/ast.ts index d8f21315c..e29c199fe 100644 --- a/typings/eslint-plugin-vue/util-types/ast/ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/ast.ts @@ -179,6 +179,8 @@ export type NodeListenerMap = { export type ESNodeListenerMap = { Identifier: ES.Identifier 'Identifier:exit': ES.Identifier + PrivateIdentifier: ES.PrivateIdentifier + 'PrivateIdentifier:exit': ES.PrivateIdentifier Literal: ES.Literal 'Literal:exit': ES.Literal Program: ES.Program @@ -317,6 +319,8 @@ export type ESNodeListenerMap = { 'ClassBody:exit': ES.ClassBody MethodDefinition: ES.MethodDefinition 'MethodDefinition:exit': ES.MethodDefinition + PropertyDefinition: ES.PropertyDefinition + 'PropertyDefinition:exit': ES.PropertyDefinition ImportDeclaration: ES.ImportDeclaration 'ImportDeclaration:exit': ES.ImportDeclaration ExportNamedDeclaration: ES.ExportNamedDeclaration diff --git a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts index 9af1e7d06..1a2519a1a 100644 --- a/typings/eslint-plugin-vue/util-types/ast/es-ast.ts +++ b/typings/eslint-plugin-vue/util-types/ast/es-ast.ts @@ -9,6 +9,7 @@ import * as TS from './ts-ast' import * as JSX from './jsx-ast' export type ESNode = + | PrivateIdentifier | Identifier | Literal | Program @@ -25,6 +26,7 @@ export type ESNode = | Pattern | ClassBody | MethodDefinition + | PropertyDefinition | ModuleDeclaration | ModuleSpecifier @@ -185,16 +187,61 @@ export interface ClassDeclaration extends HasParentNode { } export interface ClassBody extends HasParentNode { type: 'ClassBody' - body: MethodDefinition[] + body: (MethodDefinition | PropertyDefinition)[] } -export interface MethodDefinition extends HasParentNode { +interface BaseMethodDefinition extends HasParentNode { type: 'MethodDefinition' + // computed: boolean + static: boolean + // key: Expression + value: FunctionExpression + parent: ClassBody +} +export interface MethodDefinitionNonComputedName extends BaseMethodDefinition { + kind: 'constructor' | 'method' | 'get' | 'set' + computed: false + key: Identifier | Literal +} +export interface MethodDefinitionComputedName extends BaseMethodDefinition { kind: 'constructor' | 'method' | 'get' | 'set' - computed: boolean + computed: true + key: Expression +} +export interface MethodDefinitionPrivate extends BaseMethodDefinition { + kind: 'constructor' + computed: false + key: PrivateIdentifier +} +export type MethodDefinition = + | MethodDefinitionNonComputedName + | MethodDefinitionComputedName + | MethodDefinitionPrivate +interface BasePropertyDefinition extends HasParentNode { + type: 'PropertyDefinition' + // key: Expression | PrivateIdentifier + value: Expression | null + // computed: boolean static: boolean + parent: ClassBody +} +export interface PropertyDefinitionNonComputedName + extends BasePropertyDefinition { + computed: false + key: Identifier | Literal +} +export interface PropertyDefinitionComputedName extends BasePropertyDefinition { + computed: true key: Expression - value: FunctionExpression } +export interface PropertyDefinitionPrivate extends BasePropertyDefinition { + computed: false + key: PrivateIdentifier +} +export type PropertyDefinition = + | PropertyDefinitionNonComputedName + | PropertyDefinitionComputedName + | PropertyDefinitionPrivate + export type ModuleDeclaration = | ImportDeclaration | ExportNamedDeclaration @@ -284,6 +331,10 @@ export interface Identifier extends HasParentNode { type: 'Identifier' name: string } +export interface PrivateIdentifier extends HasParentNode { + type: 'PrivateIdentifier' + name: string +} export interface Literal extends HasParentNode { type: 'Literal' value: string | boolean | null | number | RegExp | BigInt @@ -304,16 +355,25 @@ export interface ObjectExpression extends HasParentNode { type: 'ObjectExpression' properties: (Property | SpreadElement)[] } -export interface Property extends HasParentNode { +interface BaseProperty extends HasParentNode { type: 'Property' kind: 'init' | 'get' | 'set' method: boolean shorthand: boolean - computed: boolean - key: Expression + // computed: boolean + // key: Expression value: Expression parent: ObjectExpression } +export interface PropertyNonComputedName extends BaseProperty { + computed: false + key: Identifier | Literal +} +export interface PropertyComputedName extends BaseProperty { + computed: true + key: Expression +} +export type Property = PropertyNonComputedName | PropertyComputedName export interface FunctionExpression extends HasParentNode { type: 'FunctionExpression' async: boolean @@ -444,13 +504,32 @@ export interface NewExpression extends HasParentNode { callee: Expression arguments: (Expression | SpreadElement)[] } -export interface MemberExpression extends HasParentNode { +interface BaseMemberExpression extends HasParentNode { type: 'MemberExpression' - computed: boolean + // computed: boolean + // object: Expression | Super + // property: Expression + optional: boolean +} +export interface MemberExpressionNonComputedName extends BaseMemberExpression { + computed: false + object: Expression | Super + property: Identifier +} +export interface MemberExpressionComputedName extends BaseMemberExpression { + computed: true object: Expression | Super property: Expression - optional: boolean } +export interface MemberExpressionPrivate extends BaseMemberExpression { + computed: false + object: Expression + property: PrivateIdentifier +} +export type MemberExpression = + | MemberExpressionNonComputedName + | MemberExpressionComputedName + | MemberExpressionPrivate export interface ChainExpression extends HasParentNode { type: 'ChainExpression' expression: ChainElement @@ -506,16 +585,28 @@ export interface ObjectPattern extends HasParentNode { type: 'ObjectPattern' properties: (AssignmentProperty | RestElement)[] } -export interface AssignmentProperty extends HasParentNode { +interface BaseAssignmentProperty extends HasParentNode { type: 'Property' kind: 'init' method: false shorthand: boolean - computed: boolean - key: Expression + // computed: boolean + // key: Expression value: Pattern parent: ObjectPattern } +export interface AssignmentPropertyNonComputedName + extends BaseAssignmentProperty { + computed: false + key: Identifier | Literal +} +export interface AssignmentPropertyComputedName extends BaseAssignmentProperty { + computed: true + key: Expression +} +export type AssignmentProperty = + | AssignmentPropertyNonComputedName + | AssignmentPropertyComputedName export interface ArrayPattern extends HasParentNode { type: 'ArrayPattern' elements: Pattern[] From 695dc3f8617a5611c147e780541e4787627dda1b Mon Sep 17 00:00:00 2001 From: yosuke ota Date: Sat, 12 Jun 2021 11:39:17 +0900 Subject: [PATCH 2/2] Update --- package.json | 1 + tests/lib/rules/script-indent.js | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 5893d0cab..4c7d6a782 100644 --- a/package.json +++ b/package.json @@ -74,6 +74,7 @@ "eslint-plugin-prettier": "^3.1.3", "eslint-plugin-vue": "file:.", "eslint4b": "^7.0.0", + "espree": "^8.0.0-0", "lodash": "^4.17.15", "mocha": "^7.1.2", "nyc": "^15.0.1", diff --git a/tests/lib/rules/script-indent.js b/tests/lib/rules/script-indent.js index e2ad23e99..37fa67293 100644 --- a/tests/lib/rules/script-indent.js +++ b/tests/lib/rules/script-indent.js @@ -115,7 +115,8 @@ const tester = new RuleTester({ parser: require.resolve('vue-eslint-parser'), parserOptions: { ecmaVersion: 2022, - sourceType: 'module' + sourceType: 'module', + parser: require.resolve('espree') // espree v8.0.0-beta.x } })