@kasperpeulen/vitest-plugin-nextjs 0.0.0-stage → 0.1.0-alpha.1

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