vinext 1.0.1 → 1.1.0

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 (159) hide show
  1. package/README.md +72 -1
  2. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  3. package/dist/build/app-page-static-eligibility.js +82 -0
  4. package/dist/build/prerender-paths.js +3 -78
  5. package/dist/build/prerender.js +33 -8
  6. package/dist/build/server-manifest.d.ts +7 -0
  7. package/dist/build/server-manifest.js +11 -1
  8. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  9. package/dist/cache/cache-adapters-virtual.js +3 -3
  10. package/dist/client/navigation-runtime.d.ts +11 -0
  11. package/dist/client/navigation-runtime.js +1 -1
  12. package/dist/config/config-matchers.js +3 -1
  13. package/dist/config/next-config.d.ts +7 -0
  14. package/dist/config/next-config.js +18 -3
  15. package/dist/entries/app-rsc-entry.d.ts +2 -0
  16. package/dist/entries/app-rsc-entry.js +64 -43
  17. package/dist/index.d.ts +4 -1
  18. package/dist/index.js +59 -30
  19. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  20. package/dist/plugins/cache-flight-codec.js +68 -0
  21. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  22. package/dist/plugins/commonjs-esm-facade.js +80 -0
  23. package/dist/plugins/fonts.d.ts +8 -11
  24. package/dist/plugins/fonts.js +71 -34
  25. package/dist/routing/file-matcher.d.ts +1 -0
  26. package/dist/routing/file-matcher.js +1 -1
  27. package/dist/server/api-handler.js +17 -7
  28. package/dist/server/app-browser-entry.js +11 -7
  29. package/dist/server/app-browser-history-controller.d.ts +2 -0
  30. package/dist/server/app-browser-history-controller.js +8 -0
  31. package/dist/server/app-browser-interception-context.js +26 -2
  32. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  33. package/dist/server/app-browser-navigation-controller.js +93 -10
  34. package/dist/server/app-fallback-renderer.d.ts +2 -0
  35. package/dist/server/app-fallback-renderer.js +1 -0
  36. package/dist/server/app-history-state.d.ts +3 -0
  37. package/dist/server/app-history-state.js +7 -0
  38. package/dist/server/app-middleware.js +2 -1
  39. package/dist/server/app-optimistic-routing.js +1 -1
  40. package/dist/server/app-page-boundary-render.d.ts +5 -0
  41. package/dist/server/app-page-boundary-render.js +6 -4
  42. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  43. package/dist/server/app-page-cache-finalizer.js +41 -7
  44. package/dist/server/app-page-cache-render.d.ts +18 -0
  45. package/dist/server/app-page-cache-render.js +63 -43
  46. package/dist/server/app-page-cache.d.ts +5 -0
  47. package/dist/server/app-page-cache.js +27 -7
  48. package/dist/server/app-page-dispatch.d.ts +9 -1
  49. package/dist/server/app-page-dispatch.js +47 -30
  50. package/dist/server/app-page-element-builder.d.ts +7 -0
  51. package/dist/server/app-page-element-builder.js +27 -42
  52. package/dist/server/app-page-execution.d.ts +15 -12
  53. package/dist/server/app-page-execution.js +30 -40
  54. package/dist/server/app-page-head.d.ts +5 -0
  55. package/dist/server/app-page-head.js +9 -3
  56. package/dist/server/app-page-probe.d.ts +0 -109
  57. package/dist/server/app-page-probe.js +3 -136
  58. package/dist/server/app-page-render-identity.d.ts +0 -1
  59. package/dist/server/app-page-render-identity.js +1 -1
  60. package/dist/server/app-page-render-observation.js +2 -2
  61. package/dist/server/app-page-render.d.ts +13 -5
  62. package/dist/server/app-page-render.js +219 -119
  63. package/dist/server/app-page-request.d.ts +0 -1
  64. package/dist/server/app-page-request.js +1 -2
  65. package/dist/server/app-page-response.d.ts +4 -3
  66. package/dist/server/app-page-response.js +4 -3
  67. package/dist/server/app-page-route-wiring.d.ts +17 -5
  68. package/dist/server/app-page-route-wiring.js +55 -33
  69. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  70. package/dist/server/app-page-rsc-render-status.js +54 -0
  71. package/dist/server/app-page-stream.d.ts +15 -8
  72. package/dist/server/app-page-stream.js +6 -0
  73. package/dist/server/app-request-stage-dispatch.js +1 -1
  74. package/dist/server/app-request-stage-independent-entry.js +4 -4
  75. package/dist/server/app-response-header-provenance.d.ts +2 -0
  76. package/dist/server/app-response-header-provenance.js +10 -4
  77. package/dist/server/app-route-handler-dispatch.js +1 -1
  78. package/dist/server/app-router-entry.js +3 -3
  79. package/dist/server/app-rsc-handler.d.ts +4 -0
  80. package/dist/server/app-rsc-handler.js +212 -96
  81. package/dist/server/app-rsc-request-normalization.d.ts +3 -1
  82. package/dist/server/app-rsc-request-normalization.js +4 -2
  83. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  84. package/dist/server/app-rsc-response-stage.js +6 -4
  85. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  86. package/dist/server/app-rsc-route-matching.js +36 -19
  87. package/dist/server/app-server-action-execution.js +1 -1
  88. package/dist/server/app-ssr-entry.d.ts +3 -2
  89. package/dist/server/app-ssr-entry.js +6 -4
  90. package/dist/server/app-ssr-stream.d.ts +6 -1
  91. package/dist/server/app-ssr-stream.js +34 -15
  92. package/dist/server/app-worker-stages.d.ts +2 -2
  93. package/dist/server/app-worker-stages.js +3 -3
  94. package/dist/server/cacheability-request.js +9 -5
  95. package/dist/server/config-headers.js +2 -0
  96. package/dist/server/dev-server.js +12 -2
  97. package/dist/server/headers.d.ts +14 -0
  98. package/dist/server/headers.js +15 -1
  99. package/dist/server/image-optimization.d.ts +16 -0
  100. package/dist/server/image-optimization.js +26 -6
  101. package/dist/server/isr-cache.js +2 -2
  102. package/dist/server/middleware-matcher-pattern.js +8 -4
  103. package/dist/server/middleware-response-headers.js +2 -0
  104. package/dist/server/multi-stage.d.ts +8 -0
  105. package/dist/server/navigation-planner.js +1 -1
  106. package/dist/server/node-response-signal.d.ts +16 -0
  107. package/dist/server/node-response-signal.js +26 -0
  108. package/dist/server/open-redirect.d.ts +47 -0
  109. package/dist/server/open-redirect.js +74 -1
  110. package/dist/server/pages-page-handler.js +21 -4
  111. package/dist/server/pages-request-stage-entry.js +8 -4
  112. package/dist/server/prod-server.d.ts +8 -7
  113. package/dist/server/prod-server.js +153 -69
  114. package/dist/server/request-pipeline.d.ts +20 -8
  115. package/dist/server/request-pipeline.js +21 -7
  116. package/dist/server/response-compression.d.ts +43 -0
  117. package/dist/server/response-compression.js +111 -0
  118. package/dist/server/seed-cache.js +10 -7
  119. package/dist/server/socket-error-backstop.d.ts +14 -0
  120. package/dist/server/socket-error-backstop.js +27 -4
  121. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  122. package/dist/shims/cache-adapter-instantiate.js +118 -0
  123. package/dist/shims/cache-callable-runtime.d.ts +2 -40
  124. package/dist/shims/cache-callable-runtime.js +50 -168
  125. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  126. package/dist/shims/cache-flight-arguments.js +56 -0
  127. package/dist/shims/cache-request-state.d.ts +15 -0
  128. package/dist/shims/cache-request-state.js +31 -8
  129. package/dist/shims/cache-runtime.d.ts +6 -19
  130. package/dist/shims/cache-runtime.js +165 -225
  131. package/dist/shims/cache.d.ts +2 -2
  132. package/dist/shims/cache.js +5 -4
  133. package/dist/shims/cacheability-classification.d.ts +7 -0
  134. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  135. package/dist/shims/error-boundary-navigation.js +19 -1
  136. package/dist/shims/error-boundary.d.ts +10 -10
  137. package/dist/shims/error-boundary.js +26 -7
  138. package/dist/shims/fetch-cache.d.ts +6 -0
  139. package/dist/shims/fetch-cache.js +19 -1
  140. package/dist/shims/form.js +1 -1
  141. package/dist/shims/headers.d.ts +13 -0
  142. package/dist/shims/headers.js +47 -4
  143. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  144. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  145. package/dist/shims/internal/headers-state.d.ts +0 -1
  146. package/dist/shims/metadata.js +3 -3
  147. package/dist/shims/navigation-errors.d.ts +6 -0
  148. package/dist/shims/navigation-errors.js +11 -1
  149. package/dist/shims/navigation.d.ts +7 -0
  150. package/dist/shims/navigation.js +14 -4
  151. package/dist/shims/request-context.d.ts +9 -0
  152. package/dist/shims/thenable-params.d.ts +4 -0
  153. package/dist/shims/thenable-params.js +25 -3
  154. package/dist/shims/unified-request-context.js +1 -0
  155. package/dist/utils/mdx-scan.d.ts +5 -0
  156. package/dist/utils/mdx-scan.js +8 -1
  157. package/dist/utils/regex-safety.d.ts +10 -0
  158. package/dist/utils/regex-safety.js +70 -18
  159. package/package.json +3 -3
package/README.md CHANGED
@@ -158,6 +158,42 @@ Environment variables: `PORT` (default `3000`), `HOST` (default `0.0.0.0`).
158
158
 
159
159
  > **Note:** Next.js standalone uses `HOSTNAME` for the bind address, but vinext uses `HOST` to avoid collision with the system-set `HOSTNAME` variable on Linux. Update your deployment config accordingly.
160
160
 
161
+ #### Custom standalone entries
162
+
163
+ The generated `server.js` is a small wrapper around `startProdServer` from `vinext/server/prod-server`. To control where the server loads the build output from, write your own entry and call `startProdServer` directly. All options are optional:
164
+
165
+ | Option | Default | Description |
166
+ | ----------------- | ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
167
+ | `port` | `PORT` env var, otherwise `3000` | Port to listen on. |
168
+ | `host` | `"0.0.0.0"` | Address to bind to. Unlike the generated `server.js`, `startProdServer` doesn't read `HOST`, so pass `process.env.HOST` yourself if you need it. |
169
+ | `outDir` | `dist`, resolved against the current directory | Build output directory containing `client/` and `server/`. Relative paths resolve against the current directory. In standalone output, this is `dist/standalone/dist`. |
170
+ | `serverDir` | `<outDir>/server` | Directory containing the server bundles, manifests, and prerendered output. |
171
+ | `rscEntryPath` | `<serverDir>/index.js` | App Router server entry. If this file exists, the server runs in App Router mode. |
172
+ | `serverEntryPath` | `<serverDir>/entry.js` | Pages Router server entry. Used when there's no App Router entry. |
173
+ | `noCompression` | `false` | Disable response compression. |
174
+
175
+ `ProdServerOptions` also has `purpose` and `silent`, which vinext uses for its own build-time servers. Leave them unset. The returned promise resolves to `{ server, port }` once the server is listening.
176
+
177
+ **Compiled single-file binaries.** A `bun build --compile` binary can't use the generated `server.js`. Bun fixes `import.meta.dirname` when it compiles the binary, so `join(import.meta.dirname, "dist")` doesn't point at the directory the binary runs from. Depending on the Bun version, it points at the build machine's path or at Bun's embedded `/$bunfs/root`. Resolve `outDir` from the executable's location instead:
178
+
179
+ ```js
180
+ // server-bin.mjs, in your project root
181
+ import path from "node:path";
182
+ import { startProdServer } from "vinext/server/prod-server";
183
+
184
+ await startProdServer({
185
+ port: Number.parseInt(process.env.PORT ?? "3000", 10),
186
+ host: process.env.HOST ?? "0.0.0.0",
187
+ outDir: path.join(path.dirname(process.execPath), "dist"),
188
+ });
189
+ ```
190
+
191
+ ```bash
192
+ bun build --compile --compile-autoload-package-json server-bin.mjs --outfile dist/standalone/server
193
+ ```
194
+
195
+ Keep the entry outside `dist/standalone`: each build deletes and recreates that directory. Compile after `vite build`, then ship the whole `dist/standalone` directory, because the `server` binary needs the `dist/` and `node_modules/` directories next to it. The server bundles in `dist/server` still import their dependencies from `node_modules` at runtime. `--compile-autoload-package-json` lets the binary read package `exports` and `main` fields when it resolves them. Under plain Node, `process.execPath` is the Node binary, so use this entry only for compiled builds.
196
+
161
197
  ### Starting a new vinext project
162
198
 
163
199
  Use `create-vinext-app` for new projects. It creates a TypeScript App Router project
@@ -792,7 +828,7 @@ their `env` binding lookups) are instantiated lazily on the first request.
792
828
 
793
829
  Registration is wired into **every router and runtime** — App Router and Pages Router, on Cloudflare Workers as well as the Node.js server (`vinext start`) and dev. It self-guards (instantiated once per isolate) and is resilient: if an adapter can't initialize on a given runtime (e.g. a KV binding doesn't exist on the Node server), vinext logs a warning and falls back to the default handler instead of failing requests.
794
830
 
795
- To write your own adapter, point a slot at any module by path and default-export a factory that receives `{ env, options }` at runtime and returns a data-cache `CacheHandler` (or a CDN adapter):
831
+ To write your own adapter, point a slot at any module by path. Its default export receives one `{ env, options }` argument at runtime and produces a data-cache `CacheHandler` (or a CDN adapter):
796
832
 
797
833
  ```ts
798
834
  vinext({
@@ -805,6 +841,41 @@ vinext({
805
841
  });
806
842
  ```
807
843
 
844
+ The default export can be a factory or a class:
845
+
846
+ ```ts
847
+ // my-adapter.js: a factory
848
+ export default ({ env, options }) => ({
849
+ async get(key) {
850
+ /* … */
851
+ },
852
+ async set(key, data) {
853
+ /* … */
854
+ },
855
+ async revalidateTag(tags) {
856
+ /* … */
857
+ },
858
+ });
859
+
860
+ // my-adapter.js: or a class
861
+ export default class MyAdapter {
862
+ constructor({ env, options }) {
863
+ /* … */
864
+ }
865
+ async get(key) {
866
+ /* … */
867
+ }
868
+ async set(key, data) {
869
+ /* … */
870
+ }
871
+ async revalidateTag(tags) {
872
+ /* … */
873
+ }
874
+ }
875
+ ```
876
+
877
+ vinext invokes class syntax with `new` and calls any other function, so existing factories keep plain-call semantics. A constructor that isn't class syntax (an ES5-compiled, bound, or Proxy-wrapped class) can't be told apart from a factory, so export a factory that constructs it: `export default (args) => new MyAdapter(args)`. The result must be the adapter itself, not a Promise. A data adapter implements `get`, `set`, and `revalidateTag`; a CDN adapter also implements `buildResponseHeaders` and sets `ownsBackgroundRevalidation` to a boolean. If the export isn't a function or the result is missing a member, vinext logs a warning naming the slot and what's wrong, then keeps the default handler.
878
+
808
879
  ## What's NOT supported (and won't be)
809
880
 
810
881
  These are intentional exclusions. For things that are missing today but on the roadmap, see [Known gaps we're working on](#known-gaps-were-working-on) above.
@@ -0,0 +1,15 @@
1
+ import { AppRoute } from "../routing/app-route-graph.js";
2
+ //#region src/build/app-page-static-eligibility.d.ts
3
+ /**
4
+ * Whether Next.js classifies an App page route as static or SSG, read from its
5
+ * layout, page and parallel-slot sources with the helpers dispatch applies to
6
+ * the loaded modules. Only such a route has listed paths.
7
+ *
8
+ * An MDX source read without the MDX parser has unknown exports, so its route
9
+ * is "unreadable". Its paths are still listed: the built runtime reads the real
10
+ * exports and never stores a route that isn't static, while an unlisted path's
11
+ * render failure would be dropped instead of failing. But no probe renders an
12
+ * unreadable route's fallback, so it never certifies one.
13
+ */
14
+ export declare function classifyAppPageRouteStaticEligibility(route: AppRoute, readMdxEsm: ((source: string) => string) | null): "eligible" | "ineligible" | "unreadable";
15
+ //#endregion
@@ -0,0 +1,82 @@
1
+ import { extractExportConstNumber, extractExportConstString, hasRuntimeExportedName } from "./report.js";
2
+ import { collectAppPageStaticGenerationRuntimes, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "../server/app-segment-config.js";
3
+ import fs from "node:fs";
4
+ //#region src/build/app-page-static-eligibility.ts
5
+ /**
6
+ * Whether Next.js classifies an App page route as static or SSG, read from its
7
+ * layout, page and parallel-slot sources with the helpers dispatch applies to
8
+ * the loaded modules. Only such a route has listed paths.
9
+ *
10
+ * An MDX source read without the MDX parser has unknown exports, so its route
11
+ * is "unreadable". Its paths are still listed: the built runtime reads the real
12
+ * exports and never stores a route that isn't static, while an unlisted path's
13
+ * render failure would be dropped instead of failing. But no probe renders an
14
+ * unreadable route's fallback, so it never certifies one.
15
+ */
16
+ function classifyAppPageRouteStaticEligibility(route, readMdxEsm) {
17
+ let unreadable = false;
18
+ const readSegmentConfig = (filePath) => {
19
+ if (!filePath) return null;
20
+ let code = fs.readFileSync(filePath, "utf8");
21
+ if (filePath.toLowerCase().endsWith(".mdx")) {
22
+ let esm = null;
23
+ try {
24
+ esm = readMdxEsm?.(code) ?? null;
25
+ } catch {}
26
+ unreadable ||= esm === null;
27
+ code = esm ?? "";
28
+ }
29
+ const dynamic = extractExportConstString(code, "dynamic");
30
+ const revalidate = extractExportConstNumber(code, "revalidate");
31
+ const runtime = extractExportConstString(code, "runtime");
32
+ return {
33
+ ...dynamic === null ? {} : { dynamic },
34
+ ...hasRuntimeExportedName(code, "generateStaticParams") ? { generateStaticParams() {} } : {},
35
+ ...revalidate === null ? {} : { revalidate },
36
+ ...runtime === null ? {} : { runtime }
37
+ };
38
+ };
39
+ const layouts = route.layouts.map(readSegmentConfig);
40
+ const page = readSegmentConfig(route.pagePath);
41
+ const parallelBranches = route.parallelSlots.map((slot) => ({
42
+ configLayouts: (slot.configLayoutPaths ?? []).map(readSegmentConfig),
43
+ configLayoutTreePositions: slot.configLayoutTreePositions ?? [],
44
+ isDefault: !slot.pagePath,
45
+ layout: readSegmentConfig(slot.layoutPath),
46
+ name: slot.name,
47
+ ownerTreePosition: slot.ownerTreePosition ?? null,
48
+ page: readSegmentConfig(slot.pagePath ?? slot.defaultPath),
49
+ routeSegments: slot.routeSegments
50
+ }));
51
+ if (unreadable) return "unreadable";
52
+ const segmentConfig = resolveAppPageSegmentConfig({
53
+ layouts,
54
+ layoutTreePositions: route.layoutTreePositions,
55
+ page,
56
+ parallelBranches,
57
+ routeSegments: route.routeSegments
58
+ });
59
+ return isAppPageStaticEligible({
60
+ dynamicConfig: segmentConfig.dynamicConfig,
61
+ hasGenerateStaticParams: hasAppPageGenerateStaticParamsAtLastDynamicSegment({
62
+ childrenSlot: route.childrenSlot ?? null,
63
+ layouts,
64
+ layoutTreePositions: route.layoutTreePositions,
65
+ page,
66
+ parallelBranches,
67
+ routeSegments: route.routeSegments
68
+ }),
69
+ isDynamicRoute: route.isDynamic,
70
+ isStaticGenerationEdgeRuntime: isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes({
71
+ childrenSlot: route.childrenSlot ?? null,
72
+ layouts,
73
+ layoutTreePositions: route.layoutTreePositions,
74
+ page,
75
+ parallelBranches,
76
+ routeSegments: route.routeSegments
77
+ }))),
78
+ revalidateSeconds: segmentConfig.revalidateSeconds
79
+ }) ? "eligible" : "ineligible";
80
+ }
81
+ //#endregion
82
+ export { classifyAppPageRouteStaticEligibility };
@@ -10,17 +10,18 @@ import { apiRouter, matchRoute, pagesRouter } from "../routing/pages-router.js";
10
10
  import "../server/headers.js";
11
11
  import { matchesMiddlewarePathname } from "../server/middleware-matcher.js";
12
12
  import { extractLocaleFromUrl, normalizeDefaultLocalePathname } from "../server/pages-i18n.js";
13
- import { classifyAppRoute, classifyAppRouteHandler, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractMiddlewareMatcherConfig, getAppRouteRenderEntryPath, hasRuntimeExportedName } from "./report.js";
13
+ import { classifyAppRoute, classifyAppRouteHandler, classifyPagesRoute, extractExportConstString, extractMiddlewareMatcherConfig, getAppRouteRenderEntryPath } from "./report.js";
14
14
  import { normalizeStaticPathname, normalizeStaticPathsEntry } from "../routing/route-pattern.js";
15
15
  import { buildPagesDataHref } from "../shims/internal/pages-data-url.js";
16
16
  import { normalizePathTrailingSlash } from "../shims/url-utils.js";
17
+ import { resolveAppPageDynamicConfig } from "../server/app-segment-config.js";
18
+ import { classifyAppPageRouteStaticEligibility } from "./app-page-static-eligibility.js";
17
19
  import { pagesRouteHasPriorityOverAppRoute } from "../server/hybrid-route-priority.js";
18
20
  import { readPrerenderSecret } from "./server-manifest.js";
19
21
  import { loadMdxEsmReader } from "../utils/mdx-scan.js";
20
22
  import { startProdServer } from "../server/prod-server.js";
21
23
  import { enterPrerenderPhase } from "./prerender-phase.js";
22
24
  import { buildUrlFromParams, layoutOnlyParamSets, localizePagesPath, resolveParentParams, routeStaticParamSets, validateDiscoveredParams } from "./prerender.js";
23
- import { collectAppPageStaticGenerationRuntimes, hasAppPageGenerateStaticParamsAtLastDynamicSegment, isAppPageStaticEligible, isEdgeRuntime, resolveAppPageDynamicConfig, resolveAppPageSegmentConfig, resolveAppPageStaticGenerationRuntime } from "../server/app-segment-config.js";
24
25
  import { resolveBuiltPagesEntryPath, resolveBuiltRscEntryPath } from "./server-entry.js";
25
26
  import fs from "node:fs";
26
27
  //#region src/build/prerender-paths.ts
@@ -351,82 +352,6 @@ function extractPagesStaticPathLocale(url, i18n) {
351
352
  url: `${rest || "/"}${query}`
352
353
  };
353
354
  }
354
- /**
355
- * Whether Next.js classifies an App page route as static or SSG, read from its
356
- * layout, page and parallel-slot sources with the helpers dispatch applies to
357
- * the loaded modules. Only such a route has listed paths.
358
- *
359
- * An MDX source read without the MDX parser has unknown exports, so its route
360
- * is "unreadable". Its paths are still listed: the built runtime reads the real
361
- * exports and never stores a route that isn't static, while an unlisted path's
362
- * render failure would be dropped instead of failing. But no probe renders an
363
- * unreadable route's fallback, so it never certifies one.
364
- */
365
- function classifyAppPageRouteStaticEligibility(route, readMdxEsm) {
366
- let unreadable = false;
367
- const readSegmentConfig = (filePath) => {
368
- if (!filePath) return null;
369
- let code = fs.readFileSync(filePath, "utf8");
370
- if (filePath.toLowerCase().endsWith(".mdx")) {
371
- let esm = null;
372
- try {
373
- esm = readMdxEsm?.(code) ?? null;
374
- } catch {}
375
- unreadable ||= esm === null;
376
- code = esm ?? "";
377
- }
378
- const dynamic = extractExportConstString(code, "dynamic");
379
- const revalidate = extractExportConstNumber(code, "revalidate");
380
- const runtime = extractExportConstString(code, "runtime");
381
- return {
382
- ...dynamic === null ? {} : { dynamic },
383
- ...hasRuntimeExportedName(code, "generateStaticParams") ? { generateStaticParams() {} } : {},
384
- ...revalidate === null ? {} : { revalidate },
385
- ...runtime === null ? {} : { runtime }
386
- };
387
- };
388
- const layouts = route.layouts.map(readSegmentConfig);
389
- const page = readSegmentConfig(route.pagePath);
390
- const parallelBranches = route.parallelSlots.map((slot) => ({
391
- configLayouts: (slot.configLayoutPaths ?? []).map(readSegmentConfig),
392
- configLayoutTreePositions: slot.configLayoutTreePositions ?? [],
393
- isDefault: !slot.pagePath,
394
- layout: readSegmentConfig(slot.layoutPath),
395
- name: slot.name,
396
- ownerTreePosition: slot.ownerTreePosition ?? null,
397
- page: readSegmentConfig(slot.pagePath ?? slot.defaultPath),
398
- routeSegments: slot.routeSegments
399
- }));
400
- if (unreadable) return "unreadable";
401
- const segmentConfig = resolveAppPageSegmentConfig({
402
- layouts,
403
- layoutTreePositions: route.layoutTreePositions,
404
- page,
405
- parallelBranches,
406
- routeSegments: route.routeSegments
407
- });
408
- return isAppPageStaticEligible({
409
- dynamicConfig: segmentConfig.dynamicConfig,
410
- hasGenerateStaticParams: hasAppPageGenerateStaticParamsAtLastDynamicSegment({
411
- childrenSlot: route.childrenSlot ?? null,
412
- layouts,
413
- layoutTreePositions: route.layoutTreePositions,
414
- page,
415
- parallelBranches,
416
- routeSegments: route.routeSegments
417
- }),
418
- isDynamicRoute: route.isDynamic,
419
- isStaticGenerationEdgeRuntime: isEdgeRuntime(resolveAppPageStaticGenerationRuntime(collectAppPageStaticGenerationRuntimes({
420
- childrenSlot: route.childrenSlot ?? null,
421
- layouts,
422
- layoutTreePositions: route.layoutTreePositions,
423
- page,
424
- parallelBranches,
425
- routeSegments: route.routeSegments
426
- }))),
427
- revalidateSeconds: segmentConfig.revalidateSeconds
428
- }) ? "eligible" : "ineligible";
429
- }
430
355
  /** Keys a path an App page route lists by the route and the pathname together. */
431
356
  function appPageListingKey(routePattern, pathname) {
432
357
  return `${routePattern}\0${pathname}`;
@@ -4,7 +4,7 @@ import { isUnknownRecord } from "../utils/record.js";
4
4
  import { BLOCKED_PAGES } from "../shims/constants.js";
5
5
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
6
6
  import { appRouteLayoutStaticParamsGroups } from "../routing/app-router.js";
7
- import { NEXT_CACHE_TAGS_HEADER, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER } from "../server/headers.js";
7
+ import { NEXT_CACHE_TAGS_HEADER, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "../server/headers.js";
8
8
  import { extractLocaleFromUrl } from "../server/pages-i18n.js";
9
9
  import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
10
10
  import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
@@ -12,6 +12,7 @@ import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cac
12
12
  import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
13
13
  import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
14
14
  import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
15
+ import { createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
15
16
  import { readPrerenderSecret } from "./server-manifest.js";
16
17
  import { buildPregeneratedConcretePathTable } from "../server/prerender-manifest.js";
17
18
  import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
@@ -1041,17 +1042,22 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1041
1042
  const responseCacheLife = readPrerenderCacheLifeHeader(response.headers);
1042
1043
  const cacheTags = readPrerenderCacheTagsHeader(response.headers);
1043
1044
  const fatal = response.headers.get(VINEXT_PRERENDER_RENDER_ERROR_HEADER) === "1";
1044
- if (!response.ok || cacheControl.includes("no-store")) {
1045
+ const specialError = mode === "export" ? null : readPrerenderSpecialErrorHeader(response.headers);
1046
+ const isSpecialError = specialError !== null;
1047
+ const ok = response.ok || isSpecialError;
1048
+ if (!ok || cacheControl.includes("no-store")) {
1045
1049
  await response.body?.cancel();
1046
1050
  return {
1047
1051
  cacheControl,
1048
1052
  linkHeader,
1049
1053
  html: null,
1050
- ok: response.ok,
1054
+ ok,
1051
1055
  requestCacheLife: null,
1052
1056
  tags: [],
1053
1057
  status: response.status,
1054
- fatal
1058
+ fatal,
1059
+ specialErrorLocation: null,
1060
+ isSpecialError
1055
1061
  };
1056
1062
  }
1057
1063
  const html = await response.text();
@@ -1064,7 +1070,9 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1064
1070
  requestCacheLife: responseCacheLife ?? processCacheLife,
1065
1071
  status: response.status,
1066
1072
  tags: cacheTags,
1067
- fatal: false
1073
+ fatal: false,
1074
+ specialErrorLocation: specialError?.location ?? null,
1075
+ isSpecialError
1068
1076
  };
1069
1077
  });
1070
1078
  const htmlCacheControl = htmlRender.cacheControl;
@@ -1092,7 +1100,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1092
1100
  error: "RSC handler returned no prerender HTML"
1093
1101
  };
1094
1102
  const html = isFallback ? markAppPprDynamicFallbackShellHtml(htmlRender.html) : htmlRender.html;
1095
- let rscData = extractRscPayloadFromPrerenderedHtml(html);
1103
+ let rscData = htmlRender.isSpecialError ? null : extractRscPayloadFromPrerenderedHtml(html);
1096
1104
  if (rscData === null) {
1097
1105
  const rscHeaders = new Headers({
1098
1106
  Accept: "text/x-component",
@@ -1100,7 +1108,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1100
1108
  });
1101
1109
  if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
1102
1110
  if (isSpeculative) rscHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
1103
- const rscRequest = new Request(`http://localhost${requestPath}`, { headers: rscHeaders });
1111
+ const rscRequest = new Request(`http://localhost${await createRscRequestUrl(requestPath, rscHeaders)}`, { headers: rscHeaders });
1104
1112
  const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
1105
1113
  if (!rscRes.ok) {
1106
1114
  await rscRes.body?.cancel();
@@ -1135,7 +1143,11 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1135
1143
  ...renderedStale === void 0 ? {} : { stale: renderedStale },
1136
1144
  router: "app",
1137
1145
  ...htmlRender.tags.length > 0 ? { tags: htmlRender.tags } : {},
1138
- ...htmlRender.linkHeader ? { headers: { link: htmlRender.linkHeader } } : {},
1146
+ ...htmlRender.linkHeader || htmlRender.specialErrorLocation !== null ? { headers: {
1147
+ ...htmlRender.linkHeader ? { link: htmlRender.linkHeader } : {},
1148
+ ...htmlRender.specialErrorLocation !== null ? { location: htmlRender.specialErrorLocation } : {}
1149
+ } } : {},
1150
+ ...htmlRender.isSpecialError ? { responseStatus: htmlRender.status } : {},
1139
1151
  ...urlPath !== routePattern ? { path: urlPath } : {},
1140
1152
  ...isFallback ? { fallback: true } : {}
1141
1153
  };
@@ -1239,6 +1251,19 @@ function readPrerenderCacheLifeHeader(headers) {
1239
1251
  return null;
1240
1252
  }
1241
1253
  }
1254
+ /** Reads the special-error marker: the page's stored headers, or null. */
1255
+ function readPrerenderSpecialErrorHeader(headers) {
1256
+ const value = headers.get(VINEXT_PRERENDER_SPECIAL_ERROR_HEADER);
1257
+ if (!value) return null;
1258
+ try {
1259
+ const parsed = JSON.parse(value);
1260
+ if (typeof parsed !== "object" || parsed === null) return null;
1261
+ const location = parsed.location;
1262
+ return { location: typeof location === "string" ? location : null };
1263
+ } catch {
1264
+ return null;
1265
+ }
1266
+ }
1242
1267
  function readPrerenderCacheTagsHeader(headers) {
1243
1268
  const value = headers.get(NEXT_CACHE_TAGS_HEADER);
1244
1269
  if (!value) return [];
@@ -14,6 +14,13 @@
14
14
  * warn when this returns `undefined`.
15
15
  */
16
16
  export declare function readPrerenderSecret(serverDir: string): string | undefined;
17
+ /**
18
+ * Read next.config `compress` from `vinext-server.json` in `serverDir`.
19
+ *
20
+ * Like `next start`, compression stays on unless the build recorded an
21
+ * explicit `compress: false`, so builds without the field keep compressing.
22
+ */
23
+ export declare function readServerCompress(serverDir: string): boolean;
17
24
  /**
18
25
  * Read every server output root that contains a deployable response-stage
19
26
  * graph. Paths are stored relative to the project root so build artifacts stay
@@ -20,6 +20,16 @@ function readPrerenderSecret(serverDir) {
20
20
  return readJsonFile(manifestPath)?.prerenderSecret;
21
21
  }
22
22
  /**
23
+ * Read next.config `compress` from `vinext-server.json` in `serverDir`.
24
+ *
25
+ * Like `next start`, compression stays on unless the build recorded an
26
+ * explicit `compress: false`, so builds without the field keep compressing.
27
+ */
28
+ function readServerCompress(serverDir) {
29
+ const manifestPath = path.join(serverDir, "vinext-server.json");
30
+ return readJsonFile(manifestPath)?.compress !== false;
31
+ }
32
+ /**
23
33
  * Read every server output root that contains a deployable response-stage
24
34
  * graph. Paths are stored relative to the project root so build artifacts stay
25
35
  * relocatable, then resolved for post-build sidecar updates.
@@ -31,4 +41,4 @@ function readServerRuntimeOutputDirs(serverDir, root) {
31
41
  return manifest.runtimeOutputDirs.filter((outputDir) => typeof outputDir === "string").map((outputDir) => path.resolve(root, outputDir));
32
42
  }
33
43
  //#endregion
34
- export { readPrerenderSecret, readServerRuntimeOutputDirs };
44
+ export { readPrerenderSecret, readServerCompress, readServerRuntimeOutputDirs };
@@ -74,7 +74,13 @@ export type CacheAdapterBuildOutput = {
74
74
  export type CacheAdapterDescriptor<O extends Record<string, unknown> = Record<string, unknown>> = {
75
75
  /**
76
76
  * Module specifier (or absolute path, e.g. from `require.resolve(...)`) whose
77
- * default export is a cache adapter factory.
77
+ * default export creates the adapter from one `{ env, options }` argument:
78
+ * either a factory `({ env, options }) => adapter` or a class
79
+ * `new Adapter({ env, options })`. Class syntax is invoked with `new`; any
80
+ * other function is called. For a constructor that is not class syntax (an
81
+ * ES5-compiled, bound or Proxy-wrapped class), export
82
+ * `(args) => new Adapter(args)`. The result must be the adapter object
83
+ * itself, not a Promise.
78
84
  */
79
85
  adapter: string;
80
86
  /** JSON-serializable options forwarded to the factory at runtime. */
@@ -103,7 +103,7 @@ function generateCacheAdaptersModule(cache) {
103
103
  "export function registerConfiguredCacheAdapters() {}",
104
104
  ""
105
105
  ].join("\n");
106
- const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config."];
106
+ const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config.", `import { instantiateCacheAdapter } from "vinext/shims/cache-adapter-instantiate";`];
107
107
  if (data?.adapter) {
108
108
  lines.push(`import __vinextDataAdapterFactory from ${JSON.stringify(data.adapter)};`);
109
109
  lines.push(`import { registerDataCacheHandler } from "vinext/shims/cache-handler";`);
@@ -114,11 +114,11 @@ function generateCacheAdaptersModule(cache) {
114
114
  } else if (dataProvidesBuildIdentity) lines.push(`import { DefaultCdnCacheAdapter } from "vinext/shims/cdn-cache";`, `import { registerCdnCacheAdapter } from "vinext/shims/cdn-cache-state";`);
115
115
  lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "function __vinextFormatAdapterError(error) {", " if (error instanceof Error && error.message) return error.message;", " try {", " return String(error);", " } catch {", " return '<unknown error>';", " }", "}", "", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (typeof process !== 'undefined' && process.env?.__VINEXT_PRERENDER_PATH_DISCOVERY === '1') return;", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
116
116
  if (data?.adapter) {
117
- lines.push(" try {", ` registerDataCacheHandler(() => __vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`);
117
+ lines.push(" try {", ` registerDataCacheHandler(() => instantiateCacheAdapter(__vinextDataAdapterFactory, { env, options: ${inlineOptions(data.adapter, data.options)} }, "data"));`);
118
118
  if (dataProvidesBuildIdentity) lines.push(" registerCdnCacheAdapter(() => new DefaultCdnCacheAdapter(", " process.env.__VINEXT_RSC_BUILD_IDENTITY || process.env.__VINEXT_BUILD_ID,", " ));");
119
119
  lines.push(" } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\\n\" + __vinextFormatAdapterError(error));", " }");
120
120
  }
121
- if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => __vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
121
+ if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => instantiateCacheAdapter(__vinextCdnAdapterFactory, { env, options: ${inlineOptions(cdn.adapter, cdn.options)} }, "cdn"));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
122
122
  lines.push("}", "");
123
123
  return lines.join("\n");
124
124
  }
@@ -57,6 +57,17 @@ export type NavigationRuntimeFunctions = {
57
57
  preparePrefetchResponse?: (response: Response) => Promise<unknown>;
58
58
  claimCurrentHistoryTreeSnapshot?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
59
59
  commitAppOwnedHistoryStateWrite?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
60
+ /**
61
+ * Called after a raw (non-app-owned) history.pushState/replaceState with a
62
+ * URL so an App Router navigation that has not committed yet is discarded,
63
+ * matching Next.js' ACTION_RESTORE.
64
+ */
65
+ discardPendingNavigation?: () => void;
66
+ /**
67
+ * Called before any history.pushState/replaceState so a navigation React is
68
+ * committing writes its URL first, as Next.js does from an insertion effect.
69
+ */
70
+ flushCommittingNavigationUrl?: () => void;
60
71
  };
61
72
  export type NavigationRuntimeBootstrap = {
62
73
  routeManifest: RouteManifest | null;
@@ -31,7 +31,7 @@ function readRuntimeWindow() {
31
31
  }
32
32
  function isNavigationRuntimeFunctions(value) {
33
33
  if (!isUnknownRecord(value)) return false;
34
- return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite"));
34
+ return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite")) && isOptionalRuntimeFunction(Reflect.get(value, "discardPendingNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "flushCommittingNavigationUrl"));
35
35
  }
36
36
  function isNavigationRuntimeRscChunk(value) {
37
37
  if (typeof value === "string") return true;
@@ -307,6 +307,7 @@ function applyMiddlewareRequestHeaders(middlewareHeaders, request) {
307
307
  method: request.method,
308
308
  headers: nextHeaders,
309
309
  body: request.body,
310
+ signal: request.signal,
310
311
  duplex: request.body ? "half" : void 0
311
312
  });
312
313
  return {
@@ -859,9 +860,10 @@ async function proxyExternalRequest(request, externalUrl) {
859
860
  try {
860
861
  upstreamResponse = await fetch(targetUrl.href, {
861
862
  ...init,
862
- signal: controller.signal
863
+ signal: AbortSignal.any([request.signal, controller.signal])
863
864
  });
864
865
  } catch (e) {
866
+ if (request.signal.aborted && !controller.signal.aborted) return new Response(null, { status: 499 });
865
867
  if (e instanceof Error && e.name === "AbortError") {
866
868
  console.error("[vinext] External rewrite proxy timeout:", targetUrl.href);
867
869
  return new Response("Gateway Timeout", { status: 504 });
@@ -111,6 +111,11 @@ export type NextConfig = {
111
111
  crossOrigin?: "anonymous" | "use-credentials";
112
112
  /** Whether to add trailing slashes */
113
113
  trailingSlash?: boolean;
114
+ /**
115
+ * Compress responses from the Node production server. Defaults to true.
116
+ * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/compress
117
+ */
118
+ compress?: boolean;
114
119
  /** Keep the original request URL visible to middleware/proxy. */
115
120
  skipProxyUrlNormalize?: boolean;
116
121
  /** @deprecated Use `skipProxyUrlNormalize` instead. */
@@ -312,6 +317,8 @@ export type ResolvedNextConfig = {
312
317
  */
313
318
  assetPrefix: string;
314
319
  trailingSlash: boolean;
320
+ /** Whether the Node production server compresses responses (`compress`, default true). */
321
+ compress: boolean;
315
322
  skipProxyUrlNormalize: boolean;
316
323
  typescript: {
317
324
  tsconfigPath?: string;
@@ -1,5 +1,5 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { normalizePageExtensions } from "../routing/file-matcher.js";
2
+ import { DEFAULT_PAGE_EXTENSIONS, normalizePageExtensions } from "../routing/file-matcher.js";
3
3
  import { loadCommonJsModule, shouldRetryAsCommonJs } from "../utils/commonjs-loader.js";
4
4
  import { isUnknownRecord } from "../utils/record.js";
5
5
  import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "../shims/constants.js";
@@ -184,11 +184,24 @@ function warnConfigLoadFailure(filename, err) {
184
184
  if (isNextIntlPlugin) console.warn("[vinext] Hint: createNextIntlPlugin() is not needed with vinext. Remove the next-intl/plugin wrapper from your next.config — vinext auto-detects next-intl and registers the i18n config alias automatically.");
185
185
  }
186
186
  /**
187
+ * The `defaultConfig` handed to a function-form next.config. Next.js passes
188
+ * its full `defaultConfig` (packages/next/src/server/config-shared.ts); vinext
189
+ * passes the subset configs are known to read, with values matching Next.js,
190
+ * so `[...defaultConfig.pageExtensions, "page.js"]` works. Fresh per call so a
191
+ * config that mutates it cannot leak into later loads.
192
+ */
193
+ function createFunctionConfigDefaults() {
194
+ return {
195
+ pageExtensions: [...DEFAULT_PAGE_EXTENSIONS],
196
+ compress: true
197
+ };
198
+ }
199
+ /**
187
200
  * Resolve a Next-style config value, calling it if it's a function-form config
188
201
  * (Next.js supports `module.exports = (phase, opts) => config`).
189
202
  */
190
203
  async function resolveConfigValue(config, phase = DEFAULT_PHASE) {
191
- if (typeof config === "function") return await config(phase, { defaultConfig: {} });
204
+ if (typeof config === "function") return await config(phase, { defaultConfig: createFunctionConfigDefaults() });
192
205
  return config;
193
206
  }
194
207
  /**
@@ -436,7 +449,7 @@ async function loadNextConfigWithPackageIdentity(root, phase) {
436
449
  },
437
450
  load(id) {
438
451
  if (id !== resolvedVirtualConfigId) return;
439
- return `import * as configModule from ${configPathLiteral};\nconst cjsModule = configModule[${JSON.stringify(VINEXT_CJS_EXPORTS_KEY)}];\nconst cjsInitial = configModule[${JSON.stringify(VINEXT_CJS_INITIAL_KEY)}];\nconst cjsExports = cjsModule && cjsModule.exports;\nconst cjsValue = cjsExports != null && (cjsExports !== cjsInitial || (typeof cjsExports === "object" && Object.keys(cjsExports).length > 0)) ? cjsExports : undefined;\nconst value = cjsValue ?? configModule.default ?? configModule;\nexport default typeof value === "function" ? await value(${phaseLiteral}, { defaultConfig: {} }) : value;\n`;
452
+ return `import * as configModule from ${configPathLiteral};\nconst cjsModule = configModule[${JSON.stringify(VINEXT_CJS_EXPORTS_KEY)}];\nconst cjsInitial = configModule[${JSON.stringify(VINEXT_CJS_INITIAL_KEY)}];\nconst cjsExports = cjsModule && cjsModule.exports;\nconst cjsValue = cjsExports != null && (cjsExports !== cjsInitial || (typeof cjsExports === "object" && Object.keys(cjsExports).length > 0)) ? cjsExports : undefined;\nconst value = cjsValue ?? configModule.default ?? configModule;\nexport default typeof value === "function" ? await value(${phaseLiteral}, { defaultConfig: ${JSON.stringify(createFunctionConfigDefaults())} }) : value;\n`;
440
453
  }
441
454
  },
442
455
  ...isTypeScriptConfig ? [cjsGlobalsInjectorPlugin(configPath)] : [],
@@ -748,6 +761,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
748
761
  basePath: "",
749
762
  assetPrefix: "",
750
763
  trailingSlash: false,
764
+ compress: true,
751
765
  skipProxyUrlNormalize: false,
752
766
  typescript: {},
753
767
  output: "",
@@ -933,6 +947,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
933
947
  basePath: config.basePath ?? "",
934
948
  assetPrefix: normalizeAssetPrefix(config.assetPrefix),
935
949
  trailingSlash: config.trailingSlash ?? false,
950
+ compress: config.compress !== false,
936
951
  skipProxyUrlNormalize: config.skipProxyUrlNormalize ?? config.skipMiddlewareUrlNormalize ?? false,
937
952
  typescript: typeof config.typescript?.tsconfigPath === "string" ? { tsconfigPath: config.typescript.tsconfigPath } : {},
938
953
  output: output === "export" || output === "standalone" ? output : "",
@@ -60,6 +60,8 @@ type AppRouterConfig = {
60
60
  globalNotFound?: boolean;
61
61
  /** Enables Next.js Cache Components semantics for App Router document HTML. */
62
62
  cacheComponents?: boolean;
63
+ /** Reads an MDX source's ESM exports, as the prerender listing does. */
64
+ readMdxEsm?: ((source: string) => string) | null;
63
65
  /** Resolved `experimental.prefetchInlining` thresholds. */
64
66
  prefetchInlining?: PrefetchInliningConfig;
65
67
  /** Whether the RSC build discovered any server references. Defaults to true. */