create-mercato-app 0.6.3-develop.3876.1.d40fe4ec2d → 0.6.3-develop.3894.1.352abf4240

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-mercato-app",
3
- "version": "0.6.3-develop.3876.1.d40fe4ec2d",
3
+ "version": "0.6.3-develop.3894.1.352abf4240",
4
4
  "type": "module",
5
5
  "description": "Create a new Open Mercato application",
6
6
  "main": "./dist/index.js",
@@ -66,6 +66,17 @@ export function isIgnorableTurboShutdownLine(line) {
66
66
  || /^command (interrupted|cancelled|canceled)/i.test(plain)
67
67
  }
68
68
 
69
+ export function isIgnorableConsolidatedWatchLine(line) {
70
+ if (typeof line !== 'string') return false
71
+ const plain = normalize(line)
72
+ if (!plain.startsWith('[watch]')) return false
73
+ return /^\[watch\] consolidated watcher: /.test(plain)
74
+ || /^\[watch\] [^:]+: rebuilding\.\.\.$/.test(plain)
75
+ || /^\[watch\] [^:]+: rebuild complete$/.test(plain)
76
+ || /^\[watch\] [^:]+: no source files found, skipping rebuild$/.test(plain)
77
+ || /^\[watch\] no workspace packages with a `watch` script /.test(plain)
78
+ }
79
+
69
80
  const FAILURE_NOISE_PREDICATES = [
70
81
  isIgnorableBoxDrawingLine,
71
82
  isIgnorableEnvInjectionLine,
@@ -89,6 +100,7 @@ const TURBO_NOISE_PREDICATES = [
89
100
  isIgnorableTurboCacheCancellationLine,
90
101
  isIgnorableTurboShutdownLine,
91
102
  isIgnorableBoxDrawingLine,
103
+ isIgnorableConsolidatedWatchLine,
92
104
  ]
93
105
 
94
106
  export function isIgnorableTurboLine(line) {
@@ -1434,6 +1434,28 @@ function classifyWatchLine(line) {
1434
1434
  progressLabel: 'Watching structural module files',
1435
1435
  }
1436
1436
  }
1437
+ if (line.startsWith('[generate:watch] Regenerating')) {
1438
+ return {
1439
+ type: 'status',
1440
+ message: '♻️ Structural change detected; regenerating generated files',
1441
+ splashPhase: startupSplashPhase,
1442
+ splashDetail: 'Regenerating generated files',
1443
+ activity: 'Regenerating generated files',
1444
+ progressCurrent: 2,
1445
+ progressLabel: 'Watching structural module files',
1446
+ }
1447
+ }
1448
+ if (line === '[generate:watch] Generators completed.') {
1449
+ return {
1450
+ type: 'status',
1451
+ message: '♻️ Generated files refreshed',
1452
+ splashPhase: startupSplashPhase,
1453
+ splashDetail: 'Generated files refreshed',
1454
+ activity: 'Generated files refreshed',
1455
+ progressCurrent: 2,
1456
+ progressLabel: 'Watching structural module files',
1457
+ }
1458
+ }
1437
1459
  if (line.startsWith('[generate:watch]')) {
1438
1460
  return {
1439
1461
  type: 'status',
@@ -1473,6 +1495,9 @@ function classifyWatchLine(line) {
1473
1495
  }
1474
1496
 
1475
1497
  function classifyServerLine(line) {
1498
+ if (line.startsWith('[generate:watch]')) {
1499
+ return classifyWatchLine(line)
1500
+ }
1476
1501
  if (line.startsWith('🚀 Running server:dev')) {
1477
1502
  return {
1478
1503
  type: 'status',
@@ -1672,6 +1697,18 @@ function startFilteredChild(args, label, classifyLine) {
1672
1697
  return child
1673
1698
  }
1674
1699
 
1700
+ function resolveGenerateWatchMode(env) {
1701
+ const raw = env.OM_DEV_GENERATE_WATCH_MODE
1702
+ if (typeof raw !== 'string') return 'in-process'
1703
+ const normalized = raw.trim().toLowerCase()
1704
+ if (normalized === 'legacy' || normalized === 'sidecar' || normalized === 'out-of-process') {
1705
+ return 'legacy'
1706
+ }
1707
+ return 'in-process'
1708
+ }
1709
+
1710
+ const generateWatchMode = resolveGenerateWatchMode(process.env)
1711
+
1675
1712
  async function runClassicRuntime() {
1676
1713
  const initialGenerate = spawnMercato(['generate'])
1677
1714
  const initialGenerateResult = await waitForExit(initialGenerate)
@@ -1684,12 +1721,19 @@ async function runClassicRuntime() {
1684
1721
  shutdown(initialGenerateExitCode)
1685
1722
  }
1686
1723
 
1687
- const watch = spawnMercato(['generate', 'watch', '--skip-initial'])
1724
+ // Default ('in-process'): `mercato server dev` runs the structural
1725
+ // regeneration watcher in-process. Set OM_DEV_GENERATE_WATCH_MODE=legacy
1726
+ // to fall back to spawning the dedicated sidecar Node process.
1727
+ const watchers = []
1728
+ if (generateWatchMode === 'legacy') {
1729
+ watchers.push(['Generator watch (legacy sidecar)', spawnMercato(['generate', 'watch', '--skip-initial'])])
1730
+ }
1688
1731
  const server = spawnMercato(['server', 'dev'])
1689
- const result = await Promise.race([
1690
- waitForExit(watch, 'Generator watch'),
1732
+ const waiters = [
1691
1733
  waitForExit(server, 'App runtime'),
1692
- ])
1734
+ ...watchers.map(([label, child]) => waitForExit(child, label)),
1735
+ ]
1736
+ const result = await Promise.race(waiters)
1693
1737
  if (isGracefulShutdownResult(result)) {
1694
1738
  return
1695
1739
  }
@@ -1707,13 +1751,16 @@ installLogToggle()
1707
1751
  initializeRuntimeSummary()
1708
1752
  printRuntimePackagesSummary()
1709
1753
 
1710
- const watch = startFilteredChild(['generate', 'watch', '--skip-initial'], 'Generator watch', classifyWatchLine)
1754
+ const sidecarWatch = generateWatchMode === 'legacy'
1755
+ ? startFilteredChild(['generate', 'watch', '--skip-initial'], 'Generator watch (legacy sidecar)', classifyWatchLine)
1756
+ : null
1711
1757
  const server = startFilteredChild(['server', 'dev'], 'App runtime', classifyServerLine)
1712
1758
 
1713
- const result = await Promise.race([
1714
- waitForExit(watch, 'Generator watch'),
1715
- waitForExit(server, 'App runtime'),
1716
- ])
1759
+ const waiters = [waitForExit(server, 'App runtime')]
1760
+ if (sidecarWatch) {
1761
+ waiters.push(waitForExit(sidecarWatch, 'Generator watch (legacy sidecar)'))
1762
+ }
1763
+ const result = await Promise.race(waiters)
1717
1764
  if (!isGracefulShutdownResult(result)) {
1718
1765
  reportUnexpectedChildExit(result)
1719
1766
  shutdown(resolveUnexpectedExitCode(result))
@@ -0,0 +1,41 @@
1
+ import spawn from 'cross-spawn'
2
+
3
+ // Windows does not propagate signals from a parent process down to grandchildren
4
+ // the way POSIX does: child.kill('SIGTERM'/'SIGKILL') only terminates the direct
5
+ // child, leaving any further descendants (next dev, mercato generate watch, etc.)
6
+ // alive and still holding ports. taskkill with /T (tree) /F (force) is the
7
+ // platform-blessed way to terminate the whole descendant tree. On POSIX we keep
8
+ // child.kill so the existing graceful-then-forced two-phase shutdown is preserved.
9
+ export function killProcessTree(child, signal, options = {}) {
10
+ if (!child) return false
11
+
12
+ const platform = options.platform ?? process.platform
13
+ const spawnImpl = options.spawn ?? spawn
14
+
15
+ if (platform === 'win32') {
16
+ const pid = child.pid
17
+ if (typeof pid !== 'number' || Number.isNaN(pid) || pid <= 0) {
18
+ return false
19
+ }
20
+ try {
21
+ const killer = spawnImpl('taskkill', ['/pid', String(pid), '/T', '/F'], {
22
+ stdio: 'ignore',
23
+ windowsHide: true,
24
+ })
25
+ if (killer && typeof killer.on === 'function') {
26
+ killer.on('error', () => { /* best-effort: taskkill may not exist on stripped images */ })
27
+ }
28
+ return true
29
+ } catch {
30
+ return false
31
+ }
32
+ }
33
+
34
+ if (child.killed) return false
35
+ try {
36
+ child.kill(signal)
37
+ return true
38
+ } catch {
39
+ return false
40
+ }
41
+ }
@@ -23,6 +23,7 @@ import {
23
23
  stripAnsi,
24
24
  } from './dev-splash-helpers.mjs'
25
25
  import { purgeAppBuildCaches } from './dev-cache-purge.mjs'
26
+ import { killProcessTree } from './dev-shutdown-utils.mjs'
26
27
  import { resolveSpawnCommand } from './dev-spawn-utils.mjs'
27
28
  import { createDevSplashCodingFlow } from './dev-splash-coding-flow.mjs'
28
29
  import { createDevSplashGitRepoFlow } from './dev-splash-git-repo-flow.mjs'
@@ -1023,13 +1024,13 @@ function shutdown(exitCode = 0) {
1023
1024
  }
1024
1025
 
1025
1026
  for (const child of alive) {
1026
- child.kill('SIGTERM')
1027
+ killProcessTree(child, 'SIGTERM')
1027
1028
  }
1028
1029
 
1029
1030
  setTimeout(() => {
1030
1031
  for (const child of children) {
1031
1032
  if (!child.killed) {
1032
- child.kill('SIGKILL')
1033
+ killProcessTree(child, 'SIGKILL')
1033
1034
  }
1034
1035
  }
1035
1036
  closeDevLogSession()
@@ -1492,10 +1493,21 @@ async function runPassthroughStage(label, commandArgs, options = {}) {
1492
1493
  console.log(`✅ ${formatProgressLine(label, stageCurrent, stageTotal, resolveProgressPercent(stageCurrent, stageTotal))} in ${formatDuration(Date.now() - startedAt)}`)
1493
1494
  }
1494
1495
 
1496
+ function resolveWatchPackagesScript() {
1497
+ // `OM_WATCH_PACKAGES_MODE=legacy` falls back to the Turbo per-package
1498
+ // fan-out for developers who need the old behavior (debugging, or pairing
1499
+ // with `OM_PACKAGE_WATCH_MODE=persistent` for hot rebuilds at the cost of
1500
+ // ~1 GB more idle RSS). Default is the consolidated single-process watcher.
1501
+ const raw = String(process.env.OM_WATCH_PACKAGES_MODE ?? '').trim().toLowerCase()
1502
+ return raw === 'legacy' ? 'watch:packages:legacy' : 'watch:packages'
1503
+ }
1504
+
1495
1505
  function startPackageWatch() {
1506
+ const watchScript = resolveWatchPackagesScript()
1507
+
1496
1508
  if (classic) {
1497
- const child = spawnCommand(yarnCommand, ['watch:packages'], {
1498
- label: 'watch:packages',
1509
+ const child = spawnCommand(yarnCommand, [watchScript], {
1510
+ label: watchScript,
1499
1511
  logFile: getDevRunnerLog(),
1500
1512
  mirrorOutput: true,
1501
1513
  })
@@ -1529,16 +1541,7 @@ function startPackageWatch() {
1529
1541
  activity: 'Workspace package watch started',
1530
1542
  })
1531
1543
 
1532
- const child = spawnCommand(yarnCommand, [
1533
- 'turbo',
1534
- 'run',
1535
- 'watch',
1536
- '--filter=./packages/*',
1537
- '--concurrency=32',
1538
- '--output-logs=errors-only',
1539
- '--log-order=grouped',
1540
- '--log-prefix=none',
1541
- ], {
1544
+ const child = spawnCommand(yarnCommand, [watchScript], {
1542
1545
  label: 'Watching workspace packages',
1543
1546
  logFile: getDevRunnerLog(),
1544
1547
  mirrorOutput: verbose,