@tsrx/eslint-plugin 0.3.124 → 0.3.127
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 +24 -195
- package/dist/index.d.ts +0 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +17 -115
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/index.ts +14 -20
package/README.md
CHANGED
|
@@ -3,222 +3,51 @@
|
|
|
3
3
|
[](https://www.npmjs.com/package/@tsrx/eslint-plugin)
|
|
4
4
|
[](https://www.npmjs.com/package/@tsrx/eslint-plugin)
|
|
5
5
|
|
|
6
|
-
ESLint
|
|
7
|
-
practices and catch common mistakes when writing Ripple applications.
|
|
8
|
-
|
|
9
|
-
Works just like `eslint-plugin-react` - simply install and use the recommended
|
|
10
|
-
config!
|
|
6
|
+
ESLint rules and flat configurations for target-neutral TSRX syntax.
|
|
11
7
|
|
|
12
8
|
## Installation
|
|
13
9
|
|
|
14
10
|
```bash
|
|
15
|
-
|
|
16
|
-
# or
|
|
17
|
-
yarn add --dev '@tsrx/eslint-plugin'
|
|
18
|
-
# or
|
|
19
|
-
pnpm add --save-dev '@tsrx/eslint-plugin'
|
|
11
|
+
pnpm add --save-dev eslint @tsrx/eslint-parser @tsrx/eslint-plugin
|
|
20
12
|
```
|
|
21
13
|
|
|
22
14
|
## Usage
|
|
23
15
|
|
|
24
|
-
### Flat Config (ESLint 9+)
|
|
25
|
-
|
|
26
16
|
```js
|
|
27
17
|
// eslint.config.js
|
|
28
|
-
import
|
|
29
|
-
|
|
30
|
-
export default [...ripple.configs.recommended];
|
|
31
|
-
```
|
|
18
|
+
import tsrx from '@tsrx/eslint-plugin';
|
|
32
19
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
- Detects and uses `@tsrx/eslint-parser` if installed for `.tsrx` files
|
|
36
|
-
- Detects and uses `@typescript-eslint/parser` if installed for `.ts`/`.tsx` files
|
|
37
|
-
- Excludes `.d.ts` files, `node_modules`, `dist`, and `build` directories from
|
|
38
|
-
linting
|
|
39
|
-
- Works with `.ts`, `.tsx`, and `.tsrx` files
|
|
40
|
-
|
|
41
|
-
### Legacy Config (.eslintrc)
|
|
42
|
-
|
|
43
|
-
```json
|
|
44
|
-
{
|
|
45
|
-
"plugins": ["ripple"],
|
|
46
|
-
"extends": ["plugin:ripple/recommended"]
|
|
47
|
-
}
|
|
20
|
+
export default [...tsrx.configs.recommended];
|
|
48
21
|
```
|
|
49
22
|
|
|
50
|
-
|
|
23
|
+
The recommended configuration:
|
|
51
24
|
|
|
52
|
-
|
|
25
|
+
- uses `@tsrx/eslint-parser` for `.tsrx` files when installed;
|
|
26
|
+
- uses `@typescript-eslint/parser` for `.ts` and `.tsx` files when installed;
|
|
27
|
+
- registers the plugin under the `tsrx` rule namespace;
|
|
28
|
+
- ignores declaration files, dependencies, and common build output folders.
|
|
53
29
|
|
|
54
|
-
|
|
55
|
-
(errors and warnings).
|
|
30
|
+
Use the strict configuration to enable the same syntax rules at error severity:
|
|
56
31
|
|
|
57
32
|
```js
|
|
58
|
-
import
|
|
59
|
-
|
|
60
|
-
export default [
|
|
61
|
-
{
|
|
62
|
-
plugins: { ripple },
|
|
63
|
-
rules: ripple.configs.recommended.rules,
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
```
|
|
33
|
+
import tsrx from '@tsrx/eslint-plugin';
|
|
67
34
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
The strict configuration enables all rules as errors.
|
|
71
|
-
|
|
72
|
-
```js
|
|
73
|
-
import ripple from '@tsrx/eslint-plugin';
|
|
74
|
-
|
|
75
|
-
export default [
|
|
76
|
-
{
|
|
77
|
-
plugins: { ripple },
|
|
78
|
-
rules: ripple.configs.strict.rules,
|
|
79
|
-
},
|
|
80
|
-
];
|
|
35
|
+
export default [...tsrx.configs.strict];
|
|
81
36
|
```
|
|
82
37
|
|
|
83
38
|
## Rules
|
|
84
39
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
```js
|
|
93
|
-
import { track } from 'ripple';
|
|
94
|
-
|
|
95
|
-
// This will cause runtime errors
|
|
96
|
-
let globalCount = track(0);
|
|
97
|
-
|
|
98
|
-
export function App() {
|
|
99
|
-
return <div>{globalCount}</div>;
|
|
100
|
-
}
|
|
101
|
-
```
|
|
102
|
-
|
|
103
|
-
✅ **Correct:**
|
|
104
|
-
|
|
105
|
-
```js
|
|
106
|
-
import { track } from 'ripple';
|
|
107
|
-
|
|
108
|
-
export function App() {
|
|
109
|
-
let count = track(0);
|
|
110
|
-
|
|
111
|
-
return <div>{count}</div>;
|
|
112
|
-
}
|
|
113
|
-
```
|
|
114
|
-
|
|
115
|
-
### `ripple/prefer-oninput` (warning, fixable)
|
|
116
|
-
|
|
117
|
-
Recommends using `onInput` instead of `onChange` for form inputs. Unlike React,
|
|
118
|
-
Ripple doesn't have synthetic events, so `onInput` is the correct event handler.
|
|
119
|
-
|
|
120
|
-
❌ **Incorrect:**
|
|
121
|
-
|
|
122
|
-
```jsx
|
|
123
|
-
<input onChange={handleChange} />
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
✅ **Correct:**
|
|
127
|
-
|
|
128
|
-
```jsx
|
|
129
|
-
<input onInput={handleInput} />
|
|
130
|
-
```
|
|
131
|
-
|
|
132
|
-
This rule is auto-fixable with `--fix`.
|
|
133
|
-
|
|
134
|
-
### `ripple/control-flow-jsx` (error)
|
|
135
|
-
|
|
136
|
-
Checks template control flow inside functions that return native TSRX. `@for`
|
|
137
|
-
blocks should render template output, while ordinary `for...of` loops inside
|
|
138
|
-
`effect()` callbacks should not render JSX.
|
|
139
|
-
|
|
140
|
-
### `ripple/no-lazy-destructuring-in-modules` (error)
|
|
141
|
-
|
|
142
|
-
Prevents using lazy destructuring (`&[]` / `&{}`) in TypeScript/JavaScript
|
|
143
|
-
modules. In `.ts`/`.js` files, you should use `.value` to read and write tracked
|
|
144
|
-
values instead.
|
|
145
|
-
|
|
146
|
-
❌ **Incorrect:**
|
|
147
|
-
|
|
148
|
-
```ts
|
|
149
|
-
// count.ts
|
|
150
|
-
import { track, effect } from 'ripple';
|
|
151
|
-
|
|
152
|
-
export function useCount() {
|
|
153
|
-
const &[count] = track(1);
|
|
154
|
-
effect(() => {
|
|
155
|
-
console.log(count); // Error: Cannot use &[] in TypeScript modules
|
|
156
|
-
});
|
|
157
|
-
return { count };
|
|
158
|
-
}
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
✅ **Correct:**
|
|
162
|
-
|
|
163
|
-
```ts
|
|
164
|
-
// count.ts
|
|
165
|
-
import { track, effect } from 'ripple';
|
|
166
|
-
|
|
167
|
-
export function useCount() {
|
|
168
|
-
const count = track(1);
|
|
169
|
-
|
|
170
|
-
// Use .value to read tracked values
|
|
171
|
-
const double = track(() => count.value * 2);
|
|
172
|
-
|
|
173
|
-
effect(() => {
|
|
174
|
-
console.log('count is', count.value);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
return { count, double };
|
|
178
|
-
}
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
**Note:** Lazy destructuring (`&[]` / `&{}`) is only valid in TSRX files. In
|
|
182
|
-
TypeScript modules, use `.value` to read and write tracked values.
|
|
183
|
-
|
|
184
|
-
### `ripple/no-return-in-component` (deprecated)
|
|
185
|
-
|
|
186
|
-
This compatibility rule is now a no-op. TSRX components are ordinary functions, so
|
|
187
|
-
returning native TSRX is the expected authoring style.
|
|
188
|
-
|
|
189
|
-
## Custom Configuration
|
|
190
|
-
|
|
191
|
-
You can customize individual rules in your ESLint config:
|
|
192
|
-
|
|
193
|
-
```js
|
|
194
|
-
export default [
|
|
195
|
-
{
|
|
196
|
-
plugins: { ripple },
|
|
197
|
-
rules: {
|
|
198
|
-
'ripple/no-module-scope-track': 'error',
|
|
199
|
-
'ripple/prefer-oninput': 'error', // Make this an error instead of warning
|
|
200
|
-
'ripple/control-flow-jsx': 'error',
|
|
201
|
-
'ripple/no-lazy-destructuring-in-modules': 'error',
|
|
202
|
-
'ripple/valid-for-of-key': 'error',
|
|
203
|
-
},
|
|
204
|
-
},
|
|
205
|
-
];
|
|
206
|
-
```
|
|
207
|
-
|
|
208
|
-
The plugin will automatically detect and use the Ripple parser for `.tsrx` files.
|
|
209
|
-
|
|
210
|
-
## License
|
|
211
|
-
|
|
212
|
-
MIT
|
|
213
|
-
|
|
214
|
-
## Contributing
|
|
215
|
-
|
|
216
|
-
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
40
|
+
- `tsrx/control-flow-jsx` requires template output in returned `@for` blocks.
|
|
41
|
+
- `tsrx/require-statement-container-body` detects component bodies that need the
|
|
42
|
+
`@{ ... }` statement-container marker.
|
|
43
|
+
- `tsrx/valid-for-of-key` validates identifiers used in TSRX loop keys.
|
|
44
|
+
- `tsrx/no-lazy-destructuring-in-modules` prevents TSRX-only `&[]` and `&{}`
|
|
45
|
+
syntax from leaking into ordinary TypeScript or JavaScript modules.
|
|
46
|
+
- `tsrx/no-return-in-component` is a deprecated no-op retained for compatibility.
|
|
217
47
|
|
|
218
|
-
|
|
48
|
+
Target runtime rules do not belong in the shared recommended configuration. For
|
|
49
|
+
example, Ripple-specific `track()` placement and DOM event guidance should be
|
|
50
|
+
provided by Ripple-owned tooling rather than applied to every TSRX target.
|
|
219
51
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
Vite plugin for Ripple
|
|
223
|
-
- [@tsrx/prettier-plugin](https://www.npmjs.com/package/@tsrx/prettier-plugin) -
|
|
224
|
-
Prettier plugin for Ripple
|
|
52
|
+
See the [TSRX documentation](https://tsrx.dev/) and
|
|
53
|
+
[`@tsrx/eslint-parser`](../eslint-parser/README.md).
|
package/dist/index.d.ts
CHANGED
|
@@ -7,8 +7,6 @@ declare const plugin: {
|
|
|
7
7
|
version: string;
|
|
8
8
|
};
|
|
9
9
|
rules: {
|
|
10
|
-
'no-module-scope-track': _$eslint.Rule.RuleModule;
|
|
11
|
-
'prefer-oninput': _$eslint.Rule.RuleModule;
|
|
12
10
|
'no-return-in-component': _$eslint.Rule.RuleModule;
|
|
13
11
|
'control-flow-jsx': _$eslint.Rule.RuleModule;
|
|
14
12
|
'no-lazy-destructuring-in-modules': _$eslint.Rule.RuleModule;
|
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":";;;cAOM,MAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,81 +1,4 @@
|
|
|
1
1
|
import { createRequire } from "module";
|
|
2
|
-
//#region src/rules/no-module-scope-track.ts
|
|
3
|
-
const rule$6 = {
|
|
4
|
-
meta: {
|
|
5
|
-
type: "problem",
|
|
6
|
-
docs: {
|
|
7
|
-
description: "Disallow calling track() at module scope",
|
|
8
|
-
recommended: true
|
|
9
|
-
},
|
|
10
|
-
messages: { moduleScope: "track() cannot be called at module scope. Move it into a function body." },
|
|
11
|
-
schema: []
|
|
12
|
-
},
|
|
13
|
-
create(context) {
|
|
14
|
-
let functionDepth = 0;
|
|
15
|
-
const incrementFunctionDepth = () => functionDepth++;
|
|
16
|
-
const decrementFunctionDepth = () => functionDepth--;
|
|
17
|
-
return {
|
|
18
|
-
FunctionDeclaration: incrementFunctionDepth,
|
|
19
|
-
"FunctionDeclaration:exit": decrementFunctionDepth,
|
|
20
|
-
FunctionExpression: incrementFunctionDepth,
|
|
21
|
-
"FunctionExpression:exit": decrementFunctionDepth,
|
|
22
|
-
ArrowFunctionExpression: incrementFunctionDepth,
|
|
23
|
-
"ArrowFunctionExpression:exit": decrementFunctionDepth,
|
|
24
|
-
CallExpression(node) {
|
|
25
|
-
if (node.callee.type === "Identifier" && node.callee.name === "track" && functionDepth === 0) context.report({
|
|
26
|
-
node,
|
|
27
|
-
messageId: "moduleScope"
|
|
28
|
-
});
|
|
29
|
-
}
|
|
30
|
-
};
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
//#endregion
|
|
34
|
-
//#region src/rules/prefer-oninput.ts
|
|
35
|
-
const rule$5 = {
|
|
36
|
-
meta: {
|
|
37
|
-
type: "suggestion",
|
|
38
|
-
docs: {
|
|
39
|
-
description: "Prefer onInput over onChange for form inputs in Ripple",
|
|
40
|
-
recommended: true
|
|
41
|
-
},
|
|
42
|
-
messages: { preferOnInput: "Use \"onInput\" instead of \"onChange\". Ripple does not have synthetic events like React." },
|
|
43
|
-
fixable: "code",
|
|
44
|
-
schema: []
|
|
45
|
-
},
|
|
46
|
-
create(context) {
|
|
47
|
-
const reported_ranges = /* @__PURE__ */ new Set();
|
|
48
|
-
function report_onchange(node) {
|
|
49
|
-
const range = node.range;
|
|
50
|
-
if (!range) return;
|
|
51
|
-
const key = `${range[0]}:${range[1]}`;
|
|
52
|
-
if (reported_ranges.has(key)) return;
|
|
53
|
-
reported_ranges.add(key);
|
|
54
|
-
context.report({
|
|
55
|
-
node,
|
|
56
|
-
messageId: "preferOnInput",
|
|
57
|
-
fix(fixer) {
|
|
58
|
-
return fixer.replaceText(node.name, "onInput");
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
}
|
|
62
|
-
return {
|
|
63
|
-
"JSXAttribute[name.name=\"onChange\"]"(node) {
|
|
64
|
-
report_onchange(node);
|
|
65
|
-
},
|
|
66
|
-
"Property[key.name=\"onChange\"]"(node) {
|
|
67
|
-
if (context.sourceCode.getAncestors(node).some((ancestor) => ancestor.type === "ObjectExpression")) context.report({
|
|
68
|
-
node,
|
|
69
|
-
messageId: "preferOnInput",
|
|
70
|
-
fix(fixer) {
|
|
71
|
-
return fixer.replaceText(node.key, "onInput");
|
|
72
|
-
}
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
//#endregion
|
|
79
2
|
//#region src/rules/no-return-in-component.ts
|
|
80
3
|
const rule$4 = {
|
|
81
4
|
meta: {
|
|
@@ -144,18 +67,14 @@ const rule$3 = {
|
|
|
144
67
|
meta: {
|
|
145
68
|
type: "problem",
|
|
146
69
|
docs: {
|
|
147
|
-
description: "Require template output in @for blocks
|
|
70
|
+
description: "Require template output in @for blocks",
|
|
148
71
|
recommended: true
|
|
149
72
|
},
|
|
150
|
-
messages: {
|
|
151
|
-
requireJsxInLoop: "@for blocks in returned TSRX should contain template output. Render an element, fragment, or nested template directive.",
|
|
152
|
-
noJsxInEffectLoop: "For...of loops inside effect() should not contain JSX. Effects are for side effects, not rendering."
|
|
153
|
-
},
|
|
73
|
+
messages: { requireJsxInLoop: "@for blocks in returned TSRX should contain template output. Render an element, fragment, or nested template directive." },
|
|
154
74
|
schema: []
|
|
155
75
|
},
|
|
156
76
|
create(context) {
|
|
157
77
|
let insideComponent = 0;
|
|
158
|
-
let insideEffect = 0;
|
|
159
78
|
let nonComponentFunctionDepth = 0;
|
|
160
79
|
const functionStack = [];
|
|
161
80
|
function containsTemplateOutput(node, visited = /* @__PURE__ */ new Set()) {
|
|
@@ -187,19 +106,6 @@ const rule$3 = {
|
|
|
187
106
|
"FunctionExpression:exit": exitFunction,
|
|
188
107
|
ArrowFunctionExpression: enterFunction,
|
|
189
108
|
"ArrowFunctionExpression:exit": exitFunction,
|
|
190
|
-
"CallExpression[callee.name='effect']"() {
|
|
191
|
-
insideEffect++;
|
|
192
|
-
},
|
|
193
|
-
"CallExpression[callee.name='effect']:exit"() {
|
|
194
|
-
insideEffect--;
|
|
195
|
-
},
|
|
196
|
-
ForOfStatement(node) {
|
|
197
|
-
if (insideComponent === 0 || insideEffect === 0) return;
|
|
198
|
-
if (containsTemplateOutput(node.body)) context.report({
|
|
199
|
-
node,
|
|
200
|
-
messageId: "noJsxInEffectLoop"
|
|
201
|
-
});
|
|
202
|
-
},
|
|
203
109
|
JSXForExpression(node) {
|
|
204
110
|
if (insideComponent === 0 || nonComponentFunctionDepth > 0) return;
|
|
205
111
|
const body = node.body;
|
|
@@ -231,7 +137,7 @@ const rule$2 = {
|
|
|
231
137
|
description: "Disallow lazy destructuring (&[] / &{}) in TypeScript/JavaScript modules",
|
|
232
138
|
recommended: true
|
|
233
139
|
},
|
|
234
|
-
messages: { noLazyDestructuring: "Lazy destructuring (&[] / &{}) cannot be used in TypeScript/JavaScript modules.
|
|
140
|
+
messages: { noLazyDestructuring: "Lazy destructuring (&[] / &{}) is TSRX syntax and cannot be used in TypeScript/JavaScript modules." },
|
|
235
141
|
schema: []
|
|
236
142
|
},
|
|
237
143
|
create(context) {
|
|
@@ -372,8 +278,6 @@ const plugin = {
|
|
|
372
278
|
version: "0.1.3"
|
|
373
279
|
},
|
|
374
280
|
rules: {
|
|
375
|
-
"no-module-scope-track": rule$6,
|
|
376
|
-
"prefer-oninput": rule$5,
|
|
377
281
|
"no-return-in-component": rule$4,
|
|
378
282
|
"control-flow-jsx": rule$3,
|
|
379
283
|
"no-lazy-destructuring-in-modules": rule$2,
|
|
@@ -411,12 +315,12 @@ const plugin = {
|
|
|
411
315
|
configs: {}
|
|
412
316
|
};
|
|
413
317
|
const require = createRequire(import.meta.url);
|
|
414
|
-
let
|
|
318
|
+
let tsrxParser;
|
|
415
319
|
let tsParser;
|
|
416
320
|
try {
|
|
417
|
-
|
|
321
|
+
tsrxParser = require("@tsrx/eslint-parser");
|
|
418
322
|
} catch {
|
|
419
|
-
|
|
323
|
+
tsrxParser = null;
|
|
420
324
|
}
|
|
421
325
|
try {
|
|
422
326
|
tsParser = require("@typescript-eslint/parser");
|
|
@@ -425,17 +329,15 @@ try {
|
|
|
425
329
|
}
|
|
426
330
|
function createConfig(name, files, parser, isTsrx) {
|
|
427
331
|
const rules = {
|
|
428
|
-
"
|
|
429
|
-
"
|
|
430
|
-
"
|
|
431
|
-
"ripple/no-lazy-destructuring-in-modules": "error",
|
|
432
|
-
"ripple/valid-for-of-key": "error"
|
|
332
|
+
"tsrx/control-flow-jsx": "error",
|
|
333
|
+
"tsrx/no-lazy-destructuring-in-modules": "error",
|
|
334
|
+
"tsrx/valid-for-of-key": "error"
|
|
433
335
|
};
|
|
434
|
-
if (isTsrx) rules["
|
|
336
|
+
if (isTsrx) rules["tsrx/require-statement-container-body"] = "error";
|
|
435
337
|
const config = {
|
|
436
338
|
name,
|
|
437
339
|
files,
|
|
438
|
-
plugins: {
|
|
340
|
+
plugins: { tsrx: plugin },
|
|
439
341
|
rules
|
|
440
342
|
};
|
|
441
343
|
if (parser) config.languageOptions = {
|
|
@@ -448,10 +350,10 @@ function createConfig(name, files, parser, isTsrx) {
|
|
|
448
350
|
return config;
|
|
449
351
|
}
|
|
450
352
|
plugin.configs.recommended = [
|
|
451
|
-
createConfig("
|
|
452
|
-
createConfig("
|
|
353
|
+
createConfig("tsrx/recommended-tsrx-files", ["**/*.tsrx"], tsrxParser, true),
|
|
354
|
+
createConfig("tsrx/recommended-typescript-files", ["**/*.ts", "**/*.tsx"], tsParser, false),
|
|
453
355
|
{
|
|
454
|
-
name: "
|
|
356
|
+
name: "tsrx/ignores",
|
|
455
357
|
ignores: [
|
|
456
358
|
"**/*.d.ts",
|
|
457
359
|
"**/node_modules/**",
|
|
@@ -461,10 +363,10 @@ plugin.configs.recommended = [
|
|
|
461
363
|
}
|
|
462
364
|
];
|
|
463
365
|
plugin.configs.strict = [
|
|
464
|
-
createConfig("
|
|
465
|
-
createConfig("
|
|
366
|
+
createConfig("tsrx/strict-tsrx-files", ["**/*.tsrx"], tsrxParser, true),
|
|
367
|
+
createConfig("tsrx/strict-typescript-files", ["**/*.ts", "**/*.tsx"], tsParser, false),
|
|
466
368
|
{
|
|
467
|
-
name: "
|
|
369
|
+
name: "tsrx/ignores",
|
|
468
370
|
ignores: [
|
|
469
371
|
"**/*.d.ts",
|
|
470
372
|
"**/node_modules/**",
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["rule","rule","rule","NESTED_BOUNDARY_TYPES","functionNode","rule","rule","rule","noModuleScopeTrack","preferOnInput","noReturnInComponent","controlFlowJsx","noLazyDestructuringInModules","validForOfKey","requireStatementContainerBody"],"sources":["../src/rules/no-module-scope-track.ts","../src/rules/prefer-oninput.ts","../src/rules/no-return-in-component.ts","../src/utils/tsrx.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-lazy-destructuring-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/rules/require-statement-container-body.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\nimport type * as AST from '@tsrx/core/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: 'track() cannot be called at module scope. Move it into a function body.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet functionDepth = 0;\n\n\t\tconst incrementFunctionDepth = () => functionDepth++;\n\t\tconst decrementFunctionDepth = () => functionDepth--;\n\n\t\treturn {\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\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 '@tsrx/core/types/estree';\nimport type * as ESTreeJSX from '@tsrx/core/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: 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 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';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Deprecated: TSRX components are functions that return native TSRX expressions.',\n\t\t\trecommended: false,\n\t\t},\n\t\tdeprecated: true,\n\t\tmessages: {\n\t\t\tnoReturn: 'TSRX components should return native TSRX expressions.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tvoid context;\n\t\treturn {};\n\t},\n};\n\nexport default rule;\n","import type * as AST from '@tsrx/core/types/estree';\n\ntype AnyNode = AST.Node & Record<string, any>;\n\nconst NESTED_BOUNDARY_TYPES = new Set([\n\t'FunctionDeclaration',\n\t'FunctionExpression',\n\t'ArrowFunctionExpression',\n\t'ClassDeclaration',\n\t'ClassExpression',\n\t'MethodDefinition',\n\t'PropertyDefinition',\n\t'StaticBlock',\n]);\n\nexport function isNativeTsrxJsxNode(node: AST.Node | null | undefined): boolean {\n\tif (!node) return false;\n\n\tif (\n\t\tnode.type === ('JSXCodeBlock' as string) ||\n\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\tnode.type === ('JSXForExpression' as string) ||\n\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\tnode.type === ('JSXTryExpression' as string)\n\t) {\n\t\treturn true;\n\t}\n\n\treturn (\n\t\t(node.type === ('JSXElement' as string) ||\n\t\t\tnode.type === ('JSXFragment' as string) ||\n\t\t\tnode.type === ('JSXStyleElement' as string)) &&\n\t\t!!(node as AnyNode).metadata?.native_tsrx\n\t);\n}\n\nexport function functionReturnsNativeTsrx(node: AST.Node): boolean {\n\tconst functionNode = node as AnyNode;\n\tconst body = functionNode.body as AnyNode | undefined;\n\n\tif (!body) {\n\t\treturn false;\n\t}\n\n\tif (isNativeTsrxJsxNode(body)) {\n\t\treturn true;\n\t}\n\n\tif (body.type !== 'BlockStatement') {\n\t\treturn false;\n\t}\n\n\treturn containsNativeTsrxReturn(body);\n}\n\nfunction containsNativeTsrxReturn(node: AnyNode): boolean {\n\tif (!node || typeof node !== 'object') {\n\t\treturn false;\n\t}\n\n\tif (node.type === 'ReturnStatement' && isNativeTsrxJsxNode(node.argument)) {\n\t\treturn true;\n\t}\n\n\tif (node.type !== 'BlockStatement' && NESTED_BOUNDARY_TYPES.has(node.type)) {\n\t\treturn false;\n\t}\n\n\tfor (const key of Object.keys(node)) {\n\t\tif (key === 'parent' || key === 'loc' || key === 'range') {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst value = node[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const child of value) {\n\t\t\t\tif (child?.type && containsNativeTsrxReturn(child)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (value?.type && containsNativeTsrxReturn(value)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n","import type { Rule } from 'eslint';\nimport type * as AST from '@tsrx/core/types/estree';\nimport { functionReturnsNativeTsrx } from '../utils/tsrx.js';\n\nconst NESTED_BOUNDARY_TYPES = new Set([\n\t'FunctionDeclaration',\n\t'FunctionExpression',\n\t'ArrowFunctionExpression',\n\t'ClassDeclaration',\n\t'ClassExpression',\n\t'MethodDefinition',\n\t'PropertyDefinition',\n]);\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription:\n\t\t\t\t'Require template output in @for blocks, but disallow JSX output in effect loops',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\trequireJsxInLoop:\n\t\t\t\t'@for blocks in returned TSRX should contain template output. Render an element, fragment, or nested template directive.',\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\t\tlet nonComponentFunctionDepth = 0;\n\t\tconst functionStack: boolean[] = [];\n\n\t\tfunction containsTemplateOutput(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\tif (visited.size > 1 && NESTED_BOUNDARY_TYPES.has(node.type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\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 === ('JSXStyleElement' as string) ||\n\t\t\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\t\t\tnode.type === ('JSXForExpression' as string) ||\n\t\t\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\t\t\tnode.type === ('JSXTryExpression' as string)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (node.type === ('JSXCodeBlock' as string)) {\n\t\t\t\tconst render = (node as any).render;\n\t\t\t\treturn !!render && containsTemplateOutput(render, visited);\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' && containsTemplateOutput(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 && containsTemplateOutput(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\tFunctionDeclaration: enterFunction,\n\t\t\t'FunctionDeclaration:exit': exitFunction,\n\t\t\tFunctionExpression: enterFunction,\n\t\t\t'FunctionExpression:exit': exitFunction,\n\t\t\tArrowFunctionExpression: enterFunction,\n\t\t\t'ArrowFunctionExpression:exit': exitFunction,\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 || insideEffect === 0) return;\n\n\t\t\t\tif (containsTemplateOutput(node.body)) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode,\n\t\t\t\t\t\tmessageId: 'noJsxInEffectLoop',\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t},\n\n\t\t\tJSXForExpression(node: AST.Node) {\n\t\t\t\tif (insideComponent === 0 || nonComponentFunctionDepth > 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst body = (node as any).body;\n\t\t\t\tif (!body || containsTemplateOutput(body)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontext.report({\n\t\t\t\t\tnode,\n\t\t\t\t\tmessageId: 'requireJsxInLoop',\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\tfunction enterFunction(node: AST.Node) {\n\t\t\tconst isComponent = functionReturnsNativeTsrx(node);\n\t\t\tfunctionStack.push(isComponent);\n\n\t\t\tif (isComponent) {\n\t\t\t\tinsideComponent++;\n\t\t\t} else if (insideComponent > 0) {\n\t\t\t\tnonComponentFunctionDepth++;\n\t\t\t}\n\t\t}\n\n\t\tfunction exitFunction() {\n\t\t\tconst isComponent = functionStack.pop();\n\n\t\t\tif (isComponent) {\n\t\t\t\tinsideComponent--;\n\t\t\t} else if (insideComponent > 0) {\n\t\t\t\tnonComponentFunctionDepth--;\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 TSRX files where lazy destructuring is valid\n\t\tif (filename && filename.endsWith('.tsrx')) {\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 '@tsrx/core/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\tconst checkForOfKey = (node: AST.ForOfStatement | AST.JSXForExpression) => {\n\t\t\tif (!node.key) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst checkIdentifier = (identifier: AST.Identifier) => {\n\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\tif (!variable) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst traverse = (node: AST.Node) => {\n\t\t\t\tif (!node) return;\n\n\t\t\t\tswitch (node.type) {\n\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t(node.elements as (AST.Expression | AST.SpreadElement)[]).forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\tnode.properties.forEach((prop: AST.Property | AST.SpreadElement) => {\n\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\ttraverse(node.key);\n\t\t};\n\n\t\t// `@for (... ; key X)` loops parse to `JSXForExpression`, while the bare\n\t\t// `for (... ; key X)` form parses to `ForOfStatement`. Both carry the same\n\t\t// `key` shape, so validate either node type.\n\t\treturn {\n\t\t\tForOfStatement: checkForOfKey,\n\t\t\tJSXForExpression: checkForOfKey,\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 type { Rule } from 'eslint';\nimport type * as AST from '@tsrx/core/types/estree';\n\ntype AnyNode = AST.Node & Record<string, any>;\n\nconst MESSAGE =\n\t\"This function body contains TSRX template output, but it is a normal JavaScript block. Add '@' before the opening brace to use a TSRX statement container.\";\n\nfunction is_template_output_statement(node: AnyNode | null | undefined): boolean {\n\tif (!node) return false;\n\n\tif (\n\t\tnode.type === ('JSXElement' as string) ||\n\t\tnode.type === ('JSXFragment' as string) ||\n\t\tnode.type === ('JSXStyleElement' as string) ||\n\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\tnode.type === ('JSXForExpression' as string) ||\n\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\tnode.type === ('JSXTryExpression' as string)\n\t) {\n\t\treturn true;\n\t}\n\n\treturn (\n\t\tnode.type === 'ExpressionStatement' &&\n\t\tis_template_output_statement((node as AnyNode).expression)\n\t);\n}\n\nfunction is_ignored_statement(node: AnyNode | null | undefined): boolean {\n\treturn !node || node.type === 'EmptyStatement';\n}\n\nfunction get_forgotten_output_statement(node: AST.Node): AnyNode | null {\n\tconst body = (node as AnyNode).body;\n\tif (!body || body.type !== 'BlockStatement') {\n\t\treturn null;\n\t}\n\n\tlet target: AnyNode | null = null;\n\tlet target_index = -1;\n\tconst statements = body.body || [];\n\tfor (let index = 0; index < statements.length; index++) {\n\t\tconst statement = statements[index] as AnyNode;\n\t\tif (is_template_output_statement(statement)) {\n\t\t\tif (target_index !== -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\ttarget = statement;\n\t\t\ttarget_index = index;\n\t\t}\n\t}\n\n\tif (!target) {\n\t\treturn null;\n\t}\n\n\tfor (const statement of statements.slice(target_index + 1)) {\n\t\tif (!is_ignored_statement(statement as AnyNode)) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn target;\n}\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Require @{...} for TSRX component bodies with setup and template output.',\n\t\t},\n\t\tfixable: 'code',\n\t\tmessages: {\n\t\t\trequireStatementContainerBody: MESSAGE,\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tfunction check_function(node: AST.Node) {\n\t\t\tif (!(node as AnyNode).returnType) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst forgotten_output = get_forgotten_output_statement(node);\n\t\t\tif (!forgotten_output) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst body = (node as AnyNode).body;\n\t\t\tcontext.report({\n\t\t\t\tnode: forgotten_output,\n\t\t\t\tmessageId: 'requireStatementContainerBody',\n\t\t\t\tfix(fixer) {\n\t\t\t\t\treturn fixer.insertTextBefore(body, '@');\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tFunctionDeclaration: check_function,\n\t\t\tFunctionExpression: check_function,\n\t\t\tArrowFunctionExpression: check_function,\n\t\t};\n\t},\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';\nimport requireStatementContainerBody from './rules/require-statement-container-body.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@tsrx/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\t'require-statement-container-body': requireStatementContainerBody,\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('@tsrx/eslint-parser');\n} catch {\n\t// @tsrx/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, isTsrx: boolean) {\n\tconst rules: Record<string, string> = {\n\t\t'ripple/no-module-scope-track': 'error',\n\t\t'ripple/prefer-oninput': 'warn',\n\t\t'ripple/control-flow-jsx': 'error',\n\t\t'ripple/no-lazy-destructuring-in-modules': 'error',\n\t\t'ripple/valid-for-of-key': 'error',\n\t};\n\n\tif (isTsrx) {\n\t\trules['ripple/require-statement-container-body'] = 'error';\n\t}\n\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};\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', ['**/*.tsrx'], rippleParser, true),\n\tcreateConfig('ripple/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),\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', ['**/*.tsrx'], rippleParser, true),\n\tcreateConfig('ripple/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),\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","NESTED_BOUNDARY_TYPES","functionNode","rule","rule","rule","noReturnInComponent","controlFlowJsx","noLazyDestructuringInModules","validForOfKey","requireStatementContainerBody"],"sources":["../src/rules/no-return-in-component.ts","../src/utils/tsrx.ts","../src/rules/control-flow-jsx.ts","../src/rules/no-lazy-destructuring-in-modules.ts","../src/rules/valid-for-of-key.ts","../src/rules/require-statement-container-body.ts","../src/index.ts"],"sourcesContent":["import type { Rule } from 'eslint';\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Deprecated: TSRX components are functions that return native TSRX expressions.',\n\t\t\trecommended: false,\n\t\t},\n\t\tdeprecated: true,\n\t\tmessages: {\n\t\t\tnoReturn: 'TSRX components should return native TSRX expressions.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tvoid context;\n\t\treturn {};\n\t},\n};\n\nexport default rule;\n","import type * as AST from '@tsrx/core/types/estree';\n\ntype AnyNode = AST.Node & Record<string, any>;\n\nconst NESTED_BOUNDARY_TYPES = new Set([\n\t'FunctionDeclaration',\n\t'FunctionExpression',\n\t'ArrowFunctionExpression',\n\t'ClassDeclaration',\n\t'ClassExpression',\n\t'MethodDefinition',\n\t'PropertyDefinition',\n\t'StaticBlock',\n]);\n\nexport function isNativeTsrxJsxNode(node: AST.Node | null | undefined): boolean {\n\tif (!node) return false;\n\n\tif (\n\t\tnode.type === ('JSXCodeBlock' as string) ||\n\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\tnode.type === ('JSXForExpression' as string) ||\n\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\tnode.type === ('JSXTryExpression' as string)\n\t) {\n\t\treturn true;\n\t}\n\n\treturn (\n\t\t(node.type === ('JSXElement' as string) ||\n\t\t\tnode.type === ('JSXFragment' as string) ||\n\t\t\tnode.type === ('JSXStyleElement' as string)) &&\n\t\t!!(node as AnyNode).metadata?.native_tsrx\n\t);\n}\n\nexport function functionReturnsNativeTsrx(node: AST.Node): boolean {\n\tconst functionNode = node as AnyNode;\n\tconst body = functionNode.body as AnyNode | undefined;\n\n\tif (!body) {\n\t\treturn false;\n\t}\n\n\tif (isNativeTsrxJsxNode(body)) {\n\t\treturn true;\n\t}\n\n\tif (body.type !== 'BlockStatement') {\n\t\treturn false;\n\t}\n\n\treturn containsNativeTsrxReturn(body);\n}\n\nfunction containsNativeTsrxReturn(node: AnyNode): boolean {\n\tif (!node || typeof node !== 'object') {\n\t\treturn false;\n\t}\n\n\tif (node.type === 'ReturnStatement' && isNativeTsrxJsxNode(node.argument)) {\n\t\treturn true;\n\t}\n\n\tif (node.type !== 'BlockStatement' && NESTED_BOUNDARY_TYPES.has(node.type)) {\n\t\treturn false;\n\t}\n\n\tfor (const key of Object.keys(node)) {\n\t\tif (key === 'parent' || key === 'loc' || key === 'range') {\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst value = node[key];\n\t\tif (Array.isArray(value)) {\n\t\t\tfor (const child of value) {\n\t\t\t\tif (child?.type && containsNativeTsrxReturn(child)) {\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t} else if (value?.type && containsNativeTsrxReturn(value)) {\n\t\t\treturn true;\n\t\t}\n\t}\n\n\treturn false;\n}\n","import type { Rule } from 'eslint';\nimport type * as AST from '@tsrx/core/types/estree';\nimport { functionReturnsNativeTsrx } from '../utils/tsrx.js';\n\nconst NESTED_BOUNDARY_TYPES = new Set([\n\t'FunctionDeclaration',\n\t'FunctionExpression',\n\t'ArrowFunctionExpression',\n\t'ClassDeclaration',\n\t'ClassExpression',\n\t'MethodDefinition',\n\t'PropertyDefinition',\n]);\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Require template output in @for blocks',\n\t\t\trecommended: true,\n\t\t},\n\t\tmessages: {\n\t\t\trequireJsxInLoop:\n\t\t\t\t'@for blocks in returned TSRX should contain template output. Render an element, fragment, or nested template directive.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tlet insideComponent = 0;\n\t\tlet nonComponentFunctionDepth = 0;\n\t\tconst functionStack: boolean[] = [];\n\n\t\tfunction containsTemplateOutput(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\tif (visited.size > 1 && NESTED_BOUNDARY_TYPES.has(node.type)) {\n\t\t\t\treturn false;\n\t\t\t}\n\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 === ('JSXStyleElement' as string) ||\n\t\t\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\t\t\tnode.type === ('JSXForExpression' as string) ||\n\t\t\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\t\t\tnode.type === ('JSXTryExpression' as string)\n\t\t\t) {\n\t\t\t\treturn true;\n\t\t\t}\n\n\t\t\tif (node.type === ('JSXCodeBlock' as string)) {\n\t\t\t\tconst render = (node as any).render;\n\t\t\t\treturn !!render && containsTemplateOutput(render, visited);\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' && containsTemplateOutput(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 && containsTemplateOutput(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\tFunctionDeclaration: enterFunction,\n\t\t\t'FunctionDeclaration:exit': exitFunction,\n\t\t\tFunctionExpression: enterFunction,\n\t\t\t'FunctionExpression:exit': exitFunction,\n\t\t\tArrowFunctionExpression: enterFunction,\n\t\t\t'ArrowFunctionExpression:exit': exitFunction,\n\n\t\t\tJSXForExpression(node: AST.Node) {\n\t\t\t\tif (insideComponent === 0 || nonComponentFunctionDepth > 0) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tconst body = (node as any).body;\n\t\t\t\tif (!body || containsTemplateOutput(body)) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tcontext.report({\n\t\t\t\t\tnode,\n\t\t\t\t\tmessageId: 'requireJsxInLoop',\n\t\t\t\t});\n\t\t\t},\n\t\t};\n\n\t\tfunction enterFunction(node: AST.Node) {\n\t\t\tconst isComponent = functionReturnsNativeTsrx(node);\n\t\t\tfunctionStack.push(isComponent);\n\n\t\t\tif (isComponent) {\n\t\t\t\tinsideComponent++;\n\t\t\t} else if (insideComponent > 0) {\n\t\t\t\tnonComponentFunctionDepth++;\n\t\t\t}\n\t\t}\n\n\t\tfunction exitFunction() {\n\t\t\tconst isComponent = functionStack.pop();\n\n\t\t\tif (isComponent) {\n\t\t\t\tinsideComponent--;\n\t\t\t} else if (insideComponent > 0) {\n\t\t\t\tnonComponentFunctionDepth--;\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 (&[] / &{}) is TSRX syntax and cannot be used in TypeScript/JavaScript modules.',\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tconst filename = context.filename;\n\n\t\t// Skip TSRX files where lazy destructuring is valid\n\t\tif (filename && filename.endsWith('.tsrx')) {\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 '@tsrx/core/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\tconst checkForOfKey = (node: AST.ForOfStatement | AST.JSXForExpression) => {\n\t\t\tif (!node.key) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst checkIdentifier = (identifier: AST.Identifier) => {\n\t\t\t\tconst scope = context.sourceCode.getScope(node);\n\t\t\t\tconst variable = findVariable(scope, identifier.name);\n\n\t\t\t\tif (!variable) {\n\t\t\t\t\tcontext.report({\n\t\t\t\t\t\tnode: identifier,\n\t\t\t\t\t\tmessageId: 'undefinedVariable',\n\t\t\t\t\t\tdata: {\n\t\t\t\t\t\t\tname: identifier.name,\n\t\t\t\t\t\t},\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tconst traverse = (node: AST.Node) => {\n\t\t\t\tif (!node) return;\n\n\t\t\t\tswitch (node.type) {\n\t\t\t\t\tcase 'Identifier':\n\t\t\t\t\t\tcheckIdentifier(node);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'MemberExpression':\n\t\t\t\t\t\ttraverse(node.object);\n\n\t\t\t\t\t\tif (node.computed) {\n\t\t\t\t\t\t\ttraverse(node.property);\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'BinaryExpression':\n\t\t\t\t\tcase 'LogicalExpression':\n\t\t\t\t\t\ttraverse(node.left);\n\t\t\t\t\t\ttraverse(node.right);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'UnaryExpression':\n\t\t\t\t\t\ttraverse(node.argument);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'CallExpression':\n\t\t\t\t\t\ttraverse(node.callee);\n\t\t\t\t\t\tnode.arguments.forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ArrayExpression':\n\t\t\t\t\t\t(node.elements as (AST.Expression | AST.SpreadElement)[]).forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ObjectExpression':\n\t\t\t\t\t\tnode.properties.forEach((prop: AST.Property | AST.SpreadElement) => {\n\t\t\t\t\t\t\tif (prop.type === 'Property') {\n\t\t\t\t\t\t\t\tif (prop.computed) {\n\t\t\t\t\t\t\t\t\ttraverse(prop.key);\n\t\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\t\ttraverse(prop.value);\n\t\t\t\t\t\t\t} else if (prop.type === 'SpreadElement') {\n\t\t\t\t\t\t\t\ttraverse(prop.argument);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'ConditionalExpression':\n\t\t\t\t\t\ttraverse(node.test);\n\t\t\t\t\t\ttraverse(node.consequent);\n\t\t\t\t\t\ttraverse(node.alternate);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 'TemplateLiteral':\n\t\t\t\t\t\tnode.expressions.forEach(traverse);\n\n\t\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t};\n\n\t\t\ttraverse(node.key);\n\t\t};\n\n\t\t// `@for (... ; key X)` loops parse to `JSXForExpression`, while the bare\n\t\t// `for (... ; key X)` form parses to `ForOfStatement`. Both carry the same\n\t\t// `key` shape, so validate either node type.\n\t\treturn {\n\t\t\tForOfStatement: checkForOfKey,\n\t\t\tJSXForExpression: checkForOfKey,\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 type { Rule } from 'eslint';\nimport type * as AST from '@tsrx/core/types/estree';\n\ntype AnyNode = AST.Node & Record<string, any>;\n\nconst MESSAGE =\n\t\"This function body contains TSRX template output, but it is a normal JavaScript block. Add '@' before the opening brace to use a TSRX statement container.\";\n\nfunction is_template_output_statement(node: AnyNode | null | undefined): boolean {\n\tif (!node) return false;\n\n\tif (\n\t\tnode.type === ('JSXElement' as string) ||\n\t\tnode.type === ('JSXFragment' as string) ||\n\t\tnode.type === ('JSXStyleElement' as string) ||\n\t\tnode.type === ('JSXIfExpression' as string) ||\n\t\tnode.type === ('JSXForExpression' as string) ||\n\t\tnode.type === ('JSXSwitchExpression' as string) ||\n\t\tnode.type === ('JSXTryExpression' as string)\n\t) {\n\t\treturn true;\n\t}\n\n\treturn (\n\t\tnode.type === 'ExpressionStatement' &&\n\t\tis_template_output_statement((node as AnyNode).expression)\n\t);\n}\n\nfunction is_ignored_statement(node: AnyNode | null | undefined): boolean {\n\treturn !node || node.type === 'EmptyStatement';\n}\n\nfunction get_forgotten_output_statement(node: AST.Node): AnyNode | null {\n\tconst body = (node as AnyNode).body;\n\tif (!body || body.type !== 'BlockStatement') {\n\t\treturn null;\n\t}\n\n\tlet target: AnyNode | null = null;\n\tlet target_index = -1;\n\tconst statements = body.body || [];\n\tfor (let index = 0; index < statements.length; index++) {\n\t\tconst statement = statements[index] as AnyNode;\n\t\tif (is_template_output_statement(statement)) {\n\t\t\tif (target_index !== -1) {\n\t\t\t\treturn null;\n\t\t\t}\n\t\t\ttarget = statement;\n\t\t\ttarget_index = index;\n\t\t}\n\t}\n\n\tif (!target) {\n\t\treturn null;\n\t}\n\n\tfor (const statement of statements.slice(target_index + 1)) {\n\t\tif (!is_ignored_statement(statement as AnyNode)) {\n\t\t\treturn null;\n\t\t}\n\t}\n\n\treturn target;\n}\n\nconst rule: Rule.RuleModule = {\n\tmeta: {\n\t\ttype: 'problem',\n\t\tdocs: {\n\t\t\tdescription: 'Require @{...} for TSRX component bodies with setup and template output.',\n\t\t},\n\t\tfixable: 'code',\n\t\tmessages: {\n\t\t\trequireStatementContainerBody: MESSAGE,\n\t\t},\n\t\tschema: [],\n\t},\n\tcreate(context) {\n\t\tfunction check_function(node: AST.Node) {\n\t\t\tif (!(node as AnyNode).returnType) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst forgotten_output = get_forgotten_output_statement(node);\n\t\t\tif (!forgotten_output) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst body = (node as AnyNode).body;\n\t\t\tcontext.report({\n\t\t\t\tnode: forgotten_output,\n\t\t\t\tmessageId: 'requireStatementContainerBody',\n\t\t\t\tfix(fixer) {\n\t\t\t\t\treturn fixer.insertTextBefore(body, '@');\n\t\t\t\t},\n\t\t\t});\n\t\t}\n\n\t\treturn {\n\t\t\tFunctionDeclaration: check_function,\n\t\t\tFunctionExpression: check_function,\n\t\t\tArrowFunctionExpression: check_function,\n\t\t};\n\t},\n};\n\nexport default rule;\n","import { createRequire } from 'module';\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';\nimport requireStatementContainerBody from './rules/require-statement-container-body.js';\n\nconst plugin = {\n\tmeta: {\n\t\tname: '@tsrx/eslint-plugin',\n\t\tversion: '0.1.3',\n\t},\n\trules: {\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\t'require-statement-container-body': requireStatementContainerBody,\n\t},\n\tconfigs: {} as any,\n};\n\n// Try to load optional parsers\nconst require = createRequire(import.meta.url);\n\nlet tsrxParser: any;\nlet tsParser: any;\n\ntry {\n\ttsrxParser = require('@tsrx/eslint-parser');\n} catch {\n\t// @tsrx/eslint-parser is optional\n\ttsrxParser = 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, isTsrx: boolean) {\n\tconst rules: Record<string, string> = {\n\t\t'tsrx/control-flow-jsx': 'error',\n\t\t'tsrx/no-lazy-destructuring-in-modules': 'error',\n\t\t'tsrx/valid-for-of-key': 'error',\n\t};\n\n\tif (isTsrx) {\n\t\trules['tsrx/require-statement-container-body'] = 'error';\n\t}\n\n\tconst config: any = {\n\t\tname,\n\t\tfiles,\n\t\tplugins: {\n\t\t\ttsrx: plugin,\n\t\t},\n\t\trules,\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('tsrx/recommended-tsrx-files', ['**/*.tsrx'], tsrxParser, true),\n\tcreateConfig('tsrx/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),\n\t{\n\t\tname: 'tsrx/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('tsrx/strict-tsrx-files', ['**/*.tsrx'], tsrxParser, true),\n\tcreateConfig('tsrx/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),\n\t{\n\t\tname: 'tsrx/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,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tsrx/eslint-plugin",
|
|
3
|
-
"version": "0.3.
|
|
4
|
-
"description": "ESLint plugin for
|
|
3
|
+
"version": "0.3.127",
|
|
4
|
+
"description": "ESLint plugin for TSRX",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "src/index.ts",
|
|
7
7
|
"exports": {
|
|
@@ -14,26 +14,26 @@
|
|
|
14
14
|
"eslint",
|
|
15
15
|
"eslintplugin",
|
|
16
16
|
"eslint-plugin",
|
|
17
|
-
"
|
|
17
|
+
"tsrx"
|
|
18
18
|
],
|
|
19
19
|
"author": "Dominic Gannaway",
|
|
20
20
|
"license": "MIT",
|
|
21
21
|
"repository": {
|
|
22
22
|
"type": "git",
|
|
23
|
-
"url": "git+https://github.com/
|
|
23
|
+
"url": "git+https://github.com/tsrx-org/tsrx.git",
|
|
24
24
|
"directory": "packages/eslint-plugin"
|
|
25
25
|
},
|
|
26
26
|
"bugs": {
|
|
27
|
-
"url": "https://github.com/
|
|
27
|
+
"url": "https://github.com/tsrx-org/tsrx/issues"
|
|
28
28
|
},
|
|
29
|
-
"homepage": "https://
|
|
29
|
+
"homepage": "https://tsrx.dev/",
|
|
30
30
|
"publishConfig": {
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
34
|
"eslint": ">=9.0.0",
|
|
35
35
|
"@typescript-eslint/parser": "^8.56.1",
|
|
36
|
-
"@tsrx/eslint-parser": "0.3.
|
|
36
|
+
"@tsrx/eslint-parser": "0.3.127"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/eslint": "^9.6.1",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"tsdown": "^0.22.0",
|
|
44
44
|
"typescript": "^5.9.3",
|
|
45
45
|
"vitest": "^4.1.6",
|
|
46
|
-
"@tsrx/eslint-parser": "0.3.
|
|
47
|
-
"@tsrx/core": "0.1.
|
|
46
|
+
"@tsrx/eslint-parser": "0.3.127",
|
|
47
|
+
"@tsrx/core": "0.1.62"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=22.0.0"
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { createRequire } from 'module';
|
|
2
|
-
import noModuleScopeTrack from './rules/no-module-scope-track.js';
|
|
3
|
-
import preferOnInput from './rules/prefer-oninput.js';
|
|
4
2
|
import noReturnInComponent from './rules/no-return-in-component.js';
|
|
5
3
|
import controlFlowJsx from './rules/control-flow-jsx.js';
|
|
6
4
|
import noLazyDestructuringInModules from './rules/no-lazy-destructuring-in-modules.js';
|
|
@@ -13,8 +11,6 @@ const plugin = {
|
|
|
13
11
|
version: '0.1.3',
|
|
14
12
|
},
|
|
15
13
|
rules: {
|
|
16
|
-
'no-module-scope-track': noModuleScopeTrack,
|
|
17
|
-
'prefer-oninput': preferOnInput,
|
|
18
14
|
'no-return-in-component': noReturnInComponent,
|
|
19
15
|
'control-flow-jsx': controlFlowJsx,
|
|
20
16
|
'no-lazy-destructuring-in-modules': noLazyDestructuringInModules,
|
|
@@ -27,14 +23,14 @@ const plugin = {
|
|
|
27
23
|
// Try to load optional parsers
|
|
28
24
|
const require = createRequire(import.meta.url);
|
|
29
25
|
|
|
30
|
-
let
|
|
26
|
+
let tsrxParser: any;
|
|
31
27
|
let tsParser: any;
|
|
32
28
|
|
|
33
29
|
try {
|
|
34
|
-
|
|
30
|
+
tsrxParser = require('@tsrx/eslint-parser');
|
|
35
31
|
} catch {
|
|
36
32
|
// @tsrx/eslint-parser is optional
|
|
37
|
-
|
|
33
|
+
tsrxParser = null;
|
|
38
34
|
}
|
|
39
35
|
|
|
40
36
|
try {
|
|
@@ -47,22 +43,20 @@ try {
|
|
|
47
43
|
// Helper to create config objects
|
|
48
44
|
function createConfig(name: string, files: string[], parser: any, isTsrx: boolean) {
|
|
49
45
|
const rules: Record<string, string> = {
|
|
50
|
-
'
|
|
51
|
-
'
|
|
52
|
-
'
|
|
53
|
-
'ripple/no-lazy-destructuring-in-modules': 'error',
|
|
54
|
-
'ripple/valid-for-of-key': 'error',
|
|
46
|
+
'tsrx/control-flow-jsx': 'error',
|
|
47
|
+
'tsrx/no-lazy-destructuring-in-modules': 'error',
|
|
48
|
+
'tsrx/valid-for-of-key': 'error',
|
|
55
49
|
};
|
|
56
50
|
|
|
57
51
|
if (isTsrx) {
|
|
58
|
-
rules['
|
|
52
|
+
rules['tsrx/require-statement-container-body'] = 'error';
|
|
59
53
|
}
|
|
60
54
|
|
|
61
55
|
const config: any = {
|
|
62
56
|
name,
|
|
63
57
|
files,
|
|
64
58
|
plugins: {
|
|
65
|
-
|
|
59
|
+
tsrx: plugin,
|
|
66
60
|
},
|
|
67
61
|
rules,
|
|
68
62
|
};
|
|
@@ -83,20 +77,20 @@ function createConfig(name: string, files: string[], parser: any, isTsrx: boolea
|
|
|
83
77
|
|
|
84
78
|
// Recommended configuration (flat config format)
|
|
85
79
|
plugin.configs.recommended = [
|
|
86
|
-
createConfig('
|
|
87
|
-
createConfig('
|
|
80
|
+
createConfig('tsrx/recommended-tsrx-files', ['**/*.tsrx'], tsrxParser, true),
|
|
81
|
+
createConfig('tsrx/recommended-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),
|
|
88
82
|
{
|
|
89
|
-
name: '
|
|
83
|
+
name: 'tsrx/ignores',
|
|
90
84
|
ignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
91
85
|
},
|
|
92
86
|
];
|
|
93
87
|
|
|
94
88
|
// Strict configuration (flat config format)
|
|
95
89
|
plugin.configs.strict = [
|
|
96
|
-
createConfig('
|
|
97
|
-
createConfig('
|
|
90
|
+
createConfig('tsrx/strict-tsrx-files', ['**/*.tsrx'], tsrxParser, true),
|
|
91
|
+
createConfig('tsrx/strict-typescript-files', ['**/*.ts', '**/*.tsx'], tsParser, false),
|
|
98
92
|
{
|
|
99
|
-
name: '
|
|
93
|
+
name: 'tsrx/ignores',
|
|
100
94
|
ignores: ['**/*.d.ts', '**/node_modules/**', '**/dist/**', '**/build/**'],
|
|
101
95
|
},
|
|
102
96
|
];
|