arcway 0.1.22 → 0.1.24
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/client/page-loader.js +45 -0
- package/client/router.js +25 -2
- package/package.json +4 -1
- package/server/boot/index.js +10 -1
- package/server/build.js +2 -0
- package/server/config/modules/pages.js +16 -1
- package/server/pages/build-cache.js +1 -1
- package/server/pages/build-client.js +23 -3
- package/server/pages/handler.js +38 -10
- package/server/pages/hmr.js +58 -31
- package/server/pages/lazy-context.js +227 -39
- package/server/pages/out-dir.js +11 -0
- package/server/pages/pages-router.js +7 -0
- package/server/pages/ssr.js +23 -7
- package/server/pages/vite-dev.js +246 -0
- package/server/pages/watcher.js +3 -1
- package/server/watcher.js +11 -2
|
@@ -28,7 +28,8 @@ import {
|
|
|
28
28
|
import { createClientBuildContext } from './build-client.js';
|
|
29
29
|
import { buildCssBundle } from './build-css.js';
|
|
30
30
|
import { resolveFonts } from './fonts.js';
|
|
31
|
-
import { buildHmrRuntimeBundle } from './hmr.js';
|
|
31
|
+
import { buildHmrRuntimeBundle, diffClientMetafiles } from './hmr.js';
|
|
32
|
+
import { syncViteHydrationEntries } from './vite-dev.js';
|
|
32
33
|
|
|
33
34
|
// Dev-mode build context that defers per-page/layout/middleware server-bundle
|
|
34
35
|
// compilation to first request. Startup is cheap: discover routes, build the
|
|
@@ -50,6 +51,7 @@ async function createLazyPagesContext(options) {
|
|
|
50
51
|
const clientTarget = options.clientTarget ?? 'es2022';
|
|
51
52
|
const minify = options.minify ?? false;
|
|
52
53
|
const devMode = options.devMode ?? true;
|
|
54
|
+
const viteDev = options.viteDev ?? false;
|
|
53
55
|
const esbuildImpl = options.esbuild ?? esbuild;
|
|
54
56
|
// Cap per-context server-bundle concurrency so a request burst after a
|
|
55
57
|
// cold start can't spawn N pages × M esbuild Go workers simultaneously.
|
|
@@ -63,6 +65,7 @@ async function createLazyPagesContext(options) {
|
|
|
63
65
|
|
|
64
66
|
let disposed = false;
|
|
65
67
|
let clientCtx = null;
|
|
68
|
+
let currentClientMetafile = null;
|
|
66
69
|
let fontFaceCss;
|
|
67
70
|
let fontPreloadHtml;
|
|
68
71
|
|
|
@@ -101,6 +104,13 @@ async function createLazyPagesContext(options) {
|
|
|
101
104
|
{ srcPath: path.resolve(m.filePath), built: false, stale: false },
|
|
102
105
|
]),
|
|
103
106
|
),
|
|
107
|
+
loadings: new Map(
|
|
108
|
+
loadings.map((l) => [
|
|
109
|
+
l.dirPath,
|
|
110
|
+
{ srcPath: path.resolve(l.filePath) },
|
|
111
|
+
]),
|
|
112
|
+
),
|
|
113
|
+
stylesPath: stylesPath ? path.resolve(stylesPath) : null,
|
|
104
114
|
version: 0,
|
|
105
115
|
};
|
|
106
116
|
|
|
@@ -120,11 +130,46 @@ async function createLazyPagesContext(options) {
|
|
|
120
130
|
events.emit(event, payload);
|
|
121
131
|
}
|
|
122
132
|
|
|
133
|
+
function applyClientResult(clientResult) {
|
|
134
|
+
for (const entry of manifest.entries) {
|
|
135
|
+
const clientBundle = clientResult.bundles.get(entry.pattern);
|
|
136
|
+
const navBundle = clientResult.navBundles.get(entry.pattern);
|
|
137
|
+
if (clientBundle) entry.clientBundle = clientBundle;
|
|
138
|
+
if (navBundle) entry.navBundle = navBundle;
|
|
139
|
+
entry.layoutClientBundles = entry.layoutDirs
|
|
140
|
+
.map((d) => clientResult.layoutNavBundles.get(d))
|
|
141
|
+
.filter((v) => v !== undefined);
|
|
142
|
+
entry.loadingClientBundles = entry.loadingDirs
|
|
143
|
+
.map((d) => clientResult.loadingNavBundles.get(d))
|
|
144
|
+
.filter((v) => v !== undefined);
|
|
145
|
+
entry.sharedChunks = clientResult.entryChunks?.get(entry.pattern) ?? [];
|
|
146
|
+
entry.sharedCssChunks = clientResult.entryCssChunks?.get(entry.pattern) ?? [];
|
|
147
|
+
}
|
|
148
|
+
currentClientMetafile = clientResult.metafile ?? currentClientMetafile;
|
|
149
|
+
}
|
|
150
|
+
|
|
123
151
|
// Client bundle + CSS + error pages + HMR runtime are built eagerly at
|
|
124
152
|
// startup. They're shared across every request and their compilation cost
|
|
125
153
|
// is bounded (a single esbuild.context for the client, one esbuild.transform
|
|
126
154
|
// for CSS), so there's no win in deferring them.
|
|
127
155
|
async function eagerSharedBuilds() {
|
|
156
|
+
if (viteDev) {
|
|
157
|
+
const [errorBundles] = await Promise.all([
|
|
158
|
+
buildErrorPageBundles(errorPages, outDir, serverTarget, {
|
|
159
|
+
rootDir,
|
|
160
|
+
devMode,
|
|
161
|
+
minify,
|
|
162
|
+
limit,
|
|
163
|
+
}),
|
|
164
|
+
syncViteHydrationEntries({ manifest, outDir }),
|
|
165
|
+
]);
|
|
166
|
+
if (errorBundles.error) manifest.errorBundle = errorBundles.error;
|
|
167
|
+
if (errorBundles.notFound) manifest.notFoundBundle = errorBundles.notFound;
|
|
168
|
+
bumpVersion();
|
|
169
|
+
emit('update', { type: 'startup', version: manifest.version });
|
|
170
|
+
return { clientMetafile: null };
|
|
171
|
+
}
|
|
172
|
+
|
|
128
173
|
clientCtx = await createClientBuildContext(
|
|
129
174
|
pages,
|
|
130
175
|
layouts,
|
|
@@ -151,20 +196,7 @@ async function createLazyPagesContext(options) {
|
|
|
151
196
|
// Fold client bundle output into the manifest so renderPage() can find
|
|
152
197
|
// navBundle / layoutClientBundles / shared chunks immediately, the same
|
|
153
198
|
// as it would from a disk-backed prod manifest.
|
|
154
|
-
|
|
155
|
-
const clientBundle = clientResult.bundles.get(entry.pattern);
|
|
156
|
-
const navBundle = clientResult.navBundles.get(entry.pattern);
|
|
157
|
-
if (clientBundle) entry.clientBundle = clientBundle;
|
|
158
|
-
if (navBundle) entry.navBundle = navBundle;
|
|
159
|
-
entry.layoutClientBundles = entry.layoutDirs
|
|
160
|
-
.map((d) => clientResult.layoutNavBundles.get(d))
|
|
161
|
-
.filter((v) => v !== undefined);
|
|
162
|
-
entry.loadingClientBundles = entry.loadingDirs
|
|
163
|
-
.map((d) => clientResult.loadingNavBundles.get(d))
|
|
164
|
-
.filter((v) => v !== undefined);
|
|
165
|
-
entry.sharedChunks = clientResult.entryChunks?.get(entry.pattern) ?? [];
|
|
166
|
-
entry.sharedCssChunks = clientResult.entryCssChunks?.get(entry.pattern) ?? [];
|
|
167
|
-
}
|
|
199
|
+
applyClientResult(clientResult);
|
|
168
200
|
if (cssBundle) manifest.cssBundle = cssBundle;
|
|
169
201
|
if (errorBundles.error) manifest.errorBundle = errorBundles.error;
|
|
170
202
|
if (errorBundles.notFound) manifest.notFoundBundle = errorBundles.notFound;
|
|
@@ -314,13 +346,55 @@ async function createLazyPagesContext(options) {
|
|
|
314
346
|
});
|
|
315
347
|
}
|
|
316
348
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
349
|
+
function markAllEntriesStale() {
|
|
350
|
+
let touched = false;
|
|
351
|
+
const affected = { pages: [], layouts: [], middlewares: [] };
|
|
352
|
+
for (const entry of manifest.entries) {
|
|
353
|
+
if (!entry.stale || entry.built) {
|
|
354
|
+
entry.stale = true;
|
|
355
|
+
entry.built = false;
|
|
356
|
+
touched = true;
|
|
357
|
+
}
|
|
358
|
+
affected.pages.push(entry.pattern);
|
|
359
|
+
}
|
|
360
|
+
for (const [dirPath, layout] of manifest.layouts.entries()) {
|
|
361
|
+
if (!layout.stale || layout.built) {
|
|
362
|
+
layout.stale = true;
|
|
363
|
+
layout.built = false;
|
|
364
|
+
touched = true;
|
|
365
|
+
}
|
|
366
|
+
affected.layouts.push(dirPath);
|
|
367
|
+
}
|
|
368
|
+
for (const [dirPath, mw] of manifest.middlewares.entries()) {
|
|
369
|
+
if (!mw.stale || mw.built) {
|
|
370
|
+
mw.stale = true;
|
|
371
|
+
mw.built = false;
|
|
372
|
+
touched = true;
|
|
373
|
+
}
|
|
374
|
+
affected.middlewares.push(dirPath);
|
|
375
|
+
}
|
|
376
|
+
return { touched, affected };
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function shouldInvalidateWholeTree(filePath, affected) {
|
|
380
|
+
if (
|
|
381
|
+
affected.pages.length > 0 ||
|
|
382
|
+
affected.layouts.length > 0 ||
|
|
383
|
+
affected.middlewares.length > 0
|
|
384
|
+
) {
|
|
385
|
+
return false;
|
|
386
|
+
}
|
|
387
|
+
const rel = path.relative(rootDir, filePath).replace(/\\/g, '/');
|
|
388
|
+
if (!rel || rel.startsWith('..')) return false;
|
|
389
|
+
return /^(components|hooks|client|packages)\//.test(rel);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function markAffectedStale(filePath) {
|
|
323
393
|
const affected = mapFileToAffected(filePath, manifest);
|
|
394
|
+
if (shouldInvalidateWholeTree(filePath, affected)) {
|
|
395
|
+
return markAllEntriesStale();
|
|
396
|
+
}
|
|
397
|
+
|
|
324
398
|
let touched = false;
|
|
325
399
|
for (const pattern of affected.pages) {
|
|
326
400
|
const entry = findEntry(pattern);
|
|
@@ -346,6 +420,16 @@ async function createLazyPagesContext(options) {
|
|
|
346
420
|
touched = true;
|
|
347
421
|
}
|
|
348
422
|
}
|
|
423
|
+
return { touched, affected };
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
// Mark every manifest entry affected by a source change as stale. Does not
|
|
427
|
+
// trigger rebuilds directly — the next `ensurePageBuilt()` call (driven by
|
|
428
|
+
// an incoming request) handles the actual work. Structural changes
|
|
429
|
+
// (add/remove files) are not handled here; the watcher will re-init the
|
|
430
|
+
// context or extend the manifest in phase 5.
|
|
431
|
+
function invalidate(filePath) {
|
|
432
|
+
const { touched, affected } = markAffectedStale(filePath);
|
|
349
433
|
if (touched) {
|
|
350
434
|
bumpVersion();
|
|
351
435
|
emit('update', { type: 'invalidate', filePath, affected, version: manifest.version });
|
|
@@ -353,6 +437,87 @@ async function createLazyPagesContext(options) {
|
|
|
353
437
|
return { touched, affected };
|
|
354
438
|
}
|
|
355
439
|
|
|
440
|
+
let inFlightClientRefresh = null;
|
|
441
|
+
|
|
442
|
+
async function handleSourceChange(filePath) {
|
|
443
|
+
if (disposed) throw new Error('Lazy pages context is disposed');
|
|
444
|
+
const { touched, affected } = markAffectedStale(filePath);
|
|
445
|
+
if (!touched) return { touched: false, affected, hmr: null };
|
|
446
|
+
if (viteDev) {
|
|
447
|
+
bumpVersion();
|
|
448
|
+
emit('update', { type: 'invalidate', filePath, affected, version: manifest.version });
|
|
449
|
+
return { touched: true, affected, hmr: null };
|
|
450
|
+
}
|
|
451
|
+
if (inFlightClientRefresh) return inFlightClientRefresh;
|
|
452
|
+
|
|
453
|
+
const task = (async () => {
|
|
454
|
+
const previousMetafile = currentClientMetafile;
|
|
455
|
+
const previousCssBundle = manifest.cssBundle ?? null;
|
|
456
|
+
const timestamp = Date.now();
|
|
457
|
+
|
|
458
|
+
const [clientResult, cssBundle] = await Promise.all([
|
|
459
|
+
clientCtx?.rebuild() ?? Promise.resolve(null),
|
|
460
|
+
buildCssBundle(pagesDir, outDir, minify, stylesPath, fontFaceCss, rootDir),
|
|
461
|
+
]);
|
|
462
|
+
|
|
463
|
+
if (clientResult) {
|
|
464
|
+
applyClientResult(clientResult);
|
|
465
|
+
}
|
|
466
|
+
if (cssBundle) manifest.cssBundle = cssBundle;
|
|
467
|
+
|
|
468
|
+
bumpVersion();
|
|
469
|
+
emit('update', { type: 'invalidate', filePath, affected, version: manifest.version });
|
|
470
|
+
|
|
471
|
+
let hmr = null;
|
|
472
|
+
if (clientResult && previousMetafile) {
|
|
473
|
+
const diff = diffClientMetafiles(previousMetafile, clientResult.metafile, outDir);
|
|
474
|
+
const modules = [...diff.changedHydrationBundles, ...diff.changedNavBundles];
|
|
475
|
+
const routeBundles = {};
|
|
476
|
+
for (const pattern of affected.pages) {
|
|
477
|
+
const entry = findEntry(pattern);
|
|
478
|
+
if (entry?.clientBundle) routeBundles[pattern] = entry.clientBundle;
|
|
479
|
+
}
|
|
480
|
+
if (diff.needsFullReload) {
|
|
481
|
+
hmr = { type: 'reload', timestamp };
|
|
482
|
+
emit('update', {
|
|
483
|
+
type: 'reload',
|
|
484
|
+
filePath,
|
|
485
|
+
reason: 'client-bundle-drift',
|
|
486
|
+
timestamp,
|
|
487
|
+
version: manifest.version,
|
|
488
|
+
});
|
|
489
|
+
} else if (modules.length > 0) {
|
|
490
|
+
hmr = { type: 'hmr-update', modules, routeBundles, timestamp };
|
|
491
|
+
emit('update', {
|
|
492
|
+
type: 'hmr-update',
|
|
493
|
+
filePath,
|
|
494
|
+
modules,
|
|
495
|
+
routeBundles,
|
|
496
|
+
timestamp,
|
|
497
|
+
version: manifest.version,
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
if (manifest.cssBundle) {
|
|
503
|
+
emit('update', {
|
|
504
|
+
type: 'css-update',
|
|
505
|
+
filePath,
|
|
506
|
+
cssBundle: manifest.cssBundle,
|
|
507
|
+
timestamp,
|
|
508
|
+
version: manifest.version,
|
|
509
|
+
});
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
return { touched: true, affected, hmr };
|
|
513
|
+
})().finally(() => {
|
|
514
|
+
inFlightClientRefresh = null;
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
inFlightClientRefresh = task;
|
|
518
|
+
return task;
|
|
519
|
+
}
|
|
520
|
+
|
|
356
521
|
// ── Structural rediscovery ─────────────────────────────────────────────
|
|
357
522
|
// `invalidate()` handles content edits to known files; `rediscover()`
|
|
358
523
|
// handles add/delete of pages, layouts, and middlewares. Re-runs the same
|
|
@@ -385,6 +550,8 @@ async function createLazyPagesContext(options) {
|
|
|
385
550
|
const result = {
|
|
386
551
|
added: { pages: [], layouts: [], middlewares: [] },
|
|
387
552
|
removed: { pages: [], layouts: [], middlewares: [] },
|
|
553
|
+
addedLoadings: [],
|
|
554
|
+
removedLoadings: [],
|
|
388
555
|
staleByCascade: [],
|
|
389
556
|
clientRebuilt: false,
|
|
390
557
|
version: manifest.version,
|
|
@@ -406,13 +573,20 @@ async function createLazyPagesContext(options) {
|
|
|
406
573
|
const addedMiddlewares = nextMiddlewares.filter((m) => !currentMwDirs.has(m.dirPath));
|
|
407
574
|
const removedMiddlewares = [...currentMwDirs].filter((d) => !nextMwDirs.has(d));
|
|
408
575
|
|
|
576
|
+
const currentLoadingDirs = new Set(manifest.loadings.keys());
|
|
577
|
+
const nextLoadingDirs = new Set(nextLoadings.map((l) => l.dirPath));
|
|
578
|
+
const addedLoadings = nextLoadings.filter((l) => !currentLoadingDirs.has(l.dirPath));
|
|
579
|
+
const removedLoadings = [...currentLoadingDirs].filter((d) => !nextLoadingDirs.has(d));
|
|
580
|
+
|
|
409
581
|
const noOp =
|
|
410
582
|
addedPages.length === 0 &&
|
|
411
583
|
removedPages.length === 0 &&
|
|
412
584
|
addedLayouts.length === 0 &&
|
|
413
585
|
removedLayouts.length === 0 &&
|
|
414
586
|
addedMiddlewares.length === 0 &&
|
|
415
|
-
removedMiddlewares.length === 0
|
|
587
|
+
removedMiddlewares.length === 0 &&
|
|
588
|
+
addedLoadings.length === 0 &&
|
|
589
|
+
removedLoadings.length === 0;
|
|
416
590
|
if (noOp) return result;
|
|
417
591
|
|
|
418
592
|
// ── Apply layout add/remove ──────────────────────────────────────
|
|
@@ -445,6 +619,17 @@ async function createLazyPagesContext(options) {
|
|
|
445
619
|
result.removed.middlewares.push(dirPath);
|
|
446
620
|
}
|
|
447
621
|
|
|
622
|
+
for (const loading of addedLoadings) {
|
|
623
|
+
manifest.loadings.set(loading.dirPath, {
|
|
624
|
+
srcPath: path.resolve(loading.filePath),
|
|
625
|
+
});
|
|
626
|
+
result.addedLoadings.push(loading.dirPath);
|
|
627
|
+
}
|
|
628
|
+
for (const dirPath of removedLoadings) {
|
|
629
|
+
manifest.loadings.delete(dirPath);
|
|
630
|
+
result.removedLoadings.push(dirPath);
|
|
631
|
+
}
|
|
632
|
+
|
|
448
633
|
// ── Apply page add/remove ────────────────────────────────────────
|
|
449
634
|
const addedPagePatterns = new Set();
|
|
450
635
|
for (const page of addedPages) {
|
|
@@ -494,8 +679,10 @@ async function createLazyPagesContext(options) {
|
|
|
494
679
|
result.added.pages.length > 0 ||
|
|
495
680
|
result.removed.pages.length > 0 ||
|
|
496
681
|
result.added.layouts.length > 0 ||
|
|
497
|
-
result.removed.layouts.length > 0
|
|
498
|
-
|
|
682
|
+
result.removed.layouts.length > 0 ||
|
|
683
|
+
result.addedLoadings.length > 0 ||
|
|
684
|
+
result.removedLoadings.length > 0;
|
|
685
|
+
if (needClientRebuild && !viteDev) {
|
|
499
686
|
if (clientCtx) {
|
|
500
687
|
try {
|
|
501
688
|
await clientCtx.dispose();
|
|
@@ -515,22 +702,14 @@ async function createLazyPagesContext(options) {
|
|
|
515
702
|
devMode,
|
|
516
703
|
);
|
|
517
704
|
const clientResult = await clientCtx.rebuild();
|
|
518
|
-
|
|
519
|
-
const clientBundle = clientResult.bundles.get(entry.pattern);
|
|
520
|
-
const navBundle = clientResult.navBundles.get(entry.pattern);
|
|
521
|
-
if (clientBundle) entry.clientBundle = clientBundle;
|
|
522
|
-
if (navBundle) entry.navBundle = navBundle;
|
|
523
|
-
entry.layoutClientBundles = entry.layoutDirs
|
|
524
|
-
.map((d) => clientResult.layoutNavBundles.get(d))
|
|
525
|
-
.filter((v) => v !== undefined);
|
|
526
|
-
entry.loadingClientBundles = entry.loadingDirs
|
|
527
|
-
.map((d) => clientResult.loadingNavBundles.get(d))
|
|
528
|
-
.filter((v) => v !== undefined);
|
|
529
|
-
entry.sharedChunks = clientResult.entryChunks?.get(entry.pattern) ?? [];
|
|
530
|
-
entry.sharedCssChunks = clientResult.entryCssChunks?.get(entry.pattern) ?? [];
|
|
531
|
-
}
|
|
705
|
+
applyClientResult(clientResult);
|
|
532
706
|
result.clientRebuilt = true;
|
|
533
707
|
}
|
|
708
|
+
if (viteDev) {
|
|
709
|
+
const nextStylesPath = await discoverStyles(pagesDir);
|
|
710
|
+
manifest.stylesPath = nextStylesPath ? path.resolve(nextStylesPath) : null;
|
|
711
|
+
await syncViteHydrationEntries({ manifest, outDir });
|
|
712
|
+
}
|
|
534
713
|
|
|
535
714
|
// ── Version bump + event emission ────────────────────────────────
|
|
536
715
|
bumpVersion();
|
|
@@ -553,6 +732,12 @@ async function createLazyPagesContext(options) {
|
|
|
553
732
|
for (const dirPath of result.removed.middlewares) {
|
|
554
733
|
emit('update', { type: 'remove-middleware', dirPath, version: manifest.version });
|
|
555
734
|
}
|
|
735
|
+
for (const dirPath of result.addedLoadings) {
|
|
736
|
+
emit('update', { type: 'add-loading', dirPath, version: manifest.version });
|
|
737
|
+
}
|
|
738
|
+
for (const dirPath of result.removedLoadings) {
|
|
739
|
+
emit('update', { type: 'remove-loading', dirPath, version: manifest.version });
|
|
740
|
+
}
|
|
556
741
|
if (result.clientRebuilt) {
|
|
557
742
|
emit('update', { type: 'client-rebuilt', version: manifest.version });
|
|
558
743
|
}
|
|
@@ -603,9 +788,11 @@ async function createLazyPagesContext(options) {
|
|
|
603
788
|
stale: m.stale,
|
|
604
789
|
...(m.lastBuiltAt ? { lastBuiltAt: m.lastBuiltAt } : {}),
|
|
605
790
|
})),
|
|
791
|
+
loadings: Array.from(manifest.loadings.keys()).map((dirPath) => ({ dirPath })),
|
|
606
792
|
...(manifest.errorBundle ? { errorBundle: manifest.errorBundle } : {}),
|
|
607
793
|
...(manifest.notFoundBundle ? { notFoundBundle: manifest.notFoundBundle } : {}),
|
|
608
794
|
...(manifest.cssBundle ? { cssBundle: manifest.cssBundle } : {}),
|
|
795
|
+
...(manifest.stylesPath ? { stylesPath: manifest.stylesPath } : {}),
|
|
609
796
|
};
|
|
610
797
|
}
|
|
611
798
|
|
|
@@ -627,12 +814,13 @@ async function createLazyPagesContext(options) {
|
|
|
627
814
|
ensureLayoutBuilt,
|
|
628
815
|
ensureMiddlewareBuilt,
|
|
629
816
|
invalidate,
|
|
817
|
+
handleSourceChange,
|
|
630
818
|
rediscover,
|
|
631
819
|
dispose,
|
|
632
820
|
on,
|
|
633
821
|
getManifestJson,
|
|
634
822
|
get clientMetafile() {
|
|
635
|
-
return startup.clientMetafile;
|
|
823
|
+
return currentClientMetafile ?? startup.clientMetafile;
|
|
636
824
|
},
|
|
637
825
|
};
|
|
638
826
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
function resolvePagesOutDir(config, rootDir, mode = 'production') {
|
|
4
|
+
const pages = config?.pages ?? {};
|
|
5
|
+
const isDev = mode === 'development';
|
|
6
|
+
const prodOutDir = pages.outDir ?? path.resolve(rootDir, '.build/pages');
|
|
7
|
+
const devOutDir = pages.devOutDir ?? path.resolve(rootDir, '.build-dev/pages');
|
|
8
|
+
return isDev ? devOutDir : prodOutDir;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export { resolvePagesOutDir };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createPagesHandler } from './handler.js';
|
|
2
2
|
import { createLazyPagesContext } from './lazy-context.js';
|
|
3
3
|
import { createPagesWatcher } from './watcher.js';
|
|
4
|
+
import { resolvePagesOutDir } from './out-dir.js';
|
|
4
5
|
|
|
5
6
|
class PagesRouter {
|
|
6
7
|
config;
|
|
@@ -24,6 +25,8 @@ class PagesRouter {
|
|
|
24
25
|
async init() {
|
|
25
26
|
const { config, rootDir, log, mode, fileWatcher } = this;
|
|
26
27
|
const isDev = mode === 'development';
|
|
28
|
+
const viteDev = isDev && config.pages?.vite?.enabled === true;
|
|
29
|
+
const outDir = resolvePagesOutDir(config, rootDir, mode);
|
|
27
30
|
|
|
28
31
|
if (isDev) {
|
|
29
32
|
// Dev mode: `createLazyPagesContext()` builds only the shared bits
|
|
@@ -35,9 +38,11 @@ class PagesRouter {
|
|
|
35
38
|
this.lazyContext = await createLazyPagesContext({
|
|
36
39
|
rootDir,
|
|
37
40
|
pagesDir: config.pages.dir,
|
|
41
|
+
outDir,
|
|
38
42
|
fonts: config.pages?.fonts,
|
|
39
43
|
minify: false,
|
|
40
44
|
devMode: true,
|
|
45
|
+
viteDev,
|
|
41
46
|
});
|
|
42
47
|
} catch (err) {
|
|
43
48
|
log.error('Pages lazy context init failed', { error: String(err) });
|
|
@@ -46,9 +51,11 @@ class PagesRouter {
|
|
|
46
51
|
|
|
47
52
|
this.handler = createPagesHandler({
|
|
48
53
|
rootDir,
|
|
54
|
+
outDir,
|
|
49
55
|
session: config.session,
|
|
50
56
|
appContext: this.appContext,
|
|
51
57
|
mode,
|
|
58
|
+
viteDev,
|
|
52
59
|
...(this.lazyContext ? { lazyContext: this.lazyContext } : {}),
|
|
53
60
|
});
|
|
54
61
|
|
package/server/pages/ssr.js
CHANGED
|
@@ -70,12 +70,14 @@ function wrapWithProviders(createElement, element, pathname, params) {
|
|
|
70
70
|
}
|
|
71
71
|
function buildCssLinkTag(manifest) {
|
|
72
72
|
if (!manifest.cssBundle) return '';
|
|
73
|
-
return `<link rel="stylesheet" href="/static/${manifest.cssBundle}" />`;
|
|
73
|
+
return `<link rel="stylesheet" data-arcway-css="global" href="/static/${manifest.cssBundle}" />`;
|
|
74
74
|
}
|
|
75
75
|
function buildRouteCssLinkTags(route) {
|
|
76
76
|
const chunks = route?.sharedCssChunks ?? [];
|
|
77
77
|
if (chunks.length === 0) return '';
|
|
78
|
-
return chunks
|
|
78
|
+
return chunks
|
|
79
|
+
.map((c) => `<link rel="stylesheet" data-arcway-css="route" href="/static/${c}" />`)
|
|
80
|
+
.join('\n');
|
|
79
81
|
}
|
|
80
82
|
function buildFontPreloadTags(manifest) {
|
|
81
83
|
return manifest.fontPreloadHtml ?? '';
|
|
@@ -97,6 +99,19 @@ function buildScriptTags(route) {
|
|
|
97
99
|
tags.push(`<script type="module" src="/static/${route.clientBundle}"></script>`);
|
|
98
100
|
return tags.join('\n');
|
|
99
101
|
}
|
|
102
|
+
function buildViteScriptTags(route) {
|
|
103
|
+
return [
|
|
104
|
+
`<script type="module">
|
|
105
|
+
import RefreshRuntime from "/@react-refresh";
|
|
106
|
+
RefreshRuntime.injectIntoGlobalHook(window);
|
|
107
|
+
window.$RefreshReg$ = () => {};
|
|
108
|
+
window.$RefreshSig$ = () => (type) => type;
|
|
109
|
+
window.__vite_plugin_react_preamble_installed__ = true;
|
|
110
|
+
</script>`,
|
|
111
|
+
`<script type="module" src="/@vite/client"></script>`,
|
|
112
|
+
`<script type="module" src="${route.clientBundle}"></script>`,
|
|
113
|
+
].join('\n');
|
|
114
|
+
}
|
|
100
115
|
function buildHtmlShell({ headHtml, fontPreloadTags, cssLinkTag, bodysuffix }) {
|
|
101
116
|
return {
|
|
102
117
|
head: `<!DOCTYPE html>
|
|
@@ -128,6 +143,7 @@ async function renderPage(
|
|
|
128
143
|
cacheVersion,
|
|
129
144
|
react,
|
|
130
145
|
devMode,
|
|
146
|
+
viteDev = false,
|
|
131
147
|
) {
|
|
132
148
|
const { createElement, renderToPipeableStream } = react;
|
|
133
149
|
const bundlePath = path.join(outDir, route.serverBundle);
|
|
@@ -155,14 +171,14 @@ async function renderPage(
|
|
|
155
171
|
layoutComponents.push(Layout);
|
|
156
172
|
}
|
|
157
173
|
}
|
|
158
|
-
const scriptTags = buildScriptTags(route);
|
|
159
|
-
const manifestCssTag = buildCssLinkTag(manifest);
|
|
160
|
-
const routeCssTags = buildRouteCssLinkTags(route);
|
|
174
|
+
const scriptTags = viteDev ? buildViteScriptTags(route) : buildScriptTags(route);
|
|
175
|
+
const manifestCssTag = viteDev ? '' : buildCssLinkTag(manifest);
|
|
176
|
+
const routeCssTags = viteDev ? '' : buildRouteCssLinkTags(route);
|
|
161
177
|
const cssLinkTag = [manifestCssTag, routeCssTags].filter(Boolean).join('\n');
|
|
162
178
|
const fontPreloadTags = buildFontPreloadTags(manifest);
|
|
163
179
|
const envScriptTag = buildEnvScriptTag(collectPublicEnv());
|
|
164
|
-
const hmrTag = devMode ? buildHmrScript() : '';
|
|
165
|
-
const liveReloadTag = devMode && !hmrTag ? buildLiveReloadScript() : '';
|
|
180
|
+
const hmrTag = devMode && !viteDev ? buildHmrScript() : '';
|
|
181
|
+
const liveReloadTag = devMode && !viteDev && !hmrTag ? buildLiveReloadScript() : '';
|
|
166
182
|
const propsJson = JSON.stringify(params).replace(/</g, '\\u003c');
|
|
167
183
|
const headData = { meta: [], links: [] };
|
|
168
184
|
setSSRHeadData(headData);
|