eslint-plugin-react-x 1.22.2-next.6 → 1.22.2-next.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/index.js +68 -122
  2. package/dist/index.mjs +68 -122
  3. package/package.json +8 -8
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var eff = require('@eslint-react/eff');
4
- var JSX8 = require('@eslint-react/jsx');
4
+ var JSX7 = require('@eslint-react/jsx');
5
5
  var shared = require('@eslint-react/shared');
6
6
  var AST11 = require('@eslint-react/ast');
7
7
  var core = require('@eslint-react/core');
@@ -35,14 +35,14 @@ function _interopNamespace(e) {
35
35
  return Object.freeze(n);
36
36
  }
37
37
 
38
- var JSX8__namespace = /*#__PURE__*/_interopNamespace(JSX8);
38
+ var JSX7__namespace = /*#__PURE__*/_interopNamespace(JSX7);
39
39
  var AST11__namespace = /*#__PURE__*/_interopNamespace(AST11);
40
40
  var VAR__namespace = /*#__PURE__*/_interopNamespace(VAR);
41
41
  var ts__default = /*#__PURE__*/_interopDefault(ts);
42
42
 
43
43
  // package.json
44
44
  var name = "eslint-plugin-react-x";
45
- var version = "1.22.2-next.6";
45
+ var version = "1.22.2-next.7";
46
46
  var createRule = shared.createRuleForPlugin("x");
47
47
 
48
48
  // src/rules/avoid-shorthand-boolean.ts
@@ -70,7 +70,7 @@ var avoid_shorthand_boolean_default = createRule({
70
70
  messageId: "avoidShorthandBoolean",
71
71
  node,
72
72
  data: {
73
- propName: JSX8__namespace.getPropName(node)
73
+ propName: JSX7__namespace.getPropName(node)
74
74
  },
75
75
  fix: (fixer) => fixer.insertTextAfter(node.name, `={true}`)
76
76
  });
@@ -151,7 +151,7 @@ var RULE_FEATURES4 = [
151
151
  "CHK"
152
152
  ];
153
153
  function getName(node) {
154
- if (node.type === types.AST_NODE_TYPES.TSAsExpression) {
154
+ if (AST11__namespace.isTypeExpression(node)) {
155
155
  return getName(node.expression);
156
156
  }
157
157
  if (node.type === types.AST_NODE_TYPES.Identifier || node.type === types.AST_NODE_TYPES.PrivateIdentifier) {
@@ -537,7 +537,7 @@ var no_children_prop_default = createRule({
537
537
  return {
538
538
  JSXElement(node) {
539
539
  const initialScope = context.sourceCode.getScope(node);
540
- const prop = JSX8__namespace.getProp(node.openingElement.attributes, "children", initialScope);
540
+ const prop = JSX7__namespace.getProp(node.openingElement.attributes, "children", initialScope);
541
541
  const reportDescriptor = eff.O.map(prop, (prop2) => ({ messageId: "noChildrenProp", node: prop2 }));
542
542
  eff.O.map(reportDescriptor, context.report);
543
543
  }
@@ -912,7 +912,7 @@ var no_context_provider_default = createRule({
912
912
  if (compareVersions.compare(version2, "19.0.0", "<")) return {};
913
913
  return {
914
914
  JSXElement(node) {
915
- const elementName = JSX8__namespace.getElementName(node.openingElement);
915
+ const elementName = JSX7__namespace.getElementName(node.openingElement);
916
916
  if (!elementName.endsWith(".Provider")) return;
917
917
  context.report({
918
918
  messageId: "noContextProvider",
@@ -1009,7 +1009,7 @@ var RULE_FEATURES22 = [
1009
1009
  "CHK"
1010
1010
  ];
1011
1011
  function getName2(node) {
1012
- if (node.type === types.AST_NODE_TYPES.TSAsExpression) {
1012
+ if (AST11__namespace.isTypeExpression(node)) {
1013
1013
  return getName2(node.expression);
1014
1014
  }
1015
1015
  if (node.type === types.AST_NODE_TYPES.Identifier || node.type === types.AST_NODE_TYPES.PrivateIdentifier) {
@@ -1123,117 +1123,63 @@ var no_duplicate_key_default = createRule({
1123
1123
  },
1124
1124
  name: RULE_NAME24,
1125
1125
  create(context) {
1126
- const state = { isWithinChildrenToArray: false };
1127
- function isKeyEqual(a, b) {
1128
- return VAR__namespace.isNodeValueEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
1129
- }
1130
- function checkIteratorElement(node) {
1131
- if (node.type !== types.AST_NODE_TYPES.JSXElement) return eff.O.none();
1132
- const initialScope = context.sourceCode.getScope(node);
1133
- return eff.F.pipe(
1134
- JSX8__namespace.findPropInAttributes(node.openingElement.attributes, initialScope)("key"),
1135
- eff.O.flatMap((k) => "value" in k ? eff.O.fromNullable(k.value) : eff.O.none()),
1136
- eff.O.flatMap((v) => {
1137
- return isKeyEqual(v, v) ? eff.O.some({
1138
- messageId: "noDuplicateKey",
1139
- node: v,
1140
- data: {
1141
- value: context.sourceCode.getText(v)
1142
- }
1143
- }) : eff.O.none();
1144
- })
1145
- );
1146
- }
1147
- function checkExpression(node) {
1148
- switch (node.type) {
1149
- case types.AST_NODE_TYPES.ConditionalExpression:
1150
- if (!("consequent" in node)) return eff.O.none();
1151
- return eff.F.pipe(
1152
- checkIteratorElement(node.consequent),
1153
- eff.O.orElse(() => checkIteratorElement(node.alternate))
1154
- );
1155
- case types.AST_NODE_TYPES.JSXElement:
1156
- case types.AST_NODE_TYPES.JSXFragment:
1157
- return checkIteratorElement(node);
1158
- case types.AST_NODE_TYPES.LogicalExpression:
1159
- if (!("left" in node)) return eff.O.none();
1160
- return eff.F.pipe(
1161
- checkIteratorElement(node.left),
1162
- eff.O.orElse(() => checkIteratorElement(node.right))
1163
- );
1164
- default:
1165
- return eff.O.none();
1166
- }
1126
+ const keyedEntries = /* @__PURE__ */ new Map();
1127
+ function isKeyValueEqual(a, b) {
1128
+ const aValue = a.value;
1129
+ const bValue = b.value;
1130
+ if (aValue === null || bValue === null) return false;
1131
+ return VAR__namespace.isNodeValueEqual(aValue, bValue, [
1132
+ context.sourceCode.getScope(aValue),
1133
+ context.sourceCode.getScope(bValue)
1134
+ ]);
1167
1135
  }
1168
- function checkBlockStatement(node) {
1169
- return AST11__namespace.getNestedReturnStatements(node).reduce((acc, statement) => {
1170
- if (!statement.argument) return acc;
1171
- const maybeDescriptor = checkIteratorElement(statement.argument);
1172
- if (eff.O.isNone(maybeDescriptor)) return acc;
1173
- const descriptor = maybeDescriptor.value;
1174
- return [...acc, descriptor];
1175
- }, []);
1176
- }
1177
- const seen = /* @__PURE__ */ new WeakSet();
1178
1136
  return {
1179
- "ArrayExpression, JSXElement > JSXElement"(node) {
1180
- if (state.isWithinChildrenToArray) return;
1181
- const elements = tsPattern.match(node).with({ type: types.AST_NODE_TYPES.ArrayExpression }, ({ elements: elements2 }) => elements2).with({ type: types.AST_NODE_TYPES.JSXElement }, ({ parent }) => "children" in parent ? parent.children : []).otherwise(() => []).filter(AST11__namespace.is(types.AST_NODE_TYPES.JSXElement)).filter((element) => !seen.has(element));
1182
- const keys = elements.reduce(
1183
- (acc, element) => {
1184
- const attr = element.openingElement.attributes.findLast((attr2) => {
1185
- if (attr2.type !== types.AST_NODE_TYPES.JSXAttribute) return false;
1186
- return attr2.name.name === "key";
1137
+ "JSXAttribute[name.name='key']"(node) {
1138
+ const jsxElement = node.parent.parent;
1139
+ switch (jsxElement.parent.type) {
1140
+ case types.AST_NODE_TYPES.ArrayExpression:
1141
+ case types.AST_NODE_TYPES.JSXElement:
1142
+ case types.AST_NODE_TYPES.JSXFragment: {
1143
+ const root = jsxElement.parent;
1144
+ const prevKeys = keyedEntries.get(root)?.keys ?? [];
1145
+ keyedEntries.set(root, {
1146
+ hasDuplicate: prevKeys.some((prevKey) => isKeyValueEqual(prevKey, node)),
1147
+ keys: [...prevKeys, node],
1148
+ root: jsxElement.parent
1187
1149
  });
1188
- if (!attr || !("value" in attr) || attr.value === null) return acc;
1189
- const { value } = attr;
1190
- if (acc.length === 0) return [[element, attr, value]];
1191
- if (acc.some(([_, _1, v]) => isKeyEqual(v, value))) {
1192
- return [...acc, [element, attr, value]];
1193
- }
1194
- return acc;
1195
- },
1196
- []
1197
- );
1198
- if (keys.length < 2) return;
1199
- for (const [element, attr, value] of keys) {
1200
- seen.add(element);
1201
- context.report({
1202
- messageId: "noDuplicateKey",
1203
- node: attr,
1204
- data: {
1205
- value: context.sourceCode.getText(value)
1150
+ break;
1151
+ }
1152
+ default: {
1153
+ const entry = eff.F.pipe(
1154
+ eff.O.Do,
1155
+ eff.O.bind("call", () => AST11__namespace.traverseUpGuard(jsxElement, AST11__namespace.isMapCallLoose)),
1156
+ eff.O.bind("ietr", ({ call }) => AST11__namespace.traverseUpStop(jsxElement, call, AST11__namespace.isFunction)),
1157
+ eff.O.bind("arg0", ({ call }) => eff.O.fromNullable(call.arguments[0]))
1158
+ );
1159
+ for (const { arg0, call, ietr } of eff.O.toArray(entry)) {
1160
+ if (AST11__namespace.unwrapTypeExpression(arg0) !== ietr) continue;
1161
+ keyedEntries.set(call, {
1162
+ hasDuplicate: node.value?.type === types.AST_NODE_TYPES.Literal,
1163
+ keys: [node],
1164
+ root: call
1165
+ });
1206
1166
  }
1207
- });
1167
+ }
1208
1168
  }
1209
1169
  },
1210
- CallExpression(node) {
1211
- state.isWithinChildrenToArray ||= core.isChildrenToArrayCall(node, context);
1212
- if (state.isWithinChildrenToArray) return;
1213
- const isMapCall = AST11__namespace.isMapCallLoose(node);
1214
- const isArrayFromCall = tsPattern.isMatching({
1215
- type: types.AST_NODE_TYPES.CallExpression,
1216
- callee: {
1217
- type: types.AST_NODE_TYPES.MemberExpression,
1218
- property: {
1219
- name: "from"
1220
- }
1221
- }
1222
- }, node);
1223
- if (!isMapCall && !isArrayFromCall) return;
1224
- const fn = node.arguments[isMapCall ? 0 : 1];
1225
- if (!AST11__namespace.isOneOf([types.AST_NODE_TYPES.ArrowFunctionExpression, types.AST_NODE_TYPES.FunctionExpression])(fn)) return;
1226
- if (fn.body.type === types.AST_NODE_TYPES.BlockStatement) {
1227
- for (const descriptor of checkBlockStatement(fn.body)) {
1228
- context.report(descriptor);
1170
+ "Program:exit"() {
1171
+ for (const { hasDuplicate, keys } of keyedEntries.values()) {
1172
+ if (!hasDuplicate) continue;
1173
+ for (const key of keys) {
1174
+ context.report({
1175
+ messageId: "noDuplicateKey",
1176
+ node: key,
1177
+ data: {
1178
+ value: context.sourceCode.getText(key)
1179
+ }
1180
+ });
1229
1181
  }
1230
- return;
1231
1182
  }
1232
- eff.O.map(checkExpression(fn.body), context.report);
1233
- },
1234
- "CallExpression:exit"(node) {
1235
- if (!core.isChildrenToArrayCall(node, context)) return;
1236
- state.isWithinChildrenToArray = false;
1237
1183
  }
1238
1184
  };
1239
1185
  },
@@ -1356,7 +1302,7 @@ var no_implicit_key_default = createRule({
1356
1302
  create(context) {
1357
1303
  function getReportDescriptor(node) {
1358
1304
  const initialScope = context.sourceCode.getScope(node);
1359
- const keyPropFound = JSX8__namespace.findPropInAttributes(node.attributes, initialScope)("key");
1305
+ const keyPropFound = JSX7__namespace.findPropInAttributes(node.attributes, initialScope)("key");
1360
1306
  const keyPropOnElement = node.attributes.some(
1361
1307
  (n) => AST11__namespace.is(types.AST_NODE_TYPES.JSXAttribute)(n) && n.name.name === "key"
1362
1308
  );
@@ -1601,7 +1547,7 @@ var no_missing_key_default = createRule({
1601
1547
  switch (node.type) {
1602
1548
  case types.AST_NODE_TYPES.JSXElement: {
1603
1549
  const initialScope = context.sourceCode.getScope(node);
1604
- if (!JSX8__namespace.hasProp(node.openingElement.attributes, "key", initialScope)) {
1550
+ if (!JSX7__namespace.hasProp(node.openingElement.attributes, "key", initialScope)) {
1605
1551
  return eff.O.some({
1606
1552
  messageId: "noMissingKey",
1607
1553
  node
@@ -1650,7 +1596,7 @@ var no_missing_key_default = createRule({
1650
1596
  if (elements.length === 0) return;
1651
1597
  const initialScope = context.sourceCode.getScope(node);
1652
1598
  for (const element of elements) {
1653
- if (!JSX8__namespace.hasProp(element.openingElement.attributes, "key", initialScope)) {
1599
+ if (!JSX7__namespace.hasProp(element.openingElement.attributes, "key", initialScope)) {
1654
1600
  context.report({
1655
1601
  messageId: "noMissingKey",
1656
1602
  node: element
@@ -1738,7 +1684,7 @@ var no_nested_components_default = createRule({
1738
1684
  if (eff.O.isNone(componentName)) continue;
1739
1685
  const name2 = componentName.value;
1740
1686
  const isInsideProperty = component.parent.type === types.AST_NODE_TYPES.Property;
1741
- const isInsideJSXPropValue = component.parent.type === types.AST_NODE_TYPES.JSXAttribute || eff.O.isSome(JSX8__namespace.traverseUpProp(node, (n) => n.value?.type === types.AST_NODE_TYPES.JSXExpressionContainer));
1687
+ const isInsideJSXPropValue = component.parent.type === types.AST_NODE_TYPES.JSXAttribute || eff.O.isSome(JSX7__namespace.traverseUpProp(node, (n) => n.value?.type === types.AST_NODE_TYPES.JSXExpressionContainer));
1742
1688
  if (isInsideJSXPropValue) {
1743
1689
  if (!core.isDeclaredInRenderPropLoose(component)) {
1744
1690
  context.report({
@@ -2362,7 +2308,7 @@ var LIFECYCLE_METHODS = /* @__PURE__ */ new Set([
2362
2308
  "UNSAFE_componentWillUpdate"
2363
2309
  ]);
2364
2310
  function getName3(node) {
2365
- if (node.type === types.AST_NODE_TYPES.TSAsExpression) {
2311
+ if (AST11__namespace.isTypeExpression(node)) {
2366
2312
  return getName3(node.expression);
2367
2313
  }
2368
2314
  if (node.type === types.AST_NODE_TYPES.Identifier || node.type === types.AST_NODE_TYPES.PrivateIdentifier) {
@@ -2476,7 +2422,7 @@ var RULE_FEATURES43 = [
2476
2422
  "CHK"
2477
2423
  ];
2478
2424
  function getName4(node) {
2479
- if (node.type === types.AST_NODE_TYPES.TSAsExpression) {
2425
+ if (AST11__namespace.isTypeExpression(node)) {
2480
2426
  return getName4(node.expression);
2481
2427
  }
2482
2428
  if (node.type === types.AST_NODE_TYPES.Identifier || node.type === types.AST_NODE_TYPES.PrivateIdentifier) {
@@ -2619,18 +2565,18 @@ var no_unused_state_default = createRule({
2619
2565
  var RULE_NAME44 = "no-useless-fragment";
2620
2566
  function check(node, context, allowExpressions) {
2621
2567
  const initialScope = context.sourceCode.getScope(node);
2622
- if (JSX8__namespace.isKeyedElement(node, initialScope)) return;
2623
- if (JSX8__namespace.isBuiltInElement(node.parent)) context.report({ messageId: "noUselessFragmentInBuiltIn", node });
2568
+ if (JSX7__namespace.isKeyedElement(node, initialScope)) return;
2569
+ if (JSX7__namespace.isBuiltInElement(node.parent)) context.report({ messageId: "noUselessFragmentInBuiltIn", node });
2624
2570
  if (node.children.length === 0) return context.report({ messageId: "noUselessFragment", node });
2625
2571
  const isChildren = AST11__namespace.isOneOf([types.AST_NODE_TYPES.JSXElement, types.AST_NODE_TYPES.JSXFragment])(node.parent);
2626
2572
  const [firstChildren] = node.children;
2627
- if (allowExpressions && node.children.length === 1 && JSX8__namespace.isLiteral(firstChildren) && !isChildren) return;
2573
+ if (allowExpressions && node.children.length === 1 && JSX7__namespace.isLiteral(firstChildren) && !isChildren) return;
2628
2574
  if (!allowExpressions && isChildren) {
2629
2575
  return context.report({ messageId: "noUselessFragment", node });
2630
2576
  } else if (!allowExpressions && !isChildren && node.children.length === 1) {
2631
2577
  return context.report({ messageId: "noUselessFragment", node });
2632
2578
  }
2633
- const nonPaddingChildren = node.children.filter((child) => !JSX8__namespace.isPaddingSpaces(child));
2579
+ const nonPaddingChildren = node.children.filter((child) => !JSX7__namespace.isPaddingSpaces(child));
2634
2580
  if (nonPaddingChildren.length > 1) return;
2635
2581
  if (nonPaddingChildren.length === 0) return context.report({ messageId: "noUselessFragment", node });
2636
2582
  const [first] = nonPaddingChildren;
@@ -2872,7 +2818,7 @@ var prefer_shorthand_boolean_default = createRule({
2872
2818
  create(context) {
2873
2819
  function getReportDescriptor(node) {
2874
2820
  const { value } = node;
2875
- const propName = JSX8__namespace.getPropName(node);
2821
+ const propName = JSX7__namespace.getPropName(node);
2876
2822
  const hasValueTrue = value?.type === types.AST_NODE_TYPES.JSXExpressionContainer && value.expression.type === types.AST_NODE_TYPES.Literal && value.expression.value === true;
2877
2823
  if (!hasValueTrue) return eff.O.none();
2878
2824
  return eff.O.some({
package/dist/index.mjs CHANGED
@@ -1,8 +1,8 @@
1
1
  import { F, O, isString, isNullable } from '@eslint-react/eff';
2
- import * as JSX8 from '@eslint-react/jsx';
2
+ import * as JSX7 from '@eslint-react/jsx';
3
3
  import { createRuleForPlugin, normalizeSettings, decodeSettings, unsafeReadSettings } from '@eslint-react/shared';
4
4
  import * as AST11 from '@eslint-react/ast';
5
- import { isForwardRefCall, isThisSetState, isClassComponent, isChildrenCount, isChildrenForEach, isChildrenMap, isChildrenOnly, isChildrenToArray, useComponentCollectorLegacy, isCloneElementCall, isCreateRefCall, isComponentName, isChildrenToArrayCall, useComponentCollector, ERFunctionComponentFlag, ERComponentHint, isDirectValueOfRenderPropertyLoose, isDeclaredInRenderPropLoose, isInsideCreateElementProps, isInsideRenderMethod, ERClassComponentFlag, isFragmentElement, isCreateElementCall, isInitializedFromReact } from '@eslint-react/core';
5
+ import { isForwardRefCall, isThisSetState, isClassComponent, isChildrenCount, isChildrenForEach, isChildrenMap, isChildrenOnly, isChildrenToArray, useComponentCollectorLegacy, isCloneElementCall, isCreateRefCall, isComponentName, useComponentCollector, ERFunctionComponentFlag, isChildrenToArrayCall, ERComponentHint, isDirectValueOfRenderPropertyLoose, isDeclaredInRenderPropLoose, isInsideCreateElementProps, isInsideRenderMethod, ERClassComponentFlag, isFragmentElement, isCreateElementCall, isInitializedFromReact } from '@eslint-react/core';
6
6
  import { AST_NODE_TYPES } from '@typescript-eslint/types';
7
7
  import { isMatching, P, match } from 'ts-pattern';
8
8
  import { compare } from 'compare-versions';
@@ -15,7 +15,7 @@ import { getTypeImmutability, isUnknown, isImmutable, isReadonlyShallow, isReado
15
15
 
16
16
  // package.json
17
17
  var name = "eslint-plugin-react-x";
18
- var version = "1.22.2-next.6";
18
+ var version = "1.22.2-next.7";
19
19
  var createRule = createRuleForPlugin("x");
20
20
 
21
21
  // src/rules/avoid-shorthand-boolean.ts
@@ -43,7 +43,7 @@ var avoid_shorthand_boolean_default = createRule({
43
43
  messageId: "avoidShorthandBoolean",
44
44
  node,
45
45
  data: {
46
- propName: JSX8.getPropName(node)
46
+ propName: JSX7.getPropName(node)
47
47
  },
48
48
  fix: (fixer) => fixer.insertTextAfter(node.name, `={true}`)
49
49
  });
@@ -124,7 +124,7 @@ var RULE_FEATURES4 = [
124
124
  "CHK"
125
125
  ];
126
126
  function getName(node) {
127
- if (node.type === AST_NODE_TYPES.TSAsExpression) {
127
+ if (AST11.isTypeExpression(node)) {
128
128
  return getName(node.expression);
129
129
  }
130
130
  if (node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier) {
@@ -510,7 +510,7 @@ var no_children_prop_default = createRule({
510
510
  return {
511
511
  JSXElement(node) {
512
512
  const initialScope = context.sourceCode.getScope(node);
513
- const prop = JSX8.getProp(node.openingElement.attributes, "children", initialScope);
513
+ const prop = JSX7.getProp(node.openingElement.attributes, "children", initialScope);
514
514
  const reportDescriptor = O.map(prop, (prop2) => ({ messageId: "noChildrenProp", node: prop2 }));
515
515
  O.map(reportDescriptor, context.report);
516
516
  }
@@ -885,7 +885,7 @@ var no_context_provider_default = createRule({
885
885
  if (compare(version2, "19.0.0", "<")) return {};
886
886
  return {
887
887
  JSXElement(node) {
888
- const elementName = JSX8.getElementName(node.openingElement);
888
+ const elementName = JSX7.getElementName(node.openingElement);
889
889
  if (!elementName.endsWith(".Provider")) return;
890
890
  context.report({
891
891
  messageId: "noContextProvider",
@@ -982,7 +982,7 @@ var RULE_FEATURES22 = [
982
982
  "CHK"
983
983
  ];
984
984
  function getName2(node) {
985
- if (node.type === AST_NODE_TYPES.TSAsExpression) {
985
+ if (AST11.isTypeExpression(node)) {
986
986
  return getName2(node.expression);
987
987
  }
988
988
  if (node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier) {
@@ -1096,117 +1096,63 @@ var no_duplicate_key_default = createRule({
1096
1096
  },
1097
1097
  name: RULE_NAME24,
1098
1098
  create(context) {
1099
- const state = { isWithinChildrenToArray: false };
1100
- function isKeyEqual(a, b) {
1101
- return VAR.isNodeValueEqual(a, b, [context.sourceCode.getScope(a), context.sourceCode.getScope(b)]);
1102
- }
1103
- function checkIteratorElement(node) {
1104
- if (node.type !== AST_NODE_TYPES.JSXElement) return O.none();
1105
- const initialScope = context.sourceCode.getScope(node);
1106
- return F.pipe(
1107
- JSX8.findPropInAttributes(node.openingElement.attributes, initialScope)("key"),
1108
- O.flatMap((k) => "value" in k ? O.fromNullable(k.value) : O.none()),
1109
- O.flatMap((v) => {
1110
- return isKeyEqual(v, v) ? O.some({
1111
- messageId: "noDuplicateKey",
1112
- node: v,
1113
- data: {
1114
- value: context.sourceCode.getText(v)
1115
- }
1116
- }) : O.none();
1117
- })
1118
- );
1119
- }
1120
- function checkExpression(node) {
1121
- switch (node.type) {
1122
- case AST_NODE_TYPES.ConditionalExpression:
1123
- if (!("consequent" in node)) return O.none();
1124
- return F.pipe(
1125
- checkIteratorElement(node.consequent),
1126
- O.orElse(() => checkIteratorElement(node.alternate))
1127
- );
1128
- case AST_NODE_TYPES.JSXElement:
1129
- case AST_NODE_TYPES.JSXFragment:
1130
- return checkIteratorElement(node);
1131
- case AST_NODE_TYPES.LogicalExpression:
1132
- if (!("left" in node)) return O.none();
1133
- return F.pipe(
1134
- checkIteratorElement(node.left),
1135
- O.orElse(() => checkIteratorElement(node.right))
1136
- );
1137
- default:
1138
- return O.none();
1139
- }
1099
+ const keyedEntries = /* @__PURE__ */ new Map();
1100
+ function isKeyValueEqual(a, b) {
1101
+ const aValue = a.value;
1102
+ const bValue = b.value;
1103
+ if (aValue === null || bValue === null) return false;
1104
+ return VAR.isNodeValueEqual(aValue, bValue, [
1105
+ context.sourceCode.getScope(aValue),
1106
+ context.sourceCode.getScope(bValue)
1107
+ ]);
1140
1108
  }
1141
- function checkBlockStatement(node) {
1142
- return AST11.getNestedReturnStatements(node).reduce((acc, statement) => {
1143
- if (!statement.argument) return acc;
1144
- const maybeDescriptor = checkIteratorElement(statement.argument);
1145
- if (O.isNone(maybeDescriptor)) return acc;
1146
- const descriptor = maybeDescriptor.value;
1147
- return [...acc, descriptor];
1148
- }, []);
1149
- }
1150
- const seen = /* @__PURE__ */ new WeakSet();
1151
1109
  return {
1152
- "ArrayExpression, JSXElement > JSXElement"(node) {
1153
- if (state.isWithinChildrenToArray) return;
1154
- const elements = match(node).with({ type: AST_NODE_TYPES.ArrayExpression }, ({ elements: elements2 }) => elements2).with({ type: AST_NODE_TYPES.JSXElement }, ({ parent }) => "children" in parent ? parent.children : []).otherwise(() => []).filter(AST11.is(AST_NODE_TYPES.JSXElement)).filter((element) => !seen.has(element));
1155
- const keys = elements.reduce(
1156
- (acc, element) => {
1157
- const attr = element.openingElement.attributes.findLast((attr2) => {
1158
- if (attr2.type !== AST_NODE_TYPES.JSXAttribute) return false;
1159
- return attr2.name.name === "key";
1110
+ "JSXAttribute[name.name='key']"(node) {
1111
+ const jsxElement = node.parent.parent;
1112
+ switch (jsxElement.parent.type) {
1113
+ case AST_NODE_TYPES.ArrayExpression:
1114
+ case AST_NODE_TYPES.JSXElement:
1115
+ case AST_NODE_TYPES.JSXFragment: {
1116
+ const root = jsxElement.parent;
1117
+ const prevKeys = keyedEntries.get(root)?.keys ?? [];
1118
+ keyedEntries.set(root, {
1119
+ hasDuplicate: prevKeys.some((prevKey) => isKeyValueEqual(prevKey, node)),
1120
+ keys: [...prevKeys, node],
1121
+ root: jsxElement.parent
1160
1122
  });
1161
- if (!attr || !("value" in attr) || attr.value === null) return acc;
1162
- const { value } = attr;
1163
- if (acc.length === 0) return [[element, attr, value]];
1164
- if (acc.some(([_, _1, v]) => isKeyEqual(v, value))) {
1165
- return [...acc, [element, attr, value]];
1166
- }
1167
- return acc;
1168
- },
1169
- []
1170
- );
1171
- if (keys.length < 2) return;
1172
- for (const [element, attr, value] of keys) {
1173
- seen.add(element);
1174
- context.report({
1175
- messageId: "noDuplicateKey",
1176
- node: attr,
1177
- data: {
1178
- value: context.sourceCode.getText(value)
1123
+ break;
1124
+ }
1125
+ default: {
1126
+ const entry = F.pipe(
1127
+ O.Do,
1128
+ O.bind("call", () => AST11.traverseUpGuard(jsxElement, AST11.isMapCallLoose)),
1129
+ O.bind("ietr", ({ call }) => AST11.traverseUpStop(jsxElement, call, AST11.isFunction)),
1130
+ O.bind("arg0", ({ call }) => O.fromNullable(call.arguments[0]))
1131
+ );
1132
+ for (const { arg0, call, ietr } of O.toArray(entry)) {
1133
+ if (AST11.unwrapTypeExpression(arg0) !== ietr) continue;
1134
+ keyedEntries.set(call, {
1135
+ hasDuplicate: node.value?.type === AST_NODE_TYPES.Literal,
1136
+ keys: [node],
1137
+ root: call
1138
+ });
1179
1139
  }
1180
- });
1140
+ }
1181
1141
  }
1182
1142
  },
1183
- CallExpression(node) {
1184
- state.isWithinChildrenToArray ||= isChildrenToArrayCall(node, context);
1185
- if (state.isWithinChildrenToArray) return;
1186
- const isMapCall = AST11.isMapCallLoose(node);
1187
- const isArrayFromCall = isMatching({
1188
- type: AST_NODE_TYPES.CallExpression,
1189
- callee: {
1190
- type: AST_NODE_TYPES.MemberExpression,
1191
- property: {
1192
- name: "from"
1193
- }
1194
- }
1195
- }, node);
1196
- if (!isMapCall && !isArrayFromCall) return;
1197
- const fn = node.arguments[isMapCall ? 0 : 1];
1198
- if (!AST11.isOneOf([AST_NODE_TYPES.ArrowFunctionExpression, AST_NODE_TYPES.FunctionExpression])(fn)) return;
1199
- if (fn.body.type === AST_NODE_TYPES.BlockStatement) {
1200
- for (const descriptor of checkBlockStatement(fn.body)) {
1201
- context.report(descriptor);
1143
+ "Program:exit"() {
1144
+ for (const { hasDuplicate, keys } of keyedEntries.values()) {
1145
+ if (!hasDuplicate) continue;
1146
+ for (const key of keys) {
1147
+ context.report({
1148
+ messageId: "noDuplicateKey",
1149
+ node: key,
1150
+ data: {
1151
+ value: context.sourceCode.getText(key)
1152
+ }
1153
+ });
1202
1154
  }
1203
- return;
1204
1155
  }
1205
- O.map(checkExpression(fn.body), context.report);
1206
- },
1207
- "CallExpression:exit"(node) {
1208
- if (!isChildrenToArrayCall(node, context)) return;
1209
- state.isWithinChildrenToArray = false;
1210
1156
  }
1211
1157
  };
1212
1158
  },
@@ -1329,7 +1275,7 @@ var no_implicit_key_default = createRule({
1329
1275
  create(context) {
1330
1276
  function getReportDescriptor(node) {
1331
1277
  const initialScope = context.sourceCode.getScope(node);
1332
- const keyPropFound = JSX8.findPropInAttributes(node.attributes, initialScope)("key");
1278
+ const keyPropFound = JSX7.findPropInAttributes(node.attributes, initialScope)("key");
1333
1279
  const keyPropOnElement = node.attributes.some(
1334
1280
  (n) => AST11.is(AST_NODE_TYPES.JSXAttribute)(n) && n.name.name === "key"
1335
1281
  );
@@ -1574,7 +1520,7 @@ var no_missing_key_default = createRule({
1574
1520
  switch (node.type) {
1575
1521
  case AST_NODE_TYPES.JSXElement: {
1576
1522
  const initialScope = context.sourceCode.getScope(node);
1577
- if (!JSX8.hasProp(node.openingElement.attributes, "key", initialScope)) {
1523
+ if (!JSX7.hasProp(node.openingElement.attributes, "key", initialScope)) {
1578
1524
  return O.some({
1579
1525
  messageId: "noMissingKey",
1580
1526
  node
@@ -1623,7 +1569,7 @@ var no_missing_key_default = createRule({
1623
1569
  if (elements.length === 0) return;
1624
1570
  const initialScope = context.sourceCode.getScope(node);
1625
1571
  for (const element of elements) {
1626
- if (!JSX8.hasProp(element.openingElement.attributes, "key", initialScope)) {
1572
+ if (!JSX7.hasProp(element.openingElement.attributes, "key", initialScope)) {
1627
1573
  context.report({
1628
1574
  messageId: "noMissingKey",
1629
1575
  node: element
@@ -1711,7 +1657,7 @@ var no_nested_components_default = createRule({
1711
1657
  if (O.isNone(componentName)) continue;
1712
1658
  const name2 = componentName.value;
1713
1659
  const isInsideProperty = component.parent.type === AST_NODE_TYPES.Property;
1714
- const isInsideJSXPropValue = component.parent.type === AST_NODE_TYPES.JSXAttribute || O.isSome(JSX8.traverseUpProp(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer));
1660
+ const isInsideJSXPropValue = component.parent.type === AST_NODE_TYPES.JSXAttribute || O.isSome(JSX7.traverseUpProp(node, (n) => n.value?.type === AST_NODE_TYPES.JSXExpressionContainer));
1715
1661
  if (isInsideJSXPropValue) {
1716
1662
  if (!isDeclaredInRenderPropLoose(component)) {
1717
1663
  context.report({
@@ -2335,7 +2281,7 @@ var LIFECYCLE_METHODS = /* @__PURE__ */ new Set([
2335
2281
  "UNSAFE_componentWillUpdate"
2336
2282
  ]);
2337
2283
  function getName3(node) {
2338
- if (node.type === AST_NODE_TYPES.TSAsExpression) {
2284
+ if (AST11.isTypeExpression(node)) {
2339
2285
  return getName3(node.expression);
2340
2286
  }
2341
2287
  if (node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier) {
@@ -2449,7 +2395,7 @@ var RULE_FEATURES43 = [
2449
2395
  "CHK"
2450
2396
  ];
2451
2397
  function getName4(node) {
2452
- if (node.type === AST_NODE_TYPES.TSAsExpression) {
2398
+ if (AST11.isTypeExpression(node)) {
2453
2399
  return getName4(node.expression);
2454
2400
  }
2455
2401
  if (node.type === AST_NODE_TYPES.Identifier || node.type === AST_NODE_TYPES.PrivateIdentifier) {
@@ -2592,18 +2538,18 @@ var no_unused_state_default = createRule({
2592
2538
  var RULE_NAME44 = "no-useless-fragment";
2593
2539
  function check(node, context, allowExpressions) {
2594
2540
  const initialScope = context.sourceCode.getScope(node);
2595
- if (JSX8.isKeyedElement(node, initialScope)) return;
2596
- if (JSX8.isBuiltInElement(node.parent)) context.report({ messageId: "noUselessFragmentInBuiltIn", node });
2541
+ if (JSX7.isKeyedElement(node, initialScope)) return;
2542
+ if (JSX7.isBuiltInElement(node.parent)) context.report({ messageId: "noUselessFragmentInBuiltIn", node });
2597
2543
  if (node.children.length === 0) return context.report({ messageId: "noUselessFragment", node });
2598
2544
  const isChildren = AST11.isOneOf([AST_NODE_TYPES.JSXElement, AST_NODE_TYPES.JSXFragment])(node.parent);
2599
2545
  const [firstChildren] = node.children;
2600
- if (allowExpressions && node.children.length === 1 && JSX8.isLiteral(firstChildren) && !isChildren) return;
2546
+ if (allowExpressions && node.children.length === 1 && JSX7.isLiteral(firstChildren) && !isChildren) return;
2601
2547
  if (!allowExpressions && isChildren) {
2602
2548
  return context.report({ messageId: "noUselessFragment", node });
2603
2549
  } else if (!allowExpressions && !isChildren && node.children.length === 1) {
2604
2550
  return context.report({ messageId: "noUselessFragment", node });
2605
2551
  }
2606
- const nonPaddingChildren = node.children.filter((child) => !JSX8.isPaddingSpaces(child));
2552
+ const nonPaddingChildren = node.children.filter((child) => !JSX7.isPaddingSpaces(child));
2607
2553
  if (nonPaddingChildren.length > 1) return;
2608
2554
  if (nonPaddingChildren.length === 0) return context.report({ messageId: "noUselessFragment", node });
2609
2555
  const [first] = nonPaddingChildren;
@@ -2845,7 +2791,7 @@ var prefer_shorthand_boolean_default = createRule({
2845
2791
  create(context) {
2846
2792
  function getReportDescriptor(node) {
2847
2793
  const { value } = node;
2848
- const propName = JSX8.getPropName(node);
2794
+ const propName = JSX7.getPropName(node);
2849
2795
  const hasValueTrue = value?.type === AST_NODE_TYPES.JSXExpressionContainer && value.expression.type === AST_NODE_TYPES.Literal && value.expression.value === true;
2850
2796
  if (!hasValueTrue) return O.none();
2851
2797
  return O.some({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-react-x",
3
- "version": "1.22.2-next.6",
3
+ "version": "1.22.2-next.7",
4
4
  "description": "A set of composable ESLint rules for libraries and frameworks that use React as a UI runtime.",
5
5
  "keywords": [
6
6
  "react",
@@ -49,13 +49,13 @@
49
49
  "string-ts": "^2.2.0",
50
50
  "ts-api-utils": "^2.0.0",
51
51
  "ts-pattern": "^5.6.0",
52
- "@eslint-react/ast": "1.22.2-next.6",
53
- "@eslint-react/core": "1.22.2-next.6",
54
- "@eslint-react/eff": "1.22.2-next.6",
55
- "@eslint-react/jsx": "1.22.2-next.6",
56
- "@eslint-react/shared": "1.22.2-next.6",
57
- "@eslint-react/types": "1.22.2-next.6",
58
- "@eslint-react/var": "1.22.2-next.6"
52
+ "@eslint-react/ast": "1.22.2-next.7",
53
+ "@eslint-react/core": "1.22.2-next.7",
54
+ "@eslint-react/eff": "1.22.2-next.7",
55
+ "@eslint-react/shared": "1.22.2-next.7",
56
+ "@eslint-react/jsx": "1.22.2-next.7",
57
+ "@eslint-react/types": "1.22.2-next.7",
58
+ "@eslint-react/var": "1.22.2-next.7"
59
59
  },
60
60
  "devDependencies": {
61
61
  "@types/react": "^19.0.2",