@llui/compiler 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/accessor-resolver.d.ts +58 -0
- package/dist/accessor-resolver.d.ts.map +1 -0
- package/dist/accessor-resolver.js +119 -0
- package/dist/accessor-resolver.js.map +1 -0
- package/dist/binding-descriptors.d.ts +105 -0
- package/dist/binding-descriptors.d.ts.map +1 -0
- package/dist/binding-descriptors.js +340 -0
- package/dist/binding-descriptors.js.map +1 -0
- package/dist/collect-deps.d.ts +49 -0
- package/dist/collect-deps.d.ts.map +1 -0
- package/dist/collect-deps.js +444 -0
- package/dist/collect-deps.js.map +1 -0
- package/dist/compiler-cache.d.ts +20 -0
- package/dist/compiler-cache.d.ts.map +1 -0
- package/dist/compiler-cache.js +20 -0
- package/dist/compiler-cache.js.map +1 -0
- package/dist/cross-file-resolver.d.ts +109 -0
- package/dist/cross-file-resolver.d.ts.map +1 -0
- package/dist/cross-file-resolver.js +530 -0
- package/dist/cross-file-resolver.js.map +1 -0
- package/dist/cross-file-walker.d.ts +63 -0
- package/dist/cross-file-walker.d.ts.map +1 -0
- package/dist/cross-file-walker.js +516 -0
- package/dist/cross-file-walker.js.map +1 -0
- package/dist/diagnostic.d.ts +76 -0
- package/dist/diagnostic.d.ts.map +1 -0
- package/dist/diagnostic.js +59 -0
- package/dist/diagnostic.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/introspection-factory.d.ts +54 -0
- package/dist/introspection-factory.d.ts.map +1 -0
- package/dist/introspection-factory.js +46 -0
- package/dist/introspection-factory.js.map +1 -0
- package/dist/manifest.d.ts +144 -0
- package/dist/manifest.d.ts.map +1 -0
- package/dist/manifest.js +209 -0
- package/dist/manifest.js.map +1 -0
- package/dist/module.d.ts +222 -0
- package/dist/module.d.ts.map +1 -0
- package/dist/module.js +256 -0
- package/dist/module.js.map +1 -0
- package/dist/modules/_element-helpers.d.ts +4 -0
- package/dist/modules/_element-helpers.d.ts.map +1 -0
- package/dist/modules/_element-helpers.js +138 -0
- package/dist/modules/_element-helpers.js.map +1 -0
- package/dist/modules/_msg-variants.d.ts +10 -0
- package/dist/modules/_msg-variants.d.ts.map +1 -0
- package/dist/modules/_msg-variants.js +97 -0
- package/dist/modules/_msg-variants.js.map +1 -0
- package/dist/modules/_shared.d.ts +16 -0
- package/dist/modules/_shared.d.ts.map +1 -0
- package/dist/modules/_shared.js +30 -0
- package/dist/modules/_shared.js.map +1 -0
- package/dist/modules/accessibility.d.ts +3 -0
- package/dist/modules/accessibility.d.ts.map +1 -0
- package/dist/modules/accessibility.js +82 -0
- package/dist/modules/accessibility.js.map +1 -0
- package/dist/modules/accessor-side-effect.d.ts +3 -0
- package/dist/modules/accessor-side-effect.d.ts.map +1 -0
- package/dist/modules/accessor-side-effect.js +113 -0
- package/dist/modules/accessor-side-effect.js.map +1 -0
- package/dist/modules/agent-emits-drift.d.ts +3 -0
- package/dist/modules/agent-emits-drift.d.ts.map +1 -0
- package/dist/modules/agent-emits-drift.js +158 -0
- package/dist/modules/agent-emits-drift.js.map +1 -0
- package/dist/modules/agent-example-on-payload.d.ts +3 -0
- package/dist/modules/agent-example-on-payload.d.ts.map +1 -0
- package/dist/modules/agent-example-on-payload.js +53 -0
- package/dist/modules/agent-example-on-payload.js.map +1 -0
- package/dist/modules/agent-exclusive-annotations.d.ts +3 -0
- package/dist/modules/agent-exclusive-annotations.d.ts.map +1 -0
- package/dist/modules/agent-exclusive-annotations.js +68 -0
- package/dist/modules/agent-exclusive-annotations.js.map +1 -0
- package/dist/modules/agent-missing-intent.d.ts +3 -0
- package/dist/modules/agent-missing-intent.d.ts.map +1 -0
- package/dist/modules/agent-missing-intent.js +47 -0
- package/dist/modules/agent-missing-intent.js.map +1 -0
- package/dist/modules/agent-msg-resolvable.d.ts +3 -0
- package/dist/modules/agent-msg-resolvable.d.ts.map +1 -0
- package/dist/modules/agent-msg-resolvable.js +161 -0
- package/dist/modules/agent-msg-resolvable.js.map +1 -0
- package/dist/modules/agent-nonextractable-handler.d.ts +3 -0
- package/dist/modules/agent-nonextractable-handler.d.ts.map +1 -0
- package/dist/modules/agent-nonextractable-handler.js +127 -0
- package/dist/modules/agent-nonextractable-handler.js.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts +3 -0
- package/dist/modules/agent-optional-field-undocumented.d.ts.map +1 -0
- package/dist/modules/agent-optional-field-undocumented.js +67 -0
- package/dist/modules/agent-optional-field-undocumented.js.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts +3 -0
- package/dist/modules/agent-tagsend-translator-missing.d.ts.map +1 -0
- package/dist/modules/agent-tagsend-translator-missing.js +58 -0
- package/dist/modules/agent-tagsend-translator-missing.js.map +1 -0
- package/dist/modules/agent-warning-on-confirm.d.ts +3 -0
- package/dist/modules/agent-warning-on-confirm.d.ts.map +1 -0
- package/dist/modules/agent-warning-on-confirm.js +46 -0
- package/dist/modules/agent-warning-on-confirm.js.map +1 -0
- package/dist/modules/async-update.d.ts +3 -0
- package/dist/modules/async-update.d.ts.map +1 -0
- package/dist/modules/async-update.js +86 -0
- package/dist/modules/async-update.js.map +1 -0
- package/dist/modules/binding-descriptors.d.ts +4 -0
- package/dist/modules/binding-descriptors.d.ts.map +1 -0
- package/dist/modules/binding-descriptors.js +48 -0
- package/dist/modules/binding-descriptors.js.map +1 -0
- package/dist/modules/bitmask-overflow.d.ts +3 -0
- package/dist/modules/bitmask-overflow.d.ts.map +1 -0
- package/dist/modules/bitmask-overflow.js +152 -0
- package/dist/modules/bitmask-overflow.js.map +1 -0
- package/dist/modules/compiler-stamp.d.ts +3 -0
- package/dist/modules/compiler-stamp.d.ts.map +1 -0
- package/dist/modules/compiler-stamp.js +44 -0
- package/dist/modules/compiler-stamp.js.map +1 -0
- package/dist/modules/component-meta.d.ts +3 -0
- package/dist/modules/component-meta.d.ts.map +1 -0
- package/dist/modules/component-meta.js +44 -0
- package/dist/modules/component-meta.js.map +1 -0
- package/dist/modules/controlled-input.d.ts +3 -0
- package/dist/modules/controlled-input.d.ts.map +1 -0
- package/dist/modules/controlled-input.js +68 -0
- package/dist/modules/controlled-input.js.map +1 -0
- package/dist/modules/core-synthesis.d.ts +18 -0
- package/dist/modules/core-synthesis.d.ts.map +1 -0
- package/dist/modules/core-synthesis.js +748 -0
- package/dist/modules/core-synthesis.js.map +1 -0
- package/dist/modules/direct-state-in-view.d.ts +3 -0
- package/dist/modules/direct-state-in-view.d.ts.map +1 -0
- package/dist/modules/direct-state-in-view.js +103 -0
- package/dist/modules/direct-state-in-view.js.map +1 -0
- package/dist/modules/each-closure-violation.d.ts +3 -0
- package/dist/modules/each-closure-violation.d.ts.map +1 -0
- package/dist/modules/each-closure-violation.js +255 -0
- package/dist/modules/each-closure-violation.js.map +1 -0
- package/dist/modules/each-memo.d.ts +15 -0
- package/dist/modules/each-memo.d.ts.map +1 -0
- package/dist/modules/each-memo.js +115 -0
- package/dist/modules/each-memo.js.map +1 -0
- package/dist/modules/effect-without-handler.d.ts +3 -0
- package/dist/modules/effect-without-handler.d.ts.map +1 -0
- package/dist/modules/effect-without-handler.js +92 -0
- package/dist/modules/effect-without-handler.js.map +1 -0
- package/dist/modules/element-rewrite.d.ts +22 -0
- package/dist/modules/element-rewrite.d.ts.map +1 -0
- package/dist/modules/element-rewrite.js +1017 -0
- package/dist/modules/element-rewrite.js.map +1 -0
- package/dist/modules/empty-props.d.ts +3 -0
- package/dist/modules/empty-props.d.ts.map +1 -0
- package/dist/modules/empty-props.js +50 -0
- package/dist/modules/empty-props.js.map +1 -0
- package/dist/modules/exhaustive-effect-handling.d.ts +3 -0
- package/dist/modules/exhaustive-effect-handling.d.ts.map +1 -0
- package/dist/modules/exhaustive-effect-handling.js +61 -0
- package/dist/modules/exhaustive-effect-handling.js.map +1 -0
- package/dist/modules/exhaustive-update.d.ts +3 -0
- package/dist/modules/exhaustive-update.d.ts.map +1 -0
- package/dist/modules/exhaustive-update.js +146 -0
- package/dist/modules/exhaustive-update.js.map +1 -0
- package/dist/modules/forgotten-spread.d.ts +3 -0
- package/dist/modules/forgotten-spread.d.ts.map +1 -0
- package/dist/modules/forgotten-spread.js +51 -0
- package/dist/modules/forgotten-spread.js.map +1 -0
- package/dist/modules/form-boilerplate.d.ts +3 -0
- package/dist/modules/form-boilerplate.d.ts.map +1 -0
- package/dist/modules/form-boilerplate.js +101 -0
- package/dist/modules/form-boilerplate.js.map +1 -0
- package/dist/modules/imperative-dom-in-view.d.ts +3 -0
- package/dist/modules/imperative-dom-in-view.d.ts.map +1 -0
- package/dist/modules/imperative-dom-in-view.js +123 -0
- package/dist/modules/imperative-dom-in-view.js.map +1 -0
- package/dist/modules/item-dedup.d.ts +7 -0
- package/dist/modules/item-dedup.d.ts.map +1 -0
- package/dist/modules/item-dedup.js +204 -0
- package/dist/modules/item-dedup.js.map +1 -0
- package/dist/modules/map-on-state-array.d.ts +3 -0
- package/dist/modules/map-on-state-array.d.ts.map +1 -0
- package/dist/modules/map-on-state-array.js +84 -0
- package/dist/modules/map-on-state-array.js.map +1 -0
- package/dist/modules/mask-legend.d.ts +10 -0
- package/dist/modules/mask-legend.d.ts.map +1 -0
- package/dist/modules/mask-legend.js +50 -0
- package/dist/modules/mask-legend.js.map +1 -0
- package/dist/modules/missing-memo.d.ts +3 -0
- package/dist/modules/missing-memo.d.ts.map +1 -0
- package/dist/modules/missing-memo.js +114 -0
- package/dist/modules/missing-memo.js.map +1 -0
- package/dist/modules/msg-annotations.d.ts +9 -0
- package/dist/modules/msg-annotations.d.ts.map +1 -0
- package/dist/modules/msg-annotations.js +54 -0
- package/dist/modules/msg-annotations.js.map +1 -0
- package/dist/modules/msg-schema.d.ts +10 -0
- package/dist/modules/msg-schema.d.ts.map +1 -0
- package/dist/modules/msg-schema.js +70 -0
- package/dist/modules/msg-schema.js.map +1 -0
- package/dist/modules/namespace-import.d.ts +3 -0
- package/dist/modules/namespace-import.d.ts.map +1 -0
- package/dist/modules/namespace-import.js +80 -0
- package/dist/modules/namespace-import.js.map +1 -0
- package/dist/modules/nested-send-in-update.d.ts +3 -0
- package/dist/modules/nested-send-in-update.d.ts.map +1 -0
- package/dist/modules/nested-send-in-update.js +77 -0
- package/dist/modules/nested-send-in-update.js.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts +3 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.d.ts.map +1 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js +100 -0
- package/dist/modules/no-barrel-import-when-subpath-exists.js.map +1 -0
- package/dist/modules/no-eager-item-accessor.d.ts +3 -0
- package/dist/modules/no-eager-item-accessor.d.ts.map +1 -0
- package/dist/modules/no-eager-item-accessor.js +74 -0
- package/dist/modules/no-eager-item-accessor.js.map +1 -0
- package/dist/modules/no-let-reactive-accessor.d.ts +3 -0
- package/dist/modules/no-let-reactive-accessor.d.ts.map +1 -0
- package/dist/modules/no-let-reactive-accessor.js +227 -0
- package/dist/modules/no-let-reactive-accessor.js.map +1 -0
- package/dist/modules/no-list-render-in-sample.d.ts +3 -0
- package/dist/modules/no-list-render-in-sample.d.ts.map +1 -0
- package/dist/modules/no-list-render-in-sample.js +89 -0
- package/dist/modules/no-list-render-in-sample.js.map +1 -0
- package/dist/modules/no-sample-in-accessor.d.ts +3 -0
- package/dist/modules/no-sample-in-accessor.d.ts.map +1 -0
- package/dist/modules/no-sample-in-accessor.js +141 -0
- package/dist/modules/no-sample-in-accessor.js.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts +3 -0
- package/dist/modules/no-sample-in-reactive-position.d.ts.map +1 -0
- package/dist/modules/no-sample-in-reactive-position.js +72 -0
- package/dist/modules/no-sample-in-reactive-position.js.map +1 -0
- package/dist/modules/pure-update-function.d.ts +3 -0
- package/dist/modules/pure-update-function.d.ts.map +1 -0
- package/dist/modules/pure-update-function.js +127 -0
- package/dist/modules/pure-update-function.js.map +1 -0
- package/dist/modules/reactive-paths.d.ts +3 -0
- package/dist/modules/reactive-paths.d.ts.map +1 -0
- package/dist/modules/reactive-paths.js +77 -0
- package/dist/modules/reactive-paths.js.map +1 -0
- package/dist/modules/row-factory.d.ts +12 -0
- package/dist/modules/row-factory.d.ts.map +1 -0
- package/dist/modules/row-factory.js +385 -0
- package/dist/modules/row-factory.js.map +1 -0
- package/dist/modules/schema-hash.d.ts +15 -0
- package/dist/modules/schema-hash.d.ts.map +1 -0
- package/dist/modules/schema-hash.js +70 -0
- package/dist/modules/schema-hash.js.map +1 -0
- package/dist/modules/spread-in-children.d.ts +3 -0
- package/dist/modules/spread-in-children.d.ts.map +1 -0
- package/dist/modules/spread-in-children.js +144 -0
- package/dist/modules/spread-in-children.js.map +1 -0
- package/dist/modules/state-mutation.d.ts +3 -0
- package/dist/modules/state-mutation.d.ts.map +1 -0
- package/dist/modules/state-mutation.js +138 -0
- package/dist/modules/state-mutation.js.map +1 -0
- package/dist/modules/state-schema.d.ts +8 -0
- package/dist/modules/state-schema.d.ts.map +1 -0
- package/dist/modules/state-schema.js +55 -0
- package/dist/modules/state-schema.js.map +1 -0
- package/dist/modules/static-items.d.ts +3 -0
- package/dist/modules/static-items.d.ts.map +1 -0
- package/dist/modules/static-items.js +125 -0
- package/dist/modules/static-items.js.map +1 -0
- package/dist/modules/static-on.d.ts +3 -0
- package/dist/modules/static-on.d.ts.map +1 -0
- package/dist/modules/static-on.js +100 -0
- package/dist/modules/static-on.js.map +1 -0
- package/dist/modules/string-effect-callback.d.ts +3 -0
- package/dist/modules/string-effect-callback.d.ts.map +1 -0
- package/dist/modules/string-effect-callback.js +50 -0
- package/dist/modules/string-effect-callback.js.map +1 -0
- package/dist/modules/structural-mask.d.ts +8 -0
- package/dist/modules/structural-mask.d.ts.map +1 -0
- package/dist/modules/structural-mask.js +76 -0
- package/dist/modules/structural-mask.js.map +1 -0
- package/dist/modules/subapp-requires-reason.d.ts +3 -0
- package/dist/modules/subapp-requires-reason.d.ts.map +1 -0
- package/dist/modules/subapp-requires-reason.js +129 -0
- package/dist/modules/subapp-requires-reason.js.map +1 -0
- package/dist/modules/text-mask.d.ts +12 -0
- package/dist/modules/text-mask.d.ts.map +1 -0
- package/dist/modules/text-mask.js +63 -0
- package/dist/modules/text-mask.js.map +1 -0
- package/dist/modules/view-bag-import.d.ts +3 -0
- package/dist/modules/view-bag-import.d.ts.map +1 -0
- package/dist/modules/view-bag-import.js +80 -0
- package/dist/modules/view-bag-import.js.map +1 -0
- package/dist/msg-annotations.d.ts +104 -0
- package/dist/msg-annotations.d.ts.map +1 -0
- package/dist/msg-annotations.js +242 -0
- package/dist/msg-annotations.js.map +1 -0
- package/dist/msg-schema.d.ts +130 -0
- package/dist/msg-schema.d.ts.map +1 -0
- package/dist/msg-schema.js +770 -0
- package/dist/msg-schema.js.map +1 -0
- package/dist/schema-hash.d.ts +16 -0
- package/dist/schema-hash.d.ts.map +1 -0
- package/dist/schema-hash.js +31 -0
- package/dist/schema-hash.js.map +1 -0
- package/dist/state-schema.d.ts +41 -0
- package/dist/state-schema.d.ts.map +1 -0
- package/dist/state-schema.js +156 -0
- package/dist/state-schema.js.map +1 -0
- package/dist/transform.d.ts +109 -0
- package/dist/transform.d.ts.map +1 -0
- package/dist/transform.js +1390 -0
- package/dist/transform.js.map +1 -0
- package/dist/version.d.ts +11 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +11 -0
- package/dist/version.js.map +1 -0
- package/package.json +47 -0
package/dist/module.js
ADDED
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// CompilerModule + ModuleRegistry — v2c §2 visitor-registry primitive.
|
|
2
|
+
//
|
|
3
|
+
// Modules accumulate findings during a single AST walk per file and
|
|
4
|
+
// contribute emissions after the walk completes. The walker visits each
|
|
5
|
+
// node once; every module registered for that node's SyntaxKind sees it.
|
|
6
|
+
//
|
|
7
|
+
// This file defines the interfaces + the registry. The actual modules
|
|
8
|
+
// (`compiler-core`, `compiler-agent`, `compiler-ssr`, `compiler-devtools`)
|
|
9
|
+
// will consume them; for v2c-partial only the primitive lands, and one
|
|
10
|
+
// proof-of-concept module exercises it.
|
|
11
|
+
//
|
|
12
|
+
// Design contract (v2c.md §2.1):
|
|
13
|
+
// - Modules NEVER walk the AST themselves — only the registry walks.
|
|
14
|
+
// This keeps the cost O(nodes), not O(modules × nodes).
|
|
15
|
+
// - Visitor order for a given SyntaxKind is the declaration order in
|
|
16
|
+
// `llui.config.ts`'s `modules: [...]` array. Observable to module
|
|
17
|
+
// authors. Alphabetical-by-name was rejected (couples correctness
|
|
18
|
+
// to package names).
|
|
19
|
+
// - Emission conflicts (two modules writing to the same field) are
|
|
20
|
+
// a hard error, not a silent overwrite. Each module owns disjoint
|
|
21
|
+
// output fields.
|
|
22
|
+
// - `runtimeImports` arrays merge by union (deduplicated). Multiple
|
|
23
|
+
// modules requesting the same runtime helper collapse to one
|
|
24
|
+
// import.
|
|
25
|
+
import ts from 'typescript';
|
|
26
|
+
/**
|
|
27
|
+
* The visitor registry. Built once per compiler boot from the user's
|
|
28
|
+
* `llui.config.ts` `modules: [...]` array; the umbrella's per-file
|
|
29
|
+
* pipeline calls `run(sourceFile, checker)` to drive a complete pass.
|
|
30
|
+
*/
|
|
31
|
+
export class ModuleRegistry {
|
|
32
|
+
modules;
|
|
33
|
+
/** Pre-indexed by SyntaxKind for O(1) dispatch. */
|
|
34
|
+
visitorsByKind;
|
|
35
|
+
constructor(modules) {
|
|
36
|
+
this.modules = modules;
|
|
37
|
+
this.verifyDependencies();
|
|
38
|
+
this.visitorsByKind = this.buildVisitorIndex();
|
|
39
|
+
}
|
|
40
|
+
verifyDependencies() {
|
|
41
|
+
const present = new Set(this.modules.map((m) => m.name));
|
|
42
|
+
for (const m of this.modules) {
|
|
43
|
+
for (const dep of m.dependsOn ?? []) {
|
|
44
|
+
if (!present.has(dep)) {
|
|
45
|
+
throw new Error(`[llui] module "${m.name}" depends on "${dep}", which is not in the active module list. ` +
|
|
46
|
+
`Add ${dep}() to your llui.config.ts modules array (must appear before "${m.name}"). ` +
|
|
47
|
+
`See docs/proposals/v2-compiler/v2c.md §2.4.`);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
buildVisitorIndex() {
|
|
53
|
+
const index = new Map();
|
|
54
|
+
for (const m of this.modules) {
|
|
55
|
+
for (const kindStr of Object.keys(m.visitors)) {
|
|
56
|
+
const kind = Number(kindStr);
|
|
57
|
+
if (!index.has(kind))
|
|
58
|
+
index.set(kind, []);
|
|
59
|
+
index.get(kind).push(m);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return index;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Run a full analysis + emission pass over `sourceFile`. Phases:
|
|
66
|
+
* 1. Pre-transform: each module's `preTransform?` fires in
|
|
67
|
+
* declaration order; the (possibly rewritten) SourceFile flows
|
|
68
|
+
* through subsequent passes.
|
|
69
|
+
* 2. Visitor walk: a single AST walk dispatches each node to every
|
|
70
|
+
* module's matching SyntaxKind handler. Read-only — visitors
|
|
71
|
+
* accumulate findings in `analysis.perModule` but cannot rewrite.
|
|
72
|
+
* 3. Transform: a `ts.transform`-style walk dispatches each
|
|
73
|
+
* `CallExpression` to every module's `transformCallEnter?`
|
|
74
|
+
* (top-down, before children recursion) and `transformCall?`
|
|
75
|
+
* (bottom-up, after children recursion) hooks in declaration
|
|
76
|
+
* order; each hook's return value (if non-null) feeds the next.
|
|
77
|
+
* Composes call-site rewrites without each module paying a
|
|
78
|
+
* whole-file walk cost.
|
|
79
|
+
* 4. Emission: each module's `emit?` fires; the registry merges
|
|
80
|
+
* contributions, detecting (field, target) conflicts.
|
|
81
|
+
*/
|
|
82
|
+
run(sourceFile, checker) {
|
|
83
|
+
const analysis = {
|
|
84
|
+
sourceFile,
|
|
85
|
+
perModule: new Map(),
|
|
86
|
+
diagnostics: [],
|
|
87
|
+
};
|
|
88
|
+
// Phase 1: pre-transform passes. Threaded SourceFile flows through
|
|
89
|
+
// each module's preTransform in declaration order. Modules without
|
|
90
|
+
// a preTransform pass through.
|
|
91
|
+
let currentSf = sourceFile;
|
|
92
|
+
for (const m of this.modules) {
|
|
93
|
+
if (!m.preTransform)
|
|
94
|
+
continue;
|
|
95
|
+
currentSf = m.preTransform({
|
|
96
|
+
factory: ts.factory,
|
|
97
|
+
analysis,
|
|
98
|
+
}, currentSf);
|
|
99
|
+
}
|
|
100
|
+
analysis.sourceFile = currentSf;
|
|
101
|
+
const ctx = {
|
|
102
|
+
sourceFile: currentSf,
|
|
103
|
+
checker,
|
|
104
|
+
getSlot: (name, init) => {
|
|
105
|
+
let slot = analysis.perModule.get(name);
|
|
106
|
+
if (slot === undefined) {
|
|
107
|
+
slot = init();
|
|
108
|
+
analysis.perModule.set(name, slot);
|
|
109
|
+
}
|
|
110
|
+
return slot;
|
|
111
|
+
},
|
|
112
|
+
reportDiagnostic: (d) => {
|
|
113
|
+
analysis.diagnostics.push(d);
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
// Phase 2: single-pass visitor walk over the (possibly
|
|
117
|
+
// pre-transformed) SourceFile.
|
|
118
|
+
const walk = (node) => {
|
|
119
|
+
const handlers = this.visitorsByKind.get(node.kind);
|
|
120
|
+
if (handlers) {
|
|
121
|
+
for (const m of handlers) {
|
|
122
|
+
const handler = m.visitors[node.kind];
|
|
123
|
+
if (handler)
|
|
124
|
+
handler(ctx, node);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
ts.forEachChild(node, walk);
|
|
128
|
+
};
|
|
129
|
+
walk(currentSf);
|
|
130
|
+
// Phase 2b: per-CallExpression transform. Modules with a
|
|
131
|
+
// `transformCallEnter` (top-down) or `transformCall` (bottom-up)
|
|
132
|
+
// hook get one chance to rewrite each call site per direction;
|
|
133
|
+
// chained in declaration order. The phase is skipped entirely
|
|
134
|
+
// when no module declares either hook (zero overhead for the
|
|
135
|
+
// common case of metadata-only modules).
|
|
136
|
+
//
|
|
137
|
+
// Within a single CallExpression visit:
|
|
138
|
+
// 1. transformCallEnter fires (declaration order) — rewrites
|
|
139
|
+
// the node BEFORE children are recursed; subsequent
|
|
140
|
+
// transformCallEnter hooks see the rewritten node.
|
|
141
|
+
// 2. ts.visitEachChild recurses into the (possibly enter-rewritten)
|
|
142
|
+
// node, visiting children.
|
|
143
|
+
// 3. transformCall fires (declaration order) — rewrites the
|
|
144
|
+
// now-children-rewritten node; subsequent transformCall hooks
|
|
145
|
+
// see the result.
|
|
146
|
+
//
|
|
147
|
+
// The two directions never interleave for a given node: all enters
|
|
148
|
+
// run, then all children visit, then all exits run.
|
|
149
|
+
const enterModules = this.modules.filter((m) => m.transformCallEnter);
|
|
150
|
+
const exitModules = this.modules.filter((m) => m.transformCall);
|
|
151
|
+
if (enterModules.length > 0 || exitModules.length > 0) {
|
|
152
|
+
const transformCtx = {
|
|
153
|
+
factory: ts.factory,
|
|
154
|
+
analysis,
|
|
155
|
+
};
|
|
156
|
+
const visit = (node) => {
|
|
157
|
+
// Top-down (enter) — fires BEFORE children recursion. Chain
|
|
158
|
+
// composes in declaration order; each enter hook sees the
|
|
159
|
+
// output of the previous one.
|
|
160
|
+
let current = node;
|
|
161
|
+
if (ts.isCallExpression(current)) {
|
|
162
|
+
for (const m of enterModules) {
|
|
163
|
+
const replaced = m.transformCallEnter(transformCtx, current);
|
|
164
|
+
if (replaced)
|
|
165
|
+
current = replaced;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// Recurse children of the (possibly enter-rewritten) node.
|
|
169
|
+
const visited = ts.visitEachChild(current, visit, undefined);
|
|
170
|
+
// Bottom-up (exit) — fires AFTER children recursion. Chain
|
|
171
|
+
// composes in declaration order; each exit hook sees the
|
|
172
|
+
// output of the previous one.
|
|
173
|
+
if (ts.isCallExpression(visited)) {
|
|
174
|
+
let result = visited;
|
|
175
|
+
for (const m of exitModules) {
|
|
176
|
+
const replaced = m.transformCall(transformCtx, result);
|
|
177
|
+
if (replaced)
|
|
178
|
+
result = replaced;
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
return visited;
|
|
183
|
+
};
|
|
184
|
+
currentSf = ts.visitNode(currentSf, visit);
|
|
185
|
+
analysis.sourceFile = currentSf;
|
|
186
|
+
}
|
|
187
|
+
// Phase 3: emission. Each module contributes after analysis
|
|
188
|
+
// completes. Conflicts on (field, target) tuples are hard errors
|
|
189
|
+
// per §2.1.
|
|
190
|
+
const emissionCtx = {
|
|
191
|
+
sourceFile: currentSf,
|
|
192
|
+
factory: ts.factory,
|
|
193
|
+
};
|
|
194
|
+
const emissions = [];
|
|
195
|
+
// Conflict detection keyed by `(field, target)` — two modules may
|
|
196
|
+
// contribute distinct per-target emissions to the same field name
|
|
197
|
+
// (e.g. component-meta for two different `component()` calls in
|
|
198
|
+
// one file), but two emissions with the same target on the same
|
|
199
|
+
// field is the hard error from §2.1.
|
|
200
|
+
//
|
|
201
|
+
// Targets are compared by object identity (not by `pos`/`end`) so
|
|
202
|
+
// synthetic nodes (factory-created with pos=-1) compare correctly.
|
|
203
|
+
// File-global emissions (target=undefined) share one bucket.
|
|
204
|
+
const globalOwnerByField = new Map();
|
|
205
|
+
const targetOwnerByField = new Map();
|
|
206
|
+
for (const m of this.modules) {
|
|
207
|
+
if (!m.emit)
|
|
208
|
+
continue;
|
|
209
|
+
const contributions = m.emit(emissionCtx, analysis);
|
|
210
|
+
for (const c of contributions) {
|
|
211
|
+
let owners;
|
|
212
|
+
if (c.target) {
|
|
213
|
+
let existing = targetOwnerByField.get(c.target);
|
|
214
|
+
if (!existing) {
|
|
215
|
+
existing = new Map();
|
|
216
|
+
targetOwnerByField.set(c.target, existing);
|
|
217
|
+
}
|
|
218
|
+
owners = existing;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
owners = globalOwnerByField;
|
|
222
|
+
}
|
|
223
|
+
const other = owners.get(c.field);
|
|
224
|
+
if (other !== undefined) {
|
|
225
|
+
throw new Error(`[llui/module-emission-conflict] Modules "${other}" and "${c.module}" both ` +
|
|
226
|
+
`contribute to ComponentDef field "${c.field}"${c.target ? ' for the same component() call site' : ''}. ` +
|
|
227
|
+
`This is a hard error — each (field, target) pair must be owned by exactly one ` +
|
|
228
|
+
`module. Either deduplicate, or move one emission to a distinct field. See ` +
|
|
229
|
+
`docs/proposals/v2-compiler/v2c.md §2.1.`);
|
|
230
|
+
}
|
|
231
|
+
owners.set(c.field, c.module);
|
|
232
|
+
emissions.push(c);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Union runtime imports.
|
|
236
|
+
const runtimeImports = new Set();
|
|
237
|
+
for (const m of this.modules) {
|
|
238
|
+
for (const imp of m.runtimeImports ?? [])
|
|
239
|
+
runtimeImports.add(imp);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
analysis,
|
|
243
|
+
emissions,
|
|
244
|
+
runtimeImports: [...runtimeImports].sort(),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/** Module names in declaration order. Adapters surface this for debug logs / config diagnostics. */
|
|
248
|
+
listModules() {
|
|
249
|
+
return this.modules.map((m) => m.name);
|
|
250
|
+
}
|
|
251
|
+
/** All diagnostic definitions across active modules. Used by adapters to enumerate stable IDs. */
|
|
252
|
+
listDiagnostics() {
|
|
253
|
+
return this.modules.flatMap((m) => m.diagnostics);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
//# sourceMappingURL=module.js.map
|
|
@@ -0,0 +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;AAuM3B;;;;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,CAAC,UAAyB,EAAE,OAAwB;QACrD,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;SACF,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 */\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\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(sourceFile: ts.SourceFile, checker?: ts.TypeChecker): 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 }\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"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_element-helpers.d.ts","sourceRoot":"","sources":["../../src/modules/_element-helpers.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,eAAe,aA2H1B,CAAA;AAEF,qEAAqE;AACrE,eAAO,MAAM,oBAAoB,aAQ/B,CAAA"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
// HTML element-helper names exported from @llui/dom that the compiler
|
|
2
|
+
// can transform and that lint rules want to match against. Mirrors the
|
|
3
|
+
// runtime export list — keep in sync.
|
|
4
|
+
export const ELEMENT_HELPERS = new Set([
|
|
5
|
+
'a',
|
|
6
|
+
'abbr',
|
|
7
|
+
'address',
|
|
8
|
+
'area',
|
|
9
|
+
'article',
|
|
10
|
+
'aside',
|
|
11
|
+
'audio',
|
|
12
|
+
'b',
|
|
13
|
+
'base',
|
|
14
|
+
'bdi',
|
|
15
|
+
'bdo',
|
|
16
|
+
'blockquote',
|
|
17
|
+
'body',
|
|
18
|
+
'br',
|
|
19
|
+
'button',
|
|
20
|
+
'canvas',
|
|
21
|
+
'caption',
|
|
22
|
+
'cite',
|
|
23
|
+
'code',
|
|
24
|
+
'col',
|
|
25
|
+
'colgroup',
|
|
26
|
+
'data',
|
|
27
|
+
'datalist',
|
|
28
|
+
'dd',
|
|
29
|
+
'del',
|
|
30
|
+
'details',
|
|
31
|
+
'dfn',
|
|
32
|
+
'dialog',
|
|
33
|
+
'div',
|
|
34
|
+
'dl',
|
|
35
|
+
'dt',
|
|
36
|
+
'em',
|
|
37
|
+
'embed',
|
|
38
|
+
'fieldset',
|
|
39
|
+
'figcaption',
|
|
40
|
+
'figure',
|
|
41
|
+
'footer',
|
|
42
|
+
'form',
|
|
43
|
+
'h1',
|
|
44
|
+
'h2',
|
|
45
|
+
'h3',
|
|
46
|
+
'h4',
|
|
47
|
+
'h5',
|
|
48
|
+
'h6',
|
|
49
|
+
'head',
|
|
50
|
+
'header',
|
|
51
|
+
'hgroup',
|
|
52
|
+
'hr',
|
|
53
|
+
'html',
|
|
54
|
+
'i',
|
|
55
|
+
'iframe',
|
|
56
|
+
'img',
|
|
57
|
+
'input',
|
|
58
|
+
'ins',
|
|
59
|
+
'kbd',
|
|
60
|
+
'label',
|
|
61
|
+
'legend',
|
|
62
|
+
'li',
|
|
63
|
+
'link',
|
|
64
|
+
'main',
|
|
65
|
+
'map',
|
|
66
|
+
'mark',
|
|
67
|
+
'menu',
|
|
68
|
+
'meta',
|
|
69
|
+
'meter',
|
|
70
|
+
'nav',
|
|
71
|
+
'noscript',
|
|
72
|
+
'object',
|
|
73
|
+
'ol',
|
|
74
|
+
'optgroup',
|
|
75
|
+
'option',
|
|
76
|
+
'output',
|
|
77
|
+
'p',
|
|
78
|
+
'picture',
|
|
79
|
+
'pre',
|
|
80
|
+
'progress',
|
|
81
|
+
'q',
|
|
82
|
+
'rp',
|
|
83
|
+
'rt',
|
|
84
|
+
'ruby',
|
|
85
|
+
's',
|
|
86
|
+
'samp',
|
|
87
|
+
'script',
|
|
88
|
+
'search',
|
|
89
|
+
'section',
|
|
90
|
+
'select',
|
|
91
|
+
'slot',
|
|
92
|
+
'small',
|
|
93
|
+
'source',
|
|
94
|
+
'span',
|
|
95
|
+
'strong',
|
|
96
|
+
'style',
|
|
97
|
+
'sub',
|
|
98
|
+
'summary',
|
|
99
|
+
'sup',
|
|
100
|
+
'table',
|
|
101
|
+
'tbody',
|
|
102
|
+
'td',
|
|
103
|
+
'template',
|
|
104
|
+
'textarea',
|
|
105
|
+
'tfoot',
|
|
106
|
+
'th',
|
|
107
|
+
'thead',
|
|
108
|
+
'time',
|
|
109
|
+
'title',
|
|
110
|
+
'tr',
|
|
111
|
+
'track',
|
|
112
|
+
'u',
|
|
113
|
+
'ul',
|
|
114
|
+
'video',
|
|
115
|
+
'wbr',
|
|
116
|
+
// SVG
|
|
117
|
+
'svg',
|
|
118
|
+
'g',
|
|
119
|
+
'path',
|
|
120
|
+
'circle',
|
|
121
|
+
'rect',
|
|
122
|
+
'line',
|
|
123
|
+
'polyline',
|
|
124
|
+
'polygon',
|
|
125
|
+
'ellipse',
|
|
126
|
+
'text',
|
|
127
|
+
]);
|
|
128
|
+
/** Elements that natively handle clicks via keyboard (a11y-safe). */
|
|
129
|
+
export const INTERACTIVE_ELEMENTS = new Set([
|
|
130
|
+
'button',
|
|
131
|
+
'a',
|
|
132
|
+
'input',
|
|
133
|
+
'select',
|
|
134
|
+
'textarea',
|
|
135
|
+
'details',
|
|
136
|
+
'summary',
|
|
137
|
+
]);
|
|
138
|
+
//# sourceMappingURL=_element-helpers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_element-helpers.js","sourceRoot":"","sources":["../../src/modules/_element-helpers.ts"],"names":[],"mappings":"AAAA,sEAAsE;AACtE,uEAAuE;AACvE,sCAAsC;AAEtC,MAAM,CAAC,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC;IACrC,GAAG;IACH,MAAM;IACN,SAAS;IACT,MAAM;IACN,SAAS;IACT,OAAO;IACP,OAAO;IACP,GAAG;IACH,MAAM;IACN,KAAK;IACL,KAAK;IACL,YAAY;IACZ,MAAM;IACN,IAAI;IACJ,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,MAAM;IACN,MAAM;IACN,KAAK;IACL,UAAU;IACV,MAAM;IACN,UAAU;IACV,IAAI;IACJ,KAAK;IACL,SAAS;IACT,KAAK;IACL,QAAQ;IACR,KAAK;IACL,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,OAAO;IACP,UAAU;IACV,YAAY;IACZ,QAAQ;IACR,QAAQ;IACR,MAAM;IACN,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,IAAI;IACJ,MAAM;IACN,GAAG;IACH,QAAQ;IACR,KAAK;IACL,OAAO;IACP,KAAK;IACL,KAAK;IACL,OAAO;IACP,QAAQ;IACR,IAAI;IACJ,MAAM;IACN,MAAM;IACN,KAAK;IACL,MAAM;IACN,MAAM;IACN,MAAM;IACN,OAAO;IACP,KAAK;IACL,UAAU;IACV,QAAQ;IACR,IAAI;IACJ,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,GAAG;IACH,SAAS;IACT,KAAK;IACL,UAAU;IACV,GAAG;IACH,IAAI;IACJ,IAAI;IACJ,MAAM;IACN,GAAG;IACH,MAAM;IACN,QAAQ;IACR,QAAQ;IACR,SAAS;IACT,QAAQ;IACR,MAAM;IACN,OAAO;IACP,QAAQ;IACR,MAAM;IACN,QAAQ;IACR,OAAO;IACP,KAAK;IACL,SAAS;IACT,KAAK;IACL,OAAO;IACP,OAAO;IACP,IAAI;IACJ,UAAU;IACV,UAAU;IACV,OAAO;IACP,IAAI;IACJ,OAAO;IACP,MAAM;IACN,OAAO;IACP,IAAI;IACJ,OAAO;IACP,GAAG;IACH,IAAI;IACJ,OAAO;IACP,KAAK;IACL,MAAM;IACN,KAAK;IACL,GAAG;IACH,MAAM;IACN,QAAQ;IACR,MAAM;IACN,MAAM;IACN,UAAU;IACV,SAAS;IACT,SAAS;IACT,MAAM;CACP,CAAC,CAAA;AAEF,qEAAqE;AACrE,MAAM,CAAC,MAAM,oBAAoB,GAAG,IAAI,GAAG,CAAC;IAC1C,QAAQ;IACR,GAAG;IACH,OAAO;IACP,QAAQ;IACR,UAAU;IACV,SAAS;IACT,SAAS;CACV,CAAC,CAAA","sourcesContent":["// HTML element-helper names exported from @llui/dom that the compiler\n// can transform and that lint rules want to match against. Mirrors the\n// runtime export list — keep in sync.\n\nexport const ELEMENT_HELPERS = new Set([\n 'a',\n 'abbr',\n 'address',\n 'area',\n 'article',\n 'aside',\n 'audio',\n 'b',\n 'base',\n 'bdi',\n 'bdo',\n 'blockquote',\n 'body',\n 'br',\n 'button',\n 'canvas',\n 'caption',\n 'cite',\n 'code',\n 'col',\n 'colgroup',\n 'data',\n 'datalist',\n 'dd',\n 'del',\n 'details',\n 'dfn',\n 'dialog',\n 'div',\n 'dl',\n 'dt',\n 'em',\n 'embed',\n 'fieldset',\n 'figcaption',\n 'figure',\n 'footer',\n 'form',\n 'h1',\n 'h2',\n 'h3',\n 'h4',\n 'h5',\n 'h6',\n 'head',\n 'header',\n 'hgroup',\n 'hr',\n 'html',\n 'i',\n 'iframe',\n 'img',\n 'input',\n 'ins',\n 'kbd',\n 'label',\n 'legend',\n 'li',\n 'link',\n 'main',\n 'map',\n 'mark',\n 'menu',\n 'meta',\n 'meter',\n 'nav',\n 'noscript',\n 'object',\n 'ol',\n 'optgroup',\n 'option',\n 'output',\n 'p',\n 'picture',\n 'pre',\n 'progress',\n 'q',\n 'rp',\n 'rt',\n 'ruby',\n 's',\n 'samp',\n 'script',\n 'search',\n 'section',\n 'select',\n 'slot',\n 'small',\n 'source',\n 'span',\n 'strong',\n 'style',\n 'sub',\n 'summary',\n 'sup',\n 'table',\n 'tbody',\n 'td',\n 'template',\n 'textarea',\n 'tfoot',\n 'th',\n 'thead',\n 'time',\n 'title',\n 'tr',\n 'track',\n 'u',\n 'ul',\n 'video',\n 'wbr',\n // SVG\n 'svg',\n 'g',\n 'path',\n 'circle',\n 'rect',\n 'line',\n 'polyline',\n 'polygon',\n 'ellipse',\n 'text',\n])\n\n/** Elements that natively handle clicks via keyboard (a11y-safe). */\nexport const INTERACTIVE_ELEMENTS = new Set([\n 'button',\n 'a',\n 'input',\n 'select',\n 'textarea',\n 'details',\n 'summary',\n])\n"]}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
export interface MsgVariant {
|
|
3
|
+
variant: string;
|
|
4
|
+
node: ts.TypeLiteralNode;
|
|
5
|
+
leadingCommentText: string;
|
|
6
|
+
}
|
|
7
|
+
export declare function forEachMsgVariant(sf: ts.SourceFile, callback: (v: MsgVariant) => void): void;
|
|
8
|
+
/** Variant has at least one property beyond `type`. */
|
|
9
|
+
export declare function variantHasPayload(node: ts.TypeLiteralNode): boolean;
|
|
10
|
+
//# sourceMappingURL=_msg-variants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_msg-variants.d.ts","sourceRoot":"","sources":["../../src/modules/_msg-variants.ts"],"names":[],"mappings":"AAqBA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B,MAAM,WAAW,UAAU;IACzB,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,EAAE,CAAC,eAAe,CAAA;IACxB,kBAAkB,EAAE,MAAM,CAAA;CAC3B;AAoCD,wBAAgB,iBAAiB,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,UAAU,KAAK,IAAI,GAAG,IAAI,CAwB5F;AAED,uDAAuD;AACvD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,GAAG,OAAO,CAQnE"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
// Shared helper for agent-protocol rules that iterate Msg union
|
|
2
|
+
// variants and inspect leading JSDoc-style comments on each. The
|
|
3
|
+
// compiler doesn't run typed-lint, so "is this a Msg union?" detection
|
|
4
|
+
// uses simpler heuristics:
|
|
5
|
+
//
|
|
6
|
+
// 1. Type alias literally named `Msg`.
|
|
7
|
+
// 2. Any type-alias whose name appears as the SECOND generic argument
|
|
8
|
+
// of a `component<S, Msg, E>()` call in the same file.
|
|
9
|
+
//
|
|
10
|
+
// File-local only. Cross-file Msg detection would need the cross-file
|
|
11
|
+
// resolver; for the v1 of these rules we accept the file-local scope
|
|
12
|
+
// (matches the original Vite-plugin diagnostic).
|
|
13
|
+
//
|
|
14
|
+
// Each variant callback receives:
|
|
15
|
+
// - `variant`: the discriminant string (the `type: '...'` literal).
|
|
16
|
+
// - `node`: the TypeLiteral AST node for the variant.
|
|
17
|
+
// - `leadingCommentText`: the source slice between the previous
|
|
18
|
+
// variant's end (or the alias start) and this variant's start.
|
|
19
|
+
// Includes JSDoc blocks, `|` tokens, and whitespace; regex-match
|
|
20
|
+
// against it the same way the ESLint rules did.
|
|
21
|
+
import ts from 'typescript';
|
|
22
|
+
function readDiscriminantLiteral(typeLit) {
|
|
23
|
+
for (const m of typeLit.members) {
|
|
24
|
+
if (!ts.isPropertySignature(m))
|
|
25
|
+
continue;
|
|
26
|
+
if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type')
|
|
27
|
+
continue;
|
|
28
|
+
const ann = m.type;
|
|
29
|
+
if (!ann || !ts.isLiteralTypeNode(ann))
|
|
30
|
+
continue;
|
|
31
|
+
const lit = ann.literal;
|
|
32
|
+
if (ts.isStringLiteral(lit))
|
|
33
|
+
return lit.text;
|
|
34
|
+
}
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
/** Names of type aliases that appear as the 2nd generic arg of `component<S,M,E>()`. */
|
|
38
|
+
function collectComponentMsgArgNames(sf) {
|
|
39
|
+
const out = new Set();
|
|
40
|
+
const walk = (n) => {
|
|
41
|
+
if (ts.isCallExpression(n) &&
|
|
42
|
+
ts.isIdentifier(n.expression) &&
|
|
43
|
+
n.expression.text === 'component' &&
|
|
44
|
+
n.typeArguments &&
|
|
45
|
+
n.typeArguments.length >= 2) {
|
|
46
|
+
const msgArg = n.typeArguments[1];
|
|
47
|
+
if (ts.isTypeReferenceNode(msgArg) && ts.isIdentifier(msgArg.typeName)) {
|
|
48
|
+
out.add(msgArg.typeName.text);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
ts.forEachChild(n, walk);
|
|
52
|
+
};
|
|
53
|
+
walk(sf);
|
|
54
|
+
return out;
|
|
55
|
+
}
|
|
56
|
+
export function forEachMsgVariant(sf, callback) {
|
|
57
|
+
const componentMsgNames = collectComponentMsgArgNames(sf);
|
|
58
|
+
for (const stmt of sf.statements) {
|
|
59
|
+
if (!ts.isTypeAliasDeclaration(stmt))
|
|
60
|
+
continue;
|
|
61
|
+
const aliasName = stmt.name.text;
|
|
62
|
+
if (aliasName !== 'Msg' && !componentMsgNames.has(aliasName))
|
|
63
|
+
continue;
|
|
64
|
+
const ann = stmt.type;
|
|
65
|
+
if (!ts.isUnionTypeNode(ann))
|
|
66
|
+
continue;
|
|
67
|
+
let prevEnd = stmt.getStart(sf);
|
|
68
|
+
for (const member of ann.types) {
|
|
69
|
+
if (!ts.isTypeLiteralNode(member)) {
|
|
70
|
+
prevEnd = member.getEnd();
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const variant = readDiscriminantLiteral(member);
|
|
74
|
+
if (!variant) {
|
|
75
|
+
prevEnd = member.getEnd();
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
const leadingCommentText = sf.text.slice(prevEnd, member.getStart(sf));
|
|
79
|
+
callback({ variant, node: member, leadingCommentText });
|
|
80
|
+
prevEnd = member.getEnd();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/** Variant has at least one property beyond `type`. */
|
|
85
|
+
export function variantHasPayload(node) {
|
|
86
|
+
for (const m of node.members) {
|
|
87
|
+
if (!ts.isPropertySignature(m))
|
|
88
|
+
continue;
|
|
89
|
+
if (!m.name || !ts.isIdentifier(m.name))
|
|
90
|
+
continue;
|
|
91
|
+
if (m.name.text === 'type')
|
|
92
|
+
continue;
|
|
93
|
+
return true;
|
|
94
|
+
}
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=_msg-variants.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_msg-variants.js","sourceRoot":"","sources":["../../src/modules/_msg-variants.ts"],"names":[],"mappings":"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","sourcesContent":["// Shared helper for agent-protocol rules that iterate Msg union\n// variants and inspect leading JSDoc-style comments on each. The\n// compiler doesn't run typed-lint, so \"is this a Msg union?\" detection\n// uses simpler heuristics:\n//\n// 1. Type alias literally named `Msg`.\n// 2. Any type-alias whose name appears as the SECOND generic argument\n// of a `component<S, Msg, E>()` call in the same file.\n//\n// File-local only. Cross-file Msg detection would need the cross-file\n// resolver; for the v1 of these rules we accept the file-local scope\n// (matches the original Vite-plugin diagnostic).\n//\n// Each variant callback receives:\n// - `variant`: the discriminant string (the `type: '...'` literal).\n// - `node`: the TypeLiteral AST node for the variant.\n// - `leadingCommentText`: the source slice between the previous\n// variant's end (or the alias start) and this variant's start.\n// Includes JSDoc blocks, `|` tokens, and whitespace; regex-match\n// against it the same way the ESLint rules did.\n\nimport ts from 'typescript'\n\nexport interface MsgVariant {\n variant: string\n node: ts.TypeLiteralNode\n leadingCommentText: string\n}\n\nfunction readDiscriminantLiteral(typeLit: ts.TypeLiteralNode): string | null {\n for (const m of typeLit.members) {\n if (!ts.isPropertySignature(m)) continue\n if (!m.name || !ts.isIdentifier(m.name) || m.name.text !== 'type') continue\n const ann = m.type\n if (!ann || !ts.isLiteralTypeNode(ann)) continue\n const lit = ann.literal\n if (ts.isStringLiteral(lit)) return lit.text\n }\n return null\n}\n\n/** Names of type aliases that appear as the 2nd generic arg of `component<S,M,E>()`. */\nfunction collectComponentMsgArgNames(sf: ts.SourceFile): Set<string> {\n const out = new Set<string>()\n const walk = (n: ts.Node): void => {\n if (\n ts.isCallExpression(n) &&\n ts.isIdentifier(n.expression) &&\n n.expression.text === 'component' &&\n n.typeArguments &&\n n.typeArguments.length >= 2\n ) {\n const msgArg = n.typeArguments[1]!\n if (ts.isTypeReferenceNode(msgArg) && ts.isIdentifier(msgArg.typeName)) {\n out.add(msgArg.typeName.text)\n }\n }\n ts.forEachChild(n, walk)\n }\n walk(sf)\n return out\n}\n\nexport function forEachMsgVariant(sf: ts.SourceFile, callback: (v: MsgVariant) => void): void {\n const componentMsgNames = collectComponentMsgArgNames(sf)\n for (const stmt of sf.statements) {\n if (!ts.isTypeAliasDeclaration(stmt)) continue\n const aliasName = stmt.name.text\n if (aliasName !== 'Msg' && !componentMsgNames.has(aliasName)) continue\n const ann = stmt.type\n if (!ts.isUnionTypeNode(ann)) continue\n let prevEnd = stmt.getStart(sf)\n for (const member of ann.types) {\n if (!ts.isTypeLiteralNode(member)) {\n prevEnd = member.getEnd()\n continue\n }\n const variant = readDiscriminantLiteral(member)\n if (!variant) {\n prevEnd = member.getEnd()\n continue\n }\n const leadingCommentText = sf.text.slice(prevEnd, member.getStart(sf))\n callback({ variant, node: member, leadingCommentText })\n prevEnd = member.getEnd()\n }\n }\n}\n\n/** Variant has at least one property beyond `type`. */\nexport function variantHasPayload(node: ts.TypeLiteralNode): boolean {\n for (const m of node.members) {\n if (!ts.isPropertySignature(m)) continue\n if (!m.name || !ts.isIdentifier(m.name)) continue\n if (m.name.text === 'type') continue\n return true\n }\n return false\n}\n"]}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import ts from 'typescript';
|
|
2
|
+
/**
|
|
3
|
+
* Walk a SourceFile and collect every `component(...)` CallExpression.
|
|
4
|
+
* Used by per-target emission modules to capture their targets in `emit`
|
|
5
|
+
* (not `visit`) so the refs match the post-Phase-2b AST.
|
|
6
|
+
*
|
|
7
|
+
* Phase 2b's `transformCall*` hooks rebuild ancestor nodes via
|
|
8
|
+
* `ts.visitEachChild` whenever any descendant is rewritten — e.g. a
|
|
9
|
+
* `text()` rewrite inside a `component()` call's view ladder
|
|
10
|
+
* invalidates every component-call ref captured during the visitor
|
|
11
|
+
* walk (Phase 2). Calling `findComponentCalls(analysis.sourceFile)` in
|
|
12
|
+
* `emit` returns refs into the post-Phase-2b tree, which is the same
|
|
13
|
+
* tree the umbrella's per-statement visitor walks.
|
|
14
|
+
*/
|
|
15
|
+
export declare function findComponentCalls(sf: ts.SourceFile): ts.CallExpression[];
|
|
16
|
+
//# sourceMappingURL=_shared.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_shared.d.ts","sourceRoot":"","sources":["../../src/modules/_shared.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,MAAM,YAAY,CAAA;AAE3B;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,EAAE,CAAC,UAAU,GAAG,EAAE,CAAC,cAAc,EAAE,CAczE"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Internal helpers shared across modules. Not part of the public
|
|
2
|
+
// CompilerModule surface — strictly implementation glue.
|
|
3
|
+
import ts from 'typescript';
|
|
4
|
+
/**
|
|
5
|
+
* Walk a SourceFile and collect every `component(...)` CallExpression.
|
|
6
|
+
* Used by per-target emission modules to capture their targets in `emit`
|
|
7
|
+
* (not `visit`) so the refs match the post-Phase-2b AST.
|
|
8
|
+
*
|
|
9
|
+
* Phase 2b's `transformCall*` hooks rebuild ancestor nodes via
|
|
10
|
+
* `ts.visitEachChild` whenever any descendant is rewritten — e.g. a
|
|
11
|
+
* `text()` rewrite inside a `component()` call's view ladder
|
|
12
|
+
* invalidates every component-call ref captured during the visitor
|
|
13
|
+
* walk (Phase 2). Calling `findComponentCalls(analysis.sourceFile)` in
|
|
14
|
+
* `emit` returns refs into the post-Phase-2b tree, which is the same
|
|
15
|
+
* tree the umbrella's per-statement visitor walks.
|
|
16
|
+
*/
|
|
17
|
+
export function findComponentCalls(sf) {
|
|
18
|
+
const out = [];
|
|
19
|
+
const walk = (n) => {
|
|
20
|
+
if (ts.isCallExpression(n) &&
|
|
21
|
+
ts.isIdentifier(n.expression) &&
|
|
22
|
+
n.expression.text === 'component') {
|
|
23
|
+
out.push(n);
|
|
24
|
+
}
|
|
25
|
+
ts.forEachChild(n, walk);
|
|
26
|
+
};
|
|
27
|
+
walk(sf);
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=_shared.js.map
|