@vmz/vmz 0.0.3 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/build-assemble.d.ts +52 -0
- package/dist/build-assemble.js +191 -0
- package/dist/cdn-policy.d.ts +196 -0
- package/dist/cdn-policy.js +443 -0
- package/dist/cli.js +152 -11
- package/dist/content-addressed-assets.d.ts +69 -0
- package/dist/content-addressed-assets.js +206 -0
- package/dist/delivery-profile.d.ts +74 -0
- package/dist/delivery-profile.js +279 -0
- package/dist/dev-session.js +49 -13
- package/dist/document-build.js +9 -4
- package/dist/document-designs.js +30 -2
- package/dist/document-enrich.js +8 -0
- package/dist/embedded-packaging.d.ts +22 -0
- package/dist/embedded-packaging.js +113 -0
- package/dist/index.d.ts +19 -3
- package/dist/index.js +35 -15
- package/dist/invocation.d.ts +8 -31
- package/dist/invocation.js +12 -33
- package/dist/locale-check.d.ts +16 -0
- package/dist/locale-check.js +131 -3
- package/dist/locale-cmd.js +2 -2
- package/dist/locale-route-emit.d.ts +34 -0
- package/dist/locale-route-emit.js +134 -0
- package/dist/locale-router.d.ts +20 -0
- package/dist/locale-router.js +68 -0
- package/dist/log.d.ts +2 -2
- package/dist/log.js +11 -3
- package/dist/pack.d.ts +40 -0
- package/dist/pack.js +108 -0
- package/dist/plugin-host.d.ts +10 -1
- package/dist/plugin-host.js +19 -2
- package/dist/port.d.ts +10 -0
- package/dist/port.js +46 -0
- package/dist/production-observability.d.ts +286 -0
- package/dist/production-observability.js +469 -0
- package/dist/production-test-pack.d.ts +144 -0
- package/dist/production-test-pack.js +447 -0
- package/dist/release-cmd.d.ts +8 -0
- package/dist/release-cmd.js +126 -0
- package/dist/release-pack.d.ts +96 -0
- package/dist/release-pack.js +346 -0
- package/dist/server-artifact.d.ts +140 -0
- package/dist/server-artifact.js +205 -0
- package/dist/server-language-backend.d.ts +89 -0
- package/dist/server-language-backend.js +121 -0
- package/dist/site-delivery.d.ts +134 -0
- package/dist/site-delivery.js +345 -0
- package/dist/static-emit.d.ts +145 -0
- package/dist/static-emit.js +577 -0
- package/package.json +13 -13
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* Emit RouteId × LocaleId realization (+ PageMeta / hreflang seed) into dist/_vmz.
|
|
4
|
+
* Consumed by serve-host + static-emit; LocaleId stays out of stable RouteId.
|
|
5
|
+
*/
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { checkLocales, localeHasErrors } from './locale-check.js';
|
|
9
|
+
import { buildLocalePageMeta, buildLocaleRouteRealizationTable } from './locale-router.js';
|
|
10
|
+
export const LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA = 'vmz.locale.route_realization.v0';
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} chunkId
|
|
13
|
+
*/
|
|
14
|
+
function pathPatternFromChunk(chunkId) {
|
|
15
|
+
const rel = String(chunkId || '').replace(/^pages\//, '');
|
|
16
|
+
const parts = rel.split('/').filter(Boolean);
|
|
17
|
+
const segs = [];
|
|
18
|
+
for (let i = 0; i < parts.length; i++) {
|
|
19
|
+
const p = parts[i];
|
|
20
|
+
if (p === 'index' && i === parts.length - 1)
|
|
21
|
+
continue;
|
|
22
|
+
segs.push(p);
|
|
23
|
+
}
|
|
24
|
+
return segs.length ? `/${segs.join('/')}` : '/';
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Layouts are not public RouteNodes for locale realization / SEO.
|
|
28
|
+
* @param {string} chunkId
|
|
29
|
+
*/
|
|
30
|
+
function isPublicPageChunk(chunkId) {
|
|
31
|
+
const id = String(chunkId || '');
|
|
32
|
+
if (!id.startsWith('pages/'))
|
|
33
|
+
return false;
|
|
34
|
+
if (/(^|\/)Layout$/.test(id))
|
|
35
|
+
return false;
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* @param {string} projectRoot
|
|
40
|
+
* @param {string} distDir
|
|
41
|
+
* @param {{ origin?: string }} [opts]
|
|
42
|
+
*/
|
|
43
|
+
export function emitLocaleRouteRealization(projectRoot, distDir, opts = {}) {
|
|
44
|
+
const report = checkLocales({ projectRoot, checkUnused: false });
|
|
45
|
+
if (localeHasErrors(report)) {
|
|
46
|
+
return { ok: false, written: [], diagnostics: report.diagnostics || [], artifact: null };
|
|
47
|
+
}
|
|
48
|
+
// Missing /locales is warning (not error) — still never silent; no realization artifact yet.
|
|
49
|
+
if (!report.manifest) {
|
|
50
|
+
return { ok: true, written: [], artifact: null, diagnostics: report.diagnostics || [] };
|
|
51
|
+
}
|
|
52
|
+
const deploymentPath = path.join(distDir, 'vmz-deployment.json');
|
|
53
|
+
if (!fs.existsSync(deploymentPath)) {
|
|
54
|
+
return {
|
|
55
|
+
ok: false,
|
|
56
|
+
written: [],
|
|
57
|
+
diagnostics: [{ severity: 'error', code: 'locale.route.missing_deployment', message: 'missing vmz-deployment.json' }],
|
|
58
|
+
artifact: null,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
const deployment = JSON.parse(fs.readFileSync(deploymentPath, 'utf8'));
|
|
62
|
+
const pages = (deployment.units || []).filter((u) => u.kind === 'page' && isPublicPageChunk(String(u.chunkId)));
|
|
63
|
+
const routes = pages.map((u) => ({
|
|
64
|
+
routeId: String(u.chunkId),
|
|
65
|
+
path: pathPatternFromChunk(String(u.chunkId)),
|
|
66
|
+
}));
|
|
67
|
+
const localeEntries = report.manifest?.locales || [];
|
|
68
|
+
const locales = localeEntries.map((l) => l.id);
|
|
69
|
+
const directions = Object.fromEntries(localeEntries.map((l) => [l.id, l.direction || 'ltr']));
|
|
70
|
+
const defaultLocale = report.manifest?.defaultLocale;
|
|
71
|
+
const routing = report.manifest?.routing || { strategy: 'prefix', defaultPrefix: 'include' };
|
|
72
|
+
const table = buildLocaleRouteRealizationTable({
|
|
73
|
+
routes,
|
|
74
|
+
locales,
|
|
75
|
+
defaultLocale,
|
|
76
|
+
routing,
|
|
77
|
+
});
|
|
78
|
+
if (table.status === 'failed') {
|
|
79
|
+
return { ok: false, written: [], diagnostics: table.diagnostics || [], artifact: null };
|
|
80
|
+
}
|
|
81
|
+
const origin = String(opts.origin || process.env.VMZ_SITE_ORIGIN || 'https://example.test').replace(/\/$/, '');
|
|
82
|
+
/** @type {any[]} */
|
|
83
|
+
const pageMetas = [];
|
|
84
|
+
for (const route of routes) {
|
|
85
|
+
for (const loc of locales) {
|
|
86
|
+
const meta = buildLocalePageMeta({
|
|
87
|
+
routeId: route.routeId,
|
|
88
|
+
localeId: loc,
|
|
89
|
+
direction: directions[loc],
|
|
90
|
+
title: route.routeId,
|
|
91
|
+
origin,
|
|
92
|
+
realizations: table.realizations,
|
|
93
|
+
locales,
|
|
94
|
+
defaultLocale,
|
|
95
|
+
});
|
|
96
|
+
pageMetas.push(meta);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
const artifact = {
|
|
100
|
+
schema: LOCALE_ROUTE_REALIZATION_ARTIFACT_SCHEMA,
|
|
101
|
+
defaultLocale,
|
|
102
|
+
locales: localeEntries.map((l) => ({
|
|
103
|
+
id: l.id,
|
|
104
|
+
label: l.label || l.id,
|
|
105
|
+
direction: l.direction || 'ltr',
|
|
106
|
+
})),
|
|
107
|
+
routing: {
|
|
108
|
+
strategy: routing.strategy || 'prefix',
|
|
109
|
+
defaultPrefix: routing.defaultPrefix || 'include',
|
|
110
|
+
defaultLocale,
|
|
111
|
+
},
|
|
112
|
+
origin,
|
|
113
|
+
routes,
|
|
114
|
+
realizations: table.realizations,
|
|
115
|
+
pageMetas,
|
|
116
|
+
};
|
|
117
|
+
const vmzDir = path.join(distDir, '_vmz');
|
|
118
|
+
fs.mkdirSync(vmzDir, { recursive: true });
|
|
119
|
+
const outPath = path.join(vmzDir, 'locale-route-realization.json');
|
|
120
|
+
fs.writeFileSync(outPath, `${JSON.stringify(artifact, null, 2)}\n`, 'utf8');
|
|
121
|
+
const manifestOut = path.join(vmzDir, 'locale-manifest.json');
|
|
122
|
+
fs.writeFileSync(manifestOut, `${JSON.stringify({
|
|
123
|
+
schema: 'vmz.locale.manifest.v0',
|
|
124
|
+
defaultLocale,
|
|
125
|
+
locales: artifact.locales,
|
|
126
|
+
routing: artifact.routing,
|
|
127
|
+
}, null, 2)}\n`, 'utf8');
|
|
128
|
+
return {
|
|
129
|
+
ok: true,
|
|
130
|
+
written: ['_vmz/locale-route-realization.json', '_vmz/locale-manifest.json'],
|
|
131
|
+
artifact,
|
|
132
|
+
diagnostics: [],
|
|
133
|
+
};
|
|
134
|
+
}
|
package/dist/locale-router.d.ts
CHANGED
|
@@ -116,6 +116,26 @@ export declare function parseLocaleFromPath(pathname: any, supportedLocales: any
|
|
|
116
116
|
localeId: string;
|
|
117
117
|
restPath: string;
|
|
118
118
|
};
|
|
119
|
+
/**
|
|
120
|
+
* Rewrite a same-app href to the given LocaleId via route realization.
|
|
121
|
+
* Stable path (no locale) is recovered first, then re-realized — Link never hardcodes locale.
|
|
122
|
+
* @param {string} href
|
|
123
|
+
* @param {string} localeId
|
|
124
|
+
* @param {{
|
|
125
|
+
* locales?: Array<{ id: string }|string>,
|
|
126
|
+
* defaultLocale?: string,
|
|
127
|
+
* routing?: { strategy?: string, defaultPrefix?: string, defaultLocale?: string },
|
|
128
|
+
* }} artifact
|
|
129
|
+
*/
|
|
130
|
+
export declare function localizeSameAppHref(href: any, localeId: any, artifact: any): any;
|
|
131
|
+
/**
|
|
132
|
+
* Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`.
|
|
133
|
+
* @param {string} html
|
|
134
|
+
* @param {string} localeId
|
|
135
|
+
* @param {Parameters<typeof localizeSameAppHref>[2]} artifact
|
|
136
|
+
* @param {(s: string) => string} [escapeAttr]
|
|
137
|
+
*/
|
|
138
|
+
export declare function localizeBodyLinks(html: any, localeId: any, artifact: any, escapeAttr?: (s: any) => string): any;
|
|
119
139
|
/**
|
|
120
140
|
* Plan redirect / negotiation for an incoming URL (omit-prefix aware).
|
|
121
141
|
* @param {{
|
package/dist/locale-router.js
CHANGED
|
@@ -260,6 +260,74 @@ export function parseLocaleFromPath(pathname, supportedLocales) {
|
|
|
260
260
|
}
|
|
261
261
|
return { localeId: null, restPath: normalizePath(pathname) };
|
|
262
262
|
}
|
|
263
|
+
/**
|
|
264
|
+
* Rewrite a same-app href to the given LocaleId via route realization.
|
|
265
|
+
* Stable path (no locale) is recovered first, then re-realized — Link never hardcodes locale.
|
|
266
|
+
* @param {string} href
|
|
267
|
+
* @param {string} localeId
|
|
268
|
+
* @param {{
|
|
269
|
+
* locales?: Array<{ id: string }|string>,
|
|
270
|
+
* defaultLocale?: string,
|
|
271
|
+
* routing?: { strategy?: string, defaultPrefix?: string, defaultLocale?: string },
|
|
272
|
+
* }} artifact
|
|
273
|
+
*/
|
|
274
|
+
export function localizeSameAppHref(href, localeId, artifact) {
|
|
275
|
+
if (!href || !localeId || !artifact)
|
|
276
|
+
return href;
|
|
277
|
+
if (href.startsWith('#') || /^(mailto|tel|javascript):/i.test(href))
|
|
278
|
+
return href;
|
|
279
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(href) && !href.startsWith('/'))
|
|
280
|
+
return href;
|
|
281
|
+
let pathname = String(href);
|
|
282
|
+
let search = '';
|
|
283
|
+
let hash = '';
|
|
284
|
+
const hashIdx = pathname.indexOf('#');
|
|
285
|
+
if (hashIdx >= 0) {
|
|
286
|
+
hash = pathname.slice(hashIdx);
|
|
287
|
+
pathname = pathname.slice(0, hashIdx);
|
|
288
|
+
}
|
|
289
|
+
const qIdx = pathname.indexOf('?');
|
|
290
|
+
if (qIdx >= 0) {
|
|
291
|
+
search = pathname.slice(qIdx);
|
|
292
|
+
pathname = pathname.slice(0, qIdx);
|
|
293
|
+
}
|
|
294
|
+
if (!pathname)
|
|
295
|
+
pathname = '/';
|
|
296
|
+
const supported = (artifact.locales || []).map((l) => (typeof l === 'string' ? l : l.id)).filter(Boolean);
|
|
297
|
+
const defaultLocale = artifact.defaultLocale || artifact.routing?.defaultLocale;
|
|
298
|
+
const routing = {
|
|
299
|
+
strategy: artifact.routing?.strategy || 'prefix',
|
|
300
|
+
defaultPrefix: artifact.routing?.defaultPrefix || 'include',
|
|
301
|
+
defaultLocale,
|
|
302
|
+
};
|
|
303
|
+
const parsed = parseLocaleFromPath(pathname, supported);
|
|
304
|
+
const rest = parsed.restPath || '/';
|
|
305
|
+
const realized = realizeRoutePath(localeId, rest, routing);
|
|
306
|
+
return `${realized.path}${search}${hash}`;
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Rewrite `<a data-vmz-route href>` in HTML body to retain `localeId`.
|
|
310
|
+
* @param {string} html
|
|
311
|
+
* @param {string} localeId
|
|
312
|
+
* @param {Parameters<typeof localizeSameAppHref>[2]} artifact
|
|
313
|
+
* @param {(s: string) => string} [escapeAttr]
|
|
314
|
+
*/
|
|
315
|
+
export function localizeBodyLinks(html, localeId, artifact, escapeAttr = (s) => String(s).replace(/&/g, '&').replace(/"/g, '"')) {
|
|
316
|
+
if (!html || !localeId || !artifact)
|
|
317
|
+
return html;
|
|
318
|
+
return String(html).replace(/<a\b([^>]*)>/gi, (full, attrs) => {
|
|
319
|
+
if (!/\bdata-vmz-route\s*=/.test(attrs))
|
|
320
|
+
return full;
|
|
321
|
+
const hm = attrs.match(/\bhref\s*=\s*"([^"]*)"/i);
|
|
322
|
+
if (!hm)
|
|
323
|
+
return full;
|
|
324
|
+
const next = localizeSameAppHref(hm[1], localeId, artifact);
|
|
325
|
+
if (next === hm[1])
|
|
326
|
+
return full;
|
|
327
|
+
const newAttrs = attrs.replace(/\bhref\s*=\s*"[^"]*"/i, `href="${escapeAttr(next)}"`);
|
|
328
|
+
return `<a${newAttrs}>`;
|
|
329
|
+
});
|
|
330
|
+
}
|
|
263
331
|
/**
|
|
264
332
|
* Plan redirect / negotiation for an incoming URL (omit-prefix aware).
|
|
265
333
|
* @param {{
|
package/dist/log.d.ts
CHANGED
|
@@ -8,10 +8,10 @@ export declare const log: {
|
|
|
8
8
|
warn(...args: any[]): void;
|
|
9
9
|
/** @param {...unknown} args */
|
|
10
10
|
error(...args: any[]): void;
|
|
11
|
-
/** @param {{ severity: string, path
|
|
11
|
+
/** @param {{ severity: string, path?: string, message: string, code?: string }} d */
|
|
12
12
|
diagnostic(d: any): void;
|
|
13
13
|
/**
|
|
14
|
-
* @param {Array<{ severity: string, path
|
|
14
|
+
* @param {Array<{ severity: string, path?: string, message: string, code?: string }>} diagnostics
|
|
15
15
|
* @param {{ denyWarnings?: boolean }} [opts]
|
|
16
16
|
* @returns {number} failing count (errors, and warnings if denyWarnings)
|
|
17
17
|
*/
|
package/dist/log.js
CHANGED
|
@@ -19,12 +19,20 @@ export const log = {
|
|
|
19
19
|
error(...args) {
|
|
20
20
|
console.error(stamp('error'), ...args);
|
|
21
21
|
},
|
|
22
|
-
/** @param {{ severity: string, path
|
|
22
|
+
/** @param {{ severity: string, path?: string, message: string, code?: string }} d */
|
|
23
23
|
diagnostic(d) {
|
|
24
|
-
|
|
24
|
+
const sev = d.severity || 'error';
|
|
25
|
+
const code = d.code ? `${d.code}: ` : '';
|
|
26
|
+
const loc = d.path ? ` (${d.path})` : '';
|
|
27
|
+
// Prefer `vmz warn|error CODE: message (path)` so locale warnings are visible, not silent.
|
|
28
|
+
if (sev === 'warning') {
|
|
29
|
+
console.error(`${stamp('warn')} ${code}${d.message}${loc}`);
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.error(`${stamp(sev === 'error' ? 'error' : sev)} ${code}${d.path ? `${d.path}: ` : ''}${d.message}`);
|
|
25
33
|
},
|
|
26
34
|
/**
|
|
27
|
-
* @param {Array<{ severity: string, path
|
|
35
|
+
* @param {Array<{ severity: string, path?: string, message: string, code?: string }>} diagnostics
|
|
28
36
|
* @param {{ denyWarnings?: boolean }} [opts]
|
|
29
37
|
* @returns {number} failing count (errors, and warnings if denyWarnings)
|
|
30
38
|
*/
|
package/dist/pack.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B4 — Pack stage: consume Deployment IR (VPG-owned units), emit pack manifest.
|
|
3
|
+
* Full oxc minify/chunk-split lands progressively; this stage always runs and
|
|
4
|
+
* records integrity digests so Assemble/Prove never skip the pack contract.
|
|
5
|
+
*/
|
|
6
|
+
export declare const PACK_MANIFEST_SCHEMA = "vmz.pack.manifest.v0";
|
|
7
|
+
/**
|
|
8
|
+
* Ensure dom split companions sit next to vmz-dom.js (barrel imports ./dom-core.js).
|
|
9
|
+
* Always refresh from `@vmz/core` when present so SSR/runtime fixes are not sticky in outDir.
|
|
10
|
+
* @param {string} outDir
|
|
11
|
+
* @param {string | null | undefined} coreDist `@vmz/core` dist root
|
|
12
|
+
* @returns {string[]} copied relative names
|
|
13
|
+
*/
|
|
14
|
+
export declare function ensureRuntimeCompanions(outDir: any, coreDist: any): any[];
|
|
15
|
+
/**
|
|
16
|
+
* @param {string} outDir
|
|
17
|
+
* @param {{
|
|
18
|
+
* release?: boolean,
|
|
19
|
+
* profileId?: string,
|
|
20
|
+
* assembly?: string,
|
|
21
|
+
* preferredClientFace?: string,
|
|
22
|
+
* coreDist?: string | null,
|
|
23
|
+
* }} [opts]
|
|
24
|
+
*/
|
|
25
|
+
export declare function packFromDeploymentIr(outDir: any, opts?: {}): {
|
|
26
|
+
manifest: {
|
|
27
|
+
schema: string;
|
|
28
|
+
profileId: any;
|
|
29
|
+
assembly: any;
|
|
30
|
+
release: boolean;
|
|
31
|
+
preferredClientFace: any;
|
|
32
|
+
deploymentSchema: any;
|
|
33
|
+
unitCount: number;
|
|
34
|
+
units: any[];
|
|
35
|
+
minify: string;
|
|
36
|
+
treeShakeBasis: string;
|
|
37
|
+
bundler: string;
|
|
38
|
+
};
|
|
39
|
+
path: string;
|
|
40
|
+
};
|
package/dist/pack.js
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* B4 — Pack stage: consume Deployment IR (VPG-owned units), emit pack manifest.
|
|
3
|
+
* Full oxc minify/chunk-split lands progressively; this stage always runs and
|
|
4
|
+
* records integrity digests so Assemble/Prove never skip the pack contract.
|
|
5
|
+
*/
|
|
6
|
+
// @ts-nocheck
|
|
7
|
+
import crypto from 'node:crypto';
|
|
8
|
+
import { copyFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
9
|
+
import path from 'node:path';
|
|
10
|
+
import { loadDeploymentIr, planBundleInputs } from './bundler-adapter.js';
|
|
11
|
+
export const PACK_MANIFEST_SCHEMA = 'vmz.pack.manifest.v0';
|
|
12
|
+
/**
|
|
13
|
+
* Ensure dom split companions sit next to vmz-dom.js (barrel imports ./dom-core.js).
|
|
14
|
+
* Always refresh from `@vmz/core` when present so SSR/runtime fixes are not sticky in outDir.
|
|
15
|
+
* @param {string} outDir
|
|
16
|
+
* @param {string | null | undefined} coreDist `@vmz/core` dist root
|
|
17
|
+
* @returns {string[]} copied relative names
|
|
18
|
+
*/
|
|
19
|
+
export function ensureRuntimeCompanions(outDir, coreDist) {
|
|
20
|
+
if (!coreDist)
|
|
21
|
+
return [];
|
|
22
|
+
const names = ['dom-core.js', 'dom-ssr.js', 'dom.client.js'];
|
|
23
|
+
const copied = [];
|
|
24
|
+
for (const name of names) {
|
|
25
|
+
const src = path.join(coreDist, name);
|
|
26
|
+
if (!existsSync(src))
|
|
27
|
+
continue;
|
|
28
|
+
const dest = path.join(outDir, name);
|
|
29
|
+
copyFileSync(src, dest);
|
|
30
|
+
copied.push(name);
|
|
31
|
+
}
|
|
32
|
+
return copied;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* @param {string} outDir
|
|
36
|
+
* @param {{
|
|
37
|
+
* release?: boolean,
|
|
38
|
+
* profileId?: string,
|
|
39
|
+
* assembly?: string,
|
|
40
|
+
* preferredClientFace?: string,
|
|
41
|
+
* coreDist?: string | null,
|
|
42
|
+
* }} [opts]
|
|
43
|
+
*/
|
|
44
|
+
export function packFromDeploymentIr(outDir, opts = {}) {
|
|
45
|
+
ensureRuntimeCompanions(outDir, opts.coreDist);
|
|
46
|
+
const ir = loadDeploymentIr(outDir);
|
|
47
|
+
const inputs = planBundleInputs(outDir, ir);
|
|
48
|
+
const units = [];
|
|
49
|
+
for (const entry of inputs) {
|
|
50
|
+
const abs = entry.entry;
|
|
51
|
+
let digest = null;
|
|
52
|
+
let bytes = 0;
|
|
53
|
+
let present = false;
|
|
54
|
+
if (existsSync(abs)) {
|
|
55
|
+
present = true;
|
|
56
|
+
const buf = readFileSync(abs);
|
|
57
|
+
bytes = buf.length;
|
|
58
|
+
digest = crypto.createHash('sha256').update(buf).digest('hex');
|
|
59
|
+
}
|
|
60
|
+
units.push({
|
|
61
|
+
chunkId: entry.chunkId,
|
|
62
|
+
kind: entry.kind,
|
|
63
|
+
entry: path.relative(outDir, abs).replace(/\\/g, '/'),
|
|
64
|
+
programIr: path.relative(outDir, entry.programIr).replace(/\\/g, '/'),
|
|
65
|
+
source: entry.source,
|
|
66
|
+
present,
|
|
67
|
+
bytes,
|
|
68
|
+
digest,
|
|
69
|
+
rebuilt: Boolean(entry.rebuilt),
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const body = {
|
|
73
|
+
schema: PACK_MANIFEST_SCHEMA,
|
|
74
|
+
profileId: opts.profileId || null,
|
|
75
|
+
assembly: opts.assembly || null,
|
|
76
|
+
release: Boolean(opts.release),
|
|
77
|
+
preferredClientFace: opts.preferredClientFace || '@vmz/core/dom/client',
|
|
78
|
+
deploymentSchema: ir.schema,
|
|
79
|
+
unitCount: units.length,
|
|
80
|
+
units,
|
|
81
|
+
minify: opts.release ? 'oxc-pending' : 'dev-identity',
|
|
82
|
+
treeShakeBasis: 'vpg-deployment-ir',
|
|
83
|
+
bundler: 'vmz-pack',
|
|
84
|
+
};
|
|
85
|
+
body.packDigest = sha256Hex(stableStringify({ ...body }));
|
|
86
|
+
const vmzDir = path.join(outDir, '_vmz');
|
|
87
|
+
mkdirSync(vmzDir, { recursive: true });
|
|
88
|
+
const file = path.join(vmzDir, 'pack-manifest.json');
|
|
89
|
+
writeFileSync(file, `${JSON.stringify(body, null, 2)}\n`, 'utf8');
|
|
90
|
+
return { manifest: body, path: file };
|
|
91
|
+
}
|
|
92
|
+
function stableStringify(value) {
|
|
93
|
+
return JSON.stringify(sortKeys(value));
|
|
94
|
+
}
|
|
95
|
+
function sortKeys(value) {
|
|
96
|
+
if (Array.isArray(value))
|
|
97
|
+
return value.map(sortKeys);
|
|
98
|
+
if (value && typeof value === 'object') {
|
|
99
|
+
const out = {};
|
|
100
|
+
for (const k of Object.keys(value).sort())
|
|
101
|
+
out[k] = sortKeys(value[k]);
|
|
102
|
+
return out;
|
|
103
|
+
}
|
|
104
|
+
return value;
|
|
105
|
+
}
|
|
106
|
+
function sha256Hex(text) {
|
|
107
|
+
return crypto.createHash('sha256').update(text, 'utf8').digest('hex');
|
|
108
|
+
}
|
package/dist/plugin-host.d.ts
CHANGED
|
@@ -12,11 +12,20 @@ export declare function importMaybeTs(full: any): Promise<any>;
|
|
|
12
12
|
/**
|
|
13
13
|
* Load `vmz.config.*` from project root (+ optional root `vmz.plugin.*`).
|
|
14
14
|
* @param {string} project
|
|
15
|
-
* @returns {Promise<{
|
|
15
|
+
* @returns {Promise<{
|
|
16
|
+
* plugins: import('@vmz/plugin').VmzPlugin[],
|
|
17
|
+
* engines: import('@vmz/plugin').VmzEngines,
|
|
18
|
+
* delivery: import('@vmz/plugin').DeliveryAuthoring | null,
|
|
19
|
+
* application: { id?: string } | null,
|
|
20
|
+
* path: string | null,
|
|
21
|
+
* pluginPath: string | null,
|
|
22
|
+
* }>}
|
|
16
23
|
*/
|
|
17
24
|
export declare function loadVmzConfig(project: any): Promise<{
|
|
18
25
|
plugins: any[];
|
|
19
26
|
engines: {};
|
|
27
|
+
delivery: any;
|
|
28
|
+
application: any;
|
|
20
29
|
path: any;
|
|
21
30
|
pluginPath: any;
|
|
22
31
|
}>;
|
package/dist/plugin-host.js
CHANGED
|
@@ -32,13 +32,24 @@ export async function importMaybeTs(full) {
|
|
|
32
32
|
/**
|
|
33
33
|
* Load `vmz.config.*` from project root (+ optional root `vmz.plugin.*`).
|
|
34
34
|
* @param {string} project
|
|
35
|
-
* @returns {Promise<{
|
|
35
|
+
* @returns {Promise<{
|
|
36
|
+
* plugins: import('@vmz/plugin').VmzPlugin[],
|
|
37
|
+
* engines: import('@vmz/plugin').VmzEngines,
|
|
38
|
+
* delivery: import('@vmz/plugin').DeliveryAuthoring | null,
|
|
39
|
+
* application: { id?: string } | null,
|
|
40
|
+
* path: string | null,
|
|
41
|
+
* pluginPath: string | null,
|
|
42
|
+
* }>}
|
|
36
43
|
*/
|
|
37
44
|
export async function loadVmzConfig(project) {
|
|
38
45
|
/** @type {import('@vmz/plugin').VmzPlugin[]} */
|
|
39
46
|
const plugins = [];
|
|
40
47
|
/** @type {import('@vmz/plugin').VmzEngines} */
|
|
41
48
|
let engines = {};
|
|
49
|
+
/** @type {import('@vmz/plugin').DeliveryAuthoring | null} */
|
|
50
|
+
let delivery = null;
|
|
51
|
+
/** @type {{ id?: string } | null} */
|
|
52
|
+
let application = null;
|
|
42
53
|
/** @type {string | null} */
|
|
43
54
|
let configPath = null;
|
|
44
55
|
/** @type {string | null} */
|
|
@@ -51,6 +62,12 @@ export async function loadVmzConfig(project) {
|
|
|
51
62
|
const cfg = await importMaybeTs(full);
|
|
52
63
|
const raw = cfg?.plugins ?? [];
|
|
53
64
|
engines = cfg?.engines && typeof cfg.engines === 'object' ? { ...cfg.engines } : {};
|
|
65
|
+
if (cfg?.delivery && typeof cfg.delivery === 'object') {
|
|
66
|
+
delivery = cfg.delivery;
|
|
67
|
+
}
|
|
68
|
+
if (cfg?.application && typeof cfg.application === 'object') {
|
|
69
|
+
application = cfg.application;
|
|
70
|
+
}
|
|
54
71
|
for (const entry of raw) {
|
|
55
72
|
plugins.push(await resolvePluginEntry(project, entry));
|
|
56
73
|
}
|
|
@@ -64,7 +81,7 @@ export async function loadVmzConfig(project) {
|
|
|
64
81
|
plugins.push(await resolvePluginEntry(project, full));
|
|
65
82
|
break;
|
|
66
83
|
}
|
|
67
|
-
return { plugins, engines, path: configPath, pluginPath };
|
|
84
|
+
return { plugins, engines, delivery, application, path: configPath, pluginPath };
|
|
68
85
|
}
|
|
69
86
|
/**
|
|
70
87
|
* @param {string} project
|
package/dist/port.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Probe for a free TCP port starting at `start` (inclusive).
|
|
3
|
+
* Used by `vmz dev` when `--port` is omitted.
|
|
4
|
+
*
|
|
5
|
+
* @param {string} host
|
|
6
|
+
* @param {number} [start=5173]
|
|
7
|
+
* @param {number} [maxTries=50]
|
|
8
|
+
* @returns {Promise<number>}
|
|
9
|
+
*/
|
|
10
|
+
export declare function findAvailablePort(host: any, start?: number, maxTries?: number): Promise<unknown>;
|
package/dist/port.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
import net from 'node:net';
|
|
3
|
+
/**
|
|
4
|
+
* Probe for a free TCP port starting at `start` (inclusive).
|
|
5
|
+
* Used by `vmz dev` when `--port` is omitted.
|
|
6
|
+
*
|
|
7
|
+
* @param {string} host
|
|
8
|
+
* @param {number} [start=5173]
|
|
9
|
+
* @param {number} [maxTries=50]
|
|
10
|
+
* @returns {Promise<number>}
|
|
11
|
+
*/
|
|
12
|
+
export function findAvailablePort(host, start = 5173, maxTries = 50) {
|
|
13
|
+
const first = Number(start);
|
|
14
|
+
if (!Number.isFinite(first) || first <= 0) {
|
|
15
|
+
return Promise.reject(new Error(`invalid start port: ${start}`));
|
|
16
|
+
}
|
|
17
|
+
return new Promise((resolve, reject) => {
|
|
18
|
+
let port = first;
|
|
19
|
+
const attempt = () => {
|
|
20
|
+
if (port > first + maxTries) {
|
|
21
|
+
reject(new Error(`no free port in ${first}..${first + maxTries} on ${host}`));
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const server = net.createServer();
|
|
25
|
+
server.unref();
|
|
26
|
+
server.once('error', (err) => {
|
|
27
|
+
if (err && err.code === 'EADDRINUSE') {
|
|
28
|
+
port += 1;
|
|
29
|
+
attempt();
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
reject(err);
|
|
33
|
+
});
|
|
34
|
+
server.once('listening', () => {
|
|
35
|
+
server.close((closeErr) => {
|
|
36
|
+
if (closeErr)
|
|
37
|
+
reject(closeErr);
|
|
38
|
+
else
|
|
39
|
+
resolve(port);
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
server.listen(port, host);
|
|
43
|
+
};
|
|
44
|
+
attempt();
|
|
45
|
+
});
|
|
46
|
+
}
|