@vmz/vmz 0.1.13 → 0.1.14

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 (51) hide show
  1. package/dist/build-assemble.js +18 -7
  2. package/dist/cdn-policy.js +5 -4
  3. package/dist/cli.js +106 -38
  4. package/dist/content-addressed-assets.d.ts +23 -2
  5. package/dist/content-addressed-assets.js +178 -32
  6. package/dist/delivery-profile.d.ts +30 -2
  7. package/dist/delivery-profile.js +139 -5
  8. package/dist/dev-session.d.ts +1 -0
  9. package/dist/dev-session.js +269 -65
  10. package/dist/dev-watch-roots.d.ts +80 -0
  11. package/dist/dev-watch-roots.js +245 -0
  12. package/dist/document-build.d.ts +2 -1
  13. package/dist/document-build.js +104 -82
  14. package/dist/document-cmd.js +2 -9
  15. package/dist/document-enrich.d.ts +2 -1
  16. package/dist/document-enrich.js +15 -4
  17. package/dist/document-integrate.js +29 -21
  18. package/dist/document-layout-render.d.ts +20 -0
  19. package/dist/document-layout-render.js +87 -0
  20. package/dist/document-routing-config.d.ts +13 -0
  21. package/dist/document-routing-config.js +40 -0
  22. package/dist/embedded-packaging.js +2 -1
  23. package/dist/index.d.ts +18 -2
  24. package/dist/index.js +63 -4
  25. package/dist/locale-check.js +28 -61
  26. package/dist/locale-cmd.js +10 -42
  27. package/dist/locale-route-emit.d.ts +4 -1
  28. package/dist/locale-route-emit.js +7 -32
  29. package/dist/locale-router.js +26 -8
  30. package/dist/native-addon.d.ts +9 -0
  31. package/dist/native-addon.js +84 -0
  32. package/dist/pack.js +3 -2
  33. package/dist/pretty-json.d.ts +19 -0
  34. package/dist/pretty-json.js +43 -0
  35. package/dist/production-observability.js +3 -2
  36. package/dist/production-test-pack.js +4 -3
  37. package/dist/public-static-assets.d.ts +24 -0
  38. package/dist/public-static-assets.js +140 -0
  39. package/dist/release-pack.js +5 -18
  40. package/dist/route-path.d.ts +35 -0
  41. package/dist/route-path.js +77 -0
  42. package/dist/server-artifact.js +4 -3
  43. package/dist/site-delivery.js +4 -3
  44. package/dist/site-favicon.d.ts +31 -0
  45. package/dist/site-favicon.js +140 -0
  46. package/dist/static-emit.d.ts +21 -0
  47. package/dist/static-emit.js +134 -97
  48. package/dist/test-cmd.js +2 -1
  49. package/dist/wechat-packaging.d.ts +22 -0
  50. package/dist/wechat-packaging.js +59 -0
  51. package/package.json +12 -12
@@ -14,9 +14,10 @@ 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 } from './index.js';
17
+ import { createWorkspace, resolveNativePath } from './index.js';
18
18
  import { emitLocaleRuntimeModules, localeHasErrors } from './locale-check.js';
19
19
  import { log } from './log.js';
20
+ import { coalesceRootBurst, collectDevWatchRoots, classifyWatchRoot, isDependencyPath, mergeDirtySets } from './dev-watch-roots.js';
20
21
  import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
21
22
  /**
22
23
  * @typedef {object} DevSessionOptions
@@ -25,6 +26,7 @@ import { diffFingerprints, fileFingerprintMap } from './watch-diff.js';
25
26
  * @property {string} [host]
26
27
  * @property {number} [port]
27
28
  * @property {number} [pollMs]
29
+ * @property {'browser' | 'mini-program-wechat'} [target]
28
30
  * @property {AbortSignal} [signal]
29
31
  * @property {typeof createWorkspace} [createWorkspaceFn]
30
32
  * @property {(opts: { project: string, outDir: string, host: string, port: number }) => import('node:child_process').ChildProcess} [spawnHostFn]
@@ -39,6 +41,7 @@ export function createDevSession(options) {
39
41
  const host = options.host ?? '127.0.0.1';
40
42
  const port = options.port ?? 5173;
41
43
  const pollMs = Math.max(50, options.pollMs ?? 300);
44
+ const wechatPreview = options.target === 'mini-program-wechat';
42
45
  const createWs = options.createWorkspaceFn ?? createWorkspace;
43
46
  const spawnHost = options.spawnHostFn ?? defaultSpawnHost;
44
47
  const softReload = options.softReloadFn ?? defaultSoftReload;
@@ -76,6 +79,33 @@ export function createDevSession(options) {
76
79
  log.info(`${label} ok (${mode}; chunks=[${chunks}]; ${(report.emitted ?? []).length} emitted)`);
77
80
  return true;
78
81
  }
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
+ }
79
109
  async function waitHostReady(timeoutMs = 8000) {
80
110
  const start = Date.now();
81
111
  while (Date.now() - start < timeoutMs) {
@@ -143,18 +173,38 @@ export function createDevSession(options) {
143
173
  throw new Error('vmz dev: integrated document build failed');
144
174
  }
145
175
  }
146
- const hostJs = path.join(outDir, 'vmz-serve-host.mjs');
147
- if (!existsSync(hostJs)) {
148
- throw new Error(`vmz dev: missing ${hostJs}`);
176
+ if (wechatPreview) {
177
+ if (!packWechatPreview()) {
178
+ throw new Error('vmz dev: wechat pack failed');
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
+ });
149
190
  }
150
- child = spawnHost({ project, outDir, host, port });
151
- await waitHostReady().catch((err) => {
152
- log.warn(String(err));
153
- });
154
191
  const docsRoot = path.join(project, 'documents');
155
192
  const localesRoot = path.join(project, 'locales');
156
- const watchRoots = [src].concat(existsSync(docsRoot) ? [docsRoot] : []).concat(existsSync(localesRoot) ? [localesRoot] : []);
157
- log.info(`dev http://${host}:${port} (watching ${watchRoots.join(', ')})`);
193
+ const designsRoot = path.join(project, 'designs');
194
+ const watched = collectDevWatchRoots({ project, outDir });
195
+ /** @type {string[]} */
196
+ const watchRoots = [...watched.roots];
197
+ /** @type {string[]} */
198
+ let dependencyRoots = [...watched.dependencyRoots];
199
+ if (wechatPreview) {
200
+ log.info(`dev → WeChat DevTools (watching ${watchRoots.join(', ')}; keep dist/wechat open)`);
201
+ }
202
+ else {
203
+ log.info(`dev → http://${host}:${port} (watching ${watchRoots.join(', ')})`);
204
+ }
205
+ if (dependencyRoots.length) {
206
+ log.info(`dev dependency watch roots (${dependencyRoots.length}): ${dependencyRoots.join(', ')}`);
207
+ }
158
208
  /** @type {Map<string, Map<string, string>>} */
159
209
  const fingerprints = new Map();
160
210
  for (const root of watchRoots) {
@@ -166,18 +216,57 @@ export function createDevSession(options) {
166
216
  };
167
217
  signal?.addEventListener('abort', onAbort, { once: true });
168
218
  /**
169
- * Wait until src/ stops changing (multi-file agent edits).
219
+ * Scan all watch roots into a batch. Does not update fingerprints.
170
220
  */
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);
221
+ function scanBatch() {
222
+ /** @type {{ srcChanged: string[], srcDeleted: string[], depChanged: string[], depDeleted: string[], designsChanged: string[], designsDeleted: string[], docsDirty: boolean, localesDirty: boolean, designsDirty: boolean }} */
223
+ const batch = {
224
+ srcChanged: [],
225
+ srcDeleted: [],
226
+ depChanged: [],
227
+ depDeleted: [],
228
+ designsChanged: [],
229
+ designsDeleted: [],
230
+ docsDirty: false,
231
+ localesDirty: false,
232
+ designsDirty: false,
233
+ };
234
+ const watchCtx = { src, docsRoot, localesRoot, designsRoot, dependencyRoots };
235
+ for (const root of watchRoots) {
236
+ const prev = fingerprints.get(root) || new Map();
237
+ const next = fileFingerprintMap(root);
177
238
  const diff = diffFingerprints(prev, next);
178
239
  if (!diff.changed.length && !diff.deleted.length)
179
- break;
180
- fingerprints.set(src, next);
240
+ continue;
241
+ const kind = classifyWatchRoot(root, watchCtx);
242
+ if (kind === 'src') {
243
+ batch.srcChanged.push(...diff.changed);
244
+ batch.srcDeleted.push(...diff.deleted);
245
+ }
246
+ else if (kind === 'locales') {
247
+ batch.localesDirty = true;
248
+ }
249
+ else if (kind === 'docs') {
250
+ batch.docsDirty = true;
251
+ }
252
+ else if (kind === 'designs') {
253
+ batch.designsDirty = true;
254
+ batch.designsChanged.push(...diff.changed);
255
+ batch.designsDeleted.push(...diff.deleted);
256
+ }
257
+ else if (kind === 'dep') {
258
+ batch.depChanged.push(...diff.changed);
259
+ batch.depDeleted.push(...diff.deleted);
260
+ }
261
+ else {
262
+ batch.docsDirty = true;
263
+ }
264
+ }
265
+ return batch;
266
+ }
267
+ function commitFingerprints() {
268
+ for (const root of watchRoots) {
269
+ fingerprints.set(root, fileFingerprintMap(root));
181
270
  }
182
271
  }
183
272
  try {
@@ -185,65 +274,125 @@ export function createDevSession(options) {
185
274
  await sleep(pollMs);
186
275
  if (stopped || signal?.aborted)
187
276
  break;
188
- if (child && child.exitCode != null) {
277
+ if (!wechatPreview && child && child.exitCode != null) {
189
278
  log.warn(`serve-host exited (${child.exitCode}) — respawning…`);
190
279
  child = spawnHost({ project, outDir, host, port });
191
280
  await waitHostReady().catch(() => { });
192
281
  continue;
193
282
  }
194
- /** @type {{ srcChanged: string[], srcDeleted: string[], docsDirty: boolean, localesDirty: boolean }} */
195
- let batch = { srcChanged: [], srcDeleted: [], docsDirty: false, localesDirty: false };
283
+ let batch;
196
284
  try {
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
- }
285
+ batch = scanBatch();
213
286
  }
214
287
  catch (err) {
215
288
  log.warn('watch error:', err);
216
289
  continue;
217
290
  }
218
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
291
+ const srcDirty = batch.srcChanged.length + batch.srcDeleted.length;
292
+ const depDirty = batch.depChanged.length + batch.depDeleted.length;
293
+ const designsDirty = batch.designsDirty || batch.designsChanged.length + batch.designsDeleted.length;
294
+ if (!srcDirty && !depDirty && !batch.docsDirty && !batch.localesDirty && !designsDirty)
219
295
  continue;
220
- if (batch.srcChanged.length + batch.srcDeleted.length > 1) {
221
- await coalesceSrcBurst();
296
+ // Coalesce multi-file bursts without dropping the initial dirty set.
297
+ if (srcDirty > 1) {
298
+ const coalesced = await coalesceRootBurst(src, fingerprints, {
299
+ changed: batch.srcChanged,
300
+ deleted: batch.srcDeleted,
301
+ });
302
+ batch.srcChanged = coalesced.changed;
303
+ batch.srcDeleted = coalesced.deleted;
222
304
  }
223
- else {
305
+ if (depDirty > 1) {
306
+ // Coalesce each dirty dependency root independently, then merge.
307
+ /** @type {{ changed: string[], deleted: string[] }} */
308
+ let acc = { changed: [...batch.depChanged], deleted: [...batch.depDeleted] };
309
+ for (const root of dependencyRoots) {
310
+ const prev = fingerprints.get(root) || new Map();
311
+ const next = fileFingerprintMap(root);
312
+ const peek = diffFingerprints(prev, next);
313
+ if (peek.changed.length + peek.deleted.length <= 1 && !acc.changed.some((f) => isDependencyPath(f, [root]))) {
314
+ continue;
315
+ }
316
+ const coalesced = await coalesceRootBurst(root, fingerprints, {
317
+ changed: acc.changed.filter((f) => isDependencyPath(f, [root])),
318
+ deleted: acc.deleted.filter((f) => isDependencyPath(f, [root])),
319
+ });
320
+ const others = {
321
+ changed: acc.changed.filter((f) => !isDependencyPath(f, [root])),
322
+ deleted: acc.deleted.filter((f) => !isDependencyPath(f, [root])),
323
+ };
324
+ acc = mergeDirtySets(others, coalesced);
325
+ }
326
+ batch.depChanged = acc.changed;
327
+ batch.depDeleted = acc.deleted;
328
+ }
329
+ else if (srcDirty === 1 || depDirty === 1) {
224
330
  await sleep(200);
225
331
  }
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;
332
+ // Refresh residual dirty after settle (without discarding coalesced sets).
333
+ const residual = scanBatch();
334
+ {
335
+ const srcMerged = mergeDirtySets({ changed: batch.srcChanged, deleted: batch.srcDeleted }, { changed: residual.srcChanged, deleted: residual.srcDeleted });
336
+ batch.srcChanged = srcMerged.changed;
337
+ batch.srcDeleted = srcMerged.deleted;
338
+ const depMerged = mergeDirtySets({ changed: batch.depChanged, deleted: batch.depDeleted }, { changed: residual.depChanged, deleted: residual.depDeleted });
339
+ batch.depChanged = depMerged.changed;
340
+ batch.depDeleted = depMerged.deleted;
341
+ batch.docsDirty = batch.docsDirty || residual.docsDirty;
342
+ batch.localesDirty = batch.localesDirty || residual.localesDirty;
343
+ batch.designsDirty = batch.designsDirty || residual.designsDirty;
344
+ const designsMerged = mergeDirtySets({ changed: batch.designsChanged, deleted: batch.designsDeleted }, { changed: residual.designsChanged, deleted: residual.designsDeleted });
345
+ batch.designsChanged = designsMerged.changed;
346
+ batch.designsDeleted = designsMerged.deleted;
347
+ }
348
+ commitFingerprints();
349
+ if (!batch.srcChanged.length &&
350
+ !batch.srcDeleted.length &&
351
+ !batch.depChanged.length &&
352
+ !batch.depDeleted.length &&
353
+ !batch.docsDirty &&
354
+ !batch.localesDirty &&
355
+ !(batch.designsDirty || batch.designsChanged.length || batch.designsDeleted.length)) {
356
+ continue;
357
+ }
358
+ // Dependency changes: conservative full rebuild + full reload (v0.1.5).
359
+ if (batch.depChanged.length || batch.depDeleted.length) {
360
+ log.info(`dependency change detected (${batch.depChanged.length} update, ${batch.depDeleted.length} delete) — full rebuild…`);
361
+ const report = rebuild();
362
+ if (!printReport(report, 'rebuild')) {
363
+ log.warn('rebuild failed — keeping previous server');
364
+ continue;
365
+ }
366
+ // Refresh watch roots in case the graph gained new packages.
367
+ const refreshed = collectDevWatchRoots({ project, outDir });
368
+ for (const r of refreshed.roots) {
369
+ if (!watchRoots.includes(r)) {
370
+ watchRoots.push(r);
371
+ fingerprints.set(r, fileFingerprintMap(r));
372
+ }
235
373
  }
236
- else if (root === localesRoot) {
237
- if (diff.changed.length || diff.deleted.length)
238
- batch.localesDirty = true;
374
+ dependencyRoots = [...refreshed.dependencyRoots];
375
+ if (batch.docsDirty || projectHasDocuments(project)) {
376
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
377
+ if (!docs.ok)
378
+ log.warn('document mount rebuild failed — keeping previous docs');
239
379
  }
240
- else if (diff.changed.length || diff.deleted.length) {
241
- batch.docsDirty = true;
380
+ if (wechatPreview) {
381
+ if (!packWechatPreview())
382
+ log.warn('wechat pack failed — keeping previous dist/wechat');
383
+ continue;
242
384
  }
243
- }
244
- if (!batch.srcChanged.length && !batch.srcDeleted.length && !batch.docsDirty && !batch.localesDirty)
385
+ const kind = await reloadAfterBuild({
386
+ affectedChunks: report.affectedChunks ?? [],
387
+ seedChunks: report.seedChunks ?? [],
388
+ emitted: report.emitted ?? [],
389
+ full: true,
390
+ islandHmr: false,
391
+ });
392
+ log.info(kind === 'respawn' ? 'reload ok (respawned; deps)' : 'soft reload ok (full page; deps)');
245
393
  continue;
246
- let needFullReload = batch.docsDirty;
394
+ }
395
+ let needFullReload = batch.docsDirty || batch.designsDirty;
247
396
  if (batch.srcChanged.length || batch.srcDeleted.length) {
248
397
  log.info(`change detected (${batch.srcChanged.length} update, ${batch.srcDeleted.length} delete) — affected rebuild…`);
249
398
  const changes = [
@@ -264,6 +413,12 @@ export function createDevSession(options) {
264
413
  needFullReload = true;
265
414
  }
266
415
  }
416
+ if (wechatPreview) {
417
+ if (!packWechatPreview()) {
418
+ log.warn('wechat pack failed — keeping previous dist/wechat');
419
+ }
420
+ continue;
421
+ }
267
422
  const kind = await reloadAfterBuild({
268
423
  affectedChunks: report.affectedChunks ?? [],
269
424
  seedChunks: report.seedChunks ?? [],
@@ -280,16 +435,56 @@ export function createDevSession(options) {
280
435
  : 'soft reload ok');
281
436
  continue;
282
437
  }
438
+ if (batch.designsChanged.length || batch.designsDeleted.length || batch.designsDirty) {
439
+ log.info(`designs change detected (${batch.designsChanged.length} update, ${batch.designsDeleted.length} delete) — rebuilding styles…`);
440
+ const changes = [
441
+ ...batch.designsChanged.map((p) => ({ path: p, kind: /** @type {'update'} */ ('update') })),
442
+ ...batch.designsDeleted.map((p) => ({ path: p, kind: /** @type {'delete'} */ ('delete') })),
443
+ ];
444
+ const report = rebuild(changes.length ? changes : undefined);
445
+ if (!printReport(report, 'rebuild')) {
446
+ log.warn('designs rebuild failed — keeping previous styles');
447
+ continue;
448
+ }
449
+ if (batch.docsDirty || projectHasDocuments(project)) {
450
+ const docs = await buildIntegratedDocuments({ projectRoot: project, outDir });
451
+ if (!docs.ok)
452
+ log.warn('document mount rebuild failed — keeping previous docs');
453
+ }
454
+ if (wechatPreview) {
455
+ if (!packWechatPreview())
456
+ log.warn('wechat pack failed — keeping previous dist/wechat');
457
+ continue;
458
+ }
459
+ const kind = await reloadAfterBuild({
460
+ affectedChunks: report.affectedChunks ?? [],
461
+ seedChunks: report.seedChunks ?? [],
462
+ emitted: report.emitted ?? [],
463
+ full: true,
464
+ islandHmr: false,
465
+ });
466
+ log.info(kind === 'respawn' ? 'reload ok (respawned; designs)' : 'soft reload ok (full page; designs)');
467
+ continue;
468
+ }
283
469
  if (batch.localesDirty) {
284
470
  log.info('locales change detected — re-emitting #locales runtime…');
285
471
  if (!emitLocales()) {
286
472
  log.warn('locale runtime emit failed — keeping previous modules');
287
473
  continue;
288
474
  }
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;
475
+ if (wechatPreview) {
476
+ if (!packWechatPreview()) {
477
+ log.warn('wechat pack failed — keeping previous dist/wechat');
478
+ }
479
+ if (!batch.docsDirty)
480
+ continue;
481
+ }
482
+ else {
483
+ const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
484
+ log.info(kind === 'respawn' ? 'reload ok (respawned; locales)' : 'soft reload ok (full page; locales)');
485
+ if (!batch.docsDirty)
486
+ continue;
487
+ }
293
488
  }
294
489
  if (batch.docsDirty) {
295
490
  log.info('documents change detected — rebuilding document mount…');
@@ -298,8 +493,15 @@ export function createDevSession(options) {
298
493
  log.warn('document mount rebuild failed — keeping previous docs');
299
494
  continue;
300
495
  }
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)');
496
+ if (wechatPreview) {
497
+ if (!packWechatPreview()) {
498
+ log.warn('wechat pack failed — keeping previous dist/wechat');
499
+ }
500
+ }
501
+ else {
502
+ const kind = await reloadAfterBuild({ full: true, islandHmr: false, emitted: [] });
503
+ log.info(kind === 'respawn' ? 'reload ok (respawned; docs)' : 'soft reload ok (full page; docs)');
504
+ }
303
505
  }
304
506
  }
305
507
  }
@@ -350,6 +552,8 @@ function defaultSpawnHost(opts) {
350
552
  VMZ_PORT: String(opts.port),
351
553
  VMZ_HOST: opts.host,
352
554
  VMZ_DEV: '1',
555
+ VMZ_PROJECT_ROOT: opts.project,
556
+ VMZ_NATIVE_NODE: resolveNativePath(),
353
557
  },
354
558
  stdio: ['ignore', 'inherit', 'inherit'],
355
559
  });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Dev watch helpers: coalesce multi-file bursts without dropping dirty set,
3
+ * and derive extra watch roots from the compile graph (deployment unit sources).
4
+ */
5
+ /**
6
+ * @typedef {{ changed: string[], deleted: string[] }} DirtySet
7
+ */
8
+ /**
9
+ * Merge dirty sets: later change cancels delete and vice versa.
10
+ * @param {DirtySet} a
11
+ * @param {DirtySet} b
12
+ * @returns {DirtySet}
13
+ */
14
+ export declare function mergeDirtySets(a: any, b: any): {
15
+ changed: unknown[];
16
+ deleted: unknown[];
17
+ };
18
+ /**
19
+ * Wait until `root` stops changing; return the **accumulated** dirty set since `initial`.
20
+ * Updates `fingerprints` for `root` as it polls. Does **not** discard `initial`.
21
+ *
22
+ * @param {string} root
23
+ * @param {Map<string, Map<string, string>>} fingerprints
24
+ * @param {DirtySet} initial
25
+ * @param {{ sleep?: (ms: number) => Promise<void>, maxRounds?: number, settleMs?: number }} [opts]
26
+ * @returns {Promise<DirtySet>}
27
+ */
28
+ export declare function coalesceRootBurst(root: any, fingerprints: any, initial: any, opts?: {}): Promise<{
29
+ changed: any[];
30
+ deleted: any[];
31
+ }>;
32
+ /**
33
+ * Walk up from a file to find a package.json directory.
34
+ * @param {string} file
35
+ * @returns {string | null}
36
+ */
37
+ export declare function findPackageRoot(file: any): string;
38
+ /**
39
+ * Prefer package/src when present; otherwise the directory containing the source file.
40
+ * @param {string} sourceFile
41
+ * @returns {string | null}
42
+ */
43
+ export declare function watchRootForSourceFile(sourceFile: any): string;
44
+ /**
45
+ * Absolute roots for workspace / file / link deps that have a src tree.
46
+ * @param {string} project
47
+ * @returns {string[]}
48
+ */
49
+ export declare function localLinkDependencyRoots(project: any): any[];
50
+ /**
51
+ * Collect watch roots: project src/locales/documents + compile-graph external sources
52
+ * + local link/workspace package roots. Never adds a bare registry node_modules tree.
53
+ *
54
+ * @param {{ project: string, outDir: string }} opts
55
+ * @returns {{ roots: string[], dependencyRoots: string[], applicationRoots: string[] }}
56
+ */
57
+ export declare function collectDevWatchRoots(opts: any): {
58
+ roots: any[];
59
+ dependencyRoots: unknown[];
60
+ applicationRoots: any[];
61
+ };
62
+ /**
63
+ * Classify which watch bucket a root belongs to.
64
+ * @param {string} root
65
+ * @param {{
66
+ * src: string,
67
+ * docsRoot: string,
68
+ * localesRoot: string,
69
+ * designsRoot: string,
70
+ * dependencyRoots: string[],
71
+ * }} ctx
72
+ * @returns {'src' | 'locales' | 'docs' | 'designs' | 'dep' | 'other'}
73
+ */
74
+ export declare function classifyWatchRoot(root: any, ctx: any): "src" | "locales" | "docs" | "designs" | "dep" | "other";
75
+ /**
76
+ * Classify whether a changed file lives under a dependency watch root (not app src).
77
+ * @param {string} file
78
+ * @param {string[]} dependencyRoots
79
+ */
80
+ export declare function isDependencyPath(file: any, dependencyRoots: any): any;