@aihu/compiler 1.0.0 → 1.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -12
- package/bin/aihu-compile.mjs +3 -3
- package/dist/index.d.ts +188 -26
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -6
- package/dist/index.js.map +1 -1
- package/package.json +12 -6
package/README.md
CHANGED
|
@@ -21,7 +21,7 @@ npm install @aihu/compiler
|
|
|
21
21
|
bun add @aihu/compiler
|
|
22
22
|
```
|
|
23
23
|
|
|
24
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
24
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
25
25
|
|
|
26
26
|
<!-- END_AUTOGEN: install -->
|
|
27
27
|
|
|
@@ -32,12 +32,12 @@ bun add @aihu/compiler
|
|
|
32
32
|
|
|
33
33
|
| | |
|
|
34
34
|
|---|---|
|
|
35
|
-
| **Version** | `1.
|
|
35
|
+
| **Version** | `1.1.1` |
|
|
36
36
|
| **Tier** | D — Compiler — Single-File Component (.aihu) → Web Component |
|
|
37
37
|
| **Published files** | 4 entries |
|
|
38
38
|
| **License** | MIT |
|
|
39
39
|
|
|
40
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
40
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
41
41
|
|
|
42
42
|
<!-- END_AUTOGEN: stats -->
|
|
43
43
|
|
|
@@ -50,7 +50,7 @@ bun add @aihu/compiler
|
|
|
50
50
|
|---|---|---|
|
|
51
51
|
| `.` | `./dist/index.js` | `—` |
|
|
52
52
|
|
|
53
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
53
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
54
54
|
|
|
55
55
|
<!-- END_AUTOGEN: exports -->
|
|
56
56
|
|
|
@@ -65,13 +65,18 @@ bun add @aihu/compiler
|
|
|
65
65
|
|
|
66
66
|
**Optional dependencies (platform-specific):**
|
|
67
67
|
|
|
68
|
-
- `@aihu/compiler-darwin-arm64` — `0.1.
|
|
69
|
-
- `@aihu/compiler-darwin-x64` — `0.1.
|
|
70
|
-
- `@aihu/compiler-linux-x64-gnu` — `0.1.
|
|
71
|
-
- `@aihu/compiler-linux-arm64-gnu` — `0.1.
|
|
72
|
-
- `@aihu/compiler-win32-x64-msvc` — `0.1.
|
|
68
|
+
- `@aihu/compiler-darwin-arm64` — `0.1.37`
|
|
69
|
+
- `@aihu/compiler-darwin-x64` — `0.1.37`
|
|
70
|
+
- `@aihu/compiler-linux-x64-gnu` — `0.1.37`
|
|
71
|
+
- `@aihu/compiler-linux-arm64-gnu` — `0.1.37`
|
|
72
|
+
- `@aihu/compiler-win32-x64-msvc` — `0.1.37`
|
|
73
|
+
- `@aihu/compiler-native-darwin-arm64` — `0.1.2`
|
|
74
|
+
- `@aihu/compiler-native-darwin-x64` — `0.1.2`
|
|
75
|
+
- `@aihu/compiler-native-linux-x64-gnu` — `0.1.2`
|
|
76
|
+
- `@aihu/compiler-native-linux-arm64-gnu` — `0.1.2`
|
|
77
|
+
- `@aihu/compiler-native-win32-x64-msvc` — `0.1.2`
|
|
73
78
|
|
|
74
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
79
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
75
80
|
|
|
76
81
|
<!-- END_AUTOGEN: deps -->
|
|
77
82
|
|
|
@@ -85,7 +90,7 @@ bun add @aihu/compiler
|
|
|
85
90
|
- [Macro Vocabulary spec](../../docs/superpowers/specs/2026-05-02-spec-macro-vocabulary.md)
|
|
86
91
|
- [Aihu framework root](../../README.md)
|
|
87
92
|
|
|
88
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
93
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
89
94
|
|
|
90
95
|
<!-- END_AUTOGEN: see-also -->
|
|
91
96
|
|
|
@@ -96,6 +101,6 @@ bun add @aihu/compiler
|
|
|
96
101
|
|
|
97
102
|
MIT — see [LICENSE](../../LICENSE).
|
|
98
103
|
|
|
99
|
-
<sub><i>Auto-generated against `@aihu/compiler@1.
|
|
104
|
+
<sub><i>Auto-generated against `@aihu/compiler@1.1.1`.</i></sub>
|
|
100
105
|
|
|
101
106
|
<!-- END_AUTOGEN: license -->
|
package/bin/aihu-compile.mjs
CHANGED
|
@@ -60,9 +60,9 @@ function isUsableExecutable(candidate) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
function resolveCompilerBinary() {
|
|
63
|
-
// 0. Env override + css-engine
|
|
64
|
-
if (process.env.
|
|
65
|
-
return process.env.
|
|
63
|
+
// 0. Env override + css-engine AIHU_COMPILE_BIN handshake (dev override).
|
|
64
|
+
if (process.env.AIHU_COMPILE_BIN) {
|
|
65
|
+
return process.env.AIHU_COMPILE_BIN
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
const descriptor = detectPlatform()
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,160 @@
|
|
|
1
|
+
//#region js/envelope.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* envelope.ts — backend dispatch for the single-parse envelope compile.
|
|
4
|
+
*
|
|
5
|
+
* Every compile request (`transform` / `compileToAst` / `compileRouteMeta`)
|
|
6
|
+
* routes memo-first, then through ONE of three backends, in order:
|
|
7
|
+
*
|
|
8
|
+
* 1. **native addon** (packages/compiler/src-native, napi) — in-process,
|
|
9
|
+
* zero spawn. Selected once per process by `_resolveCompileBackend()`.
|
|
10
|
+
* 2. **envelope CLI spawn** — the legacy `aihu-compile` spawn args PLUS
|
|
11
|
+
* `--envelope <options-json>`. A binary that knows the flag answers with
|
|
12
|
+
* one JSON envelope (single parse, every requested artifact); the JSON
|
|
13
|
+
* carries the `"envelope": 1` discriminant.
|
|
14
|
+
* 3. **legacy per-output spawn** — an OLDER binary ignores `--envelope`
|
|
15
|
+
* and answers with its normal single artifact (JS / AST JSON / route
|
|
16
|
+
* JSON), which the discriminant check detects. That output is used
|
|
17
|
+
* as-is, so stale binaries keep exactly their historical behavior —
|
|
18
|
+
* feature detection costs zero extra spawns.
|
|
19
|
+
*
|
|
20
|
+
* Backend selection is CACHED at first use (per module instance):
|
|
21
|
+
* `AIHU_COMPILER_NATIVE=0` forces spawn; an explicit `AIHU_COMPILE_BIN`
|
|
22
|
+
* binary pin forces spawn (working ON the compiler means
|
|
23
|
+
* the pinned binary must actually run — an addon silently shadowing it would
|
|
24
|
+
* reintroduce the exact quiet-wrong-answer failure the pin exists to prevent).
|
|
25
|
+
* The cache means css-engine's mid-build `AIHU_COMPILE_BIN` handshake
|
|
26
|
+
* (which programmatically sets the var AFTER the first transform) cannot
|
|
27
|
+
* de-select an already-active native backend.
|
|
28
|
+
*
|
|
29
|
+
* The memo's "binary stamp" (transform-memo.ts `_binStamp`) generalizes to
|
|
30
|
+
* whichever backend is active: the addon's `.node` file path (stat'd for
|
|
31
|
+
* mtime+size, so a rebuilt addon invalidates entries) when native, the
|
|
32
|
+
* resolved CLI binary path when spawning.
|
|
33
|
+
*/
|
|
34
|
+
/** The wire shape of a compile envelope (Rust `Envelope`, camelCase). */
|
|
35
|
+
interface CompileEnvelope {
|
|
36
|
+
envelope: number;
|
|
37
|
+
targets: Record<string, {
|
|
38
|
+
js?: string;
|
|
39
|
+
manifest?: string;
|
|
40
|
+
}>;
|
|
41
|
+
astJson?: string;
|
|
42
|
+
routeJson?: string;
|
|
43
|
+
diagnostics: unknown[];
|
|
44
|
+
}
|
|
45
|
+
/** Options forwarded to the Rust envelope API (Rust `EnvelopeOptions`). */
|
|
46
|
+
interface CompileEnvelopeOptions {
|
|
47
|
+
tag?: string;
|
|
48
|
+
path?: string;
|
|
49
|
+
targets?: string[];
|
|
50
|
+
emits?: Array<'js' | 'ast' | 'route' | 'manifest'>;
|
|
51
|
+
strictTemplates?: boolean;
|
|
52
|
+
exprParser?: string;
|
|
53
|
+
}
|
|
54
|
+
type CompileBackend = {
|
|
55
|
+
kind: 'native';
|
|
56
|
+
compileEnvelope: (source: string, optionsJson: string) => string;
|
|
57
|
+
stampPath: string;
|
|
58
|
+
} | {
|
|
59
|
+
kind: 'spawn';
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Resolve (once) which backend serves compiles for this process.
|
|
63
|
+
* @internal
|
|
64
|
+
*/
|
|
65
|
+
declare function _resolveCompileBackend(): CompileBackend;
|
|
66
|
+
/** Reset the cached backend (tests). @internal */
|
|
67
|
+
declare function _resetCompileBackend(): void;
|
|
68
|
+
/** A backend reply: either a parsed envelope, or a legacy single artifact. */
|
|
69
|
+
type EnvelopeReply = {
|
|
70
|
+
kind: 'envelope';
|
|
71
|
+
envelope: CompileEnvelope;
|
|
72
|
+
} | {
|
|
73
|
+
kind: 'legacy';
|
|
74
|
+
output: string;
|
|
75
|
+
};
|
|
76
|
+
/**
|
|
77
|
+
* Run one compile through the active backend.
|
|
78
|
+
*
|
|
79
|
+
* @param source the `.aihu` source (stdin for the spawn backend)
|
|
80
|
+
* @param legacyArgs the EXACT argv the pre-envelope spawn used for this call
|
|
81
|
+
* (`--stdin --tag … [--ast-json|--route-json|…]`). The spawn
|
|
82
|
+
* backend appends `--envelope <json>` to it, so an older
|
|
83
|
+
* binary that ignores the flag still answers the legacy
|
|
84
|
+
* request correctly.
|
|
85
|
+
* @param options the envelope options (must agree with `legacyArgs`)
|
|
86
|
+
* @internal
|
|
87
|
+
*/
|
|
88
|
+
declare function _compileViaBackend(source: string, legacyArgs: string[], options: CompileEnvelopeOptions): EnvelopeReply;
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region js/native.d.ts
|
|
91
|
+
/**
|
|
92
|
+
* @aihu/compiler native-addon loader — the in-process compile fast path.
|
|
93
|
+
*
|
|
94
|
+
* A `native.ts`-style loader in the mold of packages/server/src/native.ts:
|
|
95
|
+
* platform matrix → per-platform optionalDependency package → dev fallback,
|
|
96
|
+
* with explicit escape hatches and a cached three-state result. The addon
|
|
97
|
+
* (packages/compiler/src-native, napi-rs) exposes
|
|
98
|
+
* `compileEnvelope(source, optionsJson) → envelopeJson` — one boundary
|
|
99
|
+
* crossing per file — which `js/envelope.ts` routes `transform()` /
|
|
100
|
+
* `compileToAst()` / `compileRouteMeta()` through.
|
|
101
|
+
*
|
|
102
|
+
* States:
|
|
103
|
+
* - loaded: addon required successfully; compiles run in-process.
|
|
104
|
+
* - disabled: `AIHU_COMPILER_NATIVE=0` — the documented escape hatch.
|
|
105
|
+
* - unavailable: no addon for this platform / load failed. UNLIKE
|
|
106
|
+
* @aihu/server's fail-loud contract, a failed load here is a
|
|
107
|
+
* one-shot WARNING, not a throw: the CLI spawn path is a
|
|
108
|
+
* byte-identical fallback that always exists, so failing the
|
|
109
|
+
* whole build over a fast-path load would be strictly worse.
|
|
110
|
+
* The exception is `AIHU_COMPILER_NATIVE_ADDON=<path>` — an
|
|
111
|
+
* explicit override that fails loud (same doctrine as
|
|
112
|
+
* AIHU_COMPILE_BIN: a pinned path that doesn't load is a
|
|
113
|
+
* configuration error, and silently ignoring it hands back a
|
|
114
|
+
* plausible-looking wrong backend).
|
|
115
|
+
*/
|
|
116
|
+
interface CompilerNativeAddon {
|
|
117
|
+
compileEnvelope(source: string, optionsJson: string): string;
|
|
118
|
+
compilerVersion(): string;
|
|
119
|
+
}
|
|
120
|
+
type CompilerNativeState = {
|
|
121
|
+
kind: 'loaded';
|
|
122
|
+
addon: CompilerNativeAddon;
|
|
123
|
+
addonPath: string;
|
|
124
|
+
} | {
|
|
125
|
+
kind: 'disabled';
|
|
126
|
+
} | {
|
|
127
|
+
kind: 'unavailable';
|
|
128
|
+
error?: Error;
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Resolve (and cache) the native compiler addon. This is the ONLY module in
|
|
132
|
+
* @aihu/compiler that requires a napi `.node` file.
|
|
133
|
+
*/
|
|
134
|
+
declare function loadCompilerNative(): CompilerNativeState;
|
|
135
|
+
/** Returns the cached state kind (resolving if needed). @internal */
|
|
136
|
+
declare function _getCompilerNativeStateKind(): CompilerNativeState['kind'];
|
|
137
|
+
/** Reset the cached state. Used by tests that mock detection/env. @internal */
|
|
138
|
+
declare function _resetCompilerNative(): void;
|
|
139
|
+
//#endregion
|
|
140
|
+
//#region js/transform-memo.d.ts
|
|
141
|
+
/**
|
|
142
|
+
* FIFO size bound. 1024 entries × a few KB of compiled output ≈ a few MB —
|
|
143
|
+
* comfortably above any real app's file count (one entry per file × kind ×
|
|
144
|
+
* options variant) while keeping unbounded-session growth impossible.
|
|
145
|
+
* @internal
|
|
146
|
+
*/
|
|
147
|
+
declare const _MEMO_MAX_ENTRIES = 1024;
|
|
148
|
+
/** Test/diagnostic hook — wipe the memo and its counters. @internal */
|
|
149
|
+
declare function _clearTransformMemo(): void;
|
|
150
|
+
/** Test/diagnostic hook — current memo size + hit/miss/seed counters. @internal */
|
|
151
|
+
declare function _transformMemoStats(): {
|
|
152
|
+
size: number;
|
|
153
|
+
hits: number;
|
|
154
|
+
misses: number;
|
|
155
|
+
seeds: number;
|
|
156
|
+
};
|
|
157
|
+
//#endregion
|
|
1
158
|
//#region js/index.d.ts
|
|
2
159
|
interface VitePlugin {
|
|
3
160
|
readonly name: string;
|
|
@@ -19,11 +176,11 @@ interface VitePlugin {
|
|
|
19
176
|
*/
|
|
20
177
|
interface AihuCompilerPluginOptions {
|
|
21
178
|
/**
|
|
22
|
-
* When `true` (default), components classified as `'static'`
|
|
23
|
-
*
|
|
24
|
-
* shim that ships **zero**
|
|
25
|
-
*
|
|
26
|
-
* full runtime path.
|
|
179
|
+
* When `true` (default), components the compiler classified as `'static'`
|
|
180
|
+
* (read from the `// @aihu:island` marker via `_parseIslandMarker()`) are
|
|
181
|
+
* emitted with a minimal HTML-only registration shim that ships **zero**
|
|
182
|
+
* `@aihu/runtime` and `@aihu/signals` JS to the browser. Components
|
|
183
|
+
* classified as `'interactive'` retain the full runtime path.
|
|
27
184
|
*
|
|
28
185
|
* Setting `islands: false` opts every component back into the unified
|
|
29
186
|
* runtime path (Plan 3.2 baseline behaviour).
|
|
@@ -94,28 +251,33 @@ declare function _injectShadowMode(code: string, mode: 'light' | 'shadow'): stri
|
|
|
94
251
|
*/
|
|
95
252
|
declare function _globalizeAuthoredStyle(code: string): string;
|
|
96
253
|
/**
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
* **
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
* the
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
*
|
|
112
|
-
*
|
|
113
|
-
*
|
|
114
|
-
*
|
|
254
|
+
* Read the compiler's AUTHORITATIVE island classification from the
|
|
255
|
+
* `// @aihu:island <kind>` marker the Rust codegen emits for every component
|
|
256
|
+
* (`emit.rs`, wave 3c). A component is a **static** island (server-render
|
|
257
|
+
* only, no client hydration) or an **interactive** island (needs the signals
|
|
258
|
+
* reactivity runtime).
|
|
259
|
+
*
|
|
260
|
+
* This REPLACES the old `_classifyIsland` regex post-pass, which re-derived
|
|
261
|
+
* the answer by scanning generated code for `signal(`/`computed(`/`effect(`/…
|
|
262
|
+
* calls — a Derived-property VIOLATION (docs/plans/ssr-build-performance-
|
|
263
|
+
* findings.md §5: "the compiler answered that question when it emitted the
|
|
264
|
+
* code"). The compiler now computes the classification from the IR (the same
|
|
265
|
+
* fact-set that decides which owner-context primitives to import) and records
|
|
266
|
+
* it as this marker; the plugin merely reads it.
|
|
267
|
+
*
|
|
268
|
+
* Crucially the compiler classifies CONSERVATIVELY and knows things the regex
|
|
269
|
+
* could not: e.g. a component whose own body is inert but which declares
|
|
270
|
+
* `$prop`s is `interactive` (props are reactive inputs the parent drives, and
|
|
271
|
+
* its options-form emit is one the static-island shim cannot lower) — the old
|
|
272
|
+
* regex saw no `signal(` call and wrongly classified it `static`.
|
|
273
|
+
*
|
|
274
|
+
* Defaults to `'interactive'` if the marker is somehow absent (an old binary,
|
|
275
|
+
* or a future emit shape): the safe default never strips the runtime out from
|
|
276
|
+
* under a component that needs it.
|
|
115
277
|
*
|
|
116
278
|
* @internal
|
|
117
279
|
*/
|
|
118
|
-
declare function
|
|
280
|
+
declare function _parseIslandMarker(compiledCode: string): 'static' | 'interactive';
|
|
119
281
|
/**
|
|
120
282
|
* GX Phase 1 (#437-GX) — parse the `// @aihu:extract read=<v> call=<v>` code
|
|
121
283
|
* marker the Rust compiler emits for every server/universal build (the
|
|
@@ -214,7 +376,7 @@ declare function _buildDeferredHydration(compiledCode: string, elementTag: strin
|
|
|
214
376
|
* 2. Replace `defineElement(tag, defineComponent(setup))` with a tiny
|
|
215
377
|
* inline class that mounts the tree directly via `mount()` (which the
|
|
216
378
|
* arbor barrel already exports).
|
|
217
|
-
* 3. Tag the file with a `//
|
|
379
|
+
* 3. Tag the file with a `// AIHU_STATIC_ISLAND` comment so consumers
|
|
218
380
|
* can audit which routes shipped zero-JS-runtime.
|
|
219
381
|
*
|
|
220
382
|
* Falls back to the original code if the regex shape does not match
|
|
@@ -512,5 +674,5 @@ declare function compileRouteMeta(source: string, id?: string): RouteMeta | null
|
|
|
512
674
|
declare function _injectAutoWiring(code: string): string;
|
|
513
675
|
declare function aihuCompilerPlugin(options?: AihuCompilerPluginOptions): VitePlugin;
|
|
514
676
|
//#endregion
|
|
515
|
-
export { AihuCompilerPluginOptions, RouteMeta, SfcAst, SfcAttr, SfcMacroValue, SfcMeta, SfcNode, SfcStyleBlock, VIRTUAL_UTILITY_PREFIX, _buildDeferredHydration, _buildStaticIsland,
|
|
677
|
+
export { AihuCompilerPluginOptions, type CompileEnvelope, type CompileEnvelopeOptions, RouteMeta, SfcAst, SfcAttr, SfcMacroValue, SfcMeta, SfcNode, SfcStyleBlock, VIRTUAL_UTILITY_PREFIX, _MEMO_MAX_ENTRIES, _buildDeferredHydration, _buildStaticIsland, _clearTransformMemo, _compileViaBackend, _foldCssEngineStyles, _foldCssEngineStylesGlobal, _formatExtractCensus, _getCompilerNativeStateKind, _globalizeAuthoredStyle, _hashIdForUtilityCss, _injectAutoWiring, _injectShadowMode, _isLayoutFile, _layoutTag, _parseExtractMarker, _parseIslandMarker, _passivizeOutlet, _resetCompileBackend, _resetCompilerNative, _resolveCompileBackend, _transformMemoStats, aihuCompilerPlugin, compileRouteMeta, compileSidecar, compileToAst, kebabComponentTag, loadCompilerNative, transform };
|
|
516
678
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../js/index.ts"],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../js/envelope.ts","../js/native.ts","../js/transform-memo.ts","../js/index.ts"],"mappings":";;AAsCA;;;;;;;;;;;;;;AASA;;;;;;;;;;;;;AASA;;;;;UAlBiB,eAAA;EACf,QAAA;EACA,OAAA,EAAS,MAAA;IAAiB,EAAA;IAAa,QAAA;EAAA;EACvC,OAAA;EACA,SAAA;EACA,WAAA;AAAA;;UAIe,sBAAA;EACf,GAAA;EACA,IAAA;EACA,OAAA;EACA,KAAA,GAAQ,KAAA;EACR,eAAA;EACA,UAAA;AAAA;AAAA,KAGU,cAAA;EAEN,IAAA;EACA,eAAA,GAAkB,MAAA,UAAgB,WAAA;EAClC,SAAA;AAAA;EAEA,IAAA;AAAA;;;AA2FN;;iBAnFgB,sBAAA,CAAA,GAA0B,cAAA;;iBAoB1B,oBAAA,CAAA;;KA2BJ,aAAA;EACN,IAAA;EAAkB,QAAA,EAAU,eAAA;AAAA;EAC5B,IAAA;EAAgB,MAAA;AAAA;;;;;;;;;;;ACdtB;;iBDgDgB,kBAAA,CACd,MAAA,UACA,UAAA,YACA,OAAA,EAAS,sBAAA,GACR,aAAA;;;;AAvHH;;;;;;;;;;;;;;AASA;;;;;;;;;;UCdiB,mBAAA;EACf,eAAA,CAAgB,MAAA,UAAgB,WAAA;EAChC,eAAA;AAAA;AAAA,KAkDU,mBAAA;EACN,IAAA;EAAgB,KAAA,EAAO,mBAAA;EAAqB,SAAA;AAAA;EAC5C,IAAA;AAAA;EACA,IAAA;EAAqB,KAAA,GAAQ,KAAA;AAAA;;ADEnC;;;iBCegB,kBAAA,CAAA,GAAsB,mBAAA;;iBA6FtB,2BAAA,CAAA,GAA+B,mBAAA;;iBAK/B,oBAAA,CAAA;;;;ADrKhB;;;;;cEGa,iBAAA;;iBAyGG,mBAAA,CAAA;;iBAQA,mBAAA,CAAA;EACd,IAAA;EACA,IAAA;EACA,MAAA;EACA,KAAA;AAAA;;;UCrGQ,UAAA;EAAA,SACC,IAAA;EACT,OAAA;EACA,SAAA,IACE,MAAA,UACA,QAAA,0CAC+B,OAAA;EACjC,IAAA,IAAQ,EAAA,yCAA2C,OAAA;EACnD,SAAA,IACE,IAAA,UACA,EAAA,aACG,OAAA;IAAU,IAAA;IAAc,GAAA;EAAA;IAAiB,IAAA;IAAc,GAAA;EAAA;EHrBvB;EGuBrC,QAAA,IAAY,KAAA,GAAQ,KAAA,YAAiB,OAAA;AAAA;;;;UAMtB,yBAAA;EHzBP;;;;;AAKV;;;;;EG+BE,OAAA;EH5BsB;;;;;;AAWxB;;;;;AAoBA;;;;;AA2BA;;;EGTE,UAAA;EHUI;;;;;;;AAmCN;EGnCE,MAAA;;;;;;;;;;;;;EAcA,UAAA;AAAA;;;;;;;;;AF/CF;iBE2DgB,iBAAA,CAAkB,IAAA,UAAc,IAAA;;;;;;;;;;;iBAoBhC,uBAAA,CAAwB,IAAA;;AF3DxC;;;;;AA6FA;;;;;AAKA;;;;;;;;AClKA;;;;;AAyGA;;;iBCsDgB,kBAAA,CAAmB,YAAA;;AD9CnC;;;;;;;iBC2DgB,mBAAA,CAAoB,IAAA;EAAiB,IAAA;EAAc,IAAA;AAAA;;;AAnLvC;;;;;iBA+LZ,oBAAA,CACd,MAAA,EAAQ,WAAA;EAAsB,IAAA;EAAc,IAAA;AAAA;;;;;;;iBAiC9B,aAAA,CAAc,KAAA,UAAe,UAAA;;;;;;;iBAe7B,UAAA,CAAW,IAAA;;;;;;;;;;;AAtM3B;iBAqNgB,iBAAA,CAAkB,GAAA;;;;;;;;;AAjJlC;;;;iBAkLgB,gBAAA,CAAiB,IAAA;AA9JjC;;;;;AAoCA;;;;;AAaA;;;;;;;;AAjDA,iBA+PgB,uBAAA,CAAwB,YAAA,UAAsB,UAAA;AAlM9D;;;;;;;;;;AAkCA;;;;;AAeA;;;;;AAeA;;;;;AAiCA;AAjGA,iBA6SgB,kBAAA,CAAmB,YAAA,UAAsB,UAAA;;;;AA3GzD;;;;;iBAgKgB,SAAA,CACd,MAAA,UACA,EAAA,UACA,OAAA;EACE,UAAA;EACA,MAAA,sCA1D+B;EA4D/B,GAAA;EAPY;;;;;EAaZ,eAAA;AAAA;EAEC,IAAA;EAAc,GAAA;AAAA;;;;;;AAmJnB;;;;;AA4EA;;;;;AAcA;;;;;AA2BA;;;;;;;;;;;AAyBA;;;;;;;;;;;;;iBA9IgB,oBAAA,CAAqB,YAAA,UAAsB,GAAA;;;;;AA2J3D;;;;;AAOA;;;;cAtFa,sBAAA;AA4Fb;;;;;;;;;;;;AAAA,iBA9EgB,oBAAA,CAAqB,EAAA;;;;;;;;;;;;;;;;;;;;iBA2BrB,0BAAA,CACd,YAAA,UACA,GAAA,UACA,EAAA;EACG,IAAA;EAAc,SAAA;AAAA;;UAqBF,MAAA;EAoCX;EAlCJ,GAAA;EAoCI;EAlCJ,UAAA;EAmCI;EAjCJ,KAAA,EAAO,aAAA;EAmCH;EAjCJ,QAAA,EAAU,OAAA;EAiCiB;EA/B3B,IAAA,EAAM,OAAA;AAAA;AAAA,UAGS,aAAA;;EAEf,OAAA;EA8BI;EA5BJ,KAAA;AAAA;AAAA,UAGe,OAAA;EA0BM;EAxBrB,IAAA;AAAA;;KAIU,OAAA;EACN,IAAA;EAAiB,GAAA;EAAa,KAAA,EAAO,OAAA;EAAW,QAAA,EAAU,OAAA;AAAA;EAC1D,IAAA;EAAsB,IAAA;EAAc,KAAA,EAAO,OAAA;EAAW,QAAA,EAAU,OAAA;AAAA;EAChE,IAAA;EAAc,KAAA;AAAA;EACd,IAAA;EAAuB,IAAA;AAAA;EACvB,IAAA;EAAiB,QAAA,EAAU,KAAA;IAAQ,IAAA;IAAc,IAAA,EAAM,OAAA;EAAA;AAAA;EAEvD,IAAA;EACA,IAAA;EACA,IAAA;EACA,GAAA;EACA,GAAA;EACA,IAAA,EAAM,OAAA;EACN,SAAA,EAAW,OAAA;AAAA;EAEX,IAAA;EAAmB,IAAA;AAAA;AAoGzB;AAAA,KAjGY,OAAA;EACN,IAAA;EAAgB,IAAA;EAAc,KAAA;AAAA;EAC9B,IAAA;EAAiB,IAAA;EAAc,IAAA;AAAA;EAC/B,IAAA;EAAe,IAAA;EAAc,KAAA,EAAO,aAAA;AAAA;AAAA,KAE9B,aAAA;EACN,IAAA;EAAgB,KAAA;AAAA;EAChB,IAAA;EAAe,IAAA;AAAA;EACf,IAAA;AAAA;;;;;;AA+JN;;;;;AAiPA;;;;;;;;;;;;iBAxXgB,cAAA,CACd,MAAA,UACA,EAAA,WACA,OAAA;;;;;;EAME,eAAA;AAAA;AAAA,iBAsBY,YAAA,CAAa,MAAA,UAAgB,EAAA,YAAc,MAAA;;;;;;UAkC1C,SAAA;EACf,OAAA;EACA,IAAA;EACA,MAAA;EACA,UAAA;EACA,GAAA;EACA,MAAA;EACA,IAAA;;;;;;;EAOA,OAAA;IAAY,IAAA;IAAgB,IAAA;EAAA;;;;;;;;;EAS5B,IAAA;IAAS,IAAA;IAAe,OAAA;EAAA;AAAA;;;;;;;;;;;;;iBAeV,gBAAA,CAAiB,MAAA,UAAgB,EAAA,YAAc,SAAA;;;;;;;;iBAgC/C,iBAAA,CAAkB,IAAA;AAAA,iBAiPlB,kBAAA,CAAmB,OAAA,GAAU,yBAAA,GAA4B,UAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import{resolveCompilerBinary as e}from"./resolve-binary.js";import{execFileSync as t}from"node:child_process";import{basename as n}from"node:path";
|
|
1
|
+
import{resolveCompilerBinary as e}from"./resolve-binary.js";import{execFileSync as t}from"node:child_process";import{basename as n,dirname as r,resolve as i}from"node:path";import{existsSync as a,statSync as o}from"node:fs";import{createRequire as s}from"node:module";import{fileURLToPath as c}from"node:url";import{createHash as l}from"node:crypto";const u=r(c(import.meta.url));function d(){if(typeof process>`u`||!process.platform||!process.arch)return null;switch(`${process.platform}-${process.arch}`){case`darwin-arm64`:return{platformId:`darwin-arm64`,packageName:`@aihu/compiler-native-darwin-arm64`,nodeFile:`aihu-compiler-native.darwin-arm64.node`};case`darwin-x64`:return{platformId:`darwin-x64`,packageName:`@aihu/compiler-native-darwin-x64`,nodeFile:`aihu-compiler-native.darwin-x64.node`};case`linux-x64`:return{platformId:`linux-x64-gnu`,packageName:`@aihu/compiler-native-linux-x64-gnu`,nodeFile:`aihu-compiler-native.linux-x64-gnu.node`};case`linux-arm64`:return{platformId:`linux-arm64-gnu`,packageName:`@aihu/compiler-native-linux-arm64-gnu`,nodeFile:`aihu-compiler-native.linux-arm64-gnu.node`};case`win32-x64`:return{platformId:`win32-x64-msvc`,packageName:`@aihu/compiler-native-win32-x64-msvc`,nodeFile:`aihu-compiler-native.win32-x64-msvc.node`};default:return null}}let f=null,p=!1;function m(e){return typeof e==`object`&&!!e&&typeof e.compileEnvelope==`function`}function h(){if(f!==null)return f;if(typeof process<`u`&&process.env?.AIHU_COMPILER_NATIVE===`0`)return f={kind:`disabled`},f;let e=s(import.meta.url),t=process.env?.AIHU_COMPILER_NATIVE_ADDON;if(t){let n;try{n=e(t)}catch(e){throw Error(`[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON is set to '${t}', which failed to load: ${e.message}`)}if(!m(n))throw Error(`[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON module at '${t}' does not export compileEnvelope()`);return f={kind:`loaded`,addon:n,addonPath:t},f}let n=d();if(n===null)return f={kind:`unavailable`},f;let r=null;try{r=e.resolve(n.packageName)}catch{}let o=[i(u,`../src-native/aihu-compiler-native.node`),i(u,`../src-native/target/release/aihu-compiler-native.node`)],c=r?[r]:o.filter(e=>a(e));for(let t of c)try{let n=e(t);if(m(n))return f={kind:`loaded`,addon:n,addonPath:t},f;throw Error(`module at ${t} does not export compileEnvelope()`)}catch(e){return p||(p=!0,console.warn(`[@aihu/compiler] native addon found but failed to load; falling back to the aihu-compile spawn path (identical output, slower).
|
|
2
|
+
Candidate: ${t}\n Error: ${e.message}\n Reinstall @aihu/compiler (or rebuild: cargo build --release --manifest-path packages/compiler/src-native/Cargo.toml), or set AIHU_COMPILER_NATIVE=0 to silence this warning.`)),f={kind:`unavailable`,error:e},f}return f={kind:`unavailable`},f}function g(){return h().kind}function _(){f=null,p=!1}let v=null;function y(){if(v!==null)return v;let e=typeof process<`u`?process.env:void 0;if(e?.AIHU_COMPILER_NATIVE===`0`||e?.AIHU_COMPILE_BIN)return v={kind:`spawn`},v;let t=h();return v=t.kind===`loaded`?{kind:`native`,compileEnvelope:t.addon.compileEnvelope.bind(t.addon),stampPath:t.addonPath}:{kind:`spawn`},v}function b(){v=null}function x(){let e=y();return e.kind===`native`?e.stampPath:S()}function S(){return process.env.AIHU_COMPILE_BIN??e()}function ee(e){let t=e.trim();if(t.startsWith(`{`))try{let e=JSON.parse(t);if(typeof e==`object`&&e&&e.envelope===1)return{kind:`envelope`,envelope:e}}catch{}return{kind:`legacy`,output:e}}function C(e,n,r){let i=y(),a=JSON.stringify(r);return i.kind===`native`?{kind:`envelope`,envelope:JSON.parse(i.compileEnvelope(e,a))}:ee(t(S(),[...n,`--envelope`,a],{input:e,encoding:`utf8`}))}const te=1024,w=new Map;let T=0,E=0,D=0;function O(e){try{let t=o(e);return`${e}:${t.mtimeMs}:${t.size}`}catch{return e}}function k(e,t,n,r,i){return l(`sha256`).update(e).update(`\0`).update(n).update(`\0`).update(r).update(`\0`).update(O(i)).update(`\0`).update(t).digest(`hex`)}function A(e,t,n,r,i,a){let o=k(e,t,n,r,i),s=w.get(o);if(s!==void 0)return T++,s;let c=a();if(E++,w.size>=1024){let e=w.keys().next().value;e!==void 0&&w.delete(e)}return w.set(o,c),c}function j(e,t,n,r,i,a){let o=k(e,t,n,r,i);if(!w.has(o)){if(w.size>=1024){let e=w.keys().next().value;e!==void 0&&w.delete(e)}w.set(o,a),D++}}function M(){w.clear(),T=0,E=0,D=0}function N(){return{size:w.size,hits:T,misses:E,seeds:D}}function P(){return process.env.AIHU_COMPILE_BIN??e()}function F(e,t){return e.replace(/(defineElement\(\s*['"][^'"]+['"]\s*,\s*defineComponent\([^]*\))\s*\)/,(e,n)=>`${n}, { shadowMode: '${t}' })`)}function I(e){return e.replace(/\(ctx\.host as ShadowRoot\)\.adoptedStyleSheets\s*=\s*\[__style__\];?/,`if (!document.adoptedStyleSheets.includes(__style__)) document.adoptedStyleSheets = [...document.adoptedStyleSheets, __style__];`)}function L(e){return/^\/\/ @aihu:island (static|interactive)$/m.exec(e)?.[1]===`static`?`static`:`interactive`}function R(e){let t=/^\/\/ @aihu:extract read=(\S+) call=(\S+)$/m.exec(e);return t?{read:t[1],call:t[2]}:null}function z(e){if(e.size===0)return[];let t=new Map,n=new Map;for(let{read:r,call:i}of e.values())t.set(r,(t.get(r)??0)+1),n.set(i,(n.get(i)??0)+1);let r=[`[aihu] extract census — ${e.size} surface(s)`];for(let[e,n]of[...t.entries()].sort())r.push(` read=${e}: ${n}`);for(let[e,t]of[...n.entries()].sort())r.push(` call=${e}: ${t}`);return r}function ne(e){let t=/defineElement\(\s*['"]([^'"]+)['"]/m.exec(e);return t?t[1]??null:null}function B(e,t){let n=t.replace(/\\/g,`/`).replace(/^\.?\//,``).replace(/\/+$/,``);return n?e.replace(/\\/g,`/`).includes(`/${n}/`):!1}function V(e){return`aihu-layout-${e.toLowerCase()}`}function H(e){let t=``;for(let n=0;n<e.length;n++){let r=e.charAt(n);if(n>0&&r>=`A`&&r<=`Z`){let r=e.charAt(n-1),i=e.charAt(n+1);(r>=`a`&&r<=`z`||r>=`0`&&r<=`9`||r>=`A`&&r<=`Z`&&i>=`a`&&i<=`z`)&&(t+=`-`)}t+=r.toLowerCase()}return t}function U(e){return e.replace(/const createOutletBoundary = \(\) => \{[\s\S]*?return host;\s*\n\};/,`const createOutletBoundary = () => branch('div', { 'data-aihu-outlet': '' }, []);`)}function re(e,t){let n=e.replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/runtime'/,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`_hmrReplace`)||n.push(`_hmrReplace`),`import { ${n.join(`, `)} } from '@aihu/runtime'`}).replace(/\bdefineComponent\(/,`defineComponent(__aihu_setup__ = `),r=`
|
|
2
3
|
export { __aihu_setup__ as default }
|
|
3
4
|
|
|
4
5
|
if (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {
|
|
@@ -13,7 +14,7 @@ if (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {
|
|
|
13
14
|
})
|
|
14
15
|
}
|
|
15
16
|
`;return`let __aihu_setup__: ((ctx: any) => any) | undefined
|
|
16
|
-
`+n+r}function
|
|
17
|
+
`+n+r}function W(e,t){let n=e.replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/runtime'/,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`_hydrateOnVisible`)||n.push(`_hydrateOnVisible`),`import { ${n.join(`, `)} } from '@aihu/runtime'`}),r=n.replace(/defineElement\(\s*('[^']+'|"[^"]+")\s*,\s*defineComponent\(/,(e,t)=>`defineElement(${t}, __aihu_wrap_defer__(defineComponent(`);if(r===n)return e;let i=r.replace(/\)\s*\)\s*\nexport\s/,`)))
|
|
17
18
|
export `);return i===r&&(i=r.replace(/\)\s*\)\s*$/,`)))
|
|
18
19
|
`)),i===r?e:`
|
|
19
20
|
// Plan 3.3 (Islands) — defer attribute support. Wraps the constructor
|
|
@@ -32,13 +33,13 @@ function __aihu_wrap_defer__<T extends typeof HTMLElement>(Ctor: T): T {
|
|
|
32
33
|
}
|
|
33
34
|
return Ctor
|
|
34
35
|
}
|
|
35
|
-
`+i}function
|
|
36
|
+
`+i}function G(e,t){if(!/defineElement\(\s*['"][^'"]+['"]\s*,\s*defineComponent\(/.test(e))return e;let n=e.replace(/^\s*import\s*\{[^}]*\}\s*from\s*'@aihu\/runtime'\s*;?\s*$/m,``).replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/arbor'/,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`mount`)||n.push(`mount`),`import { ${n.join(`, `)} } from '@aihu/arbor'`}),r=JSON.stringify(t);return`// AIHU_STATIC_ISLAND — zero @aihu/runtime references\n${n.replace(/defineElement\(\s*['"][^'"]+['"]\s*,\s*defineComponent\(/,`customElements.define(${r}, class extends HTMLElement {\n connectedCallback() {\n const root = this.attachShadow({ mode: 'open' })\n const __aihu_setup__ = (`).replace(/\)\s*\)\s*$/,`)
|
|
36
37
|
mount(__aihu_setup__({ host: root, element: this }), root)
|
|
37
38
|
}
|
|
38
39
|
})
|
|
39
|
-
`)}`}function
|
|
40
|
+
`)}`}function K(e,r,i){let a=n(r,`.aihu`),o=H(a),s=/^[A-Z]/.test(a)&&!o.includes(`-`)?a:o,c=[`--stdin`,`--tag`,i?.tag??s,`--path`,r];if(i?.sidecarOut&&c.push(`--sidecar-out`,i.sidecarOut),i?.target&&c.push(`--target`,i.target),i?.strictTemplates&&c.push(`--strict-templates`),i?.sidecarOut)return{code:t(P(),c,{input:e,encoding:`utf8`}),map:null};let l=x(),u=i?.target??`universal`,d=i?.tag===void 0;return{code:A(`transform`,e,r,`target=${i?.target??``}|tag=${i?.tag??``}|strict=${i?.strictTemplates===!0}`,l,()=>{let t=C(e,c,{tag:i?.tag??s,path:r,targets:[u],emits:d?[`js`,`ast`,`route`]:[`js`],...i?.strictTemplates?{strictTemplates:!0}:{}});if(t.kind===`legacy`)return t.output;let n=t.envelope;d&&(n.astJson!==void 0&&j(`ast`,e,r,``,l,n.astJson),j(`route`,e,r,``,l,n.routeJson??`null`));let a=n.targets[u]?.js;if(a===void 0)throw Error(`[@aihu/compiler] envelope reply missing js for target '${u}'`);return a}),map:null}}function ie(e){return e.replace(/\\/g,`\\\\`).replace(/`/g,"\\`").replace(/\$\{/g,"\\${")}function q(e,t){if(!t.trim())return e;let n=ie(t),r=/(__style__\.replaceSync\(`)[^]*?(`\);)/;if(r.test(e))return e.replace(r,(e,t,r)=>`${t}${n}${r}`);let i=/defineComponent\(\s*\((__?[A-Za-z0-9_]+)\)\s*=>\s*\{/,a=i.exec(e);if(a==null)return e;let o=a[1]===`_ctx`?`ctx`:a[1],s=e.split(`
|
|
40
41
|
`),c=-1;for(let e=s.length-1;e>=0;e--){let t=(s[e]??``).trim();if(t.startsWith(`import `)||t.startsWith(`import{`)){c=e;break}}let l=`const __style__ = new CSSStyleSheet();\n__style__.replaceSync(\`${n}\`);`;c===-1?s.unshift(l):s.splice(c+1,0,l);let u=s.join(`
|
|
41
|
-
`);return u=u.replace(i,`defineComponent((${o}) => {\n (${o}.host as ShadowRoot).adoptedStyleSheets = [__style__];`),u}const
|
|
42
|
+
`);return u=u.replace(i,`defineComponent((${o}) => {\n (${o}.host as ShadowRoot).adoptedStyleSheets = [__style__];`),u}const J=`\0virtual:aihu-utility/`;function Y(e){let t=5381;for(let n=0;n<e.length;n++)t=(t*33^e.charCodeAt(n))>>>0;return t.toString(36)}function X(e,t,n){if(!t.trim())return null;let r=`${J}${Y(n)}.css`;return{code:`import ${JSON.stringify(r)};\n`+e,virtualId:r}}function ae(e,r,i){let a=[`--stdin`,`--tag`,r?n(r,`.aihu`):`Component`,`--sidecar-stdout`];return r&&a.push(`--path`,r),i?.strictTemplates&&a.push(`--strict-templates`),t(P(),a,{input:e,encoding:`utf8`,stdio:[`pipe`,`pipe`,`pipe`]})}function oe(e,t){let r=t?n(t,`.aihu`):`Component`,i=[`--stdin`,`--tag`,r,`--ast-json`];t&&i.push(`--path`,t);let a=x(),o=A(`ast`,e,t??``,``,a,()=>{let n=C(e,i,{tag:r,...t?{path:t}:{},emits:[`ast`]});if(n.kind===`legacy`)return n.output;let a=n.envelope.astJson;if(a===void 0)throw Error(`[@aihu/compiler] envelope reply missing astJson`);return a});return JSON.parse(o)}function se(e,t){let r=t?n(t,`.aihu`):`Component`,i=[`--stdin`,`--tag`,r,`--route-json`];t&&i.push(`--path`,t);let a=x(),o=A(`route`,e,t??``,``,a,()=>{let n=C(e,i,{tag:r,...t?{path:t}:{},emits:[`route`]});return n.kind===`legacy`?n.output:n.envelope.routeJson??`null`}).trim();return o===``||o===`null`?null:JSON.parse(o)}function Z(e){let t;t=e.includes(`from '@aihu/arbor'`)?e.replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/arbor'/,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`mount`)||n.push(`mount`),`import { ${n.join(`, `)} } from '@aihu/arbor'`}):`import { mount } from '@aihu/arbor'\n${e}`,/import\s+\{[^}]*\}\s+from\s+'@aihu\/signals'/.test(t)?t=t.replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/signals'/,(e,t)=>{if(e.startsWith(`import type`))return e;let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`signal`)||n.push(`signal`),`import { ${n.join(`, `)} } from '@aihu/signals'`}):/import.*from\s*'@aihu\/signals'/.test(t)?/import\s+type\s+\{[^}]*\}\s+from\s+'@aihu\/signals'/.test(t)&&!t.match(/import\s+\{[^}]*\}\s+from\s+'@aihu\/signals'/)&&(t=t.replace(/(import\s+type\s+\{[^}]*\}\s+from\s+'@aihu\/signals')/,(e,t)=>`${t}\nimport { signal } from '@aihu/signals'`)):t=t.replace(/import\s*\{[^}]*\}\s*from\s*'@aihu\/arbor'/,e=>`${e}\nimport { signal } from '@aihu/signals'`),t=t.replace(/import\s*\{([^}]*)\}\s*from\s*'@aihu\/runtime'/,(e,t)=>{let n=t.split(`,`).map(e=>e.trim()).filter(Boolean);return n.includes(`_setMount`)||n.push(`_setMount`),n.includes(`_setSignal`)||n.push(`_setSignal`),`import { ${n.join(`, `)} } from '@aihu/runtime'`});let n=t.split(`
|
|
42
43
|
`),r=-1;for(let e=n.length-1;e>=0;e--){let t=(n[e]??``).trim();if(t.startsWith(`import `)||t.startsWith(`import{`)){r=e;break}}return r!==-1&&(n.splice(r+1,0,`_setMount(mount)`,`_setSignal(signal)`,``),t=n.join(`
|
|
43
|
-
`)),t}let
|
|
44
|
+
`)),t}let Q,$=!1;async function ce(e,t){if(Q===null)return``;if(process.env.AIHU_COMPILE_BIN==null)try{process.env.AIHU_COMPILE_BIN=P()}catch{}if(Q===void 0)try{Q=await import(`@aihu/css-engine`)}catch{return Q=null,``}try{return Q.compileSfc(e,t)}catch(e){if(!$){$=!0;let t=e instanceof Error?e.message:String(e);console.warn(`[@aihu/compiler] @aihu/css-engine is installed but compileSfc() failed; utility classes will not emit. Original error: ${t}\nHint: ensure the native css-core binary is installed (install/upgrade @aihu/css-engine + its per-platform optional dep, or run \`cargo build --release -p aihu-css-core\` in a dev clone).`)}return``}}function le(e){let t=e?.islands!==!1,r=e?.shadowMode,i=e?.target,a=e?.layoutsDir??`src/layouts`,o=new Map,s=new Map;return{name:`aihu-compiler`,enforce:`pre`,buildEnd(){for(let e of z(s))console.info(e)},resolveId(e){return e.startsWith(`\0virtual:aihu-utility/`)?e:null},load(e){return e.startsWith(`\0virtual:aihu-utility/`)?o.get(e)??null:null},transform(e,c){let l=c.split(`?`)[0];if(!l.endsWith(`.aihu`))return;let u=this?.environment?.config?.consumer===`server`,d=i??(u?`server`:void 0);return(async()=>{let i=B(l,a),c=i?V(n(l,`.aihu`)):void 0,f=K(e,l,{...d?{target:d}:{},...c?{tag:c}:{}}),p=R(f.code);p&&s.set(l,p);let m=/^\/\/ @aihu:shadow (light|shadow)\b/m.exec(f.code)?.[1],h=/^\/\/ @aihu:shadow-default (light|shadow)\b/m.exec(f.code)?.[1]??(i?`light`:void 0),g=m??r??h,_=g==null?f.code:F(f.code,g);g===`light`&&(_=I(_)),i&&(_=U(_));let v=await ce(e,l);if(v)if(g===`light`){let e=X(_,v,l);e&&(o.set(e.virtualId,v),_=e.code)}else _=q(_,v);let y=ne(_),b,x=/defineComponent\(\s*\{[^]*?\bbase\s*:/.test(_);u?b=_:t&&y!==null&&!x&&g!==`light`&&L(_)===`static`?b=G(_,y):y===null?(b=_,b=Z(b)):(b=re(_,y),b=W(b,y),b=Z(b));try{let e=await import(`vite`);return u&&typeof e.transformWithOxc==`function`?{code:(await e.transformWithOxc(b,`component.ts`,{lang:`ts`,sourcemap:!1})).code,map:null}:`transformWithEsbuild`in e&&typeof e.transformWithEsbuild==`function`?{code:(await e.transformWithEsbuild(b,`component.ts`,{target:`esnext`,sourcemap:!1})).code,map:null}:{code:b,moduleType:`ts`,map:null}}catch{return{code:b,map:null}}})()}}}export{J as VIRTUAL_UTILITY_PREFIX,te as _MEMO_MAX_ENTRIES,W as _buildDeferredHydration,G as _buildStaticIsland,M as _clearTransformMemo,C as _compileViaBackend,q as _foldCssEngineStyles,X as _foldCssEngineStylesGlobal,z as _formatExtractCensus,g as _getCompilerNativeStateKind,I as _globalizeAuthoredStyle,Y as _hashIdForUtilityCss,Z as _injectAutoWiring,F as _injectShadowMode,B as _isLayoutFile,V as _layoutTag,R as _parseExtractMarker,L as _parseIslandMarker,U as _passivizeOutlet,b as _resetCompileBackend,_ as _resetCompilerNative,y as _resolveCompileBackend,N as _transformMemoStats,le as aihuCompilerPlugin,se as compileRouteMeta,ae as compileSidecar,oe as compileToAst,H as kebabComponentTag,h as loadCompilerNative,K as transform};
|
|
44
45
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../js/index.ts"],"sourcesContent":["/**\n * @aihu/compiler — TypeScript wrapper around the aihu-compile Rust binary.\n *\n * Exports:\n * transform(source, id) — compile a single .aihu file to TypeScript\n * aihuCompilerPlugin() — Vite plugin that wires transform() into the build\n */\nimport { execFileSync } from 'node:child_process'\nimport { basename } from 'node:path'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\n\n// Binary resolution: env var override, then the per-platform optionalDependency\n// package (`@aihu/compiler-<platform>`) with a workspace `target/` dev fallback —\n// see js/resolve-binary.ts (a clone of css-engine's resolver). The published\n// @aihu/compiler tarball ships only the JS shim (bin/aihu-compile.mjs); the\n// native binary arrives via the optionalDependency packages, so there is no\n// `../bin/aihu-compile` relative path anymore.\n//\n// Bug 6 fix — resolveBinPath() is CALL-TIME, not module-load-time. The Vite\n// plugin's `_maybeCompileUtilityCss` sets `process.env.SCRIBE_COMPILE_BIN` so\n// that css-engine's bundled copy of `compileToAst` spawns THIS compiler's\n// binary. Prior to this fix `binPath` was a module-scope const captured at\n// import time, so the env-var assignment was always too late and `compileSfc`\n// failed with ENOENT. Re-reading on every call is essentially free (an env\n// lookup, then a memoized resolve) and makes the SCRIBE_COMPILE_BIN handshake\n// actually work.\nfunction resolveBinPath(): string {\n return process.env.SCRIBE_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n// Minimal VitePlugin interface — avoids importing from 'vite' at compile time.\n// Structurally compatible with Vite's Plugin type.\ninterface VitePlugin {\n readonly name: string\n enforce?: 'pre' | 'post'\n resolveId?: (\n source: string,\n importer?: string,\n ) => string | null | undefined | Promise<string | null | undefined>\n load?: (id: string) => string | null | undefined | Promise<string | null | undefined>\n transform?: (\n code: string,\n id: string,\n ) => Promise<{ code: string; map: null }> | { code: string; map: null } | null | undefined\n /** GX Phase 1 (#437-GX) — end-of-build hook; prints the extract census. */\n buildEnd?: (error?: Error) => void | Promise<void>\n}\n\n/**\n * Options for `aihuCompilerPlugin()` (Plan 3.3 — Islands).\n */\nexport interface AihuCompilerPluginOptions {\n /**\n * When `true` (default), components classified as `'static'` by\n * `_classifyIsland()` are emitted with a minimal HTML-only registration\n * shim that ships **zero** `@aihu/runtime` and `@aihu/signals` JS to\n * the browser. Components classified as `'interactive'` retain the\n * full runtime path.\n *\n * Setting `islands: false` opts every component back into the unified\n * runtime path (Plan 3.2 baseline behaviour).\n */\n islands?: boolean\n\n /**\n * Project-wide rendering mode applied to every `.aihu` SFC compiled\n * by this plugin instance. When set, the plugin post-processes the\n * compiled JS to inject `, { shadowMode: '<mode>' }` as the third arg\n * to the emitted `defineElement(tag, defineComponent(...))` call.\n *\n * BINARY vocabulary (DA4 #437):\n * - `'shadow'` — shadow DOM (`attachShadow({ mode: 'open' })` internally;\n * open is the only browser mode aihu's composition/hydration\n * can use). `this.shadowRoot` is the non-null root.\n * - `'light'` — **no shadow root.** The component mounts into its own\n * element (`this.shadowRoot === null`). Required for global\n * utility-class CSS frameworks like Tailwind, UnoCSS, Pico\n * that rely on the cascade.\n *\n * Per-file override: the `$shadow: 'light' | 'shadow'` macro outranks this\n * config. Unset, pages/layouts default to `'light'` and leaves to\n * `'shadow'`.\n */\n shadowMode?: 'light' | 'shadow'\n\n /**\n * Build target threaded to the compiler binary (`--target`). Defaults to the\n * compiler's `universal` target (current behaviour). Set to `'client'` for a\n * browser bundle that must NOT ship the server `__agentBinding` (policy) and\n * instead gets the policy-free `@agent` opaque-ID dispatcher + the per-instance\n * `_registerAgentDispatcher` wiring the capability bridge reads after mount.\n * See `examples/agent-driven-demo`.\n */\n target?: 'client' | 'server' | 'universal'\n\n /**\n * Directory (relative to the project root) holding layout SFCs. Default:\n * `'src/layouts'`. Files under this directory are compiled in **layout mode**:\n * their custom element is registered under the namespaced tag\n * `aihu-layout-<stem>` (a layout stem like `app` is not a valid custom-element\n * name on its own), and their `<outlet>` lowers to a **passive**\n * `data-aihu-outlet` marker rather than the reactive route-driven boundary —\n * because `@aihu/app`'s client renderer fills the marker imperatively and the\n * reactive boundary would otherwise clear it on mount.\n *\n * Kept in sync with `@aihu/router`'s `layoutTagFor()` (`virtual:aihu-layouts`).\n */\n layoutsDir?: string\n}\n\n/**\n * Inject `{ shadowMode: '...' }` as the third argument to the emitted\n * `defineElement('tag', defineComponent(...))` call. The compiler emits\n * exactly two arguments today; this rewrites the closing of the\n * defineElement call to include the options object. Idempotent — leaves\n * code untouched when the closer is not in the expected shape.\n *\n * @internal\n */\nexport function _injectShadowMode(code: string, mode: 'light' | 'shadow'): string {\n // Match the trailing `))` that closes `defineElement(tag, defineComponent(setup))`.\n // The compiler always emits this exact two-paren close as the final tokens of\n // the defineElement call — we anchor on it and append the options object.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] is valid JS — matches any char including newlines\n const re = /(defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\([^]*\\))\\s*\\)/\n const replaced = code.replace(re, (_m, inner: string) => `${inner}, { shadowMode: '${mode}' })`)\n return replaced\n}\n\n/**\n * Light-DOM (`shadowMode:'light'`) recipes: redirect the authored `@style`\n * block's per-instance `host.adoptedStyleSheets = [__style__]` assignment to\n * `document.adoptedStyleSheets` so the recipe's class-scoped CSS reaches the\n * global cascade (a light-DOM host has no shadow root, making the original\n * setter a silent no-op). The module-level `__style__` is shared across\n * instances; the `includes` guard keeps the global adoption idempotent.\n *\n * @internal\n */\nexport function _globalizeAuthoredStyle(code: string): string {\n // The Rust codegen emits exactly: `(ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];`\n const re = /\\(ctx\\.host as ShadowRoot\\)\\.adoptedStyleSheets\\s*=\\s*\\[__style__\\];?/\n return code.replace(\n re,\n 'if (!document.adoptedStyleSheets.includes(__style__)) document.adoptedStyleSheets = [...document.adoptedStyleSheets, __style__];',\n )\n}\n\n/**\n * Classify the compiled output of a single `.aihu` module as either a\n * **static** island (no reactive state — purely declarative DOM) or an\n * **interactive** island (uses the signals reactivity system).\n *\n * The heuristic is intentionally conservative: any source-level reference\n * to a primitive that requires the `defineComponent` owner context flips\n * the file to `'interactive'`. False positives (e.g. a string literal\n * containing `signal(`) are tolerable — they only forfeit the static-island\n * optimisation. False negatives are forbidden: a static-classified file\n * MUST NOT depend on the signals runtime at execution time.\n *\n * Owner-requiring primitives covered:\n * - `signal(`, `computed(`, `effect(`, `setSignal(` (signals runtime)\n * - `onMount(`, `onCleanup(` (lifecycle hooks — throw `no owner` outside\n * `defineComponent` because they push into the active owner's mount/\n * cleanup queues)\n *\n * Plan 3.3 / acceptance criterion 1.\n *\n * @internal\n */\nexport function _classifyIsland(compiledCode: string): 'static' | 'interactive' {\n // Match call sites of the reactive + lifecycle primitives. Use word-boundary\n // anchors so identifiers like `mySignal(` or `__effect(` do not trip the\n // heuristic. The `(` is required so that bare imports of the names in an\n // unused `import { signal }` line do not flip an otherwise-static module.\n return /\\b(?:signal|computed|effect|setSignal|onMount|onCleanup)\\s*\\(/.test(compiledCode)\n ? 'interactive'\n : 'static'\n}\n\n/**\n * GX Phase 1 (#437-GX) — parse the `// @aihu:extract read=<v> call=<v>` code\n * marker the Rust compiler emits for every server/universal build (the\n * resolved policy, the ratified default included). Phase 1 consumes it only\n * for the build census below; Phase 4 (E2) will read the SAME marker for\n * governed chunk routing.\n * @internal\n */\nexport function _parseExtractMarker(code: string): { read: string; call: string } | null {\n const m = /^\\/\\/ @aihu:extract read=(\\S+) call=(\\S+)$/m.exec(code)\n return m ? { read: m[1] as string, call: m[2] as string } : null\n}\n\n/**\n * GX Phase 1 (#437-GX) — format the per-value extract census (the DA-e census\n * pattern from #437: every build PRINTS the posture distribution, so the\n * default-vs-declared migration story stays visible rather than silent).\n * Returns the printable lines; pure so tests can assert the counts.\n * @internal\n */\nexport function _formatExtractCensus(\n census: ReadonlyMap<string, { read: string; call: string }>,\n): string[] {\n if (census.size === 0) return []\n const readCounts = new Map<string, number>()\n const callCounts = new Map<string, number>()\n for (const { read, call } of census.values()) {\n readCounts.set(read, (readCounts.get(read) ?? 0) + 1)\n callCounts.set(call, (callCounts.get(call) ?? 0) + 1)\n }\n const lines = [`[aihu] extract census — ${census.size} surface(s)`]\n for (const [value, n] of [...readCounts.entries()].sort()) lines.push(` read=${value}: ${n}`)\n for (const [value, n] of [...callCounts.entries()].sort()) lines.push(` call=${value}: ${n}`)\n return lines\n}\n\n/**\n * Extract the custom element tag name from compiler-emitted code.\n * The compiler always emits `defineElement('tag-name', ...)` as the\n * first call — pull the first string literal argument.\n * Returns `null` if no `defineElement` call is found.\n * @internal\n */\nfunction _extractElementTag(code: string): string | null {\n const m = /defineElement\\(\\s*['\"]([^'\"]+)['\"]/m.exec(code)\n return m ? (m[1] ?? null) : null\n}\n\n/**\n * Is `rawId` a layout SFC (a `.aihu` file under the configured layouts dir)?\n * Root-independent: matches the `<layoutsDir>/` segment anywhere in the path,\n * which is sufficient because the layouts dir is a project-relative convention.\n * @internal\n */\nexport function _isLayoutFile(rawId: string, layoutsDir: string): boolean {\n const ld = layoutsDir\n .replace(/\\\\/g, '/')\n .replace(/^\\.?\\//, '')\n .replace(/\\/+$/, '')\n if (!ld) return false\n return rawId.replace(/\\\\/g, '/').includes(`/${ld}/`)\n}\n\n/**\n * Layout custom-element tag for a filename stem. MUST match\n * `@aihu/router`'s `layoutTagFor()` so the generated `virtual:aihu-layouts`\n * map and the registered element agree on the tag.\n * @internal\n */\nexport function _layoutTag(stem: string): string {\n return `aihu-layout-${stem.toLowerCase()}`\n}\n\n/**\n * O1a (tag naming) — JS mirror of the Rust compiler's\n * `tags::kebab_component_tag` (packages/compiler/src/tags.rs). PascalCase→kebab,\n * else lowercase-verbatim: inserts '-' before an uppercase letter when the\n * previous char is lowercase/digit, OR (acronym boundary) the previous char is\n * uppercase and the next is lowercase; then lowercases all. Applied to the\n * file stem before it is passed as `--tag` so the JS driver's define-name\n * matches the Rust one (`UserCard.aihu` → `user-card`). Validation/erroring\n * (C450) is owned by the Rust compiler — this is the infallible transform only.\n * @internal\n */\nexport function kebabComponentTag(raw: string): string {\n let out = ''\n for (let i = 0; i < raw.length; i++) {\n // charAt (not raw[i]) returns string, never string|undefined — satisfies\n // noUncheckedIndexedAccess; charAt(i+1) yields '' past the end, matching\n // the \"no next char\" case.\n const c = raw.charAt(i)\n if (i > 0 && c >= 'A' && c <= 'Z') {\n const prev = raw.charAt(i - 1)\n const next = raw.charAt(i + 1)\n const prevLower = prev >= 'a' && prev <= 'z'\n const prevDigit = prev >= '0' && prev <= '9'\n const prevUpper = prev >= 'A' && prev <= 'Z'\n const nextLower = next >= 'a' && next <= 'z'\n if (prevLower || prevDigit || (prevUpper && nextLower)) out += '-'\n }\n out += c.toLowerCase()\n }\n return out\n}\n\n/**\n * Collapse the reactive `<outlet>` boundary the Rust codegen emits into a\n * passive `data-aihu-outlet` marker. Layout SFCs are rendered by `@aihu/app`'s\n * imperative client renderer, which fills the marker itself; the default\n * boundary's mount-time `effect()` reads `useRoute()` (null under the imperative\n * path) and clears the marker, which would wipe the page the renderer inserts.\n *\n * Anchors on the exact `const createOutletBoundary = () => { … return host; };`\n * block the codegen emits (`packages/compiler/src/codegen/emit.rs`). No-op when\n * the layout declares no `<outlet>`.\n * @internal\n */\nexport function _passivizeOutlet(code: string): string {\n return code.replace(\n /const createOutletBoundary = \\(\\) => \\{[\\s\\S]*?return host;\\s*\\n\\};/,\n `const createOutletBoundary = () => branch('div', { 'data-aihu-outlet': '' }, []);`,\n )\n}\n\n/**\n * Instrument a compiled `.aihu` module with HMR support.\n *\n * The compiler always emits:\n *\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { ... }))\n *\n * This function:\n *\n * 1. Adds `_hmrReplace` to the `@aihu/runtime` import.\n * 2. Prepends a module-level slot variable `__aihu_setup__`.\n * 3. Rewrites the single `defineComponent(` call so the setup function\n * is captured via an assignment expression:\n * `defineComponent(__aihu_setup__ = ` (valid JS; assignment has\n * lower precedence than arrow fn, so `defineComponent` still\n * receives the function as its argument).\n * 4. Appends `export { __aihu_setup__ as default }` so that Vite's\n * `import.meta.hot.accept` callback receives the new setup via\n * `newModule.default` on hot reload.\n * 5. Appends the `import.meta.hot.accept` block, gated on `__DEV__`.\n *\n * The `__DEV__` guard ensures production bundlers (where they replace\n * `__DEV__` with `false`) dead-code-eliminate the entire HMR block.\n *\n * @internal\n */\nfunction _buildHmrCode(compiledCode: string, elementTag: string): string {\n // Step 1 — add _hmrReplace to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hmrReplace')) parts.push('_hmrReplace')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Step 2+3 — prepend slot variable and rewrite the defineComponent call.\n // Compiler emits exactly one `defineComponent(` followed by a function expr.\n // Rewrite: defineComponent(fn) → defineComponent(__aihu_setup__ = fn)\n // Assignment expression evaluates to `fn`, so defineComponent still\n // receives the setup function as its first argument unchanged.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const preamble = `let __aihu_setup__: ((ctx: any) => any) | undefined\\n`\n\n const patchedBody = withImport.replace(/\\bdefineComponent\\(/, 'defineComponent(__aihu_setup__ = ')\n\n const tag = JSON.stringify(elementTag)\n // Step 4+5 — postamble with default export and HMR acceptance.\n const postamble = `\nexport { __aihu_setup__ as default }\n\nif (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (!newModule) return\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const newSetup = (newModule as any)['default']\n if (typeof newSetup !== 'function') return\n document.querySelectorAll(${tag}).forEach((el) => {\n _hmrReplace(el as HTMLElement, newSetup)\n })\n })\n}\n`\n\n return preamble + patchedBody + postamble\n}\n\n/**\n * Rewrite an interactive-island module so its `connectedCallback` waits\n * for the element to scroll into view before mounting. Plan 3.3 — applied\n * only when the consumer adds `defer` to the custom element tag (e.g.\n * `<my-counter defer>`); the runtime helper checks the attribute and\n * either mounts immediately or registers an `IntersectionObserver`.\n *\n * Implementation: the helper is added as a `_hydrateOnVisible` import\n * from `@aihu/runtime`, and the compiler-emitted `defineElement(...)`\n * call is wrapped in a `defineElement` that intercepts `connectedCallback`\n * to honour the `defer` attribute.\n *\n * The whole indirection is tree-shaken when no `.aihu` module reaches\n * this branch, because `_hydrateOnVisible` is exported from its own\n * sibling module inside `@aihu/runtime`.\n *\n * @internal\n */\nexport function _buildDeferredHydration(compiledCode: string, elementTag: string): string {\n // Add _hydrateOnVisible to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hydrateOnVisible')) parts.push('_hydrateOnVisible')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Wrap the class returned by defineComponent BEFORE defineElement\n // consumes it. The HTML spec caches lifecycle callbacks at\n // customElements.define() time, so we MUST mutate the prototype\n // before that call — not after. We accomplish this with a synchronous\n // helper invoked between defineComponent and defineElement.\n //\n // Source pattern (compiler-emitted):\n // defineElement('tag', defineComponent((_ctx) => { ... }))\n //\n // After this rewrite:\n // defineElement('tag', __aihu_wrap_defer__(defineComponent((_ctx) => { ... })))\n //\n // …with __aihu_wrap_defer__ defined in the appended preamble.\n const patched = withImport.replace(\n /defineElement\\(\\s*('[^']+'|\"[^\"]+\")\\s*,\\s*defineComponent\\(/,\n (_m, tagLit: string) => `defineElement(${tagLit}, __aihu_wrap_defer__(defineComponent(`,\n )\n // Match the closing `))` of the defineElement call. The HMR pass may\n // have inserted `__aihu_setup__ = ` before the inner function, but\n // the trailing `))` shape is unchanged. Replace exactly one occurrence\n // by anchoring on end-of-string trim; bail if the shape does not match.\n if (patched === withImport) {\n // The expected `defineElement(<tag>, defineComponent(` shape was not\n // present (e.g. compiler output changed). Skip defer wrapping rather\n // than emit broken code.\n return compiledCode\n }\n // Add a trailing `)` to balance the extra `(` from __aihu_wrap_defer__.\n // Source shape after _buildHmrCode is:\n // defineElement('tag', defineComponent(__aihu_setup__ = (_ctx) => {...}))\n // export { __aihu_setup__ as default }\n // if (typeof __DEV__ !== ...) { ... }\n // We must close BEFORE the export line. Match the first `))` followed\n // by a newline and `export` (or end-of-string for the unwrapped case).\n let balanced = patched.replace(/\\)\\s*\\)\\s*\\nexport\\s/, ')))\\nexport ')\n if (balanced === patched) {\n // No HMR postamble — the `))` is at end-of-string.\n balanced = patched.replace(/\\)\\s*\\)\\s*$/, ')))\\n')\n }\n if (balanced === patched) {\n // Could not find the matching `))` — bail out.\n return compiledCode\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const helper = `\n// Plan 3.3 (Islands) — defer attribute support. Wraps the constructor\n// returned by defineComponent so instances bearing the \\`defer\\` attribute\n// hydrate lazily via IntersectionObserver. Bare instances retain the\n// eager Plan 3.2 hydration path.\nfunction __aihu_wrap_defer__<T extends typeof HTMLElement>(Ctor: T): T {\n const orig = (Ctor.prototype as unknown as { connectedCallback?: () => void }).connectedCallback\n if (typeof orig !== 'function') return Ctor\n ;(Ctor.prototype as unknown as { connectedCallback: () => void }).connectedCallback = function (this: HTMLElement) {\n if (this.hasAttribute('defer')) {\n _hydrateOnVisible(this, () => orig.call(this))\n } else {\n orig.call(this)\n }\n }\n return Ctor\n}\n`\n void elementTag\n return helper + balanced\n}\n\n/**\n * Build a static-island shim for a compiled module.\n *\n * The compiled module emitted by the Rust codegen has the shape:\n *\n * import { branch, leaf, slot } from '@aihu/arbor'\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { return <tree> }))\n *\n * For a static island we know `<tree>` contains no `signal(`/`computed(`\n * calls. We can therefore:\n *\n * 1. Drop the `@aihu/runtime` import (saves ~600 B gz of defineComponent\n * + defineElement + bootstrap glue).\n * 2. Replace `defineElement(tag, defineComponent(setup))` with a tiny\n * inline class that mounts the tree directly via `mount()` (which the\n * arbor barrel already exports).\n * 3. Tag the file with a `// SCRIBE_STATIC_ISLAND` comment so consumers\n * can audit which routes shipped zero-JS-runtime.\n *\n * Falls back to the original code if the regex shape does not match\n * (defensive: a future compiler change must opt back into static-island\n * emission explicitly rather than silently break).\n *\n * @internal\n */\nexport function _buildStaticIsland(compiledCode: string, elementTag: string): string {\n // Confirm the shape we expect: a single defineElement(...) call wrapping\n // a single defineComponent(...) call. Bail out otherwise.\n const callRe = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/\n if (!callRe.test(compiledCode)) return compiledCode\n\n // Strip the `@aihu/runtime` import line entirely — static islands\n // don't reference defineComponent/defineElement after the rewrite.\n const withoutRuntimeImport = compiledCode.replace(\n /^\\s*import\\s*\\{[^}]*\\}\\s*from\\s*'@aihu\\/runtime'\\s*;?\\s*$/m,\n '',\n )\n\n // Ensure `mount` is imported from @aihu/arbor (it already exposes\n // branch/leaf/slot, so we just append `mount` to the existing list).\n const withArborMount = withoutRuntimeImport.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n\n // Replace `defineElement('tag', defineComponent((_ctx) => { ... }))`\n // with an inline `customElements.define` whose connectedCallback mounts\n // the static tree. The setup function is captured verbatim by replacing\n // the wrapping calls with anonymous-IIFE bookends.\n const tagJson = JSON.stringify(elementTag)\n const rewritten = withArborMount\n .replace(\n /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/,\n `customElements.define(${tagJson}, class extends HTMLElement {\\n connectedCallback() {\\n const root = this.attachShadow({ mode: 'open' })\\n const __aihu_setup__ = (`,\n )\n .replace(\n /\\)\\s*\\)\\s*$/,\n `)\\n mount(__aihu_setup__({ host: root, element: this }), root)\\n }\\n})\\n`,\n )\n\n return `// SCRIBE_STATIC_ISLAND — zero @aihu/runtime references\\n${rewritten}`\n}\n\n/**\n * Compile a .aihu source string to TypeScript.\n * map is null — source maps are deferred to v1 (OQ-C8)\n *\n * B3b — when `sidecarOut` is provided, also writes the per-SFC `.aihu.ts`\n * sidecar at that path. Callers (e.g. the Vite plugin) typically pass\n * `<source-id>.ts` so `tsc --noEmit` discovers per-SFC template expressions.\n */\nexport function transform(\n source: string,\n id: string,\n options?: {\n sidecarOut?: string\n target?: 'client' | 'server' | 'universal'\n /** Override the registered custom-element tag (default: file stem). Used for layouts. */\n tag?: string\n /**\n * #486 step 4 — emit the sidecar's attribute/component-prop type layer\n * (`--strict-templates`). Affects only the type-check surface written to\n * `sidecarOut`; the compiled JS is identical either way. Default off.\n */\n strictTemplates?: boolean\n },\n): { code: string; map: null } {\n // O1a (tag naming): normalize the stem so the JS driver's define-name\n // matches the Rust compiler's (`UserCard.aihu` → `user-card`). When a\n // component-shaped (uppercase-first) stem normalizes to a hyphen-less name\n // (`Comment.aihu` → `comment`), pass the RAW stem instead so the Rust\n // compiler surfaces its C450 error — the JS never validates or errors, but\n // it must not mask the error by pre-lowercasing. An explicit `options.tag`\n // (e.g. `_layoutTag` for layouts) passes through untouched.\n const rawStem = basename(id, '.aihu')\n const kebabStem = kebabComponentTag(rawStem)\n const stem = /^[A-Z]/.test(rawStem) && !kebabStem.includes('-') ? rawStem : kebabStem\n const args = ['--stdin', '--tag', options?.tag ?? stem, '--path', id]\n if (options?.sidecarOut) {\n args.push('--sidecar-out', options.sidecarOut)\n }\n // T6 (go-public demo) — thread the build target so a client bundle gets the\n // policy-free `@agent` dispatcher (and the per-instance registration the\n // capability bridge needs) instead of the server `__agentBinding`. Defaults to\n // the compiler's `universal` target when omitted (existing behaviour).\n if (options?.target) {\n args.push('--target', options.target)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n const code = execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n })\n return {\n code,\n map: null, // source maps deferred to v1 (OQ-C8)\n }\n}\n\n/**\n * Escape a CSS string for safe interpolation inside a JS template literal.\n * The Rust codegen places the authored `@style` body raw inside a backtick\n * literal, so it already assumes no backticks in `@style`. css-engine output\n * (theme tokens + utility rules) likewise never contains backticks, but we\n * escape `\\`, `` ` `` and `${` defensively so a future token value can't\n * break out of the literal.\n *\n * @internal\n */\nfunction _escapeForTemplateLiteral(css: string): string {\n return css.replace(/\\\\/g, '\\\\\\\\').replace(/`/g, '\\\\`').replace(/\\$\\{/g, '\\\\${')\n}\n\n/**\n * Fold css-engine-produced scoped CSS into a compiled `.aihu` module.\n *\n * The Rust codegen emits the authored `@style` block (when present) as:\n *\n * const __style__ = new CSSStyleSheet();\n * __style__.replaceSync(`<authored css>`);\n * defineElement('tag', defineComponent((ctx) => {\n * (ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];\n * return ...\n * }))\n *\n * css-engine's `compileSfc` output is the COMPLETE per-SFC stylesheet:\n * `:host` theme tokens, the variant-resolved utility-class rules, AND the\n * folded authored `@style` block (under an `authored @style` CSS comment).\n * So it is authoritative — we adopt it as the single shadow `<style>` and\n * the authored `@style` keeps emitting through it (acceptance: \"@style still\n * emits correctly alongside\").\n *\n * Two shapes are handled:\n *\n * 1. **SFC has an `@style` block** — the Rust codegen already declared\n * `__style__` with the raw `@style` body. We REPLACE that body with the\n * css-engine output (which already CONTAINS the `@style` block) so the\n * `@style` rules are not duplicated. The existing `adoptedStyleSheets`\n * assignment is reused unchanged.\n *\n * 2. **SFC has NO `@style` block** — there is no `__style__`. We inject a\n * fresh `__style__` declaration after the last import and an\n * `adoptedStyleSheets` assignment as the first statement of the setup\n * function. The compiler emits the setup param as `_ctx` in this case;\n * we rename it to `ctx` so the injected `ctx.host` reference resolves.\n *\n * Runs on the RAW compiled output BEFORE the island / HMR / auto-wiring\n * transforms so those passes operate on the folded module uniformly:\n * - The static-island shim calls `__aihu_setup__({ host: root, ... })`\n * where `root` is the shadow root, so `ctx.host` is valid there too.\n * - The HMR / defer passes only touch the `defineElement(...)` wrapper and\n * the runtime import; they do not disturb `__style__` or the setup body.\n *\n * No-ops (returns input unchanged) when `css` is empty/whitespace.\n *\n * @internal\n */\nexport function _foldCssEngineStyles(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already declared __style__. css-engine\n // output already includes that @style block, so REPLACE the replaceSync body\n // (between the backticks) wholesale to avoid duplicating the @style rules.\n // The codegen emits `__style__.replaceSync(`<body>`);` as a single statement;\n // match the body non-greedily up to the closing backtick + paren.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] matches any char incl. newlines\n const styleBodyRe = /(__style__\\.replaceSync\\(`)[^]*?(`\\);)/\n if (styleBodyRe.test(compiledCode)) {\n // Use a function replacer so any `$` in the CSS isn't read as a\n // replacement-pattern backreference.\n return compiledCode.replace(styleBodyRe, (_m, open: string, close: string) => {\n return `${open}${escaped}${close}`\n })\n }\n\n // Shape 2 — no @style block. Inject a fresh stylesheet + adoption.\n // Bail (no-op) if the expected defineComponent setup shape is absent.\n // The setup param is NOT always `ctx`/`_ctx`: an agent component (one with an\n // exposed member) emits `(__aihu_ctx__)` so `_registerAgentServerBinding`\n // can read `__aihu_ctx__?.element`. Capture whatever the param is and inject\n // against it — a literal `(ctx)` anchor silently no-ops on agent components,\n // which shipped scoped utility CSS with no adopted stylesheet.\n const setupRe = /defineComponent\\(\\s*\\((__?[A-Za-z0-9_]+)\\)\\s*=>\\s*\\{/\n const m = setupRe.exec(compiledCode)\n if (m == null) return compiledCode\n // `_ctx` is codegen's \"unused ctx\" marker; the injected adoption uses it, so\n // normalize to `ctx`. Any other name (`ctx`, `__aihu_ctx__`) is referenced\n // elsewhere in the setup body and MUST be preserved verbatim.\n const setupParam = m[1] === '_ctx' ? 'ctx' : m[1]\n\n // Inject the module-level stylesheet declaration after the last import line.\n const lines = compiledCode.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n const decl = `const __style__ = new CSSStyleSheet();\\n__style__.replaceSync(\\`${escaped}\\`);`\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, decl)\n } else {\n lines.unshift(decl)\n }\n let withDecl = lines.join('\\n')\n\n // Inject the adoption as the first statement of the setup body, using the\n // ACTUAL setup param (`ctx` for a plain component, `__aihu_ctx__` for an\n // agent component). `_ctx` is normalized to `ctx` above since the injected\n // statement now references it.\n withDecl = withDecl.replace(\n setupRe,\n `defineComponent((${setupParam}) => {\\n (${setupParam}.host as ShadowRoot).adoptedStyleSheets = [__style__];`,\n )\n return withDecl\n}\n\n/**\n * Virtual-module prefix used by the `shadowMode === 'light'` branch to route\n * per-SFC utility CSS through Vite's built-in CSS pipeline. The plugin\n * (`aihuCompilerPlugin`) implements `resolveId` + `load` for ids matching\n * `VIRTUAL_UTILITY_PREFIX + '<hash>.css'`, returning the stored CSS body so\n * Vite hoists it into the bundle CSS asset (`dist/assets/*.css`) — NOT into\n * `host.adoptedStyleSheets`, which is a no-op when there is no shadow root.\n *\n * The trailing `.css` extension is mandatory: Vite's built-in CSS plugin keys\n * off the extension to know it should run the CSS pipeline on the module.\n *\n * @internal\n */\nexport const VIRTUAL_UTILITY_PREFIX = '\\0virtual:aihu-utility/'\n\n/**\n * Stable short hash for keying the virtual-CSS module per source-SFC id.\n *\n * djb2-style; collisions are tolerable here because (a) each entry stores its\n * own CSS body, so a hash collision would only matter if two distinct SFCs\n * hashed to the same key AND were processed concurrently; (b) collisions are\n * recoverable — Vite would simply load the wrong CSS for one SFC; we still\n * keyed on the unhashed id internally to avoid that. The hash only appears in\n * the bundled asset URL.\n *\n * @internal\n */\nexport function _hashIdForUtilityCss(id: string): string {\n let h = 5381\n for (let i = 0; i < id.length; i++) {\n h = ((h * 33) ^ id.charCodeAt(i)) >>> 0\n }\n return h.toString(36)\n}\n\n/**\n * Bug 6 — `shadowMode === 'light'` branch.\n *\n * Routes utility CSS to Vite's CSS pipeline (which folds CSS imports into the\n * bundled `dist/assets/*.css` asset) instead of to `host.adoptedStyleSheets`\n * (a no-op on an element with no shadow root). Returns a prelude `import` that\n * the plugin's `resolveId` + `load` hooks resolve to the stored CSS body.\n *\n * The `__style__` shadow path is NOT invoked here — utility CSS for a\n * cascade-mode component MUST hit the global stylesheet, not a per-element\n * stylesheet that would be silently dropped by `HTMLElement`'s setter.\n *\n * Authored `@style` blocks still emit through the Rust codegen's `<style>`\n * node and are unaffected. (If a component opts into `shadowMode: 'light'` and\n * authors an `@style` block, the codegen still wires it through the\n * non-shadow path — that is the runtime's contract, not this hook's.)\n *\n * @internal\n */\nexport function _foldCssEngineStylesGlobal(\n compiledCode: string,\n css: string,\n id: string,\n): { code: string; virtualId: string } | null {\n if (!css.trim()) return null\n const hash = _hashIdForUtilityCss(id)\n const virtualId = `${VIRTUAL_UTILITY_PREFIX}${hash}.css`\n // Prepend the CSS import as a side-effect-only import so Vite's CSS plugin\n // hoists it into the bundle. We use the NULL-byte virtual id form\n // (Rollup/Vite convention for \"owned by this plugin\"); other plugins will\n // skip it. The compiler's transform returns this prepended code, which the\n // downstream esbuild/oxc strip leaves untouched (it's just an import).\n const prelude = `import ${JSON.stringify(virtualId)};\\n`\n return { code: prelude + compiledCode, virtualId }\n}\n\n// ─── v1.0.10a — compiler AST-export hook ─────────────────────────────────────\n//\n// Thin TS wrapper over the `aihu-compile --ast-json` flag. Returns the parsed\n// `.aihu` SFC AST in a stable, serializable shape consumed by the CSS engine's\n// AST scanner (`css-2-ast-scanner`). Mirrors the typed contract in\n// `docs/superpowers/specs/compiler-ast-export-hook.md` §4.\n\n/** Top-level AST export — one per .aihu SFC. */\nexport interface SfcAst {\n /** Resolved custom-element tag name (meta.name → route.name → file stem). */\n tag: string\n /** AST schema version — bumped on any breaking shape change (semver-tied). */\n astVersion: 1\n /** The @style block, if the SFC declared one. */\n style: SfcStyleBlock | null\n /** Parsed template tree. null when the SFC has no @template block. */\n template: SfcNode[] | null\n /** SFC-level metadata. */\n meta: SfcMeta\n}\n\nexport interface SfcStyleBlock {\n /** Verbatim CSS body of the @style block (braces stripped, $global token removed). */\n content: string\n /** 'scoped' (default) or 'global' (@style { $global ... }). */\n scope: 'scoped' | 'global'\n}\n\nexport interface SfcMeta {\n /** From @meta { name } / @route { name } / file stem — never null after resolution. */\n name: string\n}\n\n/** Discriminated union mirroring Rust `TemplateNode`. */\nexport type SfcNode =\n | { kind: 'element'; tag: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'macroElement'; name: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'text'; value: string }\n | { kind: 'interpolation'; expr: string }\n | { kind: 'ifBlock'; branches: Array<{ cond: string; body: SfcNode[] }> }\n | {\n kind: 'eachBlock'\n list: string\n item: string\n idx: string | null\n key: string | null\n body: SfcNode[]\n emptyBody: SfcNode[] | null\n }\n | { kind: 'htmlBlock'; expr: string }\n\n/** Discriminated union mirroring Rust `Attr` — the three class-forms key on `kind`. */\nexport type SfcAttr =\n | { kind: 'static'; name: string; value: string } // Form A\n | { kind: 'binding'; name: string; expr: string } // Form B\n | { kind: 'macro'; name: string; value: SfcMacroValue } // Form C (and on:/bind:/emit:/if/each/…)\n\nexport type SfcMacroValue =\n | { form: 'quoted'; value: string }\n | { form: 'curly'; expr: string }\n | { form: 'boolean' }\n\n/**\n * Parse a .aihu source string to its structured AST.\n *\n * Thin wrapper over the Rust binary (mirrors `transform()`): spawns\n * `aihu-compile --stdin --tag <stem> --ast-json`, feeds `source` on stdin, and\n * `JSON.parse`s stdout. `id` is optional and only used to derive the tag stem\n * and the `--path` arg (for `@route` C500 checks), identical to `transform()`.\n *\n * Throws on parse failure — the Rust binary exits non-zero and `execFileSync`\n * surfaces the diagnostic (same error path as `transform()`).\n */\n/**\n * Compile an SFC to its TYPE-CHECK SURFACE and return it as a string — the\n * `.aihu.ts` sidecar's content, without writing a file.\n *\n * This is the in-memory path `aihu-tsc` uses to hand `.aihu` files to TypeScript\n * as virtual files. The surface is line-preserving: line N of the returned text\n * corresponds to line N of the `.aihu` source, which is what lets a `tsc`\n * diagnostic be mapped straight back to the line the author wrote.\n *\n * Returns `''` when the SFC has no `@template` (nothing to check).\n */\nexport function compileSidecar(\n source: string,\n id?: string,\n options?: {\n /**\n * #486 step 4 — emit the attribute/component-prop type layer\n * (`--strict-templates`). Default off: the surface stays byte-identical\n * to the pre-#486 sidecar.\n */\n strictTemplates?: boolean\n },\n): string {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--sidecar-stdout']\n if (id) {\n args.push('--path', id)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n return execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n // Capture stderr rather than inheriting it: the compiler's warnings (unhyphenated\n // tag names, undeclared cross-block refs) belong to `aihu build`, and would\n // otherwise interleave with the type diagnostics a caller is trying to read.\n // A hard compile failure still throws, carrying the message with it.\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n}\n\nexport function compileToAst(source: string, id?: string): SfcAst {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--ast-json']\n if (id) {\n args.push('--path', id)\n }\n const json = execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n })\n return JSON.parse(json) as SfcAst\n}\n\n/**\n * Structured `@route` metadata (the `.route.json` sidecar shape). All fields\n * optional — only what the SFC's `@route` block declares is present. `head` is\n * left opaque here (the router owns its shape).\n */\nexport interface RouteMeta {\n pattern?: string\n name?: string\n layout?: string\n middleware?: string[]\n ssr?: boolean\n params?: string[]\n head?: unknown\n /**\n * GX Phase 1 fan-out (#437-GX): the resolved `extract` policy — always\n * present in the binary's route-json output since 0.1.12 (the default is\n * recorded, never implied by absence). Typed loose here: consumers\n * normalize fail-closed (`deriveReadPolicy` in `@aihu/server`).\n */\n extract?: { read?: unknown; call?: unknown }\n /**\n * GX Phase 4 fan-out (#466): the `data:` governed-resource declaration\n * (70-governed-data-access §2.1) — present only when the route declares one\n * (0.1.14+). `type` keys the server registry's provider; `preview` lists\n * the locked-state fields (omitted when none declared). Consumers: the\n * server runtime's boot validation + generated loader, and the router Vite\n * layer's C486 sibling-loader conflict check (§4.7).\n */\n data?: { type?: string; preview?: string[] }\n}\n\n/**\n * Parse a `.aihu` source string and return its `@route` metadata, or `null`\n * when the SFC declares no `@route` block.\n *\n * Thin wrapper over the Rust binary (mirrors {@link compileToAst}): spawns\n * `aihu-compile --stdin --tag <stem> --route-json`, feeds `source` on stdin,\n * and `JSON.parse`s stdout. This is how build tools recover full route\n * metadata (`head`/`middleware`/`params`/`ssr`/`layout`) for the SPA build\n * path, where no `.route.json` sidecar is written to disk.\n *\n * Throws on parse failure (same error path as `transform()`/`compileToAst()`).\n */\nexport function compileRouteMeta(source: string, id?: string): RouteMeta | null {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--route-json']\n if (id) {\n args.push('--path', id)\n }\n const out = execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n }).trim()\n if (out === '' || out === 'null') return null\n return JSON.parse(out) as RouteMeta\n}\n\n/**\n * Inject `_setMount(mount)` + `_setSignal(signal)` auto-wiring into a compiled\n * `.aihu` module. Adds the necessary symbols to existing imports and inserts\n * the boot calls right after the last `import` statement.\n *\n * @internal\n */\nexport function _injectAutoWiring(code: string): string {\n // 1. Add `mount` to the @aihu/arbor import (or create it).\n let result: string\n if (code.includes(\"from '@aihu/arbor'\")) {\n result = code.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n } else {\n result = `import { mount } from '@aihu/arbor'\\n${code}`\n }\n\n // 2. Add `signal` to the non-type @aihu/signals import (or create it).\n // Note: `import\\s+\\{` does NOT match `import type {` (the regex needs `{` immediately\n // after whitespace, whereas `import type {` has `type` in between). No negation guard\n // is needed — the replace callback below already skips `import type` lines.\n if (/import\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result)) {\n // There IS a value import from signals — add `signal` if missing.\n result = result.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/signals'/,\n (_m: string, imports: string) => {\n // Skip type-only imports\n if (_m.startsWith('import type')) return _m\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('signal')) parts.push('signal')\n return `import { ${parts.join(', ')} } from '@aihu/signals'`\n },\n )\n } else if (!/import.*from\\s*'@aihu\\/signals'/.test(result)) {\n // No signals import at all — insert after arbor import\n result = result.replace(\n /import\\s*\\{[^}]*\\}\\s*from\\s*'@aihu\\/arbor'/,\n (m: string) => `${m}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n // If only `import type { Signal }` exists, insert value import after it\n else if (\n /import\\s+type\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result) &&\n !result.match(/import\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/)\n ) {\n result = result.replace(\n /(import\\s+type\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals')/,\n (_m: string, typeImport: string) => `${typeImport}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n\n // 3. Add `_setMount`, `_setSignal` to the @aihu/runtime import.\n result = result.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_setMount')) parts.push('_setMount')\n if (!parts.includes('_setSignal')) parts.push('_setSignal')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // 4. Insert boot calls after the last `import` statement.\n const lines = result.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, '_setMount(mount)', '_setSignal(signal)', '')\n result = lines.join('\\n')\n }\n\n return result\n}\n\n/**\n * Vite plugin that compiles .aihu files to TypeScript during build and dev.\n *\n * Use `enforce: 'pre'` so the hook fires before Vite/Rollup's built-in\n * parsers attempt to process the raw .aihu content as JavaScript.\n *\n * @example\n * // vite.config.ts\n * import { aihuCompilerPlugin } from '@aihu/compiler'\n * export default { plugins: [aihuCompilerPlugin()] }\n *\n * **Known Limitation — Bun + Rollup4 ESM incompatibility (v0):**\n *\n * `bun vite build` fails in the `fixtures/vite-counter` fixture with two\n * cascading errors:\n *\n * 1. **Missing devDependency:** `vite` is declared only as an optional\n * `peerDependency` in `packages/compiler/package.json`. Bun does not\n * install optional peers automatically, so `bun vite build` exits\n * immediately with `Cannot find package 'vite'`.\n *\n * 2. **Bun + Rollup4 bridge:** Even with Vite installed, Bun processes\n * `vite.config.ts` through its own internal bundler before handing off\n * to Rollup4. When `@aihu/compiler` is resolved from the workspace\n * symlink (`dist/index.js`), Bun's ESM loader evaluates the module at\n * config-load time. The subprocess call inside `transform()` depends on\n * the Rust binary being at `../bin/aihu-compile` relative to `dist/`\n * (written by the postinstall hook). In a dev workspace where postinstall\n * has not run, this path does not exist and `execFileSync` throws. Bun surfaces\n * the error as a config-load failure, not a per-file transform error,\n * causing the entire build to abort before any `.aihu` file is\n * processed.\n *\n * **Workaround (v0):** Use `bun run integrate.ts` directly from\n * `packages/compiler/fixtures/vite-counter/`. This script calls\n * `transform()` from `@aihu/compiler` without involving Vite or Rollup.\n * Preconditions: (1) `cargo build --release` in `packages/compiler/`,\n * (2) `bun install` at the repo root.\n *\n * **v1 resolution:** Add `vite` as a `devDependency` in\n * `packages/compiler/package.json`; add a WASM or pre-built binary\n * strategy so the Rust binary is bundled with the npm package and does not\n * require a separate `cargo build --release` step.\n */\n/**\n * Minimal structural type for the `@aihu/css-engine` module surface this\n * plugin uses. Declared locally so the compiler never type-imports the\n * css-engine package (which would create a compile-time edge against an\n * optional peer that may be absent).\n *\n * @internal\n */\ninterface CssEngineModule {\n compileSfc(source: string, id?: string): string\n}\n\n// Memoised resolution of the optional `@aihu/css-engine` peer. `undefined`\n// = not yet attempted; `null` = attempted and unavailable (no-op path);\n// a module object = available. The dynamic import is attempted once per\n// process — repeated absence does not re-pay the resolution cost.\nlet _cssEngine: CssEngineModule | null | undefined\n\n// Whether we've already surfaced a one-shot warning that css-engine resolved\n// but `compileSfc` threw (typically: native css-core binary unresolvable in\n// the consumer's install — e.g. lockfile pins the per-platform placeholder\n// version). The transform stays non-fatal, but going fully silent leaves users\n// chasing \"why did my utility classes never emit?\". One warn per process.\nlet _cssEngineWarned = false\n\n// The optional-peer module specifier, held in a VARIABLE so TypeScript never\n// statically resolves `@aihu/css-engine`'s declarations at typecheck time.\n// css-engine depends on @aihu/compiler for its AST, so the two form a\n// circular package relationship; under CI's frozen install + moon build\n// ordering, css-engine's `dist`/`.d.ts` are not guaranteed to exist when\n// `compiler:typecheck` runs. A literal `import('@aihu/css-engine')` makes the\n// compiler emit TS2307 in that window (the `as` cast affects the RESULT type\n// only, not whether TS attempts module resolution). Resolving through this\n// variable keeps the import fully dynamic — no compile-time edge on the peer.\nconst _CSS_ENGINE_SPECIFIER = '@aihu/css-engine'\n\n/**\n * Lazily resolve `@aihu/css-engine` and compile a `.aihu` source's utility\n * classes to scoped CSS. Returns `''` when css-engine is not installed\n * (the optional-peer no-op path) or when compilation fails for any reason —\n * a CSS-engine failure MUST NOT break an otherwise-valid `.aihu` build.\n *\n * Sets `process.env.SCRIBE_COMPILE_BIN` to this plugin's resolved compiler\n * binary before calling `compileSfc`: css-engine re-derives the SFC AST via\n * its own bundled copy of `compileToAst`, whose binary path is resolved\n * relative to the css-engine package — which does NOT ship the compiler\n * binary. Pointing it at our `binPath` guarantees the AST css-engine parses\n * is produced by the exact same compiler this build uses.\n *\n * @internal\n */\nasync function _maybeCompileUtilityCss(source: string, id: string): Promise<string> {\n if (_cssEngine === null) return ''\n // Ensure css-engine's bundled `compileToAst` spawns the SAME compiler\n // binary this plugin uses (it has no compiler binary of its own). Set\n // this BEFORE the dynamic import so that any module-load-time evaluation\n // of `process.env.SCRIBE_COMPILE_BIN` in css-engine's bundled dist (older\n // bundles capture this into a module-scope const at line 8 of\n // `packages/css-engine/dist/index.js`) sees the correct value. After Bug 6,\n // the source `compileToAst` resolves the bin lazily on each call, so once\n // css-engine is rebuilt this set-before-import is belt-and-braces.\n if (process.env.SCRIBE_COMPILE_BIN == null) {\n process.env.SCRIBE_COMPILE_BIN = resolveBinPath()\n }\n if (_cssEngine === undefined) {\n try {\n // Guarded, lazy, OPTIONAL — see the plugin transform for the rationale.\n // Importing via the `_CSS_ENGINE_SPECIFIER` variable (not a string\n // literal) keeps this fully dynamic: TS does NOT resolve the peer's\n // `.d.ts` at typecheck time, so `compiler:typecheck` passes even when\n // css-engine's `dist` has not been built (the CI build-order window).\n _cssEngine = (await import(_CSS_ENGINE_SPECIFIER)) as unknown as CssEngineModule\n } catch {\n _cssEngine = null\n return ''\n }\n }\n try {\n return _cssEngine.compileSfc(source, id)\n } catch (err) {\n // A css-engine compile failure is non-fatal: fall back to the no-op\n // path (utility classes don't emit) rather than aborting the build.\n // BUT — silently swallowing this means a user who clearly intends\n // css-engine to be active (the peer resolved) will never know their\n // utility classes are inert. Surface a one-shot warning with the\n // underlying error + an install/upgrade hint. Idempotent per process.\n if (!_cssEngineWarned) {\n _cssEngineWarned = true\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(\n `[@aihu/compiler] @aihu/css-engine is installed but compileSfc() failed; ` +\n `utility classes will not emit. Original error: ${msg}\\n` +\n `Hint: ensure the native css-core binary is installed ` +\n `(install/upgrade @aihu/css-engine + its per-platform optional dep, ` +\n `or run \\`cargo build --release -p aihu-css-core\\` in a dev clone).`,\n )\n }\n return ''\n }\n}\n\nexport function aihuCompilerPlugin(options?: AihuCompilerPluginOptions): VitePlugin {\n const islandsEnabled = options?.islands !== false\n const shadowMode = options?.shadowMode\n const target = options?.target\n const layoutsDir = options?.layoutsDir ?? 'src/layouts'\n\n // Bug 6 — per-instance store of virtual utility-CSS modules. Keyed by the\n // full virtual id (NUL-prefixed). Populated by the transform hook when\n // `shadowMode === 'light'` produces utility CSS; drained by the `load` hook\n // when Vite's CSS pipeline asks for the module body. Lives on the plugin\n // instance so multiple `aihuCompilerPlugin()` calls in the same build don't\n // alias each other's css.\n const utilityCssStore = new Map<string, string>()\n\n // GX Phase 1 (#437-GX) — per-instance extract census. Keyed by rawId,\n // populated in transform from the compiler's `// @aihu:extract` marker;\n // printed per-value in buildEnd. Every build prints the posture\n // distribution (the DA-e census pattern from #437).\n const extractCensus = new Map<string, { read: string; call: string }>()\n\n return {\n name: 'aihu-compiler',\n enforce: 'pre',\n buildEnd() {\n for (const line of _formatExtractCensus(extractCensus)) console.info(line)\n },\n resolveId(source) {\n // Own all `\\0virtual:aihu-utility/<hash>.css` ids so Vite's resolver\n // doesn't try to find them on disk. Returning the id verbatim is the\n // Rollup convention for \"I'll handle the load.\"\n if (source.startsWith(VIRTUAL_UTILITY_PREFIX)) return source\n return null\n },\n load(id) {\n if (!id.startsWith(VIRTUAL_UTILITY_PREFIX)) return null\n // Vite's CSS pipeline runs on the returned source because the id ends\n // in `.css` — it parses, minifies (in build), and hoists into a CSS\n // asset chunk that lands in `dist/assets/<name>-<hash>.css`.\n return utilityCssStore.get(id) ?? null\n },\n transform(code, id) {\n // Strip Vite query strings (e.g. `?import`, `?t=...`) before checking the extension.\n const rawId = id.split('?')[0]!\n if (!rawId.endsWith('.aihu')) return\n return (async () => {\n // No `.aihu.ts` sidecar is written any more. Type-checking goes through\n // `aihu-tsc`, which projects each `.aihu` into the TypeScript program as a\n // VIRTUAL file — so the type-check surface never lands on disk beside the\n // source, where authors saw it, editors indexed it, and `.gitignore` had to\n // hide it. A build has no business writing type-checker inputs at all.\n //\n // Layout SFCs (under the layouts dir) compile in layout mode: a\n // namespaced `aihu-layout-<stem>` tag + a passive <outlet> marker.\n const isLayout = _isLayoutFile(rawId, layoutsDir)\n const layoutTag = isLayout ? _layoutTag(basename(rawId, '.aihu')) : undefined\n const tOpts = {\n ...(target ? { target } : {}),\n ...(layoutTag ? { tag: layoutTag } : {}),\n }\n const result = transform(code, rawId, tOpts)\n // GX Phase 1 (#437-GX) — record this surface's resolved extract policy\n // for the build census (client-target builds carry no marker: policy\n // never reaches client artifacts).\n const extractMarker = _parseExtractMarker(result.code)\n if (extractMarker) extractCensus.set(rawId, extractMarker)\n // §9.4 per-file shadow override: the Rust `$shadow` macro emits a leading\n // `// @aihu:shadow <mode>` marker; it wins over the plugin's global\n // shadowMode and drives BOTH _injectShadowMode and the css fold branch.\n const perFileShadow = /^\\/\\/ @aihu:shadow (light|shadow)\\b/m.exec(result.code)?.[1] as\n | 'light'\n | 'shadow'\n | undefined\n // DA4 (#437, the ratified flip) — the IMPLICIT page default: for an\n // `@route` unit with no `$shadow` pin the compiler emits the DISTINCT\n // default-marker token `// @aihu:shadow-default light`. Layout SFCs\n // (no `@route` block, so no compiler marker) get the same 'light'\n // default from `_isLayoutFile`. Precedence, ratified: `$shadow` pin >\n // plugin-global `shadowMode` config > page/layout default 'light' >\n // leaf default 'shadow' (the runtime's `?? 'shadow'` when nothing is\n // injected) — so an explicit plugin-global config still outranks the\n // implicit default, which is why this is not the pin marker.\n const perFileShadowDefault = /^\\/\\/ @aihu:shadow-default (light|shadow)\\b/m.exec(\n result.code,\n )?.[1] as 'light' | 'shadow' | undefined\n const impliedShadowDefault = perFileShadowDefault ?? (isLayout ? 'light' : undefined)\n const effectiveShadow = perFileShadow ?? shadowMode ?? impliedShadowDefault\n let compiled =\n effectiveShadow != null ? _injectShadowMode(result.code, effectiveShadow) : result.code\n // Light-DOM: the authored `@style` block compiled to a per-instance\n // `host.adoptedStyleSheets` assignment, but a light-DOM host has no\n // shadow root so that setter is a no-op. Redirect the module-level\n // sheet to `document.adoptedStyleSheets` (idempotent) so authored recipe\n // CSS reaches the global cascade alongside the css-engine utility CSS.\n if (effectiveShadow === 'light') compiled = _globalizeAuthoredStyle(compiled)\n if (isLayout) compiled = _passivizeOutlet(compiled)\n\n // ── css-engine hook (optional, lazy, no circular dep) ──────────────\n // @aihu/css-engine depends on @aihu/compiler (for its AST), so the\n // compiler MUST NOT hard-depend on it. It is declared an OPTIONAL\n // peerDependency and pulled in ONLY via this guarded dynamic import:\n // when present, we compile the SFC's utility classes to scoped CSS\n // and fold it into the component's shadow `<style>`; when absent the\n // import throws and we no-op (utility classes simply don't emit —\n // the pre-hook behaviour). This keeps css-engine an opt-in enhancement\n // with zero dependency cycle.\n const utilityCss = await _maybeCompileUtilityCss(code, rawId)\n if (utilityCss) {\n if (effectiveShadow === 'light') {\n // Bug 6 — no shadow root → `host.adoptedStyleSheets` is a no-op.\n // Route utility CSS through Vite's CSS pipeline via a virtual\n // `.css` import so it lands in `dist/assets/*.css` and reaches the\n // global cascade. The authored `@style` block (if any) still\n // emits via the Rust codegen's normal path and is unaffected.\n const folded = _foldCssEngineStylesGlobal(compiled, utilityCss, rawId)\n if (folded) {\n utilityCssStore.set(folded.virtualId, utilityCss)\n compiled = folded.code\n }\n } else {\n // `shadowMode: 'shadow'`: fold into the\n // per-component `CSSStyleSheet` adopted by the shadow root.\n compiled = _foldCssEngineStyles(compiled, utilityCss)\n }\n }\n\n const elementTag = _extractElementTag(compiled)\n\n let out: string\n\n // §9.4 — a base-extending recipe (`defineComponent({ base: X, ... })`)\n // MUST take the full defineComponent/defineElement path: the static\n // island shim inlines `class extends HTMLElement` and cannot honor a\n // base class. Force-classify it interactive regardless of signal usage.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] is valid JS — matches any char including newlines\n const hasBase = /defineComponent\\(\\s*\\{[^]*?\\bbase\\s*:/.test(compiled)\n\n // Plan 3.3 — static-island fast path. Bypasses HMR injection because\n // a component with no signals has no setup state to hot-replace.\n // Static islands strip @aihu/runtime entirely — do NOT inject auto-wiring\n // (it would reference _setMount/_setSignal as undefined identifiers).\n //\n // DA4 (#437): like `hasBase`, a light-DOM component cannot take the\n // static-island shim — the shim inlines `attachShadow({ mode: 'open' })`\n // and cannot honor `shadowMode: 'light'` (and its tail rewrite does not\n // match the injected options argument). Keep the full runtime path so\n // the injected `{ shadowMode: 'light' }` reaches defineElement.\n if (\n islandsEnabled &&\n elementTag !== null &&\n !hasBase &&\n effectiveShadow !== 'light' &&\n _classifyIsland(compiled) === 'static'\n ) {\n out = _buildStaticIsland(compiled, elementTag)\n } else if (elementTag !== null) {\n // Inject HMR instrumentation. The injected block is gated on\n // `typeof __DEV__ !== 'undefined' && __DEV__` so production\n // bundlers dead-code-eliminate it when they set __DEV__ = false.\n out = _buildHmrCode(compiled, elementTag)\n // Plan 3.3 — interactive islands also gain `defer` attribute\n // support so individual instances can opt into lazy hydration.\n out = _buildDeferredHydration(out, elementTag)\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n } else {\n out = compiled\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n }\n\n // The Rust compiler emits TypeScript (type casts, import type, etc.) and\n // the injected HMR / defer helpers also contain TS generics and casts.\n // Vite does NOT re-run its TS-strip step when a plugin returns code for a\n // non-.ts ID, so we must strip types ourselves before returning.\n //\n // Priority: always try transformWithEsbuild first — it strips types to\n // plain JS in both Vite 5 (via esbuild) and Vite 8 (deprecated wrapper).\n // Using moduleType:'ts' only as a last resort because `import('vite')`\n // resolves to the root node_modules vite (which may be v8 even when a\n // consumer project runs v5), causing v5's Rollup to receive raw TypeScript\n // and fail on import-type / as-casts.\n try {\n const vite = await import('vite')\n if ('transformWithEsbuild' in vite && typeof vite.transformWithEsbuild === 'function') {\n const stripped = await vite.transformWithEsbuild(out, 'component.ts', {\n target: 'esnext',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n }\n // Fallback for future Vite versions where esbuild is fully removed:\n // return TS and let Rolldown strip types natively.\n // biome-ignore lint/suspicious/noExplicitAny: moduleType is rolldown API\n return { code: out, moduleType: 'ts', map: null } as any\n } catch {\n // If running outside Vite (e.g. tests, standalone transform), return as-is.\n return { code: out, map: null }\n }\n })()\n },\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../js/native.ts","../js/envelope.ts","../js/transform-memo.ts","../js/index.ts"],"sourcesContent":["/**\n * @aihu/compiler native-addon loader — the in-process compile fast path.\n *\n * A `native.ts`-style loader in the mold of packages/server/src/native.ts:\n * platform matrix → per-platform optionalDependency package → dev fallback,\n * with explicit escape hatches and a cached three-state result. The addon\n * (packages/compiler/src-native, napi-rs) exposes\n * `compileEnvelope(source, optionsJson) → envelopeJson` — one boundary\n * crossing per file — which `js/envelope.ts` routes `transform()` /\n * `compileToAst()` / `compileRouteMeta()` through.\n *\n * States:\n * - loaded: addon required successfully; compiles run in-process.\n * - disabled: `AIHU_COMPILER_NATIVE=0` — the documented escape hatch.\n * - unavailable: no addon for this platform / load failed. UNLIKE\n * @aihu/server's fail-loud contract, a failed load here is a\n * one-shot WARNING, not a throw: the CLI spawn path is a\n * byte-identical fallback that always exists, so failing the\n * whole build over a fast-path load would be strictly worse.\n * The exception is `AIHU_COMPILER_NATIVE_ADDON=<path>` — an\n * explicit override that fails loud (same doctrine as\n * AIHU_COMPILE_BIN: a pinned path that doesn't load is a\n * configuration error, and silently ignoring it hands back a\n * plausible-looking wrong backend).\n */\n\nimport { existsSync } from 'node:fs'\nimport { createRequire } from 'node:module'\nimport { dirname, resolve } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst __dirname = dirname(fileURLToPath(import.meta.url))\n\nexport interface CompilerNativeAddon {\n compileEnvelope(source: string, optionsJson: string): string\n compilerVersion(): string\n}\n\nexport interface NativePlatformDescriptor {\n readonly platformId: string\n readonly packageName: string\n readonly nodeFile: string\n}\n\nfunction detectPlatform(): NativePlatformDescriptor | null {\n if (typeof process === 'undefined' || !process.platform || !process.arch) {\n return null\n }\n const key = `${process.platform}-${process.arch}`\n switch (key) {\n case 'darwin-arm64':\n return {\n platformId: 'darwin-arm64',\n packageName: '@aihu/compiler-native-darwin-arm64',\n nodeFile: 'aihu-compiler-native.darwin-arm64.node',\n }\n case 'darwin-x64':\n return {\n platformId: 'darwin-x64',\n packageName: '@aihu/compiler-native-darwin-x64',\n nodeFile: 'aihu-compiler-native.darwin-x64.node',\n }\n case 'linux-x64':\n return {\n platformId: 'linux-x64-gnu',\n packageName: '@aihu/compiler-native-linux-x64-gnu',\n nodeFile: 'aihu-compiler-native.linux-x64-gnu.node',\n }\n case 'linux-arm64':\n return {\n platformId: 'linux-arm64-gnu',\n packageName: '@aihu/compiler-native-linux-arm64-gnu',\n nodeFile: 'aihu-compiler-native.linux-arm64-gnu.node',\n }\n case 'win32-x64':\n return {\n platformId: 'win32-x64-msvc',\n packageName: '@aihu/compiler-native-win32-x64-msvc',\n nodeFile: 'aihu-compiler-native.win32-x64-msvc.node',\n }\n default:\n return null\n }\n}\n\nexport type CompilerNativeState =\n | { kind: 'loaded'; addon: CompilerNativeAddon; addonPath: string }\n | { kind: 'disabled' }\n | { kind: 'unavailable'; error?: Error }\n\nlet _state: CompilerNativeState | null = null\nlet _warnedLoadFailure = false\n\nfunction isAddonShaped(mod: unknown): mod is CompilerNativeAddon {\n return (\n typeof mod === 'object' &&\n mod !== null &&\n typeof (mod as CompilerNativeAddon).compileEnvelope === 'function'\n )\n}\n\n/**\n * Resolve (and cache) the native compiler addon. This is the ONLY module in\n * @aihu/compiler that requires a napi `.node` file.\n */\nexport function loadCompilerNative(): CompilerNativeState {\n if (_state !== null) return _state\n\n // Escape hatch — checked before everything else.\n if (typeof process !== 'undefined' && process.env?.AIHU_COMPILER_NATIVE === '0') {\n _state = { kind: 'disabled' }\n return _state\n }\n\n const requireFn = createRequire(import.meta.url)\n\n // Explicit addon path override — fails LOUD (a pinned path that does not\n // load is a configuration error, never a silent fallthrough).\n const override = process.env?.AIHU_COMPILER_NATIVE_ADDON\n if (override) {\n let addon: unknown\n try {\n addon = requireFn(override)\n } catch (err) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON is set to '${override}', ` +\n `which failed to load: ${(err as Error).message}`,\n )\n }\n if (!isAddonShaped(addon)) {\n throw new Error(\n `[@aihu/compiler] AIHU_COMPILER_NATIVE_ADDON module at '${override}' ` +\n `does not export compileEnvelope()`,\n )\n }\n _state = { kind: 'loaded', addon, addonPath: override }\n return _state\n }\n\n const descriptor = detectPlatform()\n if (descriptor === null) {\n _state = { kind: 'unavailable' }\n return _state\n }\n\n // 1. Per-platform optionalDependency package (the published-consumer path).\n let resolvedPath: string | null = null\n try {\n resolvedPath = requireFn.resolve(descriptor.packageName)\n } catch {\n // Package not installed — fall through to the dev candidates.\n }\n\n // 2. Dev fallbacks: the standalone src-native build. `aihu-compiler-native.node`\n // is staged by `bun packages/compiler/scripts/build-native.ts` (which runs\n // cargo and copies the platform cdylib); the target/release candidate\n // covers a manual copy. This module lives at js/ (source) or dist/\n // (bundled) — both one level below the package root.\n const devCandidates = [\n resolve(__dirname, '../src-native/aihu-compiler-native.node'),\n resolve(__dirname, '../src-native/target/release/aihu-compiler-native.node'),\n ]\n const candidates = resolvedPath ? [resolvedPath] : devCandidates.filter((c) => existsSync(c))\n\n for (const candidate of candidates) {\n try {\n const addon = requireFn(candidate)\n if (isAddonShaped(addon)) {\n _state = { kind: 'loaded', addon, addonPath: candidate }\n return _state\n }\n throw new Error(`module at ${candidate} does not export compileEnvelope()`)\n } catch (err) {\n // Present-but-broken (ABI mismatch, corrupt download, placeholder file):\n // warn ONCE, loudly, then fall back to the spawn path — which is\n // byte-identical, just slower. `AIHU_COMPILER_NATIVE=0` silences this.\n if (!_warnedLoadFailure) {\n _warnedLoadFailure = true\n console.warn(\n `[@aihu/compiler] native addon found but failed to load; falling back to ` +\n `the aihu-compile spawn path (identical output, slower).\\n` +\n ` Candidate: ${candidate}\\n` +\n ` Error: ${(err as Error).message}\\n` +\n ` Reinstall @aihu/compiler (or rebuild: cargo build --release ` +\n `--manifest-path packages/compiler/src-native/Cargo.toml), or set ` +\n `AIHU_COMPILER_NATIVE=0 to silence this warning.`,\n )\n }\n _state = { kind: 'unavailable', error: err as Error }\n return _state\n }\n }\n\n _state = { kind: 'unavailable' }\n return _state\n}\n\n/** Returns the cached state kind (resolving if needed). @internal */\nexport function _getCompilerNativeStateKind(): CompilerNativeState['kind'] {\n return loadCompilerNative().kind\n}\n\n/** Reset the cached state. Used by tests that mock detection/env. @internal */\nexport function _resetCompilerNative(): void {\n _state = null\n _warnedLoadFailure = false\n}\n","/**\n * envelope.ts — backend dispatch for the single-parse envelope compile.\n *\n * Every compile request (`transform` / `compileToAst` / `compileRouteMeta`)\n * routes memo-first, then through ONE of three backends, in order:\n *\n * 1. **native addon** (packages/compiler/src-native, napi) — in-process,\n * zero spawn. Selected once per process by `_resolveCompileBackend()`.\n * 2. **envelope CLI spawn** — the legacy `aihu-compile` spawn args PLUS\n * `--envelope <options-json>`. A binary that knows the flag answers with\n * one JSON envelope (single parse, every requested artifact); the JSON\n * carries the `\"envelope\": 1` discriminant.\n * 3. **legacy per-output spawn** — an OLDER binary ignores `--envelope`\n * and answers with its normal single artifact (JS / AST JSON / route\n * JSON), which the discriminant check detects. That output is used\n * as-is, so stale binaries keep exactly their historical behavior —\n * feature detection costs zero extra spawns.\n *\n * Backend selection is CACHED at first use (per module instance):\n * `AIHU_COMPILER_NATIVE=0` forces spawn; an explicit `AIHU_COMPILE_BIN`\n * binary pin forces spawn (working ON the compiler means\n * the pinned binary must actually run — an addon silently shadowing it would\n * reintroduce the exact quiet-wrong-answer failure the pin exists to prevent).\n * The cache means css-engine's mid-build `AIHU_COMPILE_BIN` handshake\n * (which programmatically sets the var AFTER the first transform) cannot\n * de-select an already-active native backend.\n *\n * The memo's \"binary stamp\" (transform-memo.ts `_binStamp`) generalizes to\n * whichever backend is active: the addon's `.node` file path (stat'd for\n * mtime+size, so a rebuilt addon invalidates entries) when native, the\n * resolved CLI binary path when spawning.\n */\n\nimport { execFileSync } from 'node:child_process'\nimport { loadCompilerNative } from './native.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\n\n/** The wire shape of a compile envelope (Rust `Envelope`, camelCase). */\nexport interface CompileEnvelope {\n envelope: number\n targets: Record<string, { js?: string; manifest?: string }>\n astJson?: string\n routeJson?: string\n diagnostics: unknown[]\n}\n\n/** Options forwarded to the Rust envelope API (Rust `EnvelopeOptions`). */\nexport interface CompileEnvelopeOptions {\n tag?: string\n path?: string\n targets?: string[]\n emits?: Array<'js' | 'ast' | 'route' | 'manifest'>\n strictTemplates?: boolean\n exprParser?: string\n}\n\nexport type CompileBackend =\n | {\n kind: 'native'\n compileEnvelope: (source: string, optionsJson: string) => string\n stampPath: string\n }\n | { kind: 'spawn' }\n\nlet _backend: CompileBackend | null = null\n\n/**\n * Resolve (once) which backend serves compiles for this process.\n * @internal\n */\nexport function _resolveCompileBackend(): CompileBackend {\n if (_backend !== null) return _backend\n const env = typeof process !== 'undefined' ? process.env : undefined\n if (env?.AIHU_COMPILER_NATIVE === '0' || env?.AIHU_COMPILE_BIN) {\n _backend = { kind: 'spawn' }\n return _backend\n }\n const native = loadCompilerNative()\n _backend =\n native.kind === 'loaded'\n ? {\n kind: 'native',\n compileEnvelope: native.addon.compileEnvelope.bind(native.addon),\n stampPath: native.addonPath,\n }\n : { kind: 'spawn' }\n return _backend\n}\n\n/** Reset the cached backend (tests). @internal */\nexport function _resetCompileBackend(): void {\n _backend = null\n}\n\n/**\n * The identity string the memo cache stamps entries with — the active\n * backend's file (addon `.node` path, or the resolved CLI binary path).\n * `_binStamp` stats it, so rebuilding EITHER backend invalidates entries.\n * @internal\n */\nexport function _backendStampPath(): string {\n const backend = _resolveCompileBackend()\n return backend.kind === 'native' ? backend.stampPath : resolveSpawnBinPath()\n}\n\n/**\n * CLI binary resolution for the spawn backend — env override first (the\n * css-engine handshake sets AIHU_COMPILE_BIN), then the shared resolver.\n * Call-time, never cached here (Bug 6 doctrine: the env var may be set\n * between calls).\n * @internal\n */\nexport function resolveSpawnBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n/** A backend reply: either a parsed envelope, or a legacy single artifact. */\nexport type EnvelopeReply =\n | { kind: 'envelope'; envelope: CompileEnvelope }\n | { kind: 'legacy'; output: string }\n\nfunction parseEnvelopeReply(stdout: string): EnvelopeReply {\n // Envelope replies are a single JSON object carrying the `\"envelope\"`\n // discriminant. EVERY legacy output fails this test: emitted JS is not\n // JSON (starts with a comment or import), an AST export carries\n // `astVersion` but not `envelope`, a route sidecar is a plain object (or\n // the literal `null`) without it.\n const trimmed = stdout.trim()\n if (trimmed.startsWith('{')) {\n try {\n const parsed = JSON.parse(trimmed) as { envelope?: unknown }\n if (typeof parsed === 'object' && parsed !== null && parsed.envelope === 1) {\n return { kind: 'envelope', envelope: parsed as unknown as CompileEnvelope }\n }\n } catch {\n // Not JSON — legacy output.\n }\n }\n return { kind: 'legacy', output: stdout }\n}\n\n/**\n * Run one compile through the active backend.\n *\n * @param source the `.aihu` source (stdin for the spawn backend)\n * @param legacyArgs the EXACT argv the pre-envelope spawn used for this call\n * (`--stdin --tag … [--ast-json|--route-json|…]`). The spawn\n * backend appends `--envelope <json>` to it, so an older\n * binary that ignores the flag still answers the legacy\n * request correctly.\n * @param options the envelope options (must agree with `legacyArgs`)\n * @internal\n */\nexport function _compileViaBackend(\n source: string,\n legacyArgs: string[],\n options: CompileEnvelopeOptions,\n): EnvelopeReply {\n const backend = _resolveCompileBackend()\n const optionsJson = JSON.stringify(options)\n if (backend.kind === 'native') {\n const envelope = JSON.parse(backend.compileEnvelope(source, optionsJson)) as CompileEnvelope\n return { kind: 'envelope', envelope }\n }\n const stdout = execFileSync(resolveSpawnBinPath(), [...legacyArgs, '--envelope', optionsJson], {\n input: source,\n encoding: 'utf8',\n })\n return parseEnvelopeReply(stdout)\n}\n","/**\n * transform-memo.ts — content-addressed memo cache for `aihu-compile` spawns.\n *\n * Every `.aihu` compile is a subprocess spawn (~6ms each, ~63% pure spawn\n * overhead), and the SSG prerender pass re-compiles every file a SECOND time:\n * `prerenderClose` (packages/app/src/prerender.ts) boots a second Vite server\n * that REUSES the already-resolved plugin instances and re-runs the same\n * transforms via `ssrLoadModule` — identical source, identical flags,\n * byte-identical output recompiled. css-engine's `compileSfc` additionally\n * re-spawns the compiler per file for its AST pass (`compileToAst`).\n *\n * This module memoises the RAW STDOUT of a spawn, keyed by a SHA-256 digest of\n * everything that determines that stdout:\n *\n * kind (transform | ast | route) + file id + options fingerprint\n * + binary identity (path + mtime + size) + source content\n *\n * Because the key is content-addressed, watch-mode correctness is free: an\n * edit changes the source hash, so a stale entry is simply never looked up\n * again. The binary stamp (mtime+size) additionally invalidates entries when\n * the compiler binary itself is rebuilt mid-session (dev-workspace cargo\n * rebuilds); the stat is ~µs against a ~6ms spawn.\n *\n * Growth is bounded by FIFO eviction at `MAX_ENTRIES` — inert stale entries\n * from long dev sessions age out. Deliberately NOT cleared on Vite\n * `buildStart`: the SSG prerender's second server fires its own start-of-run\n * hooks, and a clear there would defeat the exact pass-two hits this cache\n * exists for.\n *\n * All exports are `_`-prefixed internals of `@aihu/compiler`; consumers go\n * through `transform()` / `compileToAst()` / `compileRouteMeta()`.\n */\nimport { createHash } from 'node:crypto'\nimport { statSync } from 'node:fs'\n\n/**\n * FIFO size bound. 1024 entries × a few KB of compiled output ≈ a few MB —\n * comfortably above any real app's file count (one entry per file × kind ×\n * options variant) while keeping unbounded-session growth impossible.\n * @internal\n */\nexport const _MEMO_MAX_ENTRIES = 1024\n\nconst cache = new Map<string, string>()\nlet hits = 0\nlet misses = 0\nlet seeds = 0\n\n/**\n * Identity stamp for the compiler binary: path + mtime + size when statable,\n * path alone otherwise (e.g. tests pointing AIHU_COMPILE_BIN at a fake).\n * A rebuilt-in-place binary changes mtime/size → old entries become inert.\n * @internal\n */\nfunction _binStamp(binPath: string): string {\n try {\n const st = statSync(binPath)\n return `${binPath}:${st.mtimeMs}:${st.size}`\n } catch {\n return binPath\n }\n}\n\n/** @internal */\nexport function _memoKey(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n): string {\n return createHash('sha256')\n .update(kind)\n .update('\\0')\n .update(id)\n .update('\\0')\n .update(optionsFingerprint)\n .update('\\0')\n .update(_binStamp(binPath))\n .update('\\0')\n .update(source)\n .digest('hex')\n}\n\n/**\n * Memoised spawn: returns the cached stdout for an identical\n * (kind, source, id, options, binary) tuple, otherwise runs `spawn()` and\n * caches its result. The cached value is the raw stdout STRING — callers that\n * return structured data (`compileToAst`, `compileRouteMeta`) re-parse per\n * call so a mutated result object can never poison the cache.\n * @internal\n */\nexport function _memoizedSpawn(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n spawn: () => string,\n): string {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n const hit = cache.get(key)\n if (hit !== undefined) {\n hits++\n return hit\n }\n const out = spawn()\n misses++\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n // FIFO: Map preserves insertion order; drop the oldest entry.\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, out)\n return out\n}\n\n/**\n * Seed a memo entry WITHOUT a spawn — the envelope path's sibling-artifact\n * write. One envelope compile of a file yields js + ast + route from a single\n * parse; the js answers the current call, and the ast/route strings are\n * seeded here under the exact keys `compileToAst` / `compileRouteMeta` will\n * look up, so their later calls for the same source are pure cache hits.\n * Never overwrites an existing entry; counts in the `seeds` stat, not\n * hits/misses.\n * @internal\n */\nexport function _seedMemo(\n kind: string,\n source: string,\n id: string,\n optionsFingerprint: string,\n binPath: string,\n value: string,\n): void {\n const key = _memoKey(kind, source, id, optionsFingerprint, binPath)\n if (cache.has(key)) return\n if (cache.size >= _MEMO_MAX_ENTRIES) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n cache.set(key, value)\n seeds++\n}\n\n/** Test/diagnostic hook — wipe the memo and its counters. @internal */\nexport function _clearTransformMemo(): void {\n cache.clear()\n hits = 0\n misses = 0\n seeds = 0\n}\n\n/** Test/diagnostic hook — current memo size + hit/miss/seed counters. @internal */\nexport function _transformMemoStats(): {\n size: number\n hits: number\n misses: number\n seeds: number\n} {\n return { size: cache.size, hits, misses, seeds }\n}\n","/**\n * @aihu/compiler — TypeScript wrapper around the aihu-compile Rust binary.\n *\n * Exports:\n * transform(source, id) — compile a single .aihu file to TypeScript\n * aihuCompilerPlugin() — Vite plugin that wires transform() into the build\n */\nimport { execFileSync } from 'node:child_process'\nimport { basename } from 'node:path'\nimport { _backendStampPath, _compileViaBackend } from './envelope.ts'\nimport { resolveCompilerBinary } from './resolve-binary.ts'\nimport { _memoizedSpawn, _seedMemo } from './transform-memo.ts'\n\nexport type { CompileEnvelope, CompileEnvelopeOptions } from './envelope.ts'\n\n// Perf — in-process napi compile backend + single-parse envelope (see\n// js/envelope.ts and js/native.ts). `transform()` / `compileToAst()` /\n// `compileRouteMeta()` route memo → native addon → envelope CLI spawn →\n// legacy per-output spawn. Re-exported as internals for tests/diagnostics.\nexport {\n _compileViaBackend,\n _resetCompileBackend,\n _resolveCompileBackend,\n} from './envelope.ts'\nexport { _getCompilerNativeStateKind, _resetCompilerNative, loadCompilerNative } from './native.ts'\n// Perf — content-addressed memo over the compile spawns (see transform-memo.ts).\n// The SSG prerender re-runs every transform against a second Vite server with\n// identical inputs; the memo turns that whole second pass (and css-engine's\n// per-file `compileToAst` re-parse) into cache lookups instead of subprocess\n// spawns. Re-exported as internals so tests can reset/observe the cache.\nexport {\n _clearTransformMemo,\n _MEMO_MAX_ENTRIES,\n _transformMemoStats,\n} from './transform-memo.ts'\n\n// Binary resolution: env var override, then the per-platform optionalDependency\n// package (`@aihu/compiler-<platform>`) with a workspace `target/` dev fallback —\n// see js/resolve-binary.ts (a clone of css-engine's resolver). The published\n// @aihu/compiler tarball ships only the JS shim (bin/aihu-compile.mjs); the\n// native binary arrives via the optionalDependency packages, so there is no\n// `../bin/aihu-compile` relative path anymore.\n//\n// Bug 6 fix — resolveBinPath() is CALL-TIME, not module-load-time. The Vite\n// plugin's `_maybeCompileUtilityCss` sets `process.env.AIHU_COMPILE_BIN` so\n// that css-engine's bundled copy of `compileToAst` spawns THIS compiler's\n// binary. Prior to this fix `binPath` was a module-scope const captured at\n// import time, so the env-var assignment was always too late and `compileSfc`\n// failed with ENOENT. Re-reading on every call is essentially free (an env\n// lookup, then a memoized resolve) and makes the AIHU_COMPILE_BIN handshake\n// actually work.\nfunction resolveBinPath(): string {\n return process.env.AIHU_COMPILE_BIN ?? resolveCompilerBinary()\n}\n\n// Minimal VitePlugin interface — avoids importing from 'vite' at compile time.\n// Structurally compatible with Vite's Plugin type.\ninterface VitePlugin {\n readonly name: string\n enforce?: 'pre' | 'post'\n resolveId?: (\n source: string,\n importer?: string,\n ) => string | null | undefined | Promise<string | null | undefined>\n load?: (id: string) => string | null | undefined | Promise<string | null | undefined>\n transform?: (\n code: string,\n id: string,\n ) => Promise<{ code: string; map: null }> | { code: string; map: null } | null | undefined\n /** GX Phase 1 (#437-GX) — end-of-build hook; prints the extract census. */\n buildEnd?: (error?: Error) => void | Promise<void>\n}\n\n/**\n * Options for `aihuCompilerPlugin()` (Plan 3.3 — Islands).\n */\nexport interface AihuCompilerPluginOptions {\n /**\n * When `true` (default), components the compiler classified as `'static'`\n * (read from the `// @aihu:island` marker via `_parseIslandMarker()`) are\n * emitted with a minimal HTML-only registration shim that ships **zero**\n * `@aihu/runtime` and `@aihu/signals` JS to the browser. Components\n * classified as `'interactive'` retain the full runtime path.\n *\n * Setting `islands: false` opts every component back into the unified\n * runtime path (Plan 3.2 baseline behaviour).\n */\n islands?: boolean\n\n /**\n * Project-wide rendering mode applied to every `.aihu` SFC compiled\n * by this plugin instance. When set, the plugin post-processes the\n * compiled JS to inject `, { shadowMode: '<mode>' }` as the third arg\n * to the emitted `defineElement(tag, defineComponent(...))` call.\n *\n * BINARY vocabulary (DA4 #437):\n * - `'shadow'` — shadow DOM (`attachShadow({ mode: 'open' })` internally;\n * open is the only browser mode aihu's composition/hydration\n * can use). `this.shadowRoot` is the non-null root.\n * - `'light'` — **no shadow root.** The component mounts into its own\n * element (`this.shadowRoot === null`). Required for global\n * utility-class CSS frameworks like Tailwind, UnoCSS, Pico\n * that rely on the cascade.\n *\n * Per-file override: the `$shadow: 'light' | 'shadow'` macro outranks this\n * config. Unset, pages/layouts default to `'light'` and leaves to\n * `'shadow'`.\n */\n shadowMode?: 'light' | 'shadow'\n\n /**\n * Build target threaded to the compiler binary (`--target`). Defaults to the\n * compiler's `universal` target (current behaviour). Set to `'client'` for a\n * browser bundle that must NOT ship the server `__agentBinding` (policy) and\n * instead gets the policy-free `@agent` opaque-ID dispatcher + the per-instance\n * `_registerAgentDispatcher` wiring the capability bridge reads after mount.\n * See `examples/agent-driven-demo`.\n */\n target?: 'client' | 'server' | 'universal'\n\n /**\n * Directory (relative to the project root) holding layout SFCs. Default:\n * `'src/layouts'`. Files under this directory are compiled in **layout mode**:\n * their custom element is registered under the namespaced tag\n * `aihu-layout-<stem>` (a layout stem like `app` is not a valid custom-element\n * name on its own), and their `<outlet>` lowers to a **passive**\n * `data-aihu-outlet` marker rather than the reactive route-driven boundary —\n * because `@aihu/app`'s client renderer fills the marker imperatively and the\n * reactive boundary would otherwise clear it on mount.\n *\n * Kept in sync with `@aihu/router`'s `layoutTagFor()` (`virtual:aihu-layouts`).\n */\n layoutsDir?: string\n}\n\n/**\n * Inject `{ shadowMode: '...' }` as the third argument to the emitted\n * `defineElement('tag', defineComponent(...))` call. The compiler emits\n * exactly two arguments today; this rewrites the closing of the\n * defineElement call to include the options object. Idempotent — leaves\n * code untouched when the closer is not in the expected shape.\n *\n * @internal\n */\nexport function _injectShadowMode(code: string, mode: 'light' | 'shadow'): string {\n // Match the trailing `))` that closes `defineElement(tag, defineComponent(setup))`.\n // The compiler always emits this exact two-paren close as the final tokens of\n // the defineElement call — we anchor on it and append the options object.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] is valid JS — matches any char including newlines\n const re = /(defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\([^]*\\))\\s*\\)/\n const replaced = code.replace(re, (_m, inner: string) => `${inner}, { shadowMode: '${mode}' })`)\n return replaced\n}\n\n/**\n * Light-DOM (`shadowMode:'light'`) recipes: redirect the authored `@style`\n * block's per-instance `host.adoptedStyleSheets = [__style__]` assignment to\n * `document.adoptedStyleSheets` so the recipe's class-scoped CSS reaches the\n * global cascade (a light-DOM host has no shadow root, making the original\n * setter a silent no-op). The module-level `__style__` is shared across\n * instances; the `includes` guard keeps the global adoption idempotent.\n *\n * @internal\n */\nexport function _globalizeAuthoredStyle(code: string): string {\n // The Rust codegen emits exactly: `(ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];`\n const re = /\\(ctx\\.host as ShadowRoot\\)\\.adoptedStyleSheets\\s*=\\s*\\[__style__\\];?/\n return code.replace(\n re,\n 'if (!document.adoptedStyleSheets.includes(__style__)) document.adoptedStyleSheets = [...document.adoptedStyleSheets, __style__];',\n )\n}\n\n/**\n * Read the compiler's AUTHORITATIVE island classification from the\n * `// @aihu:island <kind>` marker the Rust codegen emits for every component\n * (`emit.rs`, wave 3c). A component is a **static** island (server-render\n * only, no client hydration) or an **interactive** island (needs the signals\n * reactivity runtime).\n *\n * This REPLACES the old `_classifyIsland` regex post-pass, which re-derived\n * the answer by scanning generated code for `signal(`/`computed(`/`effect(`/…\n * calls — a Derived-property VIOLATION (docs/plans/ssr-build-performance-\n * findings.md §5: \"the compiler answered that question when it emitted the\n * code\"). The compiler now computes the classification from the IR (the same\n * fact-set that decides which owner-context primitives to import) and records\n * it as this marker; the plugin merely reads it.\n *\n * Crucially the compiler classifies CONSERVATIVELY and knows things the regex\n * could not: e.g. a component whose own body is inert but which declares\n * `$prop`s is `interactive` (props are reactive inputs the parent drives, and\n * its options-form emit is one the static-island shim cannot lower) — the old\n * regex saw no `signal(` call and wrongly classified it `static`.\n *\n * Defaults to `'interactive'` if the marker is somehow absent (an old binary,\n * or a future emit shape): the safe default never strips the runtime out from\n * under a component that needs it.\n *\n * @internal\n */\nexport function _parseIslandMarker(compiledCode: string): 'static' | 'interactive' {\n const m = /^\\/\\/ @aihu:island (static|interactive)$/m.exec(compiledCode)\n return m?.[1] === 'static' ? 'static' : 'interactive'\n}\n\n/**\n * GX Phase 1 (#437-GX) — parse the `// @aihu:extract read=<v> call=<v>` code\n * marker the Rust compiler emits for every server/universal build (the\n * resolved policy, the ratified default included). Phase 1 consumes it only\n * for the build census below; Phase 4 (E2) will read the SAME marker for\n * governed chunk routing.\n * @internal\n */\nexport function _parseExtractMarker(code: string): { read: string; call: string } | null {\n const m = /^\\/\\/ @aihu:extract read=(\\S+) call=(\\S+)$/m.exec(code)\n return m ? { read: m[1] as string, call: m[2] as string } : null\n}\n\n/**\n * GX Phase 1 (#437-GX) — format the per-value extract census (the DA-e census\n * pattern from #437: every build PRINTS the posture distribution, so the\n * default-vs-declared migration story stays visible rather than silent).\n * Returns the printable lines; pure so tests can assert the counts.\n * @internal\n */\nexport function _formatExtractCensus(\n census: ReadonlyMap<string, { read: string; call: string }>,\n): string[] {\n if (census.size === 0) return []\n const readCounts = new Map<string, number>()\n const callCounts = new Map<string, number>()\n for (const { read, call } of census.values()) {\n readCounts.set(read, (readCounts.get(read) ?? 0) + 1)\n callCounts.set(call, (callCounts.get(call) ?? 0) + 1)\n }\n const lines = [`[aihu] extract census — ${census.size} surface(s)`]\n for (const [value, n] of [...readCounts.entries()].sort()) lines.push(` read=${value}: ${n}`)\n for (const [value, n] of [...callCounts.entries()].sort()) lines.push(` call=${value}: ${n}`)\n return lines\n}\n\n/**\n * Extract the custom element tag name from compiler-emitted code.\n * The compiler always emits `defineElement('tag-name', ...)` as the\n * first call — pull the first string literal argument.\n * Returns `null` if no `defineElement` call is found.\n * @internal\n */\nfunction _extractElementTag(code: string): string | null {\n const m = /defineElement\\(\\s*['\"]([^'\"]+)['\"]/m.exec(code)\n return m ? (m[1] ?? null) : null\n}\n\n/**\n * Is `rawId` a layout SFC (a `.aihu` file under the configured layouts dir)?\n * Root-independent: matches the `<layoutsDir>/` segment anywhere in the path,\n * which is sufficient because the layouts dir is a project-relative convention.\n * @internal\n */\nexport function _isLayoutFile(rawId: string, layoutsDir: string): boolean {\n const ld = layoutsDir\n .replace(/\\\\/g, '/')\n .replace(/^\\.?\\//, '')\n .replace(/\\/+$/, '')\n if (!ld) return false\n return rawId.replace(/\\\\/g, '/').includes(`/${ld}/`)\n}\n\n/**\n * Layout custom-element tag for a filename stem. MUST match\n * `@aihu/router`'s `layoutTagFor()` so the generated `virtual:aihu-layouts`\n * map and the registered element agree on the tag.\n * @internal\n */\nexport function _layoutTag(stem: string): string {\n return `aihu-layout-${stem.toLowerCase()}`\n}\n\n/**\n * O1a (tag naming) — JS mirror of the Rust compiler's\n * `tags::kebab_component_tag` (packages/compiler/src/tags.rs). PascalCase→kebab,\n * else lowercase-verbatim: inserts '-' before an uppercase letter when the\n * previous char is lowercase/digit, OR (acronym boundary) the previous char is\n * uppercase and the next is lowercase; then lowercases all. Applied to the\n * file stem before it is passed as `--tag` so the JS driver's define-name\n * matches the Rust one (`UserCard.aihu` → `user-card`). Validation/erroring\n * (C450) is owned by the Rust compiler — this is the infallible transform only.\n * @internal\n */\nexport function kebabComponentTag(raw: string): string {\n let out = ''\n for (let i = 0; i < raw.length; i++) {\n // charAt (not raw[i]) returns string, never string|undefined — satisfies\n // noUncheckedIndexedAccess; charAt(i+1) yields '' past the end, matching\n // the \"no next char\" case.\n const c = raw.charAt(i)\n if (i > 0 && c >= 'A' && c <= 'Z') {\n const prev = raw.charAt(i - 1)\n const next = raw.charAt(i + 1)\n const prevLower = prev >= 'a' && prev <= 'z'\n const prevDigit = prev >= '0' && prev <= '9'\n const prevUpper = prev >= 'A' && prev <= 'Z'\n const nextLower = next >= 'a' && next <= 'z'\n if (prevLower || prevDigit || (prevUpper && nextLower)) out += '-'\n }\n out += c.toLowerCase()\n }\n return out\n}\n\n/**\n * Collapse the reactive `<outlet>` boundary the Rust codegen emits into a\n * passive `data-aihu-outlet` marker. Layout SFCs are rendered by `@aihu/app`'s\n * imperative client renderer, which fills the marker itself; the default\n * boundary's mount-time `effect()` reads `useRoute()` (null under the imperative\n * path) and clears the marker, which would wipe the page the renderer inserts.\n *\n * Anchors on the exact `const createOutletBoundary = () => { … return host; };`\n * block the codegen emits (`packages/compiler/src/codegen/emit.rs`). No-op when\n * the layout declares no `<outlet>`.\n * @internal\n */\nexport function _passivizeOutlet(code: string): string {\n return code.replace(\n /const createOutletBoundary = \\(\\) => \\{[\\s\\S]*?return host;\\s*\\n\\};/,\n `const createOutletBoundary = () => branch('div', { 'data-aihu-outlet': '' }, []);`,\n )\n}\n\n/**\n * Instrument a compiled `.aihu` module with HMR support.\n *\n * The compiler always emits:\n *\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { ... }))\n *\n * This function:\n *\n * 1. Adds `_hmrReplace` to the `@aihu/runtime` import.\n * 2. Prepends a module-level slot variable `__aihu_setup__`.\n * 3. Rewrites the single `defineComponent(` call so the setup function\n * is captured via an assignment expression:\n * `defineComponent(__aihu_setup__ = ` (valid JS; assignment has\n * lower precedence than arrow fn, so `defineComponent` still\n * receives the function as its argument).\n * 4. Appends `export { __aihu_setup__ as default }` so that Vite's\n * `import.meta.hot.accept` callback receives the new setup via\n * `newModule.default` on hot reload.\n * 5. Appends the `import.meta.hot.accept` block, gated on `__DEV__`.\n *\n * The `__DEV__` guard ensures production bundlers (where they replace\n * `__DEV__` with `false`) dead-code-eliminate the entire HMR block.\n *\n * @internal\n */\nfunction _buildHmrCode(compiledCode: string, elementTag: string): string {\n // Step 1 — add _hmrReplace to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hmrReplace')) parts.push('_hmrReplace')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Step 2+3 — prepend slot variable and rewrite the defineComponent call.\n // Compiler emits exactly one `defineComponent(` followed by a function expr.\n // Rewrite: defineComponent(fn) → defineComponent(__aihu_setup__ = fn)\n // Assignment expression evaluates to `fn`, so defineComponent still\n // receives the setup function as its first argument unchanged.\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const preamble = `let __aihu_setup__: ((ctx: any) => any) | undefined\\n`\n\n const patchedBody = withImport.replace(/\\bdefineComponent\\(/, 'defineComponent(__aihu_setup__ = ')\n\n const tag = JSON.stringify(elementTag)\n // Step 4+5 — postamble with default export and HMR acceptance.\n const postamble = `\nexport { __aihu_setup__ as default }\n\nif (typeof __DEV__ !== 'undefined' && __DEV__ && import.meta.hot) {\n import.meta.hot.accept((newModule) => {\n if (!newModule) return\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const newSetup = (newModule as any)['default']\n if (typeof newSetup !== 'function') return\n document.querySelectorAll(${tag}).forEach((el) => {\n _hmrReplace(el as HTMLElement, newSetup)\n })\n })\n}\n`\n\n return preamble + patchedBody + postamble\n}\n\n/**\n * Rewrite an interactive-island module so its `connectedCallback` waits\n * for the element to scroll into view before mounting. Plan 3.3 — applied\n * only when the consumer adds `defer` to the custom element tag (e.g.\n * `<my-counter defer>`); the runtime helper checks the attribute and\n * either mounts immediately or registers an `IntersectionObserver`.\n *\n * Implementation: the helper is added as a `_hydrateOnVisible` import\n * from `@aihu/runtime`, and the compiler-emitted `defineElement(...)`\n * call is wrapped in a `defineElement` that intercepts `connectedCallback`\n * to honour the `defer` attribute.\n *\n * The whole indirection is tree-shaken when no `.aihu` module reaches\n * this branch, because `_hydrateOnVisible` is exported from its own\n * sibling module inside `@aihu/runtime`.\n *\n * @internal\n */\nexport function _buildDeferredHydration(compiledCode: string, elementTag: string): string {\n // Add _hydrateOnVisible to the @aihu/runtime import.\n const withImport = compiledCode.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_hydrateOnVisible')) parts.push('_hydrateOnVisible')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // Wrap the class returned by defineComponent BEFORE defineElement\n // consumes it. The HTML spec caches lifecycle callbacks at\n // customElements.define() time, so we MUST mutate the prototype\n // before that call — not after. We accomplish this with a synchronous\n // helper invoked between defineComponent and defineElement.\n //\n // Source pattern (compiler-emitted):\n // defineElement('tag', defineComponent((_ctx) => { ... }))\n //\n // After this rewrite:\n // defineElement('tag', __aihu_wrap_defer__(defineComponent((_ctx) => { ... })))\n //\n // …with __aihu_wrap_defer__ defined in the appended preamble.\n const patched = withImport.replace(\n /defineElement\\(\\s*('[^']+'|\"[^\"]+\")\\s*,\\s*defineComponent\\(/,\n (_m, tagLit: string) => `defineElement(${tagLit}, __aihu_wrap_defer__(defineComponent(`,\n )\n // Match the closing `))` of the defineElement call. The HMR pass may\n // have inserted `__aihu_setup__ = ` before the inner function, but\n // the trailing `))` shape is unchanged. Replace exactly one occurrence\n // by anchoring on end-of-string trim; bail if the shape does not match.\n if (patched === withImport) {\n // The expected `defineElement(<tag>, defineComponent(` shape was not\n // present (e.g. compiler output changed). Skip defer wrapping rather\n // than emit broken code.\n return compiledCode\n }\n // Add a trailing `)` to balance the extra `(` from __aihu_wrap_defer__.\n // Source shape after _buildHmrCode is:\n // defineElement('tag', defineComponent(__aihu_setup__ = (_ctx) => {...}))\n // export { __aihu_setup__ as default }\n // if (typeof __DEV__ !== ...) { ... }\n // We must close BEFORE the export line. Match the first `))` followed\n // by a newline and `export` (or end-of-string for the unwrapped case).\n let balanced = patched.replace(/\\)\\s*\\)\\s*\\nexport\\s/, ')))\\nexport ')\n if (balanced === patched) {\n // No HMR postamble — the `))` is at end-of-string.\n balanced = patched.replace(/\\)\\s*\\)\\s*$/, ')))\\n')\n }\n if (balanced === patched) {\n // Could not find the matching `))` — bail out.\n return compiledCode\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const helper = `\n// Plan 3.3 (Islands) — defer attribute support. Wraps the constructor\n// returned by defineComponent so instances bearing the \\`defer\\` attribute\n// hydrate lazily via IntersectionObserver. Bare instances retain the\n// eager Plan 3.2 hydration path.\nfunction __aihu_wrap_defer__<T extends typeof HTMLElement>(Ctor: T): T {\n const orig = (Ctor.prototype as unknown as { connectedCallback?: () => void }).connectedCallback\n if (typeof orig !== 'function') return Ctor\n ;(Ctor.prototype as unknown as { connectedCallback: () => void }).connectedCallback = function (this: HTMLElement) {\n if (this.hasAttribute('defer')) {\n _hydrateOnVisible(this, () => orig.call(this))\n } else {\n orig.call(this)\n }\n }\n return Ctor\n}\n`\n void elementTag\n return helper + balanced\n}\n\n/**\n * Build a static-island shim for a compiled module.\n *\n * The compiled module emitted by the Rust codegen has the shape:\n *\n * import { branch, leaf, slot } from '@aihu/arbor'\n * import { defineComponent, defineElement } from '@aihu/runtime'\n * defineElement('tag', defineComponent((_ctx) => { return <tree> }))\n *\n * For a static island we know `<tree>` contains no `signal(`/`computed(`\n * calls. We can therefore:\n *\n * 1. Drop the `@aihu/runtime` import (saves ~600 B gz of defineComponent\n * + defineElement + bootstrap glue).\n * 2. Replace `defineElement(tag, defineComponent(setup))` with a tiny\n * inline class that mounts the tree directly via `mount()` (which the\n * arbor barrel already exports).\n * 3. Tag the file with a `// AIHU_STATIC_ISLAND` comment so consumers\n * can audit which routes shipped zero-JS-runtime.\n *\n * Falls back to the original code if the regex shape does not match\n * (defensive: a future compiler change must opt back into static-island\n * emission explicitly rather than silently break).\n *\n * @internal\n */\nexport function _buildStaticIsland(compiledCode: string, elementTag: string): string {\n // Confirm the shape we expect: a single defineElement(...) call wrapping\n // a single defineComponent(...) call. Bail out otherwise.\n const callRe = /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/\n if (!callRe.test(compiledCode)) return compiledCode\n\n // Strip the `@aihu/runtime` import line entirely — static islands\n // don't reference defineComponent/defineElement after the rewrite.\n const withoutRuntimeImport = compiledCode.replace(\n /^\\s*import\\s*\\{[^}]*\\}\\s*from\\s*'@aihu\\/runtime'\\s*;?\\s*$/m,\n '',\n )\n\n // Ensure `mount` is imported from @aihu/arbor (it already exposes\n // branch/leaf/slot, so we just append `mount` to the existing list).\n const withArborMount = withoutRuntimeImport.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n\n // Replace `defineElement('tag', defineComponent((_ctx) => { ... }))`\n // with an inline `customElements.define` whose connectedCallback mounts\n // the static tree. The setup function is captured verbatim by replacing\n // the wrapping calls with anonymous-IIFE bookends.\n const tagJson = JSON.stringify(elementTag)\n const rewritten = withArborMount\n .replace(\n /defineElement\\(\\s*['\"][^'\"]+['\"]\\s*,\\s*defineComponent\\(/,\n `customElements.define(${tagJson}, class extends HTMLElement {\\n connectedCallback() {\\n const root = this.attachShadow({ mode: 'open' })\\n const __aihu_setup__ = (`,\n )\n .replace(\n /\\)\\s*\\)\\s*$/,\n `)\\n mount(__aihu_setup__({ host: root, element: this }), root)\\n }\\n})\\n`,\n )\n\n return `// AIHU_STATIC_ISLAND — zero @aihu/runtime references\\n${rewritten}`\n}\n\n/**\n * Compile a .aihu source string to TypeScript.\n * map is null — source maps are deferred to v1 (OQ-C8)\n *\n * B3b — when `sidecarOut` is provided, also writes the per-SFC `.aihu.ts`\n * sidecar at that path. Callers (e.g. the Vite plugin) typically pass\n * `<source-id>.ts` so `tsc --noEmit` discovers per-SFC template expressions.\n */\nexport function transform(\n source: string,\n id: string,\n options?: {\n sidecarOut?: string\n target?: 'client' | 'server' | 'universal'\n /** Override the registered custom-element tag (default: file stem). Used for layouts. */\n tag?: string\n /**\n * #486 step 4 — emit the sidecar's attribute/component-prop type layer\n * (`--strict-templates`). Affects only the type-check surface written to\n * `sidecarOut`; the compiled JS is identical either way. Default off.\n */\n strictTemplates?: boolean\n },\n): { code: string; map: null } {\n // O1a (tag naming): normalize the stem so the JS driver's define-name\n // matches the Rust compiler's (`UserCard.aihu` → `user-card`). When a\n // component-shaped (uppercase-first) stem normalizes to a hyphen-less name\n // (`Comment.aihu` → `comment`), pass the RAW stem instead so the Rust\n // compiler surfaces its C450 error — the JS never validates or errors, but\n // it must not mask the error by pre-lowercasing. An explicit `options.tag`\n // (e.g. `_layoutTag` for layouts) passes through untouched.\n const rawStem = basename(id, '.aihu')\n const kebabStem = kebabComponentTag(rawStem)\n const stem = /^[A-Z]/.test(rawStem) && !kebabStem.includes('-') ? rawStem : kebabStem\n const args = ['--stdin', '--tag', options?.tag ?? stem, '--path', id]\n if (options?.sidecarOut) {\n args.push('--sidecar-out', options.sidecarOut)\n }\n // T6 (go-public demo) — thread the build target so a client bundle gets the\n // policy-free `@agent` dispatcher (and the per-instance registration the\n // capability bridge needs) instead of the server `__agentBinding`. Defaults to\n // the compiler's `universal` target when omitted (existing behaviour).\n if (options?.target) {\n args.push('--target', options.target)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n // `sidecarOut` BYPASSES the memo AND the envelope backends: it makes the\n // spawn write a file on disk — a cache hit would silently skip that side\n // effect, and the envelope API deliberately has no file-writing emits. The\n // Vite build path never passes it, so the SSG double-compile still fully\n // hits.\n if (options?.sidecarOut) {\n const code = execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n })\n return { code, map: null }\n }\n // Memo key: everything that shapes the emitted code. `id` is hashed\n // separately; the fingerprint carries the explicit options (`tag` covers the\n // layout override — the default stem is a pure function of `id`). The stamp\n // is the ACTIVE backend's file identity (native addon `.node` path when\n // in-process, CLI binary path when spawning) — see js/envelope.ts.\n const stamp = _backendStampPath()\n const target = options?.target ?? 'universal'\n // Sibling-artifact seeding (the single-parse envelope win): one compile of\n // a file yields js + ast + route, and the ast/route strings are seeded into\n // the memo under the exact keys `compileToAst` / `compileRouteMeta` derive\n // for the same (source, id) — so css-engine's AST pass and the router's\n // route scan become cache hits instead of re-parses. Only sound when the\n // tag is NOT overridden: those callers derive their own stem from `id`, and\n // an explicit tag (layout mode) resolves to a different define-name.\n const seedSiblings = options?.tag === undefined\n const code = _memoizedSpawn(\n 'transform',\n source,\n id,\n `target=${options?.target ?? ''}|tag=${options?.tag ?? ''}|strict=${options?.strictTemplates === true}`,\n stamp,\n () => {\n const reply = _compileViaBackend(source, args, {\n tag: options?.tag ?? stem,\n path: id,\n targets: [target],\n emits: seedSiblings ? ['js', 'ast', 'route'] : ['js'],\n ...(options?.strictTemplates ? { strictTemplates: true } : {}),\n })\n // Legacy reply — an older binary ignored `--envelope` and answered the\n // classic single-target request; its stdout IS the compiled JS.\n if (reply.kind === 'legacy') return reply.output\n const envelope = reply.envelope\n if (seedSiblings) {\n if (envelope.astJson !== undefined) {\n _seedMemo('ast', source, id, '', stamp, envelope.astJson)\n }\n _seedMemo('route', source, id, '', stamp, envelope.routeJson ?? 'null')\n }\n const js = envelope.targets[target]?.js\n if (js === undefined) {\n throw new Error(`[@aihu/compiler] envelope reply missing js for target '${target}'`)\n }\n return js\n },\n )\n return {\n code,\n map: null, // source maps deferred to v1 (OQ-C8)\n }\n}\n\n/**\n * Escape a CSS string for safe interpolation inside a JS template literal.\n * The Rust codegen places the authored `@style` body raw inside a backtick\n * literal, so it already assumes no backticks in `@style`. css-engine output\n * (theme tokens + utility rules) likewise never contains backticks, but we\n * escape `\\`, `` ` `` and `${` defensively so a future token value can't\n * break out of the literal.\n *\n * @internal\n */\nfunction _escapeForTemplateLiteral(css: string): string {\n return css.replace(/\\\\/g, '\\\\\\\\').replace(/`/g, '\\\\`').replace(/\\$\\{/g, '\\\\${')\n}\n\n/**\n * Fold css-engine-produced scoped CSS into a compiled `.aihu` module.\n *\n * The Rust codegen emits the authored `@style` block (when present) as:\n *\n * const __style__ = new CSSStyleSheet();\n * __style__.replaceSync(`<authored css>`);\n * defineElement('tag', defineComponent((ctx) => {\n * (ctx.host as ShadowRoot).adoptedStyleSheets = [__style__];\n * return ...\n * }))\n *\n * css-engine's `compileSfc` output is the COMPLETE per-SFC stylesheet:\n * `:host` theme tokens, the variant-resolved utility-class rules, AND the\n * folded authored `@style` block (under an `authored @style` CSS comment).\n * So it is authoritative — we adopt it as the single shadow `<style>` and\n * the authored `@style` keeps emitting through it (acceptance: \"@style still\n * emits correctly alongside\").\n *\n * Two shapes are handled:\n *\n * 1. **SFC has an `@style` block** — the Rust codegen already declared\n * `__style__` with the raw `@style` body. We REPLACE that body with the\n * css-engine output (which already CONTAINS the `@style` block) so the\n * `@style` rules are not duplicated. The existing `adoptedStyleSheets`\n * assignment is reused unchanged.\n *\n * 2. **SFC has NO `@style` block** — there is no `__style__`. We inject a\n * fresh `__style__` declaration after the last import and an\n * `adoptedStyleSheets` assignment as the first statement of the setup\n * function. The compiler emits the setup param as `_ctx` in this case;\n * we rename it to `ctx` so the injected `ctx.host` reference resolves.\n *\n * Runs on the RAW compiled output BEFORE the island / HMR / auto-wiring\n * transforms so those passes operate on the folded module uniformly:\n * - The static-island shim calls `__aihu_setup__({ host: root, ... })`\n * where `root` is the shadow root, so `ctx.host` is valid there too.\n * - The HMR / defer passes only touch the `defineElement(...)` wrapper and\n * the runtime import; they do not disturb `__style__` or the setup body.\n *\n * No-ops (returns input unchanged) when `css` is empty/whitespace.\n *\n * @internal\n */\nexport function _foldCssEngineStyles(compiledCode: string, css: string): string {\n if (!css.trim()) return compiledCode\n const escaped = _escapeForTemplateLiteral(css)\n\n // Shape 1 — an authored @style block already declared __style__. css-engine\n // output already includes that @style block, so REPLACE the replaceSync body\n // (between the backticks) wholesale to avoid duplicating the @style rules.\n // The codegen emits `__style__.replaceSync(`<body>`);` as a single statement;\n // match the body non-greedily up to the closing backtick + paren.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] matches any char incl. newlines\n const styleBodyRe = /(__style__\\.replaceSync\\(`)[^]*?(`\\);)/\n if (styleBodyRe.test(compiledCode)) {\n // Use a function replacer so any `$` in the CSS isn't read as a\n // replacement-pattern backreference.\n return compiledCode.replace(styleBodyRe, (_m, open: string, close: string) => {\n return `${open}${escaped}${close}`\n })\n }\n\n // Shape 2 — no @style block. Inject a fresh stylesheet + adoption.\n // Bail (no-op) if the expected defineComponent setup shape is absent.\n // The setup param is NOT always `ctx`/`_ctx`: an agent component (one with an\n // exposed member) emits `(__aihu_ctx__)` so `_registerAgentServerBinding`\n // can read `__aihu_ctx__?.element`. Capture whatever the param is and inject\n // against it — a literal `(ctx)` anchor silently no-ops on agent components,\n // which shipped scoped utility CSS with no adopted stylesheet.\n const setupRe = /defineComponent\\(\\s*\\((__?[A-Za-z0-9_]+)\\)\\s*=>\\s*\\{/\n const m = setupRe.exec(compiledCode)\n if (m == null) return compiledCode\n // `_ctx` is codegen's \"unused ctx\" marker; the injected adoption uses it, so\n // normalize to `ctx`. Any other name (`ctx`, `__aihu_ctx__`) is referenced\n // elsewhere in the setup body and MUST be preserved verbatim.\n const setupParam = m[1] === '_ctx' ? 'ctx' : m[1]\n\n // Inject the module-level stylesheet declaration after the last import line.\n const lines = compiledCode.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n const decl = `const __style__ = new CSSStyleSheet();\\n__style__.replaceSync(\\`${escaped}\\`);`\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, decl)\n } else {\n lines.unshift(decl)\n }\n let withDecl = lines.join('\\n')\n\n // Inject the adoption as the first statement of the setup body, using the\n // ACTUAL setup param (`ctx` for a plain component, `__aihu_ctx__` for an\n // agent component). `_ctx` is normalized to `ctx` above since the injected\n // statement now references it.\n withDecl = withDecl.replace(\n setupRe,\n `defineComponent((${setupParam}) => {\\n (${setupParam}.host as ShadowRoot).adoptedStyleSheets = [__style__];`,\n )\n return withDecl\n}\n\n/**\n * Virtual-module prefix used by the `shadowMode === 'light'` branch to route\n * per-SFC utility CSS through Vite's built-in CSS pipeline. The plugin\n * (`aihuCompilerPlugin`) implements `resolveId` + `load` for ids matching\n * `VIRTUAL_UTILITY_PREFIX + '<hash>.css'`, returning the stored CSS body so\n * Vite hoists it into the bundle CSS asset (`dist/assets/*.css`) — NOT into\n * `host.adoptedStyleSheets`, which is a no-op when there is no shadow root.\n *\n * The trailing `.css` extension is mandatory: Vite's built-in CSS plugin keys\n * off the extension to know it should run the CSS pipeline on the module.\n *\n * @internal\n */\nexport const VIRTUAL_UTILITY_PREFIX = '\\0virtual:aihu-utility/'\n\n/**\n * Stable short hash for keying the virtual-CSS module per source-SFC id.\n *\n * djb2-style; collisions are tolerable here because (a) each entry stores its\n * own CSS body, so a hash collision would only matter if two distinct SFCs\n * hashed to the same key AND were processed concurrently; (b) collisions are\n * recoverable — Vite would simply load the wrong CSS for one SFC; we still\n * keyed on the unhashed id internally to avoid that. The hash only appears in\n * the bundled asset URL.\n *\n * @internal\n */\nexport function _hashIdForUtilityCss(id: string): string {\n let h = 5381\n for (let i = 0; i < id.length; i++) {\n h = ((h * 33) ^ id.charCodeAt(i)) >>> 0\n }\n return h.toString(36)\n}\n\n/**\n * Bug 6 — `shadowMode === 'light'` branch.\n *\n * Routes utility CSS to Vite's CSS pipeline (which folds CSS imports into the\n * bundled `dist/assets/*.css` asset) instead of to `host.adoptedStyleSheets`\n * (a no-op on an element with no shadow root). Returns a prelude `import` that\n * the plugin's `resolveId` + `load` hooks resolve to the stored CSS body.\n *\n * The `__style__` shadow path is NOT invoked here — utility CSS for a\n * cascade-mode component MUST hit the global stylesheet, not a per-element\n * stylesheet that would be silently dropped by `HTMLElement`'s setter.\n *\n * Authored `@style` blocks still emit through the Rust codegen's `<style>`\n * node and are unaffected. (If a component opts into `shadowMode: 'light'` and\n * authors an `@style` block, the codegen still wires it through the\n * non-shadow path — that is the runtime's contract, not this hook's.)\n *\n * @internal\n */\nexport function _foldCssEngineStylesGlobal(\n compiledCode: string,\n css: string,\n id: string,\n): { code: string; virtualId: string } | null {\n if (!css.trim()) return null\n const hash = _hashIdForUtilityCss(id)\n const virtualId = `${VIRTUAL_UTILITY_PREFIX}${hash}.css`\n // Prepend the CSS import as a side-effect-only import so Vite's CSS plugin\n // hoists it into the bundle. We use the NULL-byte virtual id form\n // (Rollup/Vite convention for \"owned by this plugin\"); other plugins will\n // skip it. The compiler's transform returns this prepended code, which the\n // downstream esbuild/oxc strip leaves untouched (it's just an import).\n const prelude = `import ${JSON.stringify(virtualId)};\\n`\n return { code: prelude + compiledCode, virtualId }\n}\n\n// ─── v1.0.10a — compiler AST-export hook ─────────────────────────────────────\n//\n// Thin TS wrapper over the `aihu-compile --ast-json` flag. Returns the parsed\n// `.aihu` SFC AST in a stable, serializable shape consumed by the CSS engine's\n// AST scanner (`css-2-ast-scanner`). Mirrors the typed contract in\n// `docs/superpowers/specs/compiler-ast-export-hook.md` §4.\n\n/** Top-level AST export — one per .aihu SFC. */\nexport interface SfcAst {\n /** Resolved custom-element tag name (meta.name → route.name → file stem). */\n tag: string\n /** AST schema version — bumped on any breaking shape change (semver-tied). */\n astVersion: 1\n /** The @style block, if the SFC declared one. */\n style: SfcStyleBlock | null\n /** Parsed template tree. null when the SFC has no @template block. */\n template: SfcNode[] | null\n /** SFC-level metadata. */\n meta: SfcMeta\n}\n\nexport interface SfcStyleBlock {\n /** Verbatim CSS body of the @style block (braces stripped, $global token removed). */\n content: string\n /** 'scoped' (default) or 'global' (@style { $global ... }). */\n scope: 'scoped' | 'global'\n}\n\nexport interface SfcMeta {\n /** From @meta { name } / @route { name } / file stem — never null after resolution. */\n name: string\n}\n\n/** Discriminated union mirroring Rust `TemplateNode`. */\nexport type SfcNode =\n | { kind: 'element'; tag: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'macroElement'; name: string; attrs: SfcAttr[]; children: SfcNode[] }\n | { kind: 'text'; value: string }\n | { kind: 'interpolation'; expr: string }\n | { kind: 'ifBlock'; branches: Array<{ cond: string; body: SfcNode[] }> }\n | {\n kind: 'eachBlock'\n list: string\n item: string\n idx: string | null\n key: string | null\n body: SfcNode[]\n emptyBody: SfcNode[] | null\n }\n | { kind: 'htmlBlock'; expr: string }\n\n/** Discriminated union mirroring Rust `Attr` — the three class-forms key on `kind`. */\nexport type SfcAttr =\n | { kind: 'static'; name: string; value: string } // Form A\n | { kind: 'binding'; name: string; expr: string } // Form B\n | { kind: 'macro'; name: string; value: SfcMacroValue } // Form C (and on:/bind:/emit:/if/each/…)\n\nexport type SfcMacroValue =\n | { form: 'quoted'; value: string }\n | { form: 'curly'; expr: string }\n | { form: 'boolean' }\n\n/**\n * Parse a .aihu source string to its structured AST.\n *\n * Thin wrapper over the Rust binary (mirrors `transform()`): spawns\n * `aihu-compile --stdin --tag <stem> --ast-json`, feeds `source` on stdin, and\n * `JSON.parse`s stdout. `id` is optional and only used to derive the tag stem\n * and the `--path` arg (for `@route` C500 checks), identical to `transform()`.\n *\n * Throws on parse failure — the Rust binary exits non-zero and `execFileSync`\n * surfaces the diagnostic (same error path as `transform()`).\n */\n/**\n * Compile an SFC to its TYPE-CHECK SURFACE and return it as a string — the\n * `.aihu.ts` sidecar's content, without writing a file.\n *\n * This is the in-memory path `aihu-tsc` uses to hand `.aihu` files to TypeScript\n * as virtual files. The surface is line-preserving: line N of the returned text\n * corresponds to line N of the `.aihu` source, which is what lets a `tsc`\n * diagnostic be mapped straight back to the line the author wrote.\n *\n * Returns `''` when the SFC has no `@template` (nothing to check).\n */\nexport function compileSidecar(\n source: string,\n id?: string,\n options?: {\n /**\n * #486 step 4 — emit the attribute/component-prop type layer\n * (`--strict-templates`). Default off: the surface stays byte-identical\n * to the pre-#486 sidecar.\n */\n strictTemplates?: boolean\n },\n): string {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--sidecar-stdout']\n if (id) {\n args.push('--path', id)\n }\n if (options?.strictTemplates) {\n args.push('--strict-templates')\n }\n return execFileSync(resolveBinPath(), args, {\n input: source,\n encoding: 'utf8',\n // Capture stderr rather than inheriting it: the compiler's warnings (unhyphenated\n // tag names, undeclared cross-block refs) belong to `aihu build`, and would\n // otherwise interleave with the type diagnostics a caller is trying to read.\n // A hard compile failure still throws, carrying the message with it.\n stdio: ['pipe', 'pipe', 'pipe'],\n })\n}\n\nexport function compileToAst(source: string, id?: string): SfcAst {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--ast-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): css-engine's `compileSfc`\n // re-derives the AST per file — with the memo, the second parse of an\n // unchanged file is a lookup, and a preceding `transform()` of the same\n // source has usually SEEDED this exact entry from its envelope (one parse\n // for js + ast + route). The cached value is the raw JSON string; parse per\n // call so callers can never mutate a shared AST object.\n const stamp = _backendStampPath()\n const json = _memoizedSpawn('ast', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['ast'],\n })\n if (reply.kind === 'legacy') return reply.output\n const ast = reply.envelope.astJson\n if (ast === undefined) {\n throw new Error('[@aihu/compiler] envelope reply missing astJson')\n }\n return ast\n })\n return JSON.parse(json) as SfcAst\n}\n\n/**\n * Structured `@route` metadata (the `.route.json` sidecar shape). All fields\n * optional — only what the SFC's `@route` block declares is present. `head` is\n * left opaque here (the router owns its shape).\n */\nexport interface RouteMeta {\n pattern?: string\n name?: string\n layout?: string\n middleware?: string[]\n ssr?: boolean\n params?: string[]\n head?: unknown\n /**\n * GX Phase 1 fan-out (#437-GX): the resolved `extract` policy — always\n * present in the binary's route-json output since 0.1.12 (the default is\n * recorded, never implied by absence). Typed loose here: consumers\n * normalize fail-closed (`deriveReadPolicy` in `@aihu/server`).\n */\n extract?: { read?: unknown; call?: unknown }\n /**\n * GX Phase 4 fan-out (#466): the `data:` governed-resource declaration\n * (70-governed-data-access §2.1) — present only when the route declares one\n * (0.1.14+). `type` keys the server registry's provider; `preview` lists\n * the locked-state fields (omitted when none declared). Consumers: the\n * server runtime's boot validation + generated loader, and the router Vite\n * layer's C486 sibling-loader conflict check (§4.7).\n */\n data?: { type?: string; preview?: string[] }\n}\n\n/**\n * Parse a `.aihu` source string and return its `@route` metadata, or `null`\n * when the SFC declares no `@route` block.\n *\n * Thin wrapper over the Rust binary (mirrors {@link compileToAst}): spawns\n * `aihu-compile --stdin --tag <stem> --route-json`, feeds `source` on stdin,\n * and `JSON.parse`s stdout. This is how build tools recover full route\n * metadata (`head`/`middleware`/`params`/`ssr`/`layout`) for the SPA build\n * path, where no `.route.json` sidecar is written to disk.\n *\n * Throws on parse failure (same error path as `transform()`/`compileToAst()`).\n */\nexport function compileRouteMeta(source: string, id?: string): RouteMeta | null {\n const stem = id ? basename(id, '.aihu') : 'Component'\n const args = ['--stdin', '--tag', stem, '--route-json']\n if (id) {\n args.push('--path', id)\n }\n // Memoised (same key discipline as `transform()`): the router Vite plugin\n // calls this per route file per scan, and the SSG prerender's second server\n // triggers a full re-scan — and a preceding `transform()` of the same\n // source has usually SEEDED this entry from its envelope. Cache the raw\n // stdout; parse per call.\n const stamp = _backendStampPath()\n const out = _memoizedSpawn('route', source, id ?? '', '', stamp, () => {\n const reply = _compileViaBackend(source, args, {\n tag: stem,\n ...(id ? { path: id } : {}),\n emits: ['route'],\n })\n if (reply.kind === 'legacy') return reply.output\n return reply.envelope.routeJson ?? 'null'\n }).trim()\n if (out === '' || out === 'null') return null\n return JSON.parse(out) as RouteMeta\n}\n\n/**\n * Inject `_setMount(mount)` + `_setSignal(signal)` auto-wiring into a compiled\n * `.aihu` module. Adds the necessary symbols to existing imports and inserts\n * the boot calls right after the last `import` statement.\n *\n * @internal\n */\nexport function _injectAutoWiring(code: string): string {\n // 1. Add `mount` to the @aihu/arbor import (or create it).\n let result: string\n if (code.includes(\"from '@aihu/arbor'\")) {\n result = code.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/arbor'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('mount')) parts.push('mount')\n return `import { ${parts.join(', ')} } from '@aihu/arbor'`\n },\n )\n } else {\n result = `import { mount } from '@aihu/arbor'\\n${code}`\n }\n\n // 2. Add `signal` to the non-type @aihu/signals import (or create it).\n // Note: `import\\s+\\{` does NOT match `import type {` (the regex needs `{` immediately\n // after whitespace, whereas `import type {` has `type` in between). No negation guard\n // is needed — the replace callback below already skips `import type` lines.\n if (/import\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result)) {\n // There IS a value import from signals — add `signal` if missing.\n result = result.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/signals'/,\n (_m: string, imports: string) => {\n // Skip type-only imports\n if (_m.startsWith('import type')) return _m\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('signal')) parts.push('signal')\n return `import { ${parts.join(', ')} } from '@aihu/signals'`\n },\n )\n } else if (!/import.*from\\s*'@aihu\\/signals'/.test(result)) {\n // No signals import at all — insert after arbor import\n result = result.replace(\n /import\\s*\\{[^}]*\\}\\s*from\\s*'@aihu\\/arbor'/,\n (m: string) => `${m}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n // If only `import type { Signal }` exists, insert value import after it\n else if (\n /import\\s+type\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/.test(result) &&\n !result.match(/import\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals'/)\n ) {\n result = result.replace(\n /(import\\s+type\\s+\\{[^}]*\\}\\s+from\\s+'@aihu\\/signals')/,\n (_m: string, typeImport: string) => `${typeImport}\\nimport { signal } from '@aihu/signals'`,\n )\n }\n\n // 3. Add `_setMount`, `_setSignal` to the @aihu/runtime import.\n result = result.replace(\n /import\\s*\\{([^}]*)\\}\\s*from\\s*'@aihu\\/runtime'/,\n (_m: string, imports: string) => {\n const parts = imports\n .split(',')\n .map((s) => s.trim())\n .filter(Boolean)\n if (!parts.includes('_setMount')) parts.push('_setMount')\n if (!parts.includes('_setSignal')) parts.push('_setSignal')\n return `import { ${parts.join(', ')} } from '@aihu/runtime'`\n },\n )\n\n // 4. Insert boot calls after the last `import` statement.\n const lines = result.split('\\n')\n let lastImportIdx = -1\n for (let i = lines.length - 1; i >= 0; i--) {\n const t = (lines[i] ?? '').trim()\n if (t.startsWith('import ') || t.startsWith('import{')) {\n lastImportIdx = i\n break\n }\n }\n if (lastImportIdx !== -1) {\n lines.splice(lastImportIdx + 1, 0, '_setMount(mount)', '_setSignal(signal)', '')\n result = lines.join('\\n')\n }\n\n return result\n}\n\n/**\n * Vite plugin that compiles .aihu files to TypeScript during build and dev.\n *\n * Use `enforce: 'pre'` so the hook fires before Vite/Rollup's built-in\n * parsers attempt to process the raw .aihu content as JavaScript.\n *\n * @example\n * // vite.config.ts\n * import { aihuCompilerPlugin } from '@aihu/compiler'\n * export default { plugins: [aihuCompilerPlugin()] }\n *\n * **Known Limitation — Bun + Rollup4 ESM incompatibility (v0):**\n *\n * `bun vite build` fails in the `fixtures/vite-counter` fixture with two\n * cascading errors:\n *\n * 1. **Missing devDependency:** `vite` is declared only as an optional\n * `peerDependency` in `packages/compiler/package.json`. Bun does not\n * install optional peers automatically, so `bun vite build` exits\n * immediately with `Cannot find package 'vite'`.\n *\n * 2. **Bun + Rollup4 bridge:** Even with Vite installed, Bun processes\n * `vite.config.ts` through its own internal bundler before handing off\n * to Rollup4. When `@aihu/compiler` is resolved from the workspace\n * symlink (`dist/index.js`), Bun's ESM loader evaluates the module at\n * config-load time. The subprocess call inside `transform()` depends on\n * the Rust binary being at `../bin/aihu-compile` relative to `dist/`\n * (written by the postinstall hook). In a dev workspace where postinstall\n * has not run, this path does not exist and `execFileSync` throws. Bun surfaces\n * the error as a config-load failure, not a per-file transform error,\n * causing the entire build to abort before any `.aihu` file is\n * processed.\n *\n * **Workaround (v0):** Use `bun run integrate.ts` directly from\n * `packages/compiler/fixtures/vite-counter/`. This script calls\n * `transform()` from `@aihu/compiler` without involving Vite or Rollup.\n * Preconditions: (1) `cargo build --release` in `packages/compiler/`,\n * (2) `bun install` at the repo root.\n *\n * **v1 resolution:** Add `vite` as a `devDependency` in\n * `packages/compiler/package.json`; add a WASM or pre-built binary\n * strategy so the Rust binary is bundled with the npm package and does not\n * require a separate `cargo build --release` step.\n */\n/**\n * Minimal structural type for the `@aihu/css-engine` module surface this\n * plugin uses. Declared locally so the compiler never type-imports the\n * css-engine package (which would create a compile-time edge against an\n * optional peer that may be absent).\n *\n * @internal\n */\ninterface CssEngineModule {\n compileSfc(source: string, id?: string): string\n}\n\n// Memoised resolution of the optional `@aihu/css-engine` peer. `undefined`\n// = not yet attempted; `null` = attempted and unavailable (no-op path);\n// a module object = available. The dynamic import is attempted once per\n// process — repeated absence does not re-pay the resolution cost.\nlet _cssEngine: CssEngineModule | null | undefined\n\n// Whether we've already surfaced a one-shot warning that css-engine resolved\n// but `compileSfc` threw (typically: native css-core binary unresolvable in\n// the consumer's install — e.g. lockfile pins the per-platform placeholder\n// version). The transform stays non-fatal, but going fully silent leaves users\n// chasing \"why did my utility classes never emit?\". One warn per process.\nlet _cssEngineWarned = false\n\n// The optional-peer module specifier, held in a VARIABLE so TypeScript never\n// statically resolves `@aihu/css-engine`'s declarations at typecheck time.\n// css-engine depends on @aihu/compiler for its AST, so the two form a\n// circular package relationship; under CI's frozen install + moon build\n// ordering, css-engine's `dist`/`.d.ts` are not guaranteed to exist when\n// `compiler:typecheck` runs. A literal `import('@aihu/css-engine')` makes the\n// compiler emit TS2307 in that window (the `as` cast affects the RESULT type\n// only, not whether TS attempts module resolution). Resolving through this\n// variable keeps the import fully dynamic — no compile-time edge on the peer.\nconst _CSS_ENGINE_SPECIFIER = '@aihu/css-engine'\n\n/**\n * Lazily resolve `@aihu/css-engine` and compile a `.aihu` source's utility\n * classes to scoped CSS. Returns `''` when css-engine is not installed\n * (the optional-peer no-op path) or when compilation fails for any reason —\n * a CSS-engine failure MUST NOT break an otherwise-valid `.aihu` build.\n *\n * Sets `process.env.AIHU_COMPILE_BIN` to this plugin's resolved compiler\n * binary before calling `compileSfc`: css-engine re-derives the SFC AST via\n * its own bundled copy of `compileToAst`, whose binary path is resolved\n * relative to the css-engine package — which does NOT ship the compiler\n * binary. Pointing it at our `binPath` guarantees the AST css-engine parses\n * is produced by the exact same compiler this build uses.\n *\n * @internal\n */\nasync function _maybeCompileUtilityCss(source: string, id: string): Promise<string> {\n if (_cssEngine === null) return ''\n // Ensure css-engine's bundled `compileToAst` spawns the SAME compiler\n // binary this plugin uses (it has no compiler binary of its own). Set\n // this BEFORE the dynamic import so that any module-load-time evaluation\n // of `process.env.AIHU_COMPILE_BIN` in css-engine's bundled dist (older\n // bundles capture this into a module-scope const at line 8 of\n // `packages/css-engine/dist/index.js`) sees the correct value. After Bug 6,\n // the source `compileToAst` resolves the bin lazily on each call, so once\n // css-engine is rebuilt this set-before-import is belt-and-braces.\n if (process.env.AIHU_COMPILE_BIN == null) {\n try {\n process.env.AIHU_COMPILE_BIN = resolveBinPath()\n } catch {\n // No CLI binary resolvable (e.g. an addon-only install). css-engine's\n // NEWER bundles route compileToAst through the same native backend and\n // never read this var; older bundles will surface their own resolver\n // error, which the compileSfc try/catch below already treats as the\n // non-fatal no-op path.\n }\n }\n if (_cssEngine === undefined) {\n try {\n // Guarded, lazy, OPTIONAL — see the plugin transform for the rationale.\n // Importing via the `_CSS_ENGINE_SPECIFIER` variable (not a string\n // literal) keeps this fully dynamic: TS does NOT resolve the peer's\n // `.d.ts` at typecheck time, so `compiler:typecheck` passes even when\n // css-engine's `dist` has not been built (the CI build-order window).\n _cssEngine = (await import(_CSS_ENGINE_SPECIFIER)) as unknown as CssEngineModule\n } catch {\n _cssEngine = null\n return ''\n }\n }\n try {\n return _cssEngine.compileSfc(source, id)\n } catch (err) {\n // A css-engine compile failure is non-fatal: fall back to the no-op\n // path (utility classes don't emit) rather than aborting the build.\n // BUT — silently swallowing this means a user who clearly intends\n // css-engine to be active (the peer resolved) will never know their\n // utility classes are inert. Surface a one-shot warning with the\n // underlying error + an install/upgrade hint. Idempotent per process.\n if (!_cssEngineWarned) {\n _cssEngineWarned = true\n const msg = err instanceof Error ? err.message : String(err)\n console.warn(\n `[@aihu/compiler] @aihu/css-engine is installed but compileSfc() failed; ` +\n `utility classes will not emit. Original error: ${msg}\\n` +\n `Hint: ensure the native css-core binary is installed ` +\n `(install/upgrade @aihu/css-engine + its per-platform optional dep, ` +\n `or run \\`cargo build --release -p aihu-css-core\\` in a dev clone).`,\n )\n }\n return ''\n }\n}\n\nexport function aihuCompilerPlugin(options?: AihuCompilerPluginOptions): VitePlugin {\n const islandsEnabled = options?.islands !== false\n const shadowMode = options?.shadowMode\n const target = options?.target\n const layoutsDir = options?.layoutsDir ?? 'src/layouts'\n\n // Bug 6 — per-instance store of virtual utility-CSS modules. Keyed by the\n // full virtual id (NUL-prefixed). Populated by the transform hook when\n // `shadowMode === 'light'` produces utility CSS; drained by the `load` hook\n // when Vite's CSS pipeline asks for the module body. Lives on the plugin\n // instance so multiple `aihuCompilerPlugin()` calls in the same build don't\n // alias each other's css.\n const utilityCssStore = new Map<string, string>()\n\n // GX Phase 1 (#437-GX) — per-instance extract census. Keyed by rawId,\n // populated in transform from the compiler's `// @aihu:extract` marker;\n // printed per-value in buildEnd. Every build prints the posture\n // distribution (the DA-e census pattern from #437).\n const extractCensus = new Map<string, { read: string; call: string }>()\n\n return {\n name: 'aihu-compiler',\n enforce: 'pre',\n buildEnd() {\n for (const line of _formatExtractCensus(extractCensus)) console.info(line)\n },\n resolveId(source) {\n // Own all `\\0virtual:aihu-utility/<hash>.css` ids so Vite's resolver\n // doesn't try to find them on disk. Returning the id verbatim is the\n // Rollup convention for \"I'll handle the load.\"\n if (source.startsWith(VIRTUAL_UTILITY_PREFIX)) return source\n return null\n },\n load(id) {\n if (!id.startsWith(VIRTUAL_UTILITY_PREFIX)) return null\n // Vite's CSS pipeline runs on the returned source because the id ends\n // in `.css` — it parses, minifies (in build), and hoists into a CSS\n // asset chunk that lands in `dist/assets/<name>-<hash>.css`.\n return utilityCssStore.get(id) ?? null\n },\n transform(code, id) {\n // Strip Vite query strings (e.g. `?import`, `?t=...`) before checking the extension.\n const rawId = id.split('?')[0]!\n if (!rawId.endsWith('.aihu')) return\n // Server-environment detection (Vite Environment API). A `.aihu` loaded\n // through an SSR module runner — the `output: 'static'` prerender's\n // `ssrLoadModule` (packages/app/src/prerender.ts), or any `vite dev` SSR\n // consumer — must NOT get the client/universal target: that emits a\n // module-level `new CSSStyleSheet()` and an unguarded `customElements`\n // registration, both of which throw in the DOM-less SSR runner\n // (`CSSStyleSheet is not defined`) and silently degrade the prerender to\n // an empty SPA shell. The `server` target guards DOM registration behind\n // `typeof customElements` and exports `__ssr` (a host-less arbor factory)\n // plus the `__aihu_ssr_string__` compiled string fast path that\n // @aihu/server's `renderToString` prefers — a pure string concatenation\n // that needs no DOM. An explicit `target` option still wins (a caller\n // that pins a target owns the consequences); we only fill the SSR default\n // when none was configured. `this.environment` is absent on pre-Environment\n // -API Vite, in which case the prior universal default stands.\n const isServerEnv =\n (this as { environment?: { config?: { consumer?: string } } })?.environment?.config\n ?.consumer === 'server'\n const effectiveTarget = target ?? (isServerEnv ? 'server' : undefined)\n return (async () => {\n // No `.aihu.ts` sidecar is written any more. Type-checking goes through\n // `aihu-tsc`, which projects each `.aihu` into the TypeScript program as a\n // VIRTUAL file — so the type-check surface never lands on disk beside the\n // source, where authors saw it, editors indexed it, and `.gitignore` had to\n // hide it. A build has no business writing type-checker inputs at all.\n //\n // Layout SFCs (under the layouts dir) compile in layout mode: a\n // namespaced `aihu-layout-<stem>` tag + a passive <outlet> marker.\n const isLayout = _isLayoutFile(rawId, layoutsDir)\n const layoutTag = isLayout ? _layoutTag(basename(rawId, '.aihu')) : undefined\n const tOpts = {\n ...(effectiveTarget ? { target: effectiveTarget } : {}),\n ...(layoutTag ? { tag: layoutTag } : {}),\n }\n const result = transform(code, rawId, tOpts)\n // GX Phase 1 (#437-GX) — record this surface's resolved extract policy\n // for the build census (client-target builds carry no marker: policy\n // never reaches client artifacts).\n const extractMarker = _parseExtractMarker(result.code)\n if (extractMarker) extractCensus.set(rawId, extractMarker)\n // §9.4 per-file shadow override: the Rust `$shadow` macro emits a leading\n // `// @aihu:shadow <mode>` marker; it wins over the plugin's global\n // shadowMode and drives BOTH _injectShadowMode and the css fold branch.\n const perFileShadow = /^\\/\\/ @aihu:shadow (light|shadow)\\b/m.exec(result.code)?.[1] as\n | 'light'\n | 'shadow'\n | undefined\n // DA4 (#437, the ratified flip) — the IMPLICIT page default: for an\n // `@route` unit with no `$shadow` pin the compiler emits the DISTINCT\n // default-marker token `// @aihu:shadow-default light`. Layout SFCs\n // (no `@route` block, so no compiler marker) get the same 'light'\n // default from `_isLayoutFile`. Precedence, ratified: `$shadow` pin >\n // plugin-global `shadowMode` config > page/layout default 'light' >\n // leaf default 'shadow' (the runtime's `?? 'shadow'` when nothing is\n // injected) — so an explicit plugin-global config still outranks the\n // implicit default, which is why this is not the pin marker.\n const perFileShadowDefault = /^\\/\\/ @aihu:shadow-default (light|shadow)\\b/m.exec(\n result.code,\n )?.[1] as 'light' | 'shadow' | undefined\n const impliedShadowDefault = perFileShadowDefault ?? (isLayout ? 'light' : undefined)\n const effectiveShadow = perFileShadow ?? shadowMode ?? impliedShadowDefault\n let compiled =\n effectiveShadow != null ? _injectShadowMode(result.code, effectiveShadow) : result.code\n // Light-DOM: the authored `@style` block compiled to a per-instance\n // `host.adoptedStyleSheets` assignment, but a light-DOM host has no\n // shadow root so that setter is a no-op. Redirect the module-level\n // sheet to `document.adoptedStyleSheets` (idempotent) so authored recipe\n // CSS reaches the global cascade alongside the css-engine utility CSS.\n if (effectiveShadow === 'light') compiled = _globalizeAuthoredStyle(compiled)\n if (isLayout) compiled = _passivizeOutlet(compiled)\n\n // ── css-engine hook (optional, lazy, no circular dep) ──────────────\n // @aihu/css-engine depends on @aihu/compiler (for its AST), so the\n // compiler MUST NOT hard-depend on it. It is declared an OPTIONAL\n // peerDependency and pulled in ONLY via this guarded dynamic import:\n // when present, we compile the SFC's utility classes to scoped CSS\n // and fold it into the component's shadow `<style>`; when absent the\n // import throws and we no-op (utility classes simply don't emit —\n // the pre-hook behaviour). This keeps css-engine an opt-in enhancement\n // with zero dependency cycle.\n const utilityCss = await _maybeCompileUtilityCss(code, rawId)\n if (utilityCss) {\n if (effectiveShadow === 'light') {\n // Bug 6 — no shadow root → `host.adoptedStyleSheets` is a no-op.\n // Route utility CSS through Vite's CSS pipeline via a virtual\n // `.css` import so it lands in `dist/assets/*.css` and reaches the\n // global cascade. The authored `@style` block (if any) still\n // emits via the Rust codegen's normal path and is unaffected.\n const folded = _foldCssEngineStylesGlobal(compiled, utilityCss, rawId)\n if (folded) {\n utilityCssStore.set(folded.virtualId, utilityCss)\n compiled = folded.code\n }\n } else {\n // `shadowMode: 'shadow'`: fold into the\n // per-component `CSSStyleSheet` adopted by the shadow root.\n compiled = _foldCssEngineStyles(compiled, utilityCss)\n }\n }\n\n const elementTag = _extractElementTag(compiled)\n\n let out: string\n\n // §9.4 — a base-extending recipe (`defineComponent({ base: X, ... })`)\n // MUST take the full defineComponent/defineElement path: the static\n // island shim inlines `class extends HTMLElement` and cannot honor a\n // base class. Force-classify it interactive regardless of signal usage.\n // biome-ignore lint/correctness/noEmptyCharacterClassInRegex: [^] is valid JS — matches any char including newlines\n const hasBase = /defineComponent\\(\\s*\\{[^]*?\\bbase\\s*:/.test(compiled)\n\n // Plan 3.3 — static-island fast path. Bypasses HMR injection because\n // a component with no signals has no setup state to hot-replace.\n // Static islands strip @aihu/runtime entirely — do NOT inject auto-wiring\n // (it would reference _setMount/_setSignal as undefined identifiers).\n //\n // DA4 (#437): like `hasBase`, a light-DOM component cannot take the\n // static-island shim — the shim inlines `attachShadow({ mode: 'open' })`\n // and cannot honor `shadowMode: 'light'` (and its tail rewrite does not\n // match the injected options argument). Keep the full runtime path so\n // the injected `{ shadowMode: 'light' }` reaches defineElement.\n if (isServerEnv) {\n // Server module-runner target (the SSG prerender's `ssrLoadModule`,\n // any dev-SSR consumer). The `server` compile target already exports\n // the host-less `__ssr` factory + the `__aihu_ssr_string__` string\n // fast path and guards its own custom-element registration behind\n // `typeof customElements`. The client-only instrumentation below is\n // wrong here: HMR (`_buildHmrCode`) prepends a `let __aihu_setup__`\n // slot that COLLIDES with the server target's named\n // `const __aihu_setup__` (duplicate declaration); the static-island\n // shim, `defer` hydration, and auto-wiring all inject browser mount\n // code the SSR render never runs. Emit the server target as-is; the\n // TS-strip below still applies.\n out = compiled\n } else if (\n islandsEnabled &&\n elementTag !== null &&\n !hasBase &&\n effectiveShadow !== 'light' &&\n _parseIslandMarker(compiled) === 'static'\n ) {\n out = _buildStaticIsland(compiled, elementTag)\n } else if (elementTag !== null) {\n // Inject HMR instrumentation. The injected block is gated on\n // `typeof __DEV__ !== 'undefined' && __DEV__` so production\n // bundlers dead-code-eliminate it when they set __DEV__ = false.\n out = _buildHmrCode(compiled, elementTag)\n // Plan 3.3 — interactive islands also gain `defer` attribute\n // support so individual instances can opt into lazy hydration.\n out = _buildDeferredHydration(out, elementTag)\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n } else {\n out = compiled\n // Inject auto-wiring so consumers don't need a manual main.ts bootstrap.\n out = _injectAutoWiring(out)\n }\n\n // The Rust compiler emits TypeScript (type casts, import type, etc.) and\n // the injected HMR / defer helpers also contain TS generics and casts.\n // Vite does NOT re-run its TS-strip step when a plugin returns code for a\n // non-.ts ID, so we must strip types ourselves before returning.\n //\n // Priority: always try transformWithEsbuild first — it strips types to\n // plain JS in both Vite 5 (via esbuild) and Vite 8 (deprecated wrapper).\n // Using moduleType:'ts' only as a last resort because `import('vite')`\n // resolves to the root node_modules vite (which may be v8 even when a\n // consumer project runs v5), causing v5's Rollup to receive raw TypeScript\n // and fail on import-type / as-casts.\n try {\n const vite = await import('vite')\n // Server module-runner env (the SSG prerender's `ssrLoadModule`, any\n // dev-SSR consumer): the runner's `ssrTransform` parses JS ONLY and\n // IGNORES the `moduleType: 'ts'` hint below (it drives rolldown\n // BUNDLING, not the module-runner), so TS MUST be stripped to real JS\n // right here or the runner throws `Expected a semicolon …` on the\n // first type annotation. Prefer `transformWithOxc` — Vite 8's native\n // transform, always present, with NO separate esbuild dependency\n // (Vite 8 made esbuild optional). This is what makes the SSR/SSG path\n // robust in a consumer project that never installs esbuild; the\n // esbuild branch below stays the default for the client path (and its\n // Vite-5 compatibility). Guarded on `isServerEnv` so the client build\n // is byte-for-byte unchanged.\n if (isServerEnv && typeof vite.transformWithOxc === 'function') {\n const stripped = await vite.transformWithOxc(out, 'component.ts', {\n lang: 'ts',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n }\n if ('transformWithEsbuild' in vite && typeof vite.transformWithEsbuild === 'function') {\n const stripped = await vite.transformWithEsbuild(out, 'component.ts', {\n target: 'esnext',\n sourcemap: false,\n })\n return { code: stripped.code, map: null }\n }\n // Fallback for future Vite versions where esbuild is fully removed:\n // return TS and let Rolldown strip types natively.\n // biome-ignore lint/suspicious/noExplicitAny: moduleType is rolldown API\n return { code: out, moduleType: 'ts', map: null } as any\n } catch {\n // If running outside Vite (e.g. tests, standalone transform), return as-is.\n return { code: out, map: null }\n }\n })()\n },\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aihu/compiler",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -24,17 +24,23 @@
|
|
|
24
24
|
"sideEffects": false,
|
|
25
25
|
"scripts": {
|
|
26
26
|
"build": "rolldown -c",
|
|
27
|
+
"build:native": "bun scripts/build-native.ts",
|
|
27
28
|
"build:wasm": "wasm-pack build --target web --out-dir pkg-wasm",
|
|
28
29
|
"typecheck": "tsc --noEmit",
|
|
29
30
|
"prepublishOnly": "bun run build",
|
|
30
31
|
"codemod:template-syntax": "bun js/codemods/template-syntax/run-migration.ts"
|
|
31
32
|
},
|
|
32
33
|
"optionalDependencies": {
|
|
33
|
-
"@aihu/compiler-darwin-arm64": "0.1.
|
|
34
|
-
"@aihu/compiler-darwin-x64": "0.1.
|
|
35
|
-
"@aihu/compiler-linux-x64-gnu": "0.1.
|
|
36
|
-
"@aihu/compiler-linux-arm64-gnu": "0.1.
|
|
37
|
-
"@aihu/compiler-win32-x64-msvc": "0.1.
|
|
34
|
+
"@aihu/compiler-darwin-arm64": "0.1.37",
|
|
35
|
+
"@aihu/compiler-darwin-x64": "0.1.37",
|
|
36
|
+
"@aihu/compiler-linux-x64-gnu": "0.1.37",
|
|
37
|
+
"@aihu/compiler-linux-arm64-gnu": "0.1.37",
|
|
38
|
+
"@aihu/compiler-win32-x64-msvc": "0.1.37",
|
|
39
|
+
"@aihu/compiler-native-darwin-arm64": "0.1.2",
|
|
40
|
+
"@aihu/compiler-native-darwin-x64": "0.1.2",
|
|
41
|
+
"@aihu/compiler-native-linux-x64-gnu": "0.1.2",
|
|
42
|
+
"@aihu/compiler-native-linux-arm64-gnu": "0.1.2",
|
|
43
|
+
"@aihu/compiler-native-win32-x64-msvc": "0.1.2"
|
|
38
44
|
},
|
|
39
45
|
"peerDependencies": {
|
|
40
46
|
"vite": ">=5.0.0"
|