@lark-apaas/fullstack-cli 1.1.59-alpha.20260719124023 → 1.1.59-alpha.3
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/index.js +16 -2078
- package/package.json +1 -4
- package/templates/scripts/dev.js +43 -178
- package/templates/scripts/preview-startup-timing.cjs +0 -82
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lark-apaas/fullstack-cli",
|
|
3
|
-
"version": "1.1.59-alpha.
|
|
3
|
+
"version": "1.1.59-alpha.3",
|
|
4
4
|
"description": "CLI tool for fullstack template management",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -34,15 +34,12 @@
|
|
|
34
34
|
"@lark-apaas/http-client": "^0.1.7",
|
|
35
35
|
"@lydell/node-pty": "1.1.0",
|
|
36
36
|
"@vercel/nft": "^0.30.3",
|
|
37
|
-
"acorn": "^8.16.0",
|
|
38
|
-
"acorn-walk": "^8.3.5",
|
|
39
37
|
"commander": "^13.0.0",
|
|
40
38
|
"debug": "^4.4.3",
|
|
41
39
|
"dotenv": "^16.0.0",
|
|
42
40
|
"drizzle-kit": "0.31.5",
|
|
43
41
|
"drizzle-orm": "0.44.6",
|
|
44
42
|
"es-toolkit": "^1.44.0",
|
|
45
|
-
"esbuild": "^0.27.0",
|
|
46
43
|
"inflection": "^3.0.2",
|
|
47
44
|
"pinyin-pro": "^3.27.0",
|
|
48
45
|
"postgres": "^3.4.3",
|
package/templates/scripts/dev.js
CHANGED
|
@@ -6,13 +6,10 @@ const path = require('path');
|
|
|
6
6
|
const { spawn, execFileSync, execSync } = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
const {
|
|
9
|
-
applyPreviewArtifactHostOverrides,
|
|
10
9
|
buildListeningPortLookupArgs,
|
|
11
|
-
createDeferredTypecheckGate,
|
|
12
10
|
createPreviewPhaseReporter,
|
|
13
11
|
normalizeTcpPort,
|
|
14
12
|
parseTscWatchSummary,
|
|
15
|
-
resolvePreviewArtifactPorts,
|
|
16
13
|
resolvePreviewServerCommand,
|
|
17
14
|
waitForTcpReady,
|
|
18
15
|
} = require('./preview-startup-timing.cjs');
|
|
@@ -42,34 +39,17 @@ loadEnv();
|
|
|
42
39
|
|
|
43
40
|
// ── Configuration ─────────────────────────────────────────────────────────────
|
|
44
41
|
const LOG_DIR = process.env.LOG_DIR || 'logs';
|
|
45
|
-
const MAX_RESTART_COUNT =
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
: Infinity;
|
|
42
|
+
const MAX_RESTART_COUNT = process.env.MAX_RESTART_COUNT != null && process.env.MAX_RESTART_COUNT !== ''
|
|
43
|
+
? parseInt(process.env.MAX_RESTART_COUNT, 10)
|
|
44
|
+
: Infinity;
|
|
49
45
|
const RESTART_DELAY = parseInt(process.env.RESTART_DELAY, 10) || 2;
|
|
50
46
|
const MAX_DELAY = 8;
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
3000,
|
|
54
|
-
'SERVER_PORT'
|
|
55
|
-
);
|
|
56
|
-
const configuredClientDevPort = normalizeTcpPort(
|
|
47
|
+
const SERVER_PORT = normalizeTcpPort(process.env.SERVER_PORT, 3000, 'SERVER_PORT');
|
|
48
|
+
const CLIENT_DEV_PORT = normalizeTcpPort(
|
|
57
49
|
process.env.CLIENT_DEV_PORT,
|
|
58
50
|
8080,
|
|
59
51
|
'CLIENT_DEV_PORT'
|
|
60
52
|
);
|
|
61
|
-
const previewArtifactPorts = resolvePreviewArtifactPorts({
|
|
62
|
-
marker_file:
|
|
63
|
-
process.env.PREVIEW_ARTIFACT_RUNTIME_MARKER ||
|
|
64
|
-
'/tmp/preview-artifact/runtime.json',
|
|
65
|
-
server_port: configuredServerPort,
|
|
66
|
-
client_port: configuredClientDevPort,
|
|
67
|
-
});
|
|
68
|
-
applyPreviewArtifactHostOverrides(previewArtifactPorts, process.env);
|
|
69
|
-
const SERVER_PORT = previewArtifactPorts.server_port;
|
|
70
|
-
const CLIENT_DEV_PORT = previewArtifactPorts.client_port;
|
|
71
|
-
process.env.SERVER_PORT = String(SERVER_PORT);
|
|
72
|
-
process.env.CLIENT_DEV_PORT = String(CLIENT_DEV_PORT);
|
|
73
53
|
|
|
74
54
|
fs.mkdirSync(LOG_DIR, { recursive: true });
|
|
75
55
|
|
|
@@ -82,16 +62,11 @@ const devLogFd = fs.openSync(devLogPath, 'a');
|
|
|
82
62
|
function timestamp() {
|
|
83
63
|
const now = new Date();
|
|
84
64
|
return (
|
|
85
|
-
now.getFullYear() +
|
|
86
|
-
'-' +
|
|
87
|
-
String(now.
|
|
88
|
-
'
|
|
89
|
-
String(now.
|
|
90
|
-
' ' +
|
|
91
|
-
String(now.getHours()).padStart(2, '0') +
|
|
92
|
-
':' +
|
|
93
|
-
String(now.getMinutes()).padStart(2, '0') +
|
|
94
|
-
':' +
|
|
65
|
+
now.getFullYear() + '-' +
|
|
66
|
+
String(now.getMonth() + 1).padStart(2, '0') + '-' +
|
|
67
|
+
String(now.getDate()).padStart(2, '0') + ' ' +
|
|
68
|
+
String(now.getHours()).padStart(2, '0') + ':' +
|
|
69
|
+
String(now.getMinutes()).padStart(2, '0') + ':' +
|
|
95
70
|
String(now.getSeconds()).padStart(2, '0')
|
|
96
71
|
);
|
|
97
72
|
}
|
|
@@ -105,37 +80,25 @@ const STDOUT_MAX_INFLIGHT = 1000;
|
|
|
105
80
|
function writeOutput(msg) {
|
|
106
81
|
// File first and synchronously — read-logs reads this file; it must never be gated
|
|
107
82
|
// by the terminal consumer.
|
|
108
|
-
try {
|
|
109
|
-
fs.writeSync(devStdLogFd, msg);
|
|
110
|
-
} catch {}
|
|
83
|
+
try { fs.writeSync(devStdLogFd, msg); } catch {}
|
|
111
84
|
// stdout mirror via async fs.write: if process.stdout is a pty/pipe whose consumer
|
|
112
85
|
// stalls, the block happens on the libuv threadpool, NOT the event loop — so the
|
|
113
86
|
// synchronous log-FILE writes above (and subsequent readline callbacks) keep running.
|
|
114
87
|
// Drop overflow when too many writes are already pending (best-effort mirror).
|
|
115
|
-
if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
88
|
+
if (_stdoutInFlight >= STDOUT_MAX_INFLIGHT) { return; }
|
|
118
89
|
_stdoutInFlight++;
|
|
119
|
-
try {
|
|
120
|
-
fs.write(1, msg, () => {
|
|
121
|
-
_stdoutInFlight--;
|
|
122
|
-
});
|
|
123
|
-
} catch {
|
|
124
|
-
_stdoutInFlight--;
|
|
125
|
-
}
|
|
90
|
+
try { fs.write(1, msg, () => { _stdoutInFlight--; }); } catch { _stdoutInFlight--; }
|
|
126
91
|
}
|
|
127
92
|
|
|
128
93
|
const previewPhaseReporter = createPreviewPhaseReporter({
|
|
129
|
-
write: line => writeOutput(`${line}\n`),
|
|
94
|
+
write: (line) => writeOutput(`${line}\n`),
|
|
130
95
|
});
|
|
131
96
|
|
|
132
97
|
/** Structured event log → terminal + dev.std.log + dev.log */
|
|
133
98
|
function logEvent(level, name, message) {
|
|
134
99
|
const msg = `[${timestamp()}] [${level}] [${name}] ${message}\n`;
|
|
135
100
|
writeOutput(msg);
|
|
136
|
-
try {
|
|
137
|
-
fs.writeSync(devLogFd, msg);
|
|
138
|
-
} catch {}
|
|
101
|
+
try { fs.writeSync(devLogFd, msg); } catch {}
|
|
139
102
|
}
|
|
140
103
|
|
|
141
104
|
// ── Process group management ──────────────────────────────────────────────────
|
|
@@ -186,22 +149,14 @@ let stopping = false;
|
|
|
186
149
|
const managedProcesses = []; // { name, pid, child }
|
|
187
150
|
|
|
188
151
|
function sleep(ms) {
|
|
189
|
-
return new Promise(resolve => setTimeout(resolve, ms));
|
|
152
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
190
153
|
}
|
|
191
154
|
|
|
192
155
|
/**
|
|
193
156
|
* Start and supervise a process with auto-restart and log piping.
|
|
194
157
|
* Returns a promise that resolves when the process loop ends.
|
|
195
158
|
*/
|
|
196
|
-
function startProcess({
|
|
197
|
-
name,
|
|
198
|
-
command,
|
|
199
|
-
args,
|
|
200
|
-
cleanupPort,
|
|
201
|
-
phaseDetail = {},
|
|
202
|
-
onReady,
|
|
203
|
-
onOutputLine,
|
|
204
|
-
}) {
|
|
159
|
+
function startProcess({ name, command, args, cleanupPort, phaseDetail = {}, onReady }) {
|
|
205
160
|
const logFilePath = path.join(LOG_DIR, `${name}.std.log`);
|
|
206
161
|
const logFd = fs.openSync(logFilePath, 'a');
|
|
207
162
|
|
|
@@ -220,8 +175,8 @@ function startProcess({
|
|
|
220
175
|
portCleanup.error
|
|
221
176
|
? `Port ${cleanupPort} preflight failed: ${portCleanup.error}`
|
|
222
177
|
: stalePids.length > 0
|
|
223
|
-
|
|
224
|
-
|
|
178
|
+
? `Killed stale processes on port ${cleanupPort} before first spawn: ${stalePids.join(' ')}`
|
|
179
|
+
: `Port ${cleanupPort} is clear before first spawn`
|
|
225
180
|
);
|
|
226
181
|
previewPhaseReporter.emit(
|
|
227
182
|
name === 'server' ? 'backend_port_preflight' : 'client_port_preflight',
|
|
@@ -249,11 +204,7 @@ function startProcess({
|
|
|
249
204
|
|
|
250
205
|
const startTime = Date.now();
|
|
251
206
|
const attempt = restartCount + 1;
|
|
252
|
-
logEvent(
|
|
253
|
-
'INFO',
|
|
254
|
-
name,
|
|
255
|
-
`Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`
|
|
256
|
-
);
|
|
207
|
+
logEvent('INFO', name, `Process started (PGID: ${child.pid}): ${command} ${args.join(' ')}`);
|
|
257
208
|
const processPhase =
|
|
258
209
|
name === 'server'
|
|
259
210
|
? 'backend_process_spawn'
|
|
@@ -307,13 +258,9 @@ function startProcess({
|
|
|
307
258
|
|
|
308
259
|
// Pipe stdout and stderr through readline for timestamped logging
|
|
309
260
|
let typecheckSummaryReported = false;
|
|
310
|
-
const pipeLines = stream => {
|
|
311
|
-
const rl = readline.createInterface({
|
|
312
|
-
|
|
313
|
-
crlfDelay: Infinity,
|
|
314
|
-
});
|
|
315
|
-
rl.on('line', line => {
|
|
316
|
-
if (onOutputLine) onOutputLine(line);
|
|
261
|
+
const pipeLines = (stream) => {
|
|
262
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
263
|
+
rl.on('line', (line) => {
|
|
317
264
|
if (name === 'server-typecheck' && !typecheckSummaryReported) {
|
|
318
265
|
const summary = parseTscWatchSummary(line);
|
|
319
266
|
if (summary) {
|
|
@@ -331,9 +278,7 @@ function startProcess({
|
|
|
331
278
|
}
|
|
332
279
|
}
|
|
333
280
|
const msg = `[${timestamp()}] [${name}] ${line}\n`;
|
|
334
|
-
try {
|
|
335
|
-
fs.writeSync(logFd, msg);
|
|
336
|
-
} catch {}
|
|
281
|
+
try { fs.writeSync(logFd, msg); } catch {}
|
|
337
282
|
writeOutput(msg);
|
|
338
283
|
});
|
|
339
284
|
};
|
|
@@ -344,8 +289,8 @@ function startProcess({
|
|
|
344
289
|
// NOTE: must use 'exit', not 'close'. With shell:true, grandchild processes
|
|
345
290
|
// (e.g. nest's server) inherit stdout pipes. 'close' won't fire until ALL
|
|
346
291
|
// pipe holders exit, causing dev.js to hang when npm/nest dies but server survives.
|
|
347
|
-
const exitCode = await new Promise(resolve => {
|
|
348
|
-
child.on('exit', code => resolve(code ?? 1));
|
|
292
|
+
const exitCode = await new Promise((resolve) => {
|
|
293
|
+
child.on('exit', (code) => resolve(code ?? 1));
|
|
349
294
|
child.on('error', () => resolve(1));
|
|
350
295
|
});
|
|
351
296
|
|
|
@@ -370,11 +315,7 @@ function startProcess({
|
|
|
370
315
|
);
|
|
371
316
|
}
|
|
372
317
|
if (orphans.length > 0) {
|
|
373
|
-
logEvent(
|
|
374
|
-
'WARN',
|
|
375
|
-
name,
|
|
376
|
-
`Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`
|
|
377
|
-
);
|
|
318
|
+
logEvent('WARN', name, `Killed orphan processes on port ${cleanupPort}: ${orphans.join(' ')}`);
|
|
378
319
|
await sleep(500);
|
|
379
320
|
}
|
|
380
321
|
}
|
|
@@ -384,38 +325,21 @@ function startProcess({
|
|
|
384
325
|
const runDuration = (Date.now() - startTime) / 1000;
|
|
385
326
|
if (runDuration >= 60) {
|
|
386
327
|
restartCount = 0;
|
|
387
|
-
logEvent(
|
|
388
|
-
'INFO',
|
|
389
|
-
name,
|
|
390
|
-
`Ran for ${Math.round(runDuration)}s, resetting restart counter`
|
|
391
|
-
);
|
|
328
|
+
logEvent('INFO', name, `Ran for ${Math.round(runDuration)}s, resetting restart counter`);
|
|
392
329
|
} else {
|
|
393
330
|
restartCount++;
|
|
394
331
|
}
|
|
395
332
|
if (restartCount >= MAX_RESTART_COUNT) {
|
|
396
|
-
logEvent(
|
|
397
|
-
'ERROR',
|
|
398
|
-
name,
|
|
399
|
-
`Max restart count (${MAX_RESTART_COUNT}) reached, giving up`
|
|
400
|
-
);
|
|
333
|
+
logEvent('ERROR', name, `Max restart count (${MAX_RESTART_COUNT}) reached, giving up`);
|
|
401
334
|
break;
|
|
402
335
|
}
|
|
403
336
|
|
|
404
|
-
const delay = Math.min(
|
|
405
|
-
|
|
406
|
-
MAX_DELAY
|
|
407
|
-
);
|
|
408
|
-
logEvent(
|
|
409
|
-
'WARN',
|
|
410
|
-
name,
|
|
411
|
-
`Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`
|
|
412
|
-
);
|
|
337
|
+
const delay = Math.min(RESTART_DELAY * (1 << Math.max(0, restartCount - 1)), MAX_DELAY);
|
|
338
|
+
logEvent('WARN', name, `Process exited with code ${exitCode}, restarting (${restartCount}/${MAX_RESTART_COUNT}) in ${delay}s...`);
|
|
413
339
|
await sleep(delay * 1000);
|
|
414
340
|
}
|
|
415
341
|
|
|
416
|
-
try {
|
|
417
|
-
fs.closeSync(logFd);
|
|
418
|
-
} catch {}
|
|
342
|
+
try { fs.closeSync(logFd); } catch {}
|
|
419
343
|
};
|
|
420
344
|
|
|
421
345
|
return run();
|
|
@@ -445,11 +369,7 @@ async function cleanup() {
|
|
|
445
369
|
// Force kill any remaining
|
|
446
370
|
for (const entry of managedProcesses) {
|
|
447
371
|
if (entry.pid) {
|
|
448
|
-
logEvent(
|
|
449
|
-
'WARN',
|
|
450
|
-
'main',
|
|
451
|
-
`Force killing process group (PGID: ${entry.pid})`
|
|
452
|
-
);
|
|
372
|
+
logEvent('WARN', 'main', `Force killing process group (PGID: ${entry.pid})`);
|
|
453
373
|
killProcessGroup(entry.pid, 'SIGKILL');
|
|
454
374
|
}
|
|
455
375
|
}
|
|
@@ -458,22 +378,14 @@ async function cleanup() {
|
|
|
458
378
|
for (const port of [SERVER_PORT, CLIENT_DEV_PORT]) {
|
|
459
379
|
const portCleanup = killOrphansByPort(port);
|
|
460
380
|
if (portCleanup.error) {
|
|
461
|
-
logEvent(
|
|
462
|
-
'ERROR',
|
|
463
|
-
'main',
|
|
464
|
-
`Port ${port} cleanup failed: ${portCleanup.error}`
|
|
465
|
-
);
|
|
381
|
+
logEvent('ERROR', 'main', `Port ${port} cleanup failed: ${portCleanup.error}`);
|
|
466
382
|
}
|
|
467
383
|
}
|
|
468
384
|
|
|
469
385
|
logEvent('INFO', 'main', 'All processes stopped');
|
|
470
386
|
|
|
471
|
-
try {
|
|
472
|
-
|
|
473
|
-
} catch {}
|
|
474
|
-
try {
|
|
475
|
-
fs.closeSync(devLogFd);
|
|
476
|
-
} catch {}
|
|
387
|
+
try { fs.closeSync(devStdLogFd); } catch {}
|
|
388
|
+
try { fs.closeSync(devLogFd); } catch {}
|
|
477
389
|
|
|
478
390
|
process.exit(0);
|
|
479
391
|
}
|
|
@@ -494,22 +406,7 @@ function cleanStaleDist() {
|
|
|
494
406
|
// ── Main ──────────────────────────────────────────────────────────────────────
|
|
495
407
|
async function main() {
|
|
496
408
|
logEvent('INFO', 'main', '========== Dev session started ==========');
|
|
497
|
-
previewPhaseReporter.emit('dev_orchestrator_start', 'success', {
|
|
498
|
-
exact: true,
|
|
499
|
-
});
|
|
500
|
-
if (previewArtifactPorts.cache_active) {
|
|
501
|
-
logEvent(
|
|
502
|
-
'INFO',
|
|
503
|
-
'main',
|
|
504
|
-
`Cached generation ${previewArtifactPorts.generation_id} is serving; starting fresh services on shadow ports ${SERVER_PORT}/${CLIENT_DEV_PORT}`
|
|
505
|
-
);
|
|
506
|
-
previewPhaseReporter.emit('preview_artifact_shadow_start', 'success', {
|
|
507
|
-
exact: true,
|
|
508
|
-
generation_id: previewArtifactPorts.generation_id,
|
|
509
|
-
fresh_server_port: SERVER_PORT,
|
|
510
|
-
fresh_client_port: CLIENT_DEV_PORT,
|
|
511
|
-
});
|
|
512
|
-
}
|
|
409
|
+
previewPhaseReporter.emit('dev_orchestrator_start', 'success', { exact: true });
|
|
513
410
|
|
|
514
411
|
cleanStaleDist();
|
|
515
412
|
|
|
@@ -521,10 +418,7 @@ async function main() {
|
|
|
521
418
|
exact: true,
|
|
522
419
|
});
|
|
523
420
|
try {
|
|
524
|
-
execSync('fullstack-cli action-plugin init', {
|
|
525
|
-
cwd: PROJECT_ROOT,
|
|
526
|
-
stdio: 'inherit',
|
|
527
|
-
});
|
|
421
|
+
execSync('fullstack-cli action-plugin init', { cwd: PROJECT_ROOT, stdio: 'inherit' });
|
|
528
422
|
previewPhaseReporter.emit('action_plugin_init', 'success', {
|
|
529
423
|
duration_ms: Date.now() - actionPluginStartedAt,
|
|
530
424
|
exact: true,
|
|
@@ -536,9 +430,7 @@ async function main() {
|
|
|
536
430
|
exact: true,
|
|
537
431
|
error: error instanceof Error ? error.message : String(error),
|
|
538
432
|
});
|
|
539
|
-
writeOutput(
|
|
540
|
-
'⚠️ Action plugin initialization failed, continuing anyway...\n\n'
|
|
541
|
-
);
|
|
433
|
+
writeOutput('⚠️ Action plugin initialization failed, continuing anyway...\n\n');
|
|
542
434
|
}
|
|
543
435
|
|
|
544
436
|
// Start server and client
|
|
@@ -558,20 +450,18 @@ async function main() {
|
|
|
558
450
|
reason: serverCommand.reason,
|
|
559
451
|
missing_modules: serverCommand.missing_modules,
|
|
560
452
|
});
|
|
561
|
-
const typecheckGate = createDeferredTypecheckGate();
|
|
562
453
|
let serverTypecheckStarted = false;
|
|
563
|
-
const startServerTypecheck =
|
|
454
|
+
const startServerTypecheck = () => {
|
|
564
455
|
if (serverTypecheckStarted || !serverCommand.type_check_command) return;
|
|
565
456
|
try {
|
|
566
457
|
logEvent(
|
|
567
458
|
'INFO',
|
|
568
459
|
'server-typecheck',
|
|
569
|
-
|
|
460
|
+
'Starting deferred server type checker after backend TCP ready'
|
|
570
461
|
);
|
|
571
462
|
previewPhaseReporter.emit('backend_typecheck_deferred_start', 'success', {
|
|
572
463
|
exact: true,
|
|
573
464
|
type_check_mode: serverCommand.type_check_mode,
|
|
574
|
-
trigger,
|
|
575
465
|
});
|
|
576
466
|
const typecheckPromise = startProcess({
|
|
577
467
|
name: 'server-typecheck',
|
|
@@ -600,30 +490,6 @@ async function main() {
|
|
|
600
490
|
);
|
|
601
491
|
}
|
|
602
492
|
};
|
|
603
|
-
const handleBackendReady = () => {
|
|
604
|
-
if (!serverCommand.type_check_command) return;
|
|
605
|
-
const decision = typecheckGate.onBackendReady();
|
|
606
|
-
if (decision.should_start) {
|
|
607
|
-
startServerTypecheck(decision.reason);
|
|
608
|
-
return;
|
|
609
|
-
}
|
|
610
|
-
logEvent(
|
|
611
|
-
'INFO',
|
|
612
|
-
'server-typecheck',
|
|
613
|
-
'Skipped type checking during initial preview startup'
|
|
614
|
-
);
|
|
615
|
-
previewPhaseReporter.emit('backend_typecheck_skipped', 'success', {
|
|
616
|
-
exact: true,
|
|
617
|
-
type_check_mode: serverCommand.type_check_mode,
|
|
618
|
-
reason: decision.reason,
|
|
619
|
-
next_trigger: 'source_change_or_backend_restart',
|
|
620
|
-
});
|
|
621
|
-
};
|
|
622
|
-
const handleServerOutput = line => {
|
|
623
|
-
if (!serverCommand.type_check_command || serverTypecheckStarted) return;
|
|
624
|
-
const decision = typecheckGate.onCompilerOutput(line);
|
|
625
|
-
if (decision?.should_start) startServerTypecheck(decision.reason);
|
|
626
|
-
};
|
|
627
493
|
const serverPromise = startProcess({
|
|
628
494
|
name: 'server',
|
|
629
495
|
command: serverCommand.command,
|
|
@@ -635,8 +501,7 @@ async function main() {
|
|
|
635
501
|
type_check_mode: serverCommand.type_check_mode,
|
|
636
502
|
compiler_selection_reason: serverCommand.reason,
|
|
637
503
|
},
|
|
638
|
-
onReady:
|
|
639
|
-
onOutputLine: handleServerOutput,
|
|
504
|
+
onReady: startServerTypecheck,
|
|
640
505
|
});
|
|
641
506
|
|
|
642
507
|
const clientPromise = startProcess({
|
|
@@ -657,7 +522,7 @@ async function main() {
|
|
|
657
522
|
}
|
|
658
523
|
}
|
|
659
524
|
|
|
660
|
-
main().catch(err => {
|
|
525
|
+
main().catch((err) => {
|
|
661
526
|
console.error('Fatal error:', err);
|
|
662
527
|
process.exit(1);
|
|
663
528
|
});
|
|
@@ -70,53 +70,6 @@ function buildListeningPortLookupArgs(port) {
|
|
|
70
70
|
return [`-tiTCP:${validatedPort}`, '-sTCP:LISTEN'];
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
function resolvePreviewArtifactPorts(options) {
|
|
74
|
-
const fallback = {
|
|
75
|
-
cache_active: false,
|
|
76
|
-
server_port: normalizeTcpPort(options.server_port, 3000, 'SERVER_PORT'),
|
|
77
|
-
client_port: normalizeTcpPort(options.client_port, 8080, 'CLIENT_DEV_PORT'),
|
|
78
|
-
generation_id: '',
|
|
79
|
-
};
|
|
80
|
-
try {
|
|
81
|
-
const markerStat = fs.lstatSync(options.marker_file);
|
|
82
|
-
if (!markerStat.isFile() || markerStat.isSymbolicLink()) return fallback;
|
|
83
|
-
const marker = JSON.parse(fs.readFileSync(options.marker_file, 'utf8'));
|
|
84
|
-
if (
|
|
85
|
-
marker?.schemaVersion !== 1 ||
|
|
86
|
-
!['cached-serving', 'fresh-serving'].includes(marker?.status) ||
|
|
87
|
-
!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(marker?.generationId || '')
|
|
88
|
-
) {
|
|
89
|
-
return fallback;
|
|
90
|
-
}
|
|
91
|
-
return {
|
|
92
|
-
cache_active: true,
|
|
93
|
-
server_port: normalizeTcpPort(
|
|
94
|
-
marker.freshServerPort,
|
|
95
|
-
fallback.server_port,
|
|
96
|
-
'freshServerPort'
|
|
97
|
-
),
|
|
98
|
-
client_port: normalizeTcpPort(
|
|
99
|
-
marker.freshClientPort,
|
|
100
|
-
fallback.client_port,
|
|
101
|
-
'freshClientPort'
|
|
102
|
-
),
|
|
103
|
-
generation_id: marker.generationId,
|
|
104
|
-
};
|
|
105
|
-
} catch {
|
|
106
|
-
return fallback;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function applyPreviewArtifactHostOverrides(ports, environment) {
|
|
111
|
-
if (!ports.cache_active) return false;
|
|
112
|
-
// Cached runtime and switcher use an explicit IPv4 loopback endpoint. Pin
|
|
113
|
-
// both shadow services to the same address family; Linux commonly resolves
|
|
114
|
-
// `localhost` to IPv6-only ::1, which otherwise makes cutover impossible.
|
|
115
|
-
environment.SERVER_HOST = '127.0.0.1';
|
|
116
|
-
environment.CLIENT_DEV_HOST = '127.0.0.1';
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
73
|
function hasRuntimeCompilerPluginConsumer(projectRoot, sourceRoot) {
|
|
121
74
|
const root = path.resolve(projectRoot, sourceRoot || 'src');
|
|
122
75
|
const stack = [root];
|
|
@@ -316,38 +269,6 @@ function resolvePreviewServerCommand(options = {}) {
|
|
|
316
269
|
};
|
|
317
270
|
}
|
|
318
271
|
|
|
319
|
-
function createDeferredTypecheckGate() {
|
|
320
|
-
let initialBackendReadySeen = false;
|
|
321
|
-
|
|
322
|
-
return {
|
|
323
|
-
onBackendReady() {
|
|
324
|
-
if (!initialBackendReadySeen) {
|
|
325
|
-
initialBackendReadySeen = true;
|
|
326
|
-
return {
|
|
327
|
-
should_start: false,
|
|
328
|
-
reason: 'initial_preview_startup',
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
return {
|
|
332
|
-
should_start: true,
|
|
333
|
-
reason: 'backend_restart',
|
|
334
|
-
};
|
|
335
|
-
},
|
|
336
|
-
onCompilerOutput(line) {
|
|
337
|
-
if (
|
|
338
|
-
!initialBackendReadySeen ||
|
|
339
|
-
!/Successfully compiled:\s+\d+\s+files?\s+with\s+swc\b/i.test(line)
|
|
340
|
-
) {
|
|
341
|
-
return null;
|
|
342
|
-
}
|
|
343
|
-
return {
|
|
344
|
-
should_start: true,
|
|
345
|
-
reason: 'source_change',
|
|
346
|
-
};
|
|
347
|
-
},
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
272
|
function parseTscWatchSummary(line) {
|
|
352
273
|
const match = /Found\s+(\d+)\s+errors?\.\s+Watching for file changes\./i.exec(
|
|
353
274
|
line
|
|
@@ -447,13 +368,10 @@ async function waitForTcpReady(options) {
|
|
|
447
368
|
}
|
|
448
369
|
|
|
449
370
|
module.exports = {
|
|
450
|
-
applyPreviewArtifactHostOverrides,
|
|
451
371
|
buildListeningPortLookupArgs,
|
|
452
|
-
createDeferredTypecheckGate,
|
|
453
372
|
createPreviewPhaseReporter,
|
|
454
373
|
normalizeTcpPort,
|
|
455
374
|
parseTscWatchSummary,
|
|
456
|
-
resolvePreviewArtifactPorts,
|
|
457
375
|
resolvePreviewServerCommand,
|
|
458
376
|
waitForTcpReady,
|
|
459
377
|
};
|