You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
110 lines
2.8 KiB
110 lines
2.8 KiB
/** |
|
* @fileoverview Disallow undeclared variables in JSX |
|
* @author Yannick Croissant |
|
*/ |
|
|
|
'use strict'; |
|
|
|
const docsUrl = require('../util/docsUrl'); |
|
const jsxUtil = require('../util/jsx'); |
|
|
|
// ------------------------------------------------------------------------------ |
|
// Rule Definition |
|
// ------------------------------------------------------------------------------ |
|
|
|
module.exports = { |
|
meta: { |
|
docs: { |
|
description: 'Disallow undeclared variables in JSX', |
|
category: 'Possible Errors', |
|
recommended: true, |
|
url: docsUrl('jsx-no-undef') |
|
}, |
|
schema: [{ |
|
type: 'object', |
|
properties: { |
|
allowGlobals: { |
|
type: 'boolean' |
|
} |
|
}, |
|
additionalProperties: false |
|
}] |
|
}, |
|
|
|
create(context) { |
|
const config = context.options[0] || {}; |
|
const allowGlobals = config.allowGlobals || false; |
|
|
|
/** |
|
* Compare an identifier with the variables declared in the scope |
|
* @param {ASTNode} node - Identifier or JSXIdentifier node |
|
* @returns {void} |
|
*/ |
|
function checkIdentifierInJSX(node) { |
|
let scope = context.getScope(); |
|
const sourceCode = context.getSourceCode(); |
|
const sourceType = sourceCode.ast.sourceType; |
|
let variables = scope.variables; |
|
let scopeType = 'global'; |
|
let i; |
|
let len; |
|
|
|
// Ignore 'this' keyword (also maked as JSXIdentifier when used in JSX) |
|
if (node.name === 'this') { |
|
return; |
|
} |
|
|
|
if (!allowGlobals && sourceType === 'module') { |
|
scopeType = 'module'; |
|
} |
|
|
|
while (scope.type !== scopeType) { |
|
scope = scope.upper; |
|
variables = scope.variables.concat(variables); |
|
} |
|
if (scope.childScopes.length) { |
|
variables = scope.childScopes[0].variables.concat(variables); |
|
// Temporary fix for babel-eslint |
|
if (scope.childScopes[0].childScopes.length) { |
|
variables = scope.childScopes[0].childScopes[0].variables.concat(variables); |
|
} |
|
} |
|
|
|
for (i = 0, len = variables.length; i < len; i++) { |
|
if (variables[i].name === node.name) { |
|
return; |
|
} |
|
} |
|
|
|
context.report({ |
|
node, |
|
message: `'${node.name}' is not defined.` |
|
}); |
|
} |
|
|
|
return { |
|
JSXOpeningElement(node) { |
|
switch (node.name.type) { |
|
case 'JSXIdentifier': |
|
if (jsxUtil.isDOMComponent(node)) { |
|
return; |
|
} |
|
node = node.name; |
|
break; |
|
case 'JSXMemberExpression': |
|
node = node.name; |
|
do { |
|
node = node.object; |
|
} while (node && node.type !== 'JSXIdentifier'); |
|
break; |
|
case 'JSXNamespacedName': |
|
node = node.name.namespace; |
|
break; |
|
default: |
|
break; |
|
} |
|
checkIdentifierInJSX(node); |
|
} |
|
}; |
|
} |
|
};
|
|
|