vinext 1.2.1 → 1.3.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 (212) hide show
  1. package/README.md +12 -9
  2. package/dist/build/client-build-config.d.ts +64 -3
  3. package/dist/build/client-build-config.js +131 -4
  4. package/dist/build/client-reference-groups.d.ts +163 -0
  5. package/dist/build/client-reference-groups.js +534 -0
  6. package/dist/build/module-dependency-cache.d.ts +31 -0
  7. package/dist/build/module-dependency-cache.js +51 -1
  8. package/dist/build/next-client-runtime-manifests.d.ts +10 -1
  9. package/dist/build/next-client-runtime-manifests.js +19 -4
  10. package/dist/build/nitro-public-files.d.ts +24 -0
  11. package/dist/build/nitro-public-files.js +60 -0
  12. package/dist/build/pages-client-assets-module.js +1 -1
  13. package/dist/build/prerender-paths.js +5 -2
  14. package/dist/build/server-actions-encryption-key.d.ts +23 -0
  15. package/dist/build/server-actions-encryption-key.js +101 -0
  16. package/dist/build/ssr-manifest.d.ts +3 -0
  17. package/dist/build/ssr-manifest.js +10 -1
  18. package/dist/client/client-rewrite-params.d.ts +9 -0
  19. package/dist/client/client-rewrite-params.js +20 -0
  20. package/dist/client/dev-error-overlay.js +2 -1
  21. package/dist/client/navigation-runtime.d.ts +25 -2
  22. package/dist/client/navigation-runtime.js +42 -11
  23. package/dist/client/vinext-next-data.d.ts +0 -1
  24. package/dist/config/config-matchers.d.ts +15 -40
  25. package/dist/config/config-matchers.js +183 -255
  26. package/dist/config/config-source.d.ts +34 -0
  27. package/dist/config/config-source.js +141 -0
  28. package/dist/config/header-params.d.ts +29 -0
  29. package/dist/config/header-params.js +106 -0
  30. package/dist/config/next-config.d.ts +7 -5
  31. package/dist/config/next-config.js +120 -7
  32. package/dist/entries/app-browser-entry.d.ts +14 -1
  33. package/dist/entries/app-browser-entry.js +26 -36
  34. package/dist/entries/app-rsc-entry.d.ts +2 -0
  35. package/dist/entries/app-rsc-entry.js +92 -57
  36. package/dist/entries/app-rsc-manifest.d.ts +10 -0
  37. package/dist/entries/app-rsc-manifest.js +20 -1
  38. package/dist/entries/app-ssr-entry.d.ts +4 -1
  39. package/dist/entries/app-ssr-entry.js +9 -2
  40. package/dist/entries/pages-client-entry.d.ts +1 -0
  41. package/dist/entries/pages-client-entry.js +6 -6
  42. package/dist/entries/pages-server-entry.js +17 -14
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.js +239 -83
  45. package/dist/init.js +17 -0
  46. package/dist/plugins/optimize-imports.d.ts +25 -2
  47. package/dist/plugins/optimize-imports.js +106 -20
  48. package/dist/plugins/rsc-client-reference-loaders.d.ts +45 -1
  49. package/dist/plugins/rsc-client-reference-loaders.js +336 -18
  50. package/dist/plugins/use-cache-callable.d.ts +5 -0
  51. package/dist/plugins/use-cache-callable.js +30 -8
  52. package/dist/plugins/worker-image-imports.d.ts +1 -0
  53. package/dist/plugins/worker-image-imports.js +1 -1
  54. package/dist/routing/app-route-graph.d.ts +1 -1
  55. package/dist/routing/browser-route-manifest.d.ts +43 -0
  56. package/dist/routing/browser-route-manifest.js +195 -0
  57. package/dist/routing/hashed-route-patterns.d.ts +4 -0
  58. package/dist/routing/hashed-route-patterns.js +22 -0
  59. package/dist/routing/optimistic-route-trie.d.ts +27 -0
  60. package/dist/routing/optimistic-route-trie.js +102 -0
  61. package/dist/routing/route-validation.d.ts +6 -1
  62. package/dist/routing/route-validation.js +26 -10
  63. package/dist/server/app-action-forwarding.d.ts +8 -0
  64. package/dist/server/app-action-forwarding.js +15 -1
  65. package/dist/server/app-browser-entry.js +147 -60
  66. package/dist/server/app-browser-error.d.ts +3 -1
  67. package/dist/server/app-browser-error.js +7 -2
  68. package/dist/server/app-browser-interception-context.d.ts +2 -2
  69. package/dist/server/app-browser-mpa-navigation.d.ts +1 -0
  70. package/dist/server/app-browser-mpa-navigation.js +6 -5
  71. package/dist/server/app-browser-navigation-controller.d.ts +17 -6
  72. package/dist/server/app-browser-navigation-controller.js +88 -26
  73. package/dist/server/app-browser-state.d.ts +2 -2
  74. package/dist/server/app-browser-visible-commit.d.ts +4 -4
  75. package/dist/server/app-client-reference-preloader.d.ts +23 -0
  76. package/dist/server/app-client-reference-preloader.js +30 -7
  77. package/dist/server/app-fallback-renderer.js +2 -1
  78. package/dist/server/app-middleware.d.ts +6 -0
  79. package/dist/server/app-middleware.js +8 -10
  80. package/dist/server/app-optimistic-routing.d.ts +8 -10
  81. package/dist/server/app-optimistic-routing.js +4 -95
  82. package/dist/server/app-page-boundary-render.js +8 -1
  83. package/dist/server/app-page-cache-render.js +1 -0
  84. package/dist/server/app-page-dispatch.js +2 -0
  85. package/dist/server/app-page-render.js +3 -1
  86. package/dist/server/app-page-rsc-render-status.d.ts +1 -0
  87. package/dist/server/app-page-rsc-render-status.js +1 -0
  88. package/dist/server/app-page-search-params-observation.d.ts +1 -4
  89. package/dist/server/app-page-search-params-observation.js +8 -10
  90. package/dist/server/app-page-stream.d.ts +7 -0
  91. package/dist/server/app-page-stream.js +1 -0
  92. package/dist/server/app-pages-bridge.d.ts +2 -1
  93. package/dist/server/app-pages-bridge.js +11 -7
  94. package/dist/server/app-prerender-static-params.js +3 -2
  95. package/dist/server/app-render-dependency.js +42 -2
  96. package/dist/server/app-request-stage-independent-entry.js +2 -2
  97. package/dist/server/app-response-stage-entry.js +3 -3
  98. package/dist/server/app-route-handler-dispatch.js +3 -1
  99. package/dist/server/app-route-handler-execution.d.ts +2 -0
  100. package/dist/server/app-route-handler-execution.js +9 -2
  101. package/dist/server/app-route-handler-middleware-context.d.ts +2 -1
  102. package/dist/server/app-route-handler-middleware-context.js +3 -3
  103. package/dist/server/app-route-handler-response.d.ts +2 -1
  104. package/dist/server/app-route-handler-response.js +2 -2
  105. package/dist/server/app-route-handler-runtime.d.ts +2 -0
  106. package/dist/server/app-route-handler-runtime.js +20 -3
  107. package/dist/server/app-route-module-loader.js +7 -6
  108. package/dist/server/app-route-tree.d.ts +20 -0
  109. package/dist/server/app-route-tree.js +18 -0
  110. package/dist/server/app-router-entry.js +2 -2
  111. package/dist/server/app-rsc-combined-handler.js +8 -1
  112. package/dist/server/app-rsc-errors.d.ts +7 -0
  113. package/dist/server/app-rsc-errors.js +9 -2
  114. package/dist/server/app-rsc-handler.d.ts +4 -1
  115. package/dist/server/app-rsc-handler.js +146 -130
  116. package/dist/server/app-rsc-response-finalizer.d.ts +5 -0
  117. package/dist/server/app-rsc-response-finalizer.js +4 -1
  118. package/dist/server/app-rsc-response-stage.js +3 -1
  119. package/dist/server/app-rsc-route-matching.d.ts +1 -0
  120. package/dist/server/app-rsc-route-matching.js +1 -1
  121. package/dist/server/app-server-action-execution.d.ts +23 -6
  122. package/dist/server/app-server-action-execution.js +50 -56
  123. package/dist/server/app-ssr-entry.d.ts +6 -0
  124. package/dist/server/app-ssr-entry.js +16 -7
  125. package/dist/server/app-ssr-stream.d.ts +7 -0
  126. package/dist/server/app-ssr-stream.js +13 -1
  127. package/dist/server/config-headers.js +12 -3
  128. package/dist/server/isolate-shared-promise.d.ts +10 -0
  129. package/dist/server/isolate-shared-promise.js +33 -0
  130. package/dist/server/isr-cache.js +3 -2
  131. package/dist/server/middleware-matcher-pattern.d.ts +0 -1
  132. package/dist/server/middleware-matcher-pattern.js +11 -140
  133. package/dist/server/middleware-matcher.js +5 -6
  134. package/dist/server/middleware-response-headers.d.ts +31 -0
  135. package/dist/server/middleware-response-headers.js +57 -1
  136. package/dist/server/middleware-runtime.js +26 -13
  137. package/dist/server/navigation-planner.d.ts +2 -2
  138. package/dist/server/nitro-public-files-plugin.d.ts +38 -0
  139. package/dist/server/nitro-public-files-plugin.js +106 -0
  140. package/dist/server/nitro-public-files.d.ts +21 -0
  141. package/dist/server/nitro-public-files.js +39 -0
  142. package/dist/server/pages-asset-tags.d.ts +5 -0
  143. package/dist/server/pages-asset-tags.js +9 -2
  144. package/dist/server/pages-client-assets.d.ts +4 -1
  145. package/dist/server/pages-i18n.d.ts +18 -0
  146. package/dist/server/pages-i18n.js +18 -2
  147. package/dist/server/pages-node-compat.d.ts +5 -3
  148. package/dist/server/pages-node-compat.js +35 -16
  149. package/dist/server/pages-request-pipeline.d.ts +6 -1
  150. package/dist/server/pages-request-pipeline.js +31 -35
  151. package/dist/server/pages-request-stage-entry.js +16 -6
  152. package/dist/server/pages-response-stage-entry.js +1 -1
  153. package/dist/server/pages-revalidate.js +1 -0
  154. package/dist/server/pages-router-entry.js +1 -1
  155. package/dist/server/prod-server.d.ts +2 -2
  156. package/dist/server/prod-server.js +71 -33
  157. package/dist/server/proxy-trust.d.ts +6 -0
  158. package/dist/server/proxy-trust.js +12 -6
  159. package/dist/server/request-pipeline.d.ts +5 -0
  160. package/dist/server/request-pipeline.js +12 -1
  161. package/dist/server/server-reference-loader.js +10 -1
  162. package/dist/server/static-file-cache.js +15 -3
  163. package/dist/server/worker-utils.js +1 -1
  164. package/dist/shims/cache-handler.js +3 -2
  165. package/dist/shims/cache-request-state.d.ts +40 -3
  166. package/dist/shims/cache-request-state.js +70 -5
  167. package/dist/shims/cache-runtime.js +47 -19
  168. package/dist/shims/cache.d.ts +2 -2
  169. package/dist/shims/cache.js +17 -8
  170. package/dist/shims/cdn-purge-routes.d.ts +24 -0
  171. package/dist/shims/cdn-purge-routes.js +77 -0
  172. package/dist/shims/dynamic.js +17 -4
  173. package/dist/shims/form.js +8 -3
  174. package/dist/shims/image-props.d.ts +93 -0
  175. package/dist/shims/image-props.js +323 -0
  176. package/dist/shims/image.d.ts +4 -18
  177. package/dist/shims/image.js +4 -319
  178. package/dist/shims/internal/headers-state.d.ts +7 -0
  179. package/dist/shims/internal/headers-state.js +14 -2
  180. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +4 -1
  181. package/dist/shims/internal/hybrid-client-route-owner-direct.js +31 -14
  182. package/dist/shims/internal/hybrid-client-route-owner.js +8 -8
  183. package/dist/shims/link.js +5 -3
  184. package/dist/shims/navigation.d.ts +1 -1
  185. package/dist/shims/navigation.js +17 -5
  186. package/dist/shims/og.js +35 -10
  187. package/dist/shims/request-context.d.ts +13 -0
  188. package/dist/shims/router.js +19 -40
  189. package/dist/shims/server.d.ts +31 -4
  190. package/dist/shims/server.js +58 -10
  191. package/dist/shims/thenable-params.d.ts +6 -1
  192. package/dist/shims/thenable-params.js +11 -2
  193. package/dist/shims/unified-request-context.d.ts +5 -0
  194. package/dist/shims/unified-request-context.js +2 -0
  195. package/dist/utils/asset-prefix.d.ts +7 -0
  196. package/dist/utils/asset-prefix.js +10 -1
  197. package/dist/utils/client-build-manifest.js +12 -5
  198. package/dist/utils/client-runtime-metadata.d.ts +1 -0
  199. package/dist/utils/client-runtime-metadata.js +15 -2
  200. package/dist/utils/lazy-chunks.d.ts +4 -0
  201. package/dist/utils/manifest-paths.d.ts +14 -0
  202. package/dist/utils/manifest-paths.js +22 -1
  203. package/dist/utils/path-to-regexp.d.ts +21 -0
  204. package/dist/{server/middleware-path-to-regexp.js → utils/path-to-regexp.js} +14 -11
  205. package/dist/utils/plugin-react-version.d.ts +15 -0
  206. package/dist/utils/plugin-react-version.js +64 -0
  207. package/dist/utils/public-routes.d.ts +2 -0
  208. package/dist/utils/public-routes.js +18 -7
  209. package/package.json +1 -1
  210. package/dist/server/middleware-path-to-regexp.d.ts +0 -13
  211. package/dist/utils/regex-safety.d.ts +0 -17
  212. package/dist/utils/regex-safety.js +0 -789
@@ -1,46 +1,364 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
+ import { CLIENT_REFERENCE_GROUP_MAX_COST_BYTES, collectClientReferencePreloadPlan, collectClientReferenceRouteSignatures, measureClientReferenceGroupCosts, planClientReferenceGroups, readClientModuleBindings } from "../build/client-reference-groups.js";
3
+ import { hasUserClientChunkGroups } from "../build/client-build-config.js";
4
+ import fs from "node:fs";
5
+ import { isCSSRequest } from "vite";
1
6
  //#region src/plugins/rsc-client-reference-loaders.ts
2
7
  const CLIENT_REFERENCES_ID = "\0virtual:vite-rsc/client-references";
3
8
  const RESOLVED_ID_PROXY_PREFIX = "virtual:vite-rsc/resolved-id/";
9
+ const RESOLVED_CLIENT_REFERENCE_GROUP_PREFIX = `\0virtual:vinext-client-reference-group-`;
10
+ const CLIENT_REFERENCES_ID_RE = /^\0virtual:vite-rsc\/client-references$/;
11
+ const CLIENT_REFERENCE_GROUP_RE = /^virtual:vinext-client-reference-group-/;
12
+ const RESOLVED_CLIENT_REFERENCE_GROUP_RE = /^\0virtual:vinext-client-reference-group-/;
13
+ /**
14
+ * Whether the cost estimate loads a module. Rolldown keeps externals and
15
+ * unresolved `require()`s in `importedIds` under their specifier (a bare name
16
+ * or URL), and never settles a load of one, so only files and `\0` virtual
17
+ * modules are measured.
18
+ */
19
+ function isMeasured(id) {
20
+ return id.startsWith("\0") || path.isAbsolute(id);
21
+ }
22
+ /**
23
+ * Whether `id` shares the package `sideEffects` flag of `owner`: it is in the
24
+ * same package, and the package's `sideEffects` field is not a list of files.
25
+ * `uniformSideEffects` caches that last check per package.json path.
26
+ */
27
+ function isInPackage(id, owner, uniformSideEffects) {
28
+ const packageJsonPath = owner.packageJsonPath;
29
+ if (!packageJsonPath) return false;
30
+ const relative = path.relative(path.dirname(packageJsonPath), id);
31
+ if (relative.startsWith("..") || relative.split("/").includes("node_modules")) return false;
32
+ let uniform = uniformSideEffects.get(packageJsonPath);
33
+ if (uniform === void 0) {
34
+ try {
35
+ const { sideEffects } = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
36
+ uniform = sideEffects === void 0 || typeof sideEffects === "boolean";
37
+ } catch {
38
+ uniform = false;
39
+ }
40
+ uniformSideEffects.set(packageJsonPath, uniform);
41
+ }
42
+ return uniform;
43
+ }
4
44
  function withResolvedIdProxy(resolvedId) {
5
45
  return resolvedId.startsWith("\0") ? RESOLVED_ID_PROXY_PREFIX + encodeURIComponent(resolvedId) : resolvedId;
6
46
  }
7
- function generateClientReferenceObject(meta) {
8
- const exports = meta.renderedExports.slice().sort().map((name) => ` get ${JSON.stringify(name)}() { return m[${JSON.stringify(name)}]; },`).join("\n");
47
+ function sortedRenderedExports(meta) {
48
+ return meta.renderedExports.slice().sort();
49
+ }
50
+ function groupExportName(slotIndex, exportIndex) {
51
+ return `r${slotIndex}_${exportIndex}`;
52
+ }
53
+ function generateClientReferenceObject(meta, slot) {
54
+ const exports = sortedRenderedExports(meta).map((name, exportIndex) => {
55
+ const value = slot ? `m.${groupExportName(slot.index, exportIndex)}` : `m[${JSON.stringify(name)}]`;
56
+ return ` get ${JSON.stringify(name)}() { return ${value}; },`;
57
+ }).join("\n");
9
58
  return exports ? `{\n${exports}\n }` : "{}";
10
59
  }
11
- function generateDirectClientReferenceLoaders(metas) {
60
+ function generateClientReferenceLoaders(metas, slots = /* @__PURE__ */ new Map()) {
12
61
  return `export default {\n${metas.slice().sort((a, b) => a.referenceKey.localeCompare(b.referenceKey)).map((meta) => {
13
- const importId = withResolvedIdProxy(meta.importId);
62
+ const slot = slots.get(meta);
63
+ const source = slot ? slot.groupId : withResolvedIdProxy(meta.importId);
14
64
  return [
15
65
  ` ${JSON.stringify(meta.referenceKey)}: async () => {`,
16
- ` const m = await import(${JSON.stringify(importId)});`,
17
- ` return ${generateClientReferenceObject(meta)};`,
66
+ ` const m = await import(${JSON.stringify(source)});`,
67
+ ` return ${generateClientReferenceObject(meta, slot)};`,
18
68
  ` },`
19
69
  ].join("\n");
20
70
  }).join("\n")}\n};\n`;
21
71
  }
22
- function createRscClientReferenceLoadersPlugin() {
72
+ /**
73
+ * Re-export only each member's rendered exports, as live bindings, so unused
74
+ * exports and their dependencies still tree-shake as they do with direct
75
+ * loaders. A member without rendered exports is imported for its side effects.
76
+ */
77
+ function generateClientReferenceGroupModule(metas) {
78
+ return `${metas.map((meta, slotIndex) => {
79
+ const source = JSON.stringify(withResolvedIdProxy(meta.importId));
80
+ const specifiers = sortedRenderedExports(meta).map((name, exportIndex) => {
81
+ return `${/^[A-Za-z_$][\w$]*$/.test(name) ? name : JSON.stringify(name)} as ${groupExportName(slotIndex, exportIndex)}`;
82
+ });
83
+ return specifiers.length > 0 ? `export { ${specifiers.join(", ")} } from ${source};` : `import ${source};`;
84
+ }).join("\n")}\n`;
85
+ }
86
+ /**
87
+ * The SSR build's `routeClientReferences` export: the reference keys each route
88
+ * preloads before it renders (see server/app-client-reference-preloader.ts),
89
+ * stored as indices into `references`. Client references the plan doesn't know
90
+ * join `always`.
91
+ */
92
+ function generateRouteClientReferencesExport(plan, metaEntries) {
93
+ if (!plan) return "export const routeClientReferences = null;\n";
94
+ const metaById = new Map(metaEntries);
95
+ const references = [];
96
+ const indices = /* @__PURE__ */ new Map();
97
+ const toIndices = (referenceIds) => referenceIds.flatMap((referenceId) => {
98
+ const meta = metaById.get(referenceId);
99
+ if (!meta) return [];
100
+ let index = indices.get(referenceId);
101
+ if (index === void 0) {
102
+ index = references.length;
103
+ indices.set(referenceId, index);
104
+ references.push(meta.referenceKey);
105
+ }
106
+ return [index];
107
+ });
108
+ const manifest = {
109
+ references,
110
+ always: toIndices([...plan.preloads.always, ...metaEntries.map(([referenceId]) => referenceId).filter((referenceId) => !plan.referenceIds.has(referenceId))]),
111
+ files: plan.preloads.files.map(toIndices),
112
+ routes: Object.fromEntries(plan.preloads.routes),
113
+ routeless: plan.preloads.routeless
114
+ };
115
+ return `export const routeClientReferences = ${JSON.stringify(manifest)};\n`;
116
+ }
117
+ /**
118
+ * Replaces @vitejs/plugin-rsc's client-reference facades with per-reference
119
+ * loaders. With `routes`, the SSR build also exports the client references
120
+ * each App Router route preloads. With `grouping`, production client builds
121
+ * additionally load references that the same App Router routes reach through
122
+ * one shared route group module (see build/client-reference-groups.ts), so a
123
+ * page fetches one chunk per route group instead of one per `"use client"`
124
+ * module.
125
+ */
126
+ function createRscClientReferenceLoadersPlugin(options = {}) {
127
+ const { routes: routeOptions } = options;
128
+ const grouping = routeOptions && options.grouping;
23
129
  let rscApi;
130
+ let root = "";
131
+ let groupingEnabled = false;
132
+ let routeSignatures = /* @__PURE__ */ new Map();
133
+ // @vitejs/plugin-rsc also builds before the SSR environment.
134
+ let preloadPlan = null;
135
+ const groupModules = /* @__PURE__ */ new Map();
24
136
  return {
25
137
  name: "vinext:rsc-client-reference-loaders",
26
138
  enforce: "post",
27
139
  configResolved(config) {
140
+ root = config.root;
28
141
  rscApi = config.plugins.find((plugin) => plugin.name === "rsc:minimal")?.api;
29
142
  },
30
- transform(_code, id) {
31
- if (id !== CLIENT_REFERENCES_ID) return null;
143
+ outputOptions: {
144
+ order: "post",
145
+ handler(output) {
146
+ if (this.environment?.name !== "client") return;
147
+ groupingEnabled = !!grouping && !hasUserClientChunkGroups(output, grouping.ownClientChunkGroups);
148
+ }
149
+ },
150
+ buildStart() {
151
+ if (this.environment.name === "rsc") {
152
+ routeSignatures = /* @__PURE__ */ new Map();
153
+ preloadPlan = null;
154
+ }
155
+ if (this.environment.name === "client") groupModules.clear();
156
+ },
157
+ buildEnd(error) {
158
+ if (error || !routeOptions) return;
159
+ if (this.environment.name !== "rsc" || this.environment.mode !== "build") return;
32
160
  const manager = rscApi?.manager;
33
- if (!manager || manager.isScanBuild) return null;
34
- const metaEntries = Object.entries(manager.clientReferenceMetaMap).filter(([, meta]) => meta.serverChunk);
35
- const metas = metaEntries.map(([, meta]) => meta);
36
- if (metas.length === 0) return null;
37
- for (const [id, meta] of metaEntries) meta.groupChunkId = id;
38
- return {
39
- code: generateDirectClientReferenceLoaders(metas),
40
- map: null
161
+ const routes = routeOptions.getRoutes();
162
+ if (!manager || manager.isScanBuild || !routes) return;
163
+ const clientReferenceIds = new Set(Object.keys(manager.clientReferenceMetaMap));
164
+ const getModuleInfo = (id) => this.getModuleInfo(id);
165
+ preloadPlan = {
166
+ referenceIds: clientReferenceIds,
167
+ preloads: collectClientReferencePreloadPlan({
168
+ alwaysRoots: routeOptions.getAlwaysRenderedFiles(),
169
+ canonicalizeModuleId: routeOptions.canonicalizeModuleId,
170
+ clientReferenceIds,
171
+ entryIds: [...this.getModuleIds()].filter((id) => this.getModuleInfo(id)?.isEntry),
172
+ getModuleInfo,
173
+ routelessFiles: routeOptions.getRoutelessFiles(),
174
+ routes
175
+ })
41
176
  };
177
+ if (!grouping) return;
178
+ routeSignatures = collectClientReferenceRouteSignatures({
179
+ canonicalizeModuleId: routeOptions.canonicalizeModuleId,
180
+ clientReferenceIds,
181
+ getModuleInfo,
182
+ root,
183
+ routes,
184
+ sharedRoots: routeOptions.getSharedRoots()
185
+ });
186
+ },
187
+ resolveId: {
188
+ filter: { id: CLIENT_REFERENCE_GROUP_RE },
189
+ handler(source) {
190
+ return groupModules.has(`\0${source}`) ? `\0${source}` : null;
191
+ }
192
+ },
193
+ load: {
194
+ filter: { id: RESOLVED_CLIENT_REFERENCE_GROUP_RE },
195
+ handler(id) {
196
+ return groupModules.get(id) ?? null;
197
+ }
198
+ },
199
+ transform: {
200
+ filter: { id: CLIENT_REFERENCES_ID_RE },
201
+ async handler(code) {
202
+ const manager = rscApi?.manager;
203
+ if (!manager || manager.isScanBuild) return null;
204
+ const isServer = this.environment.config.consumer === "server";
205
+ const metaEntries = Object.entries(manager.clientReferenceMetaMap).filter(([, meta]) => meta.serverChunk);
206
+ const metas = metaEntries.map(([, meta]) => meta);
207
+ const routeClientReferencesExport = isServer ? generateRouteClientReferencesExport(preloadPlan, metaEntries) : "";
208
+ if (metas.length === 0) return isServer ? {
209
+ code: `${code}\n${routeClientReferencesExport}`,
210
+ map: null
211
+ } : null;
212
+ for (const [id, meta] of metaEntries) meta.groupChunkId = id;
213
+ const slots = /* @__PURE__ */ new Map();
214
+ const referenceIds = new Set(metaEntries.map(([referenceId]) => referenceId));
215
+ const referenceKeys = new Map(metaEntries.map(([referenceId, meta]) => [referenceId, meta.referenceKey]));
216
+ if (grouping && groupingEnabled && this.environment.name === "client" && this.environment.mode === "build" && planClientReferenceGroups({
217
+ referenceIds,
218
+ referenceKeys,
219
+ signatures: routeSignatures,
220
+ costs: new Map([...referenceIds].map((referenceId) => [referenceId, 0])),
221
+ maxCostBytes: 0
222
+ }).length > 0) {
223
+ const references = /* @__PURE__ */ new Map();
224
+ const resolutions = /* @__PURE__ */ new Map();
225
+ const packages = /* @__PURE__ */ new Map();
226
+ const externalIds = /* @__PURE__ */ new Set();
227
+ const uniformSideEffects = /* @__PURE__ */ new Map();
228
+ const resolveCache = /* @__PURE__ */ new Map();
229
+ const addResolution = (resolved) => {
230
+ resolutions.set(resolved.id, resolved);
231
+ packages.set(resolved.id, resolved);
232
+ };
233
+ await Promise.all(metaEntries.map(async ([referenceId, meta]) => {
234
+ if (meta.importId.startsWith("\0")) {
235
+ references.set(referenceId, meta.importId);
236
+ return;
237
+ }
238
+ const resolved = await this.resolve(meta.importId);
239
+ if (!resolved || resolved.external) return;
240
+ references.set(referenceId, resolved.id);
241
+ addResolution(resolved);
242
+ }));
243
+ const { external } = this.environment.config.build.rolldownOptions;
244
+ const moduleInfos = /* @__PURE__ */ new Map();
245
+ const excludedById = /* @__PURE__ */ new Map();
246
+ const isExcludedById = (moduleId) => {
247
+ let excluded = excludedById.get(moduleId);
248
+ if (excluded === void 0) {
249
+ excluded = !isMeasured(moduleId) || moduleId === CLIENT_REFERENCES_ID || moduleId.startsWith(RESOLVED_CLIENT_REFERENCE_GROUP_PREFIX) || isCSSRequest(moduleId) || grouping.isAlwaysLoadedClientModule(moduleId);
250
+ excludedById.set(moduleId, excluded);
251
+ }
252
+ return excluded;
253
+ };
254
+ const costs = await measureClientReferenceGroupCosts({
255
+ references,
256
+ loadModule: async (moduleId) => {
257
+ if (external && !resolutions.has(moduleId) && !moduleId.startsWith("\0")) {
258
+ const resolved = await this.resolve(moduleId);
259
+ if (!resolved || resolved.external) return {
260
+ code: "",
261
+ importedIds: []
262
+ };
263
+ }
264
+ const info = await this.load(resolutions.get(moduleId) ?? { id: moduleId });
265
+ moduleInfos.set(moduleId, info);
266
+ const { code, importedIds } = info;
267
+ if (code === null) return {
268
+ code,
269
+ importedIds
270
+ };
271
+ const importedIdSet = new Set(importedIds);
272
+ const owner = packages.get(moduleId);
273
+ return {
274
+ code,
275
+ importedIds,
276
+ bindings: await readClientModuleBindings({
277
+ code,
278
+ importedIds,
279
+ resolve: async (specifier) => {
280
+ if (owner && path.isAbsolute(moduleId) && (specifier.startsWith("./") || specifier.startsWith("../"))) {
281
+ const candidate = path.resolve(path.dirname(moduleId), specifier);
282
+ if (importedIdSet.has(candidate) && isInPackage(candidate, owner, uniformSideEffects)) {
283
+ packages.set(candidate, owner);
284
+ return candidate;
285
+ }
286
+ }
287
+ const key = `${path.dirname(moduleId)}\0${specifier}`;
288
+ let resolving = resolveCache.get(key);
289
+ if (!resolving) {
290
+ resolving = this.resolve(specifier, moduleId).then((resolved) => {
291
+ if (!resolved) return null;
292
+ if (resolved.external) externalIds.add(resolved.id);
293
+ else addResolution(resolved);
294
+ return resolved.id;
295
+ });
296
+ resolveCache.set(key, resolving);
297
+ }
298
+ return resolving;
299
+ }
300
+ })
301
+ };
302
+ },
303
+ isSideEffectFree: (moduleId) => packages.get(moduleId)?.moduleSideEffects === false,
304
+ isExcluded: (moduleId) => externalIds.has(moduleId) || isExcludedById(moduleId)
305
+ });
306
+ for (const [referenceId, meta] of metaEntries) {
307
+ const moduleId = references.get(referenceId);
308
+ const info = moduleId === void 0 ? void 0 : moduleInfos.get(moduleId);
309
+ if (!info || info.inputFormat === "cjs") continue;
310
+ if (meta.renderedExports.some((name) => !info.exports.includes(name))) costs.set(referenceId, null);
311
+ }
312
+ const groups = planClientReferenceGroups({
313
+ referenceIds,
314
+ referenceKeys,
315
+ signatures: routeSignatures,
316
+ costs,
317
+ maxCostBytes: CLIENT_REFERENCE_GROUP_MAX_COST_BYTES
318
+ });
319
+ const metaById = new Map(metaEntries);
320
+ for (const group of groups) {
321
+ const groupMetas = group.referenceIds.map((referenceId) => metaById.get(referenceId));
322
+ const resolvedGroupId = `${RESOLVED_CLIENT_REFERENCE_GROUP_PREFIX}${group.key}`;
323
+ groupModules.set(resolvedGroupId, generateClientReferenceGroupModule(groupMetas));
324
+ groupMetas.forEach((meta, index) => {
325
+ meta.groupChunkId = resolvedGroupId;
326
+ slots.set(meta, {
327
+ groupId: resolvedGroupId.slice(1),
328
+ index
329
+ });
330
+ });
331
+ }
332
+ }
333
+ return {
334
+ code: generateClientReferenceLoaders(metas, slots) + routeClientReferencesExport,
335
+ map: null
336
+ };
337
+ }
338
+ }
339
+ };
340
+ }
341
+ /**
342
+ * Exports a null preload plan from the client-references virtual module when
343
+ * the app registers @vitejs/plugin-rsc itself (`rsc: false`), where vinext
344
+ * doesn't add the loaders plugin above. The App Router SSR entry imports the
345
+ * plan, and null makes it preload every client reference.
346
+ */
347
+ function createRouteClientReferencesFallbackPlugin() {
348
+ return {
349
+ name: "vinext:route-client-references-fallback",
350
+ enforce: "post",
351
+ transform: {
352
+ filter: { id: CLIENT_REFERENCES_ID_RE },
353
+ handler(code) {
354
+ if (this.environment.config.consumer !== "server") return null;
355
+ return {
356
+ code: `${code}\n${generateRouteClientReferencesExport(null, [])}`,
357
+ map: null
358
+ };
359
+ }
42
360
  }
43
361
  };
44
362
  }
45
363
  //#endregion
46
- export { createRscClientReferenceLoadersPlugin };
364
+ export { createRouteClientReferencesFallbackPlugin, createRscClientReferenceLoadersPlugin };
@@ -5,6 +5,11 @@ type Options = {
5
5
  projectRoot: string;
6
6
  cacheRuntime: string;
7
7
  allowMissingRsc?: boolean;
8
+ /**
9
+ * The Server Actions encryption key vinext loaded for an auto-registered
10
+ * @vitejs/plugin-rsc, so reference names derive from the same key.
11
+ */
12
+ getEncryptionKey?: () => string | undefined;
8
13
  };
9
14
  type BrowserTransformOptions = {
10
15
  environmentName: string;
@@ -2,9 +2,10 @@ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { parseProgramAsync } from "./ast-utils.js";
3
3
  import { magicStringTransformResult } from "./transform-result.js";
4
4
  import { NODE_MODULES_PATH_RE } from "../utils/path.js";
5
+ import { getServerActionsKeyCacheFsDeny, loadOrGenerateServerActionsEncryptionKey } from "../build/server-actions-encryption-key.js";
5
6
  import { createRequire } from "node:module";
6
7
  import { pathToFileURL } from "node:url";
7
- import { createHmac, randomBytes } from "node:crypto";
8
+ import { createHmac } from "node:crypto";
8
9
  //#region src/plugins/use-cache-callable.ts
9
10
  const PLUGIN_NAME = "vinext:server-function-directives";
10
11
  const SOURCE_MODULE_ID_RE = /\.(?:tsx?|jsx?|mjs)(?:\?.*)?$/;
@@ -114,21 +115,33 @@ function transformBrowserModule(transforms, code, ast, id, moduleDirective, opti
114
115
  referenceNames: result.exportNames.map(referenceName)
115
116
  };
116
117
  }
118
+ /**
119
+ * Ported from Next.js: crates/next-custom-transforms/src/transforms/server_actions.rs
120
+ * salts server-reference IDs with the encryption key (`serverReferenceHashSalt`
121
+ * in packages/next/src/build/webpack-config.ts), so rebuilds of identical
122
+ * source emit identical client chunks. See
123
+ * loadOrGenerateServerActionsEncryptionKey for where the key comes from. The
124
+ * secret is derived under a label rather than used directly so the reference
125
+ * names reveal nothing about the key.
126
+ */
127
+ function createReferenceSecret(encryptionKey) {
128
+ return createHmac("sha256", encryptionKey).update("vinext use cache reference").digest();
129
+ }
117
130
  async function createUseCacheCallablePlugin(options) {
118
131
  const rscModulePath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc");
119
132
  const transformsPath = resolvePluginRscModule(options.projectRoot, "@vitejs/plugin-rsc/transforms");
120
133
  const rscModule = await import(pathToFileURL(rscModulePath).href);
121
134
  const transforms = await import(pathToFileURL(transformsPath).href);
122
- const referenceSecret = randomBytes(32);
135
+ let referenceSecret;
123
136
  let manager;
124
- async function analyzeModule(manager, code, id) {
137
+ async function analyzeModule(manager, secret, code, id) {
125
138
  const ast = await parseProgramAsync(code);
126
139
  const moduleDirective = findModuleUseCacheDirective(ast);
127
140
  const useServerBoundary = transforms.hasDirective(ast.body, "use server");
128
141
  if (moduleDirective && useServerBoundary) throw new Error(`A module cannot contain both ${JSON.stringify(moduleDirective)} and "use server" directives.`);
129
142
  const reference = manager.serverReferences.resolve(id, "rsc");
130
143
  const relativeImportId = manager.toRelativeId(reference.importId);
131
- const secureExportName = (name) => `$$vinext_cache_${createHmac("sha256", referenceSecret).update(relativeImportId).update("\0").update(name).digest("hex")}`;
144
+ const secureExportName = (name) => `$$vinext_cache_${createHmac("sha256", secret).update(relativeImportId).update("\0").update(name).digest("hex")}`;
132
145
  return {
133
146
  ast,
134
147
  moduleDirective,
@@ -141,11 +154,12 @@ async function createUseCacheCallablePlugin(options) {
141
154
  name: PLUGIN_NAME,
142
155
  api: { async transformWorkerModule(code, id, createReference) {
143
156
  if (!code.includes("use cache")) return;
144
- if (!manager) {
157
+ const secret = referenceSecret;
158
+ if (!manager || !secret) {
145
159
  if (findModuleUseCacheDirective(await parseProgramAsync(code))) throw new Error("vinext: cannot compile a \"use cache\" module in a Web Worker without @vitejs/plugin-rsc.");
146
160
  return;
147
161
  }
148
- const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, code, id);
162
+ const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, secret, code, id);
149
163
  if (useServerBoundary) return;
150
164
  return transformBrowserModule(transforms, code, ast, id, moduleDirective, {
151
165
  environmentName: "worker",
@@ -154,10 +168,17 @@ async function createUseCacheCallablePlugin(options) {
154
168
  createReference
155
169
  })?.output;
156
170
  } },
171
+ config(config) {
172
+ return { server: { fs: { deny: getServerActionsKeyCacheFsDeny(config.server?.fs?.deny) } } };
173
+ },
157
174
  configResolved(config) {
158
175
  const pluginApi = rscModule.getPluginApi(config);
159
176
  const hasRscPlugin = config.plugins.some((plugin) => plugin.name === "rsc");
160
177
  if (!pluginApi && options.allowMissingRsc && !hasRscPlugin) return;
178
+ referenceSecret ??= createReferenceSecret(options.getEncryptionKey?.() ?? loadOrGenerateServerActionsEncryptionKey({
179
+ root: config.root,
180
+ isBuild: config.command === "build"
181
+ }));
161
182
  if (!pluginApi?.manager.serverReferences) throw new Error("vinext: callable use cache requires @vitejs/plugin-rsc 0.5.34 or newer.");
162
183
  if (options.allowMissingRsc) {
163
184
  const useCacheIndex = config.plugins.findIndex((plugin) => plugin.name === PLUGIN_NAME);
@@ -172,12 +193,13 @@ async function createUseCacheCallablePlugin(options) {
172
193
  exclude: [NODE_MODULES_PATH_RE, RESOLVED_VIRTUAL_MODULE_ID_RE]
173
194
  } },
174
195
  async handler(code, id) {
175
- if (!manager) return;
196
+ const secret = referenceSecret;
197
+ if (!manager || !secret) return;
176
198
  if (!code.includes("use cache")) {
177
199
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
178
200
  return;
179
201
  }
180
- const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, code, id);
202
+ const { ast, moduleDirective, useServerBoundary, reference, secureExportName } = await analyzeModule(manager, secret, code, id);
181
203
  if (!(this.environment.name === "rsc")) {
182
204
  if (useServerBoundary) {
183
205
  manager.serverReferences.deleteClaim(PLUGIN_NAME, id);
@@ -1,5 +1,6 @@
1
1
  import { Plugin } from "vite";
2
2
  //#region src/plugins/worker-image-imports.d.ts
3
+ export declare const WORKER_IMAGE_METADATA_PREFIX = "\0vinext-worker-image-meta:";
3
4
  /**
4
5
  * Preserve Next.js's StaticImageData shape for dynamic image imports inside
5
6
  * Vite worker graphs. Worker builds use their own plugin container, so the
@@ -128,4 +128,4 @@ function createWorkerImageImportsPlugin(options = {}) {
128
128
  };
129
129
  }
130
130
  //#endregion
131
- export { createWorkerImageImportsPlugin };
131
+ export { WORKER_IMAGE_METADATA_PREFIX, createWorkerImageImportsPlugin };
@@ -275,7 +275,7 @@ type RouteManifestRouteHandler = {
275
275
  routeId: string;
276
276
  pattern: string;
277
277
  };
278
- type RouteManifestLayout = {
278
+ export type RouteManifestLayout = {
279
279
  id: string;
280
280
  treePath: string;
281
281
  patternParts: readonly string[];
@@ -0,0 +1,43 @@
1
+ import { GraphVersion, RouteManifest, RouteManifestInterception, RouteManifestLayout, RouteManifestRootBoundary, RouteManifestRoute, RouteManifestSlotBinding } from "./app-route-graph.js";
2
+ //#region src/routing/browser-route-manifest.d.ts
3
+ export type BrowserRouteManifestRoute = Pick<RouteManifestRoute, "id" | "patternParts" | "isDynamic" | "paramNames" | "rootBoundaryId" | "pageId" | "layoutIds" | "slotIds">;
4
+ type BrowserRouteManifestLayout = Pick<RouteManifestLayout, "id" | "patternParts" | "rootBoundaryId">;
5
+ type BrowserRouteManifestSlotBinding = Pick<RouteManifestSlotBinding, "id" | "routeId" | "slotId" | "ownerLayoutId" | "state" | "routeSegments" | "slotPatternParts" | "slotParamNames">;
6
+ export type BrowserRouteManifestInterception = Pick<RouteManifestInterception, "id" | "sourcePatternParts" | "targetPatternParts" | "slotId" | "ownerLayoutId" | "targetRouteId">;
7
+ type BrowserRouteManifestRootBoundary = Pick<RouteManifestRootBoundary, "id" | "treePath">;
8
+ /**
9
+ * The part of the RouteManifest the browser navigation runtime reads: the
10
+ * maps and fields the navigation planner, optimistic routing and interception
11
+ * context resolution use.
12
+ */
13
+ export type BrowserRouteManifest = {
14
+ graphVersion: GraphVersion;
15
+ segmentGraph: {
16
+ routes: ReadonlyMap<string, BrowserRouteManifestRoute>;
17
+ layouts: ReadonlyMap<string, BrowserRouteManifestLayout>;
18
+ slotBindings: ReadonlyMap<string, BrowserRouteManifestSlotBinding>;
19
+ interceptions: ReadonlyMap<string, BrowserRouteManifestInterception>;
20
+ interceptionsBySlotId: ReadonlyMap<string, readonly BrowserRouteManifestInterception[]>;
21
+ rootBoundaries: ReadonlyMap<string, BrowserRouteManifestRootBoundary>;
22
+ };
23
+ };
24
+ /**
25
+ * JSON form of `BrowserRouteManifest`. Every map is keyed by its values' `id`,
26
+ * so maps travel as arrays of values in map order. A route omits `id` when it
27
+ * is `route:` plus its pattern, and `interceptionsBySlotId` lists interception
28
+ * ids instead of repeating the interceptions.
29
+ */
30
+ export type BrowserRouteManifestWire = {
31
+ graphVersion: string;
32
+ routes: (Omit<BrowserRouteManifestRoute, "id"> & {
33
+ id?: string;
34
+ })[];
35
+ layouts: BrowserRouteManifestLayout[];
36
+ slotBindings: BrowserRouteManifestSlotBinding[];
37
+ interceptions: BrowserRouteManifestInterception[];
38
+ interceptionsBySlotId: [slotId: string, interceptionIds: string[]][];
39
+ rootBoundaries: BrowserRouteManifestRootBoundary[];
40
+ };
41
+ export declare function toBrowserRouteManifestWire(routeManifest: RouteManifest): BrowserRouteManifestWire;
42
+ export declare function decodeBrowserRouteManifest(wire: BrowserRouteManifestWire): BrowserRouteManifest;
43
+ //#endregion