@conorroberts/utils 0.0.78 → 0.0.82
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.
- package/dist/oxlint/config.json +1 -0
- package/dist/oxlint/index.mjs +391 -150
- package/dist/oxlint/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/oxlint/index.mjs
CHANGED
|
@@ -15,7 +15,7 @@ import { definePlugin, defineRule } from "oxlint";
|
|
|
15
15
|
* @property {boolean} returnsJsx
|
|
16
16
|
*/
|
|
17
17
|
const JSX_NODE_TYPES$2 = new Set(["JSXElement", "JSXFragment"]);
|
|
18
|
-
const FUNCTION_NODE_TYPES$
|
|
18
|
+
const FUNCTION_NODE_TYPES$5 = new Set([
|
|
19
19
|
"FunctionDeclaration",
|
|
20
20
|
"FunctionExpression",
|
|
21
21
|
"ArrowFunctionExpression"
|
|
@@ -24,12 +24,12 @@ const FUNCTION_NODE_TYPES$4 = new Set([
|
|
|
24
24
|
* @param {unknown} node
|
|
25
25
|
* @returns {node is ESTNode & { type: string }}
|
|
26
26
|
*/
|
|
27
|
-
const isNode$
|
|
27
|
+
const isNode$5 = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
28
28
|
/**
|
|
29
29
|
* @param {unknown} node
|
|
30
30
|
* @returns {node is FunctionLikeNode}
|
|
31
31
|
*/
|
|
32
|
-
const isFunctionLike$
|
|
32
|
+
const isFunctionLike$5 = (node) => isNode$5(node) && FUNCTION_NODE_TYPES$5.has(node.type);
|
|
33
33
|
/**
|
|
34
34
|
* @param {unknown} name
|
|
35
35
|
* @returns {name is string}
|
|
@@ -50,13 +50,13 @@ const getFunctionName$2 = (node) => {
|
|
|
50
50
|
if (node.id.type === "Identifier") return node.id.name;
|
|
51
51
|
}
|
|
52
52
|
const parent = node.parent;
|
|
53
|
-
if (!parent || !isNode$
|
|
53
|
+
if (!parent || !isNode$5(parent)) return "";
|
|
54
54
|
if (parent.type === "VariableDeclarator") return parent.id && parent.id.type === "Identifier" ? parent.id.name : "";
|
|
55
55
|
if (parent.type === "AssignmentExpression") return parent.left && parent.left.type === "Identifier" ? parent.left.name : "";
|
|
56
56
|
if (parent.type === "Property" || parent.type === "MethodDefinition") return "";
|
|
57
57
|
if (parent.type === "CallExpression") {
|
|
58
58
|
const callParent = parent.parent;
|
|
59
|
-
if (callParent && isNode$
|
|
59
|
+
if (callParent && isNode$5(callParent)) {
|
|
60
60
|
if (callParent.type === "VariableDeclarator") return callParent.id && callParent.id.type === "Identifier" ? callParent.id.name : "";
|
|
61
61
|
if (callParent.type === "AssignmentExpression") return callParent.left && callParent.left.type === "Identifier" ? callParent.left.name : "";
|
|
62
62
|
}
|
|
@@ -67,25 +67,25 @@ const getFunctionName$2 = (node) => {
|
|
|
67
67
|
* @param {ESTExpression | null | undefined} root
|
|
68
68
|
*/
|
|
69
69
|
const expressionContainsJsx$2 = (root) => {
|
|
70
|
-
if (!root || !isNode$
|
|
70
|
+
if (!root || !isNode$5(root)) return false;
|
|
71
71
|
const stack = [root];
|
|
72
72
|
while (stack.length > 0) {
|
|
73
73
|
const current = stack.pop();
|
|
74
|
-
if (!current || !isNode$
|
|
74
|
+
if (!current || !isNode$5(current)) continue;
|
|
75
75
|
if (JSX_NODE_TYPES$2.has(current.type)) return true;
|
|
76
|
-
if (FUNCTION_NODE_TYPES$
|
|
76
|
+
if (FUNCTION_NODE_TYPES$5.has(current.type) && current !== root) continue;
|
|
77
77
|
for (const key of Object.keys(current)) {
|
|
78
78
|
if (key === "parent") continue;
|
|
79
79
|
const value = current[key];
|
|
80
80
|
if (!value) continue;
|
|
81
81
|
if (Array.isArray(value)) {
|
|
82
|
-
for (const element of value) if (isNode$
|
|
83
|
-
} else if (isNode$
|
|
82
|
+
for (const element of value) if (isNode$5(element)) stack.push(element);
|
|
83
|
+
} else if (isNode$5(value)) stack.push(value);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
return false;
|
|
87
87
|
};
|
|
88
|
-
const rule$
|
|
88
|
+
const rule$14 = defineRule({
|
|
89
89
|
meta: {
|
|
90
90
|
type: "problem",
|
|
91
91
|
docs: {
|
|
@@ -126,20 +126,20 @@ const rule$13 = defineRule({
|
|
|
126
126
|
const fnCtx = currentFunction();
|
|
127
127
|
if (!fnCtx) return;
|
|
128
128
|
const argument = node.argument;
|
|
129
|
-
if (!argument || isFunctionLike$
|
|
129
|
+
if (!argument || isFunctionLike$5(argument)) return;
|
|
130
130
|
if (expressionContainsJsx$2(argument)) fnCtx.returnsJsx = true;
|
|
131
131
|
};
|
|
132
132
|
return {
|
|
133
133
|
FunctionDeclaration(node) {
|
|
134
|
-
if (isFunctionLike$
|
|
134
|
+
if (isFunctionLike$5(node)) enterFunction(node);
|
|
135
135
|
},
|
|
136
136
|
"FunctionDeclaration:exit": exitFunction,
|
|
137
137
|
FunctionExpression(node) {
|
|
138
|
-
if (isFunctionLike$
|
|
138
|
+
if (isFunctionLike$5(node)) enterFunction(node);
|
|
139
139
|
},
|
|
140
140
|
"FunctionExpression:exit": exitFunction,
|
|
141
141
|
ArrowFunctionExpression(node) {
|
|
142
|
-
if (isFunctionLike$
|
|
142
|
+
if (isFunctionLike$5(node)) enterFunction(node);
|
|
143
143
|
},
|
|
144
144
|
"ArrowFunctionExpression:exit": exitFunction,
|
|
145
145
|
ReturnStatement(node) {
|
|
@@ -148,11 +148,11 @@ const rule$13 = defineRule({
|
|
|
148
148
|
};
|
|
149
149
|
}
|
|
150
150
|
});
|
|
151
|
-
const jsxComponentPascalCaseRule = rule$
|
|
151
|
+
const jsxComponentPascalCaseRule = rule$14;
|
|
152
152
|
|
|
153
153
|
//#endregion
|
|
154
154
|
//#region src/oxlint-plugins/no-array-type.js
|
|
155
|
-
const rule$
|
|
155
|
+
const rule$13 = defineRule({
|
|
156
156
|
meta: {
|
|
157
157
|
type: "problem",
|
|
158
158
|
docs: {
|
|
@@ -171,7 +171,7 @@ const rule$12 = defineRule({
|
|
|
171
171
|
} };
|
|
172
172
|
}
|
|
173
173
|
});
|
|
174
|
-
const noArrayTypeRule = rule$
|
|
174
|
+
const noArrayTypeRule = rule$13;
|
|
175
175
|
|
|
176
176
|
//#endregion
|
|
177
177
|
//#region src/oxlint-plugins/no-component-date-instantiation.js
|
|
@@ -190,7 +190,7 @@ const noArrayTypeRule = rule$12;
|
|
|
190
190
|
* @property {boolean} returnsJsx
|
|
191
191
|
* @property {NewExpressionNode[]} dateInstantiations
|
|
192
192
|
*/
|
|
193
|
-
const FUNCTION_NODE_TYPES$
|
|
193
|
+
const FUNCTION_NODE_TYPES$4 = new Set([
|
|
194
194
|
"FunctionDeclaration",
|
|
195
195
|
"FunctionExpression",
|
|
196
196
|
"ArrowFunctionExpression"
|
|
@@ -199,12 +199,12 @@ const FUNCTION_NODE_TYPES$3 = new Set([
|
|
|
199
199
|
* @param {unknown} node
|
|
200
200
|
* @returns {node is ESTNode & { type: string }}
|
|
201
201
|
*/
|
|
202
|
-
const isNode$
|
|
202
|
+
const isNode$4 = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
203
203
|
/**
|
|
204
204
|
* @param {unknown} node
|
|
205
205
|
* @returns {node is FunctionLikeNode}
|
|
206
206
|
*/
|
|
207
|
-
const isFunctionLike$
|
|
207
|
+
const isFunctionLike$4 = (node) => isNode$4(node) && FUNCTION_NODE_TYPES$4.has(node.type);
|
|
208
208
|
/**
|
|
209
209
|
* Check if a function name follows React component naming convention (PascalCase)
|
|
210
210
|
* @param {unknown} name
|
|
@@ -222,7 +222,7 @@ const getFunctionName$1 = (node) => {
|
|
|
222
222
|
if (node.id.type === "Identifier") return node.id.name;
|
|
223
223
|
}
|
|
224
224
|
const parent = node.parent;
|
|
225
|
-
if (!parent || !isNode$
|
|
225
|
+
if (!parent || !isNode$4(parent)) return "";
|
|
226
226
|
if (parent.type === "VariableDeclarator") return parent.id && parent.id.type === "Identifier" ? parent.id.name : "";
|
|
227
227
|
if (parent.type === "AssignmentExpression") return parent.left && parent.left.type === "Identifier" ? parent.left.name : "";
|
|
228
228
|
if (parent.type === "Property" || parent.type === "MethodDefinition") return parent.key && parent.key.type === "Identifier" ? parent.key.name : "";
|
|
@@ -234,7 +234,7 @@ const getFunctionName$1 = (node) => {
|
|
|
234
234
|
* @returns {boolean}
|
|
235
235
|
*/
|
|
236
236
|
const isJSXNode = (node) => {
|
|
237
|
-
if (!node || !isNode$
|
|
237
|
+
if (!node || !isNode$4(node)) return false;
|
|
238
238
|
return node.type === "JSXElement" || node.type === "JSXFragment";
|
|
239
239
|
};
|
|
240
240
|
/**
|
|
@@ -246,7 +246,7 @@ const isDateInstantiation = (node) => {
|
|
|
246
246
|
if (node.callee.type === "Identifier" && node.callee.name === "Date") return true;
|
|
247
247
|
return false;
|
|
248
248
|
};
|
|
249
|
-
const rule$
|
|
249
|
+
const rule$12 = defineRule({
|
|
250
250
|
meta: {
|
|
251
251
|
type: "problem",
|
|
252
252
|
docs: {
|
|
@@ -303,15 +303,15 @@ const rule$11 = defineRule({
|
|
|
303
303
|
};
|
|
304
304
|
return {
|
|
305
305
|
FunctionDeclaration(node) {
|
|
306
|
-
if (isFunctionLike$
|
|
306
|
+
if (isFunctionLike$4(node)) enterFunction(node);
|
|
307
307
|
},
|
|
308
308
|
"FunctionDeclaration:exit": exitFunction,
|
|
309
309
|
FunctionExpression(node) {
|
|
310
|
-
if (isFunctionLike$
|
|
310
|
+
if (isFunctionLike$4(node)) enterFunction(node);
|
|
311
311
|
},
|
|
312
312
|
"FunctionExpression:exit": exitFunction,
|
|
313
313
|
ArrowFunctionExpression(node) {
|
|
314
|
-
if (isFunctionLike$
|
|
314
|
+
if (isFunctionLike$4(node)) enterFunction(node);
|
|
315
315
|
},
|
|
316
316
|
"ArrowFunctionExpression:exit": exitFunction,
|
|
317
317
|
ReturnStatement(node) {
|
|
@@ -323,7 +323,7 @@ const rule$11 = defineRule({
|
|
|
323
323
|
};
|
|
324
324
|
}
|
|
325
325
|
});
|
|
326
|
-
const noComponentDateInstantiationRule = rule$
|
|
326
|
+
const noComponentDateInstantiationRule = rule$12;
|
|
327
327
|
|
|
328
328
|
//#endregion
|
|
329
329
|
//#region src/oxlint-plugins/no-inline-components.js
|
|
@@ -358,7 +358,7 @@ const noComponentDateInstantiationRule = rule$11;
|
|
|
358
358
|
* @property {NestedFunctionRecord[]} nestedJsxChildren
|
|
359
359
|
*/
|
|
360
360
|
const JSX_NODE_TYPES$1 = new Set(["JSXElement", "JSXFragment"]);
|
|
361
|
-
const FUNCTION_NODE_TYPES$
|
|
361
|
+
const FUNCTION_NODE_TYPES$3 = new Set([
|
|
362
362
|
"FunctionDeclaration",
|
|
363
363
|
"FunctionExpression",
|
|
364
364
|
"ArrowFunctionExpression"
|
|
@@ -372,23 +372,23 @@ const isComponentName = (name) => typeof name === "string" && /^[A-Z]/.test(name
|
|
|
372
372
|
* @param {unknown} node
|
|
373
373
|
* @returns {node is ESTNode & { type: string }}
|
|
374
374
|
*/
|
|
375
|
-
const isNode$
|
|
375
|
+
const isNode$3 = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
376
376
|
/**
|
|
377
377
|
* @param {unknown} node
|
|
378
378
|
* @returns {node is FunctionLikeNode}
|
|
379
379
|
*/
|
|
380
|
-
const isFunctionLike$
|
|
380
|
+
const isFunctionLike$3 = (node) => isNode$3(node) && FUNCTION_NODE_TYPES$3.has(node.type);
|
|
381
381
|
/**
|
|
382
382
|
* @param {ESTNode | null | undefined} node
|
|
383
383
|
* @returns {FunctionLikeNode | null}
|
|
384
384
|
*/
|
|
385
|
-
const getEnclosingFunction
|
|
385
|
+
const getEnclosingFunction = (node) => {
|
|
386
386
|
const findFunction = (current) => {
|
|
387
387
|
if (!current) return null;
|
|
388
|
-
if (isFunctionLike$
|
|
389
|
-
return findFunction(isNode$
|
|
388
|
+
if (isFunctionLike$3(current)) return current;
|
|
389
|
+
return findFunction(isNode$3(current) ? current.parent ?? null : null);
|
|
390
390
|
};
|
|
391
|
-
return findFunction(isNode$
|
|
391
|
+
return findFunction(isNode$3(node) ? node.parent ?? null : null);
|
|
392
392
|
};
|
|
393
393
|
/**
|
|
394
394
|
* @param {FunctionLikeNode} node
|
|
@@ -397,16 +397,16 @@ const isFunctionUsedAsJsxProp = (node) => {
|
|
|
397
397
|
const checkJsxProp = (current) => {
|
|
398
398
|
if (!current) return false;
|
|
399
399
|
if (current.type === "JSXAttribute") return true;
|
|
400
|
-
if (isFunctionLike$
|
|
401
|
-
return checkJsxProp(isNode$
|
|
400
|
+
if (isFunctionLike$3(current)) return false;
|
|
401
|
+
return checkJsxProp(isNode$3(current) ? current.parent ?? null : null);
|
|
402
402
|
};
|
|
403
|
-
return checkJsxProp(isNode$
|
|
403
|
+
return checkJsxProp(isNode$3(node) ? node.parent ?? null : null);
|
|
404
404
|
};
|
|
405
405
|
/**
|
|
406
406
|
* @param {FunctionLikeNode} node
|
|
407
407
|
*/
|
|
408
408
|
const isFunctionImmediatelyInvoked = (node) => {
|
|
409
|
-
const parent = isNode$
|
|
409
|
+
const parent = isNode$3(node) ? node.parent ?? null : null;
|
|
410
410
|
if (!parent) return false;
|
|
411
411
|
if (parent.type === "CallExpression" && parent.callee === node) return true;
|
|
412
412
|
return false;
|
|
@@ -415,20 +415,20 @@ const isFunctionImmediatelyInvoked = (node) => {
|
|
|
415
415
|
* @param {ESTExpression | null | undefined} root
|
|
416
416
|
*/
|
|
417
417
|
const expressionContainsJsx$1 = (root) => {
|
|
418
|
-
if (!root || !isNode$
|
|
418
|
+
if (!root || !isNode$3(root)) return false;
|
|
419
419
|
const stack = [root];
|
|
420
420
|
while (stack.length > 0) {
|
|
421
421
|
const current = stack.pop();
|
|
422
|
-
if (!current || !isNode$
|
|
422
|
+
if (!current || !isNode$3(current)) continue;
|
|
423
423
|
if (JSX_NODE_TYPES$1.has(current.type)) return true;
|
|
424
|
-
if (FUNCTION_NODE_TYPES$
|
|
424
|
+
if (FUNCTION_NODE_TYPES$3.has(current.type) && current !== root) continue;
|
|
425
425
|
for (const key of Object.keys(current)) {
|
|
426
426
|
if (key === "parent") continue;
|
|
427
427
|
const value = current[key];
|
|
428
428
|
if (!value) continue;
|
|
429
429
|
if (Array.isArray(value)) {
|
|
430
|
-
for (const element of value) if (isNode$
|
|
431
|
-
} else if (isNode$
|
|
430
|
+
for (const element of value) if (isNode$3(element)) stack.push(element);
|
|
431
|
+
} else if (isNode$3(value)) stack.push(value);
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
434
|
return false;
|
|
@@ -440,7 +440,7 @@ const expressionContainsJsx$1 = (root) => {
|
|
|
440
440
|
const expressionProducesJsx = (root, bindingNames) => {
|
|
441
441
|
if (!root) return false;
|
|
442
442
|
if (expressionContainsJsx$1(root)) return true;
|
|
443
|
-
if (!isNode$
|
|
443
|
+
if (!isNode$3(root)) return false;
|
|
444
444
|
const type = root.type;
|
|
445
445
|
if (type === "Identifier") return bindingNames.has(root.name);
|
|
446
446
|
if (type === "ConditionalExpression") return expressionProducesJsx(root.consequent, bindingNames) || expressionProducesJsx(root.alternate, bindingNames);
|
|
@@ -466,7 +466,7 @@ const expressionProducesJsx = (root, bindingNames) => {
|
|
|
466
466
|
* @param {string[]} names
|
|
467
467
|
*/
|
|
468
468
|
const collectBindingNames = (pattern, names) => {
|
|
469
|
-
if (!pattern || !isNode$
|
|
469
|
+
if (!pattern || !isNode$3(pattern)) return;
|
|
470
470
|
const type = pattern.type;
|
|
471
471
|
if (type === "Identifier") {
|
|
472
472
|
names.push(pattern.name);
|
|
@@ -503,13 +503,13 @@ const getFunctionName = (node) => {
|
|
|
503
503
|
if (node.id.type === "Identifier") return node.id.name;
|
|
504
504
|
}
|
|
505
505
|
const parent = node.parent;
|
|
506
|
-
if (!parent || !isNode$
|
|
506
|
+
if (!parent || !isNode$3(parent)) return "";
|
|
507
507
|
if (parent.type === "VariableDeclarator") return parent.id && parent.id.type === "Identifier" ? parent.id.name : "";
|
|
508
508
|
if (parent.type === "AssignmentExpression") return parent.left && parent.left.type === "Identifier" ? parent.left.name : "";
|
|
509
509
|
if (parent.type === "Property" || parent.type === "MethodDefinition") return parent.key && parent.key.type === "Identifier" ? parent.key.name : "";
|
|
510
510
|
if (parent.type === "CallExpression") {
|
|
511
511
|
const callParent = parent.parent;
|
|
512
|
-
if (callParent && isNode$
|
|
512
|
+
if (callParent && isNode$3(callParent)) {
|
|
513
513
|
if (callParent.type === "VariableDeclarator") return callParent.id && callParent.id.type === "Identifier" ? callParent.id.name : "";
|
|
514
514
|
if (callParent.type === "AssignmentExpression") return callParent.left && callParent.left.type === "Identifier" ? callParent.left.name : "";
|
|
515
515
|
}
|
|
@@ -540,7 +540,7 @@ const createNestedFunctionMessage = (childName, parentName) => `JSX-returning ${
|
|
|
540
540
|
* @param {string} name
|
|
541
541
|
*/
|
|
542
542
|
const createIIFEMessage = (name) => `JSX-returning ${describeNested(name)} should not be declared as an immediately invoked function expression (IIFE). Extract it to a named function at module scope.`;
|
|
543
|
-
const rule$
|
|
543
|
+
const rule$11 = defineRule({
|
|
544
544
|
meta: {
|
|
545
545
|
type: "problem",
|
|
546
546
|
docs: {
|
|
@@ -601,7 +601,7 @@ const rule$10 = defineRule({
|
|
|
601
601
|
const fnCtx = currentFunction();
|
|
602
602
|
if (!fnCtx) return;
|
|
603
603
|
const argument = node.argument;
|
|
604
|
-
if (!argument || isFunctionLike$
|
|
604
|
+
if (!argument || isFunctionLike$3(argument)) return;
|
|
605
605
|
if (expressionProducesJsx(argument, fnCtx.jsxBindingNames)) fnCtx.returnsJsx = true;
|
|
606
606
|
};
|
|
607
607
|
/** @param {VariableDeclaratorNode} node */
|
|
@@ -609,7 +609,7 @@ const rule$10 = defineRule({
|
|
|
609
609
|
const fnCtx = currentFunction();
|
|
610
610
|
if (!fnCtx) return;
|
|
611
611
|
const init = node.init;
|
|
612
|
-
if (!init || isFunctionLike$
|
|
612
|
+
if (!init || isFunctionLike$3(init)) return;
|
|
613
613
|
if (!expressionContainsJsx$1(init)) return;
|
|
614
614
|
const names = [];
|
|
615
615
|
collectBindingNames(node.id, names);
|
|
@@ -625,7 +625,7 @@ const rule$10 = defineRule({
|
|
|
625
625
|
const fnCtx = currentFunction();
|
|
626
626
|
if (!fnCtx) return;
|
|
627
627
|
const right = node.right;
|
|
628
|
-
if (!right || isFunctionLike$
|
|
628
|
+
if (!right || isFunctionLike$3(right)) return;
|
|
629
629
|
if (!expressionContainsJsx$1(right)) return;
|
|
630
630
|
const names = [];
|
|
631
631
|
if (node.left && node.left.type === "Identifier") {
|
|
@@ -662,15 +662,15 @@ const rule$10 = defineRule({
|
|
|
662
662
|
};
|
|
663
663
|
return {
|
|
664
664
|
FunctionDeclaration(node) {
|
|
665
|
-
if (isFunctionLike$
|
|
665
|
+
if (isFunctionLike$3(node)) enterFunction(node);
|
|
666
666
|
},
|
|
667
667
|
"FunctionDeclaration:exit": exitFunction,
|
|
668
668
|
FunctionExpression(node) {
|
|
669
|
-
if (isFunctionLike$
|
|
669
|
+
if (isFunctionLike$3(node)) enterFunction(node);
|
|
670
670
|
},
|
|
671
671
|
"FunctionExpression:exit": exitFunction,
|
|
672
672
|
ArrowFunctionExpression(node) {
|
|
673
|
-
if (isFunctionLike$
|
|
673
|
+
if (isFunctionLike$3(node)) enterFunction(node);
|
|
674
674
|
},
|
|
675
675
|
"ArrowFunctionExpression:exit": exitFunction,
|
|
676
676
|
ReturnStatement(node) {
|
|
@@ -688,7 +688,7 @@ const rule$10 = defineRule({
|
|
|
688
688
|
};
|
|
689
689
|
}
|
|
690
690
|
});
|
|
691
|
-
const noInlineComponentsRule = rule$
|
|
691
|
+
const noInlineComponentsRule = rule$11;
|
|
692
692
|
|
|
693
693
|
//#endregion
|
|
694
694
|
//#region src/oxlint-plugins/no-component-pure-functions.js
|
|
@@ -698,7 +698,7 @@ const noInlineComponentsRule = rule$10;
|
|
|
698
698
|
* @typedef {import("oxlint").ESTree.Function | import("oxlint").ESTree.ArrowFunctionExpression} FunctionLikeNode
|
|
699
699
|
* @typedef {import("oxlint").ESTree.VariableDeclarator} VariableDeclaratorNode
|
|
700
700
|
*/
|
|
701
|
-
const FUNCTION_NODE_TYPES$
|
|
701
|
+
const FUNCTION_NODE_TYPES$2 = new Set([
|
|
702
702
|
"FunctionDeclaration",
|
|
703
703
|
"FunctionExpression",
|
|
704
704
|
"ArrowFunctionExpression"
|
|
@@ -707,18 +707,18 @@ const FUNCTION_NODE_TYPES$1 = new Set([
|
|
|
707
707
|
* @param {unknown} node
|
|
708
708
|
* @returns {node is ESTNode & { type: string }}
|
|
709
709
|
*/
|
|
710
|
-
const isNode$
|
|
710
|
+
const isNode$2 = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
711
711
|
/**
|
|
712
712
|
* @param {unknown} node
|
|
713
713
|
* @returns {node is FunctionLikeNode}
|
|
714
714
|
*/
|
|
715
|
-
const isFunctionLike$
|
|
715
|
+
const isFunctionLike$2 = (node) => isNode$2(node) && FUNCTION_NODE_TYPES$2.has(node.type);
|
|
716
716
|
/**
|
|
717
717
|
* Checks if a node is a React Hook call (e.g., useState, useEffect, useCallback)
|
|
718
718
|
* @param {ESTNode} node
|
|
719
719
|
* @returns {boolean}
|
|
720
720
|
*/
|
|
721
|
-
const isReactHookCall = (node) => {
|
|
721
|
+
const isReactHookCall$1 = (node) => {
|
|
722
722
|
if (node.type !== "CallExpression") return false;
|
|
723
723
|
const callee = node.callee;
|
|
724
724
|
if (!callee) return false;
|
|
@@ -731,21 +731,21 @@ const isReactHookCall = (node) => {
|
|
|
731
731
|
* @param {ESTNode} node
|
|
732
732
|
* @returns {boolean}
|
|
733
733
|
*/
|
|
734
|
-
const containsJSX = (node) => {
|
|
735
|
-
if (!node || !isNode$
|
|
734
|
+
const containsJSX$1 = (node) => {
|
|
735
|
+
if (!node || !isNode$2(node)) return false;
|
|
736
736
|
const stack = [node];
|
|
737
737
|
while (stack.length > 0) {
|
|
738
738
|
const current = stack.pop();
|
|
739
|
-
if (!current || !isNode$
|
|
739
|
+
if (!current || !isNode$2(current)) continue;
|
|
740
740
|
if (current.type === "JSXElement" || current.type === "JSXFragment") return true;
|
|
741
|
-
if (isFunctionLike$
|
|
741
|
+
if (isFunctionLike$2(current) && current !== node) continue;
|
|
742
742
|
for (const key of Object.keys(current)) {
|
|
743
743
|
if (key === "parent") continue;
|
|
744
744
|
const value = current[key];
|
|
745
745
|
if (!value) continue;
|
|
746
746
|
if (Array.isArray(value)) {
|
|
747
|
-
for (const element of value) if (isNode$
|
|
748
|
-
} else if (isNode$
|
|
747
|
+
for (const element of value) if (isNode$2(element)) stack.push(element);
|
|
748
|
+
} else if (isNode$2(value)) stack.push(value);
|
|
749
749
|
}
|
|
750
750
|
}
|
|
751
751
|
return false;
|
|
@@ -757,26 +757,26 @@ const containsJSX = (node) => {
|
|
|
757
757
|
* @param {Set<string>} localNames - Names of local variables and parameters
|
|
758
758
|
* @returns {boolean}
|
|
759
759
|
*/
|
|
760
|
-
const accessesEnclosingScope = (node, localNames) => {
|
|
760
|
+
const accessesEnclosingScope$1 = (node, localNames) => {
|
|
761
761
|
if (!node.body) return false;
|
|
762
762
|
const body = node.body;
|
|
763
|
-
if (body.type !== "BlockStatement") return checkExpressionForScopeAccess(body, localNames);
|
|
764
|
-
return checkNodeForScopeAccess(body, localNames);
|
|
763
|
+
if (body.type !== "BlockStatement") return checkExpressionForScopeAccess$1(body, localNames);
|
|
764
|
+
return checkNodeForScopeAccess$1(body, localNames);
|
|
765
765
|
};
|
|
766
766
|
/**
|
|
767
767
|
* @param {ESTNode} node
|
|
768
768
|
* @param {Set<string>} localNames
|
|
769
769
|
* @returns {boolean}
|
|
770
770
|
*/
|
|
771
|
-
const checkNodeForScopeAccess = (node, localNames) => {
|
|
772
|
-
if (!node || !isNode$
|
|
771
|
+
const checkNodeForScopeAccess$1 = (node, localNames) => {
|
|
772
|
+
if (!node || !isNode$2(node)) return false;
|
|
773
773
|
const stack = [node];
|
|
774
774
|
while (stack.length > 0) {
|
|
775
775
|
const current = stack.pop();
|
|
776
|
-
if (!current || !isNode$
|
|
776
|
+
if (!current || !isNode$2(current)) continue;
|
|
777
777
|
if (current.type === "Identifier") {
|
|
778
778
|
const parent = current.parent;
|
|
779
|
-
if (parent && isNode$
|
|
779
|
+
if (parent && isNode$2(parent) && (parent.type === "VariableDeclarator" || parent.type === "FunctionDeclaration" || parent.type === "Property" && parent.key === current)) continue;
|
|
780
780
|
if (!localNames.has(current.name)) {
|
|
781
781
|
if (!new Set([
|
|
782
782
|
"console",
|
|
@@ -811,14 +811,14 @@ const checkNodeForScopeAccess = (node, localNames) => {
|
|
|
811
811
|
]).has(current.name)) return true;
|
|
812
812
|
}
|
|
813
813
|
}
|
|
814
|
-
if (isFunctionLike$
|
|
814
|
+
if (isFunctionLike$2(current) && current !== node) continue;
|
|
815
815
|
for (const key of Object.keys(current)) {
|
|
816
816
|
if (key === "parent") continue;
|
|
817
817
|
const value = current[key];
|
|
818
818
|
if (!value) continue;
|
|
819
819
|
if (Array.isArray(value)) {
|
|
820
|
-
for (const element of value) if (isNode$
|
|
821
|
-
} else if (isNode$
|
|
820
|
+
for (const element of value) if (isNode$2(element)) stack.push(element);
|
|
821
|
+
} else if (isNode$2(value)) stack.push(value);
|
|
822
822
|
}
|
|
823
823
|
}
|
|
824
824
|
return false;
|
|
@@ -828,19 +828,19 @@ const checkNodeForScopeAccess = (node, localNames) => {
|
|
|
828
828
|
* @param {Set<string>} localNames
|
|
829
829
|
* @returns {boolean}
|
|
830
830
|
*/
|
|
831
|
-
const checkExpressionForScopeAccess = (node, localNames) => {
|
|
832
|
-
return checkNodeForScopeAccess(node, localNames);
|
|
831
|
+
const checkExpressionForScopeAccess$1 = (node, localNames) => {
|
|
832
|
+
return checkNodeForScopeAccess$1(node, localNames);
|
|
833
833
|
};
|
|
834
834
|
/**
|
|
835
835
|
* Collects all local variable names including parameters
|
|
836
836
|
* @param {FunctionLikeNode} node
|
|
837
837
|
* @returns {Set<string>}
|
|
838
838
|
*/
|
|
839
|
-
const collectLocalNames = (node) => {
|
|
839
|
+
const collectLocalNames$1 = (node) => {
|
|
840
840
|
const names = /* @__PURE__ */ new Set();
|
|
841
|
-
if (node.params) for (const param of node.params) collectPatternNames(param, names);
|
|
841
|
+
if (node.params) for (const param of node.params) collectPatternNames$1(param, names);
|
|
842
842
|
if (node.body && node.body.type === "BlockStatement") {
|
|
843
|
-
for (const statement of node.body.body) if (statement.type === "VariableDeclaration") for (const declarator of statement.declarations) collectPatternNames(declarator.id, names);
|
|
843
|
+
for (const statement of node.body.body) if (statement.type === "VariableDeclaration") for (const declarator of statement.declarations) collectPatternNames$1(declarator.id, names);
|
|
844
844
|
}
|
|
845
845
|
return names;
|
|
846
846
|
};
|
|
@@ -848,8 +848,8 @@ const collectLocalNames = (node) => {
|
|
|
848
848
|
* @param {import("oxlint").ESTree.Pattern} pattern
|
|
849
849
|
* @param {Set<string>} names
|
|
850
850
|
*/
|
|
851
|
-
const collectPatternNames = (pattern, names) => {
|
|
852
|
-
if (!pattern || !isNode$
|
|
851
|
+
const collectPatternNames$1 = (pattern, names) => {
|
|
852
|
+
if (!pattern || !isNode$2(pattern)) return;
|
|
853
853
|
if (pattern.type === "Identifier") {
|
|
854
854
|
names.add(pattern.name);
|
|
855
855
|
return;
|
|
@@ -857,24 +857,24 @@ const collectPatternNames = (pattern, names) => {
|
|
|
857
857
|
if (pattern.type === "ArrayPattern") {
|
|
858
858
|
for (const element of pattern.elements) {
|
|
859
859
|
if (!element) continue;
|
|
860
|
-
if (element.type === "RestElement") collectPatternNames(element.argument, names);
|
|
861
|
-
else collectPatternNames(element, names);
|
|
860
|
+
if (element.type === "RestElement") collectPatternNames$1(element.argument, names);
|
|
861
|
+
else collectPatternNames$1(element, names);
|
|
862
862
|
}
|
|
863
863
|
return;
|
|
864
864
|
}
|
|
865
865
|
if (pattern.type === "ObjectPattern") {
|
|
866
866
|
for (const property of pattern.properties) {
|
|
867
867
|
if (!property) continue;
|
|
868
|
-
if (property.type === "Property") collectPatternNames(property.value, names);
|
|
869
|
-
else if (property.type === "RestElement") collectPatternNames(property.argument, names);
|
|
868
|
+
if (property.type === "Property") collectPatternNames$1(property.value, names);
|
|
869
|
+
else if (property.type === "RestElement") collectPatternNames$1(property.argument, names);
|
|
870
870
|
}
|
|
871
871
|
return;
|
|
872
872
|
}
|
|
873
873
|
if (pattern.type === "AssignmentPattern") {
|
|
874
|
-
collectPatternNames(pattern.left, names);
|
|
874
|
+
collectPatternNames$1(pattern.left, names);
|
|
875
875
|
return;
|
|
876
876
|
}
|
|
877
|
-
if (pattern.type === "RestElement") collectPatternNames(pattern.argument, names);
|
|
877
|
+
if (pattern.type === "RestElement") collectPatternNames$1(pattern.argument, names);
|
|
878
878
|
};
|
|
879
879
|
/**
|
|
880
880
|
* Checks if a function is likely a React component
|
|
@@ -883,28 +883,28 @@ const collectPatternNames = (pattern, names) => {
|
|
|
883
883
|
*/
|
|
884
884
|
const isReactComponent = (node) => {
|
|
885
885
|
if (!isComponentName(getFunctionName(node))) return false;
|
|
886
|
-
return containsJSX(node) || containsHooks(node);
|
|
886
|
+
return containsJSX$1(node) || containsHooks$1(node);
|
|
887
887
|
};
|
|
888
888
|
/**
|
|
889
889
|
* Checks if a function contains React Hook calls
|
|
890
890
|
* @param {FunctionLikeNode} node
|
|
891
891
|
* @returns {boolean}
|
|
892
892
|
*/
|
|
893
|
-
const containsHooks = (node) => {
|
|
893
|
+
const containsHooks$1 = (node) => {
|
|
894
894
|
if (!node.body) return false;
|
|
895
895
|
const stack = [node.body];
|
|
896
896
|
while (stack.length > 0) {
|
|
897
897
|
const current = stack.pop();
|
|
898
|
-
if (!current || !isNode$
|
|
899
|
-
if (isReactHookCall(current)) return true;
|
|
900
|
-
if (isFunctionLike$
|
|
898
|
+
if (!current || !isNode$2(current)) continue;
|
|
899
|
+
if (isReactHookCall$1(current)) return true;
|
|
900
|
+
if (isFunctionLike$2(current) && current !== node.body) continue;
|
|
901
901
|
for (const key of Object.keys(current)) {
|
|
902
902
|
if (key === "parent") continue;
|
|
903
903
|
const value = current[key];
|
|
904
904
|
if (!value) continue;
|
|
905
905
|
if (Array.isArray(value)) {
|
|
906
|
-
for (const element of value) if (isNode$
|
|
907
|
-
} else if (isNode$
|
|
906
|
+
for (const element of value) if (isNode$2(element)) stack.push(element);
|
|
907
|
+
} else if (isNode$2(value)) stack.push(value);
|
|
908
908
|
}
|
|
909
909
|
}
|
|
910
910
|
return false;
|
|
@@ -914,10 +914,10 @@ const containsHooks = (node) => {
|
|
|
914
914
|
* @param {FunctionLikeNode} node
|
|
915
915
|
* @returns {boolean}
|
|
916
916
|
*/
|
|
917
|
-
const isWrappedInHook = (node) => {
|
|
917
|
+
const isWrappedInHook$1 = (node) => {
|
|
918
918
|
const parent = node.parent;
|
|
919
|
-
if (!parent || !isNode$
|
|
920
|
-
if (parent.type === "CallExpression" && isReactHookCall(parent)) return true;
|
|
919
|
+
if (!parent || !isNode$2(parent)) return false;
|
|
920
|
+
if (parent.type === "CallExpression" && isReactHookCall$1(parent)) return true;
|
|
921
921
|
return false;
|
|
922
922
|
};
|
|
923
923
|
/**
|
|
@@ -925,14 +925,14 @@ const isWrappedInHook = (node) => {
|
|
|
925
925
|
* @param {FunctionLikeNode} node
|
|
926
926
|
* @returns {boolean}
|
|
927
927
|
*/
|
|
928
|
-
const isPureFunction = (node) => {
|
|
929
|
-
if (containsJSX(node)) return false;
|
|
930
|
-
if (containsHooks(node)) return false;
|
|
931
|
-
if (isWrappedInHook(node)) return false;
|
|
932
|
-
if (accessesEnclosingScope(node, collectLocalNames(node))) return false;
|
|
928
|
+
const isPureFunction$1 = (node) => {
|
|
929
|
+
if (containsJSX$1(node)) return false;
|
|
930
|
+
if (containsHooks$1(node)) return false;
|
|
931
|
+
if (isWrappedInHook$1(node)) return false;
|
|
932
|
+
if (accessesEnclosingScope$1(node, collectLocalNames$1(node))) return false;
|
|
933
933
|
return true;
|
|
934
934
|
};
|
|
935
|
-
const rule$
|
|
935
|
+
const rule$10 = defineRule({
|
|
936
936
|
meta: {
|
|
937
937
|
type: "suggestion",
|
|
938
938
|
docs: {
|
|
@@ -947,11 +947,11 @@ const rule$9 = defineRule({
|
|
|
947
947
|
*/
|
|
948
948
|
const handleVariableDeclarator = (node) => {
|
|
949
949
|
const init = node.init;
|
|
950
|
-
if (!init || !isFunctionLike$
|
|
951
|
-
const enclosingFunction = getEnclosingFunction
|
|
950
|
+
if (!init || !isFunctionLike$2(init)) return;
|
|
951
|
+
const enclosingFunction = getEnclosingFunction(node);
|
|
952
952
|
if (!enclosingFunction) return;
|
|
953
953
|
if (!isReactComponent(enclosingFunction)) return;
|
|
954
|
-
if (!isPureFunction(init)) return;
|
|
954
|
+
if (!isPureFunction$1(init)) return;
|
|
955
955
|
const functionName = node.id && node.id.type === "Identifier" ? node.id.name : "this function";
|
|
956
956
|
const componentName = getFunctionName(enclosingFunction);
|
|
957
957
|
context.report({
|
|
@@ -964,12 +964,12 @@ const rule$9 = defineRule({
|
|
|
964
964
|
} };
|
|
965
965
|
}
|
|
966
966
|
});
|
|
967
|
-
const noComponentPureFunctionsRule = rule$
|
|
967
|
+
const noComponentPureFunctionsRule = rule$10;
|
|
968
968
|
|
|
969
969
|
//#endregion
|
|
970
970
|
//#region src/oxlint-plugins/no-delete.js
|
|
971
971
|
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
972
|
-
const rule$
|
|
972
|
+
const rule$9 = defineRule({
|
|
973
973
|
meta: {
|
|
974
974
|
type: "problem",
|
|
975
975
|
docs: {
|
|
@@ -988,7 +988,7 @@ const rule$8 = defineRule({
|
|
|
988
988
|
} };
|
|
989
989
|
}
|
|
990
990
|
});
|
|
991
|
-
const noDeleteRule = rule$
|
|
991
|
+
const noDeleteRule = rule$9;
|
|
992
992
|
|
|
993
993
|
//#endregion
|
|
994
994
|
//#region src/oxlint-plugins/no-emoji.js
|
|
@@ -1018,7 +1018,7 @@ const getEmojiPreview = (text) => {
|
|
|
1018
1018
|
const preview = uniqueEmojis.slice(0, 3).join(" ");
|
|
1019
1019
|
return uniqueEmojis.length > 3 ? `${preview} ...` : preview;
|
|
1020
1020
|
};
|
|
1021
|
-
const rule$
|
|
1021
|
+
const rule$8 = defineRule({
|
|
1022
1022
|
meta: {
|
|
1023
1023
|
type: "problem",
|
|
1024
1024
|
docs: {
|
|
@@ -1069,12 +1069,12 @@ const rule$7 = defineRule({
|
|
|
1069
1069
|
};
|
|
1070
1070
|
}
|
|
1071
1071
|
});
|
|
1072
|
-
const noEmojiRule = rule$
|
|
1072
|
+
const noEmojiRule = rule$8;
|
|
1073
1073
|
|
|
1074
1074
|
//#endregion
|
|
1075
1075
|
//#region src/oxlint-plugins/no-finally.js
|
|
1076
1076
|
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
1077
|
-
const rule$
|
|
1077
|
+
const rule$7 = defineRule({
|
|
1078
1078
|
meta: {
|
|
1079
1079
|
type: "problem",
|
|
1080
1080
|
docs: {
|
|
@@ -1093,12 +1093,12 @@ const rule$6 = defineRule({
|
|
|
1093
1093
|
} };
|
|
1094
1094
|
}
|
|
1095
1095
|
});
|
|
1096
|
-
const noFinallyRule = rule$
|
|
1096
|
+
const noFinallyRule = rule$7;
|
|
1097
1097
|
|
|
1098
1098
|
//#endregion
|
|
1099
1099
|
//#region src/oxlint-plugins/no-iife.js
|
|
1100
1100
|
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
1101
|
-
const rule$
|
|
1101
|
+
const rule$6 = defineRule({
|
|
1102
1102
|
meta: {
|
|
1103
1103
|
type: "problem",
|
|
1104
1104
|
docs: {
|
|
@@ -1118,7 +1118,274 @@ const rule$5 = defineRule({
|
|
|
1118
1118
|
} };
|
|
1119
1119
|
}
|
|
1120
1120
|
});
|
|
1121
|
-
const noIifeRule = rule$
|
|
1121
|
+
const noIifeRule = rule$6;
|
|
1122
|
+
|
|
1123
|
+
//#endregion
|
|
1124
|
+
//#region src/oxlint-plugins/no-nested-pure-functions.js
|
|
1125
|
+
/**
|
|
1126
|
+
* @typedef {import("oxlint").Context} RuleContext
|
|
1127
|
+
* @typedef {import("oxlint").ESTree.Node} ESTNode
|
|
1128
|
+
* @typedef {import("oxlint").ESTree.Function | import("oxlint").ESTree.ArrowFunctionExpression} FunctionLikeNode
|
|
1129
|
+
* @typedef {import("oxlint").ESTree.VariableDeclarator} VariableDeclaratorNode
|
|
1130
|
+
*/
|
|
1131
|
+
const FUNCTION_NODE_TYPES$1 = new Set([
|
|
1132
|
+
"FunctionDeclaration",
|
|
1133
|
+
"FunctionExpression",
|
|
1134
|
+
"ArrowFunctionExpression"
|
|
1135
|
+
]);
|
|
1136
|
+
/**
|
|
1137
|
+
* @param {unknown} node
|
|
1138
|
+
* @returns {node is ESTNode & { type: string }}
|
|
1139
|
+
*/
|
|
1140
|
+
const isNode$1 = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
1141
|
+
/**
|
|
1142
|
+
* @param {unknown} node
|
|
1143
|
+
* @returns {node is FunctionLikeNode}
|
|
1144
|
+
*/
|
|
1145
|
+
const isFunctionLike$1 = (node) => isNode$1(node) && FUNCTION_NODE_TYPES$1.has(node.type);
|
|
1146
|
+
/**
|
|
1147
|
+
* Checks if a node is a React Hook call (e.g., useState, useEffect, useCallback)
|
|
1148
|
+
* @param {ESTNode} node
|
|
1149
|
+
* @returns {boolean}
|
|
1150
|
+
*/
|
|
1151
|
+
const isReactHookCall = (node) => {
|
|
1152
|
+
if (node.type !== "CallExpression") return false;
|
|
1153
|
+
const callee = node.callee;
|
|
1154
|
+
if (!callee) return false;
|
|
1155
|
+
if (callee.type === "Identifier" && callee.name.startsWith("use")) return true;
|
|
1156
|
+
if (callee.type === "MemberExpression" && callee.object && callee.object.type === "Identifier" && callee.object.name === "React" && callee.property && callee.property.type === "Identifier" && callee.property.name.startsWith("use")) return true;
|
|
1157
|
+
return false;
|
|
1158
|
+
};
|
|
1159
|
+
/**
|
|
1160
|
+
* Checks if a node contains JSX
|
|
1161
|
+
* @param {ESTNode} node
|
|
1162
|
+
* @returns {boolean}
|
|
1163
|
+
*/
|
|
1164
|
+
const containsJSX = (node) => {
|
|
1165
|
+
if (!node || !isNode$1(node)) return false;
|
|
1166
|
+
const stack = [node];
|
|
1167
|
+
while (stack.length > 0) {
|
|
1168
|
+
const current = stack.pop();
|
|
1169
|
+
if (!current || !isNode$1(current)) continue;
|
|
1170
|
+
if (current.type === "JSXElement" || current.type === "JSXFragment") return true;
|
|
1171
|
+
if (isFunctionLike$1(current) && current !== node) continue;
|
|
1172
|
+
for (const key of Object.keys(current)) {
|
|
1173
|
+
if (key === "parent") continue;
|
|
1174
|
+
const value = current[key];
|
|
1175
|
+
if (!value) continue;
|
|
1176
|
+
if (Array.isArray(value)) {
|
|
1177
|
+
for (const element of value) if (isNode$1(element)) stack.push(element);
|
|
1178
|
+
} else if (isNode$1(value)) stack.push(value);
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
return false;
|
|
1182
|
+
};
|
|
1183
|
+
/**
|
|
1184
|
+
* Checks if a function accesses variables from its enclosing scope
|
|
1185
|
+
* (excluding function parameters and locally declared variables)
|
|
1186
|
+
* @param {FunctionLikeNode} node
|
|
1187
|
+
* @param {Set<string>} localNames - Names of local variables and parameters
|
|
1188
|
+
* @returns {boolean}
|
|
1189
|
+
*/
|
|
1190
|
+
const accessesEnclosingScope = (node, localNames) => {
|
|
1191
|
+
if (!node.body) return false;
|
|
1192
|
+
const body = node.body;
|
|
1193
|
+
if (body.type !== "BlockStatement") return checkExpressionForScopeAccess(body, localNames);
|
|
1194
|
+
return checkNodeForScopeAccess(body, localNames);
|
|
1195
|
+
};
|
|
1196
|
+
/**
|
|
1197
|
+
* @param {ESTNode} node
|
|
1198
|
+
* @param {Set<string>} localNames
|
|
1199
|
+
* @returns {boolean}
|
|
1200
|
+
*/
|
|
1201
|
+
const checkNodeForScopeAccess = (node, localNames) => {
|
|
1202
|
+
if (!node || !isNode$1(node)) return false;
|
|
1203
|
+
const stack = [node];
|
|
1204
|
+
while (stack.length > 0) {
|
|
1205
|
+
const current = stack.pop();
|
|
1206
|
+
if (!current || !isNode$1(current)) continue;
|
|
1207
|
+
if (current.type === "Identifier") {
|
|
1208
|
+
const parent = current.parent;
|
|
1209
|
+
if (parent && isNode$1(parent) && (parent.type === "VariableDeclarator" || parent.type === "FunctionDeclaration" || parent.type === "Property" && parent.key === current)) continue;
|
|
1210
|
+
if (!localNames.has(current.name)) {
|
|
1211
|
+
if (!new Set([
|
|
1212
|
+
"console",
|
|
1213
|
+
"Math",
|
|
1214
|
+
"Date",
|
|
1215
|
+
"JSON",
|
|
1216
|
+
"Object",
|
|
1217
|
+
"Array",
|
|
1218
|
+
"String",
|
|
1219
|
+
"Number",
|
|
1220
|
+
"Boolean",
|
|
1221
|
+
"parseInt",
|
|
1222
|
+
"parseFloat",
|
|
1223
|
+
"isNaN",
|
|
1224
|
+
"isFinite",
|
|
1225
|
+
"undefined",
|
|
1226
|
+
"null",
|
|
1227
|
+
"true",
|
|
1228
|
+
"false",
|
|
1229
|
+
"Infinity",
|
|
1230
|
+
"NaN",
|
|
1231
|
+
"Map",
|
|
1232
|
+
"Set",
|
|
1233
|
+
"WeakMap",
|
|
1234
|
+
"WeakSet",
|
|
1235
|
+
"Promise",
|
|
1236
|
+
"Symbol",
|
|
1237
|
+
"Error",
|
|
1238
|
+
"TypeError",
|
|
1239
|
+
"ReferenceError",
|
|
1240
|
+
"SyntaxError"
|
|
1241
|
+
]).has(current.name)) return true;
|
|
1242
|
+
}
|
|
1243
|
+
}
|
|
1244
|
+
if (isFunctionLike$1(current) && current !== node) continue;
|
|
1245
|
+
for (const key of Object.keys(current)) {
|
|
1246
|
+
if (key === "parent") continue;
|
|
1247
|
+
const value = current[key];
|
|
1248
|
+
if (!value) continue;
|
|
1249
|
+
if (Array.isArray(value)) {
|
|
1250
|
+
for (const element of value) if (isNode$1(element)) stack.push(element);
|
|
1251
|
+
} else if (isNode$1(value)) stack.push(value);
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
return false;
|
|
1255
|
+
};
|
|
1256
|
+
/**
|
|
1257
|
+
* @param {ESTNode} node
|
|
1258
|
+
* @param {Set<string>} localNames
|
|
1259
|
+
* @returns {boolean}
|
|
1260
|
+
*/
|
|
1261
|
+
const checkExpressionForScopeAccess = (node, localNames) => {
|
|
1262
|
+
return checkNodeForScopeAccess(node, localNames);
|
|
1263
|
+
};
|
|
1264
|
+
/**
|
|
1265
|
+
* Collects all local variable names including parameters
|
|
1266
|
+
* @param {FunctionLikeNode} node
|
|
1267
|
+
* @returns {Set<string>}
|
|
1268
|
+
*/
|
|
1269
|
+
const collectLocalNames = (node) => {
|
|
1270
|
+
const names = /* @__PURE__ */ new Set();
|
|
1271
|
+
if (node.params) for (const param of node.params) collectPatternNames(param, names);
|
|
1272
|
+
if (node.body && node.body.type === "BlockStatement") {
|
|
1273
|
+
for (const statement of node.body.body) if (statement.type === "VariableDeclaration") for (const declarator of statement.declarations) collectPatternNames(declarator.id, names);
|
|
1274
|
+
}
|
|
1275
|
+
return names;
|
|
1276
|
+
};
|
|
1277
|
+
/**
|
|
1278
|
+
* @param {import("oxlint").ESTree.Pattern} pattern
|
|
1279
|
+
* @param {Set<string>} names
|
|
1280
|
+
*/
|
|
1281
|
+
const collectPatternNames = (pattern, names) => {
|
|
1282
|
+
if (!pattern || !isNode$1(pattern)) return;
|
|
1283
|
+
if (pattern.type === "Identifier") {
|
|
1284
|
+
names.add(pattern.name);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
if (pattern.type === "ArrayPattern") {
|
|
1288
|
+
for (const element of pattern.elements) {
|
|
1289
|
+
if (!element) continue;
|
|
1290
|
+
if (element.type === "RestElement") collectPatternNames(element.argument, names);
|
|
1291
|
+
else collectPatternNames(element, names);
|
|
1292
|
+
}
|
|
1293
|
+
return;
|
|
1294
|
+
}
|
|
1295
|
+
if (pattern.type === "ObjectPattern") {
|
|
1296
|
+
for (const property of pattern.properties) {
|
|
1297
|
+
if (!property) continue;
|
|
1298
|
+
if (property.type === "Property") collectPatternNames(property.value, names);
|
|
1299
|
+
else if (property.type === "RestElement") collectPatternNames(property.argument, names);
|
|
1300
|
+
}
|
|
1301
|
+
return;
|
|
1302
|
+
}
|
|
1303
|
+
if (pattern.type === "AssignmentPattern") {
|
|
1304
|
+
collectPatternNames(pattern.left, names);
|
|
1305
|
+
return;
|
|
1306
|
+
}
|
|
1307
|
+
if (pattern.type === "RestElement") collectPatternNames(pattern.argument, names);
|
|
1308
|
+
};
|
|
1309
|
+
/**
|
|
1310
|
+
* Checks if a function contains React Hook calls
|
|
1311
|
+
* @param {FunctionLikeNode} node
|
|
1312
|
+
* @returns {boolean}
|
|
1313
|
+
*/
|
|
1314
|
+
const containsHooks = (node) => {
|
|
1315
|
+
if (!node.body) return false;
|
|
1316
|
+
const stack = [node.body];
|
|
1317
|
+
while (stack.length > 0) {
|
|
1318
|
+
const current = stack.pop();
|
|
1319
|
+
if (!current || !isNode$1(current)) continue;
|
|
1320
|
+
if (isReactHookCall(current)) return true;
|
|
1321
|
+
if (isFunctionLike$1(current) && current !== node.body) continue;
|
|
1322
|
+
for (const key of Object.keys(current)) {
|
|
1323
|
+
if (key === "parent") continue;
|
|
1324
|
+
const value = current[key];
|
|
1325
|
+
if (!value) continue;
|
|
1326
|
+
if (Array.isArray(value)) {
|
|
1327
|
+
for (const element of value) if (isNode$1(element)) stack.push(element);
|
|
1328
|
+
} else if (isNode$1(value)) stack.push(value);
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
return false;
|
|
1332
|
+
};
|
|
1333
|
+
/**
|
|
1334
|
+
* Checks if a function is wrapped in a React Hook (useCallback, useMemo, etc.)
|
|
1335
|
+
* @param {FunctionLikeNode} node
|
|
1336
|
+
* @returns {boolean}
|
|
1337
|
+
*/
|
|
1338
|
+
const isWrappedInHook = (node) => {
|
|
1339
|
+
const parent = node.parent;
|
|
1340
|
+
if (!parent || !isNode$1(parent)) return false;
|
|
1341
|
+
if (parent.type === "CallExpression" && isReactHookCall(parent)) return true;
|
|
1342
|
+
return false;
|
|
1343
|
+
};
|
|
1344
|
+
/**
|
|
1345
|
+
* Checks if a function is pure (doesn't access enclosing scope, hooks, or JSX)
|
|
1346
|
+
* @param {FunctionLikeNode} node
|
|
1347
|
+
* @returns {boolean}
|
|
1348
|
+
*/
|
|
1349
|
+
const isPureFunction = (node) => {
|
|
1350
|
+
if (containsJSX(node)) return false;
|
|
1351
|
+
if (containsHooks(node)) return false;
|
|
1352
|
+
if (isWrappedInHook(node)) return false;
|
|
1353
|
+
if (accessesEnclosingScope(node, collectLocalNames(node))) return false;
|
|
1354
|
+
return true;
|
|
1355
|
+
};
|
|
1356
|
+
const rule$5 = defineRule({
|
|
1357
|
+
meta: {
|
|
1358
|
+
type: "suggestion",
|
|
1359
|
+
docs: {
|
|
1360
|
+
description: "Prevent declaring named pure functions inside other functions. Pure functions should be extracted to module scope for better performance and reusability.",
|
|
1361
|
+
recommended: false
|
|
1362
|
+
},
|
|
1363
|
+
schema: []
|
|
1364
|
+
},
|
|
1365
|
+
createOnce(context) {
|
|
1366
|
+
/**
|
|
1367
|
+
* @param {VariableDeclaratorNode} node
|
|
1368
|
+
*/
|
|
1369
|
+
const handleVariableDeclarator = (node) => {
|
|
1370
|
+
const init = node.init;
|
|
1371
|
+
if (!init || !isFunctionLike$1(init)) return;
|
|
1372
|
+
if (!node.id || node.id.type !== "Identifier") return;
|
|
1373
|
+
const functionName = node.id.name;
|
|
1374
|
+
const enclosingFunction = getEnclosingFunction(node);
|
|
1375
|
+
if (!enclosingFunction) return;
|
|
1376
|
+
if (!isPureFunction(init)) return;
|
|
1377
|
+
const enclosingFunctionName = getFunctionName(enclosingFunction);
|
|
1378
|
+
context.report({
|
|
1379
|
+
node: init,
|
|
1380
|
+
message: `Named pure function '${functionName}' should not be declared inside '${enclosingFunctionName}'. Extract it to module scope.`
|
|
1381
|
+
});
|
|
1382
|
+
};
|
|
1383
|
+
return { VariableDeclarator(node) {
|
|
1384
|
+
if (node.type === "VariableDeclarator") handleVariableDeclarator(node);
|
|
1385
|
+
} };
|
|
1386
|
+
}
|
|
1387
|
+
});
|
|
1388
|
+
const noNestedPureFunctionsRule = rule$5;
|
|
1122
1389
|
|
|
1123
1390
|
//#endregion
|
|
1124
1391
|
//#region src/oxlint-plugins/no-promise-then.js
|
|
@@ -1239,38 +1506,11 @@ const noSwitchRule = defineRule({
|
|
|
1239
1506
|
//#endregion
|
|
1240
1507
|
//#region src/oxlint-plugins/no-top-level-let.js
|
|
1241
1508
|
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
1242
|
-
/**
|
|
1243
|
-
* Get the enclosing function node for a given node
|
|
1244
|
-
* @param {ESTNode} node
|
|
1245
|
-
* @returns {ESTNode | null}
|
|
1246
|
-
*/
|
|
1247
|
-
const getEnclosingFunction = (node) => {
|
|
1248
|
-
const findFunction = (current) => {
|
|
1249
|
-
if (!current) return null;
|
|
1250
|
-
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") return current;
|
|
1251
|
-
return findFunction(current.parent);
|
|
1252
|
-
};
|
|
1253
|
-
return findFunction(node.parent);
|
|
1254
|
-
};
|
|
1255
|
-
/**
|
|
1256
|
-
* Check if a node is inside a loop
|
|
1257
|
-
* @param {ESTNode} node
|
|
1258
|
-
* @param {ESTNode} stopAt - Stop searching when we reach this node
|
|
1259
|
-
* @returns {boolean}
|
|
1260
|
-
*/
|
|
1261
|
-
const isInsideLoop = (node, stopAt) => {
|
|
1262
|
-
const checkLoop = (current) => {
|
|
1263
|
-
if (!current || current === stopAt) return false;
|
|
1264
|
-
if (current.type === "ForStatement" || current.type === "ForInStatement" || current.type === "ForOfStatement" || current.type === "WhileStatement" || current.type === "DoWhileStatement") return true;
|
|
1265
|
-
return checkLoop(current.parent);
|
|
1266
|
-
};
|
|
1267
|
-
return checkLoop(node.parent);
|
|
1268
|
-
};
|
|
1269
1509
|
const rule$2 = defineRule({
|
|
1270
1510
|
meta: {
|
|
1271
1511
|
type: "problem",
|
|
1272
1512
|
docs: {
|
|
1273
|
-
description: "Disallow
|
|
1513
|
+
description: "Disallow `let` keyword everywhere except in for loop initializers.",
|
|
1274
1514
|
recommended: false
|
|
1275
1515
|
},
|
|
1276
1516
|
schema: []
|
|
@@ -1280,14 +1520,14 @@ const rule$2 = defineRule({
|
|
|
1280
1520
|
if (rawNode.type !== "VariableDeclaration") return;
|
|
1281
1521
|
const node = rawNode;
|
|
1282
1522
|
if (node.kind !== "let") return;
|
|
1283
|
-
const fn = getEnclosingFunction(node);
|
|
1284
|
-
if (!fn || fn.type !== "FunctionDeclaration" && fn.type !== "FunctionExpression" && fn.type !== "ArrowFunctionExpression") return;
|
|
1285
1523
|
const parent = node.parent;
|
|
1286
|
-
if (
|
|
1287
|
-
|
|
1524
|
+
if (parent) {
|
|
1525
|
+
if (parent.type === "ForStatement" && parent.init === node) return;
|
|
1526
|
+
if ((parent.type === "ForInStatement" || parent.type === "ForOfStatement") && parent.left === node) return;
|
|
1527
|
+
}
|
|
1288
1528
|
context.report({
|
|
1289
1529
|
node,
|
|
1290
|
-
message: "Avoid using `let
|
|
1530
|
+
message: "Avoid using `let`; prefer `const` or use `let` only in for loop initializers."
|
|
1291
1531
|
});
|
|
1292
1532
|
} };
|
|
1293
1533
|
}
|
|
@@ -1514,6 +1754,7 @@ const plugin = definePlugin({
|
|
|
1514
1754
|
"no-finally": noFinallyRule,
|
|
1515
1755
|
"no-iife": noIifeRule,
|
|
1516
1756
|
"no-inline-components": noInlineComponentsRule,
|
|
1757
|
+
"no-nested-pure-functions": noNestedPureFunctionsRule,
|
|
1517
1758
|
"no-promise-then": noPromiseThenRule,
|
|
1518
1759
|
"no-react-namespace": noReactNamespaceRule,
|
|
1519
1760
|
"no-switch": noSwitchRule,
|