@vmz/core 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/deployment-registry.d.ts +70 -0
- package/dist/deployment-registry.js +256 -0
- package/dist/dom-core.d.ts +1 -1
- package/dist/dom-core.js +1 -1
- package/dist/list-client-components.d.ts +44 -7
- package/dist/list-client-components.js +115 -75
- package/dist/render-host.d.ts +32 -0
- package/dist/render-host.js +81 -0
- package/dist/serve-host.mjs +19 -22
- package/dist/server.js +3 -1
- package/package.json +5 -1
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deployment graph → component registry bootstrap (shared by all SSR/DOM hosts).
|
|
3
|
+
* Replaces ad-hoc registerComponents calls with an explicit preload contract.
|
|
4
|
+
*/
|
|
5
|
+
export declare const DEPLOYMENT_SCHEMA = "vmz.deployment.v0";
|
|
6
|
+
/**
|
|
7
|
+
* @param {string} distDir
|
|
8
|
+
* @param {{ strict?: boolean }} [opts]
|
|
9
|
+
* @returns {any | null}
|
|
10
|
+
*/
|
|
11
|
+
export declare function readDeploymentDocument(distDir: any, opts?: {}): any;
|
|
12
|
+
/**
|
|
13
|
+
* @param {any} deployment
|
|
14
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source: string }>}
|
|
15
|
+
*/
|
|
16
|
+
export declare function componentEntriesFromDeployment(deployment: any): any[];
|
|
17
|
+
/**
|
|
18
|
+
* @param {any} deployment
|
|
19
|
+
* @param {string[]} rootChunkIds
|
|
20
|
+
* @returns {Set<string>}
|
|
21
|
+
*/
|
|
22
|
+
export declare function collectDependsOnClosure(deployment: any, rootChunkIds: any): Set<unknown>;
|
|
23
|
+
/**
|
|
24
|
+
* Resolve tag conflicts; strict mode throws, dev mode warns and keeps last chunkId.
|
|
25
|
+
* @param {Array<{ chunkId: string, name: string, entry: string, source?: string }>} entries
|
|
26
|
+
* @param {{ strict?: boolean }} [opts]
|
|
27
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source?: string }>}
|
|
28
|
+
*/
|
|
29
|
+
export declare function dedupeComponentEntriesByTag(entries: any, opts?: {}): any[];
|
|
30
|
+
/**
|
|
31
|
+
* @param {Array<{ chunkId: string, name: string, entry: string, source?: string }>} entries
|
|
32
|
+
* @param {Record<string, string> | undefined} explicit name → chunkId (no .client.js)
|
|
33
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source?: string }>}
|
|
34
|
+
*/
|
|
35
|
+
export declare function mergeExplicitComponentEntries(entries: any, explicit: any): unknown[];
|
|
36
|
+
/**
|
|
37
|
+
* @param {string} distDir
|
|
38
|
+
* @param {{
|
|
39
|
+
* strict?: boolean,
|
|
40
|
+
* closureRoots?: string[],
|
|
41
|
+
* explicit?: Record<string, string>,
|
|
42
|
+
* }} [opts]
|
|
43
|
+
* @returns {Promise<Array<{ chunkId: string, name: string, entry: string, source?: string }>>}
|
|
44
|
+
*/
|
|
45
|
+
export declare function loadComponentEntries(distDir: any, opts?: {}): Promise<any[]>;
|
|
46
|
+
/**
|
|
47
|
+
* @param {string} distDir
|
|
48
|
+
* @param {Array<{ chunkId: string, name: string, entry: string }>} entries
|
|
49
|
+
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
50
|
+
* @param {{
|
|
51
|
+
* cacheBust?: string | number,
|
|
52
|
+
* loaded?: Set<string>,
|
|
53
|
+
* }} [opts]
|
|
54
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
55
|
+
*/
|
|
56
|
+
export declare function importAndRegisterComponentEntries(distDir: any, entries: any, registerComponents: any, opts?: {}): Promise<{}>;
|
|
57
|
+
/**
|
|
58
|
+
* Bootstrap component registry from deployment (all, closure, or directory fallback).
|
|
59
|
+
* @param {string} distDir
|
|
60
|
+
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
61
|
+
* @param {{
|
|
62
|
+
* strict?: boolean,
|
|
63
|
+
* closureRoots?: string[],
|
|
64
|
+
* explicit?: Record<string, string>,
|
|
65
|
+
* cacheBust?: string | number,
|
|
66
|
+
* loaded?: Set<string>,
|
|
67
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
68
|
+
* }} [opts]
|
|
69
|
+
*/
|
|
70
|
+
export declare function bootstrapComponentRegistry(distDir: any, registerComponents: any, opts?: {}): Promise<{}>;
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Deployment graph → component registry bootstrap (shared by all SSR/DOM hosts).
|
|
4
|
+
* Replaces ad-hoc registerComponents calls with an explicit preload contract.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import { readdir } from 'node:fs/promises';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
import { pathToFileURL } from 'node:url';
|
|
10
|
+
export const DEPLOYMENT_SCHEMA = 'vmz.deployment.v0';
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} distDir
|
|
13
|
+
* @param {{ strict?: boolean }} [opts]
|
|
14
|
+
* @returns {any | null}
|
|
15
|
+
*/
|
|
16
|
+
export function readDeploymentDocument(distDir, opts = {}) {
|
|
17
|
+
const strict = opts.strict === true;
|
|
18
|
+
const filePath = path.join(distDir, 'vmz-deployment.json');
|
|
19
|
+
if (!fs.existsSync(filePath)) {
|
|
20
|
+
if (strict) {
|
|
21
|
+
throw new Error(`vmz: missing vmz-deployment.json under ${distDir} (strict deployment mode)`);
|
|
22
|
+
}
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
let raw;
|
|
26
|
+
try {
|
|
27
|
+
raw = fs.readFileSync(filePath, 'utf8');
|
|
28
|
+
}
|
|
29
|
+
catch (e) {
|
|
30
|
+
if (strict)
|
|
31
|
+
throw new Error(`vmz: cannot read vmz-deployment.json: ${e instanceof Error ? e.message : e}`);
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
let doc;
|
|
35
|
+
try {
|
|
36
|
+
doc = JSON.parse(raw);
|
|
37
|
+
}
|
|
38
|
+
catch (e) {
|
|
39
|
+
if (strict)
|
|
40
|
+
throw new Error(`vmz: invalid vmz-deployment.json: ${e instanceof Error ? e.message : e}`);
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
if (doc.schema !== DEPLOYMENT_SCHEMA) {
|
|
44
|
+
if (strict)
|
|
45
|
+
throw new Error(`vmz: unsupported deployment schema ${doc.schema}`);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return doc;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* @param {any} deployment
|
|
52
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source: string }>}
|
|
53
|
+
*/
|
|
54
|
+
export function componentEntriesFromDeployment(deployment) {
|
|
55
|
+
/** @type {Array<{ chunkId: string, name: string, entry: string, source: string }>} */
|
|
56
|
+
const out = [];
|
|
57
|
+
for (const unit of deployment.units || []) {
|
|
58
|
+
if (unit?.kind !== 'component')
|
|
59
|
+
continue;
|
|
60
|
+
const chunkId = String(unit.chunkId || '').replace(/\\/g, '/');
|
|
61
|
+
const name = chunkId.split('/').pop();
|
|
62
|
+
if (!name)
|
|
63
|
+
continue;
|
|
64
|
+
const entry = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
|
|
65
|
+
out.push({
|
|
66
|
+
chunkId,
|
|
67
|
+
name,
|
|
68
|
+
entry,
|
|
69
|
+
source: String(unit.source || ''),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return out.sort((a, b) => a.chunkId.localeCompare(b.chunkId));
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @param {any} deployment
|
|
76
|
+
* @param {string[]} rootChunkIds
|
|
77
|
+
* @returns {Set<string>}
|
|
78
|
+
*/
|
|
79
|
+
export function collectDependsOnClosure(deployment, rootChunkIds) {
|
|
80
|
+
/** @type {Map<string, any>} */
|
|
81
|
+
const byId = new Map();
|
|
82
|
+
for (const unit of deployment.units || []) {
|
|
83
|
+
byId.set(String(unit.chunkId || '').replace(/\\/g, '/'), unit);
|
|
84
|
+
}
|
|
85
|
+
/** @type {Set<string>} */
|
|
86
|
+
const out = new Set();
|
|
87
|
+
/** @type {string[]} */
|
|
88
|
+
const stack = rootChunkIds.map((id) => String(id).replace(/\\/g, '/')).filter(Boolean);
|
|
89
|
+
while (stack.length) {
|
|
90
|
+
const id = stack.pop();
|
|
91
|
+
if (!id || out.has(id))
|
|
92
|
+
continue;
|
|
93
|
+
out.add(id);
|
|
94
|
+
const unit = byId.get(id);
|
|
95
|
+
if (!unit)
|
|
96
|
+
continue;
|
|
97
|
+
for (const dep of unit.dependsOn || []) {
|
|
98
|
+
const d = String(dep).replace(/\\/g, '/');
|
|
99
|
+
if (!out.has(d))
|
|
100
|
+
stack.push(d);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Resolve tag conflicts; strict mode throws, dev mode warns and keeps last chunkId.
|
|
107
|
+
* @param {Array<{ chunkId: string, name: string, entry: string, source?: string }>} entries
|
|
108
|
+
* @param {{ strict?: boolean }} [opts]
|
|
109
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source?: string }>}
|
|
110
|
+
*/
|
|
111
|
+
export function dedupeComponentEntriesByTag(entries, opts = {}) {
|
|
112
|
+
const strict = opts.strict === true;
|
|
113
|
+
/** @type {Map<string, { chunkId: string, name: string, entry: string, source?: string }>} */
|
|
114
|
+
const byTag = new Map();
|
|
115
|
+
for (const e of entries) {
|
|
116
|
+
const prev = byTag.get(e.name);
|
|
117
|
+
if (prev && prev.chunkId !== e.chunkId) {
|
|
118
|
+
const msg = `vmz: component tag <${e.name}> maps to both ${prev.chunkId} and ${e.chunkId}`;
|
|
119
|
+
if (strict)
|
|
120
|
+
throw new Error(msg);
|
|
121
|
+
console.warn(`${msg}; using ${e.chunkId}`);
|
|
122
|
+
}
|
|
123
|
+
byTag.set(e.name, e);
|
|
124
|
+
}
|
|
125
|
+
return [...byTag.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @param {Array<{ chunkId: string, name: string, entry: string, source?: string }>} entries
|
|
129
|
+
* @param {Record<string, string> | undefined} explicit name → chunkId (no .client.js)
|
|
130
|
+
* @returns {Array<{ chunkId: string, name: string, entry: string, source?: string }>}
|
|
131
|
+
*/
|
|
132
|
+
export function mergeExplicitComponentEntries(entries, explicit) {
|
|
133
|
+
/** @type {Map<string, { chunkId: string, name: string, entry: string, source?: string }>} */
|
|
134
|
+
const byTag = new Map(entries.map((e) => [e.name, e]));
|
|
135
|
+
if (explicit) {
|
|
136
|
+
for (const [name, chunk] of Object.entries(explicit)) {
|
|
137
|
+
const chunkId = String(chunk).replace(/\\/g, '/');
|
|
138
|
+
byTag.set(name, {
|
|
139
|
+
chunkId,
|
|
140
|
+
name,
|
|
141
|
+
entry: `${chunkId}.client.js`,
|
|
142
|
+
source: '',
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return [...byTag.values()].sort((a, b) => a.name.localeCompare(b.name));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* @param {string} distDir
|
|
150
|
+
* @param {{
|
|
151
|
+
* strict?: boolean,
|
|
152
|
+
* closureRoots?: string[],
|
|
153
|
+
* explicit?: Record<string, string>,
|
|
154
|
+
* }} [opts]
|
|
155
|
+
* @returns {Promise<Array<{ chunkId: string, name: string, entry: string, source?: string }>>}
|
|
156
|
+
*/
|
|
157
|
+
export async function loadComponentEntries(distDir, opts = {}) {
|
|
158
|
+
const strict = opts.strict === true;
|
|
159
|
+
const deployment = readDeploymentDocument(distDir, { strict });
|
|
160
|
+
/** @type {Array<{ chunkId: string, name: string, entry: string, source?: string }>} */
|
|
161
|
+
let entries = [];
|
|
162
|
+
if (deployment) {
|
|
163
|
+
entries = componentEntriesFromDeployment(deployment);
|
|
164
|
+
if (opts.closureRoots?.length) {
|
|
165
|
+
const closure = collectDependsOnClosure(deployment, opts.closureRoots);
|
|
166
|
+
entries = entries.filter((e) => closure.has(e.chunkId));
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
else if (!strict) {
|
|
170
|
+
entries = await listComponentEntriesFromDirectory(distDir);
|
|
171
|
+
}
|
|
172
|
+
entries = mergeExplicitComponentEntries(entries, opts.explicit);
|
|
173
|
+
return dedupeComponentEntriesByTag(entries, { strict });
|
|
174
|
+
}
|
|
175
|
+
/**
|
|
176
|
+
* @param {string} distDir
|
|
177
|
+
* @returns {Promise<Array<{ chunkId: string, name: string, entry: string }>>}
|
|
178
|
+
*/
|
|
179
|
+
async function listComponentEntriesFromDirectory(distDir) {
|
|
180
|
+
const folder = path.join(distDir, 'components');
|
|
181
|
+
/** @type {string[]} */
|
|
182
|
+
let files = [];
|
|
183
|
+
try {
|
|
184
|
+
files = await readdir(folder);
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
return files
|
|
190
|
+
.filter((name) => name.endsWith('.client.js'))
|
|
191
|
+
.map((f) => {
|
|
192
|
+
const name = f.replace(/\.client\.js$/, '');
|
|
193
|
+
const chunkId = `components/${name}`;
|
|
194
|
+
return { chunkId, name, entry: `components/${name}.client.js` };
|
|
195
|
+
})
|
|
196
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* @param {string} distDir
|
|
200
|
+
* @param {Array<{ chunkId: string, name: string, entry: string }>} entries
|
|
201
|
+
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
202
|
+
* @param {{
|
|
203
|
+
* cacheBust?: string | number,
|
|
204
|
+
* loaded?: Set<string>,
|
|
205
|
+
* }} [opts]
|
|
206
|
+
* @returns {Promise<Record<string, unknown>>}
|
|
207
|
+
*/
|
|
208
|
+
export async function importAndRegisterComponentEntries(distDir, entries, registerComponents, opts = {}) {
|
|
209
|
+
/** @type {Record<string, unknown>} */
|
|
210
|
+
const map = {};
|
|
211
|
+
const loaded = opts.loaded ?? null;
|
|
212
|
+
for (const entry of entries) {
|
|
213
|
+
if (loaded && loaded.has(entry.chunkId))
|
|
214
|
+
continue;
|
|
215
|
+
const abs = path.join(distDir, entry.entry);
|
|
216
|
+
let href = pathToFileURL(abs).href;
|
|
217
|
+
if (opts.cacheBust != null && opts.cacheBust !== '') {
|
|
218
|
+
href = `${href}?t=${encodeURIComponent(String(opts.cacheBust))}`;
|
|
219
|
+
}
|
|
220
|
+
const mod = await import(href);
|
|
221
|
+
map[entry.name] = mod.default;
|
|
222
|
+
if (loaded)
|
|
223
|
+
loaded.add(entry.chunkId);
|
|
224
|
+
}
|
|
225
|
+
if (Object.keys(map).length)
|
|
226
|
+
registerComponents(map);
|
|
227
|
+
return map;
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Bootstrap component registry from deployment (all, closure, or directory fallback).
|
|
231
|
+
* @param {string} distDir
|
|
232
|
+
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
233
|
+
* @param {{
|
|
234
|
+
* strict?: boolean,
|
|
235
|
+
* closureRoots?: string[],
|
|
236
|
+
* explicit?: Record<string, string>,
|
|
237
|
+
* cacheBust?: string | number,
|
|
238
|
+
* loaded?: Set<string>,
|
|
239
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
240
|
+
* }} [opts]
|
|
241
|
+
*/
|
|
242
|
+
export async function bootstrapComponentRegistry(distDir, registerComponents, opts = {}) {
|
|
243
|
+
const preload = opts.preload ?? (opts.closureRoots?.length ? 'closure' : 'all');
|
|
244
|
+
if (preload === 'none')
|
|
245
|
+
return {};
|
|
246
|
+
const loadOpts = {
|
|
247
|
+
strict: opts.strict,
|
|
248
|
+
explicit: opts.explicit,
|
|
249
|
+
closureRoots: preload === 'closure' ? opts.closureRoots : undefined,
|
|
250
|
+
};
|
|
251
|
+
const entries = await loadComponentEntries(distDir, loadOpts);
|
|
252
|
+
return importAndRegisterComponentEntries(distDir, entries, registerComponents, {
|
|
253
|
+
cacheBust: opts.cacheBust,
|
|
254
|
+
loaded: opts.loaded,
|
|
255
|
+
});
|
|
256
|
+
}
|
package/dist/dom-core.d.ts
CHANGED
|
@@ -39,7 +39,7 @@ export declare function __vmzPrecisionSnapshot(): {
|
|
|
39
39
|
patchesByBinding: any;
|
|
40
40
|
};
|
|
41
41
|
export declare function noteDomCreate(): void;
|
|
42
|
-
/** @param {Record<string, any>} map */
|
|
42
|
+
/** @param {Record<string, any>} map — Prefer createRenderHost().ensureComponents(); process-global registry. */
|
|
43
43
|
export declare function registerComponents(map: any): void;
|
|
44
44
|
/** Sync registry lookup (SSR serialize / client). */
|
|
45
45
|
export declare function getRegisteredComponent(name: any): any;
|
package/dist/dom-core.js
CHANGED
|
@@ -144,7 +144,7 @@ function noteDomMove() {
|
|
|
144
144
|
if (precision.enabled)
|
|
145
145
|
precision.domMoves++;
|
|
146
146
|
}
|
|
147
|
-
/** @param {Record<string, any>} map */
|
|
147
|
+
/** @param {Record<string, any>} map — Prefer createRenderHost().ensureComponents(); process-global registry. */
|
|
148
148
|
export function registerComponents(map) {
|
|
149
149
|
Object.assign(components, map);
|
|
150
150
|
}
|
|
@@ -1,25 +1,62 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Discover compiled client component modules from dist (deployment graph or components/).
|
|
3
|
-
* Shared by serve-host SSR
|
|
3
|
+
* Shared by serve-host SSR, static emit, and test hosts.
|
|
4
4
|
*/
|
|
5
|
+
export { DEPLOYMENT_SCHEMA, readDeploymentDocument, componentEntriesFromDeployment, collectDependsOnClosure, dedupeComponentEntriesByTag, mergeExplicitComponentEntries, loadComponentEntries, importAndRegisterComponentEntries, bootstrapComponentRegistry, } from './deployment-registry.js';
|
|
6
|
+
export { createRenderHost } from './render-host.js';
|
|
5
7
|
/**
|
|
6
8
|
* @param {string} dir
|
|
7
|
-
* @
|
|
9
|
+
* @param {{ strict?: boolean }} [opts]
|
|
10
|
+
* @returns {Promise<Array<{ name: string, entry: string, chunkId?: string }>>}
|
|
8
11
|
*/
|
|
9
|
-
export declare function listClientComponents(dir: any): Promise<
|
|
12
|
+
export declare function listClientComponents(dir: any, opts?: {}): Promise<{
|
|
13
|
+
name: any;
|
|
14
|
+
entry: any;
|
|
15
|
+
chunkId: any;
|
|
16
|
+
}[]>;
|
|
10
17
|
/**
|
|
11
18
|
* Sync variant for callers that already use fs sync (legacy static-emit helpers).
|
|
12
19
|
* @param {string} dir
|
|
13
|
-
* @
|
|
20
|
+
* @param {{ strict?: boolean }} [opts]
|
|
21
|
+
* @returns {Array<{ name: string, entry: string, chunkId?: string }>}
|
|
14
22
|
*/
|
|
15
|
-
export declare function listClientComponentsSync(dir: any):
|
|
23
|
+
export declare function listClientComponentsSync(dir: any, opts?: {}): {
|
|
24
|
+
name: any;
|
|
25
|
+
entry: any;
|
|
26
|
+
chunkId: any;
|
|
27
|
+
}[];
|
|
16
28
|
/**
|
|
17
|
-
*
|
|
29
|
+
* @param {Array<{ name: string, entry: string, chunkId?: string }>} entries
|
|
30
|
+
* @param {Record<string, string> | undefined} explicit
|
|
31
|
+
* @returns {Array<{ name: string, entry: string, chunkId?: string }>}
|
|
32
|
+
*/
|
|
33
|
+
export declare function mergeComponentEntries(entries: any, explicit: any): {
|
|
34
|
+
name: any;
|
|
35
|
+
entry: any;
|
|
36
|
+
chunkId: any;
|
|
37
|
+
}[];
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} distDir
|
|
40
|
+
* @param {Record<string, string> | undefined} [explicit]
|
|
41
|
+
* @param {{ strict?: boolean, closureRoots?: string[] }} [opts]
|
|
42
|
+
* @returns {Promise<Array<{ name: string, entry: string, chunkId?: string }>>}
|
|
43
|
+
*/
|
|
44
|
+
export declare function resolveComponentEntries(distDir: any, explicit: any, opts?: {}): Promise<{
|
|
45
|
+
name: any;
|
|
46
|
+
entry: any;
|
|
47
|
+
chunkId: any;
|
|
48
|
+
}[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Import all (or filtered) client components and register for SSR / static emit / test hosts.
|
|
18
51
|
* @param {string} distDir
|
|
19
52
|
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
20
53
|
* @param {{
|
|
21
54
|
* cacheBust?: string | number,
|
|
22
|
-
* include?: (entry: { name: string, entry: string }) => boolean,
|
|
55
|
+
* include?: (entry: { name: string, entry: string, chunkId?: string }) => boolean,
|
|
56
|
+
* explicit?: Record<string, string>,
|
|
57
|
+
* strict?: boolean,
|
|
58
|
+
* closureRoots?: string[],
|
|
59
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
23
60
|
* }} [opts]
|
|
24
61
|
*/
|
|
25
62
|
export declare function preloadComponentRegistry(distDir: any, registerComponents: any, opts?: {}): Promise<{}>;
|
|
@@ -1,111 +1,151 @@
|
|
|
1
1
|
// @ts-nocheck
|
|
2
2
|
/**
|
|
3
3
|
* Discover compiled client component modules from dist (deployment graph or components/).
|
|
4
|
-
* Shared by serve-host SSR
|
|
4
|
+
* Shared by serve-host SSR, static emit, and test hosts.
|
|
5
5
|
*/
|
|
6
6
|
import fs from 'node:fs';
|
|
7
|
-
import { readdir
|
|
7
|
+
import { readdir } from 'node:fs/promises';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { pathToFileURL } from 'node:url';
|
|
10
|
+
import { componentEntriesFromDeployment, dedupeComponentEntriesByTag, mergeExplicitComponentEntries, readDeploymentDocument, } from './deployment-registry.js';
|
|
11
|
+
export { DEPLOYMENT_SCHEMA, readDeploymentDocument, componentEntriesFromDeployment, collectDependsOnClosure, dedupeComponentEntriesByTag, mergeExplicitComponentEntries, loadComponentEntries, importAndRegisterComponentEntries, bootstrapComponentRegistry, } from './deployment-registry.js';
|
|
12
|
+
export { createRenderHost } from './render-host.js';
|
|
10
13
|
/**
|
|
11
14
|
* @param {string} dir
|
|
12
|
-
* @
|
|
15
|
+
* @param {{ strict?: boolean }} [opts]
|
|
16
|
+
* @returns {Promise<Array<{ name: string, entry: string, chunkId?: string }>>}
|
|
13
17
|
*/
|
|
14
|
-
export async function listClientComponents(dir) {
|
|
15
|
-
|
|
16
|
-
const
|
|
18
|
+
export async function listClientComponents(dir, opts = {}) {
|
|
19
|
+
const strict = opts.strict === true;
|
|
20
|
+
const deployment = readDeploymentDocument(dir, { strict });
|
|
21
|
+
if (deployment) {
|
|
22
|
+
return dedupeComponentEntriesByTag(componentEntriesFromDeployment(deployment).map((e) => ({
|
|
23
|
+
chunkId: e.chunkId,
|
|
24
|
+
name: e.name,
|
|
25
|
+
entry: e.entry,
|
|
26
|
+
source: e.source,
|
|
27
|
+
}))).map((e) => ({
|
|
28
|
+
name: e.name,
|
|
29
|
+
entry: e.entry,
|
|
30
|
+
chunkId: e.chunkId,
|
|
31
|
+
}));
|
|
32
|
+
}
|
|
33
|
+
if (strict) {
|
|
34
|
+
throw new Error(`vmz: missing vmz-deployment.json under ${dir} (strict deployment mode)`);
|
|
35
|
+
}
|
|
36
|
+
const folder = path.join(dir, 'components');
|
|
37
|
+
/** @type {string[]} */
|
|
38
|
+
let files = [];
|
|
17
39
|
try {
|
|
18
|
-
|
|
19
|
-
const dep = JSON.parse(raw);
|
|
20
|
-
for (const unit of dep.units || []) {
|
|
21
|
-
if (unit?.kind !== 'component')
|
|
22
|
-
continue;
|
|
23
|
-
const chunkId = String(unit.chunkId || '');
|
|
24
|
-
const name = chunkId.split('/').pop();
|
|
25
|
-
if (!name)
|
|
26
|
-
continue;
|
|
27
|
-
const entry = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
|
|
28
|
-
byName.set(name, { name, entry });
|
|
29
|
-
}
|
|
40
|
+
files = await readdir(folder);
|
|
30
41
|
}
|
|
31
42
|
catch {
|
|
32
|
-
|
|
33
|
-
}
|
|
34
|
-
if (byName.size === 0) {
|
|
35
|
-
const folder = path.join(dir, 'components');
|
|
36
|
-
let files = [];
|
|
37
|
-
try {
|
|
38
|
-
files = await readdir(folder);
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return [];
|
|
42
|
-
}
|
|
43
|
-
for (const f of files.filter((name) => name.endsWith('.client.js'))) {
|
|
44
|
-
const name = f.replace(/\.client\.js$/, '');
|
|
45
|
-
byName.set(name, { name, entry: `components/${name}.client.js` });
|
|
46
|
-
}
|
|
43
|
+
return [];
|
|
47
44
|
}
|
|
48
|
-
return
|
|
45
|
+
return files
|
|
46
|
+
.filter((name) => name.endsWith('.client.js'))
|
|
47
|
+
.map((f) => {
|
|
48
|
+
const name = f.replace(/\.client\.js$/, '');
|
|
49
|
+
return { name, entry: `components/${name}.client.js`, chunkId: `components/${name}` };
|
|
50
|
+
})
|
|
51
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
49
52
|
}
|
|
50
53
|
/**
|
|
51
54
|
* Sync variant for callers that already use fs sync (legacy static-emit helpers).
|
|
52
55
|
* @param {string} dir
|
|
53
|
-
* @
|
|
56
|
+
* @param {{ strict?: boolean }} [opts]
|
|
57
|
+
* @returns {Array<{ name: string, entry: string, chunkId?: string }>}
|
|
54
58
|
*/
|
|
55
|
-
export function listClientComponentsSync(dir) {
|
|
56
|
-
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const entry = String(unit.clientEntry || `${chunkId}.client.js`).replace(/\\/g, '/');
|
|
70
|
-
byName.set(name, { name, entry });
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
catch {
|
|
74
|
-
/* fall through */
|
|
75
|
-
}
|
|
59
|
+
export function listClientComponentsSync(dir, opts = {}) {
|
|
60
|
+
const strict = opts.strict === true;
|
|
61
|
+
const deployment = readDeploymentDocument(dir, { strict });
|
|
62
|
+
if (deployment) {
|
|
63
|
+
return dedupeComponentEntriesByTag(componentEntriesFromDeployment(deployment).map((e) => ({
|
|
64
|
+
chunkId: e.chunkId,
|
|
65
|
+
name: e.name,
|
|
66
|
+
entry: e.entry,
|
|
67
|
+
source: e.source,
|
|
68
|
+
}))).map((e) => ({
|
|
69
|
+
name: e.name,
|
|
70
|
+
entry: e.entry,
|
|
71
|
+
chunkId: e.chunkId,
|
|
72
|
+
}));
|
|
76
73
|
}
|
|
77
|
-
if (
|
|
78
|
-
|
|
79
|
-
let files = [];
|
|
80
|
-
try {
|
|
81
|
-
files = fs.readdirSync(folder);
|
|
82
|
-
}
|
|
83
|
-
catch {
|
|
84
|
-
return [];
|
|
85
|
-
}
|
|
86
|
-
for (const f of files.filter((name) => name.endsWith('.client.js'))) {
|
|
87
|
-
const name = f.replace(/\.client\.js$/, '');
|
|
88
|
-
byName.set(name, { name, entry: `components/${name}.client.js` });
|
|
89
|
-
}
|
|
74
|
+
if (strict) {
|
|
75
|
+
throw new Error(`vmz: missing vmz-deployment.json under ${dir} (strict deployment mode)`);
|
|
90
76
|
}
|
|
91
|
-
|
|
77
|
+
const folder = path.join(dir, 'components');
|
|
78
|
+
/** @type {string[]} */
|
|
79
|
+
let files = [];
|
|
80
|
+
try {
|
|
81
|
+
files = fs.readdirSync(folder);
|
|
82
|
+
}
|
|
83
|
+
catch {
|
|
84
|
+
return [];
|
|
85
|
+
}
|
|
86
|
+
return files
|
|
87
|
+
.filter((name) => name.endsWith('.client.js'))
|
|
88
|
+
.map((f) => {
|
|
89
|
+
const name = f.replace(/\.client\.js$/, '');
|
|
90
|
+
return { name, entry: `components/${name}.client.js`, chunkId: `components/${name}` };
|
|
91
|
+
})
|
|
92
|
+
.sort((a, b) => a.name.localeCompare(b.name));
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* @param {Array<{ name: string, entry: string, chunkId?: string }>} entries
|
|
96
|
+
* @param {Record<string, string> | undefined} explicit
|
|
97
|
+
* @returns {Array<{ name: string, entry: string, chunkId?: string }>}
|
|
98
|
+
*/
|
|
99
|
+
export function mergeComponentEntries(entries, explicit) {
|
|
100
|
+
const normalized = entries.map((e) => ({
|
|
101
|
+
chunkId: e.chunkId || `components/${e.name}`,
|
|
102
|
+
name: e.name,
|
|
103
|
+
entry: e.entry,
|
|
104
|
+
}));
|
|
105
|
+
return mergeExplicitComponentEntries(normalized, explicit).map((e) => ({
|
|
106
|
+
name: e.name,
|
|
107
|
+
entry: e.entry,
|
|
108
|
+
chunkId: e.chunkId,
|
|
109
|
+
}));
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* @param {string} distDir
|
|
113
|
+
* @param {Record<string, string> | undefined} [explicit]
|
|
114
|
+
* @param {{ strict?: boolean, closureRoots?: string[] }} [opts]
|
|
115
|
+
* @returns {Promise<Array<{ name: string, entry: string, chunkId?: string }>>}
|
|
116
|
+
*/
|
|
117
|
+
export async function resolveComponentEntries(distDir, explicit, opts = {}) {
|
|
118
|
+
const { loadComponentEntries } = await import('./deployment-registry.js');
|
|
119
|
+
const entries = await loadComponentEntries(distDir, {
|
|
120
|
+
strict: opts.strict,
|
|
121
|
+
closureRoots: opts.closureRoots,
|
|
122
|
+
explicit,
|
|
123
|
+
});
|
|
124
|
+
return entries.map((e) => ({ name: e.name, entry: e.entry, chunkId: e.chunkId }));
|
|
92
125
|
}
|
|
93
126
|
/**
|
|
94
|
-
* Import all (or filtered) client components and register for SSR / static emit.
|
|
127
|
+
* Import all (or filtered) client components and register for SSR / static emit / test hosts.
|
|
95
128
|
* @param {string} distDir
|
|
96
129
|
* @param {(map: Record<string, unknown>) => void} registerComponents
|
|
97
130
|
* @param {{
|
|
98
131
|
* cacheBust?: string | number,
|
|
99
|
-
* include?: (entry: { name: string, entry: string }) => boolean,
|
|
132
|
+
* include?: (entry: { name: string, entry: string, chunkId?: string }) => boolean,
|
|
133
|
+
* explicit?: Record<string, string>,
|
|
134
|
+
* strict?: boolean,
|
|
135
|
+
* closureRoots?: string[],
|
|
136
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
100
137
|
* }} [opts]
|
|
101
138
|
*/
|
|
102
139
|
export async function preloadComponentRegistry(distDir, registerComponents, opts = {}) {
|
|
103
|
-
|
|
140
|
+
let entries = await resolveComponentEntries(distDir, opts.explicit, {
|
|
141
|
+
strict: opts.strict,
|
|
142
|
+
closureRoots: opts.closureRoots,
|
|
143
|
+
});
|
|
144
|
+
if (opts.include)
|
|
145
|
+
entries = entries.filter((e) => opts.include(e));
|
|
104
146
|
/** @type {Record<string, unknown>} */
|
|
105
147
|
const map = {};
|
|
106
148
|
for (const entry of entries) {
|
|
107
|
-
if (opts.include && !opts.include(entry))
|
|
108
|
-
continue;
|
|
109
149
|
const abs = path.join(distDir, entry.entry);
|
|
110
150
|
let href = pathToFileURL(abs).href;
|
|
111
151
|
if (opts.cacheBust != null && opts.cacheBust !== '') {
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Unified SSR/DOM render host — explicit deployment bootstrap before renderToString/stream/mount.
|
|
3
|
+
* Hosts must not call renderToString until ensureComponents() has run for the active closure.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* @param {string} distDir
|
|
7
|
+
* @param {{
|
|
8
|
+
* strictDeployment?: boolean,
|
|
9
|
+
* strict?: boolean,
|
|
10
|
+
* explicit?: Record<string, string>,
|
|
11
|
+
* cacheBust?: string | number,
|
|
12
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
13
|
+
* closureRoots?: string[],
|
|
14
|
+
* }} [opts]
|
|
15
|
+
*/
|
|
16
|
+
export declare function createRenderHost(distDir: any, opts?: {}): Promise<{
|
|
17
|
+
distDir: any;
|
|
18
|
+
deployment: any;
|
|
19
|
+
dom: any;
|
|
20
|
+
loadedChunkIds: Set<unknown>;
|
|
21
|
+
ensureComponents: (rootChunkIds: any) => Promise<{}>;
|
|
22
|
+
closureChunkIds: (rootChunkIds: any) => Set<unknown>;
|
|
23
|
+
renderToString: any;
|
|
24
|
+
renderToStream: any;
|
|
25
|
+
mount: any;
|
|
26
|
+
hydrate: any;
|
|
27
|
+
resume: any;
|
|
28
|
+
destroy: any;
|
|
29
|
+
flushPending: any;
|
|
30
|
+
/** @deprecated Prefer ensureComponents via createRenderHost; low-level escape hatch. */
|
|
31
|
+
registerComponents: any;
|
|
32
|
+
}>;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Unified SSR/DOM render host — explicit deployment bootstrap before renderToString/stream/mount.
|
|
4
|
+
* Hosts must not call renderToString until ensureComponents() has run for the active closure.
|
|
5
|
+
*/
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import { pathToFileURL } from 'node:url';
|
|
8
|
+
import { bootstrapComponentRegistry, collectDependsOnClosure, loadComponentEntries, readDeploymentDocument, importAndRegisterComponentEntries, } from './deployment-registry.js';
|
|
9
|
+
/**
|
|
10
|
+
* @param {string} distDir
|
|
11
|
+
* @param {{
|
|
12
|
+
* strictDeployment?: boolean,
|
|
13
|
+
* strict?: boolean,
|
|
14
|
+
* explicit?: Record<string, string>,
|
|
15
|
+
* cacheBust?: string | number,
|
|
16
|
+
* preload?: 'all' | 'closure' | 'none',
|
|
17
|
+
* closureRoots?: string[],
|
|
18
|
+
* }} [opts]
|
|
19
|
+
*/
|
|
20
|
+
export async function createRenderHost(distDir, opts = {}) {
|
|
21
|
+
const strict = opts.strictDeployment === true || opts.strict === true;
|
|
22
|
+
const domPath = path.join(distDir, 'vmz-dom.js');
|
|
23
|
+
const dom = await import(pathToFileURL(domPath).href);
|
|
24
|
+
const deployment = readDeploymentDocument(distDir, { strict });
|
|
25
|
+
/** @type {Set<string>} */
|
|
26
|
+
const loadedChunkIds = new Set();
|
|
27
|
+
const bootstrapOpts = {
|
|
28
|
+
strict,
|
|
29
|
+
explicit: opts.explicit,
|
|
30
|
+
cacheBust: opts.cacheBust,
|
|
31
|
+
loaded: loadedChunkIds,
|
|
32
|
+
preload: opts.preload ?? 'none',
|
|
33
|
+
closureRoots: opts.closureRoots,
|
|
34
|
+
};
|
|
35
|
+
if (bootstrapOpts.preload !== 'none') {
|
|
36
|
+
await bootstrapComponentRegistry(distDir, dom.registerComponents, bootstrapOpts);
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Load component closure for root chunk ids (page + layouts + fixture).
|
|
40
|
+
* @param {string[]} rootChunkIds
|
|
41
|
+
*/
|
|
42
|
+
async function ensureComponents(rootChunkIds) {
|
|
43
|
+
if (!rootChunkIds?.length)
|
|
44
|
+
return {};
|
|
45
|
+
const entries = await loadComponentEntries(distDir, {
|
|
46
|
+
strict,
|
|
47
|
+
closureRoots: rootChunkIds,
|
|
48
|
+
explicit: opts.explicit,
|
|
49
|
+
});
|
|
50
|
+
return importAndRegisterComponentEntries(distDir, entries, dom.registerComponents, {
|
|
51
|
+
cacheBust: opts.cacheBust,
|
|
52
|
+
loaded: loadedChunkIds,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Union closure chunk ids (pages + layouts) without importing yet.
|
|
57
|
+
* @param {string[]} rootChunkIds
|
|
58
|
+
*/
|
|
59
|
+
function closureChunkIds(rootChunkIds) {
|
|
60
|
+
if (!deployment || !rootChunkIds?.length)
|
|
61
|
+
return new Set(rootChunkIds || []);
|
|
62
|
+
return collectDependsOnClosure(deployment, rootChunkIds);
|
|
63
|
+
}
|
|
64
|
+
return {
|
|
65
|
+
distDir,
|
|
66
|
+
deployment,
|
|
67
|
+
dom,
|
|
68
|
+
loadedChunkIds,
|
|
69
|
+
ensureComponents,
|
|
70
|
+
closureChunkIds,
|
|
71
|
+
renderToString: dom.renderToString.bind(dom),
|
|
72
|
+
renderToStream: dom.renderToStream.bind(dom),
|
|
73
|
+
mount: dom.mount.bind(dom),
|
|
74
|
+
hydrate: dom.hydrate?.bind(dom),
|
|
75
|
+
resume: dom.resume?.bind(dom),
|
|
76
|
+
destroy: dom.destroy?.bind(dom),
|
|
77
|
+
flushPending: dom.flushPending?.bind(dom),
|
|
78
|
+
/** @deprecated Prefer ensureComponents via createRenderHost; low-level escape hatch. */
|
|
79
|
+
registerComponents: dom.registerComponents.bind(dom),
|
|
80
|
+
};
|
|
81
|
+
}
|
package/dist/serve-host.mjs
CHANGED
|
@@ -22,7 +22,7 @@ import http from 'node:http';
|
|
|
22
22
|
import { createRequire, registerHooks } from 'node:module';
|
|
23
23
|
import path from 'node:path';
|
|
24
24
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
25
|
-
import {
|
|
25
|
+
import { createRenderHost } from './render-host.js';
|
|
26
26
|
import { listClientComponents } from './list-client-components.js';
|
|
27
27
|
import { handleNodeRequest, setRoutes, setServerModuleResolver } from './vmz-runtime.js';
|
|
28
28
|
const require = createRequire(import.meta.url);
|
|
@@ -76,6 +76,8 @@ if (isDev) {
|
|
|
76
76
|
}
|
|
77
77
|
/** @type {number} */
|
|
78
78
|
let reloadToken = Date.now();
|
|
79
|
+
/** @type {Awaited<ReturnType<typeof createRenderHost>> | null} */
|
|
80
|
+
let ssrRenderHost = null;
|
|
79
81
|
/** @type {string | null} Correlatable build id from vmz dev (Living §12.8). */
|
|
80
82
|
let lastDevBuildId = null;
|
|
81
83
|
/** @type {Array<{ chunkId: string, pageRel: string, segs: ReturnType<typeof parseChunkSegments> }>} */
|
|
@@ -503,9 +505,17 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
503
505
|
const pageDocMeta = resolvePageDocumentMeta(Page);
|
|
504
506
|
const prevLocaleHint = globalThis.__vmzLocaleIdHint;
|
|
505
507
|
globalThis.__vmzLocaleIdHint = localeId;
|
|
508
|
+
if (!ssrRenderHost) {
|
|
509
|
+
ssrRenderHost = await createRenderHost(distDir, {
|
|
510
|
+
strictDeployment: !isDev,
|
|
511
|
+
preload: 'none',
|
|
512
|
+
cacheBust: reloadToken,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
await ssrRenderHost.ensureComponents([chunkId, ...layoutChain]);
|
|
506
516
|
let bodyHtml = '';
|
|
507
517
|
try {
|
|
508
|
-
for await (const chunk of renderToStream(Page, props, { signal })) {
|
|
518
|
+
for await (const chunk of ssrRenderHost.renderToStream(Page, props, { signal })) {
|
|
509
519
|
if (signal?.aborted)
|
|
510
520
|
return;
|
|
511
521
|
bodyHtml += chunk;
|
|
@@ -517,7 +527,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
517
527
|
const Layout = await loadPageCtor(layoutChain[i]);
|
|
518
528
|
if (!Layout)
|
|
519
529
|
continue;
|
|
520
|
-
bodyHtml = await renderToString(Layout, {}, { signal, slotHtml: bodyHtml });
|
|
530
|
+
bodyHtml = await ssrRenderHost.renderToString(Layout, {}, { signal, slotHtml: bodyHtml });
|
|
521
531
|
if (signal?.aborted)
|
|
522
532
|
return;
|
|
523
533
|
}
|
|
@@ -709,28 +719,18 @@ async function softReload(opts = {}) {
|
|
|
709
719
|
catch {
|
|
710
720
|
localeArtifact = null;
|
|
711
721
|
}
|
|
712
|
-
const componentEntries = await listClientComponents(distDir);
|
|
722
|
+
const componentEntries = await listClientComponents(distDir, { strict: !isDev });
|
|
723
|
+
ssrRenderHost = await createRenderHost(distDir, {
|
|
724
|
+
strictDeployment: !isDev,
|
|
725
|
+
preload: 'none',
|
|
726
|
+
cacheBust: nextToken,
|
|
727
|
+
});
|
|
713
728
|
const nextCatalog = await listPageClientFiles(distDir);
|
|
714
729
|
if (!nextCatalog.length) {
|
|
715
730
|
throw new Error(`vmz serve: no pages/**/*.client.js in ${distDir}`);
|
|
716
731
|
}
|
|
717
|
-
/** @type {Record<string, any>} */
|
|
718
|
-
const components = {};
|
|
719
732
|
/** @type {Map<string, any>} */
|
|
720
733
|
const nextCtors = new Map();
|
|
721
|
-
const affectedNames = new Set(affected
|
|
722
|
-
.map((c) => String(c))
|
|
723
|
-
.filter((c) => c.startsWith('components/') || !c.includes('/'))
|
|
724
|
-
.map((c) => c.split('/').pop())
|
|
725
|
-
.filter(Boolean));
|
|
726
|
-
for (const entry of componentEntries) {
|
|
727
|
-
if (islandHmr && affectedNames.size > 0 && !affectedNames.has(entry.name)) {
|
|
728
|
-
continue;
|
|
729
|
-
}
|
|
730
|
-
const href = bustUrl(pathToFileURL(path.join(distDir, entry.entry)).href);
|
|
731
|
-
const mod = await import(href);
|
|
732
|
-
components[entry.name] = mod.default;
|
|
733
|
-
}
|
|
734
734
|
if (!islandHmr) {
|
|
735
735
|
const pagesToLoad = reloadAllPages ? nextCatalog : nextCatalog.filter((p) => pageNeedsReload(p.chunkId, affected));
|
|
736
736
|
for (const p of pagesToLoad) {
|
|
@@ -758,9 +758,6 @@ async function softReload(opts = {}) {
|
|
|
758
758
|
}
|
|
759
759
|
}
|
|
760
760
|
}
|
|
761
|
-
if (Object.keys(components).length) {
|
|
762
|
-
registerComponents(components);
|
|
763
|
-
}
|
|
764
761
|
const indexChunk = pageCatalog.find((p) => p.chunkId === 'pages/index')?.chunkId || pageCatalog[0].chunkId;
|
|
765
762
|
const resumeEntries = await loadPageResumeEntries(distDir, indexChunk);
|
|
766
763
|
const styleMeta = await loadDeploymentStyle(distDir);
|
package/dist/server.js
CHANGED
|
@@ -231,7 +231,9 @@ export async function handleNodeRequest(req, res, opts = {}) {
|
|
|
231
231
|
if (verb === 'GET' && opts.distDir) {
|
|
232
232
|
const nodePath = await import('node:path');
|
|
233
233
|
const { readFile, stat } = await import('node:fs/promises');
|
|
234
|
-
const file = await resolveDistStatic(opts.distDir, url.pathname, nodePath, stat
|
|
234
|
+
const file = await resolveDistStatic(opts.distDir, url.pathname, nodePath, stat, {
|
|
235
|
+
cookieHeader: String(req.headers.cookie || ''),
|
|
236
|
+
});
|
|
235
237
|
const hasSsr = typeof opts.renderPageStream === 'function' ||
|
|
236
238
|
typeof opts.renderPage === 'function' ||
|
|
237
239
|
typeof opts.renderIndexStream === 'function' ||
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.11",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "VMZ production runtime core — DOM / SSR / HTTP / WriteBarrier (no compiler)",
|
|
6
6
|
"exports": {
|
|
@@ -31,6 +31,10 @@
|
|
|
31
31
|
"./component-registry": {
|
|
32
32
|
"types": "./dist/list-client-components.d.ts",
|
|
33
33
|
"default": "./dist/list-client-components.js"
|
|
34
|
+
},
|
|
35
|
+
"./render-host": {
|
|
36
|
+
"types": "./dist/render-host.d.ts",
|
|
37
|
+
"default": "./dist/render-host.js"
|
|
34
38
|
}
|
|
35
39
|
},
|
|
36
40
|
"files": [
|