@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
@@ -0,0 +1,71 @@
1
+ import { NextBuild } from "../build.js";
2
+ import { NextContext } from "./context.js";
3
+ import { NextProject } from "../project.js";
4
+ import fs from "node:fs";
5
+
6
+ //#region src/project/loaders.d.ts
7
+ type LoaderContext = {
8
+ getOptions(): unknown; /** The options as a loader of webpack 4 reads them. */
9
+ query: unknown;
10
+ mode: "development" | "production";
11
+ _module: {
12
+ buildInfo: Record<string, unknown>;
13
+ };
14
+ async(): (error: Error | null, ...result: unknown[]) => void;
15
+ callback(error: Error | null, ...result: unknown[]): void;
16
+ cacheable(flag?: boolean): void;
17
+ currentTraceSpan: TraceSpan;
18
+ resourcePath: string;
19
+ resourceQuery: string;
20
+ context: string;
21
+ rootContext: string;
22
+ emitFile(name: string, content: Buffer): void;
23
+ emitWarning(warning: Error): void;
24
+ emitError(error: Error): void;
25
+ addDependency(file: string): void;
26
+ addContextDependency(directory: string): void;
27
+ resolve(directory: string, request: string, callback: (error: Error | null, file?: string) => void): void;
28
+ getResolve(): () => Promise<never>;
29
+ loadModule(request: string, callback: (error: Error | null, source?: string, map?: null, module?: LoadedModule) => void): void;
30
+ fs: typeof fs;
31
+ utils: {
32
+ contextify(context: string, request: string): string;
33
+ };
34
+ sourceMap: boolean;
35
+ };
36
+ type TraceSpan = {
37
+ traceChild(): TraceSpan;
38
+ traceFn<T>(fn: () => T): T;
39
+ traceAsyncFn<T>(fn: () => T): T;
40
+ setAttribute(): void;
41
+ };
42
+ type Loader = (this: LoaderContext, ...input: unknown[]) => unknown;
43
+ type LoadedModule = {
44
+ dependencies: {
45
+ constructor: {
46
+ name: "HarmonyExportSpecifierDependency";
47
+ };
48
+ name: string;
49
+ }[];
50
+ };
51
+ /** Calls a webpack loader of Next for one module, the way webpack does. */
52
+ type RunLoader = (loader: Loader, options: unknown, resource: string, ...input: unknown[]) => Promise<unknown[]>;
53
+ /**
54
+ * `sassOptions` of `next.config`, as Vite's options of Sass. Next gives
55
+ * sass-loader the options of Sass, and `additionalData`, or `prependData`,
56
+ * which goes before the source of each file, as it does for Vite. A path to
57
+ * load from is one of the project, where Next's build runs. Not
58
+ * `implementation`: Vite takes `sass-embedded` where it is installed, and
59
+ * `sass` otherwise.
60
+ */
61
+ declare function sassPreprocessorOptions(sassOptions: Record<string, unknown>, root: string): Record<string, unknown> | undefined;
62
+ /**
63
+ * Next's webpack loaders for fonts and images, what they emit for the
64
+ * browser, and Next's image optimizer. `build` is the code that Next's
65
+ * runtime and React run, see build.ts.
66
+ */
67
+ declare function createLoaders(context: NextContext, build: NextBuild): Pick<NextProject, "isImage" | "loadImage" | "loadFont" | "loadCssOptions" | "assetPath" | "readEmittedFile" | "emittedFiles" | "optimizeImage" | "loadRootParams"> & {
68
+ runLoader: RunLoader;
69
+ };
70
+ //#endregion
71
+ export { Loader, LoaderContext, RunLoader, createLoaders, sassPreprocessorOptions };
@@ -0,0 +1,300 @@
1
+ import { fontLoaderPath } from "../compat/paths.js";
2
+ import { exportsOf } from "./module-exports.js";
3
+ import { createRequire } from "node:module";
4
+ import path from "node:path";
5
+ import fs from "node:fs";
6
+ import querystring from "node:querystring";
7
+ import { compileFunction } from "node:vm";
8
+ //#region src/project/loaders.ts
9
+ function hasOtherPostCssConfig(root) {
10
+ const names = (directory) => {
11
+ try {
12
+ return fs.readdirSync(directory);
13
+ } catch {
14
+ return [];
15
+ }
16
+ };
17
+ return names(root).some((name) => /^(?:\.postcssrc(?:\..+)?|postcss\.config\..+)$/.test(name)) || names(path.join(root, ".config")).some((name) => name.startsWith("postcssrc"));
18
+ }
19
+ /**
20
+ * `sassOptions` of `next.config`, as Vite's options of Sass. Next gives
21
+ * sass-loader the options of Sass, and `additionalData`, or `prependData`,
22
+ * which goes before the source of each file, as it does for Vite. A path to
23
+ * load from is one of the project, where Next's build runs. Not
24
+ * `implementation`: Vite takes `sass-embedded` where it is installed, and
25
+ * `sass` otherwise.
26
+ */
27
+ function sassPreprocessorOptions(sassOptions, root) {
28
+ const { prependData, additionalData, implementation: _, loadPaths, ...options } = sassOptions;
29
+ const data = prependData ?? additionalData;
30
+ const sass = {
31
+ ...options,
32
+ ...Array.isArray(loadPaths) && { loadPaths: loadPaths.map((entry) => path.resolve(root, String(entry))) },
33
+ ...data !== void 0 && { additionalData: typeof data === "function" ? (source, file) => data(source, {
34
+ resourcePath: file,
35
+ rootContext: root
36
+ }) : data }
37
+ };
38
+ return Object.keys(sass).length > 0 ? sass : void 0;
39
+ }
40
+ /**
41
+ * Next's webpack loaders for fonts and images, what they emit for the
42
+ * browser, and Next's image optimizer. `build` is the code that Next's
43
+ * runtime and React run, see build.ts.
44
+ */
45
+ function createLoaders(context, build) {
46
+ const { root, projectRequire, nextDir, fail, config, next } = context;
47
+ const { COMPILER_NAMES, getContentType, getExtension, getNextFontLoader, getPostCssPlugins, getSupportedBrowsers, getCssModuleLoader, localByDefault, findConfig, imageOptimizer, nextFontLoader, nextRootParamsLoader, nextImageLoader, nextImageLoaderRegex } = next;
48
+ const emitted = /* @__PURE__ */ new Map();
49
+ const span = {
50
+ traceChild: () => span,
51
+ traceFn: (fn) => fn(),
52
+ traceAsyncFn: (fn) => fn(),
53
+ setAttribute: () => {}
54
+ };
55
+ const runLoaderEmitting = (emits) => (loader, options, resource, ...input) => new Promise((resolve, reject) => {
56
+ const queryAt = resource.indexOf("?");
57
+ const resourcePath = queryAt < 0 ? resource : resource.slice(0, queryAt);
58
+ let callsBack = false;
59
+ const callback = (error, ...result) => error ? reject(error) : resolve(result);
60
+ const context = {
61
+ getOptions: () => options ?? {},
62
+ query: options ?? {},
63
+ mode: build,
64
+ _module: { buildInfo: {} },
65
+ async: () => {
66
+ callsBack = true;
67
+ return callback;
68
+ },
69
+ callback: (...args) => {
70
+ callsBack = true;
71
+ callback(...args);
72
+ },
73
+ cacheable: () => {},
74
+ currentTraceSpan: span,
75
+ resourcePath,
76
+ resourceQuery: queryAt < 0 ? "" : resource.slice(queryAt),
77
+ context: path.dirname(resourcePath),
78
+ rootContext: root,
79
+ emitFile: (name, content) => {
80
+ const file = name.replace(/^\//, "");
81
+ emitted.set(file, content);
82
+ emits?.(file);
83
+ },
84
+ emitWarning: (warning) => console.warn(warning),
85
+ emitError: reject,
86
+ addDependency: () => {},
87
+ addContextDependency: () => {},
88
+ resolve: (directory, request, callback) => {
89
+ const file = path.resolve(directory, request);
90
+ if (fs.existsSync(file)) callback(null, file);
91
+ else callback(/* @__PURE__ */ new Error(`Can't resolve '${request}' in '${directory}'`));
92
+ },
93
+ getResolve: () => () => Promise.reject(/* @__PURE__ */ new Error("Not resolved by vitest-plugin-rsc")),
94
+ loadModule: (request, respond) => {
95
+ const read = async () => {
96
+ const source = await fs.promises.readFile(request, "utf8");
97
+ const { names } = await exportsOf(source, request);
98
+ return {
99
+ source,
100
+ module: { dependencies: [...names].filter((name) => name !== "default").map((name) => ({
101
+ constructor: { name: "HarmonyExportSpecifierDependency" },
102
+ name
103
+ })) }
104
+ };
105
+ };
106
+ read().then(({ source, module }) => respond(null, source, null, module), (error) => respond(error));
107
+ },
108
+ fs,
109
+ utils: { contextify: (_context, request) => request },
110
+ sourceMap: false
111
+ };
112
+ Promise.resolve(loader.call(context, ...input)).then((result) => callsBack || resolve([result]), reject);
113
+ });
114
+ const runLoader = runLoaderEmitting();
115
+ const resolve = (id) => {
116
+ try {
117
+ return projectRequire.resolve(id);
118
+ } catch (error) {
119
+ return fail(`${id} is not there`, error);
120
+ }
121
+ };
122
+ let postcss;
123
+ try {
124
+ postcss = createRequire(path.join(nextDir, "package.json"))("postcss");
125
+ } catch (error) {
126
+ fail("it no longer depends on `postcss`", error);
127
+ }
128
+ const fontLoaders = ["google", "local"].map((name) => {
129
+ const target = resolve(`next/font/${name}/target.css`);
130
+ const loaders = getNextFontLoader({
131
+ hasAppDir: true,
132
+ isClient: true,
133
+ isServer: false,
134
+ isDevelopment: true,
135
+ assetPrefix: config.assetPrefix,
136
+ deploymentId: config.deploymentId,
137
+ experimental: config.experimental
138
+ }, async () => ({ postcss }), resolve(fontLoaderPath(name)));
139
+ const cssLoader = loaders.find((entry) => entry.loader?.includes("css-loader"));
140
+ const fontLoader = loaders.find((entry) => entry.loader === "next-font-loader");
141
+ if (!cssLoader?.loader || !fontLoader) fail("`getNextFontLoader()` no longer uses css-loader and next-font-loader");
142
+ return {
143
+ prefix: `next/font/${name}/target.css?`,
144
+ target,
145
+ cssLoader: projectRequire(cssLoader.loader).default,
146
+ cssOptions: cssLoader.options,
147
+ fontOptions: fontLoader.options
148
+ };
149
+ });
150
+ const fonts = /* @__PURE__ */ new Map();
151
+ const loadFont = async (request) => {
152
+ const font = fontLoaders.find((candidate) => request.startsWith(candidate.prefix));
153
+ const resource = font.target + request.slice(font.prefix.length - 1);
154
+ const files = [];
155
+ const [css, map, meta] = await runLoaderEmitting((file) => {
156
+ if (/\.(?:woff|woff2|eot|ttf|otf)$/.test(file)) files.push(file);
157
+ })(nextFontLoader, font.fontOptions, resource);
158
+ const [code] = await runLoader(font.cssLoader, font.cssOptions, resource, css, map, meta);
159
+ const cssModule = {
160
+ id: resource,
161
+ exports: {}
162
+ };
163
+ compileFunction(code, [
164
+ "module",
165
+ "exports",
166
+ "require"
167
+ ])(cssModule, cssModule.exports, projectRequire);
168
+ const list = cssModule.exports;
169
+ if (!Array.isArray(list) || !list.locals) fail("css-loader no longer makes a list of CSS with `locals` of a `next/font` call");
170
+ return {
171
+ css: String(list),
172
+ exports: list.locals,
173
+ files
174
+ };
175
+ };
176
+ const [cssModuleLoader] = getCssModuleLoader({
177
+ isClient: false,
178
+ isServer: true,
179
+ hasAppDir: true,
180
+ isAppDir: true,
181
+ isDevelopment: false,
182
+ experimental: {
183
+ ...config.experimental,
184
+ useLightningcss: false
185
+ }
186
+ }, postcss).filter((entry) => entry.loader?.includes("css-loader"));
187
+ const rule = cssModuleLoader?.options?.modules;
188
+ const mode = typeof rule?.mode === "string" ? rule.mode : fail("`getCssModuleLoader()` no longer gives css-loader the `mode` of a CSS module");
189
+ const getLocalIdent = typeof rule?.getLocalIdent === "function" ? rule.getLocalIdent : fail("`getCssModuleLoader()` no longer gives css-loader a `getLocalIdent`");
190
+ const checkMode = {
191
+ postcssPlugin: "vitest-plugin-rsc:next-css-module-mode",
192
+ Once(root, { result, postcss: process }) {
193
+ const file = (result.opts.from ?? "").split("?")[0];
194
+ if (!/\.module\.(?:css|scss|sass)$/.test(file)) return;
195
+ process([localByDefault({ mode })]).process(root.clone(), { from: file }).root;
196
+ }
197
+ };
198
+ const loadCssOptions = async () => {
199
+ const { experimental } = config;
200
+ const differences = [];
201
+ if (experimental.useLightningcss) differences.push("`experimental.useLightningcss` is not supported: the CSS is compiled with PostCSS");
202
+ let postcss;
203
+ if (await findConfig(root, "postcss") || !hasOtherPostCssConfig(root)) postcss = { plugins: [...await getPostCssPlugins(root, getSupportedBrowsers(root, false), experimental.disablePostcssPresetEnv), checkMode] };
204
+ else differences.push("the app's PostCSS config is one only Vite reads: a CSS module is not held to Next's mode for it");
205
+ const sass = sassPreprocessorOptions(config.sassOptions ?? {}, root);
206
+ return {
207
+ options: {
208
+ postcss,
209
+ ...sass && { preprocessorOptions: {
210
+ scss: sass,
211
+ sass
212
+ } },
213
+ modules: { generateScopedName: (name, file) => getLocalIdent({
214
+ rootContext: root,
215
+ resourcePath: file.split("?")[0]
216
+ }, void 0, name, { context: root }) }
217
+ },
218
+ differences
219
+ };
220
+ };
221
+ const emittedPath = `${config.assetPrefix.startsWith("/") ? config.assetPrefix : ""}/_next/`;
222
+ const { images } = config;
223
+ return {
224
+ runLoader,
225
+ isImage: (file) => !images.disableStaticImages && nextImageLoaderRegex.test(file),
226
+ async loadImage(file) {
227
+ const options = {
228
+ isDev: false,
229
+ compilerType: COMPILER_NAMES.client,
230
+ assetPrefix: config.assetPrefix,
231
+ basePath: config.basePath,
232
+ outputHashSalt: config.outputHashSalt
233
+ };
234
+ const [code] = await runLoader(nextImageLoader, options, file, await fs.promises.readFile(file));
235
+ if (typeof code !== "string" || !code.startsWith("export default {")) fail("next-image-loader no longer exports the data of an image");
236
+ return code;
237
+ },
238
+ loadCssOptions,
239
+ loadFont(request) {
240
+ let font = fonts.get(request);
241
+ if (!font) {
242
+ fonts.set(request, font = loadFont(request));
243
+ font.catch(() => fonts.delete(request));
244
+ }
245
+ return font;
246
+ },
247
+ assetPath: emittedPath,
248
+ async loadRootParams() {
249
+ const options = {
250
+ appDir: context.appDir,
251
+ pageExtensions: config.pageExtensions
252
+ };
253
+ const [code] = await runLoader(nextRootParamsLoader, options, context.appDir);
254
+ return String(code);
255
+ },
256
+ emittedFiles: () => [...emitted].map(([name, body]) => ({
257
+ pathname: `${emittedPath}${name}`,
258
+ body
259
+ })),
260
+ readEmittedFile(pathname) {
261
+ if (!pathname.startsWith(emittedPath)) return;
262
+ const name = pathname.slice(emittedPath.length);
263
+ const body = emitted.get(name);
264
+ if (!body) return;
265
+ return {
266
+ body,
267
+ contentType: getContentType(path.extname(name).slice(1)) ?? ""
268
+ };
269
+ },
270
+ async optimizeImage(request, response, serveFile) {
271
+ const url = new URL(request.url, "http://n");
272
+ if (url.pathname !== images.path) return false;
273
+ if (images.loader !== "default" || images.unoptimized) {
274
+ response.statusCode = 404;
275
+ response.end();
276
+ return true;
277
+ }
278
+ const { ImageOptimizerCache, ImageError } = imageOptimizer;
279
+ const query = querystring.parse(url.search.slice(1));
280
+ const params = ImageOptimizerCache.validateParams(request, query, config, false);
281
+ if ("errorMessage" in params) {
282
+ response.statusCode = 400;
283
+ response.end(params.errorMessage);
284
+ return true;
285
+ }
286
+ try {
287
+ const upstream = params.isAbsolute ? await imageOptimizer.fetchExternalImage(params.href, images.dangerouslyAllowLocalIP, images.maximumResponseBody, images.maximumRedirects) : await imageOptimizer.fetchInternalImage(params.href, request, response, images.maximumResponseBody, serveFile);
288
+ const { buffer, contentType, maxAge, etag } = await imageOptimizer.imageOptimizer(upstream, params, config, { isDev: false });
289
+ imageOptimizer.sendResponse(request, response, params.href, getExtension(contentType), buffer, etag, params.isStatic, "MISS", images, maxAge, true);
290
+ } catch (error) {
291
+ if (!(error instanceof ImageError)) throw error;
292
+ response.statusCode = error.statusCode;
293
+ response.end(error.message);
294
+ }
295
+ return true;
296
+ }
297
+ };
298
+ }
299
+ //#endregion
300
+ export { createLoaders, sassPreprocessorOptions };
@@ -0,0 +1,8 @@
1
+ import { RunLoader } from "./loaders.js";
2
+ import { NextContext } from "./context.js";
3
+ import { NextProject } from "../project.js";
4
+
5
+ //#region src/project/metadata.d.ts
6
+ declare function createMetadataLoaders(context: NextContext, runLoader: RunLoader): Pick<NextProject, "metadataImageMeta" | "isMetadataRequest" | "loadMetadata">;
7
+ //#endregion
8
+ export { createMetadataLoaders };
@@ -0,0 +1,34 @@
1
+ import fs from "node:fs";
2
+ import querystring from "node:querystring";
3
+ //#region src/project/metadata.ts
4
+ const metadataRequest = /^(next-metadata-(?:image|route)-loader)\?([^!]*)!(.*)$/;
5
+ function createMetadataLoaders(context, runLoader) {
6
+ const { fail, replace, next } = context;
7
+ const { nextMetadataImageLoader, nextMetadataRouteLoader, WEBPACK_RESOURCE_QUERIES } = next;
8
+ return {
9
+ metadataImageMeta: `?${WEBPACK_RESOURCE_QUERIES.metadataImageMeta}`,
10
+ isMetadataRequest: (source) => metadataRequest.test(source),
11
+ async loadMetadata(request) {
12
+ const [, loader, query = "", resource = ""] = metadataRequest.exec(request) ?? fail(`${request} is no request for a metadata loader`);
13
+ const options = querystring.parse(query);
14
+ if (loader === "next-metadata-route-loader") {
15
+ const file = String(options.filePath);
16
+ let [code] = await runLoader(nextMetadataRouteLoader, options, resource);
17
+ if (options.isDynamicRouteExtension !== "1" && process.env.NODE_ENV !== "production") code = replace(code, JSON.stringify("no-cache, no-store"), JSON.stringify("public, max-age=0, must-revalidate"), "the route of a static metadata file");
18
+ return {
19
+ code,
20
+ watchFiles: [file]
21
+ };
22
+ }
23
+ const file = resource.split("?")[0];
24
+ const content = await fs.promises.readFile(file);
25
+ const [code] = await runLoader(nextMetadataImageLoader, options, resource, content);
26
+ return {
27
+ code,
28
+ watchFiles: [file]
29
+ };
30
+ }
31
+ };
32
+ }
33
+ //#endregion
34
+ export { createMetadataLoaders };
@@ -0,0 +1,29 @@
1
+ //#region src/project/module-exports.d.ts
2
+ /**
3
+ * The names an ES module exports in its own code, `default` among them, and
4
+ * the modules of its `export *`. Of JavaScript, like Next's compiled files and
5
+ * the modules the plugin generates.
6
+ */
7
+ declare function declaredExports(code: string): {
8
+ names: Set<string>;
9
+ stars: string[];
10
+ };
11
+ /**
12
+ * What an ES module of Next exports: declared there, exported apart from its
13
+ * declaration, or from another module of Next with `export ... from` or
14
+ * `export *`. A module of CommonJS has none here.
15
+ */
16
+ declare function exportNamesOf(file: string, seen?: Set<string>): Set<string>;
17
+ /** Whether an ES module of Next exports a name: see `exportNamesOf()`. */
18
+ declare const hasExport: (file: string, name: string) => boolean;
19
+ /**
20
+ * What a module of the app exports, read off its code: the names, `default`
21
+ * among them, and the modules of its `export *`, whose names are theirs.
22
+ */
23
+ declare function exportsOf(code: string, file: string): Promise<{
24
+ names: Set<string>;
25
+ stars: string[];
26
+ }>;
27
+ declare function exportStubs(code: string, file: string): Promise<string>;
28
+ //#endregion
29
+ export { declaredExports, exportNamesOf, exportStubs, exportsOf, hasExport };
@@ -0,0 +1,77 @@
1
+ import { moduleFileAt } from "./context.js";
2
+ import { parseAst, transformWithOxc } from "vite";
3
+ import path from "node:path";
4
+ import fs from "node:fs";
5
+ //#region src/project/module-exports.ts
6
+ function nameOf(node) {
7
+ return "name" in node ? String(node.name) : "value" in node ? String(node.value) : "";
8
+ }
9
+ function bound(pattern, names) {
10
+ if (!pattern || !("type" in pattern)) return;
11
+ if (pattern.type === "Identifier") names.add(nameOf(pattern));
12
+ for (const key of [
13
+ "properties",
14
+ "elements",
15
+ "value",
16
+ "argument",
17
+ "left"
18
+ ]) {
19
+ const child = pattern[key];
20
+ for (const part of [child].flat()) if (typeof part === "object") bound(part, names);
21
+ }
22
+ }
23
+ /**
24
+ * The names an ES module exports in its own code, `default` among them, and
25
+ * the modules of its `export *`. Of JavaScript, like Next's compiled files and
26
+ * the modules the plugin generates.
27
+ */
28
+ function declaredExports(code) {
29
+ const names = /* @__PURE__ */ new Set();
30
+ const stars = [];
31
+ for (const node of parseAst(code).body) if (node.type === "ExportDefaultDeclaration") names.add("default");
32
+ else if (node.type === "ExportAllDeclaration") {
33
+ if (node.exported) names.add(nameOf(node.exported));
34
+ else stars.push(node.source.value);
35
+ } else if (node.type === "ExportNamedDeclaration") {
36
+ for (const specifier of node.specifiers) names.add(nameOf(specifier.exported));
37
+ const { declaration } = node;
38
+ if (declaration?.type === "VariableDeclaration") for (const { id } of declaration.declarations) bound(id, names);
39
+ else if (declaration && "id" in declaration && declaration.id) names.add(nameOf(declaration.id));
40
+ }
41
+ return {
42
+ names,
43
+ stars
44
+ };
45
+ }
46
+ /**
47
+ * What an ES module of Next exports: declared there, exported apart from its
48
+ * declaration, or from another module of Next with `export ... from` or
49
+ * `export *`. A module of CommonJS has none here.
50
+ */
51
+ function exportNamesOf(file, seen = /* @__PURE__ */ new Set()) {
52
+ if (seen.has(file)) return /* @__PURE__ */ new Set();
53
+ seen.add(file);
54
+ const { names, stars } = declaredExports(fs.readFileSync(file, "utf8"));
55
+ for (const star of stars) {
56
+ const from = star.startsWith(".") && moduleFileAt(path.resolve(path.dirname(file), star));
57
+ if (!from) continue;
58
+ for (const name of exportNamesOf(from, seen)) if (name !== "default") names.add(name);
59
+ }
60
+ return names;
61
+ }
62
+ /** Whether an ES module of Next exports a name: see `exportNamesOf()`. */
63
+ const hasExport = (file, name) => exportNamesOf(file).has(name);
64
+ /**
65
+ * What a module of the app exports, read off its code: the names, `default`
66
+ * among them, and the modules of its `export *`, whose names are theirs.
67
+ */
68
+ async function exportsOf(code, file) {
69
+ return declaredExports((await transformWithOxc(code, file, { sourcemap: false })).code);
70
+ }
71
+ async function exportStubs(code, file) {
72
+ const { names, stars } = await exportsOf(code, file);
73
+ const named = [...names].filter((name) => /^[\w$]+$/.test(name) && name !== "default");
74
+ return (named.length > 0 ? `const _ = undefined;\nexport { ${named.map((name) => `_ as ${name}`).join(", ")} };\n` : "") + (names.has("default") ? "export default undefined;\n" : "") + stars.map((star) => `export * from ${JSON.stringify(star)};\n`).join("");
75
+ }
76
+ //#endregion
77
+ export { declaredExports, exportNamesOf, exportStubs, exportsOf, hasExport };
@@ -0,0 +1,19 @@
1
+ import { NextContext } from "./context.js";
2
+ import { NextRoute } from "../project.js";
3
+
4
+ //#region src/project/routes.d.ts
5
+ /** The routes of the app, as `discoverAppRoutes()` lists them. */
6
+ type AppRoutes = Awaited<ReturnType<typeof discoverAppRoutes>>;
7
+ /**
8
+ * The routes of the app, the way `next build` lists its entries, and what
9
+ * `next build` rejects an app for.
10
+ */
11
+ declare function discoverAppRoutes(context: NextContext): Promise<{
12
+ routes: NextRoute[]; /** The pages of each route, by its pathname: a route can have a page in each of its slots. */
13
+ appPathsPerRoute: Record<string, string[]>;
14
+ strictRouteMatching: true | undefined;
15
+ edgeRouteFiles: string[];
16
+ routeFile: (route: NextRoute) => string | undefined;
17
+ }>;
18
+ //#endregion
19
+ export { AppRoutes, discoverAppRoutes };
@@ -0,0 +1,92 @@
1
+ import path from "node:path";
2
+ import fs from "node:fs";
3
+ //#region src/project/routes.ts
4
+ /**
5
+ * The routes of the app, the way `next build` lists its entries, and what
6
+ * `next build` rejects an app for.
7
+ */
8
+ async function discoverAppRoutes(context) {
9
+ const { root, fail, config, appDir, pageExtensions, next } = context;
10
+ const { APP_DIR_ALIAS, IncompatibleParallelRouteSlotsError, MissingCanonicalInterceptionRoutesError, UnmatchedAppPagesError, compareAppPaths, discoverRoutes, findMissingCanonicalInterceptionRoutes, getAppPageStaticInfo, isAppPageRoute, isAppRouteRoute, normalizeAppPath, normalizeCatchAllRoutes, selectAppPageEntry } = next;
11
+ const discovered = await discoverRoutes({
12
+ appDir,
13
+ pagesDir: void 0,
14
+ pageExtensions,
15
+ isDev: false,
16
+ baseDir: root,
17
+ isSrcDir: path.basename(path.dirname(appDir)) === "src"
18
+ });
19
+ const mappedAppPages = discovered.mappedAppPages ?? fail("`discoverRoutes()` returns no `mappedAppPages`");
20
+ const appPathsPerRoute = {};
21
+ for (const page of Object.keys(mappedAppPages)) (appPathsPerRoute[normalizeAppPath(page)] ??= []).push(page);
22
+ const { strictRouteMatching: strict } = config.experimental;
23
+ const strictRouteMatching = strict || void 0;
24
+ const { unmatchedAppPages, incompatibleParallelRouteSlots } = normalizeCatchAllRoutes(appPathsPerRoute, {
25
+ strictRouteMatching,
26
+ defaultAppPaths: Object.keys(discovered.mappedAppDefaults ?? {})
27
+ });
28
+ const pagePathsPerRoute = Object.fromEntries(Object.entries(appPathsPerRoute).flatMap(([pathname, appPaths]) => {
29
+ const pages = appPaths.filter(isAppPageRoute);
30
+ return pages.length > 0 ? [[pathname, pages]] : [];
31
+ }));
32
+ const missingCanonical = strictRouteMatching ? findMissingCanonicalInterceptionRoutes(pagePathsPerRoute) : [];
33
+ const routeErrors = [
34
+ ...missingCanonical.length > 0 ? [new MissingCanonicalInterceptionRoutesError(missingCanonical)] : [],
35
+ ...incompatibleParallelRouteSlots.length > 0 ? [new IncompatibleParallelRouteSlotsError(incompatibleParallelRouteSlots.map((slot) => ({
36
+ ...slot,
37
+ layoutFile: path.relative(root, path.join(appDir, slot.layoutPath, "layout"))
38
+ })))] : [],
39
+ ...unmatchedAppPages.length > 0 ? [new UnmatchedAppPagesError(unmatchedAppPages)] : []
40
+ ];
41
+ if (routeErrors.length > 0) throw new Error(`vitest-plugin-rsc: \`next build\` fails on the routes of this app.\n\n` + routeErrors.map((error) => error.message).join("\n\n"));
42
+ const fromRoot = (file) => path.relative(root, file).split(path.sep).join("/");
43
+ const isRouteHandler = isAppRouteRoute;
44
+ const routes = [];
45
+ for (const [pathname, appPaths] of Object.entries(appPathsPerRoute)) {
46
+ appPaths.sort(compareAppPaths);
47
+ const page = selectAppPageEntry(pathname, appPaths);
48
+ const pagePath = mappedAppPages[page];
49
+ const own = appPaths.filter((appPath) => normalizeAppPath(appPath) === pathname);
50
+ const pages = own.filter((appPath) => appPath.endsWith("/page"));
51
+ const handlers = own.filter(isRouteHandler);
52
+ if (pages.length > 0 && handlers.length > 0) throw new Error(`vitest-plugin-rsc: ${pathname} is both a page and a route handler (${pages.join(", ")} and ${handlers.join(", ")}). A path can only be one of them.`);
53
+ const kind = page.endsWith("/page") ? "page" : isRouteHandler(page) ? "route" : void 0;
54
+ if (kind) routes.push({
55
+ kind,
56
+ page,
57
+ pathname,
58
+ appPaths,
59
+ pagePath
60
+ });
61
+ }
62
+ const routeFile = (route) => route.pagePath.startsWith(APP_DIR_ALIAS) ? path.join(appDir, route.pagePath.slice(APP_DIR_ALIAS.length)) : void 0;
63
+ for (const route of routes) {
64
+ const file = routeFile(route);
65
+ if (route.kind !== "page" || !file) continue;
66
+ const hasLayout = (directory) => pageExtensions.some((extension) => fs.existsSync(path.join(directory, `layout.${extension}`))) || directory !== appDir && hasLayout(path.dirname(directory));
67
+ if (!hasLayout(path.dirname(file))) throw new Error(`vitest-plugin-rsc: ${fromRoot(file)} does not have a root layout: there is no layout file in its directory or in one above it, up to the app directory. \`next build\` stops at that too.`);
68
+ }
69
+ const edgeRouteFiles = [];
70
+ for (const route of routes) {
71
+ const file = routeFile(route);
72
+ if (!file) continue;
73
+ const { runtime } = await getAppPageStaticInfo({
74
+ pageFilePath: file,
75
+ nextConfig: config,
76
+ isDev: false,
77
+ page: route.page,
78
+ pageType: "app"
79
+ });
80
+ if (runtime === "edge") edgeRouteFiles.push(fromRoot(file));
81
+ }
82
+ return {
83
+ routes,
84
+ /** The pages of each route, by its pathname: a route can have a page in each of its slots. */
85
+ appPathsPerRoute,
86
+ strictRouteMatching,
87
+ edgeRouteFiles,
88
+ routeFile
89
+ };
90
+ }
91
+ //#endregion
92
+ export { discoverAppRoutes };
@@ -0,0 +1,46 @@
1
+ import { Header } from "../compat/types.js";
2
+ import { AppRoutes } from "./routes.js";
3
+ import { NextContext } from "./context.js";
4
+ import { NextRouting } from "../project.js";
5
+ import * as _$next_dist_build_index0 from "next/dist/build/index.js";
6
+ import * as _$next_dist_build_analysis_get_page_static_info0 from "next/dist/build/analysis/get-page-static-info.js";
7
+
8
+ //#region src/project/routing.d.ts
9
+ /**
10
+ * A rule of `headers()` in `next.config` with its `Set-Cookie` headers as
11
+ * one, joined with a comma. Next's build hands an adapter the headers of a
12
+ * rule as an object, one value per name, and `@next/routing` sets each on
13
+ * the response: the second cookie would be lost. `next start` sends every
14
+ * one. Joined, they are the way Next's build writes the `set-cookie` of a
15
+ * prerendered route, and `resolve()` in server-in-front.ts splits them
16
+ * again, with the split Next's `toNodeOutgoingHttpHeaders()` has for them.
17
+ */
18
+ declare function joinSetCookies<T extends Pick<Header, "headers">>(rule: T): T;
19
+ /** The middleware of the app, as `findMiddleware()` finds it. */
20
+ type FoundMiddleware = Awaited<ReturnType<typeof findMiddleware>>;
21
+ declare function findMiddleware(context: NextContext): Promise<{
22
+ middlewareFile: string | undefined;
23
+ middleware: {
24
+ page: string;
25
+ pagePath: string;
26
+ staticInfo: Omit<_$next_dist_build_analysis_get_page_static_info0.PageStaticInfo, "config">;
27
+ } | undefined;
28
+ }>;
29
+ /** `adapterPath` is the file of the adapter that Next's build hands the routes to: adapter.ts. */
30
+ declare function buildRouting(context: NextContext, {
31
+ routes,
32
+ appPathsPerRoute
33
+ }: AppRoutes, {
34
+ middleware
35
+ }: FoundMiddleware, adapterPath: string): Promise<{
36
+ routing: NextRouting;
37
+ routesManifest: _$next_dist_build_index0.RoutesManifest;
38
+ unmatchedRoutes: string[];
39
+ pageOf: (url: URL, {
40
+ proxy
41
+ }: {
42
+ proxy: boolean;
43
+ }) => Promise<string | undefined>;
44
+ }>;
45
+ //#endregion
46
+ export { FoundMiddleware, buildRouting, findMiddleware, joinSetCookies };