@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.
- 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 +39 -0
- package/dist/compile.js +175 -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 +330 -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/host-return-shared.d.ts +5 -0
- package/dist/host-return-shared.js +5 -0
- package/dist/host-return.d.ts +5 -0
- package/dist/host-return.js +16 -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/not-found-page.d.ts +19 -0
- package/dist/not-found-page.js +74 -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 +23 -0
- package/dist/plugin.js +1006 -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 +65 -0
- package/dist/project/webpack.js +191 -0
- package/dist/project.d.ts +269 -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 -3
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { loadNextBuild } from "../compat/build.js";
|
|
2
|
+
import { pageEnvOf, readTestEnv } from "./env.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
//#region src/project/context.ts
|
|
7
|
+
const workingDirectory = process.cwd();
|
|
8
|
+
let directoryQueue = Promise.resolve();
|
|
9
|
+
function inDirectory(directory, load) {
|
|
10
|
+
const result = directoryQueue.then(async () => {
|
|
11
|
+
process.chdir(directory);
|
|
12
|
+
try {
|
|
13
|
+
return await load();
|
|
14
|
+
} finally {
|
|
15
|
+
process.chdir(workingDirectory);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
directoryQueue = result.catch(() => {});
|
|
19
|
+
return result;
|
|
20
|
+
}
|
|
21
|
+
/** The file of a module of Next by its path, which may leave out `.js` or `/index.js`. */
|
|
22
|
+
function moduleFileAt(base) {
|
|
23
|
+
return [
|
|
24
|
+
base,
|
|
25
|
+
`${base}.js`,
|
|
26
|
+
path.join(base, "index.js")
|
|
27
|
+
].find((candidate) => fs.statSync(candidate, { throwIfNoEntry: false })?.isFile());
|
|
28
|
+
}
|
|
29
|
+
async function openNextProject(root, projectRequire, options) {
|
|
30
|
+
const nextDir = path.dirname(projectRequire.resolve("next/package.json"));
|
|
31
|
+
const { version } = projectRequire("next/package.json");
|
|
32
|
+
const [major = 0, minor = 0] = version.split(".").map(Number);
|
|
33
|
+
if (major < 16 || major === 16 && minor < 4) throw new Error(`@kasperpeulen/vitest-plugin-nextjs needs next@16.4 or later, found next@${version}.`);
|
|
34
|
+
const fail = (what, cause) => {
|
|
35
|
+
throw new Error(`vitest-plugin-rsc: next@${version} differs from the Next.js this plugin was written for: ${what}. Use a version of @kasperpeulen/vitest-plugin-nextjs that supports next@${version}.`, { cause });
|
|
36
|
+
};
|
|
37
|
+
const replace = (code, piece, replacement, where) => {
|
|
38
|
+
const replaced = code.replace(piece, replacement);
|
|
39
|
+
return replaced === code ? fail(`${where} has no \`${piece}\` to replace`) : replaced;
|
|
40
|
+
};
|
|
41
|
+
const { loadConfig, PHASE_PRODUCTION_BUILD, findPagesDir, generateBuildId, ...next } = loadNextBuild(projectRequire, fail);
|
|
42
|
+
let nextEnv;
|
|
43
|
+
try {
|
|
44
|
+
nextEnv = createRequire(path.join(nextDir, "package.json")).resolve("@next/env");
|
|
45
|
+
} catch (error) {
|
|
46
|
+
return fail(`@next/env is not found next to next (${error.message})`, error);
|
|
47
|
+
}
|
|
48
|
+
const readEnv = () => readTestEnv(root, {
|
|
49
|
+
nextEnv,
|
|
50
|
+
envWorker: options.envWorker,
|
|
51
|
+
build: !!options.static
|
|
52
|
+
}).catch((error) => {
|
|
53
|
+
throw new Error(`vitest-plugin-rsc: the env files of ${root} do not load: ${error.message}`, { cause: error });
|
|
54
|
+
});
|
|
55
|
+
const testEnv = await readEnv();
|
|
56
|
+
for (const [name, value] of Object.entries(testEnv.values)) process.env[name] ??= value;
|
|
57
|
+
const loadedConfig = await inDirectory(root, () => loadConfig(PHASE_PRODUCTION_BUILD, root, { silent: true }));
|
|
58
|
+
const config = {
|
|
59
|
+
...loadedConfig,
|
|
60
|
+
i18n: null,
|
|
61
|
+
experimental: {
|
|
62
|
+
...loadedConfig.experimental,
|
|
63
|
+
cachedNavigations: false
|
|
64
|
+
},
|
|
65
|
+
...options.static && (loadedConfig.images?.loader ?? "default") === "default" && !loadedConfig.images?.loaderFile && { images: {
|
|
66
|
+
...loadedConfig.images,
|
|
67
|
+
unoptimized: true
|
|
68
|
+
} }
|
|
69
|
+
};
|
|
70
|
+
const { appDir } = findPagesDir(root);
|
|
71
|
+
if (!appDir) throw new Error(`vitest-plugin-rsc: no \`app\` directory found in ${root}`);
|
|
72
|
+
const { pageExtensions } = config;
|
|
73
|
+
const distDir = path.join(root, config.distDir);
|
|
74
|
+
await next.swc.loadBindings(config.experimental.useWasmBinary);
|
|
75
|
+
const buildId = await generateBuildId(config.generateBuildId, () => "vitest");
|
|
76
|
+
const buildEnvironment = {
|
|
77
|
+
__NEXT_BUILD_ID: buildId,
|
|
78
|
+
__NEXT_PREVIEW_MODE_ID: "vitest-preview-mode-id",
|
|
79
|
+
__NEXT_PREVIEW_MODE_SIGNING_KEY: "vitest-preview-mode-signing-key",
|
|
80
|
+
__NEXT_PREVIEW_MODE_ENCRYPTION_KEY: "vitest-preview-mode-encryption-key".padEnd(64, "0")
|
|
81
|
+
};
|
|
82
|
+
const previewProps = {
|
|
83
|
+
previewModeId: buildEnvironment.__NEXT_PREVIEW_MODE_ID,
|
|
84
|
+
previewModeSigningKey: buildEnvironment.__NEXT_PREVIEW_MODE_SIGNING_KEY,
|
|
85
|
+
previewModeEncryptionKey: buildEnvironment.__NEXT_PREVIEW_MODE_ENCRYPTION_KEY
|
|
86
|
+
};
|
|
87
|
+
return {
|
|
88
|
+
root,
|
|
89
|
+
/** What the page's `process.env` gets, see env.ts. */
|
|
90
|
+
env: pageEnvOf(testEnv, next.getNextConfigEnv(config)),
|
|
91
|
+
/** The env again, as the env files have it now. */
|
|
92
|
+
loadEnv: async () => pageEnvOf(await readEnv(), next.getNextConfigEnv(config)),
|
|
93
|
+
/** Finds `next`, and what the project has installed next to it. Not the global one. */
|
|
94
|
+
projectRequire,
|
|
95
|
+
nextDir,
|
|
96
|
+
version,
|
|
97
|
+
fail,
|
|
98
|
+
replace,
|
|
99
|
+
config,
|
|
100
|
+
appDir,
|
|
101
|
+
pageExtensions,
|
|
102
|
+
distDir,
|
|
103
|
+
buildId,
|
|
104
|
+
buildEnvironment,
|
|
105
|
+
previewProps,
|
|
106
|
+
/** The build code of the installed `next`, see compat/build.ts. */
|
|
107
|
+
next
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
//#endregion
|
|
111
|
+
export { inDirectory, moduleFileAt, openNextProject };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { componentPagePath } from "../styles-shared.js";
|
|
2
|
+
import { AppLoaderOptions } from "../compat/types.js";
|
|
3
|
+
import { RunLoader } from "./loaders.js";
|
|
4
|
+
import { AppRoutes } from "./routes.js";
|
|
5
|
+
import { FoundMiddleware } from "./routing.js";
|
|
6
|
+
import { NextContext } from "./context.js";
|
|
7
|
+
import { NextProject } from "../project.js";
|
|
8
|
+
|
|
9
|
+
//#region src/project/entries.d.ts
|
|
10
|
+
type AppLoaderContext = {
|
|
11
|
+
getOptions(): AppLoaderOptions;
|
|
12
|
+
_module: {
|
|
13
|
+
buildInfo: Record<string, unknown>;
|
|
14
|
+
};
|
|
15
|
+
_compilation: object;
|
|
16
|
+
_compiler: {
|
|
17
|
+
context: string;
|
|
18
|
+
};
|
|
19
|
+
addDependency(file: string): void;
|
|
20
|
+
addMissingDependency(file: string): void;
|
|
21
|
+
addContextDependency(dir: string): void;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* What the loader tree of a route names a file of it: its path from the root
|
|
25
|
+
* of the project, with `/`. Next looks up the stylesheets of a segment by that
|
|
26
|
+
* name without its extension (`RouteSegment`), with a dev server and in a
|
|
27
|
+
* build. Not the absolute path that Next's webpack build names it by: a static
|
|
28
|
+
* build has the tree in its files, which are then the same on every machine.
|
|
29
|
+
* Turbopack's names are from a root too, `[project]/app/page.tsx`.
|
|
30
|
+
*/
|
|
31
|
+
declare function nameInLoaderTree(root: string, file: string): string;
|
|
32
|
+
declare function routeEntries(context: NextContext, {
|
|
33
|
+
routes,
|
|
34
|
+
appPathsPerRoute,
|
|
35
|
+
strictRouteMatching,
|
|
36
|
+
routeFile
|
|
37
|
+
}: AppRoutes, {
|
|
38
|
+
middleware,
|
|
39
|
+
middlewareFile
|
|
40
|
+
}: FoundMiddleware, builtinBoundaries: Record<string, string>, runLoader: RunLoader): Pick<NextProject, "componentRoutes" | "loadRouteEntry" | "loadMiddlewareEntry">;
|
|
41
|
+
//#endregion
|
|
42
|
+
export { AppLoaderContext, componentPagePath, nameInLoaderTree, routeEntries };
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { componentPagePath } from "../styles-shared.js";
|
|
2
|
+
import { appPageRuntime } from "../compat/paths.js";
|
|
3
|
+
import { normalizePath, parseAst } from "vite";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import querystring from "node:querystring";
|
|
7
|
+
//#region src/project/entries.ts
|
|
8
|
+
const loadOnce = "const __vitest_plugin_rsc_once__ = (load) => {\n let loaded;\n return () => (loaded ??= load().catch((error) => { loaded = undefined; throw error; }));\n};\n";
|
|
9
|
+
const segmentLoader = /\binstrumentModuleGetter\(\(\) => import\((\/\* webpackMode: "eager" \*\/ "(?:[^"\\]|\\.)*")\)\)/g;
|
|
10
|
+
function loadingOnce(code) {
|
|
11
|
+
const once = code.replace(segmentLoader, "instrumentModuleGetter(__vitest_plugin_rsc_once__(() => import($1)))");
|
|
12
|
+
return once === code ? code : loadOnce + once;
|
|
13
|
+
}
|
|
14
|
+
const componentRoot = "(vitest-plugin-rsc)";
|
|
15
|
+
const componentLayouts = "(vitest-plugin-rsc-layouts)";
|
|
16
|
+
const componentPage = `page: [__next_component__, ${JSON.stringify(componentPagePath)}]`;
|
|
17
|
+
const componentImport = `import { loadComponent as __next_component__ } from "@kasperpeulen/vitest-plugin-nextjs/rsc";\n`;
|
|
18
|
+
function stripTurbopackTransitions(code) {
|
|
19
|
+
return code.replace(/\s+with\s*\{\s*['"]turbopack-transition['"]\s*:\s*['"][^'"]*['"]\s*\}/g, "");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* What the loader tree of a route names a file of it: its path from the root
|
|
23
|
+
* of the project, with `/`. Next looks up the stylesheets of a segment by that
|
|
24
|
+
* name without its extension (`RouteSegment`), with a dev server and in a
|
|
25
|
+
* build. Not the absolute path that Next's webpack build names it by: a static
|
|
26
|
+
* build has the tree in its files, which are then the same on every machine.
|
|
27
|
+
* Turbopack's names are from a root too, `[project]/app/page.tsx`.
|
|
28
|
+
*/
|
|
29
|
+
function nameInLoaderTree(root, file) {
|
|
30
|
+
return normalizePath(path.relative(root, file));
|
|
31
|
+
}
|
|
32
|
+
const isLiteral = (node) => node?.type === "Literal";
|
|
33
|
+
function nameFiles(root, code) {
|
|
34
|
+
const files = /* @__PURE__ */ new Set();
|
|
35
|
+
const specifiers = /* @__PURE__ */ new Set();
|
|
36
|
+
const strings = [];
|
|
37
|
+
const visit = (node) => {
|
|
38
|
+
if (!node || typeof node !== "object") return;
|
|
39
|
+
const { type, source, callee, arguments: args } = node;
|
|
40
|
+
const specifier = type === "CallExpression" && callee?.type === "Identifier" && callee.name === "require" ? args?.[0] : source;
|
|
41
|
+
if (isLiteral(specifier) && typeof specifier.value === "string") {
|
|
42
|
+
specifiers.add(specifier);
|
|
43
|
+
if ((type === "ImportDeclaration" || type === "ImportExpression") && path.isAbsolute(specifier.value)) files.add(specifier.value);
|
|
44
|
+
}
|
|
45
|
+
if (isLiteral(node) && typeof node.value === "string") strings.push(node);
|
|
46
|
+
for (const value of Object.values(node)) if (value && typeof value === "object") visit(value);
|
|
47
|
+
};
|
|
48
|
+
visit(parseAst(code));
|
|
49
|
+
const names = strings.filter((string) => !specifiers.has(string) && files.has(string.value)).sort((a, b) => b.start - a.start);
|
|
50
|
+
for (const { value, start, end } of names) {
|
|
51
|
+
const name = JSON.stringify(nameInLoaderTree(root, value));
|
|
52
|
+
code = code.slice(0, start) + name + code.slice(end);
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
code,
|
|
56
|
+
segments: [...files].map((file) => ({
|
|
57
|
+
file,
|
|
58
|
+
stylesheetsKey: nameInLoaderTree(root, file).replace(/\.[^.]+$/, "")
|
|
59
|
+
}))
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
function routeEntries(context, { routes, appPathsPerRoute, strictRouteMatching, routeFile }, { middleware, middlewareFile }, builtinBoundaries, runLoader) {
|
|
63
|
+
const { root, fail, replace, config, appDir, pageExtensions, buildId, next } = context;
|
|
64
|
+
const { getEdgeServerEntry, loadEntrypoint, nextAppLoader, nextMiddlewareLoader, rootPageType } = next;
|
|
65
|
+
const bindPageEntry = (code, where) => {
|
|
66
|
+
code = replace(code, /\b__webpack_require__\b/g, "__next_require__", where);
|
|
67
|
+
if (!code.includes(`"next/dist/build/templates/app-page-runtime"`) && !code.includes(`'next/dist/build/templates/app-page-runtime'`)) fail(`${where} has no \`${appPageRuntime}\``);
|
|
68
|
+
return `import { requireModule as __next_require__ } from "@kasperpeulen/vitest-plugin-nextjs/rsc";\n${code}`;
|
|
69
|
+
};
|
|
70
|
+
async function loadNodeEntry({ page, pathname }) {
|
|
71
|
+
const segment = (name, children, modules = "{}") => `[${JSON.stringify(name)}, ${children}, ${modules}, null]`;
|
|
72
|
+
let tree = `["__PAGE__", {}, { ${componentPage} }]`;
|
|
73
|
+
for (const name of pathname.split("/").filter(Boolean).reverse()) tree = segment(name, `{ children: ${tree} }`);
|
|
74
|
+
const boundaries = Object.entries(builtinBoundaries).map(([name, file]) => `${JSON.stringify(name)}: [() => import(${JSON.stringify(file)}), ${JSON.stringify(file)}]`);
|
|
75
|
+
tree = segment(componentRoot, `{ children: ${tree} }`, `{ ${boundaries.join(", ")} }`);
|
|
76
|
+
let code;
|
|
77
|
+
try {
|
|
78
|
+
code = await loadEntrypoint("app-page", {
|
|
79
|
+
VAR_DEFINITION_PAGE: page,
|
|
80
|
+
VAR_DEFINITION_PATHNAME: pathname
|
|
81
|
+
}, {
|
|
82
|
+
tree,
|
|
83
|
+
__next_app_require__: "__webpack_require__",
|
|
84
|
+
__next_app_load_chunk__: "() => Promise.resolve()"
|
|
85
|
+
});
|
|
86
|
+
} catch (error) {
|
|
87
|
+
return fail(`the app-page template does not take the injections of next-app-loader (${error.message})`, error);
|
|
88
|
+
}
|
|
89
|
+
return componentImport + bindPageEntry(stripTurbopackTransitions(code), "the app-page template");
|
|
90
|
+
}
|
|
91
|
+
const compilation = {};
|
|
92
|
+
const loadAppRouteEntry = async (route) => {
|
|
93
|
+
const watchFiles = /* @__PURE__ */ new Set();
|
|
94
|
+
const context = {
|
|
95
|
+
getOptions: () => ({
|
|
96
|
+
name: `app${route.page}`,
|
|
97
|
+
page: route.page,
|
|
98
|
+
pagePath: route.pagePath,
|
|
99
|
+
appDir,
|
|
100
|
+
appPaths: route.appPaths,
|
|
101
|
+
allNormalizedAppPaths: Object.keys(appPathsPerRoute),
|
|
102
|
+
pageExtensions,
|
|
103
|
+
rootDir: root,
|
|
104
|
+
basePath: config.basePath,
|
|
105
|
+
assetPrefix: config.assetPrefix,
|
|
106
|
+
nextConfigOutput: config.output ?? "",
|
|
107
|
+
preferredRegion: void 0,
|
|
108
|
+
middlewareConfig: Buffer.from("{}").toString("base64"),
|
|
109
|
+
isGlobalNotFoundEnabled: config.experimental.globalNotFound || void 0,
|
|
110
|
+
explicitParallelRouteChildren: config.experimental.explicitParallelRouteChildren || void 0,
|
|
111
|
+
strictRouteMatching,
|
|
112
|
+
isFinalRouteMatcher: strictRouteMatching
|
|
113
|
+
}),
|
|
114
|
+
_module: { buildInfo: {} },
|
|
115
|
+
_compilation: compilation,
|
|
116
|
+
_compiler: { context: root },
|
|
117
|
+
addDependency: (file) => watchFiles.add(file),
|
|
118
|
+
addMissingDependency: (file) => watchFiles.add(file),
|
|
119
|
+
addContextDependency: () => {}
|
|
120
|
+
};
|
|
121
|
+
let code = stripTurbopackTransitions(await nextAppLoader.call(context));
|
|
122
|
+
const where = "the output of next-app-loader";
|
|
123
|
+
if (route.kind === "route") code = replace(code, /(\buserland: \(\)\s*=>\s*)require\(/, "$1import(", where);
|
|
124
|
+
else code = bindPageEntry(code, where);
|
|
125
|
+
const named = nameFiles(root, code);
|
|
126
|
+
const pageFile = route.kind === "page" ? routeFile(route) : void 0;
|
|
127
|
+
if (pageFile && !named.segments.some((segment) => segment.file === pageFile)) fail(`${where} no longer imports the module of a page by its file`);
|
|
128
|
+
return {
|
|
129
|
+
code: loadingOnce(named.code),
|
|
130
|
+
watchFiles: [...watchFiles].filter((file) => fs.existsSync(file)),
|
|
131
|
+
segments: route.kind === "page" ? named.segments : []
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
return {
|
|
135
|
+
componentRoutes: [...[.../* @__PURE__ */ new Set(["/", ...routes.map((route) => route.pathname)])].filter((pathname) => !/^\/_(not-found|global-error)$/.test(pathname)).map((pathname) => ({
|
|
136
|
+
kind: "page",
|
|
137
|
+
page: `${pathname === "/" ? "" : pathname}/page`,
|
|
138
|
+
pathname,
|
|
139
|
+
component: `${componentRoot}${pathname}`
|
|
140
|
+
})), ...routes.filter((route) => route.kind === "page" && routeFile(route) && !route.page.includes("/@")).map((route) => ({
|
|
141
|
+
kind: "page",
|
|
142
|
+
page: route.page,
|
|
143
|
+
pathname: route.pathname,
|
|
144
|
+
component: `${componentLayouts}${route.page}`,
|
|
145
|
+
layouts: true
|
|
146
|
+
}))],
|
|
147
|
+
async loadRouteEntry(route) {
|
|
148
|
+
if (!("component" in route)) return loadAppRouteEntry(route);
|
|
149
|
+
if (!route.layouts) return {
|
|
150
|
+
code: await loadNodeEntry(route),
|
|
151
|
+
watchFiles: [],
|
|
152
|
+
segments: []
|
|
153
|
+
};
|
|
154
|
+
const appRoute = routes.find((candidate) => candidate.page === route.page);
|
|
155
|
+
const pageFile = routeFile(appRoute);
|
|
156
|
+
const file = JSON.stringify(nameInLoaderTree(root, pageFile)).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
157
|
+
const entry = await loadAppRouteEntry(appRoute);
|
|
158
|
+
const code = replace(entry.code, new RegExp(`\\bpage: \\[\\w+, ${file}\\]`), componentPage, "the output of next-app-loader");
|
|
159
|
+
return {
|
|
160
|
+
code: componentImport + code,
|
|
161
|
+
watchFiles: entry.watchFiles,
|
|
162
|
+
segments: entry.segments.filter((segment) => segment.file !== pageFile)
|
|
163
|
+
};
|
|
164
|
+
},
|
|
165
|
+
async loadMiddlewareEntry() {
|
|
166
|
+
if (!middleware || !middlewareFile) return;
|
|
167
|
+
const [loader, options = ""] = getEdgeServerEntry({
|
|
168
|
+
rootDir: root,
|
|
169
|
+
absolutePagePath: middleware.pagePath,
|
|
170
|
+
buildId,
|
|
171
|
+
bundlePath: middleware.page.slice(1),
|
|
172
|
+
config,
|
|
173
|
+
isDev: false,
|
|
174
|
+
isServerComponent: false,
|
|
175
|
+
page: middleware.page,
|
|
176
|
+
pages: {},
|
|
177
|
+
pagesType: rootPageType,
|
|
178
|
+
middleware: middleware.staticInfo.middleware,
|
|
179
|
+
middlewareConfig: middleware.staticInfo.middleware,
|
|
180
|
+
preferredRegion: middleware.staticInfo.preferredRegion
|
|
181
|
+
}).import.replace(/!$/, "").split("?");
|
|
182
|
+
if (loader !== "next-middleware-loader") fail("`getEdgeServerEntry()` no longer loads the middleware with next-middleware-loader");
|
|
183
|
+
const [code] = await runLoader(nextMiddlewareLoader, querystring.parse(options), middlewareFile);
|
|
184
|
+
return replace(stripTurbopackTransitions(code), /\brequire\(/g, "await import(", "the middleware template");
|
|
185
|
+
}
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
//#endregion
|
|
189
|
+
export { componentPagePath, nameInLoaderTree, routeEntries };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { parentPort, workerData } from "node:worker_threads";
|
|
3
|
+
//#region src/project/env-worker.ts
|
|
4
|
+
const { nextEnv, root, files } = workerData;
|
|
5
|
+
const { processEnv } = createRequire(import.meta.url)(nextEnv);
|
|
6
|
+
const [, parsed = {}] = processEnv(files, root, console, true);
|
|
7
|
+
parentPort.postMessage(parsed);
|
|
8
|
+
//#endregion
|
|
9
|
+
export {};
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
//#region src/project/env.d.ts
|
|
2
|
+
/** The env files that a run or a build reads, by their path from the root. */
|
|
3
|
+
declare function envFilesOf(build: boolean): string[];
|
|
4
|
+
type TestEnv = {
|
|
5
|
+
/** Each name of the files, with its value. */values: Record<string, string>; /** The files that were read, by their path from the root. */
|
|
6
|
+
files: string[];
|
|
7
|
+
};
|
|
8
|
+
/**
|
|
9
|
+
* The env files of a run or a build, parsed by the project's `@next/env` in
|
|
10
|
+
* `envWorker`, a thread with `NODE_ENV=test` for all of its env: see
|
|
11
|
+
* env-worker.ts.
|
|
12
|
+
*/
|
|
13
|
+
declare function readTestEnv(root: string, {
|
|
14
|
+
nextEnv,
|
|
15
|
+
envWorker,
|
|
16
|
+
build
|
|
17
|
+
}: {
|
|
18
|
+
nextEnv: string;
|
|
19
|
+
envWorker: string;
|
|
20
|
+
build: boolean;
|
|
21
|
+
}): Promise<TestEnv>;
|
|
22
|
+
type PageEnv = TestEnv & {
|
|
23
|
+
/** The names that only `env` of `next.config` has. */configNames: string[];
|
|
24
|
+
};
|
|
25
|
+
/**
|
|
26
|
+
* What the page's `process.env` gets: the test env, and `env` of
|
|
27
|
+
* `next.config`, which Next's `getNextConfigEnv()` gives as defines.
|
|
28
|
+
*/
|
|
29
|
+
declare function pageEnvOf(testEnv: TestEnv, configDefines: Record<string, unknown>): PageEnv;
|
|
30
|
+
/**
|
|
31
|
+
* What a run or a build says of the env, once: the files it read, and in a
|
|
32
|
+
* build the names it bundles. Never a value.
|
|
33
|
+
*/
|
|
34
|
+
declare function describeEnv(env: PageEnv, build: boolean): string;
|
|
35
|
+
//#endregion
|
|
36
|
+
export { PageEnv, TestEnv, describeEnv, envFilesOf, pageEnvOf, readTestEnv };
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { Worker } from "node:worker_threads";
|
|
4
|
+
//#region src/project/env.ts
|
|
5
|
+
/** Next's env files in its test mode. A name takes the value of the first file that has it. */
|
|
6
|
+
const testEnvFiles = [
|
|
7
|
+
".env.test.local",
|
|
8
|
+
".env.test",
|
|
9
|
+
".env"
|
|
10
|
+
];
|
|
11
|
+
/** The env files that a run or a build reads, by their path from the root. */
|
|
12
|
+
function envFilesOf(build) {
|
|
13
|
+
return build ? testEnvFiles.filter((file) => !file.endsWith(".local")) : testEnvFiles;
|
|
14
|
+
}
|
|
15
|
+
const isNextsOwn = (name) => name === "NODE_ENV" || name === "NEXT_RUNTIME" || name.startsWith("__NEXT_");
|
|
16
|
+
/**
|
|
17
|
+
* The env files of a run or a build, parsed by the project's `@next/env` in
|
|
18
|
+
* `envWorker`, a thread with `NODE_ENV=test` for all of its env: see
|
|
19
|
+
* env-worker.ts.
|
|
20
|
+
*/
|
|
21
|
+
async function readTestEnv(root, { nextEnv, envWorker, build }) {
|
|
22
|
+
const files = envFilesOf(build).flatMap((file) => {
|
|
23
|
+
const stats = fs.statSync(path.join(root, file), { throwIfNoEntry: false });
|
|
24
|
+
return stats?.isFile() || stats?.isFIFO() ? [{
|
|
25
|
+
path: file,
|
|
26
|
+
contents: fs.readFileSync(path.join(root, file), "utf8"),
|
|
27
|
+
env: {}
|
|
28
|
+
}] : [];
|
|
29
|
+
});
|
|
30
|
+
if (files.length === 0) return {
|
|
31
|
+
values: {},
|
|
32
|
+
files: []
|
|
33
|
+
};
|
|
34
|
+
const worker = new Worker(envWorker, {
|
|
35
|
+
env: { NODE_ENV: "test" },
|
|
36
|
+
workerData: {
|
|
37
|
+
nextEnv,
|
|
38
|
+
root,
|
|
39
|
+
files
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
const parsed = await new Promise((resolve, reject) => {
|
|
43
|
+
worker.once("message", resolve);
|
|
44
|
+
worker.once("error", reject);
|
|
45
|
+
worker.once("exit", (code) => reject(/* @__PURE__ */ new Error(`the thread exited with ${code}`)));
|
|
46
|
+
});
|
|
47
|
+
return {
|
|
48
|
+
values: Object.fromEntries(Object.entries(parsed).filter(([name]) => !isNextsOwn(name))),
|
|
49
|
+
files: files.map((file) => file.path)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* What the page's `process.env` gets: the test env, and `env` of
|
|
54
|
+
* `next.config`, which Next's `getNextConfigEnv()` gives as defines.
|
|
55
|
+
*/
|
|
56
|
+
function pageEnvOf(testEnv, configDefines) {
|
|
57
|
+
const configEnv = Object.fromEntries(Object.entries(configDefines).map(([key, value]) => [key.slice(12), String(value)]));
|
|
58
|
+
return {
|
|
59
|
+
values: {
|
|
60
|
+
...configEnv,
|
|
61
|
+
...testEnv.values
|
|
62
|
+
},
|
|
63
|
+
files: testEnv.files,
|
|
64
|
+
configNames: Object.keys(configEnv).filter((name) => !(name in testEnv.values))
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* What a run or a build says of the env, once: the files it read, and in a
|
|
69
|
+
* build the names it bundles. Never a value.
|
|
70
|
+
*/
|
|
71
|
+
function describeEnv(env, build) {
|
|
72
|
+
const files = env.files.length > 0 ? env.files.join(", ") : `no env file, as there is no ${envFilesOf(build).join(" or ")}`;
|
|
73
|
+
if (!build) return `vitest-plugin-rsc: process.env of the app's server code is from ${files}`;
|
|
74
|
+
const fileNames = Object.keys(env.values).filter((name) => !env.configNames.includes(name));
|
|
75
|
+
return [
|
|
76
|
+
`vitest-plugin-rsc: the build puts into the site the env of ${files}`,
|
|
77
|
+
fileNames.length > 0 && `: ${fileNames.join(", ")}`,
|
|
78
|
+
env.configNames.length > 0 && `; and env of next.config: ${env.configNames.join(", ")}`
|
|
79
|
+
].filter(Boolean).join("");
|
|
80
|
+
}
|
|
81
|
+
//#endregion
|
|
82
|
+
export { describeEnv, envFilesOf, pageEnvOf, readTestEnv };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { NextBuild } from "../build.js";
|
|
2
|
+
import { NextContext } from "./context.js";
|
|
3
|
+
import { NextLayer } from "../project.js";
|
|
4
|
+
|
|
5
|
+
//#region src/project/layers.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* What Next's build gives each layer: its compile-time constants and its
|
|
8
|
+
* module aliases. And the exports of the Flight codec of the rsc layer.
|
|
9
|
+
*/
|
|
10
|
+
declare function layerTables(context: NextContext, build: NextBuild): Promise<{
|
|
11
|
+
defines: {
|
|
12
|
+
rsc: {
|
|
13
|
+
[x: string]: string;
|
|
14
|
+
};
|
|
15
|
+
ssr: {
|
|
16
|
+
[x: string]: string;
|
|
17
|
+
};
|
|
18
|
+
browser: {
|
|
19
|
+
[x: string]: string;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
aliases: {
|
|
23
|
+
rsc: Record<string, string | false>;
|
|
24
|
+
ssr: Record<string, string | false>;
|
|
25
|
+
browser: Record<string, string | false>;
|
|
26
|
+
};
|
|
27
|
+
appAliases: Record<NextLayer, Record<string, string>>;
|
|
28
|
+
flightExports: Record<"server" | "client" | "static", string[]>;
|
|
29
|
+
}>;
|
|
30
|
+
//#endregion
|
|
31
|
+
export { layerTables };
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { rscFlightCodec } from "../flight.js";
|
|
2
|
+
import { aliasPaths, nextModulesWithStores, withStoresId } from "../compat/paths.js";
|
|
3
|
+
import { createRequire } from "node:module";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import { init, parse } from "cjs-module-lexer";
|
|
7
|
+
//#region src/project/layers.ts
|
|
8
|
+
/**
|
|
9
|
+
* What Next's build gives each layer: its compile-time constants and its
|
|
10
|
+
* module aliases. And the exports of the Flight codec of the rsc layer.
|
|
11
|
+
*/
|
|
12
|
+
async function layerTables(context, build) {
|
|
13
|
+
const { root, nextDir, projectRequire, fail, config, appDir, pageExtensions, distDir, buildEnvironment, next } = context;
|
|
14
|
+
const { SUPPORTED_NATIVE_MODULES, compilerAliases, getDefineEnv, getNextPublicEnvironmentVariables, getNextConfigEnv, needsExperimentalReact } = next;
|
|
15
|
+
const definesFor = (layer) => {
|
|
16
|
+
const envKeys = new Set(Object.keys({
|
|
17
|
+
...getNextPublicEnvironmentVariables(),
|
|
18
|
+
...getNextConfigEnv(config)
|
|
19
|
+
}));
|
|
20
|
+
const defines = getDefineEnv({
|
|
21
|
+
isTurbopack: false,
|
|
22
|
+
config,
|
|
23
|
+
dev: false,
|
|
24
|
+
distDir,
|
|
25
|
+
projectPath: root,
|
|
26
|
+
fetchCacheKeyPrefix: config.experimental.fetchCacheKeyPrefix,
|
|
27
|
+
hasRewrites: false,
|
|
28
|
+
isClient: layer === "browser",
|
|
29
|
+
isEdgeServer: false,
|
|
30
|
+
isNodeServer: layer !== "browser",
|
|
31
|
+
clientRouterFilters: void 0,
|
|
32
|
+
middlewareMatchers: void 0,
|
|
33
|
+
rewrites: {
|
|
34
|
+
beforeFiles: [],
|
|
35
|
+
afterFiles: [],
|
|
36
|
+
fallback: []
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
if (layer !== "browser" && defines["process.env.NEXT_RUNTIME"] !== "\"nodejs\"") fail("`getDefineEnv()` does not define `process.env.NEXT_RUNTIME` as `nodejs`");
|
|
40
|
+
if (layer !== "browser") defines["process.env.__NEXT_USE_NODE_STREAMS"] = "false";
|
|
41
|
+
if ("process.env.__NEXT_RELATIVE_PROJECT_DIR" in defines) defines["process.env.__NEXT_RELATIVE_PROJECT_DIR"] = "\"\"";
|
|
42
|
+
return {
|
|
43
|
+
...layer !== "browser" && Object.fromEntries(Object.entries(buildEnvironment).map(([name, value]) => [`process.env.${name}`, JSON.stringify(value)])),
|
|
44
|
+
...Object.fromEntries(Object.entries(defines).filter(([key, value]) => typeof value === "string" && !envKeys.has(key)))
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
const appAliases = {
|
|
48
|
+
rsc: {},
|
|
49
|
+
ssr: {},
|
|
50
|
+
browser: {}
|
|
51
|
+
};
|
|
52
|
+
const aliasesFor = (layer) => {
|
|
53
|
+
const aliases = { "@opentelemetry/api$": aliasPaths.openTelemetry };
|
|
54
|
+
if (layer === "browser") for (const name of SUPPORTED_NATIVE_MODULES) {
|
|
55
|
+
if (name === "async_hooks") continue;
|
|
56
|
+
aliases[`${name}$`] = aliases[`node:${name}$`] = aliasPaths.polyfill(name);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
for (const kind of ["app-page", "app-route"]) {
|
|
60
|
+
const file = aliasPaths.routeModule(kind);
|
|
61
|
+
aliases[`${file}.compiled$`] = aliases[`${file}.compiled.js$`] = file;
|
|
62
|
+
}
|
|
63
|
+
for (const channel of ["", "-experimental"]) aliases[`${aliasPaths.reactDomServer(channel)}$`] = aliasPaths.reactDomServerEdge(channel, build);
|
|
64
|
+
}
|
|
65
|
+
const base = compilerAliases.createWebpackAliases({
|
|
66
|
+
distDir,
|
|
67
|
+
isClient: layer === "browser",
|
|
68
|
+
isEdgeServer: layer !== "browser",
|
|
69
|
+
dev: false,
|
|
70
|
+
config,
|
|
71
|
+
pagesDir: void 0,
|
|
72
|
+
appDir,
|
|
73
|
+
dir: root,
|
|
74
|
+
reactProductionProfiling: false
|
|
75
|
+
});
|
|
76
|
+
for (const [key, target] of Object.entries(base)) aliases[key] = Array.isArray(target) ? target.find((candidate) => ["", ...pageExtensions.map((extension) => `.${extension}`)].some((extension) => path.isAbsolute(candidate) && fs.existsSync(candidate + extension))) ?? false : target;
|
|
77
|
+
const channel = needsExperimentalReact(config) ? "-experimental" : "";
|
|
78
|
+
const vendoredReactAliases = (isEdgeServer) => compilerAliases.createVendoredReactAliases(channel, {
|
|
79
|
+
layer: layer === "browser" ? "app-pages-browser" : layer,
|
|
80
|
+
isBrowser: layer === "browser",
|
|
81
|
+
isEdgeServer,
|
|
82
|
+
reactProductionProfiling: false
|
|
83
|
+
});
|
|
84
|
+
Object.assign(aliases, compilerAliases.createServerOnlyClientOnlyAliases(layer === "rsc"), vendoredReactAliases(layer !== "browser"), compilerAliases.createNextApiEsmAliases(), compilerAliases.createAppRouterApiAliases(layer === "rsc"));
|
|
85
|
+
if (layer === "ssr") {
|
|
86
|
+
const nodeTable = vendoredReactAliases(false);
|
|
87
|
+
for (const key of ["react-dom/server$", "react-dom/static$"]) {
|
|
88
|
+
const entry = nodeTable[key];
|
|
89
|
+
if (typeof entry !== "string") return fail(`Next's alias table for Node.js has no \`${key}\``);
|
|
90
|
+
appAliases[layer][key] = entry;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
if (typeof aliases["react-server-dom-webpack/server$"] !== "string") fail("the alias tables have no `react-server-dom-webpack/server$`");
|
|
94
|
+
for (const { module } of nextModulesWithStores) {
|
|
95
|
+
const id = withStoresId(module);
|
|
96
|
+
try {
|
|
97
|
+
projectRequire.resolve(id);
|
|
98
|
+
} catch (error) {
|
|
99
|
+
return fail(`${id} is not there`, error);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
if (layer === "rsc") {
|
|
103
|
+
const contexts = aliasPaths.vendoredContexts;
|
|
104
|
+
let files;
|
|
105
|
+
try {
|
|
106
|
+
files = fs.readdirSync(path.join(nextDir, contexts.slice(5)));
|
|
107
|
+
} catch {
|
|
108
|
+
return fail(`${contexts} is not there`);
|
|
109
|
+
}
|
|
110
|
+
const names = files.filter((file) => file.endsWith(".js") && file !== "entrypoints.js").map((file) => file.slice(0, -3));
|
|
111
|
+
if (names.length === 0) fail(`${contexts} has no contexts`);
|
|
112
|
+
for (const name of names) aliases[`${aliasPaths.sharedRuntime(name)}$`] = `${contexts}/${name}`;
|
|
113
|
+
}
|
|
114
|
+
return aliases;
|
|
115
|
+
};
|
|
116
|
+
const aliases = {
|
|
117
|
+
rsc: aliasesFor("rsc"),
|
|
118
|
+
ssr: aliasesFor("ssr"),
|
|
119
|
+
browser: aliasesFor("browser")
|
|
120
|
+
};
|
|
121
|
+
await init();
|
|
122
|
+
const exportsOf = (file) => {
|
|
123
|
+
const { exports, reexports } = parse(fs.readFileSync(file, "utf8"));
|
|
124
|
+
const requireFrom = createRequire(file);
|
|
125
|
+
return [...exports.filter((name) => name !== "__esModule"), ...reexports.flatMap((reexport) => exportsOf(requireFrom.resolve(reexport)))];
|
|
126
|
+
};
|
|
127
|
+
const flightExports = {};
|
|
128
|
+
for (const entry of Object.keys(rscFlightCodec)) {
|
|
129
|
+
const specifier = `react-server-dom-webpack/${entry}`;
|
|
130
|
+
const target = aliases.rsc[`${specifier}$`];
|
|
131
|
+
if (typeof target !== "string") return fail(`the alias tables have no \`${specifier}$\``);
|
|
132
|
+
try {
|
|
133
|
+
flightExports[entry] = [...new Set(exportsOf(projectRequire.resolve(target)))];
|
|
134
|
+
} catch (error) {
|
|
135
|
+
return fail(`${target} cannot be read (${error.message})`, error);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
defines: {
|
|
140
|
+
rsc: definesFor("rsc"),
|
|
141
|
+
ssr: definesFor("ssr"),
|
|
142
|
+
browser: definesFor("browser")
|
|
143
|
+
},
|
|
144
|
+
aliases,
|
|
145
|
+
appAliases,
|
|
146
|
+
flightExports
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
//#endregion
|
|
150
|
+
export { layerTables };
|