@vmz/vmz 0.1.12 → 0.1.13
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/build-assemble.js +3 -4
- package/dist/cdn-policy.js +4 -5
- package/dist/cli.js +15 -50
- package/dist/content-addressed-assets.d.ts +2 -8
- package/dist/content-addressed-assets.js +34 -111
- package/dist/delivery-profile.d.ts +1 -11
- package/dist/delivery-profile.js +1 -70
- package/dist/dev-session.d.ts +0 -1
- package/dist/dev-session.js +65 -268
- package/dist/document-build.d.ts +1 -2
- package/dist/document-build.js +82 -104
- package/dist/document-cmd.js +9 -2
- package/dist/document-enrich.d.ts +1 -2
- package/dist/document-enrich.js +4 -15
- package/dist/document-integrate.js +21 -29
- package/dist/embedded-packaging.js +1 -2
- package/dist/index.d.ts +1 -15
- package/dist/index.js +3 -59
- package/dist/locale-check.js +61 -28
- package/dist/locale-cmd.js +42 -10
- package/dist/locale-route-emit.d.ts +1 -4
- package/dist/locale-route-emit.js +32 -7
- package/dist/locale-router.js +8 -26
- package/dist/pack.js +2 -3
- package/dist/production-observability.js +2 -3
- package/dist/production-test-pack.js +3 -4
- package/dist/release-pack.js +18 -5
- package/dist/server-artifact.js +3 -4
- package/dist/site-delivery.js +3 -4
- package/dist/static-emit.js +97 -118
- package/dist/test-cmd.js +1 -2
- package/package.json +12 -12
- package/dist/dev-watch-roots.d.ts +0 -80
- package/dist/dev-watch-roots.js +0 -245
- package/dist/document-layout-render.d.ts +0 -20
- package/dist/document-layout-render.js +0 -87
- package/dist/document-routing-config.d.ts +0 -13
- package/dist/document-routing-config.js +0 -40
- package/dist/native-addon.d.ts +0 -9
- package/dist/native-addon.js +0 -84
- package/dist/pretty-json.d.ts +0 -19
- package/dist/pretty-json.js +0 -43
- package/dist/route-path.d.ts +0 -35
- package/dist/route-path.js +0 -77
- package/dist/wechat-packaging.d.ts +0 -22
- package/dist/wechat-packaging.js +0 -59
package/dist/server-artifact.js
CHANGED
|
@@ -4,10 +4,9 @@
|
|
|
4
4
|
*/
|
|
5
5
|
// @ts-nocheck
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
|
-
import { existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { SERVER_RUNTIMES } from './delivery-profile.js';
|
|
10
|
-
import { writePrettyJsonFile } from './pretty-json.js';
|
|
11
10
|
export const SERVER_ARTIFACT_SCHEMA = 'vmz.server.artifact.v0';
|
|
12
11
|
export const HTTP_CONTRACT_SCHEMA = 'vmz.http.contract.v0';
|
|
13
12
|
export const SERVER_RUNTIME_ADAPTER_SCHEMA = 'vmz.server.runtime_adapter.v0';
|
|
@@ -117,14 +116,14 @@ export function emitServerArtifact(outDir, opts = {}) {
|
|
|
117
116
|
const vmzDir = path.join(outDir, '_vmz');
|
|
118
117
|
mkdirSync(vmzDir, { recursive: true });
|
|
119
118
|
const file = path.join(vmzDir, 'server-artifact.json');
|
|
120
|
-
|
|
119
|
+
writeFileSync(file, `${JSON.stringify(artifact, null, 2)}\n`, 'utf8');
|
|
121
120
|
const adapterDir = path.join(vmzDir, 'adapters');
|
|
122
121
|
mkdirSync(adapterDir, { recursive: true });
|
|
123
122
|
for (const adapterId of ['worker', 'rust-host']) {
|
|
124
123
|
const projection = projectServerRuntimeAdapter(artifact, adapterId);
|
|
125
124
|
const dir = path.join(adapterDir, adapterId);
|
|
126
125
|
mkdirSync(dir, { recursive: true });
|
|
127
|
-
|
|
126
|
+
writeFileSync(path.join(dir, 'adapter.json'), `${JSON.stringify(projection, null, 2)}\n`, 'utf8');
|
|
128
127
|
}
|
|
129
128
|
return { artifact, path: file, httpContractDigest };
|
|
130
129
|
}
|
package/dist/site-delivery.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import crypto from 'node:crypto';
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
|
-
import { writePrettyJsonFile } from './pretty-json.js';
|
|
10
9
|
export const SITE_DELIVERY_CONTRACT_SCHEMA = 'vmz.site.delivery_contract.v0';
|
|
11
10
|
export const SITE_DELIVERY_RESOLUTION_SCHEMA = 'vmz.site.delivery_resolution.v0';
|
|
12
11
|
/**
|
|
@@ -136,7 +135,7 @@ export function normalizeSiteDelivery(raw, opts = {}) {
|
|
|
136
135
|
artifact: String(d.artifact),
|
|
137
136
|
expectedCompatibility: d.expectedCompatibility || {
|
|
138
137
|
runtime: 'vmz',
|
|
139
|
-
deliveryProfiles: ['static', 'filesystem'],
|
|
138
|
+
deliveryProfiles: ['web-static', 'filesystem'],
|
|
140
139
|
},
|
|
141
140
|
sources,
|
|
142
141
|
resolutionPolicy: {
|
|
@@ -319,11 +318,11 @@ export function emitSiteDelivery(outDir, deliveryRaw, opts = {}) {
|
|
|
319
318
|
}
|
|
320
319
|
const vmzDir = path.join(outDir, '_vmz');
|
|
321
320
|
fs.mkdirSync(vmzDir, { recursive: true });
|
|
322
|
-
|
|
321
|
+
fs.writeFileSync(path.join(vmzDir, 'site-delivery-contract.json'), `${JSON.stringify(norm.contract, null, 2)}\n`, 'utf8');
|
|
323
322
|
let resolution = null;
|
|
324
323
|
if (opts.probes) {
|
|
325
324
|
resolution = resolveSiteRelease(norm.contract, opts.probes);
|
|
326
|
-
|
|
325
|
+
fs.writeFileSync(path.join(vmzDir, 'site-delivery-resolution.json'), `${JSON.stringify(resolution, null, 2)}\n`, 'utf8');
|
|
327
326
|
}
|
|
328
327
|
return { contract: norm.contract, resolution };
|
|
329
328
|
}
|
package/dist/static-emit.js
CHANGED
|
@@ -7,15 +7,9 @@ import crypto from 'node:crypto';
|
|
|
7
7
|
import fs from 'node:fs';
|
|
8
8
|
import path from 'node:path';
|
|
9
9
|
import { pathToFileURL } from 'node:url';
|
|
10
|
-
import { createRenderHost } from '@vmz/core/render-host';
|
|
11
|
-
import { resolveRouteLayoutChain } from '@vmz/core/route-layout-chain';
|
|
12
|
-
import { listClientComponentsSync } from '@vmz/core/component-registry';
|
|
13
10
|
import { emitCdnPolicy } from './cdn-policy.js';
|
|
14
11
|
import { emitContentAddressedAssets } from './content-addressed-assets.js';
|
|
15
12
|
import { absoluteUrl, buildLocalePageMeta, localizeBodyLinks } from './locale-router.js';
|
|
16
|
-
import { requireNativeAddon } from './native-addon.js';
|
|
17
|
-
import { writePrettyJsonFile } from './pretty-json.js';
|
|
18
|
-
import { filePathPatternFromChunk, isRouteBoundaryStem, listPublicPageUnits, parsePathPattern, unitBrowserPathPattern } from './route-path.js';
|
|
19
13
|
export const STATIC_DELIVERY_MANIFEST_SCHEMA = 'vmz.static.delivery_manifest.v0';
|
|
20
14
|
/**
|
|
21
15
|
* @param {string} distDir
|
|
@@ -32,8 +26,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
32
26
|
if (!fs.existsSync(domPath)) {
|
|
33
27
|
throw new Error(`emitWebStatic: missing ${domPath} — run vmz build first`);
|
|
34
28
|
}
|
|
35
|
-
const
|
|
36
|
-
const { renderToString, renderToStream } = host;
|
|
29
|
+
const { renderToString, renderToStream } = await import(pathToFileURL(domPath).href);
|
|
37
30
|
const pageCatalog = listPageClientFiles(distDir);
|
|
38
31
|
/** @type {Array<{
|
|
39
32
|
* routeId: string,
|
|
@@ -50,7 +43,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
50
43
|
/** @type {Array<{ routeId: string, path: string, chunkId: string, classification: string, reason: string }>} */
|
|
51
44
|
const skipped = [];
|
|
52
45
|
for (const page of pageCatalog) {
|
|
53
|
-
const pattern =
|
|
46
|
+
const pattern = patternFromSegs(page.segs);
|
|
54
47
|
const routeId = guessRouteId(distDir, page.chunkId);
|
|
55
48
|
if (page.segs.some((s) => s.kind === 'param' || s.kind === 'catch')) {
|
|
56
49
|
skipped.push({
|
|
@@ -101,8 +94,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
101
94
|
}
|
|
102
95
|
}
|
|
103
96
|
const meta = await resolvePageMeta(Page, { params, props, pathname: pattern, origin });
|
|
104
|
-
const layoutChain =
|
|
105
|
-
await host.ensureComponents([page.chunkId, ...layoutChain]);
|
|
97
|
+
const layoutChain = resolveLayoutChain(distDir, page.chunkId);
|
|
106
98
|
let bodyHtml = '';
|
|
107
99
|
for await (const chunk of renderToStream(Page, props, {})) {
|
|
108
100
|
bodyHtml += chunk;
|
|
@@ -170,11 +162,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
170
162
|
fs.writeFileSync(path.join(distDir, '404.html'), notFoundHtml, 'utf8');
|
|
171
163
|
const sitemap = buildSitemap(origin, generations);
|
|
172
164
|
fs.writeFileSync(path.join(distDir, 'sitemap.xml'), sitemap, 'utf8');
|
|
173
|
-
const
|
|
174
|
-
if (typeof nativeRobots.generateRobotsTxt !== 'function') {
|
|
175
|
-
throw new Error('vmz native addon missing generateRobotsTxt — rebuild with `pnpm napi:build`');
|
|
176
|
-
}
|
|
177
|
-
const robots = nativeRobots.generateRobotsTxt(`${origin}/sitemap.xml`);
|
|
165
|
+
const robots = `User-agent: *\nAllow: /\nDisallow: /404\nSitemap: ${origin}/sitemap.xml\n`;
|
|
178
166
|
fs.writeFileSync(path.join(distDir, 'robots.txt'), robots, 'utf8');
|
|
179
167
|
// Hard rule: no SPA fallback shim in artifact.
|
|
180
168
|
for (const bad of ['_redirects', 'vercel.json', 'netlify.toml']) {
|
|
@@ -191,7 +179,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
191
179
|
const manifest = {
|
|
192
180
|
schema: STATIC_DELIVERY_MANIFEST_SCHEMA,
|
|
193
181
|
applicationId,
|
|
194
|
-
deliveryProfile: 'static',
|
|
182
|
+
deliveryProfile: 'web-static',
|
|
195
183
|
origin,
|
|
196
184
|
generatedAt: new Date().toISOString(),
|
|
197
185
|
spaFallback: false,
|
|
@@ -219,8 +207,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
219
207
|
};
|
|
220
208
|
const digest = sha256Hex(canonicalJson(manifest));
|
|
221
209
|
manifest.manifestDigest = digest;
|
|
222
|
-
|
|
223
|
-
emitStaticClientEntries(distDir, pageCatalog);
|
|
210
|
+
fs.writeFileSync(path.join(vmzDir, 'static-delivery-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
224
211
|
const assets = emitContentAddressedAssets(distDir);
|
|
225
212
|
manifest.contentAddressedAssets = {
|
|
226
213
|
schema: assets.manifest.schema,
|
|
@@ -231,7 +218,7 @@ export async function emitWebStatic(distDir, opts = {}) {
|
|
|
231
218
|
// Re-stamp static manifest after linking asset digest (HTML already rewritten on disk).
|
|
232
219
|
delete manifest.manifestDigest;
|
|
233
220
|
manifest.manifestDigest = sha256Hex(canonicalJson(manifest));
|
|
234
|
-
|
|
221
|
+
fs.writeFileSync(path.join(vmzDir, 'static-delivery-manifest.json'), `${JSON.stringify(manifest, null, 2)}\n`, 'utf8');
|
|
235
222
|
const cdn = emitCdnPolicy(distDir, manifest);
|
|
236
223
|
return {
|
|
237
224
|
manifest,
|
|
@@ -271,11 +258,8 @@ function sortKeys(value) {
|
|
|
271
258
|
* @param {string} distDir
|
|
272
259
|
*/
|
|
273
260
|
function listPageClientFiles(distDir) {
|
|
274
|
-
const fromDep = listPagesFromDeployment(distDir);
|
|
275
|
-
if (fromDep.length)
|
|
276
|
-
return fromDep;
|
|
277
261
|
const root = path.join(distDir, 'pages');
|
|
278
|
-
/** @type {Array<{ chunkId: string, segs: ReturnType<typeof
|
|
262
|
+
/** @type {Array<{ chunkId: string, segs: ReturnType<typeof parseChunkSegments> }>} */
|
|
279
263
|
const out = [];
|
|
280
264
|
function walk(abs, relParts) {
|
|
281
265
|
let ents;
|
|
@@ -290,11 +274,10 @@ function listPageClientFiles(distDir) {
|
|
|
290
274
|
walk(path.join(abs, e.name), [...relParts, e.name]);
|
|
291
275
|
else if (e.isFile() && e.name.endsWith('.client.js')) {
|
|
292
276
|
const stem = e.name.replace(/\.client\.js$/, '');
|
|
293
|
-
if (
|
|
277
|
+
if (stem === 'Layout' || stem === 'Loading' || stem === 'Error' || stem === 'NotFound')
|
|
294
278
|
continue;
|
|
295
279
|
const chunkId = ['pages', ...relParts, stem].join('/');
|
|
296
|
-
|
|
297
|
-
out.push({ chunkId, pathPattern, segs: parsePathPattern(pathPattern) });
|
|
280
|
+
out.push({ chunkId, segs: parseChunkSegments(chunkId) });
|
|
298
281
|
}
|
|
299
282
|
}
|
|
300
283
|
}
|
|
@@ -302,26 +285,32 @@ function listPageClientFiles(distDir) {
|
|
|
302
285
|
return out;
|
|
303
286
|
}
|
|
304
287
|
/**
|
|
305
|
-
* @param {string}
|
|
288
|
+
* @param {string} chunkId
|
|
306
289
|
*/
|
|
307
|
-
function
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
290
|
+
function parseChunkSegments(chunkId) {
|
|
291
|
+
const rel = chunkId.replace(/^pages\//, '');
|
|
292
|
+
const parts = rel.split('/').filter(Boolean);
|
|
293
|
+
/** @type {Array<{ kind: 'static' | 'param' | 'catch', value?: string, name?: string }>} */
|
|
294
|
+
const segs = [];
|
|
295
|
+
for (let i = 0; i < parts.length; i++) {
|
|
296
|
+
const p = parts[i];
|
|
297
|
+
if (p.startsWith('(') && p.endsWith(')') && p.length > 2)
|
|
298
|
+
continue;
|
|
299
|
+
if (p === 'index' && i === parts.length - 1)
|
|
300
|
+
continue;
|
|
301
|
+
const catchAll = /^\[\.\.\.([^\]]+)\]$/.exec(p);
|
|
302
|
+
const param = /^\[([^\]]+)\]$/.exec(p);
|
|
303
|
+
if (catchAll)
|
|
304
|
+
segs.push({ kind: 'catch', name: catchAll[1] });
|
|
305
|
+
else if (param)
|
|
306
|
+
segs.push({ kind: 'param', name: param[1] });
|
|
307
|
+
else
|
|
308
|
+
segs.push({ kind: 'static', value: p.toLowerCase() });
|
|
321
309
|
}
|
|
310
|
+
return segs;
|
|
322
311
|
}
|
|
323
312
|
/**
|
|
324
|
-
* @param {ReturnType<typeof
|
|
313
|
+
* @param {ReturnType<typeof parseChunkSegments>} segs
|
|
325
314
|
*/
|
|
326
315
|
function patternFromSegs(segs) {
|
|
327
316
|
if (!segs.length)
|
|
@@ -354,6 +343,24 @@ async function loadCtor(distDir, chunkId) {
|
|
|
354
343
|
const mod = await import(`${href}?t=${Date.now()}`);
|
|
355
344
|
return mod.default;
|
|
356
345
|
}
|
|
346
|
+
/**
|
|
347
|
+
* @param {string} distDir
|
|
348
|
+
* @param {string} pageChunkId
|
|
349
|
+
*/
|
|
350
|
+
function resolveLayoutChain(distDir, pageChunkId) {
|
|
351
|
+
const rel = pageChunkId.replace(/^pages\//, '');
|
|
352
|
+
const parts = rel.split('/').filter(Boolean);
|
|
353
|
+
parts.pop();
|
|
354
|
+
/** @type {string[]} */
|
|
355
|
+
const chain = [];
|
|
356
|
+
for (let i = parts.length; i >= 0; i--) {
|
|
357
|
+
const dirParts = parts.slice(0, i);
|
|
358
|
+
const layoutChunk = ['pages', ...dirParts, 'Layout'].join('/');
|
|
359
|
+
if (fs.existsSync(path.join(distDir, `${layoutChunk}.client.js`)))
|
|
360
|
+
chain.unshift(layoutChunk);
|
|
361
|
+
}
|
|
362
|
+
return chain;
|
|
363
|
+
}
|
|
357
364
|
/**
|
|
358
365
|
* @param {string} distDir
|
|
359
366
|
* @param {string} chunkId
|
|
@@ -382,8 +389,8 @@ async function resolvePageMeta(Page, ctx) {
|
|
|
382
389
|
else if (Page.meta && typeof Page.meta === 'object') {
|
|
383
390
|
raw = Page.meta;
|
|
384
391
|
}
|
|
385
|
-
const title = String(raw.title || guessTitle(ctx.pathname)
|
|
386
|
-
const description = String(raw.description ||
|
|
392
|
+
const title = String(raw.title || `${guessTitle(ctx.pathname)} · VMZ`);
|
|
393
|
+
const description = String(raw.description || `VMZ page ${ctx.pathname}`);
|
|
387
394
|
const canonical = String(raw.canonical || `${ctx.origin}${ctx.pathname === '/' ? '/' : ctx.pathname}`);
|
|
388
395
|
const robots = String(raw.robots || 'index,follow');
|
|
389
396
|
const lang = String(raw.lang || 'en');
|
|
@@ -507,87 +514,59 @@ function readCssEntry(distDir) {
|
|
|
507
514
|
*/
|
|
508
515
|
function wrapDocument(input) {
|
|
509
516
|
const propsJson = JSON.stringify(input.props ?? {});
|
|
517
|
+
const layoutAttr = input.layoutChain.length ? ` data-vmz-layout="${escapeAttr(input.layoutChain.join(','))}"` : '';
|
|
518
|
+
const pageAttr = input.chunkId ? ` data-vmz-page="${escapeAttr(input.chunkId)}"` : '';
|
|
510
519
|
const localeId = input.meta.lang || 'en';
|
|
511
520
|
const dir = input.meta.dir || 'ltr';
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
521
|
+
const localeAttr = ` data-vmz-locale="${escapeAttr(localeId)}" data-vmz-dir="${escapeAttr(dir)}"`;
|
|
522
|
+
const cssLink = input.cssEntry ? ` <link rel="stylesheet" href="/${String(input.cssEntry).replace(/^\/+/, '')}" />\n` : '';
|
|
523
|
+
const entry = input.isErrorDocument ? '' : ` <script type="module" src="/entry-client.js"></script>\n`;
|
|
524
|
+
const hreflang = (input.meta.alternates || [])
|
|
525
|
+
.map((a) => ` <link rel="alternate" hreflang="${escapeAttr(a.hreflang)}" href="${escapeAttr(a.href)}" />`)
|
|
526
|
+
.join('\n');
|
|
527
|
+
const hreflangBlock = hreflang ? `${hreflang}\n` : '';
|
|
528
|
+
return `<!DOCTYPE html>
|
|
529
|
+
<html lang="${escapeAttr(localeId)}" data-locale="${escapeAttr(localeId)}" dir="${escapeAttr(dir)}">
|
|
530
|
+
<head>
|
|
531
|
+
<meta charset="utf-8" />
|
|
532
|
+
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
533
|
+
<title>${escapeHtml(input.meta.title)}</title>
|
|
534
|
+
<meta name="description" content="${escapeAttr(input.meta.description)}" />
|
|
535
|
+
<meta name="robots" content="${escapeAttr(input.meta.robots)}" />
|
|
536
|
+
<link rel="canonical" href="${escapeAttr(input.meta.canonical)}" />
|
|
537
|
+
${hreflangBlock} <meta property="og:title" content="${escapeAttr(input.meta.title)}" />
|
|
538
|
+
<meta property="og:description" content="${escapeAttr(input.meta.description)}" />
|
|
539
|
+
<meta property="og:url" content="${escapeAttr(input.meta.canonical)}" />
|
|
540
|
+
${cssLink}</head>
|
|
541
|
+
<body>
|
|
542
|
+
<div id="app"${pageAttr}${layoutAttr}${localeAttr} data-vmz-props="${escapeAttr(propsJson)}">${input.bodyHtml}</div>
|
|
543
|
+
${entry}</body>
|
|
544
|
+
</html>
|
|
545
|
+
`;
|
|
534
546
|
}
|
|
535
547
|
/**
|
|
536
548
|
* @param {string} origin
|
|
537
549
|
* @param {Array<{ canonical: string, robots: string }>} generations
|
|
538
550
|
*/
|
|
539
|
-
function buildSitemap(
|
|
540
|
-
const urls = generations
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
551
|
+
function buildSitemap(origin, generations) {
|
|
552
|
+
const urls = generations
|
|
553
|
+
.filter((g) => !String(g.robots).includes('noindex'))
|
|
554
|
+
.map((g) => ` <url>
|
|
555
|
+
<loc>${escapeXml(g.canonical)}</loc>
|
|
556
|
+
</url>`)
|
|
557
|
+
.join('\n');
|
|
558
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
559
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
560
|
+
${urls}
|
|
561
|
+
</urlset>
|
|
562
|
+
`;
|
|
546
563
|
}
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
* @param {string} distDir
|
|
550
|
-
* @param {Array<{ chunkId: string }>} pageCatalog
|
|
551
|
-
*/
|
|
552
|
-
function emitStaticClientEntries(distDir, pageCatalog) {
|
|
553
|
-
const componentEntries = listClientComponentsSync(distDir, { strict: true });
|
|
554
|
-
const indexChunk = pageCatalog.find((p) => p.chunkId === 'pages/index')?.chunkId || pageCatalog[0]?.chunkId || 'pages/index';
|
|
555
|
-
const resumeEntries = loadPageResumeEntriesSync(distDir, indexChunk);
|
|
556
|
-
const lazySet = new Set(resumeEntries
|
|
557
|
-
.filter((e) => isEventResumeStrategy(e.strategy))
|
|
558
|
-
.map((e) => e.component)
|
|
559
|
-
.filter(Boolean));
|
|
560
|
-
const eager = componentEntries.filter((e) => !lazySet.has(e.name));
|
|
561
|
-
const lazy = componentEntries.filter((e) => lazySet.has(e.name));
|
|
562
|
-
const native = requireNativeAddon();
|
|
563
|
-
if (typeof native.generateServeEntryClient !== 'function') {
|
|
564
|
-
throw new Error('vmz native addon missing generateServeEntryClient — rebuild with `pnpm napi:build`');
|
|
565
|
-
}
|
|
566
|
-
fs.writeFileSync(path.join(distDir, 'entry-client.js'), native.generateServeEntryClient(eager, lazy, ''), 'utf8');
|
|
567
|
-
if (typeof native.generateServeEntryEvent === 'function') {
|
|
568
|
-
fs.writeFileSync(path.join(distDir, 'entry-event.js'), native.generateServeEntryEvent(''), 'utf8');
|
|
569
|
-
}
|
|
564
|
+
function escapeHtml(s) {
|
|
565
|
+
return String(s).replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
|
|
570
566
|
}
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
* @param {string} chunkId
|
|
574
|
-
*/
|
|
575
|
-
function loadPageResumeEntriesSync(distDir, chunkId) {
|
|
576
|
-
try {
|
|
577
|
-
const dep = JSON.parse(fs.readFileSync(path.join(distDir, 'vmz-deployment.json'), 'utf8'));
|
|
578
|
-
const units = Array.isArray(dep.units) ? dep.units : [];
|
|
579
|
-
const page = units.find((u) => u.chunkId === chunkId) || units.find((u) => u.chunkId === 'pages/index') || units.find((u) => u.kind === 'page');
|
|
580
|
-
const entries = Array.isArray(page?.resumeEntries) ? page.resumeEntries : [];
|
|
581
|
-
return entries.map((e) => ({
|
|
582
|
-
component: String(e.component || ''),
|
|
583
|
-
strategy: String(e.strategy || ''),
|
|
584
|
-
}));
|
|
585
|
-
}
|
|
586
|
-
catch {
|
|
587
|
-
return [];
|
|
588
|
-
}
|
|
567
|
+
function escapeAttr(s) {
|
|
568
|
+
return escapeHtml(s).replaceAll('"', '"');
|
|
589
569
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
return strategy === 'event' || strategy === 'click' || String(strategy).startsWith('event:');
|
|
570
|
+
function escapeXml(s) {
|
|
571
|
+
return escapeAttr(s).replaceAll("'", ''');
|
|
593
572
|
}
|
package/dist/test-cmd.js
CHANGED
|
@@ -6,7 +6,6 @@
|
|
|
6
6
|
import path from 'node:path';
|
|
7
7
|
import { createWorkspace } from './index.js';
|
|
8
8
|
import { log } from './log.js';
|
|
9
|
-
import { generatePrettyJson } from './pretty-json.js';
|
|
10
9
|
/**
|
|
11
10
|
* @returns {Promise<typeof import('@vmz/test')>}
|
|
12
11
|
*/
|
|
@@ -318,7 +317,7 @@ export async function cmdTest(args) {
|
|
|
318
317
|
return errors.length ? 2 : 0;
|
|
319
318
|
}
|
|
320
319
|
if (wantJson) {
|
|
321
|
-
const text = `${
|
|
320
|
+
const text = `${JSON.stringify(report, null, 2)}\n`;
|
|
322
321
|
if (typeof args.json === 'string' && args.json !== 'true') {
|
|
323
322
|
const { writeFileSync } = await import('node:fs');
|
|
324
323
|
writeFileSync(path.resolve(String(args.json)), text);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vmz/vmz",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.13",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "VMZ Node toolchain — N-API workspace session + CLI (publish name @vmz/vmz)",
|
|
6
6
|
"license": "MIT",
|
|
@@ -48,15 +48,15 @@
|
|
|
48
48
|
}
|
|
49
49
|
},
|
|
50
50
|
"dependencies": {
|
|
51
|
-
"@vmz/core": "0.1.
|
|
52
|
-
"@vmz/plugin": "0.1.
|
|
53
|
-
"@vmz/protocol": "0.1.
|
|
51
|
+
"@vmz/core": "0.1.13",
|
|
52
|
+
"@vmz/plugin": "0.1.13",
|
|
53
|
+
"@vmz/protocol": "0.1.13",
|
|
54
54
|
"jiti": "^2.6.1",
|
|
55
55
|
"json5": "^2.2.3"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@vmz/plugin-markdown-it": "0.1.
|
|
59
|
-
"@vmz/test": "0.1.
|
|
58
|
+
"@vmz/plugin-markdown-it": "0.1.13",
|
|
59
|
+
"@vmz/test": "0.1.13",
|
|
60
60
|
"typescript": "^5.8.3"
|
|
61
61
|
},
|
|
62
62
|
"peerDependenciesMeta": {
|
|
@@ -90,12 +90,12 @@
|
|
|
90
90
|
"cli"
|
|
91
91
|
],
|
|
92
92
|
"optionalDependencies": {
|
|
93
|
-
"@vmz/vmz-win32-x64": "0.1.
|
|
94
|
-
"@vmz/vmz-win32-arm64": "0.1.
|
|
95
|
-
"@vmz/vmz-darwin-x64": "0.1.
|
|
96
|
-
"@vmz/vmz-darwin-arm64": "0.1.
|
|
97
|
-
"@vmz/vmz-linux-x64": "0.1.
|
|
98
|
-
"@vmz/vmz-linux-arm64": "0.1.
|
|
93
|
+
"@vmz/vmz-win32-x64": "0.1.13",
|
|
94
|
+
"@vmz/vmz-win32-arm64": "0.1.13",
|
|
95
|
+
"@vmz/vmz-darwin-x64": "0.1.13",
|
|
96
|
+
"@vmz/vmz-darwin-arm64": "0.1.13",
|
|
97
|
+
"@vmz/vmz-linux-x64": "0.1.13",
|
|
98
|
+
"@vmz/vmz-linux-arm64": "0.1.13"
|
|
99
99
|
},
|
|
100
100
|
"publishConfig": {
|
|
101
101
|
"access": "public"
|
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dev watch helpers: coalesce multi-file bursts without dropping dirty set,
|
|
3
|
-
* and derive extra watch roots from the compile graph (deployment unit sources).
|
|
4
|
-
*/
|
|
5
|
-
/**
|
|
6
|
-
* @typedef {{ changed: string[], deleted: string[] }} DirtySet
|
|
7
|
-
*/
|
|
8
|
-
/**
|
|
9
|
-
* Merge dirty sets: later change cancels delete and vice versa.
|
|
10
|
-
* @param {DirtySet} a
|
|
11
|
-
* @param {DirtySet} b
|
|
12
|
-
* @returns {DirtySet}
|
|
13
|
-
*/
|
|
14
|
-
export declare function mergeDirtySets(a: any, b: any): {
|
|
15
|
-
changed: unknown[];
|
|
16
|
-
deleted: unknown[];
|
|
17
|
-
};
|
|
18
|
-
/**
|
|
19
|
-
* Wait until `root` stops changing; return the **accumulated** dirty set since `initial`.
|
|
20
|
-
* Updates `fingerprints` for `root` as it polls. Does **not** discard `initial`.
|
|
21
|
-
*
|
|
22
|
-
* @param {string} root
|
|
23
|
-
* @param {Map<string, Map<string, string>>} fingerprints
|
|
24
|
-
* @param {DirtySet} initial
|
|
25
|
-
* @param {{ sleep?: (ms: number) => Promise<void>, maxRounds?: number, settleMs?: number }} [opts]
|
|
26
|
-
* @returns {Promise<DirtySet>}
|
|
27
|
-
*/
|
|
28
|
-
export declare function coalesceRootBurst(root: any, fingerprints: any, initial: any, opts?: {}): Promise<{
|
|
29
|
-
changed: any[];
|
|
30
|
-
deleted: any[];
|
|
31
|
-
}>;
|
|
32
|
-
/**
|
|
33
|
-
* Walk up from a file to find a package.json directory.
|
|
34
|
-
* @param {string} file
|
|
35
|
-
* @returns {string | null}
|
|
36
|
-
*/
|
|
37
|
-
export declare function findPackageRoot(file: any): string;
|
|
38
|
-
/**
|
|
39
|
-
* Prefer package/src when present; otherwise the directory containing the source file.
|
|
40
|
-
* @param {string} sourceFile
|
|
41
|
-
* @returns {string | null}
|
|
42
|
-
*/
|
|
43
|
-
export declare function watchRootForSourceFile(sourceFile: any): string;
|
|
44
|
-
/**
|
|
45
|
-
* Absolute roots for workspace / file / link deps that have a src tree.
|
|
46
|
-
* @param {string} project
|
|
47
|
-
* @returns {string[]}
|
|
48
|
-
*/
|
|
49
|
-
export declare function localLinkDependencyRoots(project: any): any[];
|
|
50
|
-
/**
|
|
51
|
-
* Collect watch roots: project src/locales/documents + compile-graph external sources
|
|
52
|
-
* + local link/workspace package roots. Never adds a bare registry node_modules tree.
|
|
53
|
-
*
|
|
54
|
-
* @param {{ project: string, outDir: string }} opts
|
|
55
|
-
* @returns {{ roots: string[], dependencyRoots: string[], applicationRoots: string[] }}
|
|
56
|
-
*/
|
|
57
|
-
export declare function collectDevWatchRoots(opts: any): {
|
|
58
|
-
roots: any[];
|
|
59
|
-
dependencyRoots: unknown[];
|
|
60
|
-
applicationRoots: any[];
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* Classify which watch bucket a root belongs to.
|
|
64
|
-
* @param {string} root
|
|
65
|
-
* @param {{
|
|
66
|
-
* src: string,
|
|
67
|
-
* docsRoot: string,
|
|
68
|
-
* localesRoot: string,
|
|
69
|
-
* designsRoot: string,
|
|
70
|
-
* dependencyRoots: string[],
|
|
71
|
-
* }} ctx
|
|
72
|
-
* @returns {'src' | 'locales' | 'docs' | 'designs' | 'dep' | 'other'}
|
|
73
|
-
*/
|
|
74
|
-
export declare function classifyWatchRoot(root: any, ctx: any): "src" | "locales" | "docs" | "designs" | "dep" | "other";
|
|
75
|
-
/**
|
|
76
|
-
* Classify whether a changed file lives under a dependency watch root (not app src).
|
|
77
|
-
* @param {string} file
|
|
78
|
-
* @param {string[]} dependencyRoots
|
|
79
|
-
*/
|
|
80
|
-
export declare function isDependencyPath(file: any, dependencyRoots: any): any;
|