@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.
Files changed (46) hide show
  1. package/dist/build-assemble.js +3 -4
  2. package/dist/cdn-policy.js +4 -5
  3. package/dist/cli.js +15 -50
  4. package/dist/content-addressed-assets.d.ts +2 -8
  5. package/dist/content-addressed-assets.js +34 -111
  6. package/dist/delivery-profile.d.ts +1 -11
  7. package/dist/delivery-profile.js +1 -70
  8. package/dist/dev-session.d.ts +0 -1
  9. package/dist/dev-session.js +65 -268
  10. package/dist/document-build.d.ts +1 -2
  11. package/dist/document-build.js +82 -104
  12. package/dist/document-cmd.js +9 -2
  13. package/dist/document-enrich.d.ts +1 -2
  14. package/dist/document-enrich.js +4 -15
  15. package/dist/document-integrate.js +21 -29
  16. package/dist/embedded-packaging.js +1 -2
  17. package/dist/index.d.ts +1 -15
  18. package/dist/index.js +3 -59
  19. package/dist/locale-check.js +61 -28
  20. package/dist/locale-cmd.js +42 -10
  21. package/dist/locale-route-emit.d.ts +1 -4
  22. package/dist/locale-route-emit.js +32 -7
  23. package/dist/locale-router.js +8 -26
  24. package/dist/pack.js +2 -3
  25. package/dist/production-observability.js +2 -3
  26. package/dist/production-test-pack.js +3 -4
  27. package/dist/release-pack.js +18 -5
  28. package/dist/server-artifact.js +3 -4
  29. package/dist/site-delivery.js +3 -4
  30. package/dist/static-emit.js +97 -118
  31. package/dist/test-cmd.js +1 -2
  32. package/package.json +12 -12
  33. package/dist/dev-watch-roots.d.ts +0 -80
  34. package/dist/dev-watch-roots.js +0 -245
  35. package/dist/document-layout-render.d.ts +0 -20
  36. package/dist/document-layout-render.js +0 -87
  37. package/dist/document-routing-config.d.ts +0 -13
  38. package/dist/document-routing-config.js +0 -40
  39. package/dist/native-addon.d.ts +0 -9
  40. package/dist/native-addon.js +0 -84
  41. package/dist/pretty-json.d.ts +0 -19
  42. package/dist/pretty-json.js +0 -43
  43. package/dist/route-path.d.ts +0 -35
  44. package/dist/route-path.js +0 -77
  45. package/dist/wechat-packaging.d.ts +0 -22
  46. package/dist/wechat-packaging.js +0 -59
@@ -1,245 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Dev watch helpers: coalesce multi-file bursts without dropping dirty set,
4
- * and derive extra watch roots from the compile graph (deployment unit sources).
5
- */
6
- import { existsSync, readFileSync, realpathSync } from 'node:fs';
7
- import path from 'node:path';
8
- import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
9
- /**
10
- * @typedef {{ changed: string[], deleted: string[] }} DirtySet
11
- */
12
- /**
13
- * Merge dirty sets: later change cancels delete and vice versa.
14
- * @param {DirtySet} a
15
- * @param {DirtySet} b
16
- * @returns {DirtySet}
17
- */
18
- export function mergeDirtySets(a, b) {
19
- const changed = new Set(a?.changed || []);
20
- const deleted = new Set(a?.deleted || []);
21
- for (const f of b?.changed || []) {
22
- changed.add(f);
23
- deleted.delete(f);
24
- }
25
- for (const f of b?.deleted || []) {
26
- deleted.add(f);
27
- changed.delete(f);
28
- }
29
- return { changed: [...changed], deleted: [...deleted] };
30
- }
31
- /**
32
- * Wait until `root` stops changing; return the **accumulated** dirty set since `initial`.
33
- * Updates `fingerprints` for `root` as it polls. Does **not** discard `initial`.
34
- *
35
- * @param {string} root
36
- * @param {Map<string, Map<string, string>>} fingerprints
37
- * @param {DirtySet} initial
38
- * @param {{ sleep?: (ms: number) => Promise<void>, maxRounds?: number, settleMs?: number }} [opts]
39
- * @returns {Promise<DirtySet>}
40
- */
41
- export async function coalesceRootBurst(root, fingerprints, initial, opts = {}) {
42
- const sleepFn = opts.sleep || ((ms) => new Promise((r) => setTimeout(r, ms)));
43
- const maxRounds = opts.maxRounds ?? 20;
44
- const settleMs = opts.settleMs ?? 220;
45
- let accumulated = {
46
- changed: [...(initial?.changed || [])],
47
- deleted: [...(initial?.deleted || [])],
48
- };
49
- let guard = 0;
50
- while (guard++ < maxRounds) {
51
- await sleepFn(settleMs);
52
- const prev = fingerprints.get(root) || new Map();
53
- const next = fileFingerprintMap(root);
54
- const diff = diffFingerprints(prev, next);
55
- fingerprints.set(root, next);
56
- if (!diff.changed.length && !diff.deleted.length)
57
- break;
58
- accumulated = mergeDirtySets(accumulated, diff);
59
- }
60
- return accumulated;
61
- }
62
- /**
63
- * Walk up from a file to find a package.json directory.
64
- * @param {string} file
65
- * @returns {string | null}
66
- */
67
- export function findPackageRoot(file) {
68
- let dir = path.dirname(path.resolve(file));
69
- for (let i = 0; i < 24; i++) {
70
- if (existsSync(path.join(dir, 'package.json')))
71
- return dir;
72
- const parent = path.dirname(dir);
73
- if (parent === dir)
74
- break;
75
- dir = parent;
76
- }
77
- return null;
78
- }
79
- /**
80
- * Prefer package/src when present; otherwise the directory containing the source file.
81
- * @param {string} sourceFile
82
- * @returns {string | null}
83
- */
84
- export function watchRootForSourceFile(sourceFile) {
85
- const abs = path.resolve(sourceFile);
86
- if (!existsSync(abs))
87
- return null;
88
- const pkg = findPackageRoot(abs);
89
- if (pkg) {
90
- const src = path.join(pkg, 'src');
91
- if (existsSync(src))
92
- return src;
93
- return pkg;
94
- }
95
- return path.dirname(abs);
96
- }
97
- /**
98
- * Absolute roots for workspace / file / link deps that have a src tree.
99
- * @param {string} project
100
- * @returns {string[]}
101
- */
102
- export function localLinkDependencyRoots(project) {
103
- const pkgPath = path.join(path.resolve(project), 'package.json');
104
- if (!existsSync(pkgPath))
105
- return [];
106
- /** @type {string[]} */
107
- const roots = [];
108
- try {
109
- const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
110
- const deps = { ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) };
111
- for (const [name, spec] of Object.entries(deps)) {
112
- if (typeof spec !== 'string')
113
- continue;
114
- let target = null;
115
- if (spec.startsWith('workspace:')) {
116
- // Resolve via node_modules (pnpm links workspace packages there).
117
- const nm = path.join(path.resolve(project), 'node_modules', ...name.split('/'));
118
- if (existsSync(path.join(nm, 'package.json')))
119
- target = nm;
120
- }
121
- else if (spec.startsWith('file:') || spec.startsWith('link:')) {
122
- const rel = spec.replace(/^(file|link):/, '');
123
- target = path.resolve(project, rel);
124
- }
125
- if (!target)
126
- continue;
127
- try {
128
- target = realpathSync(target);
129
- }
130
- catch {
131
- /* keep as-is */
132
- }
133
- const src = path.join(target, 'src');
134
- if (existsSync(src))
135
- roots.push(src);
136
- else if (existsSync(target))
137
- roots.push(target);
138
- }
139
- }
140
- catch {
141
- /* ignore */
142
- }
143
- return roots;
144
- }
145
- /**
146
- * Collect watch roots: project src/locales/documents + compile-graph external sources
147
- * + local link/workspace package roots. Never adds a bare registry node_modules tree.
148
- *
149
- * @param {{ project: string, outDir: string }} opts
150
- * @returns {{ roots: string[], dependencyRoots: string[], applicationRoots: string[] }}
151
- */
152
- export function collectDevWatchRoots(opts) {
153
- const project = path.resolve(opts.project);
154
- const outDir = path.resolve(opts.outDir);
155
- const src = path.join(project, 'src');
156
- const docsRoot = path.join(project, 'documents');
157
- const localesRoot = path.join(project, 'locales');
158
- const designsRoot = path.join(project, 'designs');
159
- /** @type {string[]} */
160
- const applicationRoots = [];
161
- if (existsSync(src))
162
- applicationRoots.push(src);
163
- if (existsSync(docsRoot))
164
- applicationRoots.push(docsRoot);
165
- if (existsSync(localesRoot))
166
- applicationRoots.push(localesRoot);
167
- if (existsSync(designsRoot))
168
- applicationRoots.push(designsRoot);
169
- /** @type {Set<string>} */
170
- const depSet = new Set();
171
- const depJson = path.join(outDir, 'vmz-deployment.json');
172
- if (existsSync(depJson)) {
173
- try {
174
- const dep = JSON.parse(readFileSync(depJson, 'utf8'));
175
- for (const unit of dep.units || []) {
176
- const source = unit?.source;
177
- if (typeof source !== 'string' || !source)
178
- continue;
179
- let abs = path.resolve(source);
180
- try {
181
- abs = realpathSync(abs);
182
- }
183
- catch {
184
- /* keep */
185
- }
186
- const underProject = abs === project || abs.startsWith(project + path.sep) || abs.startsWith(project + '/');
187
- if (underProject)
188
- continue;
189
- const root = watchRootForSourceFile(abs);
190
- if (root)
191
- depSet.add(path.resolve(root));
192
- }
193
- }
194
- catch {
195
- /* ignore corrupt deployment */
196
- }
197
- }
198
- for (const r of localLinkDependencyRoots(project)) {
199
- depSet.add(path.resolve(r));
200
- }
201
- // Drop dependency roots that are already under an application root.
202
- const dependencyRoots = [...depSet].filter((r) => {
203
- return !applicationRoots.some((app) => r === app || r.startsWith(app + path.sep) || r.startsWith(app + '/'));
204
- });
205
- const roots = [...applicationRoots];
206
- for (const r of dependencyRoots) {
207
- if (!roots.includes(r))
208
- roots.push(r);
209
- }
210
- return { roots, dependencyRoots, applicationRoots };
211
- }
212
- /**
213
- * Classify which watch bucket a root belongs to.
214
- * @param {string} root
215
- * @param {{
216
- * src: string,
217
- * docsRoot: string,
218
- * localesRoot: string,
219
- * designsRoot: string,
220
- * dependencyRoots: string[],
221
- * }} ctx
222
- * @returns {'src' | 'locales' | 'docs' | 'designs' | 'dep' | 'other'}
223
- */
224
- export function classifyWatchRoot(root, ctx) {
225
- if (root === ctx.src)
226
- return 'src';
227
- if (root === ctx.localesRoot)
228
- return 'locales';
229
- if (root === ctx.docsRoot)
230
- return 'docs';
231
- if (root === ctx.designsRoot)
232
- return 'designs';
233
- if ((ctx.dependencyRoots || []).includes(root))
234
- return 'dep';
235
- return 'other';
236
- }
237
- /**
238
- * Classify whether a changed file lives under a dependency watch root (not app src).
239
- * @param {string} file
240
- * @param {string[]} dependencyRoots
241
- */
242
- export function isDependencyPath(file, dependencyRoots) {
243
- const abs = path.resolve(file);
244
- return (dependencyRoots || []).some((r) => abs === r || abs.startsWith(r + path.sep) || abs.startsWith(r + '/'));
245
- }
@@ -1,20 +0,0 @@
1
- /**
2
- * Integrated DocumentMount — compile host chrome via DocumentLayout + createRenderHost.
3
- * Replaces the removed regex template lowering in document-host-chrome.ts.
4
- */
5
- /** @param {string} distDir */
6
- export declare function resolveDocumentLayoutChunkId(distDir: any): string;
7
- /**
8
- * @param {string} distDir
9
- */
10
- export declare function assertIntegratedDistReady(distDir: any): string;
11
- /**
12
- * @param {string} html
13
- */
14
- export declare function assertCompiledShellHtml(html: any): void;
15
- /**
16
- * @param {string} distDir
17
- * @param {string} localeId
18
- * @param {string} slotHtml
19
- */
20
- export declare function renderCompiledDocumentLayout(distDir: any, localeId: any, slotHtml: any): Promise<any>;
@@ -1,87 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Integrated DocumentMount — compile host chrome via DocumentLayout + createRenderHost.
4
- * Replaces the removed regex template lowering in document-host-chrome.ts.
5
- */
6
- import fs from 'node:fs';
7
- import path from 'node:path';
8
- import { pathToFileURL } from 'node:url';
9
- import { createRenderHost } from '@vmz/core/render-host';
10
- /** @param {string} distDir */
11
- export function resolveDocumentLayoutChunkId(distDir) {
12
- for (const chunkId of ['layouts/DocumentLayout', 'components/DocumentLayout']) {
13
- if (fs.existsSync(path.join(distDir, `${chunkId}.client.js`)))
14
- return chunkId;
15
- }
16
- return null;
17
- }
18
- /**
19
- * @param {string} distDir
20
- */
21
- export function assertIntegratedDistReady(distDir) {
22
- const dom = path.join(distDir, 'vmz-dom.js');
23
- if (!fs.existsSync(dom)) {
24
- throw new Error('integrated document mount requires vmz build output (vmz-dom.js in app dist). Run `vmz build` before document emit.');
25
- }
26
- const chunkId = resolveDocumentLayoutChunkId(distDir);
27
- if (!chunkId) {
28
- throw new Error('integrated document mount requires compiled DocumentLayout (add src/layouts/DocumentLayout.vmz and rebuild)');
29
- }
30
- return chunkId;
31
- }
32
- /**
33
- * @param {string} html
34
- */
35
- export function assertCompiledShellHtml(html) {
36
- const header = html.match(/<header[^>]*data-vmz-fixture="site-header"[\s\S]*?<\/header>/i)?.[0] ?? '';
37
- const footer = html.match(/<footer[^>]*data-vmz-fixture="site-footer"[\s\S]*?<\/footer>/i)?.[0] ?? '';
38
- for (const part of [header, footer]) {
39
- const leak = part.match(/\{[A-Za-z][A-Za-z0-9]*\}/);
40
- if (leak) {
41
- throw new Error(`document layout SSR leaked binding placeholder ${leak[0]}`);
42
- }
43
- if (/<(?:Link|Button|Icon)\b/.test(part)) {
44
- throw new Error('document layout SSR leaked uncompiled VMZ component tag in chrome');
45
- }
46
- }
47
- if (!header || !footer) {
48
- throw new Error('document layout SSR missing compiled SiteHeader/SiteFooter fixtures');
49
- }
50
- }
51
- /**
52
- * @param {string} distDir
53
- * @param {string} chunkId
54
- */
55
- async function loadCtor(distDir, chunkId) {
56
- const href = pathToFileURL(path.join(distDir, `${chunkId}.client.js`)).href;
57
- const mod = await import(`${href}?t=${Date.now()}`);
58
- return mod.default;
59
- }
60
- /**
61
- * @param {string} distDir
62
- * @param {string} localeId
63
- * @param {string} slotHtml
64
- */
65
- export async function renderCompiledDocumentLayout(distDir, localeId, slotHtml) {
66
- const chunkId = assertIntegratedDistReady(distDir);
67
- const prevHint = globalThis.__vmzLocaleIdHint;
68
- globalThis.__vmzLocaleIdHint = localeId;
69
- try {
70
- if (typeof globalThis.document !== 'undefined' && globalThis.document?.documentElement) {
71
- globalThis.document.documentElement.setAttribute('data-locale', localeId);
72
- globalThis.document.documentElement.setAttribute('lang', localeId);
73
- }
74
- const host = await createRenderHost(distDir, { strictDeployment: true, preload: 'none' });
75
- await host.ensureComponents([chunkId]);
76
- const Layout = await loadCtor(distDir, chunkId);
77
- const html = await host.renderToString(Layout, {}, { slotHtml });
78
- assertCompiledShellHtml(html);
79
- return html;
80
- }
81
- finally {
82
- if (prevHint === undefined)
83
- delete globalThis.__vmzLocaleIdHint;
84
- else
85
- globalThis.__vmzLocaleIdHint = prevHint;
86
- }
87
- }
@@ -1,13 +0,0 @@
1
- /**
2
- * Project locale routing config for document mount (locales/locales.json5).
3
- */
4
- /**
5
- * @param {string} projectRoot
6
- * @returns {{ strategy?: string, defaultLocale?: string } | null}
7
- */
8
- export declare function loadLocalesRouting(projectRoot: any): any;
9
- /**
10
- * @param {string} routeBase
11
- * @param {string} pageKey
12
- */
13
- export declare function docsRouteNone(routeBase: any, pageKey: any): string;
@@ -1,40 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Project locale routing config for document mount (locales/locales.json5).
4
- */
5
- import fs from 'node:fs';
6
- import path from 'node:path';
7
- /**
8
- * @param {string} projectRoot
9
- * @returns {{ strategy?: string, defaultLocale?: string } | null}
10
- */
11
- export function loadLocalesRouting(projectRoot) {
12
- const p = path.join(projectRoot, 'locales', 'locales.json5');
13
- if (!fs.existsSync(p))
14
- return null;
15
- try {
16
- const raw = fs.readFileSync(p, 'utf8');
17
- let s = raw.replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
18
- const m = s.match(/routing\s*:\s*\{([\s\S]*?)\}/);
19
- if (!m)
20
- return null;
21
- let block = `{${m[1]}}`;
22
- block = block.replace(/([,{]\s*)([A-Za-z_][A-Za-z0-9_]*)\s*:/g, '$1"$2":');
23
- block = block.replace(/'([^'\\]*(?:\\.[^'\\]*)*)'/g, (_, inner) => JSON.stringify(inner));
24
- block = block.replace(/,\s*([}\]])/g, '$1');
25
- return JSON.parse(block);
26
- }
27
- catch {
28
- return null;
29
- }
30
- }
31
- /**
32
- * @param {string} routeBase
33
- * @param {string} pageKey
34
- */
35
- export function docsRouteNone(routeBase, pageKey) {
36
- const base = String(routeBase || '/').replace(/\/$/, '') || '';
37
- const key = pageKey === 'index' ? '' : pageKey.replace(/\\/g, '/');
38
- const parts = [base.replace(/^\//, ''), key].filter((p) => p !== '');
39
- return '/' + (parts.length ? parts.join('/') : '');
40
- }
@@ -1,9 +0,0 @@
1
- /**
2
- * Load the N-API addon or throw (production printers must not fall back to TS).
3
- * @returns {any}
4
- */
5
- export declare function requireNativeAddon(): any;
6
- /**
7
- * @returns {any | null}
8
- */
9
- export declare function tryLoadNativeAddon(): any;
@@ -1,84 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Load the N-API `.node` addon without importing `index.js`
4
- * (avoids cycles with modules re-exported from the package entry).
5
- */
6
- import fs from 'node:fs';
7
- import { createRequire } from 'node:module';
8
- import path from 'node:path';
9
- import { fileURLToPath } from 'node:url';
10
- const require = createRequire(import.meta.url);
11
- /** @type {any} */
12
- let cached;
13
- /**
14
- * Load the N-API addon or throw (production printers must not fall back to TS).
15
- * @returns {any}
16
- */
17
- export function requireNativeAddon() {
18
- const native = tryLoadNativeAddon();
19
- if (!native) {
20
- throw new Error('vmz native addon missing — run `pnpm napi:build` (CodeGenerators live in vmz-generator via N-API)');
21
- }
22
- return native;
23
- }
24
- /**
25
- * @returns {any | null}
26
- */
27
- export function tryLoadNativeAddon() {
28
- if (cached !== undefined)
29
- return cached;
30
- try {
31
- const envPath = (typeof process.env.VMZ_NATIVE_NODE === 'string' && process.env.VMZ_NATIVE_NODE.trim()) || '';
32
- if (envPath) {
33
- cached = require(path.resolve(envPath));
34
- return cached;
35
- }
36
- const { platform, arch } = process;
37
- let triple = `${platform}-${arch}`;
38
- if (platform === 'win32' && arch === 'x64')
39
- triple = 'win32-x64-msvc';
40
- else if (platform === 'win32' && arch === 'arm64')
41
- triple = 'win32-arm64-msvc';
42
- else if (platform === 'darwin' && arch === 'arm64')
43
- triple = 'darwin-arm64';
44
- else if (platform === 'darwin' && arch === 'x64')
45
- triple = 'darwin-x64';
46
- else if (platform === 'linux' && arch === 'x64')
47
- triple = 'linux-x64-gnu';
48
- else if (platform === 'linux' && arch === 'arm64')
49
- triple = 'linux-arm64-gnu';
50
- const short = triple === 'win32-x64-msvc'
51
- ? 'win32-x64'
52
- : triple === 'win32-arm64-msvc'
53
- ? 'win32-arm64'
54
- : triple === 'linux-x64-gnu'
55
- ? 'linux-x64'
56
- : triple === 'linux-arm64-gnu'
57
- ? 'linux-arm64'
58
- : triple;
59
- const pkgRoot = path.join(path.dirname(fileURLToPath(import.meta.url)), '..');
60
- const name = `@vmz/vmz-${short}`;
61
- /** @type {string[]} */
62
- const candidates = [];
63
- try {
64
- const resolved = require.resolve(`${name}/package.json`);
65
- const dir = path.dirname(resolved);
66
- candidates.push(path.join(dir, `vmz.${triple}.node`), path.join(dir, 'vmz.node'));
67
- }
68
- catch {
69
- /* optional */
70
- }
71
- candidates.push(path.join(pkgRoot, 'node_modules', name, `vmz.${triple}.node`), path.join(pkgRoot, 'node_modules', name, 'vmz.node'));
72
- for (const p of candidates) {
73
- if (fs.existsSync(p)) {
74
- cached = require(p);
75
- return cached;
76
- }
77
- }
78
- cached = null;
79
- }
80
- catch {
81
- cached = null;
82
- }
83
- return cached;
84
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * Pretty-print a value through `vmz-generator` (N-API).
3
- * @param {unknown} value
4
- * @returns {string} Pretty JSON without trailing newline.
5
- */
6
- export declare function generatePrettyJson(value: any): any;
7
- /**
8
- * Write a pretty JSON artifact with a trailing newline.
9
- * @param {string} filePath
10
- * @param {unknown} value
11
- */
12
- export declare function writePrettyJsonFile(filePath: any, value: any): void;
13
- /**
14
- * CLI `--json` helper: write to a path when `target` is a string, else stdout.
15
- * @param {string | boolean | undefined} target
16
- * @param {unknown} value
17
- * @param {{ logWrote?: (path: string) => void }} [opts]
18
- */
19
- export declare function emitPrettyJson(target: any, value: any, opts?: {}): void;
@@ -1,43 +0,0 @@
1
- // @ts-nocheck
2
- /**
3
- * Production JSON artifact printer — always via N-API JsonCodeGenerator.
4
- * Do not use `JSON.stringify(x, null, 2)` for on-disk artifacts.
5
- */
6
- import fs from 'node:fs';
7
- import { requireNativeAddon } from './native-addon.js';
8
- /**
9
- * Pretty-print a value through `vmz-generator` (N-API).
10
- * @param {unknown} value
11
- * @returns {string} Pretty JSON without trailing newline.
12
- */
13
- export function generatePrettyJson(value) {
14
- const native = requireNativeAddon();
15
- if (typeof native.generatePrettyJson !== 'function') {
16
- throw new Error('vmz native addon missing generatePrettyJson — rebuild with `pnpm napi:build`');
17
- }
18
- return native.generatePrettyJson(JSON.stringify(value));
19
- }
20
- /**
21
- * Write a pretty JSON artifact with a trailing newline.
22
- * @param {string} filePath
23
- * @param {unknown} value
24
- */
25
- export function writePrettyJsonFile(filePath, value) {
26
- fs.writeFileSync(filePath, `${generatePrettyJson(value)}\n`, 'utf8');
27
- }
28
- /**
29
- * CLI `--json` helper: write to a path when `target` is a string, else stdout.
30
- * @param {string | boolean | undefined} target
31
- * @param {unknown} value
32
- * @param {{ logWrote?: (path: string) => void }} [opts]
33
- */
34
- export function emitPrettyJson(target, value, opts = {}) {
35
- const text = generatePrettyJson(value);
36
- if (typeof target === 'string') {
37
- fs.writeFileSync(target, `${text}\n`, 'utf8');
38
- if (typeof opts.logWrote === 'function')
39
- opts.logWrote(target);
40
- return;
41
- }
42
- console.log(text);
43
- }
@@ -1,35 +0,0 @@
1
- /**
2
- * Browser HTTP path projection from Route Graph / `vmz-deployment.json`.
3
- * Mini pack ignores this and lowers RouteId → chunk id → page stem.
4
- */
5
- export declare function isRouteBoundaryStem(stem: string): boolean;
6
- export declare function isRouteGroupDir(seg: string): boolean;
7
- /**
8
- * File-route fallback (`pages/home` → `/home`, `pages/index` → `/`).
9
- * Used only when a deployment unit has no `pathPattern`.
10
- */
11
- export declare function filePathPatternFromChunk(chunkId: string): string;
12
- export type DeploymentPageUnit = {
13
- kind?: string;
14
- chunkId?: string;
15
- clientEntry?: string;
16
- programIr?: string;
17
- pathPattern?: string;
18
- routeId?: string;
19
- };
20
- /** Canonical Browser HTTP pattern for a page unit. Mini must not read this. */
21
- export declare function unitBrowserPathPattern(unit: DeploymentPageUnit | null | undefined): string;
22
- export type PathSeg = {
23
- kind: 'static';
24
- value: string;
25
- } | {
26
- kind: 'param';
27
- name: string;
28
- } | {
29
- kind: 'catch';
30
- name: string;
31
- };
32
- export declare function parsePathPattern(pattern: string): PathSeg[];
33
- export declare function listPublicPageUnits(deployment: {
34
- units?: DeploymentPageUnit[];
35
- } | null | undefined): DeploymentPageUnit[];
@@ -1,77 +0,0 @@
1
- /**
2
- * Browser HTTP path projection from Route Graph / `vmz-deployment.json`.
3
- * Mini pack ignores this and lowers RouteId → chunk id → page stem.
4
- */
5
- export function isRouteBoundaryStem(stem) {
6
- return stem === 'Layout' || stem === 'Loading' || stem === 'Error' || stem === 'NotFound';
7
- }
8
- export function isRouteGroupDir(seg) {
9
- return typeof seg === 'string' && seg.startsWith('(') && seg.endsWith(')') && seg.length > 2;
10
- }
11
- /**
12
- * File-route fallback (`pages/home` → `/home`, `pages/index` → `/`).
13
- * Used only when a deployment unit has no `pathPattern`.
14
- */
15
- export function filePathPatternFromChunk(chunkId) {
16
- const rel = String(chunkId || '').replace(/^pages\//, '');
17
- const parts = rel.split('/').filter(Boolean);
18
- const segs = [];
19
- for (let i = 0; i < parts.length; i++) {
20
- const p = parts[i];
21
- if (isRouteGroupDir(p))
22
- continue;
23
- if (p === 'index' && i === parts.length - 1)
24
- continue;
25
- if (isRouteBoundaryStem(p))
26
- continue;
27
- segs.push(p);
28
- }
29
- return segs.length ? `/${segs.join('/')}` : '/';
30
- }
31
- /** Canonical Browser HTTP pattern for a page unit. Mini must not read this. */
32
- export function unitBrowserPathPattern(unit) {
33
- const explicit = String(unit?.pathPattern || '').trim();
34
- if (explicit)
35
- return explicit.startsWith('/') ? explicit : `/${explicit}`;
36
- return filePathPatternFromChunk(String(unit?.chunkId || ''));
37
- }
38
- export function parsePathPattern(pattern) {
39
- const raw = String(pattern || '').trim();
40
- if (!raw || raw === '/')
41
- return [];
42
- const parts = raw.replace(/^\/+/, '').replace(/\/+$/, '').split('/').filter(Boolean);
43
- const segs = [];
44
- for (const p of parts) {
45
- if (isRouteGroupDir(p))
46
- continue;
47
- segs.push(parsePathSegment(p));
48
- }
49
- return segs;
50
- }
51
- function parsePathSegment(p) {
52
- const catchAll = /^\[\.\.\.([^\]]+)\]$/.exec(p);
53
- const star = /^\*([A-Za-z_][\w]*)$/.exec(p);
54
- const param = /^\[([^\]]+)\]$/.exec(p);
55
- const colon = /^:([A-Za-z_][\w]*)$/.exec(p);
56
- if (catchAll)
57
- return { kind: 'catch', name: catchAll[1] };
58
- if (star)
59
- return { kind: 'catch', name: star[1] };
60
- if (param)
61
- return { kind: 'param', name: param[1] };
62
- if (colon)
63
- return { kind: 'param', name: colon[1] };
64
- return { kind: 'static', value: p.toLowerCase() };
65
- }
66
- export function listPublicPageUnits(deployment) {
67
- const units = Array.isArray(deployment?.units) ? deployment.units : [];
68
- return units.filter((u) => {
69
- if (u?.kind !== 'page')
70
- return false;
71
- const chunkId = String(u.chunkId || '').replace(/\\/g, '/');
72
- if (!chunkId.startsWith('pages/'))
73
- return false;
74
- const stem = chunkId.split('/').pop() || '';
75
- return !isRouteBoundaryStem(stem);
76
- });
77
- }