@vmz/core 0.1.11 → 0.1.12
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/list-client-components.js +2 -2
- package/dist/route-layout-chain.d.ts +18 -0
- package/dist/route-layout-chain.js +41 -0
- package/dist/serve-host.mjs +20 -28
- package/dist/server.js +8 -2
- package/package.json +5 -1
|
@@ -24,7 +24,7 @@ export async function listClientComponents(dir, opts = {}) {
|
|
|
24
24
|
name: e.name,
|
|
25
25
|
entry: e.entry,
|
|
26
26
|
source: e.source,
|
|
27
|
-
}))).map((e) => ({
|
|
27
|
+
})), { strict }).map((e) => ({
|
|
28
28
|
name: e.name,
|
|
29
29
|
entry: e.entry,
|
|
30
30
|
chunkId: e.chunkId,
|
|
@@ -65,7 +65,7 @@ export function listClientComponentsSync(dir, opts = {}) {
|
|
|
65
65
|
name: e.name,
|
|
66
66
|
entry: e.entry,
|
|
67
67
|
source: e.source,
|
|
68
|
-
}))).map((e) => ({
|
|
68
|
+
})), { strict }).map((e) => ({
|
|
69
69
|
name: e.name,
|
|
70
70
|
entry: e.entry,
|
|
71
71
|
chunkId: e.chunkId,
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-route layout chain: Application shell (outermost) + nested page Layout components.
|
|
3
|
+
*/
|
|
4
|
+
/** Chunk id for `src/Application.vmz` emit (`Application.client.js`). */
|
|
5
|
+
export declare const APPLICATION_SHELL_CHUNK = "Application";
|
|
6
|
+
/**
|
|
7
|
+
* True when the compile output includes a root Application shell.
|
|
8
|
+
*/
|
|
9
|
+
export declare function hasApplicationShell(distDir: string): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Nearest page Layout.client.js walking up from the page chunk (outer to inner).
|
|
12
|
+
* Does not include Application — use resolveRouteLayoutChain.
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveNestedLayoutChain(distDir: string, pageChunkId: string): string[];
|
|
15
|
+
/**
|
|
16
|
+
* Full SSR / hydrate layout chain: optional Application shell, then nested page layouts.
|
|
17
|
+
*/
|
|
18
|
+
export declare function resolveRouteLayoutChain(distDir: string, pageChunkId: string): string[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-route layout chain: Application shell (outermost) + nested page Layout components.
|
|
3
|
+
*/
|
|
4
|
+
import { existsSync } from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
/** Chunk id for `src/Application.vmz` emit (`Application.client.js`). */
|
|
7
|
+
export const APPLICATION_SHELL_CHUNK = 'Application';
|
|
8
|
+
/**
|
|
9
|
+
* True when the compile output includes a root Application shell.
|
|
10
|
+
*/
|
|
11
|
+
export function hasApplicationShell(distDir) {
|
|
12
|
+
return existsSync(path.join(distDir, `${APPLICATION_SHELL_CHUNK}.client.js`));
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Nearest page Layout.client.js walking up from the page chunk (outer to inner).
|
|
16
|
+
* Does not include Application — use resolveRouteLayoutChain.
|
|
17
|
+
*/
|
|
18
|
+
export function resolveNestedLayoutChain(distDir, pageChunkId) {
|
|
19
|
+
const rel = pageChunkId.replace(/^pages\//, '');
|
|
20
|
+
const parts = rel.split('/').filter(Boolean);
|
|
21
|
+
parts.pop();
|
|
22
|
+
const chain = [];
|
|
23
|
+
for (let i = parts.length; i >= 0; i--) {
|
|
24
|
+
const dirParts = parts.slice(0, i);
|
|
25
|
+
const layoutChunk = ['pages', ...dirParts, 'Layout'].join('/');
|
|
26
|
+
if (existsSync(path.join(distDir, `${layoutChunk}.client.js`))) {
|
|
27
|
+
chain.unshift(layoutChunk);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return chain;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Full SSR / hydrate layout chain: optional Application shell, then nested page layouts.
|
|
34
|
+
*/
|
|
35
|
+
export function resolveRouteLayoutChain(distDir, pageChunkId) {
|
|
36
|
+
const chain = resolveNestedLayoutChain(distDir, pageChunkId);
|
|
37
|
+
if (hasApplicationShell(distDir)) {
|
|
38
|
+
chain.unshift(APPLICATION_SHELL_CHUNK);
|
|
39
|
+
}
|
|
40
|
+
return chain;
|
|
41
|
+
}
|
package/dist/serve-host.mjs
CHANGED
|
@@ -24,6 +24,7 @@ import path from 'node:path';
|
|
|
24
24
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
25
25
|
import { createRenderHost } from './render-host.js';
|
|
26
26
|
import { listClientComponents } from './list-client-components.js';
|
|
27
|
+
import { resolveRouteLayoutChain } from './route-layout-chain.js';
|
|
27
28
|
import { handleNodeRequest, setRoutes, setServerModuleResolver } from './vmz-runtime.js';
|
|
28
29
|
const require = createRequire(import.meta.url);
|
|
29
30
|
const distDir = process.env.VMZ_DIST ? path.resolve(process.env.VMZ_DIST) : path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -86,6 +87,8 @@ let pageCatalog = [];
|
|
|
86
87
|
const pageCtors = new Map();
|
|
87
88
|
/** Stylesheet from deployment `cssEntry` (e.g. vmz.css). */
|
|
88
89
|
let cssEntry = null;
|
|
90
|
+
/** Fingerprint of style inputs — busts `@import` siblings when tokens change (VMZ-8). */
|
|
91
|
+
let styleBundleHash = null;
|
|
89
92
|
/** @type {{ defaultThemeId: string, themeIds: string[], activationAttr: string, contentHash: string|null } | null} */
|
|
90
93
|
let styleTheme = null;
|
|
91
94
|
/** Locale route realization artifact from `_vmz/locale-route-realization.json` (optional). */
|
|
@@ -279,7 +282,7 @@ async function renderPageStream(pathname, opts = {}) {
|
|
|
279
282
|
const resumeEntries = await loadPageResumeEntries(distDir, match.chunkId);
|
|
280
283
|
const strategies = resumeEntries.map((e) => e.strategy);
|
|
281
284
|
const eventOnlyShell = isEventOnlyShell(strategies);
|
|
282
|
-
const layoutChain =
|
|
285
|
+
const layoutChain = resolveRouteLayoutChain(distDir, match.chunkId);
|
|
283
286
|
return {
|
|
284
287
|
status,
|
|
285
288
|
stream: emitPageHtml(Page, match.chunkId, eventOnlyShell, props, opts, layoutChain, localeCtx),
|
|
@@ -549,7 +552,7 @@ async function* emitPageHtml(Page, chunkId, eventOnlyShell, props = {}, opts = {
|
|
|
549
552
|
throw new Error('vmz native addon missing generatePageShell — rebuild with `pnpm napi:build`');
|
|
550
553
|
}
|
|
551
554
|
const entrySrc = `/${eventOnlyShell ? 'entry-event.js' : 'entry-client.js'}?t=${reloadToken}`;
|
|
552
|
-
const cssHref =
|
|
555
|
+
const cssHref = cssEntryWithBust(cssEntry);
|
|
553
556
|
yield native.generatePageShell({
|
|
554
557
|
bodyHtml,
|
|
555
558
|
chunkId,
|
|
@@ -762,6 +765,7 @@ async function softReload(opts = {}) {
|
|
|
762
765
|
const resumeEntries = await loadPageResumeEntries(distDir, indexChunk);
|
|
763
766
|
const styleMeta = await loadDeploymentStyle(distDir);
|
|
764
767
|
cssEntry = styleMeta.cssEntry;
|
|
768
|
+
styleBundleHash = styleMeta.styleBundleHash;
|
|
765
769
|
styleTheme = styleMeta.styleTheme;
|
|
766
770
|
const lazyEventNames = resumeEntries
|
|
767
771
|
.filter((e) => isEventStrategy(e.strategy))
|
|
@@ -1202,32 +1206,6 @@ function isRouteGroupDir(seg) {
|
|
|
1202
1206
|
function isRouteBoundaryStem(stem) {
|
|
1203
1207
|
return stem === 'Layout' || stem === 'Loading' || stem === 'Error' || stem === 'NotFound';
|
|
1204
1208
|
}
|
|
1205
|
-
/**
|
|
1206
|
-
* Nearest `Layout.client.js` walking up from the page chunk (outer→inner).
|
|
1207
|
-
* @param {string} pageChunkId
|
|
1208
|
-
* @returns {string[]}
|
|
1209
|
-
*/
|
|
1210
|
-
function resolveLayoutChain(pageChunkId) {
|
|
1211
|
-
const rel = pageChunkId.replace(/^pages\//, '');
|
|
1212
|
-
const parts = rel.split('/').filter(Boolean);
|
|
1213
|
-
parts.pop(); // page stem
|
|
1214
|
-
/** @type {string[]} */
|
|
1215
|
-
const chain = [];
|
|
1216
|
-
for (let i = parts.length; i >= 0; i--) {
|
|
1217
|
-
const dirParts = parts.slice(0, i);
|
|
1218
|
-
const layoutChunk = ['pages', ...dirParts, 'Layout'].join('/');
|
|
1219
|
-
const abs = path.join(distDir, `${layoutChunk}.client.js`);
|
|
1220
|
-
try {
|
|
1221
|
-
// sync existence — layouts are compile artifacts next to pages
|
|
1222
|
-
if (existsSync(abs))
|
|
1223
|
-
chain.unshift(layoutChunk);
|
|
1224
|
-
}
|
|
1225
|
-
catch {
|
|
1226
|
-
/* ignore */
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
return chain;
|
|
1230
|
-
}
|
|
1231
1209
|
/**
|
|
1232
1210
|
* @param {string} pathname
|
|
1233
1211
|
* @param {typeof pageCatalog} catalog
|
|
@@ -1439,6 +1417,20 @@ function requireNativeGenerator() {
|
|
|
1439
1417
|
const THEME_STORE_KEY = 'vmz-theme';
|
|
1440
1418
|
/** Host preference key for `routing.strategy: 'none'` (cookie + localStorage). */
|
|
1441
1419
|
const LOCALE_STORE_KEY = 'vmz.locale';
|
|
1420
|
+
/**
|
|
1421
|
+
* Cache-bust stylesheet entry for dev reload (token + serve revision).
|
|
1422
|
+
* @param {string | null | undefined} entry
|
|
1423
|
+
*/
|
|
1424
|
+
function cssEntryWithBust(entry) {
|
|
1425
|
+
if (!entry)
|
|
1426
|
+
return undefined;
|
|
1427
|
+
const base = String(entry).replace(/^\/+/, '');
|
|
1428
|
+
const params = new URLSearchParams();
|
|
1429
|
+
params.set('t', String(reloadToken));
|
|
1430
|
+
if (styleBundleHash)
|
|
1431
|
+
params.set('h', styleBundleHash);
|
|
1432
|
+
return `${base}?${params.toString()}`;
|
|
1433
|
+
}
|
|
1442
1434
|
/**
|
|
1443
1435
|
* @param {string} dir
|
|
1444
1436
|
* @returns {Promise<{ cssEntry: string|null, styleTheme: typeof styleTheme, styleBundleHash: string|null }>}
|
package/dist/server.js
CHANGED
|
@@ -751,9 +751,15 @@ async function sendHtmlStream(res, status, source, signal) {
|
|
|
751
751
|
* @param {string} type
|
|
752
752
|
*/
|
|
753
753
|
function sendBytes(res, status, body, type) {
|
|
754
|
-
|
|
754
|
+
/** @type {Record<string, string | number>} */
|
|
755
|
+
const headers = {
|
|
755
756
|
'content-type': type,
|
|
756
757
|
'content-length': body.byteLength,
|
|
757
|
-
}
|
|
758
|
+
};
|
|
759
|
+
// Dev: stylesheets are rebuilt in-place — never cache @import siblings (VMZ-8).
|
|
760
|
+
if (process.env.VMZ_DEV === '1' && typeof type === 'string' && type.startsWith('text/css')) {
|
|
761
|
+
headers['cache-control'] = 'no-store';
|
|
762
|
+
}
|
|
763
|
+
res.writeHead(status, headers);
|
|
758
764
|
res.end(body);
|
|
759
765
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "VMZ production runtime core — DOM / SSR / HTTP / WriteBarrier (no compiler)",
|
|
6
6
|
"exports": {
|
|
@@ -35,6 +35,10 @@
|
|
|
35
35
|
"./render-host": {
|
|
36
36
|
"types": "./dist/render-host.d.ts",
|
|
37
37
|
"default": "./dist/render-host.js"
|
|
38
|
+
},
|
|
39
|
+
"./route-layout-chain": {
|
|
40
|
+
"types": "./dist/route-layout-chain.d.ts",
|
|
41
|
+
"default": "./dist/route-layout-chain.js"
|
|
38
42
|
}
|
|
39
43
|
},
|
|
40
44
|
"files": [
|