Skip to content

Commit 155f82b

Browse files
committed
fix: handle meta properties in spread syntax
1 parent f8268f2 commit 155f82b

9 files changed

+86
-34
lines changed

lib/rules/require-meta-fixable.js

+5-9
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ module.exports = {
4848
context.options[0] && context.options[0].catchNoFixerButFixableProperty;
4949

5050
const sourceCode = context.getSourceCode();
51+
const { scopeManager } = sourceCode;
5152
const ruleInfo = utils.getRuleInfo(sourceCode);
5253
let contextIdentifiers;
5354
let usesFixFunctions;
@@ -62,10 +63,7 @@ module.exports = {
6263

6364
return {
6465
Program(ast) {
65-
contextIdentifiers = utils.getContextIdentifiers(
66-
sourceCode.scopeManager,
67-
ast
68-
);
66+
contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
6967
},
7068
CallExpression(node) {
7169
if (
@@ -86,11 +84,9 @@ module.exports = {
8684
'Program:exit'() {
8785
const metaFixableProp =
8886
ruleInfo &&
89-
ruleInfo.meta &&
90-
ruleInfo.meta.type === 'ObjectExpression' &&
91-
ruleInfo.meta.properties.find(
92-
(prop) => utils.getKeyName(prop) === 'fixable'
93-
);
87+
utils
88+
.evaluateObjectProperties(ruleInfo.meta, scopeManager)
89+
.find((prop) => utils.getKeyName(prop) === 'fixable');
9490

9591
if (metaFixableProp) {
9692
const staticValue = getStaticValue(

lib/rules/require-meta-has-suggestions.js

+5-10
Original file line numberDiff line numberDiff line change
@@ -30,16 +30,14 @@ module.exports = {
3030

3131
create(context) {
3232
const sourceCode = context.getSourceCode();
33+
const { scopeManager } = sourceCode;
3334
const ruleInfo = utils.getRuleInfo(sourceCode);
3435
let contextIdentifiers;
3536
let ruleReportsSuggestions;
3637

3738
return {
3839
Program(ast) {
39-
contextIdentifiers = utils.getContextIdentifiers(
40-
sourceCode.scopeManager,
41-
ast
42-
);
40+
contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
4341
},
4442
CallExpression(node) {
4543
if (
@@ -78,12 +76,9 @@ module.exports = {
7876
},
7977
'Program:exit'() {
8078
const metaNode = ruleInfo && ruleInfo.meta;
81-
const hasSuggestionsProperty =
82-
metaNode && metaNode.type === 'ObjectExpression'
83-
? metaNode.properties.find(
84-
(prop) => utils.getKeyName(prop) === 'hasSuggestions'
85-
)
86-
: undefined;
79+
const hasSuggestionsProperty = utils
80+
.evaluateObjectProperties(metaNode, scopeManager)
81+
.find((prop) => utils.getKeyName(prop) === 'hasSuggestions');
8782
const hasSuggestionsStaticValue =
8883
hasSuggestionsProperty &&
8984
getStaticValue(hasSuggestionsProperty.value, context.getScope());

lib/rules/require-meta-schema.js

+3-4
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,9 @@ module.exports = {
6464
Program(ast) {
6565
contextIdentifiers = utils.getContextIdentifiers(scopeManager, ast);
6666

67-
schemaNode =
68-
metaNode &&
69-
metaNode.properties &&
70-
metaNode.properties.find(
67+
schemaNode = utils
68+
.evaluateObjectProperties(metaNode, scopeManager)
69+
.find(
7170
(p) => p.type === 'Property' && utils.getKeyName(p) === 'schema'
7271
);
7372

lib/rules/require-meta-type.js

+4-6
Original file line numberDiff line numberDiff line change
@@ -41,19 +41,17 @@ module.exports = {
4141
return {
4242
Program() {
4343
const sourceCode = context.getSourceCode();
44+
const { scopeManager } = sourceCode;
4445
const info = utils.getRuleInfo(sourceCode);
4546

4647
if (info === null) {
4748
return;
4849
}
4950

5051
const metaNode = info.meta;
51-
const typeNode =
52-
metaNode &&
53-
metaNode.properties &&
54-
metaNode.properties.find(
55-
(p) => p.type === 'Property' && utils.getKeyName(p) === 'type'
56-
);
52+
const typeNode = utils
53+
.evaluateObjectProperties(metaNode, scopeManager)
54+
.find((p) => p.type === 'Property' && utils.getKeyName(p) === 'type');
5755

5856
if (!typeNode) {
5957
context.report({

lib/utils.js

+28
Original file line numberDiff line numberDiff line change
@@ -738,4 +738,32 @@ module.exports = {
738738
).suggest === parent.parent.parent
739739
);
740740
},
741+
742+
/**
743+
* List all properties contained in an object.
744+
* Evaluates and includes any properties that may be behind spreads.
745+
* @param {Node} objectNode
746+
* @param {ScopeManager} scopeManager
747+
* @returns {Node[]} the list of all properties that could be found
748+
*/
749+
evaluateObjectProperties(objectNode, scopeManager) {
750+
if (!objectNode) {
751+
return [];
752+
}
753+
754+
const spreadElements = objectNode.properties.filter(
755+
(prop) => prop.type === 'SpreadElement'
756+
);
757+
const spreadElementNodes = spreadElements.map((spreadElement) =>
758+
findVariableValue(spreadElement.argument, scopeManager)
759+
);
760+
const spreadElementObjectNodes = spreadElementNodes.filter(
761+
(node) => node && node.type === 'ObjectExpression'
762+
);
763+
const spreadElementObjectNodeProperties = spreadElementObjectNodes.flatMap(
764+
(node) => node.properties
765+
);
766+
767+
return [...objectNode.properties, ...spreadElementObjectNodeProperties];
768+
},
741769
};

tests/lib/rules/require-meta-fixable.js

+11-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const RuleTester = require('eslint').RuleTester;
1616
// Tests
1717
// ------------------------------------------------------------------------------
1818

19-
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
19+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } });
2020
ruleTester.run('require-meta-fixable', rule, {
2121
valid: [
2222
// No `meta`.
@@ -189,6 +189,16 @@ ruleTester.run('require-meta-fixable', rule, {
189189
`,
190190
options: [{ catchNoFixerButFixableProperty: true }],
191191
},
192+
// Spread.
193+
`
194+
const extra = { 'fixable': 'code' };
195+
module.exports = {
196+
meta: { ...extra },
197+
create(context) {
198+
context.report({node, message, fix: foo});
199+
}
200+
};
201+
`,
192202
],
193203

194204
invalid: [

tests/lib/rules/require-meta-has-suggestions.js

+12-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const RuleTester = require('eslint').RuleTester;
1111
// Tests
1212
// ------------------------------------------------------------------------------
1313

14-
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
14+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } });
1515
ruleTester.run('require-meta-has-suggestions', rule, {
1616
valid: [
1717
'module.exports = context => { return {}; };',
@@ -171,7 +171,7 @@ ruleTester.run('require-meta-has-suggestions', rule, {
171171
}
172172
};
173173
`,
174-
// Spread syntax.
174+
// Unrelated spread syntax.
175175
{
176176
code: `
177177
const extra = {};
@@ -185,6 +185,16 @@ ruleTester.run('require-meta-has-suggestions', rule, {
185185
ecmaVersion: 9,
186186
},
187187
},
188+
// Related spread.
189+
`
190+
const extra = { hasSuggestions: true };
191+
module.exports = {
192+
meta: { ...extra },
193+
create(context) {
194+
context.report({node, message, suggest: [{}]});
195+
}
196+
};
197+
`,
188198
],
189199

190200
invalid: [

tests/lib/rules/require-meta-schema.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ const RuleTester = require('eslint').RuleTester;
1111
// Tests
1212
// ------------------------------------------------------------------------------
1313

14-
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
14+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } });
1515
ruleTester.run('require-meta-schema', rule, {
1616
valid: [
1717
`
@@ -109,6 +109,14 @@ ruleTester.run('require-meta-schema', rule, {
109109
code: 'module.exports = { create(context) {} };',
110110
options: [{ requireSchemaPropertyWhenOptionless: false }],
111111
},
112+
// Spread.
113+
`
114+
const extra = { schema: [] };
115+
module.exports = {
116+
meta: { ...extra },
117+
create(context) {}
118+
};
119+
`,
112120
],
113121

114122
invalid: [

tests/lib/rules/require-meta-type.js

+9-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const RuleTester = require('eslint').RuleTester;
1616
// Tests
1717
// ------------------------------------------------------------------------------
1818

19-
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 6 } });
19+
const ruleTester = new RuleTester({ parserOptions: { ecmaVersion: 9 } });
2020
ruleTester.run('require-meta-type', rule, {
2121
valid: [
2222
`
@@ -76,6 +76,14 @@ ruleTester.run('require-meta-type', rule, {
7676
`,
7777
errors: [{ messageId: 'missing' }],
7878
},
79+
// Spread.
80+
`
81+
const extra = { type: 'problem' };
82+
module.exports = {
83+
meta: { ...extra },
84+
create(context) {}
85+
};
86+
`,
7987
],
8088

8189
invalid: [

0 commit comments

Comments
 (0)