@reckona/mreact-router 0.0.120 → 0.0.122
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/README.md +1 -1
- package/dist/build.d.ts +1 -0
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +117 -46
- package/dist/build.js.map +1 -1
- package/dist/client.d.ts +1 -0
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +15 -1
- package/dist/client.js.map +1 -1
- package/dist/module-runner.js +14 -6
- package/dist/module-runner.js.map +1 -1
- package/dist/render.d.ts +1 -0
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +23 -2
- package/dist/render.js.map +1 -1
- package/dist/route-styles.d.ts +1 -0
- package/dist/route-styles.d.ts.map +1 -1
- package/dist/route-styles.js +23 -2
- package/dist/route-styles.js.map +1 -1
- package/dist/serve.d.ts.map +1 -1
- package/dist/serve.js +5 -0
- package/dist/serve.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +15 -1
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +552 -371
- package/src/client.ts +34 -11
- package/src/module-runner.ts +26 -11
- package/src/render.ts +26 -2
- package/src/route-styles.ts +30 -2
- package/src/serve.ts +11 -0
- package/src/vite.ts +24 -1
package/src/client.ts
CHANGED
|
@@ -31,10 +31,7 @@ import {
|
|
|
31
31
|
type RouterBundleAssetOutput,
|
|
32
32
|
type RouterBundleChunkOutput,
|
|
33
33
|
} from "./bundle-pipeline.js";
|
|
34
|
-
import {
|
|
35
|
-
resolveClientConsolePureFunctions,
|
|
36
|
-
type AppRouterProductionOptions,
|
|
37
|
-
} from "./config.js";
|
|
34
|
+
import { resolveClientConsolePureFunctions, type AppRouterProductionOptions } from "./config.js";
|
|
38
35
|
import type { AppRoute } from "./routes.js";
|
|
39
36
|
import { existingRouteShellCandidates } from "./route-shells.js";
|
|
40
37
|
import {
|
|
@@ -116,6 +113,7 @@ interface ClientRouteSourceTransform {
|
|
|
116
113
|
export interface ClientRouteInferenceResult {
|
|
117
114
|
client: boolean;
|
|
118
115
|
clientBoundaryImports: string[];
|
|
116
|
+
clientBoundaryFallbackImports: string[];
|
|
119
117
|
diagnostics: ClientRouteInferenceDiagnostic[];
|
|
120
118
|
}
|
|
121
119
|
|
|
@@ -265,6 +263,7 @@ export async function inferClientRouteModule(options: {
|
|
|
265
263
|
client:
|
|
266
264
|
mergedRouteInference.client || shellInferences.some((inference) => inference.client),
|
|
267
265
|
clientBoundaryImports: mergedRouteInference.clientBoundaryImports,
|
|
266
|
+
clientBoundaryFallbackImports: mergedRouteInference.clientBoundaryFallbackImports,
|
|
268
267
|
diagnostics: [
|
|
269
268
|
...mergedRouteInference.diagnostics,
|
|
270
269
|
...shellInferences.flatMap((inference) => inference.diagnostics),
|
|
@@ -325,6 +324,7 @@ function clientRouteInferenceFromBoundaryGraph(
|
|
|
325
324
|
return {
|
|
326
325
|
client: clientRoute || clientBoundaryImports.length > 0,
|
|
327
326
|
clientBoundaryImports,
|
|
327
|
+
clientBoundaryFallbackImports: [],
|
|
328
328
|
diagnostics: [],
|
|
329
329
|
};
|
|
330
330
|
}
|
|
@@ -338,6 +338,9 @@ function mergeClientRouteInference(
|
|
|
338
338
|
clientBoundaryImports: Array.from(
|
|
339
339
|
new Set([...left.clientBoundaryImports, ...right.clientBoundaryImports]),
|
|
340
340
|
),
|
|
341
|
+
clientBoundaryFallbackImports: Array.from(
|
|
342
|
+
new Set([...left.clientBoundaryFallbackImports, ...right.clientBoundaryFallbackImports]),
|
|
343
|
+
),
|
|
341
344
|
diagnostics: [...left.diagnostics, ...right.diagnostics],
|
|
342
345
|
};
|
|
343
346
|
}
|
|
@@ -466,6 +469,7 @@ export async function collectClientRouteReferences(options: {
|
|
|
466
469
|
const output = transformCompilerModuleContext({
|
|
467
470
|
code: source.code,
|
|
468
471
|
clientBoundaryImports: source.inference.clientBoundaryImports,
|
|
472
|
+
clientBoundaryFallbackImports: source.inference.clientBoundaryFallbackImports,
|
|
469
473
|
dev: false,
|
|
470
474
|
filename: source.filename,
|
|
471
475
|
moduleContext: source.moduleContext,
|
|
@@ -511,6 +515,7 @@ export async function collectClientRouteReferences(options: {
|
|
|
511
515
|
routeInference.client ||
|
|
512
516
|
sources.some((source) => source.filename !== options.filename && source.inference.client),
|
|
513
517
|
clientBoundaryImports: routeInference.clientBoundaryImports,
|
|
518
|
+
clientBoundaryFallbackImports: routeInference.clientBoundaryFallbackImports,
|
|
514
519
|
clientReferenceImports,
|
|
515
520
|
clientReferenceManifest,
|
|
516
521
|
diagnostics: sources
|
|
@@ -685,6 +690,7 @@ async function inferClientRouteModuleSource(options: {
|
|
|
685
690
|
|
|
686
691
|
try {
|
|
687
692
|
const clientBoundaryImports: string[] = [];
|
|
693
|
+
const clientBoundaryFallbackImports: string[] = [];
|
|
688
694
|
const clientBoundaryExportNames = new Set<string>();
|
|
689
695
|
const nestedClientExportNames = new Set<string>();
|
|
690
696
|
const clientReferenceSourceFiles: string[] = [];
|
|
@@ -779,7 +785,10 @@ async function inferClientRouteModuleSource(options: {
|
|
|
779
785
|
renderedComponentRoots,
|
|
780
786
|
);
|
|
781
787
|
if (
|
|
782
|
-
matchesInferredExportNames(
|
|
788
|
+
matchesInferredExportNames(
|
|
789
|
+
importedNavigationExportNames,
|
|
790
|
+
imported.navigationLinkExportNames,
|
|
791
|
+
)
|
|
783
792
|
) {
|
|
784
793
|
usesNavigationLink = true;
|
|
785
794
|
for (const exportName of renderedLocalExportNames(reference, exportInfo)) {
|
|
@@ -841,6 +850,9 @@ async function inferClientRouteModuleSource(options: {
|
|
|
841
850
|
}
|
|
842
851
|
|
|
843
852
|
clientBoundaryImports.push(reference.source);
|
|
853
|
+
if (!imported.clientBoundaryModule && isClientBoundaryFallbackEligibleSource(source)) {
|
|
854
|
+
clientBoundaryFallbackImports.push(reference.source);
|
|
855
|
+
}
|
|
844
856
|
continue;
|
|
845
857
|
}
|
|
846
858
|
|
|
@@ -933,6 +945,7 @@ async function inferClientRouteModuleSource(options: {
|
|
|
933
945
|
clientProxy ||
|
|
934
946
|
nestedClient,
|
|
935
947
|
clientBoundaryImports,
|
|
948
|
+
clientBoundaryFallbackImports,
|
|
936
949
|
clientBoundaryExportNames: Array.from(clientBoundaryExportNames),
|
|
937
950
|
clientBoundaryModule: clientProxy || implicitModuleClient,
|
|
938
951
|
nestedClientExportNames: Array.from(nestedClientExportNames),
|
|
@@ -956,6 +969,7 @@ function emptyClientRouteModuleInferenceResult(
|
|
|
956
969
|
boundaryGraphFallbackRequired: false,
|
|
957
970
|
client: false,
|
|
958
971
|
clientBoundaryImports: [],
|
|
972
|
+
clientBoundaryFallbackImports: [],
|
|
959
973
|
clientBoundaryExportNames: [],
|
|
960
974
|
clientBoundaryModule: false,
|
|
961
975
|
nestedClientExportNames: [],
|
|
@@ -992,7 +1006,12 @@ async function clientRouteModuleAnalysisForSource(options: {
|
|
|
992
1006
|
})),
|
|
993
1007
|
),
|
|
994
1008
|
);
|
|
995
|
-
setLatestModuleCacheEntry(
|
|
1009
|
+
setLatestModuleCacheEntry(
|
|
1010
|
+
options.cache.moduleAnalysisByFile,
|
|
1011
|
+
options.filename,
|
|
1012
|
+
cacheKey,
|
|
1013
|
+
analysis,
|
|
1014
|
+
);
|
|
996
1015
|
return analysis;
|
|
997
1016
|
}
|
|
998
1017
|
|
|
@@ -1090,6 +1109,10 @@ function isStyleModuleSpecifier(source: string): boolean {
|
|
|
1090
1109
|
|
|
1091
1110
|
const styleModuleExtensions = new Set([".css", ".less", ".sass", ".scss", ".styl", ".stylus"]);
|
|
1092
1111
|
|
|
1112
|
+
function isClientBoundaryFallbackEligibleSource(source: string): boolean {
|
|
1113
|
+
return !/\bon[A-Z][A-Za-z0-9_$]*\s*=/u.test(source) && !/\bglobalThis\b/u.test(source);
|
|
1114
|
+
}
|
|
1115
|
+
|
|
1093
1116
|
function renderedImportedExportNames(
|
|
1094
1117
|
reference: ClientRouteStaticImportReference,
|
|
1095
1118
|
componentRoots: ReadonlySet<string>,
|
|
@@ -1227,14 +1250,14 @@ function startsUppercase(value: string): boolean {
|
|
|
1227
1250
|
export function formatClientRouteInferenceDiagnostic(
|
|
1228
1251
|
diagnostic: ClientRouteInferenceDiagnostic,
|
|
1229
1252
|
): string {
|
|
1230
|
-
const route =
|
|
1253
|
+
const route =
|
|
1254
|
+
diagnostic.routePath === undefined ? "" : ` on route ${JSON.stringify(diagnostic.routePath)}`;
|
|
1231
1255
|
return `${diagnostic.code}: ${diagnostic.message}${route}`;
|
|
1232
1256
|
}
|
|
1233
1257
|
|
|
1234
|
-
function withClientRouteDiagnosticPath<
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
): T {
|
|
1258
|
+
function withClientRouteDiagnosticPath<
|
|
1259
|
+
T extends ClientRouteInferenceDiagnostic | ClientRouteInferenceResult,
|
|
1260
|
+
>(value: T, routePath: string | undefined): T {
|
|
1238
1261
|
if (routePath === undefined) {
|
|
1239
1262
|
return value;
|
|
1240
1263
|
}
|
package/src/module-runner.ts
CHANGED
|
@@ -411,7 +411,7 @@ async function transformServerSourceFile(
|
|
|
411
411
|
console.warn(formatClientRouteInferenceDiagnostic(diagnostic));
|
|
412
412
|
}
|
|
413
413
|
|
|
414
|
-
const cacheKey = `${options.serverOutput}\0${options.dev ? "dev" : "prod"}\0${options.filename}\0${transformedSourceHash}\0${clientInference.clientBoundaryImports.join("\0")}\0${vitePluginsCacheKey(options.vitePlugins)}`;
|
|
414
|
+
const cacheKey = `${options.serverOutput}\0${options.dev ? "dev" : "prod"}\0${options.filename}\0${transformedSourceHash}\0${clientInference.clientBoundaryImports.join("\0")}\0${clientInference.clientBoundaryFallbackImports.join("\0")}\0${vitePluginsCacheKey(options.vitePlugins)}`;
|
|
415
415
|
const cached = readRouterRuntimeCacheEntry(
|
|
416
416
|
serverSourceTransformCache,
|
|
417
417
|
cacheKey,
|
|
@@ -425,6 +425,7 @@ async function transformServerSourceFile(
|
|
|
425
425
|
const output = transformCompilerModuleContext({
|
|
426
426
|
code: source,
|
|
427
427
|
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
428
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
428
429
|
dev: options.dev,
|
|
429
430
|
filename: options.filename,
|
|
430
431
|
moduleContext,
|
|
@@ -488,12 +489,20 @@ function withNodeRequireShimForEsmBundle(options: {
|
|
|
488
489
|
return code;
|
|
489
490
|
}
|
|
490
491
|
|
|
491
|
-
return `${rewritten.needsNativeImport ? 'const __mreactNativeImport = Function("specifier", "return import(specifier)");\n' : ""}${
|
|
492
|
+
return `${rewritten.needsNativeImport ? 'const __mreactNativeImport = Function("specifier", "return import(specifier)");\n' : ""}${
|
|
493
|
+
needsFilenameGlobalShim
|
|
494
|
+
? `const __filename = ${JSON.stringify(options.filename)};
|
|
492
495
|
const __dirname = ${JSON.stringify(dirname(options.filename))};
|
|
493
|
-
`
|
|
496
|
+
`
|
|
497
|
+
: ""
|
|
498
|
+
}${
|
|
499
|
+
rewritten.needsRequire || needsRequireShim
|
|
500
|
+
? `import { createRequire as __mreactCreateRequire } from "node:module";
|
|
494
501
|
const __mreactRequire = __mreactCreateRequire(${JSON.stringify(requireBaseUrl)});
|
|
495
502
|
const require = __mreactRequire;
|
|
496
|
-
`
|
|
503
|
+
`
|
|
504
|
+
: ""
|
|
505
|
+
}${code}`;
|
|
497
506
|
}
|
|
498
507
|
|
|
499
508
|
function needsCommonJsFilenameGlobalShim(code: string): boolean {
|
|
@@ -535,7 +544,11 @@ function rewriteNodeModulesExternalImports(code: string): {
|
|
|
535
544
|
}
|
|
536
545
|
|
|
537
546
|
needsNativeImport = true;
|
|
538
|
-
const rewritten = esmImportClauseToNativeImportStatements(
|
|
547
|
+
const rewritten = esmImportClauseToNativeImportStatements(
|
|
548
|
+
clause.trim(),
|
|
549
|
+
specifier,
|
|
550
|
+
importIndex++,
|
|
551
|
+
);
|
|
539
552
|
for (const [name, replacement] of rewritten.bindings) {
|
|
540
553
|
nativeImportBindings.set(name, replacement);
|
|
541
554
|
}
|
|
@@ -601,7 +614,9 @@ function isNodeModulesPath(file: string): boolean {
|
|
|
601
614
|
function isNodeImportableModuleFile(file: string): boolean {
|
|
602
615
|
const extension = extname(file);
|
|
603
616
|
|
|
604
|
-
return
|
|
617
|
+
return (
|
|
618
|
+
extension === ".cjs" || extension === ".mjs" || extension === ".js" || extension === ".node"
|
|
619
|
+
);
|
|
605
620
|
}
|
|
606
621
|
|
|
607
622
|
function isNodeCommonJsModuleFile(file: string): boolean {
|
|
@@ -676,7 +691,10 @@ function commonJsImportClauseToRequireStatements(
|
|
|
676
691
|
const defaultName = clause.slice(0, commaIndex).trim();
|
|
677
692
|
const namedOrNamespace = clause.slice(commaIndex + 1).trim();
|
|
678
693
|
const temporaryName = `__mreactExternalCommonJs${importIndex}`;
|
|
679
|
-
const statements = [
|
|
694
|
+
const statements = [
|
|
695
|
+
`const ${temporaryName} = ${requireExpression};`,
|
|
696
|
+
`const ${defaultName} = ${temporaryName};`,
|
|
697
|
+
];
|
|
680
698
|
|
|
681
699
|
if (namedOrNamespace.startsWith("* as ")) {
|
|
682
700
|
statements.push(`const ${namedOrNamespace.slice(5).trim()} = ${temporaryName};`);
|
|
@@ -737,10 +755,7 @@ function collectNamedEsmImportBindings(
|
|
|
737
755
|
}
|
|
738
756
|
}
|
|
739
757
|
|
|
740
|
-
function namedCommonJsImportsToRequireStatements(
|
|
741
|
-
clause: string,
|
|
742
|
-
sourceExpression: string,
|
|
743
|
-
): string {
|
|
758
|
+
function namedCommonJsImportsToRequireStatements(clause: string, sourceExpression: string): string {
|
|
744
759
|
const objectBindings: string[] = [];
|
|
745
760
|
const statements: string[] = [];
|
|
746
761
|
|
package/src/render.ts
CHANGED
|
@@ -159,6 +159,7 @@ export interface RenderAppRequestOptions {
|
|
|
159
159
|
appDir: string;
|
|
160
160
|
assetBaseUrl?: string | undefined;
|
|
161
161
|
clientScripts?: ReadonlyMap<string, string>;
|
|
162
|
+
clientStylesByFile?: ReadonlyMap<string, readonly string[]>;
|
|
162
163
|
clientStyles?: ReadonlyMap<string, readonly string[]>;
|
|
163
164
|
env?: unknown;
|
|
164
165
|
importPolicy?: AppRouterImportPolicy | undefined;
|
|
@@ -303,6 +304,7 @@ async function preloadBuiltPageRouteModules(options: {
|
|
|
303
304
|
const stringOutput = transformServerModule({
|
|
304
305
|
code: routeCode,
|
|
305
306
|
clientBoundaryImports: options.analysis.clientInference.clientBoundaryImports,
|
|
307
|
+
clientBoundaryFallbackImports: options.analysis.clientInference.clientBoundaryFallbackImports,
|
|
306
308
|
filename: options.file,
|
|
307
309
|
serverModules: options.serverModules,
|
|
308
310
|
serverOutput: "string",
|
|
@@ -321,6 +323,7 @@ async function preloadBuiltPageRouteModules(options: {
|
|
|
321
323
|
const streamOutput = transformServerModule({
|
|
322
324
|
code: routeCode,
|
|
323
325
|
clientBoundaryImports: options.analysis.clientInference.clientBoundaryImports,
|
|
326
|
+
clientBoundaryFallbackImports: options.analysis.clientInference.clientBoundaryFallbackImports,
|
|
324
327
|
filename: options.file,
|
|
325
328
|
serverModules: options.serverModules,
|
|
326
329
|
serverOutput: "stream",
|
|
@@ -800,6 +803,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
800
803
|
const response = await renderSpecialRoute({
|
|
801
804
|
appDir: options.appDir,
|
|
802
805
|
assetBaseUrl: options.assetBaseUrl,
|
|
806
|
+
currentStyleSheets: options.clientStylesByFile?.get(notFoundFile),
|
|
803
807
|
error: undefined,
|
|
804
808
|
request: options.request,
|
|
805
809
|
routePath: url.pathname,
|
|
@@ -1036,6 +1040,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
1036
1040
|
const stringOutput = transformServerModule({
|
|
1037
1041
|
code: routeCode,
|
|
1038
1042
|
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
1043
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
1039
1044
|
filename: matched.route.file,
|
|
1040
1045
|
serverModules: options.serverModules,
|
|
1041
1046
|
serverOutput: "string",
|
|
@@ -1179,6 +1184,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
1179
1184
|
const output = transformServerModule({
|
|
1180
1185
|
code: routeCode,
|
|
1181
1186
|
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
1187
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
1182
1188
|
filename: matched.route.file,
|
|
1183
1189
|
serverModules: options.serverModules,
|
|
1184
1190
|
serverOutput: "stream",
|
|
@@ -1285,6 +1291,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
1285
1291
|
const output = transformServerModule({
|
|
1286
1292
|
code: routeCode,
|
|
1287
1293
|
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
1294
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
1288
1295
|
filename: matched.route.file,
|
|
1289
1296
|
serverModules: options.serverModules,
|
|
1290
1297
|
serverOutput: "string",
|
|
@@ -1449,6 +1456,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
1449
1456
|
const response = await renderSpecialRoute({
|
|
1450
1457
|
appDir: options.appDir,
|
|
1451
1458
|
assetBaseUrl: options.assetBaseUrl,
|
|
1459
|
+
currentStyleSheets: options.clientStylesByFile?.get(notFoundFile),
|
|
1452
1460
|
error: undefined,
|
|
1453
1461
|
request: options.request,
|
|
1454
1462
|
routePath: matched.route.path,
|
|
@@ -1475,6 +1483,7 @@ async function renderAppRequestInternal(options: RenderAppRequestOptions): Promi
|
|
|
1475
1483
|
const response = await renderSpecialRoute({
|
|
1476
1484
|
appDir: options.appDir,
|
|
1477
1485
|
assetBaseUrl: options.assetBaseUrl,
|
|
1486
|
+
currentStyleSheets: options.clientStylesByFile?.get(errorFile),
|
|
1478
1487
|
error,
|
|
1479
1488
|
request: options.request,
|
|
1480
1489
|
routePath: matched.route.path,
|
|
@@ -1737,6 +1746,7 @@ function boundaryFilenameCandidates(filename: string): string[] {
|
|
|
1737
1746
|
async function renderSpecialRoute(options: {
|
|
1738
1747
|
appDir: string;
|
|
1739
1748
|
assetBaseUrl?: string | undefined;
|
|
1749
|
+
currentStyleSheets?: readonly string[] | undefined;
|
|
1740
1750
|
error: unknown;
|
|
1741
1751
|
navigation?:
|
|
1742
1752
|
| {
|
|
@@ -1807,6 +1817,7 @@ async function renderSpecialRoute(options: {
|
|
|
1807
1817
|
return new Response(
|
|
1808
1818
|
`<!DOCTYPE html>${clientNavigationHeadTags({
|
|
1809
1819
|
assetBaseUrl: options.assetBaseUrl,
|
|
1820
|
+
currentStyleSheets: options.currentStyleSheets,
|
|
1810
1821
|
currentScript:
|
|
1811
1822
|
options.navigation?.clientRoute === true ? options.navigation.script : undefined,
|
|
1812
1823
|
routeScripts: options.routeScripts,
|
|
@@ -2396,6 +2407,7 @@ async function loadBundledMiddlewareModule(options: {
|
|
|
2396
2407
|
function transformServerModule(options: {
|
|
2397
2408
|
code: string;
|
|
2398
2409
|
clientBoundaryImports?: readonly string[];
|
|
2410
|
+
clientBoundaryFallbackImports?: readonly string[];
|
|
2399
2411
|
filename: string;
|
|
2400
2412
|
serverModules?: ReadonlyMap<string, BuiltServerModuleArtifact> | undefined;
|
|
2401
2413
|
serverOutput: ServerOutputMode;
|
|
@@ -2428,7 +2440,9 @@ function transformServerModule(options: {
|
|
|
2428
2440
|
}
|
|
2429
2441
|
|
|
2430
2442
|
const awaitHydrationKey = options.serverAwaitHydration === true ? "1" : "0";
|
|
2431
|
-
const
|
|
2443
|
+
const boundaryKey = options.clientBoundaryImports?.join("\0") ?? "";
|
|
2444
|
+
const fallbackKey = options.clientBoundaryFallbackImports?.join("\0") ?? "";
|
|
2445
|
+
const key = `${options.filename}\0${options.serverOutput}\0${sourceHash}\0${awaitHydrationKey}\0${boundaryKey}\0${fallbackKey}`;
|
|
2432
2446
|
const cached = readRouterRuntimeCacheEntry(
|
|
2433
2447
|
serverTransformCache,
|
|
2434
2448
|
key,
|
|
@@ -2444,6 +2458,9 @@ function transformServerModule(options: {
|
|
|
2444
2458
|
...(options.clientBoundaryImports === undefined
|
|
2445
2459
|
? {}
|
|
2446
2460
|
: { clientBoundaryImports: options.clientBoundaryImports }),
|
|
2461
|
+
...(options.clientBoundaryFallbackImports === undefined
|
|
2462
|
+
? {}
|
|
2463
|
+
: { clientBoundaryFallbackImports: options.clientBoundaryFallbackImports }),
|
|
2447
2464
|
dev: true,
|
|
2448
2465
|
filename: options.filename,
|
|
2449
2466
|
serverEscape: nativeEscapeTransform,
|
|
@@ -2523,6 +2540,7 @@ function routeSourceAnalysisFromArtifact(
|
|
|
2523
2540
|
clientInference: {
|
|
2524
2541
|
client: artifact.clientRoute,
|
|
2525
2542
|
clientBoundaryImports: [...artifact.clientBoundaryImports],
|
|
2543
|
+
clientBoundaryFallbackImports: [...(artifact.clientBoundaryFallbackImports ?? [])],
|
|
2526
2544
|
diagnostics: [],
|
|
2527
2545
|
},
|
|
2528
2546
|
hasLoader: artifact.hasLoader,
|
|
@@ -3477,7 +3495,12 @@ async function renderShellPrefixSuffix(
|
|
|
3477
3495
|
const artifact = serverModules?.get(shell.file)?.[serverOutput];
|
|
3478
3496
|
const clientInference =
|
|
3479
3497
|
artifact !== undefined && artifact.sourceHash === memoizedHashText(code)
|
|
3480
|
-
? {
|
|
3498
|
+
? {
|
|
3499
|
+
client: false,
|
|
3500
|
+
clientBoundaryImports: [],
|
|
3501
|
+
clientBoundaryFallbackImports: [],
|
|
3502
|
+
diagnostics: [],
|
|
3503
|
+
}
|
|
3481
3504
|
: await inferClientRouteModule({
|
|
3482
3505
|
cache: clientRouteInferenceCache,
|
|
3483
3506
|
code: stripRouteClientOnlyExports(code, shell.file),
|
|
@@ -3490,6 +3513,7 @@ async function renderShellPrefixSuffix(
|
|
|
3490
3513
|
const output = transformServerModule({
|
|
3491
3514
|
code,
|
|
3492
3515
|
clientBoundaryImports: clientInference.clientBoundaryImports,
|
|
3516
|
+
clientBoundaryFallbackImports: clientInference.clientBoundaryFallbackImports,
|
|
3493
3517
|
filename: shell.file,
|
|
3494
3518
|
serverModules,
|
|
3495
3519
|
serverOutput,
|
package/src/route-styles.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { access, readFile } from "node:fs/promises";
|
|
2
|
-
import { dirname, isAbsolute, relative, resolve, sep } from "node:path";
|
|
1
|
+
import { access, readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, isAbsolute, relative, resolve, sep } from "node:path";
|
|
3
3
|
import { collectStaticImportReferences } from "@reckona/mreact-compiler";
|
|
4
4
|
import { existingRouteShellCandidates } from "./route-shells.js";
|
|
5
5
|
|
|
@@ -65,6 +65,34 @@ export async function collectRouteCssHrefs(options: {
|
|
|
65
65
|
});
|
|
66
66
|
}
|
|
67
67
|
|
|
68
|
+
export async function collectSpecialBoundaryFiles(directory: string): Promise<string[]> {
|
|
69
|
+
const entries = await readdir(directory, { withFileTypes: true });
|
|
70
|
+
const files: string[] = [];
|
|
71
|
+
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const path = join(directory, entry.name);
|
|
74
|
+
|
|
75
|
+
if (entry.isDirectory()) {
|
|
76
|
+
if (entry.name === ".vite" || entry.name === "node_modules") {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
files.push(...(await collectSpecialBoundaryFiles(path)));
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (entry.isFile() && isSpecialBoundaryFilename(entry.name)) {
|
|
85
|
+
files.push(path);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return files.sort();
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function isSpecialBoundaryFilename(filename: string): boolean {
|
|
93
|
+
return /^(?:error|not-found)(?:\.mreact)?\.tsx$/u.test(filename);
|
|
94
|
+
}
|
|
95
|
+
|
|
68
96
|
function resolveCssImport(options: {
|
|
69
97
|
importer: string;
|
|
70
98
|
projectRoot: string;
|
package/src/serve.ts
CHANGED
|
@@ -41,6 +41,7 @@ interface BuiltRuntime {
|
|
|
41
41
|
allowedSourceDirs: readonly string[];
|
|
42
42
|
assetBaseUrl?: string | undefined;
|
|
43
43
|
clientScripts: ReadonlyMap<string, string>;
|
|
44
|
+
clientStylesByFile: ReadonlyMap<string, readonly string[]>;
|
|
44
45
|
clientStyles: ReadonlyMap<string, readonly string[]>;
|
|
45
46
|
hasMiddleware: boolean;
|
|
46
47
|
navigationScripts: ReadonlyMap<string, string>;
|
|
@@ -763,6 +764,7 @@ async function materializeBuiltRuntime(options: {
|
|
|
763
764
|
const serverManifest = JSON.parse(options.serverManifestText) as BuiltServerManifest;
|
|
764
765
|
const clientManifest = JSON.parse(options.clientManifestText) as {
|
|
765
766
|
routes: ClientRouteManifestEntry[];
|
|
767
|
+
styles?: Array<{ css?: readonly string[]; file: string }>;
|
|
766
768
|
};
|
|
767
769
|
const appDir = await materializeBuiltServerApp(options.runtimeDir, serverManifest);
|
|
768
770
|
const projectRoot = appDir;
|
|
@@ -818,6 +820,13 @@ async function materializeBuiltRuntime(options: {
|
|
|
818
820
|
route.css !== undefined && route.css.length > 0 ? [[route.path, route.css]] : [],
|
|
819
821
|
),
|
|
820
822
|
);
|
|
823
|
+
const clientStylesByFile = new Map(
|
|
824
|
+
(clientManifest.styles ?? []).flatMap((style) =>
|
|
825
|
+
style.css !== undefined && style.css.length > 0
|
|
826
|
+
? [[join(routesDir, style.file), style.css] as const]
|
|
827
|
+
: [],
|
|
828
|
+
),
|
|
829
|
+
);
|
|
821
830
|
const navigationScripts = new Map(
|
|
822
831
|
clientManifest.routes.flatMap((route) =>
|
|
823
832
|
route.navigation === true && route.navigationScript !== undefined
|
|
@@ -846,6 +855,7 @@ async function materializeBuiltRuntime(options: {
|
|
|
846
855
|
? {}
|
|
847
856
|
: { assetBaseUrl: serverManifest.assetBaseUrl }),
|
|
848
857
|
clientScripts,
|
|
858
|
+
clientStylesByFile,
|
|
849
859
|
clientStyles,
|
|
850
860
|
hasMiddleware,
|
|
851
861
|
navigationScripts,
|
|
@@ -1156,6 +1166,7 @@ function builtRenderAppRequestOptions(
|
|
|
1156
1166
|
appDir: options.runtime.appDir,
|
|
1157
1167
|
assetBaseUrl: options.runtime.assetBaseUrl,
|
|
1158
1168
|
clientScripts: options.runtime.clientScripts,
|
|
1169
|
+
clientStylesByFile: options.runtime.clientStylesByFile,
|
|
1159
1170
|
clientStyles: options.runtime.clientStyles,
|
|
1160
1171
|
importPolicy: {
|
|
1161
1172
|
...options.importPolicy,
|
package/src/vite.ts
CHANGED
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import { nodeRequestToWebRequest, sendResponse } from "./http.js";
|
|
39
39
|
import { renderAppRequest } from "./render.js";
|
|
40
40
|
import { stripRouteClientOnlyExports } from "./route-source.js";
|
|
41
|
-
import { collectRouteCssHrefs } from "./route-styles.js";
|
|
41
|
+
import { collectRouteCssHrefs, collectSpecialBoundaryFiles } from "./route-styles.js";
|
|
42
42
|
import { scanAppRoutes } from "./routes.js";
|
|
43
43
|
import { resolveRequestHost, type RequestHostPolicy } from "./serve.js";
|
|
44
44
|
import { hasJsxSyntax } from "./source-jsx.js";
|
|
@@ -504,6 +504,7 @@ async function handleAppRouterViteRequest(
|
|
|
504
504
|
projectRoot: project.projectRoot,
|
|
505
505
|
},
|
|
506
506
|
clientStyles: await devRouteStyles(project),
|
|
507
|
+
clientStylesByFile: await devSpecialRouteStyles(project),
|
|
507
508
|
navigationScripts: await devNavigationScripts(
|
|
508
509
|
project.routesDir,
|
|
509
510
|
options.clientRouteInferenceCache,
|
|
@@ -790,6 +791,28 @@ async function devRouteStyles(
|
|
|
790
791
|
return new Map<string, readonly string[]>(routeStyles);
|
|
791
792
|
}
|
|
792
793
|
|
|
794
|
+
async function devSpecialRouteStyles(
|
|
795
|
+
project: ResolvedAppRouterProject,
|
|
796
|
+
): Promise<ReadonlyMap<string, readonly string[]>> {
|
|
797
|
+
const entries = await Promise.all(
|
|
798
|
+
(await collectSpecialBoundaryFiles(project.routesDir)).map(async (file) => {
|
|
799
|
+
const hrefs = await collectRouteCssHrefs({
|
|
800
|
+
appDir: project.routesDir,
|
|
801
|
+
hrefPrefix: devCssPrefix,
|
|
802
|
+
pageFile: file,
|
|
803
|
+
projectRoot: project.projectRoot,
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
return hrefs.length === 0 ? undefined : ([file, hrefs as readonly string[]] as const);
|
|
807
|
+
}),
|
|
808
|
+
);
|
|
809
|
+
const routeStyles = entries.filter(
|
|
810
|
+
(entry): entry is readonly [string, readonly string[]] => entry !== undefined,
|
|
811
|
+
);
|
|
812
|
+
|
|
813
|
+
return new Map<string, readonly string[]>(routeStyles);
|
|
814
|
+
}
|
|
815
|
+
|
|
793
816
|
async function devNavigationScripts(
|
|
794
817
|
appDir: string,
|
|
795
818
|
inferenceCache?: ClientRouteInferenceCache | undefined,
|