@elaraai/east-diagnostics 1.0.90 → 1.0.91
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 +1 -1
- package/dist/src/block-scope.d.ts +2 -0
- package/dist/src/block-scope.d.ts.map +1 -1
- package/dist/src/block-scope.js +1 -1
- package/dist/src/block-scope.js.map +1 -1
- package/dist/src/rules/no-reinlined-east-binding.d.ts +10 -0
- package/dist/src/rules/no-reinlined-east-binding.d.ts.map +1 -1
- package/dist/src/rules/no-reinlined-east-binding.js +118 -27
- package/dist/src/rules/no-reinlined-east-binding.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -40,7 +40,7 @@ like any linter.
|
|
|
40
40
|
- **`no-relative-src-import`** - Importing *another* package's internals via `../src/…` or a deep `@elaraai/x/src` path instead of its published name. (A package importing its **own** `src` relatively — e.g. a spec's `../src/index.js` — is exempt; it cannot import its own published name.)
|
|
41
41
|
- **`no-let-const-in-expression`** - `$.let`/`$.const` used anywhere other than a `const`/`let` initializer, a bare statement, a `return`, or a concise arrow body (e.g. a struct-field value, array element, call argument, or chain target buries the declaration in an expression).
|
|
42
42
|
- **`no-unexecuted-east-expression`** - A bare East expression statement that is never executed with `$( … )` or bound.
|
|
43
|
-
- **`no-reinlined-east-binding`** - An East `Expr` bound to a JS `const`/`let` and
|
|
43
|
+
- **`no-reinlined-east-binding`** - An East `Expr` bound to a JS `const`/`let` and read twice inside one East body, or read inside a per-element callback, a `$.for`/`$.while` body or a nested `East.function`, is re-inlined — and re-evaluated — at each read; bind it once with `$.let`/`$.const`. One read per exclusive arm (`.ifElse`, `$.if … .else`, a `match` arm) and a `$.const` read anywhere stay silent.
|
|
44
44
|
- **`prefer-jsx-over-factory-call`** - In a `.tsx` file, a factory's `Foo.Root(...)` whose result is a JSX element — author it with the `<Foo>` tag instead.
|
|
45
45
|
- **`no-untracked-east-data`** - A bare JS `const` literal consumed in an East-typed position inside a block — bind it with `$.const`/`$.let`.
|
|
46
46
|
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import type * as ts from "typescript";
|
|
6
6
|
import type { RuleContext, TsModule } from "./types.js";
|
|
7
|
+
/** `East.function(...)` / `East.asyncFunction(...)` — the classic East block root. */
|
|
8
|
+
export declare function isEastFunctionCall(node: ts.Node, t: TsModule): node is ts.CallExpression;
|
|
7
9
|
/** True for an arrow/function whose first parameter is an East `BlockBuilder` —
|
|
8
10
|
* the shape of every East block callback: `East.function`'s `$ => …`, the JSX
|
|
9
11
|
* `<Reactive>{$ => …}}`, `ui(…, $ => …)`, a `.map(($, x) => …)` body. Keying on
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-scope.d.ts","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"block-scope.d.ts","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,KAAK,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAGxD,sFAAsF;AACtF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,CASxF;AAED;;;;sEAIsE;AACtE,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAM/E;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,EAAE,CAAC,IAAI,GAAG,SAAS,CAWxF;AAED,4FAA4F;AAC5F,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,CAEzE;AAED;4DAC4D;AAC5D,wBAAgB,cAAc,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,QAAQ,GAAG,OAAO,CAclE"}
|
package/dist/src/block-scope.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isBlockBuilderType } from "./east-type.js";
|
|
2
2
|
/** `East.function(...)` / `East.asyncFunction(...)` — the classic East block root. */
|
|
3
|
-
function isEastFunctionCall(node, t) {
|
|
3
|
+
export function isEastFunctionCall(node, t) {
|
|
4
4
|
if (!t.isCallExpression(node))
|
|
5
5
|
return false;
|
|
6
6
|
const callee = node.expression;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"block-scope.js","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC;AAEpD,sFAAsF;AACtF,
|
|
1
|
+
{"version":3,"file":"block-scope.js","sourceRoot":"","sources":["../../src/block-scope.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,13 @@
|
|
|
1
1
|
import type { EastRule } from "../types.js";
|
|
2
|
+
/** Methods of the East expression classes (and the `East.Array` / `East.Set` / `East.Dict`
|
|
3
|
+
* generators) whose callback body runs per element, per key or per iteration. The spec pins the
|
|
4
|
+
* partition: every callback-taking method of the Expr classes is here or in `ONCE_METHODS`. */
|
|
5
|
+
export declare const REPEATING_METHODS: ReadonlySet<string>;
|
|
6
|
+
/** Callback-taking methods whose body runs ONCE per call: a default producer, a single-key upsert
|
|
7
|
+
* or merge, a conditional, a handler. */
|
|
8
|
+
export declare const ONCE_METHODS: ReadonlySet<string>;
|
|
9
|
+
/** The `BlockBuilder` loop statements: their body runs per iteration. The other statement bodies
|
|
10
|
+
* (`if` / `else` / `elseIf` / `match` / `try` / `catch` / `finally`) run at most once. */
|
|
11
|
+
export declare const LOOP_METHODS: ReadonlySet<string>;
|
|
2
12
|
export declare const noReinlinedEastBinding: EastRule;
|
|
3
13
|
//# sourceMappingURL=no-reinlined-east-binding.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-reinlined-east-binding.d.ts","sourceRoot":"","sources":["../../../src/rules/no-reinlined-east-binding.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,
|
|
1
|
+
{"version":3,"file":"no-reinlined-east-binding.d.ts","sourceRoot":"","sources":["../../../src/rules/no-reinlined-east-binding.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAyB,MAAM,aAAa,CAAC;AA2BnE;;+FAE+F;AAC/F,eAAO,MAAM,iBAAiB,EAAE,WAAW,CAAC,MAAM,CAQhD,CAAC;AAEH;yCACyC;AACzC,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAE3C,CAAC;AAEH;0FAC0F;AAC1F,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAA6B,CAAC;AAqD3E,eAAO,MAAM,sBAAsB,EAAE,QAmEpC,CAAC"}
|
|
@@ -1,25 +1,104 @@
|
|
|
1
|
-
import { isEastExprType } from "../east-type.js";
|
|
1
|
+
import { isBlockBuilderType, isEastExprType } from "../east-type.js";
|
|
2
2
|
import { matchBlockBuilderCall } from "../block-builder.js";
|
|
3
|
-
import { enclosingBlockScope } from "../block-scope.js";
|
|
3
|
+
import { enclosingBlockScope, isEastFunctionCall } from "../block-scope.js";
|
|
4
4
|
const NAME = "no-reinlined-east-binding";
|
|
5
5
|
const CODE = 990010;
|
|
6
|
-
// An East `Expr` is a value tree, not a slot. Bound to a JS `const`/`let` and
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// of an `Expr` are an error, not a style
|
|
6
|
+
// An East `Expr` is a value tree, not a slot. Bound to a JS `const`/`let` and read more than once
|
|
7
|
+
// inside one East body, it is *re-inlined* at each read — the tree is duplicated, so it is
|
|
8
|
+
// re-evaluated per read and (for mutable values) gets a fresh identity each time. Read once inside a
|
|
9
|
+
// body that RUNS REPEATEDLY — a per-element callback, a `$.for`/`$.while` body, a nested
|
|
10
|
+
// `East.function` — it is re-evaluated every time that body runs. `$.let`/`$.const` introduce a
|
|
11
|
+
// single block binding evaluated once and referenced by name, which is the only way to get
|
|
12
|
+
// single-evaluation + shared identity. So such JS bindings of an `Expr` are an error, not a style
|
|
13
|
+
// nit. (The python builder is held to the same reading since #1312 removed its trace-time pass.)
|
|
13
14
|
//
|
|
14
|
-
// Scoped to the hazard
|
|
15
|
-
//
|
|
16
|
-
//
|
|
17
|
-
//
|
|
18
|
-
//
|
|
15
|
+
// Scoped to the hazard, so a correct program is never asked to change:
|
|
16
|
+
// - a single read is a harmless alias / single-pass argument, and inline composition
|
|
17
|
+
// (`Stack.VStack([A(), B(), …])`) never binds, so neither trips;
|
|
18
|
+
// - reads are counted per enclosing function: two reads in the body that bound the value, or two
|
|
19
|
+
// inside one arm, are two evaluations; one read in each of two EXCLUSIVE arms — `.ifElse(() => a,
|
|
20
|
+
// () => b)`, `$.if(…).else(…)`, the arms of `$.match` / `.match` — is at most one, and is not
|
|
21
|
+
// flagged (binding a guarded partial read above its guard would raise on the very path the guard
|
|
22
|
+
// excludes);
|
|
23
|
+
// - a read inside a body the rule cannot classify counts as once-only and stays silent.
|
|
24
|
+
/** Methods of the East expression classes (and the `East.Array` / `East.Set` / `East.Dict`
|
|
25
|
+
* generators) whose callback body runs per element, per key or per iteration. The spec pins the
|
|
26
|
+
* partition: every callback-taking method of the Expr classes is here or in `ONCE_METHODS`. */
|
|
27
|
+
export const REPEATING_METHODS = new Set([
|
|
28
|
+
"map", "filter", "filterMap", "firstMap", "flatMap", "flattenToArray", "flattenToSet", "flattenToDict",
|
|
29
|
+
"forEach", "reduce", "scan", "mapReduce", "groupReduce", "groupBy", "groupSize", "groupSum", "groupMean",
|
|
30
|
+
"groupEvery", "groupSome", "groupMaximum", "groupMinimum", "groupToArrays", "groupToSets", "groupToDicts",
|
|
31
|
+
"groupFindAll", "groupFindFirst", "groupFindMaximum", "groupFindMinimum", "toDict", "toSet", "toArray",
|
|
32
|
+
"toTree", "sort", "sortInPlace", "isSorted", "findFirst", "findAll", "findMaximum", "findMinimum",
|
|
33
|
+
"findSortedFirst", "findSortedLast", "findSortedRange", "mergeAll", "unionInPlace", "mapRows", "some",
|
|
34
|
+
"every", "sum", "mean", "maximum", "minimum", "getKeys", "generate",
|
|
35
|
+
]);
|
|
36
|
+
/** Callback-taking methods whose body runs ONCE per call: a default producer, a single-key upsert
|
|
37
|
+
* or merge, a conditional, a handler. */
|
|
38
|
+
export const ONCE_METHODS = new Set([
|
|
39
|
+
"get", "getOrInsert", "insertOrUpdate", "merge", "match", "matchTag", "unwrap", "ifElse", "and", "or", "update", "if", "try",
|
|
40
|
+
]);
|
|
41
|
+
/** The `BlockBuilder` loop statements: their body runs per iteration. The other statement bodies
|
|
42
|
+
* (`if` / `else` / `elseIf` / `match` / `try` / `catch` / `finally`) run at most once. */
|
|
43
|
+
export const LOOP_METHODS = new Set(["for", "while"]);
|
|
44
|
+
function isFunctionLike(node, t) {
|
|
45
|
+
return t.isArrowFunction(node) || t.isFunctionExpression(node) || t.isFunctionDeclaration(node) || t.isMethodDeclaration(node);
|
|
46
|
+
}
|
|
47
|
+
/** The innermost function (arrow, function expression or declaration, method) enclosing `node`. */
|
|
48
|
+
function enclosingFunction(node, t) {
|
|
49
|
+
let current = node.parent;
|
|
50
|
+
while (current !== undefined && !isFunctionLike(current, t))
|
|
51
|
+
current = current.parent;
|
|
52
|
+
return current;
|
|
53
|
+
}
|
|
54
|
+
/** How often `fn` — a function handed to a call — runs per run of the code around the call:
|
|
55
|
+
* `"callback"` (per element), `"loop body"` (per iteration), `"nested function"` (per call of the
|
|
56
|
+
* function it builds), or undefined for a body that runs at most once, or one the rule cannot
|
|
57
|
+
* classify. */
|
|
58
|
+
function hostKind(fn, ctx) {
|
|
59
|
+
const t = ctx.ts;
|
|
60
|
+
const call = fn.parent;
|
|
61
|
+
if (call === undefined || !t.isCallExpression(call) || !call.arguments.some((arg) => arg === fn))
|
|
62
|
+
return undefined;
|
|
63
|
+
const callee = call.expression; // read before the guard below narrows `call` away
|
|
64
|
+
if (isEastFunctionCall(call, t))
|
|
65
|
+
return "nested function";
|
|
66
|
+
if (!t.isPropertyAccessExpression(callee))
|
|
67
|
+
return undefined;
|
|
68
|
+
const method = callee.name.text;
|
|
69
|
+
const receiver = callee.expression;
|
|
70
|
+
if (LOOP_METHODS.has(method)) {
|
|
71
|
+
return isBlockBuilderType(ctx.checker.getTypeAtLocation(receiver)) ? "loop body" : undefined;
|
|
72
|
+
}
|
|
73
|
+
if (!REPEATING_METHODS.has(method))
|
|
74
|
+
return undefined;
|
|
75
|
+
if (isEastExprType(ctx.checker.getTypeAtLocation(receiver)))
|
|
76
|
+
return "callback";
|
|
77
|
+
// the namespace generators: `East.Array.generate(n, T, ($, i) => …)` and kin
|
|
78
|
+
if (t.isPropertyAccessExpression(receiver) && t.isIdentifier(receiver.expression) && receiver.expression.text === "East") {
|
|
79
|
+
return "callback";
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
/** The outermost repeatedly-running function between the declaration's own function (exclusive)
|
|
84
|
+
* and `use` — the one that multiplies the read the most — or undefined. */
|
|
85
|
+
function repeatingHostBetween(use, declFunction, ctx) {
|
|
86
|
+
let found;
|
|
87
|
+
let current = use.parent;
|
|
88
|
+
while (current !== undefined && current !== declFunction) {
|
|
89
|
+
if (isFunctionLike(current, ctx.ts)) {
|
|
90
|
+
const kind = hostKind(current, ctx);
|
|
91
|
+
if (kind !== undefined)
|
|
92
|
+
found = kind;
|
|
93
|
+
}
|
|
94
|
+
current = current.parent;
|
|
95
|
+
}
|
|
96
|
+
return found;
|
|
97
|
+
}
|
|
19
98
|
export const noReinlinedEastBinding = {
|
|
20
99
|
name: NAME,
|
|
21
100
|
code: CODE,
|
|
22
|
-
description: "An East Expr bound to a JS const/let and
|
|
101
|
+
description: "An East Expr bound to a JS const/let and read twice inside one East body, or read inside a per-element callback, a loop body or a nested East.function, is re-inlined — and re-evaluated — at each read; bind it once with $.let/$.const.",
|
|
23
102
|
check(node, ctx) {
|
|
24
103
|
const t = ctx.ts;
|
|
25
104
|
if (!t.isVariableDeclaration(node))
|
|
@@ -46,32 +125,44 @@ export const noReinlinedEastBinding = {
|
|
|
46
125
|
if (declSymbol === undefined)
|
|
47
126
|
return;
|
|
48
127
|
const name = node.name.text;
|
|
49
|
-
const
|
|
128
|
+
const declFunction = enclosingFunction(node, t);
|
|
129
|
+
// reads per enclosing function (an exclusive arm is a function of its own)
|
|
130
|
+
const perFunction = new Map();
|
|
131
|
+
let repeating;
|
|
50
132
|
const visit = (n) => {
|
|
51
|
-
if (t.isIdentifier(n) && n !== node.name && n.text === name) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
133
|
+
if (t.isIdentifier(n) && n !== node.name && n.text === name && ctx.checker.getSymbolAtLocation(n) === declSymbol) {
|
|
134
|
+
// a read outside every East block scope re-inlines into nothing
|
|
135
|
+
if (enclosingBlockScope(n, ctx) !== undefined) {
|
|
136
|
+
const multiplied = repeatingHostBetween(n, declFunction, ctx);
|
|
137
|
+
if (multiplied !== undefined) {
|
|
138
|
+
repeating ??= multiplied;
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
const bucket = enclosingFunction(n, t) ?? ctx.sourceFile;
|
|
142
|
+
perFunction.set(bucket, (perFunction.get(bucket) ?? 0) + 1);
|
|
143
|
+
}
|
|
56
144
|
}
|
|
57
145
|
}
|
|
58
146
|
t.forEachChild(n, visit);
|
|
59
147
|
};
|
|
60
148
|
visit(ctx.sourceFile);
|
|
61
|
-
let
|
|
62
|
-
for (const count of
|
|
63
|
-
if (count >
|
|
64
|
-
|
|
65
|
-
if (
|
|
149
|
+
let worst = 0;
|
|
150
|
+
for (const count of perFunction.values())
|
|
151
|
+
if (count > worst)
|
|
152
|
+
worst = count;
|
|
153
|
+
if (worst < 2 && repeating === undefined)
|
|
66
154
|
return;
|
|
67
155
|
const sf = ctx.sourceFile;
|
|
68
156
|
const start = node.getStart(sf);
|
|
157
|
+
const messageText = worst >= 2
|
|
158
|
+
? `This East expression is bound to a JS \`const\`/\`let\` and read ${worst} times inside one East body, so it is re-inlined — and re-evaluated, with a fresh identity for mutable values — at each read. Bind it once with \`$.const\`/\`$.let\`.`
|
|
159
|
+
: `This East expression is bound to a JS \`const\`/\`let\` outside a ${repeating} and read inside it, so it is re-inlined there — and re-evaluated every time the ${repeating} runs. Bind it once with \`$.const\`/\`$.let\` before the call.`;
|
|
69
160
|
ctx.report({
|
|
70
161
|
ruleName: NAME,
|
|
71
162
|
code: CODE,
|
|
72
163
|
start,
|
|
73
164
|
length: node.getEnd() - start,
|
|
74
|
-
messageText
|
|
165
|
+
messageText,
|
|
75
166
|
category: "error",
|
|
76
167
|
});
|
|
77
168
|
},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"no-reinlined-east-binding.js","sourceRoot":"","sources":["../../../src/rules/no-reinlined-east-binding.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"no-reinlined-east-binding.js","sourceRoot":"","sources":["../../../src/rules/no-reinlined-east-binding.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@elaraai/east-diagnostics",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.91",
|
|
4
4
|
"license": "AGPL-3.0-or-later",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "East-aware diagnostic rules over the TypeScript checker — one shared engine for the editor (tsserver plugin), the agent (headless daemon), and CI.",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@types/node": "^22.18.1",
|
|
46
46
|
"typescript": "~5.9.2",
|
|
47
|
-
"@elaraai/east": "1.0.
|
|
48
|
-
"@elaraai/e3": "1.0.
|
|
47
|
+
"@elaraai/east": "1.0.91",
|
|
48
|
+
"@elaraai/e3": "1.0.91"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "tsc",
|