UNPKG

eslint-plugin-react-x

Version:

A set of composable ESLint rules for for libraries and frameworks that use React as a UI runtime.

1,595 lines (1,584 loc) • 126 kB
'use strict'; var shared = require('@eslint-react/shared'); var ER25 = require('@eslint-react/core'); var utils = require('@typescript-eslint/utils'); var kit = require('@eslint-react/kit'); var types = require('@typescript-eslint/types'); var VAR2 = require('@eslint-react/var'); var tsPattern = require('ts-pattern'); var ts = require('typescript'); var AST8 = require('@eslint-react/ast'); var eff = require('@eslint-react/eff'); var compareVersions = require('compare-versions'); var typeUtils = require('@typescript-eslint/type-utils'); var tsApiUtils = require('ts-api-utils'); var isImmutableType = require('is-immutable-type'); function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; } function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n.default = e; return Object.freeze(n); } var ER25__namespace = /*#__PURE__*/_interopNamespace(ER25); var VAR2__namespace = /*#__PURE__*/_interopNamespace(VAR2); var ts__default = /*#__PURE__*/_interopDefault(ts); var AST8__namespace = /*#__PURE__*/_interopNamespace(AST8); var __defProp = Object.defineProperty; var __export = (target, all) => { for (var name5 in all) __defProp(target, name5, { get: all[name5], enumerable: true }); }; // src/configs/recommended.ts var recommended_exports = {}; __export(recommended_exports, { name: () => name, rules: () => rules, settings: () => settings }); var name = "react-x/recommended"; var rules = { "react-x/jsx-no-duplicate-props": "warn", "react-x/jsx-uses-react": "warn", "react-x/jsx-uses-vars": "warn", "react-x/no-access-state-in-setstate": "error", "react-x/no-array-index-key": "warn", "react-x/no-children-count": "warn", "react-x/no-children-for-each": "warn", "react-x/no-children-map": "warn", "react-x/no-children-only": "warn", "react-x/no-children-to-array": "warn", "react-x/no-clone-element": "warn", "react-x/no-comment-textnodes": "warn", "react-x/no-component-will-mount": "error", "react-x/no-component-will-receive-props": "error", "react-x/no-component-will-update": "error", "react-x/no-context-provider": "warn", "react-x/no-create-ref": "error", "react-x/no-default-props": "error", "react-x/no-direct-mutation-state": "error", "react-x/no-duplicate-key": "warn", "react-x/no-forward-ref": "warn", "react-x/no-implicit-key": "warn", "react-x/no-missing-key": "error", "react-x/no-misused-capture-owner-stack": "error", "react-x/no-nested-component-definitions": "error", "react-x/no-nested-lazy-component-declarations": "warn", "react-x/no-prop-types": "error", "react-x/no-redundant-should-component-update": "error", "react-x/no-set-state-in-component-did-mount": "warn", "react-x/no-set-state-in-component-did-update": "warn", "react-x/no-set-state-in-component-will-update": "warn", "react-x/no-string-refs": "error", "react-x/no-unsafe-component-will-mount": "warn", "react-x/no-unsafe-component-will-receive-props": "warn", "react-x/no-unsafe-component-will-update": "warn", "react-x/no-unstable-context-value": "warn", "react-x/no-unstable-default-props": "warn", "react-x/no-unused-class-component-members": "warn", "react-x/no-unused-state": "warn", "react-x/no-use-context": "warn", "react-x/no-useless-forward-ref": "warn" }; var settings = { "react-x": shared.DEFAULT_ESLINT_REACT_SETTINGS }; // src/configs/recommended-type-checked.ts var recommended_type_checked_exports = {}; __export(recommended_type_checked_exports, { name: () => name3, rules: () => rules3, settings: () => settings3 }); // src/configs/recommended-typescript.ts var recommended_typescript_exports = {}; __export(recommended_typescript_exports, { name: () => name2, rules: () => rules2, settings: () => settings2 }); var name2 = "react-x/recommended-typescript"; var rules2 = { ...rules, "react-x/jsx-no-duplicate-props": "off", "react-x/jsx-no-undef": "off", "react-x/jsx-uses-react": "off", "react-x/jsx-uses-vars": "off" }; var settings2 = { ...settings }; // src/configs/recommended-type-checked.ts var name3 = "react-x/recommended-type-checked"; var rules3 = { ...rules2, "react-x/no-leaked-conditional-rendering": "warn" // "react-x/prefer-read-only-props": "warn", }; var settings3 = { ...settings2 }; // package.json var name4 = "eslint-plugin-react-x"; var version = "1.48.4"; var createRule = utils.ESLintUtils.RuleCreator(shared.getDocsUrl("x")); // src/rules/avoid-shorthand-boolean.ts var RULE_NAME = "avoid-shorthand-boolean"; var RULE_FEATURES = []; var avoid_shorthand_boolean_default = createRule({ meta: { type: "problem", docs: { description: "Enforces explicit boolean values for boolean attributes.", [Symbol.for("rule_features")]: RULE_FEATURES }, fixable: "code", messages: { avoidShorthandBoolean: "Avoid using shorthand boolean attribute '{{propName}}'. Use '{{propName}}={true}' instead." }, schema: [] }, name: RULE_NAME, create, defaultOptions: [] }); function create(context) { return { JSXAttribute(node) { if (node.value == null) { context.report({ messageId: "avoidShorthandBoolean", node, data: { propName: ER25__namespace.getAttributeName(context, node) }, fix: (fixer) => fixer.insertTextAfter(node.name, `={true}`) }); } } }; } var RULE_NAME2 = "avoid-shorthand-fragment"; var RULE_FEATURES2 = []; var avoid_shorthand_fragment_default = createRule({ meta: { type: "problem", docs: { description: "Enforces explicit `<Fragment>` components instead of the shorthand `<>` or `</>` syntax.", [Symbol.for("rule_features")]: RULE_FEATURES2 }, messages: { avoidShorthandFragment: "Avoid using shorthand fragment syntax. Use '{{jsxFragmentFactory}}' component instead." }, schema: [] }, name: RULE_NAME2, create: create2, defaultOptions: [] }); function create2(context) { const jsxConfigFromContext = kit.JsxConfig.getFromContext(context); const jsxConfigFromAnnotation = kit.JsxConfig.getFromAnnotation(context); const jsxConfig = { ...jsxConfigFromContext, ...jsxConfigFromAnnotation }; return { JSXFragment(node) { context.report({ messageId: "avoidShorthandFragment", node, data: { jsxFragmentFactory: jsxConfig.jsxFragmentFactory } }); } }; } var RULE_NAME3 = "jsx-no-duplicate-props"; var RULE_FEATURES3 = []; var jsx_no_duplicate_props_default = createRule({ meta: { type: "problem", docs: { description: "Disallow duplicate props in JSX elements.", [Symbol.for("rule_features")]: RULE_FEATURES3 }, messages: { jsxNoDuplicateProps: "This JSX property is assigned multiple times." }, schema: [] }, name: RULE_NAME3, create: create3, defaultOptions: [] }); function create3(context) { return { JSXOpeningElement(node) { const props = []; for (const attr of node.attributes) { if (attr.type === types.AST_NODE_TYPES.JSXSpreadAttribute) { continue; } const name5 = attr.name.name; if (typeof name5 !== "string") { continue; } if (!props.includes(name5)) { props.push(name5); continue; } context.report({ messageId: "jsxNoDuplicateProps", node: attr }); } } }; } var RULE_NAME4 = "jsx-no-undef"; var RULE_FEATURES4 = []; var jsx_no_undef_default = createRule({ meta: { type: "problem", docs: { description: "Disallow undefined variables in JSX.", [Symbol.for("rule_features")]: RULE_FEATURES4 }, messages: { jsxNoUndef: "JSX variable '{{name}}' is not defined." }, schema: [] }, name: RULE_NAME4, create: create4, defaultOptions: [] }); function create4(context) { return { JSXOpeningElement(node) { const name5 = tsPattern.match(node.name).with({ type: types.AST_NODE_TYPES.JSXIdentifier }, (n) => n.name).with({ type: types.AST_NODE_TYPES.JSXMemberExpression, object: { type: types.AST_NODE_TYPES.JSXIdentifier } }, (n) => n.object.name).otherwise(() => null); if (name5 == null) return; if (name5 === "this") return; if (/^[a-z]/u.test(name5)) return; if (VAR2__namespace.findVariable(name5, context.sourceCode.getScope(node)) == null) { context.report({ messageId: "jsxNoUndef", node, data: { name: name5 } }); } } }; } var RULE_NAME5 = "jsx-uses-react"; var RULE_FEATURES5 = []; var jsx_uses_react_default = createRule({ meta: { type: "problem", docs: { description: "Marks React variables as used when JSX is used.", [Symbol.for("rule_features")]: RULE_FEATURES5 }, messages: { jsxUsesReact: "Marked {{name}} as used." }, schema: [] }, name: RULE_NAME5, create: create5, defaultOptions: [] }); function create5(context) { const jsxConfig = { ...kit.JsxConfig.getFromContext(context), ...kit.JsxConfig.getFromAnnotation(context) }; const { jsx, jsxFactory, jsxFragmentFactory } = jsxConfig; if (jsx === ts.JsxEmit.ReactJSX || jsx === ts.JsxEmit.ReactJSXDev) return {}; function handleJsxElement(node) { context.sourceCode.markVariableAsUsed(jsxFactory, node); debugReport(context, node, jsxFactory); } function handleJsxFragment(node) { context.sourceCode.markVariableAsUsed(jsxFragmentFactory, node); debugReport(context, node, jsxFragmentFactory); } return { JSXFragment: handleJsxFragment, JSXOpeningElement: handleJsxElement, JSXOpeningFragment: handleJsxElement }; } function debugReport(context, node, name5) { if (process.env["ESLINT_REACT_DEBUG"] !== "1") return; context.report({ messageId: "jsxUsesReact", node, data: { name: name5 } }); } var RULE_NAME6 = "jsx-uses-vars"; var RULE_FEATURES6 = []; var jsx_uses_vars_default = createRule({ meta: { type: "problem", docs: { description: "Marks variables used in JSX elements as used.", [Symbol.for("rule_features")]: RULE_FEATURES6 }, messages: { jsxUsesVars: "" }, schema: [] }, name: RULE_NAME6, create: create6, defaultOptions: [] }); function create6(context) { return { JSXOpeningElement(node) { switch (node.name.type) { case types.AST_NODE_TYPES.JSXIdentifier: { if (/^[a-z]/u.test(node.name.name)) { return; } context.sourceCode.markVariableAsUsed(node.name.name, node); break; } case types.AST_NODE_TYPES.JSXMemberExpression: { const { object } = node.name; if (object.type === types.AST_NODE_TYPES.JSXIdentifier) { context.sourceCode.markVariableAsUsed(object.name, node); } break; } } } }; } var RULE_NAME7 = "no-access-state-in-setstate"; var RULE_FEATURES7 = []; function isKeyLiteral(node, key) { return tsPattern.match(key).with({ type: types.AST_NODE_TYPES.Literal }, eff.constTrue).with({ type: types.AST_NODE_TYPES.TemplateLiteral, expressions: [] }, eff.constTrue).with({ type: types.AST_NODE_TYPES.Identifier }, () => !node.computed).otherwise(eff.constFalse); } var no_access_state_in_setstate_default = createRule({ meta: { type: "problem", docs: { description: "Disallow accessing `this.state` inside `setState` calls.", [Symbol.for("rule_features")]: RULE_FEATURES7 }, messages: { noAccessStateInSetstate: "Do not access 'this.state' within 'setState'. Use the update function instead." }, schema: [] }, name: RULE_NAME7, create: create7, defaultOptions: [] }); function create7(context) { if (!context.sourceCode.text.includes("setState")) { return {}; } const classEntries = []; const methodEntries = []; const setStateEntries = []; return { CallExpression(node) { if (!ER25__namespace.isThisSetState(node)) { return; } setStateEntries.push([node, false]); }, "CallExpression:exit"(node) { if (!ER25__namespace.isThisSetState(node)) { return; } setStateEntries.pop(); }, ClassDeclaration(node) { classEntries.push([node, ER25__namespace.isClassComponent(node)]); }, "ClassDeclaration:exit"() { classEntries.pop(); }, ClassExpression(node) { classEntries.push([node, ER25__namespace.isClassComponent(node)]); }, "ClassExpression:exit"() { classEntries.pop(); }, MemberExpression(node) { if (!AST8__namespace.isThisExpression(node.object)) { return; } const [currClass, isComponent = false] = classEntries.at(-1) ?? []; if (currClass == null || !isComponent) { return; } const [currMethod, isStatic = false] = methodEntries.at(-1) ?? []; if (currMethod == null || isStatic) { return; } const [setState, hasThisState = false] = setStateEntries.at(-1) ?? []; if (setState == null || hasThisState) { return; } if (AST8__namespace.getPropertyName(node.property) !== "state") { return; } context.report({ messageId: "noAccessStateInSetstate", node }); }, MethodDefinition(node) { methodEntries.push([node, node.static]); }, "MethodDefinition:exit"() { methodEntries.pop(); }, PropertyDefinition(node) { methodEntries.push([node, node.static]); }, "PropertyDefinition:exit"() { methodEntries.pop(); }, VariableDeclarator(node) { const [currClass, isComponent = false] = classEntries.at(-1) ?? []; if (currClass == null || !isComponent) { return; } const [currMethod, isStatic = false] = methodEntries.at(-1) ?? []; if (currMethod == null || isStatic) { return; } const [setState, hasThisState = false] = setStateEntries.at(-1) ?? []; if (setState == null || hasThisState) { return; } if (node.init == null || !AST8__namespace.isThisExpression(node.init) || node.id.type !== types.AST_NODE_TYPES.ObjectPattern) { return; } const hasState = node.id.properties.some( (prop) => prop.type === types.AST_NODE_TYPES.Property && isKeyLiteral(prop, prop.key) && AST8__namespace.getPropertyName(prop.key) === "state" ); if (!hasState) { return; } context.report({ messageId: "noAccessStateInSetstate", node }); } }; } var RULE_NAME8 = "no-array-index-key"; var RULE_FEATURES8 = []; var reactChildrenMethod = ["forEach", "map"]; function isReactChildrenMethod(name5) { return reactChildrenMethod.some((method) => method === name5); } function isUsingReactChildren(context, node) { const { importSource = "react" } = shared.coerceSettings(context.settings); const { callee } = node; if (!("property" in callee) || !("object" in callee) || !("name" in callee.property)) { return false; } if (!isReactChildrenMethod(callee.property.name)) { return false; } const initialScope = context.sourceCode.getScope(node); if (callee.object.type === types.AST_NODE_TYPES.Identifier && callee.object.name === "Children") { return true; } if (callee.object.type === types.AST_NODE_TYPES.MemberExpression && "name" in callee.object.object) { return ER25__namespace.isInitializedFromReact(callee.object.object.name, importSource, initialScope); } return false; } function getMapIndexParamName(context, node) { const { callee } = node; if (callee.type !== types.AST_NODE_TYPES.MemberExpression) { return eff._; } if (callee.property.type !== types.AST_NODE_TYPES.Identifier) { return eff._; } const { name: name5 } = callee.property; const indexPosition = AST8__namespace.getArrayMethodCallbackIndexParamPosition(name5); if (indexPosition === -1) { return eff._; } const callbackArg = node.arguments[isUsingReactChildren(context, node) ? 1 : 0]; if (callbackArg == null) { return eff._; } if (!AST8__namespace.isOneOf([types.AST_NODE_TYPES.ArrowFunctionExpression, types.AST_NODE_TYPES.FunctionExpression])(callbackArg)) { return eff._; } const { params } = callbackArg; if (params.length < indexPosition + 1) { return eff._; } const param = params.at(indexPosition); return param != null && "name" in param ? param.name : eff._; } function getIdentifiersFromBinaryExpression(side) { if (side.type === types.AST_NODE_TYPES.Identifier) { return [side]; } if (side.type === types.AST_NODE_TYPES.BinaryExpression) { return [ ...getIdentifiersFromBinaryExpression(side.left), ...getIdentifiersFromBinaryExpression(side.right) ]; } return []; } var no_array_index_key_default = createRule({ meta: { type: "problem", docs: { description: "Disallow an item's index in the array as its key.", [Symbol.for("rule_features")]: RULE_FEATURES8 }, messages: { noArrayIndexKey: "Do not use item index in the array as its key." }, schema: [] }, name: RULE_NAME8, create: create8, defaultOptions: [] }); function create8(context) { const report = kit.Reporter.make(context); const indexParamNames = []; function isArrayIndex(node) { return node.type === types.AST_NODE_TYPES.Identifier && indexParamNames.some((name5) => name5 != null && name5 === node.name); } function isCreateOrCloneElementCall(node) { return ER25__namespace.isCreateElementCall(context, node) || ER25__namespace.isCloneElementCall(context, node); } function getReportDescriptors(node) { switch (node.type) { // key={bar} case types.AST_NODE_TYPES.Identifier: { if (indexParamNames.some((name5) => name5 != null && name5 === node.name)) { return [{ messageId: "noArrayIndexKey", node }]; } return []; } // key={`foo-${bar}`} or key={'foo' + bar} case types.AST_NODE_TYPES.TemplateLiteral: case types.AST_NODE_TYPES.BinaryExpression: { const descriptors = []; const expressions = node.type === types.AST_NODE_TYPES.TemplateLiteral ? node.expressions : getIdentifiersFromBinaryExpression(node); for (const expression of expressions) { if (isArrayIndex(expression)) { descriptors.push({ messageId: "noArrayIndexKey", node: expression }); } } return descriptors; } // key={bar.toString()} or key={String(bar)} case types.AST_NODE_TYPES.CallExpression: { switch (true) { // key={bar.toString()} case (node.callee.type === types.AST_NODE_TYPES.MemberExpression && node.callee.property.type === types.AST_NODE_TYPES.Identifier && node.callee.property.name === "toString" && isArrayIndex(node.callee.object)): { return [{ messageId: "noArrayIndexKey", node: node.callee.object }]; } // key={String(bar)} case (node.callee.type === types.AST_NODE_TYPES.Identifier && node.callee.name === "String" && node.arguments[0] != null && isArrayIndex(node.arguments[0])): { return [{ messageId: "noArrayIndexKey", node: node.arguments[0] }]; } } } } return []; } return { CallExpression(node) { indexParamNames.push(getMapIndexParamName(context, node)); if (node.arguments.length === 0) { return; } if (!isCreateOrCloneElementCall(node)) { return; } const [, props] = node.arguments; if (props?.type !== types.AST_NODE_TYPES.ObjectExpression) { return; } for (const prop of props.properties) { if (!tsPattern.isMatching({ key: { name: "key" } })(prop)) { continue; } if (!("value" in prop)) { continue; } for (const descriptor of getReportDescriptors(prop.value)) { report.send(descriptor); } } }, "CallExpression:exit"() { indexParamNames.pop(); }, JSXAttribute(node) { if (node.name.name !== "key") { return; } if (indexParamNames.length === 0) { return; } if (node.value?.type !== types.AST_NODE_TYPES.JSXExpressionContainer) { return; } for (const descriptor of getReportDescriptors(node.value.expression)) { report.send(descriptor); } } }; } var RULE_NAME9 = "no-children-count"; var RULE_FEATURES9 = []; var no_children_count_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `Children.count`.", [Symbol.for("rule_features")]: RULE_FEATURES9 }, messages: { noChildrenCount: "Using 'Children.count' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME9, create: create9, defaultOptions: [] }); function create9(context) { return { MemberExpression(node) { if (ER25__namespace.isChildrenCount(context, node)) { context.report({ messageId: "noChildrenCount", node: node.property }); } } }; } var RULE_NAME10 = "no-children-for-each"; var RULE_FEATURES10 = []; var no_children_for_each_default = createRule({ meta: { type: "problem", docs: { description: "Disallow 'Children.forEach'.", [Symbol.for("rule_features")]: RULE_FEATURES10 }, messages: { noChildrenForEach: "Using 'Children.forEach' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME10, create: create10, defaultOptions: [] }); function create10(context) { return { MemberExpression(node) { if (ER25__namespace.isChildrenForEach(context, node)) { context.report({ messageId: "noChildrenForEach", node: node.property }); } } }; } var RULE_NAME11 = "no-children-map"; var RULE_FEATURES11 = []; var no_children_map_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `Children.map`.", [Symbol.for("rule_features")]: RULE_FEATURES11 }, messages: { noChildrenMap: "Using 'Children.map' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME11, create: create11, defaultOptions: [] }); function create11(context) { return { MemberExpression(node) { if (ER25__namespace.isChildrenMap(context, node)) { context.report({ messageId: "noChildrenMap", node: node.property }); } } }; } var RULE_NAME12 = "no-children-only"; var RULE_FEATURES12 = []; var no_children_only_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `Children.only`.", [Symbol.for("rule_features")]: RULE_FEATURES12 }, messages: { noChildrenOnly: "Using 'Children.only' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME12, create: create12, defaultOptions: [] }); function create12(context) { return { MemberExpression(node) { if (ER25__namespace.isChildrenOnly(context, node)) { context.report({ messageId: "noChildrenOnly", node: node.property }); } } }; } var RULE_NAME13 = "no-children-prop"; var RULE_FEATURES13 = []; var no_children_prop_default = createRule({ meta: { type: "problem", docs: { description: "Disallow passing `children` as a prop.", [Symbol.for("rule_features")]: RULE_FEATURES13 }, messages: { noChildrenProp: "Do not pass 'children' as props." }, schema: [] }, name: RULE_NAME13, create: create13, defaultOptions: [] }); function create13(context) { return { JSXElement(node) { const attributes = node.openingElement.attributes; const childrenProp = ER25__namespace.getAttribute(context, "children", attributes, context.sourceCode.getScope(node)); if (childrenProp != null) { context.report({ messageId: "noChildrenProp", node: childrenProp }); } } }; } var RULE_NAME14 = "no-children-to-array"; var RULE_FEATURES14 = []; var no_children_to_array_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `Children.toArray`.", [Symbol.for("rule_features")]: RULE_FEATURES14 }, messages: { noChildrenToArray: "Using 'Children.toArray' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME14, create: create14, defaultOptions: [] }); function create14(context) { return { MemberExpression(node) { if (ER25__namespace.isChildrenToArray(context, node)) { context.report({ messageId: "noChildrenToArray", node: node.property }); } } }; } var RULE_NAME15 = "no-class-component"; var RULE_FEATURES15 = []; var no_class_component_default = createRule({ meta: { type: "problem", docs: { description: "Disallow class components except for error boundaries.", [Symbol.for("rule_features")]: RULE_FEATURES15 }, messages: { noClassComponent: "Avoid using class components. Use function components instead." }, schema: [] }, name: RULE_NAME15, create: create15, defaultOptions: [] }); function create15(context) { if (!context.sourceCode.text.includes("Component")) return {}; const { ctx, listeners } = ER25__namespace.useComponentCollectorLegacy(); return { ...listeners, "Program:exit"(program) { const components = ctx.getAllComponents(program); for (const { name: name5 = "anonymous", node: component } of components.values()) { if (component.body.body.some((m) => ER25__namespace.isComponentDidCatch(m) || ER25__namespace.isGetDerivedStateFromError(m))) { continue; } context.report({ messageId: "noClassComponent", node: component, data: { name: name5 } }); } } }; } var RULE_NAME16 = "no-clone-element"; var RULE_FEATURES16 = []; var no_clone_element_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `cloneElement`.", [Symbol.for("rule_features")]: RULE_FEATURES16 }, messages: { noCloneElement: "Using 'cloneElement' is uncommon and can lead to fragile code. Use alternatives instead." }, schema: [] }, name: RULE_NAME16, create: create16, defaultOptions: [] }); function create16(context) { return { CallExpression(node) { if (ER25__namespace.isCloneElementCall(context, node)) { context.report({ messageId: "noCloneElement", node }); } } }; } var RULE_NAME17 = "no-comment-textnodes"; var RULE_FEATURES17 = []; var no_comment_textnodes_default = createRule({ meta: { type: "problem", docs: { description: "Prevents comments from being inserted as text nodes.", [Symbol.for("rule_features")]: RULE_FEATURES17 }, messages: { noCommentTextnodes: "Possible misused comment in text node. Comments inside children section of tag should be placed inside braces." }, schema: [] }, name: RULE_NAME17, create: create17, defaultOptions: [] }); function create17(context) { function hasCommentLike(node) { if (AST8__namespace.isOneOf([types.AST_NODE_TYPES.JSXAttribute, types.AST_NODE_TYPES.JSXExpressionContainer])(node.parent)) { return false; } const rawValue = context.sourceCode.getText(node); return /^\s*\/(?:\/|\*)/mu.test(rawValue); } const visitorFunction = (node) => { if (!AST8__namespace.isOneOf([types.AST_NODE_TYPES.JSXElement, types.AST_NODE_TYPES.JSXFragment])(node.parent)) { return; } if (!hasCommentLike(node)) { return; } if (!node.parent.type.includes("JSX")) { return; } context.report({ messageId: "noCommentTextnodes", node }); }; return { JSXText: visitorFunction, Literal: visitorFunction }; } var RULE_NAME18 = "no-complex-conditional-rendering"; var RULE_FEATURES18 = [ "EXP" ]; var no_complex_conditional_rendering_default = createRule({ meta: { type: "problem", docs: { description: "Disallow complex conditional rendering in JSX expressions.", [Symbol.for("rule_features")]: RULE_FEATURES18 }, messages: { noComplexConditionalRendering: "Avoid complex conditional rendering. Extract the logic into separate elements or components." }, schema: [] }, name: RULE_NAME18, create: create18, defaultOptions: [] }); function create18(context) { const visitorFunction = (node) => { const jsxExpContainer = node.parent?.parent; if (!AST8__namespace.is(types.AST_NODE_TYPES.JSXExpressionContainer)(jsxExpContainer)) { return; } if (!AST8__namespace.isOneOf([types.AST_NODE_TYPES.JSXElement, types.AST_NODE_TYPES.JSXFragment])(jsxExpContainer.parent)) { return; } if (!jsxExpContainer.parent.children.includes(jsxExpContainer)) { return; } context.report({ messageId: "noComplexConditionalRendering", node: jsxExpContainer }); }; return { "JSXExpressionContainer > ConditionalExpression > ConditionalExpression": visitorFunction, "JSXExpressionContainer > ConditionalExpression > LogicalExpression": visitorFunction, "JSXExpressionContainer > LogicalExpression > ConditionalExpression": visitorFunction, "JSXExpressionContainer > LogicalExpression[operator='&&'] > LogicalExpression[operator='||']": visitorFunction, "JSXExpressionContainer > LogicalExpression[operator='||'] > LogicalExpression[operator='&&']": visitorFunction }; } var RULE_NAME19 = "no-component-will-mount"; var RULE_FEATURES19 = [ "MOD" ]; var no_component_will_mount_default = createRule({ meta: { type: "problem", docs: { description: "Replace usages of `componentWillMount` with `UNSAFE_componentWillMount`.", [Symbol.for("rule_features")]: RULE_FEATURES19 }, fixable: "code", messages: { noComponentWillMount: "[Deprecated] Use 'UNSAFE_componentWillMount' instead." }, schema: [] }, name: RULE_NAME19, create: create19, defaultOptions: [] }); function create19(context) { if (!context.sourceCode.text.includes("componentWillMount")) return {}; const { ctx, listeners } = ER25__namespace.useComponentCollectorLegacy(); return { ...listeners, "Program:exit"(program) { const components = ctx.getAllComponents(program); for (const { node: component } of components.values()) { const { body } = component.body; for (const member of body) { if (ER25__namespace.isComponentWillMount(member)) { context.report({ messageId: "noComponentWillMount", node: member, fix(fixer) { if (!("key" in member)) { return null; } return fixer.replaceText(member.key, "UNSAFE_componentWillMount"); } }); } } } } }; } var RULE_NAME20 = "no-component-will-receive-props"; var RULE_FEATURES20 = [ "MOD" ]; var no_component_will_receive_props_default = createRule({ meta: { type: "problem", docs: { description: "Replace usages of `componentWillReceiveProps` with `UNSAFE_componentWillReceiveProps`.", [Symbol.for("rule_features")]: RULE_FEATURES20 }, fixable: "code", messages: { noComponentWillReceiveProps: "[Deprecated] Use 'UNSAFE_componentWillReceiveProps' instead." }, schema: [] }, name: RULE_NAME20, create: create20, defaultOptions: [] }); function create20(context) { if (!context.sourceCode.text.includes("componentWillReceiveProps")) return {}; const { ctx, listeners } = ER25__namespace.useComponentCollectorLegacy(); return { ...listeners, "Program:exit"(program) { const components = ctx.getAllComponents(program); for (const { node: component } of components.values()) { const { body } = component.body; for (const member of body) { if (ER25__namespace.isComponentWillReceiveProps(member)) { context.report({ messageId: "noComponentWillReceiveProps", node: member, fix(fixer) { if (!("key" in member)) { return null; } return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps"); } }); } } } } }; } var RULE_NAME21 = "no-component-will-update"; var RULE_FEATURES21 = [ "MOD" ]; var no_component_will_update_default = createRule({ meta: { type: "problem", docs: { description: "Replace usages of `componentWillUpdate` with `UNSAFE_componentWillUpdate`.", [Symbol.for("rule_features")]: RULE_FEATURES21 }, fixable: "code", messages: { noComponentWillUpdate: "[Deprecated] Use 'UNSAFE_componentWillUpdate' instead." }, schema: [] }, name: RULE_NAME21, create: create21, defaultOptions: [] }); function create21(context) { if (!context.sourceCode.text.includes("componentWillUpdate")) return {}; const { ctx, listeners } = ER25__namespace.useComponentCollectorLegacy(); return { ...listeners, "Program:exit"(program) { const components = ctx.getAllComponents(program); for (const { node: component } of components.values()) { const { body } = component.body; for (const member of body) { if (ER25__namespace.isComponentWillUpdate(member)) { context.report({ messageId: "noComponentWillUpdate", node: member, fix(fixer) { if (!("key" in member)) { return null; } return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate"); } }); } } } } }; } var RULE_NAME22 = "no-context-provider"; var RULE_FEATURES22 = [ "MOD" ]; var no_context_provider_default = createRule({ meta: { type: "problem", docs: { description: "Replace usages of `<Context.Provider>` with `<Context>`.", [Symbol.for("rule_features")]: RULE_FEATURES22 }, fixable: "code", messages: { noContextProvider: "In React 19, you can render '<Context>' as a provider instead of '<Context.Provider>'." }, schema: [] }, name: RULE_NAME22, create: create22, defaultOptions: [] }); function create22(context) { if (!context.sourceCode.text.includes("Provider")) return {}; const { version: version2 } = shared.getSettingsFromContext(context); if (compareVersions.compare(version2, "19.0.0", "<")) return {}; return { JSXElement(node) { const fullName = ER25__namespace.getElementType(context, node); const parts = fullName.split("."); const selfName = parts.pop(); const contextFullName = parts.join("."); const contextSelfName = parts.pop(); if (selfName !== "Provider") return; if (contextSelfName == null) return; context.report({ messageId: "noContextProvider", node, fix(fixer) { if (!ER25__namespace.isComponentNameLoose(contextSelfName)) return null; const openingElement = node.openingElement; const closingElement = node.closingElement; if (closingElement == null) { return fixer.replaceText(openingElement.name, contextFullName); } return [ fixer.replaceText(openingElement.name, contextFullName), fixer.replaceText(closingElement.name, contextFullName) ]; } }); } }; } var RULE_NAME23 = "no-create-ref"; var RULE_FEATURES23 = []; var no_create_ref_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `createRef` in function components.", [Symbol.for("rule_features")]: RULE_FEATURES23 }, messages: { noCreateRef: "[Deprecated] Use 'useRef' instead." }, schema: [] }, name: RULE_NAME23, create: create23, defaultOptions: [] }); function create23(context) { return { CallExpression(node) { if (ER25__namespace.isCreateRefCall(context, node) && AST8__namespace.findParentNode(node, ER25__namespace.isClassComponent) == null) { context.report({ messageId: "noCreateRef", node }); } } }; } var RULE_NAME24 = "no-default-props"; var RULE_FEATURES24 = []; var no_default_props_default = createRule({ meta: { type: "problem", docs: { description: "Disallow `defaultProps` property in favor of ES6 default parameters.", [Symbol.for("rule_features")]: RULE_FEATURES24 }, messages: { noDefaultProps: "[Deprecated] Use ES6 default parameters instead." }, schema: [] }, name: RULE_NAME24, create: create24, defaultOptions: [] }); function create24(context) { if (!context.sourceCode.text.includes("defaultProps")) return {}; return { AssignmentExpression(node) { if (node.operator !== "=" || node.left.type !== types.AST_NODE_TYPES.MemberExpression) { return; } const { object, property } = node.left; if (object.type !== types.AST_NODE_TYPES.Identifier) { return; } if (property.type !== types.AST_NODE_TYPES.Identifier || property.name !== "defaultProps") { return; } if (!ER25__namespace.isComponentNameLoose(object.name)) { return; } const variable = VAR2__namespace.findVariable(object.name, context.sourceCode.getScope(node)); const variableNode = VAR2__namespace.getVariableInitNode(variable, 0); if (variableNode == null) return; if (!AST8__namespace.isFunction(variableNode) && !ER25__namespace.isClassComponent(variableNode)) return; context.report({ messageId: "noDefaultProps", node: property }); }, PropertyDefinition(node) { if (!ER25__namespace.isClassComponent(node.parent.parent)) { return; } if (!node.static || node.key.type !== types.AST_NODE_TYPES.Identifier || node.key.name !== "defaultProps") { return; } context.report({ messageId: "noDefaultProps", node }); } }; } var RULE_NAME25 = "no-direct-mutation-state"; var RULE_FEATURES25 = []; function isConstructorFunction(node) { return AST8__namespace.isOneOf([types.AST_NODE_TYPES.FunctionDeclaration, types.AST_NODE_TYPES.FunctionExpression])(node) && AST8__namespace.isMethodOrProperty(node.parent) && node.parent.key.type === types.AST_NODE_TYPES.Identifier && node.parent.key.name === "constructor"; } var no_direct_mutation_state_default = createRule({ meta: { type: "problem", docs: { description: "Disallow direct mutation of `this.state`.", [Symbol.for("rule_features")]: RULE_FEATURES25 }, messages: { noDirectMutationState: "Do not mutate state directly. Use 'setState()' instead." }, schema: [] }, name: RULE_NAME25, create: create25, defaultOptions: [] }); function create25(context) { return { AssignmentExpression(node) { if (!ER25__namespace.isAssignmentToThisState(node)) return; const parentClass = AST8__namespace.findParentNode( node, AST8__namespace.isOneOf([ types.AST_NODE_TYPES.ClassDeclaration, types.AST_NODE_TYPES.ClassExpression ]) ); if (parentClass == null) return; if (ER25__namespace.isClassComponent(parentClass) && context.sourceCode.getScope(node).block !== AST8__namespace.findParentNode(node, isConstructorFunction)) { context.report({ messageId: "noDirectMutationState", node }); } } }; } var RULE_NAME26 = "no-duplicate-key"; var RULE_FEATURES26 = []; var no_duplicate_key_default = createRule({ meta: { type: "problem", docs: { description: "Disallow duplicate `key` on elements in the same array or a list of `children`.", [Symbol.for("rule_features")]: RULE_FEATURES26 }, messages: { noDuplicateKey: "A key must be unique. '{{value}}' is duplicated." }, schema: [] }, name: RULE_NAME26, create: create26, defaultOptions: [] }); function create26(context) { if (!context.sourceCode.getText().includes("key=")) { return {}; } const keyedEntries = /* @__PURE__ */ new Map(); function isKeyValueEqual(a, b) { const aValue = a.value; const bValue = b.value; if (aValue == null || bValue == null) { return false; } return AST8__namespace.isNodeEqual(aValue, bValue); } return { "JSXAttribute[name.name='key']"(node) { const jsxElement = node.parent.parent; switch (jsxElement.parent.type) { case types.AST_NODE_TYPES.ArrayExpression: case types.AST_NODE_TYPES.JSXElement: case types.AST_NODE_TYPES.JSXFragment: { const root = jsxElement.parent; const prevKeys = keyedEntries.get(root)?.keys ?? []; keyedEntries.set(root, { hasDuplicate: prevKeys.some((prevKey) => isKeyValueEqual(prevKey, node)), keys: [...prevKeys, node], root: jsxElement.parent }); break; } default: { const call = AST8__namespace.findParentNode(jsxElement, AST8__namespace.isArrayMapCall); const iter = AST8__namespace.findParentNode(jsxElement, (n) => n === call || AST8__namespace.isFunction(n)); if (!AST8__namespace.isFunction(iter)) return; const arg0 = call?.arguments[0]; if (call == null || arg0 == null) return; if (AST8__namespace.getJSExpression(arg0) !== iter) { return; } keyedEntries.set(call, { hasDuplicate: node.value?.type === types.AST_NODE_TYPES.Literal, keys: [node], root: call }); } } }, "Program:exit"() { for (const { hasDuplicate, keys } of keyedEntries.values()) { if (!hasDuplicate) { continue; } for (const key of keys) { context.report({ messageId: "noDuplicateKey", node: key, data: { value: context.sourceCode.getText(key) } }); } } } }; } var RULE_NAME27 = "no-forward-ref"; var RULE_FEATURES27 = [ "MOD" ]; var no_forward_ref_default = createRule({ meta: { type: "problem", docs: { description: "Replaces usages of `forwardRef` with passing `ref` as a prop.", [Symbol.for("rule_features")]: RULE_FEATURES27 }, fixable: "code", messages: { noForwardRef: "In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead." }, schema: [] }, name: RULE_NAME27, create: create27, defaultOptions: [] }); function create27(context) { if (!context.sourceCode.text.includes("forwardRef")) { return {}; } const { version: version2 } = shared.getSettingsFromContext(context); if (compareVersions.compare(version2, "19.0.0", "<")) { return {}; } return { CallExpression(node) { if (!ER25__namespace.isForwardRefCall(context, node)) { return; } const id = AST8__namespace.getFunctionId(node); context.report({ messageId: "noForwardRef", node: id ?? node, fix: getFix(context, node) }); } }; } function getFix(context, node) { return (fixer) => { const [componentNode] = node.arguments; if (componentNode == null || !AST8__namespace.isFunction(componentNode)) { return []; } return [ // unwrap component from forwardRef call fixer.removeRange([node.range[0], componentNode.range[0]]), fixer.removeRange([componentNode.range[1], node.range[1]]), // update component props and ref arguments to match the new signature ...getComponentPropsFixes( context, fixer, componentNode, node.typeArguments?.params ?? [] ) ]; }; } function getComponentPropsFixes(context, fixer, node, typeArguments) { const getText = (node2) => context.sourceCode.getText(node2); const [arg0, arg1] = node.params; const [typeArg0, typeArg1] = typeArguments; if (arg0 == null) { return []; } const fixedArg0Text = tsPattern.match(arg0).with({ type: types.AST_NODE_TYPES.Identifier }, (n) => `...${n.name}`).with({ type: types.AST_NODE_TYPES.ObjectPattern }, (n) => n.properties.map(getText).join(", ")).otherwise(() => null); const fixedArg1Text = tsPattern.match(arg1).with(tsPattern.P.nullish, () => "ref").with({ type: types.AST_NODE_TYPES.Identifier, name: "ref" }, () => "ref").with({ type: types.AST_NODE_TYPES.Identifier, name: tsPattern.P.string }, (n) => `ref: ${n.name}`).otherwise(() => null); if (fixedArg0Text == null || fixedArg1Text == null) { return []; } if (typeArg0 == null || typeArg1 == null) { return [ fixer.replaceText( arg0, [ "{", fixedArg1Text + ",", fixedArg0Text, "}" ].join(" ") ), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])] ]; } const typeArg0Text = getText(typeArg0); const typeArg1Text = getText(typeArg1); return [ fixer.replaceText( arg0, [ "{", fixedArg1Text + ",", fixedArg0Text, "}:", typeArg1Text, "&", "{", `ref?:`, `React.RefObject<${typeArg0Text} | null>`, "}" ].join(" ") ), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])] ]; } var RULE_NAME28 = "no-implicit-key"; var RULE_FEATURES28 = [ "EXP" ]; var no_implicit_key_default = createRule({ meta: { type: "problem", docs: { description: "Prevents `key` from not being explicitly specified (e.g. spreading `key` from objects).", [Symbol.for("rule_features")]: RULE_FEATURES28 }, messages: { noImplicitKey: "Do not use implicit 'key' props." }, schema: [] }, name: RULE_NAME28, create: create28, defaultOptions: [] }); function create28(context) { return { JSXOpeningElement(node) { const initialScope = context.sourceCode.getScope(node); const keyProp = ER25__namespace.getAttribute(context, "key", node.attributes, initialScope); const isKeyPropOnElement = node.attributes.some( (n) => n.type === types.AST_NODE_TYPES.JSXAttribute && n.name.type === types.AST_NODE_TYPES.JSXIdentifier && n.name.name === "key" ); if (keyProp != null && !isKeyPropOnElement) { context.report({ messageId: "noImplicitKey", node: keyProp }); } } }; } var RULE_NAME29 = "no-leaked-conditional-rendering"; var RULE_FEATURES29 = [ "TSC" ]; var tsHelpers = { isAnyType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.TypeParameter | ts__default.default.TypeFlags.Any), isBigIntType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.BigIntLike), isBooleanType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.BooleanLike), isEnumType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.EnumLike), isFalsyBigIntType: (type) => type.isLiteral() && tsPattern.isMatching({ value: { base10Value: "0" } }, type), isFalsyNumberType: (type) => type.isNumberLiteral() && type.value === 0, isFalsyStringType: (type) => type.isStringLiteral() && type.value === "", isNeverType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.Never), isNullishType: (type) => tsApiUtils.isTypeFlagSet( type, ts__default.default.TypeFlags.Null | ts__default.default.TypeFlags.Undefined | ts__default.default.TypeFlags.VoidLike ), isNumberType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.NumberLike), isObjectType: (type) => !tsApiUtils.isTypeFlagSet( type, ts__default.default.TypeFlags.Null | ts__default.default.TypeFlags.Undefined | ts__default.default.TypeFlags.VoidLike | ts__default.default.TypeFlags.BooleanLike | ts__default.default.TypeFlags.StringLike | ts__default.default.TypeFlags.NumberLike | ts__default.default.TypeFlags.BigIntLike | ts__default.default.TypeFlags.TypeParameter | ts__default.default.TypeFlags.Any | ts__default.default.TypeFlags.Unknown | ts__default.default.TypeFlags.Never ), isStringType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.StringLike), isTruthyBigIntType: (type) => type.isLiteral() && tsPattern.isMatching({ value: { base10Value: tsPattern.P.not("0") } }, type), isTruthyNumberType: (type) => type.isNumberLiteral() && type.value !== 0, isTruthyStringType: (type) => type.isStringLiteral() && type.value !== "", isUnknownType: (type) => tsApiUtils.isTypeFlagSet(type, ts__default.default.TypeFlags.Unknown) }; function inspectVariantTypes(types) { const variantTypes = /