diff --git a/rules/custom-error-definition.js b/rules/custom-error-definition.js index c5e47d69d0..b9081e9b75 100644 --- a/rules/custom-error-definition.js +++ b/rules/custom-error-definition.js @@ -54,12 +54,15 @@ const isAssignmentExpression = (node, name) => { return lhs.property.name === name; }; -const isClassProperty = (node, name) => { - if (node.type !== 'ClassProperty' || node.computed) { +const isPropertyDefinition = (node, name) => { + const {type, computed, key} = node; + if (type !== 'PropertyDefinition' && type !== 'ClassProperty') { return false; } - const {key} = node; + if (computed) { + return false; + } if (key.type !== 'Identifier') { return false; @@ -144,7 +147,7 @@ const customErrorDefinition = (context, node) => { const nameExpression = constructorBody.find(x => isAssignmentExpression(x, 'name')); if (!nameExpression) { - const nameProperty = body.find(node => isClassProperty(node, 'name')); + const nameProperty = body.find(node => isPropertyDefinition(node, 'name')); if (!nameProperty || !nameProperty.value || nameProperty.value.value !== name) { context.report({ diff --git a/rules/no-static-only-class.js b/rules/no-static-only-class.js index dc02da42f6..afdbc63b9b 100644 --- a/rules/no-static-only-class.js +++ b/rules/no-static-only-class.js @@ -23,9 +23,14 @@ const isDeclarationOfExportDefaultDeclaration = node => node.parent.type === 'ExportDefaultDeclaration' && node.parent.declaration === node; +// https://github.com/estree/estree/blob/master/stage3/class-features.md#propertydefinition +const isPropertyDefinition = node => node.type === 'PropertyDefinition' || + // Legacy node type + node.type === 'ClassProperty'; +const isMethodDefinition = node => node.type === 'MethodDefinition'; + function isStaticMember(node) { const { - type, private: isPrivate, static: isStatic, declare: isDeclare, @@ -37,7 +42,7 @@ function isStaticMember(node) { // Avoid matching unexpected node. For example: https://github.com/tc39/proposal-class-static-block /* istanbul ignore next */ - if (type !== 'ClassProperty' && type !== 'MethodDefinition') { + if (!isPropertyDefinition(node) && !isMethodDefinition(node)) { return false; } @@ -60,8 +65,6 @@ function isStaticMember(node) { } function * switchClassMemberToObjectProperty(node, sourceCode, fixer) { - const {type} = node; - const staticToken = sourceCode.getFirstToken(node); assertToken(staticToken, { expected: [ @@ -75,12 +78,12 @@ function * switchClassMemberToObjectProperty(node, sourceCode, fixer) { yield fixer.remove(staticToken); yield removeSpacesAfter(staticToken, sourceCode, fixer); - const maybeSemicolonToken = type === 'ClassProperty' ? + const maybeSemicolonToken = isPropertyDefinition(node) ? sourceCode.getLastToken(node) : sourceCode.getTokenAfter(node); const hasSemicolonToken = isSemicolonToken(maybeSemicolonToken); - if (type === 'ClassProperty') { + if (isPropertyDefinition(node)) { const {key, value} = node; if (value) { @@ -132,11 +135,11 @@ function switchClassToObject(node, sourceCode) { for (const node of body.body) { if ( - node.type === 'ClassProperty' && + isPropertyDefinition(node) && ( node.typeAnnotation || - // This is a stupid way to check if `value` of `ClassProperty` uses `this` - (node.value && sourceCode.getText(node).includes('this')) + // This is a stupid way to check if `value` of `PropertyDefinition` uses `this` + (node.value && sourceCode.getText(node.value).includes('this')) ) ) { return; diff --git a/rules/prevent-abbreviations.js b/rules/prevent-abbreviations.js index b78193c45d..377e05f069 100644 --- a/rules/prevent-abbreviations.js +++ b/rules/prevent-abbreviations.js @@ -538,7 +538,7 @@ const shouldReportIdentifierAsProperty = identifier => { } if ( - identifier.parent.type === 'ClassProperty' && + (identifier.parent.type === 'ClassProperty' || identifier.parent.type === 'PropertyDefinition') && identifier.parent.key === identifier && !identifier.parent.computed ) { diff --git a/test/custom-error-definition.mjs b/test/custom-error-definition.mjs index 591e15c5b6..933df282ae 100644 --- a/test/custom-error-definition.mjs +++ b/test/custom-error-definition.mjs @@ -504,6 +504,28 @@ runTest.babel({ } `, errors: [invalidNameError('ValidationError')] + }, + // `computed` + { + code: outdent` + const name = 'computed-name'; + class FooError extends Error { + [name] = 'FooError'; + constructor(message) { + super(message); + } + } + `, + output: outdent` + const name = 'computed-name'; + class FooError extends Error { + [name] = 'FooError'; + constructor(message) { + super(message); + } + } + `, + errors: [invalidNameError('FooError')] } ] }); diff --git a/test/no-static-only-class.mjs b/test/no-static-only-class.mjs index bddf6c3dcf..e654ef1400 100644 --- a/test/no-static-only-class.mjs +++ b/test/no-static-only-class.mjs @@ -175,6 +175,12 @@ test.typescript({ static b = this.a; } `), + // `this` in `key` should fixable + { + code: 'class A {static [this.a] = 1}', + output: 'const A = {[this.a] : 1,};', + errors: 1 + }, // This case should be fixable, but we simply check code of value includes `this` noFixingCase(outdent` class A { diff --git a/test/utils/test.mjs b/test/utils/test.mjs index 86401ff724..e86326a07b 100644 --- a/test/utils/test.mjs +++ b/test/utils/test.mjs @@ -45,7 +45,13 @@ class Tester { testerOptions.parserOptions = testerOptions.parserOptions || {}; testerOptions.parserOptions.babelOptions = testerOptions.parserOptions.babelOptions || {}; testerOptions.parserOptions.babelOptions.parserOpts = testerOptions.parserOptions.babelOptions.parserOpts || {}; - testerOptions.parserOptions.babelOptions.parserOpts.plugins = testerOptions.parserOptions.babelOptions.parserOpts.plugins || []; + let babelPlugins = testerOptions.parserOptions.babelOptions.parserOpts.plugins || []; + babelPlugins = [ + ['estree', {classFeatures: true}], + 'jsx', + 'classProperties', + ...babelPlugins + ]; return this.runTest({ ...tests, @@ -62,11 +68,7 @@ class Tester { ...testerOptions.parserOptions.babelOptions, parserOpts: { ...testerOptions.parserOptions.babelOptions.parserOpts, - plugins: [ - 'jsx', - 'classProperties', - ...testerOptions.parserOptions.babelOptions.parserOpts.plugins - ] + plugins: babelPlugins } } }