@conorroberts/utils 0.0.35 → 0.0.37
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/{env.d.ts → env.d.mts} +10 -7
- package/dist/env.mjs +32 -0
- package/dist/env.mjs.map +1 -0
- package/dist/images.d.mts +79 -0
- package/dist/images.mjs +129 -0
- package/dist/images.mjs.map +1 -0
- package/dist/oxlint/jsx-component-pascal-case.d.mts +25 -0
- package/dist/oxlint/jsx-component-pascal-case.mjs +159 -0
- package/dist/oxlint/jsx-component-pascal-case.mjs.map +1 -0
- package/dist/oxlint/no-component-date-instantiation.d.mts +27 -0
- package/dist/oxlint/no-component-date-instantiation.mjs +160 -0
- package/dist/oxlint/no-component-date-instantiation.mjs.map +1 -0
- package/dist/oxlint/no-emoji.d.mts +16 -0
- package/dist/oxlint/no-emoji.mjs +89 -0
- package/dist/oxlint/no-emoji.mjs.map +1 -0
- package/dist/oxlint/no-finally.d.mts +16 -0
- package/dist/oxlint/no-finally.mjs +32 -0
- package/dist/oxlint/no-finally.mjs.map +1 -0
- package/dist/oxlint/no-inline-components.d.mts +44 -0
- package/dist/oxlint/no-inline-components.mjs +350 -0
- package/dist/oxlint/no-inline-components.mjs.map +1 -0
- package/dist/oxlint/no-react-namespace.d.mts +16 -0
- package/dist/oxlint/no-react-namespace.mjs +75 -0
- package/dist/oxlint/no-react-namespace.mjs.map +1 -0
- package/dist/oxlint/no-switch-plugin.d.mts +15 -0
- package/dist/oxlint/no-switch-plugin.mjs +31 -0
- package/dist/oxlint/no-switch-plugin.mjs.map +1 -0
- package/dist/oxlint/no-top-level-let.d.mts +16 -0
- package/dist/oxlint/no-top-level-let.mjs +66 -0
- package/dist/oxlint/no-top-level-let.mjs.map +1 -0
- package/dist/oxlint/no-type-cast.d.mts +16 -0
- package/dist/oxlint/no-type-cast.mjs +49 -0
- package/dist/oxlint/no-type-cast.mjs.map +1 -0
- package/package.json +44 -6
- package/dist/env.js +0 -30
- package/dist/env.js.map +0 -1
- package/dist/images.d.ts +0 -77
- package/dist/images.js +0 -151
- package/dist/images.js.map +0 -1
|
@@ -0,0 +1,350 @@
|
|
|
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
|
+
return false;
|
|
140
|
+
};
|
|
141
|
+
/**
|
|
142
|
+
* @param {ESTPattern | null | undefined} pattern
|
|
143
|
+
* @param {string[]} names
|
|
144
|
+
*/
|
|
145
|
+
const collectBindingNames = (pattern, names) => {
|
|
146
|
+
if (!pattern || !isNode(pattern)) return;
|
|
147
|
+
const type = pattern.type;
|
|
148
|
+
if (type === "Identifier") {
|
|
149
|
+
names.push(pattern.name);
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (type === "ArrayPattern") {
|
|
153
|
+
for (const element of pattern.elements) {
|
|
154
|
+
if (!element) continue;
|
|
155
|
+
if (element.type === "RestElement") collectBindingNames(element.argument, names);
|
|
156
|
+
else collectBindingNames(element, names);
|
|
157
|
+
}
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (type === "ObjectPattern") {
|
|
161
|
+
for (const property of pattern.properties) {
|
|
162
|
+
if (!property) continue;
|
|
163
|
+
if (property.type === "Property") collectBindingNames(property.value, names);
|
|
164
|
+
else if (property.type === "RestElement") collectBindingNames(property.argument, names);
|
|
165
|
+
}
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (type === "AssignmentPattern") {
|
|
169
|
+
collectBindingNames(pattern.left, names);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (type === "RestElement") collectBindingNames(pattern.argument, names);
|
|
173
|
+
};
|
|
174
|
+
/**
|
|
175
|
+
* @param {FunctionLikeNode} node
|
|
176
|
+
*/
|
|
177
|
+
const getFunctionName = (node) => {
|
|
178
|
+
if (node.type === "FunctionDeclaration" && node.id && node.id.type === "Identifier") return node.id.name;
|
|
179
|
+
if ((node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && node.id) {
|
|
180
|
+
if (node.id.type === "Identifier") return node.id.name;
|
|
181
|
+
}
|
|
182
|
+
const parent = node.parent;
|
|
183
|
+
if (!parent || !isNode(parent)) return "";
|
|
184
|
+
if (parent.type === "VariableDeclarator") return parent.id && parent.id.type === "Identifier" ? parent.id.name : "";
|
|
185
|
+
if (parent.type === "AssignmentExpression") return parent.left && parent.left.type === "Identifier" ? parent.left.name : "";
|
|
186
|
+
if (parent.type === "Property" || parent.type === "MethodDefinition") return parent.key && parent.key.type === "Identifier" ? parent.key.name : "";
|
|
187
|
+
if (parent.type === "CallExpression") {
|
|
188
|
+
const callParent = parent.parent;
|
|
189
|
+
if (callParent && isNode(callParent)) {
|
|
190
|
+
if (callParent.type === "VariableDeclarator") return callParent.id && callParent.id.type === "Identifier" ? callParent.id.name : "";
|
|
191
|
+
if (callParent.type === "AssignmentExpression") return callParent.left && callParent.left.type === "Identifier" ? callParent.left.name : "";
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return "";
|
|
195
|
+
};
|
|
196
|
+
/**
|
|
197
|
+
* @param {string} name
|
|
198
|
+
*/
|
|
199
|
+
const describeFunction = (name) => name ? `function '${name}'` : "this function";
|
|
200
|
+
/**
|
|
201
|
+
* @param {string} name
|
|
202
|
+
*/
|
|
203
|
+
const describeNested = (name) => name ? `function '${name}'` : "an anonymous function";
|
|
204
|
+
/**
|
|
205
|
+
* @param {string[]} names
|
|
206
|
+
* @param {string} fnName
|
|
207
|
+
*/
|
|
208
|
+
const createAssignmentMessage = (names, fnName) => {
|
|
209
|
+
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.`;
|
|
210
|
+
};
|
|
211
|
+
/**
|
|
212
|
+
* @param {string} childName
|
|
213
|
+
* @param {string} parentName
|
|
214
|
+
*/
|
|
215
|
+
const createNestedFunctionMessage = (childName, parentName) => `JSX-returning ${describeNested(childName)} should not be declared inside ${describeFunction(parentName)}. Extract it to module scope.`;
|
|
216
|
+
/**
|
|
217
|
+
* @param {string} name
|
|
218
|
+
*/
|
|
219
|
+
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.`;
|
|
220
|
+
const rule = defineRule({
|
|
221
|
+
meta: {
|
|
222
|
+
type: "problem",
|
|
223
|
+
docs: {
|
|
224
|
+
description: "Disallow JSX-returning functions and JSX-valued assignments within other functions that also return JSX.",
|
|
225
|
+
recommended: false
|
|
226
|
+
},
|
|
227
|
+
schema: []
|
|
228
|
+
},
|
|
229
|
+
createOnce(context) {
|
|
230
|
+
/** @type {FunctionContext[]} */
|
|
231
|
+
const functionStack = [];
|
|
232
|
+
const currentFunction = () => functionStack[functionStack.length - 1] ?? null;
|
|
233
|
+
/**
|
|
234
|
+
* @param {FunctionLikeNode} node
|
|
235
|
+
*/
|
|
236
|
+
const enterFunction = (node) => {
|
|
237
|
+
/** @type {FunctionContext} */
|
|
238
|
+
const fnCtx = {
|
|
239
|
+
node,
|
|
240
|
+
parent: currentFunction(),
|
|
241
|
+
name: getFunctionName(node),
|
|
242
|
+
returnsJsx: false,
|
|
243
|
+
jsxBindingNames: /* @__PURE__ */ new Set(),
|
|
244
|
+
jsxAssignments: [],
|
|
245
|
+
nestedJsxChildren: []
|
|
246
|
+
};
|
|
247
|
+
functionStack.push(fnCtx);
|
|
248
|
+
if (node.type === "ArrowFunctionExpression" && node.body && node.body.type !== "BlockStatement") {
|
|
249
|
+
if (expressionProducesJsx(node.body, fnCtx.jsxBindingNames)) fnCtx.returnsJsx = true;
|
|
250
|
+
}
|
|
251
|
+
};
|
|
252
|
+
const exitFunction = () => {
|
|
253
|
+
const fnCtx = functionStack.pop();
|
|
254
|
+
if (!fnCtx) return;
|
|
255
|
+
if (fnCtx.returnsJsx && isFunctionImmediatelyInvoked(fnCtx.node)) {
|
|
256
|
+
context.report({
|
|
257
|
+
node: fnCtx.node,
|
|
258
|
+
message: createIIFEMessage(fnCtx.name)
|
|
259
|
+
});
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
262
|
+
if (fnCtx.parent && fnCtx.returnsJsx && fnCtx.name && !isFunctionUsedAsJsxProp(fnCtx.node)) fnCtx.parent.nestedJsxChildren.push({
|
|
263
|
+
node: fnCtx.node,
|
|
264
|
+
name: fnCtx.name
|
|
265
|
+
});
|
|
266
|
+
if (!fnCtx.returnsJsx) return;
|
|
267
|
+
for (const assignment of fnCtx.jsxAssignments) context.report({
|
|
268
|
+
node: assignment.node,
|
|
269
|
+
message: createAssignmentMessage(assignment.names, fnCtx.name)
|
|
270
|
+
});
|
|
271
|
+
for (const nested of fnCtx.nestedJsxChildren) context.report({
|
|
272
|
+
node: nested.node,
|
|
273
|
+
message: createNestedFunctionMessage(nested.name, fnCtx.name)
|
|
274
|
+
});
|
|
275
|
+
};
|
|
276
|
+
/** @param {ReturnStatementNode} node */
|
|
277
|
+
const handleReturnStatement = (node) => {
|
|
278
|
+
const fnCtx = currentFunction();
|
|
279
|
+
if (!fnCtx) return;
|
|
280
|
+
const argument = node.argument;
|
|
281
|
+
if (!argument || isFunctionLike(argument)) return;
|
|
282
|
+
if (expressionProducesJsx(argument, fnCtx.jsxBindingNames)) fnCtx.returnsJsx = true;
|
|
283
|
+
};
|
|
284
|
+
/** @param {VariableDeclaratorNode} node */
|
|
285
|
+
const handleVariableDeclarator = (node) => {
|
|
286
|
+
const fnCtx = currentFunction();
|
|
287
|
+
if (!fnCtx) return;
|
|
288
|
+
const init = node.init;
|
|
289
|
+
if (!init || isFunctionLike(init)) return;
|
|
290
|
+
if (!expressionContainsJsx(init)) return;
|
|
291
|
+
const names = [];
|
|
292
|
+
collectBindingNames(node.id, names);
|
|
293
|
+
for (const name of names) fnCtx.jsxBindingNames.add(name);
|
|
294
|
+
fnCtx.jsxAssignments.push({
|
|
295
|
+
node: init,
|
|
296
|
+
names
|
|
297
|
+
});
|
|
298
|
+
};
|
|
299
|
+
/** @param {AssignmentExpressionNode} node */
|
|
300
|
+
const handleAssignmentExpression = (node) => {
|
|
301
|
+
if (node.operator !== "=") return;
|
|
302
|
+
const fnCtx = currentFunction();
|
|
303
|
+
if (!fnCtx) return;
|
|
304
|
+
const right = node.right;
|
|
305
|
+
if (!right || isFunctionLike(right)) return;
|
|
306
|
+
if (!expressionContainsJsx(right)) return;
|
|
307
|
+
const names = [];
|
|
308
|
+
if (node.left && node.left.type === "Identifier") {
|
|
309
|
+
names.push(node.left.name);
|
|
310
|
+
fnCtx.jsxBindingNames.add(node.left.name);
|
|
311
|
+
}
|
|
312
|
+
fnCtx.jsxAssignments.push({
|
|
313
|
+
node: right,
|
|
314
|
+
names
|
|
315
|
+
});
|
|
316
|
+
};
|
|
317
|
+
return {
|
|
318
|
+
FunctionDeclaration(node) {
|
|
319
|
+
if (isFunctionLike(node)) enterFunction(node);
|
|
320
|
+
},
|
|
321
|
+
"FunctionDeclaration:exit": exitFunction,
|
|
322
|
+
FunctionExpression(node) {
|
|
323
|
+
if (isFunctionLike(node)) enterFunction(node);
|
|
324
|
+
},
|
|
325
|
+
"FunctionExpression:exit": exitFunction,
|
|
326
|
+
ArrowFunctionExpression(node) {
|
|
327
|
+
if (isFunctionLike(node)) enterFunction(node);
|
|
328
|
+
},
|
|
329
|
+
"ArrowFunctionExpression:exit": exitFunction,
|
|
330
|
+
ReturnStatement(node) {
|
|
331
|
+
if (node.type === "ReturnStatement") handleReturnStatement(node);
|
|
332
|
+
},
|
|
333
|
+
VariableDeclarator(node) {
|
|
334
|
+
if (node.type === "VariableDeclarator") handleVariableDeclarator(node);
|
|
335
|
+
},
|
|
336
|
+
AssignmentExpression(node) {
|
|
337
|
+
if (node.type === "AssignmentExpression") handleAssignmentExpression(node);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
const noInlineComponentsRule = rule;
|
|
343
|
+
var no_inline_components_default = {
|
|
344
|
+
meta: { name: "no-inline-components" },
|
|
345
|
+
rules: { "no-inline-components": rule }
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
//#endregion
|
|
349
|
+
export { no_inline_components_default as default, getEnclosingFunction, getFunctionName, isComponentName, isHookName, noInlineComponentsRule };
|
|
350
|
+
//# sourceMappingURL=no-inline-components.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-inline-components.mjs","names":[],"sources":["../../src/oxlint-plugins/no-inline-components.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\r\n\r\n/**\r\n * @typedef {import(\"oxlint\").Context} RuleContext\r\n * @typedef {import(\"oxlint\").ESTree.Node} ESTNode\r\n * @typedef {import(\"oxlint\").ESTree.Expression} ESTExpression\r\n * @typedef {import(\"oxlint\").ESTree.Pattern} ESTPattern\r\n * @typedef {import(\"oxlint\").ESTree.ReturnStatement} ReturnStatementNode\r\n * @typedef {import(\"oxlint\").ESTree.VariableDeclarator} VariableDeclaratorNode\r\n * @typedef {import(\"oxlint\").ESTree.AssignmentExpression} AssignmentExpressionNode\r\n * @typedef {import(\"oxlint\").ESTree.Function | import(\"oxlint\").ESTree.ArrowFunctionExpression} FunctionLikeNode\r\n */\r\n\r\n/**\r\n * @typedef {object} RecordedAssignment\r\n * @property {ESTExpression} node\r\n * @property {string[]} names\r\n */\r\n\r\n/**\r\n * @typedef {object} NestedFunctionRecord\r\n * @property {FunctionLikeNode} node\r\n * @property {string} name\r\n */\r\n\r\n/**\r\n * @typedef {object} FunctionContext\r\n * @property {FunctionLikeNode} node\r\n * @property {FunctionContext | null} parent\r\n * @property {string} name\r\n * @property {boolean} returnsJsx\r\n * @property {Set<string>} jsxBindingNames\r\n * @property {RecordedAssignment[]} jsxAssignments\r\n * @property {NestedFunctionRecord[]} nestedJsxChildren\r\n */\r\n\r\nconst JSX_NODE_TYPES = new Set([\"JSXElement\", \"JSXFragment\"]);\r\nconst FUNCTION_NODE_TYPES = new Set([\"FunctionDeclaration\", \"FunctionExpression\", \"ArrowFunctionExpression\"]);\r\n\r\n/**\r\n * @param {unknown} name\r\n * @returns {name is string}\r\n */\r\nexport const isComponentName = (name) => typeof name === \"string\" && /^[A-Z]/.test(name);\r\n\r\n/**\r\n * @param {unknown} name\r\n * @returns {name is string}\r\n */\r\nexport const isHookName = (name) => typeof name === \"string\" && name.startsWith(\"use\");\r\n\r\n/**\r\n * @param {unknown} node\r\n * @returns {node is ESTNode & { type: string }}\r\n */\r\nconst isNode = (node) => Boolean(node && typeof node === \"object\" && \"type\" in node);\r\n\r\n/**\r\n * @param {unknown} node\r\n * @returns {node is FunctionLikeNode}\r\n */\r\nconst isFunctionLike = (node) => isNode(node) && FUNCTION_NODE_TYPES.has(node.type);\r\n\r\n/**\r\n * @param {ESTNode | null | undefined} node\r\n * @returns {FunctionLikeNode | null}\r\n */\r\nexport const getEnclosingFunction = (node) => {\r\n const findFunction = (current) => {\r\n if (!current) return null;\r\n if (isFunctionLike(current)) {\r\n return current;\r\n }\r\n return findFunction(isNode(current) ? (current.parent ?? null) : null);\r\n };\r\n return findFunction(isNode(node) ? (node.parent ?? null) : null);\r\n};\r\n\r\n/**\r\n * @param {FunctionLikeNode} node\r\n */\r\nconst isFunctionUsedAsJsxProp = (node) => {\r\n const checkJsxProp = (current) => {\r\n if (!current) return false;\r\n if (current.type === \"JSXAttribute\") {\r\n return true;\r\n }\r\n if (isFunctionLike(current)) {\r\n return false;\r\n }\r\n return checkJsxProp(isNode(current) ? (current.parent ?? null) : null);\r\n };\r\n return checkJsxProp(isNode(node) ? (node.parent ?? null) : null);\r\n};\r\n\r\n/**\r\n * @param {FunctionLikeNode} node\r\n */\r\nconst isFunctionImmediatelyInvoked = (node) => {\r\n const parent = isNode(node) ? (node.parent ?? null) : null;\r\n if (!parent) return false;\r\n\r\n // Check if the function is the callee of a CallExpression (i.e., it's immediately invoked)\r\n if (parent.type === \"CallExpression\" && parent.callee === node) {\r\n return true;\r\n }\r\n\r\n return false;\r\n};\r\n\r\n/**\r\n * @param {ESTExpression | null | undefined} root\r\n */\r\nconst expressionContainsJsx = (root) => {\r\n if (!root || !isNode(root)) return false;\r\n\r\n const stack = [root];\r\n\r\n while (stack.length > 0) {\r\n const current = stack.pop();\r\n if (!current || !isNode(current)) continue;\r\n\r\n if (JSX_NODE_TYPES.has(current.type)) {\r\n return true;\r\n }\r\n\r\n if (FUNCTION_NODE_TYPES.has(current.type) && current !== root) {\r\n continue;\r\n }\r\n\r\n for (const key of Object.keys(current)) {\r\n if (key === \"parent\") continue;\r\n\r\n const value = current[key];\r\n if (!value) continue;\r\n\r\n if (Array.isArray(value)) {\r\n for (const element of value) {\r\n if (isNode(element)) {\r\n stack.push(element);\r\n }\r\n }\r\n } else if (isNode(value)) {\r\n stack.push(value);\r\n }\r\n }\r\n }\r\n\r\n return false;\r\n};\r\n\r\n/**\r\n * @param {ESTExpression | null | undefined} root\r\n * @param {Set<string>} bindingNames\r\n */\r\nconst expressionProducesJsx = (root, bindingNames) => {\r\n if (!root) return false;\r\n if (expressionContainsJsx(root)) return true;\r\n\r\n if (!isNode(root)) return false;\r\n\r\n const type = root.type;\r\n\r\n if (type === \"Identifier\") {\r\n return bindingNames.has(root.name);\r\n }\r\n\r\n if (type === \"ConditionalExpression\") {\r\n return expressionProducesJsx(root.consequent, bindingNames) || expressionProducesJsx(root.alternate, bindingNames);\r\n }\r\n\r\n if (type === \"LogicalExpression\") {\r\n return expressionProducesJsx(root.left, bindingNames) || expressionProducesJsx(root.right, bindingNames);\r\n }\r\n\r\n if (type === \"SequenceExpression\") {\r\n const expressions = root.expressions ?? [];\r\n const last = expressions[expressions.length - 1] ?? null;\r\n return expressionProducesJsx(last, bindingNames);\r\n }\r\n\r\n if (type === \"ArrayExpression\") {\r\n return root.elements.some((element) => {\r\n if (!element) return false;\r\n if (element.type === \"SpreadElement\") {\r\n return expressionProducesJsx(element.argument, bindingNames);\r\n }\r\n return expressionProducesJsx(element, bindingNames);\r\n });\r\n }\r\n\r\n if (type === \"ParenthesizedExpression\") {\r\n return expressionProducesJsx(root.expression, bindingNames);\r\n }\r\n\r\n if (type === \"AwaitExpression\" || type === \"UnaryExpression\" || type === \"UpdateExpression\") {\r\n return expressionProducesJsx(root.argument, bindingNames);\r\n }\r\n\r\n if (\r\n type === \"TSAsExpression\" ||\r\n type === \"TSTypeAssertion\" ||\r\n type === \"TSNonNullExpression\" ||\r\n type === \"ChainExpression\"\r\n ) {\r\n return expressionProducesJsx(root.expression, bindingNames);\r\n }\r\n\r\n return false;\r\n};\r\n\r\n/**\r\n * @param {ESTPattern | null | undefined} pattern\r\n * @param {string[]} names\r\n */\r\nconst collectBindingNames = (pattern, names) => {\r\n if (!pattern || !isNode(pattern)) return;\r\n\r\n const type = pattern.type;\r\n\r\n if (type === \"Identifier\") {\r\n names.push(pattern.name);\r\n return;\r\n }\r\n\r\n if (type === \"ArrayPattern\") {\r\n for (const element of pattern.elements) {\r\n if (!element) continue;\r\n if (element.type === \"RestElement\") {\r\n collectBindingNames(element.argument, names);\r\n } else {\r\n collectBindingNames(element, names);\r\n }\r\n }\r\n return;\r\n }\r\n\r\n if (type === \"ObjectPattern\") {\r\n for (const property of pattern.properties) {\r\n if (!property) continue;\r\n if (property.type === \"Property\") {\r\n collectBindingNames(property.value, names);\r\n } else if (property.type === \"RestElement\") {\r\n collectBindingNames(property.argument, names);\r\n }\r\n }\r\n return;\r\n }\r\n\r\n if (type === \"AssignmentPattern\") {\r\n collectBindingNames(pattern.left, names);\r\n return;\r\n }\r\n\r\n if (type === \"RestElement\") {\r\n collectBindingNames(pattern.argument, names);\r\n }\r\n};\r\n\r\n/**\r\n * @param {FunctionLikeNode} node\r\n */\r\nexport const getFunctionName = (node) => {\r\n if (node.type === \"FunctionDeclaration\" && node.id && node.id.type === \"Identifier\") {\r\n return node.id.name;\r\n }\r\n\r\n if ((node.type === \"FunctionExpression\" || node.type === \"ArrowFunctionExpression\") && node.id) {\r\n if (node.id.type === \"Identifier\") return node.id.name;\r\n }\r\n\r\n const parent = node.parent;\r\n if (!parent || !isNode(parent)) return \"\";\r\n\r\n if (parent.type === \"VariableDeclarator\") {\r\n return parent.id && parent.id.type === \"Identifier\" ? parent.id.name : \"\";\r\n }\r\n\r\n if (parent.type === \"AssignmentExpression\") {\r\n return parent.left && parent.left.type === \"Identifier\" ? parent.left.name : \"\";\r\n }\r\n\r\n if (parent.type === \"Property\" || parent.type === \"MethodDefinition\") {\r\n return parent.key && parent.key.type === \"Identifier\" ? parent.key.name : \"\";\r\n }\r\n\r\n // Handle functions passed as arguments to calls (e.g., useCallback, useMemo)\r\n if (parent.type === \"CallExpression\") {\r\n const callParent = parent.parent;\r\n if (callParent && isNode(callParent)) {\r\n if (callParent.type === \"VariableDeclarator\") {\r\n return callParent.id && callParent.id.type === \"Identifier\" ? callParent.id.name : \"\";\r\n }\r\n if (callParent.type === \"AssignmentExpression\") {\r\n return callParent.left && callParent.left.type === \"Identifier\" ? callParent.left.name : \"\";\r\n }\r\n }\r\n }\r\n\r\n return \"\";\r\n};\r\n\r\n/**\r\n * @param {string} name\r\n */\r\nconst describeFunction = (name) => (name ? `function '${name}'` : \"this function\");\r\n\r\n/**\r\n * @param {string} name\r\n */\r\nconst describeNested = (name) => (name ? `function '${name}'` : \"an anonymous function\");\r\n\r\n/**\r\n * @param {string[]} names\r\n * @param {string} fnName\r\n */\r\nconst createAssignmentMessage = (names, fnName) => {\r\n const target =\r\n names.length === 0\r\n ? \"local variables\"\r\n : names.length === 1\r\n ? `local '${names[0]}'`\r\n : `locals ${names.map((name) => `'${name}'`).join(\", \")}`;\r\n\r\n return `Avoid storing JSX in ${target} inside ${describeFunction(fnName)}; return the JSX directly instead.`;\r\n};\r\n\r\n/**\r\n * @param {string} childName\r\n * @param {string} parentName\r\n */\r\nconst createNestedFunctionMessage = (childName, parentName) =>\r\n `JSX-returning ${describeNested(childName)} should not be declared inside ${describeFunction(parentName)}. Extract it to module scope.`;\r\n\r\n/**\r\n * @param {string} name\r\n */\r\nconst createIIFEMessage = (name) =>\r\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.`;\r\n\r\nconst rule = defineRule({\r\n meta: {\r\n type: \"problem\",\r\n docs: {\r\n description:\r\n \"Disallow JSX-returning functions and JSX-valued assignments within other functions that also return JSX.\",\r\n recommended: false,\r\n },\r\n schema: [],\r\n },\r\n\r\n createOnce(context) {\r\n /** @type {FunctionContext[]} */\r\n const functionStack = [];\r\n\r\n const currentFunction = () => functionStack[functionStack.length - 1] ?? null;\r\n\r\n /**\r\n * @param {FunctionLikeNode} node\r\n */\r\n const enterFunction = (node) => {\r\n const parent = currentFunction();\r\n /** @type {FunctionContext} */\r\n const fnCtx = {\r\n node,\r\n parent,\r\n name: getFunctionName(node),\r\n returnsJsx: false,\r\n jsxBindingNames: new Set(),\r\n jsxAssignments: [],\r\n nestedJsxChildren: [],\r\n };\r\n\r\n functionStack.push(fnCtx);\r\n\r\n if (node.type === \"ArrowFunctionExpression\" && node.body && node.body.type !== \"BlockStatement\") {\r\n if (expressionProducesJsx(node.body, fnCtx.jsxBindingNames)) {\r\n fnCtx.returnsJsx = true;\r\n }\r\n }\r\n };\r\n\r\n const exitFunction = () => {\r\n const fnCtx = functionStack.pop();\r\n if (!fnCtx) return;\r\n\r\n if (fnCtx.returnsJsx && isFunctionImmediatelyInvoked(fnCtx.node)) {\r\n context.report({\r\n node: fnCtx.node,\r\n message: createIIFEMessage(fnCtx.name),\r\n });\r\n return;\r\n }\r\n\r\n if (fnCtx.parent && fnCtx.returnsJsx && fnCtx.name && !isFunctionUsedAsJsxProp(fnCtx.node)) {\r\n fnCtx.parent.nestedJsxChildren.push({ node: fnCtx.node, name: fnCtx.name });\r\n }\r\n\r\n if (!fnCtx.returnsJsx) return;\r\n\r\n for (const assignment of fnCtx.jsxAssignments) {\r\n context.report({\r\n node: assignment.node,\r\n message: createAssignmentMessage(assignment.names, fnCtx.name),\r\n });\r\n }\r\n\r\n for (const nested of fnCtx.nestedJsxChildren) {\r\n context.report({\r\n node: nested.node,\r\n message: createNestedFunctionMessage(nested.name, fnCtx.name),\r\n });\r\n }\r\n };\r\n\r\n /** @param {ReturnStatementNode} node */\r\n const handleReturnStatement = (node) => {\r\n const fnCtx = currentFunction();\r\n if (!fnCtx) return;\r\n\r\n const argument = node.argument;\r\n if (!argument || isFunctionLike(argument)) return;\r\n\r\n if (expressionProducesJsx(argument, fnCtx.jsxBindingNames)) {\r\n fnCtx.returnsJsx = true;\r\n }\r\n };\r\n\r\n /** @param {VariableDeclaratorNode} node */\r\n const handleVariableDeclarator = (node) => {\r\n const fnCtx = currentFunction();\r\n if (!fnCtx) return;\r\n\r\n const init = node.init;\r\n if (!init || isFunctionLike(init)) return;\r\n if (!expressionContainsJsx(init)) return;\r\n\r\n const names = [];\r\n collectBindingNames(node.id, names);\r\n for (const name of names) {\r\n fnCtx.jsxBindingNames.add(name);\r\n }\r\n\r\n fnCtx.jsxAssignments.push({ node: init, names });\r\n };\r\n\r\n /** @param {AssignmentExpressionNode} node */\r\n const handleAssignmentExpression = (node) => {\r\n if (node.operator !== \"=\") return;\r\n\r\n const fnCtx = currentFunction();\r\n if (!fnCtx) return;\r\n\r\n const right = node.right;\r\n if (!right || isFunctionLike(right)) return;\r\n if (!expressionContainsJsx(right)) return;\r\n\r\n const names = [];\r\n if (node.left && node.left.type === \"Identifier\") {\r\n names.push(node.left.name);\r\n fnCtx.jsxBindingNames.add(node.left.name);\r\n }\r\n\r\n fnCtx.jsxAssignments.push({ node: right, names });\r\n };\r\n\r\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\r\n FunctionDeclaration(node) {\r\n if (isFunctionLike(node)) enterFunction(node);\r\n },\r\n \"FunctionDeclaration:exit\": exitFunction,\r\n FunctionExpression(node) {\r\n if (isFunctionLike(node)) enterFunction(node);\r\n },\r\n \"FunctionExpression:exit\": exitFunction,\r\n ArrowFunctionExpression(node) {\r\n if (isFunctionLike(node)) enterFunction(node);\r\n },\r\n \"ArrowFunctionExpression:exit\": exitFunction,\r\n ReturnStatement(node) {\r\n if (node.type === \"ReturnStatement\") handleReturnStatement(node);\r\n },\r\n VariableDeclarator(node) {\r\n if (node.type === \"VariableDeclarator\") handleVariableDeclarator(node);\r\n },\r\n AssignmentExpression(node) {\r\n if (node.type === \"AssignmentExpression\") handleAssignmentExpression(node);\r\n },\r\n });\r\n },\r\n});\r\n\r\nexport const noInlineComponentsRule = rule;\r\n\r\nexport default {\r\n meta: { name: \"no-inline-components\" },\r\n rules: { \"no-inline-components\": rule },\r\n};\r\n"],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as oxlint27 from "oxlint";
|
|
2
|
+
|
|
3
|
+
//#region src/oxlint-plugins/no-react-namespace.d.ts
|
|
4
|
+
declare const noReactNamespaceRule: oxlint27.Rule;
|
|
5
|
+
declare namespace _default {
|
|
6
|
+
namespace meta {
|
|
7
|
+
let name: string;
|
|
8
|
+
}
|
|
9
|
+
let rules: {
|
|
10
|
+
"no-react-namespace": oxlint27.Rule;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
type ESTNode = oxlint27.ESTree.Node;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ESTNode, _default as default, noReactNamespaceRule };
|
|
16
|
+
//# sourceMappingURL=no-react-namespace.d.mts.map
|
|
@@ -0,0 +1,75 @@
|
|
|
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
|
+
var no_react_namespace_default = {
|
|
69
|
+
meta: { name: "no-react-namespace" },
|
|
70
|
+
rules: { "no-react-namespace": rule }
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
//#endregion
|
|
74
|
+
export { no_react_namespace_default as default, noReactNamespaceRule };
|
|
75
|
+
//# sourceMappingURL=no-react-namespace.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-react-namespace.mjs","names":[],"sources":["../../src/oxlint-plugins/no-react-namespace.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\r\n\r\n/** @typedef {import(\"oxlint\").ESTree.Node} ESTNode */\r\n\r\n/**\r\n * Check if a MemberExpression is accessing the React namespace\r\n * @param {ESTNode} node\r\n * @returns {boolean}\r\n */\r\nconst isReactNamespaceAccess = (node) => {\r\n if (node.type !== \"MemberExpression\") return false;\r\n\r\n const object = node.object;\r\n if (!object || object.type !== \"Identifier\" || object.name !== \"React\") {\r\n return false;\r\n }\r\n\r\n return true;\r\n};\r\n\r\n/**\r\n * Check if this is a type annotation context (TypeScript)\r\n * @param {ESTNode} node\r\n * @returns {boolean}\r\n */\r\nconst isTypeContext = (node) => {\r\n const checkParent = (current) => {\r\n if (!current) return false;\r\n\r\n // Type annotation contexts where React namespace is allowed\r\n const typeContextTypes = new Set([\r\n \"TSTypeReference\",\r\n \"TSTypeAnnotation\",\r\n \"TSTypeParameterInstantiation\",\r\n \"TSInterfaceHeritage\",\r\n \"TSTypeQuery\",\r\n \"TSTypeAliasDeclaration\",\r\n \"TSInterfaceDeclaration\",\r\n \"TSTypeLiteral\",\r\n \"TSPropertySignature\",\r\n \"TSIndexSignature\",\r\n \"TSMethodSignature\",\r\n \"TSCallSignatureDeclaration\",\r\n \"TSConstructSignatureDeclaration\",\r\n \"TSExpressionWithTypeArguments\",\r\n ]);\r\n\r\n if (typeContextTypes.has(current.type)) {\r\n return true;\r\n }\r\n\r\n // Stop at statement or expression boundaries\r\n if (\r\n current.type === \"ExpressionStatement\" ||\r\n current.type === \"VariableDeclarator\" ||\r\n current.type === \"CallExpression\" ||\r\n current.type === \"ReturnStatement\"\r\n ) {\r\n return false;\r\n }\r\n\r\n return checkParent(current.parent);\r\n };\r\n\r\n return checkParent(node.parent);\r\n};\r\n\r\nconst rule = defineRule({\r\n meta: {\r\n type: \"problem\",\r\n docs: {\r\n description:\r\n \"Disallow using the React namespace for accessing React APIs. Use destructured imports instead (e.g., import { useState } from 'react').\",\r\n recommended: true,\r\n },\r\n schema: [],\r\n },\r\n createOnce(context) {\r\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\r\n /**\r\n * @param {ESTNode} node\r\n */\r\n MemberExpression(node) {\r\n if (node.type !== \"MemberExpression\") return;\r\n\r\n if (!isReactNamespaceAccess(node)) return;\r\n\r\n // Allow React namespace in type annotations\r\n if (isTypeContext(node)) return;\r\n\r\n const propertyName = node.property && node.property.type === \"Identifier\" ? node.property.name : \"property\";\r\n\r\n context.report({\r\n node,\r\n message: `Avoid using 'React.${propertyName}'. Import '${propertyName}' directly from 'react' instead (e.g., import { ${propertyName} } from 'react').`,\r\n });\r\n },\r\n });\r\n },\r\n});\r\n\r\nexport const noReactNamespaceRule = rule;\r\n\r\nexport default {\r\n meta: { name: \"no-react-namespace\" },\r\n rules: { \"no-react-namespace\": rule },\r\n};\r\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;AAEpC,iCAAe;CACb,MAAM,EAAE,MAAM,sBAAsB;CACpC,OAAO,EAAE,sBAAsB,MAAM;CACtC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import * as oxlint33 from "oxlint";
|
|
2
|
+
|
|
3
|
+
//#region src/oxlint-plugins/no-switch-plugin.d.ts
|
|
4
|
+
declare namespace _default {
|
|
5
|
+
namespace meta {
|
|
6
|
+
let name: string;
|
|
7
|
+
}
|
|
8
|
+
let rules: {
|
|
9
|
+
"no-switch": oxlint33.Rule;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
type ESTNode = oxlint33.ESTree.Node;
|
|
13
|
+
//#endregion
|
|
14
|
+
export { ESTNode, _default as default };
|
|
15
|
+
//# sourceMappingURL=no-switch-plugin.d.mts.map
|
|
@@ -0,0 +1,31 @@
|
|
|
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
|
+
var no_switch_plugin_default = {
|
|
25
|
+
meta: { name: "no-switch-plugin" },
|
|
26
|
+
rules: { "no-switch": noSwitchRule }
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
//#endregion
|
|
30
|
+
export { no_switch_plugin_default as default };
|
|
31
|
+
//# sourceMappingURL=no-switch-plugin.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
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\nconst 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\nexport default {\n meta: {\n name: \"no-switch-plugin\",\n },\n rules: {\n \"no-switch\": noSwitchRule,\n },\n};\n"],"mappings":";;;;AAIA,MAAM,eAAe,WAAW;CAC9B,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;AAEF,+BAAe;CACb,MAAM,EACJ,MAAM,oBACP;CACD,OAAO,EACL,aAAa,cACd;CACF"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import * as oxlint10 from "oxlint";
|
|
2
|
+
|
|
3
|
+
//#region src/oxlint-plugins/no-top-level-let.d.ts
|
|
4
|
+
declare const noTopLevelLetRule: oxlint10.Rule;
|
|
5
|
+
declare namespace _default {
|
|
6
|
+
namespace meta {
|
|
7
|
+
let name: string;
|
|
8
|
+
}
|
|
9
|
+
let rules: {
|
|
10
|
+
"no-top-level-let": oxlint10.Rule;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
type ESTNode = oxlint10.ESTree.Node;
|
|
14
|
+
//#endregion
|
|
15
|
+
export { ESTNode, _default as default, noTopLevelLetRule };
|
|
16
|
+
//# sourceMappingURL=no-top-level-let.d.mts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
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
|
+
var no_top_level_let_default = {
|
|
60
|
+
meta: { name: "no-top-level-let" },
|
|
61
|
+
rules: { "no-top-level-let": rule }
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
//#endregion
|
|
65
|
+
export { no_top_level_let_default as default, noTopLevelLetRule };
|
|
66
|
+
//# sourceMappingURL=no-top-level-let.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"no-top-level-let.mjs","names":[],"sources":["../../src/oxlint-plugins/no-top-level-let.js"],"sourcesContent":["import { defineRule } from \"oxlint\";\r\n\r\n/** @typedef {import(\"oxlint\").ESTree.Node} ESTNode */\r\n\r\n/**\r\n * Get the enclosing function node for a given node\r\n * @param {ESTNode} node\r\n * @returns {ESTNode | null}\r\n */\r\nconst getEnclosingFunction = (node) => {\r\n const findFunction = (current) => {\r\n if (!current) return null;\r\n if (\r\n current.type === \"FunctionDeclaration\" ||\r\n current.type === \"FunctionExpression\" ||\r\n current.type === \"ArrowFunctionExpression\"\r\n ) {\r\n return current;\r\n }\r\n return findFunction(current.parent);\r\n };\r\n return findFunction(node.parent);\r\n};\r\n\r\n/**\r\n * Check if a node is inside a loop\r\n * @param {ESTNode} node\r\n * @param {ESTNode} stopAt - Stop searching when we reach this node\r\n * @returns {boolean}\r\n */\r\nconst isInsideLoop = (node, stopAt) => {\r\n const checkLoop = (current) => {\r\n if (!current || current === stopAt) return false;\r\n if (\r\n current.type === \"ForStatement\" ||\r\n current.type === \"ForInStatement\" ||\r\n current.type === \"ForOfStatement\" ||\r\n current.type === \"WhileStatement\" ||\r\n current.type === \"DoWhileStatement\"\r\n ) {\r\n return true;\r\n }\r\n return checkLoop(current.parent);\r\n };\r\n return checkLoop(node.parent);\r\n};\r\n\r\nconst rule = defineRule({\r\n meta: {\r\n type: \"problem\",\r\n docs: {\r\n description: \"Disallow top-level `let` declarations inside functions to prevent conditional reassignment.\",\r\n recommended: false,\r\n },\r\n schema: [],\r\n },\r\n\r\n createOnce(context) {\r\n return /** @type {import(\"oxlint\").VisitorWithHooks} */ ({\r\n /**\r\n * @param {ESTNode} rawNode\r\n */\r\n VariableDeclaration(rawNode) {\r\n if (rawNode.type !== \"VariableDeclaration\") return;\r\n const node = rawNode;\r\n\r\n if (node.kind !== \"let\") return;\r\n\r\n const fn = getEnclosingFunction(node);\r\n if (\r\n !fn ||\r\n (fn.type !== \"FunctionDeclaration\" &&\r\n fn.type !== \"FunctionExpression\" &&\r\n fn.type !== \"ArrowFunctionExpression\")\r\n ) {\r\n return;\r\n }\r\n\r\n const parent = node.parent;\r\n if (!parent || parent.type !== \"BlockStatement\" || parent.parent !== fn) return;\r\n\r\n // Allow let declarations inside loops\r\n if (isInsideLoop(node, fn)) return;\r\n\r\n context.report({\r\n node,\r\n message:\r\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.\",\r\n });\r\n },\r\n });\r\n },\r\n});\r\n\r\nexport const noTopLevelLetRule = rule;\r\n\r\nexport default {\r\n meta: { name: \"no-top-level-let\" },\r\n rules: { \"no-top-level-let\": rule },\r\n};\r\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;AAEjC,+BAAe;CACb,MAAM,EAAE,MAAM,oBAAoB;CAClC,OAAO,EAAE,oBAAoB,MAAM;CACpC"}
|