vinext 1.0.0-beta.2 → 1.0.0-beta.3
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 +6 -9
- package/dist/build/client-build-config.js +7 -1
- package/dist/check.js +4 -0
- package/dist/config/config-matchers.js +4 -1
- package/dist/config/next-config.d.ts +12 -3
- package/dist/config/next-config.js +4 -0
- package/dist/entries/app-browser-entry.d.ts +4 -10
- package/dist/entries/app-browser-entry.js +20 -4
- package/dist/entries/app-rsc-entry.js +3 -0
- package/dist/entries/app-rsc-manifest.js +19 -0
- package/dist/entries/pages-client-entry.js +0 -1
- package/dist/entries/pages-server-entry.js +10 -3
- package/dist/index.js +112 -43
- package/dist/init-cloudflare.d.ts +3 -1
- package/dist/init-cloudflare.js +59 -5
- package/dist/plugins/extensionless-dynamic-import.js +23 -18
- package/dist/plugins/ignore-dynamic-requests.js +3 -1
- package/dist/plugins/import-meta-url.js +17 -6
- package/dist/plugins/require-context.js +20 -15
- package/dist/plugins/transform-cache.d.ts +19 -0
- package/dist/plugins/transform-cache.js +36 -0
- package/dist/routing/app-route-graph.d.ts +14 -6
- package/dist/routing/app-route-graph.js +106 -16
- package/dist/server/api-handler.d.ts +2 -0
- package/dist/server/api-handler.js +3 -3
- package/dist/server/app-browser-entry.js +103 -31
- package/dist/server/app-browser-navigation-controller.d.ts +23 -21
- package/dist/server/app-browser-navigation-controller.js +1 -1
- package/dist/server/app-page-cache-finalizer.js +1 -1
- package/dist/server/app-page-cache.d.ts +1 -1
- package/dist/server/app-page-cache.js +16 -2
- package/dist/server/app-page-dispatch.d.ts +8 -0
- package/dist/server/app-page-dispatch.js +31 -6
- package/dist/server/app-page-element-builder.d.ts +2 -0
- package/dist/server/app-page-element-builder.js +33 -7
- package/dist/server/app-page-head.d.ts +3 -3
- package/dist/server/app-page-head.js +58 -13
- package/dist/server/app-page-http-access-fallback-metadata.d.ts +2 -2
- package/dist/server/app-page-http-access-fallback-metadata.js +9 -1
- package/dist/server/app-page-probe.d.ts +4 -0
- package/dist/server/app-page-probe.js +6 -2
- package/dist/server/app-page-request.d.ts +3 -0
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-response.js +1 -1
- package/dist/server/app-page-route-wiring.d.ts +7 -0
- package/dist/server/app-page-route-wiring.js +211 -49
- package/dist/server/app-pages-bridge.d.ts +1 -1
- package/dist/server/app-pages-bridge.js +2 -1
- package/dist/server/app-route-handler-cache.js +4 -0
- package/dist/server/app-route-handler-dispatch.js +9 -3
- package/dist/server/app-route-handler-response.js +1 -1
- package/dist/server/app-route-module-loader.d.ts +6 -0
- package/dist/server/app-route-module-loader.js +3 -0
- package/dist/server/app-router-entry.js +6 -3
- package/dist/server/app-rsc-errors.js +10 -0
- package/dist/server/app-rsc-handler.js +21 -9
- package/dist/server/app-rsc-route-matching.d.ts +9 -0
- package/dist/server/app-rsc-route-matching.js +6 -0
- package/dist/server/app-ssr-entry.js +4 -4
- package/dist/server/cache-control.d.ts +2 -2
- package/dist/server/cache-control.js +2 -1
- package/dist/server/dev-server.d.ts +3 -1
- package/dist/server/dev-server.js +145 -312
- package/dist/server/headers.d.ts +2 -2
- package/dist/server/headers.js +4 -3
- package/dist/server/isr-cache.d.ts +10 -33
- package/dist/server/isr-cache.js +33 -43
- package/dist/server/isr-decision.d.ts +2 -2
- package/dist/server/pages-api-route.d.ts +2 -0
- package/dist/server/pages-api-route.js +4 -1
- package/dist/server/pages-dev-hydration.js +0 -1
- package/dist/server/pages-get-initial-props.d.ts +1 -1
- package/dist/server/pages-get-initial-props.js +1 -4
- package/dist/server/pages-i18n.js +64 -16
- package/dist/server/pages-node-compat.d.ts +2 -0
- package/dist/server/pages-node-compat.js +7 -3
- package/dist/server/pages-page-data.d.ts +61 -9
- package/dist/server/pages-page-data.js +334 -74
- package/dist/server/pages-page-handler.d.ts +5 -1
- package/dist/server/pages-page-handler.js +115 -19
- package/dist/server/pages-page-response.d.ts +5 -3
- package/dist/server/pages-page-response.js +23 -26
- package/dist/server/pages-request-pipeline.d.ts +7 -0
- package/dist/server/pages-request-pipeline.js +5 -1
- package/dist/server/pages-revalidate.d.ts +1 -1
- package/dist/server/pages-revalidate.js +54 -8
- package/dist/server/pages-router-entry.d.ts +1 -0
- package/dist/server/pages-router-entry.js +10 -4
- package/dist/server/prod-server.d.ts +2 -1
- package/dist/server/prod-server.js +37 -13
- package/dist/server/revalidation-host.d.ts +11 -0
- package/dist/server/revalidation-host.js +19 -0
- package/dist/server/worker-revalidation-context.d.ts +13 -0
- package/dist/server/worker-revalidation-context.js +29 -0
- package/dist/shims/cache-handler.d.ts +1 -1
- package/dist/shims/cache-handler.js +10 -7
- package/dist/shims/cache-runtime.d.ts +6 -0
- package/dist/shims/cache-runtime.js +4 -1
- package/dist/shims/cache.js +1 -1
- package/dist/shims/error.js +1 -1
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-google-base.js +17 -8
- package/dist/shims/font-local.js +41 -10
- package/dist/shims/headers.d.ts +10 -13
- package/dist/shims/headers.js +85 -36
- package/dist/shims/internal/cookie-serialize.d.ts +10 -9
- package/dist/shims/internal/cookie-serialize.js +9 -8
- package/dist/shims/internal/interpolate-as.d.ts +15 -1
- package/dist/shims/internal/interpolate-as.js +27 -2
- package/dist/shims/link.js +18 -6
- package/dist/shims/metadata.d.ts +7 -17
- package/dist/shims/metadata.js +74 -24
- package/dist/shims/navigation.js +1 -1
- package/dist/shims/request-context.d.ts +4 -1
- package/dist/shims/router.js +18 -7
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.d.ts +7 -6
- package/dist/shims/server.js +156 -57
- package/dist/shims/unified-request-context.d.ts +21 -2
- package/dist/shims/unified-request-context.js +82 -1
- package/dist/utils/cache-control-metadata.d.ts +2 -1
- package/dist/utils/cache-control-metadata.js +5 -1
- package/dist/utils/html-limited-bots.js +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -35,7 +35,7 @@ import { generateRscEntry } from "./entries/app-rsc-entry.js";
|
|
|
35
35
|
import { generateSsrEntry } from "./entries/app-ssr-entry.js";
|
|
36
36
|
import { VINEXT_CACHE_CONFIG_PLUGIN_PROPERTY, VIRTUAL_CACHE_ADAPTERS, generateCacheAdaptersModule } from "./cache/cache-adapters-virtual.js";
|
|
37
37
|
import { VIRTUAL_IMAGE_ADAPTERS, generateImageAdaptersModule } from "./image/image-adapters-virtual.js";
|
|
38
|
-
import { generateBrowserEntry,
|
|
38
|
+
import { generateBrowserEntry, toLinkPrefetchRoutes } from "./entries/app-browser-entry.js";
|
|
39
39
|
import { collectRouteClassificationManifest } from "./build/route-classification-manifest.js";
|
|
40
40
|
import { planRouteClassificationInjection } from "./build/route-classification-injector.js";
|
|
41
41
|
import { PHASE_DEVELOPMENT_SERVER, PHASE_PRODUCTION_BUILD } from "./shims/constants.js";
|
|
@@ -46,6 +46,7 @@ import { precompressAssets } from "./build/precompress.js";
|
|
|
46
46
|
import { ensureAssetsIgnore } from "./build/assets-ignore.js";
|
|
47
47
|
import { emitNextClientRuntimeManifests } from "./build/next-client-runtime-manifests.js";
|
|
48
48
|
import { collectInlineCssManifest, injectInlineCssManifestGlobal } from "./build/inline-css.js";
|
|
49
|
+
import { readTrustedRevalidationHostname } from "./server/revalidation-host.js";
|
|
49
50
|
import { installDevStackSourcemapMiddleware } from "./server/dev-stack-sourcemap.js";
|
|
50
51
|
import { runPagesRequest } from "./server/pages-request-pipeline.js";
|
|
51
52
|
import { pagesRouteHasPriorityOverAppRoute, validateHybridRouteConflicts } from "./server/hybrid-route-priority.js";
|
|
@@ -83,6 +84,7 @@ import { createClientAssetFileNames, createClientCodeSplittingConfig, createClie
|
|
|
83
84
|
import { hasExportAllCandidate, stripServerExports, validatePageExports } from "./plugins/strip-server-exports.js";
|
|
84
85
|
import { removeConsoleCalls } from "./plugins/remove-console.js";
|
|
85
86
|
import { createImportMetaUrlPlugin } from "./plugins/import-meta-url.js";
|
|
87
|
+
import { createTransformCache } from "./plugins/transform-cache.js";
|
|
86
88
|
import { createRequireContextPlugin } from "./plugins/require-context.js";
|
|
87
89
|
import { createExtensionlessDynamicImportPlugin } from "./plugins/extensionless-dynamic-import.js";
|
|
88
90
|
import { createWasmModuleImportPlugin } from "./plugins/wasm-module-import.js";
|
|
@@ -114,6 +116,63 @@ const OPTIONAL_OPTIMIZE_DEPS_WARNING_RE = /Failed to resolve dependency: .*use-s
|
|
|
114
116
|
const VINEXT_FILTERED_OPTIMIZE_DEPS_WARN = Symbol.for("vinext.filteredOptimizeDepsWarn");
|
|
115
117
|
const ANSI_ESCAPE_RE = new RegExp(`${String.fromCharCode(27)}\\[[0-9;]*m`, "g");
|
|
116
118
|
installSocketErrorBackstop();
|
|
119
|
+
function isUseCacheAstNode(value) {
|
|
120
|
+
return typeof value === "object" && value !== null && typeof Reflect.get(value, "type") === "string";
|
|
121
|
+
}
|
|
122
|
+
function hasInlineUseCacheDirective(node) {
|
|
123
|
+
const body = isUseCacheAstNode(node.body) && node.body.type === "BlockStatement" ? node.body : null;
|
|
124
|
+
if (!body || !Array.isArray(body.body)) return false;
|
|
125
|
+
return body.body.some((statement) => {
|
|
126
|
+
if (!isUseCacheAstNode(statement) || statement.type !== "ExpressionStatement") return false;
|
|
127
|
+
const expression = isUseCacheAstNode(statement.expression) ? statement.expression : null;
|
|
128
|
+
return expression?.type === "Literal" && typeof expression.value === "string" && /^use cache(:\s*\w+)?$/.test(expression.value);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
function functionAcceptsSecondArgument(node) {
|
|
132
|
+
const params = Array.isArray(node.params) ? node.params : [];
|
|
133
|
+
if (params.length >= 2) return true;
|
|
134
|
+
return params.some((param) => isUseCacheAstNode(param) && param.type === "RestElement");
|
|
135
|
+
}
|
|
136
|
+
function collectInlineUseCacheSecondArgumentUsage(value) {
|
|
137
|
+
const usage = [];
|
|
138
|
+
function walk(node) {
|
|
139
|
+
if (Array.isArray(node)) {
|
|
140
|
+
for (const child of node) walk(child);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (!isUseCacheAstNode(node)) return;
|
|
144
|
+
if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression") && hasInlineUseCacheDirective(node)) usage.push(functionAcceptsSecondArgument(node));
|
|
145
|
+
for (const [key, child] of Object.entries(node)) {
|
|
146
|
+
if (key === "type" || key === "start" || key === "end" || key === "loc" || key === "parent") continue;
|
|
147
|
+
walk(child);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
walk(value);
|
|
151
|
+
return usage;
|
|
152
|
+
}
|
|
153
|
+
function collectFileUseCacheSecondArgumentUsage(body) {
|
|
154
|
+
const localFunctions = /* @__PURE__ */ new Map();
|
|
155
|
+
function collectDeclaration(value) {
|
|
156
|
+
if (!isUseCacheAstNode(value)) return;
|
|
157
|
+
if (value.type === "FunctionDeclaration") {
|
|
158
|
+
const id = isUseCacheAstNode(value.id) ? value.id : null;
|
|
159
|
+
if (id?.type === "Identifier" && typeof id.name === "string") localFunctions.set(id.name, functionAcceptsSecondArgument(value));
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
if (value.type !== "VariableDeclaration" || !Array.isArray(value.declarations)) return;
|
|
163
|
+
for (const declaration of value.declarations) {
|
|
164
|
+
if (!isUseCacheAstNode(declaration) || declaration.type !== "VariableDeclarator") continue;
|
|
165
|
+
const id = isUseCacheAstNode(declaration.id) ? declaration.id : null;
|
|
166
|
+
const init = isUseCacheAstNode(declaration.init) ? declaration.init : null;
|
|
167
|
+
if (id?.type === "Identifier" && typeof id.name === "string" && init && (init.type === "FunctionExpression" || init.type === "ArrowFunctionExpression")) localFunctions.set(id.name, functionAcceptsSecondArgument(init));
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
for (const statement of body) {
|
|
171
|
+
if (!isUseCacheAstNode(statement)) continue;
|
|
172
|
+
collectDeclaration(statement.type === "ExportNamedDeclaration" || statement.type === "ExportDefaultDeclaration" ? statement.declaration : statement);
|
|
173
|
+
}
|
|
174
|
+
return localFunctions;
|
|
175
|
+
}
|
|
117
176
|
function isInsideDirectory(dir, filePath) {
|
|
118
177
|
const relativePath = path.relative(dir, filePath);
|
|
119
178
|
return relativePath !== "" && !relativePath.startsWith("..") && !path.isAbsolute(relativePath);
|
|
@@ -475,16 +534,17 @@ const _tsconfigAliasCache = /* @__PURE__ */ new Map();
|
|
|
475
534
|
function sortTsconfigAliasesBySpecificity(aliases) {
|
|
476
535
|
return Object.fromEntries(Object.entries(aliases).sort((a, b) => b[0].length - a[0].length));
|
|
477
536
|
}
|
|
478
|
-
function resolveTsconfigAliases(projectRoot) {
|
|
479
|
-
|
|
537
|
+
function resolveTsconfigAliases(projectRoot, configuredPath) {
|
|
538
|
+
const configPath = configuredPath ? path.resolve(projectRoot, configuredPath) : void 0;
|
|
539
|
+
const cacheKey = configPath ?? projectRoot;
|
|
540
|
+
if (_tsconfigAliasCache.has(cacheKey)) return _tsconfigAliasCache.get(cacheKey);
|
|
480
541
|
let aliases = {};
|
|
481
|
-
for (const
|
|
482
|
-
const candidate = path.join(projectRoot, name);
|
|
542
|
+
for (const candidate of configPath ? [configPath] : TSCONFIG_FILES.map((name) => path.join(projectRoot, name))) {
|
|
483
543
|
if (!fs.existsSync(candidate)) continue;
|
|
484
544
|
aliases = sortTsconfigAliasesBySpecificity(loadTsconfigPathAliases(candidate, projectRoot));
|
|
485
545
|
break;
|
|
486
546
|
}
|
|
487
|
-
_tsconfigAliasCache.set(
|
|
547
|
+
_tsconfigAliasCache.set(cacheKey, aliases);
|
|
488
548
|
return aliases;
|
|
489
549
|
}
|
|
490
550
|
/**
|
|
@@ -755,7 +815,7 @@ function vinext(options = {}) {
|
|
|
755
815
|
* __NEXT_DATA__ to determine which page to hydrate.
|
|
756
816
|
*/
|
|
757
817
|
async function generateClientEntry$1() {
|
|
758
|
-
const appPrefetchRoutes = hasAppDir ? (await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher))
|
|
818
|
+
const appPrefetchRoutes = hasAppDir ? toLinkPrefetchRoutes(await appRouter(appDir, nextConfig?.pageExtensions, fileMatcher)) : [];
|
|
759
819
|
return generateClientEntry(pagesDir, nextConfig, fileMatcher, {
|
|
760
820
|
appPrefetchRoutes,
|
|
761
821
|
instrumentationClientPath,
|
|
@@ -895,6 +955,7 @@ function vinext(options = {}) {
|
|
|
895
955
|
return (typeof hook === "function" ? hook : hook.handler).call(this, config, env);
|
|
896
956
|
}
|
|
897
957
|
};
|
|
958
|
+
const cachedTypeofWindowTransform = createTransformCache();
|
|
898
959
|
const plugins = [
|
|
899
960
|
createStyledJsxPlugin(earlyBaseDir),
|
|
900
961
|
mdxProxyPlugin,
|
|
@@ -967,10 +1028,9 @@ function vinext(options = {}) {
|
|
|
967
1028
|
async config(config, env) {
|
|
968
1029
|
isServeCommand = env.command === "serve";
|
|
969
1030
|
root = toSlash(config.root ?? process.cwd());
|
|
970
|
-
const
|
|
971
|
-
|
|
1031
|
+
const userResolve = config.resolve;
|
|
1032
|
+
let tsconfigPathAliases = {};
|
|
972
1033
|
const swcHelpersAlias = resolveSwcHelpersAlias(root);
|
|
973
|
-
if (Object.keys(tsconfigPathAliases).length > 0) config.customLogger = suppressAliasCustomResolverDeprecationWarning(config.customLogger ?? createLogger(config.logLevel, { allowClearScreen: config.clearScreen }));
|
|
974
1034
|
const mode = env?.mode ?? "development";
|
|
975
1035
|
const dotenvVars = loadEnv(mode, config.envDir ?? root, "");
|
|
976
1036
|
for (const [key, value] of Object.entries(dotenvVars)) if (process.env[key] === void 0) process.env[key] = value;
|
|
@@ -1017,6 +1077,10 @@ function vinext(options = {}) {
|
|
|
1017
1077
|
buildId: sharedBuildId
|
|
1018
1078
|
};
|
|
1019
1079
|
}
|
|
1080
|
+
const configuredTsconfigPath = isUnknownRecord(nextConfig.typescript) ? typeof nextConfig.typescript.tsconfigPath === "string" ? nextConfig.typescript.tsconfigPath : void 0 : void 0;
|
|
1081
|
+
tsconfigPathAliases = resolveTsconfigAliases(root, configuredTsconfigPath);
|
|
1082
|
+
const shouldAutoEnableNativeTsconfigPaths = userResolve?.tsconfigPaths === void 0 && configuredTsconfigPath === void 0;
|
|
1083
|
+
if (Object.keys(tsconfigPathAliases).length > 0) config.customLogger = suppressAliasCustomResolverDeprecationWarning(config.customLogger ?? createLogger(config.logLevel, { allowClearScreen: config.clearScreen }));
|
|
1020
1084
|
if (rscCompatibilityId === void 0) {
|
|
1021
1085
|
const sharedRscCompatibilityId = process.env.__VINEXT_SHARED_RSC_COMPATIBILITY_ID;
|
|
1022
1086
|
rscCompatibilityId = sharedRscCompatibilityId && sharedRscCompatibilityId.length > 0 ? sharedRscCompatibilityId : createRscCompatibilityId(nextConfig);
|
|
@@ -1222,7 +1286,7 @@ function vinext(options = {}) {
|
|
|
1222
1286
|
"react/jsx-runtime",
|
|
1223
1287
|
"react/jsx-dev-runtime"
|
|
1224
1288
|
],
|
|
1225
|
-
...
|
|
1289
|
+
...shouldAutoEnableNativeTsconfigPaths ? { tsconfigPaths: true } : {}
|
|
1226
1290
|
},
|
|
1227
1291
|
oxc: {
|
|
1228
1292
|
jsx: { runtime: "automatic" },
|
|
@@ -1840,6 +1904,14 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
1840
1904
|
});
|
|
1841
1905
|
const pageExtensions = fileMatcher.extensionRegex;
|
|
1842
1906
|
let pagesRunner = null;
|
|
1907
|
+
const getTrustedDevRevalidateOrigin = () => {
|
|
1908
|
+
const resolved = server.resolvedUrls?.local[0];
|
|
1909
|
+
if (resolved) try {
|
|
1910
|
+
return new URL(resolved).origin;
|
|
1911
|
+
} catch {}
|
|
1912
|
+
const address = server.httpServer?.address();
|
|
1913
|
+
return `http://localhost:${typeof address === "object" && address ? address.port : server.config.server.port ?? 3e3}`;
|
|
1914
|
+
};
|
|
1843
1915
|
let cachedSSRHandler = null;
|
|
1844
1916
|
function getPagesRunner() {
|
|
1845
1917
|
if (!pagesRunner) pagesRunner = createDirectRunner(server.environments["ssr"] ?? Object.values(server.environments).find((e) => e !== server.environments["rsc"]) ?? Object.values(server.environments)[0]);
|
|
@@ -2161,7 +2233,16 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2161
2233
|
res.end("Forbidden");
|
|
2162
2234
|
return;
|
|
2163
2235
|
}
|
|
2164
|
-
|
|
2236
|
+
let revalidationHostname = null;
|
|
2237
|
+
if (req.headers["x-vinext-revalidate-host"] !== void 0) {
|
|
2238
|
+
const revalidationHeaders = new Headers();
|
|
2239
|
+
for (const [name, value] of Object.entries(req.headers)) {
|
|
2240
|
+
if (value === void 0 || name.startsWith(":")) continue;
|
|
2241
|
+
revalidationHeaders.set(name, Array.isArray(value) ? value.join(", ") : value);
|
|
2242
|
+
}
|
|
2243
|
+
revalidationHostname = readTrustedRevalidationHostname(revalidationHeaders, nextConfig?.i18n);
|
|
2244
|
+
}
|
|
2245
|
+
const requestHost = revalidationHostname ?? ((Array.isArray(req.headers.host) ? req.headers.host[0] : req.headers.host) || "localhost");
|
|
2165
2246
|
const requestOrigin = `http://${requestHost}`;
|
|
2166
2247
|
const getUrlHostname = (requestUrl) => new URL(requestUrl).hostname;
|
|
2167
2248
|
if (isImageOptimizationPath(url.split("?")[0])) {
|
|
@@ -2296,6 +2377,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2296
2377
|
const rawHeaders = new Headers(Object.fromEntries(Object.entries(req.headers).filter(([k, v]) => v !== void 0 && !k.startsWith(":")).map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])));
|
|
2297
2378
|
const isDataRequest = isDataReq;
|
|
2298
2379
|
const nodeRequestHeaders = filterInternalHeaders(rawHeaders);
|
|
2380
|
+
if (revalidationHostname) nodeRequestHeaders.set("host", revalidationHostname);
|
|
2299
2381
|
for (const header of INTERNAL_HEADERS) delete req.headers[header];
|
|
2300
2382
|
for (const header of VINEXT_INTERNAL_HEADERS) delete req.headers[header];
|
|
2301
2383
|
const method = req.method ?? "GET";
|
|
@@ -2416,6 +2498,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2416
2498
|
if (await handleApiRoute(getPagesRunner(), req, res, pipelineResult.apiUrl, apiRoutes, {
|
|
2417
2499
|
basePath: nextConfig?.basePath,
|
|
2418
2500
|
i18n: nextConfig?.i18n,
|
|
2501
|
+
trustedRevalidateOrigin: getTrustedDevRevalidateOrigin(),
|
|
2502
|
+
allowedRevalidateHeaderKeys: nextConfig?.allowedRevalidateHeaderKeys,
|
|
2419
2503
|
trailingSlash: nextConfig?.trailingSlash
|
|
2420
2504
|
})) return;
|
|
2421
2505
|
if (hasAppDir) return next();
|
|
@@ -2434,7 +2518,7 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2434
2518
|
}
|
|
2435
2519
|
if (!cachedSSRHandler || cachedSSRHandler.routes !== routes) cachedSSRHandler = {
|
|
2436
2520
|
routes,
|
|
2437
|
-
handler: createSSRHandler(server, getPagesRunner(), routes, pagesDir, nextConfig?.i18n, fileMatcher, nextConfig?.basePath ?? "", nextConfig?.trailingSlash ?? false, middlewarePath !== null, (nextConfig?.rewrites.beforeFiles.length ?? 0) > 0 || (nextConfig?.rewrites.afterFiles.length ?? 0) > 0 || (nextConfig?.rewrites.fallback.length ?? 0) > 0, nextConfig?.clientTraceMetadata, nextConfig?.htmlLimitedBots, nextConfig?.reactStrictMode === true)
|
|
2521
|
+
handler: createSSRHandler(server, getPagesRunner(), routes, pagesDir, nextConfig?.i18n, fileMatcher, nextConfig?.basePath ?? "", nextConfig?.trailingSlash ?? false, middlewarePath !== null, (nextConfig?.rewrites.beforeFiles.length ?? 0) > 0 || (nextConfig?.rewrites.afterFiles.length ?? 0) > 0 || (nextConfig?.rewrites.fallback.length ?? 0) > 0, nextConfig?.clientTraceMetadata, nextConfig?.htmlLimitedBots, nextConfig?.reactStrictMode === true, nextConfig?.expireTime)
|
|
2438
2522
|
};
|
|
2439
2523
|
flushStagedHeaders();
|
|
2440
2524
|
flushRequestHeaders();
|
|
@@ -2561,7 +2645,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2561
2645
|
if (useNativeTypeofWindowFolding && this.environment.config.build.write !== false) return null;
|
|
2562
2646
|
const cacheDir = `${toSlash(this.environment.config.cacheDir).replace(/\/$/, "")}/`;
|
|
2563
2647
|
if (toSlash(id).startsWith(cacheDir)) return null;
|
|
2564
|
-
|
|
2648
|
+
const replacement = getTypeofWindowReplacement(this.environment);
|
|
2649
|
+
return cachedTypeofWindowTransform(id, code, replacement, () => replaceTypeofWindow(code, replacement, id));
|
|
2565
2650
|
}
|
|
2566
2651
|
}
|
|
2567
2652
|
},
|
|
@@ -2740,32 +2825,8 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2740
2825
|
async handler(code, id) {
|
|
2741
2826
|
const ast = parseAst(code);
|
|
2742
2827
|
const cacheDirective = ast.body.find((node) => node.type === "ExpressionStatement" && node.expression?.type === "Literal" && typeof node.expression.value === "string" && node.expression.value.startsWith("use cache"));
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
const fn = node.type === "MethodDefinition" ? node.value : node;
|
|
2746
|
-
const stmts = fn?.body?.type === "BlockStatement" ? fn.body.body : null;
|
|
2747
|
-
if (Array.isArray(stmts)) {
|
|
2748
|
-
for (const stmt of stmts) if (stmt?.type === "ExpressionStatement" && stmt.expression?.type === "Literal" && typeof stmt.expression?.value === "string" && /^use cache(:\s*\w+)?$/.test(stmt.expression.value)) return true;
|
|
2749
|
-
}
|
|
2750
|
-
return false;
|
|
2751
|
-
}
|
|
2752
|
-
function astHasInlineCache(nodes) {
|
|
2753
|
-
for (const node of nodes) {
|
|
2754
|
-
if (!node || typeof node !== "object") continue;
|
|
2755
|
-
if ((node.type === "FunctionDeclaration" || node.type === "FunctionExpression" || node.type === "ArrowFunctionExpression" || node.type === "MethodDefinition") && nodeHasInlineCacheDirective(node)) return true;
|
|
2756
|
-
for (const key of Object.keys(node)) {
|
|
2757
|
-
if (key === "type" || key === "start" || key === "end" || key === "loc") continue;
|
|
2758
|
-
const child = node[key];
|
|
2759
|
-
if (Array.isArray(child) && child.some((c) => c && typeof c === "object")) {
|
|
2760
|
-
if (astHasInlineCache(child)) return true;
|
|
2761
|
-
} else if (child && typeof child === "object" && child.type) {
|
|
2762
|
-
if (astHasInlineCache([child])) return true;
|
|
2763
|
-
}
|
|
2764
|
-
}
|
|
2765
|
-
}
|
|
2766
|
-
return false;
|
|
2767
|
-
}
|
|
2768
|
-
const hasInlineCache = !cacheDirective && astHasInlineCache(ast.body);
|
|
2828
|
+
const inlineCacheSecondArgumentUsage = cacheDirective ? [] : collectInlineUseCacheSecondArgumentUsage(ast.body);
|
|
2829
|
+
const hasInlineCache = inlineCacheSecondArgumentUsage.length > 0;
|
|
2769
2830
|
if (!cacheDirective && !hasInlineCache) return null;
|
|
2770
2831
|
if (!resolvedRscTransformsPath) throw new Error("vinext: 'use cache' requires @vitejs/plugin-rsc to be installed.\nRun: " + detectPackageManager(process.cwd()) + " @vitejs/plugin-rsc");
|
|
2771
2832
|
const { transformWrapExport, transformHoistInlineDirective } = await import(pathToFileURL(resolvedRscTransformsPath).href);
|
|
@@ -2777,10 +2838,15 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2777
2838
|
const moduleFileName = path.basename(modulePath);
|
|
2778
2839
|
const isAppPageModule = hasAppDir && isInsideDirectory(appDir, modulePath) && path.parse(moduleFileName).name === "page" && fileMatcher.extensionRegex.test(moduleFileName);
|
|
2779
2840
|
const runtimeModuleUrl = pathToFileURL(resolveShimModulePath(shimsDir, "cache-runtime")).href;
|
|
2841
|
+
const secondArgumentUsage = collectFileUseCacheSecondArgumentUsage(ast.body);
|
|
2780
2842
|
const result = transformWrapExport(code, ast, {
|
|
2781
2843
|
runtime: (value, name) => {
|
|
2782
|
-
const
|
|
2783
|
-
|
|
2844
|
+
const runtimeOptions = [];
|
|
2845
|
+
const acceptsSecondArgument = secondArgumentUsage.get(value) ?? true;
|
|
2846
|
+
runtimeOptions.push(`acceptsSecondArgument: ${acceptsSecondArgument}`);
|
|
2847
|
+
if (name === "default" && isAppPageModule) runtimeOptions.push("appPageDefaultExport: true");
|
|
2848
|
+
const optionsArgument = runtimeOptions.length > 0 ? `, { ${runtimeOptions.join(", ")} }` : "";
|
|
2849
|
+
return `(await import(${JSON.stringify(runtimeModuleUrl)})).registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)}${optionsArgument})`;
|
|
2784
2850
|
},
|
|
2785
2851
|
rejectNonAsyncFunction: false,
|
|
2786
2852
|
filter: (name, meta) => {
|
|
@@ -2807,12 +2873,15 @@ export const loadServerActionClient = ${hasServerActions ? `() => import(${JSON.
|
|
|
2807
2873
|
if (hasInlineCache) {
|
|
2808
2874
|
const runtimeModuleUrl2 = pathToFileURL(resolveShimModulePath(shimsDir, "cache-runtime")).href;
|
|
2809
2875
|
try {
|
|
2876
|
+
let transformedFunctionIndex = 0;
|
|
2810
2877
|
const result = transformHoistInlineDirective(code, ast, {
|
|
2811
2878
|
directive: /^use cache(:\s*\w+)?$/,
|
|
2812
2879
|
runtime: (value, name, meta) => {
|
|
2813
2880
|
const directiveMatch = meta.directiveMatch[0];
|
|
2814
2881
|
const variant = directiveMatch === "use cache" ? "" : directiveMatch.replace("use cache:", "").trim();
|
|
2815
|
-
|
|
2882
|
+
const acceptsSecondArgument = inlineCacheSecondArgumentUsage[transformedFunctionIndex++];
|
|
2883
|
+
const optionsArgument = acceptsSecondArgument === void 0 ? "" : `, { acceptsSecondArgument: ${acceptsSecondArgument} }`;
|
|
2884
|
+
return `(await import(${JSON.stringify(runtimeModuleUrl2)})).registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)}${optionsArgument})`;
|
|
2816
2885
|
},
|
|
2817
2886
|
rejectNonAsyncFunction: false
|
|
2818
2887
|
});
|
|
@@ -25,7 +25,9 @@ type CloudflarePlatformSetupResult = {
|
|
|
25
25
|
declare function validateCloudflarePlatformSetup(context: CloudflarePlatformSetupContext, cloudflare: CloudflareInitOptions): void;
|
|
26
26
|
declare function setupCloudflarePlatform(context: CloudflarePlatformSetupContext, cloudflare: CloudflareInitOptions): CloudflarePlatformSetupResult;
|
|
27
27
|
declare function generateWranglerConfig(info: CloudflareProjectInfo, options?: CloudflareInitOptions, today?: string): string;
|
|
28
|
-
declare function updateWranglerConfigForCloudflare(code: string, options: CloudflareInitOptions
|
|
28
|
+
declare function updateWranglerConfigForCloudflare(code: string, options: CloudflareInitOptions, context?: {
|
|
29
|
+
root?: string;
|
|
30
|
+
}): string;
|
|
29
31
|
declare function getWranglerImagesBinding(code: string): string;
|
|
30
32
|
/** Generate vite.config.ts for App Router */
|
|
31
33
|
declare function generateAppRouterViteConfig(info?: CloudflareProjectInfo, options?: CloudflareInitOptions, imagesBinding?: string, prerender?: boolean): string;
|
package/dist/init-cloudflare.js
CHANGED
|
@@ -16,7 +16,7 @@ function validateCloudflarePlatformSetup(context, cloudflare) {
|
|
|
16
16
|
const projectInfo = detectProject(context.root);
|
|
17
17
|
const wranglerPath = ["wrangler.jsonc", "wrangler.json"].map((fileName) => path.join(context.root, fileName)).find((candidate) => fs.existsSync(candidate));
|
|
18
18
|
const wranglerCode = wranglerPath ? fs.readFileSync(wranglerPath, "utf-8") : void 0;
|
|
19
|
-
const updatedWranglerCode = wranglerCode ? updateWranglerConfigForCloudflare(wranglerCode, cloudflare) : void 0;
|
|
19
|
+
const updatedWranglerCode = wranglerCode ? updateWranglerConfigForCloudflare(wranglerCode, cloudflare, { root: context.root }) : void 0;
|
|
20
20
|
const imagesBinding = updatedWranglerCode ? getWranglerImagesBinding(updatedWranglerCode) : "IMAGES";
|
|
21
21
|
if (context.existingViteConfigPath) updateViteConfigForCloudflare(context.existingViteConfigPath, fs.readFileSync(context.existingViteConfigPath, "utf-8"), {
|
|
22
22
|
isAppRouter: context.isAppRouter,
|
|
@@ -56,7 +56,7 @@ function setupCloudflarePlatform(context, cloudflare) {
|
|
|
56
56
|
fs.writeFileSync(path.join(context.root, "wrangler.jsonc"), generateWranglerConfig(projectInfo, cloudflare, context.today), "utf-8");
|
|
57
57
|
generatedPlatformFiles.push("wrangler.jsonc");
|
|
58
58
|
} else if (wranglerCode) {
|
|
59
|
-
const updatedConfig = updateWranglerConfigForCloudflare(wranglerCode, cloudflare);
|
|
59
|
+
const updatedConfig = updateWranglerConfigForCloudflare(wranglerCode, cloudflare, { root: context.root });
|
|
60
60
|
if (updatedConfig !== wranglerCode) {
|
|
61
61
|
fs.writeFileSync(wranglerPath, updatedConfig, "utf-8");
|
|
62
62
|
generatedPlatformFiles.push(path.basename(wranglerPath));
|
|
@@ -79,8 +79,19 @@ function setupCloudflarePlatform(context, cloudflare) {
|
|
|
79
79
|
] : []
|
|
80
80
|
};
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* `main` is what makes a Wrangler config a Worker rather than a static-assets
|
|
84
|
+
* project. A custom `worker/index.*` wins when present; otherwise the
|
|
85
|
+
* router-selected entry resolves to the App or Pages Router handler at build
|
|
86
|
+
* time.
|
|
87
|
+
*/
|
|
88
|
+
function resolveWorkerEntry(root) {
|
|
89
|
+
if (fs.existsSync(path.join(root, "worker", "index.ts"))) return "./worker/index.ts";
|
|
90
|
+
if (fs.existsSync(path.join(root, "worker", "index.js"))) return "./worker/index.js";
|
|
91
|
+
return "vinext/server/fetch-handler";
|
|
92
|
+
}
|
|
82
93
|
function generateWranglerConfig(info, options = DEFAULT_CLOUDFLARE_INIT_OPTIONS, today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0]) {
|
|
83
|
-
const workerEntry = (
|
|
94
|
+
const workerEntry = resolveWorkerEntry(info.root);
|
|
84
95
|
const config = {
|
|
85
96
|
$schema: "node_modules/wrangler/config-schema.json",
|
|
86
97
|
name: info.projectName,
|
|
@@ -258,13 +269,20 @@ function appendTopLevelJsonProperty(code, property) {
|
|
|
258
269
|
const structuralBefore = stripJsonComments(before);
|
|
259
270
|
return `${before}${!/,\s*$/.test(structuralBefore) && !/{\s*$/.test(structuralBefore) ? "," : ""}\n${property}\n${code.slice(closing)}`;
|
|
260
271
|
}
|
|
261
|
-
function updateWranglerConfigForCloudflare(code, options) {
|
|
272
|
+
function updateWranglerConfigForCloudflare(code, options, context = {}) {
|
|
273
|
+
let config;
|
|
262
274
|
try {
|
|
263
|
-
JSON.parse(stripJsonComments(code));
|
|
275
|
+
config = JSON.parse(stripJsonComments(code));
|
|
264
276
|
} catch (cause) {
|
|
265
277
|
throw new Error("Could not parse the existing Wrangler JSON/JSONC config.", { cause });
|
|
266
278
|
}
|
|
279
|
+
if (Object.hasOwn(config, "pages_build_output_dir")) throw new Error("The existing Wrangler config uses \"pages_build_output_dir\", which cannot be combined with the Worker \"main\" required by vinext. Remove \"pages_build_output_dir\" and rerun vinext init.");
|
|
267
280
|
let output = code;
|
|
281
|
+
if (!findTopLevelJsonProperty(output, "main")) {
|
|
282
|
+
const workerEntry = resolveWorkerEntry(context.root ?? process.cwd());
|
|
283
|
+
output = appendTopLevelJsonProperty(output, ` "main": ${JSON.stringify(workerEntry)}`);
|
|
284
|
+
}
|
|
285
|
+
if (!findTopLevelJsonProperty(output, "assets")) output = appendTopLevelJsonProperty(output, " \"assets\": { \"directory\": \"dist/client\", \"not_found_handling\": \"none\", \"binding\": \"ASSETS\" }");
|
|
268
286
|
if (options.cdnCache === "workers-cache") {
|
|
269
287
|
const cacheProperty = findTopLevelJsonProperty(output, "cache");
|
|
270
288
|
if (!cacheProperty) output = appendTopLevelJsonProperty(output, " \"cache\": { \"enabled\": true }");
|
|
@@ -613,6 +631,41 @@ function endsWithCommaIgnoringWhitespaceAndComments(code) {
|
|
|
613
631
|
function cloudflarePluginExpression(isAppRouter, binding) {
|
|
614
632
|
return isAppRouter ? `${binding}({\n viteEnvironment: {\n name: "rsc",\n childEnvironments: ["ssr"],\n },\n})` : `${binding}()`;
|
|
615
633
|
}
|
|
634
|
+
/**
|
|
635
|
+
* An existing bare `cloudflare()` call is left as-is by `ensurePlugins`, which
|
|
636
|
+
* only adds plugins that are absent. For the App Router that silently drops
|
|
637
|
+
* `viteEnvironment`, so the RSC environment never runs in workerd.
|
|
638
|
+
*/
|
|
639
|
+
function ensureCloudflareViteEnvironment(output, config, binding, isAppRouter, code) {
|
|
640
|
+
if (!isAppRouter) return;
|
|
641
|
+
const call = findPluginCall(config, binding);
|
|
642
|
+
if (!call) return;
|
|
643
|
+
const viteEnvironment = `viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] }`;
|
|
644
|
+
const firstArgument = call.arguments[0];
|
|
645
|
+
if (!firstArgument) {
|
|
646
|
+
output.appendLeft(call.end - 1, `{ ${viteEnvironment} }`);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
if (firstArgument.type === "SpreadElement" || firstArgument.type !== "ObjectExpression") throw new Error("The cloudflare() plugin options must be a static object for vinext init to configure the App Router Vite environment.");
|
|
650
|
+
const argumentObject = firstArgument;
|
|
651
|
+
const existingViteEnvironment = argumentObject.properties.filter((property) => property.type === "Property" && propertyName(property) === "viteEnvironment").at(-1);
|
|
652
|
+
if (existingViteEnvironment) {
|
|
653
|
+
const propertyIndex = argumentObject.properties.lastIndexOf(existingViteEnvironment);
|
|
654
|
+
if (argumentObject.properties.slice(propertyIndex + 1).some((property) => property.type === "SpreadElement")) throw new Error("The cloudflare() viteEnvironment option must appear after any spread properties so vinext init can verify it.");
|
|
655
|
+
if (existingViteEnvironment.value.type !== "ObjectExpression") throw new Error("The cloudflare() viteEnvironment option must be a static object with name: \"rsc\" and childEnvironments containing \"ssr\".");
|
|
656
|
+
const environmentObject = existingViteEnvironment.value;
|
|
657
|
+
const nameProperties = environmentObject.properties.filter((property) => property.type === "Property" && propertyName(property) === "name");
|
|
658
|
+
const childEnvironmentProperties = environmentObject.properties.filter((property) => property.type === "Property" && propertyName(property) === "childEnvironments");
|
|
659
|
+
const name = nameProperties[0];
|
|
660
|
+
const childEnvironments = childEnvironmentProperties[0];
|
|
661
|
+
const hasAmbiguousProperties = environmentObject.properties.some((property) => property.type === "SpreadElement") || nameProperties.length !== 1 || childEnvironmentProperties.length !== 1;
|
|
662
|
+
const hasRequiredName = name?.value.type === "Literal" && name.value.value === "rsc";
|
|
663
|
+
const hasRequiredChild = childEnvironments?.value.type === "ArrayExpression" && childEnvironments.value.elements.some((element) => element?.type === "Literal" && element.value === "ssr");
|
|
664
|
+
if (hasAmbiguousProperties || !hasRequiredName || !hasRequiredChild) throw new Error("The cloudflare() viteEnvironment option must statically set name: \"rsc\" and include \"ssr\" in childEnvironments.");
|
|
665
|
+
return;
|
|
666
|
+
}
|
|
667
|
+
insertObjectProperty(output, argumentObject, `${code.slice(0, call.start).split("\n").at(-1)?.match(/^\s*/)?.[0] ?? ""} ${viteEnvironment},`, code);
|
|
668
|
+
}
|
|
616
669
|
function findPluginCall(config, binding) {
|
|
617
670
|
const plugins = findProperty(config, "plugins");
|
|
618
671
|
if (!plugins || plugins.value.type !== "ArrayExpression") return void 0;
|
|
@@ -819,6 +872,7 @@ function updateViteConfigForCloudflare(filePath, code, options) {
|
|
|
819
872
|
expression: cloudflarePluginExpression(options.isAppRouter, cloudflareBinding),
|
|
820
873
|
binding: cloudflareBinding
|
|
821
874
|
}], code);
|
|
875
|
+
ensureCloudflareViteEnvironment(output, config, cloudflareBinding, options.isAppRouter, code);
|
|
822
876
|
if (existingVinextCall) if (existingVinextCall.arguments.length === 0 && (cacheAdditions.length > 0 || imageOptimizerExpression || needsPrerender)) {
|
|
823
877
|
const properties = [];
|
|
824
878
|
if (cacheAdditions.length > 0) properties.push(`cache: { ${cacheAdditions.map(({ name, expression }) => `${name}: ${expression}`).join(", ")} }`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { DYNAMIC_IMPORT_PRESCAN, forEachAstChild, hasRange, isAstRecord, nodeArray } from "./ast-utils.js";
|
|
2
|
+
import { createTransformCache } from "./transform-cache.js";
|
|
2
3
|
import { parseAst } from "vite";
|
|
3
4
|
import MagicString from "magic-string";
|
|
4
5
|
//#region src/plugins/extensionless-dynamic-import.ts
|
|
@@ -23,6 +24,7 @@ const TRANSFORMABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
23
24
|
]);
|
|
24
25
|
function createExtensionlessDynamicImportPlugin() {
|
|
25
26
|
let moduleExtensions = MODULE_EXTENSIONS;
|
|
27
|
+
const cached = createTransformCache();
|
|
26
28
|
return {
|
|
27
29
|
name: "vinext:extensionless-dynamic-import",
|
|
28
30
|
enforce: "pre",
|
|
@@ -38,28 +40,31 @@ function createExtensionlessDynamicImportPlugin() {
|
|
|
38
40
|
code: DYNAMIC_IMPORT_PRESCAN
|
|
39
41
|
},
|
|
40
42
|
handler(code, id) {
|
|
41
|
-
|
|
42
|
-
let ast;
|
|
43
|
-
try {
|
|
44
|
-
ast = parseAst(code, { lang });
|
|
45
|
-
} catch {
|
|
46
|
-
return null;
|
|
47
|
-
}
|
|
48
|
-
const imports = collectExtensionlessImports(ast, code, moduleExtensions);
|
|
49
|
-
if (imports.length === 0) return null;
|
|
50
|
-
const output = new MagicString(code);
|
|
51
|
-
for (const dynamicImport of imports) {
|
|
52
|
-
const source = code.slice(dynamicImport.sourceStart, dynamicImport.sourceEnd);
|
|
53
|
-
output.overwrite(dynamicImport.start, dynamicImport.end, buildReplacement(source, dynamicImport.globPattern, dynamicImport.moduleExtensions));
|
|
54
|
-
}
|
|
55
|
-
return {
|
|
56
|
-
code: output.toString(),
|
|
57
|
-
map: output.generateMap({ hires: "boundary" })
|
|
58
|
-
};
|
|
43
|
+
return cached(id, code, moduleExtensions, () => transformExtensionlessImports(code, id, moduleExtensions));
|
|
59
44
|
}
|
|
60
45
|
}
|
|
61
46
|
};
|
|
62
47
|
}
|
|
48
|
+
function transformExtensionlessImports(code, id, moduleExtensions) {
|
|
49
|
+
const lang = langForId(id);
|
|
50
|
+
let ast;
|
|
51
|
+
try {
|
|
52
|
+
ast = parseAst(code, { lang });
|
|
53
|
+
} catch {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
const imports = collectExtensionlessImports(ast, code, moduleExtensions);
|
|
57
|
+
if (imports.length === 0) return null;
|
|
58
|
+
const output = new MagicString(code);
|
|
59
|
+
for (const dynamicImport of imports) {
|
|
60
|
+
const source = code.slice(dynamicImport.sourceStart, dynamicImport.sourceEnd);
|
|
61
|
+
output.overwrite(dynamicImport.start, dynamicImport.end, buildReplacement(source, dynamicImport.globPattern, dynamicImport.moduleExtensions));
|
|
62
|
+
}
|
|
63
|
+
return {
|
|
64
|
+
code: output.toString(),
|
|
65
|
+
map: output.generateMap({ hires: "boundary" })
|
|
66
|
+
};
|
|
67
|
+
}
|
|
63
68
|
function langForId(id) {
|
|
64
69
|
const clean = id.split("?", 1)[0];
|
|
65
70
|
const dot = clean.lastIndexOf(".");
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { DYNAMIC_IMPORT_PRESCAN, collectBindingNames, forEachAstChild, hasRange, isAstRecord, isIdentifierNamed, mayContainDynamicImport, nodeArray } from "./ast-utils.js";
|
|
3
|
+
import { createTransformCache } from "./transform-cache.js";
|
|
3
4
|
import { collectDirectScopeBindings, collectLoopScopeBindings, collectSwitchScopeBindings, collectVarScopeBindings, hasAstBinding, isFunctionNode } from "./ast-scope.js";
|
|
4
5
|
import { parseAst } from "vite";
|
|
5
6
|
import { fileURLToPath } from "node:url";
|
|
@@ -505,6 +506,7 @@ function transformVeryDynamicRequests(code, id) {
|
|
|
505
506
|
};
|
|
506
507
|
}
|
|
507
508
|
function createIgnoreDynamicRequestsPlugin(getTranspiledPackages = () => []) {
|
|
509
|
+
const cached = createTransformCache();
|
|
508
510
|
return {
|
|
509
511
|
name: "vinext:ignore-dynamic-requests",
|
|
510
512
|
enforce: "pre",
|
|
@@ -519,7 +521,7 @@ function createIgnoreDynamicRequestsPlugin(getTranspiledPackages = () => []) {
|
|
|
519
521
|
if (!shouldTransformVeryDynamicRequests(this.environment, cleanId, getTranspiledPackages())) return null;
|
|
520
522
|
const absoluteId = path.resolve(cleanId);
|
|
521
523
|
if (absoluteId === VINEXT_SOURCE_ROOT || absoluteId.startsWith(`${VINEXT_SOURCE_ROOT}/`) || PLUGIN_RSC_PATH.test(absoluteId)) return null;
|
|
522
|
-
return transformVeryDynamicRequests(code, id);
|
|
524
|
+
return cached(id, code, void 0, () => transformVeryDynamicRequests(code, id));
|
|
523
525
|
}
|
|
524
526
|
}
|
|
525
527
|
};
|
|
@@ -19,6 +19,7 @@ const TRANSFORMABLE_SCRIPT_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
19
19
|
function createImportMetaUrlPlugin(options) {
|
|
20
20
|
let rootPaths;
|
|
21
21
|
let outputDirs = [];
|
|
22
|
+
const transformCache = /* @__PURE__ */ new Map();
|
|
22
23
|
function getRootPaths() {
|
|
23
24
|
const root = options.getRoot();
|
|
24
25
|
if (!root) return rootPaths;
|
|
@@ -46,12 +47,22 @@ function createImportMetaUrlPlugin(options) {
|
|
|
46
47
|
if (!paths) return null;
|
|
47
48
|
const canonicalId = transformableModuleCanonicalId(cleanModuleId(id), paths);
|
|
48
49
|
if (!canonicalId) return null;
|
|
49
|
-
const
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
const environment = this.environment?.name === "client" ? "client" : "server";
|
|
51
|
+
let entry = transformCache.get(id);
|
|
52
|
+
if (!entry || entry.source !== code || entry.canonicalRoot !== paths.canonicalRoot || entry.canonicalId !== canonicalId) {
|
|
53
|
+
entry = {
|
|
54
|
+
source: code,
|
|
55
|
+
canonicalRoot: paths.canonicalRoot,
|
|
56
|
+
canonicalId,
|
|
57
|
+
results: /* @__PURE__ */ new Map()
|
|
58
|
+
};
|
|
59
|
+
transformCache.set(id, entry);
|
|
60
|
+
}
|
|
61
|
+
const cached = entry.results.get(environment);
|
|
62
|
+
if (cached) return cached.value;
|
|
63
|
+
const value = rewriteCanonicalSourceIdentity(code, canonicalId, paths, environment);
|
|
64
|
+
entry.results.set(environment, { value });
|
|
65
|
+
return value;
|
|
55
66
|
}
|
|
56
67
|
}
|
|
57
68
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { forEachAstChild, hasRange, isAstRecord, nodeArray } from "./ast-utils.js";
|
|
2
|
+
import { createTransformCache } from "./transform-cache.js";
|
|
2
3
|
import { parseAst } from "vite";
|
|
3
4
|
import MagicString from "magic-string";
|
|
4
5
|
//#region src/plugins/require-context.ts
|
|
@@ -13,6 +14,7 @@ const TRANSFORMABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
|
13
14
|
".cts"
|
|
14
15
|
]);
|
|
15
16
|
function createRequireContextPlugin() {
|
|
17
|
+
const cached = createTransformCache();
|
|
16
18
|
return {
|
|
17
19
|
name: "vinext:require-context",
|
|
18
20
|
enforce: "pre",
|
|
@@ -22,25 +24,28 @@ function createRequireContextPlugin() {
|
|
|
22
24
|
code: /\brequire\b[\s\S]*\.context/
|
|
23
25
|
},
|
|
24
26
|
handler(code, id) {
|
|
25
|
-
|
|
26
|
-
let ast;
|
|
27
|
-
try {
|
|
28
|
-
ast = parseAst(code, { lang });
|
|
29
|
-
} catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
const calls = collectRequireContextCalls(ast);
|
|
33
|
-
if (calls.length === 0) return null;
|
|
34
|
-
const output = new MagicString(code);
|
|
35
|
-
for (const call of calls) output.overwrite(call.range.start, call.range.end, buildReplacement(call));
|
|
36
|
-
return {
|
|
37
|
-
code: output.toString(),
|
|
38
|
-
map: output.generateMap({ hires: "boundary" })
|
|
39
|
-
};
|
|
27
|
+
return cached(id, code, void 0, () => transformRequireContext(code, id));
|
|
40
28
|
}
|
|
41
29
|
}
|
|
42
30
|
};
|
|
43
31
|
}
|
|
32
|
+
function transformRequireContext(code, id) {
|
|
33
|
+
const lang = langForId(id);
|
|
34
|
+
let ast;
|
|
35
|
+
try {
|
|
36
|
+
ast = parseAst(code, { lang });
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
const calls = collectRequireContextCalls(ast);
|
|
41
|
+
if (calls.length === 0) return null;
|
|
42
|
+
const output = new MagicString(code);
|
|
43
|
+
for (const call of calls) output.overwrite(call.range.start, call.range.end, buildReplacement(call));
|
|
44
|
+
return {
|
|
45
|
+
code: output.toString(),
|
|
46
|
+
map: output.generateMap({ hires: "boundary" })
|
|
47
|
+
};
|
|
48
|
+
}
|
|
44
49
|
function langForId(id) {
|
|
45
50
|
const clean = id.split("?", 1)[0];
|
|
46
51
|
const dot = clean.lastIndexOf(".");
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
//#region src/plugins/transform-cache.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Memoizes a pure per-module transform across the repeated build passes
|
|
4
|
+
* (scan client/server references + rsc/client/ssr builds), which feed the
|
|
5
|
+
* same module source through the same transform up to five times.
|
|
6
|
+
*
|
|
7
|
+
* Entries are keyed by module id and replaced when the source changes, so dev
|
|
8
|
+
* rebuilds retain at most one source version per id. `variant` distinguishes
|
|
9
|
+
* results that also depend on an environment-derived input (e.g. the
|
|
10
|
+
* typeof-window replacement); callers must pass a primitive or stable
|
|
11
|
+
* reference. Cached values include `null` ("no transform applies") so the
|
|
12
|
+
* negative case is not recomputed either.
|
|
13
|
+
*
|
|
14
|
+
* Only safe for transforms whose result depends solely on (id, source,
|
|
15
|
+
* variant) — no filesystem or plugin-state reads inside `compute`.
|
|
16
|
+
*/
|
|
17
|
+
declare function createTransformCache<V, R>(): (id: string, source: string, variant: V, compute: () => R) => R;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { createTransformCache };
|