@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
@@ -9,27 +9,21 @@ import { resolveDocumentDesignsCss } from './document-designs.js';
9
9
  import { enrichDocumentContent, pageHtmlRel } from './document-enrich.js';
10
10
  import { enrichDocumentEvidence } from './document-evidence.js';
11
11
  import { artifactHrefFromHtml, buildDocumentIslands, buildDocumentSearch, collectFenceBodies, renderIslandShellsHtml, } from './document-interactive.js';
12
- import { assertIntegratedDistReady, renderCompiledDocumentLayout } from './document-layout-render.js';
13
12
  import { resolveMarkdownEngine } from './document-markdown.js';
14
- import { loadLocalesRouting } from './document-routing-config.js';
15
13
  import { DOCUMENT_VIEW_SCHEMA } from './document-schema.js';
16
14
  import { createWorkspace } from './index.js';
17
- import { requireNativeAddon } from './native-addon.js';
18
- import { writePrettyJsonFile } from './pretty-json.js';
19
15
  /**
20
- * @param {{ projectRoot: string, outDir?: string, appDistDir?: string, strict?: boolean, engines?: { markdown?: string } }} opts
16
+ * @param {{ projectRoot: string, outDir?: string, strict?: boolean, engines?: { markdown?: string } }} opts
21
17
  */
22
18
  export async function buildDocuments(opts) {
23
19
  const projectRoot = path.resolve(opts.projectRoot);
24
20
  const outDir = path.resolve(opts.outDir || path.join(projectRoot, 'dist', 'documents'));
25
21
  const strict = Boolean(opts.strict);
26
22
  const manifest = checkDocuments({ projectRoot, strict });
27
- const routing = loadLocalesRouting(projectRoot) || { strategy: 'prefix' };
28
23
  const engine = await resolveMarkdownEngine({ engines: opts.engines, projectRoot });
29
24
  const enriched = enrichDocumentContent(manifest, {
30
25
  analyzeMarkdown: engine.analyzeMarkdown,
31
26
  projectRoot,
32
- routing,
33
27
  });
34
28
  manifest.diagnostics = enriched.diagnostics;
35
29
  const evidence = await enrichDocumentEvidence(manifest, {
@@ -64,12 +58,8 @@ export async function buildDocuments(opts) {
64
58
  });
65
59
  manifest.search = search;
66
60
  manifest.islands = islands;
67
- const integratedMount = (manifest.mounts || []).some((m) => m.mode === 'integrated');
68
- const appDistDir = resolveAppDistDir(opts, outDir);
69
- const useCompiledShell = Boolean(integratedMount && appDistDir);
70
- if (integratedMount && appDistDir) {
71
- assertIntegratedDistReady(appDistDir);
72
- }
61
+ const hostChrome = resolveHostSiteChrome(projectRoot);
62
+ const useHostShell = Boolean(hostChrome) && (manifest.mounts || []).some((m) => m.mode === 'integrated');
73
63
  fs.mkdirSync(outDir, { recursive: true });
74
64
  const designs = resolveDocumentDesignsCss(projectRoot);
75
65
  /** @type {string | null} */
@@ -100,20 +90,6 @@ export async function buildDocuments(opts) {
100
90
  pageKey: page.identity.pageKey,
101
91
  locale: page.identity.locale,
102
92
  });
103
- const slotHtml = buildDocumentSlotHtml({
104
- nav,
105
- bodyHtml: info.html,
106
- headings: info.headings,
107
- htmlRel,
108
- route: info.route,
109
- routing,
110
- searchShellHtml: shells.searchHtml,
111
- playgroundShellHtml: shells.playgroundHtml,
112
- });
113
- let compiledLayoutHtml = null;
114
- if (useCompiledShell) {
115
- compiledLayoutHtml = await renderCompiledDocumentLayout(appDistDir, page.identity.locale, slotHtml);
116
- }
117
93
  const view = {
118
94
  schema: DOCUMENT_VIEW_SCHEMA,
119
95
  pageKey: page.identity.pageKey,
@@ -127,7 +103,7 @@ export async function buildDocuments(opts) {
127
103
  designsCss: designsHref,
128
104
  noJsReadable: true,
129
105
  hydrate: 'island-only',
130
- hostShell: useCompiledShell ? 'compiled-layout' : false,
106
+ hostShell: useHostShell,
131
107
  islands: ['DocumentSearch'].concat((islands.islands || [])
132
108
  .filter((isl) => isl.kind === 'playground' &&
133
109
  isl.fence?.locale === page.identity.locale &&
@@ -137,7 +113,7 @@ export async function buildDocuments(opts) {
137
113
  const viewRel = path.posix.join('views', page.identity.locale, `${page.identity.pageKey === 'index' ? 'index' : page.identity.pageKey}.view.json`);
138
114
  const viewAbs = path.join(outDir, viewRel);
139
115
  fs.mkdirSync(path.dirname(viewAbs), { recursive: true });
140
- writePrettyJsonFile(viewAbs, view);
116
+ fs.writeFileSync(viewAbs, JSON.stringify(view, null, 2) + '\n', 'utf8');
141
117
  const html = renderStaticHtml({
142
118
  title: info.title,
143
119
  locale: page.identity.locale,
@@ -149,9 +125,7 @@ export async function buildDocuments(opts) {
149
125
  htmlRel,
150
126
  searchShellHtml: shells.searchHtml,
151
127
  playgroundShellHtml: shells.playgroundHtml,
152
- routing,
153
- compiledLayoutHtml,
154
- useCompiledShell,
128
+ hostChrome: useHostShell ? hostChrome : null,
155
129
  });
156
130
  fs.writeFileSync(htmlAbs, html, 'utf8');
157
131
  written.push({ route: info.route, htmlPath: htmlRel, viewPath: viewRel });
@@ -167,38 +141,39 @@ export async function buildDocuments(opts) {
167
141
  outDir: path.relative(projectRoot, outDir).replace(/\\/g, '/') || '.',
168
142
  designs: designs.source,
169
143
  designsCss: designsHref,
170
- hostShell: useCompiledShell ? 'compiled-layout' : 'standalone',
171
144
  pages: written,
172
145
  evidence: 'document.evidence.json',
173
146
  search: 'document.search.json',
174
147
  islands: 'document.islands.json',
175
148
  },
176
149
  };
177
- writePrettyJsonFile(path.join(outDir, 'document.manifest.json'), manifestOut);
178
- writePrettyJsonFile(path.join(outDir, 'document.evidence.json'), evidence.evidence);
179
- writePrettyJsonFile(path.join(outDir, 'document.search.json'), search);
180
- writePrettyJsonFile(path.join(outDir, 'document.islands.json'), islands);
150
+ fs.writeFileSync(path.join(outDir, 'document.manifest.json'), JSON.stringify(manifestOut, null, 2) + '\n', 'utf8');
151
+ fs.writeFileSync(path.join(outDir, 'document.evidence.json'), JSON.stringify(evidence.evidence, null, 2) + '\n', 'utf8');
152
+ fs.writeFileSync(path.join(outDir, 'document.search.json'), JSON.stringify(search, null, 2) + '\n', 'utf8');
153
+ fs.writeFileSync(path.join(outDir, 'document.islands.json'), JSON.stringify(islands, null, 2) + '\n', 'utf8');
181
154
  return { ok: true, manifest: manifestOut, outDir, pages: written, search, islands };
182
155
  }
183
156
  /**
184
- * @param {{ appDistDir?: string }} opts
185
- * @param {string} outDir
186
- */
187
- function resolveAppDistDir(opts, outDir) {
188
- if (opts.appDistDir) {
189
- const p = path.resolve(opts.appDistDir);
190
- return fs.existsSync(path.join(p, 'vmz-dom.js')) ? p : null;
191
- }
192
- return fs.existsSync(path.join(outDir, 'vmz-dom.js')) ? outDir : null;
193
- }
194
- /**
195
- * Document main column + sidebar (injected into DocumentLayout slot).
157
+ * No-JS readable static HTML: nav + main landmarks, Island shells without scripts.
158
+ * Integrated mounts reuse host SiteHeader/SiteFooter templates when present.
196
159
  */
197
- function buildDocumentSlotHtml({ nav, bodyHtml, headings, htmlRel, route, routing, searchShellHtml = '', playgroundShellHtml = '' }) {
160
+ function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, designsHref, htmlRel, searchShellHtml = '', playgroundShellHtml = '', hostChrome = null, }) {
198
161
  const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
162
+ const depth = htmlRel.split('/').length - 1;
163
+ const prefix = depth > 0 ? '../'.repeat(depth) : './';
164
+ /** @type {string[]} */
165
+ const cssHrefs = [];
166
+ if (hostChrome) {
167
+ // Integrated documents are served with pretty directory URLs. Root
168
+ // absolute assets remain correct for both emitted files and rewrites.
169
+ cssHrefs.push('/vmz.css');
170
+ }
171
+ if (designsHref)
172
+ cssHrefs.push(hostChrome ? `/${designsHref}` : prefix + designsHref);
173
+ const cssLink = cssHrefs.map((href) => ` <link rel="stylesheet" href="${esc(href)}" />`).join('\n') + (cssHrefs.length ? '\n' : '');
199
174
  const navItems = nav
200
175
  .map((n) => {
201
- const href = routing.strategy === 'none' || routing.strategy === 'domain' ? n.href : relativeHref(htmlRel, n.href, route);
176
+ const href = relativeHref(htmlRel, n.href, route);
202
177
  const current = n.href === route ? ' aria-current="page"' : '';
203
178
  return ` <li><a href="${esc(href)}"${current}>${esc(n.title)}</a></li>`;
204
179
  })
@@ -213,7 +188,21 @@ function buildDocumentSlotHtml({ nav, bodyHtml, headings, htmlRel, route, routin
213
188
  ${navItems}
214
189
  </ul>
215
190
  </nav>`;
216
- return ` <aside class="doc-sidebar">
191
+ if (hostChrome) {
192
+ const header = hostChrome.header.replace(/(<a\s+href="\/d\/?")([^>]*>文档<\/a>)/, '$1 aria-current="page"$2');
193
+ return `<!DOCTYPE html>
194
+ <html lang="${esc(locale)}">
195
+ <head>
196
+ <meta charset="utf-8" />
197
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
198
+ <title>${esc(title)}</title>
199
+ ${cssLink}</head>
200
+ <body data-vmz-hydrate="island-only">
201
+ <div class="site site--docs">
202
+ <a class="skip-link" href="#main">Skip to content</a>
203
+ ${header}
204
+ <div class="doc-body">
205
+ <aside class="doc-sidebar">
217
206
  ${docsNav}
218
207
  ${searchShellHtml}
219
208
  </aside>
@@ -222,65 +211,54 @@ ${searchShellHtml}
222
211
  ${bodyHtml}
223
212
  ${playgroundShellHtml}
224
213
  </main>
225
- </div>`;
226
- }
227
- /**
228
- * No-JS readable static HTML: nav + main landmarks, Island shells without scripts.
229
- * Integrated mounts wrap content in compiled DocumentLayout (SiteHeader/SiteFooter SSR).
230
- */
231
- function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, designsHref, htmlRel, searchShellHtml = '', playgroundShellHtml = '', routing = { strategy: 'prefix' }, compiledLayoutHtml, useCompiledShell = false, }) {
232
- const esc = (s) => String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
233
- const depth = htmlRel.split('/').length - 1;
234
- const prefix = depth > 0 ? '../'.repeat(depth) : './';
235
- /** @type {string[]} */
236
- const cssHrefs = [];
237
- if (useCompiledShell) {
238
- cssHrefs.push('/vmz.css');
239
- }
240
- if (designsHref)
241
- cssHrefs.push(useCompiledShell ? `/${designsHref}` : prefix + designsHref);
242
- /** @type {string} */
243
- let bodyInner;
244
- if (useCompiledShell) {
245
- bodyInner = compiledLayoutHtml;
214
+ </div>
215
+ </div>
216
+ ${hostChrome.footer}
217
+ </div>
218
+ </body>
219
+ </html>
220
+ `;
246
221
  }
247
- else {
248
- const navItems = nav
249
- .map((n) => {
250
- const href = routing.strategy === 'none' || routing.strategy === 'domain' ? n.href : relativeHref(htmlRel, n.href, route);
251
- const current = n.href === route ? ' aria-current="page"' : '';
252
- return ` <li><a href="${esc(href)}"${current}>${esc(n.title)}</a></li>`;
253
- })
254
- .join('\n');
255
- const toc = headings.length > 1
256
- ? `<nav aria-label="On this page" class="toc">\n <ol>\n${headings
257
- .map((h) => ` <li class="h${h.level}"><a href="#${esc(h.id)}">${esc(h.text)}</a></li>`)
258
- .join('\n')}\n </ol>\n </nav>\n`
259
- : '';
260
- bodyInner = ` <a class="skip-link" href="#main">Skip to content</a>
261
- <nav aria-label="Documents" class="doc-subnav">
262
- <ul>
263
- ${navItems}
264
- </ul>
265
- </nav>
222
+ return `<!DOCTYPE html>
223
+ <html lang="${esc(locale)}">
224
+ <head>
225
+ <meta charset="utf-8" />
226
+ <meta name="viewport" content="width=device-width, initial-scale=1" />
227
+ <title>${esc(title)}</title>
228
+ ${cssLink}</head>
229
+ <body data-vmz-hydrate="island-only">
230
+ <a class="skip-link" href="#main">Skip to content</a>
231
+ ${docsNav}
266
232
  ${searchShellHtml}
267
233
  ${toc}<main id="main">
268
234
  ${bodyHtml}
269
235
  ${playgroundShellHtml}
270
236
  </main>
237
+ </body>
238
+ </html>
271
239
  `;
272
- }
273
- const native = requireNativeAddon();
274
- if (typeof native.generateHtmlShell !== 'function') {
275
- throw new Error('vmz native addon missing generateHtmlShell — rebuild with `pnpm napi:build`');
276
- }
277
- return native.generateHtmlShell({
278
- title,
279
- lang: locale,
280
- cssHrefs,
281
- bodyHtml: bodyInner,
282
- bodyAttrs: ['data-vmz-hydrate', 'island-only'],
283
- });
240
+ }
241
+ /**
242
+ * Integrated DocumentMount: reuse host SiteHeader / SiteFooter .vmz templates.
243
+ * @param {string} projectRoot
244
+ * @returns {{ header: string, footer: string } | null}
245
+ */
246
+ function resolveHostSiteChrome(projectRoot) {
247
+ const headerPath = path.join(projectRoot, 'src', 'components', 'SiteHeader.vmz');
248
+ const footerPath = path.join(projectRoot, 'src', 'components', 'SiteFooter.vmz');
249
+ if (!fs.existsSync(headerPath) || !fs.existsSync(footerPath))
250
+ return null;
251
+ const header = extractVmzTemplateHtml(headerPath);
252
+ const footer = extractVmzTemplateHtml(footerPath);
253
+ if (!header || !footer)
254
+ return null;
255
+ return { header, footer };
256
+ }
257
+ /** @param {string} filePath */
258
+ function extractVmzTemplateHtml(filePath) {
259
+ const src = fs.readFileSync(filePath, 'utf8');
260
+ const m = src.match(/<template>([\s\S]*?)<\/template>/);
261
+ return m ? m[1].trim() : '';
284
262
  }
285
263
  function relativeHref(fromHtmlRel, toRoute, _fromRoute) {
286
264
  const toParts = String(toRoute).replace(/^\//, '').split('/').filter(Boolean);
@@ -2,6 +2,7 @@
2
2
  /**
3
3
  * `vmz document` / `vmz docs` CLI .
4
4
  */
5
+ import fs from 'node:fs';
5
6
  import path from 'node:path';
6
7
  import { buildDocuments } from './document-build.js';
7
8
  import { checkDocuments, manifestHasErrors } from './document-check.js';
@@ -11,7 +12,6 @@ import { resolveMarkdownEngine } from './document-markdown.js';
11
12
  import { createWorkspace } from './index.js';
12
13
  import { log } from './log.js';
13
14
  import { parseArgs } from './cli.js';
14
- import { emitPrettyJson } from './pretty-json.js';
15
15
  function printDocumentHelp() {
16
16
  console.log(`vmz document — project /documents domain
17
17
 
@@ -95,7 +95,14 @@ async function cmdDocumentCheck(args) {
95
95
  }
96
96
  const jsonOut = args.json;
97
97
  if (jsonOut) {
98
- emitPrettyJson(jsonOut, manifest, { logWrote: (p) => log.info(`wrote ${p}`) });
98
+ const text = JSON.stringify(manifest, null, 2);
99
+ if (typeof jsonOut === 'string') {
100
+ fs.writeFileSync(jsonOut, text + '\n', 'utf8');
101
+ log.info(`wrote ${jsonOut}`);
102
+ }
103
+ else {
104
+ console.log(text);
105
+ }
99
106
  }
100
107
  else {
101
108
  for (const d of manifest.diagnostics) {
@@ -2,9 +2,8 @@
2
2
  * @param {string} routeBase e.g. /docs
3
3
  * @param {string} locale
4
4
  * @param {string} pageKey
5
- * @param {{ strategy?: string }} [routing]
6
5
  */
7
- export declare function pageRoute(routeBase: any, locale: any, pageKey: any, routing?: {}): string;
6
+ export declare function pageRoute(routeBase: any, locale: any, pageKey: any): string;
8
7
  /**
9
8
  * Static file path relative to out dir (posix).
10
9
  * @param {string} routeBase
@@ -9,16 +9,10 @@ import { DIAG } from './document-schema.js';
9
9
  * @param {string} routeBase e.g. /docs
10
10
  * @param {string} locale
11
11
  * @param {string} pageKey
12
- * @param {{ strategy?: string }} [routing]
13
12
  */
14
- export function pageRoute(routeBase, locale, pageKey, routing = {}) {
13
+ export function pageRoute(routeBase, locale, pageKey) {
15
14
  const base = String(routeBase || '/').replace(/\/$/, '') || '';
16
15
  const key = pageKey === 'index' ? '' : pageKey.replace(/\\/g, '/');
17
- const strategy = routing.strategy || 'prefix';
18
- if (strategy === 'none' || strategy === 'domain') {
19
- const parts = [base.replace(/^\//, ''), key].filter((p) => p !== '');
20
- return '/' + (parts.length ? parts.join('/') : '');
21
- }
22
16
  const parts = [base.replace(/^\//, ''), locale, key].filter((p) => p !== '');
23
17
  return '/' + parts.join('/');
24
18
  }
@@ -41,7 +35,6 @@ export function pageHtmlRel(routeBase, locale, pageKey) {
41
35
  */
42
36
  export function enrichDocumentContent(manifest, ctx) {
43
37
  const routeBase = manifest.mounts?.[0]?.routeBase || '/docs';
44
- const routing = ctx.routing || { strategy: 'prefix' };
45
38
  /** @type {Map<string, { html: string, headings: any[], links: any[], title: string, route: string, anchors: string[] }>} */
46
39
  const byId = new Map();
47
40
  /** @type {import('./document-schema.js').DocumentDiagnostic[]} */
@@ -52,7 +45,7 @@ export function enrichDocumentContent(manifest, ctx) {
52
45
  const abs = path.isAbsolute(page.sourcePath) ? page.sourcePath : path.join(manifest.root, page.sourcePath);
53
46
  const source = fs.existsSync(abs) ? fs.readFileSync(abs, 'utf8') : '';
54
47
  const analyzed = ctx.analyzeMarkdown(source);
55
- const route = pageRoute(routeBase, page.identity.locale, page.identity.pageKey, routing);
48
+ const route = pageRoute(routeBase, page.identity.locale, page.identity.pageKey);
56
49
  const anchors = analyzed.headings.map((h) => h.id);
57
50
  const title = analyzed.headings.find((h) => h.level === 1)?.text || analyzed.headings[0]?.text || page.identity.pageKey;
58
51
  // Duplicate anchors on page
@@ -68,11 +61,7 @@ export function enrichDocumentContent(manifest, ctx) {
68
61
  }
69
62
  seen.add(id);
70
63
  }
71
- const owner = `${page.identity.locale}:${page.identity.pageKey}`;
72
- if (routing.strategy === 'none' || routing.strategy === 'domain') {
73
- routeOwners.set(route, owner);
74
- }
75
- else if (routeOwners.has(route)) {
64
+ if (routeOwners.has(route)) {
76
65
  diagnostics.push({
77
66
  code: DIAG.ROUTE_DUPLICATE,
78
67
  severity: 'error',
@@ -81,7 +70,7 @@ export function enrichDocumentContent(manifest, ctx) {
81
70
  });
82
71
  }
83
72
  else {
84
- routeOwners.set(route, owner);
73
+ routeOwners.set(route, `${page.identity.locale}:${page.identity.pageKey}`);
85
74
  }
86
75
  page.route = route;
87
76
  page.anchors = anchors;
@@ -7,10 +7,7 @@ import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { buildDocuments } from './document-build.js';
9
9
  import { resolveDocumentsRoot } from './document-check.js';
10
- import { loadLocalesRouting } from './document-routing-config.js';
11
- import { pageHtmlRel } from './document-enrich.js';
12
10
  import { log } from './log.js';
13
- import { requireNativeAddon } from './native-addon.js';
14
11
  /**
15
12
  * @param {string} projectRoot
16
13
  */
@@ -33,7 +30,6 @@ export async function buildIntegratedDocuments(opts) {
33
30
  const result = await buildDocuments({
34
31
  projectRoot,
35
32
  outDir,
36
- appDistDir: outDir,
37
33
  strict: Boolean(opts.strict),
38
34
  });
39
35
  if (!result.ok) {
@@ -43,7 +39,7 @@ export async function buildIntegratedDocuments(opts) {
43
39
  }
44
40
  return { ok: false, error: 'document diagnostics', pages: 0 };
45
41
  }
46
- writeMountRootRedirects(result.manifest, outDir, projectRoot);
42
+ writeMountRootRedirects(result.manifest, outDir);
47
43
  log.info(`document mount: pages=${result.pages.length} → ${path.relative(process.cwd(), outDir) || '.'}`);
48
44
  return { ok: true, pages: result.pages.length };
49
45
  }
@@ -54,43 +50,39 @@ export async function buildIntegratedDocuments(opts) {
54
50
  }
55
51
  }
56
52
  /**
57
- * Emit `{routeBase}/index.html` for integrated mounts.
58
- * `routing.strategy: none` → copy default-locale docs index (LocaleId is Host state).
59
- * prefix strategy → redirect HTML to `{routeBase}/{defaultLocale}/`.
53
+ * Emit `{routeBase}/index.html` → defaultLocale landing (for /d/ and /docs/).
60
54
  * @param {import('./document-schema.js').DocumentManifest} manifest
61
55
  * @param {string} outDir
62
- * @param {string} projectRoot
63
56
  */
64
- function writeMountRootRedirects(manifest, outDir, projectRoot) {
57
+ function writeMountRootRedirects(manifest, outDir) {
65
58
  const defaultLocale = manifest.defaultLocale || manifest.locales?.[0];
66
59
  if (!defaultLocale)
67
60
  return;
68
- const routing = loadLocalesRouting(projectRoot) || { strategy: 'prefix' };
69
61
  for (const mount of manifest.mounts || []) {
70
62
  if (!mount?.routeBase || mount.routeBase === '/')
71
63
  continue;
72
64
  const base = String(mount.routeBase).replace(/\/$/, '');
65
+ const target = `${base}/${defaultLocale}/`;
73
66
  const relDir = base.replace(/^\//, '');
74
67
  const abs = path.join(outDir, relDir, 'index.html');
75
68
  fs.mkdirSync(path.dirname(abs), { recursive: true });
76
- if (routing.strategy === 'none' || routing.strategy === 'domain') {
77
- const srcRel = pageHtmlRel(base, defaultLocale, 'index');
78
- const srcAbs = path.join(outDir, srcRel);
79
- if (fs.existsSync(srcAbs)) {
80
- fs.copyFileSync(srcAbs, abs);
81
- }
82
- continue;
83
- }
84
- const target = `${base}/${defaultLocale}/`;
85
- const native = requireNativeAddon();
86
- if (typeof native.generateRedirectHtml !== 'function') {
87
- throw new Error('vmz native addon missing generateRedirectHtml — rebuild with `pnpm napi:build`');
88
- }
89
- const html = native.generateRedirectHtml({
90
- lang: defaultLocale,
91
- target,
92
- title: 'Documents',
93
- });
69
+ const html = `<!DOCTYPE html>
70
+ <html lang="${escapeAttr(defaultLocale)}">
71
+ <head>
72
+ <meta charset="utf-8" />
73
+ <meta http-equiv="refresh" content="0;url=${escapeAttr(target)}" />
74
+ <link rel="canonical" href="${escapeAttr(target)}" />
75
+ <title>Documents</title>
76
+ </head>
77
+ <body>
78
+ <p><a href="${escapeAttr(target)}">Continue to ${escapeAttr(defaultLocale)} docs</a></p>
79
+ </body>
80
+ </html>
81
+ `;
94
82
  fs.writeFileSync(abs, html, 'utf8');
95
83
  }
96
84
  }
85
+ /** @param {string} s */
86
+ function escapeAttr(s) {
87
+ return String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/</g, '&lt;');
88
+ }
@@ -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 EMBEDDED_RESOURCE_INDEX_SCHEMA = 'vmz.embedded.resource_index.v0';
11
10
  /** Paths that must never enter the embedded baseline closure. */
12
11
  const SKIP_NAMES = new Set(['vmz-serve-host.mjs', 'vmz-serve-host.js', 'node_modules', '.git']);
@@ -56,7 +55,7 @@ export function emitEmbeddedPackaging(outDir, opts = {}) {
56
55
  };
57
56
  index.indexDigest = sha256Hex(canonicalJson({ ...index, indexDigest: undefined }));
58
57
  const indexPath = path.join(vmzDir, 'embedded-resource-index.json');
59
- writePrettyJsonFile(indexPath, index);
58
+ fs.writeFileSync(indexPath, `${JSON.stringify(index, null, 2)}\n`, 'utf8');
60
59
  // Optional include_bytes entry point for Rust packaging adapters
61
60
  const rsPath = path.join(vmzDir, 'embedded_site.rs');
62
61
  fs.writeFileSync(rsPath, `// @generated by vmz — rust-embedded packaging adapter (do not edit)
package/dist/index.d.ts CHANGED
@@ -53,14 +53,6 @@ export declare function handshake(host?: {
53
53
  * @returns {string | null}
54
54
  */
55
55
  export declare function resolveCoreRuntimeDist(): string;
56
- /** Runtime companions required by dist/vmz-serve-host.mjs relative imports. */
57
- export declare const SERVE_HOST_RUNTIME_FILES: string[][];
58
- /**
59
- * Copy serve-host + registry bootstrap modules from `@vmz/core` into app outDir.
60
- * @param {string} outDir
61
- * @param {string} [coreDist]
62
- */
63
- export declare function materializeServeHostRuntime(outDir: any, coreDist?: string): void;
64
56
  /**
65
57
  * @typedef {object} WorkspaceOptions
66
58
  * @property {string} root
@@ -92,7 +84,6 @@ export declare function lowerMiniprogramBindingEventJson(rootPath: any): any;
92
84
  export declare function lowerMiniprogramStructureJson(rootPath: any): any;
93
85
  export declare function lowerMiniprogramRouteServerStyleJson(rootPath: any): any;
94
86
  export declare function lowerMiniprogramToolingDeployJson(rootPath: any): any;
95
- export declare function lowerMiniprogramWechatPackagingJson(rootPath: any): any;
96
87
  export declare function lowerMiniprogramMultiAdapterJson(rootPath: any): any;
97
88
  import { createMiniHost } from './mini-host.js';
98
89
  export { createMiniHost };
@@ -155,7 +146,6 @@ export declare function checkApplicationHostCompositionJson(hostRoot: any, packa
155
146
  */
156
147
  export declare function checkApplicationDevTestDeployJson(hostRoot: any, packageRoots: any, dirtyPaths?: any[]): any;
157
148
  export { createDevSession, listWatchedFiles, srcFingerprint } from './dev-session.js';
158
- export { coalesceRootBurst, collectDevWatchRoots, mergeDirtySets, localLinkDependencyRoots, watchRootForSourceFile, } from './dev-watch-roots.js';
159
149
  export { findAvailablePort } from './port.js';
160
150
  export { runCli, parseArgs, printHelp, printGlobalHelp, printProjectHelp } from './cli.js';
161
151
  export { findNearestProjectVmz, getInvocationContext, isGlobalAllowedCommand, isUnderNodeModules, resolveThisPackageRoot, resolveVmzBin, gateGlobalProjectCommand, } from './invocation.js';
@@ -171,8 +161,7 @@ export { CDN_POLICY_MANIFEST_SCHEMA, CDN_ADAPTER_PROJECTION_SCHEMA, CACHE_HTML,
171
161
  export { SITE_DELIVERY_CONTRACT_SCHEMA, SITE_DELIVERY_RESOLUTION_SCHEMA, defineSite, normalizeSiteDelivery, normalizeSourceProbe, resolveSiteRelease, probeReleaseDirectory, emitSiteDelivery, } from './site-delivery.js';
172
162
  export { EMBEDDED_RESOURCE_INDEX_SCHEMA, emitEmbeddedPackaging, } from './embedded-packaging.js';
173
163
  export { SERVER_LANG_IDS, SERVER_LANG_ALIASES, SERVER_LANGUAGE_BACKENDS, resolveServerLanguage, assertLangRuntimePair, } from './server-language-backend.js';
174
- export { WECHAT_PACKAGING_SCHEMA, WECHAT_PACKAGING_REL, wechatPackagingFromDelivery, materializeWechatPackaging, } from './wechat-packaging.js';
175
- export { DELIVERY_PROFILE_AUTHORING_SCHEMA, BUILD_PROFILE_SELECTION_SCHEMA, ASSEMBLIES, SERVER_RUNTIMES, BUILTIN_PROFILES, pickSiteAuthoring, pickDeliveryPackaging, normalizeDeliveryAuthoring, selectBuildProfile, semanticIdsForAssembly, } from './delivery-profile.js';
164
+ export { DELIVERY_PROFILE_AUTHORING_SCHEMA, BUILD_PROFILE_SELECTION_SCHEMA, ASSEMBLIES, SERVER_RUNTIMES, BUILTIN_PROFILES, pickSiteAuthoring, normalizeDeliveryAuthoring, selectBuildProfile, semanticIdsForAssembly, } from './delivery-profile.js';
176
165
  export { PACK_MANIFEST_SCHEMA, packFromDeploymentIr, ensureRuntimeCompanions } from './pack.js';
177
166
  export { SERVER_ARTIFACT_SCHEMA, HTTP_CONTRACT_SCHEMA, SERVER_RUNTIME_ADAPTER_SCHEMA, emitServerArtifact, projectServerRuntimeAdapter, } from './server-artifact.js';
178
167
  export { BUILD_PROOF_SCHEMA, ASSEMBLE_MANIFEST_SCHEMA, assembleDelivery, emitBuildProof, } from './build-assemble.js';
@@ -242,8 +231,6 @@ declare const _default: {
242
231
  expectedProtocol: typeof expectedProtocol;
243
232
  resolveNativePath: typeof resolveNativePath;
244
233
  resolveCoreRuntimeDist: typeof resolveCoreRuntimeDist;
245
- materializeServeHostRuntime: typeof materializeServeHostRuntime;
246
- SERVE_HOST_RUNTIME_FILES: string[][];
247
234
  loadNative: typeof loadNative;
248
235
  getProtocolVersions: typeof getProtocolVersions;
249
236
  handshake: typeof handshake;
@@ -257,7 +244,6 @@ declare const _default: {
257
244
  lowerMiniprogramStructureJson: typeof lowerMiniprogramStructureJson;
258
245
  lowerMiniprogramRouteServerStyleJson: typeof lowerMiniprogramRouteServerStyleJson;
259
246
  lowerMiniprogramToolingDeployJson: typeof lowerMiniprogramToolingDeployJson;
260
- lowerMiniprogramWechatPackagingJson: typeof lowerMiniprogramWechatPackagingJson;
261
247
  lowerMiniprogramMultiAdapterJson: typeof lowerMiniprogramMultiAdapterJson;
262
248
  createMiniHost: typeof createMiniHost;
263
249
  queryProfileProtocolCatalog: typeof queryProfileProtocolCatalog;