@ripple-ts/eslint-plugin 0.3.7 → 0.3.9
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/README.md +13 -44
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +25 -65
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
- package/src/index.ts +3 -6
package/README.md
CHANGED
|
@@ -175,41 +175,11 @@ export component App() {
|
|
|
175
175
|
}
|
|
176
176
|
```
|
|
177
177
|
|
|
178
|
-
### `ripple/
|
|
178
|
+
### `ripple/no-lazy-destructuring-in-modules` (error)
|
|
179
179
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
❌ **Incorrect:**
|
|
184
|
-
|
|
185
|
-
```js
|
|
186
|
-
import { track } from 'ripple';
|
|
187
|
-
|
|
188
|
-
export component App() {
|
|
189
|
-
let count = track(0);
|
|
190
|
-
|
|
191
|
-
// Missing @ operator
|
|
192
|
-
<div>{count}</div>
|
|
193
|
-
}
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
✅ **Correct:**
|
|
197
|
-
|
|
198
|
-
```js
|
|
199
|
-
import { track } from 'ripple';
|
|
200
|
-
|
|
201
|
-
export component App() {
|
|
202
|
-
let count = track(0);
|
|
203
|
-
|
|
204
|
-
// Properly unboxed with @
|
|
205
|
-
<div>{@count}</div>
|
|
206
|
-
}
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
### `ripple/no-introspect-in-modules` (error)
|
|
210
|
-
|
|
211
|
-
Prevents using the `@` introspection operator in TypeScript/JavaScript modules. In
|
|
212
|
-
`.ts`/`.js` files, you should use `get()` and `set()` functions instead.
|
|
180
|
+
Prevents using lazy destructuring (`&[]` / `&{}`) in TypeScript/JavaScript
|
|
181
|
+
modules. In `.ts`/`.js` files, you should use `.value` to read and write tracked
|
|
182
|
+
values instead.
|
|
213
183
|
|
|
214
184
|
❌ **Incorrect:**
|
|
215
185
|
|
|
@@ -218,9 +188,9 @@ Prevents using the `@` introspection operator in TypeScript/JavaScript modules.
|
|
|
218
188
|
import { track, effect } from 'ripple';
|
|
219
189
|
|
|
220
190
|
export function useCount() {
|
|
221
|
-
const count = track(1);
|
|
191
|
+
const &[count] = track(1);
|
|
222
192
|
effect(() => {
|
|
223
|
-
console.log(
|
|
193
|
+
console.log(count); // Error: Cannot use &[] in TypeScript modules
|
|
224
194
|
});
|
|
225
195
|
return { count };
|
|
226
196
|
}
|
|
@@ -230,24 +200,24 @@ export function useCount() {
|
|
|
230
200
|
|
|
231
201
|
```ts
|
|
232
202
|
// count.ts
|
|
233
|
-
import { track, effect
|
|
203
|
+
import { track, effect } from 'ripple';
|
|
234
204
|
|
|
235
205
|
export function useCount() {
|
|
236
206
|
const count = track(1);
|
|
237
207
|
|
|
238
|
-
// Use
|
|
239
|
-
const double = track(() =>
|
|
208
|
+
// Use .value to read tracked values
|
|
209
|
+
const double = track(() => count.value * 2);
|
|
240
210
|
|
|
241
211
|
effect(() => {
|
|
242
|
-
console.log('count is',
|
|
212
|
+
console.log('count is', count.value);
|
|
243
213
|
});
|
|
244
214
|
|
|
245
215
|
return { count, double };
|
|
246
216
|
}
|
|
247
217
|
```
|
|
248
218
|
|
|
249
|
-
**Note:**
|
|
250
|
-
TypeScript modules, use `
|
|
219
|
+
**Note:** Lazy destructuring (`&[]` / `&{}`) is only valid in `.ripple` component
|
|
220
|
+
files. In TypeScript modules, use `.value` to read and write tracked values.
|
|
251
221
|
|
|
252
222
|
## Custom Configuration
|
|
253
223
|
|
|
@@ -262,8 +232,7 @@ export default [
|
|
|
262
232
|
'ripple/require-component-export': 'off', // Disable this rule
|
|
263
233
|
'ripple/prefer-oninput': 'error', // Make this an error instead of warning
|
|
264
234
|
'ripple/no-return-in-component': 'error',
|
|
265
|
-
'ripple/
|
|
266
|
-
'ripple/no-introspect-in-modules': 'error',
|
|
235
|
+
'ripple/no-lazy-destructuring-in-modules': 'error',
|
|
267
236
|
},
|
|
268
237
|
},
|
|
269
238
|
];
|
package/dist/index.d.ts
CHANGED
|
@@ -10,9 +10,8 @@ declare const plugin: {
|
|
|
10
10
|
'no-module-scope-track': eslint.Rule.RuleModule;
|
|
11
11
|
'prefer-oninput': eslint.Rule.RuleModule;
|
|
12
12
|
'no-return-in-component': eslint.Rule.RuleModule;
|
|
13
|
-
'unbox-tracked-values': eslint.Rule.RuleModule;
|
|
14
13
|
'control-flow-jsx': eslint.Rule.RuleModule;
|
|
15
|
-
'no-
|
|
14
|
+
'no-lazy-destructuring-in-modules': eslint.Rule.RuleModule;
|
|
16
15
|
'valid-for-of-key': eslint.Rule.RuleModule;
|
|
17
16
|
};
|
|
18
17
|
configs: any;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;cAQM,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
2
|
|
|
3
3
|
//#region src/rules/no-module-scope-track.ts
|
|
4
|
-
const rule$
|
|
4
|
+
const rule$5 = {
|
|
5
5
|
meta: {
|
|
6
6
|
type: "problem",
|
|
7
7
|
docs: {
|
|
@@ -39,7 +39,7 @@ const rule$6 = {
|
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
41
|
//#region src/rules/prefer-oninput.ts
|
|
42
|
-
const rule$
|
|
42
|
+
const rule$4 = {
|
|
43
43
|
meta: {
|
|
44
44
|
type: "suggestion",
|
|
45
45
|
docs: {
|
|
@@ -88,7 +88,7 @@ const rule$5 = {
|
|
|
88
88
|
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region src/rules/no-return-in-component.ts
|
|
91
|
-
const rule$
|
|
91
|
+
const rule$3 = {
|
|
92
92
|
meta: {
|
|
93
93
|
type: "problem",
|
|
94
94
|
docs: {
|
|
@@ -125,52 +125,6 @@ const rule$4 = {
|
|
|
125
125
|
}
|
|
126
126
|
};
|
|
127
127
|
|
|
128
|
-
//#endregion
|
|
129
|
-
//#region src/rules/unbox-tracked-values.ts
|
|
130
|
-
const rule$3 = {
|
|
131
|
-
meta: {
|
|
132
|
-
type: "problem",
|
|
133
|
-
docs: {
|
|
134
|
-
description: "Ensure tracked values are unboxed with @ operator",
|
|
135
|
-
recommended: true
|
|
136
|
-
},
|
|
137
|
-
messages: { needsUnbox: "Tracked value should be unboxed with @ operator. Did you mean \"@{{name}}\"?" },
|
|
138
|
-
schema: []
|
|
139
|
-
},
|
|
140
|
-
create(context) {
|
|
141
|
-
const trackedVariables = /* @__PURE__ */ new Set();
|
|
142
|
-
function isInJSXContext(node) {
|
|
143
|
-
let parent = node.parent;
|
|
144
|
-
while (parent) {
|
|
145
|
-
const parentType = parent.type;
|
|
146
|
-
if (parentType === "JSXExpressionContainer" || parentType === "JSXElement" || parentType === "JSXFragment" || parentType === "Element") return true;
|
|
147
|
-
parent = parent.parent;
|
|
148
|
-
}
|
|
149
|
-
return false;
|
|
150
|
-
}
|
|
151
|
-
function checkTrackedIdentifier(node) {
|
|
152
|
-
if (trackedVariables.has(node.name) && isInJSXContext(node)) {
|
|
153
|
-
const parent = node.parent;
|
|
154
|
-
let isUnboxed = parent && parent.type === "TrackedExpression" || node.tracked === true;
|
|
155
|
-
if (!isUnboxed) isUnboxed = context.sourceCode.text.substring(Math.max(0, node.range[0] - 1), node.range[0]) === "@";
|
|
156
|
-
if (!isUnboxed) context.report({
|
|
157
|
-
node,
|
|
158
|
-
messageId: "needsUnbox",
|
|
159
|
-
data: { name: node.name }
|
|
160
|
-
});
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
return {
|
|
164
|
-
"VariableDeclarator[init.callee.name=\"track\"]"(node) {
|
|
165
|
-
if (node.id.type === "Identifier") trackedVariables.add(node.id.name);
|
|
166
|
-
},
|
|
167
|
-
Identifier(node) {
|
|
168
|
-
checkTrackedIdentifier(node);
|
|
169
|
-
}
|
|
170
|
-
};
|
|
171
|
-
}
|
|
172
|
-
};
|
|
173
|
-
|
|
174
128
|
//#endregion
|
|
175
129
|
//#region src/rules/control-flow-jsx.ts
|
|
176
130
|
const rule$2 = {
|
|
@@ -237,26 +191,34 @@ const rule$2 = {
|
|
|
237
191
|
};
|
|
238
192
|
|
|
239
193
|
//#endregion
|
|
240
|
-
//#region src/rules/no-
|
|
194
|
+
//#region src/rules/no-lazy-destructuring-in-modules.ts
|
|
241
195
|
const rule$1 = {
|
|
242
196
|
meta: {
|
|
243
197
|
type: "problem",
|
|
244
198
|
docs: {
|
|
245
|
-
description: "Disallow
|
|
199
|
+
description: "Disallow lazy destructuring (&[] / &{}) in TypeScript/JavaScript modules",
|
|
246
200
|
recommended: true
|
|
247
201
|
},
|
|
248
|
-
messages: {
|
|
202
|
+
messages: { noLazyDestructuring: "Lazy destructuring (&[] / &{}) cannot be used in TypeScript/JavaScript modules. Use .value to read and write tracked values instead." },
|
|
249
203
|
schema: []
|
|
250
204
|
},
|
|
251
205
|
create(context) {
|
|
252
206
|
const filename = context.filename;
|
|
253
207
|
if (filename && filename.endsWith(".ripple")) return {};
|
|
254
|
-
return {
|
|
255
|
-
|
|
256
|
-
node
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
208
|
+
return {
|
|
209
|
+
ArrayPattern(node) {
|
|
210
|
+
if (node.lazy === true) context.report({
|
|
211
|
+
node,
|
|
212
|
+
messageId: "noLazyDestructuring"
|
|
213
|
+
});
|
|
214
|
+
},
|
|
215
|
+
ObjectPattern(node) {
|
|
216
|
+
if (node.lazy === true) context.report({
|
|
217
|
+
node,
|
|
218
|
+
messageId: "noLazyDestructuring"
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
};
|
|
260
222
|
}
|
|
261
223
|
};
|
|
262
224
|
|
|
@@ -347,12 +309,11 @@ const plugin = {
|
|
|
347
309
|
version: "0.1.3"
|
|
348
310
|
},
|
|
349
311
|
rules: {
|
|
350
|
-
"no-module-scope-track": rule$
|
|
351
|
-
"prefer-oninput": rule$
|
|
352
|
-
"no-return-in-component": rule$
|
|
353
|
-
"unbox-tracked-values": rule$3,
|
|
312
|
+
"no-module-scope-track": rule$5,
|
|
313
|
+
"prefer-oninput": rule$4,
|
|
314
|
+
"no-return-in-component": rule$3,
|
|
354
315
|
"control-flow-jsx": rule$2,
|
|
355
|
-
"no-
|
|
316
|
+
"no-lazy-destructuring-in-modules": rule$1,
|
|
356
317
|
"valid-for-of-key": rule
|
|
357
318
|
},
|
|
358
319
|
configs: {}
|
|
@@ -379,9 +340,8 @@ function createConfig(name, files, parser) {
|
|
|
379
340
|
"ripple/no-module-scope-track": "error",
|
|
380
341
|
"ripple/prefer-oninput": "warn",
|
|
381
342
|
"ripple/no-return-in-component": "error",
|
|
382
|
-
"ripple/unbox-tracked-values": "error",
|
|
383
343
|
"ripple/control-flow-jsx": "error",
|
|
384
|
-
"ripple/no-
|
|
344
|
+
"ripple/no-lazy-destructuring-in-modules": "error",
|
|
385
345
|
"ripple/valid-for-of-key": "error"
|
|
386
346
|
}
|
|
387
347
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["rule","rule","rule","rule","rule","rule","noModuleScopeTrack","preferOnInput","noReturnInComponent","unboxTrackedValues","controlFlowJsx","noIntrospectInModules","validForOfKey"],"sources":["../src/rules/no-module-scope-track.ts","../src/rules/prefer-oninput.ts","../src/rules/no-return-in-component.ts","../src/rules/unbox-tracked-values.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-introspect-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow calling track() at module scope',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tmoduleScope:\n\t\t\t\t'track() cannot be called at module scope. It must be called within a component context.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet componentDepth = 0;\n\t\tlet functionDepth = 0;\n\n\t\tconst incrementComponentDepth = () => componentDepth++;\n\t\tconst decrementComponentDepth = () => componentDepth--;\n\t\tconst incrementFunctionDepth = () => functionDepth++;\n\t\tconst decrementFunctionDepth = () => functionDepth--;\n\n\t\treturn {\n\t\t\t// Only track when we enter a Ripple component\n\t\t\t// Ripple's parser returns \"Component\" nodes for component declarations\n\t\t\tComponent: incrementComponentDepth,\n\t\t\t'Component:exit': decrementComponentDepth,\n\n\t\t\t// Track regular functions and arrow functions\n\t\t\tFunctionDeclaration: incrementFunctionDepth,\n\t\t\t'FunctionDeclaration:exit': decrementFunctionDepth,\n\t\t\tFunctionExpression: incrementFunctionDepth,\n\t\t\t'FunctionExpression:exit': decrementFunctionDepth,\n\t\t\tArrowFunctionExpression: incrementFunctionDepth,\n\t\t\t'ArrowFunctionExpression:exit': decrementFunctionDepth,\n\n\t\t\t// Check track() calls\n\t\t\tCallExpression(node: AST.CallExpression) {\n\t\t\t\tif (\n\t\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\t\tnode.callee.name === 'track' &&\n\t\t\t\t\tcomponentDepth === 0 &&\n\t\t\t\t\tfunctionDepth === 0\n\t\t\t\t) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'moduleScope',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\nimport type * as ESTreeJSX from 'ripple/types/estree-jsx';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'suggestion',\n\t\tdocs: {\n\t\t\tdescription: 'Prefer onInput over onChange for form inputs in Ripple',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tpreferOnInput:\n\t\t\t\t'Use \"onInput\" instead of \"onChange\". Ripple does not have synthetic events like React.',\n\t\t},\n\t\tfixable: 'code',\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst reported_ranges = new Set<string>();\n\n\t\tfunction report_onchange(node: AST.Attribute | ESTreeJSX.JSXAttribute) {\n\t\t\tconst range = node.range;\n\t\t\tif (!range) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst key = `${range[0]}:${range[1]}`;\n\t\t\tif (reported_ranges.has(key)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treported_ranges.add(key);\n\n\t\t\tcontext.report({\n\t\t\t\tnode,\n\t\t\t\tmessageId: 'preferOnInput',\n\t\t\t\tfix(fixer) {\n\t\t\t\t\treturn fixer.replaceText(node.name, 'onInput');\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\t// Check JSX attributes (standard JSX)\n\t\t\t'JSXAttribute[name.name=\"onChange\"]'(node: ESTreeJSX.JSXAttribute) {\n\t\t\t\treport_onchange(node);\n\t\t\t},\n\t\t\t// Check Attribute nodes (Ripple parser)\n\t\t\t'Attribute[name.name=\"onChange\"]'(node: AST.Attribute) {\n\t\t\t\treport_onchange(node);\n\t\t\t},\n\t\t\t// Check object properties (for spread props)\n\t\t\t'Property[key.name=\"onChange\"]'(node: AST.Property) {\n\t\t\t\t// Only report if this looks like it's in a props object\n\t\t\t\tconst ancestors = context.sourceCode.getAncestors(node);\n\t\t\t\tconst inObjectExpression = ancestors.some(\n\t\t\t\t\t(ancestor) => ancestor.type === 'ObjectExpression',\n\t\t\t\t);\n\n\t\t\t\tif (inObjectExpression) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'preferOnInput',\n\t\t\t\t\t\tfix(fixer) {\n\t\t\t\t\t\t\treturn fixer.replaceText(node.key, 'onInput');\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow return statements with JSX in Ripple components',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tnoReturn: 'Do not return JSX from Ripple components. Use JSX as statements instead.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet insideComponent = 0;\n\n\t\treturn {\n\t\t\t// Track component boundaries\n\t\t\t\"ExpressionStatement > CallExpression[callee.name='component']\"() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t\"ExpressionStatement > CallExpression[callee.name='component']:exit\"() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\t\t\t// Also track arrow functions and regular functions that might be components\n\t\t\t'VariableDeclarator[init.callee.name=\"component\"]'() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t'VariableDeclarator[init.callee.name=\"component\"]:exit'() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\t\t\t// Check return statements\n\t\t\tReturnStatement(node: AST.ReturnStatement) {\n\t\t\t\tif (insideComponent > 0 && node.argument) {\n\t\t\t\t\t// Check if returning JSX (JSXElement, JSXFragment)\n\t\t\t\t\tif (node.argument.type === 'JSXElement' || node.argument.type === 'JSXFragment') {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'noReturn',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure tracked values are unboxed with @ operator',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tneedsUnbox: 'Tracked value should be unboxed with @ operator. Did you mean \"@{{name}}\"?',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst trackedVariables = new Set<string>();\n\n\t\tfunction isInJSXContext(node: AST.Node & { parent: AST.Node }): boolean {\n\t\t\tlet parent = node.parent;\n\n\t\t\t// Walk up the AST to find if we're inside JSX/Element\n\t\t\twhile (parent) {\n\t\t\t\tconst parentType = parent.type;\n\t\t\t\t// Check for JSX context\n\t\t\t\tif (\n\t\t\t\t\tparentType === 'JSXExpressionContainer' ||\n\t\t\t\t\tparentType === 'JSXElement' ||\n\t\t\t\t\tparentType === 'JSXFragment' ||\n\t\t\t\t\t// Check for Ripple Element context\n\t\t\t\t\tparentType === 'Element'\n\t\t\t\t) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\tparent = (parent as AST.Node & { parent: AST.Node }).parent;\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction checkTrackedIdentifier(node: AST.Identifier & { parent: AST.Node }) {\n\t\t\tif (trackedVariables.has(node.name) && isInJSXContext(node)) {\n\t\t\t\tconst parent = node.parent;\n\t\t\t\tlet isUnboxed = (parent && parent.type === 'TrackedExpression') || node.tracked === true;\n\n\t\t\t\t// Fallback: check source code for @ character as the first character\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tconst firstChar = context.sourceCode.text.substring(\n\t\t\t\t\t\tMath.max(0, node.range![0] - 1),\n\t\t\t\t\t\tnode.range![0],\n\t\t\t\t\t);\n\t\t\t\t\tisUnboxed = firstChar === '@';\n\t\t\t\t}\n\n\t\t\t\tif (!isUnboxed) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'needsUnbox',\n\t\t\t\t\t\tdata: { name: node.name },\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t// Track variables that are assigned from track()\n\t\t\t'VariableDeclarator[init.callee.name=\"track\"]'(node: AST.VariableDeclarator) {\n\t\t\t\tif (node.id.type === 'Identifier') {\n\t\t\t\t\ttrackedVariables.add(node.id.name);\n\t\t\t\t}\n\t\t\t},\n\t\t\t// Check all identifiers\n\t\t\tIdentifier(node: AST.Identifier & { parent: AST.Node }) {\n\t\t\t\tcheckTrackedIdentifier(node);\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription:\n\t\t\t\t'Require JSX in for...of loops within components, but disallow JSX in for...of loops within effects',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\trequireJsxInLoop:\n\t\t\t\t'For...of loops in component bodies should contain JSX elements. Use JSX to render items.',\n\t\t\tnoJsxInEffectLoop:\n\t\t\t\t'For...of loops inside effect() should not contain JSX. Effects are for side effects, not rendering.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet insideComponent = 0;\n\t\tlet insideEffect = 0;\n\n\t\tfunction containsJSX(node: AST.Node, visited: Set<AST.Node> = new Set()): boolean {\n\t\t\tif (!node) return false;\n\n\t\t\t// Avoid infinite loops from circular references\n\t\t\tif (visited.has(node)) return false;\n\t\t\tvisited.add(node);\n\n\t\t\t// Check if current node is JSX/Element (Ripple uses 'Element' type instead of 'JSXElement')\n\t\t\tif (\n\t\t\t\tnode.type === ('JSXElement' as string) ||\n\t\t\t\tnode.type === ('JSXFragment' as string) ||\n\t\t\t\tnode.type === ('Element' as string)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst keys = Object.keys(node);\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (key === 'parent' || key === 'loc' || key === 'range') {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst value = (node as any)[key];\n\t\t\t\tif (value && typeof value === 'object') {\n\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\tfor (const item of value) {\n\t\t\t\t\t\t\tif (item && typeof item === 'object' && containsJSX(item, visited)) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value.type && containsJSX(value, visited)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn {\n\t\t\tComponent() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t'Component:exit'() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\n\t\t\t\"CallExpression[callee.name='effect']\"() {\n\t\t\t\tinsideEffect++;\n\t\t\t},\n\t\t\t\"CallExpression[callee.name='effect']:exit\"() {\n\t\t\t\tinsideEffect--;\n\t\t\t},\n\n\t\t\tForOfStatement(node: AST.ForOfStatement) {\n\t\t\t\tif (insideComponent === 0) return;\n\n\t\t\t\tconst hasJSX = containsJSX(node.body);\n\n\t\t\t\tif (insideEffect > 0) {\n\t\t\t\t\tif (hasJSX) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'noJsxInEffectLoop',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (!hasJSX) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'requireJsxInLoop',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow @ introspection operator in TypeScript/JavaScript modules',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tnoIntrospect:\n\t\t\t\t'The @ operator cannot be used in TypeScript/JavaScript modules. Use get() to read tracked values and set() to update them instead.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst filename = context.filename;\n\n\t\t// Skip .ripple files where @ operator is valid\n\t\tif (filename && filename.endsWith('.ripple')) {\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\t// Check for identifiers with the 'tracked' property\n\t\t\t// The @ operator is parsed by Ripple as an identifier with tracked=true\n\t\t\tIdentifier(node: AST.Identifier) {\n\t\t\t\tif (node.tracked === true) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'noIntrospect',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\nimport type { Scope } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure variables used in for..of key expression are defined',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tundefinedVariable: \"Variable '{{name}}' is not defined.\",\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\treturn {\n\t\t\tForOfStatement(node: AST.ForOfStatement) {\n\t\t\t\tif (!node.key) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst checkIdentifier = (identifier: AST.Identifier) => {\n\t\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\t\tif (!variable) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst traverse = (node: AST.Node) => {\n\t\t\t\t\tif (!node) return;\n\n\t\t\t\t\tswitch (node.type) {\n\t\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t\t(node.elements as (AST.Expression | AST.SpreadElement)[]).forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\t\tnode.properties.forEach((prop: AST.Property | AST.SpreadElement) => {\n\t\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttraverse(node.key);\n\t\t\t},\n\t\t};\n\t},\n};\n\nfunction findVariable(scope: Scope.Scope, name: string) {\n\tlet currentScope: Scope.Scope | null = scope;\n\n\twhile (currentScope) {\n\t\tconst variable = currentScope.variables.find((v: { name: string }) => v.name === name);\n\n\t\tif (variable) {\n\t\t\treturn variable;\n\t\t}\n\n\t\t// Also check references for global variables or variables defined in the loop itself (like the loop variable)\n\t\t// The loop variable might not be in the 'variables' list of the surrounding scope yet if we are inside the loop statement\n\t\t// But for 'for-of', the loop variable is in a special scope or the upper scope.\n\t\t// Let's rely on standard scope analysis.\n\n\t\t// Special case: check if the variable is the loop variable itself (left side of for-of)\n\t\t// The scope analysis might handle this, but let's be sure.\n\n\t\tcurrentScope = currentScope.upper;\n\t}\n\n\t// If not found in scopes, it might be a global variable.\n\t// ESLint scope analysis usually includes globals in the global scope.\n\t// However, if we are in a module, we might need to check if it's an implicit global or defined elsewhere.\n\t// For now, let's assume standard scope resolution works.\n\n\treturn null;\n}\n\nexport default rule;\n","import { createRequire } from 'module';\nimport noModuleScopeTrack from './rules/no-module-scope-track.js';\nimport preferOnInput from './rules/prefer-oninput.js';\nimport noReturnInComponent from './rules/no-return-in-component.js';\nimport unboxTrackedValues from './rules/unbox-tracked-values.js';\nimport controlFlowJsx from './rules/control-flow-jsx.js';\nimport noIntrospectInModules from './rules/no-introspect-in-modules.js';\nimport validForOfKey from './rules/valid-for-of-key.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@ripple-ts/eslint-plugin',\n\t\tversion: '0.1.3',\n\t},\n\trules: {\n\t\t'no-module-scope-track': noModuleScopeTrack,\n\t\t'prefer-oninput': preferOnInput,\n\t\t'no-return-in-component': noReturnInComponent,\n\t\t'unbox-tracked-values': unboxTrackedValues,\n\t\t'control-flow-jsx': controlFlowJsx,\n\t\t'no-introspect-in-modules': noIntrospectInModules,\n\t\t'valid-for-of-key': validForOfKey,\n\t},\n\tconfigs: {} as any,\n};\n\n// Try to load optional parsers\nconst require = createRequire(import.meta.url);\n\nlet rippleParser: any;\nlet tsParser: any;\n\ntry {\n\trippleParser = require('@ripple-ts/eslint-parser');\n} catch {\n\t// @ripple-ts/eslint-parser is optional\n\trippleParser = null;\n}\n\ntry {\n\ttsParser = require('@typescript-eslint/parser');\n} catch {\n\t// @typescript-eslint/parser is optional\n\ttsParser = null;\n}\n\n// Helper to create config objects\nfunction createConfig(name: string, files: string[], parser: any) {\n\tconst config: any = {\n\t\tname,\n\t\tfiles,\n\t\tplugins: {\n\t\t\tripple: plugin,\n\t\t},\n\t\trules: {\n\t\t\t'ripple/no-module-scope-track': 'error',\n\t\t\t'ripple/prefer-oninput': 'warn',\n\t\t\t'ripple/no-return-in-component': 'error',\n\t\t\t'ripple/unbox-tracked-values': 'error',\n\t\t\t'ripple/control-flow-jsx': 'error',\n\t\t\t'ripple/no-introspect-in-modules': 'error',\n\t\t\t'ripple/valid-for-of-key': 'error',\n\t\t},\n\t};\n\n\t// Only add parser if it's available\n\tif (parser) {\n\t\tconfig.languageOptions = {\n\t\t\tparser,\n\t\t\tparserOptions: {\n\t\t\t\tecmaVersion: 'latest',\n\t\t\t\tsourceType: 'module',\n\t\t\t},\n\t\t};\n\t}\n\n\treturn config;\n}\n\n// Recommended configuration (flat config format)\nplugin.configs.recommended = [\n\tcreateConfig('ripple/recommended-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\n// Strict configuration (flat config format)\nplugin.configs.strict = [\n\tcreateConfig('ripple/strict-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\nexport default plugin;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["rule","rule","rule","rule","rule","noModuleScopeTrack","preferOnInput","noReturnInComponent","controlFlowJsx","noLazyDestructuringInModules","validForOfKey"],"sources":["../src/rules/no-module-scope-track.ts","../src/rules/prefer-oninput.ts","../src/rules/no-return-in-component.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-lazy-destructuring-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow calling track() at module scope',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tmoduleScope:\n\t\t\t\t'track() cannot be called at module scope. It must be called within a component context.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet componentDepth = 0;\n\t\tlet functionDepth = 0;\n\n\t\tconst incrementComponentDepth = () => componentDepth++;\n\t\tconst decrementComponentDepth = () => componentDepth--;\n\t\tconst incrementFunctionDepth = () => functionDepth++;\n\t\tconst decrementFunctionDepth = () => functionDepth--;\n\n\t\treturn {\n\t\t\t// Only track when we enter a Ripple component\n\t\t\t// Ripple's parser returns \"Component\" nodes for component declarations\n\t\t\tComponent: incrementComponentDepth,\n\t\t\t'Component:exit': decrementComponentDepth,\n\n\t\t\t// Track regular functions and arrow functions\n\t\t\tFunctionDeclaration: incrementFunctionDepth,\n\t\t\t'FunctionDeclaration:exit': decrementFunctionDepth,\n\t\t\tFunctionExpression: incrementFunctionDepth,\n\t\t\t'FunctionExpression:exit': decrementFunctionDepth,\n\t\t\tArrowFunctionExpression: incrementFunctionDepth,\n\t\t\t'ArrowFunctionExpression:exit': decrementFunctionDepth,\n\n\t\t\t// Check track() calls\n\t\t\tCallExpression(node: AST.CallExpression) {\n\t\t\t\tif (\n\t\t\t\t\tnode.callee.type === 'Identifier' &&\n\t\t\t\t\tnode.callee.name === 'track' &&\n\t\t\t\t\tcomponentDepth === 0 &&\n\t\t\t\t\tfunctionDepth === 0\n\t\t\t\t) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'moduleScope',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\nimport type * as ESTreeJSX from 'ripple/types/estree-jsx';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'suggestion',\n\t\tdocs: {\n\t\t\tdescription: 'Prefer onInput over onChange for form inputs in Ripple',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tpreferOnInput:\n\t\t\t\t'Use \"onInput\" instead of \"onChange\". Ripple does not have synthetic events like React.',\n\t\t},\n\t\tfixable: 'code',\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst reported_ranges = new Set<string>();\n\n\t\tfunction report_onchange(node: AST.Attribute | ESTreeJSX.JSXAttribute) {\n\t\t\tconst range = node.range;\n\t\t\tif (!range) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst key = `${range[0]}:${range[1]}`;\n\t\t\tif (reported_ranges.has(key)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\treported_ranges.add(key);\n\n\t\t\tcontext.report({\n\t\t\t\tnode,\n\t\t\t\tmessageId: 'preferOnInput',\n\t\t\t\tfix(fixer) {\n\t\t\t\t\treturn fixer.replaceText(node.name, 'onInput');\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\t// Check JSX attributes (standard JSX)\n\t\t\t'JSXAttribute[name.name=\"onChange\"]'(node: ESTreeJSX.JSXAttribute) {\n\t\t\t\treport_onchange(node);\n\t\t\t},\n\t\t\t// Check Attribute nodes (Ripple parser)\n\t\t\t'Attribute[name.name=\"onChange\"]'(node: AST.Attribute) {\n\t\t\t\treport_onchange(node);\n\t\t\t},\n\t\t\t// Check object properties (for spread props)\n\t\t\t'Property[key.name=\"onChange\"]'(node: AST.Property) {\n\t\t\t\t// Only report if this looks like it's in a props object\n\t\t\t\tconst ancestors = context.sourceCode.getAncestors(node);\n\t\t\t\tconst inObjectExpression = ancestors.some(\n\t\t\t\t\t(ancestor) => ancestor.type === 'ObjectExpression',\n\t\t\t\t);\n\n\t\t\t\tif (inObjectExpression) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'preferOnInput',\n\t\t\t\t\t\tfix(fixer) {\n\t\t\t\t\t\t\treturn fixer.replaceText(node.key, 'onInput');\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow return statements with JSX in Ripple components',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tnoReturn: 'Do not return JSX from Ripple components. Use JSX as statements instead.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet insideComponent = 0;\n\n\t\treturn {\n\t\t\t// Track component boundaries\n\t\t\t\"ExpressionStatement > CallExpression[callee.name='component']\"() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t\"ExpressionStatement > CallExpression[callee.name='component']:exit\"() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\t\t\t// Also track arrow functions and regular functions that might be components\n\t\t\t'VariableDeclarator[init.callee.name=\"component\"]'() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t'VariableDeclarator[init.callee.name=\"component\"]:exit'() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\t\t\t// Check return statements\n\t\t\tReturnStatement(node: AST.ReturnStatement) {\n\t\t\t\tif (insideComponent > 0 && node.argument) {\n\t\t\t\t\t// Check if returning JSX (JSXElement, JSXFragment)\n\t\t\t\t\tif (node.argument.type === 'JSXElement' || node.argument.type === 'JSXFragment') {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'noReturn',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription:\n\t\t\t\t'Require JSX in for...of loops within components, but disallow JSX in for...of loops within effects',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\trequireJsxInLoop:\n\t\t\t\t'For...of loops in component bodies should contain JSX elements. Use JSX to render items.',\n\t\t\tnoJsxInEffectLoop:\n\t\t\t\t'For...of loops inside effect() should not contain JSX. Effects are for side effects, not rendering.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet insideComponent = 0;\n\t\tlet insideEffect = 0;\n\n\t\tfunction containsJSX(node: AST.Node, visited: Set<AST.Node> = new Set()): boolean {\n\t\t\tif (!node) return false;\n\n\t\t\t// Avoid infinite loops from circular references\n\t\t\tif (visited.has(node)) return false;\n\t\t\tvisited.add(node);\n\n\t\t\t// Check if current node is JSX/Element (Ripple uses 'Element' type instead of 'JSXElement')\n\t\t\tif (\n\t\t\t\tnode.type === ('JSXElement' as string) ||\n\t\t\t\tnode.type === ('JSXFragment' as string) ||\n\t\t\t\tnode.type === ('Element' as string)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tconst keys = Object.keys(node);\n\t\t\tfor (const key of keys) {\n\t\t\t\tif (key === 'parent' || key === 'loc' || key === 'range') {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tconst value = (node as any)[key];\n\t\t\t\tif (value && typeof value === 'object') {\n\t\t\t\t\tif (Array.isArray(value)) {\n\t\t\t\t\t\tfor (const item of value) {\n\t\t\t\t\t\t\tif (item && typeof item === 'object' && containsJSX(item, visited)) {\n\t\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (value.type && containsJSX(value, visited)) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn false;\n\t\t}\n\n\t\treturn {\n\t\t\tComponent() {\n\t\t\t\tinsideComponent++;\n\t\t\t},\n\t\t\t'Component:exit'() {\n\t\t\t\tinsideComponent--;\n\t\t\t},\n\n\t\t\t\"CallExpression[callee.name='effect']\"() {\n\t\t\t\tinsideEffect++;\n\t\t\t},\n\t\t\t\"CallExpression[callee.name='effect']:exit\"() {\n\t\t\t\tinsideEffect--;\n\t\t\t},\n\n\t\t\tForOfStatement(node: AST.ForOfStatement) {\n\t\t\t\tif (insideComponent === 0) return;\n\n\t\t\t\tconst hasJSX = containsJSX(node.body);\n\n\t\t\t\tif (insideEffect > 0) {\n\t\t\t\t\tif (hasJSX) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'noJsxInEffectLoop',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tif (!hasJSX) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode,\n\t\t\t\t\t\t\tmessageId: 'requireJsxInLoop',\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Disallow lazy destructuring (&[] / &{}) in TypeScript/JavaScript modules',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tnoLazyDestructuring:\n\t\t\t\t'Lazy destructuring (&[] / &{}) cannot be used in TypeScript/JavaScript modules. Use .value to read and write tracked values instead.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst filename = context.filename;\n\n\t\t// Skip .ripple files where lazy destructuring is valid\n\t\tif (filename && filename.endsWith('.ripple')) {\n\t\t\treturn {};\n\t\t}\n\n\t\treturn {\n\t\t\tArrayPattern(node: any) {\n\t\t\t\tif (node.lazy === true) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'noLazyDestructuring',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t\tObjectPattern(node: any) {\n\t\t\t\tif (node.lazy === true) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'noLazyDestructuring',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\t\t};\n\t},\n};\n\nexport default rule;\n","import type { Rule } from 'eslint';\nimport type * as AST from 'ripple/types/estree';\nimport type { Scope } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Ensure variables used in for..of key expression are defined',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\tundefinedVariable: \"Variable '{{name}}' is not defined.\",\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\treturn {\n\t\t\tForOfStatement(node: AST.ForOfStatement) {\n\t\t\t\tif (!node.key) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst checkIdentifier = (identifier: AST.Identifier) => {\n\t\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\t\tif (!variable) {\n\t\t\t\t\t\tcontext.report({\n\t\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\tconst traverse = (node: AST.Node) => {\n\t\t\t\t\tif (!node) return;\n\n\t\t\t\t\tswitch (node.type) {\n\t\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t\t(node.elements as (AST.Expression | AST.SpreadElement)[]).forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\t\tnode.properties.forEach((prop: AST.Property | AST.SpreadElement) => {\n\t\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\t\tbreak;\n\t\t\t\t\t}\n\t\t\t\t};\n\n\t\t\t\ttraverse(node.key);\n\t\t\t},\n\t\t};\n\t},\n};\n\nfunction findVariable(scope: Scope.Scope, name: string) {\n\tlet currentScope: Scope.Scope | null = scope;\n\n\twhile (currentScope) {\n\t\tconst variable = currentScope.variables.find((v: { name: string }) => v.name === name);\n\n\t\tif (variable) {\n\t\t\treturn variable;\n\t\t}\n\n\t\t// Also check references for global variables or variables defined in the loop itself (like the loop variable)\n\t\t// The loop variable might not be in the 'variables' list of the surrounding scope yet if we are inside the loop statement\n\t\t// But for 'for-of', the loop variable is in a special scope or the upper scope.\n\t\t// Let's rely on standard scope analysis.\n\n\t\t// Special case: check if the variable is the loop variable itself (left side of for-of)\n\t\t// The scope analysis might handle this, but let's be sure.\n\n\t\tcurrentScope = currentScope.upper;\n\t}\n\n\t// If not found in scopes, it might be a global variable.\n\t// ESLint scope analysis usually includes globals in the global scope.\n\t// However, if we are in a module, we might need to check if it's an implicit global or defined elsewhere.\n\t// For now, let's assume standard scope resolution works.\n\n\treturn null;\n}\n\nexport default rule;\n","import { createRequire } from 'module';\nimport noModuleScopeTrack from './rules/no-module-scope-track.js';\nimport preferOnInput from './rules/prefer-oninput.js';\nimport noReturnInComponent from './rules/no-return-in-component.js';\nimport controlFlowJsx from './rules/control-flow-jsx.js';\nimport noLazyDestructuringInModules from './rules/no-lazy-destructuring-in-modules.js';\nimport validForOfKey from './rules/valid-for-of-key.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@ripple-ts/eslint-plugin',\n\t\tversion: '0.1.3',\n\t},\n\trules: {\n\t\t'no-module-scope-track': noModuleScopeTrack,\n\t\t'prefer-oninput': preferOnInput,\n\t\t'no-return-in-component': noReturnInComponent,\n\t\t'control-flow-jsx': controlFlowJsx,\n\t\t'no-lazy-destructuring-in-modules': noLazyDestructuringInModules,\n\t\t'valid-for-of-key': validForOfKey,\n\t},\n\tconfigs: {} as any,\n};\n\n// Try to load optional parsers\nconst require = createRequire(import.meta.url);\n\nlet rippleParser: any;\nlet tsParser: any;\n\ntry {\n\trippleParser = require('@ripple-ts/eslint-parser');\n} catch {\n\t// @ripple-ts/eslint-parser is optional\n\trippleParser = null;\n}\n\ntry {\n\ttsParser = require('@typescript-eslint/parser');\n} catch {\n\t// @typescript-eslint/parser is optional\n\ttsParser = null;\n}\n\n// Helper to create config objects\nfunction createConfig(name: string, files: string[], parser: any) {\n\tconst config: any = {\n\t\tname,\n\t\tfiles,\n\t\tplugins: {\n\t\t\tripple: plugin,\n\t\t},\n\t\trules: {\n\t\t\t'ripple/no-module-scope-track': 'error',\n\t\t\t'ripple/prefer-oninput': 'warn',\n\t\t\t'ripple/no-return-in-component': 'error',\n\t\t\t'ripple/control-flow-jsx': 'error',\n\t\t\t'ripple/no-lazy-destructuring-in-modules': 'error',\n\t\t\t'ripple/valid-for-of-key': 'error',\n\t\t},\n\t};\n\n\t// Only add parser if it's available\n\tif (parser) {\n\t\tconfig.languageOptions = {\n\t\t\tparser,\n\t\t\tparserOptions: {\n\t\t\t\tecmaVersion: 'latest',\n\t\t\t\tsourceType: 'module',\n\t\t\t},\n\t\t};\n\t}\n\n\treturn config;\n}\n\n// Recommended configuration (flat config format)\nplugin.configs.recommended = [\n\tcreateConfig('ripple/recommended-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\n// Strict configuration (flat config format)\nplugin.configs.strict = [\n\tcreateConfig('ripple/strict-ripple-files', ['**/*.ripple'], rippleParser),\n\tcreateConfig('ripple/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser),\n\t{\n\t\tname: 'ripple/ignores',\n\t\tignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],\n\t},\n];\n\nexport default plugin;\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ripple-ts/eslint-plugin",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.9",
|
|
4
4
|
"description": "ESLint plugin for Ripple",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"eslint": ">=9.0.0",
|
|
35
35
|
"@typescript-eslint/parser": "^8.56.1",
|
|
36
|
-
"@ripple-ts/eslint-parser": "0.3.
|
|
36
|
+
"@ripple-ts/eslint-parser": "0.3.9"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/eslint": "^9.6.1",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"tsdown": "^0.20.3",
|
|
44
44
|
"typescript": "^5.9.3",
|
|
45
45
|
"vitest": "^4.0.18",
|
|
46
|
-
"
|
|
47
|
-
"ripple": "0.3.
|
|
46
|
+
"ripple": "0.3.9",
|
|
47
|
+
"@ripple-ts/eslint-parser": "0.3.9"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=20.0.0"
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,8 @@ import { createRequire } from 'module';
|
|
|
2
2
|
import noModuleScopeTrack from './rules/no-module-scope-track.js';
|
|
3
3
|
import preferOnInput from './rules/prefer-oninput.js';
|
|
4
4
|
import noReturnInComponent from './rules/no-return-in-component.js';
|
|
5
|
-
import unboxTrackedValues from './rules/unbox-tracked-values.js';
|
|
6
5
|
import controlFlowJsx from './rules/control-flow-jsx.js';
|
|
7
|
-
import
|
|
6
|
+
import noLazyDestructuringInModules from './rules/no-lazy-destructuring-in-modules.js';
|
|
8
7
|
import validForOfKey from './rules/valid-for-of-key.js';
|
|
9
8
|
|
|
10
9
|
const plugin = {
|
|
@@ -16,9 +15,8 @@ const plugin = {
|
|
|
16
15
|
'no-module-scope-track': noModuleScopeTrack,
|
|
17
16
|
'prefer-oninput': preferOnInput,
|
|
18
17
|
'no-return-in-component': noReturnInComponent,
|
|
19
|
-
'unbox-tracked-values': unboxTrackedValues,
|
|
20
18
|
'control-flow-jsx': controlFlowJsx,
|
|
21
|
-
'no-
|
|
19
|
+
'no-lazy-destructuring-in-modules': noLazyDestructuringInModules,
|
|
22
20
|
'valid-for-of-key': validForOfKey,
|
|
23
21
|
},
|
|
24
22
|
configs: {} as any,
|
|
@@ -56,9 +54,8 @@ function createConfig(name: string, files: string[], parser: any) {
|
|
|
56
54
|
'ripple/no-module-scope-track': 'error',
|
|
57
55
|
'ripple/prefer-oninput': 'warn',
|
|
58
56
|
'ripple/no-return-in-component': 'error',
|
|
59
|
-
'ripple/unbox-tracked-values': 'error',
|
|
60
57
|
'ripple/control-flow-jsx': 'error',
|
|
61
|
-
'ripple/no-
|
|
58
|
+
'ripple/no-lazy-destructuring-in-modules': 'error',
|
|
62
59
|
'ripple/valid-for-of-key': 'error',
|
|
63
60
|
},
|
|
64
61
|
};
|