vinext 0.1.1 → 0.1.3

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 (266) hide show
  1. package/README.md +2 -5
  2. package/dist/build/client-build-config.d.ts +7 -1
  3. package/dist/build/client-build-config.js +9 -1
  4. package/dist/build/prerender.d.ts +9 -1
  5. package/dist/build/prerender.js +41 -12
  6. package/dist/build/run-prerender.d.ts +10 -2
  7. package/dist/build/run-prerender.js +15 -1
  8. package/dist/check.js +4 -3
  9. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  10. package/dist/client/app-nav-failure-handler.js +44 -0
  11. package/dist/client/navigation-runtime.d.ts +3 -2
  12. package/dist/client/vinext-next-data.d.ts +18 -1
  13. package/dist/client/window-next.d.ts +8 -5
  14. package/dist/client/window-next.js +12 -1
  15. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  16. package/dist/config/config-matchers.d.ts +11 -4
  17. package/dist/config/config-matchers.js +88 -16
  18. package/dist/config/next-config.d.ts +59 -4
  19. package/dist/config/next-config.js +149 -48
  20. package/dist/deploy.d.ts +30 -11
  21. package/dist/deploy.js +189 -101
  22. package/dist/entries/app-browser-entry.d.ts +9 -3
  23. package/dist/entries/app-browser-entry.js +21 -3
  24. package/dist/entries/app-rsc-entry.d.ts +2 -0
  25. package/dist/entries/app-rsc-entry.js +71 -6
  26. package/dist/entries/app-rsc-manifest.js +2 -0
  27. package/dist/entries/app-ssr-entry.js +1 -1
  28. package/dist/entries/pages-client-entry.js +54 -9
  29. package/dist/entries/pages-server-entry.js +48 -11
  30. package/dist/index.d.ts +0 -2
  31. package/dist/index.js +285 -139
  32. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  33. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  34. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  35. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  36. package/dist/plugins/og-assets.js +2 -2
  37. package/dist/plugins/optimize-imports.d.ts +10 -5
  38. package/dist/plugins/optimize-imports.js +27 -21
  39. package/dist/plugins/postcss.js +7 -7
  40. package/dist/plugins/sass.d.ts +53 -24
  41. package/dist/plugins/sass.js +249 -1
  42. package/dist/plugins/typeof-window.d.ts +14 -0
  43. package/dist/plugins/typeof-window.js +150 -0
  44. package/dist/plugins/wasm-module-import.d.ts +15 -0
  45. package/dist/plugins/wasm-module-import.js +50 -0
  46. package/dist/routing/app-route-graph.d.ts +25 -2
  47. package/dist/routing/app-route-graph.js +91 -22
  48. package/dist/routing/file-matcher.d.ts +10 -1
  49. package/dist/routing/file-matcher.js +23 -2
  50. package/dist/routing/pages-router.js +3 -3
  51. package/dist/routing/utils.d.ts +35 -6
  52. package/dist/routing/utils.js +59 -7
  53. package/dist/server/api-handler.d.ts +6 -1
  54. package/dist/server/api-handler.js +21 -15
  55. package/dist/server/app-browser-action-result.d.ts +19 -6
  56. package/dist/server/app-browser-action-result.js +19 -10
  57. package/dist/server/app-browser-entry.js +269 -297
  58. package/dist/server/app-browser-error.d.ts +10 -3
  59. package/dist/server/app-browser-error.js +47 -6
  60. package/dist/server/app-browser-history-controller.d.ts +104 -0
  61. package/dist/server/app-browser-history-controller.js +210 -0
  62. package/dist/server/app-browser-hydration.d.ts +2 -0
  63. package/dist/server/app-browser-hydration.js +1 -0
  64. package/dist/server/app-browser-navigation-controller.d.ts +7 -4
  65. package/dist/server/app-browser-navigation-controller.js +33 -9
  66. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  67. package/dist/server/app-browser-rsc-redirect.js +30 -8
  68. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  69. package/dist/server/app-browser-server-action-navigation.js +9 -0
  70. package/dist/server/app-browser-state.js +4 -7
  71. package/dist/server/app-browser-stream.js +86 -43
  72. package/dist/server/app-browser-visible-commit.js +1 -1
  73. package/dist/server/app-elements-wire.d.ts +6 -1
  74. package/dist/server/app-elements-wire.js +14 -4
  75. package/dist/server/app-elements.d.ts +2 -2
  76. package/dist/server/app-elements.js +2 -2
  77. package/dist/server/app-fallback-renderer.d.ts +3 -1
  78. package/dist/server/app-fallback-renderer.js +6 -2
  79. package/dist/server/app-middleware.js +1 -0
  80. package/dist/server/app-optimistic-routing.js +24 -3
  81. package/dist/server/app-page-boundary-render.d.ts +3 -1
  82. package/dist/server/app-page-boundary-render.js +31 -16
  83. package/dist/server/app-page-cache-render.d.ts +53 -0
  84. package/dist/server/app-page-cache-render.js +91 -0
  85. package/dist/server/app-page-cache.d.ts +16 -2
  86. package/dist/server/app-page-cache.js +71 -8
  87. package/dist/server/app-page-dispatch.d.ts +34 -0
  88. package/dist/server/app-page-dispatch.js +167 -97
  89. package/dist/server/app-page-element-builder.d.ts +23 -2
  90. package/dist/server/app-page-element-builder.js +42 -10
  91. package/dist/server/app-page-execution.d.ts +7 -2
  92. package/dist/server/app-page-execution.js +53 -18
  93. package/dist/server/app-page-probe.d.ts +1 -0
  94. package/dist/server/app-page-probe.js +4 -0
  95. package/dist/server/app-page-render-observation.d.ts +3 -1
  96. package/dist/server/app-page-render-observation.js +17 -1
  97. package/dist/server/app-page-render.d.ts +13 -2
  98. package/dist/server/app-page-render.js +48 -17
  99. package/dist/server/app-page-request.d.ts +3 -0
  100. package/dist/server/app-page-request.js +5 -3
  101. package/dist/server/app-page-response.js +1 -1
  102. package/dist/server/app-page-route-wiring.d.ts +5 -1
  103. package/dist/server/app-page-route-wiring.js +21 -11
  104. package/dist/server/app-page-stream.d.ts +16 -9
  105. package/dist/server/app-page-stream.js +12 -9
  106. package/dist/server/app-pages-bridge.d.ts +18 -0
  107. package/dist/server/app-pages-bridge.js +22 -5
  108. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  109. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  110. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  111. package/dist/server/app-ppr-fallback-shell.js +8 -1
  112. package/dist/server/app-route-handler-dispatch.js +9 -2
  113. package/dist/server/app-route-handler-policy.d.ts +1 -0
  114. package/dist/server/app-route-handler-response.js +11 -10
  115. package/dist/server/app-route-handler-runtime.js +12 -1
  116. package/dist/server/app-router-entry.js +5 -0
  117. package/dist/server/app-rsc-cache-busting.js +2 -0
  118. package/dist/server/app-rsc-handler.d.ts +25 -0
  119. package/dist/server/app-rsc-handler.js +153 -53
  120. package/dist/server/app-rsc-response-finalizer.js +1 -1
  121. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  122. package/dist/server/app-rsc-route-matching.js +2 -0
  123. package/dist/server/app-segment-config.d.ts +9 -1
  124. package/dist/server/app-segment-config.js +12 -3
  125. package/dist/server/app-server-action-execution.d.ts +12 -0
  126. package/dist/server/app-server-action-execution.js +47 -15
  127. package/dist/server/app-ssr-entry.d.ts +2 -0
  128. package/dist/server/app-ssr-entry.js +81 -8
  129. package/dist/server/app-ssr-stream.js +9 -1
  130. package/dist/server/cache-control.js +4 -0
  131. package/dist/server/dev-lockfile.js +2 -1
  132. package/dist/server/dev-server.d.ts +2 -2
  133. package/dist/server/dev-server.js +287 -63
  134. package/dist/server/headers.d.ts +8 -1
  135. package/dist/server/headers.js +8 -1
  136. package/dist/server/hybrid-route-priority.d.ts +22 -0
  137. package/dist/server/hybrid-route-priority.js +33 -0
  138. package/dist/server/image-optimization.d.ts +18 -9
  139. package/dist/server/image-optimization.js +37 -23
  140. package/dist/server/implicit-tags.d.ts +2 -1
  141. package/dist/server/implicit-tags.js +4 -1
  142. package/dist/server/instrumentation-runtime.d.ts +6 -0
  143. package/dist/server/instrumentation-runtime.js +8 -0
  144. package/dist/server/isr-decision.d.ts +79 -0
  145. package/dist/server/isr-decision.js +70 -0
  146. package/dist/server/metadata-route-response.js +5 -3
  147. package/dist/server/middleware-runtime.d.ts +13 -0
  148. package/dist/server/middleware-runtime.js +11 -7
  149. package/dist/server/middleware.js +1 -0
  150. package/dist/server/navigation-planner.d.ts +186 -22
  151. package/dist/server/navigation-planner.js +302 -0
  152. package/dist/server/navigation-trace.d.ts +18 -1
  153. package/dist/server/navigation-trace.js +18 -1
  154. package/dist/server/normalize-path.d.ts +0 -8
  155. package/dist/server/normalize-path.js +3 -1
  156. package/dist/server/otel-tracer-extension.d.ts +45 -0
  157. package/dist/server/otel-tracer-extension.js +89 -0
  158. package/dist/server/pages-api-route.d.ts +20 -3
  159. package/dist/server/pages-api-route.js +19 -3
  160. package/dist/server/pages-asset-tags.d.ts +16 -4
  161. package/dist/server/pages-asset-tags.js +22 -12
  162. package/dist/server/pages-data-route.d.ts +8 -1
  163. package/dist/server/pages-data-route.js +16 -3
  164. package/dist/server/pages-get-initial-props.d.ts +54 -4
  165. package/dist/server/pages-get-initial-props.js +43 -1
  166. package/dist/server/pages-node-compat.d.ts +3 -11
  167. package/dist/server/pages-node-compat.js +175 -122
  168. package/dist/server/pages-page-data.d.ts +39 -2
  169. package/dist/server/pages-page-data.js +261 -46
  170. package/dist/server/pages-page-handler.d.ts +5 -2
  171. package/dist/server/pages-page-handler.js +78 -25
  172. package/dist/server/pages-page-response.d.ts +47 -2
  173. package/dist/server/pages-page-response.js +73 -9
  174. package/dist/server/pages-readiness.d.ts +1 -1
  175. package/dist/server/pages-request-pipeline.d.ts +16 -1
  176. package/dist/server/pages-request-pipeline.js +96 -38
  177. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  178. package/dist/server/pregenerated-concrete-paths.js +2 -19
  179. package/dist/server/prerender-manifest.d.ts +33 -0
  180. package/dist/server/prerender-manifest.js +54 -0
  181. package/dist/server/prerender-route-params.d.ts +1 -2
  182. package/dist/server/prod-server.d.ts +39 -1
  183. package/dist/server/prod-server.js +107 -37
  184. package/dist/server/request-pipeline.d.ts +3 -15
  185. package/dist/server/request-pipeline.js +58 -47
  186. package/dist/server/rsc-stream-hints.d.ts +5 -1
  187. package/dist/server/rsc-stream-hints.js +6 -1
  188. package/dist/server/seed-cache.js +10 -18
  189. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  190. package/dist/shims/app-router-scroll-state.js +14 -2
  191. package/dist/shims/app-router-scroll.d.ts +3 -0
  192. package/dist/shims/app-router-scroll.js +28 -18
  193. package/dist/shims/cache-runtime.js +12 -4
  194. package/dist/shims/cache.d.ts +1 -0
  195. package/dist/shims/cache.js +1 -1
  196. package/dist/shims/cdn-cache.d.ts +5 -5
  197. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  198. package/dist/shims/dynamic-preload-chunks.js +79 -0
  199. package/dist/shims/dynamic.d.ts +4 -0
  200. package/dist/shims/dynamic.js +4 -2
  201. package/dist/shims/error-boundary.d.ts +6 -4
  202. package/dist/shims/error-boundary.js +7 -0
  203. package/dist/shims/error.js +38 -11
  204. package/dist/shims/error.react-server.d.ts +9 -0
  205. package/dist/shims/error.react-server.js +6 -0
  206. package/dist/shims/fetch-cache.d.ts +11 -1
  207. package/dist/shims/fetch-cache.js +55 -20
  208. package/dist/shims/hash-scroll.js +6 -1
  209. package/dist/shims/head.js +6 -1
  210. package/dist/shims/headers.d.ts +16 -2
  211. package/dist/shims/headers.js +66 -5
  212. package/dist/shims/image-config.js +7 -1
  213. package/dist/shims/internal/als-registry.js +28 -1
  214. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  215. package/dist/shims/internal/app-route-detection.js +18 -23
  216. package/dist/shims/internal/app-router-context.d.ts +5 -0
  217. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  218. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  219. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  220. package/dist/shims/internal/navigation-untracked.js +55 -0
  221. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  222. package/dist/shims/internal/pages-data-target.js +17 -8
  223. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  224. package/dist/shims/internal/pages-router-accessor.js +13 -0
  225. package/dist/shims/internal/router-context.d.ts +2 -1
  226. package/dist/shims/internal/router-context.js +3 -1
  227. package/dist/shims/link.js +12 -5
  228. package/dist/shims/metadata.d.ts +6 -2
  229. package/dist/shims/metadata.js +32 -14
  230. package/dist/shims/navigation.d.ts +14 -17
  231. package/dist/shims/navigation.js +93 -46
  232. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  233. package/dist/shims/ppr-fallback-shell.js +28 -7
  234. package/dist/shims/router.d.ts +13 -2
  235. package/dist/shims/router.js +434 -116
  236. package/dist/shims/script-nonce-context.d.ts +1 -1
  237. package/dist/shims/script-nonce-context.js +11 -3
  238. package/dist/shims/server.d.ts +33 -2
  239. package/dist/shims/server.js +75 -18
  240. package/dist/shims/slot.js +1 -1
  241. package/dist/shims/unified-request-context.js +2 -0
  242. package/dist/typegen.js +1 -0
  243. package/dist/utils/built-asset-url.d.ts +4 -0
  244. package/dist/utils/built-asset-url.js +11 -0
  245. package/dist/utils/client-build-manifest.js +15 -5
  246. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  247. package/dist/utils/client-runtime-metadata.js +63 -0
  248. package/dist/utils/commonjs-loader.d.ts +16 -0
  249. package/dist/utils/commonjs-loader.js +100 -0
  250. package/dist/utils/deployment-id.d.ts +8 -0
  251. package/dist/utils/deployment-id.js +22 -0
  252. package/dist/utils/hash.d.ts +17 -1
  253. package/dist/utils/hash.js +36 -1
  254. package/dist/utils/html-limited-bots.d.ts +18 -1
  255. package/dist/utils/html-limited-bots.js +23 -1
  256. package/dist/utils/lazy-chunks.d.ts +27 -1
  257. package/dist/utils/lazy-chunks.js +65 -1
  258. package/dist/utils/manifest-paths.d.ts +20 -2
  259. package/dist/utils/manifest-paths.js +38 -3
  260. package/dist/utils/parse-cookie.d.ts +13 -0
  261. package/dist/utils/parse-cookie.js +52 -0
  262. package/dist/utils/path.d.ts +8 -1
  263. package/dist/utils/path.js +13 -1
  264. package/package.json +2 -2
  265. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  266. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -2,6 +2,11 @@
2
2
  /**
3
3
  * FNV-1a hash producing a 64-bit result (two 32-bit rounds with different seeds).
4
4
  * Used for deterministic key generation where collisions must be rare.
5
+ *
6
+ * This is a vinext-internal format: nothing outside vinext ever compares
7
+ * these values, so the algorithm only needs to be deterministic. For values
8
+ * that must be byte-for-byte identical to what Next.js emits (ETags), use
9
+ * `fnv1a52` below instead — the two are NOT interchangeable.
5
10
  */
6
11
  function fnv1a64(input) {
7
12
  let h1 = 2166136261;
@@ -16,5 +21,35 @@ function fnv1a64(input) {
16
21
  }
17
22
  return h1.toString(16).padStart(8, "0") + h2.toString(16).padStart(8, "0");
18
23
  }
24
+ /**
25
+ * FNV-1a hash producing a 52-bit result, a byte-for-byte port of Next.js's
26
+ * `fnv1a52` in packages/next/src/server/lib/etag.ts (itself derived from
27
+ * fnv-plus). Used for ETag generation, where matching Next.js's exact output
28
+ * matters: clients and CDNs holding `If-None-Match` values from a Next.js
29
+ * deployment keep revalidating (304) against vinext for unchanged payloads.
30
+ *
31
+ * Deliberately separate from `fnv1a64` above — that one is a vinext-internal
32
+ * key format and produces different values. Do not swap one for the other.
33
+ */
34
+ function fnv1a52(str) {
35
+ const len = str.length;
36
+ let i = 0, t0 = 0, v0 = 8997, t1 = 0, v1 = 33826, t2 = 0, v2 = 40164, t3 = 0, v3 = 52210;
37
+ while (i < len) {
38
+ v0 ^= str.charCodeAt(i++);
39
+ t0 = v0 * 435;
40
+ t1 = v1 * 435;
41
+ t2 = v2 * 435;
42
+ t3 = v3 * 435;
43
+ t2 += v0 << 8;
44
+ t3 += v1 << 8;
45
+ t1 += t0 >>> 16;
46
+ v0 = t0 & 65535;
47
+ t2 += t1 >>> 16;
48
+ v1 = t1 & 65535;
49
+ v3 = t3 + (t2 >>> 16) & 65535;
50
+ v2 = t2 & 65535;
51
+ }
52
+ return (v3 & 15) * 281474976710656 + v2 * 4294967296 + v1 * 65536 + (v0 ^ v3 >> 4);
53
+ }
19
54
  //#endregion
20
- export { fnv1a64 };
55
+ export { fnv1a52, fnv1a64 };
@@ -1,4 +1,21 @@
1
1
  //#region src/utils/html-limited-bots.d.ts
2
2
  declare function getHtmlLimitedBotRegex(htmlLimitedBots: string | undefined): RegExp;
3
+ /**
4
+ * Returns true when the User-Agent matches a known crawler/bot. Combines
5
+ * Next.js's "headless browser bot" check (Googlebot proper) with the
6
+ * "HTML-limited bot" list (Bingbot, DuckDuckBot, facebookexternalhit, …).
7
+ *
8
+ * Used by the Pages Router fallback path: a bot hitting an unlisted
9
+ * `fallback: true` route should get a synchronous render (real content) and
10
+ * not the loading shell, so the crawler indexes the actual page. Mirrors
11
+ * Next.js's `isBot()` in `.nextjs-ref/packages/next/src/shared/lib/router/utils/is-bot.ts`
12
+ * and the bot-aware fallback flip in
13
+ * `.nextjs-ref/packages/next/src/server/route-modules/pages/pages-handler.ts`.
14
+ *
15
+ * `htmlLimitedBots` allows next.config to override the HTML-limited list
16
+ * (same flag that drives `getHtmlLimitedBotRegex`), so a custom list applies
17
+ * to both streaming metadata gating and bot-aware fallback rendering.
18
+ */
19
+ declare function isBotUserAgent(userAgent: string, htmlLimitedBots?: string): boolean;
3
20
  //#endregion
4
- export { getHtmlLimitedBotRegex };
21
+ export { getHtmlLimitedBotRegex, isBotUserAgent };
@@ -1,5 +1,6 @@
1
1
  //#region src/utils/html-limited-bots.ts
2
2
  const HTML_LIMITED_BOT_UA_RE_STRING = String.raw`[\w-]+-Google|Google-[\w-]+|Chrome-Lighthouse|Slurp|DuckDuckBot|baiduspider|yandex|sogou|bitlybot|tumblr|vkShare|quora link preview|redditbot|ia_archiver|Bingbot|BingPreview|applebot|facebookexternalhit|facebookcatalog|Twitterbot|LinkedInBot|Slackbot|Discordbot|WhatsApp|SkypeUriPreview|Yeti|googleweblight`;
3
+ const HEADLESS_BROWSER_BOT_UA_RE = /Googlebot(?!-)|Googlebot$/i;
3
4
  const htmlLimitedBotRegexCache = /* @__PURE__ */ new Map();
4
5
  function getHtmlLimitedBotRegex(htmlLimitedBots) {
5
6
  const source = htmlLimitedBots || HTML_LIMITED_BOT_UA_RE_STRING;
@@ -9,5 +10,26 @@ function getHtmlLimitedBotRegex(htmlLimitedBots) {
9
10
  htmlLimitedBotRegexCache.set(source, regex);
10
11
  return regex;
11
12
  }
13
+ /**
14
+ * Returns true when the User-Agent matches a known crawler/bot. Combines
15
+ * Next.js's "headless browser bot" check (Googlebot proper) with the
16
+ * "HTML-limited bot" list (Bingbot, DuckDuckBot, facebookexternalhit, …).
17
+ *
18
+ * Used by the Pages Router fallback path: a bot hitting an unlisted
19
+ * `fallback: true` route should get a synchronous render (real content) and
20
+ * not the loading shell, so the crawler indexes the actual page. Mirrors
21
+ * Next.js's `isBot()` in `.nextjs-ref/packages/next/src/shared/lib/router/utils/is-bot.ts`
22
+ * and the bot-aware fallback flip in
23
+ * `.nextjs-ref/packages/next/src/server/route-modules/pages/pages-handler.ts`.
24
+ *
25
+ * `htmlLimitedBots` allows next.config to override the HTML-limited list
26
+ * (same flag that drives `getHtmlLimitedBotRegex`), so a custom list applies
27
+ * to both streaming metadata gating and bot-aware fallback rendering.
28
+ */
29
+ function isBotUserAgent(userAgent, htmlLimitedBots) {
30
+ if (!userAgent) return false;
31
+ if (HEADLESS_BROWSER_BOT_UA_RE.test(userAgent)) return true;
32
+ return getHtmlLimitedBotRegex(htmlLimitedBots).test(userAgent);
33
+ }
12
34
  //#endregion
13
- export { getHtmlLimitedBotRegex };
35
+ export { getHtmlLimitedBotRegex, isBotUserAgent };
@@ -29,5 +29,31 @@ type BuildManifestChunk = {
29
29
  * should be excluded from modulepreload hints.
30
30
  */
31
31
  declare function computeLazyChunks(buildManifest: Record<string, BuildManifestChunk>): string[];
32
+ /**
33
+ * Compute the production preload files for each module referenced by a
34
+ * `next/dynamic()` boundary.
35
+ *
36
+ * Next.js records module IDs during compilation, then resolves those IDs
37
+ * against its react-loadable manifest at render time. Vinext's equivalent
38
+ * source of truth is Vite's build manifest: each chunk lists the modules it
39
+ * reaches through `dynamicImports`, and each dynamic entry lists the JS/CSS
40
+ * files required to evaluate it.
41
+ *
42
+ * Note on shared chunks: a boundary's static-import tree (`collectStaticChunkFiles`)
43
+ * can include chunks that the page entry ALSO loads eagerly (a shared vendor
44
+ * chunk imported by both). Those files are intentionally NOT subtracted here, so
45
+ * a rendered boundary may emit a `<link rel="preload">` / `<link rel="stylesheet">`
46
+ * for a chunk the page already `<link rel="modulepreload">`s. This is harmless —
47
+ * the browser dedupes preloads by URL, `ReactDOM.preload()` dedupes script hints,
48
+ * and React's stylesheet resource model dedupes by href + precedence — and it
49
+ * mirrors Next.js listing a module's full file set in its react-loadable
50
+ * manifest. Subtracting the eager set would couple this to the entry's import
51
+ * closure for no correctness gain.
52
+ *
53
+ * @returns A map keyed by root-relative module ID, with JS/CSS files that
54
+ * should be preloaded when that dynamic boundary is rendered.
55
+ */
56
+ declare function computeDynamicImportPreloads(buildManifest: Record<string, BuildManifestChunk>): Record<string, string[]>;
57
+ declare function dynamicImportPreloadsWithBase(preloads: Record<string, string[]>, applyBase: (file: string) => string): Record<string, string[]>;
32
58
  //#endregion
33
- export { BuildManifestChunk, computeLazyChunks };
59
+ export { BuildManifestChunk, computeDynamicImportPreloads, computeLazyChunks, dynamicImportPreloadsWithBase };
@@ -44,5 +44,69 @@ function computeLazyChunks(buildManifest) {
44
44
  }
45
45
  return lazyChunks;
46
46
  }
47
+ function normalizeManifestKey(key) {
48
+ return key.split("?")[0].replace(/\\/g, "/").replace(/^\/+/, "");
49
+ }
50
+ function addFile(files, seen, file) {
51
+ if (!file || seen.has(file)) return;
52
+ seen.add(file);
53
+ files.push(file);
54
+ }
55
+ function collectStaticChunkFiles(buildManifest, key, files, seenFiles, visitedChunks) {
56
+ if (visitedChunks.has(key)) return;
57
+ visitedChunks.add(key);
58
+ const chunk = buildManifest[key];
59
+ if (!chunk) return;
60
+ if (chunk.file.endsWith(".js")) addFile(files, seenFiles, chunk.file);
61
+ for (const cssFile of chunk.css ?? []) addFile(files, seenFiles, cssFile);
62
+ for (const importedKey of chunk.imports ?? []) collectStaticChunkFiles(buildManifest, importedKey, files, seenFiles, visitedChunks);
63
+ }
64
+ /**
65
+ * Compute the production preload files for each module referenced by a
66
+ * `next/dynamic()` boundary.
67
+ *
68
+ * Next.js records module IDs during compilation, then resolves those IDs
69
+ * against its react-loadable manifest at render time. Vinext's equivalent
70
+ * source of truth is Vite's build manifest: each chunk lists the modules it
71
+ * reaches through `dynamicImports`, and each dynamic entry lists the JS/CSS
72
+ * files required to evaluate it.
73
+ *
74
+ * Note on shared chunks: a boundary's static-import tree (`collectStaticChunkFiles`)
75
+ * can include chunks that the page entry ALSO loads eagerly (a shared vendor
76
+ * chunk imported by both). Those files are intentionally NOT subtracted here, so
77
+ * a rendered boundary may emit a `<link rel="preload">` / `<link rel="stylesheet">`
78
+ * for a chunk the page already `<link rel="modulepreload">`s. This is harmless —
79
+ * the browser dedupes preloads by URL, `ReactDOM.preload()` dedupes script hints,
80
+ * and React's stylesheet resource model dedupes by href + precedence — and it
81
+ * mirrors Next.js listing a module's full file set in its react-loadable
82
+ * manifest. Subtracting the eager set would couple this to the entry's import
83
+ * closure for no correctness gain.
84
+ *
85
+ * @returns A map keyed by root-relative module ID, with JS/CSS files that
86
+ * should be preloaded when that dynamic boundary is rendered.
87
+ */
88
+ function computeDynamicImportPreloads(buildManifest) {
89
+ const preloads = {};
90
+ for (const chunk of Object.values(buildManifest)) for (const dynamicKey of chunk.dynamicImports ?? []) {
91
+ const files = [];
92
+ collectStaticChunkFiles(buildManifest, dynamicKey, files, /* @__PURE__ */ new Set(), /* @__PURE__ */ new Set());
93
+ if (files.length === 0) continue;
94
+ const normalizedKey = normalizeManifestKey(dynamicKey);
95
+ const existing = preloads[normalizedKey] ?? [];
96
+ const merged = new Set(existing);
97
+ for (const file of files) {
98
+ if (merged.has(file)) continue;
99
+ merged.add(file);
100
+ existing.push(file);
101
+ }
102
+ preloads[normalizedKey] = existing;
103
+ }
104
+ return preloads;
105
+ }
106
+ function dynamicImportPreloadsWithBase(preloads, applyBase) {
107
+ const withBase = {};
108
+ for (const [key, files] of Object.entries(preloads)) withBase[key] = files.map(applyBase);
109
+ return withBase;
110
+ }
47
111
  //#endregion
48
- export { computeLazyChunks };
112
+ export { computeDynamicImportPreloads, computeLazyChunks, dynamicImportPreloadsWithBase };
@@ -1,6 +1,24 @@
1
1
  //#region src/utils/manifest-paths.d.ts
2
2
  declare function manifestFileWithBase(file: string, base: string): string;
3
- declare function manifestFilesWithBase(files: string[], base: string): string[];
3
+ declare function manifestFileWithAssetPrefix(file: string, base: string, assetPrefix: string): string;
4
+ /**
5
+ * Resolve the URL a client asset is actually SERVED from, starting from a
6
+ * base-anchored manifest / SSR-manifest value (no leading slash), e.g.
7
+ * `"docs/cdn/_next/static/x.js"`.
8
+ *
9
+ * Next.js semantics: `assetPrefix` REPLACES `basePath` for asset URLs (they do
10
+ * not stack). So when an `assetPrefix` is configured we strip the basePath
11
+ * segment and re-anchor under the assetPrefix — matching how `next/dynamic`
12
+ * preloads are computed (`manifestFileWithAssetPrefix`) and what the prod server
13
+ * / Cloudflare ASSETS binding actually serve. Without this, a `basePath` +
14
+ * path-style `assetPrefix` build emits `/<basePath>/<assetPrefix>/_next/...`
15
+ * which 404s. When no `assetPrefix` is set, the base-anchored value is already
16
+ * the served URL and is returned unchanged (just root-anchored).
17
+ *
18
+ * Returns an absolute URL for an absolute `assetPrefix`, otherwise a
19
+ * root-relative URL beginning with `/`.
20
+ */
21
+ declare function assetServingUrlFromBaseAnchored(value: string, basePath: string, assetPrefix: string): string;
4
22
  /**
5
23
  * Strip a `base` prefix that Vite applied twice: it bakes `base` into the
6
24
  * on-disk chunk fileName and then prepends it again in `ssr-manifest.json`,
@@ -9,4 +27,4 @@ declare function manifestFilesWithBase(files: string[], base: string): string[];
9
27
  */
10
28
  declare function collapseDuplicateBase(file: string, base: string): string;
11
29
  //#endregion
12
- export { collapseDuplicateBase, manifestFileWithBase, manifestFilesWithBase };
30
+ export { assetServingUrlFromBaseAnchored, collapseDuplicateBase, manifestFileWithAssetPrefix, manifestFileWithBase };
@@ -1,3 +1,4 @@
1
+ import { ASSET_PREFIX_URL_DIR, isAbsoluteAssetPrefix, resolveAssetUrlPrefix, resolveAssetsDir } from "./asset-prefix.js";
1
2
  //#region src/utils/manifest-paths.ts
2
3
  function normalizeManifestFile(file) {
3
4
  return file.startsWith("/") ? file.slice(1) : file;
@@ -10,8 +11,42 @@ function manifestFileWithBase(file, base) {
10
11
  if (normalizedFile.startsWith(normalizedBase + "/")) return normalizedFile;
11
12
  return normalizedBase + "/" + normalizedFile;
12
13
  }
13
- function manifestFilesWithBase(files, base) {
14
- return files.map((file) => manifestFileWithBase(file, base));
14
+ function manifestFileWithAssetPrefix(file, base, assetPrefix) {
15
+ if (!assetPrefix) return manifestFileWithBase(file, base);
16
+ const normalizedFile = normalizeManifestFile(file);
17
+ const onDiskDirPrefix = normalizeManifestFile(resolveAssetsDir(assetPrefix)) + "/";
18
+ const staticDirPrefix = ASSET_PREFIX_URL_DIR + "/";
19
+ const stripped = normalizedFile.startsWith(onDiskDirPrefix) ? normalizedFile.slice(onDiskDirPrefix.length) : normalizedFile.startsWith(staticDirPrefix) ? normalizedFile.slice(staticDirPrefix.length) : normalizedFile;
20
+ const urlPrefix = resolveAssetUrlPrefix(assetPrefix);
21
+ return (isAbsoluteAssetPrefix(assetPrefix) ? urlPrefix : normalizeManifestFile(urlPrefix)) + stripped;
22
+ }
23
+ function stripBasePathSegment(file, basePath) {
24
+ const normalizedBase = normalizeManifestFile(basePath).replace(/\/+$/, "");
25
+ if (!normalizedBase) return file;
26
+ return file.startsWith(normalizedBase + "/") ? file.slice(normalizedBase.length + 1) : file;
27
+ }
28
+ /**
29
+ * Resolve the URL a client asset is actually SERVED from, starting from a
30
+ * base-anchored manifest / SSR-manifest value (no leading slash), e.g.
31
+ * `"docs/cdn/_next/static/x.js"`.
32
+ *
33
+ * Next.js semantics: `assetPrefix` REPLACES `basePath` for asset URLs (they do
34
+ * not stack). So when an `assetPrefix` is configured we strip the basePath
35
+ * segment and re-anchor under the assetPrefix — matching how `next/dynamic`
36
+ * preloads are computed (`manifestFileWithAssetPrefix`) and what the prod server
37
+ * / Cloudflare ASSETS binding actually serve. Without this, a `basePath` +
38
+ * path-style `assetPrefix` build emits `/<basePath>/<assetPrefix>/_next/...`
39
+ * which 404s. When no `assetPrefix` is set, the base-anchored value is already
40
+ * the served URL and is returned unchanged (just root-anchored).
41
+ *
42
+ * Returns an absolute URL for an absolute `assetPrefix`, otherwise a
43
+ * root-relative URL beginning with `/`.
44
+ */
45
+ function assetServingUrlFromBaseAnchored(value, basePath, assetPrefix) {
46
+ const normalized = normalizeManifestFile(value);
47
+ if (!assetPrefix) return "/" + normalized;
48
+ const anchored = manifestFileWithAssetPrefix(stripBasePathSegment(normalized, basePath), "/", assetPrefix);
49
+ return isAbsoluteAssetPrefix(assetPrefix) ? anchored : "/" + anchored;
15
50
  }
16
51
  /**
17
52
  * Strip a `base` prefix that Vite applied twice: it bakes `base` into the
@@ -28,4 +63,4 @@ function collapseDuplicateBase(file, base) {
28
63
  return normalizedFile.startsWith(doubledPrefix) ? normalizedFile.slice(normalizedBase.length + 1) : normalizedFile;
29
64
  }
30
65
  //#endregion
31
- export { collapseDuplicateBase, manifestFileWithBase, manifestFilesWithBase };
66
+ export { assetServingUrlFromBaseAnchored, collapseDuplicateBase, manifestFileWithAssetPrefix, manifestFileWithBase };
@@ -0,0 +1,13 @@
1
+ //#region src/utils/parse-cookie.d.ts
2
+ type ParsedCookies = Record<string, string>;
3
+ /**
4
+ * Parse a Cookie header using the semantics of Next.js's compiled `cookie`
5
+ * package.
6
+ */
7
+ declare function parseCookieHeader(cookieHeader: string | null | undefined): ParsedCookies;
8
+ /**
9
+ * Parse a Cookie header using Next.js/@edge-runtime RequestCookies semantics.
10
+ */
11
+ declare function parseEdgeRequestCookieHeader(cookieHeader: string): Map<string, string>;
12
+ //#endregion
13
+ export { ParsedCookies, parseCookieHeader, parseEdgeRequestCookieHeader };
@@ -0,0 +1,52 @@
1
+ //#region src/utils/parse-cookie.ts
2
+ function decodeCookieValue(value) {
3
+ try {
4
+ return {
5
+ ok: true,
6
+ value: decodeURIComponent(value)
7
+ };
8
+ } catch {
9
+ return { ok: false };
10
+ }
11
+ }
12
+ function forEachCookieHeaderPart(cookieHeader, visit) {
13
+ for (const part of cookieHeader.split(/; */)) {
14
+ if (!part) continue;
15
+ visit(part, part.indexOf("="));
16
+ }
17
+ }
18
+ /**
19
+ * Parse a Cookie header using the semantics of Next.js's compiled `cookie`
20
+ * package.
21
+ */
22
+ function parseCookieHeader(cookieHeader) {
23
+ const cookies = {};
24
+ if (!cookieHeader) return cookies;
25
+ forEachCookieHeaderPart(cookieHeader, (part, separator) => {
26
+ if (separator < 0) return;
27
+ const key = part.slice(0, separator).trim();
28
+ let value = part.slice(separator + 1).trim();
29
+ if (cookies[key] !== void 0) return;
30
+ if (value.startsWith("\"")) value = value.slice(1, -1);
31
+ const decoded = decodeCookieValue(value);
32
+ cookies[key] = decoded.ok ? decoded.value : value;
33
+ });
34
+ return cookies;
35
+ }
36
+ /**
37
+ * Parse a Cookie header using Next.js/@edge-runtime RequestCookies semantics.
38
+ */
39
+ function parseEdgeRequestCookieHeader(cookieHeader) {
40
+ const cookies = /* @__PURE__ */ new Map();
41
+ forEachCookieHeaderPart(cookieHeader, (part, separator) => {
42
+ if (separator === -1) {
43
+ cookies.set(part, "true");
44
+ return;
45
+ }
46
+ const decoded = decodeCookieValue(part.slice(separator + 1));
47
+ if (decoded.ok) cookies.set(part.slice(0, separator), decoded.value);
48
+ });
49
+ return cookies;
50
+ }
51
+ //#endregion
52
+ export { parseCookieHeader, parseEdgeRequestCookieHeader };
@@ -11,5 +11,12 @@
11
11
  * appear in filesystem paths on Linux/macOS.
12
12
  */
13
13
  declare function normalizePathSeparators(p: string): string;
14
+ declare function stripViteModuleQuery(id: string): string;
15
+ /** Strip a trailing `.js` extension from a module specifier so
16
+ * `resolveShimModulePath` looks for the correct base name (e.g. `headers.js`
17
+ * → `headers`). Public and internal shim imports may carry extensionful
18
+ * subpaths; normalising before resolution prevents looking for non-existent
19
+ * files like `headers.js.ts`. */
20
+ declare function stripJsExtension(name: string): string;
14
21
  //#endregion
15
- export { normalizePathSeparators };
22
+ export { normalizePathSeparators, stripJsExtension, stripViteModuleQuery };
@@ -14,5 +14,17 @@ const isWindows = process.platform === "win32";
14
14
  function normalizePathSeparators(p) {
15
15
  return isWindows ? p.replace(/\\/g, "/") : p;
16
16
  }
17
+ function stripViteModuleQuery(id) {
18
+ const queryIndex = id.search(/[?#]/);
19
+ return queryIndex === -1 ? id : id.slice(0, queryIndex);
20
+ }
21
+ /** Strip a trailing `.js` extension from a module specifier so
22
+ * `resolveShimModulePath` looks for the correct base name (e.g. `headers.js`
23
+ * → `headers`). Public and internal shim imports may carry extensionful
24
+ * subpaths; normalising before resolution prevents looking for non-existent
25
+ * files like `headers.js.ts`. */
26
+ function stripJsExtension(name) {
27
+ return name.endsWith(".js") ? name.slice(0, -3) : name;
28
+ }
17
29
  //#endregion
18
- export { normalizePathSeparators };
30
+ export { normalizePathSeparators, stripJsExtension, stripViteModuleQuery };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -101,7 +101,7 @@
101
101
  "react-server-dom-webpack": "^19.2.7",
102
102
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
103
103
  "vite-plus": "0.1.24",
104
- "@vinext/cloudflare": "0.1.0"
104
+ "@vinext/cloudflare": "0.1.2"
105
105
  },
106
106
  "peerDependencies": {
107
107
  "@mdx-js/rollup": "^3.0.0",
@@ -1,14 +0,0 @@
1
- //#region src/shims/internal/parse-cookie-header.d.ts
2
- /**
3
- * Port of the current Next.js/@edge-runtime request cookie parser semantics.
4
- *
5
- * Important details:
6
- * - split on a semicolon-plus-optional-spaces pattern
7
- * - preserve whitespace around names/values otherwise
8
- * - bare tokens become "true"
9
- * - malformed percent-encoded values are skipped
10
- * - duplicate names collapse to the last value via Map.set()
11
- */
12
- declare function parseCookieHeader(cookieHeader: string): Map<string, string>;
13
- //#endregion
14
- export { parseCookieHeader };
@@ -1,30 +0,0 @@
1
- //#region src/shims/internal/parse-cookie-header.ts
2
- /**
3
- * Port of the current Next.js/@edge-runtime request cookie parser semantics.
4
- *
5
- * Important details:
6
- * - split on a semicolon-plus-optional-spaces pattern
7
- * - preserve whitespace around names/values otherwise
8
- * - bare tokens become "true"
9
- * - malformed percent-encoded values are skipped
10
- * - duplicate names collapse to the last value via Map.set()
11
- */
12
- function parseCookieHeader(cookieHeader) {
13
- const cookies = /* @__PURE__ */ new Map();
14
- for (const pair of cookieHeader.split(/; */)) {
15
- if (!pair) continue;
16
- const splitAt = pair.indexOf("=");
17
- if (splitAt === -1) {
18
- cookies.set(pair, "true");
19
- continue;
20
- }
21
- const key = pair.slice(0, splitAt);
22
- const value = pair.slice(splitAt + 1);
23
- try {
24
- cookies.set(key, decodeURIComponent(value));
25
- } catch {}
26
- }
27
- return cookies;
28
- }
29
- //#endregion
30
- export { parseCookieHeader };