@conorroberts/utils 0.0.58 → 0.0.60
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/package.json +1 -45
- package/dist/env.d.mts +0 -19
- package/dist/env.mjs +0 -32
- package/dist/env.mjs.map +0 -1
- package/dist/images.d.mts +0 -101
- package/dist/images.mjs +0 -130
- package/dist/images.mjs.map +0 -1
- package/dist/oxlint/config.json +0 -72
- package/dist/oxlint/index.d.mts +0 -7
- package/dist/oxlint/index.mjs +0 -35
- package/dist/oxlint/index.mjs.map +0 -1
- package/dist/oxlint/jsx-component-pascal-case.d.mts +0 -17
- package/dist/oxlint/jsx-component-pascal-case.mjs +0 -155
- package/dist/oxlint/jsx-component-pascal-case.mjs.map +0 -1
- package/dist/oxlint/no-component-date-instantiation.d.mts +0 -19
- package/dist/oxlint/no-component-date-instantiation.mjs +0 -156
- package/dist/oxlint/no-component-date-instantiation.mjs.map +0 -1
- package/dist/oxlint/no-emoji.d.mts +0 -8
- package/dist/oxlint/no-emoji.mjs +0 -85
- package/dist/oxlint/no-emoji.mjs.map +0 -1
- package/dist/oxlint/no-finally.d.mts +0 -8
- package/dist/oxlint/no-finally.mjs +0 -28
- package/dist/oxlint/no-finally.mjs.map +0 -1
- package/dist/oxlint/no-function-call-in-jsx.d.mts +0 -11
- package/dist/oxlint/no-function-call-in-jsx.mjs +0 -70
- package/dist/oxlint/no-function-call-in-jsx.mjs.map +0 -1
- package/dist/oxlint/no-inline-components.d.mts +0 -36
- package/dist/oxlint/no-inline-components.mjs +0 -374
- package/dist/oxlint/no-inline-components.mjs.map +0 -1
- package/dist/oxlint/no-react-namespace.d.mts +0 -8
- package/dist/oxlint/no-react-namespace.mjs +0 -71
- package/dist/oxlint/no-react-namespace.mjs.map +0 -1
- package/dist/oxlint/no-switch-plugin.d.mts +0 -9
- package/dist/oxlint/no-switch-plugin.mjs +0 -27
- package/dist/oxlint/no-switch-plugin.mjs.map +0 -1
- package/dist/oxlint/no-top-level-let.d.mts +0 -8
- package/dist/oxlint/no-top-level-let.mjs +0 -62
- package/dist/oxlint/no-top-level-let.mjs.map +0 -1
- package/dist/oxlint/no-type-cast.d.mts +0 -8
- package/dist/oxlint/no-type-cast.mjs +0 -45
- package/dist/oxlint/no-type-cast.mjs.map +0 -1
- package/dist/oxlint/pretty-props.d.mts +0 -11
- package/dist/oxlint/pretty-props.mjs +0 -127
- package/dist/oxlint/pretty-props.mjs.map +0 -1
- package/dist/react.d.mts +0 -73
- package/dist/react.mjs +0 -113
- package/dist/react.mjs.map +0 -1
|
@@ -1,374 +0,0 @@
|
|
|
1
|
-
import { defineRule } from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-inline-components.js
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {import("oxlint").Context} RuleContext
|
|
6
|
-
* @typedef {import("oxlint").ESTree.Node} ESTNode
|
|
7
|
-
* @typedef {import("oxlint").ESTree.Expression} ESTExpression
|
|
8
|
-
* @typedef {import("oxlint").ESTree.Pattern} ESTPattern
|
|
9
|
-
* @typedef {import("oxlint").ESTree.ReturnStatement} ReturnStatementNode
|
|
10
|
-
* @typedef {import("oxlint").ESTree.VariableDeclarator} VariableDeclaratorNode
|
|
11
|
-
* @typedef {import("oxlint").ESTree.AssignmentExpression} AssignmentExpressionNode
|
|
12
|
-
* @typedef {import("oxlint").ESTree.Function | import("oxlint").ESTree.ArrowFunctionExpression} FunctionLikeNode
|
|
13
|
-
*/
|
|
14
|
-
/**
|
|
15
|
-
* @typedef {object} RecordedAssignment
|
|
16
|
-
* @property {ESTExpression} node
|
|
17
|
-
* @property {string[]} names
|
|
18
|
-
*/
|
|
19
|
-
/**
|
|
20
|
-
* @typedef {object} NestedFunctionRecord
|
|
21
|
-
* @property {FunctionLikeNode} node
|
|
22
|
-
* @property {string} name
|
|
23
|
-
*/
|
|
24
|
-
/**
|
|
25
|
-
* @typedef {object} FunctionContext
|
|
26
|
-
* @property {FunctionLikeNode} node
|
|
27
|
-
* @property {FunctionContext | null} parent
|
|
28
|
-
* @property {string} name
|
|
29
|
-
* @property {boolean} returnsJsx
|
|
30
|
-
* @property {Set<string>} jsxBindingNames
|
|
31
|
-
* @property {RecordedAssignment[]} jsxAssignments
|
|
32
|
-
* @property {NestedFunctionRecord[]} nestedJsxChildren
|
|
33
|
-
*/
|
|
34
|
-
const JSX_NODE_TYPES = new Set(["JSXElement", "JSXFragment"]);
|
|
35
|
-
const FUNCTION_NODE_TYPES = new Set([
|
|
36
|
-
"FunctionDeclaration",
|
|
37
|
-
"FunctionExpression",
|
|
38
|
-
"ArrowFunctionExpression"
|
|
39
|
-
]);
|
|
40
|
-
/**
|
|
41
|
-
* @param {unknown} name
|
|
42
|
-
* @returns {name is string}
|
|
43
|
-
*/
|
|
44
|
-
const isComponentName = (name) => typeof name === "string" && /^[A-Z]/.test(name);
|
|
45
|
-
/**
|
|
46
|
-
* @param {unknown} name
|
|
47
|
-
* @returns {name is string}
|
|
48
|
-
*/
|
|
49
|
-
const isHookName = (name) => typeof name === "string" && name.startsWith("use");
|
|
50
|
-
/**
|
|
51
|
-
* @param {unknown} node
|
|
52
|
-
* @returns {node is ESTNode & { type: string }}
|
|
53
|
-
*/
|
|
54
|
-
const isNode = (node) => Boolean(node && typeof node === "object" && "type" in node);
|
|
55
|
-
/**
|
|
56
|
-
* @param {unknown} node
|
|
57
|
-
* @returns {node is FunctionLikeNode}
|
|
58
|
-
*/
|
|
59
|
-
const isFunctionLike = (node) => isNode(node) && FUNCTION_NODE_TYPES.has(node.type);
|
|
60
|
-
/**
|
|
61
|
-
* @param {ESTNode | null | undefined} node
|
|
62
|
-
* @returns {FunctionLikeNode | null}
|
|
63
|
-
*/
|
|
64
|
-
const getEnclosingFunction = (node) => {
|
|
65
|
-
const findFunction = (current) => {
|
|
66
|
-
if (!current) return null;
|
|
67
|
-
if (isFunctionLike(current)) return current;
|
|
68
|
-
return findFunction(isNode(current) ? current.parent ?? null : null);
|
|
69
|
-
};
|
|
70
|
-
return findFunction(isNode(node) ? node.parent ?? null : null);
|
|
71
|
-
};
|
|
72
|
-
/**
|
|
73
|
-
* @param {FunctionLikeNode} node
|
|
74
|
-
*/
|
|
75
|
-
const isFunctionUsedAsJsxProp = (node) => {
|
|
76
|
-
const checkJsxProp = (current) => {
|
|
77
|
-
if (!current) return false;
|
|
78
|
-
if (current.type === "JSXAttribute") return true;
|
|
79
|
-
if (isFunctionLike(current)) return false;
|
|
80
|
-
return checkJsxProp(isNode(current) ? current.parent ?? null : null);
|
|
81
|
-
};
|
|
82
|
-
return checkJsxProp(isNode(node) ? node.parent ?? null : null);
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* @param {FunctionLikeNode} node
|
|
86
|
-
*/
|
|
87
|
-
const isFunctionImmediatelyInvoked = (node) => {
|
|
88
|
-
const parent = isNode(node) ? node.parent ?? null : null;
|
|
89
|
-
if (!parent) return false;
|
|
90
|
-
if (parent.type === "CallExpression" && parent.callee === node) return true;
|
|
91
|
-
return false;
|
|
92
|
-
};
|
|
93
|
-
/**
|
|
94
|
-
* @param {ESTExpression | null | undefined} root
|
|
95
|
-
*/
|
|
96
|
-
const expressionContainsJsx = (root) => {
|
|
97
|
-
if (!root || !isNode(root)) return false;
|
|
98
|
-
const stack = [root];
|
|
99
|
-
while (stack.length > 0) {
|
|
100
|
-
const current = stack.pop();
|
|
101
|
-
if (!current || !isNode(current)) continue;
|
|
102
|
-
if (JSX_NODE_TYPES.has(current.type)) return true;
|
|
103
|
-
if (FUNCTION_NODE_TYPES.has(current.type) && current !== root) continue;
|
|
104
|
-
for (const key of Object.keys(current)) {
|
|
105
|
-
if (key === "parent") continue;
|
|
106
|
-
const value = current[key];
|
|
107
|
-
if (!value) continue;
|
|
108
|
-
if (Array.isArray(value)) {
|
|
109
|
-
for (const element of value) if (isNode(element)) stack.push(element);
|
|
110
|
-
} else if (isNode(value)) stack.push(value);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
return false;
|
|
114
|
-
};
|
|
115
|
-
/**
|
|
116
|
-
* @param {ESTExpression | null | undefined} root
|
|
117
|
-
* @param {Set<string>} bindingNames
|
|
118
|
-
*/
|
|
119
|
-
const expressionProducesJsx = (root, bindingNames) => {
|
|
120
|
-
if (!root) return false;
|
|
121
|
-
if (expressionContainsJsx(root)) return true;
|
|
122
|
-
if (!isNode(root)) return false;
|
|
123
|
-
const type = root.type;
|
|
124
|
-
if (type === "Identifier") return bindingNames.has(root.name);
|
|
125
|
-
if (type === "ConditionalExpression") return expressionProducesJsx(root.consequent, bindingNames) || expressionProducesJsx(root.alternate, bindingNames);
|
|
126
|
-
if (type === "LogicalExpression") return expressionProducesJsx(root.left, bindingNames) || expressionProducesJsx(root.right, bindingNames);
|
|
127
|
-
if (type === "SequenceExpression") {
|
|
128
|
-
const expressions = root.expressions ?? [];
|
|
129
|
-
return expressionProducesJsx(expressions[expressions.length - 1] ?? null, bindingNames);
|
|
130
|
-
}
|
|
131
|
-
if (type === "ArrayExpression") return root.elements.some((element) => {
|
|
132
|
-
if (!element) return false;
|
|
133
|
-
if (element.type === "SpreadElement") return expressionProducesJsx(element.argument, bindingNames);
|
|
134
|
-
return expressionProducesJsx(element, bindingNames);
|
|
135
|
-
});
|
|
136
|
-
if (type === "ParenthesizedExpression") return expressionProducesJsx(root.expression, bindingNames);
|
|
137
|
-
if (type === "AwaitExpression" || type === "UnaryExpression" || type === "UpdateExpression") return expressionProducesJsx(root.argument, bindingNames);
|
|
138
|
-
if (type === "TSAsExpression" || type === "TSTypeAssertion" || type === "TSNonNullExpression" || type === "ChainExpression") return expressionProducesJsx(root.expression, bindingNames);
|
|
139
|
-
if (type === "CallExpression") return expressionProducesJsx(root.callee, bindingNames);
|
|
140
|
-
if (type === "MemberExpression") return expressionProducesJsx(root.object, bindingNames);
|
|
141
|
-
return false;
|
|
142
|
-
};
|
|
143
|
-
/**
|
|
144
|
-
* @param {ESTPattern | null | undefined} pattern
|
|
145
|
-
* @param {string[]} names
|
|
146
|
-
*/
|
|
147
|
-
const collectBindingNames = (pattern, names) => {
|
|
148
|
-
if (!pattern || !isNode(pattern)) return;
|
|
149
|
-
const type = pattern.type;
|
|
150
|
-
if (type === "Identifier") {
|
|
151
|
-
names.push(pattern.name);
|
|
152
|
-
return;
|
|
153
|
-
}
|
|
154
|
-
if (type === "ArrayPattern") {
|
|
155
|
-
for (const element of pattern.elements) {
|
|
156
|
-
if (!element) continue;
|
|
157
|
-
if (element.type === "RestElement") collectBindingNames(element.argument, names);
|
|
158
|
-
else collectBindingNames(element, names);
|
|
159
|
-
}
|
|
160
|
-
return;
|
|
161
|
-
}
|
|
162
|
-
if (type === "ObjectPattern") {
|
|
163
|
-
for (const property of pattern.properties) {
|
|
164
|
-
if (!property) continue;
|
|
165
|
-
if (property.type === "Property") collectBindingNames(property.value, names);
|
|
166
|
-
else if (property.type === "RestElement") collectBindingNames(property.argument, names);
|
|
167
|
-
}
|
|
168
|
-
return;
|
|
169
|
-
}
|
|
170
|
-
if (type === "AssignmentPattern") {
|
|
171
|
-
collectBindingNames(pattern.left, names);
|
|
172
|
-
return;
|
|
173
|
-
}
|
|
174
|
-
if (type === "RestElement") collectBindingNames(pattern.argument, names);
|
|
175
|
-
};
|
|
176
|
-
/**
|
|
177
|
-
* @param {FunctionLikeNode} node
|
|
178
|
-
*/
|
|
179
|
-
const getFunctionName = (node) => {
|
|
180
|
-
if (node.type === "FunctionDeclaration" && node.id && node.id.type === "Identifier") return node.id.name;
|
|
181
|
-
if ((node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.id) {
|
|
182
|
-
if (node.id.type === "Identifier") return node.id.name;
|
|
183
|
-
}
|
|
184
|
-
const parent = node.parent;
|
|
185
|
-
if (!parent || !isNode(parent)) return "";
|
|
186
|
-
if (parent.type === "VariableDeclarator") return parent.id && parent.id.type === "Identifier" ? parent.id.name : "";
|
|
187
|
-
if (parent.type === "AssignmentExpression") return parent.left && parent.left.type === "Identifier" ? parent.left.name : "";
|
|
188
|
-
if (parent.type === "Property" || parent.type === "MethodDefinition") return parent.key && parent.key.type === "Identifier" ? parent.key.name : "";
|
|
189
|
-
if (parent.type === "CallExpression") {
|
|
190
|
-
const callParent = parent.parent;
|
|
191
|
-
if (callParent && isNode(callParent)) {
|
|
192
|
-
if (callParent.type === "VariableDeclarator") return callParent.id && callParent.id.type === "Identifier" ? callParent.id.name : "";
|
|
193
|
-
if (callParent.type === "AssignmentExpression") return callParent.left && callParent.left.type === "Identifier" ? callParent.left.name : "";
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
return "";
|
|
197
|
-
};
|
|
198
|
-
/**
|
|
199
|
-
* @param {string} name
|
|
200
|
-
*/
|
|
201
|
-
const describeFunction = (name) => name ? `function '${name}'` : "this function";
|
|
202
|
-
/**
|
|
203
|
-
* @param {string} name
|
|
204
|
-
*/
|
|
205
|
-
const describeNested = (name) => name ? `function '${name}'` : "an anonymous function";
|
|
206
|
-
/**
|
|
207
|
-
* @param {string[]} names
|
|
208
|
-
* @param {string} fnName
|
|
209
|
-
*/
|
|
210
|
-
const createAssignmentMessage = (names, fnName) => {
|
|
211
|
-
return `Avoid storing JSX in ${names.length === 0 ? "local variables" : names.length === 1 ? `local '${names[0]}'` : `locals ${names.map((name) => `'${name}'`).join(", ")}`} inside ${describeFunction(fnName)}; return the JSX directly instead.`;
|
|
212
|
-
};
|
|
213
|
-
/**
|
|
214
|
-
* @param {string} childName
|
|
215
|
-
* @param {string} parentName
|
|
216
|
-
*/
|
|
217
|
-
const createNestedFunctionMessage = (childName, parentName) => `JSX-returning ${describeNested(childName)} should not be declared inside ${describeFunction(parentName)}. Extract it to module scope.`;
|
|
218
|
-
/**
|
|
219
|
-
* @param {string} name
|
|
220
|
-
*/
|
|
221
|
-
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.`;
|
|
222
|
-
const rule = defineRule({
|
|
223
|
-
meta: {
|
|
224
|
-
type: "problem",
|
|
225
|
-
docs: {
|
|
226
|
-
description: "Disallow JSX-returning functions and JSX-valued assignments within other functions that also return JSX.",
|
|
227
|
-
recommended: false
|
|
228
|
-
},
|
|
229
|
-
schema: []
|
|
230
|
-
},
|
|
231
|
-
createOnce(context) {
|
|
232
|
-
/** @type {FunctionContext[]} */
|
|
233
|
-
const functionStack = [];
|
|
234
|
-
const currentFunction = () => functionStack[functionStack.length - 1] ?? null;
|
|
235
|
-
/**
|
|
236
|
-
* @param {FunctionLikeNode} node
|
|
237
|
-
*/
|
|
238
|
-
const enterFunction = (node) => {
|
|
239
|
-
/** @type {FunctionContext} */
|
|
240
|
-
const fnCtx = {
|
|
241
|
-
node,
|
|
242
|
-
parent: currentFunction(),
|
|
243
|
-
name: getFunctionName(node),
|
|
244
|
-
returnsJsx: false,
|
|
245
|
-
jsxBindingNames: /* @__PURE__ */ new Set(),
|
|
246
|
-
jsxAssignments: [],
|
|
247
|
-
nestedJsxChildren: []
|
|
248
|
-
};
|
|
249
|
-
functionStack.push(fnCtx);
|
|
250
|
-
if (node.type === "ArrowFunctionExpression" && node.body && node.body.type !== "BlockStatement") {
|
|
251
|
-
if (expressionProducesJsx(node.body, fnCtx.jsxBindingNames)) fnCtx.returnsJsx = true;
|
|
252
|
-
}
|
|
253
|
-
};
|
|
254
|
-
const exitFunction = () => {
|
|
255
|
-
const fnCtx = functionStack.pop();
|
|
256
|
-
if (!fnCtx) return;
|
|
257
|
-
if (fnCtx.returnsJsx && isFunctionImmediatelyInvoked(fnCtx.node)) {
|
|
258
|
-
context.report({
|
|
259
|
-
node: fnCtx.node,
|
|
260
|
-
message: createIIFEMessage(fnCtx.name)
|
|
261
|
-
});
|
|
262
|
-
return;
|
|
263
|
-
}
|
|
264
|
-
if (fnCtx.parent && fnCtx.returnsJsx && fnCtx.name && !isFunctionUsedAsJsxProp(fnCtx.node)) fnCtx.parent.nestedJsxChildren.push({
|
|
265
|
-
node: fnCtx.node,
|
|
266
|
-
name: fnCtx.name
|
|
267
|
-
});
|
|
268
|
-
if (!fnCtx.returnsJsx) return;
|
|
269
|
-
for (const assignment of fnCtx.jsxAssignments) context.report({
|
|
270
|
-
node: assignment.node,
|
|
271
|
-
message: createAssignmentMessage(assignment.names, fnCtx.name)
|
|
272
|
-
});
|
|
273
|
-
for (const nested of fnCtx.nestedJsxChildren) context.report({
|
|
274
|
-
node: nested.node,
|
|
275
|
-
message: createNestedFunctionMessage(nested.name, fnCtx.name)
|
|
276
|
-
});
|
|
277
|
-
};
|
|
278
|
-
/** @param {ReturnStatementNode} node */
|
|
279
|
-
const handleReturnStatement = (node) => {
|
|
280
|
-
const fnCtx = currentFunction();
|
|
281
|
-
if (!fnCtx) return;
|
|
282
|
-
const argument = node.argument;
|
|
283
|
-
if (!argument || isFunctionLike(argument)) return;
|
|
284
|
-
if (expressionProducesJsx(argument, fnCtx.jsxBindingNames)) fnCtx.returnsJsx = true;
|
|
285
|
-
};
|
|
286
|
-
/** @param {VariableDeclaratorNode} node */
|
|
287
|
-
const handleVariableDeclarator = (node) => {
|
|
288
|
-
const fnCtx = currentFunction();
|
|
289
|
-
if (!fnCtx) return;
|
|
290
|
-
const init = node.init;
|
|
291
|
-
if (!init || isFunctionLike(init)) return;
|
|
292
|
-
if (!expressionContainsJsx(init)) return;
|
|
293
|
-
const names = [];
|
|
294
|
-
collectBindingNames(node.id, names);
|
|
295
|
-
for (const name of names) fnCtx.jsxBindingNames.add(name);
|
|
296
|
-
fnCtx.jsxAssignments.push({
|
|
297
|
-
node: init,
|
|
298
|
-
names
|
|
299
|
-
});
|
|
300
|
-
};
|
|
301
|
-
/** @param {AssignmentExpressionNode} node */
|
|
302
|
-
const handleAssignmentExpression = (node) => {
|
|
303
|
-
if (node.operator !== "=") return;
|
|
304
|
-
const fnCtx = currentFunction();
|
|
305
|
-
if (!fnCtx) return;
|
|
306
|
-
const right = node.right;
|
|
307
|
-
if (!right || isFunctionLike(right)) return;
|
|
308
|
-
if (!expressionContainsJsx(right)) return;
|
|
309
|
-
const names = [];
|
|
310
|
-
if (node.left && node.left.type === "Identifier") {
|
|
311
|
-
names.push(node.left.name);
|
|
312
|
-
fnCtx.jsxBindingNames.add(node.left.name);
|
|
313
|
-
}
|
|
314
|
-
fnCtx.jsxAssignments.push({
|
|
315
|
-
node: right,
|
|
316
|
-
names
|
|
317
|
-
});
|
|
318
|
-
};
|
|
319
|
-
/**
|
|
320
|
-
* @param {import("oxlint").ESTree.CallExpression} node
|
|
321
|
-
*/
|
|
322
|
-
const handleCallExpression = (node) => {
|
|
323
|
-
const fnCtx = currentFunction();
|
|
324
|
-
if (!fnCtx) return;
|
|
325
|
-
if (node.callee && node.callee.type === "MemberExpression" && node.callee.property && node.callee.property.type === "Identifier" && node.callee.property.name === "push") {
|
|
326
|
-
const arrayObject = node.callee.object;
|
|
327
|
-
if (arrayObject && arrayObject.type === "Identifier") {
|
|
328
|
-
const arrayName = arrayObject.name;
|
|
329
|
-
if (node.arguments.some((arg) => {
|
|
330
|
-
if (!arg || arg.type === "SpreadElement") return false;
|
|
331
|
-
return expressionContainsJsx(arg);
|
|
332
|
-
})) {
|
|
333
|
-
fnCtx.jsxBindingNames.add(arrayName);
|
|
334
|
-
fnCtx.jsxAssignments.push({
|
|
335
|
-
node,
|
|
336
|
-
names: [arrayName]
|
|
337
|
-
});
|
|
338
|
-
}
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
};
|
|
342
|
-
return {
|
|
343
|
-
FunctionDeclaration(node) {
|
|
344
|
-
if (isFunctionLike(node)) enterFunction(node);
|
|
345
|
-
},
|
|
346
|
-
"FunctionDeclaration:exit": exitFunction,
|
|
347
|
-
FunctionExpression(node) {
|
|
348
|
-
if (isFunctionLike(node)) enterFunction(node);
|
|
349
|
-
},
|
|
350
|
-
"FunctionExpression:exit": exitFunction,
|
|
351
|
-
ArrowFunctionExpression(node) {
|
|
352
|
-
if (isFunctionLike(node)) enterFunction(node);
|
|
353
|
-
},
|
|
354
|
-
"ArrowFunctionExpression:exit": exitFunction,
|
|
355
|
-
ReturnStatement(node) {
|
|
356
|
-
if (node.type === "ReturnStatement") handleReturnStatement(node);
|
|
357
|
-
},
|
|
358
|
-
VariableDeclarator(node) {
|
|
359
|
-
if (node.type === "VariableDeclarator") handleVariableDeclarator(node);
|
|
360
|
-
},
|
|
361
|
-
AssignmentExpression(node) {
|
|
362
|
-
if (node.type === "AssignmentExpression") handleAssignmentExpression(node);
|
|
363
|
-
},
|
|
364
|
-
CallExpression(node) {
|
|
365
|
-
if (node.type === "CallExpression") handleCallExpression(node);
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
}
|
|
369
|
-
});
|
|
370
|
-
const noInlineComponentsRule = rule;
|
|
371
|
-
|
|
372
|
-
//#endregion
|
|
373
|
-
export { getEnclosingFunction, getFunctionName, isComponentName, isHookName, noInlineComponentsRule };
|
|
374
|
-
//# sourceMappingURL=no-inline-components.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"no-inline-components.mjs","names":[],"sources":["../../src/oxlint-plugins/no-inline-components.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\n\n/**\n * @typedef {import(\"oxlint\").Context} RuleContext\n * @typedef {import(\"oxlint\").ESTree.Node} ESTNode\n * @typedef {import(\"oxlint\").ESTree.Expression} ESTExpression\n * @typedef {import(\"oxlint\").ESTree.Pattern} ESTPattern\n * @typedef {import(\"oxlint\").ESTree.ReturnStatement} ReturnStatementNode\n * @typedef {import(\"oxlint\").ESTree.VariableDeclarator} VariableDeclaratorNode\n * @typedef {import(\"oxlint\").ESTree.AssignmentExpression} AssignmentExpressionNode\n * @typedef {import(\"oxlint\").ESTree.Function | import(\"oxlint\").ESTree.ArrowFunctionExpression} FunctionLikeNode\n */\n\n/**\n * @typedef {object} RecordedAssignment\n * @property {ESTExpression} node\n * @property {string[]} names\n */\n\n/**\n * @typedef {object} NestedFunctionRecord\n * @property {FunctionLikeNode} node\n * @property {string} name\n */\n\n/**\n * @typedef {object} FunctionContext\n * @property {FunctionLikeNode} node\n * @property {FunctionContext | null} parent\n * @property {string} name\n * @property {boolean} returnsJsx\n * @property {Set<string>} jsxBindingNames\n * @property {RecordedAssignment[]} jsxAssignments\n * @property {NestedFunctionRecord[]} nestedJsxChildren\n */\n\nconst JSX_NODE_TYPES = new Set([\"JSXElement\", \"JSXFragment\"]);\nconst FUNCTION_NODE_TYPES = new Set([\"FunctionDeclaration\", \"FunctionExpression\", \"ArrowFunctionExpression\"]);\n\n/**\n * @param {unknown} name\n * @returns {name is string}\n */\nexport const isComponentName = (name) => typeof name === \"string\" && /^[A-Z]/.test(name);\n\n/**\n * @param {unknown} name\n * @returns {name is string}\n */\nexport const isHookName = (name) => typeof name === \"string\" && name.startsWith(\"use\");\n\n/**\n * @param {unknown} node\n * @returns {node is ESTNode & { type: string }}\n */\nconst isNode = (node) => Boolean(node && typeof node === \"object\" && \"type\" in node);\n\n/**\n * @param {unknown} node\n * @returns {node is FunctionLikeNode}\n */\nconst isFunctionLike = (node) => isNode(node) && FUNCTION_NODE_TYPES.has(node.type);\n\n/**\n * @param {ESTNode | null | undefined} node\n * @returns {FunctionLikeNode | null}\n */\nexport const getEnclosingFunction = (node) => {\n const findFunction = (current) => {\n if (!current) return null;\n if (isFunctionLike(current)) {\n return current;\n }\n return findFunction(isNode(current) ? current.parent ?? null : null);\n };\n return findFunction(isNode(node) ? node.parent ?? null : null);\n};\n\n/**\n * @param {FunctionLikeNode} node\n */\nconst isFunctionUsedAsJsxProp = (node) => {\n const checkJsxProp = (current) => {\n if (!current) return false;\n if (current.type === \"JSXAttribute\") {\n return true;\n }\n if (isFunctionLike(current)) {\n return false;\n }\n return checkJsxProp(isNode(current) ? current.parent ?? null : null);\n };\n return checkJsxProp(isNode(node) ? node.parent ?? null : null);\n};\n\n/**\n * @param {FunctionLikeNode} node\n */\nconst isFunctionImmediatelyInvoked = (node) => {\n const parent = isNode(node) ? node.parent ?? null : null;\n if (!parent) return false;\n\n // Check if the function is the callee of a CallExpression (i.e., it's immediately invoked)\n if (parent.type === \"CallExpression\" && parent.callee === node) {\n return true;\n }\n\n return false;\n};\n\n/**\n * @param {ESTExpression | null | undefined} root\n */\nconst expressionContainsJsx = (root) => {\n if (!root || !isNode(root)) return false;\n\n const stack = [root];\n\n while (stack.length > 0) {\n const current = stack.pop();\n if (!current || !isNode(current)) continue;\n\n if (JSX_NODE_TYPES.has(current.type)) {\n return true;\n }\n\n if (FUNCTION_NODE_TYPES.has(current.type) && current !== root) {\n continue;\n }\n\n for (const key of Object.keys(current)) {\n if (key === \"parent\") continue;\n\n const value = current[key];\n if (!value) continue;\n\n if (Array.isArray(value)) {\n for (const element of value) {\n if (isNode(element)) {\n stack.push(element);\n }\n }\n } else if (isNode(value)) {\n stack.push(value);\n }\n }\n }\n\n return false;\n};\n\n/**\n * @param {ESTExpression | null | undefined} root\n * @param {Set<string>} bindingNames\n */\nconst expressionProducesJsx = (root, bindingNames) => {\n if (!root) return false;\n if (expressionContainsJsx(root)) return true;\n\n if (!isNode(root)) return false;\n\n const type = root.type;\n\n if (type === \"Identifier\") {\n return bindingNames.has(root.name);\n }\n\n if (type === \"ConditionalExpression\") {\n return expressionProducesJsx(root.consequent, bindingNames) || expressionProducesJsx(root.alternate, bindingNames);\n }\n\n if (type === \"LogicalExpression\") {\n return expressionProducesJsx(root.left, bindingNames) || expressionProducesJsx(root.right, bindingNames);\n }\n\n if (type === \"SequenceExpression\") {\n const expressions = root.expressions ?? [];\n const last = expressions[expressions.length - 1] ?? null;\n return expressionProducesJsx(last, bindingNames);\n }\n\n if (type === \"ArrayExpression\") {\n return root.elements.some((element) => {\n if (!element) return false;\n if (element.type === \"SpreadElement\") {\n return expressionProducesJsx(element.argument, bindingNames);\n }\n return expressionProducesJsx(element, bindingNames);\n });\n }\n\n if (type === \"ParenthesizedExpression\") {\n return expressionProducesJsx(root.expression, bindingNames);\n }\n\n if (type === \"AwaitExpression\" || type === \"UnaryExpression\" || type === \"UpdateExpression\") {\n return expressionProducesJsx(root.argument, bindingNames);\n }\n\n if (\n type === \"TSAsExpression\" ||\n type === \"TSTypeAssertion\" ||\n type === \"TSNonNullExpression\" ||\n type === \"ChainExpression\"\n ) {\n return expressionProducesJsx(root.expression, bindingNames);\n }\n\n if (type === \"CallExpression\") {\n return expressionProducesJsx(root.callee, bindingNames);\n }\n\n if (type === \"MemberExpression\") {\n return expressionProducesJsx(root.object, bindingNames);\n }\n\n return false;\n};\n\n/**\n * @param {ESTPattern | null | undefined} pattern\n * @param {string[]} names\n */\nconst collectBindingNames = (pattern, names) => {\n if (!pattern || !isNode(pattern)) return;\n\n const type = pattern.type;\n\n if (type === \"Identifier\") {\n names.push(pattern.name);\n return;\n }\n\n if (type === \"ArrayPattern\") {\n for (const element of pattern.elements) {\n if (!element) continue;\n if (element.type === \"RestElement\") {\n collectBindingNames(element.argument, names);\n } else {\n collectBindingNames(element, names);\n }\n }\n return;\n }\n\n if (type === \"ObjectPattern\") {\n for (const property of pattern.properties) {\n if (!property) continue;\n if (property.type === \"Property\") {\n collectBindingNames(property.value, names);\n } else if (property.type === \"RestElement\") {\n collectBindingNames(property.argument, names);\n }\n }\n return;\n }\n\n if (type === \"AssignmentPattern\") {\n collectBindingNames(pattern.left, names);\n return;\n }\n\n if (type === \"RestElement\") {\n collectBindingNames(pattern.argument, names);\n }\n};\n\n/**\n * @param {FunctionLikeNode} node\n */\nexport const getFunctionName = (node) => {\n if (node.type === \"FunctionDeclaration\" && node.id && node.id.type === \"Identifier\") {\n return node.id.name;\n }\n\n if ((node.type === \"FunctionExpression\" || node.type === \"ArrowFunctionExpression\") && node.id) {\n if (node.id.type === \"Identifier\") return node.id.name;\n }\n\n const parent = node.parent;\n if (!parent || !isNode(parent)) return \"\";\n\n if (parent.type === \"VariableDeclarator\") {\n return parent.id && parent.id.type === \"Identifier\" ? parent.id.name : \"\";\n }\n\n if (parent.type === \"AssignmentExpression\") {\n return parent.left && parent.left.type === \"Identifier\" ? parent.left.name : \"\";\n }\n\n if (parent.type === \"Property\" || parent.type === \"MethodDefinition\") {\n return parent.key && parent.key.type === \"Identifier\" ? parent.key.name : \"\";\n }\n\n // Handle functions passed as arguments to calls (e.g., useCallback, useMemo)\n if (parent.type === \"CallExpression\") {\n const callParent = parent.parent;\n if (callParent && isNode(callParent)) {\n if (callParent.type === \"VariableDeclarator\") {\n return callParent.id && callParent.id.type === \"Identifier\" ? callParent.id.name : \"\";\n }\n if (callParent.type === \"AssignmentExpression\") {\n return callParent.left && callParent.left.type === \"Identifier\" ? callParent.left.name : \"\";\n }\n }\n }\n\n return \"\";\n};\n\n/**\n * @param {string} name\n */\nconst describeFunction = (name) => (name ? `function '${name}'` : \"this function\");\n\n/**\n * @param {string} name\n */\nconst describeNested = (name) => (name ? `function '${name}'` : \"an anonymous function\");\n\n/**\n * @param {string[]} names\n * @param {string} fnName\n */\nconst createAssignmentMessage = (names, fnName) => {\n const target =\n names.length === 0\n ? \"local variables\"\n : names.length === 1\n ? `local '${names[0]}'`\n : `locals ${names.map((name) => `'${name}'`).join(\", \")}`;\n\n return `Avoid storing JSX in ${target} inside ${describeFunction(fnName)}; return the JSX directly instead.`;\n};\n\n/**\n * @param {string} childName\n * @param {string} parentName\n */\nconst createNestedFunctionMessage = (childName, parentName) =>\n `JSX-returning ${describeNested(childName)} should not be declared inside ${describeFunction(parentName)}. Extract it to module scope.`;\n\n/**\n * @param {string} name\n */\nconst createIIFEMessage = (name) =>\n `JSX-returning ${describeNested(name)} should not be declared as an immediately invoked function expression (IIFE). Extract it to a named function at module scope.`;\n\nconst rule = defineRule({\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow JSX-returning functions and JSX-valued assignments within other functions that also return JSX.\",\n recommended: false,\n },\n schema: [],\n },\n\n createOnce(context) {\n /** @type {FunctionContext[]} */\n const functionStack = [];\n\n const currentFunction = () => functionStack[functionStack.length - 1] ?? null;\n\n /**\n * @param {FunctionLikeNode} node\n */\n const enterFunction = (node) => {\n const parent = currentFunction();\n /** @type {FunctionContext} */\n const fnCtx = {\n node,\n parent,\n name: getFunctionName(node),\n returnsJsx: false,\n jsxBindingNames: new Set(),\n jsxAssignments: [],\n nestedJsxChildren: [],\n };\n\n functionStack.push(fnCtx);\n\n if (node.type === \"ArrowFunctionExpression\" && node.body && node.body.type !== \"BlockStatement\") {\n if (expressionProducesJsx(node.body, fnCtx.jsxBindingNames)) {\n fnCtx.returnsJsx = true;\n }\n }\n };\n\n const exitFunction = () => {\n const fnCtx = functionStack.pop();\n if (!fnCtx) return;\n\n if (fnCtx.returnsJsx && isFunctionImmediatelyInvoked(fnCtx.node)) {\n context.report({\n node: fnCtx.node,\n message: createIIFEMessage(fnCtx.name),\n });\n return;\n }\n\n if (fnCtx.parent && fnCtx.returnsJsx && fnCtx.name && !isFunctionUsedAsJsxProp(fnCtx.node)) {\n fnCtx.parent.nestedJsxChildren.push({ node: fnCtx.node, name: fnCtx.name });\n }\n\n if (!fnCtx.returnsJsx) return;\n\n for (const assignment of fnCtx.jsxAssignments) {\n context.report({\n node: assignment.node,\n message: createAssignmentMessage(assignment.names, fnCtx.name),\n });\n }\n\n for (const nested of fnCtx.nestedJsxChildren) {\n context.report({\n node: nested.node,\n message: createNestedFunctionMessage(nested.name, fnCtx.name),\n });\n }\n };\n\n /** @param {ReturnStatementNode} node */\n const handleReturnStatement = (node) => {\n const fnCtx = currentFunction();\n if (!fnCtx) return;\n\n const argument = node.argument;\n if (!argument || isFunctionLike(argument)) return;\n\n if (expressionProducesJsx(argument, fnCtx.jsxBindingNames)) {\n fnCtx.returnsJsx = true;\n }\n };\n\n /** @param {VariableDeclaratorNode} node */\n const handleVariableDeclarator = (node) => {\n const fnCtx = currentFunction();\n if (!fnCtx) return;\n\n const init = node.init;\n if (!init || isFunctionLike(init)) return;\n if (!expressionContainsJsx(init)) return;\n\n const names = [];\n collectBindingNames(node.id, names);\n for (const name of names) {\n fnCtx.jsxBindingNames.add(name);\n }\n\n fnCtx.jsxAssignments.push({ node: init, names });\n };\n\n /** @param {AssignmentExpressionNode} node */\n const handleAssignmentExpression = (node) => {\n if (node.operator !== \"=\") return;\n\n const fnCtx = currentFunction();\n if (!fnCtx) return;\n\n const right = node.right;\n if (!right || isFunctionLike(right)) return;\n if (!expressionContainsJsx(right)) return;\n\n const names = [];\n if (node.left && node.left.type === \"Identifier\") {\n names.push(node.left.name);\n fnCtx.jsxBindingNames.add(node.left.name);\n }\n\n fnCtx.jsxAssignments.push({ node: right, names });\n };\n\n /**\n * @param {import(\"oxlint\").ESTree.CallExpression} node\n */\n const handleCallExpression = (node) => {\n const fnCtx = currentFunction();\n if (!fnCtx) return;\n\n // Check for array.push(<JSX>)\n if (\n node.callee &&\n node.callee.type === \"MemberExpression\" &&\n node.callee.property &&\n node.callee.property.type === \"Identifier\" &&\n node.callee.property.name === \"push\"\n ) {\n const arrayObject = node.callee.object;\n if (arrayObject && arrayObject.type === \"Identifier\") {\n const arrayName = arrayObject.name;\n\n // Check if any argument contains JSX\n const hasJsxArgument = node.arguments.some((arg) => {\n if (!arg || arg.type === \"SpreadElement\") return false;\n return expressionContainsJsx(arg);\n });\n\n if (hasJsxArgument) {\n fnCtx.jsxBindingNames.add(arrayName);\n fnCtx.jsxAssignments.push({ node: node, names: [arrayName] });\n }\n }\n }\n };\n\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\n FunctionDeclaration(node) {\n if (isFunctionLike(node)) enterFunction(node);\n },\n \"FunctionDeclaration:exit\": exitFunction,\n FunctionExpression(node) {\n if (isFunctionLike(node)) enterFunction(node);\n },\n \"FunctionExpression:exit\": exitFunction,\n ArrowFunctionExpression(node) {\n if (isFunctionLike(node)) enterFunction(node);\n },\n \"ArrowFunctionExpression:exit\": exitFunction,\n ReturnStatement(node) {\n if (node.type === \"ReturnStatement\") handleReturnStatement(node);\n },\n VariableDeclarator(node) {\n if (node.type === \"VariableDeclarator\") handleVariableDeclarator(node);\n },\n AssignmentExpression(node) {\n if (node.type === \"AssignmentExpression\") handleAssignmentExpression(node);\n },\n CallExpression(node) {\n if (node.type === \"CallExpression\") handleCallExpression(node);\n },\n });\n },\n});\n\nexport const noInlineComponentsRule = rule;\n\n"],"mappings":"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"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as oxlint31 from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-react-namespace.d.ts
|
|
4
|
-
declare const noReactNamespaceRule: oxlint31.Rule;
|
|
5
|
-
type ESTNode = oxlint31.ESTree.Node;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { ESTNode, noReactNamespaceRule };
|
|
8
|
-
//# sourceMappingURL=no-react-namespace.d.mts.map
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { defineRule } from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-react-namespace.js
|
|
4
|
-
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
5
|
-
/**
|
|
6
|
-
* Check if a MemberExpression is accessing the React namespace
|
|
7
|
-
* @param {ESTNode} node
|
|
8
|
-
* @returns {boolean}
|
|
9
|
-
*/
|
|
10
|
-
const isReactNamespaceAccess = (node) => {
|
|
11
|
-
if (node.type !== "MemberExpression") return false;
|
|
12
|
-
const object = node.object;
|
|
13
|
-
if (!object || object.type !== "Identifier" || object.name !== "React") return false;
|
|
14
|
-
return true;
|
|
15
|
-
};
|
|
16
|
-
/**
|
|
17
|
-
* Check if this is a type annotation context (TypeScript)
|
|
18
|
-
* @param {ESTNode} node
|
|
19
|
-
* @returns {boolean}
|
|
20
|
-
*/
|
|
21
|
-
const isTypeContext = (node) => {
|
|
22
|
-
const checkParent = (current) => {
|
|
23
|
-
if (!current) return false;
|
|
24
|
-
if (new Set([
|
|
25
|
-
"TSTypeReference",
|
|
26
|
-
"TSTypeAnnotation",
|
|
27
|
-
"TSTypeParameterInstantiation",
|
|
28
|
-
"TSInterfaceHeritage",
|
|
29
|
-
"TSTypeQuery",
|
|
30
|
-
"TSTypeAliasDeclaration",
|
|
31
|
-
"TSInterfaceDeclaration",
|
|
32
|
-
"TSTypeLiteral",
|
|
33
|
-
"TSPropertySignature",
|
|
34
|
-
"TSIndexSignature",
|
|
35
|
-
"TSMethodSignature",
|
|
36
|
-
"TSCallSignatureDeclaration",
|
|
37
|
-
"TSConstructSignatureDeclaration",
|
|
38
|
-
"TSExpressionWithTypeArguments"
|
|
39
|
-
]).has(current.type)) return true;
|
|
40
|
-
if (current.type === "ExpressionStatement" || current.type === "VariableDeclarator" || current.type === "CallExpression" || current.type === "ReturnStatement") return false;
|
|
41
|
-
return checkParent(current.parent);
|
|
42
|
-
};
|
|
43
|
-
return checkParent(node.parent);
|
|
44
|
-
};
|
|
45
|
-
const rule = defineRule({
|
|
46
|
-
meta: {
|
|
47
|
-
type: "problem",
|
|
48
|
-
docs: {
|
|
49
|
-
description: "Disallow using the React namespace for accessing React APIs. Use destructured imports instead (e.g., import { useState } from 'react').",
|
|
50
|
-
recommended: true
|
|
51
|
-
},
|
|
52
|
-
schema: []
|
|
53
|
-
},
|
|
54
|
-
createOnce(context) {
|
|
55
|
-
return { MemberExpression(node) {
|
|
56
|
-
if (node.type !== "MemberExpression") return;
|
|
57
|
-
if (!isReactNamespaceAccess(node)) return;
|
|
58
|
-
if (isTypeContext(node)) return;
|
|
59
|
-
const propertyName = node.property && node.property.type === "Identifier" ? node.property.name : "property";
|
|
60
|
-
context.report({
|
|
61
|
-
node,
|
|
62
|
-
message: `Avoid using 'React.${propertyName}'. Import '${propertyName}' directly from 'react' instead (e.g., import { ${propertyName} } from 'react').`
|
|
63
|
-
});
|
|
64
|
-
} };
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
const noReactNamespaceRule = rule;
|
|
68
|
-
|
|
69
|
-
//#endregion
|
|
70
|
-
export { noReactNamespaceRule };
|
|
71
|
-
//# sourceMappingURL=no-react-namespace.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"no-react-namespace.mjs","names":[],"sources":["../../src/oxlint-plugins/no-react-namespace.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\n\n/** @typedef {import(\"oxlint\").ESTree.Node} ESTNode */\n\n/**\n * Check if a MemberExpression is accessing the React namespace\n * @param {ESTNode} node\n * @returns {boolean}\n */\nconst isReactNamespaceAccess = (node) => {\n if (node.type !== \"MemberExpression\") return false;\n\n const object = node.object;\n if (!object || object.type !== \"Identifier\" || object.name !== \"React\") {\n return false;\n }\n\n return true;\n};\n\n/**\n * Check if this is a type annotation context (TypeScript)\n * @param {ESTNode} node\n * @returns {boolean}\n */\nconst isTypeContext = (node) => {\n const checkParent = (current) => {\n if (!current) return false;\n\n // Type annotation contexts where React namespace is allowed\n const typeContextTypes = new Set([\n \"TSTypeReference\",\n \"TSTypeAnnotation\",\n \"TSTypeParameterInstantiation\",\n \"TSInterfaceHeritage\",\n \"TSTypeQuery\",\n \"TSTypeAliasDeclaration\",\n \"TSInterfaceDeclaration\",\n \"TSTypeLiteral\",\n \"TSPropertySignature\",\n \"TSIndexSignature\",\n \"TSMethodSignature\",\n \"TSCallSignatureDeclaration\",\n \"TSConstructSignatureDeclaration\",\n \"TSExpressionWithTypeArguments\",\n ]);\n\n if (typeContextTypes.has(current.type)) {\n return true;\n }\n\n // Stop at statement or expression boundaries\n if (\n current.type === \"ExpressionStatement\" ||\n current.type === \"VariableDeclarator\" ||\n current.type === \"CallExpression\" ||\n current.type === \"ReturnStatement\"\n ) {\n return false;\n }\n\n return checkParent(current.parent);\n };\n\n return checkParent(node.parent);\n};\n\nconst rule = defineRule({\n meta: {\n type: \"problem\",\n docs: {\n description:\n \"Disallow using the React namespace for accessing React APIs. Use destructured imports instead (e.g., import { useState } from 'react').\",\n recommended: true,\n },\n schema: [],\n },\n createOnce(context) {\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\n /**\n * @param {ESTNode} node\n */\n MemberExpression(node) {\n if (node.type !== \"MemberExpression\") return;\n\n if (!isReactNamespaceAccess(node)) return;\n\n // Allow React namespace in type annotations\n if (isTypeContext(node)) return;\n\n const propertyName = node.property && node.property.type === \"Identifier\" ? node.property.name : \"property\";\n\n context.report({\n node,\n message: `Avoid using 'React.${propertyName}'. Import '${propertyName}' directly from 'react' instead (e.g., import { ${propertyName} } from 'react').`,\n });\n },\n });\n },\n});\n\nexport const noReactNamespaceRule = rule;\n\n"],"mappings":";;;;;;;;;AASA,MAAM,0BAA0B,SAAS;AACvC,KAAI,KAAK,SAAS,mBAAoB,QAAO;CAE7C,MAAM,SAAS,KAAK;AACpB,KAAI,CAAC,UAAU,OAAO,SAAS,gBAAgB,OAAO,SAAS,QAC7D,QAAO;AAGT,QAAO;;;;;;;AAQT,MAAM,iBAAiB,SAAS;CAC9B,MAAM,eAAe,YAAY;AAC/B,MAAI,CAAC,QAAS,QAAO;AAoBrB,MAjByB,IAAI,IAAI;GAC/B;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC,CAEmB,IAAI,QAAQ,KAAK,CACpC,QAAO;AAIT,MACE,QAAQ,SAAS,yBACjB,QAAQ,SAAS,wBACjB,QAAQ,SAAS,oBACjB,QAAQ,SAAS,kBAEjB,QAAO;AAGT,SAAO,YAAY,QAAQ,OAAO;;AAGpC,QAAO,YAAY,KAAK,OAAO;;AAGjC,MAAM,OAAO,WAAW;CACtB,MAAM;EACJ,MAAM;EACN,MAAM;GACJ,aACE;GACF,aAAa;GACd;EACD,QAAQ,EAAE;EACX;CACD,WAAW,SAAS;AAClB,SAAyD,EAIvD,iBAAiB,MAAM;AACrB,OAAI,KAAK,SAAS,mBAAoB;AAEtC,OAAI,CAAC,uBAAuB,KAAK,CAAE;AAGnC,OAAI,cAAc,KAAK,CAAE;GAEzB,MAAM,eAAe,KAAK,YAAY,KAAK,SAAS,SAAS,eAAe,KAAK,SAAS,OAAO;AAEjG,WAAQ,OAAO;IACb;IACA,SAAS,sBAAsB,aAAa,aAAa,aAAa,kDAAkD,aAAa;IACtI,CAAC;KAEL;;CAEJ,CAAC;AAEF,MAAa,uBAAuB"}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import * as oxlint33 from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-switch-plugin.d.ts
|
|
4
|
-
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
5
|
-
declare const noSwitchRule: oxlint33.Rule;
|
|
6
|
-
type ESTNode = oxlint33.ESTree.Node;
|
|
7
|
-
//#endregion
|
|
8
|
-
export { ESTNode, noSwitchRule };
|
|
9
|
-
//# sourceMappingURL=no-switch-plugin.d.mts.map
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { defineRule } from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-switch-plugin.js
|
|
4
|
-
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
5
|
-
const noSwitchRule = defineRule({
|
|
6
|
-
meta: {
|
|
7
|
-
type: "problem",
|
|
8
|
-
docs: {
|
|
9
|
-
description: "Disallow switch/case statements",
|
|
10
|
-
recommended: true
|
|
11
|
-
},
|
|
12
|
-
schema: []
|
|
13
|
-
},
|
|
14
|
-
createOnce(context) {
|
|
15
|
-
return { SwitchStatement(node) {
|
|
16
|
-
if (node.type !== "SwitchStatement") return;
|
|
17
|
-
context.report({
|
|
18
|
-
node,
|
|
19
|
-
message: "Use of switch/case is disallowed. Use object map or if/else instead."
|
|
20
|
-
});
|
|
21
|
-
} };
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
|
|
25
|
-
//#endregion
|
|
26
|
-
export { noSwitchRule };
|
|
27
|
-
//# sourceMappingURL=no-switch-plugin.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"no-switch-plugin.mjs","names":[],"sources":["../../src/oxlint-plugins/no-switch-plugin.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\n\n/** @typedef {import(\"oxlint\").ESTree.Node} ESTNode */\n\nexport const noSwitchRule = defineRule({\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow switch/case statements\",\n recommended: true,\n },\n schema: [],\n },\n createOnce(context) {\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\n /**\n * @param {ESTNode} node\n */\n SwitchStatement(node) {\n if (node.type !== \"SwitchStatement\") return;\n\n context.report({\n node,\n message: \"Use of switch/case is disallowed. Use object map or if/else instead.\",\n });\n },\n });\n },\n});\n"],"mappings":";;;;AAIA,MAAa,eAAe,WAAW;CACrC,MAAM;EACJ,MAAM;EACN,MAAM;GACJ,aAAa;GACb,aAAa;GACd;EACD,QAAQ,EAAE;EACX;CACD,WAAW,SAAS;AAClB,SAAyD,EAIvD,gBAAgB,MAAM;AACpB,OAAI,KAAK,SAAS,kBAAmB;AAErC,WAAQ,OAAO;IACb;IACA,SAAS;IACV,CAAC;KAEL;;CAEJ,CAAC"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as oxlint29 from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-top-level-let.d.ts
|
|
4
|
-
declare const noTopLevelLetRule: oxlint29.Rule;
|
|
5
|
-
type ESTNode = oxlint29.ESTree.Node;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { ESTNode, noTopLevelLetRule };
|
|
8
|
-
//# sourceMappingURL=no-top-level-let.d.mts.map
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { defineRule } from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-top-level-let.js
|
|
4
|
-
/** @typedef {import("oxlint").ESTree.Node} ESTNode */
|
|
5
|
-
/**
|
|
6
|
-
* Get the enclosing function node for a given node
|
|
7
|
-
* @param {ESTNode} node
|
|
8
|
-
* @returns {ESTNode | null}
|
|
9
|
-
*/
|
|
10
|
-
const getEnclosingFunction = (node) => {
|
|
11
|
-
const findFunction = (current) => {
|
|
12
|
-
if (!current) return null;
|
|
13
|
-
if (current.type === "FunctionDeclaration" || current.type === "FunctionExpression" || current.type === "ArrowFunctionExpression") return current;
|
|
14
|
-
return findFunction(current.parent);
|
|
15
|
-
};
|
|
16
|
-
return findFunction(node.parent);
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Check if a node is inside a loop
|
|
20
|
-
* @param {ESTNode} node
|
|
21
|
-
* @param {ESTNode} stopAt - Stop searching when we reach this node
|
|
22
|
-
* @returns {boolean}
|
|
23
|
-
*/
|
|
24
|
-
const isInsideLoop = (node, stopAt) => {
|
|
25
|
-
const checkLoop = (current) => {
|
|
26
|
-
if (!current || current === stopAt) return false;
|
|
27
|
-
if (current.type === "ForStatement" || current.type === "ForInStatement" || current.type === "ForOfStatement" || current.type === "WhileStatement" || current.type === "DoWhileStatement") return true;
|
|
28
|
-
return checkLoop(current.parent);
|
|
29
|
-
};
|
|
30
|
-
return checkLoop(node.parent);
|
|
31
|
-
};
|
|
32
|
-
const rule = defineRule({
|
|
33
|
-
meta: {
|
|
34
|
-
type: "problem",
|
|
35
|
-
docs: {
|
|
36
|
-
description: "Disallow top-level `let` declarations inside functions to prevent conditional reassignment.",
|
|
37
|
-
recommended: false
|
|
38
|
-
},
|
|
39
|
-
schema: []
|
|
40
|
-
},
|
|
41
|
-
createOnce(context) {
|
|
42
|
-
return { VariableDeclaration(rawNode) {
|
|
43
|
-
if (rawNode.type !== "VariableDeclaration") return;
|
|
44
|
-
const node = rawNode;
|
|
45
|
-
if (node.kind !== "let") return;
|
|
46
|
-
const fn = getEnclosingFunction(node);
|
|
47
|
-
if (!fn || fn.type !== "FunctionDeclaration" && fn.type !== "FunctionExpression" && fn.type !== "ArrowFunctionExpression") return;
|
|
48
|
-
const parent = node.parent;
|
|
49
|
-
if (!parent || parent.type !== "BlockStatement" || parent.parent !== fn) return;
|
|
50
|
-
if (isInsideLoop(node, fn)) return;
|
|
51
|
-
context.report({
|
|
52
|
-
node,
|
|
53
|
-
message: "Avoid using `let` at the top level of functions; prefer `const` with extracted functions to avoid conditional reassignment. Extract conditional logic into a separate function that returns the appropriate value."
|
|
54
|
-
});
|
|
55
|
-
} };
|
|
56
|
-
}
|
|
57
|
-
});
|
|
58
|
-
const noTopLevelLetRule = rule;
|
|
59
|
-
|
|
60
|
-
//#endregion
|
|
61
|
-
export { noTopLevelLetRule };
|
|
62
|
-
//# sourceMappingURL=no-top-level-let.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"no-top-level-let.mjs","names":[],"sources":["../../src/oxlint-plugins/no-top-level-let.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\n\n/** @typedef {import(\"oxlint\").ESTree.Node} ESTNode */\n\n/**\n * Get the enclosing function node for a given node\n * @param {ESTNode} node\n * @returns {ESTNode | null}\n */\nconst getEnclosingFunction = (node) => {\n const findFunction = (current) => {\n if (!current) return null;\n if (\n current.type === \"FunctionDeclaration\" ||\n current.type === \"FunctionExpression\" ||\n current.type === \"ArrowFunctionExpression\"\n ) {\n return current;\n }\n return findFunction(current.parent);\n };\n return findFunction(node.parent);\n};\n\n/**\n * Check if a node is inside a loop\n * @param {ESTNode} node\n * @param {ESTNode} stopAt - Stop searching when we reach this node\n * @returns {boolean}\n */\nconst isInsideLoop = (node, stopAt) => {\n const checkLoop = (current) => {\n if (!current || current === stopAt) return false;\n if (\n current.type === \"ForStatement\" ||\n current.type === \"ForInStatement\" ||\n current.type === \"ForOfStatement\" ||\n current.type === \"WhileStatement\" ||\n current.type === \"DoWhileStatement\"\n ) {\n return true;\n }\n return checkLoop(current.parent);\n };\n return checkLoop(node.parent);\n};\n\nconst rule = defineRule({\n meta: {\n type: \"problem\",\n docs: {\n description: \"Disallow top-level `let` declarations inside functions to prevent conditional reassignment.\",\n recommended: false,\n },\n schema: [],\n },\n\n createOnce(context) {\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\n /**\n * @param {ESTNode} rawNode\n */\n VariableDeclaration(rawNode) {\n if (rawNode.type !== \"VariableDeclaration\") return;\n const node = rawNode;\n\n if (node.kind !== \"let\") return;\n\n const fn = getEnclosingFunction(node);\n if (\n !fn ||\n (fn.type !== \"FunctionDeclaration\" &&\n fn.type !== \"FunctionExpression\" &&\n fn.type !== \"ArrowFunctionExpression\")\n ) {\n return;\n }\n\n const parent = node.parent;\n if (!parent || parent.type !== \"BlockStatement\" || parent.parent !== fn) return;\n\n // Allow let declarations inside loops\n if (isInsideLoop(node, fn)) return;\n\n context.report({\n node,\n message:\n \"Avoid using `let` at the top level of functions; prefer `const` with extracted functions to avoid conditional reassignment. Extract conditional logic into a separate function that returns the appropriate value.\",\n });\n },\n });\n },\n});\n\nexport const noTopLevelLetRule = rule;\n\n"],"mappings":";;;;;;;;;AASA,MAAM,wBAAwB,SAAS;CACrC,MAAM,gBAAgB,YAAY;AAChC,MAAI,CAAC,QAAS,QAAO;AACrB,MACE,QAAQ,SAAS,yBACjB,QAAQ,SAAS,wBACjB,QAAQ,SAAS,0BAEjB,QAAO;AAET,SAAO,aAAa,QAAQ,OAAO;;AAErC,QAAO,aAAa,KAAK,OAAO;;;;;;;;AASlC,MAAM,gBAAgB,MAAM,WAAW;CACrC,MAAM,aAAa,YAAY;AAC7B,MAAI,CAAC,WAAW,YAAY,OAAQ,QAAO;AAC3C,MACE,QAAQ,SAAS,kBACjB,QAAQ,SAAS,oBACjB,QAAQ,SAAS,oBACjB,QAAQ,SAAS,oBACjB,QAAQ,SAAS,mBAEjB,QAAO;AAET,SAAO,UAAU,QAAQ,OAAO;;AAElC,QAAO,UAAU,KAAK,OAAO;;AAG/B,MAAM,OAAO,WAAW;CACtB,MAAM;EACJ,MAAM;EACN,MAAM;GACJ,aAAa;GACb,aAAa;GACd;EACD,QAAQ,EAAE;EACX;CAED,WAAW,SAAS;AAClB,SAAyD,EAIvD,oBAAoB,SAAS;AAC3B,OAAI,QAAQ,SAAS,sBAAuB;GAC5C,MAAM,OAAO;AAEb,OAAI,KAAK,SAAS,MAAO;GAEzB,MAAM,KAAK,qBAAqB,KAAK;AACrC,OACE,CAAC,MACA,GAAG,SAAS,yBACX,GAAG,SAAS,wBACZ,GAAG,SAAS,0BAEd;GAGF,MAAM,SAAS,KAAK;AACpB,OAAI,CAAC,UAAU,OAAO,SAAS,oBAAoB,OAAO,WAAW,GAAI;AAGzE,OAAI,aAAa,MAAM,GAAG,CAAE;AAE5B,WAAQ,OAAO;IACb;IACA,SACE;IACH,CAAC;KAEL;;CAEJ,CAAC;AAEF,MAAa,oBAAoB"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import * as oxlint17 from "oxlint";
|
|
2
|
-
|
|
3
|
-
//#region src/oxlint-plugins/no-type-cast.d.ts
|
|
4
|
-
declare const noTypeCastRule: oxlint17.Rule;
|
|
5
|
-
type ESTNode = oxlint17.ESTree.Node;
|
|
6
|
-
//#endregion
|
|
7
|
-
export { ESTNode, noTypeCastRule };
|
|
8
|
-
//# sourceMappingURL=no-type-cast.d.mts.map
|