@zenithbuild/cli 0.6.6 → 0.6.9
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.d.ts +32 -0
- package/dist/build.js +193 -548
- package/dist/compiler-bridge-runner.d.ts +5 -0
- package/dist/compiler-bridge-runner.js +70 -0
- package/dist/component-instance-ir.d.ts +6 -0
- package/dist/component-instance-ir.js +0 -20
- package/dist/component-occurrences.d.ts +6 -0
- package/dist/component-occurrences.js +6 -28
- package/dist/dev-server.d.ts +18 -0
- package/dist/dev-server.js +65 -114
- package/dist/dev-watch.d.ts +1 -0
- package/dist/dev-watch.js +2 -2
- package/dist/index.d.ts +8 -0
- package/dist/index.js +6 -28
- package/dist/manifest.d.ts +23 -0
- package/dist/manifest.js +22 -48
- package/dist/preview.d.ts +100 -0
- package/dist/preview.js +418 -488
- package/dist/resolve-components.d.ts +39 -0
- package/dist/resolve-components.js +30 -104
- package/dist/server/resolve-request-route.d.ts +39 -0
- package/dist/server/resolve-request-route.js +104 -113
- package/dist/server-contract.d.ts +39 -0
- package/dist/server-contract.js +15 -67
- package/dist/toolchain-paths.d.ts +23 -0
- package/dist/toolchain-paths.js +139 -39
- package/dist/toolchain-runner.d.ts +33 -0
- package/dist/toolchain-runner.js +194 -0
- package/dist/types/generate-env-dts.d.ts +5 -0
- package/dist/types/generate-env-dts.js +4 -2
- package/dist/types/generate-routes-dts.d.ts +8 -0
- package/dist/types/generate-routes-dts.js +7 -5
- package/dist/types/index.d.ts +14 -0
- package/dist/types/index.js +16 -7
- package/dist/ui/env.d.ts +18 -0
- package/dist/ui/env.js +0 -12
- package/dist/ui/format.d.ts +33 -0
- package/dist/ui/format.js +8 -46
- package/dist/ui/logger.d.ts +59 -0
- package/dist/ui/logger.js +3 -32
- package/dist/version-check.d.ts +54 -0
- package/dist/version-check.js +41 -98
- package/package.json +6 -4
package/dist/dev-server.js
CHANGED
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
//
|
|
11
11
|
// V0: Uses Node.js http module + fs.watch. No external deps.
|
|
12
12
|
// ---------------------------------------------------------------------------
|
|
13
|
-
|
|
14
13
|
import { createServer } from 'node:http';
|
|
15
14
|
import { existsSync, watch } from 'node:fs';
|
|
16
15
|
import { readFile, stat } from 'node:fs/promises';
|
|
@@ -18,15 +17,8 @@ import { basename, dirname, extname, isAbsolute, join, relative, resolve } from
|
|
|
18
17
|
import { build } from './build.js';
|
|
19
18
|
import { createSilentLogger } from './ui/logger.js';
|
|
20
19
|
import { readChangeFingerprint } from './dev-watch.js';
|
|
21
|
-
import {
|
|
22
|
-
executeServerRoute,
|
|
23
|
-
injectSsrPayload,
|
|
24
|
-
loadRouteManifest,
|
|
25
|
-
resolveWithinDist,
|
|
26
|
-
toStaticFilePath
|
|
27
|
-
} from './preview.js';
|
|
20
|
+
import { executeServerRoute, injectSsrPayload, loadRouteManifest, resolveWithinDist, toStaticFilePath } from './preview.js';
|
|
28
21
|
import { resolveRequestRoute } from './server/resolve-request-route.js';
|
|
29
|
-
|
|
30
22
|
const MIME_TYPES = {
|
|
31
23
|
'.html': 'text/html',
|
|
32
24
|
'.js': 'application/javascript',
|
|
@@ -36,10 +28,8 @@ const MIME_TYPES = {
|
|
|
36
28
|
'.jpg': 'image/jpeg',
|
|
37
29
|
'.svg': 'image/svg+xml'
|
|
38
30
|
};
|
|
39
|
-
|
|
40
31
|
// Note: V0 HMR script injection has been moved to the runtime client.
|
|
41
32
|
// This server purely hosts the V1 HMR contract endpoints.
|
|
42
|
-
|
|
43
33
|
/**
|
|
44
34
|
* Create and start a development server.
|
|
45
35
|
*
|
|
@@ -47,16 +37,8 @@ const MIME_TYPES = {
|
|
|
47
37
|
* @returns {Promise<{ server: import('http').Server, port: number, close: () => void }>}
|
|
48
38
|
*/
|
|
49
39
|
export async function createDevServer(options) {
|
|
50
|
-
const {
|
|
51
|
-
pagesDir,
|
|
52
|
-
outDir,
|
|
53
|
-
port = 3000,
|
|
54
|
-
host = '127.0.0.1',
|
|
55
|
-
config = {},
|
|
56
|
-
logger: providedLogger = null
|
|
57
|
-
} = options;
|
|
40
|
+
const { pagesDir, outDir, port = 3000, host = '127.0.0.1', config = {}, logger: providedLogger = null } = options;
|
|
58
41
|
const logger = providedLogger || createSilentLogger();
|
|
59
|
-
|
|
60
42
|
const resolvedPagesDir = resolve(pagesDir);
|
|
61
43
|
const resolvedOutDir = resolve(outDir);
|
|
62
44
|
const resolvedOutDirTmp = resolve(dirname(resolvedOutDir), `${basename(resolvedOutDir)}.tmp`);
|
|
@@ -65,7 +47,6 @@ export async function createDevServer(options) {
|
|
|
65
47
|
? dirname(pagesParentDir)
|
|
66
48
|
: pagesParentDir;
|
|
67
49
|
const watchRoots = new Set([pagesParentDir]);
|
|
68
|
-
|
|
69
50
|
/** @type {import('http').ServerResponse[]} */
|
|
70
51
|
const hmrClients = [];
|
|
71
52
|
/** @type {import('fs').FSWatcher[]} */
|
|
@@ -74,12 +55,12 @@ export async function createDevServer(options) {
|
|
|
74
55
|
for (const client of hmrClients) {
|
|
75
56
|
try {
|
|
76
57
|
client.write(': ping\n\n');
|
|
77
|
-
}
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
78
60
|
// client disconnected
|
|
79
61
|
}
|
|
80
62
|
}
|
|
81
63
|
}, 15000);
|
|
82
|
-
|
|
83
64
|
let buildId = 0;
|
|
84
65
|
let pendingBuildId = 0;
|
|
85
66
|
let buildStatus = 'ok'; // 'ok' | 'error' | 'building'
|
|
@@ -88,44 +69,44 @@ export async function createDevServer(options) {
|
|
|
88
69
|
let buildError = null;
|
|
89
70
|
const traceEnabled = config.devTrace === true || process.env.ZENITH_DEV_TRACE === '1';
|
|
90
71
|
const verboseLogging = traceEnabled || logger.mode?.logLevel === 'verbose';
|
|
91
|
-
|
|
92
72
|
// Stable dev CSS endpoint points to this backing asset.
|
|
93
73
|
let currentCssAssetPath = '';
|
|
94
74
|
let currentCssHref = '';
|
|
95
75
|
let currentCssContent = '';
|
|
96
76
|
let actualPort = port;
|
|
97
|
-
|
|
98
77
|
function _publicHost() {
|
|
99
78
|
if (host === '0.0.0.0' || host === '::') {
|
|
100
79
|
return '127.0.0.1';
|
|
101
80
|
}
|
|
102
81
|
return host;
|
|
103
82
|
}
|
|
104
|
-
|
|
105
83
|
function _serverOrigin() {
|
|
106
84
|
return `http://${_publicHost()}:${actualPort}`;
|
|
107
85
|
}
|
|
108
|
-
|
|
109
86
|
function _trace(event, payload = {}) {
|
|
110
|
-
if (!traceEnabled)
|
|
87
|
+
if (!traceEnabled)
|
|
88
|
+
return;
|
|
111
89
|
try {
|
|
112
90
|
const detail = Object.keys(payload).length > 0
|
|
113
91
|
? `${event} ${JSON.stringify(payload)}`
|
|
114
92
|
: event;
|
|
115
93
|
logger.verbose('BUILD', detail);
|
|
116
|
-
}
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
117
96
|
// tracing must never break the dev server
|
|
118
97
|
}
|
|
119
98
|
}
|
|
120
|
-
|
|
121
99
|
function _classifyPath(pathname) {
|
|
122
|
-
if (pathname.startsWith('/__zenith_dev/events'))
|
|
123
|
-
|
|
124
|
-
if (pathname.startsWith('/__zenith_dev/
|
|
125
|
-
|
|
100
|
+
if (pathname.startsWith('/__zenith_dev/events'))
|
|
101
|
+
return 'dev_events';
|
|
102
|
+
if (pathname.startsWith('/__zenith_dev/state'))
|
|
103
|
+
return 'dev_state';
|
|
104
|
+
if (pathname.startsWith('/__zenith_dev/styles.css'))
|
|
105
|
+
return 'dev_styles';
|
|
106
|
+
if (pathname.startsWith('/assets/'))
|
|
107
|
+
return 'asset';
|
|
126
108
|
return 'other';
|
|
127
109
|
}
|
|
128
|
-
|
|
129
110
|
function _trace404(req, url, details = {}) {
|
|
130
111
|
_trace('http_404', {
|
|
131
112
|
method: req.method || 'GET',
|
|
@@ -134,7 +115,6 @@ export async function createDevServer(options) {
|
|
|
134
115
|
...details
|
|
135
116
|
});
|
|
136
117
|
}
|
|
137
|
-
|
|
138
118
|
function _pickCssAsset(assets) {
|
|
139
119
|
if (!Array.isArray(assets) || assets.length === 0) {
|
|
140
120
|
return '';
|
|
@@ -148,11 +128,9 @@ export async function createDevServer(options) {
|
|
|
148
128
|
const preferred = cssAssets.find((entry) => /\/styles(\.|\/|$)/.test(entry));
|
|
149
129
|
return preferred || cssAssets[0];
|
|
150
130
|
}
|
|
151
|
-
|
|
152
131
|
function _delay(ms) {
|
|
153
132
|
return new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
|
|
154
133
|
}
|
|
155
|
-
|
|
156
134
|
async function _waitForCssFile(absolutePath, retries = 16, delayMs = 40) {
|
|
157
135
|
for (let i = 0; i <= retries; i++) {
|
|
158
136
|
try {
|
|
@@ -160,7 +138,8 @@ export async function createDevServer(options) {
|
|
|
160
138
|
if (info.isFile()) {
|
|
161
139
|
return true;
|
|
162
140
|
}
|
|
163
|
-
}
|
|
141
|
+
}
|
|
142
|
+
catch {
|
|
164
143
|
// keep retrying
|
|
165
144
|
}
|
|
166
145
|
if (i < retries) {
|
|
@@ -169,7 +148,6 @@ export async function createDevServer(options) {
|
|
|
169
148
|
}
|
|
170
149
|
return false;
|
|
171
150
|
}
|
|
172
|
-
|
|
173
151
|
async function _syncCssStateFromBuild(buildResult, nextBuildId) {
|
|
174
152
|
currentCssHref = `/__zenith_dev/styles.css?buildId=${nextBuildId}`;
|
|
175
153
|
const candidate = _pickCssAsset(buildResult?.assets);
|
|
@@ -177,7 +155,6 @@ export async function createDevServer(options) {
|
|
|
177
155
|
_trace('css_sync_skipped', { reason: 'no_css_asset', buildId: nextBuildId });
|
|
178
156
|
return false;
|
|
179
157
|
}
|
|
180
|
-
|
|
181
158
|
const absoluteCssPath = join(outDir, candidate);
|
|
182
159
|
const ready = await _waitForCssFile(absoluteCssPath);
|
|
183
160
|
if (!ready) {
|
|
@@ -189,11 +166,11 @@ export async function createDevServer(options) {
|
|
|
189
166
|
});
|
|
190
167
|
return false;
|
|
191
168
|
}
|
|
192
|
-
|
|
193
169
|
let cssContent = '';
|
|
194
170
|
try {
|
|
195
171
|
cssContent = await readFile(absoluteCssPath, 'utf8');
|
|
196
|
-
}
|
|
172
|
+
}
|
|
173
|
+
catch {
|
|
197
174
|
_trace('css_sync_skipped', {
|
|
198
175
|
reason: 'css_read_failed',
|
|
199
176
|
buildId: nextBuildId,
|
|
@@ -220,12 +197,10 @@ export async function createDevServer(options) {
|
|
|
220
197
|
});
|
|
221
198
|
cssContent = '/* zenith-dev: empty css */';
|
|
222
199
|
}
|
|
223
|
-
|
|
224
200
|
currentCssAssetPath = candidate;
|
|
225
201
|
currentCssContent = cssContent;
|
|
226
202
|
return true;
|
|
227
203
|
}
|
|
228
|
-
|
|
229
204
|
function _broadcastEvent(type, payload = {}) {
|
|
230
205
|
const eventBuildId = Number.isInteger(payload.buildId) ? payload.buildId : buildId;
|
|
231
206
|
const data = JSON.stringify({
|
|
@@ -242,12 +217,12 @@ export async function createDevServer(options) {
|
|
|
242
217
|
for (const client of hmrClients) {
|
|
243
218
|
try {
|
|
244
219
|
client.write(`event: ${type}\ndata: ${data}\n\n`);
|
|
245
|
-
}
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
246
222
|
// client disconnected
|
|
247
223
|
}
|
|
248
224
|
}
|
|
249
225
|
}
|
|
250
|
-
|
|
251
226
|
// Initial build
|
|
252
227
|
try {
|
|
253
228
|
logger.build('Initial build (id=0)', { onceKey: 'dev-initial-build' });
|
|
@@ -256,7 +231,8 @@ export async function createDevServer(options) {
|
|
|
256
231
|
if (currentCssHref.length > 0) {
|
|
257
232
|
logger.css(`ready (${currentCssHref})`, { onceKey: `css-ready:${buildId}:${currentCssHref}` });
|
|
258
233
|
}
|
|
259
|
-
}
|
|
234
|
+
}
|
|
235
|
+
catch (err) {
|
|
260
236
|
buildStatus = 'error';
|
|
261
237
|
buildError = { message: err instanceof Error ? err.message : String(err) };
|
|
262
238
|
logger.error('initial build failed', {
|
|
@@ -264,14 +240,12 @@ export async function createDevServer(options) {
|
|
|
264
240
|
error: err
|
|
265
241
|
});
|
|
266
242
|
}
|
|
267
|
-
|
|
268
243
|
const server = createServer(async (req, res) => {
|
|
269
244
|
const requestBase = typeof req.headers.host === 'string' && req.headers.host.length > 0
|
|
270
245
|
? `http://${req.headers.host}`
|
|
271
246
|
: _serverOrigin();
|
|
272
247
|
const url = new URL(req.url, requestBase);
|
|
273
248
|
let pathname = url.pathname;
|
|
274
|
-
|
|
275
249
|
// Legacy HMR endpoint (deprecated but kept alive to avoid breaking old caches instantly)
|
|
276
250
|
if (pathname === '/__zenith_hmr') {
|
|
277
251
|
res.writeHead(200, {
|
|
@@ -288,11 +262,11 @@ export async function createDevServer(options) {
|
|
|
288
262
|
hmrClients.push(res);
|
|
289
263
|
req.on('close', () => {
|
|
290
264
|
const idx = hmrClients.indexOf(res);
|
|
291
|
-
if (idx !== -1)
|
|
265
|
+
if (idx !== -1)
|
|
266
|
+
hmrClients.splice(idx, 1);
|
|
292
267
|
});
|
|
293
268
|
return;
|
|
294
269
|
}
|
|
295
|
-
|
|
296
270
|
// V1 Dev State Endpoint
|
|
297
271
|
if (pathname === '/__zenith_dev/state') {
|
|
298
272
|
res.writeHead(200, {
|
|
@@ -310,7 +284,6 @@ export async function createDevServer(options) {
|
|
|
310
284
|
}));
|
|
311
285
|
return;
|
|
312
286
|
}
|
|
313
|
-
|
|
314
287
|
// V1 Dev Events Endpoint (SSE)
|
|
315
288
|
if (pathname === '/__zenith_dev/events') {
|
|
316
289
|
res.writeHead(200, {
|
|
@@ -324,11 +297,11 @@ export async function createDevServer(options) {
|
|
|
324
297
|
hmrClients.push(res);
|
|
325
298
|
req.on('close', () => {
|
|
326
299
|
const idx = hmrClients.indexOf(res);
|
|
327
|
-
if (idx !== -1)
|
|
300
|
+
if (idx !== -1)
|
|
301
|
+
hmrClients.splice(idx, 1);
|
|
328
302
|
});
|
|
329
303
|
return;
|
|
330
304
|
}
|
|
331
|
-
|
|
332
305
|
if (pathname === '/__zenith_dev/styles.css') {
|
|
333
306
|
if (typeof currentCssContent === 'string' && currentCssContent.length > 0) {
|
|
334
307
|
res.writeHead(200, {
|
|
@@ -346,7 +319,8 @@ export async function createDevServer(options) {
|
|
|
346
319
|
if (typeof css === 'string' && css.length > 0) {
|
|
347
320
|
currentCssContent = css;
|
|
348
321
|
}
|
|
349
|
-
}
|
|
322
|
+
}
|
|
323
|
+
catch {
|
|
350
324
|
// keep serving last known CSS body below
|
|
351
325
|
}
|
|
352
326
|
}
|
|
@@ -365,7 +339,6 @@ export async function createDevServer(options) {
|
|
|
365
339
|
res.end(currentCssContent);
|
|
366
340
|
return;
|
|
367
341
|
}
|
|
368
|
-
|
|
369
342
|
if (pathname === '/__zenith/route-check') {
|
|
370
343
|
try {
|
|
371
344
|
// Security: Require explicitly designated header to prevent public oracle probing
|
|
@@ -374,23 +347,19 @@ export async function createDevServer(options) {
|
|
|
374
347
|
res.end(JSON.stringify({ error: 'forbidden', message: 'invalid request context' }));
|
|
375
348
|
return;
|
|
376
349
|
}
|
|
377
|
-
|
|
378
350
|
const targetPath = String(url.searchParams.get('path') || '/');
|
|
379
|
-
|
|
380
351
|
// Security: Prevent protocol/domain injection in path
|
|
381
352
|
if (targetPath.includes('://') || targetPath.startsWith('//') || /[\r\n]/.test(targetPath)) {
|
|
382
353
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
383
354
|
res.end(JSON.stringify({ error: 'invalid_path_format' }));
|
|
384
355
|
return;
|
|
385
356
|
}
|
|
386
|
-
|
|
387
357
|
const targetUrl = new URL(targetPath, url.origin);
|
|
388
358
|
if (targetUrl.origin !== url.origin) {
|
|
389
359
|
res.writeHead(400, { 'Content-Type': 'application/json' });
|
|
390
360
|
res.end(JSON.stringify({ error: 'external_route_evaluation_forbidden' }));
|
|
391
361
|
return;
|
|
392
362
|
}
|
|
393
|
-
|
|
394
363
|
const routes = await loadRouteManifest(outDir);
|
|
395
364
|
const resolvedCheck = resolveRequestRoute(targetUrl, routes);
|
|
396
365
|
if (!resolvedCheck.matched || !resolvedCheck.route) {
|
|
@@ -398,7 +367,6 @@ export async function createDevServer(options) {
|
|
|
398
367
|
res.end(JSON.stringify({ error: 'route_not_found' }));
|
|
399
368
|
return;
|
|
400
369
|
}
|
|
401
|
-
|
|
402
370
|
const checkResult = await executeServerRoute({
|
|
403
371
|
source: resolvedCheck.route.server_script || '',
|
|
404
372
|
sourcePath: resolvedCheck.route.server_script_path || '',
|
|
@@ -420,12 +388,12 @@ export async function createDevServer(options) {
|
|
|
420
388
|
if (parsedLoc.origin !== targetUrl.origin) {
|
|
421
389
|
checkResult.result.location = '/'; // Fallback to root for open redirect attempt
|
|
422
390
|
}
|
|
423
|
-
}
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
424
393
|
checkResult.result.location = '/';
|
|
425
394
|
}
|
|
426
395
|
}
|
|
427
396
|
}
|
|
428
|
-
|
|
429
397
|
res.writeHead(200, {
|
|
430
398
|
'Content-Type': 'application/json',
|
|
431
399
|
'Cache-Control': 'no-store, no-cache, must-revalidate, proxy-revalidate',
|
|
@@ -439,13 +407,13 @@ export async function createDevServer(options) {
|
|
|
439
407
|
to: targetUrl.toString()
|
|
440
408
|
}));
|
|
441
409
|
return;
|
|
442
|
-
}
|
|
410
|
+
}
|
|
411
|
+
catch {
|
|
443
412
|
res.writeHead(500, { 'Content-Type': 'application/json' });
|
|
444
413
|
res.end(JSON.stringify({ error: 'route_check_failed' }));
|
|
445
414
|
return;
|
|
446
415
|
}
|
|
447
416
|
}
|
|
448
|
-
|
|
449
417
|
let resolvedPathFor404 = null;
|
|
450
418
|
let staticRootFor404 = null;
|
|
451
419
|
try {
|
|
@@ -460,32 +428,26 @@ export async function createDevServer(options) {
|
|
|
460
428
|
res.end(asset);
|
|
461
429
|
return;
|
|
462
430
|
}
|
|
463
|
-
|
|
464
431
|
const routes = await loadRouteManifest(outDir);
|
|
465
432
|
const resolved = resolveRequestRoute(url, routes);
|
|
466
433
|
let filePath = null;
|
|
467
|
-
|
|
468
434
|
if (resolved.matched && resolved.route) {
|
|
469
435
|
if (verboseLogging) {
|
|
470
|
-
logger.router(
|
|
471
|
-
`${req.method || 'GET'} ${pathname} -> ${resolved.route.path} params=${JSON.stringify(resolved.params)}`
|
|
472
|
-
);
|
|
436
|
+
logger.router(`${req.method || 'GET'} ${pathname} -> ${resolved.route.path} params=${JSON.stringify(resolved.params)}`);
|
|
473
437
|
}
|
|
474
438
|
const output = resolved.route.output.startsWith('/')
|
|
475
439
|
? resolved.route.output.slice(1)
|
|
476
440
|
: resolved.route.output;
|
|
477
441
|
filePath = resolveWithinDist(outDir, output);
|
|
478
|
-
}
|
|
442
|
+
}
|
|
443
|
+
else {
|
|
479
444
|
filePath = toStaticFilePath(outDir, pathname);
|
|
480
445
|
}
|
|
481
|
-
|
|
482
446
|
resolvedPathFor404 = filePath;
|
|
483
447
|
staticRootFor404 = outDir;
|
|
484
|
-
|
|
485
448
|
if (!filePath) {
|
|
486
449
|
throw new Error('not found');
|
|
487
450
|
}
|
|
488
|
-
|
|
489
451
|
let ssrPayload = null;
|
|
490
452
|
if (resolved.matched && resolved.route?.server_script && resolved.route.prerender !== true) {
|
|
491
453
|
let routeExecution = null;
|
|
@@ -501,7 +463,8 @@ export async function createDevServer(options) {
|
|
|
501
463
|
routeFile: resolved.route.server_script_path || '',
|
|
502
464
|
routeId: resolved.route.route_id || ''
|
|
503
465
|
});
|
|
504
|
-
}
|
|
466
|
+
}
|
|
467
|
+
catch (error) {
|
|
505
468
|
ssrPayload = {
|
|
506
469
|
__zenith_error: {
|
|
507
470
|
code: 'LOAD_FAILED',
|
|
@@ -509,15 +472,11 @@ export async function createDevServer(options) {
|
|
|
509
472
|
}
|
|
510
473
|
};
|
|
511
474
|
}
|
|
512
|
-
|
|
513
475
|
const trace = routeExecution?.trace || { guard: 'none', load: 'none' };
|
|
514
476
|
const routeId = resolved.route.route_id || '';
|
|
515
477
|
if (verboseLogging) {
|
|
516
|
-
logger.router(
|
|
517
|
-
`${routeId || resolved.route.path} guard=${trace.guard} load=${trace.load}`
|
|
518
|
-
);
|
|
478
|
+
logger.router(`${routeId || resolved.route.path} guard=${trace.guard} load=${trace.load}`);
|
|
519
479
|
}
|
|
520
|
-
|
|
521
480
|
const result = routeExecution?.result;
|
|
522
481
|
if (result && result.kind === 'redirect') {
|
|
523
482
|
const status = Number.isInteger(result.status) ? result.status : 302;
|
|
@@ -538,14 +497,14 @@ export async function createDevServer(options) {
|
|
|
538
497
|
ssrPayload = result.data;
|
|
539
498
|
}
|
|
540
499
|
}
|
|
541
|
-
|
|
542
500
|
let content = await readFile(filePath, 'utf8');
|
|
543
501
|
if (ssrPayload) {
|
|
544
502
|
content = injectSsrPayload(content, ssrPayload);
|
|
545
503
|
}
|
|
546
504
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
547
505
|
res.end(content);
|
|
548
|
-
}
|
|
506
|
+
}
|
|
507
|
+
catch {
|
|
549
508
|
_trace404(req, url, {
|
|
550
509
|
reason: 'not_found',
|
|
551
510
|
staticRoot: staticRootFor404,
|
|
@@ -555,7 +514,6 @@ export async function createDevServer(options) {
|
|
|
555
514
|
res.end('404 Not Found');
|
|
556
515
|
}
|
|
557
516
|
});
|
|
558
|
-
|
|
559
517
|
/**
|
|
560
518
|
* Broadcast HMR reload to all connected clients.
|
|
561
519
|
*/
|
|
@@ -563,31 +521,29 @@ export async function createDevServer(options) {
|
|
|
563
521
|
for (const client of hmrClients) {
|
|
564
522
|
try {
|
|
565
523
|
client.write('data: reload\n\n');
|
|
566
|
-
}
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
567
526
|
// client disconnected
|
|
568
527
|
}
|
|
569
528
|
}
|
|
570
529
|
}
|
|
571
|
-
|
|
572
530
|
let _buildDebounce = null;
|
|
573
531
|
let _queuedFiles = new Set();
|
|
574
532
|
const _lastQueuedFingerprints = new Map();
|
|
575
533
|
let _buildInFlight = false;
|
|
576
|
-
|
|
577
534
|
function _isWithin(parent, child) {
|
|
578
535
|
const rel = relative(parent, child);
|
|
579
536
|
return rel === '' || (!rel.startsWith('..') && !isAbsolute(rel));
|
|
580
537
|
}
|
|
581
|
-
|
|
582
538
|
function _toDisplayPath(absPath) {
|
|
583
539
|
const rel = relative(projectRoot, absPath);
|
|
584
|
-
if (rel === '')
|
|
540
|
+
if (rel === '')
|
|
541
|
+
return '.';
|
|
585
542
|
if (!rel.startsWith('..') && !isAbsolute(rel)) {
|
|
586
543
|
return rel;
|
|
587
544
|
}
|
|
588
545
|
return absPath;
|
|
589
546
|
}
|
|
590
|
-
|
|
591
547
|
function _shouldIgnoreChange(absPath) {
|
|
592
548
|
if (_isWithin(resolvedOutDir, absPath)) {
|
|
593
549
|
return true;
|
|
@@ -606,7 +562,6 @@ export async function createDevServer(options) {
|
|
|
606
562
|
|| segments.includes('target')
|
|
607
563
|
|| segments.includes('.turbo');
|
|
608
564
|
}
|
|
609
|
-
|
|
610
565
|
/**
|
|
611
566
|
* Start watching source roots for changes.
|
|
612
567
|
*/
|
|
@@ -620,7 +575,6 @@ export async function createDevServer(options) {
|
|
|
620
575
|
void drainBuildQueue();
|
|
621
576
|
}, delayMs);
|
|
622
577
|
};
|
|
623
|
-
|
|
624
578
|
const drainBuildQueue = async () => {
|
|
625
579
|
if (_buildInFlight) {
|
|
626
580
|
return;
|
|
@@ -630,39 +584,33 @@ export async function createDevServer(options) {
|
|
|
630
584
|
return;
|
|
631
585
|
}
|
|
632
586
|
_queuedFiles.clear();
|
|
633
|
-
|
|
634
587
|
_buildInFlight = true;
|
|
635
588
|
const cycleBuildId = pendingBuildId + 1;
|
|
636
589
|
pendingBuildId = cycleBuildId;
|
|
637
590
|
buildStatus = 'building';
|
|
638
591
|
logger.build(`Rebuild (id=${cycleBuildId})`);
|
|
639
592
|
_broadcastEvent('build_start', { buildId: cycleBuildId, changedFiles: changed });
|
|
640
|
-
|
|
641
593
|
const startTime = Date.now();
|
|
642
594
|
const previousCssAssetPath = currentCssAssetPath;
|
|
643
595
|
const previousCssContent = currentCssContent;
|
|
644
596
|
try {
|
|
645
597
|
const buildResult = await build({ pagesDir, outDir, config, logger });
|
|
646
598
|
const cssReady = await _syncCssStateFromBuild(buildResult, cycleBuildId);
|
|
647
|
-
const cssChanged = cssReady && (
|
|
648
|
-
|
|
649
|
-
currentCssContent !== previousCssContent
|
|
650
|
-
);
|
|
599
|
+
const cssChanged = cssReady && (currentCssAssetPath !== previousCssAssetPath ||
|
|
600
|
+
currentCssContent !== previousCssContent);
|
|
651
601
|
buildId = cycleBuildId;
|
|
652
602
|
buildStatus = 'ok';
|
|
653
603
|
buildError = null;
|
|
654
604
|
lastBuildMs = Date.now();
|
|
655
605
|
durationMs = lastBuildMs - startTime;
|
|
656
606
|
logger.build(`Complete (id=${cycleBuildId}, ${durationMs}ms)`);
|
|
657
|
-
|
|
658
607
|
_broadcastEvent('build_complete', {
|
|
659
608
|
buildId: cycleBuildId,
|
|
660
609
|
durationMs,
|
|
661
610
|
status: buildStatus,
|
|
662
611
|
cssHref: currentCssHref,
|
|
663
612
|
changedFiles: changed
|
|
664
|
-
}
|
|
665
|
-
);
|
|
613
|
+
});
|
|
666
614
|
_trace('state_snapshot', {
|
|
667
615
|
status: buildStatus,
|
|
668
616
|
buildId: cycleBuildId,
|
|
@@ -670,18 +618,17 @@ export async function createDevServer(options) {
|
|
|
670
618
|
durationMs,
|
|
671
619
|
changedFiles: changed
|
|
672
620
|
});
|
|
673
|
-
|
|
674
621
|
if (cssChanged && currentCssHref.length > 0) {
|
|
675
622
|
logger.css(`ready (${currentCssHref})`);
|
|
676
623
|
logger.hmr(`css_update (buildId=${cycleBuildId})`);
|
|
677
624
|
_broadcastEvent('css_update', { href: currentCssHref, changedFiles: changed });
|
|
678
625
|
}
|
|
679
|
-
|
|
680
626
|
const onlyCss = changed.length > 0 && changed.every((f) => f.endsWith('.css'));
|
|
681
627
|
if (!onlyCss) {
|
|
682
628
|
logger.hmr(`reload (buildId=${cycleBuildId})`);
|
|
683
629
|
_broadcastEvent('reload', { changedFiles: changed });
|
|
684
|
-
}
|
|
630
|
+
}
|
|
631
|
+
else {
|
|
685
632
|
_trace('css_only_update', {
|
|
686
633
|
buildId: cycleBuildId,
|
|
687
634
|
cssHref: currentCssHref,
|
|
@@ -689,7 +636,8 @@ export async function createDevServer(options) {
|
|
|
689
636
|
changedFiles: changed
|
|
690
637
|
});
|
|
691
638
|
}
|
|
692
|
-
}
|
|
639
|
+
}
|
|
640
|
+
catch (err) {
|
|
693
641
|
const fullError = err instanceof Error ? err.message : String(err);
|
|
694
642
|
buildStatus = 'error';
|
|
695
643
|
buildError = { message: fullError.length > 10000 ? fullError.slice(0, 10000) + '... (truncated)' : fullError };
|
|
@@ -699,7 +647,6 @@ export async function createDevServer(options) {
|
|
|
699
647
|
hint: 'fix the error and save again',
|
|
700
648
|
error: err
|
|
701
649
|
});
|
|
702
|
-
|
|
703
650
|
_broadcastEvent('build_error', { buildId: cycleBuildId, ...buildError, changedFiles: changed });
|
|
704
651
|
_trace('state_snapshot', {
|
|
705
652
|
status: buildStatus,
|
|
@@ -708,17 +655,18 @@ export async function createDevServer(options) {
|
|
|
708
655
|
durationMs,
|
|
709
656
|
error: buildError
|
|
710
657
|
});
|
|
711
|
-
}
|
|
658
|
+
}
|
|
659
|
+
finally {
|
|
712
660
|
_buildInFlight = false;
|
|
713
661
|
if (_queuedFiles.size > 0) {
|
|
714
662
|
triggerBuildDrain(20);
|
|
715
663
|
}
|
|
716
664
|
}
|
|
717
665
|
};
|
|
718
|
-
|
|
719
666
|
const roots = Array.from(watchRoots);
|
|
720
667
|
for (const root of roots) {
|
|
721
|
-
if (!existsSync(root))
|
|
668
|
+
if (!existsSync(root))
|
|
669
|
+
continue;
|
|
722
670
|
try {
|
|
723
671
|
const watcher = watch(root, { recursive: true }, (_eventType, filename) => {
|
|
724
672
|
if (!filename) {
|
|
@@ -739,17 +687,16 @@ export async function createDevServer(options) {
|
|
|
739
687
|
})();
|
|
740
688
|
});
|
|
741
689
|
_watchers.push(watcher);
|
|
742
|
-
}
|
|
690
|
+
}
|
|
691
|
+
catch {
|
|
743
692
|
// fs.watch recursive may not be supported on this platform/root
|
|
744
693
|
}
|
|
745
694
|
}
|
|
746
695
|
}
|
|
747
|
-
|
|
748
696
|
return new Promise((resolve) => {
|
|
749
697
|
server.listen(port, host, () => {
|
|
750
698
|
actualPort = server.address().port;
|
|
751
699
|
_startWatcher();
|
|
752
|
-
|
|
753
700
|
resolve({
|
|
754
701
|
server,
|
|
755
702
|
port: actualPort,
|
|
@@ -758,13 +705,17 @@ export async function createDevServer(options) {
|
|
|
758
705
|
for (const watcher of _watchers) {
|
|
759
706
|
try {
|
|
760
707
|
watcher.close();
|
|
761
|
-
}
|
|
708
|
+
}
|
|
709
|
+
catch {
|
|
762
710
|
// ignore close errors
|
|
763
711
|
}
|
|
764
712
|
}
|
|
765
713
|
_watchers = [];
|
|
766
714
|
for (const client of hmrClients) {
|
|
767
|
-
try {
|
|
715
|
+
try {
|
|
716
|
+
client.end();
|
|
717
|
+
}
|
|
718
|
+
catch { }
|
|
768
719
|
}
|
|
769
720
|
hmrClients.length = 0;
|
|
770
721
|
server.close();
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export function readChangeFingerprint(absPath: any): Promise<string>;
|
package/dist/dev-watch.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { stat } from 'node:fs/promises';
|
|
2
|
-
|
|
3
2
|
export async function readChangeFingerprint(absPath) {
|
|
4
3
|
try {
|
|
5
4
|
const info = await stat(absPath);
|
|
@@ -9,7 +8,8 @@ export async function readChangeFingerprint(absPath) {
|
|
|
9
8
|
? 'file'
|
|
10
9
|
: 'other';
|
|
11
10
|
return `${kind}:${info.mtimeMs}:${info.size}`;
|
|
12
|
-
}
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
13
|
const code = error && typeof error === 'object' ? error.code : '';
|
|
14
14
|
if (code === 'ENOENT' || code === 'ENOTDIR') {
|
|
15
15
|
return 'missing';
|
package/dist/index.d.ts
ADDED