@thomaflette/eslint-plugin-solid-2 0.1.0

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/index.cjs ADDED
@@ -0,0 +1,1920 @@
1
+ //#region \0rolldown/runtime.js
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
10
+ key = keys[i];
11
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
12
+ get: ((k) => from[k]).bind(null, key),
13
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
14
+ });
15
+ }
16
+ return to;
17
+ };
18
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
19
+ value: mod,
20
+ enumerable: true
21
+ }) : target, mod));
22
+ //#endregion
23
+ let _typescript_eslint_utils = require("@typescript-eslint/utils");
24
+ let typescript = require("typescript");
25
+ typescript = __toESM(typescript, 1);
26
+ //#region src/utils.ts
27
+ const domElementRegex = /^[a-z]/;
28
+ const isDOMElementName = (name) => domElementRegex.test(name);
29
+ /**
30
+ * Whether a JSX opening element is a host (DOM) element rather than a custom component. DOM-only
31
+ * DOM-only rules must skip components, whose attributes are author-defined props with no DOM
32
+ * semantics. A lowercase tag (`<div>`) or a namespaced tag (`<svg:rect>`) is a
33
+ * host element; a capitalized tag (`<Card>`) or a member tag (`<Foo.Bar>`) is a component.
34
+ */
35
+ const isHostElement = (opening) => {
36
+ const tag = opening.name;
37
+ if (tag.type === "JSXIdentifier") return isDOMElementName(tag.name);
38
+ return tag.type === "JSXNamespacedName";
39
+ };
40
+ const getFunctionName = (node) => {
41
+ if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression") && node.id != null) return node.id.name;
42
+ if (node.parent?.type === "VariableDeclarator" && node.parent.id.type === "Identifier") return node.parent.id.name;
43
+ return null;
44
+ };
45
+ const isFunctionNode = (node) => node?.type === "FunctionDeclaration" || node?.type === "FunctionExpression" || node?.type === "ArrowFunctionExpression";
46
+ const isJSXElementOrFragment = (node) => node?.type === "JSXElement" || node?.type === "JSXFragment";
47
+ const trace = (node, context) => {
48
+ if (node.type !== "Identifier") return node;
49
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(context.sourceCode.getScope(node), node);
50
+ const def = variable?.defs[0];
51
+ if (!variable || !def) return node;
52
+ switch (def.type) {
53
+ case "FunctionName":
54
+ case "ClassName":
55
+ case "ImportBinding": return def.node;
56
+ case "Variable": {
57
+ const declaration = def.node.parent;
58
+ if (declaration?.type === "VariableDeclaration" && declaration.kind === "const" && def.node.id.type === "Identifier" && def.node.init) return trace(def.node.init, context);
59
+ return node;
60
+ }
61
+ default: return node;
62
+ }
63
+ };
64
+ function jsxPropName(prop) {
65
+ if (prop.name.type === "JSXNamespacedName") return `${prop.name.namespace.name}:${prop.name.name.name}`;
66
+ return prop.name.name;
67
+ }
68
+ function* jsxGetAllProps(props) {
69
+ for (const attr of props) {
70
+ if (attr.type === "JSXSpreadAttribute" && attr.argument.type === "ObjectExpression") {
71
+ for (const property of attr.argument.properties) if (property.type === "Property") {
72
+ if (property.key.type === "Identifier") yield [property.key.name, property.key];
73
+ else if (property.key.type === "Literal") yield [String(property.key.value), property.key];
74
+ }
75
+ continue;
76
+ }
77
+ if (attr.type === "JSXAttribute") yield [jsxPropName(attr), attr.name];
78
+ }
79
+ }
80
+ //#endregion
81
+ //#region src/rules/create-rule.ts
82
+ const DOCS_BASE = "https://github.com/yumemi-thomas/eslint-plugin-solid-2/blob/main/docs";
83
+ const createRule = _typescript_eslint_utils.ESLintUtils.RuleCreator((name) => `${DOCS_BASE}/${name}.md`);
84
+ //#endregion
85
+ //#region src/rules/solid-rule-utils.ts
86
+ const SOLID_COMPONENT_TYPES = new Set([
87
+ "Component",
88
+ "VoidComponent",
89
+ "ParentComponent",
90
+ "FlowComponent"
91
+ ]);
92
+ /** Whether a node is an `import ... from "solid-js"` declaration. The single source of truth for
93
+ * "this binding comes from solid-js", shared by the callee, type-annotation, and alias resolvers. */
94
+ function isSolidJsImportDeclaration(node) {
95
+ return node?.type === "ImportDeclaration" && node.source.type === "Literal" && node.source.value === "solid-js";
96
+ }
97
+ function typeReferenceName(node) {
98
+ if (node?.type !== "TSTypeReference") return null;
99
+ if (node.typeName.type === "Identifier") return node.typeName.name;
100
+ if (node.typeName.type === "TSQualifiedName" && node.typeName.right.type === "Identifier") return node.typeName.right.name;
101
+ return null;
102
+ }
103
+ /**
104
+ * The identifier that carries the binding for a type reference: the type name itself for a plain
105
+ * `Component`, or the leftmost segment for a qualified `Solid.Component` (i.e. the namespace root).
106
+ * `qualified` distinguishes the two so the binding check can demand an explicit solid-js import for
107
+ * the qualified form (an unresolved `Solid.Component` is not a Solid auto-import).
108
+ */
109
+ function typeReferenceRootIdentifier(node) {
110
+ if (node?.type !== "TSTypeReference") return null;
111
+ let name = node.typeName;
112
+ let qualified = false;
113
+ while (name.type === "TSQualifiedName") {
114
+ qualified = true;
115
+ name = name.left;
116
+ }
117
+ return name.type === "Identifier" ? {
118
+ id: name,
119
+ qualified
120
+ } : null;
121
+ }
122
+ /**
123
+ * Whether a component type-name identifier actually refers to Solid's type — the type-annotation
124
+ * analogue of {@link resolveSolidCallee} (see ADR-0003). It counts as Solid when the name binds to a
125
+ * `solid-js` import, or (for a bare name only) is unresolved — an ambient/auto-import global. A name
126
+ * that binds to a local `type`/`interface` declaration or an import from another package is *not*
127
+ * Solid, which is what keeps `hasSolidComponentTypeAnnotation` from firing on same-named non-Solid
128
+ * types (a UI kit's `Component`, a local `VoidComponent`, a foreign `Ns.Component`).
129
+ */
130
+ function typeNameBindsToSolid(id, qualified, context) {
131
+ let scope = context.sourceCode.getScope(id);
132
+ while (scope) {
133
+ for (const variable of scope.variables) {
134
+ if (variable.name !== id.name) continue;
135
+ for (const def of variable.defs) {
136
+ if (def.type === "ImportBinding") return isSolidJsImportDeclaration(def.node.parent);
137
+ if (def.type === "Type" || def.type === "ClassName" || def.type === "TSEnumName") return false;
138
+ }
139
+ }
140
+ scope = scope.upper;
141
+ }
142
+ return !qualified;
143
+ }
144
+ /**
145
+ * Whether the function is declared with an explicit Solid component type annotation,
146
+ * e.g. `const C: Component<P> = (props) => ...`. This is a *sound* signal of component-hood:
147
+ * it is never true for a plain JSX-returning helper, and holds across file boundaries. The type
148
+ * must actually resolve to Solid's `Component`/`VoidComponent`/… (not a same-named local or
149
+ * third-party type) — see {@link typeNameBindsToSolid} and ADR-0002/0003.
150
+ */
151
+ function hasSolidComponentTypeAnnotation(node, context) {
152
+ const parent = node.parent;
153
+ if (parent?.type === "VariableDeclarator" && parent.id.type === "Identifier") {
154
+ const annotation = parent.id.typeAnnotation?.typeAnnotation;
155
+ const name = typeReferenceName(annotation);
156
+ if (name == null || !SOLID_COMPONENT_TYPES.has(name)) return false;
157
+ const root = typeReferenceRootIdentifier(annotation);
158
+ return root != null && typeNameBindsToSolid(root.id, root.qualified, context);
159
+ }
160
+ return false;
161
+ }
162
+ const inFileComponentVarsCache = /* @__PURE__ */ new WeakMap();
163
+ function getInFileComponentVariables(sourceCode) {
164
+ const cached = inFileComponentVarsCache.get(sourceCode);
165
+ if (cached) return cached;
166
+ const vars = /* @__PURE__ */ new Set();
167
+ const stack = [sourceCode.ast];
168
+ while (stack.length > 0) {
169
+ const node = stack.pop();
170
+ if (node.type === "JSXOpeningElement" && node.name.type === "JSXIdentifier" && !isDOMElementName(node.name.name)) {
171
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.name), node.name.name);
172
+ if (variable != null) vars.add(variable);
173
+ }
174
+ for (const key in node) {
175
+ if (key === "parent" || key === "tokens" || key === "comments") continue;
176
+ const value = node[key];
177
+ if (Array.isArray(value)) {
178
+ for (const item of value) if (item != null && typeof item === "object" && typeof item.type === "string") stack.push(item);
179
+ } else if (value != null && typeof value === "object" && typeof value.type === "string") stack.push(value);
180
+ }
181
+ }
182
+ inFileComponentVarsCache.set(sourceCode, vars);
183
+ return vars;
184
+ }
185
+ function expressionCanYieldJsx(node) {
186
+ switch (node?.type) {
187
+ case "JSXElement":
188
+ case "JSXFragment": return true;
189
+ case "ConditionalExpression": return expressionCanYieldJsx(node.consequent) || expressionCanYieldJsx(node.alternate);
190
+ case "LogicalExpression": return expressionCanYieldJsx(node.left) || expressionCanYieldJsx(node.right);
191
+ case "SequenceExpression": return expressionCanYieldJsx(node.expressions.at(-1));
192
+ default: return false;
193
+ }
194
+ }
195
+ /** Whether any `return` in the function (or an arrow's expression body) yields JSX. */
196
+ function functionReturnsJsx(node) {
197
+ if (node.body.type !== "BlockStatement") return expressionCanYieldJsx(node.body);
198
+ const statements = [...node.body.body];
199
+ while (statements.length > 0) {
200
+ const statement = statements.pop();
201
+ switch (statement.type) {
202
+ case "BlockStatement":
203
+ statements.push(...statement.body);
204
+ break;
205
+ case "IfStatement":
206
+ statements.push(statement.consequent);
207
+ if (statement.alternate) statements.push(statement.alternate);
208
+ break;
209
+ case "LabeledStatement":
210
+ case "WithStatement":
211
+ statements.push(statement.body);
212
+ break;
213
+ case "SwitchStatement":
214
+ for (const switchCase of statement.cases) statements.push(...switchCase.consequent);
215
+ break;
216
+ case "TryStatement":
217
+ statements.push(statement.block);
218
+ if (statement.handler) statements.push(statement.handler.body);
219
+ if (statement.finalizer) statements.push(statement.finalizer);
220
+ break;
221
+ case "ReturnStatement":
222
+ if (expressionCanYieldJsx(statement.argument)) return true;
223
+ break;
224
+ default: break;
225
+ }
226
+ }
227
+ return false;
228
+ }
229
+ function getTypeAwareServices(context) {
230
+ const services = context.sourceCode.parserServices;
231
+ if (services?.program != null && services.esTreeNodeToTSNodeMap != null) return services;
232
+ return null;
233
+ }
234
+ const jsxTagSymbolsCache = /* @__PURE__ */ new WeakMap();
235
+ function resolveAlias(symbol, checker) {
236
+ return symbol.flags & typescript.SymbolFlags.Alias ? checker.getAliasedSymbol(symbol) : symbol;
237
+ }
238
+ function getJsxTagSymbols(services) {
239
+ const program = services.program;
240
+ const cached = jsxTagSymbolsCache.get(program);
241
+ if (cached) return cached;
242
+ const checker = program.getTypeChecker();
243
+ const symbols = /* @__PURE__ */ new Set();
244
+ const visit = (node) => {
245
+ if (typescript.isJsxOpeningElement(node) || typescript.isJsxSelfClosingElement(node)) {
246
+ const symbol = checker.getSymbolAtLocation(node.tagName);
247
+ if (symbol) symbols.add(resolveAlias(symbol, checker));
248
+ }
249
+ typescript.forEachChild(node, visit);
250
+ };
251
+ for (const sourceFile of program.getSourceFiles()) {
252
+ if (sourceFile.isDeclarationFile || sourceFile.fileName.includes("/node_modules/")) continue;
253
+ visit(sourceFile);
254
+ }
255
+ jsxTagSymbolsCache.set(program, symbols);
256
+ return symbols;
257
+ }
258
+ function getFunctionSymbol(node, services) {
259
+ let nameNode;
260
+ if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression") && node.id != null) nameNode = node.id;
261
+ else if (node.parent?.type === "VariableDeclarator" && node.parent.id.type === "Identifier") nameNode = node.parent.id;
262
+ if (!nameNode) return;
263
+ const tsNode = services.esTreeNodeToTSNodeMap.get(nameNode);
264
+ return tsNode ? services.program.getTypeChecker().getSymbolAtLocation(tsNode) : void 0;
265
+ }
266
+ /**
267
+ * Verdict on whether `node`'s type is array-like (an array or tuple — i.e. has a numeric index
268
+ * signature), considering every union member:
269
+ *
270
+ * - `"array"` — provably array-like; safe to rewrite `.map` to `<For each>`.
271
+ * - `"not-array"` — provably not (a `Map`/`Set`/observable); `prefer-for` skips the report.
272
+ * - `"unknown"` — the type can't be resolved, is `any`/`unknown`, or mixes array and non-array
273
+ * members. `prefer-for` stays silent because a rewrite may be semantically wrong.
274
+ */
275
+ function getArrayReceiverVerdict(node, services) {
276
+ const tsNode = services.esTreeNodeToTSNodeMap.get(node);
277
+ if (!tsNode) return "unknown";
278
+ const checker = services.program.getTypeChecker();
279
+ const type = checker.getTypeAtLocation(tsNode);
280
+ const members = type.isUnion() ? type.types : [type];
281
+ if (members.some((member) => member.flags & (typescript.TypeFlags.Any | typescript.TypeFlags.Unknown))) return "unknown";
282
+ const arrayMembers = members.filter((member) => checker.getIndexTypeOfType(member, typescript.IndexKind.Number) != null);
283
+ if (arrayMembers.length === members.length) return "array";
284
+ return arrayMembers.length === 0 ? "not-array" : "unknown";
285
+ }
286
+ /**
287
+ * Sound component detection (see docs/adr/0002). A function is treated as a Solid component only by
288
+ * signals that are never true for a plain helper — never the leaky "capitalized JSX-returning
289
+ * function" guess:
290
+ *
291
+ * - **Always:** it is annotated `Component`/`VoidComponent`/… or used as `<C/>` in the same file.
292
+ * - **Only when `typescriptEnabled` and type info is present:** additionally, its symbol is used as
293
+ * a JSX tag anywhere in the program (catches exported/cross-file components).
294
+ *
295
+ * Both paths are sound (zero false positives); type information is purely additive — it finds more
296
+ * real components, never flips a verdict on correct code. The trade-off is a tolerated false
297
+ * negative: an unannotated component used only in another file is not seen without type info.
298
+ *
299
+ * The module owns its own whole-file index and reads `typescriptEnabled` from `context`, so a
300
+ * caller needs nothing but the node — no `jsxComponentNames` set to build, thread, or defer. The
301
+ * index is complete on first query (see {@link getInFileComponentVariables}), so this is correct
302
+ * when called inline during traversal.
303
+ */
304
+ function isComponent$1(node, context) {
305
+ if (hasSolidComponentTypeAnnotation(node, context)) return true;
306
+ const name = getFunctionName(node);
307
+ if (name != null) {
308
+ const fnVariable = _typescript_eslint_utils.ASTUtils.findVariable(context.sourceCode.getScope(node), name);
309
+ if (fnVariable != null && getInFileComponentVariables(context.sourceCode).has(fnVariable)) return true;
310
+ }
311
+ if (context.options[0]?.typescriptEnabled) {
312
+ const services = getTypeAwareServices(context);
313
+ if (services) {
314
+ const symbol = getFunctionSymbol(node, services);
315
+ if (symbol && getJsxTagSymbols(services).has(resolveAlias(symbol, services.program.getTypeChecker()))) return true;
316
+ }
317
+ }
318
+ return false;
319
+ }
320
+ /**
321
+ * Whether `identifier` actually refers to the Solid API for one of `canonicalNames` — a name
322
+ * imported from "solid-js" (directly or aliased), a `const` alias of one (`const c = onCleanup`),
323
+ * or an unresolved global (auto-import). Returns false when it binds to the user's own declaration
324
+ * or an import from another package, which prevents false positives on same-named non-Solid
325
+ * functions (a stream library's `flush`, a state library's `createStore`, a local `onCleanup`, ...).
326
+ *
327
+ * Delegates to {@link resolveSolidCallee}, whose `trace` step resolves both aliased imports and
328
+ * `const` aliases — so no caller needs to pre-collect import aliases (see ADR-0003).
329
+ */
330
+ function bindsToSolid(identifier, context, canonicalNames) {
331
+ return resolveSolidCallee(identifier, context, canonicalNames) != null;
332
+ }
333
+ /** Whether `node` is the `argumentIndex`th argument of a call whose callee binds to the Solid API. */
334
+ function isSolidApiCallbackArgument(node, argumentIndex, context, canonicalNames) {
335
+ return node.parent?.type === "CallExpression" && node.parent.arguments[argumentIndex] === node && node.parent.callee.type === "Identifier" && bindsToSolid(node.parent.callee, context, canonicalNames);
336
+ }
337
+ function getReturnedExpressions(node) {
338
+ if (node.type === "ArrowFunctionExpression" && node.body.type !== "BlockStatement") return [node.body];
339
+ if (node.body.type !== "BlockStatement") return [];
340
+ const returned = [];
341
+ const statements = [...node.body.body];
342
+ while (statements.length > 0) {
343
+ const statement = statements.pop();
344
+ switch (statement.type) {
345
+ case "BlockStatement":
346
+ statements.push(...statement.body);
347
+ break;
348
+ case "DoWhileStatement":
349
+ case "ForInStatement":
350
+ case "ForOfStatement":
351
+ case "ForStatement":
352
+ case "WhileStatement":
353
+ case "LabeledStatement":
354
+ case "WithStatement":
355
+ statements.push(statement.body);
356
+ break;
357
+ case "IfStatement":
358
+ statements.push(statement.consequent);
359
+ if (statement.alternate) statements.push(statement.alternate);
360
+ break;
361
+ case "SwitchStatement":
362
+ for (const switchCase of statement.cases) statements.push(...switchCase.consequent);
363
+ break;
364
+ case "TryStatement":
365
+ statements.push(statement.block);
366
+ if (statement.handler) statements.push(statement.handler.body);
367
+ if (statement.finalizer) statements.push(statement.finalizer);
368
+ break;
369
+ case "ReturnStatement":
370
+ returned.push(statement.argument ?? null);
371
+ break;
372
+ default: break;
373
+ }
374
+ }
375
+ return returned;
376
+ }
377
+ function getPropertyName$2(node) {
378
+ if (!node.computed && node.key.type === "Identifier") return node.key.name;
379
+ if (node.key.type === "Literal" && typeof node.key.value === "string") return node.key.value;
380
+ return null;
381
+ }
382
+ function getNearestFunctionAncestor(node) {
383
+ let current = node.parent;
384
+ while (current != null) {
385
+ if (isFunctionNode(current)) return current;
386
+ current = current.parent;
387
+ }
388
+ return null;
389
+ }
390
+ /**
391
+ * Resolves an identifier to the canonical Solid API name it refers to, or null. The `trace` step
392
+ * follows both aliased imports (`import { createEffect as fx }`) and `const` aliases to their
393
+ * origin, so an aliased Solid API resolves to its **canonical** name (`"createEffect"`), not the
394
+ * local alias. A bare canonical name is trusted only when unresolved (an auto-import/global); a
395
+ * name bound to a local declaration or a non-solid-js import resolves to null.
396
+ */
397
+ function resolveSolidCallee(node, context, canonicalNames) {
398
+ if (node.type !== "Identifier") return null;
399
+ const traced = trace(node, context);
400
+ if (traced.type === "Identifier" && canonicalNames.has(traced.name)) {
401
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(context.sourceCode.getScope(traced), traced);
402
+ return variable == null || variable.defs.length === 0 ? traced.name : null;
403
+ }
404
+ if (traced.type === "ImportSpecifier" && isSolidJsImportDeclaration(traced.parent)) {
405
+ const importedName = traced.imported.type === "Identifier" ? traced.imported.name : traced.imported.value;
406
+ if (canonicalNames.has(importedName)) return importedName;
407
+ }
408
+ return null;
409
+ }
410
+ /** Whether any scope in the file already declares `name`. */
411
+ function isNameTaken(sourceCode, name) {
412
+ return sourceCode.scopeManager?.scopes.some((scope) => scope.set.has(name)) ?? false;
413
+ }
414
+ /**
415
+ * Fixes that make `names` importable from solid-js at the fix site, so an autofix that emits
416
+ * `<Show>`/`<For>`/... never produces non-compiling code. Returns `[]` when every name is already
417
+ * imported from solid-js under its own name, a fix extending (or creating) the solid-js import for
418
+ * the missing ones, or `null` when a missing name is already bound to something else in the file —
419
+ * callers must then skip their autofix rather than emit a reference that resolves to the wrong
420
+ * binding.
421
+ */
422
+ function getSolidImportFixes(context, fixer, names) {
423
+ const sourceCode = context.sourceCode;
424
+ const importNode = sourceCode.ast.body.find((node) => isSolidJsImportDeclaration(node) && node.importKind !== "type");
425
+ const available = /* @__PURE__ */ new Set();
426
+ for (const specifier of importNode?.specifiers ?? []) if (specifier.type === "ImportSpecifier" && specifier.imported.type === "Identifier" && specifier.imported.name === specifier.local.name) available.add(specifier.local.name);
427
+ const missing = names.filter((name) => !available.has(name));
428
+ if (missing.length === 0) return [];
429
+ if (missing.some((name) => isNameTaken(sourceCode, name))) return null;
430
+ const lastNamed = importNode?.specifiers.findLast((specifier) => specifier.type === "ImportSpecifier");
431
+ if (lastNamed != null) return [fixer.insertTextAfter(lastNamed, `, ${missing.join(", ")}`)];
432
+ const firstStatement = sourceCode.ast.body[0];
433
+ const importText = `import { ${missing.join(", ")} } from "solid-js";\n`;
434
+ return [firstStatement != null ? fixer.insertTextBefore(firstStatement, importText) : fixer.insertTextAfterRange([0, 0], importText)];
435
+ }
436
+ //#endregion
437
+ //#region src/rules/components-return-once.ts
438
+ const ACCESSOR_FACTORIES$2 = new Set(["createMemo"]);
439
+ const PAIR_ACCESSOR_FACTORIES$2 = new Set(["createSignal", "createOptimistic"]);
440
+ const STORE_FACTORIES$1 = new Set(["createOptimisticStore", "createStore"]);
441
+ function getVariable(id, context) {
442
+ return _typescript_eslint_utils.ASTUtils.findVariable(context.sourceCode.getScope(id), id);
443
+ }
444
+ /** The `VariableDeclarator` that declared `variable`, when it was declared by one. */
445
+ function getDeclarator(variable) {
446
+ const def = variable.defs[0];
447
+ return def?.type === "Variable" ? def.node : null;
448
+ }
449
+ /** Whether `variable` is `elements[0]` of an array-pattern declaration whose init calls a factory. */
450
+ function isPairElementZero(variable, factories, context) {
451
+ const declarator = getDeclarator(variable);
452
+ if (declarator?.id.type !== "ArrayPattern" || declarator.init?.type !== "CallExpression" || declarator.init.callee.type !== "Identifier") return false;
453
+ const first = declarator.id.elements[0];
454
+ return first?.type === "Identifier" && first.name === variable.name && bindsToSolid(declarator.init.callee, context, factories);
455
+ }
456
+ /** Whether calling `variable` reads a signal/memo accessor (follows `const c = count` aliases). */
457
+ function variableIsAccessor(variable, context, seen) {
458
+ if (seen.has(variable)) return false;
459
+ seen.add(variable);
460
+ if (isPairElementZero(variable, PAIR_ACCESSOR_FACTORIES$2, context)) return true;
461
+ const declarator = getDeclarator(variable);
462
+ if (declarator?.id.type !== "Identifier" || declarator.init == null) return false;
463
+ if (declarator.init.type === "CallExpression" && declarator.init.callee.type === "Identifier" && bindsToSolid(declarator.init.callee, context, ACCESSOR_FACTORIES$2)) return true;
464
+ if (declarator.init.type === "Identifier") {
465
+ const source = getVariable(declarator.init, context);
466
+ return source != null && variableIsAccessor(source, context, seen);
467
+ }
468
+ return false;
469
+ }
470
+ /**
471
+ * Whether evaluating `node` provably performs a reactive read from `componentFn`'s perspective.
472
+ * Skips nested functions (not evaluated by the guard) and JSX (reads there compile to tracked
473
+ * scopes). Follows one-level-and-deeper `const` derivations (`const loading = props.loading`).
474
+ */
475
+ function containsReactiveRead(node, componentFn, context, seen = /* @__PURE__ */ new Set()) {
476
+ const isPropsParameter = (variable) => variable.defs.some((def) => def.type === "Parameter" && def.node === componentFn);
477
+ const memberRootIsReactive = (member) => {
478
+ let root = member;
479
+ while (root.type === "MemberExpression") root = root.object;
480
+ if (root.type !== "Identifier") return false;
481
+ const variable = getVariable(root, context);
482
+ if (variable == null) return false;
483
+ if (isPropsParameter(variable) || isPairElementZero(variable, STORE_FACTORIES$1, context)) return true;
484
+ return derivedConstIsReactive(variable);
485
+ };
486
+ const derivedConstIsReactive = (variable) => {
487
+ if (seen.has(variable)) return false;
488
+ seen.add(variable);
489
+ const declarator = getDeclarator(variable);
490
+ return declarator?.id.type === "Identifier" && declarator.init != null && containsReactiveRead(declarator.init, componentFn, context, seen);
491
+ };
492
+ const stack = [node];
493
+ while (stack.length > 0) {
494
+ const current = stack.pop();
495
+ if (isFunctionNode(current) || current.type === "JSXElement" || current.type === "JSXFragment") continue;
496
+ if (current.type === "CallExpression" && current.callee.type === "Identifier") {
497
+ const callee = getVariable(current.callee, context);
498
+ if (callee != null && variableIsAccessor(callee, context, /* @__PURE__ */ new Set())) return true;
499
+ }
500
+ if (current.type === "MemberExpression" && memberRootIsReactive(current)) return true;
501
+ if (current.type === "Identifier") {
502
+ const variable = getVariable(current, context);
503
+ if (variable != null && !isPropsParameter(variable) && derivedConstIsReactive(variable)) return true;
504
+ }
505
+ for (const key in current) {
506
+ if (key === "parent" || key === "tokens" || key === "comments") continue;
507
+ const value = current[key];
508
+ if (Array.isArray(value)) {
509
+ for (const item of value) if (item != null && typeof item === "object" && typeof item.type === "string") stack.push(item);
510
+ } else if (value != null && typeof value === "object" && typeof value.type === "string") stack.push(value);
511
+ }
512
+ }
513
+ return false;
514
+ }
515
+ /**
516
+ * The guard expressions that decide whether execution reaches `ret`: every enclosing `if` test,
517
+ * switch discriminant/case test, and loop condition between the return and the component function.
518
+ */
519
+ function collectGuardExpressions(ret, fn) {
520
+ const guards = [];
521
+ let current = ret;
522
+ while (current !== fn && current.parent != null) {
523
+ const parent = current.parent;
524
+ switch (parent.type) {
525
+ case "IfStatement":
526
+ if (parent.consequent === current || parent.alternate === current) guards.push(parent.test);
527
+ break;
528
+ case "SwitchCase":
529
+ if (parent.test != null) guards.push(parent.test);
530
+ break;
531
+ case "SwitchStatement":
532
+ guards.push(parent.discriminant);
533
+ break;
534
+ case "WhileStatement":
535
+ case "DoWhileStatement":
536
+ if (parent.body === current) guards.push(parent.test);
537
+ break;
538
+ case "ForStatement":
539
+ if (parent.body === current && parent.test != null) guards.push(parent.test);
540
+ break;
541
+ case "ForInStatement":
542
+ case "ForOfStatement":
543
+ if (parent.body === current) guards.push(parent.right);
544
+ break;
545
+ default: break;
546
+ }
547
+ current = parent;
548
+ }
549
+ return guards;
550
+ }
551
+ /**
552
+ * The decision expressions of a conditional last return: the tests of a (possibly nested) ternary
553
+ * chain, or the left operands along a logical chain (`a && b && <X/>` → `a && b`). Branch values
554
+ * are excluded — reads inside returned JSX are tracked and not this rule's concern.
555
+ */
556
+ function conditionalDecisionExpressions(argument) {
557
+ const decisions = [];
558
+ if (argument.type === "ConditionalExpression") {
559
+ let current = argument;
560
+ while (current.type === "ConditionalExpression") {
561
+ decisions.push(current.test);
562
+ current = current.alternate;
563
+ }
564
+ } else if (argument.type === "LogicalExpression") {
565
+ let current = argument;
566
+ while (current.type === "LogicalExpression") {
567
+ decisions.push(current.left);
568
+ current = current.right;
569
+ }
570
+ }
571
+ return decisions;
572
+ }
573
+ const isNothing = (node) => {
574
+ if (!node) return true;
575
+ switch (node.type) {
576
+ case "Literal": return [
577
+ null,
578
+ void 0,
579
+ false,
580
+ ""
581
+ ].includes(node.value);
582
+ case "JSXFragment": return node.children.every((child) => isNothing(child));
583
+ default: return false;
584
+ }
585
+ };
586
+ const getLineLength = (loc) => loc == null ? 0 : loc.end.line - loc.start.line + 1;
587
+ var components_return_once_default = createRule({
588
+ name: "components-return-once",
589
+ meta: {
590
+ type: "problem",
591
+ docs: { description: "Disallow early returns in components. Solid components only run once, so conditionals should stay inside JSX." },
592
+ fixable: "code",
593
+ schema: [{
594
+ type: "object",
595
+ properties: { typescriptEnabled: { type: "boolean" } },
596
+ additionalProperties: false
597
+ }],
598
+ messages: {
599
+ noConditionalReturn: "Solid components run once, so a conditional return breaks reactivity. Move the condition inside JSX, such as a fragment or `<Show />`.",
600
+ noEarlyReturn: "Solid components run once, so an early return breaks reactivity. Move the condition inside JSX, such as a fragment or `<Show />`."
601
+ }
602
+ },
603
+ defaultOptions: [{}],
604
+ create(context) {
605
+ const sourceCode = context.sourceCode;
606
+ const functionStack = [];
607
+ const putIntoJSX = (node) => {
608
+ const text = sourceCode.getText(node);
609
+ return node.type === "JSXElement" || node.type === "JSXFragment" ? text : `{${text}}`;
610
+ };
611
+ const currentFunction = () => functionStack[functionStack.length - 1];
612
+ const onFunctionEnter = (node) => {
613
+ let lastReturn;
614
+ if (node.body.type === "BlockStatement") {
615
+ const last = [...node.body.body].reverse().find((statement) => !statement.type.endsWith("Declaration"));
616
+ if (last?.type === "ReturnStatement") lastReturn = last;
617
+ }
618
+ functionStack.push({
619
+ node,
620
+ lastReturn,
621
+ earlyReturns: []
622
+ });
623
+ };
624
+ const onFunctionExit = () => {
625
+ const frame = functionStack.pop();
626
+ if (frame && isComponent$1(frame.node, context)) reportComponent(frame);
627
+ };
628
+ const reportComponent = (frame) => {
629
+ const isReactiveGuard = (expression) => containsReactiveRead(expression, frame.node, context);
630
+ for (const earlyReturn of frame.earlyReturns) {
631
+ if (!collectGuardExpressions(earlyReturn, frame.node).some(isReactiveGuard)) continue;
632
+ context.report({
633
+ node: earlyReturn,
634
+ messageId: "noEarlyReturn"
635
+ });
636
+ }
637
+ const argument = frame.lastReturn?.argument;
638
+ if ((argument?.type === "ConditionalExpression" || argument?.type === "LogicalExpression") && !conditionalDecisionExpressions(argument).some(isReactiveGuard)) return;
639
+ if (argument?.type === "ConditionalExpression") context.report({
640
+ node: argument.parent ?? argument,
641
+ messageId: "noConditionalReturn",
642
+ fix: (fixer) => {
643
+ const conditions = [{
644
+ test: argument.test,
645
+ consequent: argument.consequent
646
+ }];
647
+ let fallback = argument.alternate;
648
+ while (fallback.type === "ConditionalExpression") {
649
+ conditions.push({
650
+ test: fallback.test,
651
+ consequent: fallback.consequent
652
+ });
653
+ fallback = fallback.alternate;
654
+ }
655
+ const withImports = (names, replacement) => {
656
+ const importFixes = getSolidImportFixes(context, fixer, names);
657
+ return importFixes == null ? null : [...importFixes, replacement];
658
+ };
659
+ if (conditions.length >= 2) {
660
+ const fallbackText = !isNothing(fallback) ? ` fallback={${sourceCode.getText(fallback)}}` : "";
661
+ return withImports(["Switch", "Match"], fixer.replaceText(argument, `<Switch${fallbackText}>\n${conditions.map(({ test, consequent }) => `<Match when={${sourceCode.getText(test)}}>${putIntoJSX(consequent)}</Match>`).join("\n")}\n</Switch>`));
662
+ }
663
+ if (isNothing(argument.consequent)) return withImports(["Show"], fixer.replaceText(argument, `<Show when={!(${sourceCode.getText(argument.test)})}>${putIntoJSX(argument.alternate)}</Show>`));
664
+ if (isNothing(fallback) || getLineLength(argument.consequent.loc) >= getLineLength(fallback.loc) * 1.5) {
665
+ const fallbackText = !isNothing(fallback) ? ` fallback={${sourceCode.getText(fallback)}}` : "";
666
+ return withImports(["Show"], fixer.replaceText(argument, `<Show when={${sourceCode.getText(argument.test)}}${fallbackText}>${putIntoJSX(argument.consequent)}</Show>`));
667
+ }
668
+ return fixer.replaceText(argument, `<>${putIntoJSX(argument)}</>`);
669
+ }
670
+ });
671
+ else if (argument?.type === "LogicalExpression") context.report({
672
+ node: argument,
673
+ messageId: "noConditionalReturn"
674
+ });
675
+ };
676
+ return {
677
+ FunctionDeclaration: onFunctionEnter,
678
+ FunctionExpression: onFunctionEnter,
679
+ ArrowFunctionExpression: onFunctionEnter,
680
+ "FunctionDeclaration:exit": onFunctionExit,
681
+ "FunctionExpression:exit": onFunctionExit,
682
+ "ArrowFunctionExpression:exit": onFunctionExit,
683
+ ReturnStatement(node) {
684
+ if (functionStack.length > 0 && node !== currentFunction().lastReturn) currentFunction().earlyReturns.push(node);
685
+ }
686
+ };
687
+ }
688
+ });
689
+ //#endregion
690
+ //#region src/rules/jsx-no-duplicate-props.ts
691
+ function hasMeaningfulChildren(element) {
692
+ return element.children.some((child) => {
693
+ if (child.type === "JSXText") return child.value.trim().length > 0;
694
+ if (child.type === "JSXExpressionContainer") return child.expression.type !== "JSXEmptyExpression";
695
+ return true;
696
+ });
697
+ }
698
+ var jsx_no_duplicate_props_default = createRule({
699
+ name: "jsx-no-duplicate-props",
700
+ meta: {
701
+ type: "problem",
702
+ docs: { description: "Disallow competing JSX content sources such as children, innerHTML, and textContent." },
703
+ schema: [],
704
+ messages: { noDuplicateChildren: "Using {{used}} at the same time is not allowed." }
705
+ },
706
+ defaultOptions: [],
707
+ create(context) {
708
+ return { JSXOpeningElement(node) {
709
+ const props = /* @__PURE__ */ new Set();
710
+ for (const [name] of jsxGetAllProps(node.attributes)) props.add(name.toLowerCase());
711
+ const hasChildrenProp = props.has("children");
712
+ const element = node.parent;
713
+ const hasChildren = hasMeaningfulChildren(element);
714
+ const isHost = isHostElement(node);
715
+ const hasInnerHTML = isHost && props.has("innerhtml");
716
+ const hasTextContent = isHost && props.has("textcontent");
717
+ const used = [
718
+ hasChildrenProp && "`props.children`",
719
+ hasChildren && "JSX children",
720
+ hasInnerHTML && "`props.innerHTML`",
721
+ hasTextContent && "`props.textContent`"
722
+ ].filter(Boolean);
723
+ if (used.length > 1) context.report({
724
+ node,
725
+ messageId: "noDuplicateChildren",
726
+ data: { used: used.join(", ") }
727
+ });
728
+ } };
729
+ }
730
+ });
731
+ //#endregion
732
+ //#region src/rules/no-destructure.ts
733
+ const getName = (node) => {
734
+ switch (node.type) {
735
+ case "Literal": return typeof node.value === "string" ? node.value : null;
736
+ case "Identifier": return node.name;
737
+ case "AssignmentPattern": return getName(node.left);
738
+ default: return _typescript_eslint_utils.ASTUtils.getStringIfConstant(node);
739
+ }
740
+ };
741
+ function getAvailableName(sourceCode, preferred) {
742
+ if (!isNameTaken(sourceCode, preferred)) return preferred;
743
+ let index = 2;
744
+ while (isNameTaken(sourceCode, `${preferred}${index}`)) index += 1;
745
+ return `${preferred}${index}`;
746
+ }
747
+ const getPropertyInfo = (property) => {
748
+ const variableName = getName(property.value);
749
+ if (variableName === null) return null;
750
+ return {
751
+ init: property.value.type === "AssignmentPattern" ? property.value.right : void 0,
752
+ computed: property.computed,
753
+ real: property.key,
754
+ variableName
755
+ };
756
+ };
757
+ var no_destructure_default = createRule({
758
+ name: "no-destructure",
759
+ meta: {
760
+ type: "problem",
761
+ docs: { description: "Disallow destructuring component props. In Solid 2, destructuring props triggers top-level untracked reads." },
762
+ fixable: "code",
763
+ schema: [{
764
+ type: "object",
765
+ properties: { typescriptEnabled: { type: "boolean" } },
766
+ additionalProperties: false
767
+ }],
768
+ messages: { noDestructure: "Destructuring component props breaks Solid 2 reactivity; keep the `props` object and read properties from it." }
769
+ },
770
+ defaultOptions: [{}],
771
+ create(context) {
772
+ const sourceCode = context.sourceCode;
773
+ const onFunctionExit = (node) => {
774
+ const props = node.params[0];
775
+ if (node.params.length === 1 && props?.type === "ObjectPattern" && node.parent?.type !== "JSXExpressionContainer" && isComponent$1(node, context)) context.report({
776
+ node: props,
777
+ messageId: "noDestructure",
778
+ fix: (fixer) => fixDestructure(node, props, fixer)
779
+ });
780
+ };
781
+ function* fixDestructure(func, props, fixer) {
782
+ const sourceCode = context.sourceCode;
783
+ const importNode = sourceCode.ast.body.find((node) => node.type === "ImportDeclaration" && node.importKind !== "type" && node.source.type === "Literal" && node.source.value === "solid-js");
784
+ const properties = props.properties;
785
+ const propEntries = [];
786
+ let rest = null;
787
+ for (const property of properties) {
788
+ if (property.type === "RestElement") {
789
+ rest = property;
790
+ continue;
791
+ }
792
+ const info = getPropertyInfo(property);
793
+ if (info) propEntries.push(info);
794
+ }
795
+ const ordinaryPropertyCount = properties.filter((property) => property.type === "Property").length;
796
+ if (propEntries.length !== ordinaryPropertyCount || rest != null && rest.argument.type !== "Identifier") return;
797
+ const hasDefaults = propEntries.some((entry) => entry.init);
798
+ const propsName = getAvailableName(sourceCode, "props");
799
+ const originalPropsName = hasDefaults ? getAvailableName(sourceCode, "_props") : propsName;
800
+ const helperNames = /* @__PURE__ */ new Map();
801
+ if (importNode) for (const specifier of importNode.specifiers) {
802
+ if (specifier.type !== "ImportSpecifier") continue;
803
+ const importedName = specifier.imported.type === "Identifier" ? specifier.imported.name : specifier.imported.value;
804
+ if (importedName === "merge" || importedName === "omit") helperNames.set(importedName, specifier.local.name);
805
+ }
806
+ const resolveHelper = (importedName) => {
807
+ const existing = helperNames.get(importedName);
808
+ if (existing) return existing;
809
+ return isNameTaken(sourceCode, importedName) ? null : importedName;
810
+ };
811
+ const mergeName = hasDefaults ? resolveHelper("merge") : null;
812
+ const omitName = rest ? resolveHelper("omit") : null;
813
+ const defaultPairs = propEntries.filter((entry) => entry.init).map((entry) => {
814
+ return `${entry.computed ? `[${sourceCode.getText(entry.real)}]` : sourceCode.getText(entry.real)}: ${sourceCode.getText(entry.init)}`;
815
+ });
816
+ const omittedKeys = propEntries.map((entry) => entry.real.type === "Identifier" ? JSON.stringify(entry.real.name) : sourceCode.getText(entry.real));
817
+ const setupLines = [];
818
+ if (hasDefaults && mergeName == null || rest && omitName == null) return;
819
+ if (hasDefaults) setupLines.push(`const ${propsName} = ${mergeName}({ ${defaultPairs.join(", ")} }, ${originalPropsName});`);
820
+ if (rest) {
821
+ const restName = rest.argument.type === "Identifier" ? rest.argument.name : "rest";
822
+ const omitArgs = omittedKeys.length > 0 ? `, ${omittedKeys.join(", ")}` : "";
823
+ setupLines.push(`const ${restName} = ${omitName}(${propsName}${omitArgs});`);
824
+ }
825
+ if (setupLines.length > 0 && func.body.type !== "BlockStatement") return;
826
+ const missingHelpers = [];
827
+ if (hasDefaults && !helperNames.has("merge")) missingHelpers.push("merge");
828
+ if (rest && !helperNames.has("omit")) missingHelpers.push("omit");
829
+ if (missingHelpers.length > 0) {
830
+ const importFixes = getSolidImportFixes(context, fixer, missingHelpers);
831
+ if (importFixes == null) return;
832
+ yield* importFixes;
833
+ }
834
+ if (props.typeAnnotation) yield fixer.replaceTextRange([props.range[0], props.typeAnnotation.range[0]], originalPropsName);
835
+ else yield fixer.replaceText(props, originalPropsName);
836
+ if (setupLines.length > 0) {
837
+ if (func.body.type === "BlockStatement") {
838
+ const indent = " ".repeat(func.body.body[0]?.loc?.start.column ?? 2);
839
+ if (func.body.body.length > 0) yield fixer.insertTextBefore(func.body.body[0], `${setupLines.join(`\n${indent}`)}\n${indent}`);
840
+ else yield fixer.insertTextAfterRange([func.body.range[0], func.body.range[0] + 1], `\n${indent}${setupLines.join(`\n${indent}`)}\n`);
841
+ }
842
+ }
843
+ const scope = sourceCode.scopeManager?.acquire(func);
844
+ if (!scope) return;
845
+ for (const entry of propEntries) {
846
+ const variable = scope.set.get(entry.variableName);
847
+ if (!variable) continue;
848
+ const access = entry.real.type === "Identifier" && !entry.computed ? `.${entry.real.name}` : `[${sourceCode.getText(entry.real)}]`;
849
+ for (const reference of variable.references) if (reference.isReadOnly()) yield fixer.replaceText(reference.identifier, `${propsName}${access}`);
850
+ }
851
+ }
852
+ return {
853
+ "FunctionDeclaration:exit": onFunctionExit,
854
+ "FunctionExpression:exit": onFunctionExit,
855
+ "ArrowFunctionExpression:exit": onFunctionExit,
856
+ VariableDeclarator(node) {
857
+ if (node.id.type !== "ObjectPattern" || node.init?.type !== "Identifier") return;
858
+ const def = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.init), node.init)?.defs[0];
859
+ if (def?.type !== "Parameter" || !isFunctionNode(def.node)) return;
860
+ const firstParam = def.node.params[0];
861
+ if ((firstParam?.type === "AssignmentPattern" ? firstParam.left : firstParam) === def.name && isComponent$1(def.node, context)) context.report({
862
+ node: node.id,
863
+ messageId: "noDestructure"
864
+ });
865
+ }
866
+ };
867
+ }
868
+ });
869
+ //#endregion
870
+ //#region src/rules/no-leaf-owner-operations.ts
871
+ const TRACKED_EFFECT_NAMES = new Set(["createTrackedEffect"]);
872
+ const ON_SETTLED_NAMES = new Set(["onSettled"]);
873
+ const OWNER_CALLBACK_NAMES = new Set([
874
+ "createEffect",
875
+ "createMemo",
876
+ "createRenderEffect",
877
+ "createRoot"
878
+ ]);
879
+ const ON_CLEANUP_NAMES = new Set(["onCleanup"]);
880
+ const FLUSH_NAMES = new Set(["flush"]);
881
+ const PRIMITIVE_NAMES = new Set([
882
+ "createEffect",
883
+ "createErrorBoundary",
884
+ "createLoadingBoundary",
885
+ "createMemo",
886
+ "createOwner",
887
+ "createProjection",
888
+ "createRenderEffect",
889
+ "createRevealOrder",
890
+ "createRoot",
891
+ "createTrackedEffect",
892
+ "mapArray",
893
+ "repeat"
894
+ ]);
895
+ const FUNCTION_FORM_PRIMITIVE_NAMES = new Set([
896
+ "createOptimistic",
897
+ "createOptimisticStore",
898
+ "createSignal",
899
+ "createStore"
900
+ ]);
901
+ function hasProvablyFunctionFirstArgument(node, context) {
902
+ const first = node.arguments[0];
903
+ if (first == null || first.type === "SpreadElement") return false;
904
+ return isFunctionNode(first) || isFunctionNode(trace(first, context));
905
+ }
906
+ var no_leaf_owner_operations_default = createRule({
907
+ name: "no-leaf-owner-operations",
908
+ meta: {
909
+ type: "problem",
910
+ docs: { description: "Disallow onCleanup, reactive-primitive creation, and flush() inside the leaf owners createTrackedEffect and onSettled." },
911
+ schema: [],
912
+ messages: {
913
+ noCleanup: "Cannot use `onCleanup` inside `createTrackedEffect` or `onSettled`; return a cleanup function instead.",
914
+ noFlush: "Cannot call `flush()` from inside `createTrackedEffect` or `onSettled`; schedule work outside instead.",
915
+ noPrimitives: "Cannot create reactive primitives inside `createTrackedEffect` or `onSettled`; move them to the component body or another owner."
916
+ }
917
+ },
918
+ defaultOptions: [],
919
+ create(context) {
920
+ const forbiddenStack = [];
921
+ const onFunctionEnter = (node) => {
922
+ if (node.type !== "FunctionDeclaration" && node.type !== "FunctionExpression" && node.type !== "ArrowFunctionExpression") return;
923
+ const trackedEffect = isSolidApiCallbackArgument(node, 0, context, TRACKED_EFFECT_NAMES);
924
+ let ownerBackedSettled = false;
925
+ if (isSolidApiCallbackArgument(node, 0, context, ON_SETTLED_NAMES)) {
926
+ const settledCall = node.parent;
927
+ const enclosing = getNearestFunctionAncestor(settledCall);
928
+ ownerBackedSettled = enclosing != null && (isComponent$1(enclosing, context) || isSolidApiCallbackArgument(enclosing, 0, context, OWNER_CALLBACK_NAMES) || isSolidApiCallbackArgument(enclosing, 1, context, OWNER_CALLBACK_NAMES));
929
+ }
930
+ if (trackedEffect || ownerBackedSettled) forbiddenStack.push(node);
931
+ };
932
+ const onFunctionExit = (node) => {
933
+ if (forbiddenStack[forbiddenStack.length - 1] === node) forbiddenStack.pop();
934
+ };
935
+ return {
936
+ FunctionDeclaration: onFunctionEnter,
937
+ FunctionExpression: onFunctionEnter,
938
+ ArrowFunctionExpression: onFunctionEnter,
939
+ "FunctionDeclaration:exit": onFunctionExit,
940
+ "FunctionExpression:exit": onFunctionExit,
941
+ "ArrowFunctionExpression:exit": onFunctionExit,
942
+ CallExpression(node) {
943
+ const currentForbidden = forbiddenStack[forbiddenStack.length - 1];
944
+ if (!currentForbidden || getNearestFunctionAncestor(node) !== currentForbidden || node.callee.type !== "Identifier") return;
945
+ const callee = node.callee;
946
+ if (bindsToSolid(callee, context, ON_CLEANUP_NAMES)) context.report({
947
+ node: callee,
948
+ messageId: "noCleanup"
949
+ });
950
+ else if (bindsToSolid(callee, context, FLUSH_NAMES)) context.report({
951
+ node: callee,
952
+ messageId: "noFlush"
953
+ });
954
+ else if (bindsToSolid(callee, context, PRIMITIVE_NAMES)) context.report({
955
+ node: callee,
956
+ messageId: "noPrimitives"
957
+ });
958
+ else if (bindsToSolid(callee, context, FUNCTION_FORM_PRIMITIVE_NAMES) && hasProvablyFunctionFirstArgument(node, context)) context.report({
959
+ node: callee,
960
+ messageId: "noPrimitives"
961
+ });
962
+ }
963
+ };
964
+ }
965
+ });
966
+ //#endregion
967
+ //#region src/rules/no-owned-scope-writes.ts
968
+ const SETTER_FACTORIES = new Set([
969
+ "createOptimistic",
970
+ "createOptimisticStore",
971
+ "createSignal",
972
+ "createStore"
973
+ ]);
974
+ const OWNED_WRITE_FACTORIES = new Set(["createSignal", "createOptimistic"]);
975
+ const EFFECT_FACTORIES = new Set(["createEffect", "createRenderEffect"]);
976
+ const COMPUTE_FACTORIES$1 = new Set([
977
+ "createEffect",
978
+ "createMemo",
979
+ "createRenderEffect"
980
+ ]);
981
+ const ACTION_FACTORIES = new Set(["action"]);
982
+ function getPropertyName$1(node) {
983
+ if (!node.computed && node.key.type === "Identifier") return node.key.name;
984
+ if (node.key.type === "Literal" && typeof node.key.value === "string") return node.key.value;
985
+ return null;
986
+ }
987
+ function hasOwnedWriteOption(node) {
988
+ const options = node.arguments[1];
989
+ if (options?.type !== "ObjectExpression") return false;
990
+ return options.properties.some((property) => property.type === "Property" && getPropertyName$1(property) === "ownedWrite" && property.value.type === "Literal" && property.value.value === true);
991
+ }
992
+ function isOwnedScopeFunction(node, context) {
993
+ if (isComponent$1(node, context)) return true;
994
+ if (node.parent?.type !== "CallExpression" || node.parent.arguments[0] !== node || node.parent.callee.type !== "Identifier") return false;
995
+ const callee = node.parent.callee;
996
+ if (!bindsToSolid(callee, context, COMPUTE_FACTORIES$1)) return false;
997
+ if (bindsToSolid(callee, context, EFFECT_FACTORIES)) return node.parent.arguments.length >= 2;
998
+ return true;
999
+ }
1000
+ var no_owned_scope_writes_default = createRule({
1001
+ name: "no-owned-scope-writes",
1002
+ meta: {
1003
+ type: "problem",
1004
+ docs: { description: "Disallow signal/store writes inside component bodies and reactive compute scopes in Solid 2." },
1005
+ schema: [{
1006
+ type: "object",
1007
+ properties: { typescriptEnabled: { type: "boolean" } },
1008
+ additionalProperties: false
1009
+ }],
1010
+ messages: {
1011
+ noActionInOwnedScope: "Calling an action inside a component or reactive compute scope is not allowed in Solid 2. Call it from an event handler or another imperative scope.",
1012
+ noOwnedScopeWrite: "Writing to state inside a component or reactive compute scope is not allowed in Solid 2. Derive values instead, move the write to an event handler or apply phase, or use `ownedWrite: true` for internal `createSignal` state."
1013
+ }
1014
+ },
1015
+ defaultOptions: [{}],
1016
+ create(context) {
1017
+ const setterVariables = /* @__PURE__ */ new Map();
1018
+ const actionVariables = /* @__PURE__ */ new Set();
1019
+ const functionStack = [];
1020
+ const pendingCalls = [];
1021
+ const pendingDirectActions = [];
1022
+ const sourceCode = context.sourceCode;
1023
+ const onFunctionEnter = (node) => {
1024
+ functionStack.push(node);
1025
+ };
1026
+ const onFunctionExit = () => {
1027
+ functionStack.pop();
1028
+ };
1029
+ return {
1030
+ VariableDeclarator(node) {
1031
+ if (node.id.type === "Identifier" && node.init?.type === "CallExpression" && resolveSolidCallee(node.init.callee, context, ACTION_FACTORIES) != null) {
1032
+ const actionId = node.id;
1033
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === actionId.name);
1034
+ if (variable) actionVariables.add(variable);
1035
+ return;
1036
+ }
1037
+ if (node.id.type === "Identifier" && node.init?.type === "Identifier") {
1038
+ const source = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.init), node.init);
1039
+ if (source && actionVariables.has(source)) {
1040
+ const aliasId = node.id;
1041
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === aliasId.name);
1042
+ if (variable) actionVariables.add(variable);
1043
+ }
1044
+ }
1045
+ if (node.id.type !== "ArrayPattern" || node.init?.type !== "CallExpression" || node.init.callee.type !== "Identifier") return;
1046
+ const canonical = resolveSolidCallee(node.init.callee, context, SETTER_FACTORIES);
1047
+ if (canonical == null) return;
1048
+ const setterElement = node.id.elements[1];
1049
+ if (setterElement?.type !== "Identifier") return;
1050
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === setterElement.name);
1051
+ if (!variable) return;
1052
+ setterVariables.set(variable, { allowOwnedWrite: OWNED_WRITE_FACTORIES.has(canonical) && hasOwnedWriteOption(node.init) });
1053
+ },
1054
+ FunctionDeclaration: onFunctionEnter,
1055
+ FunctionExpression: onFunctionEnter,
1056
+ ArrowFunctionExpression: onFunctionEnter,
1057
+ "FunctionDeclaration:exit": onFunctionExit,
1058
+ "FunctionExpression:exit": onFunctionExit,
1059
+ "ArrowFunctionExpression:exit": onFunctionExit,
1060
+ CallExpression(node) {
1061
+ const enclosingFn = functionStack[functionStack.length - 1];
1062
+ if (!enclosingFn) return;
1063
+ if (node.callee.type === "CallExpression" && resolveSolidCallee(node.callee.callee, context, ACTION_FACTORIES) != null) {
1064
+ pendingDirectActions.push({
1065
+ call: node,
1066
+ enclosingFn
1067
+ });
1068
+ return;
1069
+ }
1070
+ if (node.callee.type !== "Identifier") return;
1071
+ pendingCalls.push({
1072
+ callee: node.callee,
1073
+ enclosingFn
1074
+ });
1075
+ },
1076
+ "Program:exit"() {
1077
+ const ownedScopeCache = /* @__PURE__ */ new Map();
1078
+ const isOwnedScope = (fn) => {
1079
+ let verdict = ownedScopeCache.get(fn);
1080
+ if (verdict === void 0) {
1081
+ verdict = isOwnedScopeFunction(fn, context);
1082
+ ownedScopeCache.set(fn, verdict);
1083
+ }
1084
+ return verdict;
1085
+ };
1086
+ for (const { callee, enclosingFn } of pendingCalls) {
1087
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(callee), callee);
1088
+ if (variable && actionVariables.has(variable) && isOwnedScope(enclosingFn)) {
1089
+ context.report({
1090
+ node: callee,
1091
+ messageId: "noActionInOwnedScope"
1092
+ });
1093
+ continue;
1094
+ }
1095
+ const setter = variable && setterVariables.get(variable);
1096
+ if (!setter || setter.allowOwnedWrite) continue;
1097
+ if (isOwnedScope(enclosingFn)) context.report({
1098
+ node: callee,
1099
+ messageId: "noOwnedScopeWrite"
1100
+ });
1101
+ }
1102
+ for (const { call, enclosingFn } of pendingDirectActions) if (isOwnedScope(enclosingFn)) context.report({
1103
+ node: call,
1104
+ messageId: "noActionInOwnedScope"
1105
+ });
1106
+ }
1107
+ };
1108
+ }
1109
+ });
1110
+ //#endregion
1111
+ //#region src/rules/no-reactive-read-after-await.ts
1112
+ const COMPUTE_FACTORIES = new Set([
1113
+ "createMemo",
1114
+ "createEffect",
1115
+ "createRenderEffect",
1116
+ "createProjection"
1117
+ ]);
1118
+ const ACCESSOR_FACTORIES$1 = new Set(["createMemo"]);
1119
+ const PAIR_ACCESSOR_FACTORIES$1 = new Set(["createSignal", "createOptimistic"]);
1120
+ const SOLID_ACCESSOR_TYPE_NAMES = new Set(["Accessor", "SourceAccessor"]);
1121
+ function hasGuaranteedAwait(node) {
1122
+ if (node == null) return false;
1123
+ switch (node.type) {
1124
+ case "AwaitExpression": return true;
1125
+ case "SequenceExpression": return node.expressions.some(hasGuaranteedAwait);
1126
+ case "ArrayExpression": return node.elements.some(hasGuaranteedAwait);
1127
+ case "ObjectExpression": return node.properties.some((property) => {
1128
+ if (property.type === "SpreadElement") return hasGuaranteedAwait(property.argument);
1129
+ return property.computed && hasGuaranteedAwait(property.key) || hasGuaranteedAwait(property.value);
1130
+ });
1131
+ case "TemplateLiteral": return node.expressions.some(hasGuaranteedAwait);
1132
+ case "TaggedTemplateExpression": return hasGuaranteedAwait(node.tag) || hasGuaranteedAwait(node.quasi);
1133
+ case "UnaryExpression": return hasGuaranteedAwait(node.argument);
1134
+ case "BinaryExpression": return hasGuaranteedAwait(node.left) || hasGuaranteedAwait(node.right);
1135
+ case "AssignmentExpression": return hasGuaranteedAwait(node.right);
1136
+ case "NewExpression": return hasGuaranteedAwait(node.callee) || node.arguments.some(hasGuaranteedAwait);
1137
+ case "CallExpression":
1138
+ if (node.optional) return false;
1139
+ return hasGuaranteedAwait(node.callee) || node.arguments.some(hasGuaranteedAwait);
1140
+ default: return false;
1141
+ }
1142
+ }
1143
+ function statementGuaranteesAwait(stmt) {
1144
+ switch (stmt.type) {
1145
+ case "ExpressionStatement": return hasGuaranteedAwait(stmt.expression);
1146
+ case "VariableDeclaration": return stmt.declarations.some((declarator) => hasGuaranteedAwait(declarator.init));
1147
+ case "BlockStatement": return stmt.body.some(statementGuaranteesAwait);
1148
+ default: return false;
1149
+ }
1150
+ }
1151
+ function collectTypeParts(type, acc = [], seen = /* @__PURE__ */ new Set()) {
1152
+ if (seen.has(type)) return acc;
1153
+ seen.add(type);
1154
+ acc.push(type);
1155
+ if (type.isUnion() || type.isIntersection()) for (const member of type.types) collectTypeParts(member, acc, seen);
1156
+ return acc;
1157
+ }
1158
+ function symbolIsFromSolid(symbol, checker) {
1159
+ const isSolidOrigin = (text) => text.includes("solid-js") || text.includes("@solidjs") || text.includes("solid-signals");
1160
+ if (isSolidOrigin(checker.getFullyQualifiedName(symbol))) return true;
1161
+ for (const declaration of symbol.getDeclarations() ?? []) if (isSolidOrigin(declaration.getSourceFile().fileName)) return true;
1162
+ return false;
1163
+ }
1164
+ var no_reactive_read_after_await_default = createRule({
1165
+ name: "no-reactive-read-after-await",
1166
+ meta: {
1167
+ type: "problem",
1168
+ docs: { description: "Disallow reading reactive state (signal/memo accessors) after an `await` in an async reactive computation, where it is no longer tracked as a dependency." },
1169
+ schema: [{
1170
+ type: "object",
1171
+ properties: { typescriptEnabled: { type: "boolean" } },
1172
+ additionalProperties: false
1173
+ }],
1174
+ messages: { reactiveReadAfterAwait: "Signal '{{name}}' is read after an `await` in this reactive computation. Reactive tracking ends at the first await, so this read does not register a dependency and the computation won't re-run when '{{name}}' changes. Read it before the await, or wrap it in `untrack()` if that's intentional." }
1175
+ },
1176
+ defaultOptions: [{}],
1177
+ create(context) {
1178
+ const sourceCode = context.sourceCode;
1179
+ const services = context.options[0]?.typescriptEnabled ?? false ? getTypeAwareServices(context) : null;
1180
+ const checker = services?.program.getTypeChecker() ?? null;
1181
+ const accessorVars = /* @__PURE__ */ new Set();
1182
+ const computeCallbackCache = /* @__PURE__ */ new Map();
1183
+ function isAfterAwait(call, fn) {
1184
+ let current = call;
1185
+ while (current !== fn && current.parent != null) {
1186
+ const parent = current.parent;
1187
+ if (parent.type === "BlockStatement" || parent.type === "StaticBlock") {
1188
+ const index = parent.body.indexOf(current);
1189
+ for (let i = 0; i < index; i++) if (statementGuaranteesAwait(parent.body[i])) return true;
1190
+ } else if (parent.type === "SequenceExpression") {
1191
+ const index = parent.expressions.indexOf(current);
1192
+ for (let i = 0; i < index; i++) if (hasGuaranteedAwait(parent.expressions[i])) return true;
1193
+ }
1194
+ current = parent;
1195
+ }
1196
+ return false;
1197
+ }
1198
+ function isTypeAwareComputeCallback(fn) {
1199
+ if (!services || !checker) return false;
1200
+ const parent = fn.parent;
1201
+ if (parent?.type !== "CallExpression" || parent.arguments[0] !== fn) return false;
1202
+ const calleeNode = services.esTreeNodeToTSNodeMap.get(parent.callee);
1203
+ if (!calleeNode) return false;
1204
+ let symbol = checker.getSymbolAtLocation(calleeNode);
1205
+ if (symbol && symbol.flags & typescript.SymbolFlags.Alias) symbol = checker.getAliasedSymbol(symbol);
1206
+ return symbol != null && COMPUTE_FACTORIES.has(symbol.getName()) && symbolIsFromSolid(symbol, checker);
1207
+ }
1208
+ function isComputeCallback(fn) {
1209
+ const cached = computeCallbackCache.get(fn);
1210
+ if (cached !== void 0) return cached;
1211
+ const result = isSolidApiCallbackArgument(fn, 0, context, COMPUTE_FACTORIES) || isTypeAwareComputeCallback(fn);
1212
+ computeCallbackCache.set(fn, result);
1213
+ return result;
1214
+ }
1215
+ function isSolidAccessorCallee(callee) {
1216
+ if (!services || !checker) return false;
1217
+ const calleeNode = services.esTreeNodeToTSNodeMap.get(callee);
1218
+ if (!calleeNode) return false;
1219
+ return collectTypeParts(checker.getTypeAtLocation(calleeNode)).some((part) => {
1220
+ const alias = part.aliasSymbol;
1221
+ return alias != null && SOLID_ACCESSOR_TYPE_NAMES.has(alias.getName()) && symbolIsFromSolid(alias, checker);
1222
+ });
1223
+ }
1224
+ function accessorReadName(call) {
1225
+ if (call.callee.type === "Identifier") {
1226
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(call), call.callee);
1227
+ if (variable && accessorVars.has(variable)) return call.callee.name;
1228
+ }
1229
+ if (services && isSolidAccessorCallee(call.callee)) return sourceCode.getText(call.callee);
1230
+ return null;
1231
+ }
1232
+ return {
1233
+ VariableDeclarator(node) {
1234
+ if (node.id.type === "Identifier" && node.init?.type === "CallExpression" && node.init.callee.type === "Identifier" && bindsToSolid(node.init.callee, context, ACCESSOR_FACTORIES$1)) {
1235
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.id), node.id);
1236
+ if (variable) accessorVars.add(variable);
1237
+ return;
1238
+ }
1239
+ if (node.id.type === "ArrayPattern" && node.init?.type === "CallExpression" && node.init.callee.type === "Identifier" && bindsToSolid(node.init.callee, context, PAIR_ACCESSOR_FACTORIES$1)) {
1240
+ const first = node.id.elements[0];
1241
+ if (first?.type === "Identifier") {
1242
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === first.name);
1243
+ if (variable) accessorVars.add(variable);
1244
+ }
1245
+ return;
1246
+ }
1247
+ if (node.id.type === "Identifier" && node.init?.type === "Identifier") {
1248
+ const source = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.init), node.init);
1249
+ if (source && accessorVars.has(source)) {
1250
+ const target = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.id), node.id);
1251
+ if (target) accessorVars.add(target);
1252
+ }
1253
+ }
1254
+ },
1255
+ CallExpression(node) {
1256
+ const fn = getNearestFunctionAncestor(node);
1257
+ if (!fn || !fn.async || !isComputeCallback(fn) || !isAfterAwait(node, fn)) return;
1258
+ const name = accessorReadName(node);
1259
+ if (name == null) return;
1260
+ context.report({
1261
+ node,
1262
+ messageId: "reactiveReadAfterAwait",
1263
+ data: { name }
1264
+ });
1265
+ }
1266
+ };
1267
+ }
1268
+ });
1269
+ //#endregion
1270
+ //#region src/rules/no-stale-props-alias.ts
1271
+ const UNTRACK_NAMES = new Set(["untrack"]);
1272
+ const PROPS_HELPER_NAMES = new Set(["merge", "omit"]);
1273
+ function isPropsVariableIdentifier(node, propsVariables, sourceCode) {
1274
+ if (node.type !== "Identifier") return false;
1275
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node), node);
1276
+ return variable != null && propsVariables.has(variable);
1277
+ }
1278
+ /**
1279
+ * Whether the expression, evaluated at the component's top level, performs an *eager read* of the
1280
+ * props object: a member access rooted at a props variable (`props.name`, `alias.user.name`,
1281
+ * `props[key]`) or a spread of it (`{ ...props }` reads every property). A *bare* props reference
1282
+ * is not a read — passing the object around (`merge(defaults, props)`, `omit(props, "class")`,
1283
+ * `const alias = props`) keeps reactivity intact, and treating it as one flagged the canonical
1284
+ * merge/omit patterns. A callee that might read eagerly (`format(props)`) is undecidable and is a
1285
+ * tolerated false negative.
1286
+ */
1287
+ function expressionContainsPropsRead(node, propsVariables, sourceCode, context) {
1288
+ const stack = [node];
1289
+ while (stack.length > 0) {
1290
+ const current = stack.pop();
1291
+ if (isFunctionNode(current)) continue;
1292
+ if (current.type === "CallExpression" && current.arguments[0] != null) {
1293
+ if (current.callee.type === "Identifier" && bindsToSolid(current.callee, context, UNTRACK_NAMES)) {
1294
+ const untracked = current.arguments[0];
1295
+ for (const child of getChildNodes(current)) if (child !== untracked) stack.push(child);
1296
+ continue;
1297
+ }
1298
+ }
1299
+ if (current.type === "MemberExpression") {
1300
+ let root = current;
1301
+ while (root.type === "MemberExpression") root = root.object;
1302
+ if (isPropsVariableIdentifier(root, propsVariables, sourceCode)) return true;
1303
+ }
1304
+ if (current.type === "SpreadElement" && isPropsVariableIdentifier(current.argument, propsVariables, sourceCode)) return true;
1305
+ stack.push(...getChildNodes(current));
1306
+ }
1307
+ return false;
1308
+ }
1309
+ /**
1310
+ * Whether `init` is a `merge(...)`/`omit(...)` call (bound to solid-js) that receives the props
1311
+ * object as a bare argument — the canonical defaults/rest pattern whose result is a reactive
1312
+ * props-like proxy.
1313
+ */
1314
+ function isPropsHelperAliasInit(init, propsVariables, sourceCode, context) {
1315
+ if (init.type !== "CallExpression" || init.callee.type !== "Identifier" || !bindsToSolid(init.callee, context, PROPS_HELPER_NAMES)) return false;
1316
+ return init.arguments.some((argument) => {
1317
+ if (argument.type !== "Identifier") return false;
1318
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(argument), argument);
1319
+ return variable != null && propsVariables.has(variable);
1320
+ });
1321
+ }
1322
+ function getDeclaredIdentifierVariable(declaration, sourceCode) {
1323
+ if (declaration.id.type !== "Identifier") return null;
1324
+ const id = declaration.id;
1325
+ return sourceCode.scopeManager?.getDeclaredVariables(declaration).find((variable) => variable.name === id.name) ?? null;
1326
+ }
1327
+ function hasWriteAfterInit(variable) {
1328
+ return variable.references.some((reference) => !reference.init && reference.isWrite());
1329
+ }
1330
+ function getAssignmentExpression(statement) {
1331
+ if (statement.type !== "ExpressionStatement") return null;
1332
+ const expression = statement.expression;
1333
+ return expression.type === "AssignmentExpression" && expression.operator === "=" && expression.left.type === "Identifier" ? expression : null;
1334
+ }
1335
+ function getChildNodes(node) {
1336
+ const children = [];
1337
+ for (const key in node) {
1338
+ if (key === "parent" || key === "tokens" || key === "comments") continue;
1339
+ const value = node[key];
1340
+ if (Array.isArray(value)) {
1341
+ for (const item of value) if (item != null && typeof item === "object" && typeof item.type === "string") children.push(item);
1342
+ } else if (value != null && typeof value === "object" && typeof value.type === "string") children.push(value);
1343
+ }
1344
+ return children;
1345
+ }
1346
+ var no_stale_props_alias_default = createRule({
1347
+ name: "no-stale-props-alias",
1348
+ meta: {
1349
+ type: "problem",
1350
+ docs: { description: "Disallow top-level aliases of component props, which read props outside tracking." },
1351
+ schema: [{
1352
+ type: "object",
1353
+ properties: { typescriptEnabled: { type: "boolean" } },
1354
+ additionalProperties: false
1355
+ }],
1356
+ messages: { stalePropsAlias: "`{{name}}` aliases a prop read outside tracking. Read from `props` in JSX or a tracked scope instead." }
1357
+ },
1358
+ defaultOptions: [{}],
1359
+ create(context) {
1360
+ const sourceCode = context.sourceCode;
1361
+ const checkFunction = (node) => {
1362
+ if (node.body.type !== "BlockStatement" || !isComponent$1(node, context)) return;
1363
+ const props = node.params[0];
1364
+ if (node.params.length !== 1 || props?.type !== "Identifier") return;
1365
+ const propsVariable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(props), props);
1366
+ if (propsVariable == null) return;
1367
+ const propsVariables = new Set([propsVariable]);
1368
+ for (const statement of node.body.body) {
1369
+ if (statement.type === "VariableDeclaration") {
1370
+ for (const declaration of statement.declarations) {
1371
+ const aliasId = declaration.id;
1372
+ if (aliasId.type !== "Identifier" || declaration.init == null) continue;
1373
+ const isPropsLikeAlias = isPropsVariableIdentifier(declaration.init, propsVariables, sourceCode) || isPropsHelperAliasInit(declaration.init, propsVariables, sourceCode, context);
1374
+ if (!expressionContainsPropsRead(declaration.init, propsVariables, sourceCode, context)) {
1375
+ if (isPropsLikeAlias) {
1376
+ const declared = getDeclaredIdentifierVariable(declaration, sourceCode);
1377
+ if (declared != null && !hasWriteAfterInit(declared)) propsVariables.add(declared);
1378
+ }
1379
+ continue;
1380
+ }
1381
+ const declared = getDeclaredIdentifierVariable(declaration, sourceCode);
1382
+ const isStableAlias = declared != null && !hasWriteAfterInit(declared);
1383
+ if (statement.kind === "const" || isStableAlias) context.report({
1384
+ node: declaration,
1385
+ messageId: "stalePropsAlias",
1386
+ data: { name: aliasId.name }
1387
+ });
1388
+ if (isStableAlias) propsVariables.add(declared);
1389
+ }
1390
+ continue;
1391
+ }
1392
+ const expression = getAssignmentExpression(statement);
1393
+ if (expression == null || expression.left.type !== "Identifier") continue;
1394
+ const assigned = expression.left;
1395
+ if (!expressionContainsPropsRead(expression.right, propsVariables, sourceCode, context)) continue;
1396
+ context.report({
1397
+ node: expression,
1398
+ messageId: "stalePropsAlias",
1399
+ data: { name: assigned.name }
1400
+ });
1401
+ }
1402
+ };
1403
+ return {
1404
+ "FunctionDeclaration:exit": checkFunction,
1405
+ "FunctionExpression:exit": checkFunction,
1406
+ "ArrowFunctionExpression:exit": checkFunction
1407
+ };
1408
+ }
1409
+ });
1410
+ //#endregion
1411
+ //#region src/rules/no-untracked-read-in-effect-apply.ts
1412
+ const EFFECT_NAMES = new Set(["createEffect", "createRenderEffect"]);
1413
+ const ACCESSOR_FACTORIES = new Set(["createMemo", "createProjection"]);
1414
+ const PAIR_ACCESSOR_FACTORIES = new Set(["createOptimistic", "createSignal"]);
1415
+ const STORE_FACTORIES = new Set(["createOptimisticStore", "createStore"]);
1416
+ const SAFE_HELPERS = new Set(["deep", "snapshot"]);
1417
+ var no_untracked_read_in_effect_apply_default = createRule({
1418
+ name: "no-untracked-read-in-effect-apply",
1419
+ meta: {
1420
+ type: "problem",
1421
+ docs: { description: "Disallow reading reactive state (signal accessors or store proxies) in a createEffect apply callback, which runs untracked." },
1422
+ schema: [],
1423
+ messages: {
1424
+ signalRead: "Signal '{{name}}' is called directly in an effect apply callback. The apply phase runs untracked — read it in the compute phase and use the passed value, or wrap it in `untrack()`.",
1425
+ storeProxyRead: "Effect apply callbacks run untracked in Solid 2. Extract store properties in the compute phase or use `deep()` before reading them here."
1426
+ }
1427
+ },
1428
+ defaultOptions: [],
1429
+ create(context) {
1430
+ const sourceCode = context.sourceCode;
1431
+ const reactiveVars = /* @__PURE__ */ new Map();
1432
+ const applyCallbacks = /* @__PURE__ */ new Set();
1433
+ const pendingAccessorCalls = [];
1434
+ const storeVars = /* @__PURE__ */ new Set();
1435
+ const storeShapes = /* @__PURE__ */ new Map();
1436
+ const getMemberPath = (node) => {
1437
+ const path = [];
1438
+ let current = node;
1439
+ while (current.type === "MemberExpression") {
1440
+ if (current.computed || current.property.type !== "Identifier") return null;
1441
+ path.unshift(current.property.name);
1442
+ current = current.object;
1443
+ }
1444
+ return current.type === "Identifier" ? {
1445
+ root: current,
1446
+ path
1447
+ } : null;
1448
+ };
1449
+ const pathLandsOnProxy = (shape, path) => {
1450
+ if (shape == null) return "unknown";
1451
+ let current = shape;
1452
+ for (const key of path) {
1453
+ if (current.type !== "ObjectExpression") return "unknown";
1454
+ let next;
1455
+ for (const candidate of current.properties) if (candidate.type === "Property" && getPropertyName$2(candidate) === key) {
1456
+ next = candidate.value;
1457
+ break;
1458
+ }
1459
+ if (next == null) return "unknown";
1460
+ current = next;
1461
+ }
1462
+ return current.type === "ObjectExpression" || current.type === "ArrayExpression";
1463
+ };
1464
+ const getInlineFunction = (value) => {
1465
+ if (value == null || value.type === "SpreadElement") return null;
1466
+ if (isFunctionNode(value)) return value;
1467
+ const traced = trace(value, context);
1468
+ return isFunctionNode(traced) ? traced : null;
1469
+ };
1470
+ const getInlineApplyCallback = (value) => {
1471
+ const direct = getInlineFunction(value);
1472
+ if (direct) return direct;
1473
+ if (value?.type !== "ObjectExpression") return null;
1474
+ for (const property of value.properties) {
1475
+ if (property.type !== "Property" || getPropertyName$2(property) !== "effect") continue;
1476
+ const fn = getInlineFunction(property.value);
1477
+ if (fn) return fn;
1478
+ }
1479
+ return null;
1480
+ };
1481
+ const findContainingApplyCallback = (node) => {
1482
+ const nearest = getNearestFunctionAncestor(node);
1483
+ return nearest != null && applyCallbacks.has(nearest) ? nearest : null;
1484
+ };
1485
+ const getFunctionValue = (value) => {
1486
+ if (value == null || value.type === "SpreadElement") return null;
1487
+ if (value.type === "FunctionExpression" || value.type === "ArrowFunctionExpression") return value;
1488
+ const traced = trace(value, context);
1489
+ return traced.type === "FunctionExpression" || traced.type === "ArrowFunctionExpression" ? traced : null;
1490
+ };
1491
+ const getTracedApplyCallback = (value) => {
1492
+ const direct = getFunctionValue(value);
1493
+ if (direct) return direct;
1494
+ if (value?.type !== "ObjectExpression") return null;
1495
+ for (const property of value.properties) {
1496
+ if (property.type !== "Property" || getPropertyName$2(property) !== "effect") continue;
1497
+ const effect = getFunctionValue(property.value);
1498
+ if (effect) return effect;
1499
+ }
1500
+ return null;
1501
+ };
1502
+ const isStoreSourceExpression = (node) => {
1503
+ if (node == null) return false;
1504
+ if (node.type === "CallExpression" && resolveSolidCallee(node.callee, context, SAFE_HELPERS) != null) return false;
1505
+ if (node.type === "Identifier") {
1506
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node), node);
1507
+ return variable != null && storeVars.has(variable);
1508
+ }
1509
+ if (node.type === "MemberExpression") {
1510
+ const resolved = getMemberPath(node);
1511
+ if (resolved == null) return false;
1512
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(resolved.root), resolved.root);
1513
+ if (variable == null || !storeVars.has(variable)) return false;
1514
+ return pathLandsOnProxy(storeShapes.get(variable) ?? null, resolved.path) === true;
1515
+ }
1516
+ return false;
1517
+ };
1518
+ const checkStoreProxyInApply = (node) => {
1519
+ if (resolveSolidCallee(node.callee, context, EFFECT_NAMES) == null || node.arguments.length < 2) return;
1520
+ const compute = getFunctionValue(node.arguments[0]);
1521
+ const apply = getTracedApplyCallback(node.arguments[1]);
1522
+ if (!compute || !apply) return;
1523
+ const returned = getReturnedExpressions(compute).filter((value) => value != null);
1524
+ if (returned.length === 0) return;
1525
+ if (!returned.some((value) => isStoreSourceExpression(value)) || apply.params.length === 0) return;
1526
+ const applyParam = apply.params[0];
1527
+ if (applyParam.type !== "Identifier") return;
1528
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(applyParam), applyParam);
1529
+ if (!variable) return;
1530
+ for (const reference of variable.references) {
1531
+ const identifier = reference.identifier;
1532
+ if (reference.init) continue;
1533
+ if (getNearestFunctionAncestor(identifier) !== apply) continue;
1534
+ if (identifier.parent?.type === "VariableDeclarator" && identifier.parent.init === identifier && (identifier.parent.id.type === "ObjectPattern" || identifier.parent.id.type === "ArrayPattern")) {
1535
+ context.report({
1536
+ node: identifier.parent,
1537
+ messageId: "storeProxyRead"
1538
+ });
1539
+ break;
1540
+ }
1541
+ if (identifier.parent?.type === "SpreadElement") {
1542
+ context.report({
1543
+ node: identifier.parent,
1544
+ messageId: "storeProxyRead"
1545
+ });
1546
+ break;
1547
+ }
1548
+ if (identifier.parent?.type === "MemberExpression" && identifier.parent.object === identifier) {
1549
+ context.report({
1550
+ node: identifier.parent,
1551
+ messageId: "storeProxyRead"
1552
+ });
1553
+ break;
1554
+ }
1555
+ }
1556
+ };
1557
+ return {
1558
+ VariableDeclarator(node) {
1559
+ if (node.id.type === "Identifier" && node.init?.type === "CallExpression" && node.init.callee.type === "Identifier" && bindsToSolid(node.init.callee, context, ACCESSOR_FACTORIES)) {
1560
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.id), node.id);
1561
+ if (variable) reactiveVars.set(variable, "accessor");
1562
+ return;
1563
+ }
1564
+ if (node.id.type === "ArrayPattern" && node.init?.type === "CallExpression" && node.init.callee.type === "Identifier" && bindsToSolid(node.init.callee, context, PAIR_ACCESSOR_FACTORIES)) {
1565
+ const first = node.id.elements[0];
1566
+ if (first?.type === "Identifier") {
1567
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === first.name);
1568
+ if (variable) reactiveVars.set(variable, "accessor");
1569
+ }
1570
+ return;
1571
+ }
1572
+ if (node.id.type === "Identifier" && node.init?.type === "Identifier") {
1573
+ const source = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.init), node.init);
1574
+ if (source && reactiveVars.get(source) === "accessor") {
1575
+ const target = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node.id), node.id);
1576
+ if (target) reactiveVars.set(target, "accessor");
1577
+ }
1578
+ }
1579
+ if (node.id.type === "ArrayPattern" && node.init?.type === "CallExpression" && resolveSolidCallee(node.init.callee, context, STORE_FACTORIES) != null) {
1580
+ const first = node.id.elements[0];
1581
+ if (first?.type === "Identifier") {
1582
+ const variable = sourceCode.scopeManager?.getDeclaredVariables(node).find((declared) => declared.name === first.name);
1583
+ if (variable) {
1584
+ storeVars.add(variable);
1585
+ const initialValue = node.init.arguments[0];
1586
+ storeShapes.set(variable, initialValue?.type === "ObjectExpression" ? initialValue : null);
1587
+ }
1588
+ }
1589
+ }
1590
+ },
1591
+ CallExpression(node) {
1592
+ if (node.callee.type === "Identifier" && bindsToSolid(node.callee, context, EFFECT_NAMES) && node.arguments.length >= 2) {
1593
+ const apply = getInlineApplyCallback(node.arguments[1]);
1594
+ if (apply) applyCallbacks.add(apply);
1595
+ }
1596
+ checkStoreProxyInApply(node);
1597
+ if (node.callee.type === "Identifier") pendingAccessorCalls.push(node);
1598
+ },
1599
+ "Program:exit"() {
1600
+ for (const node of pendingAccessorCalls) {
1601
+ const callee = node.callee;
1602
+ if (callee.type !== "Identifier") continue;
1603
+ const variable = _typescript_eslint_utils.ASTUtils.findVariable(sourceCode.getScope(node), callee);
1604
+ if (variable && reactiveVars.get(variable) === "accessor" && findContainingApplyCallback(node) != null) context.report({
1605
+ node,
1606
+ messageId: "signalRead",
1607
+ data: { name: callee.name }
1608
+ });
1609
+ }
1610
+ }
1611
+ };
1612
+ }
1613
+ });
1614
+ //#endregion
1615
+ //#region src/rules/prefer-for.ts
1616
+ const PURE_ARRAY_TRANSFORMS = new Set([
1617
+ "filter",
1618
+ "slice",
1619
+ "concat",
1620
+ "map",
1621
+ "flat",
1622
+ "flatMap",
1623
+ "sort",
1624
+ "reverse",
1625
+ "toSorted",
1626
+ "toReversed",
1627
+ "toSpliced",
1628
+ "with"
1629
+ ]);
1630
+ const getPropertyName = (node) => {
1631
+ if (!node.computed && node.property.type === "Identifier") return node.property.name;
1632
+ if (node.computed && node.property.type === "Literal" && typeof node.property.value === "string") return node.property.value;
1633
+ return null;
1634
+ };
1635
+ function isStaticArrayReceiver(node, context) {
1636
+ if (node.type === "ArrayExpression") return true;
1637
+ if (node.type === "Identifier") {
1638
+ const traced = trace(node, context);
1639
+ return traced !== node && traced.type === "ArrayExpression";
1640
+ }
1641
+ if (node.type === "CallExpression" && node.callee.type === "MemberExpression" && !node.callee.computed && node.callee.property.type === "Identifier" && PURE_ARRAY_TRANSFORMS.has(node.callee.property.name)) return isStaticArrayReceiver(node.callee.object, context);
1642
+ return false;
1643
+ }
1644
+ function isRenderedInJsx(node) {
1645
+ let current = node;
1646
+ for (let parent = current.parent; parent != null; parent = current.parent) {
1647
+ if (parent.type === "ChainExpression") {} else if (parent.type === "LogicalExpression" && parent.operator === "&&" && parent.right === current) {} else if (parent.type === "ConditionalExpression" && (parent.consequent === current || parent.alternate === current)) {} else if (parent.type === "JSXExpressionContainer") return isJSXElementOrFragment(parent.parent);
1648
+ else return false;
1649
+ current = parent;
1650
+ }
1651
+ return false;
1652
+ }
1653
+ var prefer_for_default = createRule({
1654
+ name: "prefer-for",
1655
+ meta: {
1656
+ type: "suggestion",
1657
+ docs: { description: "Prefer Solid's <For /> component over Array#map when rendering JSX lists." },
1658
+ fixable: "code",
1659
+ schema: [{
1660
+ type: "object",
1661
+ properties: { typescriptEnabled: { type: "boolean" } },
1662
+ additionalProperties: false
1663
+ }],
1664
+ messages: { preferFor: "Use Solid's `<For />` component for rendering JSX lists instead of `Array#map(...)`." }
1665
+ },
1666
+ defaultOptions: [{}],
1667
+ create(context) {
1668
+ const sourceCode = context.sourceCode;
1669
+ const typescriptEnabled = context.options[0]?.typescriptEnabled ?? false;
1670
+ return { CallExpression(node) {
1671
+ if (!isRenderedInJsx(node)) return;
1672
+ const callee = node.callee;
1673
+ if (callee.type !== "MemberExpression" || node.arguments.length === 0) return;
1674
+ if (node.arguments[0].type === "SpreadElement") return;
1675
+ if (getPropertyName(callee) !== "map") return;
1676
+ if (isStaticArrayReceiver(callee.object, context)) return;
1677
+ const mapFn = node.arguments[0];
1678
+ if (!isFunctionNode(mapFn) || !functionReturnsJsx(mapFn)) return;
1679
+ let receiverVerdict = "unknown";
1680
+ if (typescriptEnabled) {
1681
+ const services = getTypeAwareServices(context);
1682
+ if (services) {
1683
+ receiverVerdict = getArrayReceiverVerdict(callee.object, services);
1684
+ if (receiverVerdict === "not-array") return;
1685
+ }
1686
+ }
1687
+ const containerNode = node.parent?.type === "ChainExpression" ? node.parent : node;
1688
+ const directContainer = containerNode.parent?.type === "JSXExpressionContainer" && isJSXElementOrFragment(containerNode.parent.parent);
1689
+ const canAutoFix = receiverVerdict === "array" && directContainer && node.arguments.length === 1 && mapFn.type === "ArrowFunctionExpression" && mapFn.params.length <= 2 && mapFn.params.every((param) => param.type === "Identifier") && sourceCode.scopeManager?.acquire(mapFn) != null;
1690
+ if (receiverVerdict !== "array") return;
1691
+ context.report({
1692
+ node,
1693
+ messageId: "preferFor",
1694
+ fix: canAutoFix ? (fixer) => {
1695
+ const importFixes = getSolidImportFixes(context, fixer, ["For"]);
1696
+ if (importFixes == null) return null;
1697
+ const jsxExpressionContainerNode = containerNode.parent;
1698
+ const arrayNode = callee.object;
1699
+ const mapFnNode = node.arguments[0];
1700
+ const scope = sourceCode.scopeManager.acquire(mapFn);
1701
+ const fixes = [
1702
+ ...importFixes,
1703
+ fixer.replaceTextRange([jsxExpressionContainerNode.range[0], arrayNode.range[0]], "<For each={"),
1704
+ fixer.replaceTextRange([arrayNode.range[1], mapFnNode.range[0]], "}>{"),
1705
+ fixer.replaceTextRange([mapFnNode.range[1], jsxExpressionContainerNode.range[1]], "}</For>")
1706
+ ];
1707
+ const indexParam = mapFn.params[1];
1708
+ if (indexParam?.type === "Identifier") {
1709
+ const variable = scope.set.get(indexParam.name);
1710
+ if (variable) {
1711
+ for (const reference of variable.references) if (reference.isReadOnly()) fixes.push(fixer.replaceText(reference.identifier, `${indexParam.name}()`));
1712
+ }
1713
+ }
1714
+ return fixes;
1715
+ } : void 0
1716
+ });
1717
+ } };
1718
+ }
1719
+ });
1720
+ //#endregion
1721
+ //#region src/rules/prefer-show.ts
1722
+ const EXPENSIVE_TYPES = new Set(["JSXElement", "JSXFragment"]);
1723
+ var prefer_show_default = createRule({
1724
+ name: "prefer-show",
1725
+ meta: {
1726
+ type: "suggestion",
1727
+ docs: { description: "Prefer Solid's <Show /> component for JSX conditionals." },
1728
+ fixable: "code",
1729
+ hasSuggestions: true,
1730
+ schema: [],
1731
+ messages: {
1732
+ preferShowAnd: "Use Solid's `<Show />` component for conditionally showing content.",
1733
+ preferShowTernary: "Use Solid's `<Show />` component for conditionally showing content with a fallback.",
1734
+ convertToShow: "Convert to `<Show />`."
1735
+ }
1736
+ },
1737
+ defaultOptions: [],
1738
+ create(context) {
1739
+ const sourceCode = context.sourceCode;
1740
+ const putIntoJSX = (node) => {
1741
+ const text = sourceCode.getText(node);
1742
+ return isJSXElementOrFragment(node) ? text : `{${text}}`;
1743
+ };
1744
+ const replaceTarget = (node) => node.parent?.type === "JSXExpressionContainer" && isJSXElementOrFragment(node.parent.parent) ? node.parent : node;
1745
+ const withShowImport = (fixer, replacement) => {
1746
+ const importFixes = getSolidImportFixes(context, fixer, ["Show"]);
1747
+ return importFixes == null ? null : [...importFixes, replacement];
1748
+ };
1749
+ const logicalExpressionHandler = (node) => {
1750
+ if (node.operator !== "&&" || !EXPENSIVE_TYPES.has(node.right.type)) return;
1751
+ context.report({
1752
+ node,
1753
+ messageId: "preferShowAnd",
1754
+ suggest: [{
1755
+ messageId: "convertToShow",
1756
+ fix: (fixer) => withShowImport(fixer, fixer.replaceText(replaceTarget(node), `<Show when={${sourceCode.getText(node.left)}}>${putIntoJSX(node.right)}</Show>`))
1757
+ }]
1758
+ });
1759
+ };
1760
+ const conditionalExpressionHandler = (node) => {
1761
+ if (!EXPENSIVE_TYPES.has(node.consequent.type) && !EXPENSIVE_TYPES.has(node.alternate.type)) return;
1762
+ context.report({
1763
+ node,
1764
+ messageId: "preferShowTernary",
1765
+ fix: (fixer) => withShowImport(fixer, fixer.replaceText(replaceTarget(node), `<Show when={${sourceCode.getText(node.test)}} fallback={${sourceCode.getText(node.alternate)}}>${putIntoJSX(node.consequent)}</Show>`))
1766
+ });
1767
+ };
1768
+ const renderedExpression = (expression) => {
1769
+ if (expression.type !== "ArrowFunctionExpression") return expression;
1770
+ if (expression.body.type !== "BlockStatement") return expression.body;
1771
+ return [...expression.body.body].reverse().find((statement) => statement.type === "ReturnStatement")?.argument ?? expression;
1772
+ };
1773
+ return { JSXExpressionContainer(node) {
1774
+ if (!isJSXElementOrFragment(node.parent)) return;
1775
+ const expression = renderedExpression(node.expression);
1776
+ if (expression.type === "LogicalExpression") logicalExpressionHandler(expression);
1777
+ else if (expression.type === "ConditionalExpression") conditionalExpressionHandler(expression);
1778
+ } };
1779
+ }
1780
+ });
1781
+ //#endregion
1782
+ //#region src/rules/self-closing-comp.ts
1783
+ const voidDOMElementRegex = /^(?:area|base|br|col|embed|hr|img|input|link|meta|param|source|track|wbr)$/;
1784
+ const isComponent = (node) => node.name.type === "JSXIdentifier" && !isDOMElementName(node.name.name) || node.name.type === "JSXMemberExpression";
1785
+ const isVoidDOMElementName = (name) => voidDOMElementRegex.test(name);
1786
+ const childrenIsEmpty = (node) => node.parent.children.length === 0;
1787
+ const childrenIsMultilineSpaces = (node) => {
1788
+ const children = node.parent.children;
1789
+ return children.length === 1 && children[0].type === "JSXText" && children[0].value.includes("\n") && children[0].value.replace(/(?!\xA0)\s/g, "") === "";
1790
+ };
1791
+ const plugin = {
1792
+ meta: { name: "eslint-plugin-solid" },
1793
+ rules: {
1794
+ "components-return-once": components_return_once_default,
1795
+ "jsx-no-duplicate-props": jsx_no_duplicate_props_default,
1796
+ "no-destructure": no_destructure_default,
1797
+ "no-leaf-owner-operations": no_leaf_owner_operations_default,
1798
+ "no-owned-scope-writes": no_owned_scope_writes_default,
1799
+ "no-reactive-read-after-await": no_reactive_read_after_await_default,
1800
+ "no-stale-props-alias": no_stale_props_alias_default,
1801
+ "no-untracked-read-in-effect-apply": no_untracked_read_in_effect_apply_default,
1802
+ "prefer-for": prefer_for_default,
1803
+ "prefer-show": prefer_show_default,
1804
+ "self-closing-comp": createRule({
1805
+ name: "self-closing-comp",
1806
+ meta: {
1807
+ type: "layout",
1808
+ docs: { description: "Disallow extra closing tags for components without children." },
1809
+ fixable: "code",
1810
+ schema: [{
1811
+ type: "object",
1812
+ properties: {
1813
+ component: {
1814
+ enum: ["all", "none"],
1815
+ type: "string"
1816
+ },
1817
+ html: {
1818
+ enum: [
1819
+ "all",
1820
+ "void",
1821
+ "none"
1822
+ ],
1823
+ type: "string"
1824
+ }
1825
+ },
1826
+ additionalProperties: false
1827
+ }],
1828
+ messages: {
1829
+ dontSelfClose: "This element should not be self-closing.",
1830
+ selfClose: "Empty components are self-closing."
1831
+ }
1832
+ },
1833
+ defaultOptions: [],
1834
+ create(context) {
1835
+ const sourceCode = context.sourceCode;
1836
+ const shouldBeSelfClosedWhenPossible = (node) => {
1837
+ if (isComponent(node)) return (context.options[0]?.component ?? "all") === "all";
1838
+ if (isHostElement(node)) switch (context.options[0]?.html ?? "all") {
1839
+ case "all": return true;
1840
+ case "void": return node.name.type === "JSXIdentifier" && isVoidDOMElementName(node.name.name);
1841
+ case "none": return false;
1842
+ }
1843
+ return true;
1844
+ };
1845
+ return { JSXOpeningElement(node) {
1846
+ if (!(childrenIsEmpty(node) || childrenIsMultilineSpaces(node))) return;
1847
+ const shouldSelfClose = shouldBeSelfClosedWhenPossible(node);
1848
+ if (shouldSelfClose && !node.selfClosing) context.report({
1849
+ node,
1850
+ messageId: "selfClose",
1851
+ fix: (fixer) => {
1852
+ const openingElementEnding = node.range[1] - 1;
1853
+ const closingElementEnding = node.parent.closingElement.range[1];
1854
+ return fixer.replaceTextRange([openingElementEnding, closingElementEnding], " />");
1855
+ }
1856
+ });
1857
+ else if (!shouldSelfClose && node.selfClosing) context.report({
1858
+ node,
1859
+ messageId: "dontSelfClose",
1860
+ fix: (fixer) => {
1861
+ const tagName = sourceCode.getText(node.name);
1862
+ const selfCloseEnding = node.range[1];
1863
+ const lastTokens = sourceCode.getLastTokens(node, { count: 3 });
1864
+ const range = [sourceCode.isSpaceBetween(lastTokens[0], lastTokens[1]) ? selfCloseEnding - 3 : selfCloseEnding - 2, selfCloseEnding];
1865
+ return fixer.replaceTextRange(range, `></${tagName}>`);
1866
+ }
1867
+ });
1868
+ } };
1869
+ }
1870
+ })
1871
+ }
1872
+ };
1873
+ //#endregion
1874
+ //#region src/configs/recommended.ts
1875
+ const recommended = {
1876
+ name: "solid/recommended",
1877
+ plugins: { solid: plugin },
1878
+ rules: {
1879
+ "solid/components-return-once": "warn",
1880
+ "solid/jsx-no-duplicate-props": "error",
1881
+ "solid/no-destructure": "warn",
1882
+ "solid/no-leaf-owner-operations": "error",
1883
+ "solid/no-owned-scope-writes": "error",
1884
+ "solid/no-reactive-read-after-await": "warn",
1885
+ "solid/no-stale-props-alias": "warn",
1886
+ "solid/no-untracked-read-in-effect-apply": "warn",
1887
+ "solid/prefer-show": "warn",
1888
+ "solid/self-closing-comp": "warn"
1889
+ }
1890
+ };
1891
+ //#endregion
1892
+ //#region src/configs/recommended-type-checked.ts
1893
+ const typeAwareOverrides = {
1894
+ "solid/components-return-once": ["warn", { typescriptEnabled: true }],
1895
+ "solid/no-destructure": ["warn", { typescriptEnabled: true }],
1896
+ "solid/no-owned-scope-writes": ["error", { typescriptEnabled: true }],
1897
+ "solid/no-reactive-read-after-await": ["warn", { typescriptEnabled: true }],
1898
+ "solid/prefer-for": ["warn", { typescriptEnabled: true }]
1899
+ };
1900
+ const recommendedTypeChecked = {
1901
+ name: "solid/recommended-type-checked",
1902
+ plugins: { solid: plugin },
1903
+ rules: {
1904
+ ...recommended.rules,
1905
+ ...typeAwareOverrides
1906
+ }
1907
+ };
1908
+ //#endregion
1909
+ //#region src/index.ts
1910
+ const pluginWithConfigs = {
1911
+ ...plugin,
1912
+ configs: {
1913
+ recommended,
1914
+ "flat/recommended": recommended,
1915
+ "recommended-type-checked": recommendedTypeChecked,
1916
+ "flat/recommended-type-checked": recommendedTypeChecked
1917
+ }
1918
+ };
1919
+ //#endregion
1920
+ module.exports = pluginWithConfigs;