Skip to content

Commit 38ad521

Browse files
MrHennot-an-aardvark
authored andcommitted
Fix: Check for meta type even when using a function reference (#84)
1 parent c86c224 commit 38ad521

File tree

3 files changed

+107
-6
lines changed

3 files changed

+107
-6
lines changed

lib/rules/require-meta-type.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ module.exports = {
3030

3131
create (context) {
3232
const sourceCode = context.getSourceCode();
33-
const info = utils.getRuleInfo(sourceCode.ast);
33+
const info = utils.getRuleInfo(sourceCode.ast, sourceCode.scopeManager);
3434

3535
// ----------------------------------------------------------------------
3636
// Helpers

lib/utils.js

+66-5
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,58 @@
66
* @returns {boolean} `true` if the node is a normal function expression
77
*/
88
function isNormalFunctionExpression (node) {
9-
return (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') && !node.generator && !node.async;
9+
const functionTypes = [
10+
'FunctionExpression',
11+
'ArrowFunctionExpression',
12+
'FunctionDeclaration',
13+
];
14+
return functionTypes.includes(node.type) && !node.generator && !node.async;
15+
}
16+
17+
/**
18+
* Determines whether a node is a reference to function expression.
19+
* @param {ASTNode} node The node in question
20+
* @param {ScopeManager} scopeManager The scope manager to use for resolving references
21+
* @returns {boolean} `true` if the node is a reference to a function expression
22+
*/
23+
function isNormalFunctionExpressionReference (node, scopeManager) {
24+
if (!node || !scopeManager) {
25+
return false;
26+
}
27+
28+
if (node.type !== 'Identifier') {
29+
return false;
30+
}
31+
32+
const scope = scopeManager.acquire(node) || scopeManager.globalScope;
33+
if (!scope) {
34+
return false;
35+
}
36+
37+
const references = scope.references;
38+
const createReference = references.find(reference => {
39+
return reference.identifier === node;
40+
});
41+
42+
if (!createReference || !createReference.resolved) {
43+
return false;
44+
}
45+
46+
const definitions = createReference.resolved.defs;
47+
if (!definitions || !definitions.length) {
48+
return false;
49+
}
50+
51+
// Assumes it is immediately initialized to a function
52+
let definitionNode = definitions[0].node;
53+
54+
// If we find something like `const create = () => {}` then send the
55+
// righthand side into the type check.
56+
if (definitionNode.type === 'VariableDeclarator') {
57+
definitionNode = definitionNode.init;
58+
}
59+
60+
return isNormalFunctionExpression(definitionNode);
1061
}
1162

1263
/**
@@ -33,7 +84,7 @@ module.exports = {
3384
is an object, and `false` if module.exports is just the `create` function. If no valid ESLint rule info can be extracted
3485
from the file, the return value will be `null`.
3586
*/
36-
getRuleInfo (ast) {
87+
getRuleInfo (ast, scopeManager) {
3788
const INTERESTING_KEYS = new Set(['create', 'meta']);
3889
let exportsVarOverridden = false;
3990
let exportsIsFunction = false;
@@ -90,9 +141,19 @@ module.exports = {
90141
return currentExports;
91142
}, {});
92143

93-
return Object.prototype.hasOwnProperty.call(exportNodes, 'create') && isNormalFunctionExpression(exportNodes.create)
94-
? Object.assign({ isNewStyle: !exportsIsFunction, meta: null }, exportNodes)
95-
: null;
144+
const createExists = Object.prototype.hasOwnProperty.call(exportNodes, 'create');
145+
if (!createExists) {
146+
return null;
147+
}
148+
149+
const createIsFunction = isNormalFunctionExpression(exportNodes.create);
150+
const createIsFunctionReference = isNormalFunctionExpressionReference(exportNodes.create, scopeManager);
151+
152+
if (!createIsFunction && !createIsFunctionReference) {
153+
return null;
154+
}
155+
156+
return Object.assign({ isNewStyle: !exportsIsFunction, meta: null }, exportNodes);
96157
},
97158

98159
/**

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

+40
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,16 @@ ruleTester.run('require-meta-type', rule, {
4040
`module.exports = {
4141
create(context) {}
4242
}`,
43+
{
44+
code: `
45+
const create = {};
46+
module.exports = {
47+
meta: {},
48+
create,
49+
};
50+
`,
51+
errors: [{ messageId: 'missing' }],
52+
},
4353
],
4454

4555
invalid: [
@@ -52,6 +62,36 @@ ruleTester.run('require-meta-type', rule, {
5262
`,
5363
errors: [{ messageId: 'missing' }],
5464
},
65+
{
66+
code: `
67+
function create(context) {}
68+
module.exports = {
69+
meta: {},
70+
create,
71+
};
72+
`,
73+
errors: [{ messageId: 'missing' }],
74+
},
75+
{
76+
code: `
77+
const create = function(context) {};
78+
module.exports = {
79+
meta: {},
80+
create,
81+
};
82+
`,
83+
errors: [{ messageId: 'missing' }],
84+
},
85+
{
86+
code: `
87+
const create = (context) => {};
88+
module.exports = {
89+
meta: {},
90+
create,
91+
};
92+
`,
93+
errors: [{ messageId: 'missing' }],
94+
},
5595
{
5696
code: `
5797
module.exports = {

0 commit comments

Comments
 (0)