@reckona/mreact-router 0.0.182 → 0.0.184
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 +3 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +271 -8
- package/dist/build.js.map +1 -1
- package/dist/bundle-pipeline.d.ts.map +1 -1
- package/dist/bundle-pipeline.js +9 -0
- package/dist/bundle-pipeline.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +193 -2
- package/dist/client.js.map +1 -1
- package/dist/config.d.ts +2 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +6 -0
- package/dist/config.js.map +1 -1
- package/dist/module-runner.d.ts.map +1 -1
- package/dist/module-runner.js +132 -1
- package/dist/module-runner.js.map +1 -1
- package/dist/render.d.ts +2 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +24 -13
- package/dist/render.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +34 -1
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +406 -10
- package/src/bundle-pipeline.ts +9 -0
- package/src/client.ts +195 -2
- package/src/config.ts +8 -0
- package/src/module-runner.ts +185 -1
- package/src/render.ts +37 -15
- package/src/vite.ts +39 -1
package/dist/build.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { ServerOutputMode } from "@reckona/mreact-compiler";
|
|
1
2
|
import type { AppRoute, CompiledRouteMatcherArtifact } from "./routes.js";
|
|
2
3
|
import { type AppRouterProjectOptions, type AppRouterBuildTarget } from "./config.js";
|
|
3
4
|
import type { ModuleMetadata } from "@reckona/mreact-compiler";
|
|
@@ -169,6 +170,8 @@ export interface BuiltPrerenderedRoute {
|
|
|
169
170
|
*/
|
|
170
171
|
export declare function buildApp(options: BuildAppOptions): Promise<BuildAppResult>;
|
|
171
172
|
export declare function __mapWithBuildConcurrencyForTests<T, R>(items: readonly T[], map: (item: T, index: number) => Promise<R>, concurrency?: number): Promise<R[]>;
|
|
173
|
+
export declare function __mapServerOutputsWithBuildConcurrencyForTests<R>(serverOutputs: readonly ServerOutputMode[], map: (serverOutput: ServerOutputMode, index: number) => Promise<R>): Promise<R[]>;
|
|
174
|
+
export declare function __resolveBuildConcurrencyForTests(value: number | undefined, cores: number): number;
|
|
172
175
|
export declare function __writeServerModuleArtifactFilesForTests(serverDir: string, serverModules: Record<string, BuiltServerModuleArtifact>): Promise<{
|
|
173
176
|
files: Record<string, string>;
|
|
174
177
|
renderFiles: Record<string, string>;
|
package/dist/build.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../src/build.ts"],"names":[],"mappings":"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"}
|
package/dist/build.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
3
|
import { copyFile, cp, lstat, mkdir, readdir, readFile, rm, stat, writeFile, } from "node:fs/promises";
|
|
3
4
|
import { builtinModules } from "node:module";
|
|
4
5
|
import { availableParallelism } from "node:os";
|
|
@@ -30,7 +31,8 @@ const nativeEscapeTransform = {
|
|
|
30
31
|
batchImportName: "escapeHtmlBatch",
|
|
31
32
|
batchImportSource: "@reckona/mreact-router/native-escape",
|
|
32
33
|
};
|
|
33
|
-
const
|
|
34
|
+
const maxDefaultBuildConcurrency = 8;
|
|
35
|
+
const buildConcurrencyStorage = new AsyncLocalStorage();
|
|
34
36
|
const serverArtifactFilesystemConcurrency = 2;
|
|
35
37
|
async function validateBuildMiddlewareControls(appDir, routes) {
|
|
36
38
|
const availableIds = await collectBuildMiddlewareIds(appDir);
|
|
@@ -68,10 +70,14 @@ async function collectBuildMiddlewareIds(appDir) {
|
|
|
68
70
|
* Use this from custom build scripts when the CLI is too coarse-grained; the returned manifest paths describe the files written under `outDir`. The build reads route files, loaders, middleware, metadata, server actions, and client references, so callers should pass the same project root and source allow-list they expect to deploy.
|
|
69
71
|
*/
|
|
70
72
|
export async function buildApp(options) {
|
|
73
|
+
const project = resolveAppRouterProjectOptions(options);
|
|
74
|
+
const buildConcurrency = resolveBuildConcurrency(options.buildConcurrency ?? project.buildConcurrency);
|
|
75
|
+
return await buildConcurrencyStorage.run(buildConcurrency, async () => await buildAppWithResolvedProject(options, project));
|
|
76
|
+
}
|
|
77
|
+
async function buildAppWithResolvedProject(options, project) {
|
|
71
78
|
const timingSink = options.onBuildPhaseTiming;
|
|
72
79
|
const progressSink = options.onBuildProgress;
|
|
73
80
|
const shouldTrackBuildPhases = timingSink !== undefined || progressSink !== undefined;
|
|
74
|
-
const project = resolveAppRouterProjectOptions(options);
|
|
75
81
|
const buildTargets = resolveBuildTargets(options.targets ?? project.buildTargets);
|
|
76
82
|
const shouldBuildCloudflare = buildTargets.includes("cloudflare");
|
|
77
83
|
const shouldBuildAwsLambda = buildTargets.includes("aws-lambda");
|
|
@@ -90,6 +96,14 @@ export async function buildApp(options) {
|
|
|
90
96
|
const serverDir = join(options.outDir, "server");
|
|
91
97
|
const clientDir = join(options.outDir, "client");
|
|
92
98
|
const cloudflareDir = join(options.outDir, "cloudflare");
|
|
99
|
+
const incrementalBuildFingerprint = await createIncrementalBuildCacheFingerprint({
|
|
100
|
+
buildTargets,
|
|
101
|
+
files,
|
|
102
|
+
project,
|
|
103
|
+
routes,
|
|
104
|
+
viteDefine,
|
|
105
|
+
vitePlugins,
|
|
106
|
+
});
|
|
93
107
|
const sourceAnalysis = shouldTrackBuildPhases === false
|
|
94
108
|
? await analyzeBuildRouteSources({
|
|
95
109
|
clientRouteInferenceCache: serverClientRouteInferenceCache,
|
|
@@ -131,6 +145,14 @@ export async function buildApp(options) {
|
|
|
131
145
|
vitePlugins,
|
|
132
146
|
}));
|
|
133
147
|
}
|
|
148
|
+
if (incrementalBuildFingerprint !== undefined &&
|
|
149
|
+
(await isIncrementalBuildCacheHit({
|
|
150
|
+
buildTargets,
|
|
151
|
+
fingerprint: incrementalBuildFingerprint,
|
|
152
|
+
outDir: options.outDir,
|
|
153
|
+
}))) {
|
|
154
|
+
return { routes };
|
|
155
|
+
}
|
|
134
156
|
if (shouldTrackBuildPhases === false) {
|
|
135
157
|
await rm(options.outDir, { force: true, recursive: true });
|
|
136
158
|
await Promise.all([
|
|
@@ -255,14 +277,14 @@ export async function buildApp(options) {
|
|
|
255
277
|
})),
|
|
256
278
|
]);
|
|
257
279
|
const clientRoutes = clientBundle.routes;
|
|
258
|
-
const navigationRuntimeScript = clientRoutes.some((route) => route.navigation === true
|
|
280
|
+
const navigationRuntimeScript = clientRoutes.some((route) => route.navigation === true)
|
|
259
281
|
? shouldTrackBuildPhases === false
|
|
260
282
|
? await writeNavigationRuntimeBundle(clientDir, project.clientConsolePureFunctions)
|
|
261
283
|
: await timeBuildPhase(timingSink, progressSink, "navigationRuntime", () => writeNavigationRuntimeBundle(clientDir, project.clientConsolePureFunctions))
|
|
262
284
|
: undefined;
|
|
263
285
|
const clientManifestRoutes = navigationRuntimeScript === undefined
|
|
264
286
|
? clientRoutes
|
|
265
|
-
: clientRoutes.map((route) => route.navigation === true
|
|
287
|
+
: clientRoutes.map((route) => route.navigation === true
|
|
266
288
|
? { ...route, navigationScript: navigationRuntimeScript }
|
|
267
289
|
: route);
|
|
268
290
|
const clientManifestAssets = Array.from(new Set([
|
|
@@ -407,8 +429,207 @@ export async function buildApp(options) {
|
|
|
407
429
|
]);
|
|
408
430
|
});
|
|
409
431
|
}
|
|
432
|
+
if (incrementalBuildFingerprint !== undefined) {
|
|
433
|
+
await writeIncrementalBuildCacheManifest(options.outDir, incrementalBuildFingerprint);
|
|
434
|
+
}
|
|
410
435
|
return { routes };
|
|
411
436
|
}
|
|
437
|
+
const incrementalBuildCacheFilename = "build-cache.json";
|
|
438
|
+
const incrementalBuildCacheVersion = 1;
|
|
439
|
+
async function createIncrementalBuildCacheFingerprint(options) {
|
|
440
|
+
if (options.vitePlugins !== undefined && options.vitePlugins.length > 0) {
|
|
441
|
+
return undefined;
|
|
442
|
+
}
|
|
443
|
+
let define;
|
|
444
|
+
try {
|
|
445
|
+
const serializedDefine = JSON.stringify(options.viteDefine ?? null);
|
|
446
|
+
if (serializedDefine === undefined) {
|
|
447
|
+
return undefined;
|
|
448
|
+
}
|
|
449
|
+
define = serializedDefine;
|
|
450
|
+
}
|
|
451
|
+
catch {
|
|
452
|
+
return undefined;
|
|
453
|
+
}
|
|
454
|
+
const [publicAssets, appConventionAssets] = await Promise.all([
|
|
455
|
+
collectBuildInputFileHashes(options.project.publicDir, options.project.projectRoot),
|
|
456
|
+
collectAppConventionAssetInputHashes(options.project.routesDir, options.project.projectRoot),
|
|
457
|
+
]);
|
|
458
|
+
const sourceFiles = Object.keys(options.files)
|
|
459
|
+
.sort()
|
|
460
|
+
.map((file) => [normalizeBuildInputPath(file), hashText(options.files[file] ?? "")]);
|
|
461
|
+
const routes = options.routes
|
|
462
|
+
.map((route) => ({
|
|
463
|
+
file: normalizeBuildInputPath(relative(options.project.projectRoot, route.file)),
|
|
464
|
+
kind: route.kind,
|
|
465
|
+
path: route.path,
|
|
466
|
+
}))
|
|
467
|
+
.sort((left, right) => left.file.localeCompare(right.file));
|
|
468
|
+
const payload = {
|
|
469
|
+
appDir: normalizeBuildInputPath(relative(options.project.projectRoot, options.project.routesDir)),
|
|
470
|
+
assetBaseUrl: options.project.assetBaseUrl ?? null,
|
|
471
|
+
clientConsolePureFunctions: options.project.clientConsolePureFunctions ?? [],
|
|
472
|
+
clientSourceMaps: options.project.clientSourceMaps,
|
|
473
|
+
define,
|
|
474
|
+
nodeEnv: process.env.NODE_ENV ?? null,
|
|
475
|
+
publicAssetBaseUrl: options.project.publicAssetBaseUrl ?? null,
|
|
476
|
+
publicDir: normalizeBuildInputPath(relative(options.project.projectRoot, options.project.publicDir)),
|
|
477
|
+
routes,
|
|
478
|
+
sourceDirs: options.project.allowedSourceDirs
|
|
479
|
+
.map((directory) => normalizeBuildInputPath(relative(options.project.projectRoot, directory)))
|
|
480
|
+
.sort(),
|
|
481
|
+
sourceFiles,
|
|
482
|
+
targets: [...options.buildTargets].sort(),
|
|
483
|
+
version: incrementalBuildCacheVersion,
|
|
484
|
+
publicAssets,
|
|
485
|
+
appConventionAssets,
|
|
486
|
+
};
|
|
487
|
+
return createHash("sha256").update(JSON.stringify(payload)).digest("hex");
|
|
488
|
+
}
|
|
489
|
+
async function isIncrementalBuildCacheHit(options) {
|
|
490
|
+
const cache = await readJsonBuildOutput(join(options.outDir, incrementalBuildCacheFilename));
|
|
491
|
+
if (cache === undefined) {
|
|
492
|
+
return false;
|
|
493
|
+
}
|
|
494
|
+
return (cache.version === incrementalBuildCacheVersion &&
|
|
495
|
+
cache.fingerprint === options.fingerprint &&
|
|
496
|
+
(await hasRequiredIncrementalBuildOutputs(options.outDir, options.buildTargets)));
|
|
497
|
+
}
|
|
498
|
+
async function hasRequiredIncrementalBuildOutputs(outDir, buildTargets) {
|
|
499
|
+
const serverManifestFile = join(outDir, "server", "manifest.json");
|
|
500
|
+
const clientManifestFile = join(outDir, "client", "manifest.json");
|
|
501
|
+
const requiredFiles = [
|
|
502
|
+
serverManifestFile,
|
|
503
|
+
join(outDir, "server", "import-policy.json"),
|
|
504
|
+
clientManifestFile,
|
|
505
|
+
join(outDir, "client", ".vite", "manifest.json"),
|
|
506
|
+
join(outDir, "routes.d.ts"),
|
|
507
|
+
join(outDir, "public-assets.d.ts"),
|
|
508
|
+
...(buildTargets.includes("aws-lambda")
|
|
509
|
+
? [join(outDir, "aws-lambda", "mreact-handler.mjs")]
|
|
510
|
+
: []),
|
|
511
|
+
...(buildTargets.includes("cloudflare") ? [join(outDir, "cloudflare", "worker.mjs")] : []),
|
|
512
|
+
];
|
|
513
|
+
const [serverManifest, clientManifest] = await Promise.all([
|
|
514
|
+
readJsonBuildOutput(serverManifestFile),
|
|
515
|
+
readJsonBuildOutput(clientManifestFile),
|
|
516
|
+
]);
|
|
517
|
+
if (serverManifest === undefined || clientManifest === undefined) {
|
|
518
|
+
return false;
|
|
519
|
+
}
|
|
520
|
+
for (const file of [
|
|
521
|
+
...Object.values(serverManifest.serverModuleFiles ?? {}),
|
|
522
|
+
...Object.values(serverManifest.serverModuleRenderFiles ?? {}),
|
|
523
|
+
...Object.values(serverManifest.serverModuleRequestFiles ?? {}),
|
|
524
|
+
]) {
|
|
525
|
+
requiredFiles.push(join(outDir, "server", file));
|
|
526
|
+
}
|
|
527
|
+
for (const file of collectClientManifestOutputFiles(clientManifest)) {
|
|
528
|
+
requiredFiles.push(join(outDir, "client", file));
|
|
529
|
+
}
|
|
530
|
+
for (const file of requiredFiles) {
|
|
531
|
+
if (!(await isExistingFile(file))) {
|
|
532
|
+
return false;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
return true;
|
|
536
|
+
}
|
|
537
|
+
function collectClientManifestOutputFiles(manifest) {
|
|
538
|
+
const files = new Set();
|
|
539
|
+
for (const asset of manifest.assets ?? []) {
|
|
540
|
+
files.add(asset);
|
|
541
|
+
}
|
|
542
|
+
for (const asset of manifest.publicAssets ?? []) {
|
|
543
|
+
files.add(asset);
|
|
544
|
+
files.add(`public/${asset}`);
|
|
545
|
+
}
|
|
546
|
+
for (const route of manifest.routes) {
|
|
547
|
+
for (const file of [
|
|
548
|
+
route.script,
|
|
549
|
+
route.sourceMap,
|
|
550
|
+
route.navigationScript,
|
|
551
|
+
...(route.css ?? []),
|
|
552
|
+
...(route.imports ?? []),
|
|
553
|
+
]) {
|
|
554
|
+
if (file !== undefined) {
|
|
555
|
+
files.add(file);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return [...files];
|
|
560
|
+
}
|
|
561
|
+
async function readJsonBuildOutput(file) {
|
|
562
|
+
try {
|
|
563
|
+
return JSON.parse(await readFile(file, "utf8"));
|
|
564
|
+
}
|
|
565
|
+
catch (error) {
|
|
566
|
+
if ((isNodeError(error) && error.code === "ENOENT") ||
|
|
567
|
+
error instanceof SyntaxError) {
|
|
568
|
+
return undefined;
|
|
569
|
+
}
|
|
570
|
+
throw error;
|
|
571
|
+
}
|
|
572
|
+
}
|
|
573
|
+
async function writeIncrementalBuildCacheManifest(outDir, fingerprint) {
|
|
574
|
+
const cache = {
|
|
575
|
+
fingerprint,
|
|
576
|
+
version: incrementalBuildCacheVersion,
|
|
577
|
+
};
|
|
578
|
+
await writeFile(join(outDir, incrementalBuildCacheFilename), `${JSON.stringify(cache, null, 2)}\n`);
|
|
579
|
+
}
|
|
580
|
+
async function collectBuildInputFileHashes(directory, projectRoot) {
|
|
581
|
+
if (!(await isPublicAssetDirectory(directory))) {
|
|
582
|
+
return [];
|
|
583
|
+
}
|
|
584
|
+
const files = await collectFiles(directory);
|
|
585
|
+
const hashes = await mapWithBuildConcurrency(files, async (file) => [
|
|
586
|
+
normalizeBuildInputPath(relative(projectRoot, file)),
|
|
587
|
+
hashBuffer(await readFile(file)),
|
|
588
|
+
]);
|
|
589
|
+
return hashes.sort(([left], [right]) => left.localeCompare(right));
|
|
590
|
+
}
|
|
591
|
+
async function collectAppConventionAssetInputHashes(appDir, projectRoot) {
|
|
592
|
+
const entries = await readdir(appDir, { withFileTypes: true });
|
|
593
|
+
const assetFiles = entries
|
|
594
|
+
.filter((entry) => entry.isFile())
|
|
595
|
+
.map((entry) => join(appDir, entry.name))
|
|
596
|
+
.filter((file) => isAppFileConventionAsset(file, appDir));
|
|
597
|
+
const hashes = await mapWithBuildConcurrency(assetFiles, async (file) => [
|
|
598
|
+
normalizeBuildInputPath(relative(projectRoot, file)),
|
|
599
|
+
hashBuffer(await readFile(file)),
|
|
600
|
+
]);
|
|
601
|
+
return hashes.sort(([left], [right]) => left.localeCompare(right));
|
|
602
|
+
}
|
|
603
|
+
async function isExistingFile(file) {
|
|
604
|
+
try {
|
|
605
|
+
return (await stat(file)).isFile();
|
|
606
|
+
}
|
|
607
|
+
catch (error) {
|
|
608
|
+
if (isNodeError(error) && error.code === "ENOENT") {
|
|
609
|
+
return false;
|
|
610
|
+
}
|
|
611
|
+
throw error;
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
function normalizeBuildInputPath(path) {
|
|
615
|
+
return path.split(sep).join("/");
|
|
616
|
+
}
|
|
617
|
+
function hashBuffer(buffer) {
|
|
618
|
+
return createHash("sha256").update(buffer).digest("hex").slice(0, 16);
|
|
619
|
+
}
|
|
620
|
+
function defaultBuildConcurrency() {
|
|
621
|
+
return Math.max(1, Math.min(maxDefaultBuildConcurrency, availableParallelism()));
|
|
622
|
+
}
|
|
623
|
+
function currentBuildConcurrency() {
|
|
624
|
+
return buildConcurrencyStorage.getStore() ?? defaultBuildConcurrency();
|
|
625
|
+
}
|
|
626
|
+
function resolveBuildConcurrency(value, cores = availableParallelism()) {
|
|
627
|
+
const resolved = value ?? Math.max(1, Math.min(maxDefaultBuildConcurrency, cores));
|
|
628
|
+
if (!Number.isSafeInteger(resolved) || resolved < 1) {
|
|
629
|
+
throw new Error("mreactRouter buildConcurrency must be a positive integer.");
|
|
630
|
+
}
|
|
631
|
+
return resolved;
|
|
632
|
+
}
|
|
412
633
|
function typedRoutesDeclaration(routes) {
|
|
413
634
|
const routePaths = Array.from(new Set(routes
|
|
414
635
|
.filter((route) => route.kind === "page" || route.kind === "server")
|
|
@@ -422,20 +643,48 @@ function typedRoutesDeclaration(routes) {
|
|
|
422
643
|
return left.localeCompare(right);
|
|
423
644
|
});
|
|
424
645
|
const routeUnion = routePaths.map((routePath) => JSON.stringify(routePath)).join(" | ");
|
|
646
|
+
const routeParamEntries = routePaths.map((routePath) => ` readonly ${JSON.stringify(routePath)}: ${routeParamsType(routePath)};`);
|
|
425
647
|
return [
|
|
426
|
-
`import type { AppRouteLinkHref as MreactAppRouteLinkHref } from "@reckona/mreact-router";`,
|
|
648
|
+
`import type { AppRouteLinkHref as MreactAppRouteLinkHref, RouteParamsFor as MreactRouteParamsFor } from "@reckona/mreact-router";`,
|
|
427
649
|
``,
|
|
428
650
|
`export type AppRoutePath = ${routeUnion === "" ? "never" : routeUnion};`,
|
|
429
651
|
`export type AppRouteHref = MreactAppRouteLinkHref<AppRoutePath>;`,
|
|
652
|
+
`export type AppRouteParams<Path extends AppRoutePath> = MreactRouteParamsFor<Path>;`,
|
|
653
|
+
`export interface AppRouteParamMap {`,
|
|
654
|
+
...(routeParamEntries.length === 0 ? [` readonly [path: string]: never;`] : routeParamEntries),
|
|
655
|
+
`}`,
|
|
430
656
|
``,
|
|
431
657
|
`declare module "@reckona/mreact-router/link" {`,
|
|
432
658
|
` interface AppRouteDeclarations {`,
|
|
433
659
|
` readonly path: AppRoutePath;`,
|
|
660
|
+
` readonly params: AppRouteParamMap;`,
|
|
434
661
|
` }`,
|
|
435
662
|
`}`,
|
|
436
663
|
``,
|
|
437
664
|
].join("\n");
|
|
438
665
|
}
|
|
666
|
+
function routeParamsType(routePath) {
|
|
667
|
+
const params = routePath
|
|
668
|
+
.split("/")
|
|
669
|
+
.flatMap((segment) => {
|
|
670
|
+
if (segment.startsWith(":...")) {
|
|
671
|
+
return [{ catchAll: true, name: segment.slice(4) }];
|
|
672
|
+
}
|
|
673
|
+
if (segment.startsWith(":")) {
|
|
674
|
+
return [{ catchAll: false, name: segment.slice(1) }];
|
|
675
|
+
}
|
|
676
|
+
return [];
|
|
677
|
+
});
|
|
678
|
+
if (params.length === 0) {
|
|
679
|
+
return "Record<never, never>";
|
|
680
|
+
}
|
|
681
|
+
return `{ ${params
|
|
682
|
+
.map((param) => `readonly ${propertyKeyForType(param.name)}: ${param.catchAll ? "readonly string[]" : "string"}`)
|
|
683
|
+
.join("; ")} }`;
|
|
684
|
+
}
|
|
685
|
+
function propertyKeyForType(value) {
|
|
686
|
+
return /^[A-Za-z_$][\w$]*$/.test(value) ? value : JSON.stringify(value);
|
|
687
|
+
}
|
|
439
688
|
function typedPublicAssetsDeclaration(publicAssets) {
|
|
440
689
|
const assetUnion = publicAssets.map((assetPath) => JSON.stringify(assetPath)).join(" | ");
|
|
441
690
|
return [
|
|
@@ -467,7 +716,7 @@ async function timeBuildPhase(timingSink, progressSink, phase, run) {
|
|
|
467
716
|
function roundBuildPhaseMs(value) {
|
|
468
717
|
return Math.round(value * 100) / 100;
|
|
469
718
|
}
|
|
470
|
-
async function mapWithBuildConcurrency(items, map, concurrency =
|
|
719
|
+
async function mapWithBuildConcurrency(items, map, concurrency = currentBuildConcurrency()) {
|
|
471
720
|
if (items.length === 0) {
|
|
472
721
|
return [];
|
|
473
722
|
}
|
|
@@ -490,6 +739,15 @@ async function mapWithBuildConcurrency(items, map, concurrency = defaultBuildCon
|
|
|
490
739
|
export async function __mapWithBuildConcurrencyForTests(items, map, concurrency) {
|
|
491
740
|
return await mapWithBuildConcurrency(items, map, concurrency);
|
|
492
741
|
}
|
|
742
|
+
async function mapServerOutputsWithBuildConcurrency(serverOutputs, map) {
|
|
743
|
+
return await mapWithBuildConcurrency(serverOutputs, map, 1);
|
|
744
|
+
}
|
|
745
|
+
export async function __mapServerOutputsWithBuildConcurrencyForTests(serverOutputs, map) {
|
|
746
|
+
return await mapServerOutputsWithBuildConcurrency(serverOutputs, map);
|
|
747
|
+
}
|
|
748
|
+
export function __resolveBuildConcurrencyForTests(value, cores) {
|
|
749
|
+
return resolveBuildConcurrency(value, cores);
|
|
750
|
+
}
|
|
493
751
|
async function analyzeBuildRouteSources(options) {
|
|
494
752
|
const byFile = new Map();
|
|
495
753
|
for (const [file, source] of Object.entries(options.files)) {
|
|
@@ -1564,7 +1822,7 @@ async function buildServerModuleArtifacts(options) {
|
|
|
1564
1822
|
analysis: routeAnalysis,
|
|
1565
1823
|
});
|
|
1566
1824
|
}
|
|
1567
|
-
const serverOutputArtifacts = await
|
|
1825
|
+
const serverOutputArtifacts = await mapServerOutputsWithBuildConcurrency(serverOutputs, async (serverOutput) => {
|
|
1568
1826
|
const output = await transformServerRouteSource({
|
|
1569
1827
|
cache: options.serverTransformCache,
|
|
1570
1828
|
code,
|
|
@@ -3829,6 +4087,10 @@ function cloudflareWorkspaceRuntimePlugin() {
|
|
|
3829
4087
|
"@reckona/mreact-compat/scheduler",
|
|
3830
4088
|
packageFile("react-compat", "@reckona/mreact-compat", "scheduler"),
|
|
3831
4089
|
],
|
|
4090
|
+
[
|
|
4091
|
+
"@reckona/mreact-compat/server",
|
|
4092
|
+
packageFile("react-compat", "@reckona/mreact-compat", "server"),
|
|
4093
|
+
],
|
|
3832
4094
|
["@reckona/mreact-query", packageFile("query", "@reckona/mreact-query", "index")],
|
|
3833
4095
|
[
|
|
3834
4096
|
"@reckona/mreact-reactive-core",
|
|
@@ -4094,6 +4356,7 @@ async function writeClientRouteBundles(options) {
|
|
|
4094
4356
|
scriptBasename: routeOutput.chunk.fileName.split("/").pop() ?? "route.js",
|
|
4095
4357
|
sourceMaps: options.sourceMaps,
|
|
4096
4358
|
});
|
|
4359
|
+
const navigation = entry.navigation === true || entry.build.clientNavigation !== false;
|
|
4097
4360
|
return {
|
|
4098
4361
|
bytes: Buffer.byteLength(code),
|
|
4099
4362
|
path: entry.route.path,
|
|
@@ -4105,7 +4368,7 @@ async function writeClientRouteBundles(options) {
|
|
|
4105
4368
|
: { clientReferenceManifest: entry.build.clientReferenceManifest }),
|
|
4106
4369
|
...(entry.css.length === 0 ? {} : { css: entry.css }),
|
|
4107
4370
|
...(routeOutput.chunk.imports.length === 0 ? {} : { imports: routeOutput.chunk.imports }),
|
|
4108
|
-
...(
|
|
4371
|
+
...(navigation ? { navigation } : {}),
|
|
4109
4372
|
routeId,
|
|
4110
4373
|
script: routeOutput.chunk.fileName,
|
|
4111
4374
|
...(options.sourceMaps === "linked" ? { sourceMap } : {}),
|