@vmz/vmz 0.1.11 → 0.1.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build-assemble.js +3 -4
- package/dist/cdn-policy.js +4 -5
- package/dist/cli.js +15 -50
- package/dist/content-addressed-assets.d.ts +2 -8
- package/dist/content-addressed-assets.js +34 -110
- package/dist/delivery-profile.d.ts +1 -11
- package/dist/delivery-profile.js +1 -70
- package/dist/dev-session.d.ts +0 -1
- package/dist/dev-session.js +64 -220
- package/dist/document-build.js +34 -61
- package/dist/document-cmd.js +9 -2
- package/dist/document-enrich.d.ts +1 -2
- package/dist/document-enrich.js +4 -15
- package/dist/document-integrate.js +21 -28
- package/dist/embedded-packaging.js +1 -2
- package/dist/index.d.ts +1 -15
- package/dist/index.js +3 -58
- package/dist/locale-check.js +61 -28
- package/dist/locale-cmd.js +42 -10
- package/dist/locale-route-emit.d.ts +1 -4
- package/dist/locale-route-emit.js +32 -7
- package/dist/locale-router.js +8 -26
- package/dist/pack.js +2 -3
- package/dist/production-observability.js +2 -3
- package/dist/production-test-pack.js +3 -4
- package/dist/release-pack.js +18 -5
- package/dist/server-artifact.js +3 -4
- package/dist/site-delivery.js +3 -4
- package/dist/static-emit.js +78 -116
- package/dist/test-cmd.js +1 -2
- package/package.json +12 -12
- package/dist/dev-watch-roots.d.ts +0 -67
- package/dist/dev-watch-roots.js +0 -220
- package/dist/document-host-chrome.d.ts +0 -28
- package/dist/document-host-chrome.js +0 -128
- package/dist/native-addon.d.ts +0 -9
- package/dist/native-addon.js +0 -84
- package/dist/pretty-json.d.ts +0 -19
- package/dist/pretty-json.js +0 -43
- package/dist/route-path.d.ts +0 -35
- package/dist/route-path.js +0 -77
- package/dist/wechat-packaging.d.ts +0 -22
- package/dist/wechat-packaging.js +0 -59
package/dist/dev-session.js
CHANGED
|
@@ -14,10 +14,9 @@ import { spawn } from 'node:child_process';
|
|
|
14
14
|
import { existsSync } from 'node:fs';
|
|
15
15
|
import path from 'node:path';
|
|
16
16
|
import { buildIntegratedDocuments, projectHasDocuments } from './document-integrate.js';
|
|
17
|
-
import { createWorkspace
|
|
17
|
+
import { createWorkspace } from './index.js';
|
|
18
18
|
import { emitLocaleRuntimeModules, localeHasErrors } from './locale-check.js';
|
|
19
19
|
import { log } from './log.js';
|
|
20
|
-
import { coalesceRootBurst, collectDevWatchRoots, isDependencyPath, mergeDirtySets } from './dev-watch-roots.js';
|
|
21
20
|
import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
|
|
22
21
|
/**
|
|
23
22
|
* @typedef {object} DevSessionOptions
|
|
@@ -26,7 +25,6 @@ import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
|
|
|
26
25
|
* @property {string} [host]
|
|
27
26
|
* @property {number} [port]
|
|
28
27
|
* @property {number} [pollMs]
|
|
29
|
-
* @property {'browser' | 'mini-program-wechat'} [target]
|
|
30
28
|
* @property {AbortSignal} [signal]
|
|
31
29
|
* @property {typeof createWorkspace} [createWorkspaceFn]
|
|
32
30
|
* @property {(opts: { project: string, outDir: string, host: string, port: number }) => import('node:child_process').ChildProcess} [spawnHostFn]
|
|
@@ -41,7 +39,6 @@ export function createDevSession(options) {
|
|
|
41
39
|
const host = options.host ?? '127.0.0.1';
|
|
42
40
|
const port = options.port ?? 5173;
|
|
43
41
|
const pollMs = Math.max(50, options.pollMs ?? 300);
|
|
44
|
-
const wechatPreview = options.target === 'mini-program-wechat';
|
|
45
42
|
const createWs = options.createWorkspaceFn ?? createWorkspace;
|
|
46
43
|
const spawnHost = options.spawnHostFn ?? defaultSpawnHost;
|
|
47
44
|
const softReload = options.softReloadFn ?? defaultSoftReload;
|
|
@@ -79,33 +76,6 @@ export function createDevSession(options) {
|
|
|
79
76
|
log.info(`${label} ok (${mode}; chunks=[${chunks}]; ${(report.emitted ?? []).length} emitted)`);
|
|
80
77
|
return true;
|
|
81
78
|
}
|
|
82
|
-
/**
|
|
83
|
-
* Rewrite `dist/wechat` so WeChat DevTools (already open) recompiles WXSS.
|
|
84
|
-
* Preview is vendor compile, not the browser serve-host.
|
|
85
|
-
*/
|
|
86
|
-
function packWechatPreview() {
|
|
87
|
-
if (typeof ws.lowerMiniprogramWechatPackaging !== 'function') {
|
|
88
|
-
log.error('wechat pack: workspace missing lowerMiniprogramWechatPackaging');
|
|
89
|
-
return false;
|
|
90
|
-
}
|
|
91
|
-
let report;
|
|
92
|
-
try {
|
|
93
|
-
const raw = ws.lowerMiniprogramWechatPackaging();
|
|
94
|
-
report = typeof raw === 'string' ? JSON.parse(raw) : raw;
|
|
95
|
-
}
|
|
96
|
-
catch (err) {
|
|
97
|
-
log.error(`wechat pack failed: ${err}`);
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
log.diagnostics(report.diagnostics ?? []);
|
|
101
|
-
if (report.status !== 'ready') {
|
|
102
|
-
log.error(`wechat pack ${report.status || 'failed'}`);
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
105
|
-
const packRoot = report.packRoot || 'dist/wechat';
|
|
106
|
-
log.info(`wechat pack ok → ${path.join(project, packRoot)} (WeChat DevTools compiles WXSS here)`);
|
|
107
|
-
return true;
|
|
108
|
-
}
|
|
109
79
|
async function waitHostReady(timeoutMs = 8000) {
|
|
110
80
|
const start = Date.now();
|
|
111
81
|
while (Date.now() - start < timeoutMs) {
|
|
@@ -173,37 +143,18 @@ export function createDevSession(options) {
|
|
|
173
143
|
throw new Error('vmz dev: integrated document build failed');
|
|
174
144
|
}
|
|
175
145
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
else {
|
|
182
|
-
const hostJs = path.join(outDir, 'vmz-serve-host.mjs');
|
|
183
|
-
if (!existsSync(hostJs)) {
|
|
184
|
-
throw new Error(`vmz dev: missing ${hostJs}`);
|
|
185
|
-
}
|
|
186
|
-
child = spawnHost({ project, outDir, host, port });
|
|
187
|
-
await waitHostReady().catch((err) => {
|
|
188
|
-
log.warn(String(err));
|
|
189
|
-
});
|
|
146
|
+
const hostJs = path.join(outDir, 'vmz-serve-host.mjs');
|
|
147
|
+
if (!existsSync(hostJs)) {
|
|
148
|
+
throw new Error(`vmz dev: missing ${hostJs}`);
|
|
190
149
|
}
|
|
150
|
+
child = spawnHost({ project, outDir, host, port });
|
|
151
|
+
await waitHostReady().catch((err) => {
|
|
152
|
+
log.warn(String(err));
|
|
153
|
+
});
|
|
191
154
|
const docsRoot = path.join(project, 'documents');
|
|
192
155
|
const localesRoot = path.join(project, 'locales');
|
|
193
|
-
const
|
|
194
|
-
|
|
195
|
-
const watchRoots = [...watched.roots];
|
|
196
|
-
/** @type {string[]} */
|
|
197
|
-
let dependencyRoots = [...watched.dependencyRoots];
|
|
198
|
-
if (wechatPreview) {
|
|
199
|
-
log.info(`dev → WeChat DevTools (watching ${watchRoots.join(', ')}; keep dist/wechat open)`);
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
log.info(`dev → http://${host}:${port} (watching ${watchRoots.join(', ')})`);
|
|
203
|
-
}
|
|
204
|
-
if (dependencyRoots.length) {
|
|
205
|
-
log.info(`dev dependency watch roots (${dependencyRoots.length}): ${dependencyRoots.join(', ')}`);
|
|
206
|
-
}
|
|
156
|
+
const watchRoots = [src].concat(existsSync(docsRoot) ? [docsRoot] : []).concat(existsSync(localesRoot) ? [localesRoot] : []);
|
|
157
|
+
log.info(`dev → http://${host}:${port} (watching ${watchRoots.join(', ')})`);
|
|
207
158
|
/** @type {Map<string, Map<string, string>>} */
|
|
208
159
|
const fingerprints = new Map();
|
|
209
160
|
for (const root of watchRoots) {
|
|
@@ -215,48 +166,18 @@ export function createDevSession(options) {
|
|
|
215
166
|
};
|
|
216
167
|
signal?.addEventListener('abort', onAbort, { once: true });
|
|
217
168
|
/**
|
|
218
|
-
*
|
|
169
|
+
* Wait until src/ stops changing (multi-file agent edits).
|
|
219
170
|
*/
|
|
220
|
-
function
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
depDeleted: [],
|
|
227
|
-
docsDirty: false,
|
|
228
|
-
localesDirty: false,
|
|
229
|
-
};
|
|
230
|
-
for (const root of watchRoots) {
|
|
231
|
-
const prev = fingerprints.get(root) || new Map();
|
|
232
|
-
const next = fileFingerprintMap(root);
|
|
171
|
+
async function coalesceSrcBurst() {
|
|
172
|
+
let guard = 0;
|
|
173
|
+
while (guard++ < 20) {
|
|
174
|
+
await sleep(220);
|
|
175
|
+
const prev = fingerprints.get(src) || new Map();
|
|
176
|
+
const next = fileFingerprintMap(src);
|
|
233
177
|
const diff = diffFingerprints(prev, next);
|
|
234
178
|
if (!diff.changed.length && !diff.deleted.length)
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
batch.srcChanged.push(...diff.changed);
|
|
238
|
-
batch.srcDeleted.push(...diff.deleted);
|
|
239
|
-
}
|
|
240
|
-
else if (root === localesRoot) {
|
|
241
|
-
batch.localesDirty = true;
|
|
242
|
-
}
|
|
243
|
-
else if (root === docsRoot) {
|
|
244
|
-
batch.docsDirty = true;
|
|
245
|
-
}
|
|
246
|
-
else if (dependencyRoots.includes(root)) {
|
|
247
|
-
batch.depChanged.push(...diff.changed);
|
|
248
|
-
batch.depDeleted.push(...diff.deleted);
|
|
249
|
-
}
|
|
250
|
-
else {
|
|
251
|
-
// designs or other application roots → treat like docs (full reload)
|
|
252
|
-
batch.docsDirty = true;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return batch;
|
|
256
|
-
}
|
|
257
|
-
function commitFingerprints() {
|
|
258
|
-
for (const root of watchRoots) {
|
|
259
|
-
fingerprints.set(root, fileFingerprintMap(root));
|
|
179
|
+
break;
|
|
180
|
+
fingerprints.set(src, next);
|
|
260
181
|
}
|
|
261
182
|
}
|
|
262
183
|
try {
|
|
@@ -264,118 +185,64 @@ export function createDevSession(options) {
|
|
|
264
185
|
await sleep(pollMs);
|
|
265
186
|
if (stopped || signal?.aborted)
|
|
266
187
|
break;
|
|
267
|
-
if (
|
|
188
|
+
if (child && child.exitCode != null) {
|
|
268
189
|
log.warn(`serve-host exited (${child.exitCode}) — respawning…`);
|
|
269
190
|
child = spawnHost({ project, outDir, host, port });
|
|
270
191
|
await waitHostReady().catch(() => { });
|
|
271
192
|
continue;
|
|
272
193
|
}
|
|
273
|
-
|
|
194
|
+
/** @type {{ srcChanged: string[], srcDeleted: string[], docsDirty: boolean, localesDirty: boolean }} */
|
|
195
|
+
let batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
|
|
274
196
|
try {
|
|
275
|
-
|
|
197
|
+
for (const root of watchRoots) {
|
|
198
|
+
const prev = fingerprints.get(root) || new Map();
|
|
199
|
+
const next = fileFingerprintMap(root);
|
|
200
|
+
const diff = diffFingerprints(prev, next);
|
|
201
|
+
if (root === src) {
|
|
202
|
+
batch.srcChanged = diff.changed;
|
|
203
|
+
batch.srcDeleted = diff.deleted;
|
|
204
|
+
}
|
|
205
|
+
else if (root === localesRoot) {
|
|
206
|
+
if (diff.changed.length || diff.deleted.length)
|
|
207
|
+
batch.localesDirty = true;
|
|
208
|
+
}
|
|
209
|
+
else if (diff.changed.length || diff.deleted.length) {
|
|
210
|
+
batch.docsDirty = true;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
276
213
|
}
|
|
277
214
|
catch (err) {
|
|
278
215
|
log.warn('watch error:', err);
|
|
279
216
|
continue;
|
|
280
217
|
}
|
|
281
|
-
|
|
282
|
-
const depDirty = batch.depChanged.length + batch.depDeleted.length;
|
|
283
|
-
if (!srcDirty && !depDirty && !batch.docsDirty && !batch.localesDirty)
|
|
218
|
+
if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
|
|
284
219
|
continue;
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
const coalesced = await coalesceRootBurst(src, fingerprints, {
|
|
288
|
-
changed: batch.srcChanged,
|
|
289
|
-
deleted: batch.srcDeleted,
|
|
290
|
-
});
|
|
291
|
-
batch.srcChanged = coalesced.changed;
|
|
292
|
-
batch.srcDeleted = coalesced.deleted;
|
|
220
|
+
if (batch.srcChanged.length + batch.srcDeleted.length > 1) {
|
|
221
|
+
await coalesceSrcBurst();
|
|
293
222
|
}
|
|
294
|
-
|
|
295
|
-
// Coalesce each dirty dependency root independently, then merge.
|
|
296
|
-
/** @type {{ changed: string[], deleted: string[] }} */
|
|
297
|
-
let acc = { changed: [...batch.depChanged], deleted: [...batch.depDeleted] };
|
|
298
|
-
for (const root of dependencyRoots) {
|
|
299
|
-
const prev = fingerprints.get(root) || new Map();
|
|
300
|
-
const next = fileFingerprintMap(root);
|
|
301
|
-
const peek = diffFingerprints(prev, next);
|
|
302
|
-
if (peek.changed.length + peek.deleted.length <= 1 && !acc.changed.some((f) => isDependencyPath(f, [root]))) {
|
|
303
|
-
continue;
|
|
304
|
-
}
|
|
305
|
-
const coalesced = await coalesceRootBurst(root, fingerprints, {
|
|
306
|
-
changed: acc.changed.filter((f) => isDependencyPath(f, [root])),
|
|
307
|
-
deleted: acc.deleted.filter((f) => isDependencyPath(f, [root])),
|
|
308
|
-
});
|
|
309
|
-
const others = {
|
|
310
|
-
changed: acc.changed.filter((f) => !isDependencyPath(f, [root])),
|
|
311
|
-
deleted: acc.deleted.filter((f) => !isDependencyPath(f, [root])),
|
|
312
|
-
};
|
|
313
|
-
acc = mergeDirtySets(others, coalesced);
|
|
314
|
-
}
|
|
315
|
-
batch.depChanged = acc.changed;
|
|
316
|
-
batch.depDeleted = acc.deleted;
|
|
317
|
-
}
|
|
318
|
-
else if (srcDirty === 1 || depDirty === 1) {
|
|
223
|
+
else {
|
|
319
224
|
await sleep(200);
|
|
320
225
|
}
|
|
321
|
-
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
const
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
batch.docsDirty = batch.docsDirty || residual.docsDirty;
|
|
331
|
-
batch.localesDirty = batch.localesDirty || residual.localesDirty;
|
|
332
|
-
}
|
|
333
|
-
commitFingerprints();
|
|
334
|
-
if (!batch.srcChanged.length &&
|
|
335
|
-
!batch.srcDeleted.length &&
|
|
336
|
-
!batch.depChanged.length &&
|
|
337
|
-
!batch.depDeleted.length &&
|
|
338
|
-
!batch.docsDirty &&
|
|
339
|
-
!batch.localesDirty) {
|
|
340
|
-
continue;
|
|
341
|
-
}
|
|
342
|
-
// Dependency changes: conservative full rebuild + full reload (v0.1.5).
|
|
343
|
-
if (batch.depChanged.length || batch.depDeleted.length) {
|
|
344
|
-
log.info(`dependency change detected (${batch.depChanged.length} update, ${batch.depDeleted.length} delete) — full rebuild…`);
|
|
345
|
-
const report = rebuild();
|
|
346
|
-
if (!printReport(report, 'rebuild')) {
|
|
347
|
-
log.warn('rebuild failed — keeping previous server');
|
|
348
|
-
continue;
|
|
226
|
+
batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
|
|
227
|
+
for (const root of watchRoots) {
|
|
228
|
+
const prev = fingerprints.get(root) || new Map();
|
|
229
|
+
const next = fileFingerprintMap(root);
|
|
230
|
+
const diff = diffFingerprints(prev, next);
|
|
231
|
+
fingerprints.set(root, next);
|
|
232
|
+
if (root === src) {
|
|
233
|
+
batch.srcChanged = diff.changed;
|
|
234
|
+
batch.srcDeleted = diff.deleted;
|
|
349
235
|
}
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
if (!watchRoots.includes(r)) {
|
|
354
|
-
watchRoots.push(r);
|
|
355
|
-
fingerprints.set(r, fileFingerprintMap(r));
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
dependencyRoots = [...refreshed.dependencyRoots];
|
|
359
|
-
if (batch.docsDirty || projectHasDocuments(project)) {
|
|
360
|
-
const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
|
|
361
|
-
if (!docs.ok)
|
|
362
|
-
log.warn('document mount rebuild failed — keeping previous docs');
|
|
236
|
+
else if (root === localesRoot) {
|
|
237
|
+
if (diff.changed.length || diff.deleted.length)
|
|
238
|
+
batch.localesDirty = true;
|
|
363
239
|
}
|
|
364
|
-
if (
|
|
365
|
-
|
|
366
|
-
log.warn('wechat pack failed — keeping previous dist/wechat');
|
|
367
|
-
continue;
|
|
240
|
+
else if (diff.changed.length || diff.deleted.length) {
|
|
241
|
+
batch.docsDirty = true;
|
|
368
242
|
}
|
|
369
|
-
const kind = await reloadAfterBuild({
|
|
370
|
-
affectedChunks: report.affectedChunks ?? [],
|
|
371
|
-
seedChunks: report.seedChunks ?? [],
|
|
372
|
-
emitted: report.emitted ?? [],
|
|
373
|
-
full: true,
|
|
374
|
-
islandHmr: false,
|
|
375
|
-
});
|
|
376
|
-
log.info(kind === 'respawn' ? 'reload ok (respawned; deps)' : 'soft reload ok (full page; deps)');
|
|
377
|
-
continue;
|
|
378
243
|
}
|
|
244
|
+
if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
|
|
245
|
+
continue;
|
|
379
246
|
let needFullReload = batch.docsDirty;
|
|
380
247
|
if (batch.srcChanged.length || batch.srcDeleted.length) {
|
|
381
248
|
log.info(`change detected (${batch.srcChanged.length} update, ${batch.srcDeleted.length} delete) — affected rebuild…`);
|
|
@@ -397,12 +264,6 @@ export function createDevSession(options) {
|
|
|
397
264
|
needFullReload = true;
|
|
398
265
|
}
|
|
399
266
|
}
|
|
400
|
-
if (wechatPreview) {
|
|
401
|
-
if (!packWechatPreview()) {
|
|
402
|
-
log.warn('wechat pack failed — keeping previous dist/wechat');
|
|
403
|
-
}
|
|
404
|
-
continue;
|
|
405
|
-
}
|
|
406
267
|
const kind = await reloadAfterBuild({
|
|
407
268
|
affectedChunks: report.affectedChunks ?? [],
|
|
408
269
|
seedChunks: report.seedChunks ?? [],
|
|
@@ -425,19 +286,10 @@ export function createDevSession(options) {
|
|
|
425
286
|
log.warn('locale runtime emit failed — keeping previous modules');
|
|
426
287
|
continue;
|
|
427
288
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
if (!batch.docsDirty)
|
|
433
|
-
continue;
|
|
434
|
-
}
|
|
435
|
-
else {
|
|
436
|
-
const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
|
|
437
|
-
log.info(kind === 'respawn' ? 'reload ok (respawned; locales)' : 'soft reload ok (full page; locales)');
|
|
438
|
-
if (!batch.docsDirty)
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
289
|
+
const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
|
|
290
|
+
log.info(kind === 'respawn' ? 'reload ok (respawned; locales)' : 'soft reload ok (full page; locales)');
|
|
291
|
+
if (!batch.docsDirty)
|
|
292
|
+
continue;
|
|
441
293
|
}
|
|
442
294
|
if (batch.docsDirty) {
|
|
443
295
|
log.info('documents change detected — rebuilding document mount…');
|
|
@@ -446,15 +298,8 @@ export function createDevSession(options) {
|
|
|
446
298
|
log.warn('document mount rebuild failed — keeping previous docs');
|
|
447
299
|
continue;
|
|
448
300
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
log.warn('wechat pack failed — keeping previous dist/wechat');
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
else {
|
|
455
|
-
const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
|
|
456
|
-
log.info(kind === 'respawn' ? 'reload ok (respawned; docs)' : 'soft reload ok (full page; docs)');
|
|
457
|
-
}
|
|
301
|
+
const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
|
|
302
|
+
log.info(kind === 'respawn' ? 'reload ok (respawned; docs)' : 'soft reload ok (full page; docs)');
|
|
458
303
|
}
|
|
459
304
|
}
|
|
460
305
|
}
|
|
@@ -505,7 +350,6 @@ function defaultSpawnHost(opts) {
|
|
|
505
350
|
VMZ_PORT: String(opts.port),
|
|
506
351
|
VMZ_HOST: opts.host,
|
|
507
352
|
VMZ_DEV: '1',
|
|
508
|
-
VMZ_NATIVE_NODE: resolveNativePath(),
|
|
509
353
|
},
|
|
510
354
|
stdio: ['ignore', 'inherit', 'inherit'],
|
|
511
355
|
});
|
package/dist/document-build.js
CHANGED
|
@@ -8,13 +8,10 @@ import { checkDocuments, manifestHasErrors } from './document-check.js';
|
|
|
8
8
|
import { resolveDocumentDesignsCss } from './document-designs.js';
|
|
9
9
|
import { enrichDocumentContent, pageHtmlRel } from './document-enrich.js';
|
|
10
10
|
import { enrichDocumentEvidence } from './document-evidence.js';
|
|
11
|
-
import { docsRouteNone, loadLocaleCommonMessages, loadLocalesRouting, localeNonePickerScript, readSiteGithubUrl, renderHostChromeTemplate, } from './document-host-chrome.js';
|
|
12
11
|
import { artifactHrefFromHtml, buildDocumentIslands, buildDocumentSearch, collectFenceBodies, renderIslandShellsHtml, } from './document-interactive.js';
|
|
13
12
|
import { resolveMarkdownEngine } from './document-markdown.js';
|
|
14
13
|
import { DOCUMENT_VIEW_SCHEMA } from './document-schema.js';
|
|
15
14
|
import { createWorkspace } from './index.js';
|
|
16
|
-
import { requireNativeAddon } from './native-addon.js';
|
|
17
|
-
import { writePrettyJsonFile } from './pretty-json.js';
|
|
18
15
|
/**
|
|
19
16
|
* @param {{ projectRoot: string, outDir?: string, strict?: boolean, engines?: { markdown?: string } }} opts
|
|
20
17
|
*/
|
|
@@ -23,12 +20,10 @@ export async function buildDocuments(opts) {
|
|
|
23
20
|
const outDir = path.resolve(opts.outDir || path.join(projectRoot, 'dist', 'documents'));
|
|
24
21
|
const strict = Boolean(opts.strict);
|
|
25
22
|
const manifest = checkDocuments({ projectRoot, strict });
|
|
26
|
-
const routing = loadLocalesRouting(projectRoot) || { strategy: 'prefix' };
|
|
27
23
|
const engine = await resolveMarkdownEngine({ engines: opts.engines, projectRoot });
|
|
28
24
|
const enriched = enrichDocumentContent(manifest, {
|
|
29
25
|
analyzeMarkdown: engine.analyzeMarkdown,
|
|
30
26
|
projectRoot,
|
|
31
|
-
routing,
|
|
32
27
|
});
|
|
33
28
|
manifest.diagnostics = enriched.diagnostics;
|
|
34
29
|
const evidence = await enrichDocumentEvidence(manifest, {
|
|
@@ -63,8 +58,8 @@ export async function buildDocuments(opts) {
|
|
|
63
58
|
});
|
|
64
59
|
manifest.search = search;
|
|
65
60
|
manifest.islands = islands;
|
|
66
|
-
const
|
|
67
|
-
const useHostShell = Boolean(
|
|
61
|
+
const hostChrome = resolveHostSiteChrome(projectRoot);
|
|
62
|
+
const useHostShell = Boolean(hostChrome) && (manifest.mounts || []).some((m) => m.mode === 'integrated');
|
|
68
63
|
fs.mkdirSync(outDir, { recursive: true });
|
|
69
64
|
const designs = resolveDocumentDesignsCss(projectRoot);
|
|
70
65
|
/** @type {string | null} */
|
|
@@ -118,7 +113,7 @@ export async function buildDocuments(opts) {
|
|
|
118
113
|
const viewRel = path.posix.join('views', page.identity.locale, `${page.identity.pageKey === 'index' ? 'index' : page.identity.pageKey}.view.json`);
|
|
119
114
|
const viewAbs = path.join(outDir, viewRel);
|
|
120
115
|
fs.mkdirSync(path.dirname(viewAbs), { recursive: true });
|
|
121
|
-
|
|
116
|
+
fs.writeFileSync(viewAbs, JSON.stringify(view, null, 2) + '\n', 'utf8');
|
|
122
117
|
const html = renderStaticHtml({
|
|
123
118
|
title: info.title,
|
|
124
119
|
locale: page.identity.locale,
|
|
@@ -130,12 +125,7 @@ export async function buildDocuments(opts) {
|
|
|
130
125
|
htmlRel,
|
|
131
126
|
searchShellHtml: shells.searchHtml,
|
|
132
127
|
playgroundShellHtml: shells.playgroundHtml,
|
|
133
|
-
hostChrome: useHostShell
|
|
134
|
-
? renderHostChromeForLocale(projectRoot, page.identity.locale, routing, enriched.routeBase, hostChromeRaw)
|
|
135
|
-
: null,
|
|
136
|
-
routing,
|
|
137
|
-
routeBase: enriched.routeBase,
|
|
138
|
-
pageKey: page.identity.pageKey,
|
|
128
|
+
hostChrome: useHostShell ? hostChrome : null,
|
|
139
129
|
});
|
|
140
130
|
fs.writeFileSync(htmlAbs, html, 'utf8');
|
|
141
131
|
written.push({ route: info.route, htmlPath: htmlRel, viewPath: viewRel });
|
|
@@ -157,17 +147,17 @@ export async function buildDocuments(opts) {
|
|
|
157
147
|
islands: 'document.islands.json',
|
|
158
148
|
},
|
|
159
149
|
};
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
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');
|
|
164
154
|
return { ok: true, manifest: manifestOut, outDir, pages: written, search, islands };
|
|
165
155
|
}
|
|
166
156
|
/**
|
|
167
157
|
* No-JS readable static HTML: nav + main landmarks, Island shells without scripts.
|
|
168
158
|
* Integrated mounts reuse host SiteHeader/SiteFooter templates when present.
|
|
169
159
|
*/
|
|
170
|
-
function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, designsHref, htmlRel, searchShellHtml = '', playgroundShellHtml = '', hostChrome = null,
|
|
160
|
+
function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, designsHref, htmlRel, searchShellHtml = '', playgroundShellHtml = '', hostChrome = null, }) {
|
|
171
161
|
const esc = (s) => String(s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
172
162
|
const depth = htmlRel.split('/').length - 1;
|
|
173
163
|
const prefix = depth > 0 ? '../'.repeat(depth) : './';
|
|
@@ -180,9 +170,10 @@ function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, desig
|
|
|
180
170
|
}
|
|
181
171
|
if (designsHref)
|
|
182
172
|
cssHrefs.push(hostChrome ? `/${designsHref}` : prefix + designsHref);
|
|
173
|
+
const cssLink = cssHrefs.map((href) => ` <link rel="stylesheet" href="${esc(href)}" />`).join('\n') + (cssHrefs.length ? '\n' : '');
|
|
183
174
|
const navItems = nav
|
|
184
175
|
.map((n) => {
|
|
185
|
-
const href =
|
|
176
|
+
const href = relativeHref(htmlRel, n.href, route);
|
|
186
177
|
const current = n.href === route ? ' aria-current="page"' : '';
|
|
187
178
|
return ` <li><a href="${esc(href)}"${current}>${esc(n.title)}</a></li>`;
|
|
188
179
|
})
|
|
@@ -197,11 +188,17 @@ function renderStaticHtml({ title, locale, route, nav, bodyHtml, headings, desig
|
|
|
197
188
|
${navItems}
|
|
198
189
|
</ul>
|
|
199
190
|
</nav>`;
|
|
200
|
-
/** @type {string} */
|
|
201
|
-
let bodyInner;
|
|
202
191
|
if (hostChrome) {
|
|
203
192
|
const header = hostChrome.header.replace(/(<a\s+href="\/d\/?")([^>]*>文档<\/a>)/, '$1 aria-current="page"$2');
|
|
204
|
-
|
|
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">
|
|
205
202
|
<a class="skip-link" href="#main">Skip to content</a>
|
|
206
203
|
${header}
|
|
207
204
|
<div class="doc-body">
|
|
@@ -218,37 +215,35 @@ ${playgroundShellHtml}
|
|
|
218
215
|
</div>
|
|
219
216
|
${hostChrome.footer}
|
|
220
217
|
</div>
|
|
221
|
-
|
|
218
|
+
</body>
|
|
219
|
+
</html>
|
|
222
220
|
`;
|
|
223
221
|
}
|
|
224
|
-
|
|
225
|
-
|
|
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>
|
|
226
231
|
${docsNav}
|
|
227
232
|
${searchShellHtml}
|
|
228
233
|
${toc}<main id="main">
|
|
229
234
|
${bodyHtml}
|
|
230
235
|
${playgroundShellHtml}
|
|
231
236
|
</main>
|
|
237
|
+
</body>
|
|
238
|
+
</html>
|
|
232
239
|
`;
|
|
233
|
-
}
|
|
234
|
-
const native = requireNativeAddon();
|
|
235
|
-
if (typeof native.generateHtmlShell !== 'function') {
|
|
236
|
-
throw new Error('vmz native addon missing generateHtmlShell — rebuild with `pnpm napi:build`');
|
|
237
|
-
}
|
|
238
|
-
return native.generateHtmlShell({
|
|
239
|
-
title,
|
|
240
|
-
lang: locale,
|
|
241
|
-
cssHrefs,
|
|
242
|
-
bodyHtml: bodyInner,
|
|
243
|
-
bodyAttrs: ['data-vmz-hydrate', 'island-only'],
|
|
244
|
-
});
|
|
245
240
|
}
|
|
246
241
|
/**
|
|
247
242
|
* Integrated DocumentMount: reuse host SiteHeader / SiteFooter .vmz templates.
|
|
248
243
|
* @param {string} projectRoot
|
|
249
244
|
* @returns {{ header: string, footer: string } | null}
|
|
250
245
|
*/
|
|
251
|
-
function
|
|
246
|
+
function resolveHostSiteChrome(projectRoot) {
|
|
252
247
|
const headerPath = path.join(projectRoot, 'src', 'components', 'SiteHeader.vmz');
|
|
253
248
|
const footerPath = path.join(projectRoot, 'src', 'components', 'SiteFooter.vmz');
|
|
254
249
|
if (!fs.existsSync(headerPath) || !fs.existsSync(footerPath))
|
|
@@ -259,28 +254,6 @@ function resolveHostSiteChromeRaw(projectRoot) {
|
|
|
259
254
|
return null;
|
|
260
255
|
return { header, footer };
|
|
261
256
|
}
|
|
262
|
-
/**
|
|
263
|
-
* @param {string} projectRoot
|
|
264
|
-
* @param {string} localeId
|
|
265
|
-
* @param {{ strategy?: string }} routing
|
|
266
|
-
* @param {string} routeBase
|
|
267
|
-
* @param {{ header: string, footer: string }} raw
|
|
268
|
-
*/
|
|
269
|
-
function renderHostChromeForLocale(projectRoot, localeId, routing, routeBase, raw) {
|
|
270
|
-
const messages = loadLocaleCommonMessages(projectRoot, localeId);
|
|
271
|
-
const githubUrl = readSiteGithubUrl(projectRoot);
|
|
272
|
-
const docsRootHref = routing.strategy === 'none' || routing.strategy === 'domain'
|
|
273
|
-
? `${routeBase.replace(/\/$/, '')}/`
|
|
274
|
-
: `${routeBase.replace(/\/$/, '')}/${localeId}/`;
|
|
275
|
-
const guideHref = routing.strategy === 'none' || routing.strategy === 'domain'
|
|
276
|
-
? docsRouteNone(routeBase, 'guide/getting-started')
|
|
277
|
-
: `${docsRootHref}guide/getting-started`;
|
|
278
|
-
const opts = { docsRootHref, guideHref, githubUrl, routing };
|
|
279
|
-
return {
|
|
280
|
-
header: renderHostChromeTemplate(raw.header, localeId, messages, opts),
|
|
281
|
-
footer: renderHostChromeTemplate(raw.footer, localeId, messages, opts),
|
|
282
|
-
};
|
|
283
|
-
}
|
|
284
257
|
/** @param {string} filePath */
|
|
285
258
|
function extractVmzTemplateHtml(filePath) {
|
|
286
259
|
const src = fs.readFileSync(filePath, 'utf8');
|
package/dist/document-cmd.js
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|