@reckona/mreact-router 0.0.92 → 0.0.94
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 +9 -3
- package/dist/build.d.ts.map +1 -1
- package/dist/build.js +7 -2
- package/dist/build.js.map +1 -1
- package/dist/client-route-inference.d.ts +1 -1
- package/dist/client-route-inference.d.ts.map +1 -1
- package/dist/client-route-inference.js +1 -1
- package/dist/client-route-inference.js.map +1 -1
- package/dist/client.d.ts +12 -6
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +124 -15
- package/dist/client.js.map +1 -1
- package/dist/vite.d.ts.map +1 -1
- package/dist/vite.js +30 -6
- package/dist/vite.js.map +1 -1
- package/package.json +11 -11
- package/src/build.ts +7 -2
- package/src/client-route-inference.ts +2 -1
- package/src/client.ts +177 -15
- package/src/vite.ts +45 -11
package/src/client.ts
CHANGED
|
@@ -17,6 +17,8 @@ import type { ClientReferenceMetadata } from "@reckona/mreact-shared/compiler-co
|
|
|
17
17
|
import {
|
|
18
18
|
collectClientRouteModuleAnalysisFromContext,
|
|
19
19
|
createCompilerModuleContext,
|
|
20
|
+
readTopLevelBooleanExport,
|
|
21
|
+
readTopLevelBooleanExportFromContext,
|
|
20
22
|
stripTypeScriptWithOxc,
|
|
21
23
|
transformCompilerModuleContext,
|
|
22
24
|
type CompilerModuleContext,
|
|
@@ -122,8 +124,10 @@ interface ClientRouteModuleInferenceResult extends ClientRouteInferenceResult {
|
|
|
122
124
|
clientBoundaryModule: boolean;
|
|
123
125
|
nestedClientExportNames: string[];
|
|
124
126
|
clientReferenceSourceFiles: string[];
|
|
127
|
+
navigationLinkExportNames: string[];
|
|
125
128
|
serverOnly: boolean;
|
|
126
129
|
serverOnlyClientRuntime: boolean;
|
|
130
|
+
usesNavigationLink: boolean;
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
export interface ClientReferenceImport {
|
|
@@ -135,6 +139,7 @@ export interface ClientReferenceImport {
|
|
|
135
139
|
export interface ClientRouteReferenceResult extends ClientRouteInferenceResult {
|
|
136
140
|
clientReferenceImports: ClientReferenceImport[];
|
|
137
141
|
clientReferenceManifest: ClientReferenceMetadata[];
|
|
142
|
+
usesNavigationLink: boolean;
|
|
138
143
|
}
|
|
139
144
|
|
|
140
145
|
export interface ClientRouteInferenceDiagnostic {
|
|
@@ -498,9 +503,49 @@ export async function collectClientRouteReferences(options: {
|
|
|
498
503
|
clientReferenceImports,
|
|
499
504
|
clientReferenceManifest,
|
|
500
505
|
diagnostics: sources.flatMap((source) => source.inference.diagnostics),
|
|
506
|
+
usesNavigationLink:
|
|
507
|
+
routeInference.usesNavigationLink ||
|
|
508
|
+
sources.some(
|
|
509
|
+
(source) => source.filename !== options.filename && source.inference.usesNavigationLink,
|
|
510
|
+
),
|
|
501
511
|
};
|
|
502
512
|
}
|
|
503
513
|
|
|
514
|
+
export async function resolveNavigationRuntime(options: {
|
|
515
|
+
appDir?: string | undefined;
|
|
516
|
+
cache?: ClientRouteInferenceCache | undefined;
|
|
517
|
+
code: string;
|
|
518
|
+
filename: string;
|
|
519
|
+
references?: ClientRouteReferenceResult | undefined;
|
|
520
|
+
vitePlugins?: readonly PluginOption[] | undefined;
|
|
521
|
+
}): Promise<boolean> {
|
|
522
|
+
const cache = options.cache ?? createClientRouteInferenceCache();
|
|
523
|
+
// Read the override from the cached module context so the dev server does not
|
|
524
|
+
// re-parse every page route on each request.
|
|
525
|
+
const moduleContext = await compilerModuleContextForSource({
|
|
526
|
+
cache,
|
|
527
|
+
code: options.code,
|
|
528
|
+
filename: options.filename,
|
|
529
|
+
});
|
|
530
|
+
const override = readTopLevelBooleanExportFromContext(moduleContext, "navigationRuntime");
|
|
531
|
+
|
|
532
|
+
if (override !== undefined) {
|
|
533
|
+
return override;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const references =
|
|
537
|
+
options.references ??
|
|
538
|
+
(await collectClientRouteReferences({
|
|
539
|
+
appDir: options.appDir,
|
|
540
|
+
cache,
|
|
541
|
+
code: options.code,
|
|
542
|
+
filename: options.filename,
|
|
543
|
+
vitePlugins: options.vitePlugins,
|
|
544
|
+
}));
|
|
545
|
+
|
|
546
|
+
return references.usesNavigationLink;
|
|
547
|
+
}
|
|
548
|
+
|
|
504
549
|
async function inferClientRouteShellModules(options: {
|
|
505
550
|
appDir: string;
|
|
506
551
|
cache: ClientRouteInferenceCache;
|
|
@@ -595,11 +640,14 @@ async function inferClientRouteModuleSource(options: {
|
|
|
595
640
|
sourceTransform?: ClientRouteSourceTransform | undefined;
|
|
596
641
|
}): Promise<ClientRouteModuleInferenceResult> {
|
|
597
642
|
const analysis = await clientRouteModuleAnalysisForSource(options);
|
|
643
|
+
const usesNavigationLinkLocal = detectLinkComponentUsage(analysis);
|
|
598
644
|
|
|
599
645
|
if (isServerOnlyClientRouteSource(analysis)) {
|
|
600
646
|
return emptyClientRouteModuleInferenceResult({
|
|
647
|
+
navigationLinkExportNames: detectLinkComponentExportNames(analysis),
|
|
601
648
|
serverOnly: true,
|
|
602
649
|
serverOnlyClientRuntime: analysis.clientRuntime,
|
|
650
|
+
usesNavigationLink: usesNavigationLinkLocal,
|
|
603
651
|
});
|
|
604
652
|
}
|
|
605
653
|
|
|
@@ -625,6 +673,8 @@ async function inferClientRouteModuleSource(options: {
|
|
|
625
673
|
let boundaryGraphFallbackRequired = false;
|
|
626
674
|
let clientProxy = false;
|
|
627
675
|
let nestedClient = false;
|
|
676
|
+
let usesNavigationLink = usesNavigationLinkLocal;
|
|
677
|
+
const navigationLinkExportNames = new Set<string>(detectLinkComponentExportNames(analysis));
|
|
628
678
|
const exportInfo = analysis.topLevelExportRenderInfo;
|
|
629
679
|
const implicitModuleClient = exportInfo.length === 0 && analysis.clientRuntime;
|
|
630
680
|
for (const info of exportInfo) {
|
|
@@ -643,7 +693,14 @@ async function inferClientRouteModuleSource(options: {
|
|
|
643
693
|
}
|
|
644
694
|
const jsxComponentRoots = new Set(analysis.jsxComponentRoots);
|
|
645
695
|
const componentCallRoots = new Set(analysis.componentCallRoots);
|
|
646
|
-
|
|
696
|
+
// A bare-identifier default export (`export default Page`) is the route's
|
|
697
|
+
// rendered component even though the module body has no JSX for it; treat it
|
|
698
|
+
// as a rendered root so a re-exported imported component (and any Link it
|
|
699
|
+
// renders) is followed.
|
|
700
|
+
const renderedComponentRoots =
|
|
701
|
+
analysis.defaultExportIdentifier === undefined
|
|
702
|
+
? unionSets(jsxComponentRoots, componentCallRoots)
|
|
703
|
+
: new Set([...jsxComponentRoots, ...componentCallRoots, analysis.defaultExportIdentifier]);
|
|
647
704
|
const identifierReferences = new Set(analysis.identifierReferences);
|
|
648
705
|
|
|
649
706
|
for (const reference of analysis.staticImports) {
|
|
@@ -692,6 +749,25 @@ async function inferClientRouteModuleSource(options: {
|
|
|
692
749
|
sourceTransform: options.sourceTransform,
|
|
693
750
|
});
|
|
694
751
|
diagnostics.push(...imported.diagnostics);
|
|
752
|
+
// Propagate the navigation runtime only for imports that are actually
|
|
753
|
+
// rendered here, and only for the specific exports whose subtree renders a
|
|
754
|
+
// `Link`. A merely-referenced import (e.g. `const C = Nav`) recurses for
|
|
755
|
+
// client-boundary detection but must not pull in a transitive `Link`, and
|
|
756
|
+
// a barrel re-exporting Link-free siblings must not over-trigger.
|
|
757
|
+
if (rendered) {
|
|
758
|
+
const importedNavigationExportNames = renderedImportedExportNames(
|
|
759
|
+
reference,
|
|
760
|
+
renderedComponentRoots,
|
|
761
|
+
);
|
|
762
|
+
if (
|
|
763
|
+
matchesInferredExportNames(importedNavigationExportNames, imported.navigationLinkExportNames)
|
|
764
|
+
) {
|
|
765
|
+
usesNavigationLink = true;
|
|
766
|
+
for (const exportName of renderedLocalExportNames(reference, exportInfo)) {
|
|
767
|
+
navigationLinkExportNames.add(exportName);
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
695
771
|
|
|
696
772
|
if (!imported.client) {
|
|
697
773
|
if (rendered && imported.boundaryGraphFallbackCandidate) {
|
|
@@ -786,6 +862,23 @@ async function inferClientRouteModuleSource(options: {
|
|
|
786
862
|
sourceTransform: options.sourceTransform,
|
|
787
863
|
});
|
|
788
864
|
diagnostics.push(...exported.diagnostics);
|
|
865
|
+
// A re-export renders nothing itself, so it does not set the module's
|
|
866
|
+
// own `usesNavigationLink`; it only forwards per-export `Link` usage so
|
|
867
|
+
// an importer that renders this name can decide precisely. Map the
|
|
868
|
+
// source module's export names onto the names this barrel re-exposes:
|
|
869
|
+
// `export * from` keeps the original names, `export { A as Nav } from`
|
|
870
|
+
// renames them.
|
|
871
|
+
if (reference.exportAll) {
|
|
872
|
+
for (const exportName of exported.navigationLinkExportNames) {
|
|
873
|
+
navigationLinkExportNames.add(exportName);
|
|
874
|
+
}
|
|
875
|
+
} else {
|
|
876
|
+
for (const specifier of reference.specifiers) {
|
|
877
|
+
if (exported.navigationLinkExportNames.includes(specifier.localName)) {
|
|
878
|
+
navigationLinkExportNames.add(specifier.exportedName);
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
789
882
|
|
|
790
883
|
if (exported.clientBoundaryModule) {
|
|
791
884
|
clientProxy = true;
|
|
@@ -820,8 +913,10 @@ async function inferClientRouteModuleSource(options: {
|
|
|
820
913
|
nestedClientExportNames: Array.from(nestedClientExportNames),
|
|
821
914
|
clientReferenceSourceFiles: Array.from(new Set(clientReferenceSourceFiles)),
|
|
822
915
|
diagnostics,
|
|
916
|
+
navigationLinkExportNames: Array.from(navigationLinkExportNames),
|
|
823
917
|
serverOnly: false,
|
|
824
918
|
serverOnlyClientRuntime: false,
|
|
919
|
+
usesNavigationLink,
|
|
825
920
|
};
|
|
826
921
|
} finally {
|
|
827
922
|
options.seen.delete(options.filename);
|
|
@@ -841,8 +936,10 @@ function emptyClientRouteModuleInferenceResult(
|
|
|
841
936
|
nestedClientExportNames: [],
|
|
842
937
|
clientReferenceSourceFiles: [],
|
|
843
938
|
diagnostics: [],
|
|
939
|
+
navigationLinkExportNames: [],
|
|
844
940
|
serverOnly: false,
|
|
845
941
|
serverOnlyClientRuntime: false,
|
|
942
|
+
usesNavigationLink: false,
|
|
846
943
|
...overrides,
|
|
847
944
|
};
|
|
848
945
|
}
|
|
@@ -870,7 +967,7 @@ async function clientRouteModuleAnalysisForSource(options: {
|
|
|
870
967
|
})),
|
|
871
968
|
),
|
|
872
969
|
);
|
|
873
|
-
options.cache.moduleAnalysisByFile.
|
|
970
|
+
setLatestModuleCacheEntry(options.cache.moduleAnalysisByFile, options.filename, cacheKey, analysis);
|
|
874
971
|
return analysis;
|
|
875
972
|
}
|
|
876
973
|
|
|
@@ -892,7 +989,7 @@ export async function compilerModuleContextForSource(options: {
|
|
|
892
989
|
filename: options.filename,
|
|
893
990
|
}),
|
|
894
991
|
);
|
|
895
|
-
options.cache.moduleContextByFile.
|
|
992
|
+
setLatestModuleCacheEntry(options.cache.moduleContextByFile, options.filename, cacheKey, context);
|
|
896
993
|
return context;
|
|
897
994
|
}
|
|
898
995
|
|
|
@@ -900,6 +997,28 @@ function sourceAnalysisCacheKey(filename: string, code: string): string {
|
|
|
900
997
|
return `${filename}\0${hashSourceText(code)}`;
|
|
901
998
|
}
|
|
902
999
|
|
|
1000
|
+
// Keeps only the latest content version per file in the source-keyed caches.
|
|
1001
|
+
// The cache persists across dev requests, and keys embed a content hash, so
|
|
1002
|
+
// without this an edited file would accumulate an entry per saved version.
|
|
1003
|
+
// Filenames cannot contain the NUL separator, so the `${filename}\0` prefix
|
|
1004
|
+
// matches exactly that file's prior entries.
|
|
1005
|
+
function setLatestModuleCacheEntry<T>(
|
|
1006
|
+
map: Map<string, T>,
|
|
1007
|
+
filename: string,
|
|
1008
|
+
cacheKey: string,
|
|
1009
|
+
value: T,
|
|
1010
|
+
): void {
|
|
1011
|
+
const prefix = `${filename}\0`;
|
|
1012
|
+
|
|
1013
|
+
for (const existing of map.keys()) {
|
|
1014
|
+
if (existing !== cacheKey && existing.startsWith(prefix)) {
|
|
1015
|
+
map.delete(existing);
|
|
1016
|
+
}
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
map.set(cacheKey, value);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
903
1022
|
function hashSourceText(text: string): string {
|
|
904
1023
|
return createHash("sha256").update(text).digest("hex");
|
|
905
1024
|
}
|
|
@@ -3591,24 +3710,67 @@ function importerInRuntimePackage(
|
|
|
3591
3710
|
* source. Returns the hinted value, or `true` when no hint is present (i.e.,
|
|
3592
3711
|
* preserve the historical "navigation runtime is always present" behavior).
|
|
3593
3712
|
*
|
|
3594
|
-
*
|
|
3595
|
-
*
|
|
3596
|
-
* export const clientNavigation = false
|
|
3597
|
-
* export const clientNavigation = false ;
|
|
3598
|
-
* export const clientNavigation: boolean = false
|
|
3713
|
+
* AST-based so commented-out or string-literal occurrences of the pattern are
|
|
3714
|
+
* not mistaken for a real export.
|
|
3599
3715
|
*/
|
|
3600
3716
|
export function detectClientNavigationHint(source: string): boolean {
|
|
3601
|
-
|
|
3602
|
-
|
|
3717
|
+
return readTopLevelBooleanExport({ code: source, name: "clientNavigation" }) ?? true;
|
|
3718
|
+
}
|
|
3719
|
+
|
|
3720
|
+
// `Link` ships from the dedicated `/link` subpath and is also re-exported from
|
|
3721
|
+
// the package root for backward compatibility, so both specifiers must count.
|
|
3722
|
+
// The root entry exports many bindings, so a root import only triggers the
|
|
3723
|
+
// navigation runtime when the rendered specifier is `Link` specifically.
|
|
3724
|
+
const navigationLinkPackageSpecifiers = new Set([
|
|
3725
|
+
"@reckona/mreact-router",
|
|
3726
|
+
"@reckona/mreact-router/link",
|
|
3727
|
+
]);
|
|
3728
|
+
|
|
3729
|
+
function staticImportsRenderLink(
|
|
3730
|
+
staticImports: readonly ClientRouteStaticImportReference[],
|
|
3731
|
+
renderedRoots: ReadonlySet<string>,
|
|
3732
|
+
renderedNames: ReadonlySet<string>,
|
|
3733
|
+
): boolean {
|
|
3734
|
+
return staticImports.some(
|
|
3735
|
+
(reference) =>
|
|
3736
|
+
navigationLinkPackageSpecifiers.has(reference.source) &&
|
|
3737
|
+
reference.specifiers.some(
|
|
3738
|
+
(specifier) =>
|
|
3739
|
+
(specifier.importedName === "Link" && renderedRoots.has(specifier.localName)) ||
|
|
3740
|
+
(specifier.kind === "namespace" && renderedNames.has(`${specifier.localName}.Link`)),
|
|
3741
|
+
),
|
|
3603
3742
|
);
|
|
3604
|
-
return match === null ? true : match[1] === "true";
|
|
3605
3743
|
}
|
|
3606
3744
|
|
|
3607
|
-
|
|
3608
|
-
|
|
3609
|
-
|
|
3745
|
+
function detectLinkComponentUsage(analysis: ClientRouteModuleAnalysis): boolean {
|
|
3746
|
+
// Use the export-reachable rendered roots, not the file-wide JSX roots, so a
|
|
3747
|
+
// `Link` rendered only in dead/unreachable code does not trigger the runtime.
|
|
3748
|
+
return staticImportsRenderLink(
|
|
3749
|
+
analysis.staticImports,
|
|
3750
|
+
new Set(analysis.reachableRenderedComponentRoots),
|
|
3751
|
+
new Set(analysis.reachableRenderedComponentNames),
|
|
3610
3752
|
);
|
|
3611
|
-
|
|
3753
|
+
}
|
|
3754
|
+
|
|
3755
|
+
// Export names whose own render subtree (transitively, within this module)
|
|
3756
|
+
// renders a navigation `Link`. Lets callers attribute `Link` usage to the
|
|
3757
|
+
// specific export they render, so a barrel that re-exports both Link-using and
|
|
3758
|
+
// Link-free components only triggers the runtime for the ones actually rendered.
|
|
3759
|
+
function detectLinkComponentExportNames(analysis: ClientRouteModuleAnalysis): string[] {
|
|
3760
|
+
return Object.keys(analysis.reachableExportRenderedComponentRoots).filter((exportName) =>
|
|
3761
|
+
staticImportsRenderLink(
|
|
3762
|
+
analysis.staticImports,
|
|
3763
|
+
new Set(analysis.reachableExportRenderedComponentRoots[exportName] ?? []),
|
|
3764
|
+
new Set(analysis.reachableExportRenderedComponentNames[exportName] ?? []),
|
|
3765
|
+
),
|
|
3766
|
+
);
|
|
3767
|
+
}
|
|
3768
|
+
|
|
3769
|
+
// Reads the explicit `export const navigationRuntime = true | false` override.
|
|
3770
|
+
// Returns `undefined` when absent. AST-based so commented-out or string-literal
|
|
3771
|
+
// occurrences of the pattern are not mistaken for a real export.
|
|
3772
|
+
export function detectNavigationRuntimeOverride(source: string): boolean | undefined {
|
|
3773
|
+
return readTopLevelBooleanExport({ code: source, name: "navigationRuntime" });
|
|
3612
3774
|
}
|
|
3613
3775
|
|
|
3614
3776
|
function detectRouteCellStateHint(code: string): boolean {
|
package/src/vite.ts
CHANGED
|
@@ -23,8 +23,10 @@ import {
|
|
|
23
23
|
import type { AppRouterImportPolicy } from "./import-policy.js";
|
|
24
24
|
import {
|
|
25
25
|
collectClientRouteReferences,
|
|
26
|
-
|
|
26
|
+
createClientRouteInferenceCache,
|
|
27
27
|
isClientRouteSource,
|
|
28
|
+
resolveNavigationRuntime,
|
|
29
|
+
type ClientRouteInferenceCache,
|
|
28
30
|
} from "./client-route-inference.js";
|
|
29
31
|
import {
|
|
30
32
|
buildNavigationRuntimeBundle,
|
|
@@ -52,6 +54,8 @@ export interface AppRouterViteMiddlewareOptions extends AppRouterProjectOptions
|
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
type AppRouterViteRuntimeMiddlewareOptions = AppRouterViteMiddlewareOptions & {
|
|
57
|
+
clientRouteInferenceCache?: ClientRouteInferenceCache | undefined;
|
|
58
|
+
navigationScanVitePlugins?: readonly PluginOption[] | undefined;
|
|
55
59
|
viteDevServer?: ViteDevServer | undefined;
|
|
56
60
|
};
|
|
57
61
|
|
|
@@ -158,6 +162,12 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
158
162
|
],
|
|
159
163
|
]);
|
|
160
164
|
|
|
165
|
+
// User-declared plugins captured from the resolving config. Unlike the fully
|
|
166
|
+
// resolved `server.config.plugins`, this excludes Vite internals (e.g. the
|
|
167
|
+
// built-in CSS plugin) whose `transform` requires a dev-server environment the
|
|
168
|
+
// lightweight navigation scan cannot provide. Mirrors what the build forwards.
|
|
169
|
+
let userVitePlugins: readonly PluginOption[] | undefined;
|
|
170
|
+
|
|
161
171
|
const plugin: MreactRouterPlugin = {
|
|
162
172
|
[mreactRouterConfigKey]: {
|
|
163
173
|
...project,
|
|
@@ -165,7 +175,9 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
165
175
|
},
|
|
166
176
|
enforce: "pre",
|
|
167
177
|
name: "mreact-router",
|
|
168
|
-
config() {
|
|
178
|
+
config(userConfig) {
|
|
179
|
+
userVitePlugins = userConfig.plugins;
|
|
180
|
+
|
|
169
181
|
return {
|
|
170
182
|
optimizeDeps: {
|
|
171
183
|
exclude: [
|
|
@@ -185,6 +197,7 @@ export function createAppRouterVitePlugin(options: AppRouterVitePluginOptions):
|
|
|
185
197
|
return () => {
|
|
186
198
|
const middlewareOptions: AppRouterViteRuntimeMiddlewareOptions = {
|
|
187
199
|
...options,
|
|
200
|
+
navigationScanVitePlugins: userVitePlugins,
|
|
188
201
|
viteDevServer: server,
|
|
189
202
|
vitePlugins: server.config.plugins,
|
|
190
203
|
};
|
|
@@ -378,13 +391,18 @@ export function mreactRouterConfigFromPlugins(
|
|
|
378
391
|
export function createAppRouterViteMiddleware(
|
|
379
392
|
options: AppRouterViteMiddlewareOptions,
|
|
380
393
|
): Connect.NextHandleFunction {
|
|
394
|
+
// Reused across requests so dev navigation-script detection memoizes module
|
|
395
|
+
// contexts/analyses instead of re-walking every route's import graph per
|
|
396
|
+
// request. The source-keyed caches keep only the latest content version per
|
|
397
|
+
// file (see setLatestModuleCacheEntry), so repeated edits do not accumulate
|
|
398
|
+
// stale entries over a long dev session.
|
|
399
|
+
const runtimeOptions: AppRouterViteRuntimeMiddlewareOptions = {
|
|
400
|
+
...options,
|
|
401
|
+
clientRouteInferenceCache: createClientRouteInferenceCache(),
|
|
402
|
+
};
|
|
403
|
+
|
|
381
404
|
return (request, response, next) => {
|
|
382
|
-
void handleAppRouterViteRequest(
|
|
383
|
-
options as AppRouterViteRuntimeMiddlewareOptions,
|
|
384
|
-
request,
|
|
385
|
-
response,
|
|
386
|
-
next,
|
|
387
|
-
);
|
|
405
|
+
void handleAppRouterViteRequest(runtimeOptions, request, response, next);
|
|
388
406
|
};
|
|
389
407
|
}
|
|
390
408
|
|
|
@@ -443,7 +461,11 @@ async function handleAppRouterViteRequest(
|
|
|
443
461
|
projectRoot: project.projectRoot,
|
|
444
462
|
},
|
|
445
463
|
clientStyles: await devRouteStyles(project),
|
|
446
|
-
navigationScripts: await devNavigationScripts(
|
|
464
|
+
navigationScripts: await devNavigationScripts(
|
|
465
|
+
project.routesDir,
|
|
466
|
+
options.clientRouteInferenceCache,
|
|
467
|
+
options.navigationScanVitePlugins,
|
|
468
|
+
),
|
|
447
469
|
request,
|
|
448
470
|
routeCache: options.routeCache,
|
|
449
471
|
serverActions: options.serverActions,
|
|
@@ -671,7 +693,12 @@ async function devRouteStyles(
|
|
|
671
693
|
return new Map<string, readonly string[]>(routeStyles);
|
|
672
694
|
}
|
|
673
695
|
|
|
674
|
-
async function devNavigationScripts(
|
|
696
|
+
async function devNavigationScripts(
|
|
697
|
+
appDir: string,
|
|
698
|
+
inferenceCache?: ClientRouteInferenceCache | undefined,
|
|
699
|
+
vitePlugins?: readonly PluginOption[] | undefined,
|
|
700
|
+
): Promise<ReadonlyMap<string, string>> {
|
|
701
|
+
const cache = inferenceCache ?? createClientRouteInferenceCache();
|
|
675
702
|
const entries = await Promise.all(
|
|
676
703
|
(await scanAppRoutes({ appDir })).map(async (route) => {
|
|
677
704
|
if (route.kind !== "page") {
|
|
@@ -679,8 +706,15 @@ async function devNavigationScripts(appDir: string): Promise<ReadonlyMap<string,
|
|
|
679
706
|
}
|
|
680
707
|
|
|
681
708
|
const source = await readFile(route.file, "utf8");
|
|
709
|
+
const navigation = await resolveNavigationRuntime({
|
|
710
|
+
appDir,
|
|
711
|
+
cache,
|
|
712
|
+
code: source,
|
|
713
|
+
filename: route.file,
|
|
714
|
+
vitePlugins,
|
|
715
|
+
});
|
|
682
716
|
|
|
683
|
-
return
|
|
717
|
+
return navigation
|
|
684
718
|
? ([route.path, navigationRuntimeScriptForDev()] as const)
|
|
685
719
|
: undefined;
|
|
686
720
|
}),
|