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