@llui/compiler 0.5.2 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/accessor-resolver.d.ts +9 -1
- package/dist/accessor-resolver.d.ts.map +1 -1
- package/dist/accessor-resolver.js +68 -9
- package/dist/accessor-resolver.js.map +1 -1
- package/dist/collect-deps.d.ts.map +1 -1
- package/dist/collect-deps.js +24 -0
- package/dist/collect-deps.js.map +1 -1
- package/dist/module.d.ts +12 -1
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +2 -1
- package/dist/module.js.map +1 -1
- package/dist/modules/opaque-state-flow.d.ts.map +1 -1
- package/dist/modules/opaque-state-flow.js +66 -34
- package/dist/modules/opaque-state-flow.js.map +1 -1
- package/dist/transform.d.ts +1 -1
- package/dist/transform.d.ts.map +1 -1
- package/dist/transform.js +10 -2
- package/dist/transform.js.map +1 -1
- package/package.json +1 -1
|
@@ -53,6 +53,14 @@ export declare function isMemoCallWithArrowArg(expr: ts.Expression): expr is ts.
|
|
|
53
53
|
* - `memo((s) => …)` — returns the inner arrow
|
|
54
54
|
* - `someIdentifier` resolving to any of the above (or to a hoisted
|
|
55
55
|
* `function X(s) { … }` declaration)
|
|
56
|
+
*
|
|
57
|
+
* When `checker` is supplied, identifier resolution follows alias chains
|
|
58
|
+
* across files: `import { matrixOrEmpty } from '../state'` becomes
|
|
59
|
+
* resolvable. Without a checker the resolver falls back to file-local
|
|
60
|
+
* `const`/`function` lookup. The cross-file path requires the
|
|
61
|
+
* identifier's AST node to be bound to the checker's Program — pass
|
|
62
|
+
* nodes obtained via `program.getSourceFile(...)`, not from a freshly
|
|
63
|
+
* `ts.createSourceFile`'d copy. (See AnalysisContext.program.)
|
|
56
64
|
*/
|
|
57
|
-
export declare function resolveAccessorBody(value: ts.Expression): ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null;
|
|
65
|
+
export declare function resolveAccessorBody(value: ts.Expression, checker?: ts.TypeChecker): ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null;
|
|
58
66
|
//# sourceMappingURL=accessor-resolver.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessor-resolver.d.ts","sourceRoot":"","sources":["../src/accessor-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,EAAE,CAAC,UAAU,GACjB,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,mBAAmB,GAAG,IAAI,CA8B/C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,GAAG;IACvF,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAA;CACnF,CAQA;AAED
|
|
1
|
+
{"version":3,"file":"accessor-resolver.d.ts","sourceRoot":"","sources":["../src/accessor-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,EAAE,CAAC,UAAU,GACjB,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,mBAAmB,GAAG,IAAI,CA8B/C;AAED;;;;;GAKG;AACH,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,EAAE,CAAC,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC,cAAc,GAAG;IACvF,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,EAAE,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC,CAAA;CACnF,CAQA;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,EAAE,CAAC,UAAU,EACpB,OAAO,CAAC,EAAE,EAAE,CAAC,WAAW,GACvB,EAAE,CAAC,aAAa,GAAG,EAAE,CAAC,kBAAkB,GAAG,EAAE,CAAC,mBAAmB,GAAG,IAAI,CA0B1E"}
|
|
@@ -94,24 +94,83 @@ export function isMemoCallWithArrowArg(expr) {
|
|
|
94
94
|
* - `memo((s) => …)` — returns the inner arrow
|
|
95
95
|
* - `someIdentifier` resolving to any of the above (or to a hoisted
|
|
96
96
|
* `function X(s) { … }` declaration)
|
|
97
|
+
*
|
|
98
|
+
* When `checker` is supplied, identifier resolution follows alias chains
|
|
99
|
+
* across files: `import { matrixOrEmpty } from '../state'` becomes
|
|
100
|
+
* resolvable. Without a checker the resolver falls back to file-local
|
|
101
|
+
* `const`/`function` lookup. The cross-file path requires the
|
|
102
|
+
* identifier's AST node to be bound to the checker's Program — pass
|
|
103
|
+
* nodes obtained via `program.getSourceFile(...)`, not from a freshly
|
|
104
|
+
* `ts.createSourceFile`'d copy. (See AnalysisContext.program.)
|
|
97
105
|
*/
|
|
98
|
-
export function resolveAccessorBody(value) {
|
|
106
|
+
export function resolveAccessorBody(value, checker) {
|
|
99
107
|
if (ts.isArrowFunction(value) || ts.isFunctionExpression(value))
|
|
100
108
|
return value;
|
|
101
109
|
if (isMemoCallWithArrowArg(value)) {
|
|
102
110
|
return value.arguments[0];
|
|
103
111
|
}
|
|
104
112
|
if (ts.isIdentifier(value)) {
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
113
|
+
const local = resolveLocalConstInitializer(value);
|
|
114
|
+
if (local) {
|
|
115
|
+
if (ts.isArrowFunction(local) ||
|
|
116
|
+
ts.isFunctionExpression(local) ||
|
|
117
|
+
ts.isFunctionDeclaration(local)) {
|
|
118
|
+
return local;
|
|
119
|
+
}
|
|
120
|
+
if (isMemoCallWithArrowArg(local)) {
|
|
121
|
+
return local.arguments[0];
|
|
122
|
+
}
|
|
107
123
|
return null;
|
|
108
|
-
if (ts.isArrowFunction(resolved) ||
|
|
109
|
-
ts.isFunctionExpression(resolved) ||
|
|
110
|
-
ts.isFunctionDeclaration(resolved)) {
|
|
111
|
-
return resolved;
|
|
112
124
|
}
|
|
113
|
-
if (
|
|
114
|
-
|
|
125
|
+
if (checker) {
|
|
126
|
+
const resolved = resolveCrossFileAccessor(value, checker);
|
|
127
|
+
if (resolved)
|
|
128
|
+
return resolved;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Follow the alias chain for an identifier reference through the type
|
|
135
|
+
* checker, then inspect the resolved symbol's declarations for an arrow
|
|
136
|
+
* accessor we can mask-analyze. This is the same descent the cross-file
|
|
137
|
+
* walker does for view-helper classification (`cross-file-walker.ts`),
|
|
138
|
+
* applied here so a same-package import like
|
|
139
|
+
* `import { matrixOrEmpty } from '../state'`
|
|
140
|
+
* `value: (s) => matrixOrEmpty(s).field`
|
|
141
|
+
* doesn't trip the opaque-flow leak diagnostic — the walker descends
|
|
142
|
+
* into `matrixOrEmpty`'s body and the call is tracked.
|
|
143
|
+
*
|
|
144
|
+
* Returns null for ambient declarations, type-only imports, parameters,
|
|
145
|
+
* destructured bindings, and re-exports the checker can't pin to a
|
|
146
|
+
* function-like declaration — every shape the opaque-flow rule is
|
|
147
|
+
* documented to flag as a leak.
|
|
148
|
+
*/
|
|
149
|
+
function resolveCrossFileAccessor(use, checker) {
|
|
150
|
+
let sym = checker.getSymbolAtLocation(use);
|
|
151
|
+
if (!sym)
|
|
152
|
+
return null;
|
|
153
|
+
if (sym.flags & ts.SymbolFlags.Alias) {
|
|
154
|
+
try {
|
|
155
|
+
sym = checker.getAliasedSymbol(sym);
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
const decls = sym.getDeclarations();
|
|
162
|
+
if (!decls || decls.length === 0)
|
|
163
|
+
return null;
|
|
164
|
+
for (const decl of decls) {
|
|
165
|
+
if (ts.isFunctionDeclaration(decl) && decl.body)
|
|
166
|
+
return decl;
|
|
167
|
+
if (ts.isVariableDeclaration(decl) && decl.initializer) {
|
|
168
|
+
const init = decl.initializer;
|
|
169
|
+
if (ts.isArrowFunction(init) || ts.isFunctionExpression(init))
|
|
170
|
+
return init;
|
|
171
|
+
if (isMemoCallWithArrowArg(init)) {
|
|
172
|
+
return init.arguments[0];
|
|
173
|
+
}
|
|
115
174
|
}
|
|
116
175
|
}
|
|
117
176
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"accessor-resolver.js","sourceRoot":"","sources":["../src/accessor-resolver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;GAmBG;AAEH;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,4BAA4B,CAC1C,GAAkB;IAElB,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,CAAA;IACrB,IAAI,IAAI,GAAY,GAAG,CAAA;IACvB,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC;QACnB,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,CAAA;QAC1B,IAAI,UAAU,GAAmC,IAAI,CAAA;QACrD,IAAI,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9E,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;QAChC,CAAC;aAAM,IAAI,EAAE,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,eAAe,CAAC,MAAM,CAAC,EAAE,CAAC;YACjE,UAAU,GAAG,MAAM,CAAC,UAAU,CAAA;QAChC,CAAC;QACD,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;gBAC9B,IAAI,EAAE,CAAC,qBAAqB,CAAC,IAAI,CAAC,EAAE,CAAC;oBACnC,IAAI,IAAI,CAAC,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;wBAAE,OAAO,IAAI,CAAA;oBACrD,SAAQ;gBACV,CAAC;gBACD,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,IAAI,CAAC;oBAAE,SAAQ;gBAC3C,MAAM,KAAK,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,CAAA;gBACxC,IAAI,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC;oBAAE,SAAQ;gBAC3C,IAAI,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,MAAM,KAAK,CAAC;oBAAE,SAAQ;gBAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,eAAe,CAAC,YAAY,CAAC,CAAC,CAAE,CAAA;gBAClD,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,KAAK,IAAI;oBAAE,SAAQ;gBACpE,IAAI,CAAC,IAAI,CAAC,WAAW;oBAAE,SAAQ;gBAC/B,OAAO,IAAI,CAAC,WAAW,CAAA;YACzB,CAAC;QACH,CAAC;QACD,IAAI,GAAG,MAAM,CAAA;IACf,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CAAC,IAAmB;IAGxD,OAAO,CACL,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC;QACzB,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,UAAU,CAAC;QAChC,IAAI,CAAC,UAAU,CAAC,IAAI,KAAK,MAAM;QAC/B,IAAI,CAAC,SAAS,CAAC,MAAM,IAAI,CAAC;QAC1B,CAAC,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,IAAI,EAAE,CAAC,oBAAoB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC,CACxF,CAAA;AACH,CAAC;AAED;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAoB;IAEpB,IAAI,EAAE,CAAC,eAAe,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7E,IAAI,sBAAsB,CAAC,KAAK,CAAC,EAAE,CAAC;QAClC,OAAO,KAAK,CAAC,SAAS,CAAC,CAAC,CAA6C,CAAA;IACvE,CAAC;IACD,IAAI,EAAE,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QAC3B,MAAM,QAAQ,GAAG,4BAA4B,CAAC,KAAK,CAAC,CAAA;QACpD,IAAI,CAAC,QAAQ;YAAE,OAAO,IAAI,CAAA;QAC1B,IACE,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC;YAC5B,EAAE,CAAC,oBAAoB,CAAC,QAAQ,CAAC;YACjC,EAAE,CAAC,qBAAqB,CAAC,QAAQ,CAAC,EAClC,CAAC;YACD,OAAO,QAAQ,CAAA;QACjB,CAAC;QACD,IAAI,sBAAsB,CAAC,QAAQ,CAAC,EAAE,CAAC;YACrC,OAAO,QAAQ,CAAC,SAAS,CAAC,CAAC,CAA6C,CAAA;QAC1E,CAAC;IACH,CAAC;IACD,OAAO,IAAI,CAAA;AACb,CAAC","sourcesContent":["import ts from 'typescript'\n\n/**\n * Helpers for resolving identifier references at reactive-accessor\n * positions. Shared by `transform.ts` (compile-time prop classification\n * + Pass 2 mask injection) and `collect-deps.ts` (state-path scanning\n * for `__dirty` / `__maskLegend`).\n *\n * The compiler must distinguish the legitimate accessor shapes:\n *\n * - Inline arrow / function expression at the call site\n * - Inline `memo(arrow)` at the call site\n * - Identifier referencing a const-bound arrow / function expression\n * - Identifier referencing a hoisted function declaration\n * - Identifier referencing `const x = memo(arrow)`\n *\n * …from values we can't classify (imports, parameters, opaque calls), so\n * those can be bailed-to-runtime instead of silently miscompiled. See the\n * `disabled` binding bug, where a function reference at a reactive prop\n * position was statically assigned (`__e.disabled = isGated`) — writing\n * the function object onto the boolean DOM property and never re-evaluating.\n */\n\n/**\n * Walk parent chains to find a `const X = ...` declaration matching\n * `use.text`, or a hoisted `function X(...)` declaration. Returns the\n * resolved declaration or `null` for unresolvable references (imports,\n * parameters, this-bindings, etc.).\n *\n * Limitations:\n * - Only `const`. `let` resolution is unsafe — we can't track later\n * reassignments without a type checker.\n * - Only single-binding declarations (`const a = …`, not `const a = …, b = …`).\n * - The declaration must dominate the use (lexical scope).\n */\nexport function resolveLocalConstInitializer(\n use: ts.Identifier,\n): ts.Expression | ts.FunctionDeclaration | null {\n const name = use.text\n let node: ts.Node = use\n while (node.parent) {\n const parent = node.parent\n let statements: readonly ts.Statement[] | null = null\n if (ts.isBlock(parent) || ts.isSourceFile(parent) || ts.isModuleBlock(parent)) {\n statements = parent.statements\n } else if (ts.isCaseClause(parent) || ts.isDefaultClause(parent)) {\n statements = parent.statements\n }\n if (statements) {\n for (const stmt of statements) {\n if (ts.isFunctionDeclaration(stmt)) {\n if (stmt.name && stmt.name.text === name) return stmt\n continue\n }\n if (!ts.isVariableStatement(stmt)) continue\n const flags = stmt.declarationList.flags\n if (!(flags & ts.NodeFlags.Const)) continue\n if (stmt.declarationList.declarations.length !== 1) continue\n const decl = stmt.declarationList.declarations[0]!\n if (!ts.isIdentifier(decl.name) || decl.name.text !== name) continue\n if (!decl.initializer) continue\n return decl.initializer\n }\n }\n node = parent\n }\n return null\n}\n\n/**\n * Recognize `memo(arrow)` / `memo(fn)` calls so the inner accessor can\n * be analyzed for state-path masking. The runtime `memo()` returns a\n * cached accessor — its body's reads determine when it re-evaluates,\n * not the call site.\n */\nexport function isMemoCallWithArrowArg(expr: ts.Expression): expr is ts.CallExpression & {\n arguments: readonly [ts.ArrowFunction | ts.FunctionExpression, ...ts.Expression[]]\n} {\n return (\n ts.isCallExpression(expr) &&\n ts.isIdentifier(expr.expression) &&\n expr.expression.text === 'memo' &&\n expr.arguments.length >= 1 &&\n (ts.isArrowFunction(expr.arguments[0]!) || ts.isFunctionExpression(expr.arguments[0]!))\n )\n}\n\n/**\n * Resolve a value at a reactive-accessor position down to the callable\n * AST node we can mask-analyze. Returns `null` when the value isn't a\n * recognized accessor shape — caller leaves the call unchanged (runtime\n * falls back to FULL_MASK, which is correct just slower).\n *\n * Recognized shapes:\n * - `(s) => …` (ArrowFunction)\n * - `function (s) { … }` (FunctionExpression)\n * - `memo((s) => …)` — returns the inner arrow\n * - `someIdentifier` resolving to any of the above (or to a hoisted\n * `function X(s) { … }` declaration)\n */\nexport function resolveAccessorBody(\n value: ts.Expression,\n): ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null {\n if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) return value\n if (isMemoCallWithArrowArg(value)) {\n return value.arguments[0] as ts.ArrowFunction | ts.FunctionExpression\n }\n if (ts.isIdentifier(value)) {\n const resolved = resolveLocalConstInitializer(value)\n if (!resolved) return null\n if (\n ts.isArrowFunction(resolved) ||\n ts.isFunctionExpression(resolved) ||\n ts.isFunctionDeclaration(resolved)\n ) {\n return resolved\n }\n if (isMemoCallWithArrowArg(resolved)) {\n return resolved.arguments[0] as ts.ArrowFunction | ts.FunctionExpression\n }\n }\n return null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"accessor-resolver.js","sourceRoot":"","sources":["../src/accessor-resolver.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript'\n\n/**\n * Helpers for resolving identifier references at reactive-accessor\n * positions. Shared by `transform.ts` (compile-time prop classification\n * + Pass 2 mask injection) and `collect-deps.ts` (state-path scanning\n * for `__dirty` / `__maskLegend`).\n *\n * The compiler must distinguish the legitimate accessor shapes:\n *\n * - Inline arrow / function expression at the call site\n * - Inline `memo(arrow)` at the call site\n * - Identifier referencing a const-bound arrow / function expression\n * - Identifier referencing a hoisted function declaration\n * - Identifier referencing `const x = memo(arrow)`\n *\n * …from values we can't classify (imports, parameters, opaque calls), so\n * those can be bailed-to-runtime instead of silently miscompiled. See the\n * `disabled` binding bug, where a function reference at a reactive prop\n * position was statically assigned (`__e.disabled = isGated`) — writing\n * the function object onto the boolean DOM property and never re-evaluating.\n */\n\n/**\n * Walk parent chains to find a `const X = ...` declaration matching\n * `use.text`, or a hoisted `function X(...)` declaration. Returns the\n * resolved declaration or `null` for unresolvable references (imports,\n * parameters, this-bindings, etc.).\n *\n * Limitations:\n * - Only `const`. `let` resolution is unsafe — we can't track later\n * reassignments without a type checker.\n * - Only single-binding declarations (`const a = …`, not `const a = …, b = …`).\n * - The declaration must dominate the use (lexical scope).\n */\nexport function resolveLocalConstInitializer(\n use: ts.Identifier,\n): ts.Expression | ts.FunctionDeclaration | null {\n const name = use.text\n let node: ts.Node = use\n while (node.parent) {\n const parent = node.parent\n let statements: readonly ts.Statement[] | null = null\n if (ts.isBlock(parent) || ts.isSourceFile(parent) || ts.isModuleBlock(parent)) {\n statements = parent.statements\n } else if (ts.isCaseClause(parent) || ts.isDefaultClause(parent)) {\n statements = parent.statements\n }\n if (statements) {\n for (const stmt of statements) {\n if (ts.isFunctionDeclaration(stmt)) {\n if (stmt.name && stmt.name.text === name) return stmt\n continue\n }\n if (!ts.isVariableStatement(stmt)) continue\n const flags = stmt.declarationList.flags\n if (!(flags & ts.NodeFlags.Const)) continue\n if (stmt.declarationList.declarations.length !== 1) continue\n const decl = stmt.declarationList.declarations[0]!\n if (!ts.isIdentifier(decl.name) || decl.name.text !== name) continue\n if (!decl.initializer) continue\n return decl.initializer\n }\n }\n node = parent\n }\n return null\n}\n\n/**\n * Recognize `memo(arrow)` / `memo(fn)` calls so the inner accessor can\n * be analyzed for state-path masking. The runtime `memo()` returns a\n * cached accessor — its body's reads determine when it re-evaluates,\n * not the call site.\n */\nexport function isMemoCallWithArrowArg(expr: ts.Expression): expr is ts.CallExpression & {\n arguments: readonly [ts.ArrowFunction | ts.FunctionExpression, ...ts.Expression[]]\n} {\n return (\n ts.isCallExpression(expr) &&\n ts.isIdentifier(expr.expression) &&\n expr.expression.text === 'memo' &&\n expr.arguments.length >= 1 &&\n (ts.isArrowFunction(expr.arguments[0]!) || ts.isFunctionExpression(expr.arguments[0]!))\n )\n}\n\n/**\n * Resolve a value at a reactive-accessor position down to the callable\n * AST node we can mask-analyze. Returns `null` when the value isn't a\n * recognized accessor shape — caller leaves the call unchanged (runtime\n * falls back to FULL_MASK, which is correct just slower).\n *\n * Recognized shapes:\n * - `(s) => …` (ArrowFunction)\n * - `function (s) { … }` (FunctionExpression)\n * - `memo((s) => …)` — returns the inner arrow\n * - `someIdentifier` resolving to any of the above (or to a hoisted\n * `function X(s) { … }` declaration)\n *\n * When `checker` is supplied, identifier resolution follows alias chains\n * across files: `import { matrixOrEmpty } from '../state'` becomes\n * resolvable. Without a checker the resolver falls back to file-local\n * `const`/`function` lookup. The cross-file path requires the\n * identifier's AST node to be bound to the checker's Program — pass\n * nodes obtained via `program.getSourceFile(...)`, not from a freshly\n * `ts.createSourceFile`'d copy. (See AnalysisContext.program.)\n */\nexport function resolveAccessorBody(\n value: ts.Expression,\n checker?: ts.TypeChecker,\n): ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null {\n if (ts.isArrowFunction(value) || ts.isFunctionExpression(value)) return value\n if (isMemoCallWithArrowArg(value)) {\n return value.arguments[0] as ts.ArrowFunction | ts.FunctionExpression\n }\n if (ts.isIdentifier(value)) {\n const local = resolveLocalConstInitializer(value)\n if (local) {\n if (\n ts.isArrowFunction(local) ||\n ts.isFunctionExpression(local) ||\n ts.isFunctionDeclaration(local)\n ) {\n return local\n }\n if (isMemoCallWithArrowArg(local)) {\n return local.arguments[0] as ts.ArrowFunction | ts.FunctionExpression\n }\n return null\n }\n if (checker) {\n const resolved = resolveCrossFileAccessor(value, checker)\n if (resolved) return resolved\n }\n }\n return null\n}\n\n/**\n * Follow the alias chain for an identifier reference through the type\n * checker, then inspect the resolved symbol's declarations for an arrow\n * accessor we can mask-analyze. This is the same descent the cross-file\n * walker does for view-helper classification (`cross-file-walker.ts`),\n * applied here so a same-package import like\n * `import { matrixOrEmpty } from '../state'`\n * `value: (s) => matrixOrEmpty(s).field`\n * doesn't trip the opaque-flow leak diagnostic — the walker descends\n * into `matrixOrEmpty`'s body and the call is tracked.\n *\n * Returns null for ambient declarations, type-only imports, parameters,\n * destructured bindings, and re-exports the checker can't pin to a\n * function-like declaration — every shape the opaque-flow rule is\n * documented to flag as a leak.\n */\nfunction resolveCrossFileAccessor(\n use: ts.Identifier,\n checker: ts.TypeChecker,\n): ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration | null {\n let sym = checker.getSymbolAtLocation(use)\n if (!sym) return null\n if (sym.flags & ts.SymbolFlags.Alias) {\n try {\n sym = checker.getAliasedSymbol(sym)\n } catch {\n return null\n }\n }\n const decls = sym.getDeclarations()\n if (!decls || decls.length === 0) return null\n for (const decl of decls) {\n if (ts.isFunctionDeclaration(decl) && decl.body) return decl\n if (ts.isVariableDeclaration(decl) && decl.initializer) {\n const init = decl.initializer\n if (ts.isArrowFunction(init) || ts.isFunctionExpression(init)) return init\n if (isMemoCallWithArrowArg(init)) {\n return init.arguments[0] as ts.ArrowFunction | ts.FunctionExpression\n }\n }\n }\n return null\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect-deps.d.ts","sourceRoot":"","sources":["../src/collect-deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AA0L3B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG;IACtE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;CAChB,CAyBA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAwBhF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC/B;IACD,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,MAAM,EAAE,OAAO,CAAA;CAChB,CAsCA;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,
|
|
1
|
+
{"version":3,"file":"collect-deps.d.ts","sourceRoot":"","sources":["../src/collect-deps.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,YAAY,CAAA;AA0L3B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG;IACtE,KAAK,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IAClB,MAAM,EAAE,OAAO,CAAA;CAChB,CAyBA;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAwBhF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CACzB,MAAM,EAAE,MAAM,EACd,UAAU,CAAC,EAAE,WAAW,CAAC,MAAM,CAAC,GAC/B;IACD,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,EAAE,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvB,MAAM,EAAE,OAAO,CAAA;CAChB,CAsCA;AAeD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,GAAG,OAAO,CAuFzD"}
|
package/dist/collect-deps.js
CHANGED
|
@@ -350,6 +350,30 @@ export function isReactiveAccessor(node) {
|
|
|
350
350
|
// Skip each() key function and other non-reactive props
|
|
351
351
|
if (key.text === 'key' || key.text === 'name')
|
|
352
352
|
return false;
|
|
353
|
+
// Skip view-builder slots: `default` / `render` / `fallback` on the
|
|
354
|
+
// structural primitives. Their callbacks receive a View<S, M> bag,
|
|
355
|
+
// not state — e.g. `branch({ default: (h) => h.text(...) })`. The
|
|
356
|
+
// single param is `h`, not `s`; treating it as a reactive accessor
|
|
357
|
+
// makes the opaque-flow walker chase `h` references as if they
|
|
358
|
+
// were state. The runtime knows these slots are view builders;
|
|
359
|
+
// the compiler did not, until now.
|
|
360
|
+
if (key.text === 'default' || key.text === 'render' || key.text === 'fallback') {
|
|
361
|
+
return false;
|
|
362
|
+
}
|
|
363
|
+
// Skip `cases.<k>` — the nested-object form of branch() cases.
|
|
364
|
+
// Each value is `(h: View<S, M>) => Node[]`, same as `default`.
|
|
365
|
+
// Identified by the enclosing object literal sitting in a
|
|
366
|
+
// `cases:` property assignment.
|
|
367
|
+
const enclosingObjLit = parent.parent;
|
|
368
|
+
if (enclosingObjLit && ts.isObjectLiteralExpression(enclosingObjLit)) {
|
|
369
|
+
const outerPA = enclosingObjLit.parent;
|
|
370
|
+
if (outerPA &&
|
|
371
|
+
ts.isPropertyAssignment(outerPA) &&
|
|
372
|
+
ts.isIdentifier(outerPA.name) &&
|
|
373
|
+
outerPA.name.text === 'cases') {
|
|
374
|
+
return false;
|
|
375
|
+
}
|
|
376
|
+
}
|
|
353
377
|
// Walk up to find the enclosing call expression
|
|
354
378
|
let ancestor = parent.parent; // ObjectLiteralExpression
|
|
355
379
|
while (ancestor && !ts.isCallExpression(ancestor)) {
|
package/dist/collect-deps.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"collect-deps.js","sourceRoot":"","sources":["../src/collect-deps.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript'\nimport { resolveAccessorBody } from './accessor-resolver.js'\n\n/**\n * Names whose first arg is itself a reactive accessor (the existing\n * arrow walker handles them) or which are explicitly excluded\n * (sample/item read state imperatively / per-row, not as state\n * accessors). When a delegating accessor's body contains a call to one\n * of these, we don't follow it — recursion is reserved for \"this is\n * just a thin wrapper that hands the state to another local helper.\"\n */\nconst NON_DELEGATION_HELPERS = new Set(['sample', 'item', 'memo', 'text', 'unsafeHtml'])\n\n/**\n * Walk a delegating accessor's body looking for calls to OTHER local\n * functions that take the state param verbatim — `helper(s)` where\n * `s` matches the outer accessor's param name. For each, hand the\n * resolved declaration back so the caller can recurse into its body.\n *\n * Skips:\n * - Framework helpers (`memo`, `text`, etc.) — their arrow args are\n * visited by the top-level arrow walker; we'd double-count.\n * - Method calls (`s.items.filter(...)`) — the callee is a builtin,\n * not a local function we can resolve.\n * - Nested function bodies — params inside a `(item) => …` shadow\n * ours, so a `helper(s)` deep in there isn't (necessarily)\n * handing OUR state in. Conservative: don't recurse through\n * lambda boundaries.\n */\nfunction visitTopLevelDelegations(\n body: ts.Node,\n stateParamName: string,\n follow: (resolved: ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration) => void,\n onUnresolved?: () => void,\n): void {\n function visit(node: ts.Node): void {\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {\n const name = node.expression.text\n if (!NON_DELEGATION_HELPERS.has(name)) {\n const arg0 = node.arguments[0]\n if (arg0 && ts.isIdentifier(arg0) && arg0.text === stateParamName) {\n const resolved = resolveAccessorBody(node.expression)\n if (resolved) follow(resolved)\n else if (onUnresolved) onUnresolved()\n }\n }\n }\n // Don't descend into nested function bodies — their params shadow\n // ours, and any call inside them isn't unambiguously delegating\n // our state.\n if (\n ts.isArrowFunction(node) ||\n ts.isFunctionExpression(node) ||\n ts.isFunctionDeclaration(node)\n ) {\n return\n }\n ts.forEachChild(node, visit)\n }\n // If the body itself is a function, there's nothing at the top\n // level to inspect — its own body is a separate scope.\n if (ts.isArrowFunction(body) || ts.isFunctionExpression(body) || ts.isFunctionDeclaration(body)) {\n return\n }\n visit(body)\n}\n\n/**\n * Extract paths from a callable accessor (arrow / fn-expr / fn-decl)\n * into the given set. Recurses through call-delegations to other local\n * helpers so that `(s) => filtered(s)` / `(s) => { void s.x; return\n * inner(s) }` correctly contribute the helper's state-path reads.\n * Without recursion the precise mask under-counts — fields read only\n * via the helper drop off the bitmask, and any sibling reactive\n * accessor that reads them produces a non-zero `dirty` that AND'd with\n * the narrow each.__mask is zero, silently skipping the reconcile.\n *\n * `visited` breaks cycles on mutually-recursive helpers — terminates\n * the walk; doesn't try to be precise about what such helpers read.\n */\nfunction extractAccessorPaths(\n accessor: ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration,\n paths: Set<string>,\n visited: Set<ts.Node> = new Set(),\n opaqueOut?: { value: boolean },\n): boolean {\n if (visited.has(accessor)) return false\n visited.add(accessor)\n\n const params = accessor.parameters\n if (params.length !== 1) return false\n const paramName = params[0]!.name\n if (!ts.isIdentifier(paramName)) {\n // Destructured/anonymous param — the path walker can't follow\n // reads through it. Conservative: mark the accessor as opaque so\n // the synthesis pipeline emits a whole-state sentinel.\n if (opaqueOut) opaqueOut.value = true\n return false\n }\n if (!accessor.body) return false\n const before = paths.size\n\n extractPaths(accessor.body, paramName.text, '', paths)\n\n // Detect opaque state flow alongside path extraction. Mirrors the\n // classifier in `transform.ts`'s `computeAccessorMask` (Identifier\n // `s` used in a non-tracked position) — any leak means a precise\n // `__prefixes` table is insufficient because a field read only\n // through the leak never enters fieldBits and the runtime can't\n // dirty it on change.\n if (opaqueOut) detectOpaqueStateFlow(accessor.body, paramName.text, opaqueOut)\n\n // Follow delegations: `(s) => helper(s)` — extract `helper`'s body's\n // state paths too. Reuses the `visited` set across the recursion\n // chain so cycles terminate. When the callee is unresolvable\n // (function parameter, import, destructured), the same logic that\n // forces FULL_MASK in `computeAccessorMask` flags the file as\n // opaque here, so the sentinel gets emitted.\n visitTopLevelDelegations(\n accessor.body,\n paramName.text,\n (resolved) => {\n extractAccessorPaths(resolved, paths, visited, opaqueOut)\n },\n () => {\n if (opaqueOut) opaqueOut.value = true\n },\n )\n\n return paths.size > before\n}\n\n/**\n * Mirror of the classifier in `computeAccessorMask` (transform.ts). An\n * accessor \"leaks state\" — and so demands the conservative\n * FULL_MASK / whole-state sentinel — when the state identifier `s`\n * appears in any position OTHER than:\n * - the param binding itself\n * - the root of `s.x.y…` (PropertyAccessExpression)\n * - the root of `s['literal']` / `s[0]` (ElementAccess with literal key)\n * - arg0 of `helper(s)` with an Identifier callee (handled by the\n * delegation visitor — resolvable → recursion, unresolvable →\n * marks opaque via the callback)\n *\n * Every other context (NewExpression arg, TaggedTemplate span, spread,\n * const-alias, conditional branch, method-call arg, dynamic key\n * `s[expr]`, return-the-whole-state, …) is treated as a leak.\n */\nfunction detectOpaqueStateFlow(body: ts.Node, stateParam: string, out: { value: boolean }): void {\n function visit(node: ts.Node): void {\n if (out.value) return\n if (ts.isIdentifier(node) && node.text === stateParam) {\n const parent = node.parent\n const isBinding = !!parent && ts.isParameter(parent)\n if (!isBinding) {\n let isTracked = false\n if (parent) {\n if (ts.isPropertyAccessExpression(parent) && parent.expression === node) {\n isTracked = true\n } else if (ts.isElementAccessExpression(parent) && parent.expression === node) {\n isTracked =\n ts.isStringLiteralLike(parent.argumentExpression) ||\n ts.isNumericLiteral(parent.argumentExpression)\n } else if (\n ts.isCallExpression(parent) &&\n ts.isIdentifier(parent.expression) &&\n parent.arguments[0] === node &&\n !NON_DELEGATION_HELPERS.has(parent.expression.text)\n ) {\n // The delegation visitor either recurses into the resolved\n // body (transitively detecting opaque inside) or flags\n // opaque via its second callback for unresolvable callees.\n isTracked = true\n }\n }\n if (!isTracked) {\n out.value = true\n return\n }\n }\n }\n ts.forEachChild(node, visit)\n }\n visit(body)\n}\n\n/**\n * Walk the AST and collect every unique state access path referenced by\n * a reactive accessor. A reactive accessor is one of:\n *\n * - An inline arrow / function expression at a reactive position\n * (`text(s => s.count)`, `div({ title: s => s.title })`,\n * `show({ when: s => s.gated })`, etc.).\n * - An Identifier at a reactive position that resolves to a callable\n * in this file — a const-bound arrow / function expression,\n * a hoisted function declaration, or `const x = memo(arrow)`.\n *\n * The second case lets authors refactor a literal arrow into a named\n * helper without losing the reactive-mask optimization (a precise mask\n * for `__dirty` and structural-primitive `__mask`). Without it, the\n * runtime falls back to FULL_MASK — correct, but every binding fires\n * on every state change.\n *\n * Shared by the bit-assignment path (`collectDeps`, below) and the\n * `diagnostics.ts` bitmask-overflow warning.\n */\nexport function collectStatePathsFromSource(sourceFile: ts.SourceFile): {\n paths: Set<string>\n opaque: boolean\n} {\n const paths = new Set<string>()\n const opaqueOut = { value: false }\n\n function visit(node: ts.Node): void {\n // Inline arrow / function expression at a reactive position.\n if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {\n if (isReactiveAccessor(node)) extractAccessorPaths(node, paths, undefined, opaqueOut)\n }\n\n // Identifier at a reactive position — resolve to its declaration\n // and extract paths from the resolved body. Identifiers that\n // resolve elsewhere (imports, etc.) leave a binding the walker\n // can't see — treat the host file as opaque so the sentinel fires.\n if (ts.isIdentifier(node) && isReactiveAccessor(node)) {\n const resolved = resolveAccessorBody(node)\n if (resolved) extractAccessorPaths(resolved, paths, undefined, opaqueOut)\n else opaqueOut.value = true\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(sourceFile)\n return { paths, opaque: opaqueOut.value }\n}\n\n/**\n * Per-accessor path sets — one entry per reactive arrow/function. Used\n * by the bitmask-overflow diagnostic to find clusters of paths that\n * always fire together (co-occurrence analysis).\n */\nexport function collectAccessorPathSets(sourceFile: ts.SourceFile): Set<string>[] {\n const sets: Set<string>[] = []\n\n function visit(node: ts.Node): void {\n if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {\n if (isReactiveAccessor(node)) {\n const set = new Set<string>()\n if (extractAccessorPaths(node, set)) sets.push(set)\n }\n }\n\n if (ts.isIdentifier(node) && isReactiveAccessor(node)) {\n const resolved = resolveAccessorBody(node)\n if (resolved) {\n const set = new Set<string>()\n if (extractAccessorPaths(resolved, set)) sets.push(set)\n }\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(sourceFile)\n return sets\n}\n\n/**\n * Pre-scan a source file to collect all unique state access paths\n * referenced by reactive accessors (arrow functions in props and text() calls).\n *\n * Returns a pair of maps:\n * - `lo`: paths at bit positions 0..30, with value `1 << position`\n * - `hi`: paths at bit positions 31..61, with value `1 << (position - 31)`\n *\n * Bit positions past 61 collapse to `-1` (FULL_MASK) in the `lo` map and\n * cause every binding reading them to re-evaluate on every cycle. The\n * `bitmask-overflow` lint rule warns the user to restructure state.\n *\n * Components with ≤31 paths see an empty `hi` map; the compiler skips\n * all high-word emit so the generated code is byte-identical to the\n * pre-multi-word baseline.\n */\nexport function collectDeps(\n source: string,\n extraPaths?: ReadonlySet<string>,\n): {\n lo: Map<string, number>\n hi: Map<string, number>\n opaque: boolean\n} {\n const sourceFile = ts.createSourceFile('input.ts', source, ts.ScriptTarget.Latest, true)\n\n // Check if file imports from @llui/dom\n if (!hasLluiImport(sourceFile)) {\n return { lo: new Map(), hi: new Map(), opaque: false }\n }\n\n const { paths, opaque } = collectStatePathsFromSource(sourceFile)\n // Cross-file extension (v2c pipeline integration): the host adapter may\n // pass paths discovered by `crossFileAccessorPaths()` — paths read\n // through in-repo view-helpers in *other* files. Union them with the\n // file-local set before bit assignment. Without this merge the\n // sentinel-`show()` workaround from v2b §1 remains necessary; with\n // it, helpers in other files contribute to the consumer's __prefixes\n // table automatically.\n if (extraPaths) {\n for (const p of extraPaths) paths.add(p)\n }\n\n const lo = new Map<string, number>()\n const hi = new Map<string, number>()\n let index = 0\n for (const path of paths) {\n if (index < 31) {\n lo.set(path, 1 << index)\n } else if (index < 62) {\n hi.set(path, 1 << (index - 31))\n } else {\n // Past 61 paths — graceful FULL_MASK fallback in the low word.\n // Realistic LLui components shouldn't hit this; the lint rule\n // fires well before.\n lo.set(path, -1)\n }\n index++\n }\n\n return { lo, hi, opaque }\n}\n\nfunction hasLluiImport(sourceFile: ts.SourceFile): boolean {\n for (const stmt of sourceFile.statements) {\n if (\n ts.isImportDeclaration(stmt) &&\n ts.isStringLiteral(stmt.moduleSpecifier) &&\n stmt.moduleSpecifier.text === '@llui/dom'\n ) {\n return true\n }\n }\n return false\n}\n\n/**\n * Determines if a node is at a reactive-accessor position — either an\n * inline arrow / function expression OR an identifier that's about to\n * be resolved to one. The check is identity-based on `parent.arguments[0]`\n * etc., so the same logic works for both shapes.\n *\n * Exported so the cross-file walker can use the same gate. Without this\n * gate the walker descends into every 1-param arrow in the file —\n * including `onEffect: (bag) => bag.send(...)` — and pollutes\n * `__prefixes` with non-state property names (issue #5, bug 3).\n */\nexport function isReactiveAccessor(node: ts.Node): boolean {\n const parent = node.parent\n\n // text(s => s.count) — first arg to a call\n if (ts.isCallExpression(parent) && parent.arguments[0] === node) {\n // Skip item(t => t.id) — per-item selectors inside each() render.\n // Skip sample(s => s.x) — imperative one-shot read, no binding created\n // (both the top-level import and the destructured-from-h form).\n if (ts.isIdentifier(parent.expression)) {\n if (parent.expression.text === 'item' || parent.expression.text === 'sample') {\n return false\n }\n }\n // Skip array method callbacks: .filter(t => ...), .map(t => ...), .some(t => ...), etc.\n // Allow view-helper primitive calls: h.text(s => ...), h.memo(s => ...)\n if (ts.isPropertyAccessExpression(parent.expression)) {\n const methodName = parent.expression.name.text\n if (methodName === 'text' || methodName === 'memo') {\n return true\n }\n return false\n }\n return true\n }\n\n // div({ title: s => s.title }) — value in a property assignment inside an object literal.\n // Only treat as reactive if the containing call is a known framework API whose\n // properties are reactive accessors. Otherwise user-land helpers like\n // sliceHandler({ narrow: (m) => m.type === ... }) would pollute the path set.\n if (ts.isPropertyAssignment(parent)) {\n const key = parent.name\n if (ts.isIdentifier(key)) {\n // Skip event handlers (onClick, onInput, etc.)\n if (/^on[A-Z]/.test(key.text)) return false\n // Skip each() key function and other non-reactive props\n if (key.text === 'key' || key.text === 'name') return false\n // Walk up to find the enclosing call expression\n let ancestor: ts.Node | undefined = parent.parent // ObjectLiteralExpression\n while (ancestor && !ts.isCallExpression(ancestor)) {\n ancestor = ancestor.parent\n }\n if (!ancestor) return false\n const callExpr = ancestor as ts.CallExpression\n // Bare identifier: `scope({on: …})`, `div({title: …})`, etc.\n if (ts.isIdentifier(callExpr.expression)) {\n return REACTIVE_API_NAMES.has(callExpr.expression.text)\n }\n // Method-call form: `h.scope({on: …})`, `h.show({when: …})`, etc.\n // The docs and View bag promote this shape; without recognizing it\n // here, paths read ONLY through a structural primitive's\n // `on`/`when`/`items` accessor never enter `__prefixes`, so the\n // runtime dirty mask can't see changes to those fields and the\n // structural block silently fails to reconcile.\n if (ts.isPropertyAccessExpression(callExpr.expression)) {\n return REACTIVE_API_NAMES.has(callExpr.expression.name.text)\n }\n return false\n }\n }\n\n return false\n}\n\n// Framework APIs whose object-literal arguments contain reactive accessors.\n// Arrow functions in property values of these calls are state-tracked.\nconst REACTIVE_API_NAMES = new Set([\n // Element helpers (see ELEMENT_HELPERS in transform.ts — we keep a superset here)\n ...[\n 'a',\n 'abbr',\n 'article',\n 'aside',\n 'b',\n 'blockquote',\n 'br',\n 'button',\n 'canvas',\n 'code',\n 'dd',\n 'details',\n 'dialog',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'hr',\n 'i',\n 'iframe',\n 'img',\n 'input',\n 'label',\n 'legend',\n 'li',\n 'main',\n 'mark',\n 'nav',\n 'ol',\n 'optgroup',\n 'option',\n 'output',\n 'p',\n 'pre',\n 'progress',\n 'section',\n 'select',\n 'small',\n 'span',\n 'strong',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'textarea',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'ul',\n 'video',\n ],\n // Structural primitives\n 'each',\n 'branch',\n 'scope',\n 'show',\n 'memo',\n 'portal',\n 'foreign',\n 'child',\n 'errorBoundary',\n // track({ deps: (s) => [...] }) — explicit reactivity declaration for\n // paths static analysis can't infer. The compiler treats `deps` as a\n // reactive accessor so its paths fold into the host component's\n // __prefixes; the call expression is then stripped from emission\n // (see transform.ts). v2b §3.\n 'track',\n])\n\n/**\n * Extract state access paths from an expression body.\n * Handles:\n * - Direct property access: param.field, param.field.subfield\n * - Bracket notation with string literal: param['field']\n */\nfunction extractPaths(node: ts.Node, paramName: string, _prefix: string, paths: Set<string>): void {\n if (ts.isPropertyAccessExpression(node)) {\n // Skip if this is an intermediate in a deeper chain\n if (ts.isPropertyAccessExpression(node.parent)) {\n // handled when the leaf is visited\n }\n // Skip if this is the callee of a method call: s.todos.filter(...)\n else if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n // It's a method call — record the object, not the method\n // e.g. s.todos.filter(...) → record 'todos', not 'todos.filter'\n if (ts.isPropertyAccessExpression(node.expression)) {\n const chain = resolvePropertyChain(node.expression, paramName)\n if (chain) paths.add(chain)\n }\n } else {\n const chain = resolvePropertyChain(node, paramName)\n if (chain) {\n paths.add(chain)\n }\n }\n }\n\n if (ts.isElementAccessExpression(node)) {\n const chain = resolveElementAccess(node, paramName)\n if (chain) {\n paths.add(chain)\n }\n }\n\n ts.forEachChild(node, (child) => extractPaths(child, paramName, _prefix, paths))\n}\n\n/**\n * Resolve a property access chain like s.user.name to \"user.name\".\n * Returns null if the chain doesn't start with the state parameter.\n * Stops at depth 2.\n */\nfunction resolvePropertyChain(node: ts.PropertyAccessExpression, paramName: string): string | null {\n const parts: string[] = []\n let current: ts.Expression = node\n\n while (ts.isPropertyAccessExpression(current)) {\n parts.unshift(current.name.text)\n current = current.expression\n }\n\n // The root must be the state parameter\n if (!ts.isIdentifier(current) || current.text !== paramName) {\n return null\n }\n\n // Limit to depth 2\n if (parts.length > 2) {\n return parts.slice(0, 2).join('.')\n }\n\n return parts.join('.')\n}\n\n/**\n * Resolve bracket access with string literal: s['count'] → \"count\"\n */\nfunction resolveElementAccess(node: ts.ElementAccessExpression, paramName: string): string | null {\n if (!ts.isIdentifier(node.expression) || node.expression.text !== paramName) {\n return null\n }\n\n if (ts.isStringLiteral(node.argumentExpression)) {\n return node.argumentExpression.text\n }\n\n return null\n}\n"]}
|
|
1
|
+
{"version":3,"file":"collect-deps.js","sourceRoot":"","sources":["../src/collect-deps.ts"],"names":[],"mappings":"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","sourcesContent":["import ts from 'typescript'\nimport { resolveAccessorBody } from './accessor-resolver.js'\n\n/**\n * Names whose first arg is itself a reactive accessor (the existing\n * arrow walker handles them) or which are explicitly excluded\n * (sample/item read state imperatively / per-row, not as state\n * accessors). When a delegating accessor's body contains a call to one\n * of these, we don't follow it — recursion is reserved for \"this is\n * just a thin wrapper that hands the state to another local helper.\"\n */\nconst NON_DELEGATION_HELPERS = new Set(['sample', 'item', 'memo', 'text', 'unsafeHtml'])\n\n/**\n * Walk a delegating accessor's body looking for calls to OTHER local\n * functions that take the state param verbatim — `helper(s)` where\n * `s` matches the outer accessor's param name. For each, hand the\n * resolved declaration back so the caller can recurse into its body.\n *\n * Skips:\n * - Framework helpers (`memo`, `text`, etc.) — their arrow args are\n * visited by the top-level arrow walker; we'd double-count.\n * - Method calls (`s.items.filter(...)`) — the callee is a builtin,\n * not a local function we can resolve.\n * - Nested function bodies — params inside a `(item) => …` shadow\n * ours, so a `helper(s)` deep in there isn't (necessarily)\n * handing OUR state in. Conservative: don't recurse through\n * lambda boundaries.\n */\nfunction visitTopLevelDelegations(\n body: ts.Node,\n stateParamName: string,\n follow: (resolved: ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration) => void,\n onUnresolved?: () => void,\n): void {\n function visit(node: ts.Node): void {\n if (ts.isCallExpression(node) && ts.isIdentifier(node.expression)) {\n const name = node.expression.text\n if (!NON_DELEGATION_HELPERS.has(name)) {\n const arg0 = node.arguments[0]\n if (arg0 && ts.isIdentifier(arg0) && arg0.text === stateParamName) {\n const resolved = resolveAccessorBody(node.expression)\n if (resolved) follow(resolved)\n else if (onUnresolved) onUnresolved()\n }\n }\n }\n // Don't descend into nested function bodies — their params shadow\n // ours, and any call inside them isn't unambiguously delegating\n // our state.\n if (\n ts.isArrowFunction(node) ||\n ts.isFunctionExpression(node) ||\n ts.isFunctionDeclaration(node)\n ) {\n return\n }\n ts.forEachChild(node, visit)\n }\n // If the body itself is a function, there's nothing at the top\n // level to inspect — its own body is a separate scope.\n if (ts.isArrowFunction(body) || ts.isFunctionExpression(body) || ts.isFunctionDeclaration(body)) {\n return\n }\n visit(body)\n}\n\n/**\n * Extract paths from a callable accessor (arrow / fn-expr / fn-decl)\n * into the given set. Recurses through call-delegations to other local\n * helpers so that `(s) => filtered(s)` / `(s) => { void s.x; return\n * inner(s) }` correctly contribute the helper's state-path reads.\n * Without recursion the precise mask under-counts — fields read only\n * via the helper drop off the bitmask, and any sibling reactive\n * accessor that reads them produces a non-zero `dirty` that AND'd with\n * the narrow each.__mask is zero, silently skipping the reconcile.\n *\n * `visited` breaks cycles on mutually-recursive helpers — terminates\n * the walk; doesn't try to be precise about what such helpers read.\n */\nfunction extractAccessorPaths(\n accessor: ts.ArrowFunction | ts.FunctionExpression | ts.FunctionDeclaration,\n paths: Set<string>,\n visited: Set<ts.Node> = new Set(),\n opaqueOut?: { value: boolean },\n): boolean {\n if (visited.has(accessor)) return false\n visited.add(accessor)\n\n const params = accessor.parameters\n if (params.length !== 1) return false\n const paramName = params[0]!.name\n if (!ts.isIdentifier(paramName)) {\n // Destructured/anonymous param — the path walker can't follow\n // reads through it. Conservative: mark the accessor as opaque so\n // the synthesis pipeline emits a whole-state sentinel.\n if (opaqueOut) opaqueOut.value = true\n return false\n }\n if (!accessor.body) return false\n const before = paths.size\n\n extractPaths(accessor.body, paramName.text, '', paths)\n\n // Detect opaque state flow alongside path extraction. Mirrors the\n // classifier in `transform.ts`'s `computeAccessorMask` (Identifier\n // `s` used in a non-tracked position) — any leak means a precise\n // `__prefixes` table is insufficient because a field read only\n // through the leak never enters fieldBits and the runtime can't\n // dirty it on change.\n if (opaqueOut) detectOpaqueStateFlow(accessor.body, paramName.text, opaqueOut)\n\n // Follow delegations: `(s) => helper(s)` — extract `helper`'s body's\n // state paths too. Reuses the `visited` set across the recursion\n // chain so cycles terminate. When the callee is unresolvable\n // (function parameter, import, destructured), the same logic that\n // forces FULL_MASK in `computeAccessorMask` flags the file as\n // opaque here, so the sentinel gets emitted.\n visitTopLevelDelegations(\n accessor.body,\n paramName.text,\n (resolved) => {\n extractAccessorPaths(resolved, paths, visited, opaqueOut)\n },\n () => {\n if (opaqueOut) opaqueOut.value = true\n },\n )\n\n return paths.size > before\n}\n\n/**\n * Mirror of the classifier in `computeAccessorMask` (transform.ts). An\n * accessor \"leaks state\" — and so demands the conservative\n * FULL_MASK / whole-state sentinel — when the state identifier `s`\n * appears in any position OTHER than:\n * - the param binding itself\n * - the root of `s.x.y…` (PropertyAccessExpression)\n * - the root of `s['literal']` / `s[0]` (ElementAccess with literal key)\n * - arg0 of `helper(s)` with an Identifier callee (handled by the\n * delegation visitor — resolvable → recursion, unresolvable →\n * marks opaque via the callback)\n *\n * Every other context (NewExpression arg, TaggedTemplate span, spread,\n * const-alias, conditional branch, method-call arg, dynamic key\n * `s[expr]`, return-the-whole-state, …) is treated as a leak.\n */\nfunction detectOpaqueStateFlow(body: ts.Node, stateParam: string, out: { value: boolean }): void {\n function visit(node: ts.Node): void {\n if (out.value) return\n if (ts.isIdentifier(node) && node.text === stateParam) {\n const parent = node.parent\n const isBinding = !!parent && ts.isParameter(parent)\n if (!isBinding) {\n let isTracked = false\n if (parent) {\n if (ts.isPropertyAccessExpression(parent) && parent.expression === node) {\n isTracked = true\n } else if (ts.isElementAccessExpression(parent) && parent.expression === node) {\n isTracked =\n ts.isStringLiteralLike(parent.argumentExpression) ||\n ts.isNumericLiteral(parent.argumentExpression)\n } else if (\n ts.isCallExpression(parent) &&\n ts.isIdentifier(parent.expression) &&\n parent.arguments[0] === node &&\n !NON_DELEGATION_HELPERS.has(parent.expression.text)\n ) {\n // The delegation visitor either recurses into the resolved\n // body (transitively detecting opaque inside) or flags\n // opaque via its second callback for unresolvable callees.\n isTracked = true\n }\n }\n if (!isTracked) {\n out.value = true\n return\n }\n }\n }\n ts.forEachChild(node, visit)\n }\n visit(body)\n}\n\n/**\n * Walk the AST and collect every unique state access path referenced by\n * a reactive accessor. A reactive accessor is one of:\n *\n * - An inline arrow / function expression at a reactive position\n * (`text(s => s.count)`, `div({ title: s => s.title })`,\n * `show({ when: s => s.gated })`, etc.).\n * - An Identifier at a reactive position that resolves to a callable\n * in this file — a const-bound arrow / function expression,\n * a hoisted function declaration, or `const x = memo(arrow)`.\n *\n * The second case lets authors refactor a literal arrow into a named\n * helper without losing the reactive-mask optimization (a precise mask\n * for `__dirty` and structural-primitive `__mask`). Without it, the\n * runtime falls back to FULL_MASK — correct, but every binding fires\n * on every state change.\n *\n * Shared by the bit-assignment path (`collectDeps`, below) and the\n * `diagnostics.ts` bitmask-overflow warning.\n */\nexport function collectStatePathsFromSource(sourceFile: ts.SourceFile): {\n paths: Set<string>\n opaque: boolean\n} {\n const paths = new Set<string>()\n const opaqueOut = { value: false }\n\n function visit(node: ts.Node): void {\n // Inline arrow / function expression at a reactive position.\n if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {\n if (isReactiveAccessor(node)) extractAccessorPaths(node, paths, undefined, opaqueOut)\n }\n\n // Identifier at a reactive position — resolve to its declaration\n // and extract paths from the resolved body. Identifiers that\n // resolve elsewhere (imports, etc.) leave a binding the walker\n // can't see — treat the host file as opaque so the sentinel fires.\n if (ts.isIdentifier(node) && isReactiveAccessor(node)) {\n const resolved = resolveAccessorBody(node)\n if (resolved) extractAccessorPaths(resolved, paths, undefined, opaqueOut)\n else opaqueOut.value = true\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(sourceFile)\n return { paths, opaque: opaqueOut.value }\n}\n\n/**\n * Per-accessor path sets — one entry per reactive arrow/function. Used\n * by the bitmask-overflow diagnostic to find clusters of paths that\n * always fire together (co-occurrence analysis).\n */\nexport function collectAccessorPathSets(sourceFile: ts.SourceFile): Set<string>[] {\n const sets: Set<string>[] = []\n\n function visit(node: ts.Node): void {\n if (ts.isArrowFunction(node) || ts.isFunctionExpression(node)) {\n if (isReactiveAccessor(node)) {\n const set = new Set<string>()\n if (extractAccessorPaths(node, set)) sets.push(set)\n }\n }\n\n if (ts.isIdentifier(node) && isReactiveAccessor(node)) {\n const resolved = resolveAccessorBody(node)\n if (resolved) {\n const set = new Set<string>()\n if (extractAccessorPaths(resolved, set)) sets.push(set)\n }\n }\n\n ts.forEachChild(node, visit)\n }\n\n visit(sourceFile)\n return sets\n}\n\n/**\n * Pre-scan a source file to collect all unique state access paths\n * referenced by reactive accessors (arrow functions in props and text() calls).\n *\n * Returns a pair of maps:\n * - `lo`: paths at bit positions 0..30, with value `1 << position`\n * - `hi`: paths at bit positions 31..61, with value `1 << (position - 31)`\n *\n * Bit positions past 61 collapse to `-1` (FULL_MASK) in the `lo` map and\n * cause every binding reading them to re-evaluate on every cycle. The\n * `bitmask-overflow` lint rule warns the user to restructure state.\n *\n * Components with ≤31 paths see an empty `hi` map; the compiler skips\n * all high-word emit so the generated code is byte-identical to the\n * pre-multi-word baseline.\n */\nexport function collectDeps(\n source: string,\n extraPaths?: ReadonlySet<string>,\n): {\n lo: Map<string, number>\n hi: Map<string, number>\n opaque: boolean\n} {\n const sourceFile = ts.createSourceFile('input.ts', source, ts.ScriptTarget.Latest, true)\n\n // Check if file imports from @llui/dom\n if (!hasLluiImport(sourceFile)) {\n return { lo: new Map(), hi: new Map(), opaque: false }\n }\n\n const { paths, opaque } = collectStatePathsFromSource(sourceFile)\n // Cross-file extension (v2c pipeline integration): the host adapter may\n // pass paths discovered by `crossFileAccessorPaths()` — paths read\n // through in-repo view-helpers in *other* files. Union them with the\n // file-local set before bit assignment. Without this merge the\n // sentinel-`show()` workaround from v2b §1 remains necessary; with\n // it, helpers in other files contribute to the consumer's __prefixes\n // table automatically.\n if (extraPaths) {\n for (const p of extraPaths) paths.add(p)\n }\n\n const lo = new Map<string, number>()\n const hi = new Map<string, number>()\n let index = 0\n for (const path of paths) {\n if (index < 31) {\n lo.set(path, 1 << index)\n } else if (index < 62) {\n hi.set(path, 1 << (index - 31))\n } else {\n // Past 61 paths — graceful FULL_MASK fallback in the low word.\n // Realistic LLui components shouldn't hit this; the lint rule\n // fires well before.\n lo.set(path, -1)\n }\n index++\n }\n\n return { lo, hi, opaque }\n}\n\nfunction hasLluiImport(sourceFile: ts.SourceFile): boolean {\n for (const stmt of sourceFile.statements) {\n if (\n ts.isImportDeclaration(stmt) &&\n ts.isStringLiteral(stmt.moduleSpecifier) &&\n stmt.moduleSpecifier.text === '@llui/dom'\n ) {\n return true\n }\n }\n return false\n}\n\n/**\n * Determines if a node is at a reactive-accessor position — either an\n * inline arrow / function expression OR an identifier that's about to\n * be resolved to one. The check is identity-based on `parent.arguments[0]`\n * etc., so the same logic works for both shapes.\n *\n * Exported so the cross-file walker can use the same gate. Without this\n * gate the walker descends into every 1-param arrow in the file —\n * including `onEffect: (bag) => bag.send(...)` — and pollutes\n * `__prefixes` with non-state property names (issue #5, bug 3).\n */\nexport function isReactiveAccessor(node: ts.Node): boolean {\n const parent = node.parent\n\n // text(s => s.count) — first arg to a call\n if (ts.isCallExpression(parent) && parent.arguments[0] === node) {\n // Skip item(t => t.id) — per-item selectors inside each() render.\n // Skip sample(s => s.x) — imperative one-shot read, no binding created\n // (both the top-level import and the destructured-from-h form).\n if (ts.isIdentifier(parent.expression)) {\n if (parent.expression.text === 'item' || parent.expression.text === 'sample') {\n return false\n }\n }\n // Skip array method callbacks: .filter(t => ...), .map(t => ...), .some(t => ...), etc.\n // Allow view-helper primitive calls: h.text(s => ...), h.memo(s => ...)\n if (ts.isPropertyAccessExpression(parent.expression)) {\n const methodName = parent.expression.name.text\n if (methodName === 'text' || methodName === 'memo') {\n return true\n }\n return false\n }\n return true\n }\n\n // div({ title: s => s.title }) — value in a property assignment inside an object literal.\n // Only treat as reactive if the containing call is a known framework API whose\n // properties are reactive accessors. Otherwise user-land helpers like\n // sliceHandler({ narrow: (m) => m.type === ... }) would pollute the path set.\n if (ts.isPropertyAssignment(parent)) {\n const key = parent.name\n if (ts.isIdentifier(key)) {\n // Skip event handlers (onClick, onInput, etc.)\n if (/^on[A-Z]/.test(key.text)) return false\n // Skip each() key function and other non-reactive props\n if (key.text === 'key' || key.text === 'name') return false\n // Skip view-builder slots: `default` / `render` / `fallback` on the\n // structural primitives. Their callbacks receive a View<S, M> bag,\n // not state — e.g. `branch({ default: (h) => h.text(...) })`. The\n // single param is `h`, not `s`; treating it as a reactive accessor\n // makes the opaque-flow walker chase `h` references as if they\n // were state. The runtime knows these slots are view builders;\n // the compiler did not, until now.\n if (key.text === 'default' || key.text === 'render' || key.text === 'fallback') {\n return false\n }\n // Skip `cases.<k>` — the nested-object form of branch() cases.\n // Each value is `(h: View<S, M>) => Node[]`, same as `default`.\n // Identified by the enclosing object literal sitting in a\n // `cases:` property assignment.\n const enclosingObjLit = parent.parent\n if (enclosingObjLit && ts.isObjectLiteralExpression(enclosingObjLit)) {\n const outerPA = enclosingObjLit.parent\n if (\n outerPA &&\n ts.isPropertyAssignment(outerPA) &&\n ts.isIdentifier(outerPA.name) &&\n outerPA.name.text === 'cases'\n ) {\n return false\n }\n }\n // Walk up to find the enclosing call expression\n let ancestor: ts.Node | undefined = parent.parent // ObjectLiteralExpression\n while (ancestor && !ts.isCallExpression(ancestor)) {\n ancestor = ancestor.parent\n }\n if (!ancestor) return false\n const callExpr = ancestor as ts.CallExpression\n // Bare identifier: `scope({on: …})`, `div({title: …})`, etc.\n if (ts.isIdentifier(callExpr.expression)) {\n return REACTIVE_API_NAMES.has(callExpr.expression.text)\n }\n // Method-call form: `h.scope({on: …})`, `h.show({when: …})`, etc.\n // The docs and View bag promote this shape; without recognizing it\n // here, paths read ONLY through a structural primitive's\n // `on`/`when`/`items` accessor never enter `__prefixes`, so the\n // runtime dirty mask can't see changes to those fields and the\n // structural block silently fails to reconcile.\n if (ts.isPropertyAccessExpression(callExpr.expression)) {\n return REACTIVE_API_NAMES.has(callExpr.expression.name.text)\n }\n return false\n }\n }\n\n return false\n}\n\n// Framework APIs whose object-literal arguments contain reactive accessors.\n// Arrow functions in property values of these calls are state-tracked.\nconst REACTIVE_API_NAMES = new Set([\n // Element helpers (see ELEMENT_HELPERS in transform.ts — we keep a superset here)\n ...[\n 'a',\n 'abbr',\n 'article',\n 'aside',\n 'b',\n 'blockquote',\n 'br',\n 'button',\n 'canvas',\n 'code',\n 'dd',\n 'details',\n 'dialog',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'header',\n 'hr',\n 'i',\n 'iframe',\n 'img',\n 'input',\n 'label',\n 'legend',\n 'li',\n 'main',\n 'mark',\n 'nav',\n 'ol',\n 'optgroup',\n 'option',\n 'output',\n 'p',\n 'pre',\n 'progress',\n 'section',\n 'select',\n 'small',\n 'span',\n 'strong',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'textarea',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'tr',\n 'ul',\n 'video',\n ],\n // Structural primitives\n 'each',\n 'branch',\n 'scope',\n 'show',\n 'memo',\n 'portal',\n 'foreign',\n 'child',\n 'errorBoundary',\n // track({ deps: (s) => [...] }) — explicit reactivity declaration for\n // paths static analysis can't infer. The compiler treats `deps` as a\n // reactive accessor so its paths fold into the host component's\n // __prefixes; the call expression is then stripped from emission\n // (see transform.ts). v2b §3.\n 'track',\n])\n\n/**\n * Extract state access paths from an expression body.\n * Handles:\n * - Direct property access: param.field, param.field.subfield\n * - Bracket notation with string literal: param['field']\n */\nfunction extractPaths(node: ts.Node, paramName: string, _prefix: string, paths: Set<string>): void {\n if (ts.isPropertyAccessExpression(node)) {\n // Skip if this is an intermediate in a deeper chain\n if (ts.isPropertyAccessExpression(node.parent)) {\n // handled when the leaf is visited\n }\n // Skip if this is the callee of a method call: s.todos.filter(...)\n else if (ts.isCallExpression(node.parent) && node.parent.expression === node) {\n // It's a method call — record the object, not the method\n // e.g. s.todos.filter(...) → record 'todos', not 'todos.filter'\n if (ts.isPropertyAccessExpression(node.expression)) {\n const chain = resolvePropertyChain(node.expression, paramName)\n if (chain) paths.add(chain)\n }\n } else {\n const chain = resolvePropertyChain(node, paramName)\n if (chain) {\n paths.add(chain)\n }\n }\n }\n\n if (ts.isElementAccessExpression(node)) {\n const chain = resolveElementAccess(node, paramName)\n if (chain) {\n paths.add(chain)\n }\n }\n\n ts.forEachChild(node, (child) => extractPaths(child, paramName, _prefix, paths))\n}\n\n/**\n * Resolve a property access chain like s.user.name to \"user.name\".\n * Returns null if the chain doesn't start with the state parameter.\n * Stops at depth 2.\n */\nfunction resolvePropertyChain(node: ts.PropertyAccessExpression, paramName: string): string | null {\n const parts: string[] = []\n let current: ts.Expression = node\n\n while (ts.isPropertyAccessExpression(current)) {\n parts.unshift(current.name.text)\n current = current.expression\n }\n\n // The root must be the state parameter\n if (!ts.isIdentifier(current) || current.text !== paramName) {\n return null\n }\n\n // Limit to depth 2\n if (parts.length > 2) {\n return parts.slice(0, 2).join('.')\n }\n\n return parts.join('.')\n}\n\n/**\n * Resolve bracket access with string literal: s['count'] → \"count\"\n */\nfunction resolveElementAccess(node: ts.ElementAccessExpression, paramName: string): string | null {\n if (!ts.isIdentifier(node.expression) || node.expression.text !== paramName) {\n return null\n }\n\n if (ts.isStringLiteral(node.argumentExpression)) {\n return node.argumentExpression.text\n }\n\n return null\n}\n"]}
|
package/dist/module.d.ts
CHANGED
|
@@ -56,6 +56,17 @@ export interface AnalysisContext {
|
|
|
56
56
|
sourceFile: ts.SourceFile;
|
|
57
57
|
/** TS TypeChecker, when the host adapter has built a Program. May be undefined for AST-only paths. */
|
|
58
58
|
checker: ts.TypeChecker | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* The cross-file Program the checker is bound to, when available.
|
|
61
|
+
* Modules that need to resolve identifiers across files (e.g. the
|
|
62
|
+
* opaque-state-flow lint walking through imported helpers) must walk
|
|
63
|
+
* Program-bound nodes — the file the registry hands them is a
|
|
64
|
+
* locally-reparsed copy and its identifiers won't resolve through the
|
|
65
|
+
* checker. Use `program.getSourceFile(sourceFile.fileName)` to fetch
|
|
66
|
+
* the Program-bound counterpart. Undefined when the host doesn't
|
|
67
|
+
* supply a Program (test path, lint adapters without cross-file).
|
|
68
|
+
*/
|
|
69
|
+
program: ts.Program | undefined;
|
|
59
70
|
/**
|
|
60
71
|
* Get the named module's accumulator slot (creating it lazily). The
|
|
61
72
|
* slot is whatever shape the module wrote; type-safe access is the
|
|
@@ -245,7 +256,7 @@ export declare class ModuleRegistry {
|
|
|
245
256
|
* 4. Emission: each module's `emit?` fires; the registry merges
|
|
246
257
|
* contributions, detecting (field, target) conflicts.
|
|
247
258
|
*/
|
|
248
|
-
run(sourceFile: ts.SourceFile, checker?: ts.TypeChecker, externalTypes?: ModuleExternalTypes): RegistryRunResult;
|
|
259
|
+
run(sourceFile: ts.SourceFile, checker?: ts.TypeChecker, externalTypes?: ModuleExternalTypes, program?: ts.Program): RegistryRunResult;
|
|
249
260
|
/** Module names in declaration order. Adapters surface this for debug logs / config diagnostics. */
|
|
250
261
|
listModules(): string[];
|
|
251
262
|
/** All diagnostic definitions across active modules. Used by adapters to enumerate stable IDs. */
|
package/dist/module.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"module.d.ts","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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"}
|
package/dist/module.js
CHANGED
|
@@ -79,7 +79,7 @@ export class ModuleRegistry {
|
|
|
79
79
|
* 4. Emission: each module's `emit?` fires; the registry merges
|
|
80
80
|
* contributions, detecting (field, target) conflicts.
|
|
81
81
|
*/
|
|
82
|
-
run(sourceFile, checker, externalTypes) {
|
|
82
|
+
run(sourceFile, checker, externalTypes, program) {
|
|
83
83
|
const analysis = {
|
|
84
84
|
sourceFile,
|
|
85
85
|
perModule: new Map(),
|
|
@@ -101,6 +101,7 @@ export class ModuleRegistry {
|
|
|
101
101
|
const ctx = {
|
|
102
102
|
sourceFile: currentSf,
|
|
103
103
|
checker,
|
|
104
|
+
program,
|
|
104
105
|
getSlot: (name, init) => {
|
|
105
106
|
let slot = analysis.perModule.get(name);
|
|
106
107
|
if (slot === undefined) {
|
package/dist/module.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,EAAE;AACF,oEAAoE;AACpE,wEAAwE;AACxE,yEAAyE;AACzE,EAAE;AACF,sEAAsE;AACtE,2EAA2E;AAC3E,uEAAuE;AACvE,wCAAwC;AACxC,EAAE;AACF,iCAAiC;AACjC,uEAAuE;AACvE,4DAA4D;AAC5D,uEAAuE;AACvE,sEAAsE;AACtE,sEAAsE;AACtE,yBAAyB;AACzB,qEAAqE;AACrE,sEAAsE;AACtE,qBAAqB;AACrB,sEAAsE;AACtE,iEAAiE;AACjE,cAAc;AAEd,OAAO,EAAE,MAAM,YAAY,CAAA;AA+N3B;;;;GAIG;AACH,MAAM,OAAO,cAAc;IACR,OAAO,CAA+B;IACvD,mDAAmD;IAClC,cAAc,CAA2C;IAE1E,YAAY,OAAsC;QAChD,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,kBAAkB,EAAE,CAAA;QACzB,IAAI,CAAC,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAA;IAChD,CAAC;IAEO,kBAAkB;QACxB,MAAM,OAAO,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAA;QACxD,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,SAAS,IAAI,EAAE,EAAE,CAAC;gBACpC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC;oBACtB,MAAM,IAAI,KAAK,CACb,kBAAkB,CAAC,CAAC,IAAI,iBAAiB,GAAG,6CAA6C;wBACvF,OAAO,GAAG,gEAAgE,CAAC,CAAC,IAAI,MAAM;wBACtF,6CAA6C,CAChD,CAAA;gBACH,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAEO,iBAAiB;QACvB,MAAM,KAAK,GAAG,IAAI,GAAG,EAAwC,CAAA;QAC7D,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC9C,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAkB,CAAA;gBAC7C,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC;oBAAE,KAAK,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;gBACzC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC1B,CAAC;QACH,CAAC;QACD,OAAO,KAAK,CAAA;IACd,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACH,GAAG,CACD,UAAyB,EACzB,OAAwB,EACxB,aAAmC;QAEnC,MAAM,QAAQ,GAAiB;YAC7B,UAAU;YACV,SAAS,EAAE,IAAI,GAAG,EAAE;YACpB,WAAW,EAAE,EAAE;SAChB,CAAA;QAED,mEAAmE;QACnE,mEAAmE;QACnE,+BAA+B;QAC/B,IAAI,SAAS,GAAG,UAAU,CAAA;QAC1B,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,CAAC,YAAY;gBAAE,SAAQ;YAC7B,SAAS,GAAG,CAAC,CAAC,YAAY,CACxB;gBACE,OAAO,EAAE,EAAE,CAAC,OAAO;gBACnB,QAAQ;aACT,EACD,SAAS,CACV,CAAA;QACH,CAAC;QACD,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAA;QAC/B,MAAM,GAAG,GAAoB;YAC3B,UAAU,EAAE,SAAS;YACrB,OAAO;YACP,OAAO,EAAE,CAAI,IAAY,EAAE,IAAa,EAAK,EAAE;gBAC7C,IAAI,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAkB,CAAA;gBACxD,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;oBACvB,IAAI,GAAG,IAAI,EAAE,CAAA;oBACb,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;gBACpC,CAAC;gBACD,OAAO,IAAI,CAAA;YACb,CAAC;YACD,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE;gBACtB,QAAQ,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YAC9B,CAAC;YACD,aAAa;SACd,CAAA;QAED,uDAAuD;QACvD,+BAA+B;QAC/B,MAAM,IAAI,GAAG,CAAC,IAAa,EAAQ,EAAE;YACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;YACnD,IAAI,QAAQ,EAAE,CAAC;gBACb,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;oBACzB,MAAM,OAAO,GAAG,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;oBACrC,IAAI,OAAO;wBAAE,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;gBACjC,CAAC;YACH,CAAC;YACD,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,IAAI,CAAC,CAAA;QAC7B,CAAC,CAAA;QACD,IAAI,CAAC,SAAS,CAAC,CAAA;QAEf,yDAAyD;QACzD,iEAAiE;QACjE,+DAA+D;QAC/D,8DAA8D;QAC9D,6DAA6D;QAC7D,yCAAyC;QACzC,EAAE;QACF,wCAAwC;QACxC,+DAA+D;QAC/D,yDAAyD;QACzD,wDAAwD;QACxD,sEAAsE;QACtE,gCAAgC;QAChC,8DAA8D;QAC9D,mEAAmE;QACnE,uBAAuB;QACvB,EAAE;QACF,mEAAmE;QACnE,oDAAoD;QACpD,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,kBAAkB,CAAC,CAAA;QACrE,MAAM,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAA;QAC/D,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACtD,MAAM,YAAY,GAAyB;gBACzC,OAAO,EAAE,EAAE,CAAC,OAAO;gBACnB,QAAQ;aACT,CAAA;YACD,MAAM,KAAK,GAAe,CAAC,IAAI,EAAE,EAAE;gBACjC,4DAA4D;gBAC5D,0DAA0D;gBAC1D,8BAA8B;gBAC9B,IAAI,OAAO,GAAG,IAAI,CAAA;gBAClB,IAAI,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;oBACjC,KAAK,MAAM,CAAC,IAAI,YAAY,EAAE,CAAC;wBAC7B,MAAM,QAAQ,GAAG,CAAC,CAAC,kBAAmB,CAAC,YAAY,EAAE,OAA4B,CAAC,CAAA;wBAClF,IAAI,QAAQ;4BAAE,OAAO,GAAG,QAAQ,CAAA;oBAClC,CAAC;gBACH,CAAC;gBACD,2DAA2D;gBAC3D,MAAM,OAAO,GAAG,EAAE,CAAC,cAAc,CAAC,OAAO,EAAE,KAAK,EAAE,SAAU,CAAC,CAAA;gBAC7D,2DAA2D;gBAC3D,yDAAyD;gBACzD,8BAA8B;gBAC9B,IAAI,EAAE,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC;oBACjC,IAAI,MAAM,GAAG,OAA4B,CAAA;oBACzC,KAAK,MAAM,CAAC,IAAI,WAAW,EAAE,CAAC;wBAC5B,MAAM,QAAQ,GAAG,CAAC,CAAC,aAAc,CAAC,YAAY,EAAE,MAAM,CAAC,CAAA;wBACvD,IAAI,QAAQ;4BAAE,MAAM,GAAG,QAAQ,CAAA;oBACjC,CAAC;oBACD,OAAO,MAAM,CAAA;gBACf,CAAC;gBACD,OAAO,OAAO,CAAA;YAChB,CAAC,CAAA;YACD,SAAS,GAAG,EAAE,CAAC,SAAS,CAAC,SAAS,EAAE,KAAK,CAAkB,CAAA;YAC3D,QAAQ,CAAC,UAAU,GAAG,SAAS,CAAA;QACjC,CAAC;QAED,4DAA4D;QAC5D,iEAAiE;QACjE,YAAY;QACZ,MAAM,WAAW,GAAoB;YACnC,UAAU,EAAE,SAAS;YACrB,OAAO,EAAE,EAAE,CAAC,OAAO;SACpB,CAAA;QACD,MAAM,SAAS,GAA2B,EAAE,CAAA;QAC5C,kEAAkE;QAClE,kEAAkE;QAClE,gEAAgE;QAChE,gEAAgE;QAChE,qCAAqC;QACrC,EAAE;QACF,kEAAkE;QAClE,mEAAmE;QACnE,6DAA6D;QAC7D,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAAkB,CAAA;QACpD,MAAM,kBAAkB,GAAG,IAAI,GAAG,EAA0C,CAAA;QAC5E,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,IAAI,CAAC,CAAC,CAAC,IAAI;gBAAE,SAAQ;YACrB,MAAM,aAAa,GAAG,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;YACnD,KAAK,MAAM,CAAC,IAAI,aAAa,EAAE,CAAC;gBAC9B,IAAI,MAA2B,CAAA;gBAC/B,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC;oBACb,IAAI,QAAQ,GAAG,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAA;oBAC/C,IAAI,CAAC,QAAQ,EAAE,CAAC;wBACd,QAAQ,GAAG,IAAI,GAAG,EAAE,CAAA;wBACpB,kBAAkB,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;oBAC5C,CAAC;oBACD,MAAM,GAAG,QAAQ,CAAA;gBACnB,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,kBAAkB,CAAA;gBAC7B,CAAC;gBACD,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,CAAC,CAAA;gBACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;oBACxB,MAAM,IAAI,KAAK,CACb,4CAA4C,KAAK,UAAU,CAAC,CAAC,MAAM,SAAS;wBAC1E,qCAAqC,CAAC,CAAC,KAAK,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,qCAAqC,CAAC,CAAC,CAAC,EAAE,IAAI;wBACzG,gFAAgF;wBAChF,4EAA4E;wBAC5E,yCAAyC,CAC5C,CAAA;gBACH,CAAC;gBACD,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,KAAK,EAAE,CAAC,CAAC,MAAM,CAAC,CAAA;gBAC7B,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;YACnB,CAAC;QACH,CAAC;QAED,yBAAyB;QACzB,MAAM,cAAc,GAAG,IAAI,GAAG,EAAU,CAAA;QACxC,KAAK,MAAM,CAAC,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YAC7B,KAAK,MAAM,GAAG,IAAI,CAAC,CAAC,cAAc,IAAI,EAAE;gBAAE,cAAc,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACnE,CAAC;QAED,OAAO;YACL,QAAQ;YACR,SAAS;YACT,cAAc,EAAE,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,EAAE;SAC3C,CAAA;IACH,CAAC;IAED,oGAAoG;IACpG,WAAW;QACT,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;IACxC,CAAC;IAED,kGAAkG;IAClG,eAAe;QACb,OAAO,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,CAAC,CAAA;IACnD,CAAC;CACF","sourcesContent":["// CompilerModule + ModuleRegistry — v2c §2 visitor-registry primitive.\n//\n// Modules accumulate findings during a single AST walk per file and\n// contribute emissions after the walk completes. The walker visits each\n// node once; every module registered for that node's SyntaxKind sees it.\n//\n// This file defines the interfaces + the registry. The actual modules\n// (`compiler-core`, `compiler-agent`, `compiler-ssr`, `compiler-devtools`)\n// will consume them; for v2c-partial only the primitive lands, and one\n// proof-of-concept module exercises it.\n//\n// Design contract (v2c.md §2.1):\n// - Modules NEVER walk the AST themselves — only the registry walks.\n// This keeps the cost O(nodes), not O(modules × nodes).\n// - Visitor order for a given SyntaxKind is the declaration order in\n// `llui.config.ts`'s `modules: [...]` array. Observable to module\n// authors. Alphabetical-by-name was rejected (couples correctness\n// to package names).\n// - Emission conflicts (two modules writing to the same field) are\n// a hard error, not a silent overwrite. Each module owns disjoint\n// output fields.\n// - `runtimeImports` arrays merge by union (deduplicated). Multiple\n// modules requesting the same runtime helper collapse to one\n// import.\n\nimport ts from 'typescript'\nimport type { Diagnostic } from './diagnostic.js'\n\n// ── Module interface ────────────────────────────────────────────────\n\nexport interface DiagnosticDefinition {\n /** Stable id, e.g. `llui/opaque-view-call`. Per v2c §3 §8.2. */\n id: string\n /** One-line description; useful for adapter UIs that don't render the message. */\n description: string\n}\n\n/**\n * Per-file analysis output. Modules accumulate findings here during\n * visitor dispatch; emit consumes it. The shape is intentionally\n * open-ended — modules name their own slots and the umbrella's\n * orchestrator never inspects them, only forwards.\n */\nexport interface FileAnalysis {\n /** Source file the analysis ran over. */\n sourceFile: ts.SourceFile\n /** Per-module accumulator buckets, keyed by module name. */\n perModule: Map<string, unknown>\n /** Diagnostics emitted during the walk. */\n diagnostics: Diagnostic[]\n}\n\n/**\n * Context passed to every visitor invocation. Modules use it to record\n * findings, emit diagnostics, and consult shared state (the TS\n * Compiler-API checker, the project root, sibling-module findings if\n * dependencies allow).\n */\n/**\n * Resolved external type sources for the file under analysis. Same\n * shape as `transform.ts`'s `ExternalTypeSources`; declared here as a\n * structural minimum so the module registry doesn't import from the\n * umbrella. The host adapter (vite-plugin) supplies the values via\n * its async cross-file resolver (`findTypeSource`).\n *\n * Always undefined for test-only `transformLlui(source, fileName)`\n * invocations and for lint adapters without import resolution. Modules\n * that consume this should fall back to file-local behaviour when\n * absent.\n */\nexport interface ModuleExternalTypes {\n state?: { source: string; typeName: string }\n msg?: { source: string; typeName: string }\n effect?: { source: string; typeName: string }\n}\n\nexport interface AnalysisContext {\n sourceFile: ts.SourceFile\n /** TS TypeChecker, when the host adapter has built a Program. May be undefined for AST-only paths. */\n checker: ts.TypeChecker | undefined\n /**\n * Get the named module's accumulator slot (creating it lazily). The\n * slot is whatever shape the module wrote; type-safe access is the\n * module author's responsibility — typically via a typed `get<T>()`\n * wrapper exported alongside the module.\n */\n getSlot<T>(moduleName: string, init: () => T): T\n /** Record a diagnostic. The diagnostic's `id` should match one declared in `DiagnosticDefinition[]`. */\n reportDiagnostic(d: Diagnostic): void\n /**\n * External type sources from the host adapter's cross-file resolver.\n * Undefined when the host doesn't supply them (test path, lint-only\n * adapters without import resolution).\n */\n externalTypes?: ModuleExternalTypes\n}\n\nexport interface EmissionContribution {\n /** Module emitting this contribution — used for conflict reporting. */\n module: string\n /** Field name on the `ComponentDef` object literal (e.g. `__msgSchema`). */\n field: string\n /** AST expression to assign. The umbrella merges into the component()'s config arg. */\n value: ts.Expression\n /**\n * Optional per-call target. When set, this contribution applies only\n * to the named `component()` call expression; the umbrella's\n * emission-merger writes the field into that call's config-arg\n * object literal. When omitted, the contribution is *file-global*:\n * the merger writes the field into every `component()` call in the\n * file (the common case — `__msgSchema`, `__prefixes`, `__schemaHash`\n * are file-shape-derived).\n *\n * Per-call target is needed for `__componentMeta` (file + line vary\n * per call site) and any other field whose value depends on the\n * specific `component()` call location.\n *\n * Conflict-detection runs per-(field, target) tuple — two modules\n * may both contribute `__custom` if they target *different* call\n * expressions; same target on the same field is still an error.\n */\n target?: ts.CallExpression\n}\n\nexport interface EmissionContext {\n sourceFile: ts.SourceFile\n factory: ts.NodeFactory\n}\n\n/**\n * A compiler module declares:\n * - identification (name, compilerVersion semver against the umbrella);\n * - the diagnostics it can emit (stable IDs);\n * - per-`SyntaxKind` visitor handlers (the walker dispatches each AST\n * node once; every module with a handler for its kind sees it);\n * - optionally, an `emit` function that contributes ComponentDef fields\n * after the walk completes;\n * - optionally, `runtimeImports` declaring which `@llui/dom` symbols\n * its emissions reference.\n */\nexport interface CompilerModule {\n name: string\n /** Semver range against the compiler API. v2c §5. */\n compilerVersion: string\n /** Modules this one depends on. The registry verifies presence at activation. */\n dependsOn?: string[]\n diagnostics: DiagnosticDefinition[]\n /**\n * Optional AST pre-transform. Called once per file BEFORE the\n * visitor walk and emission phase. Returns a (possibly rewritten)\n * SourceFile; the result is threaded through subsequent modules'\n * pre-transforms (in declaration order) and then becomes the file\n * the visitor walks. Use for AST mutations the visitor model can't\n * cleanly express — adjacent statement insertion, wrapping arrow\n * expressions, etc. The agent's connect-pattern pass and the\n * universal handler-tagger are the canonical examples (MODULE-MAPPING.md\n * binding-descriptors entry).\n *\n * Most modules do NOT need this. Visitor + emit is the preferred\n * shape because it composes deterministically across modules without\n * threading a mutable SourceFile through each one. preTransform\n * exists for the cases where AST mutation is unavoidable.\n *\n * The §2.1 \"walker runs once per file\" invariant is preserved: the\n * VISITOR walk runs once. preTransform passes are additional, but\n * they're typically cheap (targeted call-site rewrites, not deep\n * recursive walks) and execute before the single visitor walk.\n */\n preTransform?(ctx: PreTransformContext, sf: ts.SourceFile): ts.SourceFile\n visitors: {\n [K in ts.SyntaxKind]?: (ctx: AnalysisContext, node: ts.Node) => void\n }\n /**\n * Optional per-call AST rewrite, BOTTOM-UP (after children visited).\n * Called once per `CallExpression` during the post-visitor transform\n * phase, AFTER analysis has accumulated findings in\n * `analysis.perModule` AND after `ts.visitEachChild` has recursively\n * rewritten the node's children. Returns either:\n * - `null` — node unchanged; chain continues with the next module's\n * transformCall (if any).\n * - a new `ts.CallExpression` — node replaced; subsequent modules'\n * transformCall hooks see the new node (composes in declaration\n * order, just like preTransform).\n *\n * Use for rewrites that depend on the rewritten children — e.g.\n * row-factory emission inspects the render body for an already-emitted\n * `elTemplate(...)` call, so element rewrites that produce\n * `elTemplate` MUST have fired first. Module authors should treat\n * transformCall as a pure function of its inputs (the node + analysis\n * findings).\n */\n transformCall?(ctx: TransformCallContext, node: ts.CallExpression): ts.CallExpression | null\n /**\n * Optional per-call AST rewrite, TOP-DOWN (before children visited).\n * Mirrors `transformCall` but fires BEFORE `ts.visitEachChild`\n * recurses into the call's children. Use when the rewrite must happen\n * before the children are visited — most commonly when the rewrite\n * changes the call's argument shape and the children's visitor would\n * misinterpret the original shape. Memo-wrapping the `items:`\n * accessor of an `each()` call is the canonical example: the wrapped\n * accessor is what subsequent passes (item-selector dedup, mask\n * injection) read.\n *\n * Both `transformCallEnter` and `transformCall` may be declared by\n * the same module; enter fires top-down before recursion, transformCall\n * fires bottom-up after. Ordering within each direction is declaration\n * order across modules; the two directions never interleave for a\n * given node.\n */\n transformCallEnter?(ctx: TransformCallContext, node: ts.CallExpression): ts.CallExpression | null\n /** Called once per file after the visitor pass completes. Returns this module's emission contributions. */\n emit?(ctx: EmissionContext, analysis: FileAnalysis): EmissionContribution[]\n /** Runtime symbol names this module's emissions reference (from `@llui/dom`). */\n runtimeImports?: string[]\n}\n\nexport interface PreTransformContext {\n factory: ts.NodeFactory\n /**\n * Shared per-file findings accumulator. preTransform passes that\n * need to communicate with their own emit step (e.g. \"this file\n * needed scope-variant registrations\") use this slot map. The same\n * `analysis.perModule` map is later passed to visitors and emit.\n */\n analysis: FileAnalysis\n}\n\n/**\n * Context passed to every `transformCall` invocation. Carries the\n * factory for building new AST nodes and a read-only view of analysis\n * findings (visitors have already completed and populated\n * `analysis.perModule` by the time transformCall fires).\n */\nexport interface TransformCallContext {\n factory: ts.NodeFactory\n /** Read-only access to visitor-phase findings. */\n analysis: FileAnalysis\n}\n\n// ── Registry ────────────────────────────────────────────────────────\n\nexport interface RegistryRunResult {\n analysis: FileAnalysis\n emissions: EmissionContribution[]\n /** Union of runtime imports from every active module. */\n runtimeImports: string[]\n}\n\n/**\n * The visitor registry. Built once per compiler boot from the user's\n * `llui.config.ts` `modules: [...]` array; the umbrella's per-file\n * pipeline calls `run(sourceFile, checker)` to drive a complete pass.\n */\nexport class ModuleRegistry {\n private readonly modules: ReadonlyArray<CompilerModule>\n /** Pre-indexed by SyntaxKind for O(1) dispatch. */\n private readonly visitorsByKind: Map<ts.SyntaxKind, Array<CompilerModule>>\n\n constructor(modules: ReadonlyArray<CompilerModule>) {\n this.modules = modules\n this.verifyDependencies()\n this.visitorsByKind = this.buildVisitorIndex()\n }\n\n private verifyDependencies(): void {\n const present = new Set(this.modules.map((m) => m.name))\n for (const m of this.modules) {\n for (const dep of m.dependsOn ?? []) {\n if (!present.has(dep)) {\n throw new Error(\n `[llui] module \"${m.name}\" depends on \"${dep}\", which is not in the active module list. ` +\n `Add ${dep}() to your llui.config.ts modules array (must appear before \"${m.name}\"). ` +\n `See docs/proposals/v2-compiler/v2c.md §2.4.`,\n )\n }\n }\n }\n }\n\n private buildVisitorIndex(): Map<ts.SyntaxKind, Array<CompilerModule>> {\n const index = new Map<ts.SyntaxKind, Array<CompilerModule>>()\n for (const m of this.modules) {\n for (const kindStr of Object.keys(m.visitors)) {\n const kind = Number(kindStr) as ts.SyntaxKind\n if (!index.has(kind)) index.set(kind, [])\n index.get(kind)!.push(m)\n }\n }\n return index\n }\n\n /**\n * Run a full analysis + emission pass over `sourceFile`. Phases:\n * 1. Pre-transform: each module's `preTransform?` fires in\n * declaration order; the (possibly rewritten) SourceFile flows\n * through subsequent passes.\n * 2. Visitor walk: a single AST walk dispatches each node to every\n * module's matching SyntaxKind handler. Read-only — visitors\n * accumulate findings in `analysis.perModule` but cannot rewrite.\n * 3. Transform: a `ts.transform`-style walk dispatches each\n * `CallExpression` to every module's `transformCallEnter?`\n * (top-down, before children recursion) and `transformCall?`\n * (bottom-up, after children recursion) hooks in declaration\n * order; each hook's return value (if non-null) feeds the next.\n * Composes call-site rewrites without each module paying a\n * whole-file walk cost.\n * 4. Emission: each module's `emit?` fires; the registry merges\n * contributions, detecting (field, target) conflicts.\n */\n run(\n sourceFile: ts.SourceFile,\n checker?: ts.TypeChecker,\n externalTypes?: ModuleExternalTypes,\n ): RegistryRunResult {\n const analysis: FileAnalysis = {\n sourceFile,\n perModule: new Map(),\n diagnostics: [],\n }\n\n // Phase 1: pre-transform passes. Threaded SourceFile flows through\n // each module's preTransform in declaration order. Modules without\n // a preTransform pass through.\n let currentSf = sourceFile\n for (const m of this.modules) {\n if (!m.preTransform) continue\n currentSf = m.preTransform(\n {\n factory: ts.factory,\n analysis,\n },\n currentSf,\n )\n }\n analysis.sourceFile = currentSf\n const ctx: AnalysisContext = {\n sourceFile: currentSf,\n checker,\n getSlot: <T>(name: string, init: () => T): T => {\n let slot = analysis.perModule.get(name) as T | undefined\n if (slot === undefined) {\n slot = init()\n analysis.perModule.set(name, slot)\n }\n return slot\n },\n reportDiagnostic: (d) => {\n analysis.diagnostics.push(d)\n },\n externalTypes,\n }\n\n // Phase 2: single-pass visitor walk over the (possibly\n // pre-transformed) SourceFile.\n const walk = (node: ts.Node): void => {\n const handlers = this.visitorsByKind.get(node.kind)\n if (handlers) {\n for (const m of handlers) {\n const handler = m.visitors[node.kind]\n if (handler) handler(ctx, node)\n }\n }\n ts.forEachChild(node, walk)\n }\n walk(currentSf)\n\n // Phase 2b: per-CallExpression transform. Modules with a\n // `transformCallEnter` (top-down) or `transformCall` (bottom-up)\n // hook get one chance to rewrite each call site per direction;\n // chained in declaration order. The phase is skipped entirely\n // when no module declares either hook (zero overhead for the\n // common case of metadata-only modules).\n //\n // Within a single CallExpression visit:\n // 1. transformCallEnter fires (declaration order) — rewrites\n // the node BEFORE children are recursed; subsequent\n // transformCallEnter hooks see the rewritten node.\n // 2. ts.visitEachChild recurses into the (possibly enter-rewritten)\n // node, visiting children.\n // 3. transformCall fires (declaration order) — rewrites the\n // now-children-rewritten node; subsequent transformCall hooks\n // see the result.\n //\n // The two directions never interleave for a given node: all enters\n // run, then all children visit, then all exits run.\n const enterModules = this.modules.filter((m) => m.transformCallEnter)\n const exitModules = this.modules.filter((m) => m.transformCall)\n if (enterModules.length > 0 || exitModules.length > 0) {\n const transformCtx: TransformCallContext = {\n factory: ts.factory,\n analysis,\n }\n const visit: ts.Visitor = (node) => {\n // Top-down (enter) — fires BEFORE children recursion. Chain\n // composes in declaration order; each enter hook sees the\n // output of the previous one.\n let current = node\n if (ts.isCallExpression(current)) {\n for (const m of enterModules) {\n const replaced = m.transformCallEnter!(transformCtx, current as ts.CallExpression)\n if (replaced) current = replaced\n }\n }\n // Recurse children of the (possibly enter-rewritten) node.\n const visited = ts.visitEachChild(current, visit, undefined!)\n // Bottom-up (exit) — fires AFTER children recursion. Chain\n // composes in declaration order; each exit hook sees the\n // output of the previous one.\n if (ts.isCallExpression(visited)) {\n let result = visited as ts.CallExpression\n for (const m of exitModules) {\n const replaced = m.transformCall!(transformCtx, result)\n if (replaced) result = replaced\n }\n return result\n }\n return visited\n }\n currentSf = ts.visitNode(currentSf, visit) as ts.SourceFile\n analysis.sourceFile = currentSf\n }\n\n // Phase 3: emission. Each module contributes after analysis\n // completes. Conflicts on (field, target) tuples are hard errors\n // per §2.1.\n const emissionCtx: EmissionContext = {\n sourceFile: currentSf,\n factory: ts.factory,\n }\n const emissions: EmissionContribution[] = []\n // Conflict detection keyed by `(field, target)` — two modules may\n // contribute distinct per-target emissions to the same field name\n // (e.g. component-meta for two different `component()` calls in\n // one file), but two emissions with the same target on the same\n // field is the hard error from §2.1.\n //\n // Targets are compared by object identity (not by `pos`/`end`) so\n // synthetic nodes (factory-created with pos=-1) compare correctly.\n // File-global emissions (target=undefined) share one bucket.\n const globalOwnerByField = new Map<string, string>()\n const targetOwnerByField = new Map<ts.CallExpression, Map<string, string>>()\n for (const m of this.modules) {\n if (!m.emit) continue\n const contributions = m.emit(emissionCtx, analysis)\n for (const c of contributions) {\n let owners: Map<string, string>\n if (c.target) {\n let existing = targetOwnerByField.get(c.target)\n if (!existing) {\n existing = new Map()\n targetOwnerByField.set(c.target, existing)\n }\n owners = existing\n } else {\n owners = globalOwnerByField\n }\n const other = owners.get(c.field)\n if (other !== undefined) {\n throw new Error(\n `[llui/module-emission-conflict] Modules \"${other}\" and \"${c.module}\" both ` +\n `contribute to ComponentDef field \"${c.field}\"${c.target ? ' for the same component() call site' : ''}. ` +\n `This is a hard error — each (field, target) pair must be owned by exactly one ` +\n `module. Either deduplicate, or move one emission to a distinct field. See ` +\n `docs/proposals/v2-compiler/v2c.md §2.1.`,\n )\n }\n owners.set(c.field, c.module)\n emissions.push(c)\n }\n }\n\n // Union runtime imports.\n const runtimeImports = new Set<string>()\n for (const m of this.modules) {\n for (const imp of m.runtimeImports ?? []) runtimeImports.add(imp)\n }\n\n return {\n analysis,\n emissions,\n runtimeImports: [...runtimeImports].sort(),\n }\n }\n\n /** Module names in declaration order. Adapters surface this for debug logs / config diagnostics. */\n listModules(): string[] {\n return this.modules.map((m) => m.name)\n }\n\n /** All diagnostic definitions across active modules. Used by adapters to enumerate stable IDs. */\n listDiagnostics(): DiagnosticDefinition[] {\n return this.modules.flatMap((m) => m.diagnostics)\n }\n}\n"]}
|
|
1
|
+
{"version":3,"file":"module.js","sourceRoot":"","sources":["../src/module.ts"],"names":[],"mappings":"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","sourcesContent":["// CompilerModule + ModuleRegistry — v2c §2 visitor-registry primitive.\n//\n// Modules accumulate findings during a single AST walk per file and\n// contribute emissions after the walk completes. The walker visits each\n// node once; every module registered for that node's SyntaxKind sees it.\n//\n// This file defines the interfaces + the registry. The actual modules\n// (`compiler-core`, `compiler-agent`, `compiler-ssr`, `compiler-devtools`)\n// will consume them; for v2c-partial only the primitive lands, and one\n// proof-of-concept module exercises it.\n//\n// Design contract (v2c.md §2.1):\n// - Modules NEVER walk the AST themselves — only the registry walks.\n// This keeps the cost O(nodes), not O(modules × nodes).\n// - Visitor order for a given SyntaxKind is the declaration order in\n// `llui.config.ts`'s `modules: [...]` array. Observable to module\n// authors. Alphabetical-by-name was rejected (couples correctness\n// to package names).\n// - Emission conflicts (two modules writing to the same field) are\n// a hard error, not a silent overwrite. Each module owns disjoint\n// output fields.\n// - `runtimeImports` arrays merge by union (deduplicated). Multiple\n// modules requesting the same runtime helper collapse to one\n// import.\n\nimport ts from 'typescript'\nimport type { Diagnostic } from './diagnostic.js'\n\n// ── Module interface ────────────────────────────────────────────────\n\nexport interface DiagnosticDefinition {\n /** Stable id, e.g. `llui/opaque-view-call`. Per v2c §3 §8.2. */\n id: string\n /** One-line description; useful for adapter UIs that don't render the message. */\n description: string\n}\n\n/**\n * Per-file analysis output. Modules accumulate findings here during\n * visitor dispatch; emit consumes it. The shape is intentionally\n * open-ended — modules name their own slots and the umbrella's\n * orchestrator never inspects them, only forwards.\n */\nexport interface FileAnalysis {\n /** Source file the analysis ran over. */\n sourceFile: ts.SourceFile\n /** Per-module accumulator buckets, keyed by module name. */\n perModule: Map<string, unknown>\n /** Diagnostics emitted during the walk. */\n diagnostics: Diagnostic[]\n}\n\n/**\n * Context passed to every visitor invocation. Modules use it to record\n * findings, emit diagnostics, and consult shared state (the TS\n * Compiler-API checker, the project root, sibling-module findings if\n * dependencies allow).\n */\n/**\n * Resolved external type sources for the file under analysis. Same\n * shape as `transform.ts`'s `ExternalTypeSources`; declared here as a\n * structural minimum so the module registry doesn't import from the\n * umbrella. The host adapter (vite-plugin) supplies the values via\n * its async cross-file resolver (`findTypeSource`).\n *\n * Always undefined for test-only `transformLlui(source, fileName)`\n * invocations and for lint adapters without import resolution. Modules\n * that consume this should fall back to file-local behaviour when\n * absent.\n */\nexport interface ModuleExternalTypes {\n state?: { source: string; typeName: string }\n msg?: { source: string; typeName: string }\n effect?: { source: string; typeName: string }\n}\n\nexport interface AnalysisContext {\n sourceFile: ts.SourceFile\n /** TS TypeChecker, when the host adapter has built a Program. May be undefined for AST-only paths. */\n checker: ts.TypeChecker | undefined\n /**\n * The cross-file Program the checker is bound to, when available.\n * Modules that need to resolve identifiers across files (e.g. the\n * opaque-state-flow lint walking through imported helpers) must walk\n * Program-bound nodes — the file the registry hands them is a\n * locally-reparsed copy and its identifiers won't resolve through the\n * checker. Use `program.getSourceFile(sourceFile.fileName)` to fetch\n * the Program-bound counterpart. Undefined when the host doesn't\n * supply a Program (test path, lint adapters without cross-file).\n */\n program: ts.Program | undefined\n /**\n * Get the named module's accumulator slot (creating it lazily). The\n * slot is whatever shape the module wrote; type-safe access is the\n * module author's responsibility — typically via a typed `get<T>()`\n * wrapper exported alongside the module.\n */\n getSlot<T>(moduleName: string, init: () => T): T\n /** Record a diagnostic. The diagnostic's `id` should match one declared in `DiagnosticDefinition[]`. */\n reportDiagnostic(d: Diagnostic): void\n /**\n * External type sources from the host adapter's cross-file resolver.\n * Undefined when the host doesn't supply them (test path, lint-only\n * adapters without import resolution).\n */\n externalTypes?: ModuleExternalTypes\n}\n\nexport interface EmissionContribution {\n /** Module emitting this contribution — used for conflict reporting. */\n module: string\n /** Field name on the `ComponentDef` object literal (e.g. `__msgSchema`). */\n field: string\n /** AST expression to assign. The umbrella merges into the component()'s config arg. */\n value: ts.Expression\n /**\n * Optional per-call target. When set, this contribution applies only\n * to the named `component()` call expression; the umbrella's\n * emission-merger writes the field into that call's config-arg\n * object literal. When omitted, the contribution is *file-global*:\n * the merger writes the field into every `component()` call in the\n * file (the common case — `__msgSchema`, `__prefixes`, `__schemaHash`\n * are file-shape-derived).\n *\n * Per-call target is needed for `__componentMeta` (file + line vary\n * per call site) and any other field whose value depends on the\n * specific `component()` call location.\n *\n * Conflict-detection runs per-(field, target) tuple — two modules\n * may both contribute `__custom` if they target *different* call\n * expressions; same target on the same field is still an error.\n */\n target?: ts.CallExpression\n}\n\nexport interface EmissionContext {\n sourceFile: ts.SourceFile\n factory: ts.NodeFactory\n}\n\n/**\n * A compiler module declares:\n * - identification (name, compilerVersion semver against the umbrella);\n * - the diagnostics it can emit (stable IDs);\n * - per-`SyntaxKind` visitor handlers (the walker dispatches each AST\n * node once; every module with a handler for its kind sees it);\n * - optionally, an `emit` function that contributes ComponentDef fields\n * after the walk completes;\n * - optionally, `runtimeImports` declaring which `@llui/dom` symbols\n * its emissions reference.\n */\nexport interface CompilerModule {\n name: string\n /** Semver range against the compiler API. v2c §5. */\n compilerVersion: string\n /** Modules this one depends on. The registry verifies presence at activation. */\n dependsOn?: string[]\n diagnostics: DiagnosticDefinition[]\n /**\n * Optional AST pre-transform. Called once per file BEFORE the\n * visitor walk and emission phase. Returns a (possibly rewritten)\n * SourceFile; the result is threaded through subsequent modules'\n * pre-transforms (in declaration order) and then becomes the file\n * the visitor walks. Use for AST mutations the visitor model can't\n * cleanly express — adjacent statement insertion, wrapping arrow\n * expressions, etc. The agent's connect-pattern pass and the\n * universal handler-tagger are the canonical examples (MODULE-MAPPING.md\n * binding-descriptors entry).\n *\n * Most modules do NOT need this. Visitor + emit is the preferred\n * shape because it composes deterministically across modules without\n * threading a mutable SourceFile through each one. preTransform\n * exists for the cases where AST mutation is unavoidable.\n *\n * The §2.1 \"walker runs once per file\" invariant is preserved: the\n * VISITOR walk runs once. preTransform passes are additional, but\n * they're typically cheap (targeted call-site rewrites, not deep\n * recursive walks) and execute before the single visitor walk.\n */\n preTransform?(ctx: PreTransformContext, sf: ts.SourceFile): ts.SourceFile\n visitors: {\n [K in ts.SyntaxKind]?: (ctx: AnalysisContext, node: ts.Node) => void\n }\n /**\n * Optional per-call AST rewrite, BOTTOM-UP (after children visited).\n * Called once per `CallExpression` during the post-visitor transform\n * phase, AFTER analysis has accumulated findings in\n * `analysis.perModule` AND after `ts.visitEachChild` has recursively\n * rewritten the node's children. Returns either:\n * - `null` — node unchanged; chain continues with the next module's\n * transformCall (if any).\n * - a new `ts.CallExpression` — node replaced; subsequent modules'\n * transformCall hooks see the new node (composes in declaration\n * order, just like preTransform).\n *\n * Use for rewrites that depend on the rewritten children — e.g.\n * row-factory emission inspects the render body for an already-emitted\n * `elTemplate(...)` call, so element rewrites that produce\n * `elTemplate` MUST have fired first. Module authors should treat\n * transformCall as a pure function of its inputs (the node + analysis\n * findings).\n */\n transformCall?(ctx: TransformCallContext, node: ts.CallExpression): ts.CallExpression | null\n /**\n * Optional per-call AST rewrite, TOP-DOWN (before children visited).\n * Mirrors `transformCall` but fires BEFORE `ts.visitEachChild`\n * recurses into the call's children. Use when the rewrite must happen\n * before the children are visited — most commonly when the rewrite\n * changes the call's argument shape and the children's visitor would\n * misinterpret the original shape. Memo-wrapping the `items:`\n * accessor of an `each()` call is the canonical example: the wrapped\n * accessor is what subsequent passes (item-selector dedup, mask\n * injection) read.\n *\n * Both `transformCallEnter` and `transformCall` may be declared by\n * the same module; enter fires top-down before recursion, transformCall\n * fires bottom-up after. Ordering within each direction is declaration\n * order across modules; the two directions never interleave for a\n * given node.\n */\n transformCallEnter?(ctx: TransformCallContext, node: ts.CallExpression): ts.CallExpression | null\n /** Called once per file after the visitor pass completes. Returns this module's emission contributions. */\n emit?(ctx: EmissionContext, analysis: FileAnalysis): EmissionContribution[]\n /** Runtime symbol names this module's emissions reference (from `@llui/dom`). */\n runtimeImports?: string[]\n}\n\nexport interface PreTransformContext {\n factory: ts.NodeFactory\n /**\n * Shared per-file findings accumulator. preTransform passes that\n * need to communicate with their own emit step (e.g. \"this file\n * needed scope-variant registrations\") use this slot map. The same\n * `analysis.perModule` map is later passed to visitors and emit.\n */\n analysis: FileAnalysis\n}\n\n/**\n * Context passed to every `transformCall` invocation. Carries the\n * factory for building new AST nodes and a read-only view of analysis\n * findings (visitors have already completed and populated\n * `analysis.perModule` by the time transformCall fires).\n */\nexport interface TransformCallContext {\n factory: ts.NodeFactory\n /** Read-only access to visitor-phase findings. */\n analysis: FileAnalysis\n}\n\n// ── Registry ────────────────────────────────────────────────────────\n\nexport interface RegistryRunResult {\n analysis: FileAnalysis\n emissions: EmissionContribution[]\n /** Union of runtime imports from every active module. */\n runtimeImports: string[]\n}\n\n/**\n * The visitor registry. Built once per compiler boot from the user's\n * `llui.config.ts` `modules: [...]` array; the umbrella's per-file\n * pipeline calls `run(sourceFile, checker)` to drive a complete pass.\n */\nexport class ModuleRegistry {\n private readonly modules: ReadonlyArray<CompilerModule>\n /** Pre-indexed by SyntaxKind for O(1) dispatch. */\n private readonly visitorsByKind: Map<ts.SyntaxKind, Array<CompilerModule>>\n\n constructor(modules: ReadonlyArray<CompilerModule>) {\n this.modules = modules\n this.verifyDependencies()\n this.visitorsByKind = this.buildVisitorIndex()\n }\n\n private verifyDependencies(): void {\n const present = new Set(this.modules.map((m) => m.name))\n for (const m of this.modules) {\n for (const dep of m.dependsOn ?? []) {\n if (!present.has(dep)) {\n throw new Error(\n `[llui] module \"${m.name}\" depends on \"${dep}\", which is not in the active module list. ` +\n `Add ${dep}() to your llui.config.ts modules array (must appear before \"${m.name}\"). ` +\n `See docs/proposals/v2-compiler/v2c.md §2.4.`,\n )\n }\n }\n }\n }\n\n private buildVisitorIndex(): Map<ts.SyntaxKind, Array<CompilerModule>> {\n const index = new Map<ts.SyntaxKind, Array<CompilerModule>>()\n for (const m of this.modules) {\n for (const kindStr of Object.keys(m.visitors)) {\n const kind = Number(kindStr) as ts.SyntaxKind\n if (!index.has(kind)) index.set(kind, [])\n index.get(kind)!.push(m)\n }\n }\n return index\n }\n\n /**\n * Run a full analysis + emission pass over `sourceFile`. Phases:\n * 1. Pre-transform: each module's `preTransform?` fires in\n * declaration order; the (possibly rewritten) SourceFile flows\n * through subsequent passes.\n * 2. Visitor walk: a single AST walk dispatches each node to every\n * module's matching SyntaxKind handler. Read-only — visitors\n * accumulate findings in `analysis.perModule` but cannot rewrite.\n * 3. Transform: a `ts.transform`-style walk dispatches each\n * `CallExpression` to every module's `transformCallEnter?`\n * (top-down, before children recursion) and `transformCall?`\n * (bottom-up, after children recursion) hooks in declaration\n * order; each hook's return value (if non-null) feeds the next.\n * Composes call-site rewrites without each module paying a\n * whole-file walk cost.\n * 4. Emission: each module's `emit?` fires; the registry merges\n * contributions, detecting (field, target) conflicts.\n */\n run(\n sourceFile: ts.SourceFile,\n checker?: ts.TypeChecker,\n externalTypes?: ModuleExternalTypes,\n program?: ts.Program,\n ): RegistryRunResult {\n const analysis: FileAnalysis = {\n sourceFile,\n perModule: new Map(),\n diagnostics: [],\n }\n\n // Phase 1: pre-transform passes. Threaded SourceFile flows through\n // each module's preTransform in declaration order. Modules without\n // a preTransform pass through.\n let currentSf = sourceFile\n for (const m of this.modules) {\n if (!m.preTransform) continue\n currentSf = m.preTransform(\n {\n factory: ts.factory,\n analysis,\n },\n currentSf,\n )\n }\n analysis.sourceFile = currentSf\n const ctx: AnalysisContext = {\n sourceFile: currentSf,\n checker,\n program,\n getSlot: <T>(name: string, init: () => T): T => {\n let slot = analysis.perModule.get(name) as T | undefined\n if (slot === undefined) {\n slot = init()\n analysis.perModule.set(name, slot)\n }\n return slot\n },\n reportDiagnostic: (d) => {\n analysis.diagnostics.push(d)\n },\n externalTypes,\n }\n\n // Phase 2: single-pass visitor walk over the (possibly\n // pre-transformed) SourceFile.\n const walk = (node: ts.Node): void => {\n const handlers = this.visitorsByKind.get(node.kind)\n if (handlers) {\n for (const m of handlers) {\n const handler = m.visitors[node.kind]\n if (handler) handler(ctx, node)\n }\n }\n ts.forEachChild(node, walk)\n }\n walk(currentSf)\n\n // Phase 2b: per-CallExpression transform. Modules with a\n // `transformCallEnter` (top-down) or `transformCall` (bottom-up)\n // hook get one chance to rewrite each call site per direction;\n // chained in declaration order. The phase is skipped entirely\n // when no module declares either hook (zero overhead for the\n // common case of metadata-only modules).\n //\n // Within a single CallExpression visit:\n // 1. transformCallEnter fires (declaration order) — rewrites\n // the node BEFORE children are recursed; subsequent\n // transformCallEnter hooks see the rewritten node.\n // 2. ts.visitEachChild recurses into the (possibly enter-rewritten)\n // node, visiting children.\n // 3. transformCall fires (declaration order) — rewrites the\n // now-children-rewritten node; subsequent transformCall hooks\n // see the result.\n //\n // The two directions never interleave for a given node: all enters\n // run, then all children visit, then all exits run.\n const enterModules = this.modules.filter((m) => m.transformCallEnter)\n const exitModules = this.modules.filter((m) => m.transformCall)\n if (enterModules.length > 0 || exitModules.length > 0) {\n const transformCtx: TransformCallContext = {\n factory: ts.factory,\n analysis,\n }\n const visit: ts.Visitor = (node) => {\n // Top-down (enter) — fires BEFORE children recursion. Chain\n // composes in declaration order; each enter hook sees the\n // output of the previous one.\n let current = node\n if (ts.isCallExpression(current)) {\n for (const m of enterModules) {\n const replaced = m.transformCallEnter!(transformCtx, current as ts.CallExpression)\n if (replaced) current = replaced\n }\n }\n // Recurse children of the (possibly enter-rewritten) node.\n const visited = ts.visitEachChild(current, visit, undefined!)\n // Bottom-up (exit) — fires AFTER children recursion. Chain\n // composes in declaration order; each exit hook sees the\n // output of the previous one.\n if (ts.isCallExpression(visited)) {\n let result = visited as ts.CallExpression\n for (const m of exitModules) {\n const replaced = m.transformCall!(transformCtx, result)\n if (replaced) result = replaced\n }\n return result\n }\n return visited\n }\n currentSf = ts.visitNode(currentSf, visit) as ts.SourceFile\n analysis.sourceFile = currentSf\n }\n\n // Phase 3: emission. Each module contributes after analysis\n // completes. Conflicts on (field, target) tuples are hard errors\n // per §2.1.\n const emissionCtx: EmissionContext = {\n sourceFile: currentSf,\n factory: ts.factory,\n }\n const emissions: EmissionContribution[] = []\n // Conflict detection keyed by `(field, target)` — two modules may\n // contribute distinct per-target emissions to the same field name\n // (e.g. component-meta for two different `component()` calls in\n // one file), but two emissions with the same target on the same\n // field is the hard error from §2.1.\n //\n // Targets are compared by object identity (not by `pos`/`end`) so\n // synthetic nodes (factory-created with pos=-1) compare correctly.\n // File-global emissions (target=undefined) share one bucket.\n const globalOwnerByField = new Map<string, string>()\n const targetOwnerByField = new Map<ts.CallExpression, Map<string, string>>()\n for (const m of this.modules) {\n if (!m.emit) continue\n const contributions = m.emit(emissionCtx, analysis)\n for (const c of contributions) {\n let owners: Map<string, string>\n if (c.target) {\n let existing = targetOwnerByField.get(c.target)\n if (!existing) {\n existing = new Map()\n targetOwnerByField.set(c.target, existing)\n }\n owners = existing\n } else {\n owners = globalOwnerByField\n }\n const other = owners.get(c.field)\n if (other !== undefined) {\n throw new Error(\n `[llui/module-emission-conflict] Modules \"${other}\" and \"${c.module}\" both ` +\n `contribute to ComponentDef field \"${c.field}\"${c.target ? ' for the same component() call site' : ''}. ` +\n `This is a hard error — each (field, target) pair must be owned by exactly one ` +\n `module. Either deduplicate, or move one emission to a distinct field. See ` +\n `docs/proposals/v2-compiler/v2c.md §2.1.`,\n )\n }\n owners.set(c.field, c.module)\n emissions.push(c)\n }\n }\n\n // Union runtime imports.\n const runtimeImports = new Set<string>()\n for (const m of this.modules) {\n for (const imp of m.runtimeImports ?? []) runtimeImports.add(imp)\n }\n\n return {\n analysis,\n emissions,\n runtimeImports: [...runtimeImports].sort(),\n }\n }\n\n /** Module names in declaration order. Adapters surface this for debug logs / config diagnostics. */\n listModules(): string[] {\n return this.modules.map((m) => m.name)\n }\n\n /** All diagnostic definitions across active modules. Used by adapters to enumerate stable IDs. */\n listDiagnostics(): DiagnosticDefinition[] {\n return this.modules.flatMap((m) => m.diagnostics)\n }\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"opaque-state-flow.d.ts","sourceRoot":"","sources":["../../src/modules/opaque-state-flow.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"opaque-state-flow.d.ts","sourceRoot":"","sources":["../../src/modules/opaque-state-flow.ts"],"names":[],"mappings":"AAwCA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,cAAc,CAAA;AAwJlD,wBAAgB,qBAAqB,IAAI,cAAc,CAoDtD"}
|