@vesk/compiler 0.2.22 → 0.2.24

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.
@@ -0,0 +1,46 @@
1
+ import type { CompileFileResult } from '@vesk/compiler/src/types';
2
+ import type { BundledModuleData, ModuleBindingData } from '@vesk/compiler/src/module-imports';
3
+ export interface PrecompiledComponentEntry {
4
+ name: string;
5
+ /** Compiled component body source (`buildComponentMap`-style function code). */
6
+ source: string;
7
+ isAsync: boolean;
8
+ }
9
+ export type PrecompileBundledModule = BundledModuleData;
10
+ export type PrecompileBinding = ModuleBindingData;
11
+ export interface PrecompileSubFilePlan {
12
+ importPath: string;
13
+ plan: PrecompileFilePlan;
14
+ }
15
+ export interface PrecompileFilePlan {
16
+ /** Class-tagged JSON of this file's IRRoot. `null` on non-rendered sub files. */
17
+ ir: unknown | null;
18
+ /** The file's own import lines (for `loadRuntimeImports`). */
19
+ runtimeImports: string[];
20
+ /** This file's own compiled components. */
21
+ components: PrecompiledComponentEntry[];
22
+ /** The module bundle backing this file's local value imports. */
23
+ modules: PrecompileBundledModule[];
24
+ /** local name -> bundled module export. */
25
+ bindings: PrecompileBinding[];
26
+ /** Top-level statements, already `defineAction`-transformed for `server`. */
27
+ topLevelCode: string[];
28
+ /** The file's `.vsk` imports, precompiled recursively. */
29
+ subFiles: PrecompileSubFilePlan[];
30
+ }
31
+ /**
32
+ * Recursively JSON-serializes an IR root. IR class instances are tagged with
33
+ * their class name so `irFromJSON` can revive them with their prototypes;
34
+ * `Set` values (IRRoot.importedNames) are tagged too. ESTree node subtrees
35
+ * (Expression.ast / RuntimeStatement.ast) are plain objects and serialize
36
+ * generically — `start`/`end` and `loc` arrays are plain JSON.
37
+ */
38
+ export declare function irToJSON(value: unknown): unknown;
39
+ export declare function irFromJSON(value: unknown): unknown;
40
+ /**
41
+ * Rebuilds a `CompileFileResult` (ir + componentMap + __vesk) from a
42
+ * `precompile.ts` plan at SSR function load time. No source, no disk, no
43
+ * syntax analysis — the plan is fully compiled at build time.
44
+ */
45
+ export declare function hydratePrecompile(plan: PrecompileFilePlan): CompileFileResult;
46
+ //# sourceMappingURL=precompile-runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precompile-runtime.d.ts","sourceRoot":"","sources":["../src/precompile-runtime.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAyBlE,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAe9F,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,uBAAuB,GAAG,iBAAiB,CAAC;AAExD,MAAM,MAAM,iBAAiB,GAAG,iBAAiB,CAAC;AAElD,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,kBAAkB;IACjC,iFAAiF;IACjF,EAAE,EAAE,OAAO,GAAG,IAAI,CAAC;IACnB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,2CAA2C;IAC3C,UAAU,EAAE,yBAAyB,EAAE,CAAC;IACxC,iEAAiE;IACjE,OAAO,EAAE,uBAAuB,EAAE,CAAC;IACnC,2CAA2C;IAC3C,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,6EAA6E;IAC7E,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,0DAA0D;IAC1D,QAAQ,EAAE,qBAAqB,EAAE,CAAC;CACnC;AAqCD;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAiBhD;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CA0BlD;AA4GD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,kBAAkB,GAAG,iBAAiB,CAE7E"}
@@ -0,0 +1,211 @@
1
+ import { createRequire } from 'node:module';
2
+ import { join } from 'node:path';
3
+ import { Expression, IRRoot, ComponentIR, StaticNode, TextNode, DynamicBinding, OpaqueDynamicRegion, MapRegion, ComponentRef, ComponentCall, SlotNode, WhileLoop, SwitchCase, SwitchBlock, TryCatch, TrackDecl, RuntimeStatement, ForLoop, ServerBlock, ClientBlock, HeadBlock, } from '@vesk/compiler/src/ir';
4
+ import { loadRuntimeImports, evalTopLevelCode } from '@vesk/compiler/src/server-utils';
5
+ // ---------------------------------------------------------------------------
6
+ // IR serialization.
7
+ // ---------------------------------------------------------------------------
8
+ const IR_CLASSES = [
9
+ ['Expression', Expression.prototype],
10
+ ['IRRoot', IRRoot.prototype],
11
+ ['ComponentIR', ComponentIR.prototype],
12
+ ['StaticNode', StaticNode.prototype],
13
+ ['TextNode', TextNode.prototype],
14
+ ['DynamicBinding', DynamicBinding.prototype],
15
+ ['OpaqueDynamicRegion', OpaqueDynamicRegion.prototype],
16
+ ['MapRegion', MapRegion.prototype],
17
+ ['ComponentRef', ComponentRef.prototype],
18
+ ['ComponentCall', ComponentCall.prototype],
19
+ ['SlotNode', SlotNode.prototype],
20
+ ['WhileLoop', WhileLoop.prototype],
21
+ ['SwitchCase', SwitchCase.prototype],
22
+ ['SwitchBlock', SwitchBlock.prototype],
23
+ ['TryCatch', TryCatch.prototype],
24
+ ['TrackDecl', TrackDecl.prototype],
25
+ ['RuntimeStatement', RuntimeStatement.prototype],
26
+ ['ForLoop', ForLoop.prototype],
27
+ ['ServerBlock', ServerBlock.prototype],
28
+ ['ClientBlock', ClientBlock.prototype],
29
+ ['HeadBlock', HeadBlock.prototype],
30
+ ];
31
+ const CLASS_PROTOS = new Map(IR_CLASSES);
32
+ const CLASS_BY_CTOR = new Map();
33
+ for (const [name, proto] of IR_CLASSES) {
34
+ const ctor = proto.constructor;
35
+ if (ctor)
36
+ CLASS_BY_CTOR.set(ctor, name);
37
+ }
38
+ /**
39
+ * Recursively JSON-serializes an IR root. IR class instances are tagged with
40
+ * their class name so `irFromJSON` can revive them with their prototypes;
41
+ * `Set` values (IRRoot.importedNames) are tagged too. ESTree node subtrees
42
+ * (Expression.ast / RuntimeStatement.ast) are plain objects and serialize
43
+ * generically — `start`/`end` and `loc` arrays are plain JSON.
44
+ */
45
+ export function irToJSON(value) {
46
+ if (value === null || typeof value !== 'object')
47
+ return value;
48
+ if (Array.isArray(value)) {
49
+ const out = [];
50
+ for (const item of value)
51
+ out.push(irToJSON(item));
52
+ return out;
53
+ }
54
+ if (value instanceof Set) {
55
+ return { __vskIrType: 'set', items: irToJSON([...value]) };
56
+ }
57
+ const clsName = CLASS_BY_CTOR.get(value.constructor);
58
+ const data = {};
59
+ for (const key of Object.keys(value)) {
60
+ data[key] = irToJSON(value[key]);
61
+ }
62
+ if (clsName)
63
+ return { __vskIrType: 'class', name: clsName, data };
64
+ return data;
65
+ }
66
+ export function irFromJSON(value) {
67
+ if (value === null || typeof value !== 'object')
68
+ return value;
69
+ if (Array.isArray(value)) {
70
+ const out = [];
71
+ for (const item of value)
72
+ out.push(irFromJSON(item));
73
+ return out;
74
+ }
75
+ const asRecord = value;
76
+ if (typeof asRecord.__vskIrType === 'string') {
77
+ if (asRecord.__vskIrType === 'set') {
78
+ const items = irFromJSON(asRecord.items);
79
+ return new Set(items);
80
+ }
81
+ if (asRecord.__vskIrType === 'class') {
82
+ const proto = CLASS_PROTOS.get(asRecord.name);
83
+ const revived = proto ? Object.create(proto) : {};
84
+ const data = irFromJSON(asRecord.data);
85
+ for (const key of Object.keys(data))
86
+ revived[key] = data[key];
87
+ return revived;
88
+ }
89
+ }
90
+ const out = {};
91
+ for (const key of Object.keys(asRecord)) {
92
+ out[key] = irFromJSON(asRecord[key]);
93
+ }
94
+ return out;
95
+ }
96
+ // ---------------------------------------------------------------------------
97
+ // Hydration.
98
+ // ---------------------------------------------------------------------------
99
+ function hydrateModuleScope(scope, plan) {
100
+ if (!plan.modules || plan.modules.length === 0)
101
+ return;
102
+ const byKey = new Map();
103
+ for (const m of plan.modules)
104
+ byKey.set(m.key, m);
105
+ const exportsByKey = new Map();
106
+ const evaluateModule = (key) => {
107
+ const existing = exportsByKey.get(key);
108
+ if (existing)
109
+ return existing;
110
+ const mod = byKey.get(key);
111
+ if (!mod)
112
+ return null;
113
+ const exportsObj = {};
114
+ exportsByKey.set(key, exportsObj);
115
+ const moduleObj = { exports: exportsObj };
116
+ const requireShim = (spec) => {
117
+ const depKey = mod.deps[spec];
118
+ if (depKey)
119
+ return evaluateModule(depKey) || {};
120
+ // Bare specifier, builtin, or node_modules dependency — hand it to the
121
+ // real loader (node_modules ships with the serverless deployment).
122
+ try {
123
+ const local = createRequire(join(mod.dir, '__vesk__.js'));
124
+ return local(spec);
125
+ }
126
+ catch (err) {
127
+ throw new Error(`Cannot resolve '${spec}' required by ${mod.key} during SSR hydrate: ${err?.message}`);
128
+ }
129
+ };
130
+ try {
131
+ const body = mod.code ? `'use strict';\n${mod.code}` : "'use strict';";
132
+ const fn = new Function('require', 'module', 'exports', '__dirname', '__filename', body);
133
+ fn(requireShim, moduleObj, moduleObj.exports, mod.dir, join(mod.dir, '__vesk__.js'));
134
+ }
135
+ catch (err) {
136
+ exportsByKey.delete(key);
137
+ console.warn(`[vesk] SSR: failed to evaluate bundled module ${mod.key} (${mod.dir}): ` +
138
+ `${err?.message ?? String(err)}`);
139
+ return null;
140
+ }
141
+ const finalized = moduleObj.exports && moduleObj.exports !== exportsObj
142
+ ? moduleObj.exports
143
+ : exportsObj;
144
+ exportsByKey.set(key, finalized);
145
+ return finalized;
146
+ };
147
+ // Evaluate every bundled module so side-effect imports (`import './x'`)
148
+ // run even when no binding references them. Cycles resolve through the
149
+ // pre-seeded partial exports objects. Dependencies pull themselves in via
150
+ // the require shim, so order across the array does not matter.
151
+ for (const m of plan.modules) {
152
+ if (!exportsByKey.has(m.key))
153
+ evaluateModule(m.key);
154
+ }
155
+ for (const binding of plan.bindings) {
156
+ const mod = exportsByKey.get(binding.key);
157
+ if (!mod)
158
+ continue;
159
+ if (binding.kind === '*') {
160
+ scope[binding.local] = mod;
161
+ }
162
+ else if (binding.kind === 'default') {
163
+ scope[binding.local] = binding.kind in mod ? mod[binding.kind] : mod;
164
+ }
165
+ else if (binding.kind in mod) {
166
+ scope[binding.local] = mod[binding.kind];
167
+ }
168
+ }
169
+ }
170
+ function hydrateFile(plan, seen) {
171
+ const ir = plan.ir ? irFromJSON(plan.ir) : new IRRoot([], [], new Set(), null, null, []);
172
+ const __vesk = loadRuntimeImports(plan.runtimeImports || []);
173
+ hydrateModuleScope(__vesk, plan);
174
+ evalTopLevelCode(plan.topLevelCode || [], __vesk);
175
+ const componentMap = new Map();
176
+ for (const entry of plan.components || []) {
177
+ let fn;
178
+ if (entry.isAsync) {
179
+ fn = new Function('props', '__registry', '__vesk', `return (async () => {\n${entry.source}\n})()`);
180
+ }
181
+ else {
182
+ fn = new Function('props', '__registry', '__vesk', entry.source);
183
+ }
184
+ fn.__veskScope = __vesk;
185
+ componentMap.set(entry.name, fn);
186
+ }
187
+ for (const sub of plan.subFiles || []) {
188
+ if (seen.has(sub.importPath))
189
+ continue;
190
+ seen.add(sub.importPath);
191
+ const subResult = hydrateFile(sub.plan, seen);
192
+ for (const [name, fn] of subResult.componentMap) {
193
+ if (!componentMap.has(name))
194
+ componentMap.set(name, fn);
195
+ }
196
+ for (const key of Object.keys(subResult.__vesk)) {
197
+ if (key in __vesk)
198
+ continue;
199
+ __vesk[key] = subResult.__vesk[key];
200
+ }
201
+ }
202
+ return { ir, componentMap, __vesk };
203
+ }
204
+ /**
205
+ * Rebuilds a `CompileFileResult` (ir + componentMap + __vesk) from a
206
+ * `precompile.ts` plan at SSR function load time. No source, no disk, no
207
+ * syntax analysis — the plan is fully compiled at build time.
208
+ */
209
+ export function hydratePrecompile(plan) {
210
+ return hydrateFile(plan, new Set());
211
+ }
@@ -0,0 +1,13 @@
1
+ import type { PrecompileFilePlan } from '@vesk/compiler/src/precompile-runtime';
2
+ /**
3
+ * Precompiles one `.vsk` file into a `PrecompileFilePlan` at BUILD time.
4
+ *
5
+ * This is the AOT counterpart of `compileFileInternal`: it fully compiles the
6
+ * file — component function sources, top-level statements (`defineAction`
7
+ * ids baked in), module bundle (closures preserved by bundling, not source
8
+ * paths), full IR — so a serverless function can rebuild the exact
9
+ * `CompileFileResult` at load time with `hydratePrecompile`, with no disk
10
+ * access and no `.vsk` source embedded in the deployment.
11
+ */
12
+ export declare function precompileFile(source: string, sourcePath?: string): PrecompileFilePlan;
13
+ //# sourceMappingURL=precompile.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"precompile.d.ts","sourceRoot":"","sources":["../src/precompile.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,kBAAkB,EAGnB,MAAM,uCAAuC,CAAC;AAG/C;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAEtF"}
@@ -0,0 +1,76 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname } from 'node:path';
3
+ import { parse } from '@vesk/compiler/src/parser';
4
+ import { generateIR } from '@vesk/compiler/src/ir-generator';
5
+ import { setVskHydrate } from '@vesk/compiler/src/server-utils';
6
+ import { buildComponentEntries } from '@vesk/compiler/src/server-jsgen';
7
+ import { transformTopLevelForActions } from '@vesk/compiler/src/actions';
8
+ import { collectVskImportPaths } from '@vesk/compiler/src/vsk-imports';
9
+ import { inlineMdImportsFrom, guessProjectRoots } from '@vesk/compiler/src/md-inline';
10
+ import { collectModuleBundle, } from '@vesk/compiler/src/module-imports';
11
+ import { irToJSON } from '@vesk/compiler/src/precompile-runtime';
12
+ /**
13
+ * Precompiles one `.vsk` file into a `PrecompileFilePlan` at BUILD time.
14
+ *
15
+ * This is the AOT counterpart of `compileFileInternal`: it fully compiles the
16
+ * file — component function sources, top-level statements (`defineAction`
17
+ * ids baked in), module bundle (closures preserved by bundling, not source
18
+ * paths), full IR — so a serverless function can rebuild the exact
19
+ * `CompileFileResult` at load time with `hydratePrecompile`, with no disk
20
+ * access and no `.vsk` source embedded in the deployment.
21
+ */
22
+ export function precompileFile(source, sourcePath) {
23
+ return precompileFileInternal(source, sourcePath, new Set());
24
+ }
25
+ function precompileFileInternal(source, sourcePath, seenImportFiles) {
26
+ if (sourcePath) {
27
+ const dir = dirname(sourcePath);
28
+ source = inlineMdImportsFrom(source, sourcePath, guessProjectRoots(dir));
29
+ }
30
+ const ast = parse(source, sourcePath ? { filename: sourcePath } : {});
31
+ const ir = generateIR(ast, source, sourcePath);
32
+ // `buildComponentEntries` consults `__vskHydrate` while generating the
33
+ // server component bodies; today's request-time `compileFile` ran under
34
+ // `setVskHydrate(true)`, so the build-time compile must match.
35
+ setVskHydrate(true);
36
+ let components = [];
37
+ try {
38
+ components = buildComponentEntries(ir).map((e) => ({
39
+ name: e.name,
40
+ source: e.source,
41
+ isAsync: e.isAsync,
42
+ }));
43
+ }
44
+ finally {
45
+ setVskHydrate(false);
46
+ }
47
+ const topLevelCode = transformTopLevelForActions(ir.topLevelCode, 'server');
48
+ const runtimeImports = ir.imports.slice();
49
+ const collector = { keysByAbs: new Map(), modules: [] };
50
+ const bindings = sourcePath !== undefined ? collectModuleBundle(ir.imports, dirname(sourcePath), collector) : [];
51
+ const subFiles = [];
52
+ if (sourcePath !== undefined) {
53
+ for (const importPath of collectVskImportPaths(ir.imports, sourcePath)) {
54
+ if (seenImportFiles.has(importPath))
55
+ continue;
56
+ seenImportFiles.add(importPath);
57
+ try {
58
+ const importedSrc = readFileSync(importPath, 'utf-8');
59
+ const subPlan = precompileFileInternal(importedSrc, importPath, seenImportFiles);
60
+ subFiles.push({ importPath, plan: subPlan });
61
+ }
62
+ catch {
63
+ // skip unresolvable imports — identical to compileFileInternal
64
+ }
65
+ }
66
+ }
67
+ return {
68
+ ir: irToJSON(ir),
69
+ runtimeImports,
70
+ components,
71
+ modules: collector.modules,
72
+ bindings,
73
+ topLevelCode,
74
+ subFiles,
75
+ };
76
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAM1D,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqB3F;AAkDD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnE;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,SAAS,EAAE,CAKjF;AAMD,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BzE;AA2HD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBrE;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAkCnG;AAoBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAiFhF"}
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAM1D,MAAM,WAAW,WAAW;IAC1B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;;GAMG;AACH,4DAA4D;AAC5D,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAajD;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAqB3F;AAkDD,wBAAgB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAUnE;AAwED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB,GAAG,SAAS,EAAE,CAKjF;AAMD,wBAAgB,cAAc,CAAC,aAAa,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CA2BzE;AA6HD,wBAAgB,cAAc,CAAC,IAAI,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAgBrE;AAED,MAAM,WAAW,oBAAoB;IACnC,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,OAAO,GAAE,oBAAyB,GAAG,MAAM,CAoCnG;AAoBD,wBAAgB,QAAQ,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,QAAQ,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAmGhF"}
package/dist/router.js CHANGED
@@ -126,6 +126,85 @@ export function extractMiddleware(sourcePath) {
126
126
  return null;
127
127
  }
128
128
  }
129
+ function isStandaloneLayoutFile(layoutPath) {
130
+ try {
131
+ if (!existsSync(layoutPath))
132
+ return false;
133
+ const src = readFileSync(layoutPath, 'utf-8');
134
+ // Fast path: if the word "standalone" doesn't appear, it's not standalone
135
+ if (!src.includes('standalone'))
136
+ return false;
137
+ try {
138
+ const ast = parse(src, { filename: 'layout.vsk' });
139
+ for (const stmt of ast.body) {
140
+ // export const standalone = true / export let standalone = true
141
+ if (stmt.type === 'ExportNamedDeclaration' && stmt.declaration?.type === 'VariableDeclaration') {
142
+ for (const decl of stmt.declaration.declarations) {
143
+ if (decl.id?.name === 'standalone' && decl.init?.type === 'Literal' && decl.init.value === true) {
144
+ return true;
145
+ }
146
+ if (decl.id?.name === 'standalone' && decl.init?.type === 'Identifier' && decl.init.name === 'true') {
147
+ return true;
148
+ }
149
+ }
150
+ }
151
+ // component Layout(props: { standalone: boolean }) or { standalone: true }
152
+ const comp = stmt.type === 'ComponentDeclaration' ? stmt
153
+ : stmt.type === 'ExportNamedDeclaration' && stmt.declaration?.type === 'ComponentDeclaration' ? stmt.declaration
154
+ : null;
155
+ if (comp && comp.params && comp.params.length > 0) {
156
+ const firstParam = comp.params[0];
157
+ // firstParam may have typeAnnotation: TSTypeAnnotation -> TSTypeLiteral with members
158
+ const typeNode = firstParam.typeAnnotation?.typeAnnotation || firstParam.typeAnnotation;
159
+ if (typeNode && typeNode.type === 'TSTypeLiteral' && Array.isArray(typeNode.members)) {
160
+ for (const mem of typeNode.members) {
161
+ if (mem.key?.name === 'standalone')
162
+ return true;
163
+ }
164
+ }
165
+ // Fallback: check raw param text for "standalone" if AST doesn't have it (e.g. no type)
166
+ if (firstParam.typeAnnotation && String(src.slice(firstParam.start, firstParam.end)).includes('standalone')) {
167
+ return true;
168
+ }
169
+ }
170
+ }
171
+ }
172
+ catch {
173
+ // Fallback to raw text check if parse fails
174
+ if (src.includes('standalone') && src.includes('true'))
175
+ return true;
176
+ }
177
+ // Final fallback: check for export standalone pattern in raw text
178
+ if (src.includes('export') && src.includes('standalone') && src.includes('true')) {
179
+ // Use token scan to avoid false positives from comments
180
+ const tokens = tokenizeCode(src);
181
+ if (tokens) {
182
+ for (let i = 0; i < tokens.length; i++) {
183
+ if (tokens[i].value === 'standalone') {
184
+ // Check if it's exported and assigned true
185
+ let hasExport = false;
186
+ for (let j = Math.max(0, i - 3); j < i; j++) {
187
+ if (tokens[j].value === 'export') {
188
+ hasExport = true;
189
+ break;
190
+ }
191
+ }
192
+ if (hasExport) {
193
+ for (let k = i + 1; k < Math.min(tokens.length, i + 4); k++) {
194
+ if (tokens[k].value === 'true')
195
+ return true;
196
+ }
197
+ }
198
+ }
199
+ }
200
+ }
201
+ }
202
+ return false;
203
+ }
204
+ catch {
205
+ return false;
206
+ }
207
+ }
129
208
  export function scanRoutes(appDir, options = {}) {
130
209
  if (!existsSync(appDir)) {
131
210
  return [];
@@ -260,6 +339,7 @@ function scanDirectory(rootDir, dir, parentPath, options) {
260
339
  const fullPath = seg
261
340
  ? (parentPath === '/' ? '/' : parentPath + '/') + seg
262
341
  : (parentPath || '/');
342
+ const isStandalone = hasLayout ? isStandaloneLayoutFile(join(dir, 'layout.vsk')) : false;
263
343
  const node = {
264
344
  path: seg,
265
345
  fullPath: collapseSlashes(fullPath) || '/',
@@ -277,6 +357,7 @@ function scanDirectory(rootDir, dir, parentPath, options) {
277
357
  children: [],
278
358
  sourceDir: dir,
279
359
  segmentCount: isGroup || dir === rootDir ? 0 : 1,
360
+ standalone: isStandalone ? true : undefined,
280
361
  };
281
362
  for (const entry of entries) {
282
363
  const entryPath = join(dir, entry);
@@ -369,13 +450,16 @@ export function generateRouteManifest(tree, options = {}) {
369
450
  parts.push(`offline: ${node.offline}`);
370
451
  if (node.network)
371
452
  parts.push(`network: ${node.network}`);
453
+ if (node.standalone)
454
+ parts.push(`standalone: true`);
372
455
  if (node.children.length > 0) {
373
456
  const childCodes = node.children.map((c) => genNode(c));
374
457
  parts.push(`children: [\n${childCodes.map((c) => '\t\t' + c).join(',\n')}\n\t]`);
375
458
  }
376
459
  const pathStr = JSON.stringify(node.fullPath);
377
460
  const groupStr = node.isGroup ? `, isGroup: true` : '';
378
- return `{ path: ${pathStr}${groupStr}, ${parts.join(', ')} }`;
461
+ const standaloneStr = node.standalone ? `, standalone: true` : '';
462
+ return `{ path: ${pathStr}${groupStr}${standaloneStr}, ${parts.join(', ')} }`;
379
463
  }
380
464
  const nodeCodes = tree.map(n => genNode(n));
381
465
  const components = flattenSources(tree);
@@ -422,12 +506,18 @@ export function matchUrl(tree, pathname) {
422
506
  if (rootNode) {
423
507
  chain.push(rootNode);
424
508
  }
509
+ function pushWithStandalone(node) {
510
+ if (node.standalone) {
511
+ chain.length = 0;
512
+ }
513
+ chain.push(node);
514
+ }
425
515
  function matchNodes(nodes, partIndex) {
426
516
  for (const node of nodes) {
427
517
  if (node.isGroup) {
428
518
  if (matchNodes(node.children, partIndex)) {
429
519
  if (node.layout)
430
- chain.push(node);
520
+ pushWithStandalone(node);
431
521
  return true;
432
522
  }
433
523
  continue;
@@ -436,8 +526,11 @@ export function matchUrl(tree, pathname) {
436
526
  return matchNodes(node.children, partIndex);
437
527
  }
438
528
  if (partIndex >= parts.length) {
439
- if (node.page) {
440
- chain.push(node);
529
+ // Only match if this node's fullPath exactly equals the requested pathname
530
+ // (e.g. "/" should only match the root, not the first child with a page)
531
+ const requestedPath = '/' + parts.join('/');
532
+ if (node.page && (node.fullPath === requestedPath || (requestedPath === '/' && node.fullPath === '/'))) {
533
+ pushWithStandalone(node);
441
534
  return true;
442
535
  }
443
536
  continue;
@@ -446,13 +539,17 @@ export function matchUrl(tree, pathname) {
446
539
  if (node.isCatchAll) {
447
540
  const paramName = node.path.startsWith(':') ? node.path.slice(1) : node.path;
448
541
  params[paramName] = parts.slice(partIndex).map(decodeURIComponent).join('/');
449
- chain.push(node);
542
+ pushWithStandalone(node);
450
543
  return true;
451
544
  }
452
545
  if (node.isDynamic) {
453
546
  const paramName = node.path.startsWith(':') ? node.path.slice(1) : node.path;
454
547
  params[paramName] = decodeURIComponent(part);
455
- chain.push(node);
548
+ pushWithStandalone(node);
549
+ const remainingDynamic = parts.slice(partIndex + 1);
550
+ if (remainingDynamic.length === 0 && node.page) {
551
+ return true;
552
+ }
456
553
  if (node.children.length > 0) {
457
554
  if (matchNodes(node.children, partIndex + 1))
458
555
  return true;
@@ -465,7 +562,11 @@ export function matchUrl(tree, pathname) {
465
562
  continue;
466
563
  }
467
564
  if (node.path === part) {
468
- chain.push(node);
565
+ pushWithStandalone(node);
566
+ const remaining = parts.slice(partIndex + 1);
567
+ if (remaining.length === 0 && node.page) {
568
+ return true;
569
+ }
469
570
  if (node.children.length > 0) {
470
571
  if (matchNodes(node.children, partIndex + 1))
471
572
  return true;
@@ -1,5 +1,7 @@
1
1
  export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, quoteAttr, safeJsonForScript, randomToken, DEFAULT_MAX_BODY_BYTES, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, csrfHmac, assertSameOrigin, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
2
2
  export { renderHeadHtml, mergeHeadHtml, } from '@vesk/compiler/src/server-head';
3
- export { irNodeToJS, generateFunctionBody, buildComponentMap, } from '@vesk/compiler/src/server-jsgen';
3
+ export { irNodeToJS, generateFunctionBody, buildComponentMap, buildComponentEntries, } from '@vesk/compiler/src/server-jsgen';
4
+ export { irToJSON, irFromJSON, hydratePrecompile, } from '@vesk/compiler/src/precompile-runtime';
5
+ export { precompileFile, } from '@vesk/compiler/src/precompile';
4
6
  export { compileFile, render, renderPage, ssg, renderFullPage, renderPageStream, buildDataScripts, applyHeadPlugins, applyHeadInjects, applyHtmlPlugins, } from '@vesk/compiler/src/server-render';
5
7
  //# sourceMappingURL=server-codegen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-codegen.d.ts","sourceRoot":"","sources":["../src/server-codegen.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,sBAAsB,EACtB,GAAG,EACH,MAAM,EACN,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,GAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,GAClB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,EACH,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC"}
1
+ {"version":3,"file":"server-codegen.d.ts","sourceRoot":"","sources":["../src/server-codegen.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,gBAAgB,EAChB,YAAY,EACZ,QAAQ,EACR,UAAU,EACV,SAAS,EACT,iBAAiB,EACjB,WAAW,EACX,sBAAsB,EACtB,GAAG,EACH,MAAM,EACN,WAAW,EACX,cAAc,EACd,oBAAoB,EACpB,mBAAmB,EACnB,cAAc,EACd,oBAAoB,EACpB,kBAAkB,EAClB,gBAAgB,EAChB,eAAe,EACf,gBAAgB,EAChB,YAAY,EACZ,OAAO,EACP,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,mBAAmB,EACnB,eAAe,EACf,WAAW,EACX,aAAa,EACb,SAAS,EACT,eAAe,EACf,SAAS,EACT,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,YAAY,EACZ,eAAe,EACf,gBAAgB,EAChB,eAAe,EACf,SAAS,EACT,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,iBAAiB,EACjB,eAAe,GAChB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,cAAc,EACd,aAAa,GACd,MAAM,gCAAgC,CAAC;AAExC,OAAO,EACL,UAAU,EACV,oBAAoB,EACpB,iBAAiB,EACjB,qBAAqB,GACtB,MAAM,iCAAiC,CAAC;AAEzC,OAAO,EACL,QAAQ,EACR,UAAU,EACV,iBAAiB,GAClB,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EACL,cAAc,GACf,MAAM,+BAA+B,CAAC;AAEvC,OAAO,EACL,WAAW,EACX,MAAM,EACN,UAAU,EACV,GAAG,EACH,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,GACjB,MAAM,kCAAkC,CAAC"}
@@ -1,4 +1,6 @@
1
1
  export { setRuntimeModule, prettifyHtml, isStatic, escapeHtml, quoteAttr, safeJsonForScript, randomToken, DEFAULT_MAX_BODY_BYTES, raw, exprJS, tryEvalExpr, childrenToHTML, extractTopLevelNames, extractRuntimeNames, buildParamInit, resolveComponentName, loadRuntimeImports, evalTopLevelCode, callStaticProps, callLoadFunction, __vskHydrate, __vskId, __vskImportedNames, resetVskState, setVskHydrate, setVskImportedNames, securityHeaders, corsHeaders, corsPreflight, csrfToken, verifyCsrfToken, csrfGuard, csrfHmac, assertSameOrigin, signCookie, unsignCookie, setSignedCookie, readSignedCookie, securityComment, redactLog, setRedactLogging, createRateLimiter, getClientIp, getClientProtocol, applyTrustProxy, } from '@vesk/compiler/src/server-utils';
2
2
  export { renderHeadHtml, mergeHeadHtml, } from '@vesk/compiler/src/server-head';
3
- export { irNodeToJS, generateFunctionBody, buildComponentMap, } from '@vesk/compiler/src/server-jsgen';
3
+ export { irNodeToJS, generateFunctionBody, buildComponentMap, buildComponentEntries, } from '@vesk/compiler/src/server-jsgen';
4
+ export { irToJSON, irFromJSON, hydratePrecompile, } from '@vesk/compiler/src/precompile-runtime';
5
+ export { precompileFile, } from '@vesk/compiler/src/precompile';
4
6
  export { compileFile, render, renderPage, ssg, renderFullPage, renderPageStream, buildDataScripts, applyHeadPlugins, applyHeadInjects, applyHtmlPlugins, } from '@vesk/compiler/src/server-render';
@@ -2,5 +2,16 @@ import type { IRNode, ComponentIR, IRRoot } from '@vesk/compiler/src/ir';
2
2
  import { type TrackedInfo } from '@vesk/compiler/src/client-codegen';
3
3
  export declare function irNodeToJS(node: IRNode, importedNames?: Set<string> | null, isAsync?: boolean, tracked?: Map<string, TrackedInfo>): string;
4
4
  export declare function generateFunctionBody(comp: ComponentIR, importedNames: Set<string>): string;
5
+ export interface ComponentMapEntry {
6
+ name: string;
7
+ source: string;
8
+ isAsync: boolean;
9
+ }
10
+ /**
11
+ * Builds the compiled body source for every component in an IR root. Used by
12
+ * both `buildComponentMap` (live functions) and `precompile.ts` (AOT SSR plans
13
+ * that reconstruct the same functions at function-bundle load time).
14
+ */
15
+ export declare function buildComponentEntries(irRoot: IRRoot): ComponentMapEntry[];
5
16
  export declare function buildComponentMap(irRoot: IRRoot, useSharedScope: boolean): Map<string, Function>;
6
17
  //# sourceMappingURL=server-jsgen.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAWrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAmDjJ;AAiUD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAmD1F;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAkChG"}
1
+ {"version":3,"file":"server-jsgen.d.ts","sourceRoot":"","sources":["../src/server-jsgen.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAOzE,OAAO,EAAkG,KAAK,WAAW,EAAE,MAAM,mCAAmC,CAAC;AAoCrK,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,IAAI,EAAE,OAAO,GAAE,OAAe,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,WAAW,CAAC,GAAG,MAAM,CAmDjJ;AAsWD,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,WAAW,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,CAmD1F;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,iBAAiB,EAAE,CA4BzE;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,cAAc,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAYhG"}