@thomas.f/prnext 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 +54 -0
- package/build/action-exports.mjs +107 -0
- package/build/actions.mjs +214 -0
- package/build/app-config.mjs +138 -0
- package/build/app-routing.mjs +119 -0
- package/build/app-static.mjs +158 -0
- package/build/app.mjs +536 -0
- package/build/cache-components.mjs +91 -0
- package/build/cache-handlers.mjs +54 -0
- package/build/check.mjs +40 -0
- package/build/compiler.mjs +222 -0
- package/build/config.mjs +193 -0
- package/build/css-modules.mjs +114 -0
- package/build/custom-routes.mjs +363 -0
- package/build/dev-worker.mjs +18 -0
- package/build/dev.mjs +76 -0
- package/build/directory-cache.mjs +29 -0
- package/build/dynamic.mjs +125 -0
- package/build/edge.mjs +141 -0
- package/build/font-data.LICENSE.txt +25 -0
- package/build/font-data.json +1 -0
- package/build/font-files.mjs +221 -0
- package/build/fonts.mjs +131 -0
- package/build/framework-imports.mjs +17 -0
- package/build/i18n.mjs +39 -0
- package/build/images-config.mjs +62 -0
- package/build/images.mjs +86 -0
- package/build/incremental.mjs +76 -0
- package/build/index.mjs +356 -0
- package/build/loader-cache.mjs +69 -0
- package/build/metadata-images.mjs +72 -0
- package/build/metadata-routes.mjs +77 -0
- package/build/middleware.mjs +231 -0
- package/build/migrate.mjs +289 -0
- package/build/module-resolution.mjs +78 -0
- package/build/native-discovery.mjs +134 -0
- package/build/native-packages.mjs +105 -0
- package/build/npm-packages.mjs +110 -0
- package/build/og.mjs +14 -0
- package/build/page-extensions.mjs +12 -0
- package/build/passthrough-loader.cjs +4 -0
- package/build/precompress.mjs +122 -0
- package/build/scan.mjs +178 -0
- package/build/script-workers.mjs +25 -0
- package/build/server-actions-config.mjs +27 -0
- package/build/source-maps.mjs +34 -0
- package/build/standalone-config.mjs +19 -0
- package/build/standalone-runner.mjs +21 -0
- package/build/standalone-worker.mjs +5 -0
- package/build/standalone.mjs +240 -0
- package/build/static-export.mjs +93 -0
- package/build/styles.mjs +174 -0
- package/build/transform-cache.mjs +50 -0
- package/build/transform.mjs +155 -0
- package/build/turbopack-conditions.mjs +59 -0
- package/build/webpack-bridge.cjs +3 -0
- package/build/webpack-graph.mjs +256 -0
- package/build/webpack-loader.cjs +9 -0
- package/cli.mjs +210 -0
- package/compat/app-client-page.cjs +32 -0
- package/compat/app-context.cjs +40 -0
- package/compat/app-context.d.cts +20 -0
- package/compat/app-dynamic.cjs +34 -0
- package/compat/app-error-boundary.cjs +53 -0
- package/compat/app-global-error-boundary.cjs +34 -0
- package/compat/app-layout-context.cjs +27 -0
- package/compat/app-navigation-boundary.cjs +47 -0
- package/compat/app.cjs +33 -0
- package/compat/app.d.cts +9 -0
- package/compat/cache-handlers.cjs +223 -0
- package/compat/cache-life.cjs +34 -0
- package/compat/cache.cjs +99 -0
- package/compat/cache.d.cts +36 -0
- package/compat/compat/router.d.cts +2 -0
- package/compat/compat-router.cjs +7 -0
- package/compat/constants.cjs +8 -0
- package/compat/constants.d.cts +7 -0
- package/compat/cookies.cjs +129 -0
- package/compat/data-cache.cjs +389 -0
- package/compat/dist/shared/lib/app-router-context.shared-runtime.d.cts +3 -0
- package/compat/dist/shared/lib/router-context.d.cts +1 -0
- package/compat/dist/shared/lib/router-context.shared-runtime.d.cts +3 -0
- package/compat/document.cjs +73 -0
- package/compat/document.d.cts +41 -0
- package/compat/draft.cjs +28 -0
- package/compat/dynamic-bailout.cjs +13 -0
- package/compat/dynamic.cjs +119 -0
- package/compat/dynamic.d.cts +32 -0
- package/compat/error.cjs +36 -0
- package/compat/error.d.cts +15 -0
- package/compat/fetch.d.cts +10 -0
- package/compat/font/google.d.cts +1 -0
- package/compat/font/local.d.cts +2 -0
- package/compat/font-google.cjs +2 -0
- package/compat/font-google.d.cts +1944 -0
- package/compat/font-local.cjs +5 -0
- package/compat/font-local.d.cts +3 -0
- package/compat/font-types.d.cts +25 -0
- package/compat/head.cjs +180 -0
- package/compat/head.d.cts +3 -0
- package/compat/headers.cjs +89 -0
- package/compat/headers.d.cts +26 -0
- package/compat/image-config.cjs +3 -0
- package/compat/image-imports.d.ts +12 -0
- package/compat/image-shared.cjs +62 -0
- package/compat/image.cjs +64 -0
- package/compat/image.d.cts +35 -0
- package/compat/incremental-cache.cjs +186 -0
- package/compat/index.d.ts +159 -0
- package/compat/instant-samples.cjs +39 -0
- package/compat/instant.cjs +13 -0
- package/compat/link.cjs +68 -0
- package/compat/link.d.cts +17 -0
- package/compat/locale.cjs +30 -0
- package/compat/metadata.d.cts +77 -0
- package/compat/navigation-controls.cjs +27 -0
- package/compat/navigation-server.cjs +25 -0
- package/compat/navigation.cjs +52 -0
- package/compat/navigation.d.cts +28 -0
- package/compat/next-app-router-context.cjs +6 -0
- package/compat/next-router-context.cjs +3 -0
- package/compat/og.cjs +32 -0
- package/compat/og.d.cts +1 -0
- package/compat/paths.cjs +33 -0
- package/compat/preview.cjs +41 -0
- package/compat/rewrite.cjs +41 -0
- package/compat/route-request.cjs +64 -0
- package/compat/router.cjs +137 -0
- package/compat/router.d.cts +58 -0
- package/compat/script-context.cjs +5 -0
- package/compat/script-loader.cjs +135 -0
- package/compat/script.cjs +69 -0
- package/compat/script.d.cts +17 -0
- package/compat/server-primitives.cjs +109 -0
- package/compat/server.cjs +13 -0
- package/compat/server.d.cts +37 -0
- package/compat/static-generation.cjs +108 -0
- package/compat/use-cache.cjs +129 -0
- package/index.mjs +1 -0
- package/native/platforms.json +6 -0
- package/native/resolve.mjs +81 -0
- package/package.json +127 -0
- package/release/verify.mjs +59 -0
- package/runtime/action-client.mjs +19 -0
- package/runtime/action-crypto.mjs +138 -0
- package/runtime/action-reference.mjs +2 -0
- package/runtime/action-server.mjs +161 -0
- package/runtime/action-ssr.mjs +5 -0
- package/runtime/api.mjs +326 -0
- package/runtime/app-client.mjs +539 -0
- package/runtime/app-content.mjs +8 -0
- package/runtime/app-errors.mjs +12 -0
- package/runtime/app-instant.mjs +30 -0
- package/runtime/app-metadata.mjs +410 -0
- package/runtime/app-partial-model.mjs +228 -0
- package/runtime/app-partial.mjs +130 -0
- package/runtime/app-prefetch.mjs +34 -0
- package/runtime/app-recovery.mjs +87 -0
- package/runtime/app-render.mjs +544 -0
- package/runtime/app-routing-access.mjs +143 -0
- package/runtime/app-routing-context.mjs +35 -0
- package/runtime/app-routing-state.mjs +61 -0
- package/runtime/app-routing.mjs +323 -0
- package/runtime/app-segments.mjs +22 -0
- package/runtime/app-static-params.mjs +120 -0
- package/runtime/app-static.mjs +86 -0
- package/runtime/app-stream-channel.mjs +92 -0
- package/runtime/app-stream-html.mjs +279 -0
- package/runtime/build-directory.mjs +48 -0
- package/runtime/cache-arguments.mjs +33 -0
- package/runtime/cache-components-rsc.mjs +64 -0
- package/runtime/cache-request.mjs +23 -0
- package/runtime/client-navigation.mjs +40 -0
- package/runtime/dev-client.mjs +129 -0
- package/runtime/dev-file-changes.mjs +110 -0
- package/runtime/document.mjs +96 -0
- package/runtime/edge.mjs +105 -0
- package/runtime/env.mjs +68 -0
- package/runtime/fetch-cache.mjs +379 -0
- package/runtime/font-preload.mjs +4 -0
- package/runtime/http.mjs +190 -0
- package/runtime/incremental-cache.mjs +83 -0
- package/runtime/metadata-route.mjs +83 -0
- package/runtime/middleware.mjs +186 -0
- package/runtime/module-loader.mjs +21 -0
- package/runtime/navigation.mjs +16 -0
- package/runtime/og-loader.mjs +17 -0
- package/runtime/pages-client.mjs +197 -0
- package/runtime/pages-error-props.mjs +6 -0
- package/runtime/pages-errors.mjs +85 -0
- package/runtime/pages-initial-props.mjs +43 -0
- package/runtime/pages-navigation.mjs +565 -0
- package/runtime/pages-paths.mjs +80 -0
- package/runtime/pages-revalidate.mjs +33 -0
- package/runtime/partial-artifact-cache.mjs +37 -0
- package/runtime/partial-template.mjs +174 -0
- package/runtime/profile.mjs +9 -0
- package/runtime/profiles.json +47 -0
- package/runtime/render.mjs +304 -0
- package/runtime/request-transport.mjs +41 -0
- package/runtime/request-work.mjs +7 -0
- package/runtime/route-static.mjs +117 -0
- package/runtime/rsc-worker.mjs +426 -0
- package/runtime/script-html.mjs +61 -0
- package/runtime/stream-select.mjs +25 -0
- package/runtime/stream-utils.mjs +127 -0
- package/runtime/transport.mjs +108 -0
- package/runtime/watch-project.mjs +130 -0
- package/runtime/worker-artifacts.mjs +22 -0
- package/runtime/worker-metadata.mjs +31 -0
- package/runtime/worker.mjs +197 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile, cp, readdir, lstat, rename, rm } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { randomUUID } from 'node:crypto';
|
|
4
|
+
|
|
5
|
+
const marker = '.prnext-export.json';
|
|
6
|
+
function filename(root, url) {
|
|
7
|
+
const parts = url.replace(/^\/+/, '').split('/').filter(Boolean).map(segment => {
|
|
8
|
+
const value = decodeURIComponent(segment);
|
|
9
|
+
if (value === '.' || value === '..' || /[\\/\0]/.test(value)) throw new Error('Invalid static export pathname');
|
|
10
|
+
return value;
|
|
11
|
+
});
|
|
12
|
+
return path.join(root, ...parts);
|
|
13
|
+
}
|
|
14
|
+
async function exists(file) { try { return await lstat(file); } catch (error) { if (error.code !== 'ENOENT') throw error; } }
|
|
15
|
+
|
|
16
|
+
/** Only browser files and public response bodies enter an export, never runtime secrets. */
|
|
17
|
+
export async function prepareStaticExport({ project, stage, manifest, config }) {
|
|
18
|
+
const reject = reason => { throw new Error(`output: 'export': ${reason}`); };
|
|
19
|
+
if (manifest.middleware) reject('middleware/proxy requires a server');
|
|
20
|
+
if (manifest.customRoutes.redirects.length || manifest.customRoutes.headers.length || Object.values(manifest.customRoutes.rewrites).some(value => value.length)) reject('headers, redirects and rewrites require a server');
|
|
21
|
+
if (Object.keys(manifest.app?.actions || {}).length) reject('Server Actions require a server');
|
|
22
|
+
const folder = path.join(stage, 'export');
|
|
23
|
+
await mkdir(folder);
|
|
24
|
+
const written = new Set();
|
|
25
|
+
async function put(url, contents, source) {
|
|
26
|
+
const file = filename(folder, url);
|
|
27
|
+
if (written.has(file) || await exists(file)) reject(`conflicting exported files at ${url}`);
|
|
28
|
+
written.add(file); await mkdir(path.dirname(file), { recursive: true });
|
|
29
|
+
if (source) await cp(source, file); else await writeFile(file, contents);
|
|
30
|
+
}
|
|
31
|
+
const mount = config.basePath || '';
|
|
32
|
+
const publicDirectory = path.join(project.root, 'public');
|
|
33
|
+
async function copyPublic(source, url) {
|
|
34
|
+
for (const item of await readdir(source, { withFileTypes: true })) {
|
|
35
|
+
if (item.isSymbolicLink()) reject(`public symlink ${item.name} cannot be exported`);
|
|
36
|
+
const next = url + '/' + item.name;
|
|
37
|
+
if (item.isDirectory()) await copyPublic(path.join(source, item.name), next);
|
|
38
|
+
else if (item.isFile()) await put(next, undefined, path.join(source, item.name));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
if (await exists(publicDirectory)) await copyPublic(publicDirectory, mount);
|
|
42
|
+
const assets = /^https?:/.test(manifest.config.assetBase) ? '/_prnext/assets' : manifest.config.assetBase;
|
|
43
|
+
await mkdir(path.dirname(filename(folder, assets)), { recursive: true });
|
|
44
|
+
if (await exists(filename(folder, assets))) reject('public files overlap the framework assets');
|
|
45
|
+
await cp(path.join(stage, 'assets'), filename(folder, assets), { recursive: true });
|
|
46
|
+
for (const route of manifest.routes) {
|
|
47
|
+
if (route.internal) continue;
|
|
48
|
+
if (route.kind === 'api' && route.router !== 'app') reject(`Pages API ${route.pattern} requires a server`);
|
|
49
|
+
if (route.ssp || route.ppr || route.pprGeneric || route.dynamicPaths?.length || route.cacheConfig?.runtime === 'edge') reject(`${route.pattern} requires request-time rendering`);
|
|
50
|
+
if (route.pattern.includes('[') && (!route.ssg || route.router !== 'app' && route.fallback !== false)) reject(`${route.pattern} needs generated paths without fallback`);
|
|
51
|
+
if (!route.pattern.includes('[') && !manifest.prerendered.some(item => item.path === route.pattern)) reject(`${route.pattern} was not fully prerendered`);
|
|
52
|
+
}
|
|
53
|
+
for (const entry of manifest.prerendered) {
|
|
54
|
+
const route = manifest.routes.find(route => route.id === entry.routeId);
|
|
55
|
+
if (!route) reject(`missing route identity for ${entry.path}`);
|
|
56
|
+
if (route.errorStatus === 404 && manifest.appNotFound) continue;
|
|
57
|
+
if (route?.internal && !route.errorStatus && route.id !== manifest.appNotFound) continue;
|
|
58
|
+
if (entry.status >= 300 && ![404, 500].includes(entry.status)) reject(`redirect/error ${entry.path} cannot be a static response`);
|
|
59
|
+
if (entry.revalidate !== undefined && entry.revalidate !== false) reject(`ISR ${entry.path} requires a server`);
|
|
60
|
+
let pathname = entry.path.replace(/\/$/, '') || '/';
|
|
61
|
+
if (route?.errorStatus) pathname = '/' + route.errorStatus;
|
|
62
|
+
if (route?.id === manifest.appNotFound) pathname = '/404';
|
|
63
|
+
const body = await readFile(path.join(stage, entry.file));
|
|
64
|
+
if (route?.kind === 'api') { await put(mount + pathname, body); continue; }
|
|
65
|
+
let html = body.toString();
|
|
66
|
+
if (html.includes(config.images.path + '?')) reject(`optimized images in ${pathname} require images.unoptimized or a custom loader`);
|
|
67
|
+
html = html.replace(/<head(?:\s[^>]*)?>/i, value => value + '<script>window.__PRNEXT_STATIC_EXPORT__=true;</script>');
|
|
68
|
+
const output = pathname === '/' ? '/index.html' : config.trailingSlash && !['/404', '/500'].includes(pathname) ? pathname + '/index.html' : pathname + '.html';
|
|
69
|
+
await put(mount + output, html);
|
|
70
|
+
if (entry.dataFile) {
|
|
71
|
+
const data = await readFile(path.join(stage, entry.dataFile));
|
|
72
|
+
if (route?.router === 'app') await put(mount + '/_prnext/flight' + (pathname === '/' ? '' : pathname) + '/index.txt', data);
|
|
73
|
+
else for (const prefix of ['/_prnext/data/', '/_next/data/']) await put(mount + prefix + manifest.buildId + (pathname === '/' ? '/index' : pathname) + '.json', data);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
await writeFile(path.join(folder, marker), JSON.stringify({ version: 1, buildId: manifest.buildId, basePath: mount, trailingSlash: config.trailingSlash }));
|
|
77
|
+
return folder;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function publishStaticExport(source, root) {
|
|
81
|
+
const destination = path.join(root, 'out'), backup = path.join(root, '.prnext-export-backup-' + randomUUID());
|
|
82
|
+
const previous = await exists(destination);
|
|
83
|
+
if (previous) {
|
|
84
|
+
if (!previous.isDirectory() || previous.isSymbolicLink() || !await exists(path.join(destination, marker))) throw new Error('Refusing to replace out/: it is not a PRNext static export');
|
|
85
|
+
await rename(destination, backup);
|
|
86
|
+
}
|
|
87
|
+
try { await rename(source, destination); }
|
|
88
|
+
catch (error) { if (previous) await rename(backup, destination); throw error; }
|
|
89
|
+
return {
|
|
90
|
+
async rollback() { await rename(destination, source); if (previous) await rename(backup, destination); },
|
|
91
|
+
async commit() { if (previous) await rm(backup, { recursive: true, force: true }); },
|
|
92
|
+
};
|
|
93
|
+
}
|
package/build/styles.mjs
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { build as bundle } from 'esbuild';
|
|
2
|
+
import { access, readFile, readdir, writeFile, rm } from 'node:fs/promises';
|
|
3
|
+
import { createRequire } from 'node:module';
|
|
4
|
+
import { randomUUID } from 'node:crypto';
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import postcss from 'postcss';
|
|
8
|
+
import valueParser from 'postcss-value-parser';
|
|
9
|
+
import flexbugs from 'postcss-flexbugs-fixes';
|
|
10
|
+
import presetEnv from 'postcss-preset-env';
|
|
11
|
+
|
|
12
|
+
export const stylesheetPattern = /\.(?:css|scss|sass)$/i;
|
|
13
|
+
export const moduleStylesheetPattern = /\.module\.(?:css|scss|sass)$/i;
|
|
14
|
+
const configPattern = /^(?:postcss\.config|\.postcssrc)\.(?:js|mjs|cjs|json)$/;
|
|
15
|
+
|
|
16
|
+
async function importConfiguration(filename) {
|
|
17
|
+
const temporary = path.join(path.dirname(filename), `.prnext-postcss-${randomUUID()}.mjs`);
|
|
18
|
+
try {
|
|
19
|
+
// Bundle local helpers afresh so an in-process rebuild sees their changes.
|
|
20
|
+
const result = await bundle({ entryPoints: [filename], bundle: true, write: false,
|
|
21
|
+
platform: 'node', target: 'node22', format: 'esm', packages: 'external', logLevel: 'silent',
|
|
22
|
+
define: { 'import.meta.url': JSON.stringify(pathToFileURL(filename).href), __filename: JSON.stringify(filename), __dirname: JSON.stringify(path.dirname(filename)) },
|
|
23
|
+
banner: { js: `import {createRequire as __prnextCreateRequire} from 'node:module';const require=__prnextCreateRequire(${JSON.stringify(pathToFileURL(filename).href)});` } });
|
|
24
|
+
await writeFile(temporary, result.outputFiles[0].contents, { flag: 'wx' });
|
|
25
|
+
return (await import(pathToFileURL(temporary).href)).default;
|
|
26
|
+
} finally { await rm(temporary, { force: true }); }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async function loadConfiguration(root) {
|
|
30
|
+
const filenames = (await readdir(root)).filter(name => configPattern.test(name) || name === '.postcssrc');
|
|
31
|
+
let metadata;
|
|
32
|
+
try { metadata = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8')); }
|
|
33
|
+
catch (error) { if (error.code !== 'ENOENT') throw error; }
|
|
34
|
+
if (filenames.length > 1 || (filenames.length && metadata?.postcss !== undefined)) throw new Error('Multiple PostCSS configurations found. Keep one postcss.config.*, .postcssrc*, or package.json postcss entry.');
|
|
35
|
+
if (!filenames.length) return metadata?.postcss;
|
|
36
|
+
const filename = path.join(root, filenames[0]);
|
|
37
|
+
return /\.(?:js|mjs|cjs)$/.test(filename) ? importConfiguration(filename) : JSON.parse(await readFile(filename, 'utf8'));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function createProcessor(root) {
|
|
41
|
+
const config = await loadConfiguration(root);
|
|
42
|
+
if (config === undefined) return postcss([flexbugs(), presetEnv({ autoprefixer: { flexbox: 'no-2009' }, stage: 3, features: { 'custom-properties': false } })]);
|
|
43
|
+
if (!config || typeof config !== 'object' || Array.isArray(config) || !config.plugins || typeof config.plugins !== 'object') throw new Error('PostCSS configuration must export an object with a plugins object or array; configuration functions are not supported.');
|
|
44
|
+
const entries = Array.isArray(config.plugins)
|
|
45
|
+
? config.plugins.filter(item => item != null).map(item => typeof item === 'string' ? [item, true] : item)
|
|
46
|
+
: Object.entries(config.plugins);
|
|
47
|
+
const require = createRequire(path.join(root, 'package.json'));
|
|
48
|
+
const plugins = [];
|
|
49
|
+
for (const item of entries) {
|
|
50
|
+
if (!Array.isArray(item) || typeof item[0] !== 'string' || item.length !== 2 || item[1] == null || !['object', 'boolean', 'string'].includes(typeof item[1])) throw new Error('PostCSS plugins must be names or [name, options] pairs, with false to disable a plugin.');
|
|
51
|
+
const [name, configuredOptions] = item;
|
|
52
|
+
const options = name === '@tailwindcss/postcss' && configuredOptions !== false
|
|
53
|
+
? { base: root, ...(typeof configuredOptions === 'object' ? configuredOptions : {}) } : configuredOptions;
|
|
54
|
+
if (options === false) continue;
|
|
55
|
+
if (/^(?:postcss-modules|postcss-modules-(?:values|scope|extract-imports|local-by-default))$/.test(name)) throw new Error(`${name} duplicates PRNext CSS Modules processing. Remove it from the PostCSS configuration.`);
|
|
56
|
+
let filename;
|
|
57
|
+
try { filename = require.resolve(name); }
|
|
58
|
+
catch (cause) { throw new Error(`PostCSS plugin ${name} is missing from the application. Install it in ${root}.`, { cause }); }
|
|
59
|
+
const imported = await import(pathToFileURL(filename).href);
|
|
60
|
+
const plugin = imported.default || imported;
|
|
61
|
+
plugins.push(options === true || (typeof options === 'object' && Object.keys(options).length === 0)
|
|
62
|
+
? (plugin.postcss === true ? plugin() : plugin) : typeof plugin === 'function' ? plugin(options) : plugin);
|
|
63
|
+
}
|
|
64
|
+
return postcss(plugins);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function rebaseReferences(root, file) {
|
|
68
|
+
const rebase = (value, node) => {
|
|
69
|
+
if (!value || /^(?:[a-z][a-z\d+.-]*:|\/|#|var\()/i.test(value)) return value;
|
|
70
|
+
let source = node.source?.input?.file || file;
|
|
71
|
+
const position = node.source?.start;
|
|
72
|
+
if (position && node.source.input.map) source = node.source.input.origin(position.line, position.column)?.file || source;
|
|
73
|
+
if (source.startsWith('file:')) source = fileURLToPath(source);
|
|
74
|
+
if (path.dirname(source) === path.dirname(file)) return value;
|
|
75
|
+
const suffixIndex = value.search(/[?#]/);
|
|
76
|
+
const pathname = suffixIndex < 0 ? value : value.slice(0, suffixIndex);
|
|
77
|
+
const suffix = suffixIndex < 0 ? '' : value.slice(suffixIndex);
|
|
78
|
+
let relative = path.relative(path.dirname(file), path.resolve(path.dirname(source), pathname)).replaceAll(path.sep, '/');
|
|
79
|
+
if (!relative.startsWith('.')) relative = './' + relative;
|
|
80
|
+
return relative + suffix;
|
|
81
|
+
};
|
|
82
|
+
const values = (value, owner, imports = false) => {
|
|
83
|
+
const parsed = valueParser(value);
|
|
84
|
+
parsed.walk(node => {
|
|
85
|
+
if (node.type === 'function' && node.value.toLowerCase() === 'url') {
|
|
86
|
+
const child = node.nodes.find(part => part.type === 'string' || part.type === 'word');
|
|
87
|
+
if (child && node.nodes.filter(part => !['space', 'comment'].includes(part.type)).length === 1) child.value = rebase(child.value, owner);
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
if (imports) {
|
|
92
|
+
const first = parsed.nodes.find(node => !['space', 'comment'].includes(node.type));
|
|
93
|
+
if (first?.type === 'string') first.value = rebase(first.value, owner);
|
|
94
|
+
}
|
|
95
|
+
return parsed.toString();
|
|
96
|
+
};
|
|
97
|
+
root.walkDecls(node => { if (/url\(/i.test(node.value)) node.value = values(node.value, node); });
|
|
98
|
+
root.walkAtRules('import', node => { node.params = values(node.params, node, true); });
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function canAccess(filename) {
|
|
102
|
+
try { await access(filename); return true; } catch { return false; }
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function packageImporter(require) {
|
|
106
|
+
return { async findFileUrl(url) {
|
|
107
|
+
if (/^(?:\.|\/|[a-z][a-z\d+.-]*:)/i.test(url)) return null;
|
|
108
|
+
const specifier = url.replace(/^~/, '');
|
|
109
|
+
const parts = specifier.split('/');
|
|
110
|
+
const name = parts.splice(0, specifier.startsWith('@') ? 2 : 1).join('/');
|
|
111
|
+
for (const directory of require.resolve.paths(name) || []) {
|
|
112
|
+
const packageDirectory = path.join(directory, name);
|
|
113
|
+
if (!await canAccess(packageDirectory)) continue;
|
|
114
|
+
if (parts.length) return pathToFileURL(path.join(packageDirectory, ...parts));
|
|
115
|
+
let metadata;
|
|
116
|
+
try { metadata = JSON.parse(await readFile(path.join(packageDirectory, 'package.json'), 'utf8')); } catch {}
|
|
117
|
+
const entry = metadata?.sass || metadata?.style || 'index';
|
|
118
|
+
return pathToFileURL(path.join(packageDirectory, entry));
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
} };
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Build-only transformations, shared and deduplicated across all three React graphs. */
|
|
125
|
+
export function createStyleProcessor({ projectRoot, sassOptions = {} }) {
|
|
126
|
+
const root = path.resolve(projectRoot);
|
|
127
|
+
const require = createRequire(path.join(root, 'package.json'));
|
|
128
|
+
const cache = new Map();
|
|
129
|
+
let processor;
|
|
130
|
+
let sass;
|
|
131
|
+
async function transform(file) {
|
|
132
|
+
let css = await readFile(file, 'utf8');
|
|
133
|
+
let sourceMap;
|
|
134
|
+
const dependencies = new Set([file]);
|
|
135
|
+
if (/\.s[ac]ss$/i.test(file)) {
|
|
136
|
+
const { implementation = 'sass', additionalData = '', includePaths = [], loadPaths = [], outputStyle, ...options } = sassOptions;
|
|
137
|
+
if (!['sass', 'sass-embedded'].includes(implementation)) throw new Error('sassOptions.implementation must be sass or sass-embedded.');
|
|
138
|
+
if (!sass) {
|
|
139
|
+
let filename;
|
|
140
|
+
try { filename = require.resolve(implementation); }
|
|
141
|
+
catch (cause) { throw new Error(`Sass stylesheet ${file} requires ${implementation} in the application. Install it with npm install --save-dev ${implementation}.`, { cause }); }
|
|
142
|
+
sass = import(pathToFileURL(filename).href);
|
|
143
|
+
}
|
|
144
|
+
const imported = await sass;
|
|
145
|
+
const compiler = imported.compileStringAsync ? imported : imported.default;
|
|
146
|
+
if (typeof additionalData === 'function') css = await additionalData(css, { resourcePath: file, rootContext: root });
|
|
147
|
+
else if (typeof additionalData === 'string') css = additionalData + '\n' + css;
|
|
148
|
+
else throw new Error('sassOptions.additionalData must be a string or function.');
|
|
149
|
+
if (typeof css !== 'string') throw new Error('sassOptions.additionalData must return a string.');
|
|
150
|
+
const compiled = await compiler.compileStringAsync(css, { ...options, ...(outputStyle ? { style: outputStyle } : {}),
|
|
151
|
+
url: pathToFileURL(file), syntax: file.endsWith('.sass') ? 'indented' : 'scss',
|
|
152
|
+
loadPaths: [...includePaths, ...loadPaths].map(directory => path.resolve(root, directory)),
|
|
153
|
+
importers: [...(options.importers || []), packageImporter(require), ...(compiler.NodePackageImporter ? [new compiler.NodePackageImporter(root)] : [])],
|
|
154
|
+
sourceMap: true, sourceMapIncludeSources: true });
|
|
155
|
+
css = compiled.css;
|
|
156
|
+
sourceMap = compiled.sourceMap;
|
|
157
|
+
for (const url of compiled.loadedUrls) if (url.protocol === 'file:') dependencies.add(fileURLToPath(url));
|
|
158
|
+
}
|
|
159
|
+
processor ??= createProcessor(root);
|
|
160
|
+
const processed = await (await processor).process(css, { from: file, to: file, map: sourceMap ? { prev: sourceMap, inline: false, annotation: false } : false });
|
|
161
|
+
for (const message of processed.messages) if (message.type === 'dependency' && message.file) dependencies.add(path.resolve(root, message.file));
|
|
162
|
+
rebaseReferences(processed.root, file);
|
|
163
|
+
const exports = Object.create(null);
|
|
164
|
+
if (moduleStylesheetPattern.test(file)) processed.root.walkRules(':export', rule => {
|
|
165
|
+
rule.walkDecls(declaration => { exports[declaration.prop] = declaration.value; });
|
|
166
|
+
rule.remove();
|
|
167
|
+
});
|
|
168
|
+
return { css: processed.root.toString(), exports, watchFiles: [...dependencies] };
|
|
169
|
+
}
|
|
170
|
+
return { process(file) {
|
|
171
|
+
if (!cache.has(file)) cache.set(file, transform(file));
|
|
172
|
+
return cache.get(file);
|
|
173
|
+
} };
|
|
174
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import {sourceMapsEnabled} from './source-maps.mjs';
|
|
3
|
+
|
|
4
|
+
const entries = new Map();
|
|
5
|
+
const byteLimit = 2 * 1024 * 1024, entryLimit = 256, itemLimit = 128 * 1024;
|
|
6
|
+
let bytes = 0, hits = 0, misses = 0;
|
|
7
|
+
export const transformCacheStats = () => ({entries:entries.size,bytes,hits,misses});
|
|
8
|
+
|
|
9
|
+
// Only pure string transformations belong here. No AST, input source, plugin
|
|
10
|
+
// closure or mutable build metadata survives in the cache. Include all options
|
|
11
|
+
// affecting output in settings, including build identity and target mode.
|
|
12
|
+
export function cachedTransform(kind, source, settings, transform) {
|
|
13
|
+
const key = createHash('sha256').update(JSON.stringify([kind,settings,sourceMapsEnabled()])).update('\0').update(source).digest('hex');
|
|
14
|
+
const found = entries.get(key);
|
|
15
|
+
if (found) {
|
|
16
|
+
hits++; entries.delete(key); entries.set(key,found);
|
|
17
|
+
return found.output;
|
|
18
|
+
}
|
|
19
|
+
misses++;
|
|
20
|
+
const output = transform();
|
|
21
|
+
// UTF-16 is an upper bound for the retained string payload. Metadata adds
|
|
22
|
+
// engine overhead; the entry count also bounds that overhead.
|
|
23
|
+
const size = output.length * 2 + key.length * 2;
|
|
24
|
+
if (size <= itemLimit) {
|
|
25
|
+
while (entries.size && (entries.size >= entryLimit || bytes + size > byteLimit)) {
|
|
26
|
+
const [oldest,value] = entries.entries().next().value;
|
|
27
|
+
entries.delete(oldest); bytes -= value.size;
|
|
28
|
+
}
|
|
29
|
+
entries.set(key,{output,size}); bytes += size;
|
|
30
|
+
}
|
|
31
|
+
return output;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Async transforms share the same payload and entry budget as synchronous ones. */
|
|
35
|
+
export async function cachedAsyncTransform(kind, source, settings, transform) {
|
|
36
|
+
const key = createHash('sha256').update(JSON.stringify([kind,settings,sourceMapsEnabled()])).update('\0').update(source).digest('hex');
|
|
37
|
+
const found = entries.get(key);
|
|
38
|
+
if (found) { hits++; entries.delete(key); entries.set(key,found); return found.output; }
|
|
39
|
+
misses++;
|
|
40
|
+
const output = await transform();
|
|
41
|
+
const size = output.length * 2 + key.length * 2;
|
|
42
|
+
if (size <= itemLimit) {
|
|
43
|
+
if (entries.has(key)) {bytes -= entries.get(key).size;entries.delete(key);}
|
|
44
|
+
while (entries.size && (entries.size >= entryLimit || bytes + size > byteLimit)) {
|
|
45
|
+
const [oldest,value] = entries.entries().next().value;entries.delete(oldest);bytes -= value.size;
|
|
46
|
+
}
|
|
47
|
+
entries.set(key,{output,size});bytes += size;
|
|
48
|
+
}
|
|
49
|
+
return output;
|
|
50
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
import {generateMapped,sourceMapsEnabled,extractSourceMap,inlineSourceMap} from './source-maps.mjs';
|
|
2
|
+
import {readFile,writeFile} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { parse } from '@babel/parser';
|
|
5
|
+
import traverseModule from '@babel/traverse';
|
|
6
|
+
import generateModule from '@babel/generator';
|
|
7
|
+
import * as t from '@babel/types';
|
|
8
|
+
import { cachedTransform } from './transform-cache.mjs';
|
|
9
|
+
|
|
10
|
+
const traverse = traverseModule.default || traverseModule;
|
|
11
|
+
const generate = generateModule.default || generateModule;
|
|
12
|
+
const serverExports = new Set(['getServerSideProps', 'getStaticProps', 'getStaticPaths']);
|
|
13
|
+
|
|
14
|
+
/** esbuild uses publicPath for both assets and chunks; Node needs relative chunk imports. */
|
|
15
|
+
export function relativeServerChunkImports(source, chunks, publicPath = '/_prnext/assets') {
|
|
16
|
+
const prefix = publicPath.replace(/\/+$/, '') + '/';
|
|
17
|
+
if (!source.includes(prefix)) return source;
|
|
18
|
+
return cachedTransform('chunk-imports',source,[[...chunks].sort(),prefix],()=>relativeChunksUncached(source,chunks,prefix));
|
|
19
|
+
}
|
|
20
|
+
function relativeChunksUncached(source,chunks,prefix) {
|
|
21
|
+
const edits = [];
|
|
22
|
+
const ast = parse(source, { sourceType: 'module' });
|
|
23
|
+
function rewrite(node) {
|
|
24
|
+
if (!node || !t.isStringLiteral(node) || !node.value.startsWith(prefix)) return;
|
|
25
|
+
const name = node.value.slice(prefix.length);
|
|
26
|
+
if (chunks.has(name)) {edits.push({ start: node.start, end: node.end, text: JSON.stringify('./' + name) });node.value='./'+name;delete node.extra;}
|
|
27
|
+
}
|
|
28
|
+
traverse(ast, {
|
|
29
|
+
ImportDeclaration(path) { rewrite(path.node.source); },
|
|
30
|
+
ExportNamedDeclaration(path) { rewrite(path.node.source); },
|
|
31
|
+
ExportAllDeclaration(path) { rewrite(path.node.source); },
|
|
32
|
+
CallExpression(path) { if (t.isImport(path.node.callee)) rewrite(path.node.arguments[0]); },
|
|
33
|
+
});
|
|
34
|
+
if(edits.length && sourceMapsEnabled() && extractSourceMap(source).map)return generateMapped(generate,ast,{comments:true},source,'bundle.mjs');
|
|
35
|
+
// Replacing only generated specifiers preserves every source line and asset URL.
|
|
36
|
+
for (const edit of edits.sort((left, right) => right.start - left.start)) source = source.slice(0, edit.start) + edit.text + source.slice(edit.end);
|
|
37
|
+
return source;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export async function rewriteServerChunks(file,chunks,publicPath){
|
|
41
|
+
const source=await readFile(file,'utf8');
|
|
42
|
+
if(!source.includes(publicPath.replace(/\/+$/,'')+'/'))return;
|
|
43
|
+
let map;
|
|
44
|
+
if(sourceMapsEnabled())try{map=JSON.parse(await readFile(file+'.map','utf8'));}catch(error){if(error.code!=='ENOENT')throw error;}
|
|
45
|
+
const result=relativeServerChunkImports(inlineSourceMap(source,map),chunks,publicPath);
|
|
46
|
+
if(map){
|
|
47
|
+
const mapped=extractSourceMap(result);
|
|
48
|
+
await writeFile(file+'.map',JSON.stringify({...mapped.map,file:path.basename(file)}));
|
|
49
|
+
await writeFile(file,mapped.code.replace(/\/\/[#@]\s*sourceMappingURL=[^\s]+/g,'')+'\n//# sourceMappingURL='+path.basename(file)+'.map\n');
|
|
50
|
+
}else await writeFile(file,result);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function parserOptions(filename) {
|
|
54
|
+
return { sourceType: 'unambiguous', sourceFilename: filename, plugins: ['jsx', ...( /\.tsx?$/.test(filename) ? ['typescript'] : [])] };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function assertSupportedSource(source, filename) {
|
|
58
|
+
if (!source.includes('use server')) return;
|
|
59
|
+
const ast = parse(source, parserOptions(filename));
|
|
60
|
+
traverse(ast, {
|
|
61
|
+
Directive(path) {
|
|
62
|
+
if (path.node.value.value === 'use server') throw new Error(`Server Actions ("use server") are not supported in the Pages Router: ${filename}. Move this action to app/ or use pages/api.`);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Strip loader exports and the now-unused dependency graph before esbuild sees a browser page. */
|
|
68
|
+
export function stripServerCode(source, filename = 'page.tsx') {
|
|
69
|
+
return cachedTransform('pages-strip',source,[filename],()=>stripServerCodeUncached(source,filename));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function stripServerCodeUncached(source, filename) {
|
|
73
|
+
const ast = parse(source, parserOptions(filename));
|
|
74
|
+
let program;
|
|
75
|
+
traverse(ast, { Program(path) { program = path; path.stop(); } });
|
|
76
|
+
const candidates = new Set();
|
|
77
|
+
const loaders = new Set();
|
|
78
|
+
const exportPaths = [];
|
|
79
|
+
|
|
80
|
+
function collect(binding) {
|
|
81
|
+
if (!binding || binding.scope !== program.scope || candidates.has(binding.identifier.name)) return;
|
|
82
|
+
candidates.add(binding.identifier.name);
|
|
83
|
+
binding.path.traverse({
|
|
84
|
+
ReferencedIdentifier(ref) { collect(ref.scope.getBinding(ref.node.name)); },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const statement of program.get('body')) {
|
|
89
|
+
if (!statement.isExportNamedDeclaration()) continue;
|
|
90
|
+
const declaration = statement.get('declaration');
|
|
91
|
+
if (declaration?.node) {
|
|
92
|
+
for (const name of Object.keys(t.getBindingIdentifiers(declaration.node))) {
|
|
93
|
+
if (serverExports.has(name)) { loaders.add(name); collect(program.scope.getBinding(name)); }
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
for (const specifier of statement.get('specifiers')) {
|
|
97
|
+
const exported = specifier.node.exported?.name ?? specifier.node.exported?.value;
|
|
98
|
+
if (!serverExports.has(exported)) continue;
|
|
99
|
+
if (specifier.node.local) {
|
|
100
|
+
loaders.add(specifier.node.local.name);
|
|
101
|
+
collect(program.scope.getBinding(specifier.node.local.name));
|
|
102
|
+
}
|
|
103
|
+
exportPaths.push(specifier);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
for (const specifier of exportPaths) {
|
|
108
|
+
const declaration = specifier.parentPath;
|
|
109
|
+
specifier.remove();
|
|
110
|
+
if (!declaration.node.specifiers.length && !declaration.node.declaration) declaration.remove();
|
|
111
|
+
}
|
|
112
|
+
for (const name of loaders) {
|
|
113
|
+
const binding = program.scope.getBinding(name);
|
|
114
|
+
if (!binding) continue;
|
|
115
|
+
if (binding.path.isImportSpecifier() || binding.path.isImportDefaultSpecifier() || binding.path.isImportNamespaceSpecifier()) {
|
|
116
|
+
const declaration = binding.path.parentPath;
|
|
117
|
+
binding.path.remove();
|
|
118
|
+
if (!declaration.node.specifiers.length) declaration.remove();
|
|
119
|
+
} else if (binding.path.isVariableDeclarator()) {
|
|
120
|
+
const declaration = binding.path.parentPath;
|
|
121
|
+
if (declaration.node.declarations.length === 1 && declaration.parentPath.isExportNamedDeclaration()) declaration.parentPath.remove();
|
|
122
|
+
else binding.path.remove();
|
|
123
|
+
} else if (binding.path.parentPath.isExportNamedDeclaration()) binding.path.parentPath.remove();
|
|
124
|
+
else binding.path.remove();
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Iteration matters: deleting an unused helper can make its database import unused.
|
|
128
|
+
let changed = true;
|
|
129
|
+
while (changed) {
|
|
130
|
+
changed = false;
|
|
131
|
+
program.scope.crawl();
|
|
132
|
+
for (const name of candidates) {
|
|
133
|
+
const binding = program.scope.getBinding(name);
|
|
134
|
+
if (!binding || binding.referenced) continue;
|
|
135
|
+
const declaration = binding.path;
|
|
136
|
+
if (declaration.isImportSpecifier() || declaration.isImportDefaultSpecifier() || declaration.isImportNamespaceSpecifier()) {
|
|
137
|
+
const parent = declaration.parentPath;
|
|
138
|
+
declaration.remove();
|
|
139
|
+
if (!parent.node.specifiers.length) parent.remove();
|
|
140
|
+
} else if (declaration.isVariableDeclarator()) {
|
|
141
|
+
// A destructuring pattern may contain bindings still used by the page.
|
|
142
|
+
const names = Object.keys(t.getBindingIdentifiers(declaration.node.id));
|
|
143
|
+
if (names.some(id => program.scope.getBinding(id)?.referenced)) continue;
|
|
144
|
+
declaration.remove();
|
|
145
|
+
} else if (declaration.isFunctionDeclaration() || declaration.isClassDeclaration()) declaration.remove();
|
|
146
|
+
else continue;
|
|
147
|
+
changed = true;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// A star export cannot be safely classified without evaluating another module.
|
|
151
|
+
if (program.get('body').some(statement => statement.isExportAllDeclaration())) {
|
|
152
|
+
throw new Error(`Wildcard re-exports in a page are not supported: ${filename}. Explicitly export the page and its data loaders so server code can be excluded from the browser.`);
|
|
153
|
+
}
|
|
154
|
+
return generateMapped(generate, ast, { comments: true }, source, filename);
|
|
155
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import picomatch from 'picomatch';
|
|
3
|
+
import {readFile} from 'node:fs/promises';
|
|
4
|
+
import {fileURLToPath} from 'node:url';
|
|
5
|
+
|
|
6
|
+
const builtins = new Set(['browser','foreign','development','production','node','edge-light']);
|
|
7
|
+
const frameworkRoot=fileURLToPath(new URL('../',import.meta.url));
|
|
8
|
+
export function validateCondition(value, depth = 0, budget = {remaining:256}) {
|
|
9
|
+
if (depth > 32 || --budget.remaining < 0) throw new TypeError('Turbopack condition is too complex');
|
|
10
|
+
if (typeof value === 'string' && builtins.has(value)) return;
|
|
11
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || value instanceof RegExp) throw new TypeError('Invalid Turbopack condition');
|
|
12
|
+
const keys = Object.keys(value);
|
|
13
|
+
if (keys.length === 1 && ['all','any','not'].includes(keys[0])) {
|
|
14
|
+
const key = keys[0];
|
|
15
|
+
if (key === 'not') return validateCondition(value.not,depth+1,budget);
|
|
16
|
+
if (!Array.isArray(value[key])) throw new TypeError(`Turbopack condition ${key} must be an array`);
|
|
17
|
+
for (const child of value[key]) validateCondition(child,depth+1,budget);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
if (!keys.length || keys.some(key=>!['path','content'].includes(key)) ||
|
|
21
|
+
('path' in value && !(value.path instanceof RegExp) && typeof value.path !== 'string') ||
|
|
22
|
+
('content' in value && !(value.content instanceof RegExp))) throw new TypeError('Invalid Turbopack path/content condition');
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const regex = (pattern,value) => {pattern.lastIndex=0;return pattern.test(value);};
|
|
26
|
+
const glob = pattern => {const match=picomatch(pattern);return file=>match(pattern.includes('/')?file:path.posix.basename(file));};
|
|
27
|
+
function compileCondition(value, state, target) {
|
|
28
|
+
if (typeof value === 'string') {
|
|
29
|
+
if (value === 'foreign') return async file=>/(^|\/)node_modules\//.test(file) || path.resolve(state.root,file).startsWith(frameworkRoot);
|
|
30
|
+
const enabled={browser:target==='browser',node:target==='node','edge-light':target==='edge',development:!!state.dev,production:!state.dev}[value];
|
|
31
|
+
return async()=>enabled;
|
|
32
|
+
}
|
|
33
|
+
if (value.not) {const child=compileCondition(value.not,state,target);return async(file,read)=>!await child(file,read);}
|
|
34
|
+
if (value.all || value.any) {
|
|
35
|
+
const all=!!value.all, children=(value.all || value.any).map(child=>compileCondition(child,state,target));
|
|
36
|
+
return async(file,read)=>{for(const child of children)if(await child(file,read)!==all)return !all;return all;};
|
|
37
|
+
}
|
|
38
|
+
const match=value.path instanceof RegExp?file=>regex(value.path,file):typeof value.path==='string'?glob(value.path):()=>true;
|
|
39
|
+
return async(file,read)=>match(file) && (!value.content || regex(value.content,await read()));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Conditions read source only when a content predicate is actually reached.
|
|
43
|
+
// No file contents survive selection, and boolean operators short-circuit.
|
|
44
|
+
export function turbopackRules(rules, state, target) {
|
|
45
|
+
const root = state.config?.turbopack?.root || state.root;
|
|
46
|
+
const conditionState = {...state,root};
|
|
47
|
+
return Object.entries(rules || {}).flatMap(([pattern,entries])=>{
|
|
48
|
+
const match=glob(pattern);
|
|
49
|
+
return (Array.isArray(entries)?entries:[entries]).map(rule=>{
|
|
50
|
+
const predicate=rule.condition===undefined?async()=>true:compileCondition(rule.condition,conditionState,target);
|
|
51
|
+
return {test:file=>match(path.relative(root,file).replaceAll(path.sep,'/')),use:rule.loaders,
|
|
52
|
+
prnextCondition:async(file,read)=>predicate(path.relative(root,file).replaceAll(path.sep,'/'),read)};
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
export function sourceReader(file) {
|
|
57
|
+
let source;
|
|
58
|
+
return ()=>source ||= readFile(file,'utf8');
|
|
59
|
+
}
|