diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..2ac4a53df23103d1d87524a0f573129a6cb428bc --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-unsafe.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..787ccbb12935bc4ffc0ef3ceeae795f87f11d129 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-unsafe.d.ts","sourceRoot":"","sources":["no-unsafe.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js new file mode 100644 index 0000000000000000000000000000000000000000..ae59b88095efae4250c255417b93cc0dbbc9c922 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unsafe.js @@ -0,0 +1,151 @@ +/** + * @fileoverview Prevent usage of unsafe lifecycle methods + * @author Sergei Startsev + */ + +'use strict'; + +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const testReactVersion = require('../util/version').testReactVersion; +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + unsafeMethod: '{{method}} is unsafe for use in async rendering. Update the component to use {{newMethod}} instead. {{details}}', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow usage of unsafe lifecycle methods', + category: 'Best Practices', + recommended: false, + url: docsUrl('no-unsafe'), + }, + + messages, + + schema: [ + { + type: 'object', + properties: { + checkAliases: { + default: false, + type: 'boolean', + }, + }, + additionalProperties: false, + }, + ], + }, + + create(context) { + const config = context.options[0] || {}; + const checkAliases = config.checkAliases || false; + + const isApplicable = testReactVersion(context, '>= 16.3.0'); + if (!isApplicable) { + return {}; + } + + const unsafe = { + UNSAFE_componentWillMount: { + newMethod: 'componentDidMount', + details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.', + }, + UNSAFE_componentWillReceiveProps: { + newMethod: 'getDerivedStateFromProps', + details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.', + }, + UNSAFE_componentWillUpdate: { + newMethod: 'componentDidUpdate', + details: 'See https://reactjs.org/blog/2018/03/27/update-on-async-rendering.html.', + }, + }; + if (checkAliases) { + unsafe.componentWillMount = unsafe.UNSAFE_componentWillMount; + unsafe.componentWillReceiveProps = unsafe.UNSAFE_componentWillReceiveProps; + unsafe.componentWillUpdate = unsafe.UNSAFE_componentWillUpdate; + } + + /** + * Returns a list of unsafe methods + * @returns {Array} A list of unsafe methods + */ + function getUnsafeMethods() { + return Object.keys(unsafe); + } + + /** + * Checks if a passed method is unsafe + * @param {string} method Life cycle method + * @returns {boolean} Returns true for unsafe methods, otherwise returns false + */ + function isUnsafe(method) { + const unsafeMethods = getUnsafeMethods(); + return unsafeMethods.indexOf(method) !== -1; + } + + /** + * Reports the error for an unsafe method + * @param {ASTNode} node The AST node being checked + * @param {string} method Life cycle method + */ + function checkUnsafe(node, method) { + if (!isUnsafe(method)) { + return; + } + + const meta = unsafe[method]; + const newMethod = meta.newMethod; + const details = meta.details; + + const propertyNode = astUtil.getComponentProperties(node) + .find((property) => astUtil.getPropertyName(property) === method); + + report(context, messages.unsafeMethod, 'unsafeMethod', { + node: propertyNode, + data: { + method, + newMethod, + details, + }, + }); + } + + /** + * Returns life cycle methods if available + * @param {ASTNode} node The AST node being checked. + * @returns {Array} The array of methods. + */ + function getLifeCycleMethods(node) { + const properties = astUtil.getComponentProperties(node); + return properties.map((property) => astUtil.getPropertyName(property)); + } + + /** + * Checks life cycle methods + * @param {ASTNode} node The AST node being checked. + */ + function checkLifeCycleMethods(node) { + if (componentUtil.isES5Component(node, context) || componentUtil.isES6Component(node, context)) { + const methods = getLifeCycleMethods(node); + methods + .sort((a, b) => a.localeCompare(b)) + .forEach((method) => checkUnsafe(node, method)); + } + } + + return { + ClassDeclaration: checkLifeCycleMethods, + ClassExpression: checkLifeCycleMethods, + ObjectExpression: checkLifeCycleMethods, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..46103656f934d46c4f3ad51b45d29551185f0698 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-unstable-nested-components.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..4b49ff298536307e95f2750d4a69d256c9b226c9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-unstable-nested-components.d.ts","sourceRoot":"","sources":["no-unstable-nested-components.js"],"names":[],"mappings":"wBAsQW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js new file mode 100644 index 0000000000000000000000000000000000000000..c330795ff0f75802ad2360b9f729d7db21459630 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unstable-nested-components.js @@ -0,0 +1,491 @@ +/** + * @fileoverview Prevent creating unstable components inside components + * @author Ari Perkkiö + */ + +'use strict'; + +const minimatch = require('minimatch'); +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); +const isCreateElement = require('../util/isCreateElement'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Constants +// ------------------------------------------------------------------------------ + +const COMPONENT_AS_PROPS_INFO = ' If you want to allow component creation in props, set allowAsProps option to true.'; +const HOOK_REGEXP = /^use[A-Z0-9].*$/; + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +/** + * Generate error message with given parent component name + * @param {string} parentName Name of the parent component, if known + * @returns {string} Error message with parent component name + */ +function generateErrorMessageWithParentName(parentName) { + return `Do not define components during render. React will see a new component type on every render and destroy the entire subtree’s DOM nodes and state (https://reactjs.org/docs/reconciliation.html#elements-of-different-types). Instead, move this component definition out of the parent component${parentName ? ` “${parentName}” ` : ' '}and pass data as props.`; +} + +/** + * Check whether given text matches the pattern passed in. + * @param {string} text Text to validate + * @param {string} pattern Pattern to match against + * @returns {boolean} + */ +function propMatchesRenderPropPattern(text, pattern) { + return typeof text === 'string' && minimatch(text, pattern); +} + +/** + * Get closest parent matching given matcher + * @param {ASTNode} node The AST node + * @param {Context} context eslint context + * @param {Function} matcher Method used to match the parent + * @returns {ASTNode} The matching parent node, if any + */ +function getClosestMatchingParent(node, context, matcher) { + if (!node || !node.parent || node.parent.type === 'Program') { + return; + } + + if (matcher(node.parent, context)) { + return node.parent; + } + + return getClosestMatchingParent(node.parent, context, matcher); +} + +/** + * Matcher used to check whether given node is a `createElement` call + * @param {ASTNode} node The AST node + * @param {Context} context eslint context + * @returns {boolean} True if node is a `createElement` call, false if not + */ +function isCreateElementMatcher(node, context) { + return ( + astUtil.isCallExpression(node) + && isCreateElement(context, node) + ); +} + +/** + * Matcher used to check whether given node is a `ObjectExpression` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `ObjectExpression`, false if not + */ +function isObjectExpressionMatcher(node) { + return node && node.type === 'ObjectExpression'; +} + +/** + * Matcher used to check whether given node is a `JSXExpressionContainer` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `JSXExpressionContainer`, false if not + */ +function isJSXExpressionContainerMatcher(node) { + return node && node.type === 'JSXExpressionContainer'; +} + +/** + * Matcher used to check whether given node is a `JSXAttribute` of `JSXExpressionContainer` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `JSXAttribute` of `JSXExpressionContainer`, false if not + */ +function isJSXAttributeOfExpressionContainerMatcher(node) { + return ( + node + && node.type === 'JSXAttribute' + && node.value + && node.value.type === 'JSXExpressionContainer' + ); +} + +/** + * Matcher used to check whether given node is an object `Property` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `Property`, false if not + */ +function isPropertyOfObjectExpressionMatcher(node) { + return ( + node + && node.parent + && node.parent.type === 'Property' + ); +} + +/** + * Check whether given node or its parent is directly inside `map` call + * ```jsx + * {items.map(item =>
  • )} + * ``` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is directly inside `map` call, false if not + */ +function isMapCall(node) { + return ( + node + && node.callee + && node.callee.property + && node.callee.property.name === 'map' + ); +} + +/** + * Check whether given node is `ReturnStatement` of a React hook + * @param {ASTNode} node The AST node + * @param {Context} context eslint context + * @returns {boolean} True if node is a `ReturnStatement` of a React hook, false if not + */ +function isReturnStatementOfHook(node, context) { + if ( + !node + || !node.parent + || node.parent.type !== 'ReturnStatement' + ) { + return false; + } + + const callExpression = getClosestMatchingParent(node, context, astUtil.isCallExpression); + return ( + callExpression + && callExpression.callee + && HOOK_REGEXP.test(callExpression.callee.name) + ); +} + +/** + * Check whether given node is declared inside a render prop + * ```jsx + *
    } /> + * {() =>
    } + * ``` + * @param {ASTNode} node The AST node + * @param {Context} context eslint context + * @param {string} propNamePattern a pattern to match render props against + * @returns {boolean} True if component is declared inside a render prop, false if not + */ +function isComponentInRenderProp(node, context, propNamePattern) { + if ( + node + && node.parent + && node.parent.type === 'Property' + && node.parent.key + && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern) + ) { + return true; + } + + // Check whether component is a render prop used as direct children, e.g. {() =>
    } + if ( + node + && node.parent + && node.parent.type === 'JSXExpressionContainer' + && node.parent.parent + && node.parent.parent.type === 'JSXElement' + ) { + return true; + } + + const jsxExpressionContainer = getClosestMatchingParent(node, context, isJSXExpressionContainerMatcher); + + // Check whether prop name indicates accepted patterns + if ( + jsxExpressionContainer + && jsxExpressionContainer.parent + && jsxExpressionContainer.parent.type === 'JSXAttribute' + && jsxExpressionContainer.parent.name + && jsxExpressionContainer.parent.name.type === 'JSXIdentifier' + ) { + const propName = jsxExpressionContainer.parent.name.name; + + // Starts with render, e.g.
    } /> + if (propMatchesRenderPropPattern(propName, propNamePattern)) { + return true; + } + + // Uses children prop explicitly, e.g.
    } /> + if (propName === 'children') { + return true; + } + } + + return false; +} + +/** + * Check whether given node is declared directly inside a render property + * ```jsx + * const rows = { render: () =>
    } + *
    }] } /> + * ``` + * @param {ASTNode} node The AST node + * @param {string} propNamePattern The pattern to match render props against + * @returns {boolean} True if component is declared inside a render property, false if not + */ +function isDirectValueOfRenderProperty(node, propNamePattern) { + return ( + node + && node.parent + && node.parent.type === 'Property' + && node.parent.key + && node.parent.key.type === 'Identifier' + && propMatchesRenderPropPattern(node.parent.key.name, propNamePattern) + ); +} + +/** + * Resolve the component name of given node + * @param {ASTNode} node The AST node of the component + * @returns {string} Name of the component, if any + */ +function resolveComponentName(node) { + const parentName = node.id && node.id.name; + if (parentName) return parentName; + + return ( + node.type === 'ArrowFunctionExpression' + && node.parent + && node.parent.id + && node.parent.id.name + ); +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow creating unstable components inside components', + category: 'Possible Errors', + recommended: false, + url: docsUrl('no-unstable-nested-components'), + }, + schema: [{ + type: 'object', + properties: { + customValidators: { + type: 'array', + items: { + type: 'string', + }, + }, + allowAsProps: { + type: 'boolean', + }, + propNamePattern: { + type: 'string', + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components, utils) => { + const allowAsProps = context.options.some((option) => option && option.allowAsProps); + const propNamePattern = (context.options[0] || {}).propNamePattern || 'render*'; + + /** + * Check whether given node is declared inside class component's render block + * ```jsx + * class Component extends React.Component { + * render() { + * class NestedClassComponent extends React.Component { + * ... + * ``` + * @param {ASTNode} node The AST node being checked + * @returns {boolean} True if node is inside class component's render block, false if not + */ + function isInsideRenderMethod(node) { + const parentComponent = utils.getParentComponent(node); + + if (!parentComponent || parentComponent.type !== 'ClassDeclaration') { + return false; + } + + return ( + node + && node.parent + && node.parent.type === 'MethodDefinition' + && node.parent.key + && node.parent.key.name === 'render' + ); + } + + /** + * Check whether given node is a function component declared inside class component. + * Util's component detection fails to detect function components inside class components. + * ```jsx + * class Component extends React.Component { + * render() { + * const NestedComponent = () =>
    ; + * ... + * ``` + * @param {ASTNode} node The AST node being checked + * @returns {boolean} True if given node a function component declared inside class component, false if not + */ + function isFunctionComponentInsideClassComponent(node) { + const parentComponent = utils.getParentComponent(node); + const parentStatelessComponent = utils.getParentStatelessComponent(node); + + return ( + parentComponent + && parentStatelessComponent + && parentComponent.type === 'ClassDeclaration' + && utils.getStatelessComponent(parentStatelessComponent) + && utils.isReturningJSX(node) + ); + } + + /** + * Check whether given node is declared inside `createElement` call's props + * ```js + * React.createElement(Component, { + * footer: () => React.createElement("div", null) + * }) + * ``` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is declare inside `createElement` call's props, false if not + */ + function isComponentInsideCreateElementsProp(node) { + if (!components.get(node)) { + return false; + } + + const createElementParent = getClosestMatchingParent(node, context, isCreateElementMatcher); + + return ( + createElementParent + && createElementParent.arguments + && createElementParent.arguments[1] === getClosestMatchingParent(node, context, isObjectExpressionMatcher) + ); + } + + /** + * Check whether given node is declared inside a component/object prop. + * ```jsx + *
    } /> + * { footer: () =>
    } + * ``` + * @param {ASTNode} node The AST node being checked + * @returns {boolean} True if node is a component declared inside prop, false if not + */ + function isComponentInProp(node) { + if (isPropertyOfObjectExpressionMatcher(node)) { + return utils.isReturningJSX(node); + } + + const jsxAttribute = getClosestMatchingParent(node, context, isJSXAttributeOfExpressionContainerMatcher); + + if (!jsxAttribute) { + return isComponentInsideCreateElementsProp(node); + } + + return utils.isReturningJSX(node); + } + + /** + * Check whether given node is a stateless component returning non-JSX + * ```jsx + * {{ a: () => null }} + * ``` + * @param {ASTNode} node The AST node being checked + * @returns {boolean} True if node is a stateless component returning non-JSX, false if not + */ + function isStatelessComponentReturningNull(node) { + const component = utils.getStatelessComponent(node); + + return component && !utils.isReturningJSX(component); + } + + /** + * Check whether given node is a unstable nested component + * @param {ASTNode} node The AST node being checked + */ + function validate(node) { + if (!node || !node.parent) { + return; + } + + const isDeclaredInsideProps = isComponentInProp(node); + + if ( + !components.get(node) + && !isFunctionComponentInsideClassComponent(node) + && !isDeclaredInsideProps) { + return; + } + + if ( + // Support allowAsProps option + (isDeclaredInsideProps && (allowAsProps || isComponentInRenderProp(node, context, propNamePattern))) + + // Prevent reporting components created inside Array.map calls + || isMapCall(node) + || isMapCall(node.parent) + + // Do not mark components declared inside hooks (or falsy '() => null' clean-up methods) + || isReturnStatementOfHook(node, context) + + // Do not mark objects containing render methods + || isDirectValueOfRenderProperty(node, propNamePattern) + + // Prevent reporting nested class components twice + || isInsideRenderMethod(node) + + // Prevent falsely reporting detected "components" which do not return JSX + || isStatelessComponentReturningNull(node) + ) { + return; + } + + // Get the closest parent component + const parentComponent = getClosestMatchingParent( + node, + context, + (nodeToMatch) => components.get(nodeToMatch) + ); + + if (parentComponent) { + const parentName = resolveComponentName(parentComponent); + + // Exclude lowercase parents, e.g. function createTestComponent() + // React-dom prevents creating lowercase components + if (parentName && parentName[0] === parentName[0].toLowerCase()) { + return; + } + + let message = generateErrorMessageWithParentName(parentName); + + // Add information about allowAsProps option when component is declared inside prop + if (isDeclaredInsideProps && !allowAsProps) { + message += COMPONENT_AS_PROPS_INFO; + } + + report(context, message, null, { + node, + }); + } + } + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + return { + FunctionDeclaration(node) { validate(node); }, + ArrowFunctionExpression(node) { validate(node); }, + FunctionExpression(node) { validate(node); }, + ClassDeclaration(node) { validate(node); }, + CallExpression(node) { validate(node); }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7bbe6855c503ff10e8ee8ff2d5477fb175d20777 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-unused-class-component-methods.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..81f888e34049df67f69921da3ce9477ca499079a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-unused-class-component-methods.d.ts","sourceRoot":"","sources":["no-unused-class-component-methods.js"],"names":[],"mappings":"wBAoGW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js new file mode 100644 index 0000000000000000000000000000000000000000..4356cc2a5e5efa9a378a995affb77666f89d559e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-class-component-methods.js @@ -0,0 +1,258 @@ +/** + * @fileoverview Prevent declaring unused methods and properties of component class + * @author Paweł Nowak, Berton Zhu + */ + +'use strict'; + +const docsUrl = require('../util/docsUrl'); +const componentUtil = require('../util/componentUtil'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const LIFECYCLE_METHODS = new Set([ + 'constructor', + 'componentDidCatch', + 'componentDidMount', + 'componentDidUpdate', + 'componentWillMount', + 'componentWillReceiveProps', + 'componentWillUnmount', + 'componentWillUpdate', + 'getChildContext', + 'getSnapshotBeforeUpdate', + 'render', + 'shouldComponentUpdate', + 'UNSAFE_componentWillMount', + 'UNSAFE_componentWillReceiveProps', + 'UNSAFE_componentWillUpdate', +]); + +const ES6_LIFECYCLE = new Set([ + 'state', +]); + +const ES5_LIFECYCLE = new Set([ + 'getInitialState', + 'getDefaultProps', + 'mixins', +]); + +function isKeyLiteralLike(node, property) { + return property.type === 'Literal' + || (property.type === 'TemplateLiteral' && property.expressions.length === 0) + || (node.computed === false && property.type === 'Identifier'); +} + +// Descend through all wrapping TypeCastExpressions and return the expression +// that was cast. +function uncast(node) { + while (node.type === 'TypeCastExpression') { + node = node.expression; + } + return node; +} + +// Return the name of an identifier or the string value of a literal. Useful +// anywhere that a literal may be used as a key (e.g., member expressions, +// method definitions, ObjectExpression property keys). +function getName(node) { + node = uncast(node); + const type = node.type; + + if (type === 'Identifier') { + return node.name; + } + if (type === 'Literal') { + return String(node.value); + } + if (type === 'TemplateLiteral' && node.expressions.length === 0) { + return node.quasis[0].value.raw; + } + return null; +} + +function isThisExpression(node) { + return uncast(node).type === 'ThisExpression'; +} + +function getInitialClassInfo(node, isClass) { + return { + classNode: node, + isClass, + // Set of nodes where properties were defined. + properties: new Set(), + + // Set of names of properties that we've seen used. + usedProperties: new Set(), + + inStatic: false, + }; +} + +const messages = { + unused: 'Unused method or property "{{name}}"', + unusedWithClass: 'Unused method or property "{{name}}" of class "{{className}}"', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow declaring unused methods of component class', + category: 'Best Practices', + recommended: false, + url: docsUrl('no-unused-class-component-methods'), + }, + messages, + schema: [], + }, + + create: ((context) => { + let classInfo = null; + + // Takes an ObjectExpression node and adds all named Property nodes to the + // current set of properties. + function addProperty(node) { + classInfo.properties.add(node); + } + + // Adds the name of the given node as a used property if the node is an + // Identifier or a Literal. Other node types are ignored. + function addUsedProperty(node) { + const name = getName(node); + if (name) { + classInfo.usedProperties.add(name); + } + } + + function reportUnusedProperties() { + // Report all unused properties. + for (const node of classInfo.properties) { // eslint-disable-line no-restricted-syntax + const name = getName(node); + if ( + !classInfo.usedProperties.has(name) + && !LIFECYCLE_METHODS.has(name) + && (classInfo.isClass ? !ES6_LIFECYCLE.has(name) : !ES5_LIFECYCLE.has(name)) + ) { + const className = (classInfo.classNode.id && classInfo.classNode.id.name) || ''; + + const messageID = className ? 'unusedWithClass' : 'unused'; + report( + context, + messages[messageID], + messageID, + { + node, + data: { + name, + className, + }, + } + ); + } + } + } + + function exitMethod() { + if (!classInfo || !classInfo.inStatic) { + return; + } + + classInfo.inStatic = false; + } + + return { + ClassDeclaration(node) { + if (componentUtil.isES6Component(node, context)) { + classInfo = getInitialClassInfo(node, true); + } + }, + + ObjectExpression(node) { + if (componentUtil.isES5Component(node, context)) { + classInfo = getInitialClassInfo(node, false); + } + }, + + 'ClassDeclaration:exit'() { + if (!classInfo) { + return; + } + reportUnusedProperties(); + classInfo = null; + }, + + 'ObjectExpression:exit'(node) { + if (!classInfo || classInfo.classNode !== node) { + return; + } + reportUnusedProperties(); + classInfo = null; + }, + + Property(node) { + if (!classInfo || classInfo.classNode !== node.parent) { + return; + } + + if (isKeyLiteralLike(node, node.key)) { + addProperty(node.key); + } + }, + + 'ClassProperty, MethodDefinition, PropertyDefinition'(node) { + if (!classInfo) { + return; + } + + if (node.static) { + classInfo.inStatic = true; + return; + } + + if (isKeyLiteralLike(node, node.key)) { + addProperty(node.key); + } + }, + + 'ClassProperty:exit': exitMethod, + 'MethodDefinition:exit': exitMethod, + 'PropertyDefinition:exit': exitMethod, + + MemberExpression(node) { + if (!classInfo || classInfo.inStatic) { + return; + } + + if (isThisExpression(node.object) && isKeyLiteralLike(node, node.property)) { + if (node.parent.type === 'AssignmentExpression' && node.parent.left === node) { + // detect `this.property = xxx` + addProperty(node.property); + } else { + // detect `this.property()`, `x = this.property`, etc. + addUsedProperty(node.property); + } + } + }, + + VariableDeclarator(node) { + if (!classInfo || classInfo.inStatic) { + return; + } + + // detect `{ foo, bar: baz } = this` + if (node.init && isThisExpression(node.init) && node.id.type === 'ObjectPattern') { + node.id.properties + .filter((prop) => prop.type === 'Property' && isKeyLiteralLike(prop, prop.key)) + .forEach((prop) => { + addUsedProperty('key' in prop ? prop.key : undefined); + }); + } + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..b6ca198b1048f650759b01d4f51f6ccb24264c75 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-unused-prop-types.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..db35eda1c8ccfedeebc793e858d160f7b2f596f8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-unused-prop-types.d.ts","sourceRoot":"","sources":["no-unused-prop-types.js"],"names":[],"mappings":"wBAiCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js new file mode 100644 index 0000000000000000000000000000000000000000..09dcfb5b588fc13ceaa09dd72aa96d9b64bbca3d --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-prop-types.js @@ -0,0 +1,171 @@ +/** + * @fileoverview Prevent definitions of unused prop types + * @author Evgueni Naverniouk + */ + +'use strict'; + +const values = require('object.values'); + +// As for exceptions for props.children or props.className (and alike) look at +// https://github.com/jsx-eslint/eslint-plugin-react/issues/7 + +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +/** + * Checks if the component must be validated + * @param {Object} component The component to process + * @returns {boolean} True if the component must be validated, false if not. + */ +function mustBeValidated(component) { + return !!component && !component.ignoreUnusedPropTypesValidation; +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + unusedPropType: '\'{{name}}\' PropType is defined but prop is never used', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow definitions of unused propTypes', + category: 'Best Practices', + recommended: false, + url: docsUrl('no-unused-prop-types'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + ignore: { + type: 'array', + items: { + type: 'string', + }, + uniqueItems: true, + }, + customValidators: { + type: 'array', + items: { + type: 'string', + }, + }, + skipShapeProps: { + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components) => { + const defaults = { skipShapeProps: true, customValidators: [], ignore: [] }; + const configuration = Object.assign({}, defaults, context.options[0] || {}); + + /** + * Checks if the prop is ignored + * @param {string} name Name of the prop to check. + * @returns {boolean} True if the prop is ignored, false if not. + */ + function isIgnored(name) { + return configuration.ignore.indexOf(name) !== -1; + } + + /** + * Checks if a prop is used + * @param {ASTNode} node The AST node being checked. + * @param {Object} prop Declared prop object + * @returns {boolean} True if the prop is used, false if not. + */ + function isPropUsed(node, prop) { + const usedPropTypes = node.usedPropTypes || []; + for (let i = 0, l = usedPropTypes.length; i < l; i++) { + const usedProp = usedPropTypes[i]; + if ( + prop.type === 'shape' + || prop.type === 'exact' + || prop.name === '__ANY_KEY__' + || usedProp.name === prop.name + ) { + return true; + } + } + + return false; + } + + /** + * Used to recursively loop through each declared prop type + * @param {Object} component The component to process + * @param {ASTNode[]|true} props List of props to validate + */ + function reportUnusedPropType(component, props) { + // Skip props that check instances + if (props === true) { + return; + } + + Object.keys(props || {}).forEach((key) => { + const prop = props[key]; + // Skip props that check instances + if (prop === true) { + return; + } + + if ((prop.type === 'shape' || prop.type === 'exact') && configuration.skipShapeProps) { + return; + } + + if (prop.node && prop.node.typeAnnotation && prop.node.typeAnnotation.typeAnnotation + && prop.node.typeAnnotation.typeAnnotation.type === 'TSNeverKeyword') { + return; + } + + if (prop.node && !isIgnored(prop.fullName) && !isPropUsed(component, prop)) { + report(context, messages.unusedPropType, 'unusedPropType', { + node: prop.node.key || prop.node, + data: { + name: prop.fullName, + }, + }); + } + + if (prop.children) { + reportUnusedPropType(component, prop.children); + } + }); + } + + /** + * Reports unused proptypes for a given component + * @param {Object} component The component to process + */ + function reportUnusedPropTypes(component) { + reportUnusedPropType(component, component.declaredPropTypes); + } + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + return { + 'Program:exit'() { + // Report undeclared proptypes for all classes + values(components.list()) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + reportUnusedPropTypes(component); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..6d090aa45c27c62250ecdb2e602cd08f36dc20b0 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-unused-state.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..fe3ea0acf52a64e714d3a8c41c1413e9879a8662 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-unused-state.d.ts","sourceRoot":"","sources":["no-unused-state.js"],"names":[],"mappings":"wBAgFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js new file mode 100644 index 0000000000000000000000000000000000000000..c1986755c4260a7e5acde6cd9b21039f896db23c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-unused-state.js @@ -0,0 +1,529 @@ +/** + * @fileoverview Attempts to discover all state fields in a React component and + * warn if any of them are never read. + * + * State field definitions are collected from `this.state = {}` assignments in + * the constructor, objects passed to `this.setState()`, and `state = {}` class + * property assignments. + */ + +'use strict'; + +const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; + +// Descend through all wrapping TypeCastExpressions and return the expression +// that was cast. +function uncast(node) { + while (node.type === 'TypeCastExpression') { + node = node.expression; + } + return node; +} + +// Return the name of an identifier or the string value of a literal. Useful +// anywhere that a literal may be used as a key (e.g., member expressions, +// method definitions, ObjectExpression property keys). +function getName(node) { + node = uncast(node); + const type = node.type; + + if (type === 'Identifier') { + return node.name; + } + if (type === 'Literal') { + return String(node.value); + } + if (type === 'TemplateLiteral' && node.expressions.length === 0) { + return node.quasis[0].value.raw; + } + return null; +} + +function isThisExpression(node) { + return astUtil.unwrapTSAsExpression(uncast(node)).type === 'ThisExpression'; +} + +function getInitialClassInfo() { + return { + // Set of nodes where state fields were defined. + stateFields: new Set(), + + // Set of names of state fields that we've seen used. + usedStateFields: new Set(), + + // Names of local variables that may be pointing to this.state. To + // track this properly, we would need to keep track of all locals, + // shadowing, assignments, etc. To keep things simple, we only + // maintain one set of aliases per method and accept that it will + // produce some false negatives. + aliases: null, + }; +} + +function isSetStateCall(node) { + const unwrappedCalleeNode = astUtil.unwrapTSAsExpression(node.callee); + + return ( + unwrappedCalleeNode.type === 'MemberExpression' + && isThisExpression(unwrappedCalleeNode.object) + && getName(unwrappedCalleeNode.property) === 'setState' + ); +} + +const messages = { + unusedStateField: 'Unused state field: \'{{name}}\'', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow definitions of unused state', + category: 'Best Practices', + recommended: false, + url: docsUrl('no-unused-state'), + }, + + messages, + + schema: [], + }, + + create(context) { + // Non-null when we are inside a React component ClassDeclaration and we have + // not yet encountered any use of this.state which we have chosen not to + // analyze. If we encounter any such usage (like this.state being spread as + // JSX attributes), then this is again set to null. + let classInfo = null; + + function isStateParameterReference(node) { + const classMethods = [ + 'shouldComponentUpdate', + 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', + 'getSnapshotBeforeUpdate', + 'componentDidUpdate', + ]; + + let scope = getScope(context, node); + while (scope) { + const parent = scope.block && scope.block.parent; + if ( + parent + && parent.type === 'MethodDefinition' && ( + (parent.static && parent.key.name === 'getDerivedStateFromProps') + || classMethods.indexOf(parent.key.name) !== -1 + ) + && parent.value.type === 'FunctionExpression' + && parent.value.params[1] + && parent.value.params[1].name === node.name + ) { + return true; + } + scope = scope.upper; + } + + return false; + } + + // Returns true if the given node is possibly a reference to `this.state` or the state parameter of + // a lifecycle method. + function isStateReference(node) { + node = uncast(node); + + const isDirectStateReference = node.type === 'MemberExpression' + && isThisExpression(node.object) + && node.property.name === 'state'; + + const isAliasedStateReference = node.type === 'Identifier' + && classInfo.aliases + && classInfo.aliases.has(node.name); + + return isDirectStateReference || isAliasedStateReference || isStateParameterReference(node); + } + + // Takes an ObjectExpression node and adds all named Property nodes to the + // current set of state fields. + function addStateFields(node) { + node.properties.filter((prop) => ( + prop.type === 'Property' + && (prop.key.type === 'Literal' + || (prop.key.type === 'TemplateLiteral' && prop.key.expressions.length === 0) + || (prop.computed === false && prop.key.type === 'Identifier')) + && getName(prop.key) !== null + )).forEach((prop) => { + classInfo.stateFields.add(prop); + }); + } + + // Adds the name of the given node as a used state field if the node is an + // Identifier or a Literal. Other node types are ignored. + function addUsedStateField(node) { + if (!classInfo) { + return; + } + const name = getName(node); + if (name) { + classInfo.usedStateFields.add(name); + } + } + + // Records used state fields and new aliases for an ObjectPattern which + // destructures `this.state`. + function handleStateDestructuring(node) { + node.properties.forEach((prop) => { + if (prop.type === 'Property') { + addUsedStateField(prop.key); + } else if ( + (prop.type === 'ExperimentalRestProperty' || prop.type === 'RestElement') + && classInfo.aliases + ) { + classInfo.aliases.add(getName(prop.argument)); + } + }); + } + + // Used to record used state fields and new aliases for both + // AssignmentExpressions and VariableDeclarators. + function handleAssignment(left, right) { + const unwrappedRight = astUtil.unwrapTSAsExpression(right); + + switch (left.type) { + case 'Identifier': + if (isStateReference(unwrappedRight) && classInfo.aliases) { + classInfo.aliases.add(left.name); + } + break; + case 'ObjectPattern': + if (isStateReference(unwrappedRight)) { + handleStateDestructuring(left); + } else if (isThisExpression(unwrappedRight) && classInfo.aliases) { + left.properties.forEach((prop) => { + if (prop.type === 'Property' && getName(prop.key) === 'state') { + const name = getName(prop.value); + if (name) { + classInfo.aliases.add(name); + } else if (prop.value.type === 'ObjectPattern') { + handleStateDestructuring(prop.value); + } + } + }); + } + break; + default: + // pass + } + } + + function reportUnusedFields() { + // Report all unused state fields. + classInfo.stateFields.forEach((node) => { + const name = getName(node.key); + if (!classInfo.usedStateFields.has(name)) { + report(context, messages.unusedStateField, 'unusedStateField', { + node, + data: { + name, + }, + }); + } + }); + } + + function handleES6ComponentEnter(node) { + if (componentUtil.isES6Component(node, context)) { + classInfo = getInitialClassInfo(); + } + } + + function handleES6ComponentExit() { + if (!classInfo) { + return; + } + reportUnusedFields(); + classInfo = null; + } + + function isGDSFP(node) { + const name = getName(node.key); + if ( + !node.static + || name !== 'getDerivedStateFromProps' + || !node.value + || !node.value.params + || node.value.params.length < 2 // no `state` argument + ) { + return false; + } + return true; + } + + return { + ClassDeclaration: handleES6ComponentEnter, + + 'ClassDeclaration:exit': handleES6ComponentExit, + + ClassExpression: handleES6ComponentEnter, + + 'ClassExpression:exit': handleES6ComponentExit, + + ObjectExpression(node) { + if (componentUtil.isES5Component(node, context)) { + classInfo = getInitialClassInfo(); + } + }, + + 'ObjectExpression:exit'(node) { + if (!classInfo) { + return; + } + + if (componentUtil.isES5Component(node, context)) { + reportUnusedFields(); + classInfo = null; + } + }, + + CallExpression(node) { + if (!classInfo) { + return; + } + + const unwrappedNode = astUtil.unwrapTSAsExpression(node); + const unwrappedArgumentNode = astUtil.unwrapTSAsExpression(unwrappedNode.arguments[0]); + + // If we're looking at a `this.setState({})` invocation, record all the + // properties as state fields. + if ( + isSetStateCall(unwrappedNode) + && unwrappedNode.arguments.length > 0 + && unwrappedArgumentNode.type === 'ObjectExpression' + ) { + addStateFields(unwrappedArgumentNode); + } else if ( + isSetStateCall(unwrappedNode) + && unwrappedNode.arguments.length > 0 + && unwrappedArgumentNode.type === 'ArrowFunctionExpression' + ) { + const unwrappedBodyNode = astUtil.unwrapTSAsExpression(unwrappedArgumentNode.body); + + if (unwrappedBodyNode.type === 'ObjectExpression') { + addStateFields(unwrappedBodyNode); + } + if (unwrappedArgumentNode.params.length > 0 && classInfo.aliases) { + const firstParam = unwrappedArgumentNode.params[0]; + if (firstParam.type === 'ObjectPattern') { + handleStateDestructuring(firstParam); + } else { + classInfo.aliases.add(getName(firstParam)); + } + } + } + }, + + 'ClassProperty, PropertyDefinition'(node) { + if (!classInfo) { + return; + } + // If we see state being assigned as a class property using an object + // expression, record all the fields of that object as state fields. + const unwrappedValueNode = astUtil.unwrapTSAsExpression(node.value); + + const name = getName(node.key); + if ( + name === 'state' + && !node.static + && unwrappedValueNode + && unwrappedValueNode.type === 'ObjectExpression' + ) { + addStateFields(unwrappedValueNode); + } + + if ( + !node.static + && unwrappedValueNode + && unwrappedValueNode.type === 'ArrowFunctionExpression' + ) { + // Create a new set for this.state aliases local to this method. + classInfo.aliases = new Set(); + } + }, + + 'ClassProperty:exit'(node) { + if ( + classInfo + && !node.static + && node.value + && node.value.type === 'ArrowFunctionExpression' + ) { + // Forget our set of local aliases. + classInfo.aliases = null; + } + }, + + 'PropertyDefinition, ClassProperty'(node) { + if (!isGDSFP(node)) { + return; + } + + const childScope = getScope(context, node).childScopes.find((x) => x.block === node.value); + if (!childScope) { + return; + } + const scope = childScope.variableScope.childScopes.find((x) => x.block === node.value); + const stateArg = node.value.params[1]; // probably "state" + if (!scope || !scope.variables) { + return; + } + const argVar = scope.variables.find((x) => x.name === stateArg.name); + + if (argVar) { + const stateRefs = argVar.references; + + stateRefs.forEach((ref) => { + const identifier = ref.identifier; + if (identifier && identifier.parent && identifier.parent.type === 'MemberExpression') { + addUsedStateField(identifier.parent.property); + } + }); + } + }, + + 'PropertyDefinition:exit'(node) { + if ( + classInfo + && !node.static + && node.value + && node.value.type === 'ArrowFunctionExpression' + && !isGDSFP(node) + ) { + // Forget our set of local aliases. + classInfo.aliases = null; + } + }, + + MethodDefinition() { + if (!classInfo) { + return; + } + // Create a new set for this.state aliases local to this method. + classInfo.aliases = new Set(); + }, + + 'MethodDefinition:exit'() { + if (!classInfo) { + return; + } + // Forget our set of local aliases. + classInfo.aliases = null; + }, + + FunctionExpression(node) { + if (!classInfo) { + return; + } + + const parent = node.parent; + if (!componentUtil.isES5Component(parent.parent, context)) { + return; + } + + if ( + 'key' in parent + && 'name' in parent.key + && parent.key.name === 'getInitialState' + ) { + const body = node.body.body; + const lastBodyNode = body[body.length - 1]; + + if ( + lastBodyNode.type === 'ReturnStatement' + && lastBodyNode.argument.type === 'ObjectExpression' + ) { + addStateFields(lastBodyNode.argument); + } + } else { + // Create a new set for this.state aliases local to this method. + classInfo.aliases = new Set(); + } + }, + + AssignmentExpression(node) { + if (!classInfo) { + return; + } + + const unwrappedLeft = astUtil.unwrapTSAsExpression(node.left); + const unwrappedRight = astUtil.unwrapTSAsExpression(node.right); + + // Check for assignments like `this.state = {}` + if ( + unwrappedLeft.type === 'MemberExpression' + && isThisExpression(unwrappedLeft.object) + && getName(unwrappedLeft.property) === 'state' + && unwrappedRight.type === 'ObjectExpression' + ) { + // Find the nearest function expression containing this assignment. + /** @type {import('eslint').Rule.Node} */ + let fn = node; + while (fn.type !== 'FunctionExpression' && fn.parent) { + fn = fn.parent; + } + // If the nearest containing function is the constructor, then we want + // to record all the assigned properties as state fields. + if ( + fn.parent + && fn.parent.type === 'MethodDefinition' + && fn.parent.kind === 'constructor' + ) { + addStateFields(unwrappedRight); + } + } else { + // Check for assignments like `alias = this.state` and record the alias. + handleAssignment(unwrappedLeft, unwrappedRight); + } + }, + + VariableDeclarator(node) { + if (!classInfo || !node.init) { + return; + } + handleAssignment(node.id, node.init); + }, + + 'MemberExpression, OptionalMemberExpression'(node) { + if (!classInfo) { + return; + } + if (isStateReference(astUtil.unwrapTSAsExpression(node.object))) { + // If we see this.state[foo] access, give up. + if (node.computed && node.property.type !== 'Literal') { + classInfo = null; + return; + } + // Otherwise, record that we saw this property being accessed. + addUsedStateField(node.property); + // If we see a `this.state` access in a CallExpression, give up. + } else if (isStateReference(node) && astUtil.isCallExpression(node.parent)) { + classInfo = null; + } + }, + + JSXSpreadAttribute(node) { + if (classInfo && isStateReference(node.argument)) { + classInfo = null; + } + }, + + 'ExperimentalSpreadProperty, SpreadElement'(node) { + if (classInfo && isStateReference(node.argument)) { + classInfo = null; + } + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..379e664fd041d617b577904d6c799548c01994ff --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=no-will-update-set-state.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..102f1db5365c49094425d520cffe622c59d7b021 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"no-will-update-set-state.d.ts","sourceRoot":"","sources":["no-will-update-set-state.js"],"names":[],"mappings":"wBAUW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js new file mode 100644 index 0000000000000000000000000000000000000000..16e6be43b9223b02789555641ef352a586d602bb --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/no-will-update-set-state.js @@ -0,0 +1,15 @@ +/** + * @fileoverview Prevent usage of setState in componentWillUpdate + * @author Yannick Croissant + */ + +'use strict'; + +const makeNoMethodSetStateRule = require('../util/makeNoMethodSetStateRule'); +const testReactVersion = require('../util/version').testReactVersion; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = makeNoMethodSetStateRule( + 'componentWillUpdate', + (context) => testReactVersion(context, '>= 16.3.0') +); diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..54dba7057f6374b213849b00b53c8202dce787be --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=prefer-es6-class.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..1c0017d5acfc1da1822eb3124b7ecf22051d0d65 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prefer-es6-class.d.ts","sourceRoot":"","sources":["prefer-es6-class.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js new file mode 100644 index 0000000000000000000000000000000000000000..a7c1ff56fc4ea094094ddca55c3a4c6ff6dfb53e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-es6-class.js @@ -0,0 +1,58 @@ +/** + * @fileoverview Enforce ES5 or ES6 class for React Components + * @author Dan Hamilton + */ + +'use strict'; + +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + shouldUseES6Class: 'Component should use es6 class instead of createClass', + shouldUseCreateClass: 'Component should use createClass instead of es6 class', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce ES5 or ES6 class for React Components', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('prefer-es6-class'), + }, + + messages, + + schema: [{ + enum: ['always', 'never'], + }], + }, + + create(context) { + const configuration = context.options[0] || 'always'; + + return { + ObjectExpression(node) { + if (componentUtil.isES5Component(node, context) && configuration === 'always') { + report(context, messages.shouldUseES6Class, 'shouldUseES6Class', { + node, + }); + } + }, + ClassDeclaration(node) { + if (componentUtil.isES6Component(node, context) && configuration === 'never') { + report(context, messages.shouldUseCreateClass, 'shouldUseCreateClass', { + node, + }); + } + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c40516246c2ddddaecda372103cd1e2b6915e0b6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=prefer-exact-props.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..6cef9979c0a5386400b296a38d5f6c4894d34e9b --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prefer-exact-props.d.ts","sourceRoot":"","sources":["prefer-exact-props.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js new file mode 100644 index 0000000000000000000000000000000000000000..5227d9448b09822d1c10f06b7b9b420e20e32b4d --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-exact-props.js @@ -0,0 +1,162 @@ +/** + * @fileoverview Prefer exact proptype definitions + */ + +'use strict'; + +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); +const propsUtil = require('../util/props'); +const propWrapperUtil = require('../util/propWrapper'); +const variableUtil = require('../util/variable'); +const report = require('../util/report'); +const getText = require('../util/eslint').getText; + +// ----------------------------------------------------------------------------- +// Rule Definition +// ----------------------------------------------------------------------------- + +const messages = { + propTypes: 'Component propTypes should be exact by using {{exactPropWrappers}}.', + flow: 'Component flow props should be set with exact objects.', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Prefer exact proptype definitions', + category: 'Possible Errors', + recommended: false, + url: docsUrl('prefer-exact-props'), + }, + messages, + schema: [], + }, + + create: Components.detect((context, components, utils) => { + const typeAliases = {}; + const exactWrappers = propWrapperUtil.getExactPropWrapperFunctions(context); + + function getPropTypesErrorMessage() { + const formattedWrappers = propWrapperUtil.formatPropWrapperFunctions(exactWrappers); + const message = exactWrappers.size > 1 ? `one of ${formattedWrappers}` : formattedWrappers; + return { exactPropWrappers: message }; + } + + function isNonExactObjectTypeAnnotation(node) { + return ( + node + && node.type === 'ObjectTypeAnnotation' + && node.properties.length > 0 + && !node.exact + ); + } + + function hasNonExactObjectTypeAnnotation(node) { + const typeAnnotation = node.typeAnnotation; + return ( + typeAnnotation + && typeAnnotation.typeAnnotation + && isNonExactObjectTypeAnnotation(typeAnnotation.typeAnnotation) + ); + } + + function hasGenericTypeAnnotation(node) { + const typeAnnotation = node.typeAnnotation; + return ( + typeAnnotation + && typeAnnotation.typeAnnotation + && typeAnnotation.typeAnnotation.type === 'GenericTypeAnnotation' + ); + } + + function isNonEmptyObjectExpression(node) { + return ( + node + && node.type === 'ObjectExpression' + && node.properties.length > 0 + ); + } + + function isNonExactPropWrapperFunction(node) { + return ( + astUtil.isCallExpression(node) + && !propWrapperUtil.isExactPropWrapperFunction(context, getText(context, node.callee)) + ); + } + + function reportPropTypesError(node) { + report(context, messages.propTypes, 'propTypes', { + node, + data: getPropTypesErrorMessage(), + }); + } + + function reportFlowError(node) { + report(context, messages.flow, 'flow', { + node, + }); + } + + return { + TypeAlias(node) { + // working around an issue with eslint@3 and babel-eslint not finding the TypeAlias in scope + typeAliases[node.id.name] = node; + }, + + 'ClassProperty, PropertyDefinition'(node) { + if (!propsUtil.isPropTypesDeclaration(node)) { + return; + } + + if (hasNonExactObjectTypeAnnotation(node)) { + reportFlowError(node); + } else if (exactWrappers.size > 0 && isNonEmptyObjectExpression(node.value)) { + reportPropTypesError(node); + } else if (exactWrappers.size > 0 && isNonExactPropWrapperFunction(node.value)) { + reportPropTypesError(node); + } + }, + + Identifier(node) { + if (!utils.getStatelessComponent(node.parent)) { + return; + } + + if (hasNonExactObjectTypeAnnotation(node)) { + reportFlowError(node); + } else if (hasGenericTypeAnnotation(node)) { + const identifier = node.typeAnnotation.typeAnnotation.id.name; + const typeAlias = typeAliases[identifier]; + const propsDefinition = typeAlias ? typeAlias.right : null; + if (isNonExactObjectTypeAnnotation(propsDefinition)) { + reportFlowError(node); + } + } + }, + + MemberExpression(node) { + if (!propsUtil.isPropTypesDeclaration(node) || exactWrappers.size === 0) { + return; + } + + const right = node.parent.right; + if (isNonEmptyObjectExpression(right)) { + reportPropTypesError(node); + } else if (isNonExactPropWrapperFunction(right)) { + reportPropTypesError(node); + } else if (right.type === 'Identifier') { + const identifier = right.name; + const propsDefinition = variableUtil.findVariableByName(context, node, identifier); + if (isNonEmptyObjectExpression(propsDefinition)) { + reportPropTypesError(node); + } else if (isNonExactPropWrapperFunction(propsDefinition)) { + reportPropTypesError(node); + } + } + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..0dcd83526a06d60a6ef2a9942190a30be7c82d9a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=prefer-read-only-props.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..efa56e363d9f2750a47661581afc40a04c601541 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prefer-read-only-props.d.ts","sourceRoot":"","sources":["prefer-read-only-props.js"],"names":[],"mappings":"wBAiDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js new file mode 100644 index 0000000000000000000000000000000000000000..c00b09f9e48a59e137ec908d4b3804f6d713c7dc --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-read-only-props.js @@ -0,0 +1,117 @@ +/** + * @fileoverview Require component props to be typed as read-only. + * @author Luke Zapart + */ + +'use strict'; + +const flatMap = require('array.prototype.flatmap'); +const values = require('object.values'); + +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +function isFlowPropertyType(node) { + return node.type === 'ObjectTypeProperty'; +} + +function isTypescriptPropertyType(node) { + return node.type === 'TSPropertySignature'; +} + +function isCovariant(node) { + return (node.variance && node.variance.kind === 'plus') + || ( + node.parent + && node.parent.parent + && node.parent.parent.parent + && node.parent.parent.parent.id + && node.parent.parent.parent.id.name === '$ReadOnly' + ); +} + +function isReadonly(node) { + return ( + node.typeAnnotation + && node.typeAnnotation.parent + && node.typeAnnotation.parent.readonly + ); +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + readOnlyProp: 'Prop \'{{name}}\' should be read-only.', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce that props are read-only', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('prefer-read-only-props'), + }, + fixable: 'code', + + messages, + + schema: [], + }, + + create: Components.detect((context, components) => { + function reportReadOnlyProp(prop, propName, fixer) { + report(context, messages.readOnlyProp, 'readOnlyProp', { + node: prop.node, + data: { + name: propName, + }, + fix: fixer, + }); + } + + return { + 'Program:exit'() { + flatMap( + values(components.list()), + (component) => component.declaredPropTypes || [] + ).forEach((declaredPropTypes) => { + Object.keys(declaredPropTypes).forEach((propName) => { + const prop = declaredPropTypes[propName]; + if (!prop.node) { + return; + } + + if (isFlowPropertyType(prop.node)) { + if (!isCovariant(prop.node)) { + reportReadOnlyProp(prop, propName, (fixer) => { + if (!prop.node.variance) { + // Insert covariance + return fixer.insertTextBefore(prop.node, '+'); + } + + // Replace contravariance with covariance + return fixer.replaceText(prop.node.variance, '+'); + }); + } + + return; + } + + if (isTypescriptPropertyType(prop.node)) { + if (!isReadonly(prop.node)) { + reportReadOnlyProp(prop, propName, (fixer) => ( + fixer.insertTextBefore(prop.node, 'readonly ') + )); + } + } + }); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..a37682838f685b145bc979eff1fed0d9ad5c555e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=prefer-stateless-function.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..fc5a3abc4cdd65028a708f9a19c3c4776c58320e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prefer-stateless-function.d.ts","sourceRoot":"","sources":["prefer-stateless-function.js"],"names":[],"mappings":"wBA8BW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js new file mode 100644 index 0000000000000000000000000000000000000000..46847cb1950708b849a32d53152cddc85a585a2c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prefer-stateless-function.js @@ -0,0 +1,397 @@ +/** + * @fileoverview Enforce stateless components to be written as a pure function + * @author Yannick Croissant + * @author Alberto Rodríguez + * @copyright 2015 Alberto Rodríguez. All rights reserved. + */ + +'use strict'; + +const values = require('object.values'); + +const Components = require('../util/Components'); +const testReactVersion = require('../util/version').testReactVersion; +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getScope = eslintUtil.getScope; +const getText = eslintUtil.getText; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + componentShouldBePure: 'Component should be written as a pure function', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce stateless components to be written as a pure function', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('prefer-stateless-function'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + ignorePureComponents: { + default: false, + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components, utils) => { + const configuration = context.options[0] || {}; + const ignorePureComponents = configuration.ignorePureComponents || false; + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + /** + * Checks whether a given array of statements is a single call of `super`. + * @see eslint no-useless-constructor rule + * @param {ASTNode[]} body - An array of statements to check. + * @returns {boolean} `true` if the body is a single call of `super`. + */ + function isSingleSuperCall(body) { + return ( + body.length === 1 + && body[0].type === 'ExpressionStatement' + && astUtil.isCallExpression(body[0].expression) + && body[0].expression.callee.type === 'Super' + ); + } + + /** + * Checks whether a given node is a pattern which doesn't have any side effects. + * Default parameters and Destructuring parameters can have side effects. + * @see eslint no-useless-constructor rule + * @param {ASTNode} node - A pattern node. + * @returns {boolean} `true` if the node doesn't have any side effects. + */ + function isSimple(node) { + return node.type === 'Identifier' || node.type === 'RestElement'; + } + + /** + * Checks whether a given array of expressions is `...arguments` or not. + * `super(...arguments)` passes all arguments through. + * @see eslint no-useless-constructor rule + * @param {ASTNode[]} superArgs - An array of expressions to check. + * @returns {boolean} `true` if the superArgs is `...arguments`. + */ + function isSpreadArguments(superArgs) { + return ( + superArgs.length === 1 + && superArgs[0].type === 'SpreadElement' + && superArgs[0].argument.type === 'Identifier' + && superArgs[0].argument.name === 'arguments' + ); + } + + /** + * Checks whether given 2 nodes are identifiers which have the same name or not. + * @see eslint no-useless-constructor rule + * @param {ASTNode} ctorParam - A node to check. + * @param {ASTNode} superArg - A node to check. + * @returns {boolean} `true` if the nodes are identifiers which have the same + * name. + */ + function isValidIdentifierPair(ctorParam, superArg) { + return ( + ctorParam.type === 'Identifier' + && superArg.type === 'Identifier' + && ctorParam.name === superArg.name + ); + } + + /** + * Checks whether given 2 nodes are a rest/spread pair which has the same values. + * @see eslint no-useless-constructor rule + * @param {ASTNode} ctorParam - A node to check. + * @param {ASTNode} superArg - A node to check. + * @returns {boolean} `true` if the nodes are a rest/spread pair which has the + * same values. + */ + function isValidRestSpreadPair(ctorParam, superArg) { + return ( + ctorParam.type === 'RestElement' + && superArg.type === 'SpreadElement' + && isValidIdentifierPair(ctorParam.argument, superArg.argument) + ); + } + + /** + * Checks whether given 2 nodes have the same value or not. + * @see eslint no-useless-constructor rule + * @param {ASTNode} ctorParam - A node to check. + * @param {ASTNode} superArg - A node to check. + * @returns {boolean} `true` if the nodes have the same value or not. + */ + function isValidPair(ctorParam, superArg) { + return ( + isValidIdentifierPair(ctorParam, superArg) + || isValidRestSpreadPair(ctorParam, superArg) + ); + } + + /** + * Checks whether the parameters of a constructor and the arguments of `super()` + * have the same values or not. + * @see eslint no-useless-constructor rule + * @param {ASTNode[]} ctorParams - The parameters of a constructor to check. + * @param {ASTNode} superArgs - The arguments of `super()` to check. + * @returns {boolean} `true` if those have the same values. + */ + function isPassingThrough(ctorParams, superArgs) { + if (ctorParams.length !== superArgs.length) { + return false; + } + + for (let i = 0; i < ctorParams.length; ++i) { + if (!isValidPair(ctorParams[i], superArgs[i])) { + return false; + } + } + + return true; + } + + /** + * Checks whether the constructor body is a redundant super call. + * @see eslint no-useless-constructor rule + * @param {Array} body - constructor body content. + * @param {Array} ctorParams - The params to check against super call. + * @returns {boolean} true if the constructor body is redundant + */ + function isRedundantSuperCall(body, ctorParams) { + return ( + isSingleSuperCall(body) + && ctorParams.every(isSimple) + && ( + isSpreadArguments(body[0].expression.arguments) + || isPassingThrough(ctorParams, body[0].expression.arguments) + ) + ); + } + + /** + * Check if a given AST node have any other properties the ones available in stateless components + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if the node has at least one other property, false if not. + */ + function hasOtherProperties(node) { + const properties = astUtil.getComponentProperties(node); + return properties.some((property) => { + const name = astUtil.getPropertyName(property); + const isDisplayName = name === 'displayName'; + const isPropTypes = name === 'propTypes' || ((name === 'props') && property.typeAnnotation); + const contextTypes = name === 'contextTypes'; + const defaultProps = name === 'defaultProps'; + const isUselessConstructor = property.kind === 'constructor' + && !!property.value.body + && isRedundantSuperCall(property.value.body.body, property.value.params); + const isRender = name === 'render'; + return !isDisplayName && !isPropTypes && !contextTypes && !defaultProps && !isUselessConstructor && !isRender; + }); + } + + /** + * Mark component as pure as declared + * @param {ASTNode} node The AST node being checked. + */ + function markSCUAsDeclared(node) { + components.set(node, { + hasSCU: true, + }); + } + + /** + * Mark childContextTypes as declared + * @param {ASTNode} node The AST node being checked. + */ + function markChildContextTypesAsDeclared(node) { + components.set(node, { + hasChildContextTypes: true, + }); + } + + /** + * Mark a setState as used + * @param {ASTNode} node The AST node being checked. + */ + function markThisAsUsed(node) { + components.set(node, { + useThis: true, + }); + } + + /** + * Mark a props or context as used + * @param {ASTNode} node The AST node being checked. + */ + function markPropsOrContextAsUsed(node) { + components.set(node, { + usePropsOrContext: true, + }); + } + + /** + * Mark a ref as used + * @param {ASTNode} node The AST node being checked. + */ + function markRefAsUsed(node) { + components.set(node, { + useRef: true, + }); + } + + /** + * Mark return as invalid + * @param {ASTNode} node The AST node being checked. + */ + function markReturnAsInvalid(node) { + components.set(node, { + invalidReturn: true, + }); + } + + /** + * Mark a ClassDeclaration as having used decorators + * @param {ASTNode} node The AST node being checked. + */ + function markDecoratorsAsUsed(node) { + components.set(node, { + useDecorators: true, + }); + } + + function visitClass(node) { + if (ignorePureComponents && componentUtil.isPureComponent(node, context)) { + markSCUAsDeclared(node); + } + + if (node.decorators && node.decorators.length) { + markDecoratorsAsUsed(node); + } + } + + return { + ClassDeclaration: visitClass, + ClassExpression: visitClass, + + // Mark `this` destructuring as a usage of `this` + VariableDeclarator(node) { + // Ignore destructuring on other than `this` + if (!node.id || node.id.type !== 'ObjectPattern' || !node.init || node.init.type !== 'ThisExpression') { + return; + } + // Ignore `props` and `context` + const useThis = node.id.properties.some((property) => { + const name = astUtil.getPropertyName(property); + return name !== 'props' && name !== 'context'; + }); + if (!useThis) { + markPropsOrContextAsUsed(node); + return; + } + markThisAsUsed(node); + }, + + // Mark `this` usage + MemberExpression(node) { + if (node.object.type !== 'ThisExpression') { + if (node.property && node.property.name === 'childContextTypes') { + const component = utils.getRelatedComponent(node); + if (!component) { + return; + } + markChildContextTypesAsDeclared(component.node); + } + return; + // Ignore calls to `this.props` and `this.context` + } + if ( + (node.property.name || node.property.value) === 'props' + || (node.property.name || node.property.value) === 'context' + ) { + markPropsOrContextAsUsed(node); + return; + } + markThisAsUsed(node); + }, + + // Mark `ref` usage + JSXAttribute(node) { + const name = getText(context, node.name); + if (name !== 'ref') { + return; + } + markRefAsUsed(node); + }, + + // Mark `render` that do not return some JSX + ReturnStatement(node) { + let blockNode; + let scope = getScope(context, node); + while (scope) { + blockNode = scope.block && scope.block.parent; + if (blockNode && (blockNode.type === 'MethodDefinition' || blockNode.type === 'Property')) { + break; + } + scope = scope.upper; + } + const isRender = blockNode + && blockNode.key + && blockNode.key.name === 'render'; + const allowNull = testReactVersion(context, '>= 15.0.0'); // Stateless components can return null since React 15 + const isReturningJSX = utils.isReturningJSX(node, !allowNull); + const isReturningNull = node.argument && (node.argument.value === null || node.argument.value === false); + if ( + !isRender + || (allowNull && (isReturningJSX || isReturningNull)) + || (!allowNull && isReturningJSX) + ) { + return; + } + markReturnAsInvalid(node); + }, + + 'Program:exit'() { + const list = components.list(); + values(list) + .filter((component) => ( + !hasOtherProperties(component.node) + && !component.useThis + && !component.useRef + && !component.invalidReturn + && !component.hasChildContextTypes + && !component.useDecorators + && !component.hasSCU + && ( + componentUtil.isES5Component(component.node, context) + || componentUtil.isES6Component(component.node, context) + ) + )) + .forEach((component) => { + report(context, messages.componentShouldBePure, 'componentShouldBePure', { + node: component.node, + }); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts b/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..152cbca81171066ac7c6e08deb9194e9273a22bd --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=prop-types.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..331e73f93d16e8b939b2b2826ad9bad7b4c5b710 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prop-types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"prop-types.d.ts","sourceRoot":"","sources":["prop-types.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/prop-types.js b/node_modules/eslint-plugin-react/lib/rules/prop-types.js new file mode 100644 index 0000000000000000000000000000000000000000..dabe5b0e7b64c1b6c7107866b9fe785d81716e38 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/prop-types.js @@ -0,0 +1,225 @@ +/** + * @fileoverview Prevent missing props validation in a React component definition + * @author Yannick Croissant + */ + +'use strict'; + +// As for exceptions for props.children or props.className (and alike) look at +// https://github.com/jsx-eslint/eslint-plugin-react/issues/7 + +const values = require('object.values'); + +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + missingPropType: '\'{{name}}\' is missing in props validation', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow missing props validation in a React component definition', + category: 'Best Practices', + recommended: true, + url: docsUrl('prop-types'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + ignore: { + type: 'array', + items: { + type: 'string', + }, + }, + customValidators: { + type: 'array', + items: { + type: 'string', + }, + }, + skipUndeclared: { + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components) => { + const configuration = context.options[0] || {}; + const ignored = configuration.ignore || []; + const skipUndeclared = configuration.skipUndeclared || false; + + /** + * Checks if the prop is ignored + * @param {string} name Name of the prop to check. + * @returns {boolean} True if the prop is ignored, false if not. + */ + function isIgnored(name) { + return ignored.indexOf(name) !== -1; + } + + /** + * Checks if the component must be validated + * @param {Object} component The component to process + * @returns {boolean} True if the component must be validated, false if not. + */ + function mustBeValidated(component) { + const isSkippedByConfig = skipUndeclared && typeof component.declaredPropTypes === 'undefined'; + return !!( + component + && component.usedPropTypes + && !component.ignorePropsValidation + && !isSkippedByConfig + ); + } + + /** + * Internal: Checks if the prop is declared + * @param {Object} declaredPropTypes Description of propTypes declared in the current component + * @param {string[]} keyList Dot separated name of the prop to check. + * @returns {boolean} True if the prop is declared, false if not. + */ + function internalIsDeclaredInComponent(declaredPropTypes, keyList) { + for (let i = 0, j = keyList.length; i < j; i++) { + const key = keyList[i]; + const propType = ( + declaredPropTypes && ( + // Check if this key is declared + (declaredPropTypes[key] // If not, check if this type accepts any key + || declaredPropTypes.__ANY_KEY__) // eslint-disable-line no-underscore-dangle + ) + ); + + if (!propType) { + // If it's a computed property, we can't make any further analysis, but is valid + return key === '__COMPUTED_PROP__'; + } + if (typeof propType === 'object' && !propType.type) { + return true; + } + // Consider every children as declared + if (propType.children === true || propType.containsUnresolvedSpread || propType.containsIndexers) { + return true; + } + if (propType.acceptedProperties) { + return key in propType.acceptedProperties; + } + if (propType.type === 'union') { + // If we fall in this case, we know there is at least one complex type in the union + if (i + 1 >= j) { + // this is the last key, accept everything + return true; + } + // non trivial, check all of them + const unionTypes = propType.children; + const unionPropType = {}; + for (let k = 0, z = unionTypes.length; k < z; k++) { + unionPropType[key] = unionTypes[k]; + const isValid = internalIsDeclaredInComponent( + unionPropType, + keyList.slice(i) + ); + if (isValid) { + return true; + } + } + + // every possible union were invalid + return false; + } + declaredPropTypes = propType.children; + } + return true; + } + + /** + * Checks if the prop is declared + * @param {ASTNode} node The AST node being checked. + * @param {string[]} names List of names of the prop to check. + * @returns {boolean} True if the prop is declared, false if not. + */ + function isDeclaredInComponent(node, names) { + while (node) { + const component = components.get(node); + + const isDeclared = component && component.confidence >= 2 + && internalIsDeclaredInComponent(component.declaredPropTypes || {}, names); + + if (isDeclared) { + return true; + } + + node = node.parent; + } + return false; + } + + /** + * Reports undeclared proptypes for a given component + * @param {Object} component The component to process + */ + function reportUndeclaredPropTypes(component) { + const undeclareds = component.usedPropTypes.filter((propType) => ( + propType.node + && !isIgnored(propType.allNames[0]) + && !isDeclaredInComponent(component.node, propType.allNames) + )); + undeclareds.forEach((propType) => { + report(context, messages.missingPropType, 'missingPropType', { + node: propType.node, + data: { + name: propType.allNames.join('.').replace(/\.__COMPUTED_PROP__/g, '[]'), + }, + }); + }); + } + + /** + * @param {Object} component The current component to process + * @param {Array} list The all components to process + * @returns {boolean} True if the component is nested False if not. + */ + function checkNestedComponent(component, list) { + const componentIsMemo = component.node.callee && component.node.callee.name === 'memo'; + const argumentIsForwardRef = component.node.arguments && component.node.arguments[0].callee && component.node.arguments[0].callee.name === 'forwardRef'; + if (componentIsMemo && argumentIsForwardRef) { + const forwardComponent = list.find( + (innerComponent) => ( + innerComponent.node.range[0] === component.node.arguments[0].range[0] + && innerComponent.node.range[0] === component.node.arguments[0].range[0] + )); + + const isValidated = mustBeValidated(forwardComponent); + const isIgnorePropsValidation = forwardComponent.ignorePropsValidation; + + return isIgnorePropsValidation || isValidated; + } + } + + return { + 'Program:exit'() { + const list = components.list(); + // Report undeclared proptypes for all classes + values(list) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + if (checkNestedComponent(component, values(list))) return; + reportUndeclaredPropTypes(component); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..095a757fb2740b93d7010417be8dbf84e4f4f3bb --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=react-in-jsx-scope.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..c6044a03ba60106c7cd35baf77cb31ef9503fe01 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"react-in-jsx-scope.d.ts","sourceRoot":"","sources":["react-in-jsx-scope.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js new file mode 100644 index 0000000000000000000000000000000000000000..097e6477649668abdae55075ba571288dde7e959 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/react-in-jsx-scope.js @@ -0,0 +1,56 @@ +/** + * @fileoverview Prevent missing React when using JSX + * @author Glen Mailer + */ + +'use strict'; + +const variableUtil = require('../util/variable'); +const pragmaUtil = require('../util/pragma'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +// ----------------------------------------------------------------------------- +// Rule Definition +// ----------------------------------------------------------------------------- + +const messages = { + notInScope: '\'{{name}}\' must be in scope when using JSX', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow missing React when using JSX', + category: 'Possible Errors', + recommended: true, + url: docsUrl('react-in-jsx-scope'), + }, + + messages, + + schema: [], + }, + + create(context) { + const pragma = pragmaUtil.getFromContext(context); + + function checkIfReactIsInScope(node) { + if (variableUtil.getVariableFromContext(context, node, pragma)) { + return; + } + report(context, messages.notInScope, 'notInScope', { + node, + data: { + name: pragma, + }, + }); + } + + return { + JSXOpeningElement: checkIfReactIsInScope, + JSXOpeningFragment: checkIfReactIsInScope, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts b/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..ab0628f8068668058a10e0e3ce2ca414539ae76f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=require-default-props.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..f9b5dd01e48d3ffc8a530e42879a9f4e6a1e2e42 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-default-props.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"require-default-props.d.ts","sourceRoot":"","sources":["require-default-props.js"],"names":[],"mappings":"wBAiCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-default-props.js b/node_modules/eslint-plugin-react/lib/rules/require-default-props.js new file mode 100644 index 0000000000000000000000000000000000000000..c186279cd4ea80657f6d7ad087983a61738464e7 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-default-props.js @@ -0,0 +1,209 @@ +/** + * @fileOverview Enforce a defaultProps definition for every prop that is not a required prop. + * @author Vitor Balocco + */ + +'use strict'; + +const entries = require('object.entries'); +const values = require('object.values'); +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + noDefaultWithRequired: 'propType "{{name}}" is required and should not have a defaultProps declaration.', + shouldHaveDefault: 'propType "{{name}}" is not required, but has no corresponding defaultProps declaration.', + noDefaultPropsWithFunction: 'Don’t use defaultProps with function components.', + shouldAssignObjectDefault: 'propType "{{name}}" is not required, but has no corresponding default argument value.', + destructureInSignature: 'Must destructure props in the function signature to initialize an optional prop.', +}; + +function isPropWithNoDefaulVal(prop) { + if (prop.type === 'RestElement' || prop.type === 'ExperimentalRestProperty') { + return false; + } + return prop.value.type !== 'AssignmentPattern'; +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce a defaultProps definition for every prop that is not a required prop', + category: 'Best Practices', + url: docsUrl('require-default-props'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + forbidDefaultForRequired: { + type: 'boolean', + }, + classes: { + enum: ['defaultProps', 'ignore'], + }, + functions: { + enum: ['defaultArguments', 'defaultProps', 'ignore'], + }, + /** + * @deprecated + */ + ignoreFunctionalComponents: { + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components) => { + const configuration = context.options[0] || {}; + const forbidDefaultForRequired = configuration.forbidDefaultForRequired || false; + const classes = configuration.classes || 'defaultProps'; + /** + * @todo + * - Remove ignoreFunctionalComponents + * - Change default to 'defaultArguments' + */ + const functions = configuration.ignoreFunctionalComponents + ? 'ignore' + : configuration.functions || 'defaultProps'; + + /** + * Reports all propTypes passed in that don't have a defaultProps counterpart. + * @param {Object[]} propTypes List of propTypes to check. + * @param {Object} defaultProps Object of defaultProps to check. Keys are the props names. + * @return {void} + */ + function reportPropTypesWithoutDefault(propTypes, defaultProps) { + entries(propTypes).forEach((propType) => { + const propName = propType[0]; + const prop = propType[1]; + + if (!prop.node) { + return; + } + if (prop.isRequired) { + if (forbidDefaultForRequired && defaultProps[propName]) { + report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', { + node: prop.node, + data: { name: propName }, + }); + } + return; + } + + if (defaultProps[propName]) { + return; + } + + report(context, messages.shouldHaveDefault, 'shouldHaveDefault', { + node: prop.node, + data: { name: propName }, + }); + }); + } + + /** + * If functions option is 'defaultArguments', reports defaultProps is used and all params that doesn't initialized. + * @param {Object} componentNode Node of component. + * @param {Object[]} declaredPropTypes List of propTypes to check `isRequired`. + * @param {Object} defaultProps Object of defaultProps to check used. + */ + function reportFunctionComponent(componentNode, declaredPropTypes, defaultProps) { + if (defaultProps) { + report(context, messages.noDefaultPropsWithFunction, 'noDefaultPropsWithFunction', { + node: componentNode, + }); + } + + const props = componentNode.params[0]; + const propTypes = declaredPropTypes; + + if (!props) { + return; + } + + if (props.type === 'Identifier') { + const hasOptionalProp = values(propTypes).some((propType) => !propType.isRequired); + if (hasOptionalProp) { + report(context, messages.destructureInSignature, 'destructureInSignature', { + node: props, + }); + } + } else if (props.type === 'ObjectPattern') { + // Filter required props with default value and report error + props.properties.filter((prop) => { + const propName = prop && prop.key && prop.key.name; + const isPropRequired = propTypes[propName] && propTypes[propName].isRequired; + return propTypes[propName] && isPropRequired && !isPropWithNoDefaulVal(prop); + }).forEach((prop) => { + report(context, messages.noDefaultWithRequired, 'noDefaultWithRequired', { + node: prop, + data: { name: prop.key.name }, + }); + }); + + // Filter non required props with no default value and report error + props.properties.filter((prop) => { + const propName = prop && prop.key && prop.key.name; + const isPropRequired = propTypes[propName] && propTypes[propName].isRequired; + return propTypes[propName] && !isPropRequired && isPropWithNoDefaulVal(prop); + }).forEach((prop) => { + report(context, messages.shouldAssignObjectDefault, 'shouldAssignObjectDefault', { + node: prop, + data: { name: prop.key.name }, + }); + }); + } + } + + // -------------------------------------------------------------------------- + // Public API + // -------------------------------------------------------------------------- + + return { + 'Program:exit'() { + const list = components.list(); + + values(list).filter((component) => { + if (functions === 'ignore' && astUtil.isFunctionLike(component.node)) { + return false; + } + if (classes === 'ignore' && astUtil.isClass(component.node)) { + return false; + } + + // If this defaultProps is "unresolved", then we should ignore this component and not report + // any errors for it, to avoid false-positives with e.g. external defaultProps declarations or spread operators. + if (component.defaultProps === 'unresolved') { + return false; + } + return component.declaredPropTypes !== undefined; + }).forEach((component) => { + if (functions === 'defaultArguments' && astUtil.isFunctionLike(component.node)) { + reportFunctionComponent( + component.node, + component.declaredPropTypes, + component.defaultProps + ); + } else { + reportPropTypesWithoutDefault( + component.declaredPropTypes, + component.defaultProps || {} + ); + } + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts b/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e92c3e2d189881e6953cd9d3f386536c4c24fbfe --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=require-optimization.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..3220ba0c5af1611c48559d88a885b19da0a32024 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-optimization.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"require-optimization.d.ts","sourceRoot":"","sources":["require-optimization.js"],"names":[],"mappings":"wBAmBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-optimization.js b/node_modules/eslint-plugin-react/lib/rules/require-optimization.js new file mode 100644 index 0000000000000000000000000000000000000000..add2ccf95af4101c9ee6190770ce0f270cf6f836 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-optimization.js @@ -0,0 +1,240 @@ +/** + * @fileoverview Enforce React components to have a shouldComponentUpdate method + * @author Evgueni Naverniouk + */ + +'use strict'; + +const values = require('object.values'); + +const Components = require('../util/Components'); +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; + +const messages = { + noShouldComponentUpdate: 'Component is not optimized. Please add a shouldComponentUpdate method.', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce React components to have a shouldComponentUpdate method', + category: 'Best Practices', + recommended: false, + url: docsUrl('require-optimization'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + allowDecorators: { + type: 'array', + items: { + type: 'string', + }, + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components) => { + const configuration = context.options[0] || {}; + const allowDecorators = configuration.allowDecorators || []; + + /** + * Checks to see if our component is decorated by PureRenderMixin via reactMixin + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if node is decorated with a PureRenderMixin, false if not. + */ + function hasPureRenderDecorator(node) { + if (node.decorators && node.decorators.length) { + for (let i = 0, l = node.decorators.length; i < l; i++) { + if ( + node.decorators[i].expression + && node.decorators[i].expression.callee + && node.decorators[i].expression.callee.object + && node.decorators[i].expression.callee.object.name === 'reactMixin' + && node.decorators[i].expression.callee.property + && node.decorators[i].expression.callee.property.name === 'decorate' + && node.decorators[i].expression.arguments + && node.decorators[i].expression.arguments.length + && node.decorators[i].expression.arguments[0].name === 'PureRenderMixin' + ) { + return true; + } + } + } + + return false; + } + + /** + * Checks to see if our component is custom decorated + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if node is decorated name with a custom decorated, false if not. + */ + function hasCustomDecorator(node) { + const allowLength = allowDecorators.length; + + if (allowLength && node.decorators && node.decorators.length) { + for (let i = 0; i < allowLength; i++) { + for (let j = 0, l = node.decorators.length; j < l; j++) { + const expression = node.decorators[j].expression; + if ( + expression + && expression.name === allowDecorators[i] + ) { + return true; + } + } + } + } + + return false; + } + + /** + * Checks if we are declaring a shouldComponentUpdate method + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if we are declaring a shouldComponentUpdate method, false if not. + */ + function isSCUDeclared(node) { + return !!node && node.name === 'shouldComponentUpdate'; + } + + /** + * Checks if we are declaring a PureRenderMixin mixin + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if we are declaring a PureRenderMixin method, false if not. + */ + function isPureRenderDeclared(node) { + let hasPR = false; + if (node.value && node.value.elements) { + for (let i = 0, l = node.value.elements.length; i < l; i++) { + if (node.value.elements[i] && node.value.elements[i].name === 'PureRenderMixin') { + hasPR = true; + break; + } + } + } + + return ( + !!node + && node.key.name === 'mixins' + && hasPR + ); + } + + /** + * Mark shouldComponentUpdate as declared + * @param {ASTNode} node The AST node being checked. + */ + function markSCUAsDeclared(node) { + components.set(node, { + hasSCU: true, + }); + } + + /** + * Reports missing optimization for a given component + * @param {Object} component The component to process + */ + function reportMissingOptimization(component) { + report(context, messages.noShouldComponentUpdate, 'noShouldComponentUpdate', { + node: component.node, + }); + } + + /** + * Checks if we are declaring function in class + * @param {ASTNode} node + * @returns {boolean} True if we are declaring function in class, false if not. + */ + function isFunctionInClass(node) { + let blockNode; + let scope = getScope(context, node); + while (scope) { + blockNode = scope.block; + if (blockNode && blockNode.type === 'ClassDeclaration') { + return true; + } + scope = scope.upper; + } + + return false; + } + + return { + ArrowFunctionExpression(node) { + // Skip if the function is declared in the class + if (isFunctionInClass(node)) { + return; + } + // Stateless Functional Components cannot be optimized (yet) + markSCUAsDeclared(node); + }, + + ClassDeclaration(node) { + if (!( + hasPureRenderDecorator(node) + || hasCustomDecorator(node) + || componentUtil.isPureComponent(node, context) + )) { + return; + } + markSCUAsDeclared(node); + }, + + FunctionDeclaration(node) { + // Skip if the function is declared in the class + if (isFunctionInClass(node)) { + return; + } + // Stateless Functional Components cannot be optimized (yet) + markSCUAsDeclared(node); + }, + + FunctionExpression(node) { + // Skip if the function is declared in the class + if (isFunctionInClass(node)) { + return; + } + // Stateless Functional Components cannot be optimized (yet) + markSCUAsDeclared(node); + }, + + MethodDefinition(node) { + if (!isSCUDeclared(node.key)) { + return; + } + markSCUAsDeclared(node); + }, + + ObjectExpression(node) { + // Search for the shouldComponentUpdate declaration + const found = node.properties.some((property) => ( + property.key + && (isSCUDeclared(property.key) || isPureRenderDeclared(property)) + )); + if (found) { + markSCUAsDeclared(node); + } + }, + + 'Program:exit'() { + // Report missing shouldComponentUpdate for all components + values(components.list()) + .filter((component) => !component.hasSCU) + .forEach((component) => { + reportMissingOptimization(component); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts b/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..d2e87bdafa43fd5b0d1552485e628d510dfea74d --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=require-render-return.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..519ae2820b70a6e001f0a63eea9abf951367f093 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-render-return.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"require-render-return.d.ts","sourceRoot":"","sources":["require-render-return.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/require-render-return.js b/node_modules/eslint-plugin-react/lib/rules/require-render-return.js new file mode 100644 index 0000000000000000000000000000000000000000..c46613e4205e374841c74682a2d81307718a474e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/require-render-return.js @@ -0,0 +1,106 @@ +/** + * @fileoverview Enforce ES5 or ES6 class for returning value in render function. + * @author Mark Orel + */ + +'use strict'; + +const values = require('object.values'); + +const Components = require('../util/Components'); +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); +const getAncestors = require('../util/eslint').getAncestors; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + noRenderReturn: 'Your render method should have a return statement', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce ES5 or ES6 class for returning value in render function', + category: 'Possible Errors', + recommended: true, + url: docsUrl('require-render-return'), + }, + + messages, + + schema: [], + }, + + create: Components.detect((context, components) => { + /** + * Mark a return statement as present + * @param {ASTNode} node The AST node being checked. + */ + function markReturnStatementPresent(node) { + components.set(node, { + hasReturnStatement: true, + }); + } + + /** + * Find render method in a given AST node + * @param {ASTNode} node The component to find render method. + * @returns {ASTNode} Method node if found, undefined if not. + */ + function findRenderMethod(node) { + const properties = astUtil.getComponentProperties(node); + return properties + .filter((property) => astUtil.getPropertyName(property) === 'render' && property.value) + .find((property) => astUtil.isFunctionLikeExpression(property.value)); + } + + return { + ReturnStatement(node) { + const ancestors = getAncestors(context, node).reverse(); + let depth = 0; + ancestors.forEach((ancestor) => { + if (/Function(Expression|Declaration)$/.test(ancestor.type)) { + depth += 1; + } + if ( + /(MethodDefinition|Property|ClassProperty|PropertyDefinition)$/.test(ancestor.type) + && astUtil.getPropertyName(ancestor) === 'render' + && depth <= 1 + ) { + markReturnStatementPresent(node); + } + }); + }, + + ArrowFunctionExpression(node) { + if (node.expression === false || astUtil.getPropertyName(node.parent) !== 'render') { + return; + } + markReturnStatementPresent(node); + }, + + 'Program:exit'() { + values(components.list()) + .filter((component) => ( + findRenderMethod(component.node) + && !component.hasReturnStatement + && ( + componentUtil.isES5Component(component.node, context) + || componentUtil.isES6Component(component.node, context) + ) + )) + .forEach((component) => { + report(context, messages.noRenderReturn, 'noRenderReturn', { + node: findRenderMethod(component.node), + }); + }); + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..44790cba19478f648b1ff9fb17624932b2eba74f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=self-closing-comp.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..0c3abb4cced9b944d0c5f03b514d26057367eb2c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"self-closing-comp.d.ts","sourceRoot":"","sources":["self-closing-comp.js"],"names":[],"mappings":"wBA4CW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js new file mode 100644 index 0000000000000000000000000000000000000000..23e168605f366735ba51f04a2d2bc2e4debe7691 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/self-closing-comp.js @@ -0,0 +1,104 @@ +/** + * @fileoverview Prevent extra closing tags for components without children + * @author Yannick Croissant + */ + +'use strict'; + +const docsUrl = require('../util/docsUrl'); +const jsxUtil = require('../util/jsx'); +const report = require('../util/report'); + +const optionDefaults = { component: true, html: true }; + +function isComponent(node) { + return ( + node.name + && (node.name.type === 'JSXIdentifier' || node.name.type === 'JSXMemberExpression') + && !jsxUtil.isDOMComponent(node) + ); +} + +function childrenIsEmpty(node) { + return node.parent.children.length === 0; +} + +function childrenIsMultilineSpaces(node) { + const childrens = node.parent.children; + + return ( + childrens.length === 1 + && (childrens[0].type === 'Literal' || childrens[0].type === 'JSXText') + && childrens[0].value.indexOf('\n') !== -1 + && childrens[0].value.replace(/(?!\xA0)\s/g, '') === '' + ); +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + notSelfClosing: 'Empty components are self-closing', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow extra closing tags for components without children', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('self-closing-comp'), + }, + fixable: 'code', + + messages, + + schema: [{ + type: 'object', + properties: { + component: { + default: optionDefaults.component, + type: 'boolean', + }, + html: { + default: optionDefaults.html, + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create(context) { + function isShouldBeSelfClosed(node) { + const configuration = Object.assign({}, optionDefaults, context.options[0]); + return ( + (configuration.component && isComponent(node)) + || (configuration.html && jsxUtil.isDOMComponent(node)) + ) && !node.selfClosing && (childrenIsEmpty(node) || childrenIsMultilineSpaces(node)); + } + + return { + JSXOpeningElement(node) { + if (!isShouldBeSelfClosed(node)) { + return; + } + report(context, messages.notSelfClosing, 'notSelfClosing', { + node, + fix(fixer) { + // Represents the last character of the JSXOpeningElement, the '>' character + const openingElementEnding = node.range[1] - 1; + // Represents the last character of the JSXClosingElement, the '>' character + const closingElementEnding = node.parent.closingElement.range[1]; + + // Replace />.*<\/.*>/ with '/>' + const range = [openingElementEnding, closingElementEnding]; + return fixer.replaceTextRange(range, ' />'); + }, + }); + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts b/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..fd6c9dc8a3b910aebf82a3611d5e6cd902045bb1 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=sort-comp.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..aaf7ac4f324669f8bca4bf9c4921a904bf75b1a6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-comp.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"sort-comp.d.ts","sourceRoot":"","sources":["sort-comp.js"],"names":[],"mappings":"wBAwFW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-comp.js b/node_modules/eslint-plugin-react/lib/rules/sort-comp.js new file mode 100644 index 0000000000000000000000000000000000000000..ee409b780d4df0acf3469c9cbe72b37af6fad83c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-comp.js @@ -0,0 +1,449 @@ +/** + * @fileoverview Enforce component methods order + * @author Yannick Croissant + */ + +'use strict'; + +const has = require('hasown'); +const entries = require('object.entries'); +const values = require('object.values'); +const arrayIncludes = require('array-includes'); + +const Components = require('../util/Components'); +const astUtil = require('../util/ast'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +const defaultConfig = { + order: [ + 'static-methods', + 'lifecycle', + 'everything-else', + 'render', + ], + groups: { + lifecycle: [ + 'displayName', + 'propTypes', + 'contextTypes', + 'childContextTypes', + 'mixins', + 'statics', + 'defaultProps', + 'constructor', + 'getDefaultProps', + 'state', + 'getInitialState', + 'getChildContext', + 'getDerivedStateFromProps', + 'componentWillMount', + 'UNSAFE_componentWillMount', + 'componentDidMount', + 'componentWillReceiveProps', + 'UNSAFE_componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', + 'getSnapshotBeforeUpdate', + 'componentDidUpdate', + 'componentDidCatch', + 'componentWillUnmount', + ], + }, +}; + +/** + * Get the methods order from the default config and the user config + * @param {Object} userConfig The user configuration. + * @returns {Array} Methods order + */ +function getMethodsOrder(userConfig) { + userConfig = userConfig || {}; + + const groups = Object.assign({}, defaultConfig.groups, userConfig.groups); + const order = userConfig.order || defaultConfig.order; + + let config = []; + let entry; + for (let i = 0, j = order.length; i < j; i++) { + entry = order[i]; + if (has(groups, entry)) { + config = config.concat(groups[entry]); + } else { + config.push(entry); + } + } + + return config; +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + unsortedProps: '{{propA}} should be placed {{position}} {{propB}}', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce component methods order', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('sort-comp'), + }, + + messages, + + schema: [{ + type: 'object', + properties: { + order: { + type: 'array', + items: { + type: 'string', + }, + }, + groups: { + type: 'object', + patternProperties: { + '^.*$': { + type: 'array', + items: { + type: 'string', + }, + }, + }, + }, + }, + additionalProperties: false, + }], + }, + + create: Components.detect((context, components) => { + /** @satisfies {Record} */ + const errors = {}; + const methodsOrder = getMethodsOrder(context.options[0]); + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + const regExpRegExp = /\/(.*)\/([gimsuy]*)/; + + /** + * Get indexes of the matching patterns in methods order configuration + * @param {Object} method - Method metadata. + * @returns {Array} The matching patterns indexes. Return [Infinity] if there is no match. + */ + function getRefPropIndexes(method) { + const methodGroupIndexes = []; + + methodsOrder.forEach((currentGroup, groupIndex) => { + if (currentGroup === 'getters') { + if (method.getter) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'setters') { + if (method.setter) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'type-annotations') { + if (method.typeAnnotation) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'static-variables') { + if (method.staticVariable) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'static-methods') { + if (method.staticMethod) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'instance-variables') { + if (method.instanceVariable) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === 'instance-methods') { + if (method.instanceMethod) { + methodGroupIndexes.push(groupIndex); + } + } else if (arrayIncludes([ + 'displayName', + 'propTypes', + 'contextTypes', + 'childContextTypes', + 'mixins', + 'statics', + 'defaultProps', + 'constructor', + 'getDefaultProps', + 'state', + 'getInitialState', + 'getChildContext', + 'getDerivedStateFromProps', + 'componentWillMount', + 'UNSAFE_componentWillMount', + 'componentDidMount', + 'componentWillReceiveProps', + 'UNSAFE_componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', + 'getSnapshotBeforeUpdate', + 'componentDidUpdate', + 'componentDidCatch', + 'componentWillUnmount', + 'render', + ], currentGroup)) { + if (currentGroup === method.name) { + methodGroupIndexes.push(groupIndex); + } + } else { + // Is the group a regex? + const isRegExp = currentGroup.match(regExpRegExp); + if (isRegExp) { + const isMatching = new RegExp(isRegExp[1], isRegExp[2]).test(method.name); + if (isMatching) { + methodGroupIndexes.push(groupIndex); + } + } else if (currentGroup === method.name) { + methodGroupIndexes.push(groupIndex); + } + } + }); + + // No matching pattern, return 'everything-else' index + if (methodGroupIndexes.length === 0) { + const everythingElseIndex = methodsOrder.indexOf('everything-else'); + + if (everythingElseIndex !== -1) { + methodGroupIndexes.push(everythingElseIndex); + } else { + // No matching pattern and no 'everything-else' group + methodGroupIndexes.push(Infinity); + } + } + + return methodGroupIndexes; + } + + /** + * Get properties name + * @param {Object} node - Property. + * @returns {string} Property name. + */ + function getPropertyName(node) { + if (node.kind === 'get') { + return 'getter functions'; + } + + if (node.kind === 'set') { + return 'setter functions'; + } + + return astUtil.getPropertyName(node); + } + + /** + * Store a new error in the error list + * @param {Object} propA - Mispositioned property. + * @param {Object} propB - Reference property. + */ + function storeError(propA, propB) { + // Initialize the error object if needed + if (!errors[propA.index]) { + errors[propA.index] = { + node: propA.node, + score: 0, + closest: { + distance: Infinity, + ref: { + node: null, + index: 0, + }, + }, + }; + } + // Increment the prop score + errors[propA.index].score += 1; + // Stop here if we already have pushed another node at this position + if (getPropertyName(errors[propA.index].node) !== getPropertyName(propA.node)) { + return; + } + // Stop here if we already have a closer reference + if (Math.abs(propA.index - propB.index) > errors[propA.index].closest.distance) { + return; + } + // Update the closest reference + errors[propA.index].closest.distance = Math.abs(propA.index - propB.index); + errors[propA.index].closest.ref.node = propB.node; + errors[propA.index].closest.ref.index = propB.index; + } + + /** + * Dedupe errors, only keep the ones with the highest score and delete the others + */ + function dedupeErrors() { + entries(errors).forEach((entry) => { + const i = entry[0]; + const error = entry[1]; + + const index = error.closest.ref.index; + if (errors[index]) { + if (error.score > errors[index].score) { + delete errors[index]; + } else { + delete errors[i]; + } + } + }); + } + + /** + * Report errors + */ + function reportErrors() { + dedupeErrors(); + + entries(errors).forEach((entry) => { + const nodeA = entry[1].node; + const nodeB = entry[1].closest.ref.node; + const indexA = entry[0]; + const indexB = entry[1].closest.ref.index; + + report(context, messages.unsortedProps, 'unsortedProps', { + node: nodeA, + data: { + propA: getPropertyName(nodeA), + propB: getPropertyName(nodeB), + position: indexA < indexB ? 'before' : 'after', + }, + }); + }); + } + + /** + * Compare two properties and find out if they are in the right order + * @param {Array} propertiesInfos Array containing all the properties metadata. + * @param {Object} propA First property name and metadata + * @param {Object} propB Second property name. + * @returns {Object} Object containing a correct true/false flag and the correct indexes for the two properties. + */ + function comparePropsOrder(propertiesInfos, propA, propB) { + let i; + let j; + let k; + let l; + let refIndexA; + let refIndexB; + + // Get references indexes (the correct position) for given properties + const refIndexesA = getRefPropIndexes(propA); + const refIndexesB = getRefPropIndexes(propB); + + // Get current indexes for given properties + const classIndexA = propertiesInfos.indexOf(propA); + const classIndexB = propertiesInfos.indexOf(propB); + + // Loop around the references indexes for the 1st property + for (i = 0, j = refIndexesA.length; i < j; i++) { + refIndexA = refIndexesA[i]; + + // Loop around the properties for the 2nd property (for comparison) + for (k = 0, l = refIndexesB.length; k < l; k++) { + refIndexB = refIndexesB[k]; + + if ( + // Comparing the same properties + refIndexA === refIndexB + // 1st property is placed before the 2nd one in reference and in current component + || ((refIndexA < refIndexB) && (classIndexA < classIndexB)) + // 1st property is placed after the 2nd one in reference and in current component + || ((refIndexA > refIndexB) && (classIndexA > classIndexB)) + ) { + return { + correct: true, + indexA: classIndexA, + indexB: classIndexB, + }; + } + } + } + + // We did not find any correct match between reference and current component + return { + correct: false, + indexA: refIndexA, + indexB: refIndexB, + }; + } + + /** + * Check properties order from a properties list and store the eventual errors + * @param {Array} properties Array containing all the properties. + */ + function checkPropsOrder(properties) { + const propertiesInfos = properties.map((node) => ({ + name: getPropertyName(node), + getter: node.kind === 'get', + setter: node.kind === 'set', + staticVariable: node.static + && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition') + && (!node.value || !astUtil.isFunctionLikeExpression(node.value)), + staticMethod: node.static + && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition' || node.type === 'MethodDefinition') + && node.value + && (astUtil.isFunctionLikeExpression(node.value)), + instanceVariable: !node.static + && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition') + && (!node.value || !astUtil.isFunctionLikeExpression(node.value)), + instanceMethod: !node.static + && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition') + && node.value + && (astUtil.isFunctionLikeExpression(node.value)), + typeAnnotation: !!node.typeAnnotation && node.value === null, + })); + + // Loop around the properties + propertiesInfos.forEach((propA, i) => { + // Loop around the properties a second time (for comparison) + propertiesInfos.forEach((propB, k) => { + if (i === k) { + return; + } + + // Compare the properties order + const order = comparePropsOrder(propertiesInfos, propA, propB); + + if (!order.correct) { + // Store an error if the order is incorrect + storeError({ + node: properties[i], + index: order.indexA, + }, { + node: properties[k], + index: order.indexB, + }); + } + }); + }); + } + + return { + 'Program:exit'() { + values(components.list()).forEach((component) => { + const properties = astUtil.getComponentProperties(component.node); + checkPropsOrder(properties); + }); + + reportErrors(); + }, + }; + }), + + defaultConfig, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..a3ec8afd13f9acd84dd3a1213174a9ca32a99b8c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=sort-default-props.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..2b4122164b14f01c1890ab045b9c83f5489752a9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"sort-default-props.d.ts","sourceRoot":"","sources":["sort-default-props.js"],"names":[],"mappings":"wBAwBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js new file mode 100644 index 0000000000000000000000000000000000000000..3f3c6ea9b3bae8786af8f347d3e2dff9b5fc1528 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-default-props.js @@ -0,0 +1,180 @@ +/** + * @fileoverview Enforce default props alphabetical sorting + * @author Vladimir Kattsov + * @deprecated + */ + +'use strict'; + +const variableUtil = require('../util/variable'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getText = eslintUtil.getText; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + propsNotSorted: 'Default prop types declarations should be sorted alphabetically', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce defaultProps declarations alphabetical sorting', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('sort-default-props'), + }, + // fixable: 'code', + + messages, + + schema: [{ + type: 'object', + properties: { + ignoreCase: { + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create(context) { + const configuration = context.options[0] || {}; + const ignoreCase = configuration.ignoreCase || false; + + /** + * Get properties name + * @param {Object} node - Property. + * @returns {string} Property name. + */ + function getPropertyName(node) { + if (node.key || ['MethodDefinition', 'Property'].indexOf(node.type) !== -1) { + return node.key.name; + } + if (node.type === 'MemberExpression') { + return node.property.name; + // Special case for class properties + // (babel-eslint@5 does not expose property name so we have to rely on tokens) + } + if (node.type === 'ClassProperty') { + const tokens = getFirstTokens(context, node, 2); + return tokens[1] && tokens[1].type === 'Identifier' ? tokens[1].value : tokens[0].value; + } + return ''; + } + + /** + * Checks if the Identifier node passed in looks like a defaultProps declaration. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not + */ + function isDefaultPropsDeclaration(node) { + const propName = getPropertyName(node); + return (propName === 'defaultProps' || propName === 'getDefaultProps'); + } + + function getKey(node) { + return getText(context, node.key || node.argument); + } + + /** + * Find a variable by name in the current scope. + * @param {ASTNode} node The node to look for. + * @param {string} name Name of the variable to look for. + * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. + */ + function findVariableByName(node, name) { + const variable = variableUtil.getVariableFromContext(context, node, name); + + if (!variable || !variable.defs[0] || !variable.defs[0].node) { + return null; + } + + if (variable.defs[0].node.type === 'TypeAlias') { + return variable.defs[0].node.right; + } + + return variable.defs[0].node.init; + } + + /** + * Checks if defaultProps declarations are sorted + * @param {Array} declarations The array of AST nodes being checked. + * @returns {void} + */ + function checkSorted(declarations) { + // function fix(fixer) { + // return propTypesSortUtil.fixPropTypesSort(context, fixer, declarations, ignoreCase); + // } + + declarations.reduce((prev, curr, idx, decls) => { + if (/Spread(?:Property|Element)$/.test(curr.type)) { + return decls[idx + 1]; + } + + let prevPropName = getKey(prev); + let currentPropName = getKey(curr); + + if (ignoreCase) { + prevPropName = prevPropName.toLowerCase(); + currentPropName = currentPropName.toLowerCase(); + } + + if (currentPropName < prevPropName) { + report(context, messages.propsNotSorted, 'propsNotSorted', { + node: curr, + // fix + }); + + return prev; + } + + return curr; + }, declarations[0]); + } + + function checkNode(node) { + if (!node) { + return; + } + if (node.type === 'ObjectExpression') { + checkSorted(node.properties); + } else if (node.type === 'Identifier') { + const propTypesObject = findVariableByName(node, node.name); + if (propTypesObject && propTypesObject.properties) { + checkSorted(propTypesObject.properties); + } + } + } + + // -------------------------------------------------------------------------- + // Public API + // -------------------------------------------------------------------------- + + return { + 'ClassProperty, PropertyDefinition'(node) { + if (!isDefaultPropsDeclaration(node)) { + return; + } + + checkNode(node.value); + }, + + MemberExpression(node) { + if (!isDefaultPropsDeclaration(node)) { + return; + } + + checkNode('right' in node.parent && node.parent.right); + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9f8a21699b2fb933940bbca58c3ba40f66190400 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=sort-prop-types.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..9a696d47371f9806b0be5a42d081edd28a0885a3 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"sort-prop-types.d.ts","sourceRoot":"","sources":["sort-prop-types.js"],"names":[],"mappings":"wBAsCW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js new file mode 100644 index 0000000000000000000000000000000000000000..6aaebea184cde95baafc85413fe3de5aeb0a2421 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/sort-prop-types.js @@ -0,0 +1,316 @@ +/** + * @fileoverview Enforce propTypes declarations alphabetical sorting + */ + +'use strict'; + +const astUtil = require('../util/ast'); +const variableUtil = require('../util/variable'); +const propsUtil = require('../util/props'); +const docsUrl = require('../util/docsUrl'); +const propWrapperUtil = require('../util/propWrapper'); +const propTypesSortUtil = require('../util/propTypesSort'); +const report = require('../util/report'); +const eslintUtil = require('../util/eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + requiredPropsFirst: 'Required prop types must be listed before all other prop types', + callbackPropsLast: 'Callback prop types must be listed after all other prop types', + propsNotSorted: 'Prop types declarations should be sorted alphabetically', +}; + +function getKey(context, node) { + if (node.type === 'ObjectTypeProperty') { + return getSourceCode(context).getFirstToken(node).value; + } + if (node.key && node.key.value) { + return node.key.value; + } + return getText(context, node.key || node.argument); +} + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce propTypes declarations alphabetical sorting', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('sort-prop-types'), + }, + fixable: 'code', + + messages, + + schema: [{ + type: 'object', + properties: { + requiredFirst: { + type: 'boolean', + }, + callbacksLast: { + type: 'boolean', + }, + ignoreCase: { + type: 'boolean', + }, + // Whether alphabetical sorting should be enforced + noSortAlphabetically: { + type: 'boolean', + }, + sortShapeProp: { + type: 'boolean', + }, + checkTypes: { + type: 'boolean', + }, + }, + additionalProperties: false, + }], + }, + + create(context) { + const configuration = context.options[0] || {}; + const requiredFirst = configuration.requiredFirst || false; + const callbacksLast = configuration.callbacksLast || false; + const ignoreCase = configuration.ignoreCase || false; + const noSortAlphabetically = configuration.noSortAlphabetically || false; + const sortShapeProp = configuration.sortShapeProp || false; + const checkTypes = configuration.checkTypes || false; + + const typeAnnotations = new Map(); + + /** + * Checks if propTypes declarations are sorted + * @param {Array} declarations The array of AST nodes being checked. + * @returns {void} + */ + function checkSorted(declarations) { + // Declarations will be `undefined` if the `shape` is not a literal. For + // example, if it is a propType imported from another file. + if (!declarations) { + return; + } + + function fix(fixer) { + return propTypesSortUtil.fixPropTypesSort( + context, + fixer, + declarations, + ignoreCase, + requiredFirst, + callbacksLast, + noSortAlphabetically, + sortShapeProp, + checkTypes + ); + } + + const callbackPropsLastSeen = new WeakSet(); + const requiredPropsFirstSeen = new WeakSet(); + const propsNotSortedSeen = new WeakSet(); + + declarations.reduce((prev, curr, idx, decls) => { + if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') { + return decls[idx + 1]; + } + + let prevPropName = getKey(context, prev); + let currentPropName = getKey(context, curr); + const previousIsRequired = propTypesSortUtil.isRequiredProp(prev); + const currentIsRequired = propTypesSortUtil.isRequiredProp(curr); + const previousIsCallback = propTypesSortUtil.isCallbackPropName(prevPropName); + const currentIsCallback = propTypesSortUtil.isCallbackPropName(currentPropName); + + if (ignoreCase) { + prevPropName = String(prevPropName).toLowerCase(); + currentPropName = String(currentPropName).toLowerCase(); + } + + if (requiredFirst) { + if (previousIsRequired && !currentIsRequired) { + // Transition between required and non-required. Don't compare for alphabetical. + return curr; + } + if (!previousIsRequired && currentIsRequired) { + // Encountered a non-required prop after a required prop + if (!requiredPropsFirstSeen.has(curr)) { + requiredPropsFirstSeen.add(curr); + report(context, messages.requiredPropsFirst, 'requiredPropsFirst', { + node: curr, + fix, + }); + } + return curr; + } + } + + if (callbacksLast) { + if (!previousIsCallback && currentIsCallback) { + // Entering the callback prop section + return curr; + } + if (previousIsCallback && !currentIsCallback) { + // Encountered a non-callback prop after a callback prop + if (!callbackPropsLastSeen.has(prev)) { + callbackPropsLastSeen.add(prev); + report(context, messages.callbackPropsLast, 'callbackPropsLast', { + node: prev, + fix, + }); + } + return prev; + } + } + + if (!noSortAlphabetically && currentPropName < prevPropName) { + if (!propsNotSortedSeen.has(curr)) { + propsNotSortedSeen.add(curr); + report(context, messages.propsNotSorted, 'propsNotSorted', { + node: curr, + fix, + }); + } + return prev; + } + + return curr; + }, declarations[0]); + } + + function checkNode(node) { + if (!node) { + return; + } + + if (node.type === 'ObjectExpression') { + checkSorted(node.properties); + } else if (node.type === 'Identifier') { + const propTypesObject = variableUtil.findVariableByName(context, node, node.name); + if (propTypesObject && propTypesObject.properties) { + checkSorted(propTypesObject.properties); + } + } else if (astUtil.isCallExpression(node)) { + const innerNode = node.arguments && node.arguments[0]; + if (propWrapperUtil.isPropWrapperFunction(context, node.callee.name) && innerNode) { + checkNode(innerNode); + } + } + } + + function handleFunctionComponent(node) { + const firstArg = node.params + && node.params.length > 0 + && node.params[0].typeAnnotation + && node.params[0].typeAnnotation.typeAnnotation; + if (firstArg && firstArg.type === 'TSTypeReference') { + const propType = typeAnnotations.get(firstArg.typeName.name) + && typeAnnotations.get(firstArg.typeName.name)[0]; + if (propType && propType.members) { + checkSorted(propType.members); + } + } else if (firstArg && firstArg.type === 'TSTypeLiteral') { + if (firstArg.members) { + checkSorted(firstArg.members); + } + } else if (firstArg && firstArg.type === 'GenericTypeAnnotation') { + const propType = typeAnnotations.get(firstArg.id.name) + && typeAnnotations.get(firstArg.id.name)[0]; + if (propType && propType.properties) { + checkSorted(propType.properties); + } + } else if (firstArg && firstArg.type === 'ObjectTypeAnnotation') { + if (firstArg.properties) { + checkSorted(firstArg.properties); + } + } + } + + return Object.assign({ + CallExpression(node) { + if (!sortShapeProp || !propTypesSortUtil.isShapeProp(node) || !(node.arguments && node.arguments[0])) { + return; + } + + const firstArg = node.arguments[0]; + if (firstArg.properties) { + checkSorted(firstArg.properties); + } else if (firstArg.type === 'Identifier') { + const variable = variableUtil.findVariableByName(context, node, firstArg.name); + if (variable && variable.properties) { + checkSorted(variable.properties); + } + } + }, + + 'ClassProperty, PropertyDefinition'(node) { + if (!propsUtil.isPropTypesDeclaration(node)) { + return; + } + checkNode(node.value); + }, + + MemberExpression(node) { + if (!propsUtil.isPropTypesDeclaration(node)) { + return; + } + + checkNode(node.parent.right); + }, + + ObjectExpression(node) { + node.properties.forEach((property) => { + if (!property.key) { + return; + } + + if (!propsUtil.isPropTypesDeclaration(property)) { + return; + } + if (property.value.type === 'ObjectExpression') { + checkSorted(property.value.properties); + } + }); + }, + }, checkTypes ? { + TSTypeLiteral(node) { + if (node && node.parent.id) { + const currentNode = [].concat( + typeAnnotations.get(node.parent.id.name) || [], + node + ); + typeAnnotations.set(node.parent.id.name, currentNode); + } + }, + + TypeAlias(node) { + if (node.right.type === 'ObjectTypeAnnotation') { + const currentNode = [].concat( + typeAnnotations.get(node.id.name) || [], + node.right + ); + typeAnnotations.set(node.id.name, currentNode); + } + }, + + TSTypeAliasDeclaration(node) { + if (node.typeAnnotation.type === 'TSTypeLiteral' || node.typeAnnotation.type === 'ObjectTypeAnnotation') { + const currentNode = [].concat( + typeAnnotations.get(node.id.name) || [], + node.typeAnnotation + ); + typeAnnotations.set(node.id.name, currentNode); + } + }, + FunctionDeclaration: handleFunctionComponent, + ArrowFunctionExpression: handleFunctionComponent, + } : null); + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..6dceb413aebc7ef27df50dd6be2b71aa0d7f43b8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=state-in-constructor.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..893161bf6397093362cb305b72a980f53c207a34 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"state-in-constructor.d.ts","sourceRoot":"","sources":["state-in-constructor.js"],"names":[],"mappings":"wBAqBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js new file mode 100644 index 0000000000000000000000000000000000000000..9dd756b4d23fdb17dc826e02c139d70e5e093632 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/state-in-constructor.js @@ -0,0 +1,68 @@ +/** + * @fileoverview Enforce the state initialization style to be either in a constructor or with a class property + * @author Kanitkorn Sujautra + */ + +'use strict'; + +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const docsUrl = require('../util/docsUrl'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + stateInitConstructor: 'State initialization should be in a constructor', + stateInitClassProp: 'State initialization should be in a class property', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce class component state initialization style', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('state-in-constructor'), + }, + + messages, + + schema: [{ + enum: ['always', 'never'], + }], + }, + + create(context) { + const option = context.options[0] || 'always'; + return { + 'ClassProperty, PropertyDefinition'(node) { + if ( + option === 'always' + && !node.static + && node.key.name === 'state' + && componentUtil.getParentES6Component(context, node) + ) { + report(context, messages.stateInitConstructor, 'stateInitConstructor', { + node, + }); + } + }, + AssignmentExpression(node) { + if ( + option === 'never' + && componentUtil.isStateMemberExpression(node.left) + && astUtil.inConstructor(context, node) + && componentUtil.getParentES6Component(context, node) + ) { + report(context, messages.stateInitClassProp, 'stateInitClassProp', { + node, + }); + } + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..fc42b165ecbd44130ab8b734185131eed8678621 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=static-property-placement.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..1ccec750e82ea86d2216c13d819a106864b4cbc9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"static-property-placement.d.ts","sourceRoot":"","sources":["static-property-placement.js"],"names":[],"mappings":"wBA0DW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js new file mode 100644 index 0000000000000000000000000000000000000000..ce38a8e42e27ac3cfb56400122c5860227fe4dc5 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/static-property-placement.js @@ -0,0 +1,194 @@ +/** + * @fileoverview Defines where React component static properties should be positioned. + * @author Daniel Mason + */ + +'use strict'; + +const fromEntries = require('object.fromentries'); +const Components = require('../util/Components'); +const docsUrl = require('../util/docsUrl'); +const astUtil = require('../util/ast'); +const componentUtil = require('../util/componentUtil'); +const propsUtil = require('../util/props'); +const report = require('../util/report'); +const getScope = require('../util/eslint').getScope; + +// ------------------------------------------------------------------------------ +// Positioning Options +// ------------------------------------------------------------------------------ +const STATIC_PUBLIC_FIELD = 'static public field'; +const STATIC_GETTER = 'static getter'; +const PROPERTY_ASSIGNMENT = 'property assignment'; +const POSITION_SETTINGS = [STATIC_PUBLIC_FIELD, STATIC_GETTER, PROPERTY_ASSIGNMENT]; + +// ------------------------------------------------------------------------------ +// Rule messages +// ------------------------------------------------------------------------------ +const ERROR_MESSAGES = { + [STATIC_PUBLIC_FIELD]: 'notStaticClassProp', + [STATIC_GETTER]: 'notGetterClassFunc', + [PROPERTY_ASSIGNMENT]: 'declareOutsideClass', +}; + +// ------------------------------------------------------------------------------ +// Properties to check +// ------------------------------------------------------------------------------ +const propertiesToCheck = { + propTypes: propsUtil.isPropTypesDeclaration, + defaultProps: propsUtil.isDefaultPropsDeclaration, + childContextTypes: propsUtil.isChildContextTypesDeclaration, + contextTypes: propsUtil.isContextTypesDeclaration, + contextType: propsUtil.isContextTypeDeclaration, + displayName: (node) => propsUtil.isDisplayNameDeclaration(astUtil.getPropertyNameNode(node)), +}; + +const classProperties = Object.keys(propertiesToCheck); +const schemaProperties = fromEntries(classProperties.map((property) => [property, { enum: POSITION_SETTINGS }])); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + notStaticClassProp: '\'{{name}}\' should be declared as a static class property.', + notGetterClassFunc: '\'{{name}}\' should be declared as a static getter class function.', + declareOutsideClass: '\'{{name}}\' should be declared outside the class body.', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforces where React component static properties should be positioned.', + category: 'Stylistic Issues', + recommended: false, + url: docsUrl('static-property-placement'), + }, + fixable: null, // or 'code' or 'whitespace' + + messages, + + schema: [ + { enum: POSITION_SETTINGS }, + { + type: 'object', + properties: schemaProperties, + additionalProperties: false, + }, + ], + }, + + create: Components.detect((context, components, utils) => { + // variables should be defined here + const options = context.options; + const defaultCheckType = options[0] || STATIC_PUBLIC_FIELD; + const hasAdditionalConfig = options.length > 1; + const additionalConfig = hasAdditionalConfig ? options[1] : {}; + + // Set config + const config = fromEntries(classProperties.map((property) => [ + property, + additionalConfig[property] || defaultCheckType, + ])); + + // ---------------------------------------------------------------------- + // Helpers + // ---------------------------------------------------------------------- + + /** + * Checks if we are declaring context in class + * @param {ASTNode} node + * @returns {boolean} True if we are declaring context in class, false if not. + */ + function isContextInClass(node) { + let blockNode; + let scope = getScope(context, node); + while (scope) { + blockNode = scope.block; + if (blockNode && blockNode.type === 'ClassDeclaration') { + return true; + } + scope = scope.upper; + } + + return false; + } + + /** + * Check if we should report this property node + * @param {ASTNode} node + * @param {string} expectedRule + */ + function reportNodeIncorrectlyPositioned(node, expectedRule) { + // Detect if this node is an expected property declaration adn return the property name + const name = classProperties.find((propertyName) => { + if (propertiesToCheck[propertyName](node)) { + return !!propertyName; + } + + return false; + }); + + // If name is set but the configured rule does not match expected then report error + if ( + name + && ( + config[name] !== expectedRule + || (!node.static && (config[name] === STATIC_PUBLIC_FIELD || config[name] === STATIC_GETTER)) + ) + ) { + const messageId = ERROR_MESSAGES[config[name]]; + report(context, messages[messageId], messageId, { + node, + data: { name }, + }); + } + } + + // ---------------------------------------------------------------------- + // Public + // ---------------------------------------------------------------------- + return { + 'ClassProperty, PropertyDefinition'(node) { + if (!componentUtil.getParentES6Component(context, node)) { + return; + } + + reportNodeIncorrectlyPositioned(node, STATIC_PUBLIC_FIELD); + }, + + MemberExpression(node) { + // If definition type is undefined then it must not be a defining expression or if the definition is inside a + // class body then skip this node. + const right = node.parent.right; + if (!right || right.type === 'undefined' || isContextInClass(node)) { + return; + } + + // Get the related component + const relatedComponent = utils.getRelatedComponent(node); + + // If the related component is not an ES6 component then skip this node + if (!relatedComponent || !componentUtil.isES6Component(relatedComponent.node, context)) { + return; + } + + // Report if needed + reportNodeIncorrectlyPositioned(node, PROPERTY_ASSIGNMENT); + }, + + MethodDefinition(node) { + // If the function is inside a class and is static getter then check if correctly positioned + if ( + componentUtil.getParentES6Component(context, node) + && node.static + && node.kind === 'get' + ) { + // Report error if needed + reportNodeIncorrectlyPositioned(node, STATIC_GETTER); + } + }, + }; + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..3d5ea86da39f69f1809ece6409f24481fe3751b5 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=style-prop-object.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..fb945fe10fa1359cbbd0a30155246b62ac2cfb88 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"style-prop-object.d.ts","sourceRoot":"","sources":["style-prop-object.js"],"names":[],"mappings":"wBAoBW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js new file mode 100644 index 0000000000000000000000000000000000000000..f2c63e525f6689398777d57cd5b58f93e54f0c53 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/style-prop-object.js @@ -0,0 +1,145 @@ +/** + * @fileoverview Enforce style prop value is an object + * @author David Petersen + */ + +'use strict'; + +const variableUtil = require('../util/variable'); +const docsUrl = require('../util/docsUrl'); +const isCreateElement = require('../util/isCreateElement'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const messages = { + stylePropNotObject: 'Style prop value must be an object', +}; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Enforce style prop value is an object', + category: 'Possible Errors', + recommended: false, + url: docsUrl('style-prop-object'), + }, + + messages, + + schema: [ + { + type: 'object', + properties: { + allow: { + type: 'array', + items: { + type: 'string', + }, + additionalItems: false, + uniqueItems: true, + }, + }, + }, + ], + }, + + create(context) { + const allowed = new Set(((context.options.length > 0) && context.options[0].allow) || []); + + /** + * @param {ASTNode} expression An Identifier node + * @returns {boolean} + */ + function isNonNullaryLiteral(expression) { + return expression.type === 'Literal' && expression.value !== null; + } + + /** + * @param {object} node A Identifier node + */ + function checkIdentifiers(node) { + const variable = variableUtil.getVariableFromContext(context, node, node.name); + + if (!variable || !variable.defs[0] || !variable.defs[0].node.init) { + return; + } + + if (isNonNullaryLiteral(variable.defs[0].node.init)) { + report(context, messages.stylePropNotObject, 'stylePropNotObject', { + node, + }); + } + } + + return { + CallExpression(node) { + if ( + isCreateElement(context, node) + && node.arguments.length > 1 + ) { + if ('name' in node.arguments[0] && node.arguments[0].name) { + // store name of component + const componentName = node.arguments[0].name; + + // allowed list contains the name + if (allowed.has(componentName)) { + // abort operation + return; + } + } + if (node.arguments[1].type === 'ObjectExpression') { + const style = node.arguments[1].properties.find((property) => ( + 'key' in property + && property.key + && 'name' in property.key + && property.key.name === 'style' + && !property.computed + )); + + if (style && 'value' in style) { + if (style.value.type === 'Identifier') { + checkIdentifiers(style.value); + } else if (isNonNullaryLiteral(style.value)) { + report(context, messages.stylePropNotObject, 'stylePropNotObject', { + node: style.value, + }); + } + } + } + } + }, + + JSXAttribute(node) { + if (!node.value || node.name.name !== 'style') { + return; + } + // store parent element + const parentElement = node.parent; + + // parent element is a JSXOpeningElement + if (parentElement && parentElement.type === 'JSXOpeningElement') { + // get the name of the JSX element + const name = parentElement.name && parentElement.name.name; + + // allowed list contains the name + if (allowed.has(name)) { + // abort operation + return; + } + } + + if (node.value.type !== 'JSXExpressionContainer' || isNonNullaryLiteral(node.value.expression)) { + report(context, messages.stylePropNotObject, 'stylePropNotObject', { + node, + }); + } else if (node.value.expression.type === 'Identifier') { + checkIdentifiers(node.value.expression); + } + }, + }; + }, +}; diff --git a/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..043a90617a1a626f43471416ca456ed191dad000 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts @@ -0,0 +1,3 @@ +declare const _exports: import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=void-dom-elements-no-children.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..85f86b207ad6f5691218fea249dac882c6a3d04a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"void-dom-elements-no-children.d.ts","sourceRoot":"","sources":["void-dom-elements-no-children.js"],"names":[],"mappings":"wBAiDW,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js new file mode 100644 index 0000000000000000000000000000000000000000..f8187b09eab10d54df0357154bd184af32bdd9e8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/rules/void-dom-elements-no-children.js @@ -0,0 +1,166 @@ +/** + * @fileoverview Prevent void elements (e.g. ,
    ) from receiving + * children + * @author Joe Lencioni + */ + +'use strict'; + +const has = require('hasown'); + +const docsUrl = require('../util/docsUrl'); +const isCreateElement = require('../util/isCreateElement'); +const report = require('../util/report'); + +// ------------------------------------------------------------------------------ +// Helpers +// ------------------------------------------------------------------------------ + +// Using an object here to avoid array scan. We should switch to Set once +// support is good enough. +const VOID_DOM_ELEMENTS = { + area: true, + base: true, + br: true, + col: true, + embed: true, + hr: true, + img: true, + input: true, + keygen: true, + link: true, + menuitem: true, + meta: true, + param: true, + source: true, + track: true, + wbr: true, +}; + +function isVoidDOMElement(elementName) { + return has(VOID_DOM_ELEMENTS, elementName); +} + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +const noChildrenInVoidEl = 'Void DOM element <{{element}} /> cannot receive children.'; + +/** @type {import('eslint').Rule.RuleModule} */ +module.exports = { + meta: { + docs: { + description: 'Disallow void DOM elements (e.g. ``, `
    `) from receiving children', + category: 'Best Practices', + recommended: false, + url: docsUrl('void-dom-elements-no-children'), + }, + + messages: { + noChildrenInVoidEl, + }, + + schema: [], + }, + + create: (context) => ({ + JSXElement(node) { + const elementName = node.openingElement.name.name; + + if (!isVoidDOMElement(elementName)) { + // e.g.
    + return; + } + + if (node.children.length > 0) { + // e.g.
    Foo
    + report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', { + node, + data: { + element: elementName, + }, + }); + } + + const attributes = node.openingElement.attributes; + + const hasChildrenAttributeOrDanger = attributes.some((attribute) => { + if (!attribute.name) { + return false; + } + + return attribute.name.name === 'children' || attribute.name.name === 'dangerouslySetInnerHTML'; + }); + + if (hasChildrenAttributeOrDanger) { + // e.g.
    + report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', { + node, + data: { + element: elementName, + }, + }); + } + }, + + CallExpression(node) { + if (node.callee.type !== 'MemberExpression' && node.callee.type !== 'Identifier') { + return; + } + + if (!isCreateElement(context, node)) { + return; + } + + const args = node.arguments; + + if (args.length < 1) { + // React.createElement() should not crash linter + return; + } + + const elementName = 'value' in args[0] ? args[0].value : undefined; + + if (!isVoidDOMElement(elementName)) { + // e.g. React.createElement('div'); + return; + } + + if (args.length < 2 || args[1].type !== 'ObjectExpression') { + return; + } + + const firstChild = args[2]; + if (firstChild) { + // e.g. React.createElement('br', undefined, 'Foo') + report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', { + node, + data: { + element: elementName, + }, + }); + } + + const props = args[1].properties; + + const hasChildrenPropOrDanger = props.some((prop) => { + if (!('key' in prop) || !prop.key || !('name' in prop.key)) { + return false; + } + + return prop.key.name === 'children' || prop.key.name === 'dangerouslySetInnerHTML'; + }); + + if (hasChildrenPropOrDanger) { + // e.g. React.createElement('br', { children: 'Foo' }) + report(context, noChildrenInVoidEl, 'noChildrenInVoidEl', { + node, + data: { + element: elementName, + }, + }); + } + }, + }), +}; diff --git a/node_modules/eslint-plugin-react/lib/types.d.ts b/node_modules/eslint-plugin-react/lib/types.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..30df9c02c1bad654f1867980ecc3fbbe73d7bbaf --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/types.d.ts @@ -0,0 +1,29 @@ +import eslint from 'eslint'; +import estree from 'estree'; + +declare global { + interface ASTNode extends estree.BaseNode { + [_: string]: any; // TODO: fixme + } + type Scope = eslint.Scope.Scope; + type Token = eslint.AST.Token; + type Fixer = eslint.Rule.RuleFixer; + type JSXAttribute = ASTNode; + type JSXElement = ASTNode; + type JSXFragment = ASTNode; + type JSXOpeningElement = ASTNode; + type JSXSpreadAttribute = ASTNode; + + type Context = eslint.Rule.RuleContext; + + type TypeDeclarationBuilder = (annotation: ASTNode, parentName: string, seen: Set) => object; + + type TypeDeclarationBuilders = { + [k in string]: TypeDeclarationBuilder; + }; + + type UnionTypeDefinition = { + type: 'union' | 'shape'; + children: unknown[]; + }; +} diff --git a/node_modules/eslint-plugin-react/lib/util/Components.d.ts b/node_modules/eslint-plugin-react/lib/util/Components.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d6d2826e873153cd786f281f4fa400b8574971d --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/Components.d.ts @@ -0,0 +1,76 @@ +declare const _exports: typeof Components & { + detect(rule: any): (context?: any) => { + [_: string]: Function; + }; +}; +export = _exports; +/** + * Components + */ +declare class Components { + /** + * Add a node to the components list, or update it if it's already in the list + * + * @param {ASTNode} node The AST node being added. + * @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes) + * @returns {Object} Added component object + */ + add(node: ASTNode, confidence: number): any; + /** + * Find a component in the list using its node + * + * @param {ASTNode} node The AST node being searched. + * @returns {Object} Component object, undefined if the component is not found or has confidence value of 0. + */ + get(node: ASTNode): any; + /** + * Update a component in the list + * + * @param {ASTNode} node The AST node being updated. + * @param {Object} props Additional properties to add to the component. + */ + set(node: ASTNode, props: any): void; + /** + * Return the components list + * Components for which we are not confident are not returned + * + * @returns {Object} Components list + */ + list(): any; + /** + * Return the length of the components list + * Components for which we are not confident are not counted + * + * @returns {number} Components list length + */ + length(): number; + /** + * Return the node naming the default React import + * It can be used to determine the local name of import, even if it's imported + * with an unusual name. + * + * @returns {ASTNode} React default import node + */ + getDefaultReactImports(): ASTNode; + /** + * Return the nodes of all React named imports + * + * @returns {Object} The list of React named imports + */ + getNamedReactImports(): any; + /** + * Add the default React import specifier to the scope + * + * @param {ASTNode} specifier The AST Node of the default React import + * @returns {void} + */ + addDefaultReactImport(specifier: ASTNode): void; + /** + * Add a named React import specifier to the scope + * + * @param {ASTNode} specifier The AST Node of a named React import + * @returns {void} + */ + addNamedReactImport(specifier: ASTNode): void; +} +//# sourceMappingURL=Components.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map b/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..a273c5d55f5e95c4627434db5b972d5873138c26 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/Components.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Components.d.ts","sourceRoot":"","sources":["Components.js"],"names":[],"mappings":";;;;;;AA2DA;;GAEG;AACH;IAME;;;;;;OAMG;IACH,UAJW,OAAO,cACP,MAAM,OAmBhB;IAED;;;;;OAKG;IACH,UAHW,OAAO,OAUjB;IAED;;;;;OAKG;IACH,UAHW,OAAO,oBAwBjB;IAED;;;;;OAKG;IACH,YAoCC;IAED;;;;;OAKG;IACH,UAFa,MAAM,CAKlB;IAED;;;;;;OAMG;IACH,0BAFa,OAAO,CAInB;IAED;;;;OAIG;IACH,4BAEC;IAED;;;;;OAKG;IACH,iCAHW,OAAO,GACL,IAAI,CAOhB;IAED;;;;;OAKG;IACH,+BAHW,OAAO,GACL,IAAI,CAOhB;CACF"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/Components.js b/node_modules/eslint-plugin-react/lib/util/Components.js new file mode 100644 index 0000000000000000000000000000000000000000..446fd9022af8c4a0ea38c0c57089d93b41411da9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/Components.js @@ -0,0 +1,959 @@ +/** + * @fileoverview Utility class and functions for React components detection + * @author Yannick Croissant + */ + +'use strict'; + +const arrayIncludes = require('array-includes'); +const fromEntries = require('object.fromentries'); +const values = require('object.values'); +const iterFrom = require('es-iterator-helpers/Iterator.from'); +const map = require('es-iterator-helpers/Iterator.prototype.map'); + +const variableUtil = require('./variable'); +const pragmaUtil = require('./pragma'); +const astUtil = require('./ast'); +const componentUtil = require('./componentUtil'); +const propTypesUtil = require('./propTypes'); +const jsxUtil = require('./jsx'); +const usedPropTypesUtil = require('./usedPropTypes'); +const defaultPropsUtil = require('./defaultProps'); +const isFirstLetterCapitalized = require('./isFirstLetterCapitalized'); +const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getText = eslintUtil.getText; + +function getId(node) { + return node ? `${node.range[0]}:${node.range[1]}` : ''; +} + +function usedPropTypesAreEquivalent(propA, propB) { + if (propA.name === propB.name) { + if (!propA.allNames && !propB.allNames) { + return true; + } + if (Array.isArray(propA.allNames) && Array.isArray(propB.allNames) && propA.allNames.join('') === propB.allNames.join('')) { + return true; + } + return false; + } + return false; +} + +function mergeUsedPropTypes(propsList, newPropsList) { + const propsToAdd = newPropsList.filter((newProp) => { + const newPropIsAlreadyInTheList = propsList.some((prop) => usedPropTypesAreEquivalent(prop, newProp)); + return !newPropIsAlreadyInTheList; + }); + + return propsList.concat(propsToAdd); +} + +const USE_HOOK_PREFIX_REGEX = /^use[A-Z]/; + +const Lists = new WeakMap(); +const ReactImports = new WeakMap(); + +/** + * Components + */ +class Components { + constructor() { + Lists.set(this, {}); + ReactImports.set(this, {}); + } + + /** + * Add a node to the components list, or update it if it's already in the list + * + * @param {ASTNode} node The AST node being added. + * @param {number} confidence Confidence in the component detection (0=banned, 1=maybe, 2=yes) + * @returns {Object} Added component object + */ + add(node, confidence) { + const id = getId(node); + const list = Lists.get(this); + if (list[id]) { + if (confidence === 0 || list[id].confidence === 0) { + list[id].confidence = 0; + } else { + list[id].confidence = Math.max(list[id].confidence, confidence); + } + return list[id]; + } + list[id] = { + node, + confidence, + }; + return list[id]; + } + + /** + * Find a component in the list using its node + * + * @param {ASTNode} node The AST node being searched. + * @returns {Object} Component object, undefined if the component is not found or has confidence value of 0. + */ + get(node) { + const id = getId(node); + const item = Lists.get(this)[id]; + if (item && item.confidence >= 1) { + return item; + } + return null; + } + + /** + * Update a component in the list + * + * @param {ASTNode} node The AST node being updated. + * @param {Object} props Additional properties to add to the component. + */ + set(node, props) { + const list = Lists.get(this); + let component = list[getId(node)]; + while (!component || component.confidence < 1) { + node = node.parent; + if (!node) { + return; + } + component = list[getId(node)]; + } + + Object.assign( + component, + props, + { + usedPropTypes: mergeUsedPropTypes( + component.usedPropTypes || [], + props.usedPropTypes || [] + ), + } + ); + } + + /** + * Return the components list + * Components for which we are not confident are not returned + * + * @returns {Object} Components list + */ + list() { + const thisList = Lists.get(this); + const list = {}; + const usedPropTypes = {}; + + // Find props used in components for which we are not confident + Object.keys(thisList).filter((i) => thisList[i].confidence < 2).forEach((i) => { + let component = null; + let node = null; + node = thisList[i].node; + while (!component && node.parent) { + node = node.parent; + // Stop moving up if we reach a decorator + if (node.type === 'Decorator') { + break; + } + component = this.get(node); + } + if (component) { + const newUsedProps = (thisList[i].usedPropTypes || []).filter((propType) => !propType.node || propType.node.kind !== 'init'); + + const componentId = getId(component.node); + + usedPropTypes[componentId] = mergeUsedPropTypes(usedPropTypes[componentId] || [], newUsedProps); + } + }); + + // Assign used props in not confident components to the parent component + Object.keys(thisList).filter((j) => thisList[j].confidence >= 2).forEach((j) => { + const id = getId(thisList[j].node); + list[j] = thisList[j]; + if (usedPropTypes[id]) { + list[j].usedPropTypes = mergeUsedPropTypes(list[j].usedPropTypes || [], usedPropTypes[id]); + } + }); + return list; + } + + /** + * Return the length of the components list + * Components for which we are not confident are not counted + * + * @returns {number} Components list length + */ + length() { + const list = Lists.get(this); + return values(list).filter((component) => component.confidence >= 2).length; + } + + /** + * Return the node naming the default React import + * It can be used to determine the local name of import, even if it's imported + * with an unusual name. + * + * @returns {ASTNode} React default import node + */ + getDefaultReactImports() { + return ReactImports.get(this).defaultReactImports; + } + + /** + * Return the nodes of all React named imports + * + * @returns {Object} The list of React named imports + */ + getNamedReactImports() { + return ReactImports.get(this).namedReactImports; + } + + /** + * Add the default React import specifier to the scope + * + * @param {ASTNode} specifier The AST Node of the default React import + * @returns {void} + */ + addDefaultReactImport(specifier) { + const info = ReactImports.get(this); + ReactImports.set(this, Object.assign({}, info, { + defaultReactImports: (info.defaultReactImports || []).concat(specifier), + })); + } + + /** + * Add a named React import specifier to the scope + * + * @param {ASTNode} specifier The AST Node of a named React import + * @returns {void} + */ + addNamedReactImport(specifier) { + const info = ReactImports.get(this); + ReactImports.set(this, Object.assign({}, info, { + namedReactImports: (info.namedReactImports || []).concat(specifier), + })); + } +} + +function getWrapperFunctions(context, pragma) { + const componentWrapperFunctions = context.settings.componentWrapperFunctions || []; + + // eslint-disable-next-line arrow-body-style + return componentWrapperFunctions.map((wrapperFunction) => { + return typeof wrapperFunction === 'string' + ? { property: wrapperFunction } + : Object.assign({}, wrapperFunction, { + object: wrapperFunction.object === '' ? pragma : wrapperFunction.object, + }); + }).concat([ + { property: 'forwardRef', object: pragma }, + { property: 'memo', object: pragma }, + ]); +} + +// eslint-disable-next-line valid-jsdoc +/** + * Merge many eslint rules into one + * @param {{[_: string]: Function}[]} rules the returned values for eslint rule.create(context) + * @returns {{[_: string]: Function}} merged rule + */ +function mergeRules(rules) { + /** @type {Map} */ + const handlersByKey = new Map(); + rules.forEach((rule) => { + Object.keys(rule).forEach((key) => { + const fns = handlersByKey.get(key); + if (!fns) { + handlersByKey.set(key, [rule[key]]); + } else { + fns.push(rule[key]); + } + }); + }); + + /** @type {{ [key: string]: Function }} */ + return fromEntries(map(iterFrom(handlersByKey), (entry) => [ + entry[0], + function mergedHandler(node) { + entry[1].forEach((fn) => { + fn(node); + }); + }, + ])); +} + +function componentRule(rule, context) { + const pragma = pragmaUtil.getFromContext(context); + const components = new Components(); + const wrapperFunctions = getWrapperFunctions(context, pragma); + + // Utilities for component detection + const utils = { + /** + * Check if variable is destructured from pragma import + * + * @param {ASTNode} node The AST node to check + * @param {string} variable The variable name to check + * @returns {boolean} True if createElement is destructured from the pragma + */ + isDestructuredFromPragmaImport(node, variable) { + return isDestructuredFromPragmaImport(context, node, variable); + }, + + /** + * @param {ASTNode} node + * @param {boolean=} strict + * @returns {boolean} + */ + isReturningJSX(node, strict) { + return jsxUtil.isReturningJSX(context, node, strict, true); + }, + + isReturningJSXOrNull(node, strict) { + return jsxUtil.isReturningJSX(context, node, strict); + }, + + isReturningOnlyNull(node) { + return jsxUtil.isReturningOnlyNull(node, context); + }, + + getPragmaComponentWrapper(node) { + let isPragmaComponentWrapper; + let currentNode = node; + let prevNode; + do { + currentNode = currentNode.parent; + isPragmaComponentWrapper = this.isPragmaComponentWrapper(currentNode); + if (isPragmaComponentWrapper) { + prevNode = currentNode; + } + } while (isPragmaComponentWrapper); + + return prevNode; + }, + + getComponentNameFromJSXElement(node) { + if (node.type !== 'JSXElement') { + return null; + } + if (node.openingElement && node.openingElement.name && node.openingElement.name.name) { + return node.openingElement.name.name; + } + return null; + }, + + /** + * Getting the first JSX element's name. + * @param {object} node + * @returns {string | null} + */ + getNameOfWrappedComponent(node) { + if (node.length < 1) { + return null; + } + const body = node[0].body; + if (!body) { + return null; + } + if (body.type === 'JSXElement') { + return this.getComponentNameFromJSXElement(body); + } + if (body.type === 'BlockStatement') { + const jsxElement = body.body.find((item) => item.type === 'ReturnStatement'); + return jsxElement + && jsxElement.argument + && this.getComponentNameFromJSXElement(jsxElement.argument); + } + return null; + }, + + /** + * Get the list of names of components created till now + * @returns {string | boolean} + */ + getDetectedComponents() { + const list = components.list(); + return values(list).filter((val) => { + if (val.node.type === 'ClassDeclaration') { + return true; + } + if ( + val.node.type === 'ArrowFunctionExpression' + && val.node.parent + && val.node.parent.type === 'VariableDeclarator' + && val.node.parent.id + ) { + return true; + } + return false; + }).map((val) => { + if (val.node.type === 'ArrowFunctionExpression') return val.node.parent.id.name; + return val.node.id && val.node.id.name; + }); + }, + + /** + * It will check whether memo/forwardRef is wrapping existing component or + * creating a new one. + * @param {object} node + * @returns {boolean} + */ + nodeWrapsComponent(node) { + const childComponent = this.getNameOfWrappedComponent(node.arguments); + const componentList = this.getDetectedComponents(); + return !!childComponent && arrayIncludes(componentList, childComponent); + }, + + isPragmaComponentWrapper(node) { + if (!astUtil.isCallExpression(node)) { + return false; + } + + return wrapperFunctions.some((wrapperFunction) => { + if (node.callee.type === 'MemberExpression') { + return wrapperFunction.object + && wrapperFunction.object === node.callee.object.name + && wrapperFunction.property === node.callee.property.name + && !this.nodeWrapsComponent(node); + } + return wrapperFunction.property === node.callee.name + && (!wrapperFunction.object + // Functions coming from the current pragma need special handling + || (wrapperFunction.object === pragma && this.isDestructuredFromPragmaImport(node, node.callee.name)) + ); + }); + }, + + /** + * Find a return statement in the current node + * + * @param {ASTNode} node The AST node being checked + */ + findReturnStatement: astUtil.findReturnStatement, + + /** + * Get the parent component node from the current scope + * @param {ASTNode} node + * + * @returns {ASTNode} component node, null if we are not in a component + */ + getParentComponent(node) { + return ( + componentUtil.getParentES6Component(context, node) + || componentUtil.getParentES5Component(context, node) + || utils.getParentStatelessComponent(node) + ); + }, + + /** + * @param {ASTNode} node + * @returns {boolean} + */ + isInAllowedPositionForComponent(node) { + switch (node.parent.type) { + case 'VariableDeclarator': + case 'AssignmentExpression': + case 'Property': + case 'ReturnStatement': + case 'ExportDefaultDeclaration': + case 'ArrowFunctionExpression': { + return true; + } + case 'SequenceExpression': { + return utils.isInAllowedPositionForComponent(node.parent) + && node === node.parent.expressions[node.parent.expressions.length - 1]; + } + default: + return false; + } + }, + + /** + * Get node if node is a stateless component, or node.parent in cases like + * `React.memo` or `React.forwardRef`. Otherwise returns `undefined`. + * @param {ASTNode} node + * @returns {ASTNode | undefined} + */ + getStatelessComponent(node) { + const parent = node.parent; + if ( + node.type === 'FunctionDeclaration' + && (!node.id || isFirstLetterCapitalized(node.id.name)) + && utils.isReturningJSXOrNull(node) + ) { + return node; + } + + if (node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression') { + const isPropertyAssignment = parent.type === 'AssignmentExpression' + && parent.left.type === 'MemberExpression'; + const isModuleExportsAssignment = isPropertyAssignment + && parent.left.object.name === 'module' + && parent.left.property.name === 'exports'; + + if (node.parent.type === 'ExportDefaultDeclaration') { + if (utils.isReturningJSX(node)) { + return node; + } + return undefined; + } + + if (node.parent.type === 'VariableDeclarator' && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(node.parent.id.name)) { + return node; + } + return undefined; + } + + // case: const any = () => { return (props) => null } + // case: const any = () => (props) => null + if ( + (node.parent.type === 'ReturnStatement' || (node.parent.type === 'ArrowFunctionExpression' && node.parent.expression)) + && !utils.isReturningJSX(node) + ) { + return undefined; + } + + // case: any = () => { return => null } + // case: any = () => null + if (node.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(node.parent.left.name)) { + return node; + } + return undefined; + } + + // case: any = () => () => null + if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(node.parent.parent.left.name)) { + return node; + } + return undefined; + } + + // case: { any: () => () => null } + if (node.parent.type === 'ArrowFunctionExpression' && node.parent.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(node.parent.parent.key.name)) { + return node; + } + return undefined; + } + + // case: any = function() {return function() {return null;};} + if (node.parent.type === 'ReturnStatement') { + if (isFirstLetterCapitalized(node.id && node.id.name)) { + return node; + } + const functionExpr = node.parent.parent.parent; + if (functionExpr.parent.type === 'AssignmentExpression' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(functionExpr.parent.left.name)) { + return node; + } + return undefined; + } + } + + // case: { any: function() {return function() {return null;};} } + if (node.parent.type === 'ReturnStatement') { + const functionExpr = node.parent.parent.parent; + if (functionExpr.parent.type === 'Property' && !isPropertyAssignment && utils.isReturningJSXOrNull(node)) { + if (isFirstLetterCapitalized(functionExpr.parent.key.name)) { + return node; + } + return undefined; + } + } + + // for case abc = { [someobject.somekey]: props => { ... return not-jsx } } + if ( + node.parent + && node.parent.key + && node.parent.key.type === 'MemberExpression' + && !utils.isReturningJSX(node) + && !utils.isReturningOnlyNull(node) + ) { + return undefined; + } + + if ( + node.parent.type === 'Property' && ( + (node.parent.method && !node.parent.computed) // case: { f() { return ... } } + || (!node.id && !node.parent.computed) // case: { f: () => ... } + ) + ) { + if ( + isFirstLetterCapitalized(node.parent.key.name) + && utils.isReturningJSX(node) + ) { + return node; + } + return undefined; + } + + // Case like `React.memo(() => <>)` or `React.forwardRef(...)` + const pragmaComponentWrapper = utils.getPragmaComponentWrapper(node); + if (pragmaComponentWrapper && utils.isReturningJSXOrNull(node)) { + return pragmaComponentWrapper; + } + + if (!(utils.isInAllowedPositionForComponent(node) && utils.isReturningJSXOrNull(node))) { + return undefined; + } + + if (utils.isParentComponentNotStatelessComponent(node)) { + return undefined; + } + + if (node.id) { + return isFirstLetterCapitalized(node.id.name) ? node : undefined; + } + + if ( + isPropertyAssignment + && !isModuleExportsAssignment + && !isFirstLetterCapitalized(parent.left.property.name) + ) { + return undefined; + } + + if (parent.type === 'Property' && utils.isReturningOnlyNull(node)) { + return undefined; + } + + return node; + } + + return undefined; + }, + + /** + * Get the parent stateless component node from the current scope + * + * @param {ASTNode} node The AST node being checked + * @returns {ASTNode} component node, null if we are not in a component + */ + getParentStatelessComponent(node) { + let scope = getScope(context, node); + while (scope) { + const statelessComponent = utils.getStatelessComponent(scope.block); + if (statelessComponent) { + return statelessComponent; + } + scope = scope.upper; + } + return null; + }, + + /** + * Get the related component from a node + * + * @param {ASTNode} node The AST node being checked (must be a MemberExpression). + * @returns {ASTNode | null} component node, null if we cannot find the component + */ + getRelatedComponent(node) { + let i; + let j; + let k; + let l; + let componentNode; + // Get the component path + const componentPath = []; + let nodeTemp = node; + while (nodeTemp) { + if (nodeTemp.property && nodeTemp.property.type === 'Identifier') { + componentPath.push(nodeTemp.property.name); + } + if (nodeTemp.object && nodeTemp.object.type === 'Identifier') { + componentPath.push(nodeTemp.object.name); + } + nodeTemp = nodeTemp.object; + } + componentPath.reverse(); + const componentName = componentPath.slice(0, componentPath.length - 1).join('.'); + + // Find the variable in the current scope + const variableName = componentPath.shift(); + if (!variableName) { + return null; + } + const variableInScope = variableUtil.getVariableFromContext(context, node, variableName); + if (!variableInScope) { + return null; + } + + // Try to find the component using variable references + variableInScope.references.some((ref) => { + let refId = ref.identifier; + if (refId.parent && refId.parent.type === 'MemberExpression') { + refId = refId.parent; + } + if (getText(context, refId) !== componentName) { + return false; + } + if (refId.type === 'MemberExpression') { + componentNode = refId.parent.right; + } else if ( + refId.parent + && refId.parent.type === 'VariableDeclarator' + && refId.parent.init + && refId.parent.init.type !== 'Identifier' + ) { + componentNode = refId.parent.init; + } + return true; + }); + + if (componentNode) { + // Return the component + return components.add(componentNode, 1); + } + + // Try to find the component using variable declarations + const defs = variableInScope.defs; + const defInScope = defs.find((def) => ( + def.type === 'ClassName' + || def.type === 'FunctionName' + || def.type === 'Variable' + )); + if (!defInScope || !defInScope.node) { + return null; + } + componentNode = defInScope.node.init || defInScope.node; + + // Traverse the node properties to the component declaration + for (i = 0, j = componentPath.length; i < j; i++) { + if (!componentNode.properties) { + continue; // eslint-disable-line no-continue + } + for (k = 0, l = componentNode.properties.length; k < l; k++) { + if (componentNode.properties[k].key && componentNode.properties[k].key.name === componentPath[i]) { + componentNode = componentNode.properties[k]; + break; + } + } + if (!componentNode || !componentNode.value) { + return null; + } + componentNode = componentNode.value; + } + + // Return the component + return components.add(componentNode, 1); + }, + + isParentComponentNotStatelessComponent(node) { + return !!( + node.parent + && node.parent.key + && node.parent.key.type === 'Identifier' + // custom component functions must start with a capital letter (returns false otherwise) + && node.parent.key.name.charAt(0) === node.parent.key.name.charAt(0).toLowerCase() + // react render function cannot have params + && !!(node.params || []).length + ); + }, + + /** + * Identify whether a node (CallExpression) is a call to a React hook + * + * @param {ASTNode} node The AST node being searched. (expects CallExpression) + * @param {('useCallback'|'useContext'|'useDebugValue'|'useEffect'|'useImperativeHandle'|'useLayoutEffect'|'useMemo'|'useReducer'|'useRef'|'useState')[]} [expectedHookNames] React hook names to which search is limited. + * @returns {boolean} True if the node is a call to a React hook + */ + isReactHookCall(node, expectedHookNames) { + if (!astUtil.isCallExpression(node)) { + return false; + } + + const defaultReactImports = components.getDefaultReactImports(); + const namedReactImports = components.getNamedReactImports(); + + const defaultReactImportName = defaultReactImports + && defaultReactImports[0] + && defaultReactImports[0].local.name; + const reactHookImportSpecifiers = namedReactImports + && namedReactImports.filter((specifier) => USE_HOOK_PREFIX_REGEX.test(specifier.imported.name)); + const reactHookImportNames = reactHookImportSpecifiers + && fromEntries(reactHookImportSpecifiers.map((specifier) => [specifier.local.name, specifier.imported.name])); + + const isPotentialReactHookCall = defaultReactImportName + && node.callee.type === 'MemberExpression' + && node.callee.object.type === 'Identifier' + && node.callee.object.name === defaultReactImportName + && node.callee.property.type === 'Identifier' + && node.callee.property.name.match(USE_HOOK_PREFIX_REGEX); + + const isPotentialHookCall = reactHookImportNames + && node.callee.type === 'Identifier' + && node.callee.name.match(USE_HOOK_PREFIX_REGEX); + + const scope = (isPotentialReactHookCall || isPotentialHookCall) && getScope(context, node); + + const reactResolvedDefs = isPotentialReactHookCall + && scope.references + && scope.references.find( + (reference) => reference.identifier.name === defaultReactImportName + ).resolved.defs; + + const isReactShadowed = isPotentialReactHookCall && reactResolvedDefs + && reactResolvedDefs.some((reactDef) => reactDef.type !== 'ImportBinding'); + + const potentialHookReference = isPotentialHookCall + && scope.references + && scope.references.find( + (reference) => reactHookImportNames[reference.identifier.name] + ); + + const hookResolvedDefs = potentialHookReference && potentialHookReference.resolved.defs; + const localHookName = ( + isPotentialReactHookCall + && node.callee.property.name + ) || ( + isPotentialHookCall + && potentialHookReference + && node.callee.name + ); + const isHookShadowed = isPotentialHookCall + && hookResolvedDefs + && hookResolvedDefs.some( + (hookDef) => hookDef.name.name === localHookName + && hookDef.type !== 'ImportBinding' + ); + + const isHookCall = (isPotentialReactHookCall && !isReactShadowed) + || (isPotentialHookCall && localHookName && !isHookShadowed); + + if (!isHookCall) { + return false; + } + + if (!expectedHookNames) { + return true; + } + + return arrayIncludes( + expectedHookNames, + (reactHookImportNames && reactHookImportNames[localHookName]) || localHookName + ); + }, + }; + + // Component detection instructions + const detectionInstructions = { + CallExpression(node) { + if (!utils.isPragmaComponentWrapper(node)) { + return; + } + if (node.arguments.length > 0 && astUtil.isFunctionLikeExpression(node.arguments[0])) { + components.add(node, 2); + } + }, + + ClassExpression(node) { + if (!componentUtil.isES6Component(node, context)) { + return; + } + components.add(node, 2); + }, + + ClassDeclaration(node) { + if (!componentUtil.isES6Component(node, context)) { + return; + } + components.add(node, 2); + }, + + ObjectExpression(node) { + if (!componentUtil.isES5Component(node, context)) { + return; + } + components.add(node, 2); + }, + + FunctionExpression(node) { + if (node.async && node.generator) { + components.add(node, 0); + return; + } + + const component = utils.getStatelessComponent(node); + if (!component) { + return; + } + components.add(component, 2); + }, + + FunctionDeclaration(node) { + if (node.async && node.generator) { + components.add(node, 0); + return; + } + + const cNode = utils.getStatelessComponent(node); + if (!cNode) { + return; + } + components.add(cNode, 2); + }, + + ArrowFunctionExpression(node) { + const component = utils.getStatelessComponent(node); + if (!component) { + return; + } + components.add(component, 2); + }, + + ThisExpression(node) { + const component = utils.getParentStatelessComponent(node); + if (!component || !/Function/.test(component.type) || !node.parent.property) { + return; + } + // Ban functions accessing a property on a ThisExpression + components.add(node, 0); + }, + }; + + // Detect React import specifiers + const reactImportInstructions = { + ImportDeclaration(node) { + const isReactImported = node.source.type === 'Literal' && node.source.value === 'react'; + if (!isReactImported) { + return; + } + + node.specifiers.forEach((specifier) => { + if (specifier.type === 'ImportDefaultSpecifier') { + components.addDefaultReactImport(specifier); + } + if (specifier.type === 'ImportSpecifier') { + components.addNamedReactImport(specifier); + } + }); + }, + }; + + const ruleInstructions = rule(context, components, utils); + const propTypesInstructions = propTypesUtil(context, components, utils); + const usedPropTypesInstructions = usedPropTypesUtil(context, components, utils); + const defaultPropsInstructions = defaultPropsUtil(context, components, utils); + + const mergedRule = mergeRules([ + detectionInstructions, + propTypesInstructions, + usedPropTypesInstructions, + defaultPropsInstructions, + reactImportInstructions, + ruleInstructions, + ]); + + return mergedRule; +} + +module.exports = Object.assign(Components, { + detect(rule) { + return componentRule.bind(this, rule); + }, +}); diff --git a/node_modules/eslint-plugin-react/lib/util/annotations.d.ts b/node_modules/eslint-plugin-react/lib/util/annotations.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..1be0cb6c6a32d6a2dd7fe679fc1975ad7b9e6934 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/annotations.d.ts @@ -0,0 +1,8 @@ +/** + * Checks if we are declaring a `props` argument with a flow type annotation. + * @param {ASTNode} node The AST node being checked. + * @param {Object} context + * @returns {boolean} True if the node is a type annotated props declaration, false if not. + */ +export function isAnnotatedFunctionPropsDeclaration(node: ASTNode, context: any): boolean; +//# sourceMappingURL=annotations.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map b/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..e6aaca1afbad8d2fbe53cd813fa570333ffede99 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/annotations.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"annotations.d.ts","sourceRoot":"","sources":["annotations.js"],"names":[],"mappings":"AAUA;;;;;GAKG;AACH,0DAJW,OAAO,iBAEL,OAAO,CAenB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/annotations.js b/node_modules/eslint-plugin-react/lib/util/annotations.js new file mode 100644 index 0000000000000000000000000000000000000000..2bc1437ff1d8ffd62632170376c4307d1d408ebe --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/annotations.js @@ -0,0 +1,34 @@ +/** + * @fileoverview Utility functions for type annotation detection. + * @author Yannick Croissant + * @author Vitor Balocco + */ + +'use strict'; + +const getFirstTokens = require('./eslint').getFirstTokens; + +/** + * Checks if we are declaring a `props` argument with a flow type annotation. + * @param {ASTNode} node The AST node being checked. + * @param {Object} context + * @returns {boolean} True if the node is a type annotated props declaration, false if not. + */ +function isAnnotatedFunctionPropsDeclaration(node, context) { + if (!node || !node.params || !node.params.length) { + return false; + } + + const typeNode = node.params[0].type === 'AssignmentPattern' ? node.params[0].left : node.params[0]; + + const tokens = getFirstTokens(context, typeNode, 2); + const isAnnotated = typeNode.typeAnnotation; + const isDestructuredProps = typeNode.type === 'ObjectPattern'; + const isProps = tokens[0].value === 'props' || (tokens[1] && tokens[1].value === 'props'); + + return (isAnnotated && (isDestructuredProps || isProps)); +} + +module.exports = { + isAnnotatedFunctionPropsDeclaration, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/ast.d.ts b/node_modules/eslint-plugin-react/lib/util/ast.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f12bf678fa34e1e1c9c9181028005722b92d3717 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/ast.d.ts @@ -0,0 +1,136 @@ +/** + * Find a return statement in the current node + * + * @param {ASTNode} node The AST node being checked + * @returns {ASTNode | false} + */ +export function findReturnStatement(node: ASTNode): ASTNode | false; +/** + * Get properties for a given AST node + * @param {ASTNode} node The AST node being checked. + * @returns {Array} Properties array. + */ +export function getComponentProperties(node: ASTNode): any[]; +/** + * Gets the first node in a line from the initial node, excluding whitespace. + * @param {Object} context The node to check + * @param {ASTNode} node The node to check + * @return {ASTNode} the first node in the line + */ +export function getFirstNodeInLine(context: any, node: ASTNode): ASTNode; +/** + * Retrieve the name of a key node + * @param {Context} context The AST node with the key. + * @param {any} node The AST node with the key. + * @return {string | undefined} the name of the key + */ +export function getKeyValue(context: Context, node: any): string | undefined; +/** + * Get properties name + * @param {Object} node - Property. + * @returns {string} Property name. + */ +export function getPropertyName(node: any): string; +/** + * Get node with property's name + * @param {Object} node - Property. + * @returns {Object} Property name node. + */ +export function getPropertyNameNode(node: any): any; +/** + * Check if we are in a class constructor + * @param {Context} context + * @param {ASTNode} node The AST node being checked. + * @return {boolean} + */ +export function inConstructor(context: Context, node: ASTNode): boolean; +/** + * Checks if a node is being assigned a value: props.bar = 'bar' + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} + */ +export function isAssignmentLHS(node: ASTNode): boolean; +/** + * Matcher used to check whether given node is a `CallExpression` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `CallExpression`, false if not + */ +export function isCallExpression(node: ASTNode): boolean; +/** + * Checks if the node is a class. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a class + */ +export function isClass(node: ASTNode): boolean; +/** + * Checks if the node is a function. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function + */ +export function isFunction(node: ASTNode): boolean; +/** + * Checks if node is a function declaration or expression or arrow function. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function-like + */ +export function isFunctionLike(node: ASTNode): boolean; +/** + * Checks if the node is a function or arrow function expression. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function-like expression + */ +export function isFunctionLikeExpression(node: ASTNode): boolean; +/** + * Checks if the node is the first in its line, excluding whitespace. + * @param {Object} context The node to check + * @param {ASTNode} node The node to check + * @return {boolean} true if it's the first node in its line + */ +export function isNodeFirstInLine(context: any, node: ASTNode): boolean; +/** + * Checks if a node is surrounded by parenthesis. + * + * @param {object} context - Context from the rule + * @param {ASTNode} node - Node to be checked + * @returns {boolean} + */ +export function isParenthesized(context: object, node: ASTNode): boolean; +export function isTSAsExpression(node: any): boolean; +export function isTSFunctionType(node: any): boolean; +export function isTSInterfaceDeclaration(node: any): boolean; +export function isTSInterfaceHeritage(node: any): boolean; +export function isTSIntersectionType(node: any): boolean; +export function isTSParenthesizedType(node: any): boolean; +export function isTSTypeAliasDeclaration(node: any): boolean; +export function isTSTypeAnnotation(node: any): boolean; +export function isTSTypeDeclaration(node: any): boolean; +export function isTSTypeLiteral(node: any): boolean; +export function isTSTypeParameterInstantiation(node: any): boolean; +export function isTSTypeQuery(node: any): boolean; +export function isTSTypeReference(node: any): boolean; +/** + * Wrapper for estraverse.traverse + * + * @param {ASTNode} ASTnode The AST node being checked + * @param {Object} visitor Visitor Object for estraverse + */ +export function traverse(ASTnode: ASTNode, visitor: any): void; +/** + * Helper function for traversing "returns" (return statements or the + * returned expression in the case of an arrow function) of a function + * + * @param {ASTNode} ASTNode The AST node being checked + * @param {Context} context The context of `ASTNode`. + * @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn + * Function to execute for each returnStatement found + * @returns {undefined} + */ +export function traverseReturns(ASTNode: ASTNode, context: Context, onReturn: (returnValue: ASTNode, breakTraverse: () => void) => void): undefined; +/** + * Extracts the expression node that is wrapped inside a TS type assertion + * + * @param {ASTNode} node - potential TS node + * @returns {ASTNode} - unwrapped expression node + */ +export function unwrapTSAsExpression(node: ASTNode): ASTNode; +//# sourceMappingURL=ast.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map b/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..99dc25eec8c8f083e6c5b177f9eb836ea9f5039f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/ast.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["ast.js"],"names":[],"mappings":"AAkDA;;;;;GAKG;AACH,0CAHW,OAAO,GACL,OAAO,GAAG,KAAK,CAa3B;AA0GD;;;;GAIG;AACH,6CAHW,OAAO,SAajB;AAED;;;;;GAKG;AACH,uDAHW,OAAO,GACN,OAAO,CAgBlB;AA8ED;;;;;GAKG;AACH,qCAJW,OAAO,QACP,GAAG,GACF,MAAM,GAAG,SAAS,CAqB7B;AAtJD;;;;GAIG;AACH,4CAFa,MAAM,CAKlB;AA3BD;;;;GAIG;AACH,oDAYC;AAoGD;;;;;GAKG;AACH,uCAJW,OAAO,QACP,OAAO,GACN,OAAO,CAYlB;AAuDD;;;;GAIG;AACH,sCAHW,OAAO,GACL,OAAO,CAQnB;AAMD;;;;GAIG;AACH,uCAHW,OAAO,GACL,OAAO,CAInB;AAxGD;;;;GAIG;AACH,8BAHW,OAAO,GACN,OAAO,CAIlB;AAzBD;;;;GAIG;AACH,iCAHW,OAAO,GACN,OAAO,CAIlB;AAED;;;;GAIG;AACH,qCAHW,OAAO,GACN,OAAO,CAIlB;AAzBD;;;;GAIG;AACH,+CAHW,OAAO,GACN,OAAO,CAIlB;AApBD;;;;;GAKG;AACH,sDAHW,OAAO,GACN,OAAO,CAOlB;AA4FD;;;;;;GAMG;AACH,yCAJW,MAAM,QACN,OAAO,GACL,OAAO,CAUnB;AAeD,qDAEC;AAqFD,qDAIC;AAtCD,6DAOC;AAbD,0DAIC;AAVD,yDAIC;AAoCD,0DAIC;AAbD,6DAOC;AAlDD,uDAIC;AA6BD,wDAQC;AAnCD,oDAIC;AA4DD,mEAIC;AAVD,kDAIC;AA1ED,sDAIC;AAtWD;;;;;GAKG;AACH,kCAHW,OAAO,sBAgBjB;AAqCD;;;;;;;;;GASG;AACH,yCANW,OAAO,WACP,OAAO,0BACO,OAAO,iBAAiB,MAAM,IAAI,KAAK,IAAI,GAEvD,SAAS,CAgErB;AAwND;;;;;GAKG;AACH,2CAHW,OAAO,GACL,OAAO,CAInB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/ast.js b/node_modules/eslint-plugin-react/lib/util/ast.js new file mode 100644 index 0000000000000000000000000000000000000000..452b1a1ef2819fff07b9a99e449c856325219890 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/ast.js @@ -0,0 +1,483 @@ +/** + * @fileoverview Utility functions for AST + */ + +'use strict'; + +const estraverse = require('estraverse'); +const eslintUtil = require('./eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; +// const pragmaUtil = require('./pragma'); + +/** + * Wrapper for estraverse.traverse + * + * @param {ASTNode} ASTnode The AST node being checked + * @param {Object} visitor Visitor Object for estraverse + */ +function traverse(ASTnode, visitor) { + const opts = Object.assign({}, { + fallback(node) { + return Object.keys(node).filter((key) => key === 'children' || key === 'argument'); + }, + }, visitor); + + opts.keys = Object.assign({}, visitor.keys, { + JSXElement: ['children'], + JSXFragment: ['children'], + }); + + estraverse.traverse(ASTnode, opts); +} + +function loopNodes(nodes) { + for (let i = nodes.length - 1; i >= 0; i--) { + if (nodes[i].type === 'ReturnStatement') { + return nodes[i]; + } + if (nodes[i].type === 'SwitchStatement') { + const j = nodes[i].cases.length - 1; + if (j >= 0) { + return loopNodes(nodes[i].cases[j].consequent); + } + } + } + return false; +} + +/** + * Find a return statement in the current node + * + * @param {ASTNode} node The AST node being checked + * @returns {ASTNode | false} + */ +function findReturnStatement(node) { + if ( + (!node.value || !node.value.body || !node.value.body.body) + && (!node.body || !node.body.body) + ) { + return false; + } + + const bodyNodes = node.value ? node.value.body.body : node.body.body; + + return loopNodes(bodyNodes); +} + +// eslint-disable-next-line valid-jsdoc -- valid-jsdoc cannot parse function types. +/** + * Helper function for traversing "returns" (return statements or the + * returned expression in the case of an arrow function) of a function + * + * @param {ASTNode} ASTNode The AST node being checked + * @param {Context} context The context of `ASTNode`. + * @param {(returnValue: ASTNode, breakTraverse: () => void) => void} onReturn + * Function to execute for each returnStatement found + * @returns {undefined} + */ +function traverseReturns(ASTNode, context, onReturn) { + const nodeType = ASTNode.type; + + if (nodeType === 'ReturnStatement') { + onReturn(ASTNode.argument, () => {}); + return; + } + + if (nodeType === 'ArrowFunctionExpression' && ASTNode.expression) { + onReturn(ASTNode.body, () => {}); + return; + } + + /* TODO: properly warn on React.forwardRefs having typo properties + if (astUtil.isCallExpression(ASTNode)) { + const callee = ASTNode.callee; + const pragma = pragmaUtil.getFromContext(context); + if ( + callee.type === 'MemberExpression' + && callee.object.type === 'Identifier' + && callee.object.name === pragma + && callee.property.type === 'Identifier' + && callee.property.name === 'forwardRef' + && ASTNode.arguments.length > 0 + ) { + return enterFunc(ASTNode.arguments[0]); + } + return; + } + */ + + if ( + nodeType !== 'FunctionExpression' + && nodeType !== 'FunctionDeclaration' + && nodeType !== 'ArrowFunctionExpression' + && nodeType !== 'MethodDefinition' + ) { + return; + } + + traverse(ASTNode.body, { + enter(node) { + const breakTraverse = () => { + this.break(); + }; + switch (node.type) { + case 'ReturnStatement': + this.skip(); + onReturn(node.argument, breakTraverse); + return; + case 'BlockStatement': + case 'IfStatement': + case 'ForStatement': + case 'WhileStatement': + case 'SwitchStatement': + case 'SwitchCase': + return; + default: + this.skip(); + } + }, + }); +} + +/** + * Get node with property's name + * @param {Object} node - Property. + * @returns {Object} Property name node. + */ +function getPropertyNameNode(node) { + if ( + node.key + || node.type === 'MethodDefinition' + || node.type === 'Property' + ) { + return node.key; + } + if (node.type === 'MemberExpression') { + return node.property; + } + return null; +} + +/** + * Get properties name + * @param {Object} node - Property. + * @returns {string} Property name. + */ +function getPropertyName(node) { + const nameNode = getPropertyNameNode(node); + return nameNode ? nameNode.name : ''; +} + +/** + * Get properties for a given AST node + * @param {ASTNode} node The AST node being checked. + * @returns {Array} Properties array. + */ +function getComponentProperties(node) { + switch (node.type) { + case 'ClassDeclaration': + case 'ClassExpression': + return node.body.body; + case 'ObjectExpression': + return node.properties; + default: + return []; + } +} + +/** + * Gets the first node in a line from the initial node, excluding whitespace. + * @param {Object} context The node to check + * @param {ASTNode} node The node to check + * @return {ASTNode} the first node in the line + */ +function getFirstNodeInLine(context, node) { + const sourceCode = getSourceCode(context); + let token = node; + let lines; + do { + token = sourceCode.getTokenBefore(token); + lines = token.type === 'JSXText' + ? token.value.split('\n') + : null; + } while ( + token.type === 'JSXText' + && /^\s*$/.test(lines[lines.length - 1]) + ); + return token; +} + +/** + * Checks if the node is the first in its line, excluding whitespace. + * @param {Object} context The node to check + * @param {ASTNode} node The node to check + * @return {boolean} true if it's the first node in its line + */ +function isNodeFirstInLine(context, node) { + const token = getFirstNodeInLine(context, node); + const startLine = node.loc.start.line; + const endLine = token ? token.loc.end.line : -1; + return startLine !== endLine; +} + +/** + * Checks if the node is a function or arrow function expression. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function-like expression + */ +function isFunctionLikeExpression(node) { + return node.type === 'FunctionExpression' || node.type === 'ArrowFunctionExpression'; +} + +/** + * Checks if the node is a function. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function + */ +function isFunction(node) { + return node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration'; +} + +/** + * Checks if node is a function declaration or expression or arrow function. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a function-like + */ +function isFunctionLike(node) { + return node.type === 'FunctionDeclaration' || isFunctionLikeExpression(node); +} + +/** + * Checks if the node is a class. + * @param {ASTNode} node The node to check + * @return {boolean} true if it's a class + */ +function isClass(node) { + return node.type === 'ClassDeclaration' || node.type === 'ClassExpression'; +} + +/** + * Check if we are in a class constructor + * @param {Context} context + * @param {ASTNode} node The AST node being checked. + * @return {boolean} + */ +function inConstructor(context, node) { + let scope = getScope(context, node); + while (scope) { + // @ts-ignore + if (scope.block && scope.block.parent && scope.block.parent.kind === 'constructor') { + return true; + } + scope = scope.upper; + } + return false; +} + +/** + * Removes quotes from around an identifier. + * @param {string} string the identifier to strip + * @returns {string} + */ +function stripQuotes(string) { + return string.replace(/^'|'$/g, ''); +} + +/** + * Retrieve the name of a key node + * @param {Context} context The AST node with the key. + * @param {any} node The AST node with the key. + * @return {string | undefined} the name of the key + */ +function getKeyValue(context, node) { + if (node.type === 'ObjectTypeProperty') { + const tokens = getFirstTokens(context, node, 2); + return (tokens[0].value === '+' || tokens[0].value === '-' + ? tokens[1].value + : stripQuotes(tokens[0].value) + ); + } + if (node.type === 'GenericTypeAnnotation') { + return node.id.name; + } + if (node.type === 'ObjectTypeAnnotation') { + return; + } + const key = node.key || node.argument; + if (!key) { + return; + } + return key.type === 'Identifier' ? key.name : key.value; +} + +/** + * Checks if a node is surrounded by parenthesis. + * + * @param {object} context - Context from the rule + * @param {ASTNode} node - Node to be checked + * @returns {boolean} + */ +function isParenthesized(context, node) { + const sourceCode = getSourceCode(context); + const previousToken = sourceCode.getTokenBefore(node); + const nextToken = sourceCode.getTokenAfter(node); + + return !!previousToken && !!nextToken + && previousToken.value === '(' && previousToken.range[1] <= node.range[0] + && nextToken.value === ')' && nextToken.range[0] >= node.range[1]; +} + +/** + * Checks if a node is being assigned a value: props.bar = 'bar' + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} + */ +function isAssignmentLHS(node) { + return ( + node.parent + && node.parent.type === 'AssignmentExpression' + && node.parent.left === node + ); +} + +function isTSAsExpression(node) { + return node && node.type === 'TSAsExpression'; +} + +/** + * Matcher used to check whether given node is a `CallExpression` + * @param {ASTNode} node The AST node + * @returns {boolean} True if node is a `CallExpression`, false if not + */ +function isCallExpression(node) { + return node && node.type === 'CallExpression'; +} + +/** + * Extracts the expression node that is wrapped inside a TS type assertion + * + * @param {ASTNode} node - potential TS node + * @returns {ASTNode} - unwrapped expression node + */ +function unwrapTSAsExpression(node) { + return isTSAsExpression(node) ? node.expression : node; +} + +function isTSTypeReference(node) { + if (!node) return false; + + return node.type === 'TSTypeReference'; +} + +function isTSTypeAnnotation(node) { + if (!node) { return false; } + + return node.type === 'TSTypeAnnotation'; +} + +function isTSTypeLiteral(node) { + if (!node) { return false; } + + return node.type === 'TSTypeLiteral'; +} + +function isTSIntersectionType(node) { + if (!node) { return false; } + + return node.type === 'TSIntersectionType'; +} + +function isTSInterfaceHeritage(node) { + if (!node) { return false; } + + return node.type === 'TSInterfaceHeritage'; +} + +function isTSInterfaceDeclaration(node) { + if (!node) { return false; } + + return (node.type === 'ExportNamedDeclaration' && node.declaration + ? node.declaration.type + : node.type + ) === 'TSInterfaceDeclaration'; +} + +function isTSTypeDeclaration(node) { + if (!node) { return false; } + + const nodeToCheck = node.type === 'ExportNamedDeclaration' && node.declaration + ? node.declaration + : node; + + return nodeToCheck.type === 'VariableDeclaration' && nodeToCheck.kind === 'type'; +} + +function isTSTypeAliasDeclaration(node) { + if (!node) { return false; } + + if (node.type === 'ExportNamedDeclaration' && node.declaration) { + return node.declaration.type === 'TSTypeAliasDeclaration' && node.exportKind === 'type'; + } + return node.type === 'TSTypeAliasDeclaration'; +} + +function isTSParenthesizedType(node) { + if (!node) { return false; } + + return node.type === 'TSTypeAliasDeclaration'; +} + +function isTSFunctionType(node) { + if (!node) { return false; } + + return node.type === 'TSFunctionType'; +} + +function isTSTypeQuery(node) { + if (!node) { return false; } + + return node.type === 'TSTypeQuery'; +} + +function isTSTypeParameterInstantiation(node) { + if (!node) { return false; } + + return node.type === 'TSTypeParameterInstantiation'; +} + +module.exports = { + findReturnStatement, + getComponentProperties, + getFirstNodeInLine, + getKeyValue, + getPropertyName, + getPropertyNameNode, + inConstructor, + isAssignmentLHS, + isCallExpression, + isClass, + isFunction, + isFunctionLike, + isFunctionLikeExpression, + isNodeFirstInLine, + isParenthesized, + isTSAsExpression, + isTSFunctionType, + isTSInterfaceDeclaration, + isTSInterfaceHeritage, + isTSIntersectionType, + isTSParenthesizedType, + isTSTypeAliasDeclaration, + isTSTypeAnnotation, + isTSTypeDeclaration, + isTSTypeLiteral, + isTSTypeParameterInstantiation, + isTSTypeQuery, + isTSTypeReference, + traverse, + traverseReturns, + unwrapTSAsExpression, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts b/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..af65a90cd42c7f7d91b131f99becaaba8ccd2bb8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts @@ -0,0 +1,46 @@ +/** + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +export function isES5Component(node: ASTNode, context: Context): boolean; +/** + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +export function isES6Component(node: ASTNode, context: Context): boolean; +/** + * Get the parent ES5 component node from the current scope + * @param {Context} context + * @param {ASTNode} node + * @returns {ASTNode|null} + */ +export function getParentES5Component(context: Context, node: ASTNode): ASTNode | null; +/** + * Get the parent ES6 component node from the current scope + * @param {Context} context + * @param {ASTNode} node + * @returns {ASTNode | null} + */ +export function getParentES6Component(context: Context, node: ASTNode): ASTNode | null; +/** + * Check if the node is explicitly declared as a descendant of a React Component + * @param {any} node + * @param {Context} context + * @returns {boolean} + */ +export function isExplicitComponent(node: any, context: Context): boolean; +/** + * Checks if a component extends React.PureComponent + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +export function isPureComponent(node: ASTNode, context: Context): boolean; +/** + * @param {ASTNode} node + * @returns {boolean} + */ +export function isStateMemberExpression(node: ASTNode): boolean; +//# sourceMappingURL=componentUtil.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map b/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..11fa8ea550732bfe91490cdb9449046515200ad6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/componentUtil.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"componentUtil.d.ts","sourceRoot":"","sources":["componentUtil.js"],"names":[],"mappings":"AAiCA;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAyCD;;;;GAIG;AACH,qCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAmBnB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAC,IAAI,CAaxB;AAED;;;;;GAKG;AACH,+CAJW,OAAO,QACP,OAAO,GACL,OAAO,GAAG,IAAI,CAY1B;AAlGD;;;;;GAKG;AACH,0CAJW,GAAG,WACH,OAAO,GACL,OAAO,CAiCnB;AA+DD;;;;;GAKG;AACH,sCAJW,OAAO,WACP,OAAO,GACL,OAAO,CAQnB;AAED;;;GAGG;AACH,8CAHW,OAAO,GACL,OAAO,CAMnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/componentUtil.js b/node_modules/eslint-plugin-react/lib/util/componentUtil.js new file mode 100644 index 0000000000000000000000000000000000000000..8bd2c720de580ba7268fea15d2d6199eaafbc4c3 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/componentUtil.js @@ -0,0 +1,190 @@ +'use strict'; + +const doctrine = require('doctrine'); +const pragmaUtil = require('./pragma'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + +// eslint-disable-next-line valid-jsdoc +/** + * @template {(_: object) => any} T + * @param {T} fn + * @returns {T} + */ +function memoize(fn) { + const cache = new WeakMap(); + // @ts-ignore + return function memoizedFn(arg) { + const cachedValue = cache.get(arg); + if (cachedValue !== undefined) { + return cachedValue; + } + const v = fn(arg); + cache.set(arg, v); + return v; + }; +} + +const getPragma = memoize(pragmaUtil.getFromContext); +const getCreateClass = memoize(pragmaUtil.getCreateClassFromContext); + +/** + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +function isES5Component(node, context) { + const pragma = getPragma(context); + const createClass = getCreateClass(context); + + if (!node.parent || !node.parent.callee) { + return false; + } + const callee = node.parent.callee; + // React.createClass({}) + if (callee.type === 'MemberExpression') { + return callee.object.name === pragma && callee.property.name === createClass; + } + // createClass({}) + if (callee.type === 'Identifier') { + return callee.name === createClass; + } + return false; +} + +/** + * Check if the node is explicitly declared as a descendant of a React Component + * @param {any} node + * @param {Context} context + * @returns {boolean} + */ +function isExplicitComponent(node, context) { + const sourceCode = getSourceCode(context); + let comment; + // Sometimes the passed node may not have been parsed yet by eslint, and this function call crashes. + // Can be removed when eslint sets "parent" property for all nodes on initial AST traversal: https://github.com/eslint/eslint-scope/issues/27 + // eslint-disable-next-line no-warning-comments + // FIXME: Remove try/catch when https://github.com/eslint/eslint-scope/issues/27 is implemented. + try { + comment = sourceCode.getJSDocComment(node); + } catch (e) { + comment = null; + } + + if (comment === null) { + return false; + } + + let commentAst; + try { + commentAst = doctrine.parse(comment.value, { + unwrap: true, + tags: ['extends', 'augments'], + }); + } catch (e) { + // handle a bug in the archived `doctrine`, see #2596 + return false; + } + + const relevantTags = commentAst.tags.filter((tag) => tag.name === 'React.Component' || tag.name === 'React.PureComponent'); + + return relevantTags.length > 0; +} + +/** + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +function isES6Component(node, context) { + const pragma = getPragma(context); + if (isExplicitComponent(node, context)) { + return true; + } + + if (!node.superClass) { + return false; + } + if (node.superClass.type === 'MemberExpression') { + return node.superClass.object.name === pragma + && /^(Pure)?Component$/.test(node.superClass.property.name); + } + if (node.superClass.type === 'Identifier') { + return /^(Pure)?Component$/.test(node.superClass.name); + } + return false; +} + +/** + * Get the parent ES5 component node from the current scope + * @param {Context} context + * @param {ASTNode} node + * @returns {ASTNode|null} + */ +function getParentES5Component(context, node) { + let scope = getScope(context, node); + while (scope) { + // @ts-ignore + node = scope.block && scope.block.parent && scope.block.parent.parent; + if (node && isES5Component(node, context)) { + return node; + } + scope = scope.upper; + } + return null; +} + +/** + * Get the parent ES6 component node from the current scope + * @param {Context} context + * @param {ASTNode} node + * @returns {ASTNode | null} + */ +function getParentES6Component(context, node) { + let scope = getScope(context, node); + while (scope && scope.type !== 'class') { + scope = scope.upper; + } + node = scope && scope.block; + if (!node || !isES6Component(node, context)) { + return null; + } + return node; +} + +/** + * Checks if a component extends React.PureComponent + * @param {ASTNode} node + * @param {Context} context + * @returns {boolean} + */ +function isPureComponent(node, context) { + const pragma = getPragma(context); + if (node.superClass) { + return new RegExp(`^(${pragma}\\.)?PureComponent$`).test(getText(context, node.superClass)); + } + return false; +} + +/** + * @param {ASTNode} node + * @returns {boolean} + */ +function isStateMemberExpression(node) { + return node.type === 'MemberExpression' + && node.object.type === 'ThisExpression' + && node.property.name === 'state'; +} + +module.exports = { + isES5Component, + isES6Component, + getParentES5Component, + getParentES6Component, + isExplicitComponent, + isPureComponent, + isStateMemberExpression, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts b/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..a0dfc377c79e28700d166596fd011a824a45c9b0 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts @@ -0,0 +1,8 @@ +declare function _exports(context: any, components: any, utils: any): { + MemberExpression(node: any): void; + MethodDefinition(node: any): void; + 'ClassProperty, PropertyDefinition'(node: any): void; + ObjectExpression(node: any): void; +}; +export = _exports; +//# sourceMappingURL=defaultProps.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map b/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..5d743f695c04c262c073d663e23ceaff1dc89b82 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/defaultProps.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"defaultProps.d.ts","sourceRoot":"","sources":["defaultProps.js"],"names":[],"mappings":"AAgBiB;;;;;EA0PhB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/defaultProps.js b/node_modules/eslint-plugin-react/lib/util/defaultProps.js new file mode 100644 index 0000000000000000000000000000000000000000..b0e88844d530472bd19eb22f72a734b6b7bc24bd --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/defaultProps.js @@ -0,0 +1,267 @@ +/** + * @fileoverview Common defaultProps detection functionality. + */ + +'use strict'; + +const fromEntries = require('object.fromentries'); +const astUtil = require('./ast'); +const componentUtil = require('./componentUtil'); +const propsUtil = require('./props'); +const variableUtil = require('./variable'); +const propWrapperUtil = require('./propWrapper'); +const getText = require('./eslint').getText; + +const QUOTES_REGEX = /^["']|["']$/g; + +module.exports = function defaultPropsInstructions(context, components, utils) { + /** + * Try to resolve the node passed in to a variable in the current scope. If the node passed in is not + * an Identifier, then the node is simply returned. + * @param {ASTNode} node The node to resolve. + * @returns {ASTNode|null} Return null if the value could not be resolved, ASTNode otherwise. + */ + function resolveNodeValue(node) { + if (node.type === 'Identifier') { + return variableUtil.findVariableByName(context, node, node.name); + } + if ( + astUtil.isCallExpression(node) + && propWrapperUtil.isPropWrapperFunction(context, node.callee.name) + && node.arguments && node.arguments[0] + ) { + return resolveNodeValue(node.arguments[0]); + } + return node; + } + + /** + * Extracts a DefaultProp from an ObjectExpression node. + * @param {ASTNode} objectExpression ObjectExpression node. + * @returns {Object|string} Object representation of a defaultProp, to be consumed by + * `addDefaultPropsToComponent`, or string "unresolved", if the defaultProps + * from this ObjectExpression can't be resolved. + */ + function getDefaultPropsFromObjectExpression(objectExpression) { + const hasSpread = objectExpression.properties.find((property) => property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement'); + + if (hasSpread) { + return 'unresolved'; + } + + return objectExpression.properties.map((defaultProp) => ({ + name: getText(context, defaultProp.key).replace(QUOTES_REGEX, ''), + node: defaultProp, + })); + } + + /** + * Marks a component's DefaultProps declaration as "unresolved". A component's DefaultProps is + * marked as "unresolved" if we cannot safely infer the values of its defaultProps declarations + * without risking false negatives. + * @param {Object} component The component to mark. + * @returns {void} + */ + function markDefaultPropsAsUnresolved(component) { + components.set(component.node, { + defaultProps: 'unresolved', + }); + } + + /** + * Adds defaultProps to the component passed in. + * @param {ASTNode} component The component to add the defaultProps to. + * @param {Object[]|'unresolved'} defaultProps defaultProps to add to the component or the string "unresolved" + * if this component has defaultProps that can't be resolved. + * @returns {void} + */ + function addDefaultPropsToComponent(component, defaultProps) { + // Early return if this component's defaultProps is already marked as "unresolved". + if (component.defaultProps === 'unresolved') { + return; + } + + if (defaultProps === 'unresolved') { + markDefaultPropsAsUnresolved(component); + return; + } + + const defaults = component.defaultProps || {}; + const newDefaultProps = Object.assign( + {}, + defaults, + fromEntries(defaultProps.map((prop) => [prop.name, prop])) + ); + + components.set(component.node, { + defaultProps: newDefaultProps, + }); + } + + return { + MemberExpression(node) { + const isDefaultProp = propsUtil.isDefaultPropsDeclaration(node); + + if (!isDefaultProp) { + return; + } + + // find component this defaultProps belongs to + const component = utils.getRelatedComponent(node); + if (!component) { + return; + } + + // e.g.: + // MyComponent.propTypes = { + // foo: React.PropTypes.string.isRequired, + // bar: React.PropTypes.string + // }; + // + // or: + // + // MyComponent.propTypes = myPropTypes; + if (node.parent.type === 'AssignmentExpression') { + const expression = resolveNodeValue(node.parent.right); + if (!expression || expression.type !== 'ObjectExpression') { + // If a value can't be found, we mark the defaultProps declaration as "unresolved", because + // we should ignore this component and not report any errors for it, to avoid false-positives + // with e.g. external defaultProps declarations. + if (isDefaultProp) { + markDefaultPropsAsUnresolved(component); + } + + return; + } + + addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression)); + + return; + } + + // e.g.: + // MyComponent.propTypes.baz = React.PropTypes.string; + if (node.parent.type === 'MemberExpression' && node.parent.parent + && node.parent.parent.type === 'AssignmentExpression') { + addDefaultPropsToComponent(component, [{ + name: node.parent.property.name, + node: node.parent.parent, + }]); + } + }, + + // e.g.: + // class Hello extends React.Component { + // static get defaultProps() { + // return { + // name: 'Dean' + // }; + // } + // render() { + // return
    Hello {this.props.name}
    ; + // } + // } + MethodDefinition(node) { + if (!node.static || node.kind !== 'get') { + return; + } + + if (!propsUtil.isDefaultPropsDeclaration(node)) { + return; + } + + // find component this propTypes/defaultProps belongs to + const component = components.get(componentUtil.getParentES6Component(context, node)); + if (!component) { + return; + } + + const returnStatement = utils.findReturnStatement(node); + if (!returnStatement) { + return; + } + + const expression = resolveNodeValue(returnStatement.argument); + if (!expression || expression.type !== 'ObjectExpression') { + return; + } + + addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression)); + }, + + // e.g.: + // class Greeting extends React.Component { + // render() { + // return ( + //

    Hello, {this.props.foo} {this.props.bar}

    + // ); + // } + // static defaultProps = { + // foo: 'bar', + // bar: 'baz' + // }; + // } + 'ClassProperty, PropertyDefinition'(node) { + if (!(node.static && node.value)) { + return; + } + + const propName = astUtil.getPropertyName(node); + const isDefaultProp = propName === 'defaultProps' || propName === 'getDefaultProps'; + + if (!isDefaultProp) { + return; + } + + // find component this propTypes/defaultProps belongs to + const component = components.get(componentUtil.getParentES6Component(context, node)); + if (!component) { + return; + } + + const expression = resolveNodeValue(node.value); + if (!expression || expression.type !== 'ObjectExpression') { + return; + } + + addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(expression)); + }, + + // e.g.: + // React.createClass({ + // render: function() { + // return
    {this.props.foo}
    ; + // }, + // getDefaultProps: function() { + // return { + // foo: 'default' + // }; + // } + // }); + ObjectExpression(node) { + // find component this propTypes/defaultProps belongs to + const component = componentUtil.isES5Component(node, context) && components.get(node); + if (!component) { + return; + } + + // Search for the proptypes declaration + node.properties.forEach((property) => { + if (property.type === 'ExperimentalSpreadProperty' || property.type === 'SpreadElement') { + return; + } + + const isDefaultProp = propsUtil.isDefaultPropsDeclaration(property); + + if (isDefaultProp && property.value.type === 'FunctionExpression') { + const returnStatement = utils.findReturnStatement(property); + if (!returnStatement || returnStatement.argument.type !== 'ObjectExpression') { + return; + } + + addDefaultPropsToComponent(component, getDefaultPropsFromObjectExpression(returnStatement.argument)); + } + }); + }, + }; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts b/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..ccc89a4950b5121ad14482ce1e82bacdafe6f6f1 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts @@ -0,0 +1,3 @@ +export = docsUrl; +declare function docsUrl(ruleName: any): string; +//# sourceMappingURL=docsUrl.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map b/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..6588409d88c150f7988ad2e3fc52c792a2dc62ee --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/docsUrl.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"docsUrl.d.ts","sourceRoot":"","sources":["docsUrl.js"],"names":[],"mappings":";AAEA,gDAEC"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/docsUrl.js b/node_modules/eslint-plugin-react/lib/util/docsUrl.js new file mode 100644 index 0000000000000000000000000000000000000000..62961cf958066aa2e4c5e25759f6dfa01629e01b --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/docsUrl.js @@ -0,0 +1,7 @@ +'use strict'; + +function docsUrl(ruleName) { + return `https://github.com/jsx-eslint/eslint-plugin-react/tree/master/docs/rules/${ruleName}.md`; +} + +module.exports = docsUrl; diff --git a/node_modules/eslint-plugin-react/lib/util/error.d.ts b/node_modules/eslint-plugin-react/lib/util/error.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..ff3bad7953b33707c6609d12fbc2f574bd571973 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/error.d.ts @@ -0,0 +1,7 @@ +export = error; +/** + * Logs out a message if there is no format option set. + * @param {string} message - Message to log. + */ +declare function error(message: string): void; +//# sourceMappingURL=error.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/error.d.ts.map b/node_modules/eslint-plugin-react/lib/util/error.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..b5f169e3e26e6e9fd2cf3fc7f9bb7b4fff907e70 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/error.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["error.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,gCAFW,MAAM,QAOhB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/error.js b/node_modules/eslint-plugin-react/lib/util/error.js new file mode 100644 index 0000000000000000000000000000000000000000..cca6e6c13f53c7cad685d41933a734ae5ca24467 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/error.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Logs out a message if there is no format option set. + * @param {string} message - Message to log. + */ +function error(message) { + if (!/=-(f|-format)=/.test(process.argv.join('='))) { + // eslint-disable-next-line no-console + console.error(message); + } +} + +module.exports = error; diff --git a/node_modules/eslint-plugin-react/lib/util/eslint.d.ts b/node_modules/eslint-plugin-react/lib/util/eslint.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f1a24ff96f02cc0f3ddea85f25a0a6a1ddf6275f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/eslint.d.ts @@ -0,0 +1,7 @@ +export function getAncestors(context: any, node: any): any; +export function getFirstTokens(context: any, node: any, count: any): any; +export function getScope(context: any, node: any): any; +export function getSourceCode(context: any): any; +export function getText(context: any, ...args: any[]): any; +export function markVariableAsUsed(name: any, node: any, context: any): any; +//# sourceMappingURL=eslint.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map b/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..d1764ac768c86c7466b694e6923fab485f88ba59 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/eslint.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"eslint.d.ts","sourceRoot":"","sources":["eslint.js"],"names":[],"mappings":"AAMA,2DAGC;AAkBD,yEAGC;AAnBD,uDAOC;AAhBD,iDAEC;AA4BD,2DAIC;AAhBD,4EAKC"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/eslint.js b/node_modules/eslint-plugin-react/lib/util/eslint.js new file mode 100644 index 0000000000000000000000000000000000000000..79a0537f3b4f52b04b29d46e89ce00f6da8fe579 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/eslint.js @@ -0,0 +1,46 @@ +'use strict'; + +function getSourceCode(context) { + return context.getSourceCode ? context.getSourceCode() : context.sourceCode; +} + +function getAncestors(context, node) { + const sourceCode = getSourceCode(context); + return sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors(); +} + +function getScope(context, node) { + const sourceCode = getSourceCode(context); + if (sourceCode.getScope) { + return sourceCode.getScope(node); + } + + return context.getScope(); +} + +function markVariableAsUsed(name, node, context) { + const sourceCode = getSourceCode(context); + return sourceCode.markVariableAsUsed + ? sourceCode.markVariableAsUsed(name, node) + : context.markVariableAsUsed(name); +} + +function getFirstTokens(context, node, count) { + const sourceCode = getSourceCode(context); + return sourceCode.getFirstTokens ? sourceCode.getFirstTokens(node, count) : context.getFirstTokens(node, count); +} + +function getText(context) { + const sourceCode = getSourceCode(context); + const args = Array.prototype.slice.call(arguments, 1); + return sourceCode.getText ? sourceCode.getText.apply(sourceCode, args) : context.getSource.apply(context, args); +} + +module.exports = { + getAncestors, + getFirstTokens, + getScope, + getSourceCode, + getText, + markVariableAsUsed, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9c8b9fceaf5331afa026e48c933518eb7204cf8f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts @@ -0,0 +1,8 @@ +export = getTokenBeforeClosingBracket; +/** + * Find the token before the closing bracket. + * @param {ASTNode} node - The JSX element node. + * @returns {Token} The token before the closing bracket. + */ +declare function getTokenBeforeClosingBracket(node: ASTNode): Token; +//# sourceMappingURL=getTokenBeforeClosingBracket.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..0e36c60d0181998bc9d0c829e572e48d9e56d9fd --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"getTokenBeforeClosingBracket.d.ts","sourceRoot":"","sources":["getTokenBeforeClosingBracket.js"],"names":[],"mappings":";AAEA;;;;GAIG;AACH,oDAHW,OAAO,GACL,KAAK,CAQjB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js new file mode 100644 index 0000000000000000000000000000000000000000..8bd277a24936cb1c52629e558307a490da593144 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/getTokenBeforeClosingBracket.js @@ -0,0 +1,16 @@ +'use strict'; + +/** + * Find the token before the closing bracket. + * @param {ASTNode} node - The JSX element node. + * @returns {Token} The token before the closing bracket. + */ +function getTokenBeforeClosingBracket(node) { + const attributes = node.attributes; + if (!attributes || attributes.length === 0) { + return node.name; + } + return attributes[attributes.length - 1]; +} + +module.exports = getTokenBeforeClosingBracket; diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts b/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..7436da3ff757a23e630819f89274fb3d83192fe1 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts @@ -0,0 +1,3 @@ +declare function _exports(node: ASTNode): boolean; +export = _exports; +//# sourceMappingURL=isCreateContext.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map b/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..4650ed51784cd8b34d12aa0498d3bf392fc0fdf8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateContext.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"isCreateContext.d.ts","sourceRoot":"","sources":["isCreateContext.js"],"names":[],"mappings":"AASiB,gCAHN,OAAO,GACL,OAAO,CA8CnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateContext.js b/node_modules/eslint-plugin-react/lib/util/isCreateContext.js new file mode 100644 index 0000000000000000000000000000000000000000..fd73ecc31dabb296d1d393bad97bb866da80499a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateContext.js @@ -0,0 +1,54 @@ +'use strict'; + +const astUtil = require('./ast'); + +/** + * Checks if the node is a React.createContext call + * @param {ASTNode} node - The AST node being checked. + * @returns {boolean} - True if node is a React.createContext call, false if not. + */ +module.exports = function isCreateContext(node) { + if ( + node.init + && node.init.callee + ) { + if ( + astUtil.isCallExpression(node.init) + && node.init.callee.name === 'createContext' + ) { + return true; + } + + if ( + node.init.callee.type === 'MemberExpression' + && node.init.callee.property + && node.init.callee.property.name === 'createContext' + ) { + return true; + } + } + + if ( + node.expression + && node.expression.type === 'AssignmentExpression' + && node.expression.operator === '=' + && astUtil.isCallExpression(node.expression.right) + && node.expression.right.callee + ) { + const right = node.expression.right; + + if (right.callee.name === 'createContext') { + return true; + } + + if ( + right.callee.type === 'MemberExpression' + && right.callee.property + && right.callee.property.name === 'createContext' + ) { + return true; + } + } + + return false; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts b/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..dcb2f06c9191f4b2d62233e1af733442e8c058b4 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts @@ -0,0 +1,3 @@ +declare function _exports(context: Context, node: ASTNode): boolean; +export = _exports; +//# sourceMappingURL=isCreateElement.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map b/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..2e9ad83754d65c314f19d77597d5b25ce48183ca --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateElement.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"isCreateElement.d.ts","sourceRoot":"","sources":["isCreateElement.js"],"names":[],"mappings":"AAWiB,mCAJN,OAAO,QACP,OAAO,GACL,OAAO,CAwBnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isCreateElement.js b/node_modules/eslint-plugin-react/lib/util/isCreateElement.js new file mode 100644 index 0000000000000000000000000000000000000000..be93cec5f7090d77765c780193e4aff2b114678a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isCreateElement.js @@ -0,0 +1,34 @@ +'use strict'; + +const pragmaUtil = require('./pragma'); +const isDestructuredFromPragmaImport = require('./isDestructuredFromPragmaImport'); + +/** + * Checks if the node is a createElement call + * @param {Context} context - The AST node being checked. + * @param {ASTNode} node - The AST node being checked. + * @returns {boolean} - True if node is a createElement call object literal, False if not. +*/ +module.exports = function isCreateElement(context, node) { + if (!node.callee) { + return false; + } + + if ( + node.callee.type === 'MemberExpression' + && node.callee.property.name === 'createElement' + && node.callee.object + && node.callee.object.name === pragmaUtil.getFromContext(context) + ) { + return true; + } + + if ( + node.callee.name === 'createElement' + && isDestructuredFromPragmaImport(context, node, 'createElement') + ) { + return true; + } + + return false; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..2d7d1aed6af5bec15741d68f94f247ab63f82676 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts @@ -0,0 +1,3 @@ +declare function _exports(context: Context, node: ASTNode, variable: string, ...args: any[]): boolean; +export = _exports; +//# sourceMappingURL=isDestructuredFromPragmaImport.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..ed12410f853aa7f6a72bfbb4e1a8323fb632ad8d --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"isDestructuredFromPragmaImport.d.ts","sourceRoot":"","sources":["isDestructuredFromPragmaImport.js"],"names":[],"mappings":"AAciB,mCALN,OAAO,QACP,OAAO,YACP,MAAM,mBACJ,OAAO,CAmEnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js new file mode 100644 index 0000000000000000000000000000000000000000..122fb545af479f2c62f10eb9277a3d6d776b8af2 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isDestructuredFromPragmaImport.js @@ -0,0 +1,80 @@ +'use strict'; + +const astUtil = require('./ast'); +const pragmaUtil = require('./pragma'); +const variableUtil = require('./variable'); + +/** + * Check if variable is destructured from pragma import + * + * @param {Context} context eslint context + * @param {ASTNode} node The AST node to check + * @param {string} variable The variable name to check + * @returns {boolean} True if createElement is destructured from the pragma + */ +module.exports = function isDestructuredFromPragmaImport(context, node, variable) { + const pragma = pragmaUtil.getFromContext(context); + const variableInScope = variableUtil.getVariableFromContext(context, node, variable); + if (variableInScope) { + const latestDef = variableUtil.getLatestVariableDefinition(variableInScope); + if (latestDef) { + // check if latest definition is a variable declaration: 'variable = value' + if (latestDef.node.type === 'VariableDeclarator' && latestDef.node.init) { + // check for: 'variable = pragma.variable' + if ( + latestDef.node.init.type === 'MemberExpression' + && latestDef.node.init.object.type === 'Identifier' + && latestDef.node.init.object.name === pragma + ) { + return true; + } + // check for: '{variable} = pragma' + if ( + latestDef.node.init.type === 'Identifier' + && latestDef.node.init.name === pragma + ) { + return true; + } + + // "require('react')" + let requireExpression = null; + + // get "require('react')" from: "{variable} = require('react')" + if (astUtil.isCallExpression(latestDef.node.init)) { + requireExpression = latestDef.node.init; + } + // get "require('react')" from: "variable = require('react').variable" + if ( + !requireExpression + && latestDef.node.init.type === 'MemberExpression' + && astUtil.isCallExpression(latestDef.node.init.object) + ) { + requireExpression = latestDef.node.init.object; + } + + // check proper require. + if ( + requireExpression + && requireExpression.callee + && requireExpression.callee.name === 'require' + && requireExpression.arguments[0] + && requireExpression.arguments[0].value === pragma.toLocaleLowerCase() + ) { + return true; + } + + return false; + } + + // latest definition is an import declaration: import {} from 'react' + if ( + latestDef.parent + && latestDef.parent.type === 'ImportDeclaration' + && latestDef.parent.source.value === pragma.toLocaleLowerCase() + ) { + return true; + } + } + } + return false; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e611b06c77397b6e716be95cc32c62c6f57cd61a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts @@ -0,0 +1,3 @@ +declare function _exports(word: string): boolean; +export = _exports; +//# sourceMappingURL=isFirstLetterCapitalized.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..54562c6150bd2021100de1e331cd05019f6d59cd --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"isFirstLetterCapitalized.d.ts","sourceRoot":"","sources":["isFirstLetterCapitalized.js"],"names":[],"mappings":"AAOiB,gCAHN,MAAM,GACJ,OAAO,CAQnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js new file mode 100644 index 0000000000000000000000000000000000000000..3d6252168f25523dbf4867167cb9e9646bc2b12f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/isFirstLetterCapitalized.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Check if the first letter of a string is capitalized. + * @param {string} word String to check + * @returns {boolean} True if first letter is capitalized. + */ +module.exports = function isFirstLetterCapitalized(word) { + if (!word) { + return false; + } + const firstLetter = word.replace(/^_+/, '').charAt(0); + return firstLetter.toUpperCase() === firstLetter; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/jsx.d.ts b/node_modules/eslint-plugin-react/lib/util/jsx.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..f4cf074a69638c0700494c83e37ab02c64092ef9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/jsx.d.ts @@ -0,0 +1,51 @@ +/** + * Checks if a node represents a DOM element according to React. + * @param {object} node - JSXOpeningElement to check. + * @returns {boolean} Whether or not the node corresponds to a DOM element. + */ +export function isDOMComponent(node: object): boolean; +/** + * Test whether a JSXElement is a fragment + * @param {JSXElement} node + * @param {string} reactPragma + * @param {string} fragmentPragma + * @returns {boolean} + */ +export function isFragment(node: JSXElement, reactPragma: string, fragmentPragma: string): boolean; +/** + * Checks if a node represents a JSX element or fragment. + * @param {object} node - node to check. + * @returns {boolean} Whether or not the node if a JSX element or fragment. + */ +export function isJSX(node: object): boolean; +/** + * Check if node is like `key={...}` as in `` + * @param {ASTNode} node + * @returns {boolean} + */ +export function isJSXAttributeKey(node: ASTNode): boolean; +/** + * Check if value has only whitespaces + * @param {unknown} value + * @returns {boolean} + */ +export function isWhiteSpaces(value: unknown): boolean; +/** + * Check if the node is returning JSX or null + * + * @param {Context} context The context of `ASTNode`. + * @param {ASTNode} ASTnode The AST node being checked + * @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases + * @param {boolean} [ignoreNull] If true, null return values will be ignored + * @returns {boolean} True if the node is returning JSX or null, false if not + */ +export function isReturningJSX(context: Context, ASTnode: ASTNode, strict?: boolean, ignoreNull?: boolean): boolean; +/** + * Check if the node is returning only null values + * + * @param {ASTNode} ASTnode The AST node being checked + * @param {Context} context The context of `ASTNode`. + * @returns {boolean} True if the node is returning only null values + */ +export function isReturningOnlyNull(ASTnode: ASTNode, context: Context): boolean; +//# sourceMappingURL=jsx.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map b/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..ae317433064a25fb436e7c2b6509ab904b7f27f9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/jsx.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"jsx.d.ts","sourceRoot":"","sources":["jsx.js"],"names":[],"mappings":"AAgBA;;;;GAIG;AACH,qCAHW,MAAM,GACJ,OAAO,CAKnB;AAED;;;;;;GAMG;AACH,iCALW,UAAU,eACV,MAAM,kBACN,MAAM,GACJ,OAAO,CAsBnB;AAED;;;;GAIG;AACH,4BAHW,MAAM,GACJ,OAAO,CAInB;AAED;;;;GAIG;AACH,wCAHW,OAAO,GACL,OAAO,CAOnB;AAED;;;;GAIG;AACH,qCAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;;;;;GAQG;AACH,wCANW,OAAO,WACP,OAAO,WACP,OAAO,eACP,OAAO,GACL,OAAO,CAgDnB;AAED;;;;;;GAMG;AACH,6CAJW,OAAO,WACP,OAAO,GACL,OAAO,CAsCnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/jsx.js b/node_modules/eslint-plugin-react/lib/util/jsx.js new file mode 100644 index 0000000000000000000000000000000000000000..db3413745a0e00d2cce6245db7abfe0001205fb3 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/jsx.js @@ -0,0 +1,196 @@ +/** + * @fileoverview Utility functions for JSX + */ + +'use strict'; + +const elementType = require('jsx-ast-utils/elementType'); + +const astUtil = require('./ast'); +const isCreateElement = require('./isCreateElement'); +const variableUtil = require('./variable'); + +// See https://github.com/babel/babel/blob/ce420ba51c68591e057696ef43e028f41c6e04cd/packages/babel-types/src/validators/react/isCompatTag.js +// for why we only test for the first character +const COMPAT_TAG_REGEX = /^[a-z]/; + +/** + * Checks if a node represents a DOM element according to React. + * @param {object} node - JSXOpeningElement to check. + * @returns {boolean} Whether or not the node corresponds to a DOM element. + */ +function isDOMComponent(node) { + const name = elementType(node); + return COMPAT_TAG_REGEX.test(name); +} + +/** + * Test whether a JSXElement is a fragment + * @param {JSXElement} node + * @param {string} reactPragma + * @param {string} fragmentPragma + * @returns {boolean} + */ +function isFragment(node, reactPragma, fragmentPragma) { + const name = node.openingElement.name; + + // + if (name.type === 'JSXIdentifier' && name.name === fragmentPragma) { + return true; + } + + // + if ( + name.type === 'JSXMemberExpression' + && name.object.type === 'JSXIdentifier' + && name.object.name === reactPragma + && name.property.type === 'JSXIdentifier' + && name.property.name === fragmentPragma + ) { + return true; + } + + return false; +} + +/** + * Checks if a node represents a JSX element or fragment. + * @param {object} node - node to check. + * @returns {boolean} Whether or not the node if a JSX element or fragment. + */ +function isJSX(node) { + return node && ['JSXElement', 'JSXFragment'].indexOf(node.type) >= 0; +} + +/** + * Check if node is like `key={...}` as in `` + * @param {ASTNode} node + * @returns {boolean} + */ +function isJSXAttributeKey(node) { + return node.type === 'JSXAttribute' + && node.name + && node.name.type === 'JSXIdentifier' + && node.name.name === 'key'; +} + +/** + * Check if value has only whitespaces + * @param {unknown} value + * @returns {boolean} + */ +function isWhiteSpaces(value) { + return typeof value === 'string' ? /^\s*$/.test(value) : false; +} + +/** + * Check if the node is returning JSX or null + * + * @param {Context} context The context of `ASTNode`. + * @param {ASTNode} ASTnode The AST node being checked + * @param {boolean} [strict] If true, in a ternary condition the node must return JSX in both cases + * @param {boolean} [ignoreNull] If true, null return values will be ignored + * @returns {boolean} True if the node is returning JSX or null, false if not + */ +function isReturningJSX(context, ASTnode, strict, ignoreNull) { + const isJSXValue = (node) => { + if (!node) { + return false; + } + switch (node.type) { + case 'ConditionalExpression': + if (strict) { + return isJSXValue(node.consequent) && isJSXValue(node.alternate); + } + return isJSXValue(node.consequent) || isJSXValue(node.alternate); + case 'LogicalExpression': + if (strict) { + return isJSXValue(node.left) && isJSXValue(node.right); + } + return isJSXValue(node.left) || isJSXValue(node.right); + case 'SequenceExpression': + return isJSXValue(node.expressions[node.expressions.length - 1]); + case 'JSXElement': + case 'JSXFragment': + return true; + case 'CallExpression': + return isCreateElement(context, node); + case 'Literal': + if (!ignoreNull && node.value === null) { + return true; + } + return false; + case 'Identifier': { + const variable = variableUtil.findVariableByName(context, node, node.name); + return isJSX(variable); + } + default: + return false; + } + }; + + let found = false; + astUtil.traverseReturns(ASTnode, context, (node, breakTraverse) => { + if (isJSXValue(node)) { + found = true; + breakTraverse(); + } + }); + + return found; +} + +/** + * Check if the node is returning only null values + * + * @param {ASTNode} ASTnode The AST node being checked + * @param {Context} context The context of `ASTNode`. + * @returns {boolean} True if the node is returning only null values + */ +function isReturningOnlyNull(ASTnode, context) { + let found = false; + let foundSomethingElse = false; + astUtil.traverseReturns(ASTnode, context, (node) => { + // Traverse return statement + astUtil.traverse(node, { + enter(childNode) { + const setFound = () => { + found = true; + this.skip(); + }; + const setFoundSomethingElse = () => { + foundSomethingElse = true; + this.skip(); + }; + switch (childNode.type) { + case 'ReturnStatement': + break; + case 'ConditionalExpression': + if (childNode.consequent.value === null && childNode.alternate.value === null) { + setFound(); + } + break; + case 'Literal': + if (childNode.value === null) { + setFound(); + } + break; + default: + setFoundSomethingElse(); + } + }, + }); + }); + + return found && !foundSomethingElse; +} + +module.exports = { + isDOMComponent, + isFragment, + isJSX, + isJSXAttributeKey, + isWhiteSpaces, + isReturningJSX, + isReturningOnlyNull, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..bc3d81e0f48898f313af8737e5cca08887597afe --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts @@ -0,0 +1,6 @@ +declare const _exports: { + instance: string[]; + static: string[]; +}; +export = _exports; +//# sourceMappingURL=lifecycleMethods.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..886b55d59757fcdebc2d48ccb3696b4b5b7a4143 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"lifecycleMethods.d.ts","sourceRoot":"","sources":["lifecycleMethods.js"],"names":[],"mappings":""} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js new file mode 100644 index 0000000000000000000000000000000000000000..8670eadc84d0fd97ede68b725b34a4a375d600e6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/lifecycleMethods.js @@ -0,0 +1,30 @@ +/** + * @fileoverview lifecycle methods + * @author Tan Nguyen + */ + +'use strict'; + +module.exports = { + instance: [ + 'getDefaultProps', + 'getInitialState', + 'getChildContext', + 'componentWillMount', + 'UNSAFE_componentWillMount', + 'componentDidMount', + 'componentWillReceiveProps', + 'UNSAFE_componentWillReceiveProps', + 'shouldComponentUpdate', + 'componentWillUpdate', + 'UNSAFE_componentWillUpdate', + 'getSnapshotBeforeUpdate', + 'componentDidUpdate', + 'componentDidCatch', + 'componentWillUnmount', + 'render', + ], + static: [ + 'getDerivedStateFromProps', + ], +}; diff --git a/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts b/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..6211d0971eb52a8e0daaa0ddef38760c565e0f73 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts @@ -0,0 +1,3 @@ +export function getFormComponents(context: any): Map; +export function getLinkComponents(context: any): Map; +//# sourceMappingURL=linkComponents.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map b/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..3c08a43ab063280d232cac95d8c378cabbe9fd15 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/linkComponents.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"linkComponents.d.ts","sourceRoot":"","sources":["linkComponents.js"],"names":[],"mappings":"AAmBA,+DAWC;AAED,+DAWC"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/linkComponents.js b/node_modules/eslint-plugin-react/lib/util/linkComponents.js new file mode 100644 index 0000000000000000000000000000000000000000..181ed377b88fe3362e45d2d18943c4bee76f18bb --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/linkComponents.js @@ -0,0 +1,49 @@ +/** + * @fileoverview Utility functions for propWrapperFunctions setting + */ + +'use strict'; + +const iterFrom = require('es-iterator-helpers/Iterator.from'); +const map = require('es-iterator-helpers/Iterator.prototype.map'); + +/** TODO: type {(string | { name: string, linkAttribute: string })[]} */ +/** @type {any} */ +const DEFAULT_LINK_COMPONENTS = ['a']; +const DEFAULT_LINK_ATTRIBUTE = 'href'; + +/** TODO: type {(string | { name: string, formAttribute: string })[]} */ +/** @type {any} */ +const DEFAULT_FORM_COMPONENTS = ['form']; +const DEFAULT_FORM_ATTRIBUTE = 'action'; + +function getFormComponents(context) { + const settings = context.settings || {}; + const formComponents = /** @type {typeof DEFAULT_FORM_COMPONENTS} */ ( + DEFAULT_FORM_COMPONENTS.concat(settings.formComponents || []) + ); + return new Map(map(iterFrom(formComponents), (value) => { + if (typeof value === 'string') { + return [value, [DEFAULT_FORM_ATTRIBUTE]]; + } + return [value.name, [].concat(value.formAttribute)]; + })); +} + +function getLinkComponents(context) { + const settings = context.settings || {}; + const linkComponents = /** @type {typeof DEFAULT_LINK_COMPONENTS} */ ( + DEFAULT_LINK_COMPONENTS.concat(settings.linkComponents || []) + ); + return new Map(map(iterFrom(linkComponents), (value) => { + if (typeof value === 'string') { + return [value, [DEFAULT_LINK_ATTRIBUTE]]; + } + return [value.name, [].concat(value.linkAttribute)]; + })); +} + +module.exports = { + getFormComponents, + getLinkComponents, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/log.d.ts b/node_modules/eslint-plugin-react/lib/util/log.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..0f511b4568466dd8ed77ae82c37fa0babb5d64f3 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/log.d.ts @@ -0,0 +1,7 @@ +export = log; +/** + * Logs out a message if there is no format option set. + * @param {string} message - Message to log. + */ +declare function log(message: string): void; +//# sourceMappingURL=log.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/log.d.ts.map b/node_modules/eslint-plugin-react/lib/util/log.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..7d3c55a0a7f6da65940c036e661bc93d7079ab64 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/log.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"log.d.ts","sourceRoot":"","sources":["log.js"],"names":[],"mappings":";AAEA;;;GAGG;AACH,8BAFW,MAAM,QAOhB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/log.js b/node_modules/eslint-plugin-react/lib/util/log.js new file mode 100644 index 0000000000000000000000000000000000000000..55271e15f2966dd4a8c05a4349e6187a9c7f9053 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/log.js @@ -0,0 +1,14 @@ +'use strict'; + +/** + * Logs out a message if there is no format option set. + * @param {string} message - Message to log. + */ +function log(message) { + if (!/=-(f|-format)=/.test(process.argv.join('='))) { + // eslint-disable-next-line no-console + console.log(message); + } +} + +module.exports = log; diff --git a/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..d898058ce9c8f19e58c4d5217b18f390f281fb21 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts @@ -0,0 +1,3 @@ +declare function _exports(methodName: string, shouldCheckUnsafeCb?: (context: import('eslint').Rule.RuleContext) => boolean): import('eslint').Rule.RuleModule; +export = _exports; +//# sourceMappingURL=makeNoMethodSetStateRule.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..994c1eab9133579ec26cdd8e620991a378caa034 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"makeNoMethodSetStateRule.d.ts","sourceRoot":"","sources":["makeNoMethodSetStateRule.js"],"names":[],"mappings":"AAoDiB,sCAJN,MAAM,kCACI,OAAO,QAAQ,EAAE,IAAI,CAAC,WAAW,KAAK,OAAO,GACrD,OAAO,QAAQ,EAAE,IAAI,CAAC,UAAU,CA+E5C"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js new file mode 100644 index 0000000000000000000000000000000000000000..b6c55d5cf61fac650fb67b1a91d037baf4992e36 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/makeNoMethodSetStateRule.js @@ -0,0 +1,130 @@ +/** + * @fileoverview Prevent usage of setState in lifecycle methods + * @author Yannick Croissant + */ + +'use strict'; + +const findLast = require('array.prototype.findlast'); + +const docsUrl = require('./docsUrl'); +const report = require('./report'); +const getAncestors = require('./eslint').getAncestors; +const testReactVersion = require('./version').testReactVersion; + +// ------------------------------------------------------------------------------ +// Rule Definition +// ------------------------------------------------------------------------------ + +function mapTitle(methodName) { + const map = { + componentDidMount: 'did-mount', + componentDidUpdate: 'did-update', + componentWillUpdate: 'will-update', + }; + const title = map[methodName]; + if (!title) { + throw Error(`No docsUrl for '${methodName}'`); + } + return `no-${title}-set-state`; +} + +const messages = { + noSetState: 'Do not use setState in {{name}}', +}; + +const methodNoopsAsOf = { + componentDidMount: '>= 16.3.0', + componentDidUpdate: '>= 16.3.0', +}; + +function shouldBeNoop(context, methodName) { + return methodName in methodNoopsAsOf + && testReactVersion(context, methodNoopsAsOf[methodName]) + && !testReactVersion(context, '999.999.999'); // for when the version is not specified +} + +// eslint-disable-next-line valid-jsdoc +/** + * @param {string} methodName + * @param {(context: import('eslint').Rule.RuleContext) => boolean} [shouldCheckUnsafeCb] + * @returns {import('eslint').Rule.RuleModule} + */ +module.exports = function makeNoMethodSetStateRule(methodName, shouldCheckUnsafeCb) { + return { + meta: { + docs: { + description: `Disallow usage of setState in ${methodName}`, + category: 'Best Practices', + recommended: false, + url: docsUrl(mapTitle(methodName)), + }, + + messages, + + schema: [{ + enum: ['disallow-in-func'], + }], + }, + + create(context) { + const mode = context.options[0] || 'allow-in-func'; + + function nameMatches(name) { + if (name === methodName) { + return true; + } + + if (typeof shouldCheckUnsafeCb === 'function' && shouldCheckUnsafeCb(context)) { + return name === `UNSAFE_${methodName}`; + } + + return false; + } + + if (shouldBeNoop(context, methodName)) { + return {}; + } + + // -------------------------------------------------------------------------- + // Public + // -------------------------------------------------------------------------- + + return { + CallExpression(node) { + const callee = node.callee; + if ( + callee.type !== 'MemberExpression' + || callee.object.type !== 'ThisExpression' + || !('name' in callee.property) + || callee.property.name !== 'setState' + ) { + return; + } + const ancestors = getAncestors(context, node); + let depth = 0; + findLast(ancestors, (ancestor) => { + // ancestors.some((ancestor) => { + if (/Function(Expression|Declaration)$/.test(ancestor.type)) { + depth += 1; + } + if ( + (ancestor.type !== 'Property' && ancestor.type !== 'MethodDefinition' && ancestor.type !== 'ClassProperty' && ancestor.type !== 'PropertyDefinition') + || !nameMatches(ancestor.key.name) + || (mode !== 'disallow-in-func' && depth > 1) + ) { + return false; + } + report(context, messages.noSetState, 'noSetState', { + node: callee, + data: { + name: ancestor.key.name, + }, + }); + return true; + }); + }, + }; + }, + }; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/message.d.ts b/node_modules/eslint-plugin-react/lib/util/message.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..b7d480927918939921ec05023c1b9984576e716c --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/message.d.ts @@ -0,0 +1,9 @@ +declare function _exports(messageId: any, message: any): { + messageId: any; + message?: undefined; +} | { + message: any; + messageId?: undefined; +}; +export = _exports; +//# sourceMappingURL=message.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/message.d.ts.map b/node_modules/eslint-plugin-react/lib/util/message.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..51592f94d8d9812c9c0600eebee906d940ec1406 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/message.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"message.d.ts","sourceRoot":"","sources":["message.js"],"names":[],"mappings":"AAKiB;;;;;;EAEhB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/message.js b/node_modules/eslint-plugin-react/lib/util/message.js new file mode 100644 index 0000000000000000000000000000000000000000..0c9962a4de876af69a7a9d22686f88b9a7648da4 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/message.js @@ -0,0 +1,8 @@ +'use strict'; + +const semver = require('semver'); +const eslintPkg = require('eslint/package.json'); + +module.exports = function getMessageData(messageId, message) { + return messageId && semver.satisfies(eslintPkg.version, '>= 4.15') ? { messageId } : { message }; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/pragma.d.ts b/node_modules/eslint-plugin-react/lib/util/pragma.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..34ab6eeeb487b0a22d5f37738ac0c2696053cdca --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/pragma.d.ts @@ -0,0 +1,16 @@ +/** + * @param {Context} context + * @returns {string} + */ +export function getCreateClassFromContext(context: Context): string; +/** + * @param {Context} context + * @returns {string} + */ +export function getFragmentFromContext(context: Context): string; +/** + * @param {Context} context + * @returns {string} + */ +export function getFromContext(context: Context): string; +//# sourceMappingURL=pragma.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map b/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..c48ecd220593dbfb49453593672037054eb983fa --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/pragma.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"pragma.d.ts","sourceRoot":"","sources":["pragma.js"],"names":[],"mappings":"AAaA;;;GAGG;AACH,mDAHW,OAAO,GACL,MAAM,CAYlB;AAED;;;GAGG;AACH,gDAHW,OAAO,GACL,MAAM,CAYlB;AAED;;;GAGG;AACH,wCAHW,OAAO,GACL,MAAM,CAqBlB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/pragma.js b/node_modules/eslint-plugin-react/lib/util/pragma.js new file mode 100644 index 0000000000000000000000000000000000000000..be96c189b6aae007ad8fffee52685c708b4bd875 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/pragma.js @@ -0,0 +1,75 @@ +/** + * @fileoverview Utility functions for React pragma configuration + * @author Yannick Croissant + */ + +'use strict'; + +const getSourceCode = require('./eslint').getSourceCode; + +const JSX_ANNOTATION_REGEX = /@jsx\s+([^\s]+)/; +// Does not check for reserved keywords or unicode characters +const JS_IDENTIFIER_REGEX = /^[_$a-zA-Z][_$a-zA-Z0-9]*$/; + +/** + * @param {Context} context + * @returns {string} + */ +function getCreateClassFromContext(context) { + let pragma = 'createReactClass'; + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + if (context.settings.react && context.settings.react.createClass) { + pragma = context.settings.react.createClass; + } + if (!JS_IDENTIFIER_REGEX.test(pragma)) { + throw new Error(`createClass pragma ${pragma} is not a valid function name`); + } + return pragma; +} + +/** + * @param {Context} context + * @returns {string} + */ +function getFragmentFromContext(context) { + let pragma = 'Fragment'; + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + if (context.settings.react && context.settings.react.fragment) { + pragma = context.settings.react.fragment; + } + if (!JS_IDENTIFIER_REGEX.test(pragma)) { + throw new Error(`Fragment pragma ${pragma} is not a valid identifier`); + } + return pragma; +} + +/** + * @param {Context} context + * @returns {string} + */ +function getFromContext(context) { + let pragma = 'React'; + + const sourceCode = getSourceCode(context); + const pragmaNode = sourceCode.getAllComments().find((node) => JSX_ANNOTATION_REGEX.test(node.value)); + + if (pragmaNode) { + const matches = JSX_ANNOTATION_REGEX.exec(pragmaNode.value); + pragma = matches[1].split('.')[0]; + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + } else if (context.settings.react && context.settings.react.pragma) { + pragma = context.settings.react.pragma; + } + + if (!JS_IDENTIFIER_REGEX.test(pragma)) { + console.warn(`React pragma ${pragma} is not a valid identifier`); + return 'React'; + } + return pragma; +} + +module.exports = { + getCreateClassFromContext, + getFragmentFromContext, + getFromContext, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts b/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..c6ad76d279c143b3635ef69dcae458c833a4d755 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts @@ -0,0 +1,20 @@ +declare function _exports(context: any, components: any, utils: any): { + ClassExpression(node: any): void; + ClassDeclaration(node: any): void; + 'ClassProperty, PropertyDefinition'(node: any): void; + ObjectExpression(node: any): void; + FunctionExpression(node: any): void; + ImportDeclaration(node: any): void; + FunctionDeclaration: (node: ASTNode, rootNode: ASTNode) => void; + ArrowFunctionExpression: (node: ASTNode, rootNode: ASTNode) => void; + MemberExpression(node: any): void; + MethodDefinition(node: any): void; + TypeAlias(node: any): void; + TypeParameterDeclaration(node: any): void; + Program(): void; + BlockStatement(): void; + 'BlockStatement:exit'(): void; + 'Program:exit'(): void; +}; +export = _exports; +//# sourceMappingURL=propTypes.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map b/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..c8828840c260ad28675e41f2020d0ce1d0fd21ca --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"propTypes.d.ts","sourceRoot":"","sources":["propTypes.js"],"names":[],"mappings":"AAoGiB;;;;;;;gCA06BJ,OAAO,YAEP,OAAO;oCAFP,OAAO,YAEP,OAAO;;;;;;;;;EAqQnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propTypes.js b/node_modules/eslint-plugin-react/lib/util/propTypes.js new file mode 100644 index 0000000000000000000000000000000000000000..7cec5abbdd51fcfa791762fe5a816fc8dcc2d5f9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypes.js @@ -0,0 +1,1302 @@ +/** + * @fileoverview Common propTypes detection functionality. + */ + +'use strict'; + +const flatMap = require('array.prototype.flatmap'); + +const annotations = require('./annotations'); +const propsUtil = require('./props'); +const variableUtil = require('./variable'); +const testFlowVersion = require('./version').testFlowVersion; +const propWrapperUtil = require('./propWrapper'); +const astUtil = require('./ast'); +const isFirstLetterCapitalized = require('./isFirstLetterCapitalized'); +const eslintUtil = require('./eslint'); + +const getFirstTokens = eslintUtil.getFirstTokens; +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + +/** + * Check if node is function type. + * @param {ASTNode} node + * @returns {boolean} + */ +function isFunctionType(node) { + if (!node) return false; + const nodeType = node.type; + return nodeType === 'FunctionDeclaration' + || nodeType === 'FunctionExpression' + || nodeType === 'ArrowFunctionExpression'; +} + +/** + * Checks if we are declaring a props as a generic type in a flow-annotated class. + * + * @param {ASTNode} node the AST node being checked. + * @returns {boolean} True if the node is a class with generic prop types, false if not. + */ +function isSuperTypeParameterPropsDeclaration(node) { + if (node && (node.type === 'ClassDeclaration' || node.type === 'ClassExpression')) { + const parameters = propsUtil.getSuperTypeArguments(node); + if (parameters && parameters.params.length > 0) { + return true; + } + } + return false; +} + +/** + * Iterates through a properties node, like a customized forEach. + * @param {Object} context Array of properties to iterate. + * @param {Object[]} properties Array of properties to iterate. + * @param {Function} fn Function to call on each property, receives property key + and property value. (key, value) => void + * @param {Function} [handleSpreadFn] Function to call on each ObjectTypeSpreadProperty, receives the + argument + */ +function iterateProperties(context, properties, fn, handleSpreadFn) { + if (properties && properties.length && typeof fn === 'function') { + for (let i = 0, j = properties.length; i < j; i++) { + const node = properties[i]; + const key = astUtil.getKeyValue(context, node); + + if (node.type === 'ObjectTypeSpreadProperty' && typeof handleSpreadFn === 'function') { + handleSpreadFn(node.argument); + } + + const value = node.value; + fn(key, value, node); + } + } +} + +/** + * Checks if a node is inside a class body. + * + * @param {ASTNode} node the AST node being checked. + * @returns {boolean} True if the node has a ClassBody ancestor, false if not. + */ +function isInsideClassBody(node) { + let parent = node.parent; + while (parent) { + if (parent.type === 'ClassBody') { + return true; + } + parent = parent.parent; + } + return false; +} + +function startWithCapitalizedLetter(node) { + return ( + node.parent.type === 'VariableDeclarator' + && !isFirstLetterCapitalized(node.parent.id.name) + ); +} + +module.exports = function propTypesInstructions(context, components, utils) { + // Used to track the type annotations in scope. + // Necessary because babel's scopes do not track type annotations. + let stack = null; + + const classExpressions = []; + const defaults = { customValidators: [] }; + const configuration = Object.assign({}, defaults, context.options[0] || {}); + const customValidators = configuration.customValidators; + const allowedGenericTypes = new Set(['ComponentProps', 'ComponentPropsWithRef', 'ComponentPropsWithoutRef', 'forwardRef', 'ForwardRefRenderFunction', 'VFC', 'VoidFunctionComponent', 'PropsWithChildren', 'SFC', 'StatelessComponent', 'FunctionComponent', 'FC']); + const genericTypeParamIndexWherePropsArePresent = { + ComponentProps: 0, + ComponentPropsWithRef: 0, + ComponentPropsWithoutRef: 0, + ForwardRefRenderFunction: 1, + forwardRef: 1, + VoidFunctionComponent: 0, + VFC: 0, + PropsWithChildren: 0, + SFC: 0, + StatelessComponent: 0, + FunctionComponent: 0, + FC: 0, + }; + const genericReactTypesImport = new Set(); + // import { FC as X } from 'react' -> localToImportedMap = { x: FC } + const localToImportedMap = {}; + + /** + * Returns the full scope. + * @returns {Object} The whole scope. + */ + function typeScope() { + return stack[stack.length - 1]; + } + + /** + * Gets a node from the scope. + * @param {string} key The name of the identifier to access. + * @returns {ASTNode} The ASTNode associated with the given identifier. + */ + function getInTypeScope(key) { + return stack[stack.length - 1][key]; + } + + /** + * Sets the new value in the scope. + * @param {string} key The name of the identifier to access + * @param {ASTNode} value The new value for the identifier. + * @returns {ASTNode} The ASTNode associated with the given identifier. + */ + function setInTypeScope(key, value) { + stack[stack.length - 1][key] = value; + return value; + } + + /** + * Checks if prop should be validated by plugin-react-proptypes + * @param {string} validator Name of validator to check. + * @returns {boolean} True if validator should be checked by custom validator. + */ + function hasCustomValidator(validator) { + return customValidators.indexOf(validator) !== -1; + } + + /* eslint-disable no-use-before-define */ + /** @type {TypeDeclarationBuilders} */ + const typeDeclarationBuilders = { + GenericTypeAnnotation(annotation, parentName, seen) { + if (getInTypeScope(annotation.id.name)) { + return buildTypeAnnotationDeclarationTypes(getInTypeScope(annotation.id.name), parentName, seen); + } + return {}; + }, + + ObjectTypeAnnotation(annotation, parentName, seen) { + let containsUnresolvedObjectTypeSpread = false; + let containsSpread = false; + const containsIndexers = !!annotation.indexers && annotation.indexers.length > 0; + const shapeTypeDefinition = { + type: 'shape', + children: {}, + }; + iterateProperties( + context, + annotation.properties, + (childKey, childValue, propNode) => { + const fullName = [parentName, childKey].join('.'); + if (childKey || childValue) { + const types = buildTypeAnnotationDeclarationTypes(childValue, fullName, seen); + types.fullName = fullName; + types.name = childKey; + types.node = propNode; + types.isRequired = !childValue.optional; + shapeTypeDefinition.children[childKey] = types; + } + }, + (spreadNode) => { + const key = astUtil.getKeyValue(context, spreadNode); + const types = buildTypeAnnotationDeclarationTypes(spreadNode, key, seen); + if (!types.children) { + containsUnresolvedObjectTypeSpread = true; + } else { + Object.assign(shapeTypeDefinition, types.children); + } + containsSpread = true; + } + ); + + // Mark if this shape has spread or an indexer. We will know to consider all props from this shape as having propTypes, + // but still have the ability to detect unused children of this shape. + shapeTypeDefinition.containsUnresolvedSpread = containsUnresolvedObjectTypeSpread; + shapeTypeDefinition.containsIndexers = containsIndexers; + // Deprecated: containsSpread is not used anymore in the codebase, ensure to keep API backward compatibility + shapeTypeDefinition.containsSpread = containsSpread; + + return shapeTypeDefinition; + }, + + UnionTypeAnnotation(annotation, parentName, seen) { + /** @type {UnionTypeDefinition} */ + const unionTypeDefinition = { + type: 'union', + children: annotation.types.map((type) => buildTypeAnnotationDeclarationTypes(type, parentName, seen)), + }; + if (unionTypeDefinition.children.length === 0) { + // no complex type found, simply accept everything + return {}; + } + return unionTypeDefinition; + }, + + ArrayTypeAnnotation(annotation, parentName, seen) { + const fullName = [parentName, '*'].join('.'); + const child = buildTypeAnnotationDeclarationTypes(annotation.elementType, fullName, seen); + child.fullName = fullName; + child.name = '__ANY_KEY__'; + child.node = annotation; + return { + type: 'object', + children: { + __ANY_KEY__: child, + }, + }; + }, + }; + /* eslint-enable no-use-before-define */ + + /** + * Resolve the type annotation for a given node. + * Flow annotations are sometimes wrapped in outer `TypeAnnotation` + * and `NullableTypeAnnotation` nodes which obscure the annotation we're + * interested in. + * This method also resolves type aliases where possible. + * + * @param {ASTNode} node The annotation or a node containing the type annotation. + * @returns {ASTNode} The resolved type annotation for the node. + */ + function resolveTypeAnnotation(node) { + let annotation = (node.left && node.left.typeAnnotation) || node.typeAnnotation || node; + while (annotation && (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation')) { + annotation = annotation.typeAnnotation; + } + if (annotation.type === 'GenericTypeAnnotation' && getInTypeScope(annotation.id.name)) { + return getInTypeScope(annotation.id.name); + } + return annotation; + } + + /** + * Creates the representation of the React props type annotation for the component. + * The representation is used to verify nested used properties. + * @param {ASTNode} annotation Type annotation for the props class property. + * @param {string} parentName + * @param {Set} [seen] + * @return {Object} The representation of the declaration, empty object means + * the property is declared without the need for further analysis. + */ + function buildTypeAnnotationDeclarationTypes(annotation, parentName, seen) { + if (typeof seen === 'undefined') { + // Keeps track of annotations we've already seen to + // prevent problems with recursive types. + seen = new Set(); + } + if (seen.has(annotation)) { + // This must be a recursive type annotation, so just accept anything. + return {}; + } + seen.add(annotation); + + if (annotation.type in typeDeclarationBuilders) { + return typeDeclarationBuilders[annotation.type](annotation, parentName, seen); + } + return {}; + } + + /** + * Marks all props found inside ObjectTypeAnnotation as declared. + * + * Modifies the declaredProperties object + * @param {ASTNode} propTypes + * @param {Object} declaredPropTypes + * @returns {boolean} True if propTypes should be ignored (e.g. when a type can't be resolved, when it is imported) + */ + function declarePropTypesForObjectTypeAnnotation(propTypes, declaredPropTypes) { + let ignorePropsValidation = false; + + iterateProperties(context, propTypes.properties, (key, value, propNode) => { + if (!value) { + ignorePropsValidation = ignorePropsValidation || propNode.type !== 'ObjectTypeSpreadProperty'; + return; + } + + const types = buildTypeAnnotationDeclarationTypes(value, key); + types.fullName = key; + types.name = key; + types.node = propNode; + types.isRequired = !propNode.optional; + declaredPropTypes[key] = types; + }, (spreadNode) => { + const key = astUtil.getKeyValue(context, spreadNode); + const spreadAnnotation = getInTypeScope(key); + if (!spreadAnnotation) { + ignorePropsValidation = true; + } else { + const spreadIgnoreValidation = declarePropTypesForObjectTypeAnnotation(spreadAnnotation, declaredPropTypes); + ignorePropsValidation = ignorePropsValidation || spreadIgnoreValidation; + } + }); + + return ignorePropsValidation; + } + + /** + * Marks all props found inside IntersectionTypeAnnotation as declared. + * Since InterSectionTypeAnnotations can be nested, this handles recursively. + * + * Modifies the declaredPropTypes object + * @param {ASTNode} propTypes + * @param {Object} declaredPropTypes + * @returns {boolean} True if propTypes should be ignored (e.g. when a type can't be resolved, when it is imported) + */ + function declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes) { + return propTypes.types.some((annotation) => { + if (annotation.type === 'ObjectTypeAnnotation') { + return declarePropTypesForObjectTypeAnnotation(annotation, declaredPropTypes); + } + + if (annotation.type === 'UnionTypeAnnotation') { + return true; + } + + // Type can't be resolved + if (!annotation.id) { + return true; + } + + const typeNode = getInTypeScope(annotation.id.name); + + if (!typeNode) { + return true; + } + if (typeNode.type === 'IntersectionTypeAnnotation') { + return declarePropTypesForIntersectionTypeAnnotation(typeNode, declaredPropTypes); + } + + return declarePropTypesForObjectTypeAnnotation(typeNode, declaredPropTypes); + }); + } + + /** + * Resolve node of type Identifier when building declaration types. + * @param {ASTNode} node + * @param {ASTNode} rootNode + * @param {Function} callback called with the resolved value only if resolved. + */ + function resolveValueForIdentifierNode(node, rootNode, callback) { + if ( + rootNode + && node + && node.type === 'Identifier' + ) { + const scope = getScope(context, rootNode); + const identVariable = scope.variableScope.variables.find( + (variable) => variable.name === node.name + ); + if (identVariable) { + const definition = identVariable.defs[identVariable.defs.length - 1]; + callback(definition.node.init); + } + } + } + + /** + * Creates the representation of the React propTypes for the component. + * The representation is used to verify nested used properties. + * @param {ASTNode} value Node of the PropTypes for the desired property + * @param {string} parentName + * @param {ASTNode} rootNode + * @return {Object} The representation of the declaration, empty object means + * the property is declared without the need for further analysis. + */ + function buildReactDeclarationTypes(value, parentName, rootNode) { + if ( + value + && value.callee + && value.callee.object + && hasCustomValidator(value.callee.object.name) + ) { + return {}; + } + + let identNodeResolved = false; + // Resolve identifier node for cases where isRequired is set in + // the variable declaration or not at all. + // const variableType = PropTypes.shape({ foo: ... }).isRequired + // propTypes = { + // example: variableType + // } + // -------- + // const variableType = PropTypes.shape({ foo: ... }) + // propTypes = { + // example: variableType + // } + resolveValueForIdentifierNode(value, rootNode, (newValue) => { + identNodeResolved = true; + value = newValue; + }); + + if ( + value + && value.type === 'MemberExpression' + && value.property + && value.property.name === 'isRequired' + ) { + value = value.object; + } + + // Resolve identifier node for cases where isRequired is set in + // the prop types. + // const variableType = PropTypes.shape({ foo: ... }) + // propTypes = { + // example: variableType.isRequired + // } + if (!identNodeResolved) { + resolveValueForIdentifierNode(value, rootNode, (newValue) => { + value = newValue; + }); + } + + // Verify PropTypes that are functions + if ( + astUtil.isCallExpression(value) + && value.callee + && value.callee.property + && value.callee.property.name + && value.arguments + && value.arguments.length > 0 + ) { + const callName = value.callee.property.name; + const argument = value.arguments[0]; + switch (callName) { + case 'shape': + case 'exact': { + if (argument.type !== 'ObjectExpression') { + // Invalid proptype or cannot analyse statically + return {}; + } + const shapeTypeDefinition = { + type: callName, + children: {}, + }; + iterateProperties(context, argument.properties, (childKey, childValue, propNode) => { + if (childValue) { // skip spread propTypes + const fullName = [parentName, childKey].join('.'); + const types = buildReactDeclarationTypes(childValue, fullName, rootNode); + types.fullName = fullName; + types.name = childKey; + types.node = propNode; + shapeTypeDefinition.children[childKey] = types; + } + }); + return shapeTypeDefinition; + } + case 'arrayOf': + case 'objectOf': { + const fullName = [parentName, '*'].join('.'); + const child = buildReactDeclarationTypes(argument, fullName, rootNode); + child.fullName = fullName; + child.name = '__ANY_KEY__'; + child.node = argument; + return { + type: 'object', + children: { + __ANY_KEY__: child, + }, + }; + } + case 'oneOfType': { + if ( + !argument.elements + || argument.elements.length === 0 + ) { + // Invalid proptype or cannot analyse statically + return {}; + } + + /** @type {UnionTypeDefinition} */ + const unionTypeDefinition = { + type: 'union', + children: argument.elements.map((element) => buildReactDeclarationTypes(element, parentName, rootNode)), + }; + if (unionTypeDefinition.children.length === 0) { + // no complex type found, simply accept everything + return {}; + } + return unionTypeDefinition; + } + default: + return {}; + } + } + // Unknown property or accepts everything (any, object, ...) + return {}; + } + + function isValidReactGenericTypeAnnotation(annotation) { + if (annotation.typeName) { + if (annotation.typeName.name) { // if FC + const typeName = annotation.typeName.name; + if (!genericReactTypesImport.has(typeName)) { + return false; + } + } else if (annotation.typeName.right.name) { // if React.FC + const right = annotation.typeName.right.name; + const left = annotation.typeName.left.name; + + if (!genericReactTypesImport.has(left) || !allowedGenericTypes.has(right)) { + return false; + } + } + } + return true; + } + + /** + * Returns the left most typeName of a node, e.g: FC, React.FC + * The representation is used to verify nested used properties. + * @param {ASTNode} node + * @return {string | undefined} + */ + function getLeftMostTypeName(node) { + if (node.name) return node.name; + if (node.left) return getLeftMostTypeName(node.left); + } + + function getRightMostTypeName(node) { + if (node.name) return node.name; + if (node.right) return getRightMostTypeName(node.right); + } + + /** + * Returns true if the node is either a interface or type alias declaration + * @param {ASTNode} node + * @return {boolean} + */ + function filterInterfaceOrTypeAlias(node) { + return ( + astUtil.isTSInterfaceDeclaration(node) || astUtil.isTSTypeAliasDeclaration(node) + ); + } + + /** + * Returns true if the interface or type alias declaration node name matches the type-name str + * @param {ASTNode} node + * @param {string} typeName + * @return {boolean} + */ + function filterInterfaceOrAliasByName(node, typeName) { + return ( + node.id + && node.id.name === typeName + ) || ( + node.declaration + && node.declaration.id + && node.declaration.id.name === typeName + ); + } + + class DeclarePropTypesForTSTypeAnnotation { + constructor(propTypes, declaredPropTypes, rootNode) { + this.propTypes = propTypes; + this.declaredPropTypes = declaredPropTypes; + this.foundDeclaredPropertiesList = []; + this.referenceNameMap = new Set(); + this.sourceCode = getSourceCode(context); + this.shouldIgnorePropTypes = false; + this.rootNode = rootNode; + this.visitTSNode(this.propTypes); + this.endAndStructDeclaredPropTypes(); + } + + /** + * The node will be distribute to different function. + * @param {ASTNode} node + */ + visitTSNode(node) { + if (!node) return; + if (astUtil.isTSTypeAnnotation(node)) { + const typeAnnotation = node.typeAnnotation; + this.visitTSNode(typeAnnotation); + } else if (astUtil.isTSTypeReference(node)) { + this.searchDeclarationByName(node); + } else if (astUtil.isTSInterfaceHeritage(node)) { + this.searchDeclarationByName(node); + } else if (astUtil.isTSTypeLiteral(node)) { + // Check node is an object literal + if (Array.isArray(node.members)) { + this.foundDeclaredPropertiesList = this.foundDeclaredPropertiesList.concat(node.members); + } + } else if (astUtil.isTSIntersectionType(node)) { + this.convertIntersectionTypeToPropTypes(node); + } else if (astUtil.isTSParenthesizedType(node)) { + const typeAnnotation = node.typeAnnotation; + this.visitTSNode(typeAnnotation); + } else if (astUtil.isTSTypeParameterInstantiation(node)) { + if (Array.isArray(node.params)) { + node.params.forEach((x) => this.visitTSNode(x)); + } + } else { + this.shouldIgnorePropTypes = true; + } + } + + /** + * Search TSInterfaceDeclaration or TSTypeAliasDeclaration, + * by using TSTypeReference and TSInterfaceHeritage name. + * @param {ASTNode} node + */ + searchDeclarationByName(node) { + let typeName; + if (astUtil.isTSTypeReference(node)) { + typeName = node.typeName.name; + const leftMostName = getLeftMostTypeName(node.typeName); + const shouldTraverseTypeParams = genericReactTypesImport.has(leftMostName); + const nodeTypeArguments = propsUtil.getTypeArguments(node); + if (shouldTraverseTypeParams && nodeTypeArguments && nodeTypeArguments.length !== 0) { + // All react Generic types are derived from: + // type PropsWithChildren

    = P & { children?: ReactNode | undefined } + // So we should construct an optional children prop + this.shouldSpecifyOptionalChildrenProps = true; + + const rightMostName = getRightMostTypeName(node.typeName); + if ( + leftMostName === 'React' + && ( + rightMostName === 'HTMLAttributes' + || rightMostName === 'HTMLElement' + || rightMostName === 'HTMLProps' + ) + ) { + this.shouldSpecifyClassNameProp = true; + } + + const importedName = localToImportedMap[rightMostName]; + const idx = genericTypeParamIndexWherePropsArePresent[ + leftMostName !== rightMostName ? rightMostName : importedName + ]; + const nextNode = nodeTypeArguments.params[idx]; + this.visitTSNode(nextNode); + return; + } + } else if (astUtil.isTSInterfaceHeritage(node)) { + if (!node.expression && node.id) { + typeName = node.id.name; + } else { + typeName = node.expression.name; + } + } + if (!typeName) { + this.shouldIgnorePropTypes = true; + return; + } + if (typeName === 'ReturnType') { + this.convertReturnTypeToPropTypes(node, this.rootNode); + return; + } + // Prevent recursive inheritance will cause maximum callstack. + if (this.referenceNameMap.has(typeName)) { + this.shouldIgnorePropTypes = true; + return; + } + // Add typeName to Set and consider it as traversed. + this.referenceNameMap.add(typeName); + + /** + * From line 577 to line 581, and line 588 to line 590 are trying to handle typescript-eslint-parser + * Need to be deprecated after remove typescript-eslint-parser support. + */ + const candidateTypes = this.sourceCode.ast.body.filter((item) => astUtil.isTSTypeDeclaration(item)); + + const declarations = flatMap( + candidateTypes, + (type) => ( + type.declarations + || ( + type.declaration + && type.declaration.declarations + ) + || type.declaration + ) + ); + + // we tried to find either an interface or a type with the TypeReference name + const typeDeclaration = declarations.filter((dec) => dec.id.name === typeName); + + const interfaceDeclarations = this.sourceCode.ast.body + .filter(filterInterfaceOrTypeAlias) + .filter((item) => filterInterfaceOrAliasByName(item, typeName)) + .map((item) => (item.declaration || item)); + + if (typeDeclaration.length !== 0) { + typeDeclaration.map((t) => t.init || t.typeAnnotation).forEach(this.visitTSNode, this); + } else if (interfaceDeclarations.length !== 0) { + interfaceDeclarations.forEach(this.traverseDeclaredInterfaceOrTypeAlias, this); + } else { + this.shouldIgnorePropTypes = true; + } + } + + /** + * Traverse TSInterfaceDeclaration and TSTypeAliasDeclaration + * which retrieve from function searchDeclarationByName; + * @param {ASTNode} node + */ + traverseDeclaredInterfaceOrTypeAlias(node) { + if (astUtil.isTSInterfaceDeclaration(node)) { + // Handle TSInterfaceDeclaration interface Props { name: string, id: number}, should put in properties list directly; + this.foundDeclaredPropertiesList = this.foundDeclaredPropertiesList.concat(node.body.body); + } + // Handle TSTypeAliasDeclaration type Props = {name:string} + if (astUtil.isTSTypeAliasDeclaration(node)) { + const typeAnnotation = node.typeAnnotation; + this.visitTSNode(typeAnnotation); + } + if (Array.isArray(node.extends)) { + node.extends.forEach((x) => this.visitTSNode(x)); + // This line is trying to handle typescript-eslint-parser + // typescript-eslint-parser extension is name as heritage + } else if (Array.isArray(node.heritage)) { + node.heritage.forEach((x) => this.visitTSNode(x)); + } + } + + convertIntersectionTypeToPropTypes(node) { + if (!node) return; + if (Array.isArray(node.types)) { + node.types.forEach((x) => this.visitTSNode(x)); + } else { + this.shouldIgnorePropTypes = true; + } + } + + convertReturnTypeToPropTypes(node, rootNode) { + // ReturnType should always have one parameter + const nodeTypeArguments = propsUtil.getTypeArguments(node); + if (nodeTypeArguments) { + if (nodeTypeArguments.params.length === 1) { + let returnType = nodeTypeArguments.params[0]; + // This line is trying to handle typescript-eslint-parser + // typescript-eslint-parser TSTypeQuery is wrapped by TSTypeReference + if (astUtil.isTSTypeReference(returnType)) { + returnType = returnType.typeName; + } + // Handle ReturnType + if (astUtil.isTSTypeQuery(returnType)) { + const returnTypeFunction = flatMap(this.sourceCode.ast.body + .filter((item) => item.type === 'VariableDeclaration' + && item.declarations.find((dec) => dec.id.name === returnType.exprName.name) + ), (type) => type.declarations).map((dec) => dec.init); + + if (Array.isArray(returnTypeFunction)) { + if (returnTypeFunction.length === 0) { + // Cannot find identifier in current scope. It might be an exported type. + this.shouldIgnorePropTypes = true; + return; + } + returnTypeFunction.forEach((func) => { + if (isFunctionType(func)) { + let res = func.body; + if (res.type === 'BlockStatement') { + res = astUtil.findReturnStatement(func); + if (res) { + res = res.argument; + } + } + switch (res.type) { + case 'ObjectExpression': + iterateProperties(context, res.properties, (key, value, propNode) => { + if (propNode && astUtil.isCallExpression(propNode.argument)) { + const propNodeTypeArguments = propsUtil.getTypeArguments(propNode.argument); + if (propNodeTypeArguments) { + this.visitTSNode(propNodeTypeArguments); + } else { + // Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types. + this.shouldIgnorePropTypes = true; + return; + } + } + if (!value) { + this.shouldIgnorePropTypes = true; + return; + } + const types = buildReactDeclarationTypes(value, key, rootNode); + types.fullName = key; + types.name = key; + types.node = propNode; + types.isRequired = propsUtil.isRequiredPropType(value); + this.declaredPropTypes[key] = types; + }); + break; + case 'CallExpression': + if (propsUtil.getTypeArguments(res)) { + this.visitTSNode(propsUtil.getTypeArguments(res)); + } else { + // Ignore this CallExpression return value since it doesn't have any typeParameters to let us know it's types. + this.shouldIgnorePropTypes = true; + } + break; + default: + } + } + }); + return; + } + } + // Handle ReturnType<()=>returnType> + if (astUtil.isTSFunctionType(returnType)) { + if (astUtil.isTSTypeAnnotation(returnType.returnType)) { + this.visitTSNode(returnType.returnType); + return; + } + // This line is trying to handle typescript-eslint-parser + // typescript-eslint-parser TSFunction name returnType as typeAnnotation + if (astUtil.isTSTypeAnnotation(returnType.typeAnnotation)) { + this.visitTSNode(returnType.typeAnnotation); + return; + } + } + } + } + this.shouldIgnorePropTypes = true; + } + + endAndStructDeclaredPropTypes() { + if (this.shouldSpecifyOptionalChildrenProps) { + this.declaredPropTypes.children = { + fullName: 'children', + name: 'children', + isRequired: false, + }; + } + if (this.shouldSpecifyClassNameProp) { + this.declaredPropTypes.className = { + fullName: 'className', + name: 'className', + isRequired: false, + }; + } + + this.foundDeclaredPropertiesList.forEach((tsInterfaceBody) => { + if (tsInterfaceBody && (tsInterfaceBody.type === 'TSPropertySignature' || tsInterfaceBody.type === 'TSMethodSignature')) { + let accessor = 'name'; + if (tsInterfaceBody.key.type === 'Literal') { + if (typeof tsInterfaceBody.key.value === 'number') { + accessor = 'raw'; + } else { + accessor = 'value'; + } + } + this.declaredPropTypes[tsInterfaceBody.key[accessor]] = { + fullName: tsInterfaceBody.key[accessor], + name: tsInterfaceBody.key[accessor], + node: tsInterfaceBody, + isRequired: !tsInterfaceBody.optional, + }; + } + }); + } + } + + /** + * Mark a prop type as declared + * @param {ASTNode} node The AST node being checked. + * @param {ASTNode} propTypes The AST node containing the proptypes + * @param {ASTNode} rootNode + */ + function markPropTypesAsDeclared(node, propTypes, rootNode) { + let componentNode = node; + while (componentNode && !components.get(componentNode)) { + componentNode = componentNode.parent; + } + const component = components.get(componentNode); + let declaredPropTypes = (component && component.declaredPropTypes) || {}; + let ignorePropsValidation = (component && component.ignorePropsValidation) || false; + switch (propTypes && propTypes.type) { + case 'ObjectTypeAnnotation': + ignorePropsValidation = declarePropTypesForObjectTypeAnnotation(propTypes, declaredPropTypes); + break; + case 'ObjectExpression': + iterateProperties(context, propTypes.properties, (key, value, propNode) => { + if (!value) { + ignorePropsValidation = true; + return; + } + const types = buildReactDeclarationTypes(value, key, rootNode); + types.fullName = key; + types.name = key; + types.node = propNode; + types.isRequired = propsUtil.isRequiredPropType(value); + declaredPropTypes[key] = types; + }); + break; + case 'MemberExpression': { + let curDeclaredPropTypes = declaredPropTypes; + // Walk the list of properties, until we reach the assignment + // ie: ClassX.propTypes.a.b.c = ... + while ( + propTypes + && propTypes.parent + && propTypes.parent.type !== 'AssignmentExpression' + && propTypes.property + && curDeclaredPropTypes + ) { + const propName = propTypes.property.name; + if (propName in curDeclaredPropTypes) { + curDeclaredPropTypes = curDeclaredPropTypes[propName].children; + propTypes = propTypes.parent; + } else { + // This will crash at runtime because we haven't seen this key before + // stop this and do not declare it + propTypes = null; + } + } + if (propTypes && propTypes.parent && propTypes.property) { + if (!(propTypes === propTypes.parent.left && propTypes.parent.left.object)) { + ignorePropsValidation = true; + break; + } + const parentProp = getText(context, propTypes.parent.left.object).replace(/^.*\.propTypes\./, ''); + const types = buildReactDeclarationTypes( + propTypes.parent.right, + parentProp, + rootNode + ); + + types.name = propTypes.property.name; + types.fullName = [parentProp, propTypes.property.name].join('.'); + types.node = propTypes.parent; + types.isRequired = propsUtil.isRequiredPropType(propTypes.parent.right); + curDeclaredPropTypes[propTypes.property.name] = types; + } else { + let isUsedInPropTypes = false; + let n = propTypes; + while (n) { + if (((n.type === 'AssignmentExpression') && propsUtil.isPropTypesDeclaration(n.left)) + || ((n.type === 'ClassProperty' || n.type === 'PropertyDefinition' || n.type === 'Property') && propsUtil.isPropTypesDeclaration(n))) { + // Found a propType used inside of another propType. This is not considered usage, we'll still validate + // this component. + isUsedInPropTypes = true; + break; + } + n = n.parent; + } + if (!isUsedInPropTypes) { + ignorePropsValidation = true; + } + } + break; + } + case 'Identifier': { + const firstMatchingVariable = variableUtil.getVariableFromContext(context, node, propTypes.name); + if (firstMatchingVariable) { + const defInScope = firstMatchingVariable.defs[firstMatchingVariable.defs.length - 1]; + markPropTypesAsDeclared(node, defInScope.node && defInScope.node.init, rootNode); + return; + } + ignorePropsValidation = true; + break; + } + case 'CallExpression': { + if ( + propWrapperUtil.isPropWrapperFunction( + context, + getText(context, propTypes.callee) + ) + && propTypes.arguments && propTypes.arguments[0] + ) { + markPropTypesAsDeclared(node, propTypes.arguments[0], rootNode); + return; + } + break; + } + case 'IntersectionTypeAnnotation': + ignorePropsValidation = declarePropTypesForIntersectionTypeAnnotation(propTypes, declaredPropTypes); + break; + case 'GenericTypeAnnotation': + if (propTypes.id.name === '$ReadOnly') { + const propTypeArguments = propsUtil.getTypeArguments(propTypes); + ignorePropsValidation = declarePropTypesForObjectTypeAnnotation( + propTypeArguments.params[0], + declaredPropTypes + ); + } else { + ignorePropsValidation = true; + } + break; + case 'TSTypeReference': + case 'TSTypeAnnotation': { + const tsTypeAnnotation = new DeclarePropTypesForTSTypeAnnotation(propTypes, declaredPropTypes, rootNode); + ignorePropsValidation = tsTypeAnnotation.shouldIgnorePropTypes; + declaredPropTypes = tsTypeAnnotation.declaredPropTypes; + } + break; + case null: + break; + default: + ignorePropsValidation = true; + break; + } + + components.set(node, { + declaredPropTypes, + ignorePropsValidation, + }); + } + + /** + * @param {ASTNode} node We expect either an ArrowFunctionExpression, + * FunctionDeclaration, or FunctionExpression + * @param {ASTNode} rootNode + */ + function markAnnotatedFunctionArgumentsAsDeclared(node, rootNode) { + if (!node.params || !node.params.length) { + return; + } + + let propTypesArguments = null; + if (node.parent) { + propTypesArguments = propsUtil.getTypeArguments(node.parent); + } + + if ( + node.parent + && node.parent.callee + && propTypesArguments + && propTypesArguments.params + && ( + node.parent.callee.name === 'forwardRef' || ( + node.parent.callee.object + && node.parent.callee.property + && node.parent.callee.object.name === 'React' + && node.parent.callee.property.name === 'forwardRef' + ) + ) + ) { + const declaredPropTypes = {}; + const obj = new DeclarePropTypesForTSTypeAnnotation(propTypesArguments.params[1], declaredPropTypes, rootNode); + components.set(node, { + declaredPropTypes: obj.declaredPropTypes, + ignorePropsValidation: obj.shouldIgnorePropTypes, + }); + return; + } + + const siblingIdentifier = node.parent && node.parent.id; + const siblingHasTypeAnnotation = siblingIdentifier && siblingIdentifier.typeAnnotation; + const isNodeAnnotated = annotations.isAnnotatedFunctionPropsDeclaration(node, context); + + if (!isNodeAnnotated && !siblingHasTypeAnnotation) { + return; + } + + // https://github.com/jsx-eslint/eslint-plugin-react/issues/2784 + if (isInsideClassBody(node) && !astUtil.isFunction(node)) { + return; + } + + // Should ignore function that not return JSXElement + if (!utils.isReturningJSXOrNull(node) || startWithCapitalizedLetter(node)) { + return; + } + + if (isNodeAnnotated) { + const param = node.params[0]; + if (param.typeAnnotation && param.typeAnnotation.typeAnnotation && param.typeAnnotation.typeAnnotation.type === 'UnionTypeAnnotation') { + param.typeAnnotation.typeAnnotation.types.forEach((annotation) => { + if (annotation.type === 'GenericTypeAnnotation') { + markPropTypesAsDeclared(node, resolveTypeAnnotation(annotation), rootNode); + } else { + markPropTypesAsDeclared(node, annotation, rootNode); + } + }); + } else { + markPropTypesAsDeclared(node, resolveTypeAnnotation(param), rootNode); + } + } else { + // implements what's discussed here: https://github.com/jsx-eslint/eslint-plugin-react/issues/2777#issuecomment-683944481 + const annotation = siblingIdentifier.typeAnnotation.typeAnnotation; + + if ( + annotation + && annotation.type !== 'TSTypeReference' + && propsUtil.getTypeArguments(annotation) == null + ) { + return; + } + + if (!isValidReactGenericTypeAnnotation(annotation)) return; + + markPropTypesAsDeclared(node, resolveTypeAnnotation(siblingIdentifier), rootNode); + } + } + + /** + * Resolve the type annotation for a given class declaration node. + * + * @param {ASTNode} node The annotation or a node containing the type annotation. + * @returns {ASTNode} The resolved type annotation for the node. + */ + function resolveSuperParameterPropsType(node) { + let propsParameterPosition; + const parameters = propsUtil.getSuperTypeArguments(node); + + try { + // Flow <=0.52 had 3 required TypedParameters of which the second one is the Props. + // Flow >=0.53 has 2 optional TypedParameters of which the first one is the Props. + propsParameterPosition = testFlowVersion(context, '>= 0.53.0') ? 0 : 1; + } catch (e) { + // In case there is no flow version defined, we can safely assume that when there are 3 Props we are dealing with version <= 0.52 + propsParameterPosition = parameters.params.length <= 2 ? 0 : 1; + } + + let annotation = parameters.params[propsParameterPosition]; + while (annotation && (annotation.type === 'TypeAnnotation' || annotation.type === 'NullableTypeAnnotation')) { + annotation = annotation.typeAnnotation; + } + + if (annotation && annotation.type === 'GenericTypeAnnotation' && getInTypeScope(annotation.id.name)) { + return getInTypeScope(annotation.id.name); + } + return annotation; + } + + /** + * Checks if we are declaring a `props` class property with a flow type annotation. + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if the node is a type annotated props declaration, false if not. + */ + function isAnnotatedClassPropsDeclaration(node) { + if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) { + const tokens = getFirstTokens(context, node, 2); + if ( + node.typeAnnotation && ( + tokens[0].value === 'props' + || (tokens[1] && tokens[1].value === 'props') + ) + ) { + return true; + } + } + return false; + } + + return { + ClassExpression(node) { + // TypeParameterDeclaration need to be added to typeScope in order to handle ClassExpressions. + // This visitor is executed before TypeParameterDeclaration are scoped, therefore we postpone + // processing class expressions until when the program exists. + classExpressions.push(node); + }, + + ClassDeclaration(node) { + if (isSuperTypeParameterPropsDeclaration(node)) { + markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node); + } + }, + + 'ClassProperty, PropertyDefinition'(node) { + if (isAnnotatedClassPropsDeclaration(node)) { + markPropTypesAsDeclared(node, resolveTypeAnnotation(node), node); + } else if (propsUtil.isPropTypesDeclaration(node)) { + markPropTypesAsDeclared(node, node.value, node); + } + }, + + ObjectExpression(node) { + // Search for the proptypes declaration + node.properties.forEach((property) => { + if (!propsUtil.isPropTypesDeclaration(property)) { + return; + } + markPropTypesAsDeclared(node, property.value, node); + }); + }, + + FunctionExpression(node) { + if (node.parent.type !== 'MethodDefinition') { + markAnnotatedFunctionArgumentsAsDeclared(node, node); + } + }, + + ImportDeclaration(node) { + // parse `import ... from 'react` + if (node.source.value === 'react') { + node.specifiers.forEach((specifier) => { + if ( + // handles import * as X from 'react' + specifier.type === 'ImportNamespaceSpecifier' + // handles import React from 'react' + || specifier.type === 'ImportDefaultSpecifier' + ) { + genericReactTypesImport.add(specifier.local.name); + } + + // handles import { FC } from 'react' or import { FC as X } from 'react' + if (specifier.type === 'ImportSpecifier' && allowedGenericTypes.has(specifier.imported.name)) { + genericReactTypesImport.add(specifier.local.name); + localToImportedMap[specifier.local.name] = specifier.imported.name; + } + }); + } + }, + + FunctionDeclaration: markAnnotatedFunctionArgumentsAsDeclared, + + ArrowFunctionExpression: markAnnotatedFunctionArgumentsAsDeclared, + + MemberExpression(node) { + if (propsUtil.isPropTypesDeclaration(node)) { + const component = utils.getRelatedComponent(node); + if (!component) { + return; + } + try { + markPropTypesAsDeclared(component.node, node.parent.right || node.parent, node); + } catch (e) { + if (e.constructor !== RangeError) { throw e; } + } + } + }, + + MethodDefinition(node) { + if (!node.static || node.kind !== 'get' || !propsUtil.isPropTypesDeclaration(node)) { + return; + } + + let i = node.value.body.body.length - 1; + for (; i >= 0; i--) { + if (node.value.body.body[i].type === 'ReturnStatement') { + break; + } + } + + if (i >= 0) { + markPropTypesAsDeclared(node, node.value.body.body[i].argument, node); + } + }, + + TypeAlias(node) { + setInTypeScope(node.id.name, node.right); + }, + + TypeParameterDeclaration(node) { + const identifier = node.params[0]; + + if (identifier.typeAnnotation) { + setInTypeScope(identifier.name, identifier.typeAnnotation.typeAnnotation); + } + }, + + Program() { + stack = [{}]; + }, + + BlockStatement() { + stack.push(Object.create(typeScope())); + }, + + 'BlockStatement:exit'() { + stack.pop(); + }, + + 'Program:exit'() { + classExpressions.forEach((node) => { + if (isSuperTypeParameterPropsDeclaration(node)) { + markPropTypesAsDeclared(node, resolveSuperParameterPropsType(node), node); + } + }); + }, + }; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts b/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..01c2d4006281551b4a22ae9eb3a7c7062734bb28 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts @@ -0,0 +1,40 @@ +/** + * Fixes sort order of prop types. + * + * @param {Context} context the second element to compare. + * @param {Fixer} fixer the first element to compare. + * @param {Array} declarations The context of the two nodes. + * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements. + * @param {boolean=} requiredFirst whether or not to sort required elements first. + * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else. + * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements. + * @param {boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape. + * @param {boolean=} checkTypes whether or not sorting of prop type definitions are checked. + * @returns {Object|*|{range, text}} the sort order of the two elements. + */ +export function fixPropTypesSort(context: Context, fixer: Fixer, declarations: any[], ignoreCase?: boolean | undefined, requiredFirst?: boolean | undefined, callbacksLast?: boolean | undefined, noSortAlphabetically?: boolean | undefined, sortShapeProp?: boolean | undefined, checkTypes?: boolean | undefined): any | any | { + range; + text; +}; +/** + * Checks if the proptype is a callback by checking if it starts with 'on'. + * + * @param {string} propName the name of the proptype to check. + * @returns {boolean} true if the proptype is a callback. + */ +export function isCallbackPropName(propName: string): boolean; +/** + * Checks if the prop is required or not. + * + * @param {ASTNode} node the prop to check. + * @returns {boolean} true if the prop is required. + */ +export function isRequiredProp(node: ASTNode): boolean; +/** + * Checks if the prop is PropTypes.shape. + * + * @param {ASTNode} node the prop to check. + * @returns {boolean} true if the prop is PropTypes.shape. + */ +export function isShapeProp(node: ASTNode): boolean; +//# sourceMappingURL=propTypesSort.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map b/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..e9c8efbc3ed073421a433c34d7f48772aff0bd3f --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypesSort.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"propTypesSort.d.ts","sourceRoot":"","sources":["propTypesSort.js"],"names":[],"mappings":"AA4HA;;;;;;;;;;;;;GAaG;AACH,0CAXW,OAAO,SACP,KAAK,oCAEL,OAAO,8BACP,OAAO,8BACP,OAAO,qCACP,OAAO,8BACP,OAAO,2BACP,OAAO,eACL,YAAS;IAAC,KAAK,CAAC;IAAC,IAAI,CAAA;CAAC,CAyFlC;AA7LD;;;;;GAKG;AACH,6CAHW,MAAM,GACJ,OAAO,CAInB;AAlBD;;;;;GAKG;AACH,qCAHW,OAAO,GACL,OAAO,CAInB;AAYD;;;;;GAKG;AACH,kCAHW,OAAO,GACL,OAAO,CASnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propTypesSort.js b/node_modules/eslint-plugin-react/lib/util/propTypesSort.js new file mode 100644 index 0000000000000000000000000000000000000000..4b42904c63ad1d7d854a76b20855c56244a860c4 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propTypesSort.js @@ -0,0 +1,233 @@ +/** + * @fileoverview Common propTypes sorting functionality. + */ + +'use strict'; + +const toSorted = require('array.prototype.tosorted'); + +const astUtil = require('./ast'); +const eslintUtil = require('./eslint'); + +const getSourceCode = eslintUtil.getSourceCode; +const getText = eslintUtil.getText; + +/** + * Returns the value name of a node. + * + * @param {ASTNode} node the node to check. + * @returns {string} The name of the node. + */ +function getValueName(node) { + return node.type === 'Property' + && node.value.property + && node.value.property.name; +} + +/** + * Checks if the prop is required or not. + * + * @param {ASTNode} node the prop to check. + * @returns {boolean} true if the prop is required. + */ +function isRequiredProp(node) { + return getValueName(node) === 'isRequired'; +} + +/** + * Checks if the proptype is a callback by checking if it starts with 'on'. + * + * @param {string} propName the name of the proptype to check. + * @returns {boolean} true if the proptype is a callback. + */ +function isCallbackPropName(propName) { + return /^on[A-Z]/.test(propName); +} + +/** + * Checks if the prop is PropTypes.shape. + * + * @param {ASTNode} node the prop to check. + * @returns {boolean} true if the prop is PropTypes.shape. + */ +function isShapeProp(node) { + return !!( + node + && node.callee + && node.callee.property + && node.callee.property.name === 'shape' + ); +} + +/** + * Returns the properties of a PropTypes.shape. + * + * @param {ASTNode} node the prop to check. + * @returns {Array} the properties of the PropTypes.shape node. + */ +function getShapeProperties(node) { + return node.arguments + && node.arguments[0] + && node.arguments[0].properties; +} + +/** + * Compares two elements. + * + * @param {ASTNode} a the first element to compare. + * @param {ASTNode} b the second element to compare. + * @param {Context} context The context of the two nodes. + * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements. + * @param {boolean=} requiredFirst whether or not to sort required elements first. + * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else. + * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements. + * @returns {number} the sort order of the two elements. + */ +function sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically) { + const aKey = String(astUtil.getKeyValue(context, a)); + const bKey = String(astUtil.getKeyValue(context, b)); + + if (requiredFirst) { + if (isRequiredProp(a) && !isRequiredProp(b)) { + return -1; + } + if (!isRequiredProp(a) && isRequiredProp(b)) { + return 1; + } + } + + if (callbacksLast) { + if (isCallbackPropName(aKey) && !isCallbackPropName(bKey)) { + return 1; + } + if (!isCallbackPropName(aKey) && isCallbackPropName(bKey)) { + return -1; + } + } + + if (!noSortAlphabetically) { + if (ignoreCase) { + return aKey.localeCompare(bKey); + } + + if (aKey < bKey) { + return -1; + } + if (aKey > bKey) { + return 1; + } + } + return 0; +} + +const commentnodeMap = new WeakMap(); // all nodes reference WeakMap for start and end range + +/** + * Fixes sort order of prop types. + * + * @param {Context} context the second element to compare. + * @param {Fixer} fixer the first element to compare. + * @param {Array} declarations The context of the two nodes. + * @param {boolean=} ignoreCase whether or not to ignore case when comparing the two elements. + * @param {boolean=} requiredFirst whether or not to sort required elements first. + * @param {boolean=} callbacksLast whether or not to sort callbacks after everything else. + * @param {boolean=} noSortAlphabetically whether or not to disable alphabetical sorting of the elements. + * @param {boolean=} sortShapeProp whether or not to sort propTypes defined in PropTypes.shape. + * @param {boolean=} checkTypes whether or not sorting of prop type definitions are checked. + * @returns {Object|*|{range, text}} the sort order of the two elements. + */ +function fixPropTypesSort( + context, + fixer, + declarations, + ignoreCase, + requiredFirst, + callbacksLast, + noSortAlphabetically, + sortShapeProp, + checkTypes +) { + function sortInSource(allNodes, source) { + const originalSource = source; + const sourceCode = getSourceCode(context); + for (let i = 0; i < allNodes.length; i++) { + const node = allNodes[i]; + let commentAfter = []; + let commentBefore = []; + let newStart = 0; + let newEnd = 0; + try { + commentBefore = sourceCode.getCommentsBefore(node); + commentAfter = sourceCode.getCommentsAfter(node); + } catch (e) { /**/ } + + if (commentAfter.length === 0 || commentBefore.length === 0) { + newStart = node.range[0]; + newEnd = node.range[1]; + } + + const firstCommentBefore = commentBefore[0]; + if (commentBefore.length >= 1) { + newStart = firstCommentBefore.range[0]; + } + const lastCommentAfter = commentAfter[commentAfter.length - 1]; + if (commentAfter.length >= 1) { + newEnd = lastCommentAfter.range[1]; + } + commentnodeMap.set(node, { start: newStart, end: newEnd, hasComment: true }); + } + const nodeGroups = allNodes.reduce((acc, curr) => { + if (curr.type === 'ExperimentalSpreadProperty' || curr.type === 'SpreadElement') { + acc.push([]); + } else { + acc[acc.length - 1].push(curr); + } + return acc; + }, [[]]); + + nodeGroups.forEach((nodes) => { + const sortedAttributes = toSorted( + nodes, + (a, b) => sorter(a, b, context, ignoreCase, requiredFirst, callbacksLast, noSortAlphabetically) + ); + + const sourceCodeText = getText(context); + let separator = ''; + source = nodes.reduceRight((acc, attr, index) => { + const sortedAttr = sortedAttributes[index]; + const commentNode = commentnodeMap.get(sortedAttr); + let sortedAttrText = sourceCodeText.slice(commentNode.start, commentNode.end); + const sortedAttrTextLastChar = sortedAttrText[sortedAttrText.length - 1]; + if (!separator && [';', ','].some((allowedSep) => sortedAttrTextLastChar === allowedSep)) { + separator = sortedAttrTextLastChar; + } + if (sortShapeProp && isShapeProp(sortedAttr.value)) { + const shape = getShapeProperties(sortedAttr.value); + if (shape) { + const attrSource = sortInSource( + shape, + originalSource + ); + sortedAttrText = attrSource.slice(sortedAttr.range[0], sortedAttr.range[1]); + } + } + const sortedAttrTextVal = checkTypes && !sortedAttrText.endsWith(separator) ? `${sortedAttrText}${separator}` : sortedAttrText; + return `${acc.slice(0, commentnodeMap.get(attr).start)}${sortedAttrTextVal}${acc.slice(commentnodeMap.get(attr).end)}`; + }, source); + }); + return source; + } + + const source = sortInSource(declarations, getText(context)); + + const rangeStart = commentnodeMap.get(declarations[0]).start; + const rangeEnd = commentnodeMap.get(declarations[declarations.length - 1]).end; + return fixer.replaceTextRange([rangeStart, rangeEnd], source.slice(rangeStart, rangeEnd)); +} + +module.exports = { + fixPropTypesSort, + isCallbackPropName, + isRequiredProp, + isShapeProp, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts b/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..74e8cca7ffa877ef47aac67aaec612d430a961ac --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts @@ -0,0 +1,6 @@ +export function formatPropWrapperFunctions(propWrapperFunctions: any): string; +export function getExactPropWrapperFunctions(context: any): Set; +export function getPropWrapperFunctions(context: any): Set; +export function isExactPropWrapperFunction(context: any, name: any): any; +export function isPropWrapperFunction(context: any, name: any): any; +//# sourceMappingURL=propWrapper.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map b/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..adf81d91e5cd5c7c172b1c476e0afecc37806080 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propWrapper.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"propWrapper.d.ts","sourceRoot":"","sources":["propWrapper.js"],"names":[],"mappings":"AA0CA,8EAUC;AArBD,qEAIC;AAhBD,gEAEC;AAgBD,yEAGC;AAjBD,oEAMC"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/propWrapper.js b/node_modules/eslint-plugin-react/lib/util/propWrapper.js new file mode 100644 index 0000000000000000000000000000000000000000..66dac8803d1123db917fec53e5c86d4b34e8479e --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/propWrapper.js @@ -0,0 +1,61 @@ +/** + * @fileoverview Utility functions for propWrapperFunctions setting + */ + +'use strict'; + +const filter = require('es-iterator-helpers/Iterator.prototype.filter'); +const some = require('es-iterator-helpers/Iterator.prototype.some'); + +function searchPropWrapperFunctions(name, propWrapperFunctions) { + const splitName = name.split('.'); + return some(propWrapperFunctions.values(), (func) => { + if (splitName.length === 2 && func.object === splitName[0] && func.property === splitName[1]) { + return true; + } + return name === func || func.property === name; + }); +} + +function getPropWrapperFunctions(context) { + return new Set(context.settings.propWrapperFunctions || []); +} + +function isPropWrapperFunction(context, name) { + if (typeof name !== 'string') { + return false; + } + const propWrapperFunctions = getPropWrapperFunctions(context); + return searchPropWrapperFunctions(name, propWrapperFunctions); +} + +function getExactPropWrapperFunctions(context) { + const propWrapperFunctions = getPropWrapperFunctions(context); + const exactPropWrappers = filter(propWrapperFunctions.values(), (func) => func.exact === true); + return new Set(exactPropWrappers); +} + +function isExactPropWrapperFunction(context, name) { + const exactPropWrappers = getExactPropWrapperFunctions(context); + return searchPropWrapperFunctions(name, exactPropWrappers); +} + +function formatPropWrapperFunctions(propWrapperFunctions) { + return Array.from(propWrapperFunctions, (func) => { + if (func.object && func.property) { + return `'${func.object}.${func.property}'`; + } + if (func.property) { + return `'${func.property}'`; + } + return `'${func}'`; + }).join(', '); +} + +module.exports = { + formatPropWrapperFunctions, + getExactPropWrapperFunctions, + getPropWrapperFunctions, + isExactPropWrapperFunction, + isPropWrapperFunction, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/props.d.ts b/node_modules/eslint-plugin-react/lib/util/props.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..40bea76d4e62fc0276fceace45b04a0418b20755 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/props.d.ts @@ -0,0 +1,55 @@ +/** + * Checks if the Identifier node passed in looks like a propTypes declaration. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @returns {boolean} `true` if the node is a propTypes declaration, `false` if not + */ +export function isPropTypesDeclaration(node: ASTNode): boolean; +/** + * Checks if the node passed in looks like a contextTypes declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a contextTypes declaration, `false` if not + */ +export function isContextTypesDeclaration(node: ASTNode): boolean; +/** + * Checks if the node passed in looks like a contextType declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a contextType declaration, `false` if not + */ +export function isContextTypeDeclaration(node: ASTNode): boolean; +/** + * Checks if the node passed in looks like a childContextTypes declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a childContextTypes declaration, `false` if not + */ +export function isChildContextTypesDeclaration(node: ASTNode): boolean; +/** + * Checks if the Identifier node passed in looks like a defaultProps declaration. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not + */ +export function isDefaultPropsDeclaration(node: ASTNode): boolean; +/** + * Checks if we are declaring a display name + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if we are declaring a display name, false if not. + */ +export function isDisplayNameDeclaration(node: ASTNode): boolean; +/** + * Checks if the PropTypes MemberExpression node passed in declares a required propType. + * @param {ASTNode} propTypeExpression node to check. Must be a `PropTypes` MemberExpression. + * @returns {boolean} `true` if this PropType is required, `false` if not. + */ +export function isRequiredPropType(propTypeExpression: ASTNode): boolean; +/** + * Returns the type arguments of a node or type parameters if type arguments are not available. + * @param {ASTNode} node The node to get the type arguments from. + * @returns {ASTNode} The type arguments or type parameters of the node. + */ +export function getTypeArguments(node: ASTNode): ASTNode; +/** + * Returns the super type arguments of a node or super type parameters if type arguments are not available. + * @param {ASTNode} node The node to get the super type arguments from. + * @returns {ASTNode} The super type arguments or parameters of the node. + */ +export function getSuperTypeArguments(node: ASTNode): ASTNode; +//# sourceMappingURL=props.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/props.d.ts.map b/node_modules/eslint-plugin-react/lib/util/props.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..cc23fd7eeecc17ff577df07a82090831243b9dd1 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/props.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"props.d.ts","sourceRoot":"","sources":["props.js"],"names":[],"mappings":"AAQA;;;;GAIG;AACH,6CAHW,OAAO,GACL,OAAO,CAUnB;AAED;;;;GAIG;AACH,gDAHW,OAAO,GACL,OAAO,CAUnB;AAED;;;;GAIG;AACH,+CAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;GAIG;AACH,qDAHW,OAAO,GACL,OAAO,CAInB;AAED;;;;GAIG;AACH,gDAHW,OAAO,GACL,OAAO,CAKnB;AAED;;;;GAIG;AACH,+CAHW,OAAO,GACL,OAAO,CAcnB;AAED;;;;GAIG;AACH,uDAHW,OAAO,GACL,OAAO,CAKnB;AAED;;;;GAIG;AACH,uCAHW,OAAO,GACL,OAAO,CAOnB;AAED;;;;GAIG;AACH,4CAHW,OAAO,GACL,OAAO,CAOnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/props.js b/node_modules/eslint-plugin-react/lib/util/props.js new file mode 100644 index 0000000000000000000000000000000000000000..ac4ed8703bc7be88616051226aeb9c0eaf3a20e6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/props.js @@ -0,0 +1,130 @@ +/** + * @fileoverview Utility functions for props + */ + +'use strict'; + +const astUtil = require('./ast'); + +/** + * Checks if the Identifier node passed in looks like a propTypes declaration. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @returns {boolean} `true` if the node is a propTypes declaration, `false` if not + */ +function isPropTypesDeclaration(node) { + if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) { + // Flow support + if (node.typeAnnotation && node.key.name === 'props') { + return true; + } + } + return astUtil.getPropertyName(node) === 'propTypes'; +} + +/** + * Checks if the node passed in looks like a contextTypes declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a contextTypes declaration, `false` if not + */ +function isContextTypesDeclaration(node) { + if (node && (node.type === 'ClassProperty' || node.type === 'PropertyDefinition')) { + // Flow support + if (node.typeAnnotation && node.key.name === 'context') { + return true; + } + } + return astUtil.getPropertyName(node) === 'contextTypes'; +} + +/** + * Checks if the node passed in looks like a contextType declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a contextType declaration, `false` if not + */ +function isContextTypeDeclaration(node) { + return astUtil.getPropertyName(node) === 'contextType'; +} + +/** + * Checks if the node passed in looks like a childContextTypes declaration. + * @param {ASTNode} node The node to check. + * @returns {boolean} `true` if the node is a childContextTypes declaration, `false` if not + */ +function isChildContextTypesDeclaration(node) { + return astUtil.getPropertyName(node) === 'childContextTypes'; +} + +/** + * Checks if the Identifier node passed in looks like a defaultProps declaration. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @returns {boolean} `true` if the node is a defaultProps declaration, `false` if not + */ +function isDefaultPropsDeclaration(node) { + const propName = astUtil.getPropertyName(node); + return (propName === 'defaultProps' || propName === 'getDefaultProps'); +} + +/** + * Checks if we are declaring a display name + * @param {ASTNode} node The AST node being checked. + * @returns {boolean} True if we are declaring a display name, false if not. + */ +function isDisplayNameDeclaration(node) { + switch (node.type) { + case 'ClassProperty': + case 'PropertyDefinition': + return node.key && node.key.name === 'displayName'; + case 'Identifier': + return node.name === 'displayName'; + case 'Literal': + return node.value === 'displayName'; + default: + return false; + } +} + +/** + * Checks if the PropTypes MemberExpression node passed in declares a required propType. + * @param {ASTNode} propTypeExpression node to check. Must be a `PropTypes` MemberExpression. + * @returns {boolean} `true` if this PropType is required, `false` if not. + */ +function isRequiredPropType(propTypeExpression) { + return propTypeExpression.type === 'MemberExpression' + && propTypeExpression.property.name === 'isRequired'; +} + +/** + * Returns the type arguments of a node or type parameters if type arguments are not available. + * @param {ASTNode} node The node to get the type arguments from. + * @returns {ASTNode} The type arguments or type parameters of the node. + */ +function getTypeArguments(node) { + if ('typeArguments' in node) { + return node.typeArguments; + } + return node.typeParameters; +} + +/** + * Returns the super type arguments of a node or super type parameters if type arguments are not available. + * @param {ASTNode} node The node to get the super type arguments from. + * @returns {ASTNode} The super type arguments or parameters of the node. + */ +function getSuperTypeArguments(node) { + if ('superTypeArguments' in node) { + return node.superTypeArguments; + } + return node.superTypeParameters; +} + +module.exports = { + isPropTypesDeclaration, + isContextTypesDeclaration, + isContextTypeDeclaration, + isChildContextTypesDeclaration, + isDefaultPropsDeclaration, + isDisplayNameDeclaration, + isRequiredPropType, + getTypeArguments, + getSuperTypeArguments, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/report.d.ts b/node_modules/eslint-plugin-react/lib/util/report.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..306332c3cb4431b7642f79262028678e2db4e17a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/report.d.ts @@ -0,0 +1,3 @@ +declare function _exports(context: any, message: any, messageId: any, data: any): void; +export = _exports; +//# sourceMappingURL=report.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/report.d.ts.map b/node_modules/eslint-plugin-react/lib/util/report.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..01007c159f22c06bec55df3b2048434528aa4e01 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/report.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"report.d.ts","sourceRoot":"","sources":["report.js"],"names":[],"mappings":"AAIiB,uFAOhB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/report.js b/node_modules/eslint-plugin-react/lib/util/report.js new file mode 100644 index 0000000000000000000000000000000000000000..9c10a13501e99cf28abe2c2c812960aae646efc9 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/report.js @@ -0,0 +1,12 @@ +'use strict'; + +const getMessageData = require('./message'); + +module.exports = function report(context, message, messageId, data) { + context.report( + Object.assign( + getMessageData(messageId, message), + data + ) + ); +}; diff --git a/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..9e3c1a98cc70e0eadb2111abba0b8225df679fa7 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts @@ -0,0 +1,15 @@ +declare function _exports(context: any, components: any, utils: any): { + VariableDeclarator(node: any): void; + FunctionDeclaration: (node: ASTNode) => void; + ArrowFunctionExpression: (node: ASTNode) => void; + FunctionExpression: (node: ASTNode) => void; + 'FunctionDeclaration:exit': () => void; + 'ArrowFunctionExpression:exit': () => void; + 'FunctionExpression:exit': () => void; + JSXSpreadAttribute(node: any): void; + 'MemberExpression, OptionalMemberExpression'(node: any): void; + ObjectPattern(node: any): void; + 'Program:exit'(): void; +}; +export = _exports; +//# sourceMappingURL=usedPropTypes.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..eea2ef9a0fbee6f6969cd3642b209a6e23fa9593 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"usedPropTypes.d.ts","sourceRoot":"","sources":["usedPropTypes.js"],"names":[],"mappings":"AAiTiB;;gCA2JJ,OAAO;oCAAP,OAAO;+BAAP,OAAO;;;;;;;;EA2HnB"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js new file mode 100644 index 0000000000000000000000000000000000000000..41eb307d6bd5d7533b4d453f074ec63d9675cbb0 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/usedPropTypes.js @@ -0,0 +1,584 @@ +/** + * @fileoverview Common used propTypes detection functionality. + */ + +'use strict'; + +const values = require('object.values'); + +const astUtil = require('./ast'); +const componentUtil = require('./componentUtil'); +const testReactVersion = require('./version').testReactVersion; +const ast = require('./ast'); +const eslintUtil = require('./eslint'); + +const getScope = eslintUtil.getScope; +const getSourceCode = eslintUtil.getSourceCode; + +// ------------------------------------------------------------------------------ +// Constants +// ------------------------------------------------------------------------------ + +const LIFE_CYCLE_METHODS = ['componentWillReceiveProps', 'shouldComponentUpdate', 'componentWillUpdate', 'componentDidUpdate']; +const ASYNC_SAFE_LIFE_CYCLE_METHODS = ['getDerivedStateFromProps', 'getSnapshotBeforeUpdate', 'UNSAFE_componentWillReceiveProps', 'UNSAFE_componentWillUpdate']; + +function createPropVariables() { + /** @type {Map} Maps the variable to its definition. `props.a.b` is stored as `['a', 'b']` */ + let propVariables = new Map(); + let hasBeenWritten = false; + const stack = [{ propVariables, hasBeenWritten }]; + return { + pushScope() { + // popVariables is not copied until first write. + stack.push({ propVariables, hasBeenWritten: false }); + }, + popScope() { + stack.pop(); + propVariables = stack[stack.length - 1].propVariables; + hasBeenWritten = stack[stack.length - 1].hasBeenWritten; + }, + /** + * Add a variable name to the current scope + * @param {string} name + * @param {string[]} allNames Example: `props.a.b` should be formatted as `['a', 'b']` + * @returns {Map} + */ + set(name, allNames) { + if (!hasBeenWritten) { + // copy on write + propVariables = new Map(propVariables); + Object.assign(stack[stack.length - 1], { propVariables, hasBeenWritten: true }); + stack[stack.length - 1].hasBeenWritten = true; + } + return propVariables.set(name, allNames); + }, + /** + * Get the definition of a variable. + * @param {string} name + * @returns {string[]} Example: `props.a.b` is represented by `['a', 'b']` + */ + get(name) { + return propVariables.get(name); + }, + }; +} + +/** + * Checks if the string is one of `props`, `nextProps`, or `prevProps` + * @param {string} name The AST node being checked. + * @returns {boolean} True if the prop name matches + */ +function isCommonVariableNameForProps(name) { + return name === 'props' || name === 'nextProps' || name === 'prevProps'; +} + +/** + * Checks if the component must be validated + * @param {Object} component The component to process + * @returns {boolean} True if the component must be validated, false if not. + */ +function mustBeValidated(component) { + return !!(component && !component.ignorePropsValidation); +} + +/** + * Check if we are in a lifecycle method + * @param {object} context + * @param {ASTNode} node The AST node being checked. + * @param {boolean} checkAsyncSafeLifeCycles + * @return {boolean} true if we are in a class constructor, false if not + */ +function inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) { + let scope = getScope(context, node); + while (scope) { + if (scope.block && scope.block.parent && scope.block.parent.key) { + const name = scope.block.parent.key.name; + + if (LIFE_CYCLE_METHODS.indexOf(name) >= 0) { + return true; + } + if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(name) >= 0) { + return true; + } + } + scope = scope.upper; + } + return false; +} + +/** + * Returns true if the given node is a React Component lifecycle method + * @param {ASTNode} node The AST node being checked. + * @param {boolean} checkAsyncSafeLifeCycles + * @return {boolean} True if the node is a lifecycle method + */ +function isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles) { + if (node.key) { + if (node.kind === 'constructor') { + return true; + } + + const nodeKeyName = node.key.name; + + if (typeof nodeKeyName !== 'string') { + return false; + } + + if (LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { + return true; + } + if (checkAsyncSafeLifeCycles && ASYNC_SAFE_LIFE_CYCLE_METHODS.indexOf(nodeKeyName) >= 0) { + return true; + } + } + + return false; +} + +/** + * Returns true if the given node is inside a React Component lifecycle + * method. + * @param {ASTNode} node The AST node being checked. + * @param {boolean} checkAsyncSafeLifeCycles + * @return {boolean} True if the node is inside a lifecycle method + */ +function isInLifeCycleMethod(node, checkAsyncSafeLifeCycles) { + if ( + (node.type === 'MethodDefinition' || node.type === 'Property') + && isNodeALifeCycleMethod(node, checkAsyncSafeLifeCycles) + ) { + return true; + } + + if (node.parent) { + return isInLifeCycleMethod(node.parent, checkAsyncSafeLifeCycles); + } + + return false; +} + +/** + * Check if a function node is a setState updater + * @param {ASTNode} node a function node + * @return {boolean} + */ +function isSetStateUpdater(node) { + const unwrappedParentCalleeNode = astUtil.isCallExpression(node.parent) + && ast.unwrapTSAsExpression(node.parent.callee); + + return unwrappedParentCalleeNode + && unwrappedParentCalleeNode.property + && unwrappedParentCalleeNode.property.name === 'setState' + // Make sure we are in the updater not the callback + && node.parent.arguments[0] === node; +} + +function isPropArgumentInSetStateUpdater(context, node, name) { + if (typeof name !== 'string') { + return; + } + let scope = getScope(context, node); + while (scope) { + const unwrappedParentCalleeNode = scope.block + && astUtil.isCallExpression(scope.block.parent) + && ast.unwrapTSAsExpression(scope.block.parent.callee); + if ( + unwrappedParentCalleeNode + && unwrappedParentCalleeNode.property + && unwrappedParentCalleeNode.property.name === 'setState' + // Make sure we are in the updater not the callback + && scope.block.parent.arguments[0].range[0] === scope.block.range[0] + && scope.block.parent.arguments[0].params + && scope.block.parent.arguments[0].params.length > 1 + ) { + return scope.block.parent.arguments[0].params[1].name === name; + } + scope = scope.upper; + } + return false; +} + +/** + * @param {Context} context + * @param {ASTNode} node + * @returns {boolean} + */ +function isInClassComponent(context, node) { + return !!(componentUtil.getParentES6Component(context, node) || componentUtil.getParentES5Component(context, node)); +} + +/** + * Checks if the node is `this.props` + * @param {ASTNode|undefined} node + * @returns {boolean} + */ +function isThisDotProps(node) { + return !!node + && node.type === 'MemberExpression' + && ast.unwrapTSAsExpression(node.object).type === 'ThisExpression' + && node.property.name === 'props'; +} + +/** + * Checks if the prop has spread operator. + * @param {object} context + * @param {ASTNode} node The AST node being marked. + * @returns {boolean} True if the prop has spread operator, false if not. + */ +function hasSpreadOperator(context, node) { + const tokens = getSourceCode(context).getTokens(node); + return tokens.length && tokens[0].value === '...'; +} + +/** + * Checks if the node is a propTypes usage of the form `this.props.*`, `props.*`, `prevProps.*`, or `nextProps.*`. + * @param {Context} context + * @param {ASTNode} node + * @param {Object} utils + * @param {boolean} checkAsyncSafeLifeCycles + * @returns {boolean} + */ +function isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles) { + const unwrappedObjectNode = ast.unwrapTSAsExpression(node.object); + + if (isInClassComponent(context, node)) { + // this.props.* + if (isThisDotProps(unwrappedObjectNode)) { + return true; + } + // props.* or prevProps.* or nextProps.* + if ( + isCommonVariableNameForProps(unwrappedObjectNode.name) + && (inLifeCycleMethod(context, node, checkAsyncSafeLifeCycles) || astUtil.inConstructor(context, node)) + ) { + return true; + } + // this.setState((_, props) => props.*)) + if (isPropArgumentInSetStateUpdater(context, node, unwrappedObjectNode.name)) { + return true; + } + return false; + } + // props.* in function component + return unwrappedObjectNode.name === 'props' && !ast.isAssignmentLHS(node); +} + +/** + * Retrieve the name of a property node + * @param {Context} context + * @param {ASTNode} node The AST node with the property. + * @param {Object} utils + * @param {boolean} checkAsyncSafeLifeCycles + * @return {string|undefined} the name of the property or undefined if not found + */ +function getPropertyName(context, node, utils, checkAsyncSafeLifeCycles) { + const property = node.property; + if (property) { + switch (property.type) { + case 'Identifier': + if (node.computed) { + return '__COMPUTED_PROP__'; + } + return property.name; + case 'MemberExpression': + return; + case 'Literal': + // Accept computed properties that are literal strings + if (typeof property.value === 'string') { + return property.value; + } + // Accept number as well but only accept props[123] + if (typeof property.value === 'number') { + if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) { + return property.raw; + } + } + // falls through + default: + if (node.computed) { + return '__COMPUTED_PROP__'; + } + break; + } + } +} + +module.exports = function usedPropTypesInstructions(context, components, utils) { + const checkAsyncSafeLifeCycles = testReactVersion(context, '>= 16.3.0'); + + const propVariables = createPropVariables(); + const pushScope = propVariables.pushScope; + const popScope = propVariables.popScope; + + /** + * Mark a prop type as used + * @param {ASTNode} node The AST node being marked. + * @param {string[]} [parentNames] + */ + function markPropTypesAsUsed(node, parentNames) { + parentNames = parentNames || []; + let type; + let name; + let allNames; + let properties; + switch (node.type) { + case 'OptionalMemberExpression': + case 'MemberExpression': + name = getPropertyName(context, node, utils, checkAsyncSafeLifeCycles); + if (name) { + allNames = parentNames.concat(name); + if ( + // Match props.foo.bar, don't match bar[props.foo] + node.parent.type === 'MemberExpression' + && node.parent.object === node + ) { + markPropTypesAsUsed(node.parent, allNames); + } + // Handle the destructuring part of `const {foo} = props.a.b` + if ( + node.parent.type === 'VariableDeclarator' + && node.parent.id.type === 'ObjectPattern' + ) { + node.parent.id.parent = node.parent; // patch for bug in eslint@4 in which ObjectPattern has no parent + markPropTypesAsUsed(node.parent.id, allNames); + } + + // const a = props.a + if ( + node.parent.type === 'VariableDeclarator' + && node.parent.id.type === 'Identifier' + ) { + propVariables.set(node.parent.id.name, allNames); + } + // Do not mark computed props as used. + type = name !== '__COMPUTED_PROP__' ? 'direct' : null; + } + break; + case 'ArrowFunctionExpression': + case 'FunctionDeclaration': + case 'FunctionExpression': { + if (node.params.length === 0) { + break; + } + type = 'destructuring'; + const propParam = isSetStateUpdater(node) ? node.params[1] : node.params[0]; + properties = propParam.type === 'AssignmentPattern' + ? propParam.left.properties + : propParam.properties; + break; + } + case 'ObjectPattern': + type = 'destructuring'; + properties = node.properties; + break; + case 'TSEmptyBodyFunctionExpression': + break; + default: + throw new Error(`${node.type} ASTNodes are not handled by markPropTypesAsUsed`); + } + + const component = components.get(utils.getParentComponent(node)); + const usedPropTypes = (component && component.usedPropTypes) || []; + let ignoreUnusedPropTypesValidation = (component && component.ignoreUnusedPropTypesValidation) || false; + + switch (type) { + case 'direct': { + // Ignore Object methods + if (name in Object.prototype) { + break; + } + + const reportedNode = node.property; + usedPropTypes.push({ + name, + allNames, + node: reportedNode, + }); + break; + } + case 'destructuring': { + for (let k = 0, l = (properties || []).length; k < l; k++) { + if (hasSpreadOperator(context, properties[k]) || properties[k].computed) { + ignoreUnusedPropTypesValidation = true; + break; + } + const propName = ast.getKeyValue(context, properties[k]); + + if (!propName || properties[k].type !== 'Property') { + break; + } + + usedPropTypes.push({ + allNames: parentNames.concat([propName]), + name: propName, + node: properties[k], + }); + + if (properties[k].value.type === 'ObjectPattern') { + markPropTypesAsUsed(properties[k].value, parentNames.concat([propName])); + } else if (properties[k].value.type === 'Identifier') { + propVariables.set(properties[k].value.name, parentNames.concat(propName)); + } + } + break; + } + default: + break; + } + + components.set(component ? component.node : node, { + usedPropTypes, + ignoreUnusedPropTypesValidation, + }); + } + + /** + * @param {ASTNode} node We expect either an ArrowFunctionExpression, + * FunctionDeclaration, or FunctionExpression + */ + function markDestructuredFunctionArgumentsAsUsed(node) { + const param = node.params && isSetStateUpdater(node) ? node.params[1] : node.params[0]; + + const destructuring = param && ( + param.type === 'ObjectPattern' + || ((param.type === 'AssignmentPattern') && (param.left.type === 'ObjectPattern')) + ); + + if (destructuring && (components.get(node) || components.get(node.parent))) { + markPropTypesAsUsed(node); + } + } + + function handleSetStateUpdater(node) { + if (!node.params || node.params.length < 2 || !isSetStateUpdater(node)) { + return; + } + markPropTypesAsUsed(node); + } + + /** + * Handle both stateless functions and setState updater functions. + * @param {ASTNode} node We expect either an ArrowFunctionExpression, + * FunctionDeclaration, or FunctionExpression + */ + function handleFunctionLikeExpressions(node) { + pushScope(); + handleSetStateUpdater(node); + markDestructuredFunctionArgumentsAsUsed(node); + } + + function handleCustomValidators(component) { + const propTypes = component.declaredPropTypes; + if (!propTypes) { + return; + } + + Object.keys(propTypes).forEach((key) => { + const node = propTypes[key].node; + + if (node && node.value && astUtil.isFunctionLikeExpression(node.value)) { + markPropTypesAsUsed(node.value); + } + }); + } + + return { + VariableDeclarator(node) { + const unwrappedInitNode = ast.unwrapTSAsExpression(node.init); + + // let props = this.props + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node) && node.id.type === 'Identifier') { + propVariables.set(node.id.name, []); + } + + // Only handles destructuring + if (node.id.type !== 'ObjectPattern' || !unwrappedInitNode) { + return; + } + + // let {props: {firstname}} = this + const propsProperty = node.id.properties.find((property) => ( + property.key + && (property.key.name === 'props' || property.key.value === 'props') + )); + + if (unwrappedInitNode.type === 'ThisExpression' && propsProperty && propsProperty.value.type === 'ObjectPattern') { + markPropTypesAsUsed(propsProperty.value); + return; + } + + // let {props} = this + if (unwrappedInitNode.type === 'ThisExpression' && propsProperty && propsProperty.value.name === 'props') { + propVariables.set('props', []); + return; + } + + // let {firstname} = props + if ( + isCommonVariableNameForProps(unwrappedInitNode.name) + && (utils.getParentStatelessComponent(node) || isInLifeCycleMethod(node, checkAsyncSafeLifeCycles)) + ) { + markPropTypesAsUsed(node.id); + return; + } + + // let {firstname} = this.props + if (isThisDotProps(unwrappedInitNode) && isInClassComponent(context, node)) { + markPropTypesAsUsed(node.id); + return; + } + + // let {firstname} = thing, where thing is defined by const thing = this.props.**.* + if (propVariables.get(unwrappedInitNode.name)) { + markPropTypesAsUsed(node.id, propVariables.get(unwrappedInitNode.name)); + } + }, + + FunctionDeclaration: handleFunctionLikeExpressions, + + ArrowFunctionExpression: handleFunctionLikeExpressions, + + FunctionExpression: handleFunctionLikeExpressions, + + 'FunctionDeclaration:exit': popScope, + + 'ArrowFunctionExpression:exit': popScope, + + 'FunctionExpression:exit': popScope, + + JSXSpreadAttribute(node) { + const component = components.get(utils.getParentComponent(node)); + components.set(component ? component.node : node, { + ignoreUnusedPropTypesValidation: node.argument.type !== 'ObjectExpression', + }); + }, + + 'MemberExpression, OptionalMemberExpression'(node) { + if (isPropTypesUsageByMemberExpression(context, node, utils, checkAsyncSafeLifeCycles)) { + markPropTypesAsUsed(node); + return; + } + + const propVariable = propVariables.get(ast.unwrapTSAsExpression(node.object).name); + if (propVariable) { + markPropTypesAsUsed(node, propVariable); + } + }, + + ObjectPattern(node) { + // If the object pattern is a destructured props object in a lifecycle + // method -- mark it for used props. + if (isNodeALifeCycleMethod(node.parent.parent, checkAsyncSafeLifeCycles) && node.properties.length > 0) { + markPropTypesAsUsed(node.parent); + } + }, + + 'Program:exit'() { + values(components.list()) + .filter((component) => mustBeValidated(component)) + .forEach((component) => { + handleCustomValidators(component); + }); + }, + }; +}; diff --git a/node_modules/eslint-plugin-react/lib/util/variable.d.ts b/node_modules/eslint-plugin-react/lib/util/variable.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..de070fe91fca826218b1a81c26cf555713a1b5e6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/variable.d.ts @@ -0,0 +1,38 @@ +/** + * Search a particular variable in a list + * @param {Array} variables The variables list. + * @param {string} name The name of the variable to search. + * @returns {boolean} True if the variable was found, false if not. + */ +export function findVariable(variables: any[], name: string): boolean; +/** + * Find a variable by name in the current scope. + * @param {Object} context The current rule context. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @param {string} name Name of the variable to look for. + * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. + */ +export function findVariableByName(context: any, node: ASTNode, name: string): ASTNode | null; +/** + * Find and return a particular variable in a list + * @param {Array} variables The variables list. + * @param {string} name The name of the variable to search. + * @returns {Object} Variable if the variable was found, null if not. + */ +export function getVariable(variables: any[], name: string): any; +/** + * Searches for a variable in the given scope. + * + * @param {Object} context The current rule context. + * @param {ASTNode} node The node to start looking from. + * @param {string} name The name of the variable to search. + * @returns {Object | undefined} Variable if the variable was found, undefined if not. + */ +export function getVariableFromContext(context: any, node: ASTNode, name: string): any | undefined; +/** + * Returns the latest definition of the variable. + * @param {Object} variable + * @returns {Object | undefined} The latest variable definition or undefined. + */ +export function getLatestVariableDefinition(variable: any): any | undefined; +//# sourceMappingURL=variable.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map b/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..5187e71363c4bb4d530e9ebdfa14e7dde94f2cfa --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/variable.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"variable.d.ts","sourceRoot":"","sources":["variable.js"],"names":[],"mappings":"AASA;;;;;GAKG;AACH,qDAHW,MAAM,GACJ,OAAO,CAInB;AA0CD;;;;;;GAMG;AACH,uDAJW,OAAO,QACN,MAAM,GACL,OAAO,GAAC,IAAI,CAkBxB;AA/DD;;;;;GAKG;AACH,oDAHW,MAAM,OAKhB;AAED;;;;;;;GAOG;AACH,2DAJW,OAAO,QACP,MAAM,GACJ,MAAS,SAAS,CAsB9B;AA2BD;;;;GAIG;AACH,4DAFa,MAAS,SAAS,CAI9B"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/variable.js b/node_modules/eslint-plugin-react/lib/util/variable.js new file mode 100644 index 0000000000000000000000000000000000000000..a92a20930f24844a39ca2b07b2e5e868f59c215a --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/variable.js @@ -0,0 +1,100 @@ +/** + * @fileoverview Utility functions for React components detection + * @author Yannick Croissant + */ + +'use strict'; + +const getScope = require('./eslint').getScope; + +/** + * Search a particular variable in a list + * @param {Array} variables The variables list. + * @param {string} name The name of the variable to search. + * @returns {boolean} True if the variable was found, false if not. + */ +function findVariable(variables, name) { + return variables.some((variable) => variable.name === name); +} + +/** + * Find and return a particular variable in a list + * @param {Array} variables The variables list. + * @param {string} name The name of the variable to search. + * @returns {Object} Variable if the variable was found, null if not. + */ +function getVariable(variables, name) { + return variables.find((variable) => variable.name === name); +} + +/** + * Searches for a variable in the given scope. + * + * @param {Object} context The current rule context. + * @param {ASTNode} node The node to start looking from. + * @param {string} name The name of the variable to search. + * @returns {Object | undefined} Variable if the variable was found, undefined if not. + */ +function getVariableFromContext(context, node, name) { + let scope = getScope(context, node); + + while (scope) { + let variable = getVariable(scope.variables, name); + + if (!variable && scope.childScopes.length) { + variable = getVariable(scope.childScopes[0].variables, name); + + if (!variable && scope.childScopes[0].childScopes.length) { + variable = getVariable(scope.childScopes[0].childScopes[0].variables, name); + } + } + + if (variable) { + return variable; + } + scope = scope.upper; + } + return undefined; +} + +/** + * Find a variable by name in the current scope. + * @param {Object} context The current rule context. + * @param {ASTNode} node The node to check. Must be an Identifier node. + * @param {string} name Name of the variable to look for. + * @returns {ASTNode|null} Return null if the variable could not be found, ASTNode otherwise. + */ +function findVariableByName(context, node, name) { + const variable = getVariableFromContext(context, node, name); + + if (!variable || !variable.defs[0] || !variable.defs[0].node) { + return null; + } + + if (variable.defs[0].node.type === 'TypeAlias') { + return variable.defs[0].node.right; + } + + if (variable.defs[0].type === 'ImportBinding') { + return variable.defs[0].node; + } + + return variable.defs[0].node.init; +} + +/** + * Returns the latest definition of the variable. + * @param {Object} variable + * @returns {Object | undefined} The latest variable definition or undefined. + */ +function getLatestVariableDefinition(variable) { + return variable.defs[variable.defs.length - 1]; +} + +module.exports = { + findVariable, + findVariableByName, + getVariable, + getVariableFromContext, + getLatestVariableDefinition, +}; diff --git a/node_modules/eslint-plugin-react/lib/util/version.d.ts b/node_modules/eslint-plugin-react/lib/util/version.d.ts new file mode 100644 index 0000000000000000000000000000000000000000..e423fc38d766a8c8ac2180519fb07ba844f285f8 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/version.d.ts @@ -0,0 +1,6 @@ +export function testReactVersion(context: any, semverRange: any): boolean; +export function testFlowVersion(context: any, semverRange: any): boolean; +export function resetWarningFlag(): void; +export function resetDetectedVersion(): void; +export function resetDefaultVersion(): void; +//# sourceMappingURL=version.d.ts.map \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/version.d.ts.map b/node_modules/eslint-plugin-react/lib/util/version.d.ts.map new file mode 100644 index 0000000000000000000000000000000000000000..e9d69637bc0629745cbe07ca50cf907f6dc36771 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/version.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"version.d.ts","sourceRoot":"","sources":["version.js"],"names":[],"mappings":"AAmLA,0EAEC;AAED,yEAEC;AAvKD,yCAEC;AAID,6CAEC;AA6BD,4CAEC"} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/lib/util/version.js b/node_modules/eslint-plugin-react/lib/util/version.js new file mode 100644 index 0000000000000000000000000000000000000000..c9dc683b7d149c5a3a941bdcbd32f657facdfbf6 --- /dev/null +++ b/node_modules/eslint-plugin-react/lib/util/version.js @@ -0,0 +1,194 @@ +/** + * @fileoverview Utility functions for React and Flow version configuration + * @author Yannick Croissant + */ + +'use strict'; + +const fs = require('fs'); +const path = require('path'); + +const resolve = require('resolve'); +const semver = require('semver'); +const error = require('./error'); + +const ULTIMATE_LATEST_SEMVER = '999.999.999'; + +let warnedForMissingVersion = false; + +function resetWarningFlag() { + warnedForMissingVersion = false; +} + +let cachedDetectedReactVersion; + +function resetDetectedVersion() { + cachedDetectedReactVersion = undefined; +} + +function resolveBasedir(contextOrFilename) { + if (contextOrFilename) { + const filename = typeof contextOrFilename === 'string' ? contextOrFilename : contextOrFilename.getFilename(); + const dirname = path.dirname(filename); + try { + if (fs.statSync(filename).isFile()) { + // dirname must be dir here + return dirname; + } + } catch (err) { + // https://github.com/eslint/eslint/issues/11989 + if (err.code === 'ENOTDIR') { + // virtual filename could be recursive + return resolveBasedir(dirname); + } + } + } + return process.cwd(); +} + +function convertConfVerToSemver(confVer) { + const fullSemverString = /^[0-9]+\.[0-9]+$/.test(confVer) ? `${confVer}.0` : confVer; + return semver.coerce(fullSemverString.split('.').map((part) => Number(part)).join('.')); +} + +let defaultVersion = ULTIMATE_LATEST_SEMVER; + +function resetDefaultVersion() { + defaultVersion = ULTIMATE_LATEST_SEMVER; +} + +function readDefaultReactVersionFromContext(context) { + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + if (context.settings && context.settings.react && context.settings.react.defaultVersion) { + let settingsDefaultVersion = context.settings.react.defaultVersion; + if (typeof settingsDefaultVersion !== 'string') { + error(`Warning: default React version specified in eslint-pluigin-react-settings must be a string; got "${typeof settingsDefaultVersion}"`); + } + settingsDefaultVersion = String(settingsDefaultVersion); + const result = convertConfVerToSemver(settingsDefaultVersion); + if (result) { + defaultVersion = result.version; + } else { + error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${settingsDefaultVersion}”. Falling back to latest version as default.`); + } + } else { + defaultVersion = ULTIMATE_LATEST_SEMVER; + } +} + +// TODO, semver-major: remove context fallback +function detectReactVersion(context) { + if (cachedDetectedReactVersion) { + return cachedDetectedReactVersion; + } + + const basedir = resolveBasedir(context); + + try { + const reactPath = resolve.sync('react', { basedir }); + const react = require(reactPath); // eslint-disable-line global-require, import/no-dynamic-require + cachedDetectedReactVersion = react.version; + return cachedDetectedReactVersion; + } catch (e) { + if (e.code === 'MODULE_NOT_FOUND') { + if (!warnedForMissingVersion) { + let sentence2 = 'Assuming latest React version for linting.'; + if (defaultVersion !== ULTIMATE_LATEST_SEMVER) { + sentence2 = `Assuming default React version for linting: "${defaultVersion}".`; + } + error(`Warning: React version was set to "detect" in eslint-plugin-react settings, but the "react" package is not installed. ${sentence2}`); + warnedForMissingVersion = true; + } + cachedDetectedReactVersion = defaultVersion; + return cachedDetectedReactVersion; + } + throw e; + } +} + +function getReactVersionFromContext(context) { + readDefaultReactVersionFromContext(context); + let confVer = defaultVersion; + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + if (context.settings && context.settings.react && context.settings.react.version) { + let settingsVersion = context.settings.react.version; + if (settingsVersion === 'detect') { + settingsVersion = detectReactVersion(context); + } + if (typeof settingsVersion !== 'string') { + error(`Warning: React version specified in eslint-plugin-react-settings must be a string; got “${typeof settingsVersion}”`); + } + confVer = String(settingsVersion); + } else if (!warnedForMissingVersion) { + error('Warning: React version not specified in eslint-plugin-react settings. See https://github.com/jsx-eslint/eslint-plugin-react#configuration .'); + warnedForMissingVersion = true; + } + + const result = convertConfVerToSemver(confVer); + if (!result) { + error(`Warning: React version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`); + } + return result ? result.version : defaultVersion; +} + +// TODO, semver-major: remove context fallback +function detectFlowVersion(context) { + const basedir = resolveBasedir(context); + + try { + const flowPackageJsonPath = resolve.sync('flow-bin/package.json', { basedir }); + const flowPackageJson = require(flowPackageJsonPath); // eslint-disable-line global-require, import/no-dynamic-require + return flowPackageJson.version; + } catch (e) { + if (e.code === 'MODULE_NOT_FOUND') { + error('Warning: Flow version was set to "detect" in eslint-plugin-react settings, ' + + 'but the "flow-bin" package is not installed. Assuming latest Flow version for linting.'); + return ULTIMATE_LATEST_SEMVER; + } + throw e; + } +} + +function getFlowVersionFromContext(context) { + let confVer = defaultVersion; + // .eslintrc shared settings (https://eslint.org/docs/user-guide/configuring#adding-shared-settings) + if (context.settings.react && context.settings.react.flowVersion) { + let flowVersion = context.settings.react.flowVersion; + if (flowVersion === 'detect') { + flowVersion = detectFlowVersion(context); + } + if (typeof flowVersion !== 'string') { + error('Warning: Flow version specified in eslint-plugin-react-settings must be a string; ' + + `got “${typeof flowVersion}”`); + } + confVer = String(flowVersion); + } else { + throw 'Could not retrieve flowVersion from settings'; // eslint-disable-line no-throw-literal + } + + const result = convertConfVerToSemver(confVer); + if (!result) { + error(`Warning: Flow version specified in eslint-plugin-react-settings must be a valid semver version, or "detect"; got “${confVer}”`); + } + return result ? result.version : defaultVersion; +} + +function test(semverRange, confVer) { + return semver.satisfies(confVer, semverRange); +} + +function testReactVersion(context, semverRange) { + return test(semverRange, getReactVersionFromContext(context)); +} + +function testFlowVersion(context, semverRange) { + return test(semverRange, getFlowVersionFromContext(context)); +} + +module.exports = { + testReactVersion, + testFlowVersion, + resetWarningFlag, + resetDetectedVersion, + resetDefaultVersion, +}; diff --git a/node_modules/eslint-plugin-react/node_modules/.bin/resolve b/node_modules/eslint-plugin-react/node_modules/.bin/resolve new file mode 100644 index 0000000000000000000000000000000000000000..21d1a87eec1fe1557a535fc0cdfd877908cb5c71 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/.bin/resolve @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +'use strict'; + +var path = require('path'); +var fs = require('fs'); + +if ( + String(process.env.npm_lifecycle_script).slice(0, 8) !== 'resolve ' + && ( + !process.argv + || process.argv.length < 2 + || (process.argv[1] !== __filename && fs.statSync(process.argv[1]).ino !== fs.statSync(__filename).ino) + || (process.env.npm_lifecycle_event !== 'npx' && process.env._ && fs.realpathSync(path.resolve(process.env._)) !== __filename) + ) +) { + console.error('Error: `resolve` must be run directly as an executable'); + process.exit(1); +} + +var supportsPreserveSymlinkFlag = require('supports-preserve-symlinks-flag'); + +var preserveSymlinks = false; +for (var i = 2; i < process.argv.length; i += 1) { + if (process.argv[i].slice(0, 2) === '--') { + if (supportsPreserveSymlinkFlag && process.argv[i] === '--preserve-symlinks') { + preserveSymlinks = true; + } else if (process.argv[i].length > 2) { + console.error('Unknown argument ' + process.argv[i].replace(/[=].*$/, '')); + process.exit(2); + } + process.argv.splice(i, 1); + i -= 1; + if (process.argv[i] === '--') { break; } // eslint-disable-line no-restricted-syntax + } +} + +if (process.argv.length < 3) { + console.error('Error: `resolve` expects a specifier'); + process.exit(2); +} + +var resolve = require('../'); + +var result = resolve.sync(process.argv[2], { + basedir: process.cwd(), + preserveSymlinks: preserveSymlinks +}); + +console.log(result); diff --git a/node_modules/eslint-plugin-react/node_modules/.bin/semver b/node_modules/eslint-plugin-react/node_modules/.bin/semver new file mode 100644 index 0000000000000000000000000000000000000000..666034a75d8442be9bb2d9c55c3909b55a093cb5 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/.bin/semver @@ -0,0 +1,174 @@ +#!/usr/bin/env node +// Standalone semver comparison program. +// Exits successfully and prints matching version(s) if +// any supplied version is valid and passes all tests. + +var argv = process.argv.slice(2) + +var versions = [] + +var range = [] + +var inc = null + +var version = require('../package.json').version + +var loose = false + +var includePrerelease = false + +var coerce = false + +var rtl = false + +var identifier + +var semver = require('../semver') + +var reverse = false + +var options = {} + +main() + +function main () { + if (!argv.length) return help() + while (argv.length) { + var a = argv.shift() + var indexOfEqualSign = a.indexOf('=') + if (indexOfEqualSign !== -1) { + a = a.slice(0, indexOfEqualSign) + argv.unshift(a.slice(indexOfEqualSign + 1)) + } + switch (a) { + case '-rv': case '-rev': case '--rev': case '--reverse': + reverse = true + break + case '-l': case '--loose': + loose = true + break + case '-p': case '--include-prerelease': + includePrerelease = true + break + case '-v': case '--version': + versions.push(argv.shift()) + break + case '-i': case '--inc': case '--increment': + switch (argv[0]) { + case 'major': case 'minor': case 'patch': case 'prerelease': + case 'premajor': case 'preminor': case 'prepatch': + inc = argv.shift() + break + default: + inc = 'patch' + break + } + break + case '--preid': + identifier = argv.shift() + break + case '-r': case '--range': + range.push(argv.shift()) + break + case '-c': case '--coerce': + coerce = true + break + case '--rtl': + rtl = true + break + case '--ltr': + rtl = false + break + case '-h': case '--help': case '-?': + return help() + default: + versions.push(a) + break + } + } + + var options = { loose: loose, includePrerelease: includePrerelease, rtl: rtl } + + versions = versions.map(function (v) { + return coerce ? (semver.coerce(v, options) || { version: v }).version : v + }).filter(function (v) { + return semver.valid(v) + }) + if (!versions.length) return fail() + if (inc && (versions.length !== 1 || range.length)) { return failInc() } + + for (var i = 0, l = range.length; i < l; i++) { + versions = versions.filter(function (v) { + return semver.satisfies(v, range[i], options) + }) + if (!versions.length) return fail() + } + return success(versions) +} + +function failInc () { + console.error('--inc can only be used on a single version with no range') + fail() +} + +function fail () { process.exit(1) } + +function success () { + var compare = reverse ? 'rcompare' : 'compare' + versions.sort(function (a, b) { + return semver[compare](a, b, options) + }).map(function (v) { + return semver.clean(v, options) + }).map(function (v) { + return inc ? semver.inc(v, inc, options, identifier) : v + }).forEach(function (v, i, _) { console.log(v) }) +} + +function help () { + console.log(['SemVer ' + version, + '', + 'A JavaScript implementation of the https://semver.org/ specification', + 'Copyright Isaac Z. Schlueter', + '', + 'Usage: semver [options] [ [...]]', + 'Prints valid versions sorted by SemVer precedence', + '', + 'Options:', + '-r --range ', + ' Print versions that match the specified range.', + '', + '-i --increment []', + ' Increment a version by the specified level. Level can', + ' be one of: major, minor, patch, premajor, preminor,', + " prepatch, or prerelease. Default level is 'patch'.", + ' Only one version may be specified.', + '', + '--preid ', + ' Identifier to be used to prefix premajor, preminor,', + ' prepatch or prerelease version increments.', + '', + '-l --loose', + ' Interpret versions and ranges loosely', + '', + '-p --include-prerelease', + ' Always include prerelease versions in range matching', + '', + '-c --coerce', + ' Coerce a string into SemVer if possible', + ' (does not imply --loose)', + '', + '--rtl', + ' Coerce version strings right to left', + '', + '--ltr', + ' Coerce version strings left to right (default)', + '', + 'Program exits successfully if any valid version satisfies', + 'all supplied ranges, and prints all satisfying versions.', + '', + 'If no satisfying versions are found, then exits failure.', + '', + 'Versions are printed in ascending order, so supplying', + 'multiple versions to the utility will just sort them.' + ].join('\n')) +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig b/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig new file mode 100644 index 0000000000000000000000000000000000000000..a4eb76e1d7ce44b87bbd3145ed2cb8c88d57d191 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/.editorconfig @@ -0,0 +1,34 @@ +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true +max_line_length = 200 + +[*.js] +block_comment_start = /* +block_comment = * +block_comment_end = */ + +[*.yml] +indent_size = 1 + +[package.json] +indent_style = tab + +[CHANGELOG.md] +indent_style = space +indent_size = 2 + +[{*.json,Makefile}] +max_line_length = unset + +[test/{dotdot,resolver,module_dir,multirepo,node_path,pathfilter,precedence}/**/*] +indent_style = unset +indent_size = unset +max_line_length = unset +insert_final_newline = unset diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/.eslintrc b/node_modules/eslint-plugin-react/node_modules/resolve/.eslintrc new file mode 100644 index 0000000000000000000000000000000000000000..1cc95e4643561308da9195f05b957df484f03e14 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/.eslintrc @@ -0,0 +1,65 @@ +{ + "root": true, + + "extends": "@ljharb", + + "rules": { + "indent": [2, 4], + "strict": 0, + "complexity": 0, + "consistent-return": 0, + "curly": 0, + "dot-notation": [2, { "allowKeywords": true }], + "func-name-matching": 0, + "func-style": 0, + "global-require": 1, + "id-length": [2, { "min": 1, "max": 40 }], + "max-lines": [2, 350], + "max-lines-per-function": 1, + "max-nested-callbacks": 0, + "max-params": 0, + "max-statements-per-line": [2, { "max": 2 }], + "max-statements": 0, + "no-magic-numbers": 0, + "no-shadow": 0, + "no-use-before-define": 0, + "sort-keys": 0, + }, + "overrides": [ + { + "files": "bin/**", + "rules": { + "no-process-exit": "off", + }, + }, + { + "files": "example/**", + "rules": { + "no-console": 0, + }, + }, + { + "files": "test/resolver/nested_symlinks/mylib/*.js", + "rules": { + "no-throw-literal": 0, + }, + }, + { + "files": "test/**", + "parserOptions": { + "ecmaVersion": 5, + "allowReserved": false, + }, + "rules": { + "dot-notation": [2, { "allowPattern": "throws" }], + "max-lines": 0, + "max-lines-per-function": 0, + "no-unused-vars": [2, { "vars": "all", "args": "none" }], + }, + }, + ], + + "ignorePatterns": [ + "./test/resolver/malformed_package_json/package.json", + ], +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml b/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml new file mode 100644 index 0000000000000000000000000000000000000000..d9c05955459660bd6b6a972499502b8d09958220 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/.github/FUNDING.yml @@ -0,0 +1,12 @@ +# These are supported funding model platforms + +github: [ljharb] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: npm/resolve +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +otechie: # Replace with a single Otechie username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE b/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..ff4fce28af33a4504d6960856a0bd603860a62e0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2012 James Halliday + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/SECURITY.md b/node_modules/eslint-plugin-react/node_modules/resolve/SECURITY.md new file mode 100644 index 0000000000000000000000000000000000000000..82e4285adc6285693cd6c06af02d606e09dd8fcd --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/SECURITY.md @@ -0,0 +1,3 @@ +# Security + +Please email [@ljharb](https://github.com/ljharb) or see https://tidelift.com/security if you have a potential security vulnerability to report. diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/async.js b/node_modules/eslint-plugin-react/node_modules/resolve/async.js new file mode 100644 index 0000000000000000000000000000000000000000..f38c5813ebd04ab8bf6988cc0186b9f95433f7e2 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/async.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./lib/async'); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve b/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve new file mode 100644 index 0000000000000000000000000000000000000000..21d1a87eec1fe1557a535fc0cdfd877908cb5c71 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/bin/resolve @@ -0,0 +1,50 @@ +#!/usr/bin/env node + +'use strict'; + +var path = require('path'); +var fs = require('fs'); + +if ( + String(process.env.npm_lifecycle_script).slice(0, 8) !== 'resolve ' + && ( + !process.argv + || process.argv.length < 2 + || (process.argv[1] !== __filename && fs.statSync(process.argv[1]).ino !== fs.statSync(__filename).ino) + || (process.env.npm_lifecycle_event !== 'npx' && process.env._ && fs.realpathSync(path.resolve(process.env._)) !== __filename) + ) +) { + console.error('Error: `resolve` must be run directly as an executable'); + process.exit(1); +} + +var supportsPreserveSymlinkFlag = require('supports-preserve-symlinks-flag'); + +var preserveSymlinks = false; +for (var i = 2; i < process.argv.length; i += 1) { + if (process.argv[i].slice(0, 2) === '--') { + if (supportsPreserveSymlinkFlag && process.argv[i] === '--preserve-symlinks') { + preserveSymlinks = true; + } else if (process.argv[i].length > 2) { + console.error('Unknown argument ' + process.argv[i].replace(/[=].*$/, '')); + process.exit(2); + } + process.argv.splice(i, 1); + i -= 1; + if (process.argv[i] === '--') { break; } // eslint-disable-line no-restricted-syntax + } +} + +if (process.argv.length < 3) { + console.error('Error: `resolve` expects a specifier'); + process.exit(2); +} + +var resolve = require('../'); + +var result = resolve.sync(process.argv[2], { + basedir: process.cwd(), + preserveSymlinks: preserveSymlinks +}); + +console.log(result); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js b/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js new file mode 100644 index 0000000000000000000000000000000000000000..20e65dc281dbaa3947a1c5d1b1a0bc94167c2073 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/example/async.js @@ -0,0 +1,5 @@ +var resolve = require('../'); +resolve('tap', { basedir: __dirname }, function (err, res) { + if (err) console.error(err); + else console.log(res); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js new file mode 100644 index 0000000000000000000000000000000000000000..54b2cc1004223d114e38bbd46fe9a0f5b73ca7e2 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/example/sync.js @@ -0,0 +1,3 @@ +var resolve = require('../'); +var res = resolve.sync('tap', { basedir: __dirname }); +console.log(res); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/index.js new file mode 100644 index 0000000000000000000000000000000000000000..46746e47d77e865093c907d123f2115dd6cd8ae0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/index.js @@ -0,0 +1,4 @@ +var async = require('./lib/async'); +async.sync = require('./lib/sync'); + +module.exports = async; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs b/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs new file mode 100644 index 0000000000000000000000000000000000000000..875341347183ecd78c0faae0c63c5ba5e481bf25 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/index.mjs @@ -0,0 +1,4 @@ +import async from 'resolve/async'; +import sync from 'resolve/sync'; + +export { async, sync }; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js new file mode 100644 index 0000000000000000000000000000000000000000..07f18a680437e58a7a2ae7c7f5f747da4375656f --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/async.js @@ -0,0 +1,349 @@ +var fs = require('fs'); +var getHomedir = require('./homedir'); +var path = require('path'); +var caller = require('./caller'); +var nodeModulesPaths = require('./node-modules-paths'); +var normalizeOptions = require('./normalize-options'); +var isCore = require('is-core-module'); + +var realpathFS = process.platform !== 'win32' && fs.realpath && typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; + +var homedir = getHomedir(); +var defaultPaths = function () { + return [ + path.join(homedir, '.node_modules'), + path.join(homedir, '.node_libraries') + ]; +}; + +var defaultIsFile = function isFile(file, cb) { + fs.stat(file, function (err, stat) { + if (!err) { + return cb(null, stat.isFile() || stat.isFIFO()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); +}; + +var defaultIsDir = function isDirectory(dir, cb) { + fs.stat(dir, function (err, stat) { + if (!err) { + return cb(null, stat.isDirectory()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); +}; + +var defaultRealpath = function realpath(x, cb) { + realpathFS(x, function (realpathErr, realPath) { + if (realpathErr && realpathErr.code !== 'ENOENT') cb(realpathErr); + else cb(null, realpathErr ? x : realPath); + }); +}; + +var maybeRealpath = function maybeRealpath(realpath, x, opts, cb) { + if (!opts || !opts.preserveSymlinks) { + realpath(x, cb); + } else { + cb(null, x); + } +}; + +var defaultReadPackage = function defaultReadPackage(readFile, pkgfile, cb) { + readFile(pkgfile, function (readFileErr, body) { + if (readFileErr) cb(readFileErr); + else { + try { + var pkg = JSON.parse(body); + cb(null, pkg); + } catch (jsonErr) { + cb(jsonErr); + } + } + }); +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + +module.exports = function resolve(x, options, callback) { + var cb = callback; + var opts = options; + if (typeof options === 'function') { + cb = opts; + opts = {}; + } + if (typeof x !== 'string') { + var err = new TypeError('Path must be a string.'); + return process.nextTick(function () { + cb(err); + }); + } + + opts = normalizeOptions(x, opts); + + var isFile = opts.isFile || defaultIsFile; + var isDirectory = opts.isDirectory || defaultIsDir; + var readFile = opts.readFile || fs.readFile; + var realpath = opts.realpath || defaultRealpath; + var readPackage = opts.readPackage || defaultReadPackage; + if (opts.readFile && opts.readPackage) { + var conflictErr = new TypeError('`readFile` and `readPackage` are mutually exclusive.'); + return process.nextTick(function () { + cb(conflictErr); + }); + } + var packageIterator = opts.packageIterator; + + var extensions = opts.extensions || ['.js']; + var includeCoreModules = opts.includeCoreModules !== false; + var basedir = opts.basedir || path.dirname(caller()); + var parent = opts.filename || basedir; + + opts.paths = opts.paths || defaultPaths(); + + // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory + var absoluteStart = path.resolve(basedir); + + maybeRealpath( + realpath, + absoluteStart, + opts, + function (err, realStart) { + if (err) cb(err); + else validateBasedir(realStart); + } + ); + + function validateBasedir(basedir) { + if (opts.basedir) { + var dirError = new TypeError('Provided basedir "' + basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory')); + dirError.code = 'INVALID_BASEDIR'; + isDirectory(basedir, function (err, result) { + if (err) return cb(err); + if (!result) { return cb(dirError); } + validBasedir(basedir); + }); + } else { + validBasedir(basedir); + } + } + + var res; + function validBasedir(basedir) { + if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { + res = path.resolve(basedir, x); + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; + if ((/\/$/).test(x) && res === basedir) { + loadAsDirectory(res, opts.package, onfile); + } else loadAsFile(res, opts.package, onfile); + } else if (includeCoreModules && isCore(x)) { + return cb(null, x); + } else loadNodeModules(x, basedir, function (err, n, pkg) { + if (err) cb(err); + else if (n) { + return maybeRealpath(realpath, n, opts, function (err, realN) { + if (err) { + cb(err); + } else { + cb(null, realN, pkg); + } + }); + } else { + var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + moduleError.code = 'MODULE_NOT_FOUND'; + cb(moduleError); + } + }); + } + + function onfile(err, m, pkg) { + if (err) cb(err); + else if (m) cb(null, m, pkg); + else loadAsDirectory(res, function (err, d, pkg) { + if (err) cb(err); + else if (d) { + maybeRealpath(realpath, d, opts, function (err, realD) { + if (err) { + cb(err); + } else { + cb(null, realD, pkg); + } + }); + } else { + var moduleError = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + moduleError.code = 'MODULE_NOT_FOUND'; + cb(moduleError); + } + }); + } + + function loadAsFile(x, thePackage, callback) { + var loadAsFilePackage = thePackage; + var cb = callback; + if (typeof loadAsFilePackage === 'function') { + cb = loadAsFilePackage; + loadAsFilePackage = undefined; + } + + var exts = [''].concat(extensions); + load(exts, x, loadAsFilePackage); + + function load(exts, x, loadPackage) { + if (exts.length === 0) return cb(null, undefined, loadPackage); + var file = x + exts[0]; + + var pkg = loadPackage; + if (pkg) onpkg(null, pkg); + else loadpkg(path.dirname(file), onpkg); + + function onpkg(err, pkg_, dir) { + pkg = pkg_; + if (err) return cb(err); + if (dir && pkg && opts.pathFilter) { + var rfile = path.relative(dir, file); + var rel = rfile.slice(0, rfile.length - exts[0].length); + var r = opts.pathFilter(pkg, x, rel); + if (r) return load( + [''].concat(extensions.slice()), + path.resolve(dir, r), + pkg + ); + } + isFile(file, onex); + } + function onex(err, ex) { + if (err) return cb(err); + if (ex) return cb(null, file, pkg); + load(exts.slice(1), x, pkg); + } + } + } + + function loadpkg(dir, cb) { + if (dir === '' || dir === '/') return cb(null); + if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { + return cb(null); + } + if ((/[/\\]node_modules[/\\]*$/).test(dir)) return cb(null); + + maybeRealpath(realpath, dir, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return loadpkg(path.dirname(dir), cb); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { + // on err, ex is false + if (!ex) return loadpkg(path.dirname(dir), cb); + + readPackage(readFile, pkgfile, function (err, pkgParam) { + if (err && !(err instanceof SyntaxError)) cb(err); + + var pkg = pkgParam; + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile, dir); + } + cb(null, pkg, dir); + }); + }); + }); + } + + function loadAsDirectory(x, loadAsDirectoryPackage, callback) { + var cb = callback; + var fpkg = loadAsDirectoryPackage; + if (typeof fpkg === 'function') { + cb = fpkg; + fpkg = opts.package; + } + + maybeRealpath(realpath, x, opts, function (unwrapErr, pkgdir) { + if (unwrapErr) return loadAsDirectory(path.dirname(x), fpkg, cb); + var pkgfile = path.join(pkgdir, 'package.json'); + isFile(pkgfile, function (err, ex) { + if (err) return cb(err); + if (!ex) return loadAsFile(path.join(x, 'index'), fpkg, cb); + + readPackage(readFile, pkgfile, function (err, pkgParam) { + if (err) return cb(err); + + var pkg = pkgParam; + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile, pkgdir); + } + + if (pkg && pkg.main) { + if (typeof pkg.main !== 'string') { + var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); + mainError.code = 'INVALID_PACKAGE_MAIN'; + return cb(mainError); + } + if (pkg.main === '.' || pkg.main === './') { + pkg.main = 'index'; + } + loadAsFile(path.resolve(x, pkg.main), pkg, function (err, m, pkg) { + if (err) return cb(err); + if (m) return cb(null, m, pkg); + if (!pkg) return loadAsFile(path.join(x, 'index'), pkg, cb); + + var dir = path.resolve(x, pkg.main); + loadAsDirectory(dir, pkg, function (err, n, pkg) { + if (err) return cb(err); + if (n) return cb(null, n, pkg); + loadAsFile(path.join(x, 'index'), pkg, function (err, m, pkg) { + if (err) return cb(err); + if (m) return cb(null, m, pkg); + var incorrectMainError = new Error("Cannot find module '" + path.resolve(x, pkg.main) + "'. Please verify that the package.json has a valid \"main\" entry"); + incorrectMainError.code = 'INCORRECT_PACKAGE_MAIN'; + return cb(incorrectMainError); + }); + }); + }); + return; + } + + loadAsFile(path.join(x, '/index'), pkg, cb); + }); + }); + }); + } + + function processDirs(cb, dirs) { + if (dirs.length === 0) return cb(null, undefined); + var dir = dirs[0]; + + isDirectory(path.dirname(dir), isdir); + + function isdir(err, isdir) { + if (err) return cb(err); + if (!isdir) return processDirs(cb, dirs.slice(1)); + loadAsFile(dir, opts.package, onfile); + } + + function onfile(err, m, pkg) { + if (err) return cb(err); + if (m) return cb(null, m, pkg); + loadAsDirectory(dir, opts.package, ondir); + } + + function ondir(err, n, pkg) { + if (err) return cb(err); + if (n) return cb(null, n, pkg); + processDirs(cb, dirs.slice(1)); + } + } + function loadNodeModules(x, start, cb) { + var thunk = function () { return getPackageCandidates(x, start, opts); }; + processDirs( + cb, + packageIterator ? packageIterator(x, start, thunk, opts) : thunk() + ); + } +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js new file mode 100644 index 0000000000000000000000000000000000000000..b14a2804ae828a4c39c9f949611236cd6ef7a45d --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/caller.js @@ -0,0 +1,8 @@ +module.exports = function () { + // see https://code.google.com/p/v8/wiki/JavaScriptStackTraceApi + var origPrepareStackTrace = Error.prepareStackTrace; + Error.prepareStackTrace = function (_, stack) { return stack; }; + var stack = (new Error()).stack; + Error.prepareStackTrace = origPrepareStackTrace; + return stack[2].getFileName(); +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js new file mode 100644 index 0000000000000000000000000000000000000000..5ffdf73bb336aea8649f285547a6f08f779a7be1 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/homedir.js @@ -0,0 +1,24 @@ +'use strict'; + +var os = require('os'); + +// adapted from https://github.com/sindresorhus/os-homedir/blob/11e089f4754db38bb535e5a8416320c4446e8cfd/index.js + +module.exports = os.homedir || function homedir() { + var home = process.env.HOME; + var user = process.env.LOGNAME || process.env.USER || process.env.LNAME || process.env.USERNAME; + + if (process.platform === 'win32') { + return process.env.USERPROFILE || process.env.HOMEDRIVE + process.env.HOMEPATH || home || null; + } + + if (process.platform === 'darwin') { + return home || (user ? '/Users/' + user : null); + } + + if (process.platform === 'linux') { + return home || (process.getuid() === 0 ? '/root' : (user ? '/home/' + user : null)); // eslint-disable-line no-extra-parens + } + + return home || null; +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js new file mode 100644 index 0000000000000000000000000000000000000000..1cff0107b5862c3cf080030ac3eeb8244e7328b5 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/node-modules-paths.js @@ -0,0 +1,42 @@ +var path = require('path'); +var parse = path.parse || require('path-parse'); // eslint-disable-line global-require + +var getNodeModulesDirs = function getNodeModulesDirs(absoluteStart, modules) { + var prefix = '/'; + if ((/^([A-Za-z]:)/).test(absoluteStart)) { + prefix = ''; + } else if ((/^\\\\/).test(absoluteStart)) { + prefix = '\\\\'; + } + + var paths = [absoluteStart]; + var parsed = parse(absoluteStart); + while (parsed.dir !== paths[paths.length - 1]) { + paths.push(parsed.dir); + parsed = parse(parsed.dir); + } + + return paths.reduce(function (dirs, aPath) { + return dirs.concat(modules.map(function (moduleDir) { + return path.resolve(prefix, aPath, moduleDir); + })); + }, []); +}; + +module.exports = function nodeModulesPaths(start, opts, request) { + var modules = opts && opts.moduleDirectory + ? [].concat(opts.moduleDirectory) + : ['node_modules']; + + if (opts && typeof opts.paths === 'function') { + return opts.paths( + request, + start, + function () { return getNodeModulesDirs(start, modules); }, + opts + ); + } + + var dirs = getNodeModulesDirs(start, modules); + return opts && opts.paths ? dirs.concat(opts.paths) : dirs; +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js new file mode 100644 index 0000000000000000000000000000000000000000..4b56904eaea72ba024f96728fff40e1be4af7df0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/normalize-options.js @@ -0,0 +1,10 @@ +module.exports = function (x, opts) { + /** + * This file is purposefully a passthrough. It's expected that third-party + * environments will override it at runtime in order to inject special logic + * into `resolve` (by manipulating the options). One such example is the PnP + * code path in Yarn. + */ + + return opts || {}; +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js new file mode 100644 index 0000000000000000000000000000000000000000..ddc468adcf019c39dc57f3f71e7511d132ce8adf --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/lib/sync.js @@ -0,0 +1,221 @@ +var isCore = require('is-core-module'); +var fs = require('fs'); +var path = require('path'); +var getHomedir = require('./homedir'); +var caller = require('./caller'); +var nodeModulesPaths = require('./node-modules-paths'); +var normalizeOptions = require('./normalize-options'); + +var realpathFS = process.platform !== 'win32' && fs.realpathSync && typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; + +var homedir = getHomedir(); +var defaultPaths = function () { + return [ + path.join(homedir, '.node_modules'), + path.join(homedir, '.node_libraries') + ]; +}; + +var defaultIsFile = function isFile(file) { + try { + var stat = fs.statSync(file, { throwIfNoEntry: false }); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return !!stat && (stat.isFile() || stat.isFIFO()); +}; + +var defaultIsDir = function isDirectory(dir) { + try { + var stat = fs.statSync(dir, { throwIfNoEntry: false }); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return !!stat && stat.isDirectory(); +}; + +var defaultRealpathSync = function realpathSync(x) { + try { + return realpathFS(x); + } catch (realpathErr) { + if (realpathErr.code !== 'ENOENT') { + throw realpathErr; + } + } + return x; +}; + +var maybeRealpathSync = function maybeRealpathSync(realpathSync, x, opts) { + if (!opts || !opts.preserveSymlinks) { + return realpathSync(x); + } + return x; +}; + +var defaultReadPackageSync = function defaultReadPackageSync(readFileSync, pkgfile) { + return JSON.parse(readFileSync(pkgfile)); +}; + +var getPackageCandidates = function getPackageCandidates(x, start, opts) { + var dirs = nodeModulesPaths(start, opts, x); + for (var i = 0; i < dirs.length; i++) { + dirs[i] = path.join(dirs[i], x); + } + return dirs; +}; + +module.exports = function resolveSync(x, options) { + if (typeof x !== 'string') { + throw new TypeError('Path must be a string.'); + } + var opts = normalizeOptions(x, options); + + var isFile = opts.isFile || defaultIsFile; + var isDirectory = opts.isDirectory || defaultIsDir; + var readFileSync = opts.readFileSync || fs.readFileSync; + var realpathSync = opts.realpathSync || defaultRealpathSync; + var readPackageSync = opts.readPackageSync || defaultReadPackageSync; + if (opts.readFileSync && opts.readPackageSync) { + throw new TypeError('`readFileSync` and `readPackageSync` are mutually exclusive.'); + } + var packageIterator = opts.packageIterator; + + var extensions = opts.extensions || ['.js']; + var includeCoreModules = opts.includeCoreModules !== false; + var basedir = opts.basedir || path.dirname(caller()); + var parent = opts.filename || basedir; + + opts.paths = opts.paths || defaultPaths(); + + // ensure that `basedir` is an absolute path at this point, resolving against the process' current working directory + var absoluteStart = maybeRealpathSync(realpathSync, path.resolve(basedir), opts); + + if (opts.basedir && !isDirectory(absoluteStart)) { + var dirError = new TypeError('Provided basedir "' + opts.basedir + '" is not a directory' + (opts.preserveSymlinks ? '' : ', or a symlink to a directory')); + dirError.code = 'INVALID_BASEDIR'; + throw dirError; + } + + if ((/^(?:\.\.?(?:\/|$)|\/|([A-Za-z]:)?[/\\])/).test(x)) { + var res = path.resolve(absoluteStart, x); + if (x === '.' || x === '..' || x.slice(-1) === '/') res += '/'; + var m = loadAsFileSync(res) || loadAsDirectorySync(res); + if (m) return maybeRealpathSync(realpathSync, m, opts); + } else if (includeCoreModules && isCore(x)) { + return x; + } else { + var n = loadNodeModulesSync(x, absoluteStart); + if (n) return maybeRealpathSync(realpathSync, n, opts); + } + + var err = new Error("Cannot find module '" + x + "' from '" + parent + "'"); + err.code = 'MODULE_NOT_FOUND'; + throw err; + + function loadAsFileSync(x) { + var pkg = loadpkg(path.dirname(x)); + + if (pkg && pkg.dir && pkg.pkg && opts.pathFilter) { + var rfile = path.relative(pkg.dir, x); + var r = opts.pathFilter(pkg.pkg, x, rfile); + if (r) { + x = path.resolve(pkg.dir, r); // eslint-disable-line no-param-reassign + } + } + + if (isFile(x)) { + return x; + } + + for (var i = 0; i < extensions.length; i++) { + var file = x + extensions[i]; + if (isFile(file)) { + return file; + } + } + } + + function loadpkg(dir) { + if (dir === '' || dir === '/') return; + if (process.platform === 'win32' && (/^\w:[/\\]*$/).test(dir)) { + return; + } + if ((/[/\\]node_modules[/\\]*$/).test(dir)) return; + + var pkgfile = path.join(isDirectory(dir) ? maybeRealpathSync(realpathSync, dir, opts) : dir, 'package.json'); + + if (!isFile(pkgfile)) { + return loadpkg(path.dirname(dir)); + } + + var pkg; + try { + pkg = readPackageSync(readFileSync, pkgfile); + } catch (e) { + if (!(e instanceof SyntaxError)) { + throw e; + } + } + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile, dir); + } + + return { pkg: pkg, dir: dir }; + } + + function loadAsDirectorySync(x) { + var pkgfile = path.join(isDirectory(x) ? maybeRealpathSync(realpathSync, x, opts) : x, '/package.json'); + if (isFile(pkgfile)) { + try { + var pkg = readPackageSync(readFileSync, pkgfile); + } catch (e) {} + + if (pkg && opts.packageFilter) { + pkg = opts.packageFilter(pkg, pkgfile, x); + } + + if (pkg && pkg.main) { + if (typeof pkg.main !== 'string') { + var mainError = new TypeError('package “' + pkg.name + '” `main` must be a string'); + mainError.code = 'INVALID_PACKAGE_MAIN'; + throw mainError; + } + if (pkg.main === '.' || pkg.main === './') { + pkg.main = 'index'; + } + try { + var mainPath = path.resolve(x, pkg.main); + var m = loadAsFileSync(mainPath); + if (m) return m; + var n = loadAsDirectorySync(mainPath); + if (n) return n; + var checkIndex = loadAsFileSync(path.resolve(x, 'index')); + if (checkIndex) return checkIndex; + } catch (e) { } + var incorrectMainError = new Error("Cannot find module '" + path.resolve(x, pkg.main) + "'. Please verify that the package.json has a valid \"main\" entry"); + incorrectMainError.code = 'INCORRECT_PACKAGE_MAIN'; + throw incorrectMainError; + } + } + + return loadAsFileSync(path.join(x, '/index')); + } + + function loadNodeModulesSync(x, start) { + var thunk = function () { return getPackageCandidates(x, start, opts); }; + var dirs = packageIterator ? packageIterator(x, start, thunk, opts) : thunk(); + + for (var i = 0; i < dirs.length; i++) { + var dir = dirs[i]; + if (isDirectory(path.dirname(dir))) { + var m = loadAsFileSync(dir); + if (m) return m; + var n = loadAsDirectorySync(dir); + if (n) return n; + } + } + } +}; diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/package.json new file mode 100644 index 0000000000000000000000000000000000000000..e4f4b5094cbca27d0dbcdfd2d2c030bb94ba6b89 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/package.json @@ -0,0 +1,83 @@ +{ + "name": "resolve", + "description": "resolve like require.resolve() on behalf of files asynchronously and synchronously", + "version": "2.0.0-next.5", + "repository": { + "type": "git", + "url": "git://github.com/browserify/resolve.git" + }, + "bin": { + "resolve": "./bin/resolve" + }, + "main": "index.js", + "exports": { + ".": [ + { + "import": "./index.mjs", + "default": "./index.js" + }, + "./index.js" + ], + "./sync": "./lib/sync.js", + "./async": "./lib/async.js", + "./package.json": "./package.json" + }, + "keywords": [ + "resolve", + "require", + "node", + "module" + ], + "scripts": { + "prepack": "npmignore --auto --commentLines=autogenerated", + "prepublishOnly": "safe-publish-latest", + "prepublish": "not-in-publish || npm run prepublishOnly", + "prelint": "eclint check $(git ls-files | xargs find 2> /dev/null | grep -vE 'node_modules|\\.git')", + "lint": "eslint --ext=js,mjs --no-eslintrc -c .eslintrc . 'bin/**'", + "pretests-only": "cd ./test/resolver/nested_symlinks && node mylib/sync && node mylib/async", + "tests-only": "tape test/*.js", + "pretest": "npm run lint", + "test": "npm run --silent tests-only", + "posttest": "npm run test:multirepo && aud --production", + "test:multirepo": "cd ./test/resolver/multirepo && npm install && npm test" + }, + "devDependencies": { + "@ljharb/eslint-config": "^21.1.0", + "array.prototype.map": "^1.0.6", + "aud": "^2.0.3", + "copy-dir": "^1.3.0", + "eclint": "^2.8.1", + "eslint": "=8.8.0", + "in-publish": "^2.0.1", + "mkdirp": "^0.5.5", + "mv": "^2.1.1", + "npmignore": "^0.3.0", + "object-keys": "^1.1.1", + "rimraf": "^2.7.1", + "safe-publish-latest": "^2.0.0", + "tap": "^0.4.13", + "tape": "^5.7.0", + "tmp": "^0.0.31" + }, + "license": "MIT", + "author": { + "name": "James Halliday", + "email": "mail@substack.net", + "url": "http://substack.net" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + }, + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "publishConfig": { + "ignore": [ + ".github/workflows", + "appveyor.yml", + "test/resolver/malformed_package_json" + ] + } +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown b/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown new file mode 100644 index 0000000000000000000000000000000000000000..9e15d7a064656b0e841d2efdc07cbcfd59b92680 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/readme.markdown @@ -0,0 +1,294 @@ +# resolve [![Version Badge][2]][1] + +implements the [node `require.resolve()` algorithm](https://nodejs.org/api/modules.html#modules_all_together) such that you can `require.resolve()` on behalf of a file asynchronously and synchronously + +[![github actions][actions-image]][actions-url] +[![coverage][codecov-image]][codecov-url] +[![dependency status][5]][6] +[![dev dependency status][7]][8] +[![License][license-image]][license-url] +[![Downloads][downloads-image]][downloads-url] + +[![npm badge][11]][1] + +# example + +asynchronously resolve: + +```js +var resolve = require('resolve/async'); // or, require('resolve') +resolve('tap', { basedir: __dirname }, function (err, res) { + if (err) console.error(err); + else console.log(res); +}); +``` + +``` +$ node example/async.js +/home/substack/projects/node-resolve/node_modules/tap/lib/main.js +``` + +synchronously resolve: + +```js +var resolve = require('resolve/sync'); // or, `require('resolve').sync +var res = resolve('tap', { basedir: __dirname }); +console.log(res); +``` + +``` +$ node example/sync.js +/home/substack/projects/node-resolve/node_modules/tap/lib/main.js +``` + +# methods + +```js +var resolve = require('resolve'); +var async = require('resolve/async'); +var sync = require('resolve/sync'); +``` + +For both the synchronous and asynchronous methods, errors may have any of the following `err.code` values: + +- `MODULE_NOT_FOUND`: the given path string (`id`) could not be resolved to a module +- `INVALID_BASEDIR`: the specified `opts.basedir` doesn't exist, or is not a directory +- `INVALID_PACKAGE_MAIN`: a `package.json` was encountered with an invalid `main` property (eg. not a string) + +## resolve(id, opts={}, cb) + +Asynchronously resolve the module path string `id` into `cb(err, res [, pkg])`, where `pkg` (if defined) is the data from `package.json`. + +options are: + +* opts.basedir - directory to begin resolving from + +* opts.package - `package.json` data applicable to the module being loaded + +* opts.extensions - array of file extensions to search in order + +* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search + +* opts.readFile - how to read files asynchronously + +* opts.isFile - function to asynchronously test whether a file exists + +* opts.isDirectory - function to asynchronously test whether a file exists and is a directory + +* opts.realpath - function to asynchronously resolve a potential symlink to its real path + +* `opts.readPackage(readFile, pkgfile, cb)` - function to asynchronously read and parse a package.json file + * readFile - the passed `opts.readFile` or `fs.readFile` if not specified + * pkgfile - path to package.json + * cb - callback. a SyntaxError error argument will be ignored, all other error arguments will be treated as an error. + +* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field + * pkg - package data + * pkgfile - path to package.json + * dir - directory that contains package.json + +* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package + * pkg - package data + * path - the path being resolved + * relativePath - the path relative from the package.json location + * returns - a relative path that will be joined from the package.json location + +* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this) + + For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function + * request - the import specifier being resolved + * start - lookup path + * getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution + * opts - the resolution options + +* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this) + * request - the import specifier being resolved + * start - lookup path + * getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution + * opts - the resolution options + +* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"` + +* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving. +This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag. + +default `opts` values: + +```js +{ + paths: [], + basedir: __dirname, + extensions: ['.js'], + includeCoreModules: true, + readFile: fs.readFile, + isFile: function isFile(file, cb) { + fs.stat(file, function (err, stat) { + if (!err) { + return cb(null, stat.isFile() || stat.isFIFO()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); + }, + isDirectory: function isDirectory(dir, cb) { + fs.stat(dir, function (err, stat) { + if (!err) { + return cb(null, stat.isDirectory()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); + }, + realpath: function realpath(file, cb) { + var realpath = typeof fs.realpath.native === 'function' ? fs.realpath.native : fs.realpath; + realpath(file, function (realPathErr, realPath) { + if (realPathErr && realPathErr.code !== 'ENOENT') cb(realPathErr); + else cb(null, realPathErr ? file : realPath); + }); + }, + readPackage: function defaultReadPackage(readFile, pkgfile, cb) { + readFile(pkgfile, function (readFileErr, body) { + if (readFileErr) cb(readFileErr); + else { + try { + var pkg = JSON.parse(body); + cb(null, pkg); + } catch (jsonErr) { + cb(jsonErr); + } + } + }); + }, + moduleDirectory: 'node_modules', + preserveSymlinks: false +} +``` + +## resolve.sync(id, opts) + +Synchronously resolve the module path string `id`, returning the result and +throwing an error when `id` can't be resolved. + +options are: + +* opts.basedir - directory to begin resolving from + +* opts.extensions - array of file extensions to search in order + +* opts.includeCoreModules - set to `false` to exclude node core modules (e.g. `fs`) from the search + +* opts.readFileSync - how to read files synchronously + +* opts.isFile - function to synchronously test whether a file exists + +* opts.isDirectory - function to synchronously test whether a file exists and is a directory + +* opts.realpathSync - function to synchronously resolve a potential symlink to its real path + +* `opts.readPackageSync(readFileSync, pkgfile)` - function to synchronously read and parse a package.json file. a thrown SyntaxError will be ignored, all other exceptions will propagate. + * readFileSync - the passed `opts.readFileSync` or `fs.readFileSync` if not specified + * pkgfile - path to package.json + +* `opts.packageFilter(pkg, pkgfile, dir)` - transform the parsed package.json contents before looking at the "main" field + * pkg - package data + * pkgfile - path to package.json + * dir - directory that contains package.json + +* `opts.pathFilter(pkg, path, relativePath)` - transform a path within a package + * pkg - package data + * path - the path being resolved + * relativePath - the path relative from the package.json location + * returns - a relative path that will be joined from the package.json location + +* opts.paths - require.paths array to use if nothing is found on the normal `node_modules` recursive walk (probably don't use this) + + For advanced users, `paths` can also be a `opts.paths(request, start, opts)` function + * request - the import specifier being resolved + * start - lookup path + * getNodeModulesDirs - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution + * opts - the resolution options + +* `opts.packageIterator(request, start, opts)` - return the list of candidate paths where the packages sources may be found (probably don't use this) + * request - the import specifier being resolved + * start - lookup path + * getPackageCandidates - a thunk (no-argument function) that returns the paths using standard `node_modules` resolution + * opts - the resolution options + +* opts.moduleDirectory - directory (or directories) in which to recursively look for modules. default: `"node_modules"` + +* opts.preserveSymlinks - if true, doesn't resolve `basedir` to real path before resolving. +This is the way Node resolves dependencies when executed with the [--preserve-symlinks](https://nodejs.org/api/all.html#cli_preserve_symlinks) flag. + +default `opts` values: + +```js +{ + paths: [], + basedir: __dirname, + extensions: ['.js'], + includeCoreModules: true, + readFileSync: fs.readFileSync, + isFile: function isFile(file) { + try { + var stat = fs.statSync(file); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return stat.isFile() || stat.isFIFO(); + }, + isDirectory: function isDirectory(dir) { + try { + var stat = fs.statSync(dir); + } catch (e) { + if (e && (e.code === 'ENOENT' || e.code === 'ENOTDIR')) return false; + throw e; + } + return stat.isDirectory(); + }, + realpathSync: function realpathSync(file) { + try { + var realpath = typeof fs.realpathSync.native === 'function' ? fs.realpathSync.native : fs.realpathSync; + return realpath(file); + } catch (realPathErr) { + if (realPathErr.code !== 'ENOENT') { + throw realPathErr; + } + } + return file; + }, + readPackageSync: function defaultReadPackageSync(readFileSync, pkgfile) { + return JSON.parse(readFileSync(pkgfile)); + }, + moduleDirectory: 'node_modules', + preserveSymlinks: false +} +``` + +# install + +With [npm](https://npmjs.org) do: + +```sh +npm install resolve +``` + +# license + +MIT + +[1]: https://npmjs.org/package/resolve +[2]: https://versionbadg.es/browserify/resolve.svg +[5]: https://david-dm.org/browserify/resolve.svg +[6]: https://david-dm.org/browserify/resolve +[7]: https://david-dm.org/browserify/resolve/dev-status.svg +[8]: https://david-dm.org/browserify/resolve#info=devDependencies +[11]: https://nodei.co/npm/resolve.png?downloads=true&stars=true +[license-image]: https://img.shields.io/npm/l/resolve.svg +[license-url]: LICENSE +[downloads-image]: https://img.shields.io/npm/dm/resolve.svg +[downloads-url]: https://npm-stat.com/charts.html?package=resolve +[codecov-image]: https://codecov.io/gh/browserify/resolve/branch/main/graphs/badge.svg +[codecov-url]: https://app.codecov.io/gh/browserify/resolve/ +[actions-image]: https://img.shields.io/endpoint?url=https://github-actions-badge-u3jn4tfpocch.runkit.sh/browserify/resolve +[actions-url]: https://github.com/browserify/resolve/actions diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/sync.js new file mode 100644 index 0000000000000000000000000000000000000000..cd0ee040177e92c676b1a063406c743289719bd0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/sync.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require('./lib/sync'); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js new file mode 100644 index 0000000000000000000000000000000000000000..30806659be2ef27d410722636d6f79c8f8e999b0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot.js @@ -0,0 +1,29 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('dotdot', function (t) { + t.plan(4); + var dir = path.join(__dirname, '/dotdot/abc'); + + resolve('..', { basedir: dir }, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(__dirname, 'dotdot/index.js')); + }); + + resolve('.', { basedir: dir }, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, 'index.js')); + }); +}); + +test('dotdot sync', function (t) { + t.plan(2); + var dir = path.join(__dirname, '/dotdot/abc'); + + var a = resolve.sync('..', { basedir: dir }); + t.equal(a, path.join(__dirname, 'dotdot/index.js')); + + var b = resolve.sync('.', { basedir: dir }); + t.equal(b, path.join(dir, 'index.js')); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot/abc/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot/abc/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/dotdot/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js new file mode 100644 index 0000000000000000000000000000000000000000..94d42b5e658d139884a750a04caf192ef51a6a3d --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/faulty_basedir.js @@ -0,0 +1,29 @@ +var test = require('tape'); +var path = require('path'); +var resolve = require('../'); + +test('faulty basedir must produce error in windows', { skip: process.platform !== 'win32' }, function (t) { + t.plan(1); + + var resolverDir = 'C:\\a\\b\\c\\d'; + + resolve('tape/lib/test.js', { basedir: resolverDir }, function (err, res, pkg) { + t.equal(!!err, true); + }); +}); + +test('non-existent basedir should not throw when preserveSymlinks is false', function (t) { + t.plan(2); + + var opts = { + basedir: path.join(path.sep, 'unreal', 'path', 'that', 'does', 'not', 'exist'), + preserveSymlinks: false + }; + + var module = './dotdot/abc'; + + resolve(module, opts, function (err, res) { + t.equal(err.code, 'INVALID_BASEDIR'); + t.equal(res, undefined); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js new file mode 100644 index 0000000000000000000000000000000000000000..6388671de4d59c4f3d69d44bb1da319d25d19658 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/filter.js @@ -0,0 +1,37 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('filter', function (t) { + t.plan(5); + var dir = path.join(__dirname, 'resolver'); + var packageFilterArgs; + resolve('./baz', { + basedir: dir, + packageFilter: function (pkg, pkgfile, dir) { + pkg.main = 'doom'; // eslint-disable-line no-param-reassign + packageFilterArgs = [pkg, pkgfile, dir]; + return pkg; + } + }, function (err, res, pkg) { + if (err) t.fail(err); + + t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works'); + + var packageData = packageFilterArgs[0]; + t.equal(pkg, packageData, 'first packageFilter argument is "pkg"'); + t.equal(packageData.main, 'doom', 'package "main" was altered'); + + var packageFile = packageFilterArgs[1]; + t.equal( + packageFile, + path.join(dir, 'baz/package.json'), + 'second packageFilter argument is "pkgfile"' + ); + + var packageFileDir = packageFilterArgs[2]; + t.equal(packageFileDir, path.join(dir, 'baz'), 'third packageFilter argument is "dir"'); + + t.end(); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js new file mode 100644 index 0000000000000000000000000000000000000000..0ee8a4da645278fb02cef25047febfb32859202e --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/filter_sync.js @@ -0,0 +1,33 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('filter', function (t) { + var dir = path.join(__dirname, 'resolver'); + var packageFilterArgs; + var res = resolve.sync('./baz', { + basedir: dir, + packageFilter: function (pkg, pkgfile, dir) { + pkg.main = 'doom'; // eslint-disable-line no-param-reassign + packageFilterArgs = [pkg, pkgfile, dir]; + return pkg; + } + }); + + t.equal(res, path.join(dir, 'baz/doom.js'), 'changing the package "main" works'); + + var packageData = packageFilterArgs[0]; + t.equal(packageData.main, 'doom', 'package "main" was altered'); + + var packageFile = packageFilterArgs[1]; + t.equal( + packageFile, + path.join(dir, 'baz/package.json'), + 'second packageFilter argument is "pkgfile"' + ); + + var packageDir = packageFilterArgs[2]; + t.equal(packageDir, path.join(dir, 'baz'), 'third packageFilter argument is "dir"'); + + t.end(); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js new file mode 100644 index 0000000000000000000000000000000000000000..3b8c9b32c87bd73fb2a3ec42494d13faae1594e4 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths.js @@ -0,0 +1,127 @@ +'use strict'; + +var fs = require('fs'); +var homedir = require('../lib/homedir'); +var path = require('path'); + +var test = require('tape'); +var mkdirp = require('mkdirp'); +var rimraf = require('rimraf'); +var mv = require('mv'); +var copyDir = require('copy-dir'); +var tmp = require('tmp'); + +var HOME = homedir(); + +var hnm = path.join(HOME, '.node_modules'); +var hnl = path.join(HOME, '.node_libraries'); + +var resolve = require('../async'); + +function makeDir(t, dir, cb) { + mkdirp(dir, function (err) { + if (err) { + cb(err); + } else { + t.teardown(function cleanup() { + rimraf.sync(dir); + }); + cb(); + } + }); +} + +function makeTempDir(t, dir, cb) { + if (fs.existsSync(dir)) { + var tmpResult = tmp.dirSync(); + t.teardown(tmpResult.removeCallback); + var backup = path.join(tmpResult.name, path.basename(dir)); + mv(dir, backup, function (err) { + if (err) { + cb(err); + } else { + t.teardown(function () { + mv(backup, dir, cb); + }); + makeDir(t, dir, cb); + } + }); + } else { + makeDir(t, dir, cb); + } +} + +test('homedir module paths', function (t) { + t.plan(7); + + makeTempDir(t, hnm, function (err) { + t.error(err, 'no error with HNM temp dir'); + if (err) { + return t.end(); + } + + var bazHNMDir = path.join(hnm, 'baz'); + var dotMainDir = path.join(hnm, 'dot_main'); + copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir); + copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir); + + var bazPkg = { name: 'baz', main: 'quux.js' }; + var dotMainPkg = { main: 'index' }; + + var bazHNMmain = path.join(bazHNMDir, 'quux.js'); + t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`'); + var dotMainMain = path.join(dotMainDir, 'index.js'); + t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`'); + + makeTempDir(t, hnl, function (err) { + t.error(err, 'no error with HNL temp dir'); + if (err) { + return t.end(); + } + var bazHNLDir = path.join(hnl, 'baz'); + copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir); + + var dotSlashMainDir = path.join(hnl, 'dot_slash_main'); + var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js'); + var dotSlashMainPkg = { main: 'index' }; + copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir); + + t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`'); + t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`'); + + t.test('with temp dirs', function (st) { + st.plan(3); + + st.test('just in `$HOME/.node_modules`', function (s2t) { + s2t.plan(3); + + resolve('dot_main', function (err, res, pkg) { + s2t.error(err, 'no error resolving `dot_main`'); + s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`'); + s2t.deepEqual(pkg, dotMainPkg); + }); + }); + + st.test('just in `$HOME/.node_libraries`', function (s2t) { + s2t.plan(3); + + resolve('dot_slash_main', function (err, res, pkg) { + s2t.error(err, 'no error resolving `dot_slash_main`'); + s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`'); + s2t.deepEqual(pkg, dotSlashMainPkg); + }); + }); + + st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) { + s2t.plan(3); + + resolve('baz', function (err, res, pkg) { + s2t.error(err, 'no error resolving `baz`'); + s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both'); + s2t.deepEqual(pkg, bazPkg); + }); + }); + }); + }); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js new file mode 100644 index 0000000000000000000000000000000000000000..5d2c56fd35d0a3ad072c3549f12695c93654aaed --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/home_paths_sync.js @@ -0,0 +1,114 @@ +'use strict'; + +var fs = require('fs'); +var homedir = require('../lib/homedir'); +var path = require('path'); + +var test = require('tape'); +var mkdirp = require('mkdirp'); +var rimraf = require('rimraf'); +var mv = require('mv'); +var copyDir = require('copy-dir'); +var tmp = require('tmp'); + +var HOME = homedir(); + +var hnm = path.join(HOME, '.node_modules'); +var hnl = path.join(HOME, '.node_libraries'); + +var resolve = require('../sync'); + +function makeDir(t, dir, cb) { + mkdirp(dir, function (err) { + if (err) { + cb(err); + } else { + t.teardown(function cleanup() { + rimraf.sync(dir); + }); + cb(); + } + }); +} + +function makeTempDir(t, dir, cb) { + if (fs.existsSync(dir)) { + var tmpResult = tmp.dirSync(); + t.teardown(tmpResult.removeCallback); + var backup = path.join(tmpResult.name, path.basename(dir)); + mv(dir, backup, function (err) { + if (err) { + cb(err); + } else { + t.teardown(function () { + mv(backup, dir, cb); + }); + makeDir(t, dir, cb); + } + }); + } else { + makeDir(t, dir, cb); + } +} + +test('homedir module paths', function (t) { + t.plan(7); + + makeTempDir(t, hnm, function (err) { + t.error(err, 'no error with HNM temp dir'); + if (err) { + return t.end(); + } + + var bazHNMDir = path.join(hnm, 'baz'); + var dotMainDir = path.join(hnm, 'dot_main'); + copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNMDir); + copyDir.sync(path.join(__dirname, 'resolver/dot_main'), dotMainDir); + + var bazHNMmain = path.join(bazHNMDir, 'quux.js'); + t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`'); + var dotMainMain = path.join(dotMainDir, 'index.js'); + t.equal(require.resolve('dot_main'), dotMainMain, 'sanity check: require.resolve finds `dot_main`'); + + makeTempDir(t, hnl, function (err) { + t.error(err, 'no error with HNL temp dir'); + if (err) { + return t.end(); + } + var bazHNLDir = path.join(hnl, 'baz'); + copyDir.sync(path.join(__dirname, 'resolver/baz'), bazHNLDir); + + var dotSlashMainDir = path.join(hnl, 'dot_slash_main'); + var dotSlashMainMain = path.join(dotSlashMainDir, 'index.js'); + copyDir.sync(path.join(__dirname, 'resolver/dot_slash_main'), dotSlashMainDir); + + t.equal(require.resolve('baz'), bazHNMmain, 'sanity check: require.resolve finds HNM `baz`'); + t.equal(require.resolve('dot_slash_main'), dotSlashMainMain, 'sanity check: require.resolve finds HNL `dot_slash_main`'); + + t.test('with temp dirs', function (st) { + st.plan(3); + + st.test('just in `$HOME/.node_modules`', function (s2t) { + s2t.plan(1); + + var res = resolve('dot_main'); + s2t.equal(res, dotMainMain, '`dot_main` resolves in `$HOME/.node_modules`'); + }); + + st.test('just in `$HOME/.node_libraries`', function (s2t) { + s2t.plan(1); + + var res = resolve('dot_slash_main'); + s2t.equal(res, dotSlashMainMain, '`dot_slash_main` resolves in `$HOME/.node_libraries`'); + }); + + st.test('in `$HOME/.node_libraries` and `$HOME/.node_modules`', function (s2t) { + s2t.plan(1); + + var res = resolve('baz'); + s2t.equal(res, bazHNMmain, '`baz` resolves in `$HOME/.node_modules` when in both'); + }); + }); + }); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js new file mode 100644 index 0000000000000000000000000000000000000000..611627549889541b31e4eb8d8f9277871a452076 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/mock.js @@ -0,0 +1,315 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('mock', function (t) { + t.plan(8); + + var files = {}; + files[path.resolve('/foo/bar/baz.js')] = 'beep'; + + var dirs = {}; + dirs[path.resolve('/foo/bar')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file))); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + readFile: function (file, cb) { + cb(null, files[path.resolve(file)]); + }, + realpath: function (file, cb) { + cb(null, file); + } + }; + } + + resolve('./baz', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/baz.js')); + t.equal(pkg, undefined); + }); + + resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/baz.js')); + t.equal(pkg, undefined); + }); + + resolve('baz', opts('/foo/bar'), function (err, res) { + t.equal(err.message, "Cannot find module 'baz' from '" + path.resolve('/foo/bar') + "'"); + t.equal(err.code, 'MODULE_NOT_FOUND'); + }); + + resolve('../baz', opts('/foo/bar'), function (err, res) { + t.equal(err.message, "Cannot find module '../baz' from '" + path.resolve('/foo/bar') + "'"); + t.equal(err.code, 'MODULE_NOT_FOUND'); + }); +}); + +test('mock from package', function (t) { + t.plan(8); + + var files = {}; + files[path.resolve('/foo/bar/baz.js')] = 'beep'; + + var dirs = {}; + dirs[path.resolve('/foo/bar')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, file)); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + 'package': { main: 'bar' }, + readFile: function (file, cb) { + cb(null, files[file]); + }, + realpath: function (file, cb) { + cb(null, file); + } + }; + } + + resolve('./baz', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/baz.js')); + t.equal(pkg && pkg.main, 'bar'); + }); + + resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/baz.js')); + t.equal(pkg && pkg.main, 'bar'); + }); + + resolve('baz', opts('/foo/bar'), function (err, res) { + t.equal(err.message, "Cannot find module 'baz' from '" + path.resolve('/foo/bar') + "'"); + t.equal(err.code, 'MODULE_NOT_FOUND'); + }); + + resolve('../baz', opts('/foo/bar'), function (err, res) { + t.equal(err.message, "Cannot find module '../baz' from '" + path.resolve('/foo/bar') + "'"); + t.equal(err.code, 'MODULE_NOT_FOUND'); + }); +}); + +test('mock package', function (t) { + t.plan(2); + + var files = {}; + files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep'; + files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({ + main: './baz.js' + }); + + var dirs = {}; + dirs[path.resolve('/foo')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file))); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + readFile: function (file, cb) { + cb(null, files[path.resolve(file)]); + }, + realpath: function (file, cb) { + cb(null, file); + } + }; + } + + resolve('bar', opts('/foo'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/node_modules/bar/baz.js')); + t.equal(pkg && pkg.main, './baz.js'); + }); +}); + +test('mock package from package', function (t) { + t.plan(2); + + var files = {}; + files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep'; + files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({ + main: './baz.js' + }); + + var dirs = {}; + dirs[path.resolve('/foo')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file))); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + 'package': { main: 'bar' }, + readFile: function (file, cb) { + cb(null, files[path.resolve(file)]); + }, + realpath: function (file, cb) { + cb(null, file); + } + }; + } + + resolve('bar', opts('/foo'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/node_modules/bar/baz.js')); + t.equal(pkg && pkg.main, './baz.js'); + }); +}); + +test('symlinked', function (t) { + t.plan(4); + + var files = {}; + files[path.resolve('/foo/bar/baz.js')] = 'beep'; + files[path.resolve('/foo/bar/symlinked/baz.js')] = 'beep'; + + var dirs = {}; + dirs[path.resolve('/foo/bar')] = true; + dirs[path.resolve('/foo/bar/symlinked')] = true; + + function opts(basedir) { + return { + preserveSymlinks: false, + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file))); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + readFile: function (file, cb) { + cb(null, files[path.resolve(file)]); + }, + realpath: function (file, cb) { + var resolved = path.resolve(file); + + if (resolved.indexOf('symlinked') >= 0) { + cb(null, resolved); + return; + } + + var ext = path.extname(resolved); + + if (ext) { + var dir = path.dirname(resolved); + var base = path.basename(resolved); + cb(null, path.join(dir, 'symlinked', base)); + } else { + cb(null, path.join(resolved, 'symlinked')); + } + } + }; + } + + resolve('./baz', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/symlinked/baz.js')); + t.equal(pkg, undefined); + }); + + resolve('./baz.js', opts('/foo/bar'), function (err, res, pkg) { + if (err) return t.fail(err); + t.equal(res, path.resolve('/foo/bar/symlinked/baz.js')); + t.equal(pkg, undefined); + }); +}); + +test('readPackage', function (t) { + t.plan(3); + + var files = {}; + files[path.resolve('/foo/node_modules/bar/something-else.js')] = 'beep'; + files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({ + main: './baz.js' + }); + files[path.resolve('/foo/node_modules/bar/baz.js')] = 'boop'; + + var dirs = {}; + dirs[path.resolve('/foo')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file, cb) { + cb(null, Object.prototype.hasOwnProperty.call(files, path.resolve(file))); + }, + isDirectory: function (dir, cb) { + cb(null, !!dirs[path.resolve(dir)]); + }, + 'package': { main: 'bar' }, + readFile: function (file, cb) { + cb(null, files[path.resolve(file)]); + }, + realpath: function (file, cb) { + cb(null, file); + } + }; + } + + t.test('with readFile', function (st) { + st.plan(3); + + resolve('bar', opts('/foo'), function (err, res, pkg) { + st.error(err); + st.equal(res, path.resolve('/foo/node_modules/bar/baz.js')); + st.equal(pkg && pkg.main, './baz.js'); + }); + }); + + var readPackage = function (readFile, file, cb) { + var barPackage = path.join('bar', 'package.json'); + if (file.slice(-barPackage.length) === barPackage) { + cb(null, { main: './something-else.js' }); + } else { + cb(null, JSON.parse(files[path.resolve(file)])); + } + }; + + t.test('with readPackage', function (st) { + st.plan(3); + + var options = opts('/foo'); + delete options.readFile; + options.readPackage = readPackage; + resolve('bar', options, function (err, res, pkg) { + st.error(err); + st.equal(res, path.resolve('/foo/node_modules/bar/something-else.js')); + st.equal(pkg && pkg.main, './something-else.js'); + }); + }); + + t.test('with readFile and readPackage', function (st) { + st.plan(1); + + var options = opts('/foo'); + options.readPackage = readPackage; + resolve('bar', options, function (err) { + st.throws(function () { throw err; }, TypeError, 'errors when both readFile and readPackage are provided'); + }); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js new file mode 100644 index 0000000000000000000000000000000000000000..fa19f647e03d300e3b09a5818ae23e72a5d78456 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/mock_sync.js @@ -0,0 +1,215 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('mock', function (t) { + t.plan(4); + + var files = {}; + files[path.resolve('/foo/bar/baz.js')] = 'beep'; + + var dirs = {}; + dirs[path.resolve('/foo/bar')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file) { + return Object.prototype.hasOwnProperty.call(files, path.resolve(file)); + }, + isDirectory: function (dir) { + return !!dirs[path.resolve(dir)]; + }, + readFileSync: function (file) { + return files[path.resolve(file)]; + }, + realpathSync: function (file) { + return file; + } + }; + } + + t.equal( + resolve.sync('./baz', opts('/foo/bar')), + path.resolve('/foo/bar/baz.js') + ); + + t.equal( + resolve.sync('./baz.js', opts('/foo/bar')), + path.resolve('/foo/bar/baz.js') + ); + + t.throws(function () { + resolve.sync('baz', opts('/foo/bar')); + }); + + t.throws(function () { + resolve.sync('../baz', opts('/foo/bar')); + }); +}); + +test('mock package', function (t) { + t.plan(1); + + var files = {}; + files[path.resolve('/foo/node_modules/bar/baz.js')] = 'beep'; + files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({ + main: './baz.js' + }); + + var dirs = {}; + dirs[path.resolve('/foo')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir) { + return { + basedir: path.resolve(basedir), + isFile: function (file) { + return Object.prototype.hasOwnProperty.call(files, path.resolve(file)); + }, + isDirectory: function (dir) { + return !!dirs[path.resolve(dir)]; + }, + readFileSync: function (file) { + return files[path.resolve(file)]; + }, + realpathSync: function (file) { + return file; + } + }; + } + + t.equal( + resolve.sync('bar', opts('/foo')), + path.resolve('/foo/node_modules/bar/baz.js') + ); +}); + +test('symlinked', function (t) { + t.plan(2); + + var files = {}; + files[path.resolve('/foo/bar/baz.js')] = 'beep'; + files[path.resolve('/foo/bar/symlinked/baz.js')] = 'beep'; + + var dirs = {}; + dirs[path.resolve('/foo/bar')] = true; + dirs[path.resolve('/foo/bar/symlinked')] = true; + + function opts(basedir) { + return { + preserveSymlinks: false, + basedir: path.resolve(basedir), + isFile: function (file) { + return Object.prototype.hasOwnProperty.call(files, path.resolve(file)); + }, + isDirectory: function (dir) { + return !!dirs[path.resolve(dir)]; + }, + readFileSync: function (file) { + return files[path.resolve(file)]; + }, + realpathSync: function (file) { + var resolved = path.resolve(file); + + if (resolved.indexOf('symlinked') >= 0) { + return resolved; + } + + var ext = path.extname(resolved); + + if (ext) { + var dir = path.dirname(resolved); + var base = path.basename(resolved); + return path.join(dir, 'symlinked', base); + } + return path.join(resolved, 'symlinked'); + } + }; + } + + t.equal( + resolve.sync('./baz', opts('/foo/bar')), + path.resolve('/foo/bar/symlinked/baz.js') + ); + + t.equal( + resolve.sync('./baz.js', opts('/foo/bar')), + path.resolve('/foo/bar/symlinked/baz.js') + ); +}); + +test('readPackageSync', function (t) { + t.plan(3); + + var files = {}; + files[path.resolve('/foo/node_modules/bar/something-else.js')] = 'beep'; + files[path.resolve('/foo/node_modules/bar/package.json')] = JSON.stringify({ + main: './baz.js' + }); + files[path.resolve('/foo/node_modules/bar/baz.js')] = 'boop'; + + var dirs = {}; + dirs[path.resolve('/foo')] = true; + dirs[path.resolve('/foo/node_modules')] = true; + + function opts(basedir, useReadPackage) { + return { + basedir: path.resolve(basedir), + isFile: function (file) { + return Object.prototype.hasOwnProperty.call(files, path.resolve(file)); + }, + isDirectory: function (dir) { + return !!dirs[path.resolve(dir)]; + }, + readFileSync: useReadPackage ? null : function (file) { + return files[path.resolve(file)]; + }, + realpathSync: function (file) { + return file; + } + }; + } + t.test('with readFile', function (st) { + st.plan(1); + + st.equal( + resolve.sync('bar', opts('/foo')), + path.resolve('/foo/node_modules/bar/baz.js') + ); + }); + + var readPackageSync = function (readFileSync, file) { + if (file.indexOf(path.join('bar', 'package.json')) >= 0) { + return { main: './something-else.js' }; + } + return JSON.parse(files[path.resolve(file)]); + }; + + t.test('with readPackage', function (st) { + st.plan(1); + + var options = opts('/foo'); + delete options.readFileSync; + options.readPackageSync = readPackageSync; + + st.equal( + resolve.sync('bar', options), + path.resolve('/foo/node_modules/bar/something-else.js') + ); + }); + + t.test('with readFile and readPackage', function (st) { + st.plan(1); + + var options = opts('/foo'); + options.readPackageSync = readPackageSync; + st.throws( + function () { resolve.sync('bar', options); }, + TypeError, + 'errors when both readFile and readPackage are provided' + ); + }); +}); + diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js new file mode 100644 index 0000000000000000000000000000000000000000..b50e5bb1751d69694deaae36d5f0bc6691498008 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir.js @@ -0,0 +1,56 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('moduleDirectory strings', function (t) { + t.plan(4); + var dir = path.join(__dirname, 'module_dir'); + var xopts = { + basedir: dir, + moduleDirectory: 'xmodules' + }; + resolve('aaa', xopts, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, '/xmodules/aaa/index.js')); + }); + + var yopts = { + basedir: dir, + moduleDirectory: 'ymodules' + }; + resolve('aaa', yopts, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, '/ymodules/aaa/index.js')); + }); +}); + +test('moduleDirectory array', function (t) { + t.plan(6); + var dir = path.join(__dirname, 'module_dir'); + var aopts = { + basedir: dir, + moduleDirectory: ['xmodules', 'ymodules', 'zmodules'] + }; + resolve('aaa', aopts, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, '/xmodules/aaa/index.js')); + }); + + var bopts = { + basedir: dir, + moduleDirectory: ['zmodules', 'ymodules', 'xmodules'] + }; + resolve('aaa', bopts, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, '/ymodules/aaa/index.js')); + }); + + var copts = { + basedir: dir, + moduleDirectory: ['xmodules', 'ymodules', 'zmodules'] + }; + resolve('bbb', copts, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, '/zmodules/bbb/main.js')); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/xmodules/aaa/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/xmodules/aaa/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/ymodules/aaa/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/ymodules/aaa/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/main.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/main.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json new file mode 100644 index 0000000000000000000000000000000000000000..c13b8cf6acfd3344bc2c7969a31d930d933fdf22 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/module_dir/zmodules/bbb/package.json @@ -0,0 +1,3 @@ +{ + "main": "main.js" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js new file mode 100644 index 0000000000000000000000000000000000000000..675441db2ced7b7facac9b7344fe8ea98c16e45b --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/node-modules-paths.js @@ -0,0 +1,143 @@ +var test = require('tape'); +var path = require('path'); +var parse = path.parse || require('path-parse'); +var keys = require('object-keys'); + +var nodeModulesPaths = require('../lib/node-modules-paths'); + +var verifyDirs = function verifyDirs(t, start, dirs, moduleDirectories, paths) { + var moduleDirs = [].concat(moduleDirectories || 'node_modules'); + if (paths) { + for (var k = 0; k < paths.length; ++k) { + moduleDirs.push(path.basename(paths[k])); + } + } + + var foundModuleDirs = {}; + var uniqueDirs = {}; + var parsedDirs = {}; + for (var i = 0; i < dirs.length; ++i) { + var parsed = parse(dirs[i]); + if (!foundModuleDirs[parsed.base]) { foundModuleDirs[parsed.base] = 0; } + foundModuleDirs[parsed.base] += 1; + parsedDirs[parsed.dir] = true; + uniqueDirs[dirs[i]] = true; + } + t.equal(keys(parsedDirs).length >= start.split(path.sep).length, true, 'there are >= dirs than "start" has'); + var foundModuleDirNames = keys(foundModuleDirs); + t.deepEqual(foundModuleDirNames, moduleDirs, 'all desired module dirs were found'); + t.equal(keys(uniqueDirs).length, dirs.length, 'all dirs provided were unique'); + + var counts = {}; + for (var j = 0; j < foundModuleDirNames.length; ++j) { + counts[foundModuleDirs[j]] = true; + } + t.equal(keys(counts).length, 1, 'all found module directories had the same count'); +}; + +test('node-modules-paths', function (t) { + t.test('no options', function (t) { + var start = path.join(__dirname, 'resolver'); + var dirs = nodeModulesPaths(start); + + verifyDirs(t, start, dirs); + + t.end(); + }); + + t.test('empty options', function (t) { + var start = path.join(__dirname, 'resolver'); + var dirs = nodeModulesPaths(start, {}); + + verifyDirs(t, start, dirs); + + t.end(); + }); + + t.test('with paths=array option', function (t) { + var start = path.join(__dirname, 'resolver'); + var paths = ['a', 'b']; + var dirs = nodeModulesPaths(start, { paths: paths }); + + verifyDirs(t, start, dirs, null, paths); + + t.end(); + }); + + t.test('with paths=function option', function (t) { + var paths = function paths(request, absoluteStart, getNodeModulesDirs, opts) { + return getNodeModulesDirs().concat(path.join(absoluteStart, 'not node modules', request)); + }; + + var start = path.join(__dirname, 'resolver'); + var dirs = nodeModulesPaths(start, { paths: paths }, 'pkg'); + + verifyDirs(t, start, dirs, null, [path.join(start, 'not node modules', 'pkg')]); + + t.end(); + }); + + t.test('with paths=function skipping node modules resolution', function (t) { + var paths = function paths(request, absoluteStart, getNodeModulesDirs, opts) { + return []; + }; + var start = path.join(__dirname, 'resolver'); + var dirs = nodeModulesPaths(start, { paths: paths }); + t.deepEqual(dirs, [], 'no node_modules was computed'); + t.end(); + }); + + t.test('with moduleDirectory option', function (t) { + var start = path.join(__dirname, 'resolver'); + var moduleDirectory = 'not node modules'; + var dirs = nodeModulesPaths(start, { moduleDirectory: moduleDirectory }); + + verifyDirs(t, start, dirs, moduleDirectory); + + t.end(); + }); + + t.test('with 1 moduleDirectory and paths options', function (t) { + var start = path.join(__dirname, 'resolver'); + var paths = ['a', 'b']; + var moduleDirectory = 'not node modules'; + var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectory }); + + verifyDirs(t, start, dirs, moduleDirectory, paths); + + t.end(); + }); + + t.test('with 1+ moduleDirectory and paths options', function (t) { + var start = path.join(__dirname, 'resolver'); + var paths = ['a', 'b']; + var moduleDirectories = ['not node modules', 'other modules']; + var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories }); + + verifyDirs(t, start, dirs, moduleDirectories, paths); + + t.end(); + }); + + t.test('combine paths correctly on Windows', function (t) { + var start = 'C:\\Users\\username\\myProject\\src'; + var paths = []; + var moduleDirectories = ['node_modules', start]; + var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories }); + + t.equal(dirs.indexOf(path.resolve(start)) > -1, true, 'should contain start dir'); + + t.end(); + }); + + t.test('combine paths correctly on non-Windows', { skip: process.platform === 'win32' }, function (t) { + var start = '/Users/username/git/myProject/src'; + var paths = []; + var moduleDirectories = ['node_modules', '/Users/username/git/myProject/src']; + var dirs = nodeModulesPaths(start, { paths: paths, moduleDirectory: moduleDirectories }); + + t.equal(dirs.indexOf(path.resolve(start)) > -1, true, 'should contain start dir'); + + t.end(); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js new file mode 100644 index 0000000000000000000000000000000000000000..e463d6c8c313b3554d5d4e07643fea29cfa86c51 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path.js @@ -0,0 +1,70 @@ +var fs = require('fs'); +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('$NODE_PATH', function (t) { + t.plan(8); + + var isDir = function (dir, cb) { + if (dir === '/node_path' || dir === 'node_path/x') { + return cb(null, true); + } + fs.stat(dir, function (err, stat) { + if (!err) { + return cb(null, stat.isDirectory()); + } + if (err.code === 'ENOENT' || err.code === 'ENOTDIR') return cb(null, false); + return cb(err); + }); + }; + + resolve('aaa', { + paths: [ + path.join(__dirname, '/node_path/x'), + path.join(__dirname, '/node_path/y') + ], + basedir: __dirname, + isDirectory: isDir + }, function (err, res) { + t.error(err); + t.equal(res, path.join(__dirname, '/node_path/x/aaa/index.js'), 'aaa resolves'); + }); + + resolve('bbb', { + paths: [ + path.join(__dirname, '/node_path/x'), + path.join(__dirname, '/node_path/y') + ], + basedir: __dirname, + isDirectory: isDir + }, function (err, res) { + t.error(err); + t.equal(res, path.join(__dirname, '/node_path/y/bbb/index.js'), 'bbb resolves'); + }); + + resolve('ccc', { + paths: [ + path.join(__dirname, '/node_path/x'), + path.join(__dirname, '/node_path/y') + ], + basedir: __dirname, + isDirectory: isDir + }, function (err, res) { + t.error(err); + t.equal(res, path.join(__dirname, '/node_path/x/ccc/index.js'), 'ccc resolves'); + }); + + // ensure that relative paths still resolve against the regular `node_modules` correctly + resolve('tap', { + paths: [ + 'node_path' + ], + basedir: path.join(__dirname, 'node_path/x'), + isDirectory: isDir + }, function (err, res) { + var root = require('tap/package.json').main; // eslint-disable-line global-require + t.error(err); + t.equal(res, path.resolve(__dirname, '..', 'node_modules/tap', root), 'tap resolves'); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/x/aaa/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/x/aaa/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/x/ccc/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/x/ccc/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/y/bbb/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/y/bbb/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/y/ccc/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/node_path/y/ccc/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js new file mode 100644 index 0000000000000000000000000000000000000000..ef63c40f9393dc63219d5e3debb7ed6db175c455 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/nonstring.js @@ -0,0 +1,9 @@ +var test = require('tape'); +var resolve = require('../'); + +test('nonstring', function (t) { + t.plan(1); + resolve(555, function (err, res, pkg) { + t.ok(err); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js new file mode 100644 index 0000000000000000000000000000000000000000..16519aeae51c4fb65b8ccdd69f303a2315358ef3 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter.js @@ -0,0 +1,75 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +var resolverDir = path.join(__dirname, '/pathfilter/deep_ref'); + +var pathFilterFactory = function (t) { + return function (pkg, x, remainder) { + t.equal(pkg.version, '1.2.3'); + t.equal(x, path.join(resolverDir, 'node_modules/deep/ref')); + t.equal(remainder, 'ref'); + return 'alt'; + }; +}; + +test('#62: deep module references and the pathFilter', function (t) { + t.test('deep/ref.js', function (st) { + st.plan(3); + + resolve('deep/ref', { basedir: resolverDir }, function (err, res, pkg) { + if (err) st.fail(err); + + st.equal(pkg.version, '1.2.3'); + st.equal(res, path.join(resolverDir, 'node_modules/deep/ref.js')); + }); + + var res = resolve.sync('deep/ref', { basedir: resolverDir }); + st.equal(res, path.join(resolverDir, 'node_modules/deep/ref.js')); + }); + + t.test('deep/deeper/ref', function (st) { + st.plan(4); + + resolve( + 'deep/deeper/ref', + { basedir: resolverDir }, + function (err, res, pkg) { + if (err) t.fail(err); + st.notEqual(pkg, undefined); + st.equal(pkg.version, '1.2.3'); + st.equal(res, path.join(resolverDir, 'node_modules/deep/deeper/ref.js')); + } + ); + + var res = resolve.sync( + 'deep/deeper/ref', + { basedir: resolverDir } + ); + st.equal(res, path.join(resolverDir, 'node_modules/deep/deeper/ref.js')); + }); + + t.test('deep/ref alt', function (st) { + st.plan(8); + + var pathFilter = pathFilterFactory(st); + + var res = resolve.sync( + 'deep/ref', + { basedir: resolverDir, pathFilter: pathFilter } + ); + st.equal(res, path.join(resolverDir, 'node_modules/deep/alt.js')); + + resolve( + 'deep/ref', + { basedir: resolverDir, pathFilter: pathFilter }, + function (err, res, pkg) { + if (err) st.fail(err); + st.equal(res, path.join(resolverDir, 'node_modules/deep/alt.js')); + st.end(); + } + ); + }); + + t.end(); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter/deep_ref/main.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter/deep_ref/main.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js new file mode 100644 index 0000000000000000000000000000000000000000..e1ff72843c0462e81300750c5c30e698266c4f16 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/pathfilter_sync.js @@ -0,0 +1,24 @@ +var test = require('tape'); +var path = require('path'); +var resolve = require('../'); + +test('synchronous pathfilter', function (t) { + var res; + var resolverDir = __dirname + '/pathfilter/deep_ref'; + var pathFilter = function (pkg, x, remainder) { + t.equal(pkg.version, '1.2.3'); + t.equal(x, path.join(resolverDir, 'node_modules', 'deep', 'ref')); + t.equal(remainder, 'ref'); + return 'alt'; + }; + + res = resolve.sync('deep/ref', { basedir: resolverDir }); + t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'ref.js')); + + res = resolve.sync('deep/deeper/ref', { basedir: resolverDir }); + t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'deeper', 'ref.js')); + + res = resolve.sync('deep/ref', { basedir: resolverDir, pathFilter: pathFilter }); + t.equal(res, path.join(resolverDir, 'node_modules', 'deep', 'alt.js')); + t.end(); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js new file mode 100644 index 0000000000000000000000000000000000000000..2febb598fbc06832ce5b076735cb9f79fb257c7c --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence.js @@ -0,0 +1,23 @@ +var path = require('path'); +var test = require('tape'); +var resolve = require('../'); + +test('precedence', function (t) { + t.plan(3); + var dir = path.join(__dirname, 'precedence/aaa'); + + resolve('./', { basedir: dir }, function (err, res, pkg) { + t.ifError(err); + t.equal(res, path.join(dir, 'index.js')); + t.equal(pkg.name, 'resolve'); + }); +}); + +test('./ should not load ${dir}.js', function (t) { // eslint-disable-line no-template-curly-in-string + t.plan(1); + var dir = path.join(__dirname, 'precedence/bbb'); + + resolve('./', { basedir: dir }, function (err, res, pkg) { + t.ok(err); + }); +}); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa/main.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/aaa/main.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/bbb.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/bbb.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/bbb/main.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/precedence/bbb/main.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/doom.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/doom.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json new file mode 100644 index 0000000000000000000000000000000000000000..2f77720b8672a0e2ed8b49636775335884cb3ec0 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/package.json @@ -0,0 +1,4 @@ +{ + "name": "baz", + "main": "quux.js" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/quux.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/baz/quux.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/a.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/a.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/b.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/b.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json new file mode 100644 index 0000000000000000000000000000000000000000..bf406f0830f8aab6aaec9e3f60f1623fbe6854e6 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/browser_field/package.json @@ -0,0 +1,5 @@ +{ + "name": "browser_field", + "main": "a", + "browser": "b" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee new file mode 100644 index 0000000000000000000000000000000000000000..8b137891791fe96927ad78e64b0aad7bded08bdc --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/cup.coffee @@ -0,0 +1 @@ + diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..d7f4fc8079f60aaee820b293537b21d488a1bc31 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_main/package.json @@ -0,0 +1,3 @@ +{ + "main": "." +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f51287b9d1e739a8827578a541e89f7e91450615 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/dot_slash_main/package.json @@ -0,0 +1,3 @@ +{ + "main": "./" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..dbb176e65c61b760b5df1b2d717846a98614e02f --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/empty_main/package.json @@ -0,0 +1,3 @@ +{ + "main": "" +} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..a7416c0c7aa4a1df56419f92dcc7519836db8978 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/false_main/package.json @@ -0,0 +1,4 @@ +{ + "name": "false_main", + "main": false +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/foo.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/foo.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..b7188041763f8a94977f173a26c41fdbf5f2effb --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/incorrect_main/package.json @@ -0,0 +1,3 @@ +{ + "main": "wrong.js" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0590748642ae2eadb17726ec3d1b501c94652277 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/invalid_main/package.json @@ -0,0 +1,7 @@ +{ + "name": "invalid_main", + "main": [ + "why is this a thing", + "srsly omg wtf" + ] +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f2c75cf8e443b44d42ba5fd2527554f1f90c0db2 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_index/package.json @@ -0,0 +1,3 @@ +{ + "main": "index.js" +} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..0bf896ce2608fb56bba875b23b90fb0e670db264 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/missing_main/package.json @@ -0,0 +1,3 @@ +{ + "notmain": "index.js" +} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/mug.coffee b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/mug.coffee new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/mug.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/mug.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json new file mode 100644 index 0000000000000000000000000000000000000000..d6707ca0cd64d48d212b90abe2e72f24b092572f --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/lerna.json @@ -0,0 +1,6 @@ +{ + "packages": [ + "packages/*" + ], + "version": "0.0.0" +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json new file mode 100644 index 0000000000000000000000000000000000000000..4391d392ea2761caea4d8286cc8ccc56cfa3e4a5 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/package.json @@ -0,0 +1,20 @@ +{ + "name": "ljharb-monorepo-symlink-test", + "private": true, + "version": "0.0.0", + "description": "", + "main": "index.js", + "scripts": { + "postinstall": "lerna bootstrap", + "test": "node packages/package-a" + }, + "author": "", + "license": "MIT", + "dependencies": { + "jquery": "^3.3.1", + "resolve": "../../../" + }, + "devDependencies": { + "lerna": "^3.4.3" + } +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js new file mode 100644 index 0000000000000000000000000000000000000000..8875a32df0ffc4eda94bb6c6dd9855ac604820c4 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/index.js @@ -0,0 +1,35 @@ +'use strict'; + +var assert = require('assert'); +var path = require('path'); +var resolve = require('resolve'); + +var basedir = __dirname + '/node_modules/@my-scope/package-b'; + +var expected = path.join(__dirname, '../../node_modules/jquery/dist/jquery.js'); + +/* + * preserveSymlinks === false + * will search NPM package from + * - packages/package-b/node_modules + * - packages/node_modules + * - node_modules + */ +assert.equal(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: false }), expected); +assert.equal(resolve.sync('../../node_modules/jquery', { basedir: basedir, preserveSymlinks: false }), expected); + +/* + * preserveSymlinks === true + * will search NPM package from + * - packages/package-a/node_modules/@my-scope/packages/package-b/node_modules + * - packages/package-a/node_modules/@my-scope/packages/node_modules + * - packages/package-a/node_modules/@my-scope/node_modules + * - packages/package-a/node_modules/node_modules + * - packages/package-a/node_modules + * - packages/node_modules + * - node_modules + */ +assert.equal(resolve.sync('jquery', { basedir: basedir, preserveSymlinks: true }), expected); +assert.equal(resolve.sync('../../../../../node_modules/jquery', { basedir: basedir, preserveSymlinks: true }), expected); + +console.log(' * all monorepo paths successfully resolved through symlinks'); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json new file mode 100644 index 0000000000000000000000000000000000000000..204de51e05878b3451223ba654c5e4a790ef1e51 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-a/package.json @@ -0,0 +1,14 @@ +{ + "name": "@my-scope/package-a", + "version": "0.0.0", + "private": true, + "description": "", + "license": "MIT", + "main": "index.js", + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "@my-scope/package-b": "^0.0.0" + } +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..f57c3b5f5e454d3948e1a4bc5aec10fdcd79d8b5 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/multirepo/packages/package-b/package.json @@ -0,0 +1,14 @@ +{ + "name": "@my-scope/package-b", + "private": true, + "version": "0.0.0", + "description": "", + "license": "MIT", + "main": "index.js", + "scripts": { + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "dependencies": { + "@my-scope/package-a": "^0.0.0" + } +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js new file mode 100644 index 0000000000000000000000000000000000000000..9b4846a82a77be169097f041f791070732229cb7 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/async.js @@ -0,0 +1,26 @@ +var a = require.resolve('buffer/').replace(process.cwd(), '$CWD'); +var b; +var c; + +var test = function test() { + console.log(a, ': require.resolve, preserveSymlinks ' + (process.execArgv.indexOf('preserve-symlinks') > -1 ? 'true' : 'false')); + console.log(b, ': preserveSymlinks true'); + console.log(c, ': preserveSymlinks false'); + + if (a !== b && a !== c) { + throw 'async: no match'; + } + console.log('async: success! a matched either b or c\n'); +}; + +require('resolve')('buffer/', { preserveSymlinks: true }, function (err, result) { + if (err) { throw err; } + b = result.replace(process.cwd(), '$CWD'); + if (b && c) { test(); } +}); +require('resolve')('buffer/', { preserveSymlinks: false }, function (err, result) { + if (err) { throw err; } + c = result.replace(process.cwd(), '$CWD'); + if (b && c) { test(); } +}); + diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json new file mode 100644 index 0000000000000000000000000000000000000000..acfe9e9517720ab5532c247052fadb214c5ffef3 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/package.json @@ -0,0 +1,15 @@ +{ + "name": "mylib", + "version": "0.0.0", + "description": "", + "private": true, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "buffer": "*" + } +} diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js new file mode 100644 index 0000000000000000000000000000000000000000..3283efc2ec81f8d2a62be9a1fd28a192403ce549 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/nested_symlinks/mylib/sync.js @@ -0,0 +1,12 @@ +var a = require.resolve('buffer/').replace(process.cwd(), '$CWD'); +var b = require('resolve').sync('buffer/', { preserveSymlinks: true }).replace(process.cwd(), '$CWD'); +var c = require('resolve').sync('buffer/', { preserveSymlinks: false }).replace(process.cwd(), '$CWD'); + +console.log(a, ': require.resolve, preserveSymlinks ' + (process.execArgv.indexOf('preserve-symlinks') > -1 ? 'true' : 'false')); +console.log(b, ': preserveSymlinks true'); +console.log(c, ': preserveSymlinks false'); + +if (a !== b && a !== c) { + throw 'sync: no match'; +} +console.log('sync: success! a matched either b or c\n'); diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json new file mode 100644 index 0000000000000000000000000000000000000000..b82890e2b0f11de27628dd9347e003a852d07759 --- /dev/null +++ b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/null_main/package.json @@ -0,0 +1,3 @@ +{ + "main": null +} \ No newline at end of file diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/other_path/lib/other-lib.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/other_path/lib/other-lib.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/other_path/root.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/other_path/root.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/quux/foo/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/quux/foo/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/same_names/foo.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/same_names/foo.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/same_names/foo/index.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/same_names/foo/index.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/_/node_modules/foo.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/_/node_modules/foo.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/_/symlink_target/.gitkeep new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/package/bar.js b/node_modules/eslint-plugin-react/node_modules/resolve/test/resolver/symlinked/package/bar.js new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391