@kasperpeulen/vitest-plugin-nextjs 0.0.0-alpha.0 → 0.1.0-alpha.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 (250) hide show
  1. package/README.md +249 -2
  2. package/dist/_virtual/_rolldown/runtime.js +14 -0
  3. package/dist/action-ids.d.ts +18 -0
  4. package/dist/action-ids.js +44 -0
  5. package/dist/adapter.d.ts +8 -0
  6. package/dist/adapter.js +12 -0
  7. package/dist/app-requests.d.ts +63 -0
  8. package/dist/app-requests.js +253 -0
  9. package/dist/browser-context.d.ts +24 -0
  10. package/dist/browser-context.js +157 -0
  11. package/dist/build-output.d.ts +68 -0
  12. package/dist/build-output.js +216 -0
  13. package/dist/build.d.ts +26 -0
  14. package/dist/build.js +39 -0
  15. package/dist/built-files.d.ts +9 -0
  16. package/dist/built-files.js +22 -0
  17. package/dist/cache.d.ts +44 -0
  18. package/dist/cache.js +99 -0
  19. package/dist/client-components.d.ts +3 -0
  20. package/dist/client-components.js +4 -0
  21. package/dist/client-file-key.d.ts +5 -0
  22. package/dist/client-file-key.js +5 -0
  23. package/dist/client-files.d.ts +83 -0
  24. package/dist/client-files.js +186 -0
  25. package/dist/client-graph.d.ts +94 -0
  26. package/dist/client-graph.js +266 -0
  27. package/dist/client-modules.d.ts +10 -0
  28. package/dist/client-modules.js +43 -0
  29. package/dist/client.d.ts +42 -0
  30. package/dist/client.js +263 -0
  31. package/dist/commonjs.d.ts +44 -0
  32. package/dist/commonjs.js +111 -0
  33. package/dist/compat/browser/client.d.ts +19 -0
  34. package/dist/compat/browser/client.js +13 -0
  35. package/dist/compat/build.d.ts +145 -0
  36. package/dist/compat/build.js +149 -0
  37. package/dist/compat/checks.d.ts +16 -0
  38. package/dist/compat/checks.js +148 -0
  39. package/dist/compat/patches.d.ts +13 -0
  40. package/dist/compat/patches.js +30 -0
  41. package/dist/compat/paths.d.ts +74 -0
  42. package/dist/compat/paths.js +160 -0
  43. package/dist/compat/rsc/handlers.d.ts +2 -0
  44. package/dist/compat/rsc/handlers.js +2 -0
  45. package/dist/compat/rsc/prerender.d.ts +10 -0
  46. package/dist/compat/rsc/prerender.js +9 -0
  47. package/dist/compat/server/headers.d.ts +3 -0
  48. package/dist/compat/server/headers.js +3 -0
  49. package/dist/compat/ssr/build-output.d.ts +4 -0
  50. package/dist/compat/ssr/build-output.js +3 -0
  51. package/dist/compat/ssr/cache.d.ts +6 -0
  52. package/dist/compat/ssr/cache.js +6 -0
  53. package/dist/compat/ssr/handler.d.ts +8 -0
  54. package/dist/compat/ssr/handler.js +8 -0
  55. package/dist/compat/ssr/node-server.d.ts +5 -0
  56. package/dist/compat/ssr/node-server.js +4 -0
  57. package/dist/compat/ssr/render-errors.d.ts +4 -0
  58. package/dist/compat/ssr/render-errors.js +4 -0
  59. package/dist/compat/ssr/senders.d.ts +6 -0
  60. package/dist/compat/ssr/senders.js +6 -0
  61. package/dist/compat/types.d.ts +8 -0
  62. package/dist/compat/types.js +1 -0
  63. package/dist/compile.d.ts +31 -0
  64. package/dist/compile.js +140 -0
  65. package/dist/disk.d.ts +16 -0
  66. package/dist/disk.js +13 -0
  67. package/dist/document-loads.d.ts +15 -0
  68. package/dist/document-loads.js +53 -0
  69. package/dist/document.d.ts +79 -0
  70. package/dist/document.js +328 -0
  71. package/dist/downloads.d.ts +15 -0
  72. package/dist/downloads.js +71 -0
  73. package/dist/esm-require.d.ts +11 -0
  74. package/dist/esm-require.js +32 -0
  75. package/dist/fast-crypto/index.js +1093 -0
  76. package/dist/fast-crypto.d.ts +20 -0
  77. package/dist/fast-crypto.js +151 -0
  78. package/dist/flight.d.ts +19 -0
  79. package/dist/flight.js +42 -0
  80. package/dist/form-submission.d.ts +15 -0
  81. package/dist/form-submission.js +27 -0
  82. package/dist/from-page.d.ts +5 -0
  83. package/dist/from-page.js +44 -0
  84. package/dist/globals.d.ts +1 -0
  85. package/dist/globals.js +197 -0
  86. package/dist/host-document.d.ts +26 -0
  87. package/dist/host-document.js +34 -0
  88. package/dist/index.d.ts +280 -0
  89. package/dist/index.js +281 -0
  90. package/dist/internal.d.ts +2 -0
  91. package/dist/internal.js +3 -0
  92. package/dist/leftovers.d.ts +11 -0
  93. package/dist/leftovers.js +55 -0
  94. package/dist/mode.d.ts +169 -0
  95. package/dist/mode.js +31 -0
  96. package/dist/node/LICENSES.md +3540 -0
  97. package/dist/node/assert/strict.js +29 -0
  98. package/dist/node/assert.js +3044 -0
  99. package/dist/node/buffer.js +1499 -0
  100. package/dist/node/child_process.js +45 -0
  101. package/dist/node/cluster.js +85 -0
  102. package/dist/node/console.js +96 -0
  103. package/dist/node/constants.js +495 -0
  104. package/dist/node/crypto.js +1388 -0
  105. package/dist/node/dgram.js +78 -0
  106. package/dist/node/diagnostics_channel.js +122 -0
  107. package/dist/node/dns/promises.js +129 -0
  108. package/dist/node/dns.js +137 -0
  109. package/dist/node/domain.js +43 -0
  110. package/dist/node/events.js +1155 -0
  111. package/dist/node/fs/promises.js +40 -0
  112. package/dist/node/fs.js +10375 -0
  113. package/dist/node/http.js +326 -0
  114. package/dist/node/http2.js +305 -0
  115. package/dist/node/https.js +57 -0
  116. package/dist/node/inspector/promises.js +66 -0
  117. package/dist/node/inspector.js +76 -0
  118. package/dist/node/module.js +205 -0
  119. package/dist/node/net.js +392 -0
  120. package/dist/node/os.js +259 -0
  121. package/dist/node/path/posix.js +25 -0
  122. package/dist/node/path/win32.js +25 -0
  123. package/dist/node/path.js +384 -0
  124. package/dist/node/perf_hooks.js +313 -0
  125. package/dist/node/process.js +452 -0
  126. package/dist/node/punycode.js +178 -0
  127. package/dist/node/querystring.js +934 -0
  128. package/dist/node/readline/promises.js +71 -0
  129. package/dist/node/readline.js +56 -0
  130. package/dist/node/repl.js +44 -0
  131. package/dist/node/sqlite.js +22 -0
  132. package/dist/node/stream/consumers.js +47 -0
  133. package/dist/node/stream/promises.js +6 -0
  134. package/dist/node/stream/web.js +52 -0
  135. package/dist/node/stream.js +4323 -0
  136. package/dist/node/string_decoder.js +276 -0
  137. package/dist/node/sys.js +6 -0
  138. package/dist/node/timers/promises.js +71 -0
  139. package/dist/node/timers.js +133 -0
  140. package/dist/node/tls.js +122 -0
  141. package/dist/node/trace_events.js +23 -0
  142. package/dist/node/tty.js +69 -0
  143. package/dist/node/url.js +1426 -0
  144. package/dist/node/util/types.js +1456 -0
  145. package/dist/node/util.js +4807 -0
  146. package/dist/node/v8.js +160 -0
  147. package/dist/node/vm.js +88 -0
  148. package/dist/node/wasi.js +16 -0
  149. package/dist/node/worker_threads.js +123 -0
  150. package/dist/node/zlib.js +1396 -0
  151. package/dist/node-builtins.d.ts +40 -0
  152. package/dist/node-builtins.js +74 -0
  153. package/dist/node-on-page.d.ts +46 -0
  154. package/dist/node-on-page.js +101 -0
  155. package/dist/node-platform.d.ts +36 -0
  156. package/dist/node-platform.js +152 -0
  157. package/dist/node-rendered.d.ts +21 -0
  158. package/dist/node-rendered.js +26 -0
  159. package/dist/node-server.d.ts +46 -0
  160. package/dist/node-server.js +193 -0
  161. package/dist/optimizer-cache-key.d.ts +26 -0
  162. package/dist/optimizer-cache-key.js +73 -0
  163. package/dist/own-package.d.ts +1 -0
  164. package/dist/own-package.js +6 -0
  165. package/dist/page-store.d.ts +33 -0
  166. package/dist/page-store.js +80 -0
  167. package/dist/page-timers.d.ts +22 -0
  168. package/dist/page-timers.js +47 -0
  169. package/dist/page.d.ts +112 -0
  170. package/dist/page.js +549 -0
  171. package/dist/paths.d.ts +13 -0
  172. package/dist/paths.js +47 -0
  173. package/dist/plugin.d.ts +22 -0
  174. package/dist/plugin.js +995 -0
  175. package/dist/prerender-verdict.d.ts +8 -0
  176. package/dist/prerender-verdict.js +67 -0
  177. package/dist/project/context.d.ts +546 -0
  178. package/dist/project/context.js +111 -0
  179. package/dist/project/entries.d.ts +42 -0
  180. package/dist/project/entries.js +189 -0
  181. package/dist/project/env-worker.d.ts +1 -0
  182. package/dist/project/env-worker.js +9 -0
  183. package/dist/project/env.d.ts +36 -0
  184. package/dist/project/env.js +82 -0
  185. package/dist/project/layers.d.ts +31 -0
  186. package/dist/project/layers.js +150 -0
  187. package/dist/project/loaders.d.ts +71 -0
  188. package/dist/project/loaders.js +300 -0
  189. package/dist/project/metadata.d.ts +8 -0
  190. package/dist/project/metadata.js +34 -0
  191. package/dist/project/module-exports.d.ts +29 -0
  192. package/dist/project/module-exports.js +77 -0
  193. package/dist/project/routes.d.ts +19 -0
  194. package/dist/project/routes.js +92 -0
  195. package/dist/project/routing.d.ts +46 -0
  196. package/dist/project/routing.js +280 -0
  197. package/dist/project/transform.d.ts +8 -0
  198. package/dist/project/transform.js +110 -0
  199. package/dist/project/webpack.d.ts +46 -0
  200. package/dist/project/webpack.js +160 -0
  201. package/dist/project.d.ts +264 -0
  202. package/dist/project.js +58 -0
  203. package/dist/registry-global.d.ts +5 -0
  204. package/dist/registry-global.js +5 -0
  205. package/dist/registry.d.ts +241 -0
  206. package/dist/registry.js +30 -0
  207. package/dist/route-chunks.d.ts +13 -0
  208. package/dist/route-chunks.js +32 -0
  209. package/dist/route-lookup.d.ts +34 -0
  210. package/dist/route-lookup.js +207 -0
  211. package/dist/routes-ahead-shared.d.ts +17 -0
  212. package/dist/routes-ahead-shared.js +9 -0
  213. package/dist/routes-ahead.d.ts +10 -0
  214. package/dist/routes-ahead.js +54 -0
  215. package/dist/rsc.d.ts +19 -0
  216. package/dist/rsc.js +171 -0
  217. package/dist/server-action.d.ts +9 -0
  218. package/dist/server-action.js +15 -0
  219. package/dist/server-code.d.ts +176 -0
  220. package/dist/server-code.js +357 -0
  221. package/dist/server-in-front.d.ts +37 -0
  222. package/dist/server-in-front.js +90 -0
  223. package/dist/server-layers.d.ts +22 -0
  224. package/dist/server-layers.js +33 -0
  225. package/dist/setup-browser.d.ts +1 -0
  226. package/dist/setup-browser.js +8 -0
  227. package/dist/setup-node.d.ts +1 -0
  228. package/dist/setup-node.js +6 -0
  229. package/dist/setup.d.ts +8 -0
  230. package/dist/setup.js +62 -0
  231. package/dist/speed-features.d.ts +13 -0
  232. package/dist/speed-features.js +95 -0
  233. package/dist/ssr.d.ts +70 -0
  234. package/dist/ssr.js +480 -0
  235. package/dist/static-build.d.ts +36 -0
  236. package/dist/static-build.js +92 -0
  237. package/dist/storage.d.ts +31 -0
  238. package/dist/storage.js +94 -0
  239. package/dist/styles-shared.d.ts +137 -0
  240. package/dist/styles-shared.js +124 -0
  241. package/dist/styles.d.ts +102 -0
  242. package/dist/styles.js +730 -0
  243. package/dist/stylesheet-versions.d.ts +17 -0
  244. package/dist/stylesheet-versions.js +43 -0
  245. package/dist/tester.html +10 -0
  246. package/dist/use-cache.d.ts +15 -0
  247. package/dist/use-cache.js +93 -0
  248. package/dist/vitest-version.d.ts +6 -0
  249. package/dist/vitest-version.js +12 -0
  250. package/package.json +125 -4
package/dist/plugin.js ADDED
@@ -0,0 +1,995 @@
1
+ import { registryGlobal } from "./registry-global.js";
2
+ import { nextBuilds, nextProductionPlugin } from "./build.js";
3
+ import { createCompilePlugin, createDependencyCompilePlugin, createScanCompilePlugin } from "./compile.js";
4
+ import { builtHostFileOf, changesInPlace, createStyles } from "./styles.js";
5
+ import { clientFiles, clientReferenceOfModule } from "./client-files.js";
6
+ import { createCommonJsPlugin } from "./commonjs.js";
7
+ import { esmRequirePlugin } from "./esm-require.js";
8
+ import { fastCryptoModule, fastCryptoModuleOf, fastCryptoSetup } from "./fast-crypto.js";
9
+ import { flightBridge } from "./flight.js";
10
+ import { resolveSpeedFeatures } from "./mode.js";
11
+ import { browserMapping, isNativeNodeModule, isNodeModule, nativeRequirePlugin, nodeModuleOf } from "./node-builtins.js";
12
+ import { devAssetPrefix, prerenderImports, runtimeImports as runtimeImports$1, serverReferenceInfo } from "./compat/paths.js";
13
+ import { createNodePlatform, instrumentationPlugin } from "./node-platform.js";
14
+ import { optimizerCacheKey } from "./optimizer-cache-key.js";
15
+ import "./own-package.js";
16
+ import { pageTimersDefine, pageTimersPlugin } from "./page-timers.js";
17
+ import { createPathsPlugin } from "./paths.js";
18
+ import { nextPatches } from "./compat/patches.js";
19
+ import { describeEnv, envFilesOf } from "./project/env.js";
20
+ import { moduleFileAt } from "./project/context.js";
21
+ import { loadNextProject } from "./project.js";
22
+ import { compileServerCode, createServerCode } from "./server-code.js";
23
+ import { createUseCachePlugin, useCacheModule } from "./use-cache.js";
24
+ import { routeLookupPlugin } from "./route-lookup.js";
25
+ import { routesAheadPlugin } from "./routes-ahead.js";
26
+ import { nextStaticBuild, withPathsFromRoot } from "./static-build.js";
27
+ import { assertVitestVersion } from "./vitest-version.js";
28
+ import { createFilter, defaultServerMainFields, normalizePath, parseAst, parseAstAsync, send, transformWithOxc } from "vite";
29
+ import path from "node:path";
30
+ import { hasDirective, transformDirectiveProxyExport } from "@vitejs/plugin-rsc/transforms";
31
+ import { createHash } from "node:crypto";
32
+ import fs from "node:fs";
33
+ import { getPluginApi } from "@vitejs/plugin-rsc/plugin";
34
+ import { fileURLToPath } from "node:url";
35
+ import { isOwnFile, ownPackageNames } from "@kasperpeulen/vitest-plugin-rsc/internal/own-packages";
36
+ import "@kasperpeulen/vitest-plugin-rsc/internal/compile-cache";
37
+ import { escapePath } from "tinyglobby";
38
+ import { isTestServer, servesPageInNode } from "@kasperpeulen/vitest-plugin-rsc/internal/page-in-node";
39
+ import { createRunnerEnvironmentPlugins } from "@kasperpeulen/vitest-plugin-rsc/internal/runner-environment";
40
+ //#region src/plugin.ts
41
+ const environmentOf = {
42
+ rsc: "client",
43
+ ssr: "next_ssr",
44
+ browser: "react_client"
45
+ };
46
+ const layers = Object.keys(environmentOf);
47
+ const registry = `globalThis.${registryGlobal}`;
48
+ const fileEntry = (file) => escapePath(normalizePath(file));
49
+ const appEntriesOf = (project) => `${fileEntry(project.appDir)}/**/*.{js,jsx,ts,tsx}`;
50
+ const manifestId = "virtual:vitest-plugin-rsc/next-manifest";
51
+ const virtual = (name) => `virtual:vitest-plugin-rsc/next-${name}`;
52
+ const routeEntryPrefix = virtual("route/");
53
+ const routeLists = {
54
+ page: virtual("app-pages"),
55
+ route: virtual("route-handlers")
56
+ };
57
+ const routeKinds = Object.keys(routeLists);
58
+ const middlewareId = virtual("middleware");
59
+ const middlewareEntryId = virtual("middleware-entry");
60
+ const metadataPrefix = virtual("metadata/");
61
+ const envId = virtual("env");
62
+ const cacheHandlersId = virtual("cache-handlers");
63
+ const rootParamsId = virtual("root-params");
64
+ const prerenderId = virtual("prerender");
65
+ const nextRouting = "@next/routing";
66
+ const entryOf = (route) => route.component ?? route.page;
67
+ const isRouteModule = (id) => id.startsWith(routeEntryPrefix) || routeKinds.some((kind) => id === routeLists[kind]);
68
+ function importedByRoute(module) {
69
+ const seen = /* @__PURE__ */ new Set([module]);
70
+ for (const node of seen) {
71
+ if (changesInPlace(node)) continue;
72
+ for (const importer of node.importers) {
73
+ const id = importer.id?.startsWith("\0") ? importer.id.slice(1) : void 0;
74
+ if (id !== void 0 && (isRouteModule(id) || id === middlewareId || id === middlewareEntryId)) return true;
75
+ seen.add(importer);
76
+ }
77
+ }
78
+ return false;
79
+ }
80
+ const hmrBoundary = (entries) => `import.meta.hot?.accept([${entries.join(", ")}]);\n`;
81
+ const bridgePrefix = "\0vitest-plugin-rsc/next-bridge/";
82
+ const emptyModuleId = "\0vitest-plugin-rsc/next-empty";
83
+ const emptyModule = "const empty = {};\nexport default empty;\n";
84
+ const vendoredFlight = (entry) => `@vitejs/plugin-rsc/vendor/react-server-dom/${entry}`;
85
+ const ownFile = (name) => normalizePath(fileURLToPath(new URL(`./${name}${path.extname(import.meta.url)}`, import.meta.url)));
86
+ const setupFile = ownFile("setup-browser");
87
+ const nodeSetupFile = ownFile("setup-node");
88
+ const testingLibrary = {
89
+ specifier: "@kasperpeulen/vitest-plugin-nextjs/testing-library",
90
+ file: ownFile("index")
91
+ };
92
+ const testerHtml = fileURLToPath(new URL("./tester.html", import.meta.url));
93
+ const serverReferenceInfoShim = `
94
+ import * as original from ${JSON.stringify(serverReferenceInfo)};
95
+ export * from ${JSON.stringify(serverReferenceInfo)};
96
+ const isNextId = (id) => id.length === original.SERVER_REFERENCE_ID_LENGTH && /^[0-9a-f]+$/i.test(id);
97
+ export function mightBeServerReferenceId(id) {
98
+ return typeof id === "string" && (original.mightBeServerReferenceId(id) || id.includes("#"));
99
+ }
100
+ export function extractInfoFromServerReferenceId(id) {
101
+ return isNextId(id)
102
+ ? original.extractInfoFromServerReferenceId(id)
103
+ : { type: "server-action", usedArgs: [true, true, true, true, true, true], hasRestArgs: true };
104
+ }
105
+ `;
106
+ const devAssetPrefixShim = `export const assetPrefix = "";\n`;
107
+ const nodePlatform = createNodePlatform(registry, bridgePrefix);
108
+ const saidOfNext = /* @__PURE__ */ new Set();
109
+ const fastCryptoPrefix = `${bridgePrefix}fast-crypto/`;
110
+ const fastCryptoSetupId = "virtual:vitest-plugin-rsc/next-fast-crypto";
111
+ function createLayerResolver(getProject, layer, fastCrypto, nodeModules) {
112
+ let aliases;
113
+ const normalized = /* @__PURE__ */ new Map();
114
+ function nextFile(specifier) {
115
+ return moduleFileAt(path.join(getProject().nextDir, specifier.slice(5)));
116
+ }
117
+ function toSpecifier(file) {
118
+ return `next/${path.relative(getProject().nextDir, file).split(path.sep).join("/")}`;
119
+ }
120
+ function getAliases() {
121
+ if (aliases) return aliases;
122
+ const { nextDir } = getProject();
123
+ const table = { ...getProject().aliases[layer] };
124
+ const flightDir = path.posix.dirname(table["react-server-dom-webpack/server$"]);
125
+ for (const entry of [
126
+ "server.edge",
127
+ "static.edge",
128
+ "client.edge",
129
+ "client.browser"
130
+ ]) table[`${vendoredFlight(entry)}$`] = `${flightDir}/${entry}`;
131
+ aliases = Object.entries(table).flatMap(([key, target]) => {
132
+ if (/^next\/dist\/\w+$/.test(key)) return [];
133
+ let exact = key.endsWith("$");
134
+ key = key.replace(/\$$/, "");
135
+ if (path.isAbsolute(key) && !path.relative(nextDir, key).startsWith("..")) {
136
+ key = toSpecifier(key);
137
+ exact = true;
138
+ if (/^next\/[\w-]+\.js$/.test(key)) return [{
139
+ key,
140
+ exact,
141
+ target
142
+ }, {
143
+ key: key.slice(0, -3),
144
+ exact,
145
+ target
146
+ }];
147
+ }
148
+ return [{
149
+ key,
150
+ exact,
151
+ target
152
+ }];
153
+ });
154
+ return aliases;
155
+ }
156
+ function applyAlias(source) {
157
+ for (const { key, exact, target } of getAliases()) {
158
+ if (source !== key && (exact || !source.startsWith(`${key}/`))) continue;
159
+ const aliased = target && target + source.slice(key.length);
160
+ if (aliased && source !== key && aliased.startsWith("next/") && !nextFile(aliased)) continue;
161
+ return aliased;
162
+ }
163
+ }
164
+ function isDependencyOfNext(target) {
165
+ return path.isAbsolute(target) && normalizePath(target).includes("/node_modules/") && path.relative(getProject().nextDir, target).startsWith("..");
166
+ }
167
+ function bridgeOf(source) {
168
+ if (layer === "rsc") {
169
+ const flight = /^react-server-dom-webpack\/(server|static|client)(\.edge)?$/.exec(source);
170
+ if (flight) return `${bridgePrefix}flight-${flight[1]}`;
171
+ }
172
+ if (source === "next/dist/esm/shared/lib/server-reference-info.js") return `${bridgePrefix}server-reference-info`;
173
+ if (devAssetPrefix.test(source)) return `${bridgePrefix}dev-asset-prefix`;
174
+ if (layer === "rsc" && /^next\/dist\/(esm\/)?server\/route-modules\/app-page\/module\.compiled(\.js)?$/.test(source)) return `${bridgePrefix}ssr-app-page-module`;
175
+ if (layer !== "browser") return nodePlatform.moduleOf(source);
176
+ }
177
+ /**
178
+ * Where a specifier leads in this layer: another specifier, a bridge module
179
+ * (`\0...`), or `false` for a module the layer leaves empty. For the app's
180
+ * code, `ofApp`, with the entries Next's own files do not get, like React's
181
+ * `react-dom/server` for Node.js in the ssr layer (project/layers.ts).
182
+ */
183
+ function normalize(source, ofApp = false) {
184
+ const appTarget = ofApp ? getProject().appAliases[layer][`${source}$`] : void 0;
185
+ if (appTarget !== void 0) return normalize(appTarget);
186
+ const cached = normalized.get(source);
187
+ if (cached !== void 0) return cached;
188
+ const own = layer !== "browser" ? nodeModuleOf(source, nodeModules()) : void 0;
189
+ if (own) {
190
+ normalized.set(source, own);
191
+ return own;
192
+ }
193
+ let current = source;
194
+ for (let i = 0; i < 10 && current; i++) {
195
+ const bridge = bridgeOf(current);
196
+ if (bridge) {
197
+ current = bridge;
198
+ break;
199
+ }
200
+ const commonJs = current.replace(/^next\/dist\/esm\//, "next/dist/");
201
+ let next = applyAlias(current) ?? applyAlias(commonJs);
202
+ if (next === void 0 && current.startsWith("next/")) {
203
+ const file = nextFile(commonJs.replace(/^next\/dist\/(?!compiled\/)/, "next/dist/esm/")) ?? nextFile(commonJs);
204
+ if (file) next = toSpecifier(file);
205
+ }
206
+ if (next === void 0 || next === current) break;
207
+ current = next;
208
+ }
209
+ normalized.set(source, current);
210
+ return current;
211
+ }
212
+ /**
213
+ * The name this layer pre-bundles an entry of `optimizeDeps.include` under:
214
+ * the specifier `plugin()` resolves it to, or none for a module the layer
215
+ * leaves empty, bridges, or imports by its file.
216
+ */
217
+ function preBundledName(entry) {
218
+ const source = entry.slice(entry.lastIndexOf(">") + 1).trim();
219
+ const target = normalize(source, true);
220
+ if (target === false || target.startsWith("\0") || isNodeModule(target) || layer !== "browser" && isNativeNodeModule(target)) return;
221
+ if (isDependencyOfNext(target)) return entry;
222
+ if (path.isAbsolute(target)) return;
223
+ return target === source ? entry : target;
224
+ }
225
+ function plugin() {
226
+ return {
227
+ name: `vitest-plugin-rsc:next-resolve:${layer}`,
228
+ enforce: "pre",
229
+ async resolveId(source, importer, options) {
230
+ if (source.startsWith(bridgePrefix) || source === emptyModuleId) return source;
231
+ if (source.startsWith("\0")) return;
232
+ if (layer !== "browser" && fastCrypto() && !importer?.startsWith(fastCryptoPrefix)) {
233
+ const name = fastCryptoModuleOf(source);
234
+ const native = nodeModules() === "native";
235
+ if (name && (name !== "node-crypto" || native || browserMapping(source, importer) === void 0)) return fastCryptoPrefix + name;
236
+ }
237
+ if (importer === `${bridgePrefix}server-reference-info`) return nextFile(serverReferenceInfo);
238
+ if (source === nextRouting) {
239
+ if (!importer || !isOwnFile(importer)) return;
240
+ return this.resolve(source, path.join(getProject().root, "package.json"), {
241
+ ...options,
242
+ skipSelf: true
243
+ });
244
+ }
245
+ let specifier = source;
246
+ if (source.startsWith(".") && importer) specifier = path.resolve(path.dirname(importer.split("?")[0]), source);
247
+ if (path.isAbsolute(specifier)) {
248
+ const relative = path.relative(getProject().nextDir, specifier.split("?")[0]);
249
+ if (relative.startsWith("..")) return;
250
+ specifier = `next/${relative.split(path.sep).join("/")}`;
251
+ }
252
+ const ofApp = !!importer && !normalizePath(importer).includes("/next/dist/");
253
+ const target = normalize(specifier, ofApp);
254
+ if (target === false) return emptyModuleId;
255
+ if (layer !== "browser" && isNativeNodeModule(target)) return {
256
+ id: target,
257
+ external: true
258
+ };
259
+ if (isNodeModule(target)) {
260
+ const mapping = browserMapping(source, importer);
261
+ if (mapping === false) return emptyModuleId;
262
+ if (mapping !== void 0) return;
263
+ if (!this.environment) return target;
264
+ return this.resolve(target, importer, {
265
+ ...options,
266
+ skipSelf: true
267
+ });
268
+ }
269
+ if (target.startsWith("\0")) return target;
270
+ if (target === source) return;
271
+ if (specifier !== source && target === specifier && !nextFile(target)) return;
272
+ if (isDependencyOfNext(target) && importer && !importer.includes("/node_modules/")) return this.resolve(source, path.join(getProject().nextDir, "package.json"), {
273
+ ...options,
274
+ skipSelf: true
275
+ });
276
+ if (this.environment?.mode === "build" && source === specifier && target.startsWith("next/") && !target.startsWith("next/dist/compiled/")) {
277
+ const file = nextFile(target);
278
+ if (file) return normalizePath(file);
279
+ }
280
+ const from = target.startsWith("next/") ? path.join(getProject().root, "package.json") : importer;
281
+ return this.resolve(target, from, {
282
+ ...options,
283
+ skipSelf: true
284
+ });
285
+ },
286
+ load(id) {
287
+ if (id === emptyModuleId) return emptyModule;
288
+ if (!id.startsWith(bridgePrefix)) return;
289
+ const name = id.slice(31);
290
+ if (name === "server-reference-info") return serverReferenceInfoShim;
291
+ if (name === "dev-asset-prefix") return devAssetPrefixShim;
292
+ if (id.startsWith(fastCryptoPrefix)) return fastCryptoModule(id.slice(fastCryptoPrefix.length), nodeModuleOf("node:crypto", nodeModules()));
293
+ const { flightExports, version, newExports } = getProject();
294
+ const nodeModule = nodePlatform.load(name, newExports[name], version);
295
+ if (nodeModule) return nodeModule;
296
+ const entry = name.slice(7);
297
+ return flightBridge(entry, flightExports[entry], version, registry);
298
+ }
299
+ };
300
+ }
301
+ return {
302
+ normalize,
303
+ nextFile,
304
+ toSpecifier,
305
+ isDependencyOfNext,
306
+ preBundledName,
307
+ plugin
308
+ };
309
+ }
310
+ function nextClientBoundaryPlugin(getProject, resolver) {
311
+ return {
312
+ name: "vitest-plugin-rsc:next-client-boundary",
313
+ async transform(code, id) {
314
+ const file = normalizePath(id.split("?")[0]);
315
+ if (!file.startsWith(`${normalizePath(getProject().nextDir)}/`)) return;
316
+ if (!code.includes("use client")) return;
317
+ const specifier = resolver.toSpecifier(file);
318
+ const result = transformDirectiveProxyExport(await parseAstAsync(code), {
319
+ directive: "use client",
320
+ code,
321
+ runtime: (name) => `$$ReactServer.registerClientReference(() => { throw new Error(${JSON.stringify(`${specifier}#${name} is a Client Component and cannot be called on the server`)}) }, ${JSON.stringify(`/@id/${specifier}`)}, ${JSON.stringify(name)})`
322
+ });
323
+ if (!result) return;
324
+ result.output.prepend(`import * as $$ReactServer from ${JSON.stringify(vendoredFlight("server.edge"))};\n`);
325
+ return {
326
+ code: result.output.toString(),
327
+ map: null
328
+ };
329
+ }
330
+ };
331
+ }
332
+ function flightConsolePlugin() {
333
+ return {
334
+ name: "vitest-plugin-rsc:flight-console",
335
+ transform: {
336
+ filter: { id: /\/react-server-dom-webpack-server\.[^/]+\.development\.js$/ },
337
+ handler(code, id) {
338
+ const patched = code.replace(/function patchConsole\([^)]*\)\s*\{/, "$& return;");
339
+ if (patched === code) this.warn(`vitest-plugin-rsc: React's patchConsole() in ${id} is not turned off.`);
340
+ return {
341
+ code: patched,
342
+ map: null
343
+ };
344
+ }
345
+ }
346
+ };
347
+ }
348
+ function nextPatchesPlugin() {
349
+ const patches = nextPatches(registry);
350
+ return {
351
+ name: "vitest-plugin-rsc:next-patches",
352
+ transform: {
353
+ filter: { id: { include: patches.map(({ file }) => file) } },
354
+ handler(code, id) {
355
+ let patched = code;
356
+ for (const { file, lost, edits } of patches) {
357
+ if (!file.test(id)) continue;
358
+ for (const [find, replace] of edits) {
359
+ if (!patched.includes(find)) {
360
+ this.warn(`vitest-plugin-rsc: ${id} has no \`${find.trim()}\`: ${lost}.`);
361
+ continue;
362
+ }
363
+ patched = patched.replaceAll(find, () => replace);
364
+ }
365
+ }
366
+ return patched === code ? void 0 : {
367
+ code: patched,
368
+ map: null
369
+ };
370
+ }
371
+ }
372
+ };
373
+ }
374
+ function definesOf(project, layer) {
375
+ const { "process.env.NODE_ENV": _, ...defines } = project.defines[layer];
376
+ return {
377
+ ...defines,
378
+ __dirname: "\"\"",
379
+ ...layer === "browser" && {
380
+ __webpack_require__: `${registry}.browserRequire`,
381
+ "process.env.__NEXT_TEST_MODE": "true"
382
+ }
383
+ };
384
+ }
385
+ function findMetadataRequests(code) {
386
+ return [...code.matchAll(/["'](next-metadata-(?:image|route)-loader\?[^"']+)["']/g)].map((match) => match[1]);
387
+ }
388
+ /**
389
+ * The files of the cache handlers that `next.config` names, which Next's
390
+ * server loads before its first request (cache.ts): `cacheHandler`, and one
391
+ * of `cacheHandlers` for each kind of `"use cache"` that has one. A path, or
392
+ * a `file:` URL, as Next takes one. A relative path is of the project, as
393
+ * Next's server resolves it.
394
+ */
395
+ function cacheHandlerFilesOf(project) {
396
+ const { cacheHandler, cacheHandlers = {} } = project.config;
397
+ const fileOf = (file) => normalizePath(file.startsWith("file:") ? fileURLToPath(file) : path.resolve(project.root, file));
398
+ return {
399
+ cacheHandler: cacheHandler ? fileOf(cacheHandler) : void 0,
400
+ cacheHandlers: Object.fromEntries(Object.entries(cacheHandlers).flatMap(([kind, file]) => file ? [[kind, fileOf(file)]] : []))
401
+ };
402
+ }
403
+ /**
404
+ * What the dependency optimizer of the ssr layer, which loads the cache
405
+ * handlers, is to know of them before the first request. Vite's scan does not
406
+ * follow the `import()` of a file that a virtual module makes from the
407
+ * config. A file of Next, like its `default.external.js`, is a dependency:
408
+ * the layer imports it by the specifier `toSpecifier()` gives it. Any other
409
+ * file, of the app or of another package, is an entry of the scan, which
410
+ * then finds what it imports.
411
+ */
412
+ function cacheHandlerScan(project, toSpecifier) {
413
+ const { cacheHandler, cacheHandlers } = cacheHandlerFilesOf(project);
414
+ const files = [...new Set([cacheHandler ?? [], Object.values(cacheHandlers)].flat())];
415
+ const isOfNext = (file) => {
416
+ const relative = path.relative(project.nextDir, file);
417
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
418
+ };
419
+ return {
420
+ include: files.filter(isOfNext).map(toSpecifier),
421
+ entries: files.filter((file) => !isOfNext(file)).map(fileEntry)
422
+ };
423
+ }
424
+ function findImports(code) {
425
+ return [...code.matchAll(/(?:\bfrom\s*|\bimport\s*\(?\s*|\brequire\s*\(\s*)(?:\/\*.*?\*\/\s*)?["']([^"'?!]+)["']/g)].map((match) => match[1]);
426
+ }
427
+ const clientBoundaryFiles = /* @__PURE__ */ new Map();
428
+ function findClientBoundaries(nextDir, root, resolver) {
429
+ let files = clientBoundaryFiles.get(nextDir);
430
+ if (!files) {
431
+ files = scanClientBoundaries(nextDir, root);
432
+ clientBoundaryFiles.set(nextDir, files);
433
+ }
434
+ return files.map((file) => resolver.toSpecifier(file)).sort();
435
+ }
436
+ function scanClientBoundaries(nextDir, root) {
437
+ const scripts = fs.readdirSync(path.join(nextDir, "dist/esm"), {
438
+ recursive: true,
439
+ withFileTypes: true
440
+ }).flatMap((entry) => entry.isFile() && entry.name.endsWith(".js") ? [path.join(entry.parentPath, entry.name)] : []);
441
+ const key = createHash("sha256");
442
+ for (const file of scripts) {
443
+ const stats = fs.statSync(file, { throwIfNoEntry: false });
444
+ key.update(`${file}\0${stats?.size}\0${stats?.mtimeMs}\n`);
445
+ }
446
+ const cache = path.join(root, "node_modules/.cache/vitest-plugin-rsc/client-boundaries-1.json");
447
+ const digest = key.digest("hex");
448
+ try {
449
+ const cached = JSON.parse(fs.readFileSync(cache, "utf8"));
450
+ if (cached.key === digest) return cached.files;
451
+ } catch {}
452
+ const files = scripts.filter((file) => {
453
+ const code = fs.readFileSync(file, "utf8");
454
+ return code.includes("use client") && hasDirective(parseAst(code).body, "use client");
455
+ });
456
+ try {
457
+ fs.mkdirSync(path.dirname(cache), { recursive: true });
458
+ const written = `${cache}.${process.pid}`;
459
+ fs.writeFileSync(written, JSON.stringify({
460
+ key: digest,
461
+ files
462
+ }));
463
+ fs.renameSync(written, cache);
464
+ } catch {}
465
+ return files;
466
+ }
467
+ const react = [
468
+ "react",
469
+ "react/jsx-runtime",
470
+ "react/jsx-dev-runtime",
471
+ "react/compiler-runtime"
472
+ ];
473
+ const runtimeImports = {
474
+ rsc: [
475
+ ...react,
476
+ "react-dom",
477
+ ...runtimeImports$1.rsc,
478
+ vendoredFlight("server.edge"),
479
+ vendoredFlight("static.edge"),
480
+ vendoredFlight("client.edge")
481
+ ],
482
+ ssr: [
483
+ ...react,
484
+ "react-dom",
485
+ ...runtimeImports$1.ssr,
486
+ vendoredFlight("client.edge"),
487
+ "next/navigation"
488
+ ],
489
+ browser: [
490
+ ...react,
491
+ "react-dom",
492
+ "react-dom/client",
493
+ ...runtimeImports$1.browser,
494
+ "next/navigation",
495
+ vendoredFlight("client.browser")
496
+ ]
497
+ };
498
+ function vitestPluginNext(options = {}) {
499
+ const chosen = resolveSpeedFeatures(options, process.env);
500
+ let speedFeatures = {
501
+ ...chosen,
502
+ fastCrypto: false,
503
+ cacheDevServer: false
504
+ };
505
+ let nodeModules = "stand-ins";
506
+ let project;
507
+ let cssDifferences = [];
508
+ const serverCode = createServerCode(registry, options);
509
+ const getProject = () => project;
510
+ if (options.build !== void 0 && !nextBuilds.includes(options.build)) throw new Error(`vitest-plugin-rsc: \`build\` is "development" or "production", not ${JSON.stringify(options.build)}.`);
511
+ let build = options.build ?? "development";
512
+ let env;
513
+ let envLoad = Promise.resolve();
514
+ let server;
515
+ let cacheHandlerEntries = [];
516
+ let underVitest = false;
517
+ const pluginApis = /* @__PURE__ */ new WeakMap();
518
+ const clientReferenceOf = (id, config) => {
519
+ if (!pluginApis.has(config)) pluginApis.set(config, getPluginApi(config));
520
+ const references = pluginApis.get(config)?.manager.clientReferenceMetaMap;
521
+ return references && clientReferenceOfModule(references, id);
522
+ };
523
+ const hostPackagesPlugin = {
524
+ name: "vitest-plugin-rsc:next-host-packages",
525
+ resolveId: (source) => serverCode.isHostPackage(source) ? {
526
+ id: source,
527
+ external: true
528
+ } : void 0
529
+ };
530
+ const resolvers = Object.fromEntries(layers.map((layer) => [layer, createLayerResolver(getProject, layer, () => speedFeatures.fastCrypto, () => nodeModules)]));
531
+ let pageRoutes;
532
+ const styles = createStyles({
533
+ getProject,
534
+ environments: environmentOf,
535
+ isServerCode: (file) => serverCode.isServerCode(file, "rsc"),
536
+ isHostFile: serverCode.isHostFile,
537
+ isHostPackageFile: serverCode.isHostPackageFile,
538
+ pageRoutes() {
539
+ if (pageRoutes?.of !== project) {
540
+ const routes = [...project.routes, ...project.componentRoutes].flatMap((route) => route.kind === "page" ? [[entryOf(route), route]] : []);
541
+ pageRoutes = {
542
+ of: project,
543
+ routes: new Map(routes)
544
+ };
545
+ }
546
+ return pageRoutes.routes;
547
+ },
548
+ entryIdOf: (entry) => `\0${routeEntryPrefix}${[...project.routes, ...project.componentRoutes].findIndex((route) => route.kind === "page" && entryOf(route) === entry)}`,
549
+ lists: routeKinds.map((kind) => `\0${routeLists[kind]}`)
550
+ });
551
+ const generatedDefines = () => ({
552
+ ...definesOf(project, "rsc"),
553
+ ...build === "production" && { "process.env.NODE_ENV": "\"production\"" }
554
+ });
555
+ return [
556
+ {
557
+ name: "vitest-plugin-rsc:next-scan",
558
+ configureServer: {
559
+ order: "pre",
560
+ handler(server) {
561
+ const rsc = server.config.environments[environmentOf.rsc];
562
+ const input = rsc.input ?? rsc.build.rolldownOptions.input;
563
+ const inputFiles = typeof input === "string" ? [input] : Object.values(input ?? {});
564
+ const hostEntries = rsc.optimizeDeps.entries ?? (inputFiles.length > 0 ? inputFiles.map(fileEntry) : "**/*.html");
565
+ rsc.optimizeDeps.entries = [
566
+ ...[hostEntries].flat(),
567
+ appEntriesOf(project),
568
+ ...project.middlewareFile ? [fileEntry(project.middlewareFile)] : []
569
+ ];
570
+ const ssr = server.config.environments[environmentOf.ssr];
571
+ const isOwnOrPackageFile = (entry) => {
572
+ const file = normalizePath(entry);
573
+ return file.includes("/node_modules/") || isOwnFile(file);
574
+ };
575
+ ssr.optimizeDeps.entries = [
576
+ ...[ssr.optimizeDeps.entries ?? []].flat(),
577
+ ...rsc.optimizeDeps.entries.filter((entry) => !isOwnOrPackageFile(entry)),
578
+ ...cacheHandlerEntries
579
+ ];
580
+ }
581
+ }
582
+ },
583
+ ...createRunnerEnvironmentPlugins(environmentOf.ssr),
584
+ createPathsPlugin(getProject),
585
+ {
586
+ name: "vitest-plugin-rsc:next",
587
+ enforce: "pre",
588
+ async config(config, { command }) {
589
+ const isBuild = command === "build";
590
+ build = options.build ?? (isBuild ? "production" : "development");
591
+ const testRun = isTestServer(config, command);
592
+ speedFeatures = {
593
+ ...chosen,
594
+ fastCrypto: chosen.fastCrypto && testRun,
595
+ cacheDevServer: chosen.cacheDevServer && testRun
596
+ };
597
+ const pageInNode = servesPageInNode(config, command);
598
+ nodeModules = pageInNode ? "native" : "stand-ins";
599
+ project = await loadNextProject(path.resolve(config.root ?? process.cwd()), void 0, {
600
+ build,
601
+ static: isBuild
602
+ });
603
+ serverCode.configure(project.root, pageInNode ? "native" : "compiled");
604
+ env = project.env;
605
+ if (isBuild) serverCode.addTestFiles(() => false);
606
+ const appPageEntries = await Promise.all([...project.routes, ...project.componentRoutes.slice(0, 1)].map((candidate) => project.loadRouteEntry(candidate)));
607
+ const metadataModules = await Promise.all([...new Set(appPageEntries.flatMap(({ code }) => findMetadataRequests(code)))].map((request) => project.loadMetadata(request)));
608
+ const entryImports = [
609
+ ...appPageEntries.flatMap(({ code }) => findImports(code)),
610
+ ...metadataModules.flatMap(({ code }) => findImports(code)),
611
+ ...findImports(await project.loadMiddlewareEntry() ?? ""),
612
+ ...findImports(await project.loadRootParams())
613
+ ];
614
+ const cacheHandlers = cacheHandlerScan(project, resolvers.ssr.toSpecifier);
615
+ cacheHandlerEntries = cacheHandlers.entries;
616
+ const apiImports = (layer) => Object.entries(project.aliases[layer]).flatMap(([file, target]) => path.dirname(file) === project.nextDir && target && !file.endsWith("og.js") ? [target] : []);
617
+ const toInclude = (layer, sources) => [...new Set(sources.flatMap((source) => {
618
+ const target = resolvers[layer].normalize(source);
619
+ return target && target.startsWith("next/dist/") ? [target] : [];
620
+ }))];
621
+ const rscInclude = toInclude("rsc", [
622
+ ...entryImports,
623
+ ...apiImports("rsc"),
624
+ ...runtimeImports.rsc,
625
+ ...build === "production" ? prerenderImports : []
626
+ ]);
627
+ const clientBoundaries = findClientBoundaries(project.nextDir, project.root, resolvers.rsc);
628
+ const include = {
629
+ rsc: [...rscInclude, nextRouting],
630
+ ssr: [...toInclude("ssr", [
631
+ ...apiImports("ssr"),
632
+ ...runtimeImports.ssr,
633
+ ...clientBoundaries,
634
+ ...cacheHandlers.include
635
+ ]), nextRouting],
636
+ browser: toInclude("browser", [
637
+ ...apiImports("browser"),
638
+ ...runtimeImports.browser,
639
+ ...clientBoundaries
640
+ ])
641
+ };
642
+ const dependenciesOfNext = (layer) => Object.entries(project.aliases[layer]).flatMap(([key, target]) => key.endsWith("$") && target && resolvers[layer].isDependencyOfNext(target) ? [`next > ${key.slice(0, -1)}`] : []);
643
+ const optimizeDeps = (layer) => ({
644
+ include: [
645
+ ...include[layer],
646
+ ...dependenciesOfNext(layer),
647
+ ...project.webpackPackages[layer]
648
+ ],
649
+ rolldownOptions: {
650
+ plugins: [
651
+ ...build === "production" ? [nextProductionPlugin(project.nextDir)] : [],
652
+ ...layer !== "browser" && nodeModules === "native" ? [nativeRequirePlugin()] : [],
653
+ ...layer === "browser" ? [hostPackagesPlugin] : [],
654
+ resolvers[layer].plugin(),
655
+ createDependencyCompilePlugin(getProject, layer, serverCode.isAppCode),
656
+ createScanCompilePlugin(getProject, layer, serverCode.isAppCode),
657
+ ...layer === "rsc" ? [nextClientBoundaryPlugin(getProject, resolvers.rsc)] : [],
658
+ flightConsolePlugin(),
659
+ nextPatchesPlugin(),
660
+ esmRequirePlugin(),
661
+ ...layer === "browser" ? [] : [
662
+ instrumentationPlugin(),
663
+ ...layer === "rsc" ? [serverCode.optimizerPlugin()] : [],
664
+ serverCode.asyncFunctionsOptimizerPlugin(layer)
665
+ ]
666
+ ],
667
+ shimMissingExports: true,
668
+ moduleTypes: { ".js": "jsx" },
669
+ transform: { define: {
670
+ ...definesOf(project, layer),
671
+ ...layer === "browser" ? pageTimersDefine : serverCode.optimizerDefine(layer),
672
+ ...layer === "browser" ? serverCode.browserCacheKey : serverCode.cacheKey,
673
+ ...optimizerCacheKey(project, layer),
674
+ __vitest_plugin_rsc_fast_crypto__: JSON.stringify(speedFeatures.fastCrypto)
675
+ } }
676
+ }
677
+ });
678
+ const css = await project.loadCssOptions();
679
+ cssDifferences = [...css.differences];
680
+ const cssOptions = { ...css.options };
681
+ if (config.css?.preprocessorOptions !== void 0 && cssOptions.preprocessorOptions) {
682
+ delete cssOptions.preprocessorOptions;
683
+ cssDifferences.push("`css.preprocessorOptions` of the Vitest config is used, not `sassOptions` of next.config");
684
+ }
685
+ if (config.css?.transformer === "lightningcss") {
686
+ delete cssOptions.postcss;
687
+ delete cssOptions.modules;
688
+ cssDifferences.push("`css.transformer` of the Vitest config compiles the CSS with Lightning CSS: not with Next's PostCSS plugins, its mode of a CSS module or its class names");
689
+ } else {
690
+ if (config.css?.postcss !== void 0) {
691
+ delete cssOptions.postcss;
692
+ cssDifferences.push("`css.postcss` of the Vitest config is used, not Next's PostCSS plugins, nor Next's mode of a CSS module");
693
+ }
694
+ if (config.css?.modules !== void 0) {
695
+ delete cssOptions.modules;
696
+ cssDifferences.push("`css.modules` of the Vitest config names the classes of a CSS module, not Next's rule");
697
+ }
698
+ }
699
+ const test = config.test ??= {};
700
+ test.setupFiles = [setupFile, ...[test.setupFiles ?? []].flat()];
701
+ (test.browser ??= {}).testerHtmlPath ??= testerHtml;
702
+ test.browser.dependencySourcemaps ??= false;
703
+ if (speedFeatures.watchDom) ((test.expect ??= {}).poll ??= {}).interval ??= 5;
704
+ const jsx = build === "production" && config.oxc !== false && typeof config.oxc?.jsx !== "string" ? { oxc: { jsx: { development: false } } } : {};
705
+ const nodeEnv = JSON.stringify(build);
706
+ const builtLayer = (layer) => isBuild && {
707
+ define: {
708
+ "process.env.NODE_ENV": nodeEnv,
709
+ "global.process.env.NODE_ENV": nodeEnv,
710
+ "globalThis.process.env.NODE_ENV": nodeEnv,
711
+ ...layer === "browser" && { __vite_rsc_require__: `${registry}.browserRequire` }
712
+ },
713
+ build: { rolldownOptions: { shimMissingExports: true } }
714
+ };
715
+ return {
716
+ ...jsx,
717
+ ...speedFeatures.fastCrypto && { cacheDir: path.join(config.cacheDir || "node_modules/.vite", "fast-crypto") },
718
+ css: cssOptions,
719
+ environments: {
720
+ [environmentOf.rsc]: {
721
+ ...builtLayer("rsc"),
722
+ optimizeDeps: optimizeDeps("rsc")
723
+ },
724
+ [environmentOf.ssr]: {
725
+ ...builtLayer("ssr"),
726
+ consumer: "client",
727
+ resolve: {
728
+ conditions: [
729
+ ...(config.resolve?.conditions ?? []).filter((condition) => condition === "vitest-plugin-rsc-source"),
730
+ "module",
731
+ pageInNode ? "node" : "browser",
732
+ "development|production"
733
+ ],
734
+ ...pageInNode && { mainFields: [...defaultServerMainFields] }
735
+ },
736
+ dev: {
737
+ moduleRunnerTransform: true,
738
+ ...testRun && { warmup: [ownFile("ssr")] }
739
+ },
740
+ optimizeDeps: {
741
+ ...optimizeDeps("ssr"),
742
+ exclude: [...ownPackageNames(), "@vitejs/plugin-rsc"]
743
+ }
744
+ },
745
+ [environmentOf.browser]: {
746
+ ...builtLayer("browser"),
747
+ optimizeDeps: optimizeDeps("browser")
748
+ }
749
+ }
750
+ };
751
+ },
752
+ configureServer(vite) {
753
+ server = vite;
754
+ const mainApp = "static/chunks/main-app.js";
755
+ vite.middlewares.use((request, response, next) => {
756
+ const { pathname } = new URL(request.url ?? "/", "http://n");
757
+ if (pathname !== `${project.assetPath}${mainApp}`) return next();
758
+ send(request, response, "", "js", {
759
+ headers: vite.config.server.headers,
760
+ map: { mappings: "" }
761
+ });
762
+ });
763
+ },
764
+ configEnvironment: {
765
+ order: "post",
766
+ handler(name, config, { command }) {
767
+ const layer = layers.find((layer) => environmentOf[layer] === name);
768
+ if (!layer) return;
769
+ const optimizeDeps = config.optimizeDeps;
770
+ if (optimizeDeps?.include) optimizeDeps.include = [...new Set(optimizeDeps.include.flatMap((entry) => resolvers[layer].preBundledName(entry) ?? []))];
771
+ if (layer === "rsc" && nodeModules === "native" && config.resolve?.conditions) {
772
+ config.resolve.conditions = config.resolve.conditions.map((condition) => condition === "browser" ? "node" : condition);
773
+ config.resolve.mainFields = [...defaultServerMainFields];
774
+ }
775
+ if (command === "build") return { keepProcessEnv: true };
776
+ }
777
+ },
778
+ configResolved(config) {
779
+ config.logger.info(describeEnv(env, config.command === "build"));
780
+ for (const difference of [...cssDifferences, ...project.webpackDifferences]) config.logger.warnOnce(`vitest-plugin-rsc: ${difference}.`);
781
+ if (project.edgeRouteFiles.length > 0) config.logger.warnOnce(`vitest-plugin-rsc: Next.js has deprecated its edge runtime. These routes ask for it with \`export const runtime = "edge"\` and run on Node.js here, like the others: ${project.edgeRouteFiles.join(", ")}`);
782
+ const newExports = Object.values(project.newExports).flat();
783
+ const aboutNext = `vitest-plugin-rsc: next@${project.version} has exports that the plugin's stand-ins for its modules have not. Each throws when it is used: ` + newExports.map(({ name, module }) => `\`${name}\` of ${module}`).join(", ") + `. Use a version of @kasperpeulen/vitest-plugin-nextjs that supports next@${project.version}.`;
784
+ if (newExports.length > 0 && !saidOfNext.has(aboutNext)) {
785
+ saidOfNext.add(aboutNext);
786
+ config.logger.warn(aboutNext);
787
+ }
788
+ if (project.unmatchedRoutes.length > 0) config.logger.warnOnce("vitest-plugin-rsc: @next/routing does not find a dynamic route under a folder with a name that a URL percent-encodes. These routes get the not-found page: " + project.unmatchedRoutes.join(", "));
789
+ },
790
+ configureVitest({ vitest, project: testProject }) {
791
+ underVitest = true;
792
+ assertVitestVersion(vitest.version);
793
+ const test = testProject.config;
794
+ const isSetupFile = (file) => test.setupFiles.some((setupFile) => normalizePath(setupFile) === file);
795
+ const isIncluded = createFilter(test.include, test.exclude, { resolve: test.dir || test.root });
796
+ serverCode.addTestFiles((file) => isSetupFile(file) || isIncluded(file));
797
+ if (!test.browser.enabled) test.setupFiles = test.setupFiles.map((file) => normalizePath(file) === setupFile ? nodeSetupFile : file);
798
+ },
799
+ async hotUpdate({ file, modules }) {
800
+ if (underVitest) return;
801
+ if (envFilesOf(false).some((name) => normalizePath(path.join(project.root, name)) === file)) {
802
+ if (this.environment.name !== environmentOf.rsc) return [];
803
+ const loaded = envLoad.then(() => project.loadEnv());
804
+ envLoad = loaded.catch(() => {});
805
+ env = await loaded;
806
+ for (const environment of Object.values(server?.environments ?? {})) {
807
+ const envModule = environment.moduleGraph.getModuleById(`\0${envId}`);
808
+ if (envModule) environment.moduleGraph.invalidateModule(envModule);
809
+ }
810
+ this.environment.logger.info(`page reload ${path.relative(this.environment.config.root, file)} (vitest-plugin-rsc: an env file changed)`, { timestamp: true });
811
+ this.environment.hot.send({
812
+ type: "full-reload",
813
+ path: "*",
814
+ triggeredBy: file
815
+ });
816
+ return [];
817
+ }
818
+ if (this.environment.name !== environmentOf.rsc) return;
819
+ if (!modules.some(importedByRoute)) return;
820
+ this.environment.logger.info(`page reload ${path.relative(this.environment.config.root, file)} (vitest-plugin-rsc: a file of the app changed)`, { timestamp: true });
821
+ this.environment.hot.send({
822
+ type: "full-reload",
823
+ path: "*",
824
+ triggeredBy: file
825
+ });
826
+ return [];
827
+ },
828
+ resolveId(source) {
829
+ if (source === manifestId || source === envId || isRouteModule(source)) return `\0${source}`;
830
+ if (source === fastCryptoSetupId) return `\0${source}`;
831
+ if (source === middlewareId || source === middlewareEntryId) return `\0${source}`;
832
+ if (source === "virtual:vitest-plugin-rsc/next-use-cache" || source === cacheHandlersId) return `\0${source}`;
833
+ if (source === prerenderId) return `\0${source}`;
834
+ if (source === "next/root-params") return `\0${rootParamsId}`;
835
+ if (project.isMetadataRequest(source)) return `\0${metadataPrefix}${source}`;
836
+ if (source.endsWith(project.metadataImageMeta)) return source.slice(0, -project.metadataImageMeta.length);
837
+ },
838
+ async load(id) {
839
+ if (id.startsWith(`\0${metadataPrefix}`)) {
840
+ const { code, watchFiles } = await project.loadMetadata(id.slice(metadataPrefix.length + 1));
841
+ for (const file of watchFiles) this.addWatchFile(file);
842
+ return serverCode.compile(code, "next-metadata.js", generatedDefines());
843
+ }
844
+ if (id === `\0virtual:vitest-plugin-rsc/next-use-cache`) return useCacheModule(registry);
845
+ if (id === `\0${rootParamsId}`) return project.loadRootParams();
846
+ if (id === `\0${prerenderId}`) return build === "production" ? `export { prerenderVerdictOf } from ${JSON.stringify(ownFile("prerender-verdict"))};\n` : "export function prerenderVerdictOf() {\n throw new Error('vitest-plugin-rsc: only `build: \"production\"` prerenders');\n}\n";
847
+ if (id === `\0${cacheHandlersId}`) {
848
+ const { cacheHandler, cacheHandlers } = cacheHandlerFilesOf(project);
849
+ const load = (file) => `() => import(${JSON.stringify(file)})`;
850
+ const kinds = Object.entries(cacheHandlers).map(([kind, file]) => ` ${JSON.stringify(kind)}: ${load(file)},`);
851
+ return `export const cacheHandler = ${cacheHandler ? load(cacheHandler) : "undefined"};\nexport const cacheHandlers = {\n${kinds.join("\n")}\n};\n`;
852
+ }
853
+ if (id === `\0${fastCryptoSetupId}`) return fastCryptoSetup(speedFeatures.fastCrypto);
854
+ if (id === `\0${envId}`) {
855
+ for (const name of env.files) this.addWatchFile(path.join(project.root, name));
856
+ return `export default ${JSON.stringify(env.values)};\n`;
857
+ }
858
+ if (id === `\0${manifestId}`) {
859
+ const routes = project.routes.map(({ kind, page, pathname }) => ({
860
+ kind,
861
+ page,
862
+ pathname
863
+ }));
864
+ return `export const routes = ${JSON.stringify([...routes, ...project.componentRoutes])};\nexport const nextConfig = ${JSON.stringify(this.environment.mode === "build" ? withPathsFromRoot(project.config, project.root) : project.config)};\nexport const routing = ${JSON.stringify(project.routing)};\nexport const routesManifest = ${JSON.stringify(project.routesManifest)};\nexport const preview = ${JSON.stringify(project.preview)};\nexport const build = ${JSON.stringify(build)};\nexport const speedFeatures = ${JSON.stringify(speedFeatures)};\n`;
865
+ }
866
+ const hasMiddleware = project.middlewareFile !== void 0 && this.environment.name === environmentOf.rsc;
867
+ if (id === `\0${middlewareId}`) {
868
+ if (!hasMiddleware) return "export default undefined;\n";
869
+ const entry = JSON.stringify(middlewareEntryId);
870
+ return `export default () => import(${entry});\n${hmrBoundary([entry])}`;
871
+ }
872
+ if (id === `\0${middlewareEntryId}`) {
873
+ const code = hasMiddleware && await project.loadMiddlewareEntry();
874
+ if (!code) return "export {};";
875
+ return serverCode.compile(code, "next-middleware-entry.js", generatedDefines());
876
+ }
877
+ if (!id.startsWith("\0") || !isRouteModule(id.slice(1))) return;
878
+ if (this.environment.name !== environmentOf.rsc) return "export default {};";
879
+ const routes = [...project.routes, ...project.componentRoutes];
880
+ const listed = routeKinds.find((kind) => id === `\0${routeLists[kind]}`);
881
+ if (listed) {
882
+ const entries = routes.flatMap((route, index) => route.kind === listed ? [{
883
+ name: JSON.stringify(entryOf(route)),
884
+ entry: JSON.stringify(routeEntryPrefix + index)
885
+ }] : []);
886
+ return `export default {\n${entries.map(({ name, entry }) => ` ${name}: () => import(${entry}),`).join("\n")}\n};\n${hmrBoundary(entries.map(({ entry }) => entry))}`;
887
+ }
888
+ const route = routes[Number(id.slice(routeEntryPrefix.length + 1))];
889
+ const entry = await project.loadRouteEntry(route);
890
+ for (const file of entry.watchFiles) this.addWatchFile(file);
891
+ return serverCode.compile(entry.code, `${id.replace(/\W+/g, "-")}.js`, generatedDefines());
892
+ }
893
+ },
894
+ {
895
+ name: "vitest-plugin-rsc:next-setup-files",
896
+ enforce: "post",
897
+ configureVitest({ project: testProject }) {
898
+ const test = testProject.config;
899
+ if (!test.browser.enabled) testProject.provide("vitest-plugin-rsc:setup-files", test.setupFiles);
900
+ }
901
+ },
902
+ ...clientFiles({
903
+ environments: {
904
+ rsc: environmentOf.rsc,
905
+ browser: environmentOf.browser
906
+ },
907
+ testingLibrary,
908
+ clientComponents: "@kasperpeulen/vitest-plugin-nextjs/client-components",
909
+ internal: ownFile("internal"),
910
+ isHostFile: serverCode.isHostFile,
911
+ isTestFile: serverCode.isTestFile,
912
+ isHostPackage: serverCode.isHostPackage,
913
+ clientReferenceOf
914
+ }),
915
+ routeLookupPlugin({
916
+ getProject,
917
+ specifier: testingLibrary.specifier
918
+ }),
919
+ serverCode.plugin({
920
+ [environmentOf.rsc]: "rsc",
921
+ [environmentOf.ssr]: "ssr"
922
+ }),
923
+ pageTimersPlugin(environmentOf.browser, serverCode.isHostFile),
924
+ ...styles.plugins,
925
+ routesAheadPlugin({
926
+ root: () => getProject().root,
927
+ compile: styles.compileRoute
928
+ }),
929
+ serverCode.asyncFunctionsPlugin(environmentOf.rsc),
930
+ createUseCachePlugin(getProject, (environment) => layers.find((layer) => environmentOf[layer] === environment), serverCode.isAppCode),
931
+ createCompilePlugin(getProject, (environment) => layers.find((layer) => environmentOf[layer] === environment), serverCode.isAppCode),
932
+ createCommonJsPlugin((environment) => layers.find((layer) => environmentOf[layer] === environment), serverCode.isAppCode, (source, layer) => layer !== "browser" && isNativeNodeModule(nodeModuleOf(source, nodeModules) ?? "")),
933
+ ...layers.map((layer) => ({
934
+ ...resolvers[layer].plugin(),
935
+ applyToEnvironment: (environment) => environment.name === environmentOf[layer]
936
+ })),
937
+ ...layers.map((layer) => dependencyBuildPlugin(getProject, serverCode, layer)),
938
+ {
939
+ ...flightConsolePlugin(),
940
+ apply: "build"
941
+ },
942
+ {
943
+ ...nextPatchesPlugin(),
944
+ apply: "build"
945
+ },
946
+ {
947
+ ...instrumentationPlugin(),
948
+ apply: "build"
949
+ },
950
+ nextStaticBuild({
951
+ environments: environmentOf,
952
+ entries: {
953
+ ssr: "@kasperpeulen/vitest-plugin-nextjs/ssr",
954
+ browser: "@kasperpeulen/vitest-plugin-nextjs/client"
955
+ },
956
+ emittedFiles: () => [...project.emittedFiles(), ...styles.builtFiles()],
957
+ rscModule: ownFile("rsc"),
958
+ isHostFile: (id) => builtHostFileOf(id, project.root, serverCode.isHostFile) !== void 0
959
+ })
960
+ ];
961
+ }
962
+ function dependencyBuildPlugin(getProject, serverCode, layer) {
963
+ const compile = createDependencyCompilePlugin(getProject, layer, serverCode.isAppCode);
964
+ return {
965
+ name: `vitest-plugin-rsc:next-build-dependency:${layer}`,
966
+ apply: "build",
967
+ applyToEnvironment: (environment) => environment.name === environmentOf[layer],
968
+ async transform(code, id) {
969
+ const file = id.split("?")[0];
970
+ if (!normalizePath(file).includes("/node_modules/") || !/\.[cm]?js$/.test(file)) return;
971
+ const compiled = await compile.transform(code, file);
972
+ const isServerCode = layer !== "browser" && serverCode.isServerCode(file, layer);
973
+ const forStores = isServerCode ? serverCode.forStores(this, compiled?.code ?? code, file, layer) : void 0;
974
+ const source = forStores?.code ?? compiled?.code ?? code;
975
+ const kept = source === (compiled?.code ?? code) ? compiled : { code: source };
976
+ const define = Object.fromEntries(Object.entries(definesOf(getProject(), layer)).filter(([key]) => source.includes(key.replace(/^typeof /, ""))));
977
+ if (!isServerCode && Object.keys(define).length === 0) return kept;
978
+ try {
979
+ return (isServerCode ? await compileServerCode(source, file, registry, {
980
+ ...define,
981
+ ...forStores?.define
982
+ }) : await transformWithOxc(source, file, {
983
+ lang: "js",
984
+ define
985
+ })) ?? kept;
986
+ } catch (error) {
987
+ const reason = String(error instanceof Error ? error.message : error);
988
+ this.warn(`vitest-plugin-rsc: ${file} keeps the constants of Next's ${layer} layer, it does not parse as JavaScript. ${reason.replace(/\s+/g, " ")}`);
989
+ return kept;
990
+ }
991
+ }
992
+ };
993
+ }
994
+ //#endregion
995
+ export { vitestPluginNext };