vinext 0.0.51 → 0.0.52

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 (307) hide show
  1. package/dist/build/precompress.d.ts +7 -7
  2. package/dist/build/precompress.js +18 -17
  3. package/dist/build/precompress.js.map +1 -1
  4. package/dist/build/prerender.d.ts +3 -14
  5. package/dist/build/prerender.js +40 -40
  6. package/dist/build/prerender.js.map +1 -1
  7. package/dist/check.js +4 -0
  8. package/dist/check.js.map +1 -1
  9. package/dist/cli-args.d.ts +1 -0
  10. package/dist/cli-args.js +5 -0
  11. package/dist/cli-args.js.map +1 -1
  12. package/dist/cli.js +39 -0
  13. package/dist/cli.js.map +1 -1
  14. package/dist/client/navigation-runtime.d.ts +47 -0
  15. package/dist/client/navigation-runtime.js +156 -0
  16. package/dist/client/navigation-runtime.js.map +1 -0
  17. package/dist/client/pages-router-link-navigation.d.ts +26 -0
  18. package/dist/client/pages-router-link-navigation.js +14 -0
  19. package/dist/client/pages-router-link-navigation.js.map +1 -0
  20. package/dist/client/vinext-next-data.d.ts +12 -2
  21. package/dist/client/vinext-next-data.js +50 -1
  22. package/dist/client/vinext-next-data.js.map +1 -0
  23. package/dist/cloudflare/kv-cache-handler.js +2 -1
  24. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  25. package/dist/config/config-matchers.d.ts +63 -16
  26. package/dist/config/config-matchers.js +143 -8
  27. package/dist/config/config-matchers.js.map +1 -1
  28. package/dist/config/next-config.d.ts +20 -2
  29. package/dist/config/next-config.js +11 -1
  30. package/dist/config/next-config.js.map +1 -1
  31. package/dist/deploy.js +101 -39
  32. package/dist/deploy.js.map +1 -1
  33. package/dist/entries/app-browser-entry.js +9 -3
  34. package/dist/entries/app-browser-entry.js.map +1 -1
  35. package/dist/entries/app-rsc-entry.js +53 -13
  36. package/dist/entries/app-rsc-entry.js.map +1 -1
  37. package/dist/entries/app-rsc-manifest.d.ts +1 -0
  38. package/dist/entries/app-rsc-manifest.js +53 -6
  39. package/dist/entries/app-rsc-manifest.js.map +1 -1
  40. package/dist/entries/app-ssr-entry.d.ts +3 -3
  41. package/dist/entries/app-ssr-entry.js +4 -4
  42. package/dist/entries/app-ssr-entry.js.map +1 -1
  43. package/dist/entries/pages-client-entry.js +18 -2
  44. package/dist/entries/pages-client-entry.js.map +1 -1
  45. package/dist/entries/pages-server-entry.js +58 -8
  46. package/dist/entries/pages-server-entry.js.map +1 -1
  47. package/dist/entries/runtime-entry-module.d.ts +2 -1
  48. package/dist/entries/runtime-entry-module.js +9 -3
  49. package/dist/entries/runtime-entry-module.js.map +1 -1
  50. package/dist/index.js +132 -40
  51. package/dist/index.js.map +1 -1
  52. package/dist/plugins/css-data-url.d.ts +7 -0
  53. package/dist/plugins/css-data-url.js +81 -0
  54. package/dist/plugins/css-data-url.js.map +1 -0
  55. package/dist/plugins/fonts.js +5 -3
  56. package/dist/plugins/fonts.js.map +1 -1
  57. package/dist/plugins/middleware-server-only.d.ts +54 -0
  58. package/dist/plugins/middleware-server-only.js +91 -0
  59. package/dist/plugins/middleware-server-only.js.map +1 -0
  60. package/dist/plugins/optimize-imports.js +4 -4
  61. package/dist/plugins/optimize-imports.js.map +1 -1
  62. package/dist/plugins/strip-server-exports.js +5 -8
  63. package/dist/plugins/strip-server-exports.js.map +1 -1
  64. package/dist/routing/app-route-graph.d.ts +20 -1
  65. package/dist/routing/app-route-graph.js +58 -6
  66. package/dist/routing/app-route-graph.js.map +1 -1
  67. package/dist/routing/app-router.d.ts +2 -2
  68. package/dist/routing/app-router.js +2 -2
  69. package/dist/routing/app-router.js.map +1 -1
  70. package/dist/routing/utils.d.ts +2 -1
  71. package/dist/routing/utils.js +4 -1
  72. package/dist/routing/utils.js.map +1 -1
  73. package/dist/server/api-handler.js +139 -37
  74. package/dist/server/api-handler.js.map +1 -1
  75. package/dist/server/app-browser-entry.js +293 -149
  76. package/dist/server/app-browser-entry.js.map +1 -1
  77. package/dist/server/app-browser-interception-context.d.ts +24 -0
  78. package/dist/server/app-browser-interception-context.js +32 -0
  79. package/dist/server/app-browser-interception-context.js.map +1 -0
  80. package/dist/server/app-browser-navigation-controller.d.ts +3 -1
  81. package/dist/server/app-browser-navigation-controller.js +5 -1
  82. package/dist/server/app-browser-navigation-controller.js.map +1 -1
  83. package/dist/server/app-browser-rsc-redirect.d.ts +2 -1
  84. package/dist/server/app-browser-rsc-redirect.js +2 -2
  85. package/dist/server/app-browser-rsc-redirect.js.map +1 -1
  86. package/dist/server/app-browser-state.d.ts +18 -1
  87. package/dist/server/app-browser-state.js +19 -1
  88. package/dist/server/app-browser-state.js.map +1 -1
  89. package/dist/server/app-browser-stream.d.ts +5 -14
  90. package/dist/server/app-browser-stream.js +13 -7
  91. package/dist/server/app-browser-stream.js.map +1 -1
  92. package/dist/server/app-browser-visible-commit.d.ts +2 -1
  93. package/dist/server/app-browser-visible-commit.js +1 -0
  94. package/dist/server/app-browser-visible-commit.js.map +1 -1
  95. package/dist/server/app-elements-wire.d.ts +10 -5
  96. package/dist/server/app-elements-wire.js +84 -2
  97. package/dist/server/app-elements-wire.js.map +1 -1
  98. package/dist/server/app-elements.d.ts +3 -2
  99. package/dist/server/app-elements.js +3 -2
  100. package/dist/server/app-elements.js.map +1 -1
  101. package/dist/server/app-fallback-renderer.js +5 -3
  102. package/dist/server/app-fallback-renderer.js.map +1 -1
  103. package/dist/server/app-middleware.d.ts +13 -0
  104. package/dist/server/app-middleware.js +3 -1
  105. package/dist/server/app-middleware.js.map +1 -1
  106. package/dist/server/app-optimistic-routing.d.ts +54 -0
  107. package/dist/server/app-optimistic-routing.js +200 -0
  108. package/dist/server/app-optimistic-routing.js.map +1 -0
  109. package/dist/server/app-page-cache.d.ts +13 -1
  110. package/dist/server/app-page-cache.js +61 -6
  111. package/dist/server/app-page-cache.js.map +1 -1
  112. package/dist/server/app-page-dispatch.d.ts +2 -0
  113. package/dist/server/app-page-dispatch.js +28 -1
  114. package/dist/server/app-page-dispatch.js.map +1 -1
  115. package/dist/server/app-page-element-builder.js +2 -1
  116. package/dist/server/app-page-element-builder.js.map +1 -1
  117. package/dist/server/app-page-execution.d.ts +28 -1
  118. package/dist/server/app-page-execution.js +89 -4
  119. package/dist/server/app-page-execution.js.map +1 -1
  120. package/dist/server/app-page-head.js +21 -2
  121. package/dist/server/app-page-head.js.map +1 -1
  122. package/dist/server/app-page-probe.js +1 -1
  123. package/dist/server/app-page-render.d.ts +2 -0
  124. package/dist/server/app-page-render.js +2 -1
  125. package/dist/server/app-page-render.js.map +1 -1
  126. package/dist/server/app-page-response.js +4 -3
  127. package/dist/server/app-page-response.js.map +1 -1
  128. package/dist/server/app-page-route-wiring.js +17 -10
  129. package/dist/server/app-page-route-wiring.js.map +1 -1
  130. package/dist/server/app-page-stream.d.ts +3 -0
  131. package/dist/server/app-page-stream.js +1 -0
  132. package/dist/server/app-page-stream.js.map +1 -1
  133. package/dist/server/app-prerender-static-params.d.ts +2 -1
  134. package/dist/server/app-prerender-static-params.js +44 -8
  135. package/dist/server/app-prerender-static-params.js.map +1 -1
  136. package/dist/server/app-route-handler-cache.d.ts +2 -2
  137. package/dist/server/app-route-handler-cache.js +3 -2
  138. package/dist/server/app-route-handler-cache.js.map +1 -1
  139. package/dist/server/app-route-handler-dispatch.d.ts +6 -1
  140. package/dist/server/app-route-handler-dispatch.js +1 -1
  141. package/dist/server/app-route-handler-dispatch.js.map +1 -1
  142. package/dist/server/app-route-handler-execution.d.ts +17 -2
  143. package/dist/server/app-route-handler-execution.js.map +1 -1
  144. package/dist/server/app-route-handler-response.js +5 -4
  145. package/dist/server/app-route-handler-response.js.map +1 -1
  146. package/dist/server/app-router-entry.js +6 -2
  147. package/dist/server/app-router-entry.js.map +1 -1
  148. package/dist/server/app-rsc-handler.d.ts +9 -1
  149. package/dist/server/app-rsc-handler.js +32 -14
  150. package/dist/server/app-rsc-handler.js.map +1 -1
  151. package/dist/server/app-rsc-render-mode.d.ts +4 -3
  152. package/dist/server/app-rsc-render-mode.js +7 -1
  153. package/dist/server/app-rsc-render-mode.js.map +1 -1
  154. package/dist/server/app-rsc-request-normalization.d.ts +4 -1
  155. package/dist/server/app-rsc-request-normalization.js +4 -1
  156. package/dist/server/app-rsc-request-normalization.js.map +1 -1
  157. package/dist/server/app-rsc-response-finalizer.d.ts +8 -1
  158. package/dist/server/app-rsc-response-finalizer.js +10 -3
  159. package/dist/server/app-rsc-response-finalizer.js.map +1 -1
  160. package/dist/server/app-rsc-route-matching.js +2 -2
  161. package/dist/server/app-rsc-route-matching.js.map +1 -1
  162. package/dist/server/app-server-action-execution.js +1 -1
  163. package/dist/server/app-ssr-entry.d.ts +2 -0
  164. package/dist/server/app-ssr-entry.js +56 -55
  165. package/dist/server/app-ssr-entry.js.map +1 -1
  166. package/dist/server/app-ssr-stream.d.ts +6 -1
  167. package/dist/server/app-ssr-stream.js +17 -3
  168. package/dist/server/app-ssr-stream.js.map +1 -1
  169. package/dist/server/artifact-compatibility.d.ts +1 -1
  170. package/dist/server/artifact-compatibility.js.map +1 -1
  171. package/dist/server/cache-headers.d.ts +7 -0
  172. package/dist/server/cache-headers.js +19 -0
  173. package/dist/server/cache-headers.js.map +1 -0
  174. package/dist/server/cache-proof.d.ts +49 -3
  175. package/dist/server/cache-proof.js +78 -22
  176. package/dist/server/cache-proof.js.map +1 -1
  177. package/dist/server/client-reuse-manifest.d.ts +99 -0
  178. package/dist/server/client-reuse-manifest.js +212 -0
  179. package/dist/server/client-reuse-manifest.js.map +1 -0
  180. package/dist/server/default-global-error-module.d.ts +20 -0
  181. package/dist/server/default-global-error-module.js +20 -0
  182. package/dist/server/default-global-error-module.js.map +1 -0
  183. package/dist/server/dev-server.d.ts +9 -1
  184. package/dist/server/dev-server.js +76 -29
  185. package/dist/server/dev-server.js.map +1 -1
  186. package/dist/server/edge-api-runtime.d.ts +5 -0
  187. package/dist/server/edge-api-runtime.js +8 -0
  188. package/dist/server/edge-api-runtime.js.map +1 -0
  189. package/dist/server/headers.d.ts +18 -1
  190. package/dist/server/headers.js +18 -1
  191. package/dist/server/headers.js.map +1 -1
  192. package/dist/server/http-error-responses.d.ts +16 -1
  193. package/dist/server/http-error-responses.js +21 -1
  194. package/dist/server/http-error-responses.js.map +1 -1
  195. package/dist/server/isr-cache.d.ts +6 -2
  196. package/dist/server/isr-cache.js +20 -4
  197. package/dist/server/isr-cache.js.map +1 -1
  198. package/dist/server/middleware-runtime.d.ts +15 -0
  199. package/dist/server/middleware-runtime.js +59 -7
  200. package/dist/server/middleware-runtime.js.map +1 -1
  201. package/dist/server/middleware.d.ts +1 -1
  202. package/dist/server/middleware.js +4 -2
  203. package/dist/server/middleware.js.map +1 -1
  204. package/dist/server/navigation-planner.d.ts +9 -3
  205. package/dist/server/navigation-planner.js +98 -25
  206. package/dist/server/navigation-planner.js.map +1 -1
  207. package/dist/server/navigation-trace.d.ts +2 -1
  208. package/dist/server/navigation-trace.js +1 -0
  209. package/dist/server/navigation-trace.js.map +1 -1
  210. package/dist/server/pages-api-route.d.ts +27 -1
  211. package/dist/server/pages-api-route.js +24 -3
  212. package/dist/server/pages-api-route.js.map +1 -1
  213. package/dist/server/pages-data-route.d.ts +77 -0
  214. package/dist/server/pages-data-route.js +97 -0
  215. package/dist/server/pages-data-route.js.map +1 -0
  216. package/dist/server/pages-i18n.d.ts +51 -1
  217. package/dist/server/pages-i18n.js +61 -1
  218. package/dist/server/pages-i18n.js.map +1 -1
  219. package/dist/server/pages-page-data.d.ts +29 -2
  220. package/dist/server/pages-page-data.js +31 -17
  221. package/dist/server/pages-page-data.js.map +1 -1
  222. package/dist/server/pages-page-response.d.ts +11 -1
  223. package/dist/server/pages-page-response.js +5 -3
  224. package/dist/server/pages-page-response.js.map +1 -1
  225. package/dist/server/prod-server.d.ts +13 -15
  226. package/dist/server/prod-server.js +109 -56
  227. package/dist/server/prod-server.js.map +1 -1
  228. package/dist/server/request-pipeline.d.ts +11 -2
  229. package/dist/server/request-pipeline.js +28 -11
  230. package/dist/server/request-pipeline.js.map +1 -1
  231. package/dist/server/seed-cache.d.ts +12 -31
  232. package/dist/server/seed-cache.js +22 -35
  233. package/dist/server/seed-cache.js.map +1 -1
  234. package/dist/server/server-action-not-found.js +8 -3
  235. package/dist/server/server-action-not-found.js.map +1 -1
  236. package/dist/server/skip-cache-proof.d.ts +41 -0
  237. package/dist/server/skip-cache-proof.js +101 -0
  238. package/dist/server/skip-cache-proof.js.map +1 -0
  239. package/dist/server/static-file-cache.d.ts +1 -1
  240. package/dist/server/static-file-cache.js +7 -6
  241. package/dist/server/static-file-cache.js.map +1 -1
  242. package/dist/shims/client-locale.d.ts +15 -0
  243. package/dist/shims/client-locale.js +13 -0
  244. package/dist/shims/client-locale.js.map +1 -0
  245. package/dist/shims/default-global-error.d.ts +32 -0
  246. package/dist/shims/default-global-error.js +181 -0
  247. package/dist/shims/default-global-error.js.map +1 -0
  248. package/dist/shims/document.d.ts +59 -3
  249. package/dist/shims/document.js +36 -5
  250. package/dist/shims/document.js.map +1 -1
  251. package/dist/shims/error-boundary.d.ts +2 -2
  252. package/dist/shims/form.js +13 -6
  253. package/dist/shims/form.js.map +1 -1
  254. package/dist/shims/link.d.ts +21 -3
  255. package/dist/shims/link.js +131 -22
  256. package/dist/shims/link.js.map +1 -1
  257. package/dist/shims/metadata.js +4 -4
  258. package/dist/shims/metadata.js.map +1 -1
  259. package/dist/shims/navigation.d.ts +8 -2
  260. package/dist/shims/navigation.js +36 -15
  261. package/dist/shims/navigation.js.map +1 -1
  262. package/dist/shims/og.d.ts +18 -2
  263. package/dist/shims/og.js +49 -1
  264. package/dist/shims/og.js.map +1 -0
  265. package/dist/shims/request-state-types.d.ts +1 -1
  266. package/dist/shims/root-params.d.ts +3 -1
  267. package/dist/shims/root-params.js +11 -3
  268. package/dist/shims/root-params.js.map +1 -1
  269. package/dist/shims/router-state.d.ts +1 -0
  270. package/dist/shims/router-state.js.map +1 -1
  271. package/dist/shims/router.d.ts +12 -5
  272. package/dist/shims/router.js +172 -22
  273. package/dist/shims/router.js.map +1 -1
  274. package/dist/shims/server.d.ts +21 -4
  275. package/dist/shims/server.js +29 -9
  276. package/dist/shims/server.js.map +1 -1
  277. package/dist/shims/slot.js +5 -1
  278. package/dist/shims/slot.js.map +1 -1
  279. package/dist/shims/unified-request-context.d.ts +1 -1
  280. package/dist/shims/url-safety.d.ts +23 -1
  281. package/dist/shims/url-safety.js +29 -2
  282. package/dist/shims/url-safety.js.map +1 -1
  283. package/dist/typegen.d.ts +10 -0
  284. package/dist/typegen.js +242 -0
  285. package/dist/typegen.js.map +1 -0
  286. package/dist/utils/asset-prefix.d.ts +33 -5
  287. package/dist/utils/asset-prefix.js +39 -6
  288. package/dist/utils/asset-prefix.js.map +1 -1
  289. package/dist/utils/cache-control-metadata.d.ts +2 -1
  290. package/dist/utils/cache-control-metadata.js +1 -3
  291. package/dist/utils/cache-control-metadata.js.map +1 -1
  292. package/dist/utils/domain-locale.d.ts +2 -1
  293. package/dist/utils/domain-locale.js +9 -1
  294. package/dist/utils/domain-locale.js.map +1 -1
  295. package/dist/utils/lazy-chunks.d.ts +1 -1
  296. package/dist/utils/lazy-chunks.js +1 -1
  297. package/dist/utils/lazy-chunks.js.map +1 -1
  298. package/dist/utils/prerender-output-paths.d.ts +15 -0
  299. package/dist/utils/prerender-output-paths.js +24 -0
  300. package/dist/utils/prerender-output-paths.js.map +1 -0
  301. package/dist/utils/query.d.ts +17 -1
  302. package/dist/utils/query.js +36 -1
  303. package/dist/utils/query.js.map +1 -1
  304. package/dist/utils/record.d.ts +5 -0
  305. package/dist/utils/record.js +8 -0
  306. package/dist/utils/record.js.map +1 -0
  307. package/package.json +11 -3
@@ -0,0 +1,242 @@
1
+ import { decodeRouteSegment } from "./routing/utils.js";
2
+ import { patternToNextFormat } from "./routing/route-validation.js";
3
+ import { isInvisibleSegment } from "./routing/app-route-graph.js";
4
+ import { appRouteGraph } from "./routing/app-router.js";
5
+ import path from "node:path";
6
+ import fs from "node:fs/promises";
7
+ //#region src/typegen.ts
8
+ const NEXT_ENV_FILE_CONTENT = `/// <reference types="next" />
9
+ /// <reference types="next/image-types/global" />
10
+ import "./.next/types/routes.d.ts";
11
+
12
+ // NOTE: This file should not be edited
13
+ // see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
14
+ `;
15
+ async function generateRouteTypes(options) {
16
+ const root = path.resolve(options.root);
17
+ const appDir = options.appDir ? path.resolve(options.appDir) : await findAppDir(root);
18
+ const outPath = path.join(root, ".next", "types", "routes.d.ts");
19
+ const content = appDir ? renderRouteTypes(await collectRouteTypeModel(appDir, options.pageExtensions)) : renderRouteTypes(emptyRouteTypeModel());
20
+ await fs.mkdir(path.dirname(outPath), { recursive: true });
21
+ await fs.writeFile(outPath, content, "utf-8");
22
+ await ensureNextEnvFile(root);
23
+ return outPath;
24
+ }
25
+ async function ensureNextEnvFile(root) {
26
+ const envPath = path.join(root, "next-env.d.ts");
27
+ try {
28
+ await fs.writeFile(envPath, NEXT_ENV_FILE_CONTENT, {
29
+ encoding: "utf-8",
30
+ flag: "wx"
31
+ });
32
+ } catch (error) {
33
+ if (error.code !== "EEXIST") throw error;
34
+ }
35
+ }
36
+ function emptyRouteTypeModel() {
37
+ return {
38
+ pageRoutes: [],
39
+ layoutRoutes: [],
40
+ routeHandlerRoutes: [],
41
+ params: /* @__PURE__ */ new Map(),
42
+ layoutSlots: /* @__PURE__ */ new Map()
43
+ };
44
+ }
45
+ async function collectRouteTypeModel(appDir, pageExtensions) {
46
+ const graph = await appRouteGraph(appDir, pageExtensions);
47
+ const model = emptyRouteTypeModel();
48
+ const segmentGraph = graph.routeManifest.segmentGraph;
49
+ const layoutRouteKeys = createLayoutRouteKeyMap(segmentGraph.layouts.values());
50
+ const pageRouteSet = /* @__PURE__ */ new Set();
51
+ const layoutRouteSet = /* @__PURE__ */ new Set();
52
+ const routeHandlerRouteSet = /* @__PURE__ */ new Set();
53
+ for (const route of segmentGraph.pages.values()) {
54
+ const routeEntry = segmentGraph.routes.get(route.routeId);
55
+ addRoute(model.pageRoutes, pageRouteSet, model.params, patternToNextFormat(route.pattern), paramsForPatternParts(routeEntry?.patternParts ?? []));
56
+ }
57
+ for (const route of segmentGraph.routeHandlers.values()) {
58
+ const routeEntry = segmentGraph.routes.get(route.routeId);
59
+ addRoute(model.routeHandlerRoutes, routeHandlerRouteSet, model.params, patternToNextFormat(route.pattern), paramsForPatternParts(routeEntry?.patternParts ?? []));
60
+ }
61
+ for (const layout of segmentGraph.layouts.values()) {
62
+ const route = layoutRouteKeys.get(layout.treePath) ?? treePathToRouteLiteral(layout.treePath);
63
+ addRoute(model.layoutRoutes, layoutRouteSet, model.params, route, paramsForPatternParts(layout.patternParts));
64
+ }
65
+ const layoutSlotSets = /* @__PURE__ */ new Map();
66
+ for (const slot of segmentGraph.slots.values()) {
67
+ const layoutRoute = layoutRouteKeyForSlot(slot, segmentGraph.layouts, layoutRouteKeys);
68
+ if (!layoutRoute) continue;
69
+ let slotNames = layoutSlotSets.get(layoutRoute);
70
+ if (!slotNames) {
71
+ slotNames = /* @__PURE__ */ new Set();
72
+ layoutSlotSets.set(layoutRoute, slotNames);
73
+ model.layoutSlots.set(layoutRoute, []);
74
+ }
75
+ if (!slotNames.has(slot.name)) {
76
+ slotNames.add(slot.name);
77
+ model.layoutSlots.get(layoutRoute)?.push(slot.name);
78
+ }
79
+ }
80
+ model.pageRoutes.sort(compareStrings);
81
+ model.layoutRoutes.sort(compareStrings);
82
+ model.routeHandlerRoutes.sort(compareStrings);
83
+ for (const slotNames of model.layoutSlots.values()) slotNames.sort(compareStrings);
84
+ return model;
85
+ }
86
+ async function findAppDir(root) {
87
+ for (const rel of ["app", path.join("src", "app")]) {
88
+ const candidate = path.join(root, rel);
89
+ try {
90
+ if ((await fs.stat(candidate)).isDirectory()) return candidate;
91
+ } catch {}
92
+ }
93
+ return null;
94
+ }
95
+ function renderRouteTypes(model) {
96
+ const allRoutes = uniqueSorted([
97
+ ...model.pageRoutes,
98
+ ...model.layoutRoutes,
99
+ ...model.routeHandlerRoutes
100
+ ]);
101
+ return `// This file is generated by vinext. Do not edit.
102
+ import type * as React from "react";
103
+
104
+ declare global {
105
+ type PageProps<Route extends VinextRouteTypes.PageRoute = VinextRouteTypes.PageRoute> = {
106
+ params: Promise<VinextRouteTypes.ParamMap[Route]>;
107
+ searchParams: Promise<Record<string, string | string[] | undefined>>;
108
+ };
109
+
110
+ type LayoutProps<Route extends VinextRouteTypes.LayoutRoute> = {
111
+ params: Promise<VinextRouteTypes.ParamMap[Route]>;
112
+ children: React.ReactNode;
113
+ } & {
114
+ [K in VinextRouteTypes.LayoutSlotMap[Route]]: React.ReactNode;
115
+ };
116
+
117
+ type RouteContext<Route extends VinextRouteTypes.RouteHandlerRoute = VinextRouteTypes.RouteHandlerRoute> = {
118
+ params: Promise<VinextRouteTypes.ParamMap[Route]>;
119
+ };
120
+ }
121
+
122
+ declare namespace VinextRouteTypes {
123
+ type PageRoute = ${routeUnion(model.pageRoutes)};
124
+ type LayoutRoute = ${routeUnion(model.layoutRoutes)};
125
+ type RouteHandlerRoute = ${routeUnion(model.routeHandlerRoutes)};
126
+ type AppRoute = ${routeUnion(allRoutes)};
127
+
128
+ interface ParamMap {
129
+ ${renderParamMap(allRoutes, model.params)}
130
+ }
131
+
132
+ interface LayoutSlotMap {
133
+ ${renderLayoutSlotMap(model.layoutRoutes, model.layoutSlots)}
134
+ }
135
+ }
136
+
137
+ export {};
138
+ `;
139
+ }
140
+ function renderParamMap(routes, params) {
141
+ if (routes.length === 0) return " [route: string]: {};\n";
142
+ return routes.map((route) => ` ${quote(route)}: ${renderParamShape(params.get(route) ?? /* @__PURE__ */ new Map())};`).join("\n");
143
+ }
144
+ function renderParamShape(params) {
145
+ if (params.size === 0) return "{}";
146
+ return `{ ${Array.from(params.entries()).sort(([left], [right]) => compareStrings(left, right)).map(([name, kind]) => {
147
+ const optional = kind === "string[]?";
148
+ const valueType = optional ? "string[]" : kind;
149
+ return `${propertyName(name)}${optional ? "?" : ""}: ${valueType};`;
150
+ }).join(" ")} }`;
151
+ }
152
+ function renderLayoutSlotMap(layoutRoutes, layoutSlots) {
153
+ if (layoutRoutes.length === 0) return " [route: string]: never;\n";
154
+ return layoutRoutes.map((route) => {
155
+ const slots = layoutSlots.get(route) ?? [];
156
+ return ` ${quote(route)}: ${routeUnion(slots)};`;
157
+ }).join("\n");
158
+ }
159
+ function paramsForPatternParts(patternParts) {
160
+ const params = /* @__PURE__ */ new Map();
161
+ for (const part of patternParts) {
162
+ if (!part.startsWith(":")) continue;
163
+ if (part.endsWith("+")) params.set(part.slice(1, -1), "string[]");
164
+ else if (part.endsWith("*")) params.set(part.slice(1, -1), "string[]?");
165
+ else params.set(part.slice(1), "string");
166
+ }
167
+ return params;
168
+ }
169
+ function createLayoutRouteKeyMap(layouts) {
170
+ const treePathsByRoute = /* @__PURE__ */ new Map();
171
+ for (const { treePath } of layouts) {
172
+ const route = treePathToRouteLiteral(treePath);
173
+ const treePaths = treePathsByRoute.get(route) ?? [];
174
+ treePaths.push(treePath);
175
+ treePathsByRoute.set(route, treePaths);
176
+ }
177
+ const keys = /* @__PURE__ */ new Map();
178
+ for (const [route, treePaths] of treePathsByRoute) for (const treePath of treePaths) keys.set(treePath, treePaths.length === 1 ? route : treePathToScopedLayoutRouteLiteral(treePath));
179
+ return keys;
180
+ }
181
+ function layoutRouteKeyForSlot(slot, layouts, layoutRouteKeys) {
182
+ if (!slot.ownerLayoutId) return null;
183
+ const layout = layouts.get(slot.ownerLayoutId);
184
+ if (!layout) throw new Error(`[vinext] App route graph invariant violated: slot ${slot.id} references missing owner layout ${slot.ownerLayoutId}`);
185
+ return layoutRouteKeys.get(layout.treePath) ?? treePathToRouteLiteral(layout.treePath);
186
+ }
187
+ /** Convert a layout tree path to its URL route literal, stripping invisible segments. */
188
+ function treePathToRouteLiteral(treePath) {
189
+ if (treePath === "/") return "/";
190
+ const segments = treePath.split("/").filter(Boolean).filter((segment) => !isInvisibleSegment(segment)).map((segment) => decodeRouteSegment(segment));
191
+ return segments.length === 0 ? "/" : `/${segments.join("/")}`;
192
+ }
193
+ /**
194
+ * Convert a layout tree path to a scoped route literal that preserves
195
+ * route-group and `@slot` segments. Used only as a fallback key when multiple
196
+ * layouts collapse to the same URL route literal, so consumers can keep their
197
+ * slot/params typings distinct.
198
+ */
199
+ function treePathToScopedLayoutRouteLiteral(treePath) {
200
+ if (treePath === "/") return "/";
201
+ const segments = treePath.split("/").filter(Boolean).filter((segment) => segment !== ".").map((segment) => decodeRouteSegment(segment));
202
+ return segments.length === 0 ? "/" : `/${segments.join("/")}`;
203
+ }
204
+ function addRoute(routes, seen, params, route, paramShape) {
205
+ if (!seen.has(route)) {
206
+ seen.add(route);
207
+ routes.push(route);
208
+ }
209
+ const existingParamShape = params.get(route);
210
+ if (existingParamShape) {
211
+ if (!paramShapesEqual(existingParamShape, paramShape)) throw new Error(`[vinext] Conflicting route param shapes generated for ${route}`);
212
+ return;
213
+ }
214
+ params.set(route, paramShape);
215
+ }
216
+ function paramShapesEqual(left, right) {
217
+ if (left.size !== right.size) return false;
218
+ for (const [name, kind] of left) if (right.get(name) !== kind) return false;
219
+ return true;
220
+ }
221
+ function uniqueSorted(values) {
222
+ return Array.from(new Set(values)).sort(compareStrings);
223
+ }
224
+ function routeUnion(routes) {
225
+ if (routes.length === 0) return "never";
226
+ return routes.map(quote).join(" | ");
227
+ }
228
+ function propertyName(name) {
229
+ return /^[A-Za-z_$][\w$]*$/.test(name) ? name : quote(name);
230
+ }
231
+ function quote(value) {
232
+ return JSON.stringify(value);
233
+ }
234
+ function compareStrings(left, right) {
235
+ if (left < right) return -1;
236
+ if (left > right) return 1;
237
+ return 0;
238
+ }
239
+ //#endregion
240
+ export { generateRouteTypes };
241
+
242
+ //# sourceMappingURL=typegen.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"typegen.js","names":[],"sources":["../src/typegen.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { isInvisibleSegment } from \"./routing/app-route-graph.js\";\nimport { appRouteGraph } from \"./routing/app-router.js\";\nimport { patternToNextFormat } from \"./routing/route-validation.js\";\nimport { decodeRouteSegment } from \"./routing/utils.js\";\n\ntype GenerateRouteTypesOptions = {\n root: string;\n appDir?: string | null;\n pageExtensions?: readonly string[];\n};\n\ntype ParamShape = Map<string, \"string\" | \"string[]\" | \"string[]?\">;\n\nconst NEXT_ENV_FILE_CONTENT = `/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\nimport \"./.next/types/routes.d.ts\";\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.\n`;\n\nexport async function generateRouteTypes(options: GenerateRouteTypesOptions): Promise<string> {\n const root = path.resolve(options.root);\n const appDir = options.appDir ? path.resolve(options.appDir) : await findAppDir(root);\n const outPath = path.join(root, \".next\", \"types\", \"routes.d.ts\");\n\n const content = appDir\n ? renderRouteTypes(await collectRouteTypeModel(appDir, options.pageExtensions))\n : renderRouteTypes(emptyRouteTypeModel());\n\n await fs.mkdir(path.dirname(outPath), { recursive: true });\n await fs.writeFile(outPath, content, \"utf-8\");\n await ensureNextEnvFile(root);\n return outPath;\n}\n\nasync function ensureNextEnvFile(root: string): Promise<void> {\n const envPath = path.join(root, \"next-env.d.ts\");\n try {\n await fs.writeFile(envPath, NEXT_ENV_FILE_CONTENT, { encoding: \"utf-8\", flag: \"wx\" });\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== \"EEXIST\") throw error;\n }\n}\n\ntype RouteTypeModel = {\n pageRoutes: string[];\n layoutRoutes: string[];\n routeHandlerRoutes: string[];\n params: Map<string, ParamShape>;\n layoutSlots: Map<string, string[]>;\n};\n\nfunction emptyRouteTypeModel(): RouteTypeModel {\n return {\n pageRoutes: [],\n layoutRoutes: [],\n routeHandlerRoutes: [],\n params: new Map(),\n layoutSlots: new Map(),\n };\n}\n\nasync function collectRouteTypeModel(\n appDir: string,\n pageExtensions?: readonly string[],\n): Promise<RouteTypeModel> {\n const graph = await appRouteGraph(appDir, pageExtensions);\n const model = emptyRouteTypeModel();\n const segmentGraph = graph.routeManifest.segmentGraph;\n const layoutRouteKeys = createLayoutRouteKeyMap(segmentGraph.layouts.values());\n const pageRouteSet = new Set<string>();\n const layoutRouteSet = new Set<string>();\n const routeHandlerRouteSet = new Set<string>();\n\n for (const route of segmentGraph.pages.values()) {\n const routeEntry = segmentGraph.routes.get(route.routeId);\n addRoute(\n model.pageRoutes,\n pageRouteSet,\n model.params,\n patternToNextFormat(route.pattern),\n paramsForPatternParts(routeEntry?.patternParts ?? []),\n );\n }\n\n for (const route of segmentGraph.routeHandlers.values()) {\n const routeEntry = segmentGraph.routes.get(route.routeId);\n addRoute(\n model.routeHandlerRoutes,\n routeHandlerRouteSet,\n model.params,\n patternToNextFormat(route.pattern),\n paramsForPatternParts(routeEntry?.patternParts ?? []),\n );\n }\n\n for (const layout of segmentGraph.layouts.values()) {\n const route = layoutRouteKeys.get(layout.treePath) ?? treePathToRouteLiteral(layout.treePath);\n addRoute(\n model.layoutRoutes,\n layoutRouteSet,\n model.params,\n route,\n paramsForPatternParts(layout.patternParts),\n );\n }\n\n const layoutSlotSets = new Map<string, Set<string>>();\n for (const slot of segmentGraph.slots.values()) {\n const layoutRoute = layoutRouteKeyForSlot(slot, segmentGraph.layouts, layoutRouteKeys);\n if (!layoutRoute) continue;\n\n let slotNames = layoutSlotSets.get(layoutRoute);\n if (!slotNames) {\n slotNames = new Set();\n layoutSlotSets.set(layoutRoute, slotNames);\n model.layoutSlots.set(layoutRoute, []);\n }\n if (!slotNames.has(slot.name)) {\n slotNames.add(slot.name);\n model.layoutSlots.get(layoutRoute)?.push(slot.name);\n }\n }\n\n // Sort all collected route lists once after collection. addRoute() and the\n // slot loop above intentionally skip per-insertion sorts to keep collection\n // O(n) — the rendered output relies on stable sorted order, so the single\n // pass here is enough.\n model.pageRoutes.sort(compareStrings);\n model.layoutRoutes.sort(compareStrings);\n model.routeHandlerRoutes.sort(compareStrings);\n for (const slotNames of model.layoutSlots.values()) slotNames.sort(compareStrings);\n\n return model;\n}\n\nasync function findAppDir(root: string): Promise<string | null> {\n for (const rel of [\"app\", path.join(\"src\", \"app\")]) {\n const candidate = path.join(root, rel);\n try {\n const stat = await fs.stat(candidate);\n if (stat.isDirectory()) return candidate;\n } catch {\n // Try the next conventional app directory.\n }\n }\n return null;\n}\n\nfunction renderRouteTypes(model: RouteTypeModel): string {\n const allRoutes = uniqueSorted([\n ...model.pageRoutes,\n ...model.layoutRoutes,\n ...model.routeHandlerRoutes,\n ]);\n\n return `// This file is generated by vinext. Do not edit.\nimport type * as React from \"react\";\n\ndeclare global {\n type PageProps<Route extends VinextRouteTypes.PageRoute = VinextRouteTypes.PageRoute> = {\n params: Promise<VinextRouteTypes.ParamMap[Route]>;\n searchParams: Promise<Record<string, string | string[] | undefined>>;\n };\n\n type LayoutProps<Route extends VinextRouteTypes.LayoutRoute> = {\n params: Promise<VinextRouteTypes.ParamMap[Route]>;\n children: React.ReactNode;\n } & {\n [K in VinextRouteTypes.LayoutSlotMap[Route]]: React.ReactNode;\n };\n\n type RouteContext<Route extends VinextRouteTypes.RouteHandlerRoute = VinextRouteTypes.RouteHandlerRoute> = {\n params: Promise<VinextRouteTypes.ParamMap[Route]>;\n };\n}\n\ndeclare namespace VinextRouteTypes {\n type PageRoute = ${routeUnion(model.pageRoutes)};\n type LayoutRoute = ${routeUnion(model.layoutRoutes)};\n type RouteHandlerRoute = ${routeUnion(model.routeHandlerRoutes)};\n type AppRoute = ${routeUnion(allRoutes)};\n\n interface ParamMap {\n${renderParamMap(allRoutes, model.params)}\n }\n\n interface LayoutSlotMap {\n${renderLayoutSlotMap(model.layoutRoutes, model.layoutSlots)}\n }\n}\n\nexport {};\n`;\n}\n\nfunction renderParamMap(\n routes: readonly string[],\n params: ReadonlyMap<string, ParamShape>,\n): string {\n if (routes.length === 0) return \" [route: string]: {};\\n\";\n\n return routes\n .map((route) => ` ${quote(route)}: ${renderParamShape(params.get(route) ?? new Map())};`)\n .join(\"\\n\");\n}\n\nfunction renderParamShape(params: ParamShape): string {\n if (params.size === 0) return \"{}\";\n\n const fields = Array.from(params.entries())\n .sort(([left], [right]) => compareStrings(left, right))\n .map(([name, kind]) => {\n const optional = kind === \"string[]?\";\n const valueType = optional ? \"string[]\" : kind;\n return `${propertyName(name)}${optional ? \"?\" : \"\"}: ${valueType};`;\n });\n\n return `{ ${fields.join(\" \")} }`;\n}\n\nfunction renderLayoutSlotMap(\n layoutRoutes: readonly string[],\n layoutSlots: ReadonlyMap<string, readonly string[]>,\n): string {\n if (layoutRoutes.length === 0) return \" [route: string]: never;\\n\";\n\n return layoutRoutes\n .map((route) => {\n const slots = layoutSlots.get(route) ?? [];\n return ` ${quote(route)}: ${routeUnion(slots)};`;\n })\n .join(\"\\n\");\n}\n\nfunction paramsForPatternParts(patternParts: readonly string[]): ParamShape {\n const params: ParamShape = new Map();\n for (const part of patternParts) {\n if (!part.startsWith(\":\")) continue;\n\n if (part.endsWith(\"+\")) {\n params.set(part.slice(1, -1), \"string[]\");\n } else if (part.endsWith(\"*\")) {\n params.set(part.slice(1, -1), \"string[]?\");\n } else {\n params.set(part.slice(1), \"string\");\n }\n }\n return params;\n}\n\nfunction createLayoutRouteKeyMap(layouts: Iterable<{ treePath: string }>): Map<string, string> {\n const treePathsByRoute = new Map<string, string[]>();\n for (const { treePath } of layouts) {\n const route = treePathToRouteLiteral(treePath);\n const treePaths = treePathsByRoute.get(route) ?? [];\n treePaths.push(treePath);\n treePathsByRoute.set(route, treePaths);\n }\n\n const keys = new Map<string, string>();\n for (const [route, treePaths] of treePathsByRoute) {\n for (const treePath of treePaths) {\n keys.set(\n treePath,\n treePaths.length === 1 ? route : treePathToScopedLayoutRouteLiteral(treePath),\n );\n }\n }\n return keys;\n}\n\nfunction layoutRouteKeyForSlot(\n slot: { id: string; ownerLayoutId: string | null },\n layouts: ReadonlyMap<string, { treePath: string }>,\n layoutRouteKeys: ReadonlyMap<string, string>,\n): string | null {\n if (!slot.ownerLayoutId) return null;\n\n const layout = layouts.get(slot.ownerLayoutId);\n if (!layout) {\n throw new Error(\n `[vinext] App route graph invariant violated: slot ${slot.id} references missing owner layout ${slot.ownerLayoutId}`,\n );\n }\n\n return layoutRouteKeys.get(layout.treePath) ?? treePathToRouteLiteral(layout.treePath);\n}\n\n/** Convert a layout tree path to its URL route literal, stripping invisible segments. */\nfunction treePathToRouteLiteral(treePath: string): string {\n if (treePath === \"/\") return \"/\";\n\n const segments = treePath\n .split(\"/\")\n .filter(Boolean)\n .filter((segment) => !isInvisibleSegment(segment))\n .map((segment) => decodeRouteSegment(segment));\n return segments.length === 0 ? \"/\" : `/${segments.join(\"/\")}`;\n}\n\n/**\n * Convert a layout tree path to a scoped route literal that preserves\n * route-group and `@slot` segments. Used only as a fallback key when multiple\n * layouts collapse to the same URL route literal, so consumers can keep their\n * slot/params typings distinct.\n */\nfunction treePathToScopedLayoutRouteLiteral(treePath: string): string {\n if (treePath === \"/\") return \"/\";\n\n const segments = treePath\n .split(\"/\")\n .filter(Boolean)\n .filter((segment) => segment !== \".\")\n .map((segment) => decodeRouteSegment(segment));\n return segments.length === 0 ? \"/\" : `/${segments.join(\"/\")}`;\n}\n\nfunction addRoute(\n routes: string[],\n seen: Set<string>,\n params: Map<string, ParamShape>,\n route: string,\n paramShape: ParamShape,\n): void {\n if (!seen.has(route)) {\n seen.add(route);\n routes.push(route);\n }\n const existingParamShape = params.get(route);\n if (existingParamShape) {\n if (!paramShapesEqual(existingParamShape, paramShape)) {\n throw new Error(`[vinext] Conflicting route param shapes generated for ${route}`);\n }\n return;\n }\n params.set(route, paramShape);\n}\n\nfunction paramShapesEqual(left: ParamShape, right: ParamShape): boolean {\n if (left.size !== right.size) return false;\n for (const [name, kind] of left) {\n if (right.get(name) !== kind) return false;\n }\n return true;\n}\n\nfunction uniqueSorted(values: readonly string[]): string[] {\n return Array.from(new Set(values)).sort(compareStrings);\n}\n\nfunction routeUnion(routes: readonly string[]): string {\n if (routes.length === 0) return \"never\";\n return routes.map(quote).join(\" | \");\n}\n\nfunction propertyName(name: string): string {\n return /^[A-Za-z_$][\\w$]*$/.test(name) ? name : quote(name);\n}\n\nfunction quote(value: string): string {\n return JSON.stringify(value);\n}\n\nfunction compareStrings(left: string, right: string): number {\n if (left < right) return -1;\n if (left > right) return 1;\n return 0;\n}\n"],"mappings":"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"}
@@ -6,8 +6,9 @@
6
6
  * static asset URL emitted in the page. It is distinct from `basePath`, which
7
7
  * affects route URLs.
8
8
  *
9
- * - A `assetPrefix` of `""` (empty) means "no prefix" behaviour matches the
10
- * historical default.
9
+ * - A `assetPrefix` of `""` (empty) means "no prefix". URLs are emitted as
10
+ * `/_next/static/...` (Next.js's canonical convention) and assets are
11
+ * written to `dist/client/_next/static/...` so the on-disk layout matches.
11
12
  * - A path prefix like `"/custom-asset-prefix"` is applied as a URL prefix
12
13
  * relative to the deployment origin. The prod server must also serve assets
13
14
  * under that prefix.
@@ -37,8 +38,14 @@ declare function isAbsoluteAssetPrefix(assetPrefix: string): boolean;
37
38
  * server) serves them directly without a runtime rewrite.
38
39
  * - Absolute URL (with or without path component): write to `_next/static/`.
39
40
  * Runtime serving is best-effort — the CDN is expected to serve these.
40
- * - Empty: keep the historical default of `assets/`, preserving disk layout
41
- * for projects that haven't opted into `assetPrefix`.
41
+ * - Empty: write to `_next/static/` so the on-disk layout matches the URL
42
+ * Next.js itself emits (and that the Next.js client runtime + upstream
43
+ * test suites assert against). This makes `assetPrefix` consistent in
44
+ * both the configured and unconfigured cases — the URL contract is
45
+ * always `/<prefix?>/_next/static/...`, and the on-disk layout mirrors
46
+ * it 1:1 so the static-file layer can serve hits and return
47
+ * `404 + "Not Found"` on misses without any URL-shape special-casing
48
+ * upstream of it.
42
49
  */
43
50
  declare function resolveAssetsDir(assetPrefix: string): string;
44
51
  /**
@@ -64,6 +71,27 @@ declare function resolveAssetUrlPrefix(assetPrefix: string): string;
64
71
  * requests that arrive at the deployment origin under the configured prefix.
65
72
  */
66
73
  declare function assetPrefixPathname(assetPrefix: string): string;
74
+ /**
75
+ * Whether the incoming request pathname targets the canonical `_next/static/`
76
+ * tree, after stripping any configured `basePath` and `assetPrefix` path
77
+ * component.
78
+ *
79
+ * Used by the Cloudflare worker entry to recognise asset-shaped requests
80
+ * that the ASSETS binding didn't serve, so they can short-circuit with a
81
+ * plain-text 404 instead of falling through to the RSC handler (which
82
+ * would render the full HTML 404 page). Mirrors Next.js's behaviour in
83
+ * `packages/next/src/server/lib/router-server.ts` where
84
+ * `realRequestPathname` is stripped of basePath/assetPrefix/i18n locale
85
+ * before the `startsWith('/_next/static/')` check.
86
+ *
87
+ * - `pathname`: incoming request pathname (with leading slash, no query).
88
+ * - `basePath`: configured `basePath` (e.g. `"/docs"`) or `""`.
89
+ * - `assetPathPrefix`: path component of `assetPrefix` (e.g. `"/cdn"`) or
90
+ * `""`. Use `assetPrefixPathname()` to derive this from a raw assetPrefix.
91
+ *
92
+ * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts
93
+ */
94
+ declare function isNextStaticPath(pathname: string, basePath: string, assetPathPrefix: string): boolean;
67
95
  //#endregion
68
- export { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, resolveAssetUrlPrefix, resolveAssetsDir };
96
+ export { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, isNextStaticPath, resolveAssetUrlPrefix, resolveAssetsDir };
69
97
  //# sourceMappingURL=asset-prefix.d.ts.map
@@ -6,8 +6,9 @@
6
6
  * static asset URL emitted in the page. It is distinct from `basePath`, which
7
7
  * affects route URLs.
8
8
  *
9
- * - A `assetPrefix` of `""` (empty) means "no prefix" behaviour matches the
10
- * historical default.
9
+ * - A `assetPrefix` of `""` (empty) means "no prefix". URLs are emitted as
10
+ * `/_next/static/...` (Next.js's canonical convention) and assets are
11
+ * written to `dist/client/_next/static/...` so the on-disk layout matches.
11
12
  * - A path prefix like `"/custom-asset-prefix"` is applied as a URL prefix
12
13
  * relative to the deployment origin. The prod server must also serve assets
13
14
  * under that prefix.
@@ -39,11 +40,17 @@ function isAbsoluteAssetPrefix(assetPrefix) {
39
40
  * server) serves them directly without a runtime rewrite.
40
41
  * - Absolute URL (with or without path component): write to `_next/static/`.
41
42
  * Runtime serving is best-effort — the CDN is expected to serve these.
42
- * - Empty: keep the historical default of `assets/`, preserving disk layout
43
- * for projects that haven't opted into `assetPrefix`.
43
+ * - Empty: write to `_next/static/` so the on-disk layout matches the URL
44
+ * Next.js itself emits (and that the Next.js client runtime + upstream
45
+ * test suites assert against). This makes `assetPrefix` consistent in
46
+ * both the configured and unconfigured cases — the URL contract is
47
+ * always `/<prefix?>/_next/static/...`, and the on-disk layout mirrors
48
+ * it 1:1 so the static-file layer can serve hits and return
49
+ * `404 + "Not Found"` on misses without any URL-shape special-casing
50
+ * upstream of it.
44
51
  */
45
52
  function resolveAssetsDir(assetPrefix) {
46
- if (!assetPrefix) return "assets";
53
+ if (!assetPrefix) return ASSET_PREFIX_URL_DIR;
47
54
  if (isAbsoluteAssetPrefix(assetPrefix)) return ASSET_PREFIX_URL_DIR;
48
55
  let stripped = assetPrefix;
49
56
  while (stripped.startsWith("/")) stripped = stripped.slice(1);
@@ -85,7 +92,33 @@ function assetPrefixPathname(assetPrefix) {
85
92
  return "";
86
93
  }
87
94
  }
95
+ /**
96
+ * Whether the incoming request pathname targets the canonical `_next/static/`
97
+ * tree, after stripping any configured `basePath` and `assetPrefix` path
98
+ * component.
99
+ *
100
+ * Used by the Cloudflare worker entry to recognise asset-shaped requests
101
+ * that the ASSETS binding didn't serve, so they can short-circuit with a
102
+ * plain-text 404 instead of falling through to the RSC handler (which
103
+ * would render the full HTML 404 page). Mirrors Next.js's behaviour in
104
+ * `packages/next/src/server/lib/router-server.ts` where
105
+ * `realRequestPathname` is stripped of basePath/assetPrefix/i18n locale
106
+ * before the `startsWith('/_next/static/')` check.
107
+ *
108
+ * - `pathname`: incoming request pathname (with leading slash, no query).
109
+ * - `basePath`: configured `basePath` (e.g. `"/docs"`) or `""`.
110
+ * - `assetPathPrefix`: path component of `assetPrefix` (e.g. `"/cdn"`) or
111
+ * `""`. Use `assetPrefixPathname()` to derive this from a raw assetPrefix.
112
+ *
113
+ * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts
114
+ */
115
+ function isNextStaticPath(pathname, basePath, assetPathPrefix) {
116
+ let p = pathname;
117
+ if (basePath && (p === basePath || p.startsWith(basePath + "/"))) p = p.slice(basePath.length) || "/";
118
+ if (assetPathPrefix && (p === assetPathPrefix || p.startsWith(assetPathPrefix + "/"))) p = p.slice(assetPathPrefix.length) || "/";
119
+ return p.startsWith(`/${ASSET_PREFIX_URL_DIR}/`);
120
+ }
88
121
  //#endregion
89
- export { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, resolveAssetUrlPrefix, resolveAssetsDir };
122
+ export { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, isNextStaticPath, resolveAssetUrlPrefix, resolveAssetsDir };
90
123
 
91
124
  //# sourceMappingURL=asset-prefix.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"asset-prefix.js","names":[],"sources":["../../src/utils/asset-prefix.ts"],"sourcesContent":["/**\n * Shared helpers for next.config `assetPrefix`.\n *\n * Mirrors Next.js semantics: `assetPrefix` is prepended to every JS/CSS/image/\n * static asset URL emitted in the page. It is distinct from `basePath`, which\n * affects route URLs.\n *\n * - A `assetPrefix` of `\"\"` (empty) means \"no prefix\" behaviour matches the\n * historical default.\n * - A path prefix like `\"/custom-asset-prefix\"` is applied as a URL prefix\n * relative to the deployment origin. The prod server must also serve assets\n * under that prefix.\n * - An absolute URL like `\"https://cdn.example.com\"` makes asset URLs fully\n * qualified. Runtime serving on the deployment origin is a no-op — the CDN\n * serves the assets directly.\n *\n * The path component of the asset URL after the prefix is always\n * `/_next/static/<filename>` to match Next.js's convention. This is the\n * convention upstream test suites assert against.\n *\n * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix\n */\n\n/**\n * Suffix appended to the assetPrefix (or used standalone when no prefix is\n * configured) to form the leading portion of every emitted asset URL.\n * Matches Next.js: assets always live under `/_next/static/`.\n */\nexport const ASSET_PREFIX_URL_DIR = \"_next/static\";\n\n/** Whether `assetPrefix` is an absolute URL (vs a path prefix). */\nexport function isAbsoluteAssetPrefix(assetPrefix: string): boolean {\n return /^https?:\\/\\//i.test(assetPrefix);\n}\n\n/**\n * Compute the on-disk `build.assetsDir` for the given `assetPrefix`.\n *\n * - Path prefix (`/cdn`): write to `cdn/_next/static/` so the on-disk layout\n * matches the URL — the Cloudflare ASSETS binding (and any static file\n * server) serves them directly without a runtime rewrite.\n * - Absolute URL (with or without path component): write to `_next/static/`.\n * Runtime serving is best-effort — the CDN is expected to serve these.\n * - Empty: keep the historical default of `assets/`, preserving disk layout\n * for projects that haven't opted into `assetPrefix`.\n */\nexport function resolveAssetsDir(assetPrefix: string): string {\n if (!assetPrefix) return \"assets\";\n if (isAbsoluteAssetPrefix(assetPrefix)) {\n // Files on disk land at `_next/static/...`. The absolute URL is applied\n // at URL-rendering time via renderBuiltUrl; on-disk path is irrelevant\n // to the CDN.\n return ASSET_PREFIX_URL_DIR;\n }\n // Path prefix — strip leading slash so the path joins cleanly with outDir.\n // Use an explicit loop instead of `replace(/^\\/+/, \"\")` so CodeQL doesn't\n // flag the regex as polynomial-time on uncontrolled input.\n let stripped = assetPrefix;\n while (stripped.startsWith(\"/\")) stripped = stripped.slice(1);\n return `${stripped}/${ASSET_PREFIX_URL_DIR}`;\n}\n\n/**\n * Build the URL prefix to apply to emitted asset URLs. Returns the full URL\n * prefix including the `_next/static/` directory, with a trailing slash.\n *\n * Examples:\n * - `\"\"` → `\"/_next/static/\"`\n * - `\"/cdn\"` → `\"/cdn/_next/static/\"`\n * - `\"https://cdn.example.com\"` → `\"https://cdn.example.com/_next/static/\"`\n * - `\"https://cdn.example.com/sub\"` → `\"https://cdn.example.com/sub/_next/static/\"`\n */\nexport function resolveAssetUrlPrefix(assetPrefix: string): string {\n if (!assetPrefix) return `/${ASSET_PREFIX_URL_DIR}/`;\n return `${assetPrefix}/${ASSET_PREFIX_URL_DIR}/`;\n}\n\n/**\n * Extract the path portion of `assetPrefix` for use in runtime URL matching.\n *\n * - For a path prefix: returns it verbatim (e.g. `/custom-asset-prefix`).\n * - For an absolute URL: returns its pathname stripped of trailing slashes\n * (e.g. `\"https://cdn.example.com/sub\"` → `/sub`, plain origin → `\"\"`).\n * - For empty input: returns `\"\"`.\n *\n * Used by the prod server and Cloudflare worker entry to recognise asset\n * requests that arrive at the deployment origin under the configured prefix.\n */\nexport function assetPrefixPathname(assetPrefix: string): string {\n if (!assetPrefix) return \"\";\n if (!isAbsoluteAssetPrefix(assetPrefix)) return assetPrefix;\n try {\n let pathname = new URL(assetPrefix).pathname;\n while (pathname.endsWith(\"/\")) pathname = pathname.slice(0, -1);\n return pathname === \"\" ? \"\" : pathname;\n } catch {\n return \"\";\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA4BA,MAAa,uBAAuB;;AAGpC,SAAgB,sBAAsB,aAA8B;CAClE,OAAO,gBAAgB,KAAK,YAAY;;;;;;;;;;;;;AAc1C,SAAgB,iBAAiB,aAA6B;CAC5D,IAAI,CAAC,aAAa,OAAO;CACzB,IAAI,sBAAsB,YAAY,EAIpC,OAAO;CAKT,IAAI,WAAW;CACf,OAAO,SAAS,WAAW,IAAI,EAAE,WAAW,SAAS,MAAM,EAAE;CAC7D,OAAO,GAAG,SAAS,GAAG;;;;;;;;;;;;AAaxB,SAAgB,sBAAsB,aAA6B;CACjE,IAAI,CAAC,aAAa,OAAO,IAAI,qBAAqB;CAClD,OAAO,GAAG,YAAY,GAAG,qBAAqB;;;;;;;;;;;;;AAchD,SAAgB,oBAAoB,aAA6B;CAC/D,IAAI,CAAC,aAAa,OAAO;CACzB,IAAI,CAAC,sBAAsB,YAAY,EAAE,OAAO;CAChD,IAAI;EACF,IAAI,WAAW,IAAI,IAAI,YAAY,CAAC;EACpC,OAAO,SAAS,SAAS,IAAI,EAAE,WAAW,SAAS,MAAM,GAAG,GAAG;EAC/D,OAAO,aAAa,KAAK,KAAK;SACxB;EACN,OAAO"}
1
+ {"version":3,"file":"asset-prefix.js","names":[],"sources":["../../src/utils/asset-prefix.ts"],"sourcesContent":["/**\n * Shared helpers for next.config `assetPrefix`.\n *\n * Mirrors Next.js semantics: `assetPrefix` is prepended to every JS/CSS/image/\n * static asset URL emitted in the page. It is distinct from `basePath`, which\n * affects route URLs.\n *\n * - A `assetPrefix` of `\"\"` (empty) means \"no prefix\". URLs are emitted as\n * `/_next/static/...` (Next.js's canonical convention) and assets are\n * written to `dist/client/_next/static/...` so the on-disk layout matches.\n * - A path prefix like `\"/custom-asset-prefix\"` is applied as a URL prefix\n * relative to the deployment origin. The prod server must also serve assets\n * under that prefix.\n * - An absolute URL like `\"https://cdn.example.com\"` makes asset URLs fully\n * qualified. Runtime serving on the deployment origin is a no-op — the CDN\n * serves the assets directly.\n *\n * The path component of the asset URL after the prefix is always\n * `/_next/static/<filename>` to match Next.js's convention. This is the\n * convention upstream test suites assert against.\n *\n * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix\n */\n\n/**\n * Suffix appended to the assetPrefix (or used standalone when no prefix is\n * configured) to form the leading portion of every emitted asset URL.\n * Matches Next.js: assets always live under `/_next/static/`.\n */\nexport const ASSET_PREFIX_URL_DIR = \"_next/static\";\n\n/** Whether `assetPrefix` is an absolute URL (vs a path prefix). */\nexport function isAbsoluteAssetPrefix(assetPrefix: string): boolean {\n return /^https?:\\/\\//i.test(assetPrefix);\n}\n\n/**\n * Compute the on-disk `build.assetsDir` for the given `assetPrefix`.\n *\n * - Path prefix (`/cdn`): write to `cdn/_next/static/` so the on-disk layout\n * matches the URL — the Cloudflare ASSETS binding (and any static file\n * server) serves them directly without a runtime rewrite.\n * - Absolute URL (with or without path component): write to `_next/static/`.\n * Runtime serving is best-effort — the CDN is expected to serve these.\n * - Empty: write to `_next/static/` so the on-disk layout matches the URL\n * Next.js itself emits (and that the Next.js client runtime + upstream\n * test suites assert against). This makes `assetPrefix` consistent in\n * both the configured and unconfigured cases — the URL contract is\n * always `/<prefix?>/_next/static/...`, and the on-disk layout mirrors\n * it 1:1 so the static-file layer can serve hits and return\n * `404 + \"Not Found\"` on misses without any URL-shape special-casing\n * upstream of it.\n */\nexport function resolveAssetsDir(assetPrefix: string): string {\n if (!assetPrefix) return ASSET_PREFIX_URL_DIR;\n if (isAbsoluteAssetPrefix(assetPrefix)) {\n // Files on disk land at `_next/static/...`. The absolute URL is applied\n // at URL-rendering time via renderBuiltUrl; on-disk path is irrelevant\n // to the CDN.\n return ASSET_PREFIX_URL_DIR;\n }\n // Path prefix — strip leading slash so the path joins cleanly with outDir.\n // Use an explicit loop instead of `replace(/^\\/+/, \"\")` so CodeQL doesn't\n // flag the regex as polynomial-time on uncontrolled input.\n let stripped = assetPrefix;\n while (stripped.startsWith(\"/\")) stripped = stripped.slice(1);\n return `${stripped}/${ASSET_PREFIX_URL_DIR}`;\n}\n\n/**\n * Build the URL prefix to apply to emitted asset URLs. Returns the full URL\n * prefix including the `_next/static/` directory, with a trailing slash.\n *\n * Examples:\n * - `\"\"` → `\"/_next/static/\"`\n * - `\"/cdn\"` → `\"/cdn/_next/static/\"`\n * - `\"https://cdn.example.com\"` → `\"https://cdn.example.com/_next/static/\"`\n * - `\"https://cdn.example.com/sub\"` → `\"https://cdn.example.com/sub/_next/static/\"`\n */\nexport function resolveAssetUrlPrefix(assetPrefix: string): string {\n if (!assetPrefix) return `/${ASSET_PREFIX_URL_DIR}/`;\n return `${assetPrefix}/${ASSET_PREFIX_URL_DIR}/`;\n}\n\n/**\n * Extract the path portion of `assetPrefix` for use in runtime URL matching.\n *\n * - For a path prefix: returns it verbatim (e.g. `/custom-asset-prefix`).\n * - For an absolute URL: returns its pathname stripped of trailing slashes\n * (e.g. `\"https://cdn.example.com/sub\"` → `/sub`, plain origin → `\"\"`).\n * - For empty input: returns `\"\"`.\n *\n * Used by the prod server and Cloudflare worker entry to recognise asset\n * requests that arrive at the deployment origin under the configured prefix.\n */\nexport function assetPrefixPathname(assetPrefix: string): string {\n if (!assetPrefix) return \"\";\n if (!isAbsoluteAssetPrefix(assetPrefix)) return assetPrefix;\n try {\n let pathname = new URL(assetPrefix).pathname;\n while (pathname.endsWith(\"/\")) pathname = pathname.slice(0, -1);\n return pathname === \"\" ? \"\" : pathname;\n } catch {\n return \"\";\n }\n}\n\n/**\n * Whether the incoming request pathname targets the canonical `_next/static/`\n * tree, after stripping any configured `basePath` and `assetPrefix` path\n * component.\n *\n * Used by the Cloudflare worker entry to recognise asset-shaped requests\n * that the ASSETS binding didn't serve, so they can short-circuit with a\n * plain-text 404 instead of falling through to the RSC handler (which\n * would render the full HTML 404 page). Mirrors Next.js's behaviour in\n * `packages/next/src/server/lib/router-server.ts` where\n * `realRequestPathname` is stripped of basePath/assetPrefix/i18n locale\n * before the `startsWith('/_next/static/')` check.\n *\n * - `pathname`: incoming request pathname (with leading slash, no query).\n * - `basePath`: configured `basePath` (e.g. `\"/docs\"`) or `\"\"`.\n * - `assetPathPrefix`: path component of `assetPrefix` (e.g. `\"/cdn\"`) or\n * `\"\"`. Use `assetPrefixPathname()` to derive this from a raw assetPrefix.\n *\n * @see https://github.com/vercel/next.js/blob/canary/packages/next/src/server/lib/router-server.ts\n */\nexport function isNextStaticPath(\n pathname: string,\n basePath: string,\n assetPathPrefix: string,\n): boolean {\n let p = pathname;\n if (basePath && (p === basePath || p.startsWith(basePath + \"/\"))) {\n p = p.slice(basePath.length) || \"/\";\n }\n if (assetPathPrefix && (p === assetPathPrefix || p.startsWith(assetPathPrefix + \"/\"))) {\n p = p.slice(assetPathPrefix.length) || \"/\";\n }\n return p.startsWith(`/${ASSET_PREFIX_URL_DIR}/`);\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAa,uBAAuB;;AAGpC,SAAgB,sBAAsB,aAA8B;CAClE,OAAO,gBAAgB,KAAK,YAAY;;;;;;;;;;;;;;;;;;;AAoB1C,SAAgB,iBAAiB,aAA6B;CAC5D,IAAI,CAAC,aAAa,OAAO;CACzB,IAAI,sBAAsB,YAAY,EAIpC,OAAO;CAKT,IAAI,WAAW;CACf,OAAO,SAAS,WAAW,IAAI,EAAE,WAAW,SAAS,MAAM,EAAE;CAC7D,OAAO,GAAG,SAAS,GAAG;;;;;;;;;;;;AAaxB,SAAgB,sBAAsB,aAA6B;CACjE,IAAI,CAAC,aAAa,OAAO,IAAI,qBAAqB;CAClD,OAAO,GAAG,YAAY,GAAG,qBAAqB;;;;;;;;;;;;;AAchD,SAAgB,oBAAoB,aAA6B;CAC/D,IAAI,CAAC,aAAa,OAAO;CACzB,IAAI,CAAC,sBAAsB,YAAY,EAAE,OAAO;CAChD,IAAI;EACF,IAAI,WAAW,IAAI,IAAI,YAAY,CAAC;EACpC,OAAO,SAAS,SAAS,IAAI,EAAE,WAAW,SAAS,MAAM,GAAG,GAAG;EAC/D,OAAO,aAAa,KAAK,KAAK;SACxB;EACN,OAAO;;;;;;;;;;;;;;;;;;;;;;;AAwBX,SAAgB,iBACd,UACA,UACA,iBACS;CACT,IAAI,IAAI;CACR,IAAI,aAAa,MAAM,YAAY,EAAE,WAAW,WAAW,IAAI,GAC7D,IAAI,EAAE,MAAM,SAAS,OAAO,IAAI;CAElC,IAAI,oBAAoB,MAAM,mBAAmB,EAAE,WAAW,kBAAkB,IAAI,GAClF,IAAI,EAAE,MAAM,gBAAgB,OAAO,IAAI;CAEzC,OAAO,EAAE,WAAW,IAAI,qBAAqB,GAAG"}
@@ -1,5 +1,6 @@
1
+ import { isUnknownRecord } from "./record.js";
2
+
1
3
  //#region src/utils/cache-control-metadata.d.ts
2
- declare function isUnknownRecord(value: unknown): value is Record<string, unknown>;
3
4
  declare function readCacheControlNumberField(ctx: Record<string, unknown> | undefined, field: string): number | undefined;
4
5
  //#endregion
5
6
  export { isUnknownRecord, readCacheControlNumberField };
@@ -1,7 +1,5 @@
1
+ import { isUnknownRecord } from "./record.js";
1
2
  //#region src/utils/cache-control-metadata.ts
2
- function isUnknownRecord(value) {
3
- return value !== null && typeof value === "object" && !Array.isArray(value);
4
- }
5
3
  function readRecordField(ctx, field) {
6
4
  const value = ctx?.[field];
7
5
  return isUnknownRecord(value) ? value : void 0;
@@ -1 +1 @@
1
- {"version":3,"file":"cache-control-metadata.js","names":[],"sources":["../../src/utils/cache-control-metadata.ts"],"sourcesContent":["export function isUnknownRecord(value: unknown): value is Record<string, unknown> {\n return value !== null && typeof value === \"object\" && !Array.isArray(value);\n}\n\nfunction readRecordField(\n ctx: Record<string, unknown> | undefined,\n field: string,\n): Record<string, unknown> | undefined {\n const value = ctx?.[field];\n return isUnknownRecord(value) ? value : undefined;\n}\n\nexport function readCacheControlNumberField(\n ctx: Record<string, unknown> | undefined,\n field: string,\n): number | undefined {\n const cacheControl = readRecordField(ctx, \"cacheControl\");\n const value = cacheControl?.[field] ?? ctx?.[field];\n return typeof value === \"number\" ? value : undefined;\n}\n"],"mappings":";AAAA,SAAgB,gBAAgB,OAAkD;CAChF,OAAO,UAAU,QAAQ,OAAO,UAAU,YAAY,CAAC,MAAM,QAAQ,MAAM;;AAG7E,SAAS,gBACP,KACA,OACqC;CACrC,MAAM,QAAQ,MAAM;CACpB,OAAO,gBAAgB,MAAM,GAAG,QAAQ,KAAA;;AAG1C,SAAgB,4BACd,KACA,OACoB;CAEpB,MAAM,QADe,gBAAgB,KAAK,eAChB,GAAG,UAAU,MAAM;CAC7C,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA"}
1
+ {"version":3,"file":"cache-control-metadata.js","names":[],"sources":["../../src/utils/cache-control-metadata.ts"],"sourcesContent":["import { isUnknownRecord } from \"./record.js\";\nexport { isUnknownRecord } from \"./record.js\";\n\nfunction readRecordField(\n ctx: Record<string, unknown> | undefined,\n field: string,\n): Record<string, unknown> | undefined {\n const value = ctx?.[field];\n return isUnknownRecord(value) ? value : undefined;\n}\n\nexport function readCacheControlNumberField(\n ctx: Record<string, unknown> | undefined,\n field: string,\n): number | undefined {\n const cacheControl = readRecordField(ctx, \"cacheControl\");\n const value = cacheControl?.[field] ?? ctx?.[field];\n return typeof value === \"number\" ? value : undefined;\n}\n"],"mappings":";;AAGA,SAAS,gBACP,KACA,OACqC;CACrC,MAAM,QAAQ,MAAM;CACpB,OAAO,gBAAgB,MAAM,GAAG,QAAQ,KAAA;;AAG1C,SAAgB,4BACd,KACA,OACoB;CAEpB,MAAM,QADe,gBAAgB,KAAK,eAChB,GAAG,UAAU,MAAM;CAC7C,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAA"}
@@ -12,6 +12,7 @@ declare function normalizeDomainHostname(hostname: string | null | undefined): s
12
12
  */
13
13
  declare function detectDomainLocale(domainItems?: readonly DomainLocale[], hostname?: string, detectedLocale?: string): DomainLocale | undefined;
14
14
  declare function addLocalePrefix(path: string, locale: string, localeDefault: string): string;
15
+ declare function getLocalePathPrefix(path: string, locales: readonly string[] | undefined): string | undefined;
15
16
  declare function getDomainLocaleUrl(url: string, locale: string, {
16
17
  basePath,
17
18
  currentHostname,
@@ -22,5 +23,5 @@ declare function getDomainLocaleUrl(url: string, locale: string, {
22
23
  domainItems?: readonly DomainLocale[];
23
24
  }): string | undefined;
24
25
  //#endregion
25
- export { DomainLocale, addLocalePrefix, detectDomainLocale, getDomainLocaleUrl, normalizeDomainHostname };
26
+ export { DomainLocale, addLocalePrefix, detectDomainLocale, getDomainLocaleUrl, getLocalePathPrefix, normalizeDomainHostname };
26
27
  //# sourceMappingURL=domain-locale.d.ts.map
@@ -25,6 +25,14 @@ function addLocalePrefix(path, locale, localeDefault) {
25
25
  if (normalizedPathname === localePrefix || normalizedPathname.startsWith(`${localePrefix}/`)) return path.startsWith("/") ? path : pathWithLeadingSlash;
26
26
  return `/${locale}${pathWithLeadingSlash}`;
27
27
  }
28
+ function getLocalePathPrefix(path, locales) {
29
+ if (!locales?.length) return void 0;
30
+ const pathWithLeadingSlash = path.startsWith("/") ? path : `/${path}`;
31
+ const firstSegment = (pathWithLeadingSlash.split(/[?#]/, 1)[0] ?? pathWithLeadingSlash).split("/").find(Boolean);
32
+ if (!firstSegment) return void 0;
33
+ const normalizedSegment = firstSegment.toLowerCase();
34
+ return locales.find((locale) => locale.toLowerCase() === normalizedSegment);
35
+ }
28
36
  function withBasePath(path, basePath = "") {
29
37
  if (!basePath) return path;
30
38
  return basePath + path;
@@ -39,6 +47,6 @@ function getDomainLocaleUrl(url, locale, { basePath, currentHostname, domainItem
39
47
  return `${`http${targetDomain.http ? "" : "s"}://`}${targetDomain.domain}${withBasePath(localizedPath, basePath)}`;
40
48
  }
41
49
  //#endregion
42
- export { addLocalePrefix, detectDomainLocale, getDomainLocaleUrl, normalizeDomainHostname };
50
+ export { addLocalePrefix, detectDomainLocale, getDomainLocaleUrl, getLocalePathPrefix, normalizeDomainHostname };
43
51
 
44
52
  //# sourceMappingURL=domain-locale.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"domain-locale.js","names":[],"sources":["../../src/utils/domain-locale.ts"],"sourcesContent":["import type { NextI18nConfig } from \"../config/next-config.js\";\n\nexport type DomainLocale = NonNullable<NextI18nConfig[\"domains\"]>[number];\n\nexport function normalizeDomainHostname(hostname: string | null | undefined): string | undefined {\n if (!hostname) return undefined;\n return hostname.split(\",\", 1)[0]?.trim().split(\":\", 1)[0]?.toLowerCase() || undefined;\n}\n\n/**\n * Match a configured domain either by hostname or locale.\n * When both are provided, the checks intentionally use OR semantics so the\n * same helper can cover Next.js's hostname lookup and preferred-locale lookup.\n * If both are passed, the first domain matching either input wins, so callers\n * should pass hostname or detectedLocale, not both.\n */\nexport function detectDomainLocale(\n domainItems?: readonly DomainLocale[],\n hostname?: string,\n detectedLocale?: string,\n): DomainLocale | undefined {\n if (!domainItems?.length) return undefined;\n\n const normalizedHostname = normalizeDomainHostname(hostname);\n const normalizedLocale = detectedLocale?.toLowerCase();\n\n for (const item of domainItems) {\n const domainHostname = normalizeDomainHostname(item.domain);\n if (\n normalizedHostname === domainHostname ||\n normalizedLocale === item.defaultLocale.toLowerCase() ||\n item.locales?.some((locale) => locale.toLowerCase() === normalizedLocale)\n ) {\n return item;\n }\n }\n\n return undefined;\n}\n\nexport function addLocalePrefix(path: string, locale: string, localeDefault: string): string {\n const normalizedLocale = locale.toLowerCase();\n if (normalizedLocale === localeDefault.toLowerCase()) return path;\n\n const pathWithLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n const pathname = pathWithLeadingSlash.split(/[?#]/, 1)[0] ?? pathWithLeadingSlash;\n const normalizedPathname = pathname.toLowerCase();\n const localePrefix = `/${normalizedLocale}`;\n\n if (normalizedPathname === localePrefix || normalizedPathname.startsWith(`${localePrefix}/`)) {\n return path.startsWith(\"/\") ? path : pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash}`;\n}\n\nfunction withBasePath(path: string, basePath = \"\"): string {\n if (!basePath) return path;\n return basePath + path;\n}\n\nexport function getDomainLocaleUrl(\n url: string,\n locale: string,\n {\n basePath,\n currentHostname,\n domainItems,\n }: {\n basePath?: string;\n currentHostname?: string | null;\n domainItems?: readonly DomainLocale[];\n },\n): string | undefined {\n if (!domainItems?.length) return undefined;\n\n const targetDomain = detectDomainLocale(domainItems, undefined, locale);\n if (!targetDomain) return undefined;\n\n const currentDomain = detectDomainLocale(domainItems, currentHostname ?? undefined);\n const localizedPath = addLocalePrefix(url, locale, targetDomain.defaultLocale);\n\n if (\n currentDomain &&\n normalizeDomainHostname(currentDomain.domain) === normalizeDomainHostname(targetDomain.domain)\n ) {\n // Same-domain switches fall back to the caller's standard locale-prefix\n // logic. This relies on __VINEXT_DEFAULT_LOCALE__ matching the current\n // domain's defaultLocale, which the server entry keeps in sync.\n return undefined;\n }\n\n const scheme = `http${targetDomain.http ? \"\" : \"s\"}://`;\n return `${scheme}${targetDomain.domain}${withBasePath(localizedPath, basePath)}`;\n}\n"],"mappings":";AAIA,SAAgB,wBAAwB,UAAyD;CAC/F,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,OAAO,SAAS,MAAM,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC,IAAI,aAAa,IAAI,KAAA;;;;;;;;;AAU9E,SAAgB,mBACd,aACA,UACA,gBAC0B;CAC1B,IAAI,CAAC,aAAa,QAAQ,OAAO,KAAA;CAEjC,MAAM,qBAAqB,wBAAwB,SAAS;CAC5D,MAAM,mBAAmB,gBAAgB,aAAa;CAEtD,KAAK,MAAM,QAAQ,aAEjB,IACE,uBAFqB,wBAAwB,KAAK,OAEb,IACrC,qBAAqB,KAAK,cAAc,aAAa,IACrD,KAAK,SAAS,MAAM,WAAW,OAAO,aAAa,KAAK,iBAAiB,EAEzE,OAAO;;AAOb,SAAgB,gBAAgB,MAAc,QAAgB,eAA+B;CAC3F,MAAM,mBAAmB,OAAO,aAAa;CAC7C,IAAI,qBAAqB,cAAc,aAAa,EAAE,OAAO;CAE7D,MAAM,uBAAuB,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;CAE/D,MAAM,sBADW,qBAAqB,MAAM,QAAQ,EAAE,CAAC,MAAM,sBACzB,aAAa;CACjD,MAAM,eAAe,IAAI;CAEzB,IAAI,uBAAuB,gBAAgB,mBAAmB,WAAW,GAAG,aAAa,GAAG,EAC1F,OAAO,KAAK,WAAW,IAAI,GAAG,OAAO;CAGvC,OAAO,IAAI,SAAS;;AAGtB,SAAS,aAAa,MAAc,WAAW,IAAY;CACzD,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,WAAW;;AAGpB,SAAgB,mBACd,KACA,QACA,EACE,UACA,iBACA,eAMkB;CACpB,IAAI,CAAC,aAAa,QAAQ,OAAO,KAAA;CAEjC,MAAM,eAAe,mBAAmB,aAAa,KAAA,GAAW,OAAO;CACvE,IAAI,CAAC,cAAc,OAAO,KAAA;CAE1B,MAAM,gBAAgB,mBAAmB,aAAa,mBAAmB,KAAA,EAAU;CACnF,MAAM,gBAAgB,gBAAgB,KAAK,QAAQ,aAAa,cAAc;CAE9E,IACE,iBACA,wBAAwB,cAAc,OAAO,KAAK,wBAAwB,aAAa,OAAO,EAK9F;CAIF,OAAO,GAAG,OADY,aAAa,OAAO,KAAK,IAAI,OAChC,aAAa,SAAS,aAAa,eAAe,SAAS"}
1
+ {"version":3,"file":"domain-locale.js","names":[],"sources":["../../src/utils/domain-locale.ts"],"sourcesContent":["import type { NextI18nConfig } from \"../config/next-config.js\";\n\nexport type DomainLocale = NonNullable<NextI18nConfig[\"domains\"]>[number];\n\nexport function normalizeDomainHostname(hostname: string | null | undefined): string | undefined {\n if (!hostname) return undefined;\n return hostname.split(\",\", 1)[0]?.trim().split(\":\", 1)[0]?.toLowerCase() || undefined;\n}\n\n/**\n * Match a configured domain either by hostname or locale.\n * When both are provided, the checks intentionally use OR semantics so the\n * same helper can cover Next.js's hostname lookup and preferred-locale lookup.\n * If both are passed, the first domain matching either input wins, so callers\n * should pass hostname or detectedLocale, not both.\n */\nexport function detectDomainLocale(\n domainItems?: readonly DomainLocale[],\n hostname?: string,\n detectedLocale?: string,\n): DomainLocale | undefined {\n if (!domainItems?.length) return undefined;\n\n const normalizedHostname = normalizeDomainHostname(hostname);\n const normalizedLocale = detectedLocale?.toLowerCase();\n\n for (const item of domainItems) {\n const domainHostname = normalizeDomainHostname(item.domain);\n if (\n normalizedHostname === domainHostname ||\n normalizedLocale === item.defaultLocale.toLowerCase() ||\n item.locales?.some((locale) => locale.toLowerCase() === normalizedLocale)\n ) {\n return item;\n }\n }\n\n return undefined;\n}\n\nexport function addLocalePrefix(path: string, locale: string, localeDefault: string): string {\n const normalizedLocale = locale.toLowerCase();\n if (normalizedLocale === localeDefault.toLowerCase()) return path;\n\n const pathWithLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n const pathname = pathWithLeadingSlash.split(/[?#]/, 1)[0] ?? pathWithLeadingSlash;\n const normalizedPathname = pathname.toLowerCase();\n const localePrefix = `/${normalizedLocale}`;\n\n if (normalizedPathname === localePrefix || normalizedPathname.startsWith(`${localePrefix}/`)) {\n return path.startsWith(\"/\") ? path : pathWithLeadingSlash;\n }\n\n return `/${locale}${pathWithLeadingSlash}`;\n}\n\nexport function getLocalePathPrefix(\n path: string,\n locales: readonly string[] | undefined,\n): string | undefined {\n if (!locales?.length) return undefined;\n\n const pathWithLeadingSlash = path.startsWith(\"/\") ? path : `/${path}`;\n const pathname = pathWithLeadingSlash.split(/[?#]/, 1)[0] ?? pathWithLeadingSlash;\n const firstSegment = pathname.split(\"/\").find(Boolean);\n if (!firstSegment) return undefined;\n\n const normalizedSegment = firstSegment.toLowerCase();\n return locales.find((locale) => locale.toLowerCase() === normalizedSegment);\n}\n\nfunction withBasePath(path: string, basePath = \"\"): string {\n if (!basePath) return path;\n return basePath + path;\n}\n\nexport function getDomainLocaleUrl(\n url: string,\n locale: string,\n {\n basePath,\n currentHostname,\n domainItems,\n }: {\n basePath?: string;\n currentHostname?: string | null;\n domainItems?: readonly DomainLocale[];\n },\n): string | undefined {\n if (!domainItems?.length) return undefined;\n\n const targetDomain = detectDomainLocale(domainItems, undefined, locale);\n if (!targetDomain) return undefined;\n\n const currentDomain = detectDomainLocale(domainItems, currentHostname ?? undefined);\n const localizedPath = addLocalePrefix(url, locale, targetDomain.defaultLocale);\n\n if (\n currentDomain &&\n normalizeDomainHostname(currentDomain.domain) === normalizeDomainHostname(targetDomain.domain)\n ) {\n // Same-domain switches fall back to the caller's standard locale-prefix\n // logic. This relies on __VINEXT_DEFAULT_LOCALE__ matching the current\n // domain's defaultLocale, which the server entry keeps in sync.\n return undefined;\n }\n\n const scheme = `http${targetDomain.http ? \"\" : \"s\"}://`;\n return `${scheme}${targetDomain.domain}${withBasePath(localizedPath, basePath)}`;\n}\n"],"mappings":";AAIA,SAAgB,wBAAwB,UAAyD;CAC/F,IAAI,CAAC,UAAU,OAAO,KAAA;CACtB,OAAO,SAAS,MAAM,KAAK,EAAE,CAAC,IAAI,MAAM,CAAC,MAAM,KAAK,EAAE,CAAC,IAAI,aAAa,IAAI,KAAA;;;;;;;;;AAU9E,SAAgB,mBACd,aACA,UACA,gBAC0B;CAC1B,IAAI,CAAC,aAAa,QAAQ,OAAO,KAAA;CAEjC,MAAM,qBAAqB,wBAAwB,SAAS;CAC5D,MAAM,mBAAmB,gBAAgB,aAAa;CAEtD,KAAK,MAAM,QAAQ,aAEjB,IACE,uBAFqB,wBAAwB,KAAK,OAEb,IACrC,qBAAqB,KAAK,cAAc,aAAa,IACrD,KAAK,SAAS,MAAM,WAAW,OAAO,aAAa,KAAK,iBAAiB,EAEzE,OAAO;;AAOb,SAAgB,gBAAgB,MAAc,QAAgB,eAA+B;CAC3F,MAAM,mBAAmB,OAAO,aAAa;CAC7C,IAAI,qBAAqB,cAAc,aAAa,EAAE,OAAO;CAE7D,MAAM,uBAAuB,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;CAE/D,MAAM,sBADW,qBAAqB,MAAM,QAAQ,EAAE,CAAC,MAAM,sBACzB,aAAa;CACjD,MAAM,eAAe,IAAI;CAEzB,IAAI,uBAAuB,gBAAgB,mBAAmB,WAAW,GAAG,aAAa,GAAG,EAC1F,OAAO,KAAK,WAAW,IAAI,GAAG,OAAO;CAGvC,OAAO,IAAI,SAAS;;AAGtB,SAAgB,oBACd,MACA,SACoB;CACpB,IAAI,CAAC,SAAS,QAAQ,OAAO,KAAA;CAE7B,MAAM,uBAAuB,KAAK,WAAW,IAAI,GAAG,OAAO,IAAI;CAE/D,MAAM,gBADW,qBAAqB,MAAM,QAAQ,EAAE,CAAC,MAAM,sBAC/B,MAAM,IAAI,CAAC,KAAK,QAAQ;CACtD,IAAI,CAAC,cAAc,OAAO,KAAA;CAE1B,MAAM,oBAAoB,aAAa,aAAa;CACpD,OAAO,QAAQ,MAAM,WAAW,OAAO,aAAa,KAAK,kBAAkB;;AAG7E,SAAS,aAAa,MAAc,WAAW,IAAY;CACzD,IAAI,CAAC,UAAU,OAAO;CACtB,OAAO,WAAW;;AAGpB,SAAgB,mBACd,KACA,QACA,EACE,UACA,iBACA,eAMkB;CACpB,IAAI,CAAC,aAAa,QAAQ,OAAO,KAAA;CAEjC,MAAM,eAAe,mBAAmB,aAAa,KAAA,GAAW,OAAO;CACvE,IAAI,CAAC,cAAc,OAAO,KAAA;CAE1B,MAAM,gBAAgB,mBAAmB,aAAa,mBAAmB,KAAA,EAAU;CACnF,MAAM,gBAAgB,gBAAgB,KAAK,QAAQ,aAAa,cAAc;CAE9E,IACE,iBACA,wBAAwB,cAAc,OAAO,KAAK,wBAAwB,aAAa,OAAO,EAK9F;CAIF,OAAO,GAAG,OADY,aAAa,OAAO,KAAK,IAAI,OAChC,aAAa,SAAS,aAAa,eAAe,SAAS"}
@@ -25,7 +25,7 @@ type BuildManifestChunk = {
25
25
  * @param buildManifest - Vite's build manifest (manifest.json), which is a
26
26
  * Record<string, ManifestChunk> where each chunk has `file`, `imports`,
27
27
  * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.
28
- * @returns Array of chunk filenames (e.g. "assets/mermaid-NOHMQCX5.js") that
28
+ * @returns Array of chunk filenames (e.g. "_next/static/mermaid-NOHMQCX5.js") that
29
29
  * should be excluded from modulepreload hints.
30
30
  */
31
31
  declare function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[];
@@ -13,7 +13,7 @@
13
13
  * @param buildManifest - Vite's build manifest (manifest.json), which is a
14
14
  * Record<string, ManifestChunk> where each chunk has `file`, `imports`,
15
15
  * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.
16
- * @returns Array of chunk filenames (e.g. "assets/mermaid-NOHMQCX5.js") that
16
+ * @returns Array of chunk filenames (e.g. "_next/static/mermaid-NOHMQCX5.js") that
17
17
  * should be excluded from modulepreload hints.
18
18
  */
19
19
  function computeLazyChunks(buildManifest) {
@@ -1 +1 @@
1
- {"version":3,"file":"lazy-chunks.js","names":[],"sources":["../../src/utils/lazy-chunks.ts"],"sourcesContent":["/**\n * Build-manifest chunk metadata used to compute lazy chunks.\n */\ntype BuildManifestChunk = {\n file: string;\n isEntry?: boolean;\n isDynamicEntry?: boolean;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n};\n\n/**\n * Compute the set of chunk filenames that are ONLY reachable through dynamic\n * imports (i.e. behind React.lazy(), next/dynamic, or manual import()).\n *\n * These chunks should NOT be modulepreloaded in the HTML — they will be\n * fetched on demand when the dynamic import executes.\n *\n * Algorithm: Starting from all entry chunks in the build manifest, walk the\n * static `imports` tree (breadth-first). Any chunk file NOT reached by this\n * walk is only reachable through `dynamicImports` and is therefore \"lazy\".\n *\n * @param buildManifest - Vite's build manifest (manifest.json), which is a\n * Record<string, ManifestChunk> where each chunk has `file`, `imports`,\n * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.\n * @returns Array of chunk filenames (e.g. \"assets/mermaid-NOHMQCX5.js\") that\n * should be excluded from modulepreload hints.\n */\nexport function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[] {\n // Collect all chunk files that are statically reachable from entries\n const eagerFiles = new Set<string>();\n const visited = new Set<string>();\n const queue: string[] = [];\n\n // Start BFS from all entry chunks\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.isEntry) {\n queue.push(key);\n }\n }\n\n while (queue.length > 0) {\n const key = queue.shift();\n if (!key || visited.has(key)) continue;\n visited.add(key);\n\n const chunk = buildManifest[key];\n if (!chunk) continue;\n\n // Mark this chunk's file as eager\n eagerFiles.add(chunk.file);\n\n // Also mark its CSS as eager (CSS should always be preloaded to avoid FOUC)\n if (chunk.css) {\n for (const cssFile of chunk.css) {\n eagerFiles.add(cssFile);\n }\n }\n\n // Follow only static imports — NOT dynamicImports\n if (chunk.imports) {\n for (const imp of chunk.imports) {\n if (!visited.has(imp)) {\n queue.push(imp);\n }\n }\n }\n }\n\n // Any JS file in the manifest that's NOT in eagerFiles is a lazy chunk\n const lazyChunks: string[] = [];\n const allFiles = new Set<string>();\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.file && !allFiles.has(chunk.file)) {\n allFiles.add(chunk.file);\n if (!eagerFiles.has(chunk.file) && chunk.file.endsWith(\".js\")) {\n lazyChunks.push(chunk.file);\n }\n }\n }\n\n return lazyChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,eAA6D;CAE7F,MAAM,6BAAa,IAAI,KAAa;CACpC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,EAAE;CAG1B,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAE1C,IADc,cAAc,KAClB,SACR,MAAM,KAAK,IAAI;CAInB,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,OAAO;EACzB,IAAI,CAAC,OAAO,QAAQ,IAAI,IAAI,EAAE;EAC9B,QAAQ,IAAI,IAAI;EAEhB,MAAM,QAAQ,cAAc;EAC5B,IAAI,CAAC,OAAO;EAGZ,WAAW,IAAI,MAAM,KAAK;EAG1B,IAAI,MAAM,KACR,KAAK,MAAM,WAAW,MAAM,KAC1B,WAAW,IAAI,QAAQ;EAK3B,IAAI,MAAM;QACH,MAAM,OAAO,MAAM,SACtB,IAAI,CAAC,QAAQ,IAAI,IAAI,EACnB,MAAM,KAAK,IAAI;;;CAOvB,MAAM,aAAuB,EAAE;CAC/B,MAAM,2BAAW,IAAI,KAAa;CAClC,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAAE;EAC5C,MAAM,QAAQ,cAAc;EAC5B,IAAI,MAAM,QAAQ,CAAC,SAAS,IAAI,MAAM,KAAK,EAAE;GAC3C,SAAS,IAAI,MAAM,KAAK;GACxB,IAAI,CAAC,WAAW,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,EAC3D,WAAW,KAAK,MAAM,KAAK;;;CAKjC,OAAO"}
1
+ {"version":3,"file":"lazy-chunks.js","names":[],"sources":["../../src/utils/lazy-chunks.ts"],"sourcesContent":["/**\n * Build-manifest chunk metadata used to compute lazy chunks.\n */\ntype BuildManifestChunk = {\n file: string;\n isEntry?: boolean;\n isDynamicEntry?: boolean;\n imports?: string[];\n dynamicImports?: string[];\n css?: string[];\n assets?: string[];\n};\n\n/**\n * Compute the set of chunk filenames that are ONLY reachable through dynamic\n * imports (i.e. behind React.lazy(), next/dynamic, or manual import()).\n *\n * These chunks should NOT be modulepreloaded in the HTML — they will be\n * fetched on demand when the dynamic import executes.\n *\n * Algorithm: Starting from all entry chunks in the build manifest, walk the\n * static `imports` tree (breadth-first). Any chunk file NOT reached by this\n * walk is only reachable through `dynamicImports` and is therefore \"lazy\".\n *\n * @param buildManifest - Vite's build manifest (manifest.json), which is a\n * Record<string, ManifestChunk> where each chunk has `file`, `imports`,\n * `dynamicImports`, `isEntry`, and `isDynamicEntry` fields.\n * @returns Array of chunk filenames (e.g. \"_next/static/mermaid-NOHMQCX5.js\") that\n * should be excluded from modulepreload hints.\n */\nexport function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[] {\n // Collect all chunk files that are statically reachable from entries\n const eagerFiles = new Set<string>();\n const visited = new Set<string>();\n const queue: string[] = [];\n\n // Start BFS from all entry chunks\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.isEntry) {\n queue.push(key);\n }\n }\n\n while (queue.length > 0) {\n const key = queue.shift();\n if (!key || visited.has(key)) continue;\n visited.add(key);\n\n const chunk = buildManifest[key];\n if (!chunk) continue;\n\n // Mark this chunk's file as eager\n eagerFiles.add(chunk.file);\n\n // Also mark its CSS as eager (CSS should always be preloaded to avoid FOUC)\n if (chunk.css) {\n for (const cssFile of chunk.css) {\n eagerFiles.add(cssFile);\n }\n }\n\n // Follow only static imports — NOT dynamicImports\n if (chunk.imports) {\n for (const imp of chunk.imports) {\n if (!visited.has(imp)) {\n queue.push(imp);\n }\n }\n }\n }\n\n // Any JS file in the manifest that's NOT in eagerFiles is a lazy chunk\n const lazyChunks: string[] = [];\n const allFiles = new Set<string>();\n for (const key of Object.keys(buildManifest)) {\n const chunk = buildManifest[key];\n if (chunk.file && !allFiles.has(chunk.file)) {\n allFiles.add(chunk.file);\n if (!eagerFiles.has(chunk.file) && chunk.file.endsWith(\".js\")) {\n lazyChunks.push(chunk.file);\n }\n }\n }\n\n return lazyChunks;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AA8BA,SAAgB,kBAAkB,eAA6D;CAE7F,MAAM,6BAAa,IAAI,KAAa;CACpC,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,QAAkB,EAAE;CAG1B,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAE1C,IADc,cAAc,KAClB,SACR,MAAM,KAAK,IAAI;CAInB,OAAO,MAAM,SAAS,GAAG;EACvB,MAAM,MAAM,MAAM,OAAO;EACzB,IAAI,CAAC,OAAO,QAAQ,IAAI,IAAI,EAAE;EAC9B,QAAQ,IAAI,IAAI;EAEhB,MAAM,QAAQ,cAAc;EAC5B,IAAI,CAAC,OAAO;EAGZ,WAAW,IAAI,MAAM,KAAK;EAG1B,IAAI,MAAM,KACR,KAAK,MAAM,WAAW,MAAM,KAC1B,WAAW,IAAI,QAAQ;EAK3B,IAAI,MAAM;QACH,MAAM,OAAO,MAAM,SACtB,IAAI,CAAC,QAAQ,IAAI,IAAI,EACnB,MAAM,KAAK,IAAI;;;CAOvB,MAAM,aAAuB,EAAE;CAC/B,MAAM,2BAAW,IAAI,KAAa;CAClC,KAAK,MAAM,OAAO,OAAO,KAAK,cAAc,EAAE;EAC5C,MAAM,QAAQ,cAAc;EAC5B,IAAI,MAAM,QAAQ,CAAC,SAAS,IAAI,MAAM,KAAK,EAAE;GAC3C,SAAS,IAAI,MAAM,KAAK;GACxB,IAAI,CAAC,WAAW,IAAI,MAAM,KAAK,IAAI,MAAM,KAAK,SAAS,MAAM,EAC3D,WAAW,KAAK,MAAM,KAAK;;;CAKjC,OAAO"}
@@ -0,0 +1,15 @@
1
+ //#region src/utils/prerender-output-paths.d.ts
2
+ /**
3
+ * Determine the HTML output file path for a prerendered URL.
4
+ * Respects trailingSlash config.
5
+ */
6
+ declare function getOutputPath(urlPath: string, trailingSlash: boolean): string;
7
+ /**
8
+ * Determine the RSC output file path for a prerendered URL.
9
+ * "/blog/hello-world" -> "blog/hello-world.rsc"
10
+ * "/" -> "index.rsc"
11
+ */
12
+ declare function getRscOutputPath(urlPath: string): string;
13
+ //#endregion
14
+ export { getOutputPath, getRscOutputPath };
15
+ //# sourceMappingURL=prerender-output-paths.d.ts.map
@@ -0,0 +1,24 @@
1
+ //#region src/utils/prerender-output-paths.ts
2
+ /**
3
+ * Determine the HTML output file path for a prerendered URL.
4
+ * Respects trailingSlash config.
5
+ */
6
+ function getOutputPath(urlPath, trailingSlash) {
7
+ if (urlPath === "/") return "index.html";
8
+ const clean = urlPath.replace(/^\//, "");
9
+ if (trailingSlash) return `${clean}/index.html`;
10
+ return `${clean}.html`;
11
+ }
12
+ /**
13
+ * Determine the RSC output file path for a prerendered URL.
14
+ * "/blog/hello-world" -> "blog/hello-world.rsc"
15
+ * "/" -> "index.rsc"
16
+ */
17
+ function getRscOutputPath(urlPath) {
18
+ if (urlPath === "/") return "index.rsc";
19
+ return urlPath.replace(/^\//, "") + ".rsc";
20
+ }
21
+ //#endregion
22
+ export { getOutputPath, getRscOutputPath };
23
+
24
+ //# sourceMappingURL=prerender-output-paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"prerender-output-paths.js","names":[],"sources":["../../src/utils/prerender-output-paths.ts"],"sourcesContent":["/**\n * Determine the HTML output file path for a prerendered URL.\n * Respects trailingSlash config.\n */\nexport function getOutputPath(urlPath: string, trailingSlash: boolean): string {\n if (urlPath === \"/\") return \"index.html\";\n const clean = urlPath.replace(/^\\//, \"\");\n if (trailingSlash) return `${clean}/index.html`;\n return `${clean}.html`;\n}\n\n/**\n * Determine the RSC output file path for a prerendered URL.\n * \"/blog/hello-world\" -> \"blog/hello-world.rsc\"\n * \"/\" -> \"index.rsc\"\n */\nexport function getRscOutputPath(urlPath: string): string {\n if (urlPath === \"/\") return \"index.rsc\";\n return urlPath.replace(/^\\//, \"\") + \".rsc\";\n}\n"],"mappings":";;;;;AAIA,SAAgB,cAAc,SAAiB,eAAgC;CAC7E,IAAI,YAAY,KAAK,OAAO;CAC5B,MAAM,QAAQ,QAAQ,QAAQ,OAAO,GAAG;CACxC,IAAI,eAAe,OAAO,GAAG,MAAM;CACnC,OAAO,GAAG,MAAM;;;;;;;AAQlB,SAAgB,iBAAiB,SAAyB;CACxD,IAAI,YAAY,KAAK,OAAO;CAC5B,OAAO,QAAQ,QAAQ,OAAO,GAAG,GAAG"}