@vmz/vmz 0.3.11 → 0.3.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 (52) hide show
  1. package/delivery-runtime-files.json +1 -0
  2. package/dist/cli/cli.js +46 -23
  3. package/dist/cli/format-cmd.d.ts +9 -17
  4. package/dist/cli/format-cmd.js +37 -49
  5. package/dist/cli/format-config.d.ts +6 -9
  6. package/dist/cli/format-config.js +5 -29
  7. package/dist/delivery/delivery-profile.js +1 -1
  8. package/dist/dev/dev-session.js +16 -5
  9. package/dist/document/document-base-css.d.ts +15 -0
  10. package/dist/document/document-base-css.js +578 -0
  11. package/dist/document/document-build.d.ts +1 -0
  12. package/dist/document/document-build.js +94 -78
  13. package/dist/document/document-client.d.ts +13 -0
  14. package/dist/document/document-client.js +217 -0
  15. package/dist/document/document-cmd.js +3 -1
  16. package/dist/document/document-designs.d.ts +9 -3
  17. package/dist/document/document-designs.js +20 -34
  18. package/dist/document/document-enrich.d.ts +9 -0
  19. package/dist/document/document-enrich.js +55 -0
  20. package/dist/document/document-highlight.d.ts +26 -0
  21. package/dist/document/document-highlight.js +54 -0
  22. package/dist/document/document-integrate.d.ts +5 -0
  23. package/dist/document/document-integrate.js +1 -0
  24. package/dist/document/document-interactive.js +10 -2
  25. package/dist/document/document-layout-render.js +6 -2
  26. package/dist/document/document-schema.d.ts +5 -0
  27. package/dist/workspace/build-assemble.js +18 -6
  28. package/dist/workspace/cdn-policy.js +12 -0
  29. package/dist/workspace/content-addressed-assets.d.ts +6 -17
  30. package/dist/workspace/content-addressed-assets.js +10 -95
  31. package/dist/workspace/delivery-runtime-imports.js +3 -0
  32. package/dist/workspace/native-addon.d.ts +4 -0
  33. package/dist/workspace/pack-native.d.ts +139 -0
  34. package/dist/workspace/pack-native.js +273 -0
  35. package/dist/workspace/pack.d.ts +113 -27
  36. package/dist/workspace/pack.js +271 -54
  37. package/dist/workspace/plugin-host.d.ts +4 -4
  38. package/dist/workspace/plugin-host.js +9 -12
  39. package/dist/workspace/production-observability.d.ts +2 -0
  40. package/dist/workspace/production-observability.js +6 -2
  41. package/dist/workspace/project-build.js +26 -4
  42. package/dist/workspace/public-api.d.ts +7 -3
  43. package/dist/workspace/public-api.js +5 -3
  44. package/dist/workspace/static-emit.d.ts +30 -0
  45. package/dist/workspace/static-emit.js +211 -18
  46. package/dist/workspace/user-config.d.ts +112 -0
  47. package/dist/workspace/user-config.js +12 -0
  48. package/host-runtime-files.json +1 -0
  49. package/locales/en-US/cli.json +5 -4
  50. package/package.json +20 -20
  51. package/dist/workspace/pack-client-packages.d.ts +0 -32
  52. package/dist/workspace/pack-client-packages.js +0 -437
@@ -33,6 +33,7 @@
33
33
  { "src": "browser/resume/resume.js", "out": "browser/resume/resume.js" },
34
34
  { "src": "ssr/render.js", "out": "ssr/render.js" },
35
35
  { "src": "ssr/serialize.js", "out": "ssr/serialize.js" },
36
+ { "src": "ssr/onmount.js", "out": "ssr/onmount.js" },
36
37
  { "src": "ssr/document.js", "out": "ssr/document.js" }
37
38
  ]
38
39
  }
package/dist/cli/cli.js CHANGED
@@ -2,6 +2,7 @@
2
2
  * Node CLI command implementations .
3
3
  */
4
4
  import { spawn } from 'node:child_process';
5
+ import { existsSync, statSync } from 'node:fs';
5
6
  import path from 'node:path';
6
7
  import { createCli } from '@vmz/commander';
7
8
  import { registerApplicationCommands } from './application-cmd.js';
@@ -14,7 +15,7 @@ import { buildIntegratedDocuments, projectHasDocuments } from '../document/docum
14
15
  import { registerExplainCommand } from './explain-cmd.js';
15
16
  import { registerGithubActionsCommand } from './github-actions-cmd.js';
16
17
  import { registerInitCommands } from './init-cmd.js';
17
- import { createWorkspace, getProtocolVersions, HOST_PROTOCOL, materializeServeHostRuntime, materializeWechatPackaging, resolveCoreRuntimeDist, resolveNativePath, } from '../workspace/public-api.js';
18
+ import { createWorkspace, getProtocolVersions, HOST_PROTOCOL, materializeServeHostRuntime, materializeWechatPackaging, resolveNativePath, } from '../workspace/public-api.js';
18
19
  import { gateGlobalProjectCommand, getInvocationContext, isGlobalAllowedCommand } from '../workspace/invocation.js';
19
20
  import { emitLocaleRuntimeModules, localeHasErrors } from '../locale/locale-check.js';
20
21
  import { registerLocaleCommands } from '../locale/locale-cmd.js';
@@ -22,15 +23,17 @@ import { emitLocaleRouteRealization } from '../locale/locale-route-emit.js';
22
23
  import { log } from '../workspace/log.js';
23
24
  import { resolveCliLogLevel, setCliLogLevel } from '../workspace/log-level.js';
24
25
  import { parseCliLogStyle, setCliLogStyle } from '../workspace/log-style.js';
25
- import { packFromDeploymentIr } from '../workspace/pack.js';
26
+ import { ensureRuntimeCompanions, packFromDeploymentIr, packSsrFromDeploymentIr } from '../workspace/pack.js';
26
27
  import { registerPlanCommands } from './plan-cmd.js';
27
28
  import { loadVmzConfig } from '../workspace/plugin-host.js';
28
29
  import { findAvailablePort } from '../workspace/port.js';
29
30
  import { registerRefactorCommands } from './refactor-cmd.js';
30
31
  import { registerArtifactCommands } from './release-cmd.js';
31
- import { readPackageMeta, resolveWorkspaceDirs } from '../workspace/resolve.js';
32
+ import { findPackageJson, readPackageMeta, resolveWorkspaceDirs } from '../workspace/resolve.js';
32
33
  import { emitRouteCatalog } from '../workspace/route-catalog-emit.js';
34
+ import { resolveCoreRuntimeDist } from '../workspace/runtime-dist.js';
33
35
  import { registerTestCommand } from './test-cmd.js';
36
+ import { emitStaticBootstrapEntries } from '../workspace/static-emit.js';
34
37
  export function printGlobalHelp() {
35
38
  return buildProductCli({ mode: 'global' }).parse(['help']);
36
39
  }
@@ -75,6 +78,7 @@ function buildProductCli(opts = {}) {
75
78
  withServeExtras(withCoreOpts(cli.command('serve', 'cli.cmd.serve'))).action((options) => cmdServe(options));
76
79
  withServeExtras(withCoreOpts(cli.command('dev', 'cli.cmd.dev'))).action((options) => cmdDev(options));
77
80
  withCoreOpts(cli.command('format', 'cli.cmd.format'))
81
+ .option('--rust', 'cli.opt.format-rust')
78
82
  .option('--no-javascript', 'cli.opt.format-no-javascript')
79
83
  .option('--no-rust', 'cli.opt.format-no-rust')
80
84
  .option('--no-vmz', 'cli.opt.format-no-vmz')
@@ -269,8 +273,21 @@ async function cmdBuild(args) {
269
273
  if (routeCatalog.written.length) {
270
274
  log.info(`route catalog (${routeCatalog.written.length} artifact(s))`);
271
275
  }
276
+ if (!wechatPack) {
277
+ try {
278
+ // Node graph must be packed before any SSR consumer (document
279
+ // build, static emit, render host) imports a compiled chunk.
280
+ ensureRuntimeCompanions(outDir, resolveCoreRuntimeDist());
281
+ const nodePack = packSsrFromDeploymentIr(outDir, { projectRoot: project });
282
+ log.info(`node pack ok (entries=${nodePack.manifest.entryCount}, chunks=${nodePack.manifest.chunkCount}, digest=${String(nodePack.manifest.packDigest).slice(0, 12)}…)`);
283
+ }
284
+ catch (err) {
285
+ log.error(`node pack failed: ${err instanceof Error ? err.message : String(err)}`);
286
+ return 1;
287
+ }
288
+ }
272
289
  if (projectHasDocuments(project)) {
273
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
290
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: cfg.engines });
274
291
  if (!docs.ok)
275
292
  return 1;
276
293
  }
@@ -299,21 +316,16 @@ async function cmdBuild(args) {
299
316
  }
300
317
  let pack = null;
301
318
  try {
319
+ // Browser pack entries are the static bootstrap modules; static emit
320
+ // runs after pack (HTML resolves `_vmz/pack/`), so write them first.
321
+ emitStaticBootstrapEntries(outDir);
302
322
  pack = packFromDeploymentIr(outDir, {
303
323
  release: Boolean(args.release),
304
324
  profileId: selected.selection.profileId,
305
325
  assembly: selected.selection.assembly,
306
- coreDist: resolveCoreRuntimeDist(),
307
326
  projectRoot: project,
308
327
  });
309
- const lower = pack.manifest?.clientPackageLowering;
310
- if (lower?.bareSpecs?.length) {
311
- log.info(`pack client packages (${lower.bareSpecs.length} bare → vendor; rewritten=${lower.rewrittenFiles})`);
312
- }
313
- if (lower?.remainingBareSpecs?.length) {
314
- log.warn(`pack client packages: ${lower.remainingBareSpecs.length} bare specifier(s) still unresolved for browser: ${lower.remainingBareSpecs.slice(0, 8).join(', ')}${lower.remainingBareSpecs.length > 8 ? '…' : ''}`);
315
- }
316
- log.info(`pack ok (units=${pack.manifest.unitCount}, digest=${String(pack.manifest.packDigest).slice(0, 12)}…)`);
328
+ log.info(`pack ok (entries=${pack.manifest.entryCount}, chunks=${pack.manifest.chunkCount}, digest=${String(pack.manifest.packDigest).slice(0, 12)}…)`);
317
329
  }
318
330
  catch (err) {
319
331
  log.error(`pack failed: ${err instanceof Error ? err.message : String(err)}`);
@@ -513,24 +525,35 @@ async function cmdDev(args) {
513
525
  }
514
526
  }
515
527
  async function cmdFormat(args) {
516
- const pathArg = args._[0] ?? '.';
517
- const { project, outDir } = resolveWorkspaceDirs({
518
- path: pathArg,
519
- outDir: typeof args['out-dir'] === 'string' ? args['out-dir'] : undefined,
520
- });
528
+ const rawPath = args._[0];
529
+ if (typeof rawPath !== 'string' || rawPath.trim() === '') {
530
+ log.error('format: explicit <file|dir> path required — no implicit workspace scope (pass `.` for the current directory)');
531
+ return 1;
532
+ }
533
+ const target = path.resolve(process.cwd(), rawPath);
534
+ if (!existsSync(target)) {
535
+ log.error(`format: path does not exist: ${target}`);
536
+ return 1;
537
+ }
538
+ if (args.rust && args['no-rust']) {
539
+ log.error('format: conflicting `--rust` and `--no-rust`');
540
+ return 1;
541
+ }
542
+ const startDir = statSync(target).isDirectory() ? target : path.dirname(target);
543
+ const pkg = findPackageJson(startDir);
544
+ const configRoot = pkg ? path.dirname(pkg) : startDir;
521
545
  const checkOnly = Boolean(args.check);
522
- log.info(`format ${project}${checkOnly ? ' --check' : ''}`);
546
+ log.info(`format ${target}${checkOnly ? ' --check' : ''}`);
523
547
  const { runFormatCommand } = await import('./format-cmd.js');
524
548
  return runFormatCommand({
525
- project,
526
- outDir,
549
+ target,
550
+ configRoot,
527
551
  check: checkOnly,
528
552
  overrides: {
529
553
  javascript: args['no-javascript'] ? false : undefined,
530
- rust: args['no-rust'] ? false : undefined,
554
+ rust: args.rust ? true : args['no-rust'] ? false : undefined,
531
555
  vmz: args['no-vmz'] ? false : undefined,
532
556
  },
533
- createWorkspace,
534
557
  });
535
558
  }
536
559
  function cmdLint(args) {
@@ -1,28 +1,20 @@
1
1
  /**
2
- * `vmz format` — workspace surfaces (JS/TS + Rust) + `.vmz` authoring formatter.
2
+ * `vmz format` — explicit-path formatter.
3
+ *
4
+ * Scope contract: the positional path is the whole scope. A file formats only
5
+ * itself; a directory is walked locally. Nothing ever falls back to a
6
+ * workspace-wide pass, and Rust runs per-file `rustfmt` — never `cargo fmt`.
3
7
  */
4
8
  export type FormatCommandOptions = {
5
- project: string;
6
- outDir: string;
9
+ /** Absolute explicit file or directory (already validated to exist). */
10
+ target: string;
11
+ /** Project root used to load `vmz.config` and the style config. */
12
+ configRoot: string;
7
13
  check: boolean;
8
14
  overrides?: {
9
15
  javascript?: boolean;
10
16
  rust?: boolean;
11
17
  vmz?: boolean;
12
18
  };
13
- createWorkspace: (opts: {
14
- root: string;
15
- outDir: string;
16
- }) => {
17
- format: (checkOnly?: boolean) => {
18
- filesChecked: number;
19
- filesWritten: number;
20
- filesNeedWrite: number;
21
- diagnostics?: Array<{
22
- severity?: string;
23
- }>;
24
- };
25
- dispose: () => void;
26
- };
27
19
  };
28
20
  export declare function runFormatCommand(opts: FormatCommandOptions): Promise<number>;
@@ -1,65 +1,53 @@
1
1
  /**
2
- * `vmz format` — workspace surfaces (JS/TS + Rust) + `.vmz` authoring formatter.
2
+ * `vmz format` — explicit-path formatter.
3
+ *
4
+ * Scope contract: the positional path is the whole scope. A file formats only
5
+ * itself; a directory is walked locally. Nothing ever falls back to a
6
+ * workspace-wide pass, and Rust runs per-file `rustfmt` — never `cargo fmt`.
3
7
  */
8
+ import fs from 'node:fs';
9
+ import path from 'node:path';
4
10
  import { loadNative } from '../workspace/public-api.js';
5
11
  import { loadVmzConfig } from '../workspace/plugin-host.js';
6
12
  import { log } from '../workspace/log.js';
7
13
  import { resolveFormatConfig } from './format-config.js';
8
14
  export async function runFormatCommand(opts) {
9
- const { project, outDir, check, createWorkspace } = opts;
10
- const { format: formatConfig } = await loadVmzConfig(project);
11
- const resolved = resolveFormatConfig(project, formatConfig);
12
- if (opts.overrides?.javascript !== undefined)
13
- resolved.javascript = opts.overrides.javascript;
14
- if (opts.overrides?.rust !== undefined)
15
- resolved.rust = opts.overrides.rust;
16
- if (opts.overrides?.vmz !== undefined)
17
- resolved.vmz = opts.overrides.vmz;
15
+ const { target, configRoot, check } = opts;
16
+ const { format: formatConfig } = await loadVmzConfig(configRoot);
17
+ const resolved = resolveFormatConfig(formatConfig);
18
18
  const native = loadNative();
19
- let exit = 0;
20
- if (resolved.javascript || resolved.rust) {
21
- const report = native.formatWorkspaceRun({
22
- cwd: project,
19
+ let report;
20
+ try {
21
+ report = native.formatTargetsRun({
22
+ targets: [target],
23
23
  check,
24
- includes: resolved.includes,
24
+ rust: opts.overrides?.rust,
25
+ javascript: opts.overrides?.javascript,
26
+ vmz: opts.overrides?.vmz,
25
27
  excludes: resolved.excludes,
26
- rust: resolved.rust,
27
- javascript: resolved.javascript,
28
- styleConfig: resolved.style,
28
+ styleConfig: path.resolve(configRoot, resolved.style),
29
29
  });
30
- if (report.errors.length > 0) {
31
- for (const message of report.errors) {
32
- log.error(message);
33
- }
30
+ }
31
+ catch (err) {
32
+ log.error(err instanceof Error ? err.message : String(err));
33
+ return 1;
34
+ }
35
+ let exit = 0;
36
+ for (const message of report.errors) {
37
+ log.error(message);
38
+ }
39
+ if (report.errors.length > 0)
40
+ exit = 1;
41
+ if (log.diagnostics(report.diagnostics ?? []))
42
+ exit = 1;
43
+ const scope = fs.statSync(target).isDirectory() ? `${target}${path.sep}` : target;
44
+ if (check) {
45
+ log.info(`format ${scope}: ${report.needWrite} file(s) would change, ${report.unchanged} unchanged`);
46
+ if (report.needWrite > 0)
34
47
  exit = 1;
35
- }
36
- else if (check && report.formatted === 0 && report.unchanged > 0) {
37
- log.info(`workspace format: no changes needed (${report.unchanged} file(s) checked)`);
38
- }
39
- else if (!check) {
40
- log.info(`workspace format: ${report.formatted} file(s) updated, ${report.unchanged} unchanged`);
41
- }
42
48
  }
43
- if (resolved.vmz) {
44
- log.info(`format .vmz ${project}${check ? ' --check' : ''}`);
45
- const ws = createWorkspace({ root: project, outDir });
46
- try {
47
- const report = ws.format(check);
48
- const errors = log.diagnostics(report.diagnostics ?? []);
49
- if (check) {
50
- log.info(`checked ${report.filesChecked} .vmz file(s); ${report.filesNeedWrite} need write`);
51
- if (report.filesNeedWrite > 0)
52
- exit = 1;
53
- }
54
- else {
55
- log.info(`formatted ${report.filesWritten}/${report.filesChecked} .vmz file(s)`);
56
- }
57
- if (errors)
58
- exit = 1;
59
- }
60
- finally {
61
- ws.dispose();
62
- }
49
+ else {
50
+ log.info(`format ${scope}: ${report.formatted} file(s) updated, ${report.unchanged} unchanged`);
63
51
  }
64
52
  return exit;
65
53
  }
@@ -1,16 +1,13 @@
1
1
  /**
2
2
  * `vmz.config` `format` resolution for `vmz format`.
3
+ *
4
+ * Scope always comes from the explicit CLI path — config only narrows
5
+ * (`excludes`) and styles. There are no config-driven `includes`.
3
6
  */
4
- import type { VmzFormatConfig } from '@vmz/plugin';
7
+ import type { VmzFormatConfig } from '../workspace/user-config.js';
5
8
  export type ResolvedFormatOptions = {
6
- includes?: string[];
7
9
  excludes: string[];
8
- rust: boolean;
9
- javascript: boolean;
10
- vmz: boolean;
11
10
  style: string;
12
11
  };
13
- /** VMZ hybrid monorepo — JS/TS surfaces only, never whole-repo walk. */
14
- export declare const VMZ_FRAMEWORK_INCLUDES: readonly ["scripts/**", "packages/runtimes/**", "packages/examples/**", "packages/editors/**", "packages/ui/**", "packages/plugins/**", "packages/content/**", "packages/homepage/**", "package.json", "biome.json", "vmz.config.ts", "nifty.config.ts"];
15
- /** Merge `vmz.config` `format` with framework monorepo defaults. */
16
- export declare function resolveFormatConfig(projectRoot: string, format?: VmzFormatConfig): ResolvedFormatOptions;
12
+ /** Merge `vmz.config` `format` excludes / style with the defaults. */
13
+ export declare function resolveFormatConfig(format?: VmzFormatConfig | null): ResolvedFormatOptions;
@@ -1,38 +1,14 @@
1
1
  /**
2
2
  * `vmz.config` `format` resolution for `vmz format`.
3
+ *
4
+ * Scope always comes from the explicit CLI path — config only narrows
5
+ * (`excludes`) and styles. There are no config-driven `includes`.
3
6
  */
4
- import { existsSync } from 'node:fs';
5
- import path from 'node:path';
6
- /** VMZ hybrid monorepo — JS/TS surfaces only, never whole-repo walk. */
7
- export const VMZ_FRAMEWORK_INCLUDES = [
8
- 'scripts/**',
9
- 'packages/runtimes/**',
10
- 'packages/examples/**',
11
- 'packages/editors/**',
12
- 'packages/ui/**',
13
- 'packages/plugins/**',
14
- 'packages/content/**',
15
- 'packages/homepage/**',
16
- 'package.json',
17
- 'biome.json',
18
- 'vmz.config.ts',
19
- 'nifty.config.ts',
20
- ];
21
7
  const DEFAULT_EXCLUDES = ['**/fixtures/**', '**/dist/**', '**/node_modules/**', '**/target/**'];
22
- function isFrameworkMonorepoRoot(projectRoot) {
23
- return existsSync(path.join(projectRoot, 'packages', 'runtimes'));
24
- }
25
- /** Merge `vmz.config` `format` with framework monorepo defaults. */
26
- export function resolveFormatConfig(projectRoot, format) {
27
- const frameworkRoot = isFrameworkMonorepoRoot(projectRoot);
28
- const hasCargo = existsSync(path.join(projectRoot, 'Cargo.toml'));
29
- const hasFormatSection = format !== undefined;
8
+ /** Merge `vmz.config` `format` excludes / style with the defaults. */
9
+ export function resolveFormatConfig(format) {
30
10
  return {
31
- includes: format?.includes ?? (frameworkRoot ? [...VMZ_FRAMEWORK_INCLUDES] : undefined),
32
11
  excludes: [...DEFAULT_EXCLUDES, ...(format?.excludes ?? [])],
33
- rust: format?.rust ?? (hasCargo && (frameworkRoot || hasFormatSection)),
34
- javascript: format?.javascript ?? (frameworkRoot || hasFormatSection),
35
- vmz: format?.vmz ?? true,
36
12
  style: format?.style ?? 'biome.json',
37
13
  };
38
14
  }
@@ -391,7 +391,7 @@ export function semanticIdsForAssembly(assembly) {
391
391
  case 'rust-embedded':
392
392
  return ['site-fallback', 'asset-graph'];
393
393
  case 'local-static':
394
- return ['asset-graph'];
394
+ return ['static-delivery', 'asset-graph'];
395
395
  default:
396
396
  return [];
397
397
  }
@@ -14,6 +14,7 @@ import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
14
14
  import path from 'node:path';
15
15
  import { buildIntegratedDocuments, projectHasDocuments } from '../document/document-integrate.js';
16
16
  import { createWorkspace, resolveNativePath } from '../workspace/public-api.js';
17
+ import { loadVmzConfig } from '../workspace/plugin-host.js';
17
18
  import { materializeWechatPackaging } from '../workspace/wechat-packaging.js';
18
19
  import { emitLocaleRuntimeModules, localeHasErrors } from '../locale/locale-check.js';
19
20
  import { log } from '../workspace/log.js';
@@ -199,6 +200,16 @@ export function createDevSession(options) {
199
200
  return 'respawn';
200
201
  }
201
202
  }
203
+ let docEnginesPromise = null;
204
+ /** `engines.code` / `engines.markdown` for the document layer (loaded once per session). */
205
+ function docEngines() {
206
+ if (!docEnginesPromise) {
207
+ docEnginesPromise = loadVmzConfig(project)
208
+ .then((cfg) => cfg.engines)
209
+ .catch(() => undefined);
210
+ }
211
+ return docEnginesPromise;
212
+ }
202
213
  async function start() {
203
214
  const src = path.join(project, 'src');
204
215
  if (!existsSync(src)) {
@@ -226,7 +237,7 @@ export function createDevSession(options) {
226
237
  // Reclaim orphan serve-host from a previous crashed launcher (same outDir).
227
238
  reclaimStaleDevHost(outDir);
228
239
  if (projectHasDocuments(project)) {
229
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
240
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: await docEngines() });
230
241
  if (!docs.ok) {
231
242
  throw new Error('vmz dev: integrated document build failed');
232
243
  }
@@ -533,7 +544,7 @@ export function createDevSession(options) {
533
544
  }
534
545
  dependencyRoots = [...refreshed.dependencyRoots];
535
546
  if (batch.docsDirty || projectHasDocuments(project)) {
536
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
547
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: await docEngines() });
537
548
  if (!docs.ok)
538
549
  log.warn('document mount rebuild failed — keeping previous docs');
539
550
  }
@@ -563,7 +574,7 @@ export function createDevSession(options) {
563
574
  }
564
575
  await emitPublicAndTrack();
565
576
  if (batch.docsDirty || projectHasDocuments(project)) {
566
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
577
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: await docEngines() });
567
578
  if (!docs.ok) {
568
579
  log.warn('document mount rebuild failed — keeping previous docs');
569
580
  }
@@ -605,7 +616,7 @@ export function createDevSession(options) {
605
616
  }
606
617
  await emitPublicAndTrack();
607
618
  if (batch.docsDirty || projectHasDocuments(project)) {
608
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
619
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: await docEngines() });
609
620
  if (!docs.ok)
610
621
  log.warn('document mount rebuild failed — keeping previous docs');
611
622
  }
@@ -646,7 +657,7 @@ export function createDevSession(options) {
646
657
  }
647
658
  if (batch.docsDirty) {
648
659
  log.info('documents change detected — rebuilding document mount…');
649
- const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
660
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir, engines: await docEngines() });
650
661
  if (!docs.ok) {
651
662
  log.warn('document mount rebuild failed — keeping previous docs');
652
663
  continue;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Framework-owned document base stylesheet (`assets/vmz-document.css`).
3
+ *
4
+ * The single canonical document surface for every site: sidebar + article +
5
+ * toc grid, article type scale, code/table/quote blocks, island shells and the
6
+ * responsive breakpoints. Structural colors come from `--vmz-*` semantic tokens
7
+ * only (site brand flows in through the compiled `vmz-designs.css`); syntax
8
+ * colors inside code blocks come from the Shiki theme and stay identical on
9
+ * every site. Sites do not ship document stylesheets.
10
+ *
11
+ * House rules (规划设计/vmz 05): pure-white base with near-black blocks for
12
+ * rhythm, body text ≥ 16px, small text ≥ 12px at weight ≥ 400, no uppercase
13
+ * letter-spaced micro labels, no 3D / volumetric surfaces.
14
+ */
15
+ export declare const DOCUMENT_BASE_CSS = "/* vmz document layer \u2014 canonical document surface (token-driven). */\n\n.site--docs {\n min-height: 100vh;\n display: flex;\n flex-direction: column;\n background: var(--vmz-surface-paper, #ffffff);\n}\n\n.site--docs > .doc-body {\n flex: 1 0 auto;\n display: grid;\n grid-template-columns: 17rem minmax(0, 1fr);\n align-items: start;\n width: 100%;\n max-width: 94rem;\n margin: 0 auto;\n}\n\n/* Sidebar -------------------------------------------------------------- */\n\n.doc-sidebar {\n position: sticky;\n top: var(--vmz-layout-header-height-compact, 3.5rem);\n align-self: start;\n max-height: calc(100vh - var(--vmz-layout-header-height-compact, 3.5rem));\n overflow-y: auto;\n padding: 2rem 1.25rem 3rem 1.5rem;\n border-right: 1px solid var(--vmz-line-default, #e5e6eb);\n}\n\n/* Density activation lives in this framework layer: sites only ship tokens. */\n\n:where([data-density=\"compact\"]) .doc-sidebar {\n padding-top: var(--vmz-density-compact-padding-y, 1.5rem);\n padding-bottom: 2rem;\n}\n\n:where([data-density=\"dense\"]) .doc-sidebar {\n padding-top: var(--vmz-density-dense-padding-y, 1rem);\n padding-bottom: 1.5rem;\n}\n\n.doc-sidebar__label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--vmz-text-muted, #6b7178);\n margin: 0 0 var(--vmz-density-control-padding-y, 0.75rem);\n}\n\n.doc-subnav ul {\n list-style: none;\n margin: 0;\n padding: 0;\n display: grid;\n gap: 2px;\n}\n\n.doc-subnav a {\n display: block;\n padding: 0.4375rem 0.625rem;\n font-size: 0.875rem;\n line-height: 1.45;\n color: var(--vmz-text-steel, #51565d);\n text-decoration: none;\n border-left: 2px solid transparent;\n border-radius: 0 4px 4px 0;\n transition: color 120ms ease, background 120ms ease, border-color 120ms ease;\n}\n\n.doc-subnav a:hover {\n color: var(--vmz-text-ink, #1f2329);\n background: var(--vmz-surface-mist, #f7f8fa);\n text-decoration: none;\n}\n\n.doc-subnav a[aria-current=\"page\"] {\n color: var(--vmz-brand-primary, #1677ff);\n border-left-color: var(--vmz-brand-primary, #1677ff);\n background: var(--vmz-surface-paper, #ffffff);\n font-weight: 500;\n}\n\n.doc-search {\n margin-top: 1rem;\n padding-top: 0.75rem;\n border-top: 1px solid var(--vmz-line-default, #e5e6eb);\n position: relative;\n}\n\n.doc-search__input {\n width: 100%;\n box-sizing: border-box;\n padding: 0.4375rem 0.625rem;\n font-size: 0.875rem;\n line-height: 1.4;\n color: var(--vmz-text-ink, #1f2329);\n background: var(--vmz-surface-paper, #ffffff);\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: 6px;\n}\n\n.doc-search__input:focus-visible {\n outline: 2px solid var(--vmz-brand-primary, #1677ff);\n outline-offset: 1px;\n}\n\n.doc-search__results {\n list-style: none;\n margin: 0.375rem 0 0;\n padding: 0.25rem;\n display: grid;\n gap: 1px;\n background: var(--vmz-surface-paper, #ffffff);\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: 6px;\n max-height: 16rem;\n overflow-y: auto;\n}\n\n.doc-search__results[hidden] {\n display: none;\n}\n\n.doc-search__results a {\n display: block;\n padding: 0.375rem 0.5rem;\n font-size: 0.8125rem;\n line-height: 1.4;\n color: var(--vmz-text-steel, #51565d);\n text-decoration: none;\n border-radius: 4px;\n overflow-wrap: break-word;\n}\n\n.doc-search__ctx {\n color: var(--vmz-text-muted, #6b7178);\n}\n\n.doc-search__results li[aria-selected=\"true\"] a,\n.doc-search__results a:hover {\n background: var(--vmz-surface-mist, #f7f8fa);\n color: var(--vmz-text-ink, #1f2329);\n}\n\n.doc-search__empty {\n padding: 0.375rem 0.5rem;\n font-size: 0.8125rem;\n color: var(--vmz-text-muted, #6b7178);\n}\n\n.doc-search__hint {\n margin: 0.375rem 0 0;\n font-size: 0.75rem;\n line-height: 1.5;\n color: var(--vmz-text-muted, #6b7178);\n}\n\n/* Content grid --------------------------------------------------------- */\n\n.doc-content {\n display: grid;\n grid-template-columns: minmax(0, 1fr) 13rem;\n gap: 3rem;\n padding: 3rem 3rem 4rem;\n min-width: 0;\n}\n\n.doc-content > main {\n min-width: 0;\n max-width: 46rem;\n}\n\n/* Breadcrumb ----------------------------------------------------------- */\n\n.doc-breadcrumb {\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--vmz-text-muted, #6b7178);\n margin: 0 0 2rem;\n display: flex;\n gap: 0.375rem;\n align-items: baseline;\n}\n\n.doc-breadcrumb a {\n color: var(--vmz-text-steel, #51565d);\n text-decoration: none;\n}\n\n.doc-breadcrumb a:hover {\n color: var(--vmz-brand-primary, #1677ff);\n text-decoration: underline;\n text-underline-offset: 2px;\n}\n\n.doc-breadcrumb [aria-current] {\n color: var(--vmz-text-ink, #1f2329);\n font-weight: 500;\n}\n\n/* On this page --------------------------------------------------------- */\n\n.toc {\n position: sticky;\n top: calc(var(--vmz-layout-header-height-compact, 3.5rem) + 1.25rem);\n align-self: start;\n order: 2;\n border-left: 1px solid var(--vmz-line-subtle, #f0f1f3);\n padding-left: 1rem;\n}\n\n.toc__label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--vmz-text-muted, #6b7178);\n margin: 0 0 0.5rem;\n}\n\n.toc ol {\n list-style: none;\n margin: 0;\n padding: 0;\n display: grid;\n gap: 0.25rem;\n}\n\n.toc a {\n color: var(--vmz-text-steel, #51565d);\n text-decoration: none;\n display: block;\n line-height: 1.4;\n font-size: 0.75rem;\n}\n\n.toc a:hover {\n color: var(--vmz-brand-primary, #1677ff);\n}\n\n.toc .h3 {\n padding-left: 0.75rem;\n color: var(--vmz-text-muted, #6b7178);\n}\n\n/* Article typography --------------------------------------------------- */\n\n.doc-content main h1 {\n font-family: var(--vmz-font-display, var(--vmz-font-sans, sans-serif));\n font-size: clamp(2rem, 2.8vw, 2.5rem);\n font-weight: 650;\n letter-spacing: -0.03em;\n line-height: 1.12;\n margin: 0 0 1.25rem;\n color: var(--vmz-text-ink, #1f2329);\n}\n\n.doc-content main h2 {\n font-size: 1.4375rem;\n letter-spacing: -0.02em;\n margin: 3rem 0 0.875rem;\n color: var(--vmz-text-ink, #1f2329);\n}\n\n.doc-content main h3 {\n font-size: 1.125rem;\n margin: 2rem 0 0.5rem;\n color: var(--vmz-text-ink, #1f2329);\n}\n\n.doc-content main h4 {\n font-size: 1rem;\n margin: 1.75rem 0 0.375rem;\n color: var(--vmz-text-ink, #1f2329);\n}\n\n.doc-content main p,\n.doc-content main li {\n font-size: 1rem;\n line-height: 1.75;\n color: var(--vmz-text-steel, #51565d);\n}\n\n.doc-content main p {\n margin: 0 0 1.125rem;\n}\n\n.doc-content main ul,\n.doc-content main ol {\n margin: 0 0 1.25rem;\n padding-left: 1.375rem;\n}\n\n.doc-content main li + li {\n margin-top: 0.375rem;\n}\n\n.doc-content main li::marker {\n color: var(--vmz-line-strong, #c9cdd4);\n}\n\n.doc-content main a {\n color: var(--vmz-brand-primary, #1677ff);\n text-decoration: underline;\n text-underline-offset: 2px;\n text-decoration-color: var(--vmz-line-strong, #c9cdd4);\n overflow-wrap: break-word;\n}\n\n.doc-content main a:hover {\n text-decoration-color: var(--vmz-brand-primary, #1677ff);\n}\n\n.doc-content main strong {\n color: var(--vmz-text-ink, #1f2329);\n font-weight: 600;\n}\n\n.doc-content main hr {\n border: 0;\n border-top: 1px solid var(--vmz-line-default, #e5e6eb);\n margin: 1.75rem 0;\n}\n\n/* Inline code */\n.doc-content main :not(pre) > code {\n font-family: var(--vmz-font-mono, ui-monospace, monospace);\n font-size: 0.875rem;\n color: var(--vmz-text-ink, #1f2329);\n background: var(--vmz-surface-mist, #f7f8fa);\n border: 1px solid var(--vmz-line-subtle, #f0f1f3);\n border-radius: 4px;\n padding: 0.0625rem 0.3125rem;\n word-break: break-word;\n}\n\n/* Code blocks: near-black surface, token chrome, theme-owned syntax colors */\n.doc-content main pre {\n position: relative;\n margin: 0 0 1.375rem;\n padding: 1.125rem 1.25rem;\n overflow-x: auto;\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: 8px;\n background: var(--vmz-surface-code, #0f1115);\n color: var(--vmz-text-on-ink, #f7f8fa);\n font-size: 0.84375rem;\n line-height: 1.7;\n tab-size: 4;\n}\n\n/* Copy button (injected by assets/vmz-document.js; hidden without JS) */\n.doc-copy {\n position: absolute;\n top: 0.5rem;\n right: 0.5rem;\n padding: 0.1875rem 0.5rem;\n font-size: 0.75rem;\n font-weight: 400;\n line-height: 1.4;\n color: var(--vmz-text-muted, #6b7178);\n background: var(--vmz-surface-mist, #f7f8fa);\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: 4px;\n cursor: pointer;\n opacity: 0;\n transition: opacity 120ms ease;\n}\n\npre:hover .doc-copy,\npre:focus-within .doc-copy,\n.doc-copy:focus-visible {\n opacity: 1;\n}\n\n.doc-copy--done {\n color: var(--vmz-status-success-accent, #237804);\n}\n\n/* Heading self anchors (build-time, no-JS usable) */\n.h-anchor {\n margin-left: 0.4em;\n font-size: 0.8em;\n font-weight: 400;\n text-decoration: none;\n color: var(--vmz-text-muted, #6b7178);\n opacity: 0;\n transition: opacity 120ms ease;\n}\n\n.doc-content main h2:hover .h-anchor,\n.doc-content main h3:hover .h-anchor,\n.doc-content main h4:hover .h-anchor,\n.h-anchor:focus-visible {\n opacity: 1;\n}\n\n.doc-content main pre code {\n font-family: var(--vmz-font-mono, ui-monospace, monospace);\n font-size: inherit;\n background: transparent;\n border: 0;\n padding: 0;\n color: inherit;\n}\n\n/* Tables */\n.doc-content main table {\n width: 100%;\n border-collapse: separate;\n border-spacing: 0;\n margin: 0.75rem 0 1.5rem;\n font-size: 0.9375rem;\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: 10px;\n display: block;\n overflow-x: auto;\n}\n\n.doc-content main thead th {\n background: var(--vmz-surface-mist, #f7f8fa);\n border-bottom: 1px solid var(--vmz-line-default, #e5e6eb);\n font-size: 0.8125rem;\n font-weight: 600;\n color: var(--vmz-text-muted, #6b7178);\n text-align: left;\n padding: 0.6875rem 1rem;\n white-space: nowrap;\n}\n\n.doc-content main td,\n.doc-content main tbody th {\n border-bottom: 1px solid var(--vmz-line-subtle, #f0f1f3);\n padding: 0.6875rem 1rem;\n vertical-align: top;\n line-height: 1.6;\n color: var(--vmz-text-steel, #51565d);\n}\n\n.doc-content main tbody tr:last-child td {\n border-bottom: 0;\n}\n\n.doc-content main tbody tr:hover td {\n background: var(--vmz-surface-fill, #fafbfc);\n}\n\n.doc-content main td code {\n font-size: 0.8125rem;\n}\n\n/* Blockquote / callouts */\n.doc-content main blockquote {\n margin: 0 0 1.25rem;\n padding: 0.875rem 1.125rem;\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-left: 3px solid var(--vmz-brand-primary, #1677ff);\n border-radius: 0 8px 8px 0;\n background: var(--vmz-surface-mist, #f7f8fa);\n font-size: 1rem;\n color: var(--vmz-text-steel, #51565d);\n}\n\n.doc-content main blockquote p {\n margin: 0;\n font-size: 1rem;\n}\n\n.doc-content main .warn {\n background: var(--vmz-status-warning-background, #fff7e6);\n border: 1px solid var(--vmz-status-warning-border, #ffe7ba);\n border-left: 3px solid var(--vmz-status-warning-accent, #d48806);\n color: var(--vmz-status-warning-foreground, #ad6800);\n padding: 0.875rem 1.125rem;\n border-radius: 0 8px 8px 0;\n margin: 0 0 1.25rem;\n font-size: 0.9375rem;\n line-height: 1.65;\n}\n\n.doc-content main .warn p {\n color: inherit;\n font-size: inherit;\n margin: 0;\n}\n\n/* Playground / island shells */\n.doc-content main [data-vmz-island],\n.doc-content main .doc-playground {\n border: 1px solid var(--vmz-line-default, #e5e6eb);\n border-radius: var(--vmz-radius-xs, 4px);\n background: var(--vmz-surface-fill, #fafbfc);\n padding: 0.75rem;\n margin: 0 0 1rem;\n}\n\n.doc-content main .doc-playground-source,\n.doc-content main .doc-playground-fallback {\n font-size: 0.8125rem;\n color: var(--vmz-text-muted, #6b7178);\n}\n\n/* Standalone mount emits the same .site.site--docs > .doc-body skeleton as\n the integrated shell \u2014 no second DOM shape. */\n\n.skip-link {\n position: absolute;\n left: -999px;\n top: 0.75rem;\n z-index: 50;\n padding: 0.45rem 0.75rem;\n background: var(--vmz-brand-primary, #1677ff);\n color: var(--vmz-text-on-brand, #ffffff);\n text-decoration: none;\n}\n\n.skip-link:focus {\n left: 0.75rem;\n}\n\n/* Responsive ------------------------------------------------------------ */\n\n@media (max-width: 1100px) {\n .doc-content {\n grid-template-columns: minmax(0, 1fr);\n padding: 1.75rem 1.5rem 2.5rem;\n }\n\n .toc {\n position: static;\n order: -1;\n border-left: 0;\n border-bottom: 1px solid var(--vmz-line-subtle, #f0f1f3);\n padding: 0 0 0.75rem;\n margin-bottom: 1.5rem;\n }\n}\n\n@media (max-width: 860px) {\n .site--docs > .doc-body,\n body > .doc-body {\n grid-template-columns: 1fr;\n }\n\n .doc-sidebar {\n position: static;\n max-height: none;\n border-right: 0;\n border-bottom: 1px solid var(--vmz-line-default, #e5e6eb);\n padding: 1rem;\n }\n\n .doc-content {\n padding: 1.25rem 1rem 2rem;\n }\n\n .doc-content main h1 {\n font-size: 1.375rem;\n }\n\n .doc-content main pre {\n font-size: 0.75rem;\n }\n}\n";