eslint-plugin-react-x 5.24.0-beta.0 → 5.24.0-beta.2

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 (2) hide show
  1. package/dist/index.js +759 -740
  2. package/package.json +7 -7
package/dist/index.js CHANGED
@@ -145,7 +145,7 @@ const rules$6 = {
145
145
  //#endregion
146
146
  //#region package.json
147
147
  var name$6 = "eslint-plugin-react-x";
148
- var version = "5.24.0-beta.0";
148
+ var version = "5.24.0-beta.2";
149
149
 
150
150
  //#endregion
151
151
  //#region src/utils/create-rule.ts
@@ -1451,9 +1451,9 @@ function isRefLikeName$1(name) {
1451
1451
  return name === "ref" || name.endsWith("Ref");
1452
1452
  }
1453
1453
  function hasRefLikeNameInChain(node) {
1454
- const unwrapped = Extract.unwrap(node);
1455
- if (!Check.isExpression(unwrapped)) return false;
1456
- return Extract.getMemberChain(unwrapped).some((member) => Check.isIdentifier(member) && isRefLikeName$1(member.name));
1454
+ const expr = Extract.unwrap(node);
1455
+ if (!Check.isExpression(expr)) return false;
1456
+ return Extract.getMemberChain(expr).some((member) => Check.isIdentifier(member) && isRefLikeName$1(member.name));
1457
1457
  }
1458
1458
  function isInitializedFromCall(context, node, isCall) {
1459
1459
  const root = Extract.getMemberChain(node).at(0);
@@ -2093,694 +2093,111 @@ function create$41(context) {
2093
2093
  }
2094
2094
 
2095
2095
  //#endregion
2096
- //#region src/rules/no-class-component/no-class-component.ts
2097
- const RULE_NAME$40 = "no-class-component";
2098
- var no_class_component_default = createRule({
2099
- meta: {
2100
- type: "suggestion",
2101
- docs: { description: "Disallows class components except for error boundaries." },
2102
- messages: { default: "Avoid using class components. Use function components instead." },
2103
- schema: []
2104
- },
2105
- name: RULE_NAME$40,
2106
- create: create$40,
2107
- defaultOptions: []
2108
- });
2109
- function create$40(context) {
2110
- if (!context.sourceCode.text.includes("Component")) return {};
2111
- const { api, visitor } = core.getClassComponentCollector(context);
2112
- return merge(visitor, { "Program:exit"(program) {
2113
- for (const { id, name = "anonymous", node: component } of api.getAllComponents(program)) {
2114
- if (component.body.body.some((m) => core.isComponentDidCatch(m) || core.isGetDerivedStateFromError(m))) continue;
2115
- const classToken = context.sourceCode.getFirstToken(component, { filter: (token) => token.value === "class" });
2116
- context.report({
2117
- data: { name },
2118
- loc: (id ?? classToken ?? component).loc,
2119
- messageId: "default",
2120
- node: component
2121
- });
2122
- }
2123
- } });
2124
- }
2125
-
2126
- //#endregion
2127
- //#region src/rules/no-clone-element/no-clone-element.ts
2128
- const RULE_NAME$39 = "no-clone-element";
2129
- var no_clone_element_default = createRule({
2130
- meta: {
2131
- type: "suggestion",
2132
- docs: { description: "Disallows 'cloneElement'." },
2133
- messages: { default: "Using 'cloneElement' is uncommon and can lead to fragile code. Use alternatives instead." },
2134
- schema: []
2135
- },
2136
- name: RULE_NAME$39,
2137
- create: create$39,
2138
- defaultOptions: []
2139
- });
2140
- function create$39(context) {
2141
- return { CallExpression(node) {
2142
- if (core.isCloneElementCall(context, node)) context.report({
2143
- messageId: "default",
2144
- node
2145
- });
2146
- } };
2096
+ //#region ../../.pkgs/eff/dist/index.js
2097
+ function not(predicate) {
2098
+ return (data) => !predicate(data);
2147
2099
  }
2148
-
2149
- //#endregion
2150
- //#region src/rules/no-component-will-mount/no-component-will-mount.ts
2151
- const RULE_NAME$38 = "no-component-will-mount";
2152
- var no_component_will_mount_default = createRule({
2153
- meta: {
2154
- type: "problem",
2155
- docs: { description: "Replaces usage of 'componentWillMount' with 'UNSAFE_componentWillMount'." },
2156
- fixable: "code",
2157
- messages: { default: "[Deprecated] Use 'UNSAFE_componentWillMount' instead." },
2158
- schema: []
2159
- },
2160
- name: RULE_NAME$38,
2161
- create: create$38,
2162
- defaultOptions: []
2163
- });
2164
- function create$38(context) {
2165
- if (!context.sourceCode.text.includes("componentWillMount")) return {};
2166
- const { api, visitor } = core.getClassComponentCollector(context);
2167
- return merge(visitor, { "Program:exit"(program) {
2168
- for (const { node: component } of api.getAllComponents(program)) {
2169
- const { body } = component.body;
2170
- for (const member of body) if (core.isComponentWillMount(member)) context.report({
2171
- fix(fixer) {
2172
- if (!("key" in member)) return null;
2173
- return fixer.replaceText(member.key, "UNSAFE_componentWillMount");
2174
- },
2175
- messageId: "default",
2176
- node: member
2177
- });
2178
- }
2179
- } });
2100
+ function or(a, b) {
2101
+ return (data) => a(data) || b(data);
2180
2102
  }
2181
-
2182
- //#endregion
2183
- //#region src/rules/no-component-will-receive-props/no-component-will-receive-props.ts
2184
- const RULE_NAME$37 = "no-component-will-receive-props";
2185
- var no_component_will_receive_props_default = createRule({
2186
- meta: {
2187
- type: "problem",
2188
- docs: { description: "Replaces usage of 'componentWillReceiveProps' with 'UNSAFE_componentWillReceiveProps'." },
2189
- fixable: "code",
2190
- messages: { default: "[Deprecated] Use 'UNSAFE_componentWillReceiveProps' instead." },
2191
- schema: []
2192
- },
2193
- name: RULE_NAME$37,
2194
- create: create$37,
2195
- defaultOptions: []
2196
- });
2197
- function create$37(context) {
2198
- if (!context.sourceCode.text.includes("componentWillReceiveProps")) return {};
2199
- const { api, visitor } = core.getClassComponentCollector(context);
2200
- return merge(visitor, { "Program:exit"(program) {
2201
- for (const { node: component } of api.getAllComponents(program)) {
2202
- const { body } = component.body;
2203
- for (const member of body) if (core.isComponentWillReceiveProps(member)) context.report({
2204
- fix(fixer) {
2205
- if (!("key" in member)) return null;
2206
- return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps");
2207
- },
2208
- messageId: "default",
2209
- node: member
2210
- });
2211
- }
2212
- } });
2103
+ /**
2104
+ * A function that checks if the passed parameter is a string and narrows its type accordingly.
2105
+ *
2106
+ * @param data - The variable to check.
2107
+ * @returns True if the passed input is a string, false otherwise.
2108
+ * @category guards
2109
+ */
2110
+ function isString(data) {
2111
+ return typeof data === "string";
2213
2112
  }
2214
-
2215
- //#endregion
2216
- //#region src/rules/no-component-will-update/no-component-will-update.ts
2217
- const RULE_NAME$36 = "no-component-will-update";
2218
- var no_component_will_update_default = createRule({
2219
- meta: {
2220
- type: "problem",
2221
- docs: { description: "Replaces usage of 'componentWillUpdate' with 'UNSAFE_componentWillUpdate'." },
2222
- fixable: "code",
2223
- messages: { default: "[Deprecated] Use 'UNSAFE_componentWillUpdate' instead." },
2224
- schema: []
2225
- },
2226
- name: RULE_NAME$36,
2227
- create: create$36,
2228
- defaultOptions: []
2229
- });
2230
- function create$36(context) {
2231
- if (!context.sourceCode.text.includes("componentWillUpdate")) return {};
2232
- const { api, visitor } = core.getClassComponentCollector(context);
2233
- return merge(visitor, { "Program:exit"(program) {
2234
- for (const { node: component } of api.getAllComponents(program)) {
2235
- const { body } = component.body;
2236
- for (const member of body) if (core.isComponentWillUpdate(member)) context.report({
2237
- fix(fixer) {
2238
- if (!("key" in member)) return null;
2239
- return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate");
2240
- },
2241
- messageId: "default",
2242
- node: member
2243
- });
2113
+ /**
2114
+ * Applies a `pipe` method's variadic arguments to an initial value from left
2115
+ * to right.
2116
+ *
2117
+ * **When to use**
2118
+ *
2119
+ * Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
2120
+ * object.
2121
+ *
2122
+ * **Details**
2123
+ *
2124
+ * This helper is intended for implementing `Pipeable.pipe` methods that
2125
+ * receive JavaScript's `arguments` object. With no functions it returns the
2126
+ * original value; otherwise it feeds each result into the next function.
2127
+ *
2128
+ * **Example** (Implementing a pipe method)
2129
+ *
2130
+ * ```ts
2131
+ * import { Pipeable } from "effect"
2132
+ *
2133
+ * class NumberBox {
2134
+ * constructor(readonly value: number) {}
2135
+ *
2136
+ * pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
2137
+ * return Pipeable.pipeArguments(this.value, arguments) as number
2138
+ * }
2139
+ * }
2140
+ *
2141
+ * const result = new NumberBox(5).pipe(
2142
+ * (n) => n + 2,
2143
+ * (n) => n * 3
2144
+ * )
2145
+ * console.log(result) // 21
2146
+ * ```
2147
+ *
2148
+ * @category combinators
2149
+ * @since 2.0.0
2150
+ */
2151
+ const pipeArguments = (self, args) => {
2152
+ switch (args.length) {
2153
+ case 0: return self;
2154
+ case 1: return args[0](self);
2155
+ case 2: return args[1](args[0](self));
2156
+ case 3: return args[2](args[1](args[0](self)));
2157
+ case 4: return args[3](args[2](args[1](args[0](self))));
2158
+ case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
2159
+ case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
2160
+ case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
2161
+ case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
2162
+ case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
2163
+ default: {
2164
+ let ret = self;
2165
+ for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
2166
+ return ret;
2244
2167
  }
2245
- } });
2246
- }
2247
-
2248
- //#endregion
2249
- //#region src/rules/no-context-provider/no-context-provider.ts
2250
- const RULE_NAME$35 = "no-context-provider";
2251
- var no_context_provider_default = createRule({
2252
- meta: {
2253
- type: "suggestion",
2254
- docs: { description: "Replaces usage of '<Context.Provider>' with '<Context>'." },
2255
- fixable: "code",
2256
- hasSuggestions: true,
2257
- messages: {
2258
- default: "In React 19, you can render '<Context>' as a provider instead of '<Context.Provider>'.",
2259
- replace: "Replace '<Context.Provider>' with '<Context>'."
2260
- },
2261
- schema: []
2262
- },
2263
- name: RULE_NAME$35,
2264
- create: create$35,
2265
- defaultOptions: []
2266
- });
2267
- function create$35(context) {
2268
- if (!context.sourceCode.text.includes("Provider")) return {};
2269
- const { version } = getSettingsFromContext(context);
2270
- if (compare(version, "19.0.0", "<")) return {};
2271
- return { JSXElement(node) {
2272
- const parts = getElementFullType(node).split(".");
2273
- const selfName = parts.pop();
2274
- const contextFullName = parts.join(".");
2275
- const contextSelfName = parts.pop();
2276
- if (selfName !== "Provider") return;
2277
- if (contextSelfName == null || !contextSelfName.endsWith("Context")) return;
2278
- context.report({
2279
- messageId: "default",
2280
- node,
2281
- suggest: [{
2282
- fix(fixer) {
2283
- if (!core.isFunctionComponentNameLoose(contextSelfName)) return null;
2284
- const openingElement = node.openingElement;
2285
- const closingElement = node.closingElement;
2286
- if (closingElement == null) return fixer.replaceText(openingElement.name, contextFullName);
2287
- return [fixer.replaceText(openingElement.name, contextFullName), fixer.replaceText(closingElement.name, contextFullName)];
2288
- },
2289
- messageId: "replace"
2290
- }]
2291
- });
2292
- } };
2293
- }
2294
-
2295
- //#endregion
2296
- //#region src/rules/no-create-ref/no-create-ref.ts
2297
- const RULE_NAME$34 = "no-create-ref";
2298
- var no_create_ref_default = createRule({
2299
- meta: {
2300
- type: "suggestion",
2301
- docs: { description: "Disallows 'createRef' in function components and Hooks." },
2302
- messages: { default: "[Deprecated] Use 'useRef' instead." },
2303
- schema: []
2304
- },
2305
- name: RULE_NAME$34,
2306
- create: create$34,
2307
- defaultOptions: []
2308
- });
2309
- function create$34(context) {
2310
- if (!context.sourceCode.text.includes("createRef")) return {};
2311
- const fc = core.getFunctionComponentCollector(context);
2312
- const hc = core.getHookCollector(context);
2313
- const createRefCalls = /* @__PURE__ */ new Set();
2314
- return merge(fc.visitor, hc.visitor, {
2315
- CallExpression(node) {
2316
- if (!core.isCreateRefCall(context, node)) return;
2317
- createRefCalls.add(node);
2318
- },
2319
- "Program:exit"(node) {
2320
- const comps = fc.api.getAllComponents(node);
2321
- const hooks = hc.api.getAllHooks(node);
2322
- const funcs = [...comps, ...hooks];
2323
- for (const call of createRefCalls) {
2324
- if (Traverse.findParent(call, (n) => funcs.some((f) => f.node === n)) == null) continue;
2325
- context.report({
2326
- messageId: "default",
2327
- node: call
2328
- });
2329
- }
2330
- }
2331
- });
2332
- }
2333
-
2334
- //#endregion
2335
- //#region src/rules/no-direct-mutation-state/no-direct-mutation-state.ts
2336
- const RULE_NAME$33 = "no-direct-mutation-state";
2337
- var no_direct_mutation_state_default = createRule({
2338
- meta: {
2339
- type: "problem",
2340
- docs: { description: "Disallows direct mutation of 'this.state'." },
2341
- messages: { default: "Do not mutate state directly. Use 'setState()' instead." },
2342
- schema: []
2343
- },
2344
- name: RULE_NAME$33,
2345
- create: create$33,
2346
- defaultOptions: []
2347
- });
2348
- function isConstructorFunction(node) {
2349
- return Check.isOneOf([AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression])(node) && Check.isPropertyOrMethod(node.parent) && Check.isIdentifier(node.parent.key, "constructor");
2350
- }
2351
- function isAssignmentToThisState(node) {
2352
- const { left } = node;
2353
- let current = Extract.unwrap(left);
2354
- while (current.type === AST_NODE_TYPES.MemberExpression) {
2355
- const object = Extract.unwrap(current.object);
2356
- const property = current.property;
2357
- if (object.type === AST_NODE_TYPES.ThisExpression && Check.isIdentifier(property, "state")) return true;
2358
- current = object;
2359
- }
2360
- return false;
2361
- }
2362
- function create$33(context) {
2363
- return { AssignmentExpression(node) {
2364
- if (!isAssignmentToThisState(node)) return;
2365
- const fact = Traverse.findParent(node, Check.isClass);
2366
- if (fact == null) return;
2367
- if (core.isClassComponent(fact) && context.sourceCode.getScope(node).block !== Traverse.findParent(node, isConstructorFunction)) context.report({
2368
- messageId: "default",
2369
- node
2370
- });
2371
- } };
2372
- }
2373
-
2374
- //#endregion
2375
- //#region src/rules/no-duplicate-key/no-duplicate-key.ts
2376
- const RULE_NAME$32 = "no-duplicate-key";
2377
- var no_duplicate_key_default = createRule({
2378
- meta: {
2379
- type: "problem",
2380
- docs: { description: "Prevents duplicate 'key' props on sibling elements when rendering lists." },
2381
- messages: { default: "The 'key' prop must be unique to its sibling elements." },
2382
- schema: []
2383
- },
2384
- name: RULE_NAME$32,
2385
- create: create$32,
2386
- defaultOptions: []
2387
- });
2388
- function create$32(context) {
2389
- if (!context.sourceCode.text.includes("key=")) return {};
2390
- const keyedEntries = /* @__PURE__ */ new Map();
2391
- function isKeyValueEqual(a, b) {
2392
- const aValue = a.value;
2393
- const bValue = b.value;
2394
- if (aValue == null || bValue == null) return false;
2395
- return Compare.isEqual(aValue, bValue);
2396
2168
  }
2397
- return {
2398
- "JSXAttribute[name.name='key']"(node) {
2399
- const jsxElement = node.parent.parent;
2400
- switch (jsxElement.parent.type) {
2401
- case AST_NODE_TYPES.ArrayExpression:
2402
- case AST_NODE_TYPES.JSXElement:
2403
- case AST_NODE_TYPES.JSXFragment: {
2404
- const root = jsxElement.parent;
2405
- const prevKeys = keyedEntries.get(root)?.keys ?? [];
2406
- keyedEntries.set(root, {
2407
- hasDuplicate: prevKeys.some((prevKey) => isKeyValueEqual(prevKey, node)),
2408
- keys: [...prevKeys, node],
2409
- root: jsxElement.parent
2410
- });
2411
- break;
2412
- }
2413
- default: {
2414
- const call = Traverse.findParent(jsxElement, (n) => {
2415
- if (n.type !== AST_NODE_TYPES.CallExpression) return false;
2416
- const callee = Extract.unwrap(n.callee);
2417
- if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
2418
- return Check.isIdentifier(callee.property, "map");
2419
- });
2420
- const iter = Traverse.findParent(jsxElement, Check.isFunction, (n) => n === call);
2421
- if (!Check.isFunction(iter)) return;
2422
- const arg0 = call?.arguments[0];
2423
- if (call == null || arg0 == null) return;
2424
- if (Extract.unwrap(arg0) !== iter) return;
2425
- keyedEntries.set(call, {
2426
- hasDuplicate: node.value?.type === AST_NODE_TYPES.Literal,
2427
- keys: [node],
2428
- root: call
2429
- });
2430
- }
2431
- }
2432
- },
2433
- "Program:exit"() {
2434
- for (const { hasDuplicate, keys } of keyedEntries.values()) {
2435
- if (!hasDuplicate) continue;
2436
- for (const key of keys) context.report({
2437
- data: { value: context.sourceCode.getText(key) },
2438
- messageId: "default",
2439
- node: key
2440
- });
2441
- }
2442
- }
2443
- };
2444
- }
2445
-
2446
- //#endregion
2447
- //#region src/rules/no-forward-ref/no-forward-ref.ts
2448
- const RULE_NAME$31 = "no-forward-ref";
2449
- var no_forward_ref_default = createRule({
2450
- meta: {
2451
- type: "suggestion",
2452
- docs: { description: "Replaces usage of 'forwardRef' with passing 'ref' as a prop." },
2453
- fixable: "code",
2454
- hasSuggestions: true,
2455
- messages: {
2456
- default: "In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead.",
2457
- replace: "Replace 'forwardRef' with passing 'ref' as a prop."
2458
- },
2459
- schema: []
2460
- },
2461
- name: RULE_NAME$31,
2462
- create: create$31,
2463
- defaultOptions: []
2464
- });
2465
- function create$31(context) {
2466
- if (!context.sourceCode.text.includes("forwardRef")) return {};
2467
- const { version } = getSettingsFromContext(context);
2468
- if (compare(version, "19.0.0", "<")) return {};
2469
- return { CallExpression(node) {
2470
- if (!core.isForwardRefCall(context, node)) return;
2471
- const id = core.getFunctionId(node);
2472
- const suggest = couldFix(context, node) ? [{
2473
- fix: buildFix(context, node),
2474
- messageId: "replace"
2475
- }] : [];
2476
- context.report({
2477
- messageId: "default",
2478
- node: id ?? node,
2479
- suggest
2480
- });
2481
- } };
2482
- }
2169
+ };
2483
2170
  /**
2484
- * Determine whether the given CallExpression can be safely auto-fixed by replacing
2485
- * the usage of `forwardRef` with passing `ref` as a prop
2171
+ * Reusable prototype that implements `Pipeable.pipe`.
2486
2172
  *
2487
- * @param context The rule context object
2488
- * @param node The CallExpression node to check
2489
- * @returns True if the call can be auto-fixed, false otherwise
2173
+ * **When to use**
2174
+ *
2175
+ * Use when classes or object prototypes can reuse this value when they need the
2176
+ * standard pipe implementation backed by `pipeArguments`.
2177
+ *
2178
+ * @category prototypes
2179
+ * @since 3.15.0
2490
2180
  */
2491
- function couldFix(context, node) {
2492
- const { importSource } = getSettingsFromContext(context);
2493
- const initialScope = context.sourceCode.getScope(node);
2494
- const callee = Extract.unwrap(node.callee);
2495
- switch (callee.type) {
2496
- case AST_NODE_TYPES.Identifier: return isInitializedFromReact(callee.name, initialScope, importSource);
2497
- case AST_NODE_TYPES.MemberExpression: return Check.isIdentifier(callee.object) && isInitializedFromReact(callee.object.name, initialScope, importSource);
2498
- default: return false;
2499
- }
2500
- }
2181
+ const Prototype = { pipe() {
2182
+ return pipeArguments(this, arguments);
2183
+ } };
2501
2184
  /**
2502
- * Generates the fix for the `forwardRef` call
2503
- * @param context The rule context
2504
- * @param node The `forwardRef` call expression
2505
- * @returns A fixer function that applies the changes
2185
+ * Provides a base constructor whose instances implement the standard `Pipeable.pipe`
2186
+ * method.
2187
+ *
2188
+ * **When to use**
2189
+ *
2190
+ * Use when you need to define a class that supports Effect-style method
2191
+ * chaining through `.pipe(...)`.
2192
+ *
2193
+ * @category constructors
2194
+ * @since 3.15.0
2506
2195
  */
2507
- function buildFix(context, node) {
2508
- return (fixer) => {
2509
- const [componentNode] = node.arguments;
2510
- if (componentNode == null || !Check.isFunction(componentNode)) return [];
2511
- return [
2512
- fixer.removeRange([node.range[0], componentNode.range[0]]),
2513
- fixer.removeRange([componentNode.range[1], node.range[1]]),
2514
- ...buildFixForComponentProps(context, fixer, componentNode, node.typeArguments?.params ?? [])
2515
- ];
2516
- };
2517
- }
2518
- /**
2519
- * Generates fixes for the component's props and ref arguments
2520
- * @param context The rule context
2521
- * @param fixer The rule fixer
2522
- * @param node The function component node
2523
- * @param typeArguments The type arguments from the `forwardRef` call
2524
- * @returns An array of fixes for the component's signature
2525
- */
2526
- function buildFixForComponentProps(context, fixer, node, typeArguments) {
2527
- const getText = (node) => context.sourceCode.getText(node);
2528
- const [arg0, arg1] = node.params;
2529
- const [typeArg0, typeArg1] = typeArguments;
2530
- if (arg0 == null) {
2531
- const openParen = context.sourceCode.getFirstToken(node, { filter: (t) => t.value === "(" });
2532
- if (openParen == null) return [];
2533
- if (typeArg0 == null || typeArg1 == null) return [];
2534
- const typeArg0Text = getText(typeArg0);
2535
- const typeArg1Text = getText(typeArg1);
2536
- return [fixer.insertTextAfter(openParen, `{ ref }: ${typeArg1Text} & { ref?: React.RefObject<${typeArg0Text} | null> }`)];
2537
- }
2538
- 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(() => null);
2539
- const fixedArg1Text = match(arg1).with(P.nullish, () => "ref").with({
2540
- type: AST_NODE_TYPES.Identifier,
2541
- name: "ref"
2542
- }, () => "ref").with({
2543
- type: AST_NODE_TYPES.Identifier,
2544
- name: P.string
2545
- }, (n) => `ref: ${n.name}`).otherwise(() => null);
2546
- if (fixedArg0Text == null || fixedArg1Text == null) return [];
2547
- if (typeArg0 == null || typeArg1 == null) return [fixer.replaceText(arg0, [
2548
- "{",
2549
- fixedArg1Text + ",",
2550
- fixedArg0Text,
2551
- "}"
2552
- ].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
2553
- const typeArg0Text = getText(typeArg0);
2554
- const typeArg1Text = getText(typeArg1);
2555
- return [fixer.replaceText(arg0, [
2556
- "{",
2557
- fixedArg1Text + ",",
2558
- fixedArg0Text,
2559
- "}:",
2560
- typeArg1Text,
2561
- "&",
2562
- "{",
2563
- `ref?:`,
2564
- `React.RefObject<${typeArg0Text} | null>`,
2565
- "}"
2566
- ].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
2567
- }
2568
-
2569
- //#endregion
2570
- //#region src/utils/create-implicit-prop-listener.ts
2571
- /**
2572
- * Creates a listener that detects JSX spread attributes implicitly passing a given prop
2573
- * (ex: 'children', 'key', 'ref') to a component.
2574
- *
2575
- * For each union constituent of the spread argument's type, the prop is reported unless:
2576
- * 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
2577
- * 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
2578
- *
2579
- * @param context The ESLint rule context
2580
- * @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
2581
- * @param options.name The name of the prop to detect
2582
- * @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
2583
- * @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
2584
- * @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
2585
- * @returns A rule listener reporting on JSX spread attributes
2586
- */
2587
- function createImplicitPropListener(context, options) {
2588
- const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
2589
- const services = ESLintUtils.getParserServices(context, false);
2590
- const checker = services.program.getTypeChecker();
2591
- const getFqn = (symbol) => core.getFullyQualifiedNameEx(checker, symbol).toLowerCase();
2592
- return { JSXSpreadAttribute(node) {
2593
- for (const type of unionConstituents(getConstrainedTypeAtLocation(services, node.argument))) {
2594
- const prop = type.getProperty(name);
2595
- if (prop == null) continue;
2596
- if (isAllowedProp(getFqn(prop))) continue;
2597
- const propType = checker.getTypeOfSymbol(prop);
2598
- const propTypeSymbol = propType.aliasSymbol ?? propType.symbol;
2599
- if (propTypeSymbol != null && isAllowedType(getFqn(propTypeSymbol))) continue;
2600
- onImplicitProp(node);
2601
- }
2602
- } };
2603
- }
2604
-
2605
- //#endregion
2606
- //#region src/rules/no-implicit-children/no-implicit-children.ts
2607
- const RULE_NAME$30 = "no-implicit-children";
2608
- const RE_REACT_CHILDREN_TYPE = /react\.(reactnode|reactelement|reactportal)$/i;
2609
- var no_implicit_children_default = createRule({
2610
- meta: {
2611
- type: "problem",
2612
- docs: { description: "Prevents implicitly passing the 'children' prop to components." },
2613
- messages: { default: "This spread attribute implicitly passes the 'children' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'children' prop, use 'children={value}'." },
2614
- schema: []
2615
- },
2616
- name: RULE_NAME$30,
2617
- create: create$30,
2618
- defaultOptions: []
2619
- });
2620
- function create$30(context) {
2621
- return createImplicitPropListener(context, {
2622
- name: "children",
2623
- isAllowedProp: (fqn) => fqn.endsWith("attributes.children") || fqn.endsWith("propswithchildren.children"),
2624
- isAllowedType: (fqn) => RE_REACT_CHILDREN_TYPE.test(fqn) || fqn === "jsx.element",
2625
- onImplicitProp(node) {
2626
- context.report({
2627
- messageId: "default",
2628
- node
2629
- });
2630
- }
2631
- });
2632
- }
2633
-
2634
- //#endregion
2635
- //#region src/rules/no-implicit-key/no-implicit-key.ts
2636
- const RULE_NAME$29 = "no-implicit-key";
2637
- var no_implicit_key_default = createRule({
2638
- meta: {
2639
- type: "problem",
2640
- docs: { description: "Prevents implicitly passing the 'key' prop to components." },
2641
- messages: { default: "This spread attribute implicitly passes the 'key' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'key' prop, use 'key={value}'." },
2642
- schema: []
2643
- },
2644
- name: RULE_NAME$29,
2645
- create: create$29,
2646
- defaultOptions: []
2647
- });
2648
- function create$29(context) {
2649
- return createImplicitPropListener(context, {
2650
- name: "key",
2651
- isAllowedProp: (fqn) => fqn.endsWith("react.attributes.key"),
2652
- isAllowedType: (fqn) => fqn.endsWith("react.key"),
2653
- onImplicitProp(node) {
2654
- context.report({
2655
- messageId: "default",
2656
- node
2657
- });
2658
- }
2659
- });
2660
- }
2661
-
2662
- //#endregion
2663
- //#region src/rules/no-implicit-ref/no-implicit-ref.ts
2664
- const RULE_NAME$28 = "no-implicit-ref";
2665
- const RE_REACT_REF_TYPE = /react\.(ref|legacyref|refcallback|refobject)$/i;
2666
- var no_implicit_ref_default = createRule({
2667
- meta: {
2668
- type: "problem",
2669
- docs: { description: "Prevents implicitly passing the 'ref' prop to components." },
2670
- messages: { default: "This spread attribute implicitly passes the 'ref' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'ref' prop, use 'ref={value}'." },
2671
- schema: []
2672
- },
2673
- name: RULE_NAME$28,
2674
- create: create$28,
2675
- defaultOptions: []
2676
- });
2677
- function create$28(context) {
2678
- return createImplicitPropListener(context, {
2679
- name: "ref",
2680
- isAllowedProp: (fqn) => fqn.endsWith("attributes.ref"),
2681
- isAllowedType: (fqn) => RE_REACT_REF_TYPE.test(fqn),
2682
- onImplicitProp(node) {
2683
- context.report({
2684
- messageId: "default",
2685
- node
2686
- });
2687
- }
2688
- });
2689
- }
2690
-
2691
- //#endregion
2692
- //#region ../../.pkgs/eff/dist/index.js
2693
- function not(predicate) {
2694
- return (data) => !predicate(data);
2695
- }
2696
- /**
2697
- * Applies a `pipe` method's variadic arguments to an initial value from left
2698
- * to right.
2699
- *
2700
- * **When to use**
2701
- *
2702
- * Use to implement a custom `.pipe(...)` method from JavaScript's `arguments`
2703
- * object.
2704
- *
2705
- * **Details**
2706
- *
2707
- * This helper is intended for implementing `Pipeable.pipe` methods that
2708
- * receive JavaScript's `arguments` object. With no functions it returns the
2709
- * original value; otherwise it feeds each result into the next function.
2710
- *
2711
- * **Example** (Implementing a pipe method)
2712
- *
2713
- * ```ts
2714
- * import { Pipeable } from "effect"
2715
- *
2716
- * class NumberBox {
2717
- * constructor(readonly value: number) {}
2718
- *
2719
- * pipe(..._fns: ReadonlyArray<(value: number) => number>): number {
2720
- * return Pipeable.pipeArguments(this.value, arguments) as number
2721
- * }
2722
- * }
2723
- *
2724
- * const result = new NumberBox(5).pipe(
2725
- * (n) => n + 2,
2726
- * (n) => n * 3
2727
- * )
2728
- * console.log(result) // 21
2729
- * ```
2730
- *
2731
- * @category combinators
2732
- * @since 2.0.0
2733
- */
2734
- const pipeArguments = (self, args) => {
2735
- switch (args.length) {
2736
- case 0: return self;
2737
- case 1: return args[0](self);
2738
- case 2: return args[1](args[0](self));
2739
- case 3: return args[2](args[1](args[0](self)));
2740
- case 4: return args[3](args[2](args[1](args[0](self))));
2741
- case 5: return args[4](args[3](args[2](args[1](args[0](self)))));
2742
- case 6: return args[5](args[4](args[3](args[2](args[1](args[0](self))))));
2743
- case 7: return args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))));
2744
- case 8: return args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self))))))));
2745
- case 9: return args[8](args[7](args[6](args[5](args[4](args[3](args[2](args[1](args[0](self)))))))));
2746
- default: {
2747
- let ret = self;
2748
- for (let i = 0, len = args.length; i < len; i++) ret = args[i](ret);
2749
- return ret;
2750
- }
2751
- }
2752
- };
2753
- /**
2754
- * Reusable prototype that implements `Pipeable.pipe`.
2755
- *
2756
- * **When to use**
2757
- *
2758
- * Use when classes or object prototypes can reuse this value when they need the
2759
- * standard pipe implementation backed by `pipeArguments`.
2760
- *
2761
- * @category prototypes
2762
- * @since 3.15.0
2763
- */
2764
- const Prototype = { pipe() {
2765
- return pipeArguments(this, arguments);
2766
- } };
2767
- /**
2768
- * Provides a base constructor whose instances implement the standard `Pipeable.pipe`
2769
- * method.
2770
- *
2771
- * **When to use**
2772
- *
2773
- * Use when you need to define a class that supports Effect-style method
2774
- * chaining through `.pipe(...)`.
2775
- *
2776
- * @category constructors
2777
- * @since 3.15.0
2778
- */
2779
- const Class = (function() {
2780
- function PipeableBase() {}
2781
- PipeableBase.prototype = Prototype;
2782
- return PipeableBase;
2783
- })();
2196
+ const Class = (function() {
2197
+ function PipeableBase() {}
2198
+ PipeableBase.prototype = Prototype;
2199
+ return PipeableBase;
2200
+ })();
2784
2201
  /**
2785
2202
  * Provides small helpers for defining and reusing TypeScript functions.
2786
2203
  *
@@ -3137,68 +2554,664 @@ function flow(ab, bc, cd, de, ef, fg, gh, hi, ij) {
3137
2554
  * console.log(typeof buildUser) // "function"
3138
2555
  * ```
3139
2556
  *
3140
- * @category utility types
3141
- * @since 2.0.0
2557
+ * @category utility types
2558
+ * @since 2.0.0
2559
+ */
2560
+ const hole = cast(absurd);
2561
+ function getOrInsertComputed(map, key, callback) {
2562
+ if (map.has(key)) return map.get(key);
2563
+ const value = callback(key);
2564
+ map.set(key, value);
2565
+ return value;
2566
+ }
2567
+ /**
2568
+ * Drops the longest prefix of elements from an array that satisfy the given predicate.
2569
+ *
2570
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
2571
+ *
2572
+ * @param pred - The predicate to test each element with.
2573
+ * @returns A new array without the matching prefix.
2574
+ * @example
2575
+ * ```ts
2576
+ * import * as assert from "node:assert"
2577
+ * import { dropWhile, pipe } from "@local/eff"
2578
+ *
2579
+ * // data-first
2580
+ * assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
2581
+ *
2582
+ * // data-last
2583
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
2584
+ * ```
2585
+ * @category array
2586
+ */
2587
+ const dropWhile = dual(2, (xs, pred) => {
2588
+ const len = xs.length;
2589
+ let idx = 0;
2590
+ while (idx < len && pred(xs[idx])) idx++;
2591
+ return xs.slice(idx);
2592
+ });
2593
+ /**
2594
+ * Takes the longest prefix of elements from an array that satisfy the given predicate.
2595
+ *
2596
+ * Supports both data-first and data-last (`pipe`-friendly) call styles.
2597
+ *
2598
+ * @param pred - The predicate to test each element with.
2599
+ * @returns A new array containing only the matching prefix.
2600
+ * @example
2601
+ * ```ts
2602
+ * import * as assert from "node:assert"
2603
+ * import { pipe, takeWhile } from "@local/eff"
2604
+ *
2605
+ * // data-first
2606
+ * assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
2607
+ *
2608
+ * // data-last
2609
+ * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
2610
+ * ```
2611
+ * @category array
2612
+ */
2613
+ const takeWhile = dual(2, (xs, pred) => {
2614
+ const len = xs.length;
2615
+ let idx = 0;
2616
+ while (idx < len && pred(xs[idx])) idx++;
2617
+ return xs.slice(0, idx);
2618
+ });
2619
+
2620
+ //#endregion
2621
+ //#region src/rules/no-class-component/no-class-component.ts
2622
+ const RULE_NAME$40 = "no-class-component";
2623
+ var no_class_component_default = createRule({
2624
+ meta: {
2625
+ type: "suggestion",
2626
+ docs: { description: "Disallows class components except for error boundaries." },
2627
+ messages: { default: "Avoid using class components. Use function components instead." },
2628
+ schema: []
2629
+ },
2630
+ name: RULE_NAME$40,
2631
+ create: create$40,
2632
+ defaultOptions: []
2633
+ });
2634
+ function create$40(context) {
2635
+ if (!context.sourceCode.text.includes("Component")) return {};
2636
+ const { api, visitor } = core.getClassComponentCollector(context);
2637
+ return merge(visitor, { "Program:exit"(program) {
2638
+ for (const { id, name = "anonymous", node: component } of api.getAllComponents(program)) {
2639
+ if (component.body.body.some(or(core.isComponentDidCatch, core.isGetDerivedStateFromError))) continue;
2640
+ const classToken = context.sourceCode.getFirstToken(component, { filter: (token) => token.value === "class" });
2641
+ context.report({
2642
+ data: { name },
2643
+ loc: (id ?? classToken ?? component).loc,
2644
+ messageId: "default",
2645
+ node: component
2646
+ });
2647
+ }
2648
+ } });
2649
+ }
2650
+
2651
+ //#endregion
2652
+ //#region src/rules/no-clone-element/no-clone-element.ts
2653
+ const RULE_NAME$39 = "no-clone-element";
2654
+ var no_clone_element_default = createRule({
2655
+ meta: {
2656
+ type: "suggestion",
2657
+ docs: { description: "Disallows 'cloneElement'." },
2658
+ messages: { default: "Using 'cloneElement' is uncommon and can lead to fragile code. Use alternatives instead." },
2659
+ schema: []
2660
+ },
2661
+ name: RULE_NAME$39,
2662
+ create: create$39,
2663
+ defaultOptions: []
2664
+ });
2665
+ function create$39(context) {
2666
+ return { CallExpression(node) {
2667
+ if (core.isCloneElementCall(context, node)) context.report({
2668
+ messageId: "default",
2669
+ node
2670
+ });
2671
+ } };
2672
+ }
2673
+
2674
+ //#endregion
2675
+ //#region src/rules/no-component-will-mount/no-component-will-mount.ts
2676
+ const RULE_NAME$38 = "no-component-will-mount";
2677
+ var no_component_will_mount_default = createRule({
2678
+ meta: {
2679
+ type: "problem",
2680
+ docs: { description: "Replaces usage of 'componentWillMount' with 'UNSAFE_componentWillMount'." },
2681
+ fixable: "code",
2682
+ messages: { default: "[Deprecated] Use 'UNSAFE_componentWillMount' instead." },
2683
+ schema: []
2684
+ },
2685
+ name: RULE_NAME$38,
2686
+ create: create$38,
2687
+ defaultOptions: []
2688
+ });
2689
+ function create$38(context) {
2690
+ if (!context.sourceCode.text.includes("componentWillMount")) return {};
2691
+ const { api, visitor } = core.getClassComponentCollector(context);
2692
+ return merge(visitor, { "Program:exit"(program) {
2693
+ for (const { node: component } of api.getAllComponents(program)) {
2694
+ const { body } = component.body;
2695
+ for (const member of body) if (core.isComponentWillMount(member)) context.report({
2696
+ fix(fixer) {
2697
+ if (!("key" in member)) return null;
2698
+ return fixer.replaceText(member.key, "UNSAFE_componentWillMount");
2699
+ },
2700
+ messageId: "default",
2701
+ node: member
2702
+ });
2703
+ }
2704
+ } });
2705
+ }
2706
+
2707
+ //#endregion
2708
+ //#region src/rules/no-component-will-receive-props/no-component-will-receive-props.ts
2709
+ const RULE_NAME$37 = "no-component-will-receive-props";
2710
+ var no_component_will_receive_props_default = createRule({
2711
+ meta: {
2712
+ type: "problem",
2713
+ docs: { description: "Replaces usage of 'componentWillReceiveProps' with 'UNSAFE_componentWillReceiveProps'." },
2714
+ fixable: "code",
2715
+ messages: { default: "[Deprecated] Use 'UNSAFE_componentWillReceiveProps' instead." },
2716
+ schema: []
2717
+ },
2718
+ name: RULE_NAME$37,
2719
+ create: create$37,
2720
+ defaultOptions: []
2721
+ });
2722
+ function create$37(context) {
2723
+ if (!context.sourceCode.text.includes("componentWillReceiveProps")) return {};
2724
+ const { api, visitor } = core.getClassComponentCollector(context);
2725
+ return merge(visitor, { "Program:exit"(program) {
2726
+ for (const { node: component } of api.getAllComponents(program)) {
2727
+ const { body } = component.body;
2728
+ for (const member of body) if (core.isComponentWillReceiveProps(member)) context.report({
2729
+ fix(fixer) {
2730
+ if (!("key" in member)) return null;
2731
+ return fixer.replaceText(member.key, "UNSAFE_componentWillReceiveProps");
2732
+ },
2733
+ messageId: "default",
2734
+ node: member
2735
+ });
2736
+ }
2737
+ } });
2738
+ }
2739
+
2740
+ //#endregion
2741
+ //#region src/rules/no-component-will-update/no-component-will-update.ts
2742
+ const RULE_NAME$36 = "no-component-will-update";
2743
+ var no_component_will_update_default = createRule({
2744
+ meta: {
2745
+ type: "problem",
2746
+ docs: { description: "Replaces usage of 'componentWillUpdate' with 'UNSAFE_componentWillUpdate'." },
2747
+ fixable: "code",
2748
+ messages: { default: "[Deprecated] Use 'UNSAFE_componentWillUpdate' instead." },
2749
+ schema: []
2750
+ },
2751
+ name: RULE_NAME$36,
2752
+ create: create$36,
2753
+ defaultOptions: []
2754
+ });
2755
+ function create$36(context) {
2756
+ if (!context.sourceCode.text.includes("componentWillUpdate")) return {};
2757
+ const { api, visitor } = core.getClassComponentCollector(context);
2758
+ return merge(visitor, { "Program:exit"(program) {
2759
+ for (const { node: component } of api.getAllComponents(program)) {
2760
+ const { body } = component.body;
2761
+ for (const member of body) if (core.isComponentWillUpdate(member)) context.report({
2762
+ fix(fixer) {
2763
+ if (!("key" in member)) return null;
2764
+ return fixer.replaceText(member.key, "UNSAFE_componentWillUpdate");
2765
+ },
2766
+ messageId: "default",
2767
+ node: member
2768
+ });
2769
+ }
2770
+ } });
2771
+ }
2772
+
2773
+ //#endregion
2774
+ //#region src/rules/no-context-provider/no-context-provider.ts
2775
+ const RULE_NAME$35 = "no-context-provider";
2776
+ var no_context_provider_default = createRule({
2777
+ meta: {
2778
+ type: "suggestion",
2779
+ docs: { description: "Replaces usage of '<Context.Provider>' with '<Context>'." },
2780
+ fixable: "code",
2781
+ hasSuggestions: true,
2782
+ messages: {
2783
+ default: "In React 19, you can render '<Context>' as a provider instead of '<Context.Provider>'.",
2784
+ replace: "Replace '<Context.Provider>' with '<Context>'."
2785
+ },
2786
+ schema: []
2787
+ },
2788
+ name: RULE_NAME$35,
2789
+ create: create$35,
2790
+ defaultOptions: []
2791
+ });
2792
+ function create$35(context) {
2793
+ if (!context.sourceCode.text.includes("Provider")) return {};
2794
+ const { version } = getSettingsFromContext(context);
2795
+ if (compare(version, "19.0.0", "<")) return {};
2796
+ return { JSXElement(node) {
2797
+ const parts = getElementFullType(node).split(".");
2798
+ const selfName = parts.pop();
2799
+ const contextFullName = parts.join(".");
2800
+ const contextSelfName = parts.pop();
2801
+ if (selfName !== "Provider") return;
2802
+ if (contextSelfName == null || !contextSelfName.endsWith("Context")) return;
2803
+ context.report({
2804
+ messageId: "default",
2805
+ node,
2806
+ suggest: [{
2807
+ fix(fixer) {
2808
+ if (!core.isFunctionComponentNameLoose(contextSelfName)) return null;
2809
+ const openingElement = node.openingElement;
2810
+ const closingElement = node.closingElement;
2811
+ if (closingElement == null) return fixer.replaceText(openingElement.name, contextFullName);
2812
+ return [fixer.replaceText(openingElement.name, contextFullName), fixer.replaceText(closingElement.name, contextFullName)];
2813
+ },
2814
+ messageId: "replace"
2815
+ }]
2816
+ });
2817
+ } };
2818
+ }
2819
+
2820
+ //#endregion
2821
+ //#region src/rules/no-create-ref/no-create-ref.ts
2822
+ const RULE_NAME$34 = "no-create-ref";
2823
+ var no_create_ref_default = createRule({
2824
+ meta: {
2825
+ type: "suggestion",
2826
+ docs: { description: "Disallows 'createRef' in function components and Hooks." },
2827
+ messages: { default: "[Deprecated] Use 'useRef' instead." },
2828
+ schema: []
2829
+ },
2830
+ name: RULE_NAME$34,
2831
+ create: create$34,
2832
+ defaultOptions: []
2833
+ });
2834
+ function create$34(context) {
2835
+ if (!context.sourceCode.text.includes("createRef")) return {};
2836
+ const fc = core.getFunctionComponentCollector(context);
2837
+ const hc = core.getHookCollector(context);
2838
+ const createRefCalls = /* @__PURE__ */ new Set();
2839
+ return merge(fc.visitor, hc.visitor, {
2840
+ CallExpression(node) {
2841
+ if (!core.isCreateRefCall(context, node)) return;
2842
+ createRefCalls.add(node);
2843
+ },
2844
+ "Program:exit"(node) {
2845
+ const comps = fc.api.getAllComponents(node);
2846
+ const hooks = hc.api.getAllHooks(node);
2847
+ const funcs = [...comps, ...hooks];
2848
+ for (const call of createRefCalls) {
2849
+ if (Traverse.findParent(call, (n) => funcs.some((f) => f.node === n)) == null) continue;
2850
+ context.report({
2851
+ messageId: "default",
2852
+ node: call
2853
+ });
2854
+ }
2855
+ }
2856
+ });
2857
+ }
2858
+
2859
+ //#endregion
2860
+ //#region src/rules/no-direct-mutation-state/no-direct-mutation-state.ts
2861
+ const RULE_NAME$33 = "no-direct-mutation-state";
2862
+ var no_direct_mutation_state_default = createRule({
2863
+ meta: {
2864
+ type: "problem",
2865
+ docs: { description: "Disallows direct mutation of 'this.state'." },
2866
+ messages: { default: "Do not mutate state directly. Use 'setState()' instead." },
2867
+ schema: []
2868
+ },
2869
+ name: RULE_NAME$33,
2870
+ create: create$33,
2871
+ defaultOptions: []
2872
+ });
2873
+ function isConstructorFunction(node) {
2874
+ return Check.isOneOf([AST_NODE_TYPES.FunctionDeclaration, AST_NODE_TYPES.FunctionExpression])(node) && Check.isPropertyOrMethod(node.parent) && Check.isIdentifier(node.parent.key, "constructor");
2875
+ }
2876
+ function isAssignmentToThisState(node) {
2877
+ const { left } = node;
2878
+ let current = Extract.unwrap(left);
2879
+ while (current.type === AST_NODE_TYPES.MemberExpression) {
2880
+ const object = Extract.unwrap(current.object);
2881
+ const property = current.property;
2882
+ if (object.type === AST_NODE_TYPES.ThisExpression && Check.isIdentifier(property, "state")) return true;
2883
+ current = object;
2884
+ }
2885
+ return false;
2886
+ }
2887
+ function create$33(context) {
2888
+ return { AssignmentExpression(node) {
2889
+ if (!isAssignmentToThisState(node)) return;
2890
+ const fact = Traverse.findParent(node, Check.isClass);
2891
+ if (fact == null) return;
2892
+ if (core.isClassComponent(fact) && context.sourceCode.getScope(node).block !== Traverse.findParent(node, isConstructorFunction)) context.report({
2893
+ messageId: "default",
2894
+ node
2895
+ });
2896
+ } };
2897
+ }
2898
+
2899
+ //#endregion
2900
+ //#region src/rules/no-duplicate-key/no-duplicate-key.ts
2901
+ const RULE_NAME$32 = "no-duplicate-key";
2902
+ var no_duplicate_key_default = createRule({
2903
+ meta: {
2904
+ type: "problem",
2905
+ docs: { description: "Prevents duplicate 'key' props on sibling elements when rendering lists." },
2906
+ messages: { default: "The 'key' prop must be unique to its sibling elements." },
2907
+ schema: []
2908
+ },
2909
+ name: RULE_NAME$32,
2910
+ create: create$32,
2911
+ defaultOptions: []
2912
+ });
2913
+ function create$32(context) {
2914
+ if (!context.sourceCode.text.includes("key=")) return {};
2915
+ const keyedEntries = /* @__PURE__ */ new Map();
2916
+ function isKeyValueEqual(a, b) {
2917
+ const aValue = a.value;
2918
+ const bValue = b.value;
2919
+ if (aValue == null || bValue == null) return false;
2920
+ return Compare.isEqual(aValue, bValue);
2921
+ }
2922
+ return {
2923
+ "JSXAttribute[name.name='key']"(node) {
2924
+ const jsxElement = node.parent.parent;
2925
+ switch (jsxElement.parent.type) {
2926
+ case AST_NODE_TYPES.ArrayExpression:
2927
+ case AST_NODE_TYPES.JSXElement:
2928
+ case AST_NODE_TYPES.JSXFragment: {
2929
+ const root = jsxElement.parent;
2930
+ const prevKeys = keyedEntries.get(root)?.keys ?? [];
2931
+ keyedEntries.set(root, {
2932
+ hasDuplicate: prevKeys.some((prevKey) => isKeyValueEqual(prevKey, node)),
2933
+ keys: [...prevKeys, node],
2934
+ root: jsxElement.parent
2935
+ });
2936
+ break;
2937
+ }
2938
+ default: {
2939
+ const call = Traverse.findParent(jsxElement, (n) => {
2940
+ if (n.type !== AST_NODE_TYPES.CallExpression) return false;
2941
+ const callee = Extract.unwrap(n.callee);
2942
+ if (callee.type !== AST_NODE_TYPES.MemberExpression) return false;
2943
+ return Check.isIdentifier(callee.property, "map");
2944
+ });
2945
+ const iter = Traverse.findParent(jsxElement, Check.isFunction, (n) => n === call);
2946
+ if (!Check.isFunction(iter)) return;
2947
+ const arg0 = call?.arguments[0];
2948
+ if (call == null || arg0 == null) return;
2949
+ if (Extract.unwrap(arg0) !== iter) return;
2950
+ keyedEntries.set(call, {
2951
+ hasDuplicate: node.value?.type === AST_NODE_TYPES.Literal,
2952
+ keys: [node],
2953
+ root: call
2954
+ });
2955
+ }
2956
+ }
2957
+ },
2958
+ "Program:exit"() {
2959
+ for (const { hasDuplicate, keys } of keyedEntries.values()) {
2960
+ if (!hasDuplicate) continue;
2961
+ for (const key of keys) context.report({
2962
+ data: { value: context.sourceCode.getText(key) },
2963
+ messageId: "default",
2964
+ node: key
2965
+ });
2966
+ }
2967
+ }
2968
+ };
2969
+ }
2970
+
2971
+ //#endregion
2972
+ //#region src/rules/no-forward-ref/no-forward-ref.ts
2973
+ const RULE_NAME$31 = "no-forward-ref";
2974
+ var no_forward_ref_default = createRule({
2975
+ meta: {
2976
+ type: "suggestion",
2977
+ docs: { description: "Replaces usage of 'forwardRef' with passing 'ref' as a prop." },
2978
+ fixable: "code",
2979
+ hasSuggestions: true,
2980
+ messages: {
2981
+ default: "In React 19, 'forwardRef' is no longer necessary. Pass 'ref' as a prop instead.",
2982
+ replace: "Replace 'forwardRef' with passing 'ref' as a prop."
2983
+ },
2984
+ schema: []
2985
+ },
2986
+ name: RULE_NAME$31,
2987
+ create: create$31,
2988
+ defaultOptions: []
2989
+ });
2990
+ function create$31(context) {
2991
+ if (!context.sourceCode.text.includes("forwardRef")) return {};
2992
+ const { version } = getSettingsFromContext(context);
2993
+ if (compare(version, "19.0.0", "<")) return {};
2994
+ return { CallExpression(node) {
2995
+ if (!core.isForwardRefCall(context, node)) return;
2996
+ const id = core.getFunctionId(node);
2997
+ const suggest = couldFix(context, node) ? [{
2998
+ fix: buildFix(context, node),
2999
+ messageId: "replace"
3000
+ }] : [];
3001
+ context.report({
3002
+ messageId: "default",
3003
+ node: id ?? node,
3004
+ suggest
3005
+ });
3006
+ } };
3007
+ }
3008
+ /**
3009
+ * Determine whether the given CallExpression can be safely auto-fixed by replacing
3010
+ * the usage of `forwardRef` with passing `ref` as a prop
3011
+ *
3012
+ * @param context The rule context object
3013
+ * @param node The CallExpression node to check
3014
+ * @returns True if the call can be auto-fixed, false otherwise
3142
3015
  */
3143
- const hole = cast(absurd);
3144
- function getOrInsertComputed(map, key, callback) {
3145
- if (map.has(key)) return map.get(key);
3146
- const value = callback(key);
3147
- map.set(key, value);
3148
- return value;
3016
+ function couldFix(context, node) {
3017
+ const { importSource } = getSettingsFromContext(context);
3018
+ const initialScope = context.sourceCode.getScope(node);
3019
+ const callee = Extract.unwrap(node.callee);
3020
+ switch (callee.type) {
3021
+ case AST_NODE_TYPES.Identifier: return isInitializedFromReact(callee.name, initialScope, importSource);
3022
+ case AST_NODE_TYPES.MemberExpression: return Check.isIdentifier(callee.object) && isInitializedFromReact(callee.object.name, initialScope, importSource);
3023
+ default: return false;
3024
+ }
3149
3025
  }
3150
3026
  /**
3151
- * Drops the longest prefix of elements from an array that satisfy the given predicate.
3152
- *
3153
- * Supports both data-first and data-last (`pipe`-friendly) call styles.
3154
- *
3155
- * @param pred - The predicate to test each element with.
3156
- * @returns A new array without the matching prefix.
3157
- * @example
3158
- * ```ts
3159
- * import * as assert from "node:assert"
3160
- * import { dropWhile, pipe } from "@local/eff"
3161
- *
3162
- * // data-first
3163
- * assert.deepStrictEqual(dropWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [3, 2, 1])
3164
- *
3165
- * // data-last
3166
- * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], dropWhile((n: number) => n < 3)), [3, 2, 1])
3167
- * ```
3168
- * @category array
3027
+ * Generates the fix for the `forwardRef` call
3028
+ * @param context The rule context
3029
+ * @param node The `forwardRef` call expression
3030
+ * @returns A fixer function that applies the changes
3169
3031
  */
3170
- const dropWhile = dual(2, (xs, pred) => {
3171
- const len = xs.length;
3172
- let idx = 0;
3173
- while (idx < len && pred(xs[idx])) idx++;
3174
- return xs.slice(idx);
3175
- });
3032
+ function buildFix(context, node) {
3033
+ return (fixer) => {
3034
+ const [componentNode] = node.arguments;
3035
+ if (componentNode == null || !Check.isFunction(componentNode)) return [];
3036
+ return [
3037
+ fixer.removeRange([node.range[0], componentNode.range[0]]),
3038
+ fixer.removeRange([componentNode.range[1], node.range[1]]),
3039
+ ...buildFixForComponentProps(context, fixer, componentNode, node.typeArguments?.params ?? [])
3040
+ ];
3041
+ };
3042
+ }
3176
3043
  /**
3177
- * Takes the longest prefix of elements from an array that satisfy the given predicate.
3178
- *
3179
- * Supports both data-first and data-last (`pipe`-friendly) call styles.
3180
- *
3181
- * @param pred - The predicate to test each element with.
3182
- * @returns A new array containing only the matching prefix.
3183
- * @example
3184
- * ```ts
3185
- * import * as assert from "node:assert"
3186
- * import { pipe, takeWhile } from "@local/eff"
3044
+ * Generates fixes for the component's props and ref arguments
3045
+ * @param context The rule context
3046
+ * @param fixer The rule fixer
3047
+ * @param node The function component node
3048
+ * @param typeArguments The type arguments from the `forwardRef` call
3049
+ * @returns An array of fixes for the component's signature
3050
+ */
3051
+ function buildFixForComponentProps(context, fixer, node, typeArguments) {
3052
+ const getText = (node) => context.sourceCode.getText(node);
3053
+ const [arg0, arg1] = node.params;
3054
+ const [typeArg0, typeArg1] = typeArguments;
3055
+ if (arg0 == null) {
3056
+ const openParen = context.sourceCode.getFirstToken(node, { filter: (t) => t.value === "(" });
3057
+ if (openParen == null) return [];
3058
+ if (typeArg0 == null || typeArg1 == null) return [];
3059
+ const typeArg0Text = getText(typeArg0);
3060
+ const typeArg1Text = getText(typeArg1);
3061
+ return [fixer.insertTextAfter(openParen, `{ ref }: ${typeArg1Text} & { ref?: React.RefObject<${typeArg0Text} | null> }`)];
3062
+ }
3063
+ 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(() => null);
3064
+ const fixedArg1Text = match(arg1).with(P.nullish, () => "ref").with({
3065
+ type: AST_NODE_TYPES.Identifier,
3066
+ name: "ref"
3067
+ }, () => "ref").with({
3068
+ type: AST_NODE_TYPES.Identifier,
3069
+ name: P.string
3070
+ }, (n) => `ref: ${n.name}`).otherwise(() => null);
3071
+ if (fixedArg0Text == null || fixedArg1Text == null) return [];
3072
+ if (typeArg0 == null || typeArg1 == null) return [fixer.replaceText(arg0, [
3073
+ "{",
3074
+ fixedArg1Text + ",",
3075
+ fixedArg0Text,
3076
+ "}"
3077
+ ].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
3078
+ const typeArg0Text = getText(typeArg0);
3079
+ const typeArg1Text = getText(typeArg1);
3080
+ return [fixer.replaceText(arg0, [
3081
+ "{",
3082
+ fixedArg1Text + ",",
3083
+ fixedArg0Text,
3084
+ "}:",
3085
+ typeArg1Text,
3086
+ "&",
3087
+ "{",
3088
+ `ref?:`,
3089
+ `React.RefObject<${typeArg0Text} | null>`,
3090
+ "}"
3091
+ ].join(" ")), ...arg1 == null ? [] : [fixer.remove(arg1), fixer.removeRange([arg0.range[1], arg1.range[0]])]];
3092
+ }
3093
+
3094
+ //#endregion
3095
+ //#region src/utils/create-implicit-prop-listener.ts
3096
+ /**
3097
+ * Creates a listener that detects JSX spread attributes implicitly passing a given prop
3098
+ * (ex: 'children', 'key', 'ref') to a component.
3187
3099
  *
3188
- * // data-first
3189
- * assert.deepStrictEqual(takeWhile([1, 2, 3, 2, 1], (n: number) => n < 3), [1, 2])
3100
+ * For each union constituent of the spread argument's type, the prop is reported unless:
3101
+ * 1. The prop symbol's fully qualified name is allowed (pass-through of React internally defined props)
3102
+ * 2. The fully qualified name of the prop's type symbol (or its alias) is allowed (React type aliases)
3190
3103
  *
3191
- * // data-last
3192
- * assert.deepStrictEqual(pipe([1, 2, 3, 2, 1], takeWhile((n: number) => n < 3)), [1, 2])
3193
- * ```
3194
- * @category array
3104
+ * @param context The ESLint rule context
3105
+ * @param options The prop name, the allow-list predicates (receive lowercased fully qualified names), and the report callback
3106
+ * @param options.name The name of the prop to detect
3107
+ * @param options.isAllowedProp A predicate that receives the prop symbol's lowercased fully qualified name and returns `true` if it is allowed
3108
+ * @param options.isAllowedType A predicate that receives the prop's type symbol's lowercased fully qualified name and returns `true` if it is allowed
3109
+ * @param options.onImplicitProp A callback invoked with the spread attribute node when an implicit prop is detected
3110
+ * @returns A rule listener reporting on JSX spread attributes
3195
3111
  */
3196
- const takeWhile = dual(2, (xs, pred) => {
3197
- const len = xs.length;
3198
- let idx = 0;
3199
- while (idx < len && pred(xs[idx])) idx++;
3200
- return xs.slice(0, idx);
3112
+ function createImplicitPropListener(context, options) {
3113
+ const { name, isAllowedProp, isAllowedType, onImplicitProp } = options;
3114
+ const services = ESLintUtils.getParserServices(context, false);
3115
+ const checker = services.program.getTypeChecker();
3116
+ const getFqn = (symbol) => core.getFullyQualifiedNameEx(checker, symbol).toLowerCase();
3117
+ return { JSXSpreadAttribute(node) {
3118
+ for (const type of unionConstituents(getConstrainedTypeAtLocation(services, node.argument))) {
3119
+ const prop = type.getProperty(name);
3120
+ if (prop == null) continue;
3121
+ if (isAllowedProp(getFqn(prop))) continue;
3122
+ const propType = checker.getTypeOfSymbol(prop);
3123
+ const propTypeSymbol = propType.aliasSymbol ?? propType.symbol;
3124
+ if (propTypeSymbol != null && isAllowedType(getFqn(propTypeSymbol))) continue;
3125
+ onImplicitProp(node);
3126
+ }
3127
+ } };
3128
+ }
3129
+
3130
+ //#endregion
3131
+ //#region src/rules/no-implicit-children/no-implicit-children.ts
3132
+ const RULE_NAME$30 = "no-implicit-children";
3133
+ const RE_REACT_CHILDREN_TYPE = /react\.(reactnode|reactelement|reactportal)$/i;
3134
+ var no_implicit_children_default = createRule({
3135
+ meta: {
3136
+ type: "problem",
3137
+ docs: { description: "Prevents implicitly passing the 'children' prop to components." },
3138
+ messages: { default: "This spread attribute implicitly passes the 'children' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'children' prop, use 'children={value}'." },
3139
+ schema: []
3140
+ },
3141
+ name: RULE_NAME$30,
3142
+ create: create$30,
3143
+ defaultOptions: []
3144
+ });
3145
+ function create$30(context) {
3146
+ return createImplicitPropListener(context, {
3147
+ name: "children",
3148
+ isAllowedProp: (fqn) => fqn.endsWith("attributes.children") || fqn.endsWith("propswithchildren.children"),
3149
+ isAllowedType: (fqn) => RE_REACT_CHILDREN_TYPE.test(fqn) || fqn === "jsx.element",
3150
+ onImplicitProp(node) {
3151
+ context.report({
3152
+ messageId: "default",
3153
+ node
3154
+ });
3155
+ }
3156
+ });
3157
+ }
3158
+
3159
+ //#endregion
3160
+ //#region src/rules/no-implicit-key/no-implicit-key.ts
3161
+ const RULE_NAME$29 = "no-implicit-key";
3162
+ var no_implicit_key_default = createRule({
3163
+ meta: {
3164
+ type: "problem",
3165
+ docs: { description: "Prevents implicitly passing the 'key' prop to components." },
3166
+ messages: { default: "This spread attribute implicitly passes the 'key' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'key' prop, use 'key={value}'." },
3167
+ schema: []
3168
+ },
3169
+ name: RULE_NAME$29,
3170
+ create: create$29,
3171
+ defaultOptions: []
3172
+ });
3173
+ function create$29(context) {
3174
+ return createImplicitPropListener(context, {
3175
+ name: "key",
3176
+ isAllowedProp: (fqn) => fqn.endsWith("react.attributes.key"),
3177
+ isAllowedType: (fqn) => fqn.endsWith("react.key"),
3178
+ onImplicitProp(node) {
3179
+ context.report({
3180
+ messageId: "default",
3181
+ node
3182
+ });
3183
+ }
3184
+ });
3185
+ }
3186
+
3187
+ //#endregion
3188
+ //#region src/rules/no-implicit-ref/no-implicit-ref.ts
3189
+ const RULE_NAME$28 = "no-implicit-ref";
3190
+ const RE_REACT_REF_TYPE = /react\.(ref|legacyref|refcallback|refobject)$/i;
3191
+ var no_implicit_ref_default = createRule({
3192
+ meta: {
3193
+ type: "problem",
3194
+ docs: { description: "Prevents implicitly passing the 'ref' prop to components." },
3195
+ messages: { default: "This spread attribute implicitly passes the 'ref' prop to a component, this could lead to unexpected behavior. If you intend to pass the 'ref' prop, use 'ref={value}'." },
3196
+ schema: []
3197
+ },
3198
+ name: RULE_NAME$28,
3199
+ create: create$28,
3200
+ defaultOptions: []
3201
3201
  });
3202
+ function create$28(context) {
3203
+ return createImplicitPropListener(context, {
3204
+ name: "ref",
3205
+ isAllowedProp: (fqn) => fqn.endsWith("attributes.ref"),
3206
+ isAllowedType: (fqn) => RE_REACT_REF_TYPE.test(fqn),
3207
+ onImplicitProp(node) {
3208
+ context.report({
3209
+ messageId: "default",
3210
+ node
3211
+ });
3212
+ }
3213
+ });
3214
+ }
3202
3215
 
3203
3216
  //#endregion
3204
3217
  //#region src/rules/no-leaked-conditional-rendering/no-leaked-conditional-rendering.ts
@@ -4358,7 +4371,7 @@ function collectUsedPropKeysOfReference(context, usedPropKeys, ref) {
4358
4371
  function getKeyOfExpression(expr) {
4359
4372
  switch (expr.type) {
4360
4373
  case AST_NODE_TYPES.Identifier: return expr.name;
4361
- case AST_NODE_TYPES.Literal: if (typeof expr.value === "string") return expr.value;
4374
+ case AST_NODE_TYPES.Literal: if (isString(expr.value)) return expr.value;
4362
4375
  }
4363
4376
  return null;
4364
4377
  }
@@ -7872,7 +7885,7 @@ const IDENTIFIER_VISITOR_KEYS = {
7872
7885
  [AST_NODE_TYPES.AssignmentPattern]: ["left", "right"],
7873
7886
  [AST_NODE_TYPES.AwaitExpression]: ["argument"],
7874
7887
  [AST_NODE_TYPES.BinaryExpression]: ["left", "right"],
7875
- [AST_NODE_TYPES.CallExpression]: ["arguments", "callee"],
7888
+ [AST_NODE_TYPES.CallExpression]: ["callee", "arguments"],
7876
7889
  [AST_NODE_TYPES.ChainExpression]: ["expression"],
7877
7890
  [AST_NODE_TYPES.ConditionalExpression]: [
7878
7891
  "test",
@@ -7884,7 +7897,7 @@ const IDENTIFIER_VISITOR_KEYS = {
7884
7897
  [AST_NODE_TYPES.ImportExpression]: ["source"],
7885
7898
  [AST_NODE_TYPES.LogicalExpression]: ["left", "right"],
7886
7899
  [AST_NODE_TYPES.MemberExpression]: ["object", "property"],
7887
- [AST_NODE_TYPES.NewExpression]: ["arguments", "callee"],
7900
+ [AST_NODE_TYPES.NewExpression]: ["callee", "arguments"],
7888
7901
  [AST_NODE_TYPES.ObjectExpression]: ["properties"],
7889
7902
  [AST_NODE_TYPES.ObjectPattern]: ["properties"],
7890
7903
  [AST_NODE_TYPES.Property]: ["value"],
@@ -8140,6 +8153,12 @@ function isArgumentUsingRefValue(context, node) {
8140
8153
  case AST_NODE_TYPES.UpdateExpression: return isUsingRefValue(n.argument);
8141
8154
  case AST_NODE_TYPES.ConditionalExpression: return isUsingRefValue(n.consequent) || isUsingRefValue(n.alternate);
8142
8155
  case AST_NODE_TYPES.SequenceExpression: return n.expressions.some(isUsingRefValue);
8156
+ case AST_NODE_TYPES.TemplateLiteral: return n.expressions.some(isUsingRefValue);
8157
+ case AST_NODE_TYPES.TaggedTemplateExpression: return isUsingRefValue(n.tag) || isUsingRefValue(n.quasi);
8158
+ case AST_NODE_TYPES.ObjectExpression: return n.properties.some(isUsingRefValue);
8159
+ case AST_NODE_TYPES.Property: return isUsingRefValue(n.value);
8160
+ case AST_NODE_TYPES.ArrayExpression: return n.elements.some((element) => element != null && isUsingRefValue(element));
8161
+ case AST_NODE_TYPES.SpreadElement: return isUsingRefValue(n.argument);
8143
8162
  case AST_NODE_TYPES.AssignmentExpression: return isUsingRefValue(n.right);
8144
8163
  default: return false;
8145
8164
  }
@@ -8991,7 +9010,7 @@ const EXPRESSION_VISITOR_KEYS = {
8991
9010
  [AST_NODE_TYPES.AssignmentPattern]: ["left", "right"],
8992
9011
  [AST_NODE_TYPES.AwaitExpression]: ["argument"],
8993
9012
  [AST_NODE_TYPES.BinaryExpression]: ["left", "right"],
8994
- [AST_NODE_TYPES.CallExpression]: ["arguments", "callee"],
9013
+ [AST_NODE_TYPES.CallExpression]: ["callee", "arguments"],
8995
9014
  [AST_NODE_TYPES.ChainExpression]: ["expression"],
8996
9015
  [AST_NODE_TYPES.ConditionalExpression]: [
8997
9016
  "test",
@@ -9014,7 +9033,7 @@ const EXPRESSION_VISITOR_KEYS = {
9014
9033
  [AST_NODE_TYPES.JSXSpreadChild]: ["expression"],
9015
9034
  [AST_NODE_TYPES.LogicalExpression]: ["left", "right"],
9016
9035
  [AST_NODE_TYPES.MemberExpression]: ["object", "property"],
9017
- [AST_NODE_TYPES.NewExpression]: ["arguments", "callee"],
9036
+ [AST_NODE_TYPES.NewExpression]: ["callee", "arguments"],
9018
9037
  [AST_NODE_TYPES.ObjectExpression]: ["properties"],
9019
9038
  [AST_NODE_TYPES.ObjectPattern]: ["properties"],
9020
9039
  [AST_NODE_TYPES.Property]: ["value"],