From 2a3de9cd19739794291e2d15ce8c2c5e7709a213 Mon Sep 17 00:00:00 2001 From: Yusei Ueno Date: Fri, 25 Nov 2022 09:47:30 +0900 Subject: [PATCH 1/3] fix(eslint-plugin): [prefer-optional-chain] collect MetaProperty type --- packages/eslint-plugin/src/rules/prefer-optional-chain.ts | 4 ++++ .../eslint-plugin/tests/rules/prefer-optional-chain.test.ts | 1 + 2 files changed, 5 insertions(+) diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index 73fbcc27af3..9e3910afb98 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -381,6 +381,10 @@ export default util.createRule({ objectText = getMemberExpressionText(node.object); break; + case AST_NODE_TYPES.MetaProperty: + objectText = getText(node.object.meta); + break; + case AST_NODE_TYPES.ThisExpression: objectText = getText(node.object); break; diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts index e1a1467d80f..a7fecfe9266 100644 --- a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts @@ -211,6 +211,7 @@ ruleTester.run('prefer-optional-chain', rule, { '!foo!.bar || !foo!.bar.baz;', '!foo!.bar!.baz || !foo!.bar!.baz!.paz;', '!foo.bar!.baz || !foo.bar!.baz!.paz;', + '!import.meta.url || true;', ], invalid: [ ...baseCases, From 027ce98b8af1a901ee4362cc252295bc03e6ca31 Mon Sep 17 00:00:00 2001 From: Yusei Ueno Date: Tue, 29 Nov 2022 09:49:55 +0900 Subject: [PATCH 2/3] fix(eslint-plugin): [prefer-optional-chain] collect MetaProperty type --- .../src/rules/prefer-optional-chain.ts | 22 ++++++--- .../tests/rules/prefer-optional-chain.test.ts | 49 ++++++++++++++++++- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts index 9e3910afb98..9a4949b734b 100644 --- a/packages/eslint-plugin/src/rules/prefer-optional-chain.ts +++ b/packages/eslint-plugin/src/rules/prefer-optional-chain.ts @@ -11,7 +11,8 @@ type ValidChainTarget = | TSESTree.ChainExpression | TSESTree.Identifier | TSESTree.MemberExpression - | TSESTree.ThisExpression; + | TSESTree.ThisExpression + | TSESTree.MetaProperty; /* The AST is always constructed such the first element is always the deepest element. @@ -115,10 +116,12 @@ export default util.createRule({ 'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > Identifier', 'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > MemberExpression', 'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > ChainExpression > MemberExpression', + 'LogicalExpression[operator="||"] > UnaryExpression[operator="!"] > MetaProperty', ].join(',')]( initialIdentifierOrNotEqualsExpr: | TSESTree.Identifier - | TSESTree.MemberExpression, + | TSESTree.MemberExpression + | TSESTree.MetaProperty, ): void { // selector guarantees this cast const initialExpression = ( @@ -190,13 +193,15 @@ export default util.createRule({ 'LogicalExpression[operator="&&"] > Identifier', 'LogicalExpression[operator="&&"] > MemberExpression', 'LogicalExpression[operator="&&"] > ChainExpression > MemberExpression', + 'LogicalExpression[operator="&&"] > MetaProperty', 'LogicalExpression[operator="&&"] > BinaryExpression[operator="!=="]', 'LogicalExpression[operator="&&"] > BinaryExpression[operator="!="]', ].join(',')]( initialIdentifierOrNotEqualsExpr: | TSESTree.BinaryExpression | TSESTree.Identifier - | TSESTree.MemberExpression, + | TSESTree.MemberExpression + | TSESTree.MetaProperty, ): void { // selector guarantees this cast const initialExpression = ( @@ -342,6 +347,10 @@ export default util.createRule({ return node.name; } + if (node.type === AST_NODE_TYPES.MetaProperty) { + return `${node.meta.name}.${node.property.name}`; + } + if (node.type === AST_NODE_TYPES.ThisExpression) { return 'this'; } @@ -382,9 +391,6 @@ export default util.createRule({ break; case AST_NODE_TYPES.MetaProperty: - objectText = getText(node.object.meta); - break; - case AST_NODE_TYPES.ThisExpression: objectText = getText(node.object); break; @@ -445,6 +451,7 @@ const ALLOWED_MEMBER_OBJECT_TYPES: ReadonlySet = new Set([ AST_NODE_TYPES.Identifier, AST_NODE_TYPES.MemberExpression, AST_NODE_TYPES.ThisExpression, + AST_NODE_TYPES.MetaProperty, ]); const ALLOWED_COMPUTED_PROP_TYPES: ReadonlySet = new Set([ AST_NODE_TYPES.Identifier, @@ -613,7 +620,8 @@ function isValidChainTarget( if ( allowIdentifier && (node.type === AST_NODE_TYPES.Identifier || - node.type === AST_NODE_TYPES.ThisExpression) + node.type === AST_NODE_TYPES.ThisExpression || + node.type === AST_NODE_TYPES.MetaProperty) ) { return true; } diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts index a7fecfe9266..680d112fae8 100644 --- a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts @@ -211,7 +211,8 @@ ruleTester.run('prefer-optional-chain', rule, { '!foo!.bar || !foo!.bar.baz;', '!foo!.bar!.baz || !foo!.bar!.baz!.paz;', '!foo.bar!.baz || !foo.bar!.baz!.paz;', - '!import.meta.url || true;', + 'import.meta || import.meta.foo;', + '!import.meta && !import.meta.foo;', ], invalid: [ ...baseCases, @@ -1322,5 +1323,51 @@ foo?.bar(/* comment */a, }, ], }, + { + code: noFormat`import.meta && import.meta?.baz;`, + output: null, + errors: [ + { + messageId: 'preferOptionalChain', + suggestions: [ + { + messageId: 'optionalChainSuggest', + output: noFormat`import.meta?.baz;`, + }, + ], + }, + ], + }, + { + code: noFormat`!import.meta || !import.meta?.baz;`, + output: null, + errors: [ + { + messageId: 'preferOptionalChain', + suggestions: [ + { + messageId: 'optionalChainSuggest', + output: noFormat`!import.meta?.baz;`, + }, + ], + }, + ], + }, + + { + code: noFormat`import.meta && import.meta?.() && import.meta?.().baz;`, + output: null, + errors: [ + { + messageId: 'preferOptionalChain', + suggestions: [ + { + messageId: 'optionalChainSuggest', + output: noFormat`import.meta?.()?.baz;`, + }, + ], + }, + ], + }, ], }); From d01801d7612f0fa955353c8b08d7882d81cc497a Mon Sep 17 00:00:00 2001 From: Josh Goldberg Date: Tue, 29 Nov 2022 13:57:30 -0500 Subject: [PATCH 3/3] Added a few more tests --- .../tests/rules/prefer-optional-chain.test.ts | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts index 680d112fae8..120ad20aaea 100644 --- a/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts +++ b/packages/eslint-plugin/tests/rules/prefer-optional-chain.test.ts @@ -211,8 +211,12 @@ ruleTester.run('prefer-optional-chain', rule, { '!foo!.bar || !foo!.bar.baz;', '!foo!.bar!.baz || !foo!.bar!.baz!.paz;', '!foo.bar!.baz || !foo.bar!.baz!.paz;', + 'import.meta || true;', 'import.meta || import.meta.foo;', + '!import.meta && false;', '!import.meta && !import.meta.foo;', + 'new.target || new.target.length;', + '!new.target || true;', ], invalid: [ ...baseCases, @@ -1323,6 +1327,33 @@ foo?.bar(/* comment */a, }, ], }, + { + code: ` + class Foo { + constructor() { + new.target && new.target.length; + } + } + `, + output: null, + errors: [ + { + messageId: 'preferOptionalChain', + suggestions: [ + { + messageId: 'optionalChainSuggest', + output: ` + class Foo { + constructor() { + new.target?.length; + } + } + `, + }, + ], + }, + ], + }, { code: noFormat`import.meta && import.meta?.baz;`, output: null,