@rangojs/router 0.0.0-experimental.ce770c9a → 0.0.0-experimental.d7eeaa75

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.
Files changed (119) hide show
  1. package/README.md +76 -18
  2. package/dist/bin/rango.js +130 -47
  3. package/dist/vite/index.js +702 -231
  4. package/dist/vite/index.js.bak +5448 -0
  5. package/package.json +2 -2
  6. package/skills/cache-guide/SKILL.md +32 -0
  7. package/skills/caching/SKILL.md +8 -0
  8. package/skills/links/SKILL.md +3 -1
  9. package/skills/loader/SKILL.md +53 -43
  10. package/skills/middleware/SKILL.md +2 -0
  11. package/skills/parallel/SKILL.md +67 -0
  12. package/skills/prerender/SKILL.md +110 -68
  13. package/skills/route/SKILL.md +31 -0
  14. package/skills/router-setup/SKILL.md +87 -2
  15. package/skills/typesafety/SKILL.md +10 -0
  16. package/src/__internal.ts +1 -1
  17. package/src/browser/app-version.ts +14 -0
  18. package/src/browser/navigation-bridge.ts +18 -3
  19. package/src/browser/navigation-client.ts +64 -40
  20. package/src/browser/navigation-store.ts +43 -8
  21. package/src/browser/partial-update.ts +37 -4
  22. package/src/browser/prefetch/fetch.ts +8 -2
  23. package/src/browser/prefetch/queue.ts +61 -29
  24. package/src/browser/prefetch/resource-ready.ts +77 -0
  25. package/src/browser/react/Link.tsx +44 -8
  26. package/src/browser/react/NavigationProvider.tsx +13 -4
  27. package/src/browser/react/context.ts +7 -2
  28. package/src/browser/react/use-handle.ts +9 -58
  29. package/src/browser/react/use-router.ts +21 -8
  30. package/src/browser/rsc-router.tsx +26 -3
  31. package/src/browser/scroll-restoration.ts +10 -8
  32. package/src/browser/server-action-bridge.ts +8 -6
  33. package/src/browser/types.ts +27 -5
  34. package/src/build/generate-manifest.ts +6 -6
  35. package/src/build/generate-route-types.ts +3 -0
  36. package/src/build/route-types/include-resolution.ts +8 -1
  37. package/src/build/route-types/router-processing.ts +211 -72
  38. package/src/build/route-types/scan-filter.ts +8 -1
  39. package/src/cache/cache-runtime.ts +15 -11
  40. package/src/cache/cache-scope.ts +46 -5
  41. package/src/cache/taint.ts +55 -0
  42. package/src/client.tsx +2 -56
  43. package/src/context-var.ts +72 -2
  44. package/src/handle.ts +40 -0
  45. package/src/index.rsc.ts +3 -1
  46. package/src/index.ts +12 -0
  47. package/src/prerender/store.ts +5 -4
  48. package/src/prerender.ts +138 -77
  49. package/src/reverse.ts +22 -1
  50. package/src/route-definition/dsl-helpers.ts +42 -19
  51. package/src/route-definition/helpers-types.ts +10 -6
  52. package/src/route-definition/index.ts +3 -0
  53. package/src/route-definition/redirect.ts +9 -1
  54. package/src/route-definition/resolve-handler-use.ts +149 -0
  55. package/src/route-types.ts +11 -0
  56. package/src/router/content-negotiation.ts +100 -1
  57. package/src/router/handler-context.ts +79 -23
  58. package/src/router/intercept-resolution.ts +9 -4
  59. package/src/router/loader-resolution.ts +156 -21
  60. package/src/router/match-api.ts +124 -189
  61. package/src/router/match-middleware/background-revalidation.ts +12 -1
  62. package/src/router/match-middleware/cache-lookup.ts +70 -10
  63. package/src/router/match-middleware/cache-store.ts +21 -4
  64. package/src/router/match-middleware/segment-resolution.ts +53 -0
  65. package/src/router/match-result.ts +93 -9
  66. package/src/router/middleware-types.ts +6 -8
  67. package/src/router/middleware.ts +2 -5
  68. package/src/router/navigation-snapshot.ts +182 -0
  69. package/src/router/prerender-match.ts +110 -10
  70. package/src/router/preview-match.ts +30 -102
  71. package/src/router/request-classification.ts +310 -0
  72. package/src/router/route-snapshot.ts +245 -0
  73. package/src/router/router-context.ts +1 -0
  74. package/src/router/router-interfaces.ts +36 -4
  75. package/src/router/router-options.ts +37 -11
  76. package/src/router/segment-resolution/fresh.ts +103 -21
  77. package/src/router/segment-resolution/helpers.ts +29 -24
  78. package/src/router/segment-resolution/revalidation.ts +126 -30
  79. package/src/router/types.ts +1 -0
  80. package/src/router.ts +54 -5
  81. package/src/rsc/handler.ts +472 -372
  82. package/src/rsc/loader-fetch.ts +23 -3
  83. package/src/rsc/manifest-init.ts +5 -1
  84. package/src/rsc/progressive-enhancement.ts +14 -2
  85. package/src/rsc/rsc-rendering.ts +10 -1
  86. package/src/rsc/server-action.ts +8 -0
  87. package/src/rsc/ssr-setup.ts +2 -2
  88. package/src/rsc/types.ts +9 -1
  89. package/src/server/context.ts +50 -1
  90. package/src/server/handle-store.ts +19 -0
  91. package/src/server/loader-registry.ts +9 -8
  92. package/src/server/request-context.ts +175 -15
  93. package/src/ssr/index.tsx +3 -0
  94. package/src/static-handler.ts +18 -6
  95. package/src/types/cache-types.ts +4 -4
  96. package/src/types/handler-context.ts +137 -33
  97. package/src/types/loader-types.ts +36 -9
  98. package/src/types/route-entry.ts +1 -1
  99. package/src/types/segments.ts +1 -0
  100. package/src/urls/path-helper-types.ts +9 -2
  101. package/src/urls/path-helper.ts +47 -12
  102. package/src/urls/pattern-types.ts +12 -0
  103. package/src/urls/response-types.ts +16 -6
  104. package/src/use-loader.tsx +77 -5
  105. package/src/vite/discovery/bundle-postprocess.ts +30 -33
  106. package/src/vite/discovery/discover-routers.ts +5 -1
  107. package/src/vite/discovery/prerender-collection.ts +128 -74
  108. package/src/vite/discovery/state.ts +13 -4
  109. package/src/vite/index.ts +4 -0
  110. package/src/vite/plugin-types.ts +60 -5
  111. package/src/vite/plugins/expose-id-utils.ts +12 -0
  112. package/src/vite/plugins/expose-ids/handler-transform.ts +30 -0
  113. package/src/vite/plugins/expose-internal-ids.ts +257 -40
  114. package/src/vite/plugins/performance-tracks.ts +88 -0
  115. package/src/vite/plugins/refresh-cmd.ts +88 -26
  116. package/src/vite/rango.ts +19 -2
  117. package/src/vite/router-discovery.ts +178 -37
  118. package/src/vite/utils/prerender-utils.ts +18 -0
  119. package/src/vite/utils/shared-utils.ts +3 -2
package/README.md CHANGED
@@ -91,24 +91,29 @@ This file is a server/RSC module and should import router construction APIs from
91
91
 
92
92
  ```tsx
93
93
  // src/router.tsx
94
- import { createRouter, urls } from "@rangojs/router";
95
- import { Document } from "./document";
94
+ import { createRouter } from "@rangojs/router";
96
95
 
97
- const blogPatterns = urls(({ path }) => [
98
- path("/", BlogIndexPage, { name: "index" }),
99
- path("/:slug", BlogPostPage, { name: "post" }),
96
+ export const router = createRouter().routes(({ path }) => [
97
+ path("/", HomePage, { name: "home" }),
98
+ path("/about", AboutPage, { name: "about" }),
100
99
  ]);
101
100
 
101
+ export const reverse = router.reverse;
102
+ // reverse("home") -> "/"
103
+ ```
104
+
105
+ For larger apps, extract route modules with `urls()` and compose with `include()`:
106
+
107
+ ```tsx
108
+ import { createRouter, urls } from "@rangojs/router";
109
+ import { blogPatterns } from "./urls/blog";
110
+
102
111
  const urlpatterns = urls(({ path, include }) => [
103
112
  path("/", HomePage, { name: "home" }),
104
113
  include("/blog", blogPatterns, { name: "blog" }),
105
114
  ]);
106
115
 
107
- export const router = createRouter({ document: Document }).routes(urlpatterns);
108
-
109
- // Export typed reverse function for URL generation by route name
110
- export const reverse = router.reverse;
111
-
116
+ export const router = createRouter().routes(urlpatterns);
112
117
  // reverse("blog.post", { slug: "hello-world" }) -> "/blog/hello-world"
113
118
  ```
114
119
 
@@ -711,10 +716,12 @@ export const BlogPost = Prerender(
711
716
 
712
717
  ### Passthrough for Unknown Params
713
718
 
719
+ Wrap a `Prerender` definition with `Passthrough()` to add a live handler for unknown params at runtime. The build handler runs at build time, the live handler runs at request time for params not in the prerender cache.
720
+
714
721
  ```tsx
715
- import { Prerender } from "@rangojs/router";
722
+ import { Prerender, Passthrough } from "@rangojs/router";
716
723
 
717
- export const ProductPage = Prerender(
724
+ export const ProductPageDef = Prerender(
718
725
  async () => {
719
726
  const featured = await db.getFeaturedProducts();
720
727
  return featured.map((p) => ({ id: p.id }));
@@ -723,16 +730,22 @@ export const ProductPage = Prerender(
723
730
  const product = await db.getProduct(ctx.params.id);
724
731
  return <Product data={product} />;
725
732
  },
726
- { passthrough: true },
727
733
  );
728
- ```
729
734
 
730
- With `passthrough: true`, known params are served from the build-time cache and unknown params fall through to live rendering.
735
+ // In route definition:
736
+ path(
737
+ "/products/:id",
738
+ Passthrough(ProductPageDef, async (ctx) => {
739
+ const product = await ctx.env.DB.getProduct(ctx.params.id);
740
+ return <Product data={product} />;
741
+ }),
742
+ );
743
+ ```
731
744
 
732
- Handlers can also skip individual param sets with `ctx.passthrough()`, deferring them to the live handler at runtime:
745
+ Build handlers can also skip individual param sets with `ctx.passthrough()`, deferring them to the live handler:
733
746
 
734
747
  ```tsx
735
- export const ProductPage = Prerender(
748
+ export const ProductPageDef = Prerender(
736
749
  async () => {
737
750
  const all = await db.getAllProducts();
738
751
  return all.map((p) => ({ id: p.id }));
@@ -742,10 +755,55 @@ export const ProductPage = Prerender(
742
755
  if (!product.published) return ctx.passthrough();
743
756
  return <Product data={product} />;
744
757
  },
745
- { passthrough: true },
746
758
  );
747
759
  ```
748
760
 
761
+ ### Build-Time Environment Bindings
762
+
763
+ Prerender handlers can access platform bindings (KV, D1, R2) at build time when `buildEnv` is configured in the Vite plugin:
764
+
765
+ ```ts
766
+ // vite.config.ts
767
+ import { rango } from "@rangojs/router/vite";
768
+
769
+ rango({ preset: "cloudflare", buildEnv: "auto" });
770
+ ```
771
+
772
+ With `buildEnv: "auto"`, the plugin calls `wrangler.getPlatformProxy()` to provide local bindings. Handlers then access `ctx.env` during build:
773
+
774
+ ```tsx
775
+ export const BlogPosts = Prerender<{ slug: string }>(
776
+ async (ctx) => {
777
+ const rows = await ctx.env.DB.prepare("SELECT slug FROM posts").all();
778
+ return rows.map((r) => ({ slug: r.slug }));
779
+ },
780
+ async (ctx) => {
781
+ const post = await ctx.env.DB.prepare("SELECT * FROM posts WHERE slug = ?")
782
+ .bind(ctx.params.slug)
783
+ .first();
784
+ return <BlogPost post={post} />;
785
+ },
786
+ );
787
+ ```
788
+
789
+ `buildEnv` also accepts a factory function or plain object:
790
+
791
+ ```ts
792
+ // Custom factory
793
+ rango({
794
+ buildEnv: async (ctx) => {
795
+ const { getPlatformProxy } = await import("wrangler");
796
+ const proxy = await getPlatformProxy();
797
+ return { env: proxy.env, dispose: proxy.dispose };
798
+ },
799
+ });
800
+
801
+ // Plain object (Node.js)
802
+ rango({ buildEnv: { DATABASE_URL: process.env.DATABASE_URL } });
803
+ ```
804
+
805
+ Build-time env applies to both production builds and dev on-demand prerender. Without `buildEnv`, accessing `ctx.env` in a Prerender handler throws with a clear error.
806
+
749
807
  ## Theme
750
808
 
751
809
  ### Router Configuration
package/dist/bin/rango.js CHANGED
@@ -218,7 +218,8 @@ function findTsFiles(dir, filter) {
218
218
  for (const entry of entries) {
219
219
  const fullPath = join(dir, entry.name);
220
220
  if (entry.isDirectory()) {
221
- if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
221
+ if (entry.name === "node_modules" || entry.name.startsWith(".") || entry.name === "dist" || entry.name === "build" || entry.name === "coverage")
222
+ continue;
222
223
  results.push(...findTsFiles(fullPath, filter));
223
224
  } else if ((entry.name.endsWith(".ts") || entry.name.endsWith(".tsx") || entry.name.endsWith(".js") || entry.name.endsWith(".jsx")) && !entry.name.includes(".gen.")) {
224
225
  if (filter && !filter(fullPath)) continue;
@@ -450,7 +451,7 @@ function buildRouteMapFromBlock(block, fullSource, filePath, visited, searchSche
450
451
  }
451
452
  return routeMap;
452
453
  }
453
- function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut) {
454
+ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagnosticsOut, inlineBlock) {
454
455
  visited = visited ?? /* @__PURE__ */ new Set();
455
456
  const realPath = resolve(filePath);
456
457
  const key = variableName ? `${realPath}:${variableName}` : realPath;
@@ -466,7 +467,9 @@ function buildCombinedRouteMapWithSearch(filePath, variableName, visited, diagno
466
467
  return { routes: {}, searchSchemas: {} };
467
468
  }
468
469
  let block;
469
- if (variableName) {
470
+ if (inlineBlock) {
471
+ block = inlineBlock;
472
+ } else if (variableName) {
470
473
  const extracted = extractUrlsBlockForVariable(source, variableName);
471
474
  if (!extracted) return { routes: {}, searchSchemas: {} };
472
475
  block = extracted;
@@ -671,7 +674,7 @@ Router root: ${conflict.ancestor}
671
674
  Nested router: ${conflict.nested}
672
675
  Move the nested router into a sibling directory or configure it as a separate app root.`;
673
676
  }
674
- function extractUrlsVariableFromRouter(code) {
677
+ function extractUrlsFromRouter(code) {
675
678
  const sourceFile = ts5.createSourceFile(
676
679
  "router.tsx",
677
680
  code,
@@ -685,24 +688,70 @@ function extractUrlsVariableFromRouter(code) {
685
688
  const callee = node.expression;
686
689
  return ts5.isIdentifier(callee) && callee.text === "createRouter";
687
690
  }
691
+ function isInlineBuilder(node) {
692
+ return ts5.isArrowFunction(node) || ts5.isFunctionExpression(node);
693
+ }
694
+ function isRoutesOnCreateRouter(node) {
695
+ if (!ts5.isPropertyAccessExpression(node.expression) || node.expression.name.text !== "routes")
696
+ return false;
697
+ let inner = node.expression.expression;
698
+ while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
699
+ inner = inner.expression.expression;
700
+ }
701
+ return isCreateRouterCall(inner);
702
+ }
688
703
  function visit(node) {
689
704
  if (result) return;
690
- if (ts5.isCallExpression(node) && ts5.isPropertyAccessExpression(node.expression) && node.expression.name.text === "routes" && node.arguments.length >= 1 && ts5.isIdentifier(node.arguments[0])) {
691
- let inner = node.expression.expression;
692
- while (ts5.isCallExpression(inner) && ts5.isPropertyAccessExpression(inner.expression)) {
693
- inner = inner.expression.expression;
694
- }
695
- if (isCreateRouterCall(inner)) {
696
- result = node.arguments[0].text;
697
- return;
705
+ if (ts5.isCallExpression(node) && node.arguments.length >= 1 && isRoutesOnCreateRouter(node)) {
706
+ const arg = node.arguments[0];
707
+ if (ts5.isIdentifier(arg)) {
708
+ result = { kind: "variable", name: arg.text };
709
+ } else if (isInlineBuilder(arg)) {
710
+ result = { kind: "inline", block: arg.getText(sourceFile) };
698
711
  }
712
+ return;
699
713
  }
700
714
  if (isCreateRouterCall(node)) {
701
715
  const callExpr = node;
702
- for (const arg of callExpr.arguments) {
716
+ for (const callArg of callExpr.arguments) {
717
+ if (ts5.isObjectLiteralExpression(callArg)) {
718
+ for (const prop of callArg.properties) {
719
+ if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls") {
720
+ if (ts5.isIdentifier(prop.initializer)) {
721
+ result = { kind: "variable", name: prop.initializer.text };
722
+ } else if (isInlineBuilder(prop.initializer)) {
723
+ result = {
724
+ kind: "inline",
725
+ block: prop.initializer.getText(sourceFile)
726
+ };
727
+ }
728
+ return;
729
+ }
730
+ }
731
+ }
732
+ }
733
+ }
734
+ ts5.forEachChild(node, visit);
735
+ }
736
+ visit(sourceFile);
737
+ return result;
738
+ }
739
+ function extractBasenameFromRouter(code) {
740
+ const sourceFile = ts5.createSourceFile(
741
+ "router.tsx",
742
+ code,
743
+ ts5.ScriptTarget.Latest,
744
+ true,
745
+ ts5.ScriptKind.TSX
746
+ );
747
+ let result;
748
+ function visit(node) {
749
+ if (result !== void 0) return;
750
+ if (ts5.isCallExpression(node) && ts5.isIdentifier(node.expression) && node.expression.text === "createRouter") {
751
+ for (const arg of node.arguments) {
703
752
  if (ts5.isObjectLiteralExpression(arg)) {
704
753
  for (const prop of arg.properties) {
705
- if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "urls" && ts5.isIdentifier(prop.initializer)) {
754
+ if (ts5.isPropertyAssignment(prop) && ts5.isIdentifier(prop.name) && prop.name.text === "basename" && ts5.isStringLiteral(prop.initializer)) {
706
755
  result = prop.initializer.text;
707
756
  return;
708
757
  }
@@ -715,6 +764,19 @@ function extractUrlsVariableFromRouter(code) {
715
764
  visit(sourceFile);
716
765
  return result;
717
766
  }
767
+ function applyBasenameToRoutes(result, basename2) {
768
+ const prefixed = {};
769
+ for (const [name, pattern] of Object.entries(result.routes)) {
770
+ if (pattern === "/") {
771
+ prefixed[name] = basename2;
772
+ } else if (basename2.endsWith("/") && pattern.startsWith("/")) {
773
+ prefixed[name] = basename2 + pattern.slice(1);
774
+ } else {
775
+ prefixed[name] = basename2 + pattern;
776
+ }
777
+ }
778
+ return { routes: prefixed, searchSchemas: result.searchSchemas };
779
+ }
718
780
  function buildCombinedRouteMapForRouterFile(routerFilePath) {
719
781
  let routerSource;
720
782
  try {
@@ -722,19 +784,40 @@ function buildCombinedRouteMapForRouterFile(routerFilePath) {
722
784
  } catch {
723
785
  return { routes: {}, searchSchemas: {} };
724
786
  }
725
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
726
- if (!urlsVarName) {
787
+ const extraction = extractUrlsFromRouter(routerSource);
788
+ if (!extraction) {
727
789
  return { routes: {}, searchSchemas: {} };
728
790
  }
729
- const imported = resolveImportedVariable(routerSource, urlsVarName);
730
- if (imported) {
731
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
732
- if (!targetFile) {
733
- return { routes: {}, searchSchemas: {} };
791
+ const rawBasename = extractBasenameFromRouter(routerSource);
792
+ const basename2 = rawBasename ? ("/" + rawBasename.replace(/^\/+|\/+$/g, "")).replace(/^\/$/, "") : void 0;
793
+ let result;
794
+ if (extraction.kind === "inline") {
795
+ result = buildCombinedRouteMapWithSearch(
796
+ routerFilePath,
797
+ void 0,
798
+ void 0,
799
+ void 0,
800
+ extraction.block
801
+ );
802
+ } else {
803
+ const imported = resolveImportedVariable(routerSource, extraction.name);
804
+ if (imported) {
805
+ const targetFile = resolveImportPath(imported.specifier, routerFilePath);
806
+ if (!targetFile) {
807
+ return { routes: {}, searchSchemas: {} };
808
+ }
809
+ result = buildCombinedRouteMapWithSearch(
810
+ targetFile,
811
+ imported.exportedName
812
+ );
813
+ } else {
814
+ result = buildCombinedRouteMapWithSearch(routerFilePath, extraction.name);
734
815
  }
735
- return buildCombinedRouteMapWithSearch(targetFile, imported.exportedName);
736
816
  }
737
- return buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
817
+ if (basename2) {
818
+ result = applyBasenameToRoutes(result, basename2);
819
+ }
820
+ return result;
738
821
  }
739
822
  function detectUnresolvableIncludes(routerFilePath) {
740
823
  const realPath = resolve2(routerFilePath);
@@ -744,9 +827,20 @@ function detectUnresolvableIncludes(routerFilePath) {
744
827
  } catch {
745
828
  return [];
746
829
  }
747
- const urlsVarName = extractUrlsVariableFromRouter(source);
748
- if (!urlsVarName) return [];
749
- const imported = resolveImportedVariable(source, urlsVarName);
830
+ const extraction = extractUrlsFromRouter(source);
831
+ if (!extraction) return [];
832
+ const diagnostics = [];
833
+ if (extraction.kind === "inline") {
834
+ buildCombinedRouteMapWithSearch(
835
+ realPath,
836
+ void 0,
837
+ /* @__PURE__ */ new Set(),
838
+ diagnostics,
839
+ extraction.block
840
+ );
841
+ return diagnostics;
842
+ }
843
+ const imported = resolveImportedVariable(source, extraction.name);
750
844
  let targetFile;
751
845
  let exportedName;
752
846
  if (imported) {
@@ -766,9 +860,8 @@ function detectUnresolvableIncludes(routerFilePath) {
766
860
  exportedName = imported.exportedName;
767
861
  } else {
768
862
  targetFile = realPath;
769
- exportedName = urlsVarName;
863
+ exportedName = extraction.name;
770
864
  }
771
- const diagnostics = [];
772
865
  buildCombinedRouteMapWithSearch(
773
866
  targetFile,
774
867
  exportedName,
@@ -816,25 +909,15 @@ function writeCombinedRouteTypes(root, knownRouterFiles, opts) {
816
909
  throw new Error(formatNestedRouterConflictError(nestedRouterConflict));
817
910
  }
818
911
  for (const routerFilePath of routerFilePaths) {
819
- let routerSource;
820
- try {
821
- routerSource = readFileSync3(routerFilePath, "utf-8");
822
- } catch {
823
- continue;
824
- }
825
- const urlsVarName = extractUrlsVariableFromRouter(routerSource);
826
- if (!urlsVarName) continue;
827
- let result;
828
- const imported = resolveImportedVariable(routerSource, urlsVarName);
829
- if (imported) {
830
- const targetFile = resolveImportPath(imported.specifier, routerFilePath);
831
- if (!targetFile) continue;
832
- result = buildCombinedRouteMapWithSearch(
833
- targetFile,
834
- imported.exportedName
835
- );
836
- } else {
837
- result = buildCombinedRouteMapWithSearch(routerFilePath, urlsVarName);
912
+ const result = buildCombinedRouteMapForRouterFile(routerFilePath);
913
+ if (Object.keys(result.routes).length === 0 && Object.keys(result.searchSchemas).length === 0) {
914
+ let routerSource;
915
+ try {
916
+ routerSource = readFileSync3(routerFilePath, "utf-8");
917
+ } catch {
918
+ continue;
919
+ }
920
+ if (!extractUrlsFromRouter(routerSource)) continue;
838
921
  }
839
922
  const routerBasename = pathBasename(routerFilePath).replace(
840
923
  /\.(tsx?|jsx?)$/,