vinext 0.1.0 → 0.1.2

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 (205) hide show
  1. package/README.md +2 -5
  2. package/dist/build/assets-ignore.d.ts +32 -0
  3. package/dist/build/assets-ignore.js +48 -0
  4. package/dist/build/client-build-config.d.ts +33 -1
  5. package/dist/build/client-build-config.js +66 -1
  6. package/dist/check.js +4 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/client/navigation-runtime.d.ts +11 -2
  9. package/dist/client/navigation-runtime.js +1 -1
  10. package/dist/client/vinext-next-data.d.ts +2 -1
  11. package/dist/client/window-next.d.ts +6 -4
  12. package/dist/config/config-matchers.d.ts +31 -5
  13. package/dist/config/config-matchers.js +50 -3
  14. package/dist/config/next-config.d.ts +29 -3
  15. package/dist/config/next-config.js +32 -2
  16. package/dist/deploy.js +47 -304
  17. package/dist/entries/app-rsc-entry.d.ts +8 -2
  18. package/dist/entries/app-rsc-entry.js +61 -5
  19. package/dist/entries/app-rsc-manifest.js +20 -2
  20. package/dist/entries/pages-client-entry.js +1 -1
  21. package/dist/entries/pages-server-entry.js +16 -7
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +233 -280
  24. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  25. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  26. package/dist/plugins/og-assets.js +2 -2
  27. package/dist/plugins/optimize-imports.d.ts +8 -4
  28. package/dist/plugins/optimize-imports.js +16 -12
  29. package/dist/plugins/postcss.js +18 -14
  30. package/dist/plugins/require-context.d.ts +6 -0
  31. package/dist/plugins/require-context.js +184 -0
  32. package/dist/plugins/sass.d.ts +53 -24
  33. package/dist/plugins/sass.js +249 -1
  34. package/dist/plugins/wasm-module-import.d.ts +15 -0
  35. package/dist/plugins/wasm-module-import.js +50 -0
  36. package/dist/routing/app-route-graph.d.ts +35 -2
  37. package/dist/routing/app-route-graph.js +179 -8
  38. package/dist/routing/file-matcher.js +1 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +16 -1
  41. package/dist/server/api-handler.js +4 -0
  42. package/dist/server/app-browser-entry.js +155 -215
  43. package/dist/server/app-browser-error.d.ts +4 -1
  44. package/dist/server/app-browser-error.js +7 -1
  45. package/dist/server/app-browser-history-controller.d.ts +104 -0
  46. package/dist/server/app-browser-history-controller.js +210 -0
  47. package/dist/server/app-browser-interception-context.d.ts +2 -1
  48. package/dist/server/app-browser-interception-context.js +15 -2
  49. package/dist/server/app-browser-navigation-controller.d.ts +13 -2
  50. package/dist/server/app-browser-navigation-controller.js +83 -4
  51. package/dist/server/app-browser-popstate.d.ts +12 -3
  52. package/dist/server/app-browser-popstate.js +19 -4
  53. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  54. package/dist/server/app-browser-rsc-redirect.js +30 -8
  55. package/dist/server/app-browser-state.d.ts +3 -0
  56. package/dist/server/app-browser-state.js +10 -10
  57. package/dist/server/app-browser-visible-commit.js +10 -8
  58. package/dist/server/app-fallback-renderer.d.ts +2 -1
  59. package/dist/server/app-fallback-renderer.js +3 -1
  60. package/dist/server/app-history-state.d.ts +45 -1
  61. package/dist/server/app-history-state.js +109 -1
  62. package/dist/server/app-middleware.js +1 -0
  63. package/dist/server/app-optimistic-routing.js +22 -1
  64. package/dist/server/app-page-boundary-render.d.ts +2 -1
  65. package/dist/server/app-page-boundary-render.js +45 -21
  66. package/dist/server/app-page-cache.js +9 -7
  67. package/dist/server/app-page-dispatch.d.ts +14 -0
  68. package/dist/server/app-page-dispatch.js +21 -6
  69. package/dist/server/app-page-element-builder.d.ts +23 -2
  70. package/dist/server/app-page-element-builder.js +58 -17
  71. package/dist/server/app-page-execution.d.ts +1 -1
  72. package/dist/server/app-page-execution.js +32 -17
  73. package/dist/server/app-page-render.d.ts +7 -1
  74. package/dist/server/app-page-render.js +11 -16
  75. package/dist/server/app-page-request.d.ts +9 -6
  76. package/dist/server/app-page-request.js +14 -10
  77. package/dist/server/app-page-response.d.ts +2 -2
  78. package/dist/server/app-page-response.js +2 -2
  79. package/dist/server/app-page-route-wiring.d.ts +3 -1
  80. package/dist/server/app-page-route-wiring.js +10 -8
  81. package/dist/server/app-page-stream.d.ts +37 -7
  82. package/dist/server/app-page-stream.js +36 -6
  83. package/dist/server/app-pages-bridge.d.ts +16 -0
  84. package/dist/server/app-pages-bridge.js +23 -3
  85. package/dist/server/app-route-handler-cache.d.ts +1 -0
  86. package/dist/server/app-route-handler-cache.js +1 -0
  87. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  88. package/dist/server/app-route-handler-dispatch.js +2 -0
  89. package/dist/server/app-route-handler-execution.d.ts +1 -0
  90. package/dist/server/app-route-handler-execution.js +1 -0
  91. package/dist/server/app-route-handler-response.js +11 -10
  92. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  93. package/dist/server/app-route-handler-runtime.js +15 -3
  94. package/dist/server/app-rsc-handler.d.ts +1 -0
  95. package/dist/server/app-rsc-handler.js +5 -4
  96. package/dist/server/app-rsc-response-finalizer.js +1 -1
  97. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  98. package/dist/server/app-rsc-route-matching.js +29 -4
  99. package/dist/server/app-server-action-execution.d.ts +22 -1
  100. package/dist/server/app-server-action-execution.js +73 -12
  101. package/dist/server/app-ssr-entry.d.ts +6 -0
  102. package/dist/server/app-ssr-entry.js +19 -3
  103. package/dist/server/app-ssr-stream.js +9 -1
  104. package/dist/server/dev-lockfile.js +2 -1
  105. package/dist/server/dev-server.d.ts +1 -1
  106. package/dist/server/dev-server.js +97 -43
  107. package/dist/server/headers.d.ts +8 -1
  108. package/dist/server/headers.js +8 -1
  109. package/dist/server/instrumentation-runtime.d.ts +6 -0
  110. package/dist/server/instrumentation-runtime.js +8 -0
  111. package/dist/server/isr-cache.d.ts +37 -1
  112. package/dist/server/isr-cache.js +85 -1
  113. package/dist/server/isr-decision.d.ts +79 -0
  114. package/dist/server/isr-decision.js +70 -0
  115. package/dist/server/metadata-route-response.js +5 -3
  116. package/dist/server/middleware-runtime.d.ts +13 -0
  117. package/dist/server/middleware-runtime.js +11 -7
  118. package/dist/server/middleware.js +1 -0
  119. package/dist/server/navigation-planner.d.ts +62 -1
  120. package/dist/server/navigation-planner.js +193 -3
  121. package/dist/server/navigation-trace.d.ts +12 -2
  122. package/dist/server/navigation-trace.js +11 -1
  123. package/dist/server/normalize-path.d.ts +0 -8
  124. package/dist/server/normalize-path.js +3 -1
  125. package/dist/server/otel-tracer-extension.d.ts +45 -0
  126. package/dist/server/otel-tracer-extension.js +89 -0
  127. package/dist/server/pages-api-route.d.ts +14 -3
  128. package/dist/server/pages-api-route.js +6 -1
  129. package/dist/server/pages-asset-tags.d.ts +15 -4
  130. package/dist/server/pages-asset-tags.js +18 -12
  131. package/dist/server/pages-data-route.js +5 -1
  132. package/dist/server/pages-node-compat.d.ts +5 -11
  133. package/dist/server/pages-node-compat.js +175 -118
  134. package/dist/server/pages-page-data.d.ts +38 -7
  135. package/dist/server/pages-page-data.js +64 -18
  136. package/dist/server/pages-page-handler.d.ts +10 -2
  137. package/dist/server/pages-page-handler.js +49 -20
  138. package/dist/server/pages-page-response.d.ts +55 -2
  139. package/dist/server/pages-page-response.js +74 -6
  140. package/dist/server/pages-readiness.d.ts +36 -0
  141. package/dist/server/pages-readiness.js +21 -0
  142. package/dist/server/pages-request-pipeline.d.ts +113 -0
  143. package/dist/server/pages-request-pipeline.js +230 -0
  144. package/dist/server/pages-revalidate.d.ts +15 -0
  145. package/dist/server/pages-revalidate.js +19 -0
  146. package/dist/server/prod-server.d.ts +45 -3
  147. package/dist/server/prod-server.js +182 -234
  148. package/dist/server/socket-error-backstop.d.ts +19 -1
  149. package/dist/server/socket-error-backstop.js +77 -4
  150. package/dist/shims/app-router-scroll.js +22 -4
  151. package/dist/shims/cache-runtime.js +39 -2
  152. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  153. package/dist/shims/dynamic-preload-chunks.js +77 -0
  154. package/dist/shims/dynamic.d.ts +4 -0
  155. package/dist/shims/dynamic.js +4 -2
  156. package/dist/shims/error-boundary.d.ts +17 -7
  157. package/dist/shims/error-boundary.js +8 -1
  158. package/dist/shims/error.js +37 -11
  159. package/dist/shims/fetch-cache.d.ts +22 -1
  160. package/dist/shims/fetch-cache.js +28 -1
  161. package/dist/shims/hash-scroll.d.ts +1 -0
  162. package/dist/shims/hash-scroll.js +3 -1
  163. package/dist/shims/head.js +6 -1
  164. package/dist/shims/headers.d.ts +16 -2
  165. package/dist/shims/headers.js +37 -1
  166. package/dist/shims/image-config.js +7 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  168. package/dist/shims/internal/app-route-detection.js +10 -6
  169. package/dist/shims/internal/app-router-context.d.ts +5 -0
  170. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  171. package/dist/shims/internal/link-status-registry.js +42 -0
  172. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  173. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  174. package/dist/shims/internal/utils.d.ts +1 -0
  175. package/dist/shims/link.js +20 -6
  176. package/dist/shims/metadata.d.ts +6 -2
  177. package/dist/shims/metadata.js +32 -14
  178. package/dist/shims/navigation.d.ts +9 -18
  179. package/dist/shims/navigation.js +96 -23
  180. package/dist/shims/router-state.d.ts +1 -0
  181. package/dist/shims/router-state.js +2 -0
  182. package/dist/shims/router.d.ts +6 -3
  183. package/dist/shims/router.js +156 -22
  184. package/dist/shims/script-nonce-context.d.ts +1 -1
  185. package/dist/shims/script-nonce-context.js +11 -3
  186. package/dist/shims/server.d.ts +17 -1
  187. package/dist/shims/server.js +31 -6
  188. package/dist/shims/slot.js +1 -1
  189. package/dist/shims/unified-request-context.js +1 -0
  190. package/dist/typegen.js +1 -0
  191. package/dist/utils/client-build-manifest.d.ts +8 -1
  192. package/dist/utils/client-build-manifest.js +41 -6
  193. package/dist/utils/client-entry-manifest.d.ts +11 -0
  194. package/dist/utils/client-entry-manifest.js +29 -0
  195. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  196. package/dist/utils/client-runtime-metadata.js +63 -0
  197. package/dist/utils/hash.d.ts +17 -1
  198. package/dist/utils/hash.js +36 -1
  199. package/dist/utils/lazy-chunks.d.ts +27 -1
  200. package/dist/utils/lazy-chunks.js +65 -1
  201. package/dist/utils/manifest-paths.d.ts +20 -2
  202. package/dist/utils/manifest-paths.js +38 -3
  203. package/dist/utils/path.d.ts +2 -1
  204. package/dist/utils/path.js +5 -1
  205. package/package.json +6 -2
@@ -0,0 +1,63 @@
1
+ import { resolveAssetsDir } from "./asset-prefix.js";
2
+ import { manifestFileWithAssetPrefix, manifestFileWithBase } from "./manifest-paths.js";
3
+ import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "./client-build-manifest.js";
4
+ import { findClientEntryFileFromVinextManifest, findPagesClientEntryFileFromVinextManifest, readClientEntryManifest } from "./client-entry-manifest.js";
5
+ import { computeDynamicImportPreloads, computeLazyChunks, dynamicImportPreloadsWithBase } from "./lazy-chunks.js";
6
+ import path from "node:path";
7
+ //#region src/utils/client-runtime-metadata.ts
8
+ /**
9
+ * Read the client build manifest and compute runtime metadata used by
10
+ * Cloudflare worker entry injection and Node production server startup.
11
+ *
12
+ * - `lazyChunks` — chunks only reachable through dynamic `import()`, excluded
13
+ * from modulepreload hints.
14
+ * - `dynamicPreloads` — per-module JS/CSS files for rendered `next/dynamic()`
15
+ * boundaries, injected as preload links during SSR.
16
+ * - `clientEntryFile` — the client entry chunk filename (optional, only
17
+ * needed for Pages Router).
18
+ *
19
+ * All file paths are normalised with the configured `assetBase` (basePath)
20
+ * and `assetPrefix`.
21
+ */
22
+ function computeClientRuntimeMetadata(opts) {
23
+ const buildManifest = readClientBuildManifest(path.join(opts.clientDir, ".vite", "manifest.json"));
24
+ const metadata = {};
25
+ if (opts.includeClientEntry) {
26
+ const clientEntryManifest = readClientEntryManifest(opts.clientDir);
27
+ const entryOptions = {
28
+ buildManifest,
29
+ clientDir: opts.clientDir,
30
+ assetsSubdir: resolveAssetsDir(opts.assetPrefix),
31
+ assetBase: opts.assetBase
32
+ };
33
+ const entry = opts.includeClientEntry === "pages-client-entry" ? findPagesClientEntryFileFromVinextManifest(clientEntryManifest, opts.assetBase) ?? findPagesClientEntryFile(entryOptions) : findClientEntryFileFromVinextManifest(clientEntryManifest, opts.assetBase) ?? findClientEntryFile(entryOptions);
34
+ if (entry) metadata.clientEntryFile = entry;
35
+ }
36
+ if (!buildManifest) return metadata;
37
+ const lazyChunks = computeLazyChunks(buildManifest).map((file) => manifestFileWithBase(file, opts.assetBase));
38
+ if (lazyChunks.length > 0) metadata.lazyChunks = lazyChunks;
39
+ const dynamicPreloads = dynamicImportPreloadsWithBase(computeDynamicImportPreloads(buildManifest), (file) => manifestFileWithAssetPrefix(file, opts.assetBase, opts.assetPrefix));
40
+ if (Object.keys(dynamicPreloads).length > 0) metadata.dynamicPreloads = dynamicPreloads;
41
+ return metadata;
42
+ }
43
+ /**
44
+ * Serialize runtime metadata into the `globalThis.__VINEXT_*` assignment script
45
+ * that the Cloudflare `closeBundle` hook prepends to the worker entry. Returns
46
+ * `""` when there is nothing to inject.
47
+ *
48
+ * Both the App Router and Pages Router closeBundle paths call this (and the
49
+ * deploy tests mirror it), so the injection shape stays in one place. The caller
50
+ * decides which fields to pass — e.g. App Router only forwards `clientEntryFile`
51
+ * for mixed app+pages builds (where `computeClientRuntimeMetadata` was asked for
52
+ * the Pages client entry); pure App Router leaves it undefined.
53
+ */
54
+ function buildRuntimeGlobalsScript(input) {
55
+ const globals = [];
56
+ if (input.clientEntryFile) globals.push(`globalThis.__VINEXT_CLIENT_ENTRY__ = ${JSON.stringify(input.clientEntryFile)};`);
57
+ if (input.ssrManifest && Object.keys(input.ssrManifest).length > 0) globals.push(`globalThis.__VINEXT_SSR_MANIFEST__ = ${JSON.stringify(input.ssrManifest)};`);
58
+ if (input.lazyChunks && input.lazyChunks.length > 0) globals.push(`globalThis.__VINEXT_LAZY_CHUNKS__ = ${JSON.stringify(input.lazyChunks)};`);
59
+ if (input.dynamicPreloads && Object.keys(input.dynamicPreloads).length > 0) globals.push(`globalThis.__VINEXT_DYNAMIC_PRELOADS__ = ${JSON.stringify(input.dynamicPreloads)};`);
60
+ return globals.join("\n");
61
+ }
62
+ //#endregion
63
+ export { buildRuntimeGlobalsScript, computeClientRuntimeMetadata };
@@ -2,7 +2,23 @@
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
  declare function fnv1a64(input: string): string;
12
+ /**
13
+ * FNV-1a hash producing a 52-bit result, a byte-for-byte port of Next.js's
14
+ * `fnv1a52` in packages/next/src/server/lib/etag.ts (itself derived from
15
+ * fnv-plus). Used for ETag generation, where matching Next.js's exact output
16
+ * matters: clients and CDNs holding `If-None-Match` values from a Next.js
17
+ * deployment keep revalidating (304) against vinext for unchanged payloads.
18
+ *
19
+ * Deliberately separate from `fnv1a64` above — that one is a vinext-internal
20
+ * key format and produces different values. Do not swap one for the other.
21
+ */
22
+ declare function fnv1a52(str: string): number;
7
23
  //#endregion
8
- export { fnv1a64 };
24
+ export { fnv1a52, fnv1a64 };
@@ -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 };
@@ -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 };
@@ -11,5 +11,6 @@
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;
14
15
  //#endregion
15
- export { normalizePathSeparators };
16
+ export { normalizePathSeparators, stripViteModuleQuery };
@@ -14,5 +14,9 @@ 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
+ }
17
21
  //#endregion
18
- export { normalizePathSeparators };
22
+ export { normalizePathSeparators, stripViteModuleQuery };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -49,6 +49,10 @@
49
49
  "types": "./dist/server/request-pipeline.d.ts",
50
50
  "import": "./dist/server/request-pipeline.js"
51
51
  },
52
+ "./server/pages-request-pipeline": {
53
+ "types": "./dist/server/pages-request-pipeline.d.ts",
54
+ "import": "./dist/server/pages-request-pipeline.js"
55
+ },
52
56
  "./server/app-router-entry": {
53
57
  "types": "./dist/server/app-router-entry.d.ts",
54
58
  "import": "./dist/server/app-router-entry.js"
@@ -97,7 +101,7 @@
97
101
  "react-server-dom-webpack": "^19.2.7",
98
102
  "vite": "npm:@voidzero-dev/vite-plus-core@0.1.24",
99
103
  "vite-plus": "0.1.24",
100
- "@vinext/cloudflare": "0.1.0"
104
+ "@vinext/cloudflare": "0.1.1"
101
105
  },
102
106
  "peerDependencies": {
103
107
  "@mdx-js/rollup": "^3.0.0",