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
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
1714
|
-
|
|
1715
|
-
waitForExit(
|
|
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
|
+
}
|
package/template/scripts/dev.mjs
CHANGED
|
@@ -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
|
|
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
|
|
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, [
|
|
1498
|
-
label:
|
|
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,
|