vinext 1.4.1 → 1.4.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 (143) hide show
  1. package/README.md +5 -2
  2. package/dist/build/action-owner-manifest.js +1 -0
  3. package/dist/build/prerender-paths.d.ts +11 -1
  4. package/dist/build/prerender-paths.js +36 -10
  5. package/dist/check.js +1 -1
  6. package/dist/cli.js +1 -1
  7. package/dist/client/dev-error-overlay.d.ts +2 -1
  8. package/dist/client/dev-error-overlay.js +8 -3
  9. package/dist/config/config-matchers.js +8 -4
  10. package/dist/config/destination-params.js +5 -2
  11. package/dist/config/next-config.d.ts +25 -1
  12. package/dist/config/next-config.js +31 -1
  13. package/dist/entries/app-rsc-entry.js +65 -30
  14. package/dist/entries/app-rsc-manifest.js +4 -0
  15. package/dist/entries/pages-server-entry.js +19 -4
  16. package/dist/index.js +37 -37
  17. package/dist/init-cloudflare.js +18 -2
  18. package/dist/init-platform.js +2 -2
  19. package/dist/init.js +42 -5
  20. package/dist/plugins/css-target.d.ts +18 -0
  21. package/dist/plugins/css-target.js +93 -0
  22. package/dist/routing/app-route-graph.d.ts +10 -0
  23. package/dist/routing/app-route-graph.js +15 -1
  24. package/dist/server/api-handler.d.ts +2 -0
  25. package/dist/server/api-handler.js +10 -8
  26. package/dist/server/app-action-forwarding.js +3 -1
  27. package/dist/server/app-browser-entry.js +37 -41
  28. package/dist/server/app-browser-error.d.ts +1 -1
  29. package/dist/server/app-browser-error.js +13 -4
  30. package/dist/server/app-browser-supplemental-refresh.d.ts +13 -0
  31. package/dist/server/app-browser-supplemental-refresh.js +36 -4
  32. package/dist/server/app-elements.d.ts +3 -0
  33. package/dist/server/app-elements.js +14 -1
  34. package/dist/server/app-fallback-renderer.js +2 -2
  35. package/dist/server/app-middleware.d.ts +2 -0
  36. package/dist/server/app-middleware.js +1 -0
  37. package/dist/server/app-optimistic-routing.d.ts +12 -1
  38. package/dist/server/app-optimistic-routing.js +38 -16
  39. package/dist/server/app-page-boundary-render.js +4 -2
  40. package/dist/server/app-page-cache-render.js +1 -1
  41. package/dist/server/app-page-dispatch.d.ts +11 -1
  42. package/dist/server/app-page-dispatch.js +78 -31
  43. package/dist/server/app-page-element-builder.d.ts +11 -20
  44. package/dist/server/app-page-element-builder.js +39 -50
  45. package/dist/server/app-page-intercept-segment-config.d.ts +56 -0
  46. package/dist/server/app-page-intercept-segment-config.js +90 -0
  47. package/dist/server/app-page-navigation-params.d.ts +23 -0
  48. package/dist/server/app-page-navigation-params.js +49 -0
  49. package/dist/server/app-page-render.d.ts +5 -2
  50. package/dist/server/app-page-render.js +17 -3
  51. package/dist/server/app-page-request.d.ts +24 -2
  52. package/dist/server/app-page-request.js +10 -10
  53. package/dist/server/app-page-route-wiring.d.ts +17 -5
  54. package/dist/server/app-page-route-wiring.js +19 -13
  55. package/dist/server/app-page-segment-plan.js +9 -1
  56. package/dist/server/app-page-segment-state.d.ts +9 -0
  57. package/dist/server/app-page-segment-state.js +54 -1
  58. package/dist/server/app-pages-bridge.d.ts +6 -0
  59. package/dist/server/app-pages-bridge.js +8 -3
  60. package/dist/server/app-request-stage-independent-entry.js +2 -3
  61. package/dist/server/app-response-stage-entry.js +1 -0
  62. package/dist/server/app-route-module-loader.d.ts +2 -0
  63. package/dist/server/app-route-module-loader.js +1 -0
  64. package/dist/server/app-router-entry.js +2 -3
  65. package/dist/server/app-rsc-cache-busting.d.ts +8 -0
  66. package/dist/server/app-rsc-cache-busting.js +13 -1
  67. package/dist/server/app-rsc-combined-handler.js +2 -1
  68. package/dist/server/app-rsc-handler.d.ts +14 -1
  69. package/dist/server/app-rsc-handler.js +111 -62
  70. package/dist/server/app-rsc-response-finalizer.d.ts +18 -3
  71. package/dist/server/app-rsc-response-finalizer.js +61 -7
  72. package/dist/server/app-rsc-route-matching.d.ts +14 -0
  73. package/dist/server/app-rsc-route-matching.js +42 -18
  74. package/dist/server/app-server-action-execution.js +16 -10
  75. package/dist/server/app-ssr-entry.js +8 -0
  76. package/dist/server/cache-control.d.ts +2 -0
  77. package/dist/server/cache-control.js +3 -1
  78. package/dist/server/config-headers.d.ts +7 -0
  79. package/dist/server/config-headers.js +20 -1
  80. package/dist/server/image-animation.d.ts +9 -0
  81. package/dist/server/image-animation.js +118 -0
  82. package/dist/server/image-local-patterns.d.ts +21 -0
  83. package/dist/server/image-local-patterns.js +59 -0
  84. package/dist/server/image-optimization.d.ts +48 -27
  85. package/dist/server/image-optimization.js +316 -22
  86. package/dist/server/image-source-reader.d.ts +34 -0
  87. package/dist/server/image-source-reader.js +126 -0
  88. package/dist/server/metadata-route-response.d.ts +6 -1
  89. package/dist/server/metadata-route-response.js +46 -10
  90. package/dist/server/middleware-matcher.d.ts +1 -0
  91. package/dist/server/middleware-matcher.js +1 -1
  92. package/dist/server/middleware-runtime.d.ts +5 -0
  93. package/dist/server/middleware-runtime.js +48 -15
  94. package/dist/server/middleware.d.ts +1 -1
  95. package/dist/server/middleware.js +2 -1
  96. package/dist/server/nitro-public-files-plugin.js +20 -8
  97. package/dist/server/nitro-request-target-plugin.d.ts +17 -0
  98. package/dist/server/nitro-request-target-plugin.js +22 -0
  99. package/dist/server/open-redirect.d.ts +2 -1
  100. package/dist/server/open-redirect.js +2 -1
  101. package/dist/server/pages-client-assets.d.ts +1 -0
  102. package/dist/server/pages-data-route.d.ts +5 -1
  103. package/dist/server/pages-data-route.js +14 -6
  104. package/dist/server/pages-i18n.d.ts +7 -0
  105. package/dist/server/pages-i18n.js +14 -1
  106. package/dist/server/pages-node-compat.js +7 -3
  107. package/dist/server/pages-page-handler.js +4 -4
  108. package/dist/server/pages-request-pipeline.d.ts +9 -0
  109. package/dist/server/pages-request-pipeline.js +35 -15
  110. package/dist/server/pages-request-stage-entry.js +8 -2
  111. package/dist/server/pages-router-entry.js +3 -0
  112. package/dist/server/prod-server.d.ts +1 -1
  113. package/dist/server/prod-server.js +64 -62
  114. package/dist/server/request-pipeline.d.ts +5 -0
  115. package/dist/server/request-pipeline.js +11 -1
  116. package/dist/server/response-stage-cacheability.d.ts +7 -0
  117. package/dist/server/response-stage-cacheability.js +6 -5
  118. package/dist/server/srvx-middleware-origin.d.ts +13 -0
  119. package/dist/server/srvx-middleware-origin.js +19 -0
  120. package/dist/server/srvx-request-target.d.ts +12 -0
  121. package/dist/server/srvx-request-target.js +20 -0
  122. package/dist/server/static-file-cache.js +10 -22
  123. package/dist/server/worker-image-optimization.d.ts +10 -3
  124. package/dist/server/worker-image-optimization.js +43 -9
  125. package/dist/server/worker-revalidation-context.js +1 -0
  126. package/dist/server/worker-utils.d.ts +8 -0
  127. package/dist/server/worker-utils.js +19 -2
  128. package/dist/shims/error-boundary.d.ts +6 -2
  129. package/dist/shims/error-boundary.js +4 -2
  130. package/dist/shims/image-props.d.ts +6 -0
  131. package/dist/shims/image-props.js +39 -3
  132. package/dist/shims/image.js +3 -3
  133. package/dist/shims/request-context.d.ts +3 -1
  134. package/dist/shims/slot.d.ts +3 -0
  135. package/dist/shims/slot.js +67 -9
  136. package/dist/shims/url-utils.js +6 -1
  137. package/dist/utils/base-path.d.ts +7 -0
  138. package/dist/utils/base-path.js +11 -1
  139. package/dist/utils/client-entry-manifest.d.ts +5 -0
  140. package/dist/utils/client-entry-manifest.js +1 -0
  141. package/dist/utils/client-runtime-metadata.d.ts +5 -0
  142. package/dist/utils/client-runtime-metadata.js +33 -7
  143. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
  import { APP_BFCACHE_SEGMENT_IDENTITIES_KEY, AppElementsWire, UNMATCHED_SLOT } from "../server/app-elements-wire.js";
3
- import "../server/app-elements.js";
3
+ import { readOptimisticStateKeyRebinder } from "../server/app-elements.js";
4
4
  import { isNestedBfcacheSlotSegmentId, isNestedBfcacheSlotSegmentIdFor } from "../server/bfcache-identity.js";
5
5
  import { notFound } from "./navigation-errors.js";
6
6
  import { getBfcacheIdMapContext, getBfcacheSegmentIdContext } from "./navigation-context-state.js";
@@ -51,6 +51,25 @@ const BfcacheSegmentIdContext = getBfcacheSegmentIdContext();
51
51
  const EMPTY_BFCACHE_STATE_KEYS = Object.freeze({});
52
52
  const MAX_BFCACHE_SLOT_ENTRIES_WITH_CACHE_COMPONENTS = 3;
53
53
  const MAX_BFCACHE_SLOT_ENTRIES_WITHOUT_CACHE_COMPONENTS = 1;
54
+ const rootBfcacheRetainedKeysReports = /* @__PURE__ */ new Map();
55
+ function isBfcacheIdentityRetained(identity) {
56
+ return hasBfcacheRetainedKey(rootBfcacheRetainedKeysReports, identity);
57
+ }
58
+ function hasBfcacheRetainedKey(reports, identity) {
59
+ for (const report of reports.values()) {
60
+ if (report.keys.includes(identity) || report.hiddenKeys.includes(identity)) return true;
61
+ if (hasBfcacheRetainedKey(report.activeReports, identity)) return true;
62
+ }
63
+ return false;
64
+ }
65
+ const BfcacheRetainedKeysContext = React$1.createContext(rootBfcacheRetainedKeysReports);
66
+ function collectBfcacheRetainedKeys(reports, keys) {
67
+ for (const report of reports.values()) {
68
+ for (const key of report.keys) keys.add(key);
69
+ for (const key of report.hiddenKeys) keys.add(key);
70
+ collectBfcacheRetainedKeys(report.activeReports, keys);
71
+ }
72
+ }
54
73
  const BfcacheIdentityMapContext = React$1.createContext(EMPTY_BFCACHE_STATE_KEYS);
55
74
  function isCacheComponentsEnabled() {
56
75
  return String(process.env.__NEXT_CACHE_COMPONENTS) === "true";
@@ -136,34 +155,71 @@ function warnTransportMetadataEntry(id) {
136
155
  * falling back to the live boundary values for entries that predate per-entry
137
156
  * capture.
138
157
  */
139
- function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, SegmentContext }) {
158
+ function BfcacheEntryProviders({ entry, fallbackElements, fallbackSegmentId, fallbackStateKeyMap, retainedKeysReports, SegmentContext }) {
140
159
  return /* @__PURE__ */ jsx(BfcacheIdentityMapContext.Provider, {
141
160
  value: entry.stateKeyMap ?? fallbackStateKeyMap,
142
161
  children: /* @__PURE__ */ jsx(ElementsContext.Provider, {
143
162
  value: entry.elements ?? fallbackElements,
144
163
  children: /* @__PURE__ */ jsx(SegmentContext.Provider, {
145
164
  value: entry.segmentId ?? fallbackSegmentId,
146
- children: entry.content
165
+ children: /* @__PURE__ */ jsx(BfcacheRetainedKeysContext.Provider, {
166
+ value: retainedKeysReports,
167
+ children: entry.content
168
+ })
147
169
  })
148
170
  })
149
171
  });
150
172
  }
151
- function useBfcacheSlotEntries(activeEntry) {
173
+ function useBfcacheSlotEntries(listId, activeEntry) {
152
174
  const snapshotsByStateKey = React$1.useRef(/* @__PURE__ */ new Map());
153
175
  const [entryOrder, setEntryOrder] = React$1.useState(() => [activeEntry.stateKey]);
176
+ const [retainedKeysReportsByStateKey] = React$1.useState(() => /* @__PURE__ */ new Map());
177
+ const getRetainedKeysReports = (stateKey) => {
178
+ let reports = retainedKeysReportsByStateKey.get(stateKey);
179
+ if (!reports) {
180
+ reports = /* @__PURE__ */ new Map();
181
+ retainedKeysReportsByStateKey.set(stateKey, reports);
182
+ }
183
+ return reports;
184
+ };
154
185
  const committedSnapshots = snapshotsByStateKey.current;
155
186
  const staged = stageBfcacheSlotEntryForRender(committedSnapshots, entryOrder, activeEntry);
187
+ for (const [index, entry] of staged.entries.entries()) {
188
+ if (entry.stateKey === activeEntry.stateKey || entry.retainedKeys) continue;
189
+ const retainedKeys = /* @__PURE__ */ new Set([entry.stateKey]);
190
+ collectBfcacheRetainedKeys(getRetainedKeysReports(entry.stateKey), retainedKeys);
191
+ const hiddenEntry = {
192
+ ...entry,
193
+ retainedKeys: [...retainedKeys]
194
+ };
195
+ staged.entries[index] = hiddenEntry;
196
+ staged.snapshots.set(entry.stateKey, hiddenEntry);
197
+ }
156
198
  const nextOrder = staged.order;
157
199
  const orderChanged = !haveSameBfcacheSlotEntryOrder(entryOrder, nextOrder);
158
200
  React$1.useLayoutEffect(() => {
159
201
  snapshotsByStateKey.current = staged.snapshots;
160
- }, [staged.snapshots]);
202
+ for (const stateKey of retainedKeysReportsByStateKey.keys()) if (!staged.snapshots.has(stateKey)) retainedKeysReportsByStateKey.delete(stateKey);
203
+ }, [retainedKeysReportsByStateKey, staged.snapshots]);
204
+ const hiddenKeys = [...new Set(staged.entries.flatMap((entry) => entry.retainedKeys ?? []))];
205
+ const enclosingReports = React$1.useContext(BfcacheRetainedKeysContext);
206
+ const report = {
207
+ activeReports: getRetainedKeysReports(activeEntry.stateKey),
208
+ hiddenKeys,
209
+ keys: staged.entries.map((entry) => entry.stateKey)
210
+ };
211
+ React$1.useLayoutEffect(() => {
212
+ enclosingReports.set(listId, report);
213
+ });
161
214
  if (process.env.NODE_ENV !== "production" && !staged.snapshots.has(activeEntry.stateKey)) throw new Error("BFCache Activity slot is missing the active entry snapshot");
162
215
  if (orderChanged) setEntryOrder(nextOrder);
163
- return staged.entries;
216
+ return {
217
+ entries: staged.entries,
218
+ getRetainedKeysReports
219
+ };
164
220
  }
165
221
  function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, SegmentContext, stateKeyMap }) {
166
- const renderEntries = useBfcacheSlotEntries({
222
+ const { entries: renderEntries, getRetainedKeysReports } = useBfcacheSlotEntries(id, {
167
223
  content,
168
224
  elements,
169
225
  segmentId: id,
@@ -177,6 +233,7 @@ function BfcacheActivitySlotBoundary({ activeStateKey, content, elements, id, Se
177
233
  fallbackElements: elements,
178
234
  fallbackSegmentId: id,
179
235
  fallbackStateKeyMap: stateKeyMap,
236
+ retainedKeysReports: getRetainedKeysReports(entry.stateKey),
180
237
  SegmentContext
181
238
  })
182
239
  }, entry.stateKey)) });
@@ -228,7 +285,8 @@ function BfcacheChildrenActivityBoundary({ children, id, IdMapContext, SegmentCo
228
285
  const identityMap = React$1.useContext(BfcacheIdentityMapContext);
229
286
  const bfcacheIdMap = React$1.useContext(IdMapContext);
230
287
  const segmentId = React$1.useContext(SegmentContext);
231
- const activeStateKey = stateKey ?? (id === void 0 ? void 0 : resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap));
288
+ const rebindStateKey = readOptimisticStateKeyRebinder(elements);
289
+ const activeStateKey = stateKey !== void 0 ? rebindStateKey?.(stateKey) ?? stateKey : id === void 0 ? void 0 : resolveBfcacheSegmentStateKey(id, identityMap, bfcacheIdMap);
232
290
  if (segmentId === null || activeStateKey === void 0) return /* @__PURE__ */ jsx(Fragment$1, { children });
233
291
  return /* @__PURE__ */ jsx(BfcacheActivitySlotBoundary, {
234
292
  activeStateKey,
@@ -375,4 +433,4 @@ function ParallelSlot({ name }) {
375
433
  return React$1.useContext(ParallelSlotsContext)?.[name] ?? null;
376
434
  }
377
435
  //#endregion
378
- export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };
436
+ export { BfcacheChildrenBoundary, BfcacheIdentityMapContext, BfcacheSegmentBoundary, Children, ChildrenContext, ElementsContext, ParallelSlot, ParallelSlotsContext, RenderedSearchContext, Slot, UNMATCHED_SLOT, bindAppElementsRenderedSearch, getNonCacheComponentsSegmentKey, isBfcacheIdentityRetained, mergeElements, resolveBfcacheSegmentStateKey, setAppElementsRenderedSearch, stageBfcacheSlotEntryForRender, updateBfcacheSlotEntryOrder };
@@ -115,12 +115,17 @@ function normalizePathTrailingSlash(path, trailingSlash) {
115
115
  if (!path.startsWith("/") || path.startsWith("//") || process.env.__VINEXT_MANUAL_TRAILING_SLASH === "true") return path;
116
116
  const { pathname, query, hash } = parsePath(path);
117
117
  if (trailingSlash) {
118
- if (/\.[^/]+\/?$/.test(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
118
+ if (hasFileLikeLastSegment(pathname)) return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
119
119
  if (pathname.endsWith("/")) return `${pathname}${query}${hash}`;
120
120
  return `${pathname}/${query}${hash}`;
121
121
  }
122
122
  return `${removeRouteTrailingSlash(pathname)}${query}${hash}`;
123
123
  }
124
+ function hasFileLikeLastSegment(pathname) {
125
+ const end = pathname.endsWith("/") ? pathname.length - 1 : pathname.length;
126
+ const dot = pathname.indexOf(".", pathname.lastIndexOf("/", end - 1) + 1);
127
+ return dot !== -1 && dot < end - 1;
128
+ }
124
129
  /**
125
130
  * Prepend basePath to a local path for browser URLs / fetches.
126
131
  */
@@ -21,6 +21,13 @@ export declare function stripBasePath(pathname: string, basePath: string): strin
21
21
  * this pathname-only helper.
22
22
  */
23
23
  export declare function addBasePathToPathname(pathname: string, basePath: string | undefined): string;
24
+ /**
25
+ * Re-add a basePath that `stripBasePath` removed. Unlike
26
+ * `addBasePathToPathname`, this always prepends it: a stripped route whose
27
+ * first segment equals the basePath (basePath "/docs", route "/docs/a") still
28
+ * starts with the basePath and must not be mistaken for the original URL.
29
+ */
30
+ export declare function restoreBasePath(pathname: string, basePath: string | undefined): string;
24
31
  /**
25
32
  * Remove trailing slashes from a pathname while preserving the root "/".
26
33
  * Collapses any number of trailing slashes ("/a//" → "/a"). Used by the
@@ -31,6 +31,16 @@ function addBasePathToPathname(pathname, basePath) {
31
31
  return pathname === "/" ? basePath : `${basePath}${pathname}`;
32
32
  }
33
33
  /**
34
+ * Re-add a basePath that `stripBasePath` removed. Unlike
35
+ * `addBasePathToPathname`, this always prepends it: a stripped route whose
36
+ * first segment equals the basePath (basePath "/docs", route "/docs/a") still
37
+ * starts with the basePath and must not be mistaken for the original URL.
38
+ */
39
+ function restoreBasePath(pathname, basePath) {
40
+ if (!basePath) return pathname;
41
+ return pathname === "/" ? basePath : `${basePath}${pathname}`;
42
+ }
43
+ /**
34
44
  * Remove trailing slashes from a pathname while preserving the root "/".
35
45
  * Collapses any number of trailing slashes ("/a//" → "/a"). Used by the
36
46
  * trailing-slash redirect path and route pattern normalization.
@@ -42,4 +52,4 @@ function removeTrailingSlash(pathname) {
42
52
  return end === 0 ? "/" : pathname.slice(0, end);
43
53
  }
44
54
  //#endregion
45
- export { addBasePathToPathname, hasBasePath, removeTrailingSlash, stripBasePath };
55
+ export { addBasePathToPathname, hasBasePath, removeTrailingSlash, restoreBasePath, stripBasePath };
@@ -11,6 +11,11 @@ export declare const VINEXT_CLIENT_REFERENCE_SOURCES = ".vite/vinext-client-refe
11
11
  export type ClientEntryManifest = {
12
12
  pagesClientEntry?: string;
13
13
  appBrowserEntry?: string;
14
+ /**
15
+ * The chunk holding the App Router browser runtime, whose evaluation starts
16
+ * hydration. A user chunk group can move it out of `appBrowserEntry`.
17
+ */
18
+ appBrowserRuntime?: string;
14
19
  /** The App Router RouteManifest chunk, which the browser entry imports as it boots. */
15
20
  appRouteManifest?: string;
16
21
  /** What each App Router client reference loads in the browser. */
@@ -20,6 +20,7 @@ function readClientEntryManifest(clientDir) {
20
20
  const manifest = {};
21
21
  if (typeof value.pagesClientEntry === "string") manifest.pagesClientEntry = value.pagesClientEntry;
22
22
  if (typeof value.appBrowserEntry === "string") manifest.appBrowserEntry = value.appBrowserEntry;
23
+ if (typeof value.appBrowserRuntime === "string") manifest.appBrowserRuntime = value.appBrowserRuntime;
23
24
  if (typeof value.appRouteManifest === "string") manifest.appRouteManifest = value.appRouteManifest;
24
25
  if (isUnknownRecord(value.appClientReferenceChunks)) manifest.appClientReferenceChunks = value.appClientReferenceChunks;
25
26
  return manifest.pagesClientEntry || manifest.appBrowserEntry ? manifest : void 0;
@@ -3,6 +3,7 @@ import { ClientReferenceChunks } from "./client-reference-chunks.js";
3
3
  type ClientRuntimeMetadata = {
4
4
  clientEntryFile?: string;
5
5
  appBootstrapPreinitModules?: string[];
6
+ appBootstrapPreloadModules?: string[];
6
7
  appRouteManifestModule?: string;
7
8
  appClientReferenceChunks?: ClientReferenceChunks;
8
9
  cssGraph?: Record<string, {
@@ -17,6 +18,10 @@ type ClientRuntimeMetadata = {
17
18
  * Read the client build manifest and compute runtime metadata used by
18
19
  * Cloudflare worker entry injection and Node production server startup.
19
20
  *
21
+ * - `appBootstrapPreinitModules` — chunks the App Router browser entry imports,
22
+ * preinitialized during SSR.
23
+ * - `appBootstrapPreloadModules` — those of them that would start hydration if
24
+ * evaluated ahead of the shell, preloaded during SSR instead.
20
25
  * - `appRouteManifestModule` — the App Router RouteManifest chunk, which the
21
26
  * browser entry imports as it boots, preloaded during SSR.
22
27
  * - `appClientReferenceChunks` — what each App Router client reference loads
@@ -41,11 +41,28 @@ function collectCssGraph(buildManifest, applyBase) {
41
41
  }
42
42
  return Object.keys(cssGraph).length > 0 ? cssGraph : void 0;
43
43
  }
44
- function collectAppBootstrapPreinitModules(buildManifest, appBrowserEntry, applyBase) {
45
- if (!appBrowserEntry) return void 0;
44
+ /**
45
+ * The chunks the App Router browser entry statically imports, split by whether
46
+ * they can run ahead of the shell. A preinitialized module evaluates as soon as
47
+ * it loads, which on a reload can be before `<body>` has streamed. Evaluating
48
+ * the browser runtime starts hydration, so a chunk that holds or imports it is
49
+ * only preloaded, and runs when React's bootstrap script does. A user chunk
50
+ * group can put the runtime in one of these chunks.
51
+ */
52
+ function collectAppBootstrapModules(buildManifest, appBrowserEntry, appBrowserRuntime, applyBase) {
53
+ if (!appBrowserEntry) return {};
46
54
  const entryKey = Object.keys(buildManifest).find((key) => buildManifest[key].file === appBrowserEntry);
47
- if (!entryKey) return void 0;
48
- const modules = [];
55
+ if (!entryKey) return {};
56
+ function evaluatesRuntime(key, seen = /* @__PURE__ */ new Set()) {
57
+ if (seen.has(key)) return false;
58
+ seen.add(key);
59
+ const chunk = buildManifest[key];
60
+ if (!chunk) return false;
61
+ if (chunk.file === appBrowserRuntime) return true;
62
+ return (chunk.imports ?? []).some((importedKey) => evaluatesRuntime(importedKey, seen));
63
+ }
64
+ const preinit = [];
65
+ const preload = [];
49
66
  const seenFiles = /* @__PURE__ */ new Set();
50
67
  const visitedChunks = /* @__PURE__ */ new Set();
51
68
  function visit(key) {
@@ -57,18 +74,25 @@ function collectAppBootstrapPreinitModules(buildManifest, appBrowserEntry, apply
57
74
  const importedChunk = buildManifest[importedKey];
58
75
  if (importedChunk?.file.endsWith(".js") && !seenFiles.has(importedChunk.file)) {
59
76
  seenFiles.add(importedChunk.file);
60
- modules.push(applyBase(importedChunk.file));
77
+ (evaluatesRuntime(importedKey) ? preload : preinit).push(applyBase(importedChunk.file));
61
78
  }
62
79
  visit(importedKey);
63
80
  }
64
81
  }
65
82
  visit(entryKey);
66
- return modules.length > 0 ? modules : void 0;
83
+ return {
84
+ ...preinit.length > 0 ? { preinit } : {},
85
+ ...preload.length > 0 ? { preload } : {}
86
+ };
67
87
  }
68
88
  /**
69
89
  * Read the client build manifest and compute runtime metadata used by
70
90
  * Cloudflare worker entry injection and Node production server startup.
71
91
  *
92
+ * - `appBootstrapPreinitModules` — chunks the App Router browser entry imports,
93
+ * preinitialized during SSR.
94
+ * - `appBootstrapPreloadModules` — those of them that would start hydration if
95
+ * evaluated ahead of the shell, preloaded during SSR instead.
72
96
  * - `appRouteManifestModule` — the App Router RouteManifest chunk, which the
73
97
  * browser entry imports as it boots, preloaded during SSR.
74
98
  * - `appClientReferenceChunks` — what each App Router client reference loads
@@ -118,7 +142,9 @@ function computeClientRuntimeMetadata(opts) {
118
142
  metadata.cssGraph = collectCssGraph(buildManifest, (file) => manifestFileWithBase(file, opts.assetBase));
119
143
  metadata.sharedChunks = collectSharedChunkFiles(buildManifest, (file) => manifestFileWithBase(file, opts.assetBase));
120
144
  }
121
- metadata.appBootstrapPreinitModules = collectAppBootstrapPreinitModules(buildManifest, clientEntryManifest?.appBrowserEntry, appAssetUrl);
145
+ const appBootstrapModules = collectAppBootstrapModules(buildManifest, clientEntryManifest?.appBrowserEntry, clientEntryManifest?.appBrowserRuntime, appAssetUrl);
146
+ metadata.appBootstrapPreinitModules = appBootstrapModules.preinit;
147
+ metadata.appBootstrapPreloadModules = appBootstrapModules.preload;
122
148
  if (clientEntryManifest?.appRouteManifest) metadata.appRouteManifestModule = appAssetUrl(clientEntryManifest.appRouteManifest);
123
149
  const lazyChunks = computeLazyChunks(buildManifest).map((file) => manifestFileWithBase(file, opts.assetBase));
124
150
  if (lazyChunks.length > 0) metadata.lazyChunks = lazyChunks;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vinext",
3
- "version": "1.4.1",
3
+ "version": "1.4.3",
4
4
  "description": "Run Next.js apps on Vite. Drop-in replacement for the next CLI.",
5
5
  "homepage": "https://vinext.dev/",
6
6
  "license": "MIT",