@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,280 @@
|
|
|
1
|
+
import { receivers } from "../adapter.js";
|
|
2
|
+
import { inDirectory } from "./context.js";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import os from "node:os";
|
|
6
|
+
//#region src/project/routing.ts
|
|
7
|
+
function urlPathname(pathname) {
|
|
8
|
+
const url = new URL("http://localhost");
|
|
9
|
+
url.pathname = pathname;
|
|
10
|
+
return url.pathname;
|
|
11
|
+
}
|
|
12
|
+
function outputPagesOf(outputs, trailingSlash) {
|
|
13
|
+
const pages = {};
|
|
14
|
+
for (const { pathname, sourcePage } of [...outputs.appPages, ...outputs.appRoutes]) for (const requested of /* @__PURE__ */ new Set([pathname, urlPathname(pathname)])) {
|
|
15
|
+
pages[requested] = sourcePage;
|
|
16
|
+
if (trailingSlash && !requested.endsWith("/")) pages[`${requested}/`] = sourcePage;
|
|
17
|
+
}
|
|
18
|
+
return pages;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* A rule of `headers()` in `next.config` with its `Set-Cookie` headers as
|
|
22
|
+
* one, joined with a comma. Next's build hands an adapter the headers of a
|
|
23
|
+
* rule as an object, one value per name, and `@next/routing` sets each on
|
|
24
|
+
* the response: the second cookie would be lost. `next start` sends every
|
|
25
|
+
* one. Joined, they are the way Next's build writes the `set-cookie` of a
|
|
26
|
+
* prerendered route, and `resolve()` in server-in-front.ts splits them
|
|
27
|
+
* again, with the split Next's `toNodeOutgoingHttpHeaders()` has for them.
|
|
28
|
+
*/
|
|
29
|
+
function joinSetCookies(rule) {
|
|
30
|
+
const isCookie = ({ key }) => key.toLowerCase() === "set-cookie";
|
|
31
|
+
const cookies = rule.headers.filter(isCookie);
|
|
32
|
+
if (cookies.length < 2) return rule;
|
|
33
|
+
const joined = {
|
|
34
|
+
key: "Set-Cookie",
|
|
35
|
+
value: cookies.map(({ value }) => value).join(", ")
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
...rule,
|
|
39
|
+
headers: [...rule.headers.filter((header) => !isCookie(header)), joined]
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function findMiddleware(context) {
|
|
43
|
+
const { root, fail, config, appDir, pageExtensions, next } = context;
|
|
44
|
+
const { MIDDLEWARE_FILENAME, PROXY_FILENAME, createPagesMapping, getFilesInDir, getStaticInfoIncludingLayouts, rootPageType } = next;
|
|
45
|
+
const rootDir = path.dirname(appDir);
|
|
46
|
+
const rootFiles = await getFilesInDir(rootDir);
|
|
47
|
+
const middlewareFiles = [PROXY_FILENAME, MIDDLEWARE_FILENAME].flatMap((name) => {
|
|
48
|
+
const file = pageExtensions.map((extension) => `${name}.${extension}`).find((candidate) => rootFiles.has(candidate));
|
|
49
|
+
return file ? [path.join(rootDir, file)] : [];
|
|
50
|
+
});
|
|
51
|
+
if (middlewareFiles.length > 1) {
|
|
52
|
+
const files = middlewareFiles.map((file) => path.relative(root, file));
|
|
53
|
+
throw new Error(`vitest-plugin-rsc: the app has both ${files.join(" and ")}. Next.js takes one of them.`);
|
|
54
|
+
}
|
|
55
|
+
const [middlewareFile] = middlewareFiles;
|
|
56
|
+
return {
|
|
57
|
+
middlewareFile,
|
|
58
|
+
middleware: middlewareFile ? await (async () => {
|
|
59
|
+
const mapped = await createPagesMapping({
|
|
60
|
+
isDev: false,
|
|
61
|
+
pageExtensions,
|
|
62
|
+
pagePaths: [middlewareFile.replace(root, "")],
|
|
63
|
+
pagesType: rootPageType,
|
|
64
|
+
pagesDir: void 0,
|
|
65
|
+
appDir,
|
|
66
|
+
appDirOnly: true
|
|
67
|
+
});
|
|
68
|
+
const [entry] = Object.entries(mapped);
|
|
69
|
+
const [page, pagePath] = entry ?? fail(`\`createPagesMapping()\` has no page for ${middlewareFile}`);
|
|
70
|
+
return {
|
|
71
|
+
page,
|
|
72
|
+
pagePath,
|
|
73
|
+
staticInfo: await getStaticInfoIncludingLayouts({
|
|
74
|
+
isInsideAppDir: false,
|
|
75
|
+
pageExtensions,
|
|
76
|
+
pageFilePath: middlewareFile,
|
|
77
|
+
appDir,
|
|
78
|
+
config,
|
|
79
|
+
isDev: false,
|
|
80
|
+
page
|
|
81
|
+
})
|
|
82
|
+
};
|
|
83
|
+
})() : void 0
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
/** `adapterPath` is the file of the adapter that Next's build hands the routes to: adapter.ts. */
|
|
87
|
+
async function buildRouting(context, { routes, appPathsPerRoute }, { middleware }, adapterPath) {
|
|
88
|
+
const { root, projectRequire, version, fail, config, buildId, buildEnvironment, previewProps, next } = context;
|
|
89
|
+
const { Bundler, NEXT_URL, generateInterceptionRoutesRewrites, generateRoutesManifest, getNamedMiddlewareRegex, handleBuildComplete, loadCustomRoutes } = next;
|
|
90
|
+
let routingVersion;
|
|
91
|
+
try {
|
|
92
|
+
({version: routingVersion} = projectRequire("@next/routing/package.json"));
|
|
93
|
+
} catch (error) {
|
|
94
|
+
throw new Error(`@kasperpeulen/vitest-plugin-nextjs needs @next/routing, the package of Next.js that resolves the route of a request. Install it at the version of next: @next/routing@${version}.`, { cause: error });
|
|
95
|
+
}
|
|
96
|
+
if (routingVersion !== version) throw new Error(`@kasperpeulen/vitest-plugin-nextjs needs @next/routing at the version of next, and found @next/routing@${routingVersion} next to next@${version}. Install @next/routing@${version}.`);
|
|
97
|
+
const edgeFunction = (name, page, matchers = []) => ({
|
|
98
|
+
name,
|
|
99
|
+
page,
|
|
100
|
+
matchers,
|
|
101
|
+
env: buildEnvironment,
|
|
102
|
+
entrypoint: "",
|
|
103
|
+
files: [],
|
|
104
|
+
wasm: [],
|
|
105
|
+
assets: []
|
|
106
|
+
});
|
|
107
|
+
const middlewareManifest = {
|
|
108
|
+
version: 3,
|
|
109
|
+
sortedMiddleware: middleware ? ["/"] : [],
|
|
110
|
+
middleware: middleware ? { "/": edgeFunction(middleware.page.slice(1), "/", middleware.staticInfo.middleware?.matchers ?? [{
|
|
111
|
+
regexp: getNamedMiddlewareRegex("/", { catchAll: true }).namedRegex,
|
|
112
|
+
originalSource: "/:path*"
|
|
113
|
+
}]) } : {},
|
|
114
|
+
functions: Object.fromEntries(routes.map((route) => [route.page, edgeFunction(`app${route.page}`, route.page)]))
|
|
115
|
+
};
|
|
116
|
+
const routedConfig = {
|
|
117
|
+
...config,
|
|
118
|
+
output: void 0
|
|
119
|
+
};
|
|
120
|
+
const { built, routesManifest } = await inDirectory(root, async () => {
|
|
121
|
+
const custom = await loadCustomRoutes(routedConfig);
|
|
122
|
+
const { redirects, rewrites } = custom;
|
|
123
|
+
const headers = custom.headers.map(joinSetCookies);
|
|
124
|
+
const onMatchHeaders = custom.onMatchHeaders.map(joinSetCookies);
|
|
125
|
+
const appPaths = Object.keys(appPathsPerRoute);
|
|
126
|
+
rewrites.beforeFiles.push(...generateInterceptionRoutesRewrites(appPaths, config.basePath));
|
|
127
|
+
const { routesManifest } = generateRoutesManifest({
|
|
128
|
+
appType: "app",
|
|
129
|
+
pageKeys: {
|
|
130
|
+
pages: [],
|
|
131
|
+
app: appPaths
|
|
132
|
+
},
|
|
133
|
+
config: routedConfig,
|
|
134
|
+
redirects,
|
|
135
|
+
headers,
|
|
136
|
+
onMatchHeaders,
|
|
137
|
+
rewrites,
|
|
138
|
+
restrictedRedirectPaths: [`${config.basePath}/_next`],
|
|
139
|
+
isAppPPREnabled: Boolean(config.cacheComponents),
|
|
140
|
+
deploymentId: config.deploymentId
|
|
141
|
+
});
|
|
142
|
+
const buildDir = fs.mkdtempSync(path.join(os.tmpdir(), "vitest-plugin-rsc-"));
|
|
143
|
+
fs.mkdirSync(path.join(buildDir, "static"));
|
|
144
|
+
const { log } = console;
|
|
145
|
+
console.log = (...message) => {
|
|
146
|
+
if (!String(message[0]).includes("onBuildComplete")) log(...message);
|
|
147
|
+
};
|
|
148
|
+
try {
|
|
149
|
+
return {
|
|
150
|
+
built: await new Promise((resolve, reject) => {
|
|
151
|
+
receivers.set(buildDir, resolve);
|
|
152
|
+
handleBuildComplete({
|
|
153
|
+
adapterPath,
|
|
154
|
+
dir: root,
|
|
155
|
+
distDir: buildDir,
|
|
156
|
+
configOutDir: path.join(root, "out"),
|
|
157
|
+
repoRoot: config.repoRoot,
|
|
158
|
+
outputFileTracingRoot: config.outputFileTracingRoot || root,
|
|
159
|
+
config: routedConfig,
|
|
160
|
+
appType: "app",
|
|
161
|
+
buildId,
|
|
162
|
+
nextVersion: version,
|
|
163
|
+
bundler: Bundler.Turbopack,
|
|
164
|
+
routesManifest,
|
|
165
|
+
middlewareManifest,
|
|
166
|
+
prerenderManifest: {
|
|
167
|
+
version: 4,
|
|
168
|
+
routes: {},
|
|
169
|
+
dynamicRoutes: {},
|
|
170
|
+
notFoundRoutes: [],
|
|
171
|
+
preview: previewProps
|
|
172
|
+
},
|
|
173
|
+
functionsConfigManifest: {
|
|
174
|
+
version: 1,
|
|
175
|
+
functions: {}
|
|
176
|
+
},
|
|
177
|
+
previewProps,
|
|
178
|
+
pageKeys: [],
|
|
179
|
+
appPageKeys: routes.map((route) => route.page),
|
|
180
|
+
staticPages: /* @__PURE__ */ new Set(),
|
|
181
|
+
serverPropsPages: /* @__PURE__ */ new Set(),
|
|
182
|
+
requiredServerFiles: [],
|
|
183
|
+
hasNodeMiddleware: false,
|
|
184
|
+
hasInstrumentationHook: false,
|
|
185
|
+
hasStatic404: false,
|
|
186
|
+
hasStatic500: false
|
|
187
|
+
}).then(() => reject(/* @__PURE__ */ new Error("it does not call `onBuildComplete` of the adapter")), reject);
|
|
188
|
+
}),
|
|
189
|
+
routesManifest
|
|
190
|
+
};
|
|
191
|
+
} catch (error) {
|
|
192
|
+
return fail(`\`handleBuildComplete()\` does not hand an adapter the routes of an app without a build on disk (${error.message})`, error);
|
|
193
|
+
} finally {
|
|
194
|
+
console.log = log;
|
|
195
|
+
receivers.delete(buildDir);
|
|
196
|
+
fs.rmSync(buildDir, {
|
|
197
|
+
recursive: true,
|
|
198
|
+
force: true
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
for (const phase of [
|
|
203
|
+
"beforeMiddleware",
|
|
204
|
+
"beforeFiles",
|
|
205
|
+
"afterFiles",
|
|
206
|
+
"dynamicRoutes",
|
|
207
|
+
"onMatch",
|
|
208
|
+
"fallback"
|
|
209
|
+
]) if (!Array.isArray(built.routing?.[phase])) fail(`\`handleBuildComplete()\` hands an adapter no \`routing.${phase}\``);
|
|
210
|
+
const routing = {
|
|
211
|
+
appRoutes: {
|
|
212
|
+
...built.routing,
|
|
213
|
+
beforeMiddleware: [],
|
|
214
|
+
middlewareMatchers: [],
|
|
215
|
+
beforeFiles: built.routing.beforeFiles.filter((route) => route.has?.some((has) => has.type === "header" && has.key.toLowerCase() === NEXT_URL)),
|
|
216
|
+
afterFiles: [],
|
|
217
|
+
fallback: []
|
|
218
|
+
},
|
|
219
|
+
routes: built.routing,
|
|
220
|
+
basePath: config.basePath,
|
|
221
|
+
buildId,
|
|
222
|
+
outputs: outputPagesOf(built.outputs, config.trailingSlash)
|
|
223
|
+
};
|
|
224
|
+
const { resolveRoutes } = projectRequire("@next/routing");
|
|
225
|
+
const outputPages = new Set(Object.values(routing.outputs));
|
|
226
|
+
const unmatchedRoutes = [];
|
|
227
|
+
for (const route of routes) {
|
|
228
|
+
if (!outputPages.has(route.page)) fail(`\`handleBuildComplete()\` has no output for ${route.page}`);
|
|
229
|
+
const isDynamic = route.pathname.includes("[");
|
|
230
|
+
const sample = route.pathname.replace(/\[\[?(?:\.\.\.)?[^\]]+\]\]?/g, "-x-");
|
|
231
|
+
const pathname = config.basePath + (sample === "/" && config.basePath ? "" : sample);
|
|
232
|
+
const url = new URL("http://localhost");
|
|
233
|
+
url.pathname = pathname;
|
|
234
|
+
if (isDynamic && url.pathname !== pathname) {
|
|
235
|
+
unmatchedRoutes.push(route.pathname);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
const resolved = await resolveRoutes({
|
|
239
|
+
url,
|
|
240
|
+
headers: new Headers(),
|
|
241
|
+
requestBody: new ReadableStream(),
|
|
242
|
+
basePath: routing.basePath,
|
|
243
|
+
buildId: routing.buildId,
|
|
244
|
+
pathnames: Object.keys(routing.outputs),
|
|
245
|
+
routes: routing.appRoutes,
|
|
246
|
+
invokeMiddleware: async () => ({})
|
|
247
|
+
});
|
|
248
|
+
const page = routing.outputs[resolved.resolvedPathname ?? ""];
|
|
249
|
+
if (isDynamic ? page === void 0 : page !== route.page) fail(`\`resolveRoutes()\` of @next/routing@${routingVersion} does not find ${route.pathname} in the routes that \`handleBuildComplete()\` hands an adapter`);
|
|
250
|
+
}
|
|
251
|
+
const pageOf = async (url, { proxy }) => {
|
|
252
|
+
for (let redirects = 0; redirects <= maxRedirects; redirects++) {
|
|
253
|
+
const resolved = await resolveRoutes({
|
|
254
|
+
url,
|
|
255
|
+
headers: new Headers(),
|
|
256
|
+
requestBody: new ReadableStream(),
|
|
257
|
+
basePath: routing.basePath,
|
|
258
|
+
buildId: routing.buildId,
|
|
259
|
+
pathnames: Object.keys(routing.outputs),
|
|
260
|
+
routes: proxy ? routing.routes : routing.appRoutes,
|
|
261
|
+
invokeMiddleware: async () => ({})
|
|
262
|
+
});
|
|
263
|
+
const status = resolved.redirect?.status ?? resolved.status ?? 0;
|
|
264
|
+
const header = resolved.resolvedHeaders?.get("location");
|
|
265
|
+
const location = resolved.redirect?.url ?? (status >= 300 && status < 400 && header ? new URL(header, url) : void 0);
|
|
266
|
+
if (!location) return routing.outputs[resolved.resolvedPathname ?? ""];
|
|
267
|
+
if (location.origin !== url.origin) return;
|
|
268
|
+
url = location;
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
return {
|
|
272
|
+
routing,
|
|
273
|
+
routesManifest,
|
|
274
|
+
unmatchedRoutes,
|
|
275
|
+
pageOf
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
const maxRedirects = 20;
|
|
279
|
+
//#endregion
|
|
280
|
+
export { buildRouting, findMiddleware, joinSetCookies };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NextContext } from "./context.js";
|
|
2
|
+
import { NextProject } from "../project.js";
|
|
3
|
+
|
|
4
|
+
//#region src/project/transform.d.ts
|
|
5
|
+
/** Next's SWC transform of a module of the app, and the `paths` of its tsconfig. */
|
|
6
|
+
declare function createCompiler(context: NextContext): Promise<Pick<NextProject, "compile" | "paths" | "dependencyCompileOptions">>;
|
|
7
|
+
//#endregion
|
|
8
|
+
export { createCompiler };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { exportStubs } from "./module-exports.js";
|
|
2
|
+
import { normalizePath } from "vite";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { stripVTControlCharacters } from "node:util";
|
|
5
|
+
//#region src/project/transform.ts
|
|
6
|
+
const compiledByVite = /\.(?:m?ts|[jt]sx)$/;
|
|
7
|
+
/** Next's SWC transform of a module of the app, and the `paths` of its tsconfig. */
|
|
8
|
+
async function createCompiler(context) {
|
|
9
|
+
const { root, fail, config, appDir, pageExtensions, distDir, next } = context;
|
|
10
|
+
const { WEBPACK_LAYERS, getLoaderSWCOptions, getRSCModuleInformation, loadJsConfig, swc } = next;
|
|
11
|
+
const { jsConfig, resolvedBaseUrl } = await loadJsConfig(root, config);
|
|
12
|
+
const paths = resolvedBaseUrl && {
|
|
13
|
+
baseUrl: resolvedBaseUrl.baseUrl,
|
|
14
|
+
explicitBaseUrl: !resolvedBaseUrl.isImplicit,
|
|
15
|
+
patterns: jsConfig?.compilerOptions?.paths ?? {}
|
|
16
|
+
};
|
|
17
|
+
const bundleLayers = {
|
|
18
|
+
rsc: WEBPACK_LAYERS.reactServerComponents,
|
|
19
|
+
ssr: WEBPACK_LAYERS.serverSideRendering,
|
|
20
|
+
browser: WEBPACK_LAYERS.appPagesBrowser
|
|
21
|
+
};
|
|
22
|
+
const swcOptions = (file, layer) => {
|
|
23
|
+
const { serverActions: _serverActions, cjsRequireOptimizer: _cjsRequireOptimizer, env: _env, ...options } = getLoaderSWCOptions({
|
|
24
|
+
filename: file,
|
|
25
|
+
development: false,
|
|
26
|
+
isServer: layer !== "browser",
|
|
27
|
+
pagesDir: void 0,
|
|
28
|
+
appDir,
|
|
29
|
+
isPageFile: false,
|
|
30
|
+
isCacheComponents: config.cacheComponents,
|
|
31
|
+
hasReactRefresh: false,
|
|
32
|
+
configDir: root,
|
|
33
|
+
modularizeImports: void 0,
|
|
34
|
+
optimizePackageImports: void 0,
|
|
35
|
+
swcPlugins: config.experimental.swcPlugins,
|
|
36
|
+
compilerOptions: config.compiler,
|
|
37
|
+
jsConfig,
|
|
38
|
+
supportedBrowsers: void 0,
|
|
39
|
+
swcCacheDir: path.join(distDir, "cache", "swc"),
|
|
40
|
+
relativeFilePathFromRoot: path.relative(root, file),
|
|
41
|
+
serverComponents: true,
|
|
42
|
+
serverReferenceHashSalt: "",
|
|
43
|
+
bundleLayer: bundleLayers[layer],
|
|
44
|
+
esm: true,
|
|
45
|
+
cacheHandlers: config.cacheHandlers,
|
|
46
|
+
useCacheEnabled: config.experimental.useCache,
|
|
47
|
+
taintEnabled: config.experimental.taint,
|
|
48
|
+
pageExtensions
|
|
49
|
+
});
|
|
50
|
+
const { optimizer: _optimizer, regenerator: _regenerator, react, ...transform } = options.jsc.transform;
|
|
51
|
+
return {
|
|
52
|
+
...options,
|
|
53
|
+
jsc: {
|
|
54
|
+
...options.jsc,
|
|
55
|
+
target: "esnext",
|
|
56
|
+
externalHelpers: false,
|
|
57
|
+
experimental: {
|
|
58
|
+
...options.jsc.experimental,
|
|
59
|
+
emitAssertForImportAttributes: false
|
|
60
|
+
},
|
|
61
|
+
transform: {
|
|
62
|
+
...transform,
|
|
63
|
+
react: compiledByVite.test(file) ? {
|
|
64
|
+
...react,
|
|
65
|
+
runtime: "preserve"
|
|
66
|
+
} : react
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
filename: file,
|
|
70
|
+
sourceFileName: file,
|
|
71
|
+
sourceMaps: true
|
|
72
|
+
};
|
|
73
|
+
};
|
|
74
|
+
const compile = async (code, file, layer) => {
|
|
75
|
+
const options = swcOptions(file, layer);
|
|
76
|
+
let output;
|
|
77
|
+
try {
|
|
78
|
+
output = await swc.transform(code, options);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const exports = await exportStubs(code, file).catch(() => Promise.reject(error));
|
|
81
|
+
const message = stripVTControlCharacters(error.message ?? String(error)).trim().replace(/(,-\[)(.+?)(:\d+:\d+\])/g, (frame, start, file, end) => path.isAbsolute(file) ? `${start}${normalizePath(path.relative(root, file))}${end}` : frame).replaceAll(root + path.sep, "");
|
|
82
|
+
return {
|
|
83
|
+
code: `throw new Error(${JSON.stringify(message)});\n${exports}`,
|
|
84
|
+
refused: true
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
if (layer === "rsc" && getRSCModuleInformation(output.code, true).type === "client") {
|
|
88
|
+
if (compiledByVite.test(file)) return;
|
|
89
|
+
return swc.transform(code, {
|
|
90
|
+
...options,
|
|
91
|
+
serverComponents: void 0
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
return output;
|
|
95
|
+
};
|
|
96
|
+
if (!(await compile(`import { Inter } from "next/font/google";\nexport const inter = Inter({});\n`, path.join(appDir, "layout.js"), "rsc"))?.code.includes("next/font/google/target.css?")) fail("the SWC transform no longer turns a call of a `next/font` function into an import");
|
|
97
|
+
if (await compile(`"use client";\nexport const a = 1;\n`, path.join(appDir, "a.ts"), "rsc")) fail("the SWC transform no longer marks a `\"use client\"` module of the rsc layer");
|
|
98
|
+
const dependency = path.join(root, "node_modules", "dependency.js");
|
|
99
|
+
return {
|
|
100
|
+
compile,
|
|
101
|
+
paths,
|
|
102
|
+
dependencyCompileOptions: {
|
|
103
|
+
rsc: swcOptions(dependency, "rsc"),
|
|
104
|
+
ssr: swcOptions(dependency, "ssr"),
|
|
105
|
+
browser: swcOptions(dependency, "browser")
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { createCompiler };
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { RunLoader } from "./loaders.js";
|
|
2
|
+
import { NextContext } from "./context.js";
|
|
3
|
+
import { NextProject } from "../project.js";
|
|
4
|
+
|
|
5
|
+
//#region src/project/webpack.d.ts
|
|
6
|
+
/**
|
|
7
|
+
* What a rule matches, as webpack takes it: a string is the start of the path,
|
|
8
|
+
* a function decides, and a list matches where one of it does.
|
|
9
|
+
*/
|
|
10
|
+
type WebpackCondition = RegExp | string | ((value: string) => boolean) | WebpackCondition[];
|
|
11
|
+
/** A rule of the `webpack` function: the files it is for, and their loaders. */
|
|
12
|
+
type WebpackRule = {
|
|
13
|
+
/** Of the path of the file, as `include` and `exclude`. */test: WebpackCondition;
|
|
14
|
+
include?: WebpackCondition;
|
|
15
|
+
exclude?: WebpackCondition; /** Of the query of the file, `?collection=docs`, with its `?`. */
|
|
16
|
+
resourceQuery?: WebpackCondition;
|
|
17
|
+
enforce?: "pre" | "post"; /** As webpack calls them: the last first. */
|
|
18
|
+
loaders: {
|
|
19
|
+
loader: string;
|
|
20
|
+
options: unknown;
|
|
21
|
+
}[]; /** Whether Next's compiler, `defaultLoaders.babel`, runs after them. */
|
|
22
|
+
compile: boolean;
|
|
23
|
+
};
|
|
24
|
+
/** The loaders of the rules that match a file, as webpack runs them. */
|
|
25
|
+
type WebpackLoaders = Pick<WebpackRule, "loaders" | "compile">;
|
|
26
|
+
type WebpackFunction = {
|
|
27
|
+
rules: WebpackRule[]; /** The candidates of each alias it sets, in order: the first that resolves is the one. */
|
|
28
|
+
aliases: Record<string, string[]>; /** What of it does not apply, to say when a run starts. */
|
|
29
|
+
differences: string[];
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Calls the `webpack` function of a `next.config` with a config of webpack
|
|
33
|
+
* that has what the plugin reads of it, as Next calls it for the compiler of
|
|
34
|
+
* a layer, and reads the rules and the aliases it adds.
|
|
35
|
+
*/
|
|
36
|
+
declare function readWebpackFunction(webpack: unknown, options: {
|
|
37
|
+
root: string;
|
|
38
|
+
isServer: boolean;
|
|
39
|
+
buildId: string;
|
|
40
|
+
config: unknown;
|
|
41
|
+
}): WebpackFunction;
|
|
42
|
+
/**
|
|
43
|
+
* The loaders of the rules for a file, as webpack matches them: `test`,
|
|
44
|
+
* `include` and `exclude` its path, `resourceQuery` its query. Each rule that
|
|
45
|
+
* matches adds its loaders, as webpack runs them: those of `enforce: "pre"`
|
|
46
|
+
* first, then the others, then those of `enforce: "post"`. Fumadocs matches an
|
|
47
|
+
* `.mdx` file with `?collection=docs`, and a `meta.json` with it by its query.
|
|
48
|
+
*/
|
|
49
|
+
declare function loadersOf(rules: WebpackRule[], id: string): WebpackLoaders | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* The candidates of an alias for an import, as webpack takes them: a name
|
|
52
|
+
* matches the import and what is under it, and one that ends with `$` only
|
|
53
|
+
* the import itself.
|
|
54
|
+
*/
|
|
55
|
+
declare function aliasOf(aliases: Record<string, string[]>, source: string): string[] | undefined;
|
|
56
|
+
/**
|
|
57
|
+
* A file of a package by the import that names it, `@next/mdx/mdx-components.js`:
|
|
58
|
+
* what Vite pre-bundles. The candidate of an alias is often the file, as
|
|
59
|
+
* `require.resolve()` finds it.
|
|
60
|
+
*/
|
|
61
|
+
declare function importOfFile(file: string): string;
|
|
62
|
+
/** The rules and aliases of the `webpack` function in `next.config`, for each layer. */
|
|
63
|
+
declare function createWebpackRules(context: NextContext, runLoader: RunLoader): Pick<NextProject, "webpackRuleOf" | "loadWithWebpackRule" | "webpackAliasOf" | "webpackPackages" | "webpackDifferences">;
|
|
64
|
+
//#endregion
|
|
65
|
+
export { WebpackCondition, WebpackFunction, WebpackLoaders, WebpackRule, aliasOf, createWebpackRules, importOfFile, loadersOf, readWebpackFunction };
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
//#region src/project/webpack.ts
|
|
4
|
+
/**
|
|
5
|
+
* Calls the `webpack` function of a `next.config` with a config of webpack
|
|
6
|
+
* that has what the plugin reads of it, as Next calls it for the compiler of
|
|
7
|
+
* a layer, and reads the rules and the aliases it adds.
|
|
8
|
+
*/
|
|
9
|
+
function readWebpackFunction(webpack, options) {
|
|
10
|
+
const read = {
|
|
11
|
+
rules: [],
|
|
12
|
+
aliases: {},
|
|
13
|
+
differences: []
|
|
14
|
+
};
|
|
15
|
+
if (typeof webpack !== "function") return read;
|
|
16
|
+
const babel = {
|
|
17
|
+
loader: "next-swc-loader",
|
|
18
|
+
options: {}
|
|
19
|
+
};
|
|
20
|
+
const before = {
|
|
21
|
+
resolve: { alias: {} },
|
|
22
|
+
module: { rules: [] },
|
|
23
|
+
plugins: []
|
|
24
|
+
};
|
|
25
|
+
let after;
|
|
26
|
+
try {
|
|
27
|
+
after = webpack(before, {
|
|
28
|
+
dir: options.root,
|
|
29
|
+
dev: false,
|
|
30
|
+
isServer: options.isServer,
|
|
31
|
+
buildId: options.buildId,
|
|
32
|
+
config: options.config,
|
|
33
|
+
defaultLoaders: { babel },
|
|
34
|
+
totalPages: 0,
|
|
35
|
+
...options.isServer && { nextRuntime: "nodejs" }
|
|
36
|
+
});
|
|
37
|
+
} catch (error) {
|
|
38
|
+
read.differences.push(`the \`webpack\` function of next.config failed, so none of it applies: ${error.message}`);
|
|
39
|
+
return read;
|
|
40
|
+
}
|
|
41
|
+
for (const rule of after?.module?.rules ?? []) {
|
|
42
|
+
const { test, include, exclude, resourceQuery, enforce, use, ...rest } = rule ?? {};
|
|
43
|
+
const uses = Array.isArray(use) ? use : [use];
|
|
44
|
+
const loaders = uses.flatMap((entry) => {
|
|
45
|
+
if (entry === babel) return [];
|
|
46
|
+
if (typeof entry === "string") return [{
|
|
47
|
+
loader: entry,
|
|
48
|
+
options: void 0
|
|
49
|
+
}];
|
|
50
|
+
const { loader, options } = entry ?? {};
|
|
51
|
+
return typeof loader === "string" ? [{
|
|
52
|
+
loader,
|
|
53
|
+
options
|
|
54
|
+
}] : [];
|
|
55
|
+
});
|
|
56
|
+
if (!isCondition(test) || [
|
|
57
|
+
include,
|
|
58
|
+
exclude,
|
|
59
|
+
resourceQuery
|
|
60
|
+
].some((condition) => condition !== void 0 && !isCondition(condition)) || enforce !== void 0 && enforce !== "pre" && enforce !== "post" || Object.keys(rest).length > 0 || loaders.length + (uses.includes(babel) ? 1 : 0) !== uses.length) {
|
|
61
|
+
read.differences.push(`a rule of the \`webpack\` function of next.config does not apply: only one with a \`test\` and the loaders in \`use\` does, with \`include\`, \`exclude\`, \`resourceQuery\` and \`enforce\` or not, not ${String(test ?? "this one")}`);
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
read.rules.push({
|
|
65
|
+
test,
|
|
66
|
+
...include !== void 0 && { include },
|
|
67
|
+
...exclude !== void 0 && { exclude },
|
|
68
|
+
...resourceQuery !== void 0 && { resourceQuery },
|
|
69
|
+
...enforce !== void 0 && { enforce },
|
|
70
|
+
loaders,
|
|
71
|
+
compile: uses.includes(babel)
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
for (const [name, target] of Object.entries(after?.resolve?.alias ?? {})) {
|
|
75
|
+
const candidates = (Array.isArray(target) ? target : [target]).filter((candidate) => typeof candidate === "string");
|
|
76
|
+
read.aliases[name] = candidates.map((candidate) => candidate.replace(/^private-next-root-dir(?=\/|$)/, options.root));
|
|
77
|
+
}
|
|
78
|
+
if ((after?.plugins?.length ?? 0) > 0) read.differences.push("the plugins of webpack that the `webpack` function of next.config adds do not run");
|
|
79
|
+
return read;
|
|
80
|
+
}
|
|
81
|
+
const isCondition = (condition) => condition instanceof RegExp || typeof condition === "string" || typeof condition === "function" || Array.isArray(condition) && condition.every(isCondition);
|
|
82
|
+
const matches = (condition, value) => condition instanceof RegExp ? condition.test(value) : typeof condition === "string" ? value.startsWith(condition) : typeof condition === "function" ? condition(value) : condition.some((entry) => matches(entry, value));
|
|
83
|
+
const viteQuery = /[?&](?:raw|url|inline|no-inline|worker|worker_file|sharedworker)(?=[=&]|$)/;
|
|
84
|
+
/**
|
|
85
|
+
* The loaders of the rules for a file, as webpack matches them: `test`,
|
|
86
|
+
* `include` and `exclude` its path, `resourceQuery` its query. Each rule that
|
|
87
|
+
* matches adds its loaders, as webpack runs them: those of `enforce: "pre"`
|
|
88
|
+
* first, then the others, then those of `enforce: "post"`. Fumadocs matches an
|
|
89
|
+
* `.mdx` file with `?collection=docs`, and a `meta.json` with it by its query.
|
|
90
|
+
*/
|
|
91
|
+
function loadersOf(rules, id) {
|
|
92
|
+
if (viteQuery.test(id)) return;
|
|
93
|
+
const queryAt = id.indexOf("?");
|
|
94
|
+
const file = queryAt < 0 ? id : id.slice(0, queryAt);
|
|
95
|
+
const query = queryAt < 0 ? "" : id.slice(queryAt);
|
|
96
|
+
const matched = rules.filter((rule) => matches(rule.test, file) && (rule.include === void 0 || matches(rule.include, file)) && (rule.exclude === void 0 || !matches(rule.exclude, file)) && (rule.resourceQuery === void 0 || matches(rule.resourceQuery, query)));
|
|
97
|
+
if (matched.length === 0) return;
|
|
98
|
+
const phase = ({ enforce }) => enforce === "post" ? 0 : enforce === "pre" ? 2 : 1;
|
|
99
|
+
return {
|
|
100
|
+
loaders: matched.toSorted((a, b) => phase(a) - phase(b)).flatMap((rule) => rule.loaders),
|
|
101
|
+
compile: matched.some((rule) => rule.compile)
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The candidates of an alias for an import, as webpack takes them: a name
|
|
106
|
+
* matches the import and what is under it, and one that ends with `$` only
|
|
107
|
+
* the import itself.
|
|
108
|
+
*/
|
|
109
|
+
function aliasOf(aliases, source) {
|
|
110
|
+
for (const [name, candidates] of Object.entries(aliases)) if (name.endsWith("$")) {
|
|
111
|
+
if (source === name.slice(0, -1)) return candidates;
|
|
112
|
+
} else if (source === name) return candidates;
|
|
113
|
+
else if (source.startsWith(`${name}/`)) return candidates.map((candidate) => candidate + source.slice(name.length));
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* A file of a package by the import that names it, `@next/mdx/mdx-components.js`:
|
|
117
|
+
* what Vite pre-bundles. The candidate of an alias is often the file, as
|
|
118
|
+
* `require.resolve()` finds it.
|
|
119
|
+
*/
|
|
120
|
+
function importOfFile(file) {
|
|
121
|
+
if (!path.isAbsolute(file) || !file.split(path.sep).includes("node_modules")) return file;
|
|
122
|
+
for (let dir = path.dirname(file); path.basename(dir) !== "node_modules";) {
|
|
123
|
+
const manifest = path.join(dir, "package.json");
|
|
124
|
+
const name = fs.existsSync(manifest) ? JSON.parse(fs.readFileSync(manifest, "utf8")).name : void 0;
|
|
125
|
+
if (name) return `${name}/${path.relative(dir, file).split(path.sep).join("/")}`;
|
|
126
|
+
const parent = path.dirname(dir);
|
|
127
|
+
if (parent === dir) break;
|
|
128
|
+
dir = parent;
|
|
129
|
+
}
|
|
130
|
+
return file;
|
|
131
|
+
}
|
|
132
|
+
/** The rules and aliases of the `webpack` function in `next.config`, for each layer. */
|
|
133
|
+
function createWebpackRules(context, runLoader) {
|
|
134
|
+
const { root, projectRequire, config, buildId } = context;
|
|
135
|
+
const read = (isServer) => readWebpackFunction(config.webpack, {
|
|
136
|
+
root,
|
|
137
|
+
isServer,
|
|
138
|
+
buildId,
|
|
139
|
+
config
|
|
140
|
+
});
|
|
141
|
+
const client = read(false);
|
|
142
|
+
const server = read(true);
|
|
143
|
+
const of = {
|
|
144
|
+
rsc: server,
|
|
145
|
+
ssr: server,
|
|
146
|
+
browser: client
|
|
147
|
+
};
|
|
148
|
+
const ruleOf = (id, layer) => loadersOf(of[layer].rules, id);
|
|
149
|
+
const loaders = /* @__PURE__ */ new Map();
|
|
150
|
+
const loaderAt = (request) => {
|
|
151
|
+
let loader = loaders.get(request);
|
|
152
|
+
if (!loader) {
|
|
153
|
+
const loaded = projectRequire(request);
|
|
154
|
+
loader = typeof loaded === "function" ? loaded : loaded.default;
|
|
155
|
+
loaders.set(request, loader);
|
|
156
|
+
}
|
|
157
|
+
return loader;
|
|
158
|
+
};
|
|
159
|
+
const packagesOf = ({ aliases }) => Object.values(aliases).flat().map(importOfFile).filter((candidate) => {
|
|
160
|
+
if (candidate.startsWith(".") || path.isAbsolute(candidate)) return false;
|
|
161
|
+
try {
|
|
162
|
+
projectRequire.resolve(candidate);
|
|
163
|
+
return true;
|
|
164
|
+
} catch {
|
|
165
|
+
return false;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
return {
|
|
169
|
+
webpackRuleOf: ruleOf,
|
|
170
|
+
webpackPackages: {
|
|
171
|
+
rsc: packagesOf(server),
|
|
172
|
+
ssr: packagesOf(server),
|
|
173
|
+
browser: packagesOf(client)
|
|
174
|
+
},
|
|
175
|
+
async loadWithWebpackRule(id, layer, source) {
|
|
176
|
+
const rule = ruleOf(id, layer);
|
|
177
|
+
if (!rule) return;
|
|
178
|
+
let code = source ?? await fs.promises.readFile(id.split("?")[0]);
|
|
179
|
+
for (const { loader, options } of rule.loaders.toReversed()) {
|
|
180
|
+
const run = loaderAt(loader);
|
|
181
|
+
const input = run.raw ? Buffer.from(code) : String(code);
|
|
182
|
+
[code] = await runLoader(run, options, id, input);
|
|
183
|
+
}
|
|
184
|
+
return String(code);
|
|
185
|
+
},
|
|
186
|
+
webpackAliasOf: (source, layer) => aliasOf(of[layer].aliases, source)?.map(importOfFile),
|
|
187
|
+
webpackDifferences: Array.from(/* @__PURE__ */ new Set([...client.differences, ...server.differences]))
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
//#endregion
|
|
191
|
+
export { aliasOf, createWebpackRules, importOfFile, loadersOf, readWebpackFunction };
|