eslint-plugin-react-x
Version:
A set of composable linting rules for libraries and frameworks that use React as a UI runtime.
1,618 lines (1,610 loc) • 115 kB
JavaScript
import { createRuleForPlugin, getSettingsFromContext, unsafeDecodeSettings, DEFAULT_ESLINT_REACT_SETTINGS } from '@eslint-react/shared';
import * as JSX8 from '@eslint-react/jsx';
import * as AST12 from '@eslint-react/ast';
import { isForwardRefCall, isClassComponent, isThisSetState, isChildrenCount, isChildrenForEach, isChildrenMap, isChildrenOnly, isChildrenToArray, useComponentCollectorLegacy, isComponentDidCatch, isGetDerivedStateFromError, isCloneElementCall, isCreateRefCall, isComponentName, isAssignmentToThisState, useComponentCollector, DEFAULT_COMPONENT_HINT, ERComponentFlag, getInstanceId, isInstanceIdEqual, DISPLAY_NAME_ASSIGNMENT_SELECTOR, isCreateContextCall, isChildrenToArrayCall, ERComponentHint, isDirectValueOfRenderPropertyLoose, isDeclaredInRenderPropLoose, isInsideRenderMethod, isCreateElementCall, isReactHookCall, isReactHookCallWithNameAlias, isGetDerivedStateFromProps, isInitializedFromReact } from '@eslint-react/core';
import { _, getOrUpdate, constTrue, constFalse, identity } from '@eslint-react/eff';
import { AST_NODE_TYPES } from '@typescript-eslint/types';
import { isMatching, match, P } from 'ts-pattern';
import { compare } from 'compare-versions';
import * as VAR from '@eslint-react/var';
import { getConstrainedTypeAtLocation, isTypeReadonly } from '@typescript-eslint/type-utils';
import { ESLintUtils } from '@typescript-eslint/utils';
import { unionTypeParts, isTypeFlagSet, isTrueLiteralType, isFalseLiteralType } from 'ts-api-utils';
import ts from 'typescript';
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/ensure-forward-ref-using-ref": "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-jsx-props": "warn",
"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-nested-components": "error",
"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/use-jsx-vars": "warn"
};
var settings = {
"react-x": 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/dom/no-unknown-property": "off",
"react-x/no-duplicate-jsx-props": "off",
"react-x/use-jsx-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.31.0";
var createRule = createRuleForPlugin("x");
// src/rules/avoid-shorthand-boolean.ts
var RULE_NAME = "avoid-shorthand-boolean";
var RULE_FEATURES = [
"CHK"
];
var avoid_shorthand_boolean_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using shorthand 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(context) {
return {
JSXAttribute(node) {
if (node.value === null) {
context.report({
messageId: "avoidShorthandBoolean",
node,
data: {
propName: JSX8.getAttributeName(node)
},
fix: (fixer) => fixer.insertTextAfter(node.name, `={true}`)
});
}
}
};
},
defaultOptions: []
});
// src/rules/avoid-shorthand-fragment.ts
var RULE_NAME2 = "avoid-shorthand-fragment";
var RULE_FEATURES2 = [
"CHK"
];
var avoid_shorthand_fragment_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using shorthand fragment syntax",
[Symbol.for("rule_features")]: RULE_FEATURES2
},
messages: {
avoidShorthandFragment: "Avoid using shorthand fragment syntax. Use 'Fragment' component instead."
},
schema: []
},
name: RULE_NAME2,
create(context) {
return {
JSXFragment(node) {
context.report({
messageId: "avoidShorthandFragment",
node
});
}
};
},
defaultOptions: []
});
var RULE_NAME3 = "ensure-forward-ref-using-ref";
var RULE_FEATURES3 = [
"CHK"
];
var ensure_forward_ref_using_ref_default = createRule({
meta: {
type: "problem",
docs: {
description: "require a 'ref' parameter to be set when using 'forwardRef'",
[Symbol.for("rule_features")]: RULE_FEATURES3
},
messages: {
ensureForwardRefUsingRef: "A 'forwardRef' is used with this component but no 'ref' parameter is set."
},
schema: []
},
name: RULE_NAME3,
create(context) {
return {
CallExpression(node) {
if (!isForwardRefCall(context, node)) {
return;
}
const [component] = node.arguments;
if (component == null || !AST12.isFunction(component)) {
return;
}
const ref = component.params[1];
if (ref != null) {
return;
}
context.report({
messageId: "ensureForwardRefUsingRef",
node: component
});
}
};
},
defaultOptions: []
});
var RULE_NAME4 = "no-access-state-in-setstate";
var RULE_FEATURES4 = [
"CHK"
];
function isKeyLiteral(node, key) {
return match(key).with({ type: AST_NODE_TYPES.Literal }, constTrue).with({ type: AST_NODE_TYPES.TemplateLiteral, expressions: [] }, constTrue).with({ type: AST_NODE_TYPES.Identifier }, () => !node.computed).otherwise(constFalse);
}
var no_access_state_in_setstate_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow accessing 'this.state' within 'setState'",
[Symbol.for("rule_features")]: RULE_FEATURES4
},
messages: {
noAccessStateInSetstate: "Do not access 'this.state' within 'setState'. Use the update function instead."
},
schema: []
},
name: RULE_NAME4,
create(context) {
if (!context.sourceCode.text.includes("setState")) {
return {};
}
const classEntries = [];
const methodEntries = [];
const setStateEntries = [];
return {
CallExpression(node) {
if (!isThisSetState(node)) {
return;
}
setStateEntries.push([node, false]);
},
"CallExpression:exit"(node) {
if (!isThisSetState(node)) {
return;
}
setStateEntries.pop();
},
ClassDeclaration(node) {
classEntries.push([node, isClassComponent(node)]);
},
"ClassDeclaration:exit"() {
classEntries.pop();
},
ClassExpression(node) {
classEntries.push([node, isClassComponent(node)]);
},
"ClassExpression:exit"() {
classEntries.pop();
},
MemberExpression(node) {
if (!AST12.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 (AST12.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 || !AST12.isThisExpression(node.init) || node.id.type !== AST_NODE_TYPES.ObjectPattern) {
return;
}
const hasState = node.id.properties.some(
(prop) => prop.type === AST_NODE_TYPES.Property && isKeyLiteral(prop, prop.key) && AST12.getPropertyName(prop.key) === "state"
);
if (!hasState) {
return;
}
context.report({ messageId: "noAccessStateInSetstate", node });
}
};
},
defaultOptions: []
});
var RULE_NAME5 = "no-array-index-key";
var RULE_FEATURES5 = [
"CHK"
];
var reactChildrenMethod = ["forEach", "map"];
var iteratorFunctionIndexParamPosition = /* @__PURE__ */ new Map([
["every", 1],
["filter", 1],
["find", 1],
["findIndex", 1],
["findLast", 1],
["findLastIndex", 1],
["flatMap", 1],
["forEach", 1],
["map", 1],
["reduce", 2],
["reduceRight", 2],
["some", 1]
]);
function isReactChildrenMethod(name5) {
return reactChildrenMethod.some((method) => method === name5);
}
function isUsingReactChildren(context, node) {
const { importSource = "react" } = unsafeDecodeSettings(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 === AST_NODE_TYPES.Identifier && callee.object.name === "Children") {
return true;
}
if (callee.object.type === AST_NODE_TYPES.MemberExpression && "name" in callee.object.object) {
return isInitializedFromReact(callee.object.object.name, importSource, initialScope);
}
return false;
}
function getMapIndexParamName(context, node) {
const { callee } = node;
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
return _;
}
if (callee.property.type !== AST_NODE_TYPES.Identifier) {
return _;
}
const { name: name5 } = callee.property;
if (!iteratorFunctionIndexParamPosition.has(name5)) {
return _;
}
const callbackArg = node.arguments[isUsingReactChildren(context, node) ? 1 : 0];
if (callbackArg == null) {
return _;
}
if (!AST12.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(callbackArg)) {
return _;
}
const { params } = callbackArg;
const indexParamPosition = iteratorFunctionIndexParamPosition.get(name5);
if (indexParamPosition == null) {
return _;
}
if (params.length < indexParamPosition + 1) {
return _;
}
const param = params.at(indexParamPosition);
return param != null && "name" in param ? param.name : _;
}
var no_array_index_key_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using an item's index in the array as its key",
[Symbol.for("rule_features")]: RULE_FEATURES5
},
messages: {
noArrayIndexKey: "Do not use item index in the array as its key."
},
schema: []
},
name: RULE_NAME5,
create(context) {
const indexParamNames = [];
function isArrayIndex(node) {
return node.type === AST_NODE_TYPES.Identifier && indexParamNames.some((name5) => name5 != null && name5 === node.name);
}
function isCreateOrCloneElementCall(node) {
return isCreateElementCall(context, node) || isCloneElementCall(context, node);
}
function getReportDescriptors(node) {
switch (node.type) {
// key={bar}
case 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 AST_NODE_TYPES.TemplateLiteral:
case AST_NODE_TYPES.BinaryExpression: {
const descriptors = [];
const expressions = node.type === 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 AST_NODE_TYPES.CallExpression: {
switch (true) {
// key={bar.toString()}
case (node.callee.type === AST_NODE_TYPES.MemberExpression && node.callee.property.type === 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 === 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 !== AST_NODE_TYPES.ObjectExpression) {
return;
}
for (const prop of props.properties) {
if (!isMatching({ key: { name: "key" } })(prop)) {
continue;
}
if (!("value" in prop)) {
continue;
}
const descriptors = getReportDescriptors(prop.value);
for (const descriptor of descriptors) {
context.report(descriptor);
}
}
},
"CallExpression:exit"() {
indexParamNames.pop();
},
JSXAttribute(node) {
if (node.name.name !== "key") {
return;
}
if (indexParamNames.length === 0) {
return;
}
if (node.value?.type !== AST_NODE_TYPES.JSXExpressionContainer) {
return;
}
const descriptors = getReportDescriptors(node.value.expression);
for (const descriptor of descriptors) {
context.report(descriptor);
}
}
};
},
defaultOptions: []
});
function getIdentifiersFromBinaryExpression(side) {
if (side.type === AST_NODE_TYPES.Identifier) {
return [side];
}
if (side.type === AST_NODE_TYPES.BinaryExpression) {
return [
...getIdentifiersFromBinaryExpression(side.left),
...getIdentifiersFromBinaryExpression(side.right)
];
}
return [];
}
var RULE_NAME6 = "no-children-count";
var RULE_FEATURES6 = [
"CHK"
];
var no_children_count_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'Children.count'",
[Symbol.for("rule_features")]: RULE_FEATURES6
},
messages: {
noChildrenCount: "Using 'Children.count' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME6,
create(context) {
return {
MemberExpression(node) {
if (isChildrenCount(context, node)) {
context.report({
messageId: "noChildrenCount",
node: node.property
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME7 = "no-children-for-each";
var RULE_FEATURES7 = [
"CHK"
];
var no_children_for_each_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'Children.forEach'",
[Symbol.for("rule_features")]: RULE_FEATURES7
},
messages: {
noChildrenForEach: "Using 'Children.forEach' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME7,
create(context) {
return {
MemberExpression(node) {
if (isChildrenForEach(context, node)) {
context.report({
messageId: "noChildrenForEach",
node: node.property
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME8 = "no-children-map";
var RULE_FEATURES8 = [
"CHK"
];
var no_children_map_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'Children.map'",
[Symbol.for("rule_features")]: RULE_FEATURES8
},
messages: {
noChildrenMap: "Using 'Children.map' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME8,
create(context) {
return {
MemberExpression(node) {
if (isChildrenMap(context, node)) {
context.report({
messageId: "noChildrenMap",
node: node.property
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME9 = "no-children-only";
var RULE_FEATURES9 = [
"CHK"
];
var no_children_only_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'Children.only'",
[Symbol.for("rule_features")]: RULE_FEATURES9
},
messages: {
noChildrenOnly: "Using 'Children.only' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME9,
create(context) {
return {
MemberExpression(node) {
if (isChildrenOnly(context, node)) {
context.report({
messageId: "noChildrenOnly",
node: node.property
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME10 = "no-children-prop";
var RULE_FEATURES10 = [
"CHK"
];
var no_children_prop_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow passing 'children' as props",
[Symbol.for("rule_features")]: RULE_FEATURES10
},
messages: {
noChildrenProp: "Do not pass 'children' as props."
},
schema: []
},
name: RULE_NAME10,
create(context) {
return {
JSXElement(node) {
const attribute = JSX8.getAttribute(
"children",
node.openingElement.attributes,
context.sourceCode.getScope(node)
);
if (attribute != null) {
context.report({
messageId: "noChildrenProp",
node: attribute
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME11 = "no-children-to-array";
var RULE_FEATURES11 = [
"CHK"
];
var no_children_to_array_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'Children.toArray'",
[Symbol.for("rule_features")]: RULE_FEATURES11
},
messages: {
noChildrenToArray: "Using 'Children.toArray' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME11,
create(context) {
return {
MemberExpression(node) {
if (isChildrenToArray(context, node)) {
context.report({
messageId: "noChildrenToArray",
node: node.property
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME12 = "no-class-component";
var RULE_FEATURES12 = [
"CHK"
];
var no_class_component_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using class components",
[Symbol.for("rule_features")]: RULE_FEATURES12
},
messages: {
noClassComponent: "Do not use class components. Use function components instead."
},
schema: []
},
name: RULE_NAME12,
create(context) {
if (!context.sourceCode.text.includes("Component")) {
return {};
}
const { ctx, listeners } = useComponentCollectorLegacy();
return {
...listeners,
"Program:exit"(node) {
const components = ctx.getAllComponents(node);
for (const { name: name5 = "anonymous", node: component } of components.values()) {
if (component.body.body.some((m) => isComponentDidCatch(m) || isGetDerivedStateFromError(m))) {
continue;
}
context.report({
messageId: "noClassComponent",
node: component,
data: {
name: name5
}
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME13 = "no-clone-element";
var RULE_FEATURES13 = [
"CHK"
];
var no_clone_element_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'cloneElement'",
[Symbol.for("rule_features")]: RULE_FEATURES13
},
messages: {
noCloneElement: "Using 'cloneElement' is uncommon and can lead to fragile code. Use alternatives instead."
},
schema: []
},
name: RULE_NAME13,
create(context) {
return {
CallExpression(node) {
if (!isCloneElementCall(context, node)) {
return;
}
context.report({
messageId: "noCloneElement",
node
});
}
};
},
defaultOptions: []
});
var RULE_NAME14 = "no-comment-textnodes";
var RULE_FEATURES14 = [
"CHK"
];
var no_comment_textnodes_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow comments from being inserted as text nodes",
[Symbol.for("rule_features")]: RULE_FEATURES14
},
messages: {
noCommentTextnodes: "Possible misused comment in text node. Comments inside children section of tag should be placed inside braces."
},
schema: []
},
name: RULE_NAME14,
create(context) {
function hasCommentLike(node) {
if (AST12.isOneOf([AST_NODE_TYPES.JSXAttribute, AST_NODE_TYPES.JSXExpressionContainer])(node.parent)) {
return false;
}
const rawValue = context.sourceCode.getText(node);
return /^\s*\/(?:\/|\*)/mu.test(rawValue);
}
const visitorFunction = (node) => {
if (!AST12.isOneOf([AST_NODE_TYPES.JSXElement, 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
};
},
defaultOptions: []
});
var RULE_NAME15 = "no-complex-conditional-rendering";
var RULE_FEATURES15 = [
"CHK"
];
var no_complex_conditional_rendering_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow complex conditional rendering",
[Symbol.for("rule_features")]: RULE_FEATURES15
},
messages: {
noComplexConditionalRendering: "Avoid complex conditional rendering. Extract the logic into separate elements or components."
},
schema: []
},
name: RULE_NAME15,
create(context) {
const visitorFunction = (node) => {
const jsxExpContainer = node.parent?.parent;
if (!AST12.is(AST_NODE_TYPES.JSXExpressionContainer)(jsxExpContainer)) {
return;
}
if (!AST12.isOneOf([AST_NODE_TYPES.JSXElement, 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
};
},
defaultOptions: []
});
var RULE_NAME16 = "no-component-will-mount";
var RULE_FEATURES16 = [
"CHK",
"MOD"
];
function isComponentWillMount(node) {
return AST12.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillMount";
}
var no_component_will_mount_default = createRule({
meta: {
type: "problem",
docs: {
description: "replace 'componentWillMount' with 'UNSAFE_componentWillMount'",
[Symbol.for("rule_features")]: RULE_FEATURES16
},
fixable: "code",
messages: {
noComponentWillMount: "[Deprecated] Use 'UNSAFE_componentWillMount' instead."
},
schema: []
},
name: RULE_NAME16,
create(context) {
if (!context.sourceCode.text.includes("componentWillMount")) {
return {};
}
const { ctx, listeners } = useComponentCollectorLegacy();
return {
...listeners,
"Program:exit"(node) {
const components = ctx.getAllComponents(node);
for (const { node: component } of components.values()) {
const { body } = component.body;
for (const member of body) {
if (isComponentWillMount(member)) {
context.report({
messageId: "noComponentWillMount",
node: member,
fix(fixer) {
if (!("key" in member)) {
return null;
}
return fixer.replaceText(member.key, "UNSAFE_componentWillMount");
}
});
}
}
}
}
};
},
defaultOptions: []
});
var RULE_NAME17 = "no-component-will-receive-props";
var RULE_FEATURES17 = [
"CHK",
"MOD"
];
function isComponentWillUpdate(node) {
return AST12.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillReceiveProps";
}
var no_component_will_receive_props_default = createRule({
meta: {
type: "problem",
docs: {
description: "replace 'componentWillReceiveProps' with 'UNSAFE_componentWillReceiveProps'",
[Symbol.for("rule_features")]: RULE_FEATURES17
},
fixable: "code",
messages: {
noComponentWillReceiveProps: "[Deprecated] Use 'UNSAFE_componentWillReceiveProps' instead."
},
schema: []
},
name: RULE_NAME17,
create(context) {
if (!context.sourceCode.text.includes("componentWillReceiveProps")) {
return {};
}
const { ctx, listeners } = useComponentCollectorLegacy();
return {
...listeners,
"Program:exit"(node) {
const components = ctx.getAllComponents(node);
for (const { node: component } of components.values()) {
const { body } = component.body;
for (const member of body) {
if (isComponentWillUpdate(member)) {
context.report({
messageId: "noComponentWillReceiveProps",
node: member,
fix(fixer) {
if (!("key" in member)) {
return null;
}
return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps");
}
});
}
}
}
}
};
},
defaultOptions: []
});
var RULE_NAME18 = "no-component-will-update";
var RULE_FEATURES18 = [
"CHK",
"MOD"
];
function isComponentWillUpdate2(node) {
return AST12.isMethodOrProperty(node) && node.key.type === AST_NODE_TYPES.Identifier && node.key.name === "componentWillUpdate";
}
var no_component_will_update_default = createRule({
meta: {
type: "problem",
docs: {
description: "replace 'componentWillUpdate' with 'UNSAFE_componentWillUpdate'",
[Symbol.for("rule_features")]: RULE_FEATURES18
},
fixable: "code",
messages: {
noComponentWillUpdate: "[Deprecated] Use 'UNSAFE_componentWillUpdate' instead."
},
schema: []
},
name: RULE_NAME18,
create(context) {
if (!context.sourceCode.text.includes("componentWillUpdate")) {
return {};
}
const { ctx, listeners } = useComponentCollectorLegacy();
return {
...listeners,
"Program:exit"(node) {
const components = ctx.getAllComponents(node);
for (const { node: component } of components.values()) {
const { body } = component.body;
for (const member of body) {
if (isComponentWillUpdate2(member)) {
context.report({
messageId: "noComponentWillUpdate",
node: member,
fix(fixer) {
if (!("key" in member)) {
return null;
}
return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate");
}
});
}
}
}
}
};
},
defaultOptions: []
});
var RULE_NAME19 = "no-context-provider";
var RULE_FEATURES19 = [
"CHK",
"MOD"
];
var no_context_provider_default = createRule({
meta: {
type: "problem",
docs: {
description: "replace '<Context.Provider>' with '<Context>'",
[Symbol.for("rule_features")]: RULE_FEATURES19
},
fixable: "code",
messages: {
noContextProvider: "In React 19, you can render '<{{contextName}}>' as a provider instead of '<{{contextName}}.Provider>'."
},
schema: []
},
name: RULE_NAME19,
create(context) {
if (!context.sourceCode.text.includes(".Provider")) return {};
const { version: version2 } = getSettingsFromContext(context);
if (compare(version2, "19.0.0", "<")) {
return {};
}
return {
JSXElement(node) {
const [name5, ...rest] = JSX8.getElementType(node).split(".").reverse();
if (name5 !== "Provider") return;
const contextName = rest.reverse().join(".");
context.report({
messageId: "noContextProvider",
node,
data: {
contextName
},
fix(fixer) {
const openingElement = node.openingElement;
const closingElement = node.closingElement;
if (closingElement == null) {
return fixer.replaceText(openingElement.name, contextName);
}
return [
fixer.replaceText(openingElement.name, contextName),
fixer.replaceText(closingElement.name, contextName)
];
}
});
}
};
},
defaultOptions: []
});
var RULE_NAME20 = "no-create-ref";
var RULE_FEATURES20 = [
"CHK"
];
var no_create_ref_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'createRef' in function components",
[Symbol.for("rule_features")]: RULE_FEATURES20
},
messages: {
noCreateRef: "[Deprecated] Use 'useRef' instead."
},
schema: []
},
name: RULE_NAME20,
create(context) {
return {
CallExpression(node) {
if (isCreateRefCall(context, node) && AST12.findParentNode(node, isClassComponent) == null) {
context.report({ messageId: "noCreateRef", node });
}
}
};
},
defaultOptions: []
});
var RULE_NAME21 = "no-default-props";
var RULE_FEATURES21 = [
"CHK"
];
var no_default_props_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow using 'defaultProps' property in components",
[Symbol.for("rule_features")]: RULE_FEATURES21
},
messages: {
noDefaultProps: "[Deprecated] Use ES6 default parameters instead."
},
schema: []
},
name: RULE_NAME21,
create(context) {
if (!context.sourceCode.text.includes("defaultProps")) {
return {};
}
return {
AssignmentExpression(node) {
if (node.operator !== "=" || node.left.type !== AST_NODE_TYPES.MemberExpression) {
return;
}
const { object, property } = node.left;
if (object.type !== AST_NODE_TYPES.Identifier) {
return;
}
if (property.type !== AST_NODE_TYPES.Identifier || property.name !== "defaultProps") {
return;
}
if (!isComponentName(object.name)) {
return;
}
const variable = VAR.findVariable(object.name, context.sourceCode.getScope(node));
const variableNode = VAR.getVariableInitNode(variable, 0);
if (variableNode == null) return;
if (!AST12.isFunction(variableNode) && !isClassComponent(variableNode)) return;
context.report({ messageId: "noDefaultProps", node: property });
},
PropertyDefinition(node) {
if (!isClassComponent(node.parent.parent)) {
return;
}
if (!node.static || node.key.type !== AST_NODE_TYPES.Identifier || node.key.name !== "defaultProps") {
return;
}
context.report({ messageId: "noDefaultProps", node });
}
};
},
defaultOptions: []
});
var RULE_NAME22 = "no-direct-mutation-state";
var RULE_FEATURES22 = [
"CHK"
];
function isConstructorFunction(node) {
return AST12.isOneOf([AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression])(node) && AST12.isMethodOrProperty(node.parent) && node.parent.key.type === 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 state",
[Symbol.for("rule_features")]: RULE_FEATURES22
},
messages: {
noDirectMutationState: "Do not mutate state directly. Use 'setState()' instead."
},
schema: []
},
name: RULE_NAME22,
create(context) {
return {
AssignmentExpression(node) {
if (!isAssignmentToThisState(node)) {
return;
}
const parentClass = AST12.findParentNode(
node,
AST12.isOneOf([
AST_NODE_TYPES.ClassDeclaration,
AST_NODE_TYPES.ClassExpression
])
);
if (parentClass == null) return;
if (isClassComponent(parentClass) && context.sourceCode.getScope(node).block !== AST12.findParentNode(node, isConstructorFunction)) {
context.report({
messageId: "noDirectMutationState",
node
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME23 = "no-duplicate-jsx-props";
var RULE_FEATURES23 = [
"CHK"
];
var no_duplicate_jsx_props_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow duplicate props",
[Symbol.for("rule_features")]: RULE_FEATURES23
},
messages: {
noDuplicateJsxProps: "This JSX property is assigned multiple times."
},
schema: []
},
name: RULE_NAME23,
create(context) {
return {
JSXOpeningElement(node) {
const props = [];
for (const attr of node.attributes) {
if (attr.type === 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: "noDuplicateJsxProps",
node: attr
});
}
}
};
},
defaultOptions: []
});
var RULE_NAME24 = "no-duplicate-key";
var RULE_FEATURES24 = [
"CHK"
];
var no_duplicate_key_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow duplicate keys when rendering list",
[Symbol.for("rule_features")]: RULE_FEATURES24
},
messages: {
noDuplicateKey: "A key must be unique. '{{value}}' is duplicated."
},
schema: []
},
name: RULE_NAME24,
create(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 AST12.isNodeEqual(aValue, bValue);
}
return {
"JSXAttribute[name.name='key']"(node) {
const jsxElement = node.parent.parent;
switch (jsxElement.parent.type) {
case AST_NODE_TYPES.ArrayExpression:
case AST_NODE_TYPES.JSXElement:
case 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 = AST12.findParentNode(jsxElement, AST12.isMapCallLoose);
const iter = AST12.findParentNode(jsxElement, (n) => n === call || AST12.isFunction(n));
if (!AST12.isFunction(iter)) return;
const arg0 = call?.arguments[0];
if (call == null || arg0 == null) return;
if (AST12.getEcmaExpression(arg0) !== iter) {
return;
}
keyedEntries.set(call, {
hasDuplicate: node.value?.type === 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)
}
});
}
}
}
};
},
defaultOptions: []
});
var RULE_NAME25 = "no-forward-ref";
var RULE_FEATURES25 = [
"CHK",
"MOD"
];
var no_forward_ref_default = createRule({
meta: {
type: "problem",
docs: {
description: "replace 'forwardRef' with passing 'ref' as a prop",
[Symbol.for("rule_features")]: RULE_FEATURES25
},
fixable: "code",
messages: {
noForwardRef: "In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead."
},
schema: []
},
name: RULE_NAME25,
create(context) {
if (!context.sourceCode.text.includes("forwardRef")) {
return {};
}
const { version: version2 } = getSettingsFromContext(context);
if (compare(version2, "19.0.0", "<")) {
return {};
}
return {
CallExpression(node) {
if (!isForwardRefCall(context, node)) {
return;
}
context.report({
messageId: "noForwardRef",
node,
fix: getFix(context, node)
});
}
};
},
defaultOptions: []
});
function getFix(context, node) {
return (fixer) => {
const [componentNode] = node.arguments;
if (componentNode == null || !AST12.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 = match(arg0).with({ type: AST_NODE_TYPES.Identifier }, (n) => `...${n.name}`).with({ type: AST_NODE_TYPES.ObjectPattern }, (n) => n.properties.map(getText).join(", ")).otherwise(() => _);
const fixedArg1Text = match(arg1).with(P.nullish, () => "ref").with({ type: AST_NODE_TYPES.Identifier, name: "ref" }, () => "ref").with({ type: AST_NODE_TYPES.Identifier, name: P.string }, (n) => `ref: ${n.name}`).otherwise(() => _);
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_NAME26 = "no-implicit-key";
var RULE_FEATURES26 = [
"CHK"
];
var no_implicit_key_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow implicit 'key' props",
[Symbol.for("rule_features")]: RULE_FEATURES26
},
messages: {
noImplicitKey: "Do not use implicit 'key' props."
},
schema: []
},
name: RULE_NAME26,
create(context) {
return {
JSXOpeningElement(node) {
const initialScope = context.sourceCode.getScope(node);
const keyPropFound = JSX8.getAttribute("key", node.attributes, initialScope);
const keyPropOnElement = node.attributes.some(
(n) => n.type === AST_NODE_TYPES.JSXAttribute && n.name.type === AST_NODE_TYPES.JSXIdentifier && n.name.name === "key"
);
if (keyPropFound != null && !keyPropOnElement) {
context.report({ messageId: "noImplicitKey", node: keyPropFound });
}
}
};
},
defaultOptions: []
});
var RULE_NAME27 = "no-leaked-conditional-rendering";
var RULE_FEATURES27 = [
"CHK",
"TSC"
];
var tsHelpers = {
isAnyType: (type) => isTypeFlagSet(type, ts.TypeFlags.TypeParameter | ts.TypeFlags.Any),
isBigIntType: (type) => isTypeFlagSet(type, ts.TypeFlags.BigIntLike),
isBooleanType: (type) => isTypeFlagSet(type, ts.TypeFlags.BooleanLike),
isEnumType: (type) => isTypeFlagSet(type, ts.TypeFlags.EnumLike),
isFalsyBigIntType: (type) => type.isLiteral() && isMatching({ value: { base10Value: "0" } }, type),
isFalsyNumberType: (type) => type.isNumberLiteral() && type.value === 0,
isFalsyStringType: (type) => type.isStringLiteral() && type.value === "",
isNeverType: (type) => isTypeFlagSet(type, ts.TypeFlags.Never),
isNullishType: (type) => isTypeFlagSet(
type,
ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike
),
isNumberType: (type) => isTypeFlagSet(type, ts.TypeFlags.NumberLike),
isObjectType: (type) => !isTypeFlagSet(
type,
ts.TypeFlags.Null | ts.TypeFlags.Undefined | ts.TypeFlags.VoidLike | ts.TypeFlags.BooleanLike | ts.TypeFlags.StringLike | ts.TypeFlags.NumberLike | ts.TypeFlags.BigIntLike | ts.TypeFlags.TypeParameter | ts.TypeFlags.Any | ts.TypeFlags.Unknown | ts.TypeFlags.Never
),
isStringType: (type) => isTypeFlagSet(type, ts.TypeFlags.StringLike),
isTruthyBigIntType: (type) => type.isLiteral() && isMatching({ value: { base10Value: P.not("0") } }, type),
isTruthyNumberType: (type) => type.isNumberLiteral() && type.value !== 0,
isTruthyStringType: (type) => type.isStringLiteral() && type.value !== "",
isUnknownType: (type) => isTypeFlagSet(type, ts.TypeFlags.Unknown)
};
function inspectVariantTypes(types) {
const variantTypes = /* @__PURE__ */ new Set();
if (types.some(tsHelpers.isUnknownType)) {
variantTypes.add("unknown");
return variantTypes;
}
if (types.some(tsHelpers.isNullishType)) {
variantTypes.add("nullish");
}
const booleans = types.filter(tsHelpers.isBooleanType);
switch (true) {
case (booleans.length === 1 && booleans[0] != null): {
const first = booleans[0];
if (isTrueLiteralType(first)) {
variantTypes.add("truthy boolean");
} else if (isFalseLiteralType(first)) {
variantTypes.add("falsy boolean");
}
break;
}
case booleans.length === 2: {
variantTypes.add("boolean");
break;
}
}
const strings = types.filter(tsHelpers.isStringType);
if (strings.length > 0) {
const evaluated = match(strings).when((types2) => types2.every(tsHelpers.isTruthyStringType), () => "truthy string").when((types2) => types2.every(tsHelpers.isFalsyStringType), () => "falsy string").otherwise(() => "string");
variantTypes.add(evaluated);
}
const bigints = types.filter(tsHelpers.isBigIntType);
if (bigints.length > 0) {
const evaluated = match(bigints).when((types2) => types2.every(tsHelpers.isTruthyBigIntType), () => "truthy bigint").when((types2) => types2.every(tsHelpers.isFalsyBigIntType), () => "falsy bigint").otherwise(() => "bigint");
variantTypes.add(evaluated);
}
const numbers = types.filter(tsHelpers.isNumberType);
if (numbers.length > 0) {
const evaluated = match(numbers).when((types2) => types2.every(tsHelpers.isTruthyNumberType), () => "truthy number").when((types2) => types2.every(tsHelpers.isFalsyNumberType), () => "falsy number").otherwise(() => "number");
variantTypes.add(evaluated);
}
if (types.some(tsHelpers.isEnumType)) {
variantTypes.add("enum");
}
if (types.some(tsHelpers.isObjectType)) {
variantTypes.add("object");
}
if (types.some(tsHelpers.isAnyType)) {
variantTypes.add("any");
}
if (types.some(tsHelpers.isNeverType)) {
variantTypes.add("never");
}
return variantTypes;
}
var no_leaked_conditional_rendering_default = createRule({
meta: {
type: "problem",
docs: {
description: "disallow problematic leaked values from being rendered",
[Symbol.for("rule_features")]: RULE_FEATURES27
},
messages: {
noLeakedConditionalRendering: "Potential leaked value {{value}} that might cause unintentionally rendered values or rendering crashes."
},
schema: []
},
name: RULE_NAME27,
create(context) {
if (!context.sourceCode.text.includes("&&") && !context.sourceCode.text.includes("?")) {
return {};
}
const { version: version2 } = getSettingsFromContext(context);
const allowedVariants = [
"any",
"boolean",
"nullish",
"object",
"falsy boolean",
"truthy bigint",
"truthy boolean",
"truthy number",
"truthy string",
...compare(version2, "18.0.0", "<") ? [] : ["string", "falsy string"]
];
const services = ESLintUtils.getParserServices(context, false);
function getReportDescriptor(node) {
if (node == null) return _;
return match(node).when(AST12.isJSX, () => _).with({ type: AST_NODE_TYPES.LogicalExpression, operator: "&&" }, ({ left, right }) => {
const isLeftUnaryNot = left.ty