vinext 0.0.26 → 0.0.28
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 +89 -85
- package/dist/build/static-export.d.ts +1 -1
- package/dist/build/static-export.d.ts.map +1 -1
- package/dist/build/static-export.js +5 -9
- package/dist/build/static-export.js.map +1 -1
- package/dist/check.d.ts.map +1 -1
- package/dist/check.js +152 -48
- package/dist/check.js.map +1 -1
- package/dist/cli.js +10 -11
- package/dist/cli.js.map +1 -1
- package/dist/cloudflare/kv-cache-handler.d.ts +43 -1
- package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -1
- package/dist/cloudflare/kv-cache-handler.js +135 -44
- package/dist/cloudflare/kv-cache-handler.js.map +1 -1
- package/dist/cloudflare/tpr.d.ts.map +1 -1
- package/dist/cloudflare/tpr.js +15 -4
- package/dist/cloudflare/tpr.js.map +1 -1
- package/dist/config/config-matchers.d.ts +28 -0
- package/dist/config/config-matchers.d.ts.map +1 -1
- package/dist/config/config-matchers.js +353 -79
- package/dist/config/config-matchers.js.map +1 -1
- package/dist/config/dotenv.d.ts.map +1 -1
- package/dist/config/dotenv.js +1 -6
- package/dist/config/dotenv.js.map +1 -1
- package/dist/config/next-config.d.ts +7 -0
- package/dist/config/next-config.d.ts.map +1 -1
- package/dist/config/next-config.js +44 -19
- package/dist/config/next-config.js.map +1 -1
- package/dist/deploy.d.ts +1 -1
- package/dist/deploy.d.ts.map +1 -1
- package/dist/deploy.js +81 -48
- package/dist/deploy.js.map +1 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -1
- package/dist/entries/app-rsc-entry.d.ts.map +1 -1
- package/dist/entries/app-rsc-entry.js +584 -113
- package/dist/entries/app-rsc-entry.js.map +1 -1
- package/dist/entries/pages-client-entry.d.ts.map +1 -1
- package/dist/entries/pages-client-entry.js +5 -3
- package/dist/entries/pages-client-entry.js.map +1 -1
- package/dist/entries/pages-server-entry.d.ts.map +1 -1
- package/dist/entries/pages-server-entry.js +100 -32
- package/dist/entries/pages-server-entry.js.map +1 -1
- package/dist/index.d.ts +24 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +327 -154
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +6 -5
- package/dist/init.js.map +1 -1
- package/dist/plugins/client-reference-dedup.d.ts +19 -0
- package/dist/plugins/client-reference-dedup.d.ts.map +1 -0
- package/dist/plugins/client-reference-dedup.js +96 -0
- package/dist/plugins/client-reference-dedup.js.map +1 -0
- package/dist/routing/app-router.d.ts +2 -0
- package/dist/routing/app-router.d.ts.map +1 -1
- package/dist/routing/app-router.js +70 -107
- package/dist/routing/app-router.js.map +1 -1
- package/dist/routing/file-matcher.d.ts.map +1 -1
- package/dist/routing/file-matcher.js.map +1 -1
- package/dist/routing/pages-router.d.ts +3 -1
- package/dist/routing/pages-router.d.ts.map +1 -1
- package/dist/routing/pages-router.js +33 -18
- package/dist/routing/pages-router.js.map +1 -1
- package/dist/routing/route-validation.d.ts +8 -0
- package/dist/routing/route-validation.d.ts.map +1 -0
- package/dist/routing/route-validation.js +124 -0
- package/dist/routing/route-validation.js.map +1 -0
- package/dist/routing/utils.d.ts.map +1 -1
- package/dist/routing/utils.js.map +1 -1
- package/dist/server/api-handler.d.ts.map +1 -1
- package/dist/server/api-handler.js +31 -9
- package/dist/server/api-handler.js.map +1 -1
- package/dist/server/app-router-entry.d.ts +3 -2
- package/dist/server/app-router-entry.d.ts.map +1 -1
- package/dist/server/app-router-entry.js +8 -4
- package/dist/server/app-router-entry.js.map +1 -1
- package/dist/server/dev-module-runner.d.ts.map +1 -1
- package/dist/server/dev-module-runner.js +1 -1
- package/dist/server/dev-module-runner.js.map +1 -1
- package/dist/server/dev-origin-check.d.ts.map +1 -1
- package/dist/server/dev-origin-check.js.map +1 -1
- package/dist/server/dev-server.d.ts.map +1 -1
- package/dist/server/dev-server.js +39 -21
- package/dist/server/dev-server.js.map +1 -1
- package/dist/server/image-optimization.d.ts.map +1 -1
- package/dist/server/image-optimization.js.map +1 -1
- package/dist/server/instrumentation.js +1 -1
- package/dist/server/instrumentation.js.map +1 -1
- package/dist/server/isr-cache.d.ts +5 -1
- package/dist/server/isr-cache.d.ts.map +1 -1
- package/dist/server/isr-cache.js +13 -3
- package/dist/server/isr-cache.js.map +1 -1
- package/dist/server/metadata-routes.d.ts +8 -2
- package/dist/server/metadata-routes.d.ts.map +1 -1
- package/dist/server/metadata-routes.js +78 -45
- package/dist/server/metadata-routes.js.map +1 -1
- package/dist/server/middleware-codegen.d.ts +1 -1
- package/dist/server/middleware-codegen.d.ts.map +1 -1
- package/dist/server/middleware-codegen.js +177 -22
- package/dist/server/middleware-codegen.js.map +1 -1
- package/dist/server/middleware-request-headers.d.ts +9 -0
- package/dist/server/middleware-request-headers.d.ts.map +1 -0
- package/dist/server/middleware-request-headers.js +77 -0
- package/dist/server/middleware-request-headers.js.map +1 -0
- package/dist/server/middleware.d.ts +9 -8
- package/dist/server/middleware.d.ts.map +1 -1
- package/dist/server/middleware.js +112 -32
- package/dist/server/middleware.js.map +1 -1
- package/dist/server/normalize-path.js.map +1 -1
- package/dist/server/prod-server.d.ts +1 -1
- package/dist/server/prod-server.d.ts.map +1 -1
- package/dist/server/prod-server.js +127 -82
- package/dist/server/prod-server.js.map +1 -1
- package/dist/server/request-pipeline.d.ts +2 -1
- package/dist/server/request-pipeline.d.ts.map +1 -1
- package/dist/server/request-pipeline.js +5 -7
- package/dist/server/request-pipeline.js.map +1 -1
- package/dist/shims/cache-runtime.d.ts.map +1 -1
- package/dist/shims/cache-runtime.js +21 -16
- package/dist/shims/cache-runtime.js.map +1 -1
- package/dist/shims/cache.d.ts +2 -0
- package/dist/shims/cache.d.ts.map +1 -1
- package/dist/shims/cache.js +38 -25
- package/dist/shims/cache.js.map +1 -1
- package/dist/shims/constants.d.ts.map +1 -1
- package/dist/shims/constants.js +1 -6
- package/dist/shims/constants.js.map +1 -1
- package/dist/shims/dynamic.d.ts.map +1 -1
- package/dist/shims/dynamic.js +1 -1
- package/dist/shims/dynamic.js.map +1 -1
- package/dist/shims/error-boundary.d.ts.map +1 -1
- package/dist/shims/error-boundary.js +2 -3
- package/dist/shims/error-boundary.js.map +1 -1
- package/dist/shims/error.d.ts.map +1 -1
- package/dist/shims/error.js +1 -3
- package/dist/shims/error.js.map +1 -1
- package/dist/shims/fetch-cache.d.ts.map +1 -1
- package/dist/shims/fetch-cache.js +57 -30
- package/dist/shims/fetch-cache.js.map +1 -1
- package/dist/shims/font-google-base.d.ts.map +1 -1
- package/dist/shims/font-google-base.js +16 -4
- package/dist/shims/font-google-base.js.map +1 -1
- package/dist/shims/font-google.d.ts +1 -1
- package/dist/shims/font-google.d.ts.map +1 -1
- package/dist/shims/font-google.generated.d.ts.map +1 -1
- package/dist/shims/font-google.generated.js +412 -206
- package/dist/shims/font-google.generated.js.map +1 -1
- package/dist/shims/font-google.js +1 -1
- package/dist/shims/font-google.js.map +1 -1
- package/dist/shims/font-local.d.ts.map +1 -1
- package/dist/shims/font-local.js +13 -3
- package/dist/shims/font-local.js.map +1 -1
- package/dist/shims/form.d.ts.map +1 -1
- package/dist/shims/form.js +105 -10
- package/dist/shims/form.js.map +1 -1
- package/dist/shims/head.d.ts.map +1 -1
- package/dist/shims/head.js +10 -8
- package/dist/shims/head.js.map +1 -1
- package/dist/shims/headers.d.ts +34 -8
- package/dist/shims/headers.d.ts.map +1 -1
- package/dist/shims/headers.js +268 -53
- package/dist/shims/headers.js.map +1 -1
- package/dist/shims/image.d.ts.map +1 -1
- package/dist/shims/image.js +35 -8
- package/dist/shims/image.js.map +1 -1
- package/dist/shims/internal/parse-cookie-header.d.ts +12 -0
- package/dist/shims/internal/parse-cookie-header.d.ts.map +1 -0
- package/dist/shims/internal/parse-cookie-header.js +32 -0
- package/dist/shims/internal/parse-cookie-header.js.map +1 -0
- package/dist/shims/legacy-image.d.ts.map +1 -1
- package/dist/shims/legacy-image.js +1 -1
- package/dist/shims/legacy-image.js.map +1 -1
- package/dist/shims/link.d.ts +2 -1
- package/dist/shims/link.d.ts.map +1 -1
- package/dist/shims/link.js +37 -17
- package/dist/shims/link.js.map +1 -1
- package/dist/shims/metadata.d.ts +12 -2
- package/dist/shims/metadata.d.ts.map +1 -1
- package/dist/shims/metadata.js +10 -8
- package/dist/shims/metadata.js.map +1 -1
- package/dist/shims/navigation-state.d.ts.map +1 -1
- package/dist/shims/navigation-state.js +3 -2
- package/dist/shims/navigation-state.js.map +1 -1
- package/dist/shims/navigation.d.ts +3 -7
- package/dist/shims/navigation.d.ts.map +1 -1
- package/dist/shims/navigation.js +46 -29
- package/dist/shims/navigation.js.map +1 -1
- package/dist/shims/readonly-url-search-params.d.ts +11 -0
- package/dist/shims/readonly-url-search-params.d.ts.map +1 -0
- package/dist/shims/readonly-url-search-params.js +24 -0
- package/dist/shims/readonly-url-search-params.js.map +1 -0
- package/dist/shims/request-context.d.ts +50 -0
- package/dist/shims/request-context.d.ts.map +1 -0
- package/dist/shims/request-context.js +59 -0
- package/dist/shims/request-context.js.map +1 -0
- package/dist/shims/router-state.d.ts.map +1 -1
- package/dist/shims/router-state.js +2 -1
- package/dist/shims/router-state.js.map +1 -1
- package/dist/shims/router.d.ts +4 -3
- package/dist/shims/router.d.ts.map +1 -1
- package/dist/shims/router.js +59 -53
- package/dist/shims/router.js.map +1 -1
- package/dist/shims/script.d.ts.map +1 -1
- package/dist/shims/script.js.map +1 -1
- package/dist/shims/server.d.ts +14 -1
- package/dist/shims/server.d.ts.map +1 -1
- package/dist/shims/server.js +107 -47
- package/dist/shims/server.js.map +1 -1
- package/dist/shims/url-utils.d.ts.map +1 -1
- package/dist/shims/url-utils.js +1 -3
- package/dist/shims/url-utils.js.map +1 -1
- package/dist/utils/base-path.d.ts +17 -0
- package/dist/utils/base-path.d.ts.map +1 -0
- package/dist/utils/base-path.js +25 -0
- package/dist/utils/base-path.js.map +1 -0
- package/dist/utils/manifest-paths.d.ts +4 -0
- package/dist/utils/manifest-paths.d.ts.map +1 -0
- package/dist/utils/manifest-paths.js +20 -0
- package/dist/utils/manifest-paths.js.map +1 -0
- package/dist/utils/project.d.ts.map +1 -1
- package/dist/utils/project.js +2 -4
- package/dist/utils/project.js.map +1 -1
- package/dist/utils/query.d.ts +9 -0
- package/dist/utils/query.d.ts.map +1 -1
- package/dist/utils/query.js +59 -7
- package/dist/utils/query.js.map +1 -1
- package/package.json +47 -33
package/dist/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadEnv, parseAst } from "vite";
|
|
2
|
-
import { pagesRouter, apiRouter, invalidateRouteCache, matchRoute } from "./routing/pages-router.js";
|
|
2
|
+
import { pagesRouter, apiRouter, invalidateRouteCache, matchRoute, } from "./routing/pages-router.js";
|
|
3
3
|
import { generateServerEntry as _generateServerEntry } from "./entries/pages-server-entry.js";
|
|
4
4
|
import { generateClientEntry as _generateClientEntry } from "./entries/pages-client-entry.js";
|
|
5
5
|
import { appRouter, invalidateAppRouteCache } from "./routing/app-router.js";
|
|
@@ -19,8 +19,12 @@ import { PHASE_PRODUCTION_BUILD, PHASE_DEVELOPMENT_SERVER } from "./shims/consta
|
|
|
19
19
|
import { validateDevRequest } from "./server/dev-origin-check.js";
|
|
20
20
|
import { isExternalUrl, proxyExternalRequest, matchHeaders, matchRedirect, matchRewrite, requestContextFromRequest, sanitizeDestination, } from "./config/config-matchers.js";
|
|
21
21
|
import { scanMetadataFiles } from "./server/metadata-routes.js";
|
|
22
|
+
import { buildRequestHeadersFromMiddlewareResponse } from "./server/middleware-request-headers.js";
|
|
22
23
|
import { detectPackageManager } from "./utils/project.js";
|
|
24
|
+
import { manifestFileWithBase, manifestFilesWithBase, normalizeManifestFile, } from "./utils/manifest-paths.js";
|
|
25
|
+
import { hasBasePath } from "./utils/base-path.js";
|
|
23
26
|
import { asyncHooksStubPlugin } from "./plugins/async-hooks-stub.js";
|
|
27
|
+
import { clientReferenceDedupPlugin } from "./plugins/client-reference-dedup.js";
|
|
24
28
|
import { hasWranglerConfig, formatMissingCloudflarePluginError } from "./deploy.js";
|
|
25
29
|
import tsconfigPaths from "vite-tsconfig-paths";
|
|
26
30
|
import react from "@vitejs/plugin-react";
|
|
@@ -66,7 +70,11 @@ async function fetchAndCacheFont(cssUrl, family, cacheDir) {
|
|
|
66
70
|
while ((urlMatch = urlRe.exec(css)) !== null) {
|
|
67
71
|
const fontUrl = urlMatch[1];
|
|
68
72
|
if (!urls.has(fontUrl)) {
|
|
69
|
-
const ext = fontUrl.includes(".woff2")
|
|
73
|
+
const ext = fontUrl.includes(".woff2")
|
|
74
|
+
? ".woff2"
|
|
75
|
+
: fontUrl.includes(".woff")
|
|
76
|
+
? ".woff"
|
|
77
|
+
: ".ttf";
|
|
70
78
|
const fileHash = createHash("md5").update(fontUrl).digest("hex").slice(0, 8);
|
|
71
79
|
urls.set(fontUrl, `${family.toLowerCase().replace(/\s+/g, "-")}-${fileHash}${ext}`);
|
|
72
80
|
}
|
|
@@ -133,7 +141,9 @@ function extractStaticValue(node) {
|
|
|
133
141
|
return node.value;
|
|
134
142
|
case "UnaryExpression":
|
|
135
143
|
// Handle negative numbers: -1, -3.14
|
|
136
|
-
if (node.operator === "-" &&
|
|
144
|
+
if (node.operator === "-" &&
|
|
145
|
+
node.argument?.type === "Literal" &&
|
|
146
|
+
typeof node.argument.value === "number") {
|
|
137
147
|
return -node.argument.value;
|
|
138
148
|
}
|
|
139
149
|
return undefined;
|
|
@@ -244,7 +254,9 @@ async function resolvePostcssStringPlugins(projectRoot) {
|
|
|
244
254
|
// Load the config file
|
|
245
255
|
let config;
|
|
246
256
|
try {
|
|
247
|
-
if (configPath.endsWith(".json") ||
|
|
257
|
+
if (configPath.endsWith(".json") ||
|
|
258
|
+
configPath.endsWith(".yaml") ||
|
|
259
|
+
configPath.endsWith(".yml")) {
|
|
248
260
|
// JSON/YAML configs use object form — postcss-load-config handles these fine
|
|
249
261
|
return undefined;
|
|
250
262
|
}
|
|
@@ -267,8 +279,7 @@ async function resolvePostcssStringPlugins(projectRoot) {
|
|
|
267
279
|
// (either bare strings or tuple form ["plugin-name", { options }])
|
|
268
280
|
if (!config || !Array.isArray(config.plugins))
|
|
269
281
|
return undefined;
|
|
270
|
-
const hasStringPlugins = config.plugins.some((p) => typeof p === "string" ||
|
|
271
|
-
(Array.isArray(p) && typeof p[0] === "string"));
|
|
282
|
+
const hasStringPlugins = config.plugins.some((p) => typeof p === "string" || (Array.isArray(p) && typeof p[0] === "string"));
|
|
272
283
|
if (!hasStringPlugins)
|
|
273
284
|
return undefined;
|
|
274
285
|
// Resolve string plugin names to actual plugin functions
|
|
@@ -366,9 +377,7 @@ function clientManualChunks(id) {
|
|
|
366
377
|
const pkg = getPackageName(id);
|
|
367
378
|
if (!pkg)
|
|
368
379
|
return undefined;
|
|
369
|
-
if (pkg === "react" ||
|
|
370
|
-
pkg === "react-dom" ||
|
|
371
|
-
pkg === "scheduler") {
|
|
380
|
+
if (pkg === "react" || pkg === "react-dom" || pkg === "scheduler") {
|
|
372
381
|
return "framework";
|
|
373
382
|
}
|
|
374
383
|
// Let Rollup handle all other vendor code via its default
|
|
@@ -495,6 +504,57 @@ function computeLazyChunks(buildManifest) {
|
|
|
495
504
|
}
|
|
496
505
|
return lazyChunks;
|
|
497
506
|
}
|
|
507
|
+
function normalizeManifestModuleId(moduleId, root) {
|
|
508
|
+
const normalizedId = moduleId.replace(/\\/g, "/");
|
|
509
|
+
const isWindowsAbsolute = /^[a-zA-Z]:[\\/]/.test(moduleId) || moduleId.startsWith("\\\\");
|
|
510
|
+
if (isWindowsAbsolute) {
|
|
511
|
+
const relativeId = path.win32.relative(root, moduleId).replace(/\\/g, "/");
|
|
512
|
+
if (!relativeId || relativeId.startsWith("../"))
|
|
513
|
+
return normalizedId;
|
|
514
|
+
return relativeId;
|
|
515
|
+
}
|
|
516
|
+
if (!path.isAbsolute(moduleId))
|
|
517
|
+
return normalizedId;
|
|
518
|
+
const relativeId = path.relative(root, moduleId).replace(/\\/g, "/");
|
|
519
|
+
if (!relativeId || relativeId.startsWith("../"))
|
|
520
|
+
return normalizedId;
|
|
521
|
+
return relativeId;
|
|
522
|
+
}
|
|
523
|
+
function augmentSsrManifestFromBundle(ssrManifest, bundle, root, base = "/") {
|
|
524
|
+
const nextManifest = Object.fromEntries(Object.entries(ssrManifest).map(([key, files]) => [
|
|
525
|
+
key,
|
|
526
|
+
new Set(files.map((file) => normalizeManifestFile(file))),
|
|
527
|
+
]));
|
|
528
|
+
for (const item of Object.values(bundle)) {
|
|
529
|
+
if (item.type !== "chunk")
|
|
530
|
+
continue;
|
|
531
|
+
const chunk = item;
|
|
532
|
+
const files = new Set();
|
|
533
|
+
files.add(manifestFileWithBase(chunk.fileName, base));
|
|
534
|
+
for (const importedFile of chunk.imports ?? []) {
|
|
535
|
+
files.add(manifestFileWithBase(importedFile, base));
|
|
536
|
+
}
|
|
537
|
+
for (const cssFile of chunk.viteMetadata?.importedCss ?? []) {
|
|
538
|
+
files.add(manifestFileWithBase(cssFile, base));
|
|
539
|
+
}
|
|
540
|
+
for (const assetFile of chunk.viteMetadata?.importedAssets ?? []) {
|
|
541
|
+
files.add(manifestFileWithBase(assetFile, base));
|
|
542
|
+
}
|
|
543
|
+
for (const moduleId of Object.keys(chunk.modules ?? {})) {
|
|
544
|
+
const key = normalizeManifestModuleId(moduleId, root);
|
|
545
|
+
if (key.startsWith("node_modules/") || key.includes("/node_modules/"))
|
|
546
|
+
continue;
|
|
547
|
+
if (key.startsWith("\0"))
|
|
548
|
+
continue;
|
|
549
|
+
if (!nextManifest[key])
|
|
550
|
+
nextManifest[key] = new Set();
|
|
551
|
+
for (const file of files) {
|
|
552
|
+
nextManifest[key].add(file);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
return Object.fromEntries(Object.entries(nextManifest).map(([key, files]) => [key, [...files]]));
|
|
557
|
+
}
|
|
498
558
|
export default function vinext(options = {}) {
|
|
499
559
|
let root;
|
|
500
560
|
let pagesDir;
|
|
@@ -566,11 +626,12 @@ export default function vinext(options = {}) {
|
|
|
566
626
|
if (earlyAppDirExists && autoRsc) {
|
|
567
627
|
if (!resolvedRscPath) {
|
|
568
628
|
throw new Error("vinext: App Router detected but @vitejs/plugin-rsc is not installed.\n" +
|
|
569
|
-
"Run: " +
|
|
629
|
+
"Run: " +
|
|
630
|
+
detectPackageManager(process.cwd()) +
|
|
631
|
+
" @vitejs/plugin-rsc");
|
|
570
632
|
}
|
|
571
633
|
const rscImport = import(pathToFileURL(resolvedRscPath).href);
|
|
572
|
-
rscPluginPromise = rscImport
|
|
573
|
-
.then((mod) => {
|
|
634
|
+
rscPluginPromise = rscImport.then((mod) => {
|
|
574
635
|
const rsc = mod.default;
|
|
575
636
|
return rsc({
|
|
576
637
|
entries: {
|
|
@@ -585,9 +646,7 @@ export default function vinext(options = {}) {
|
|
|
585
646
|
// Shared state for the MDX proxy plugin. Populated during config() if MDX
|
|
586
647
|
// files are detected and @mdx-js/rollup is installed.
|
|
587
648
|
let mdxDelegate = null;
|
|
588
|
-
const reactPlugin = options.react === false
|
|
589
|
-
? false
|
|
590
|
-
: react(options.react === true ? undefined : options.react);
|
|
649
|
+
const reactPlugin = options.react === false ? false : react(options.react === true ? undefined : options.react);
|
|
591
650
|
const plugins = [
|
|
592
651
|
// Resolve tsconfig paths/baseUrl aliases so real-world Next.js repos
|
|
593
652
|
// that use @/*, #/*, or baseUrl imports work out of the box.
|
|
@@ -689,7 +748,9 @@ export default function vinext(options = {}) {
|
|
|
689
748
|
// server-side validation. Matches Next.js behavior: only configured
|
|
690
749
|
// sizes are accepted by the image optimization endpoint.
|
|
691
750
|
{
|
|
692
|
-
const deviceSizes = nextConfig.images?.deviceSizes ?? [
|
|
751
|
+
const deviceSizes = nextConfig.images?.deviceSizes ?? [
|
|
752
|
+
640, 750, 828, 1080, 1200, 1920, 2048, 3840,
|
|
753
|
+
];
|
|
693
754
|
const imageSizes = nextConfig.images?.imageSizes ?? [16, 32, 48, 64, 96, 128, 256, 384];
|
|
694
755
|
defines["process.env.__VINEXT_IMAGE_DEVICE_SIZES"] = JSON.stringify(JSON.stringify(deviceSizes));
|
|
695
756
|
defines["process.env.__VINEXT_IMAGE_SIZES"] = JSON.stringify(JSON.stringify(imageSizes));
|
|
@@ -701,6 +762,11 @@ export default function vinext(options = {}) {
|
|
|
701
762
|
// __prerender_bypass cookie is consistent across all server
|
|
702
763
|
// instances (e.g. multiple Cloudflare Workers isolates).
|
|
703
764
|
defines["process.env.__VINEXT_DRAFT_SECRET"] = JSON.stringify(crypto.randomUUID());
|
|
765
|
+
// Build ID — resolved from next.config generateBuildId() or random UUID.
|
|
766
|
+
// Exposed so server entries and the next/server shim can inject it.
|
|
767
|
+
// Also used to namespace ISR cache keys so old cached entries from a
|
|
768
|
+
// previous deploy are never served by the new one.
|
|
769
|
+
defines["process.env.__VINEXT_BUILD_ID"] = JSON.stringify(nextConfig.buildId);
|
|
704
770
|
// Build the shim alias map — used by both resolve.alias and resolveId
|
|
705
771
|
// (resolveId handles .js extension variants for libraries like nuqs)
|
|
706
772
|
nextShimMap = {
|
|
@@ -770,8 +836,14 @@ export default function vinext(options = {}) {
|
|
|
770
836
|
}
|
|
771
837
|
}
|
|
772
838
|
flattenPlugins(config.plugins ?? []);
|
|
773
|
-
hasCloudflarePlugin = pluginsFlat.some((p) => p &&
|
|
774
|
-
|
|
839
|
+
hasCloudflarePlugin = pluginsFlat.some((p) => p &&
|
|
840
|
+
typeof p === "object" &&
|
|
841
|
+
typeof p.name === "string" &&
|
|
842
|
+
(p.name === "vite-plugin-cloudflare" || p.name.startsWith("vite-plugin-cloudflare:")));
|
|
843
|
+
hasNitroPlugin = pluginsFlat.some((p) => p &&
|
|
844
|
+
typeof p === "object" &&
|
|
845
|
+
typeof p.name === "string" &&
|
|
846
|
+
(p.name === "nitro" || p.name.startsWith("nitro:")));
|
|
775
847
|
// Resolve PostCSS string plugin names that Vite can't handle.
|
|
776
848
|
// Next.js projects commonly use array-form plugins like
|
|
777
849
|
// `plugins: ["@tailwindcss/postcss"]` which postcss-load-config
|
|
@@ -785,9 +857,12 @@ export default function vinext(options = {}) {
|
|
|
785
857
|
}
|
|
786
858
|
// Auto-inject @mdx-js/rollup when MDX files exist and no MDX plugin is
|
|
787
859
|
// already configured. Applies remark/rehype plugins from next.config.
|
|
788
|
-
const hasMdxPlugin = pluginsFlat.some((p) => p &&
|
|
860
|
+
const hasMdxPlugin = pluginsFlat.some((p) => p &&
|
|
861
|
+
typeof p === "object" &&
|
|
862
|
+
typeof p.name === "string" &&
|
|
789
863
|
(p.name === "@mdx-js/rollup" || p.name === "mdx"));
|
|
790
|
-
if (!hasMdxPlugin &&
|
|
864
|
+
if (!hasMdxPlugin &&
|
|
865
|
+
hasMdxFiles(root, hasAppDir ? appDir : null, hasPagesDir ? pagesDir : null)) {
|
|
791
866
|
try {
|
|
792
867
|
const mdxRollup = await import("@mdx-js/rollup");
|
|
793
868
|
const mdxFactory = mdxRollup.default ?? mdxRollup;
|
|
@@ -811,7 +886,9 @@ export default function vinext(options = {}) {
|
|
|
811
886
|
catch {
|
|
812
887
|
// @mdx-js/rollup not installed — warn but don't fail
|
|
813
888
|
console.warn("[vinext] MDX files detected but @mdx-js/rollup is not installed. " +
|
|
814
|
-
"Install it with: " +
|
|
889
|
+
"Install it with: " +
|
|
890
|
+
detectPackageManager(process.cwd()) +
|
|
891
|
+
" @mdx-js/rollup");
|
|
815
892
|
}
|
|
816
893
|
}
|
|
817
894
|
// Detect if this is a standalone SSR build (set by `vite build --ssr`
|
|
@@ -893,12 +970,14 @@ export default function vinext(options = {}) {
|
|
|
893
970
|
// Skip when targeting bundled runtimes (Cloudflare/Nitro bundle everything).
|
|
894
971
|
// This also resolves extensionless-import issues in packages like
|
|
895
972
|
// `validator` (see #189) by routing them through Vite's resolver.
|
|
896
|
-
...(hasCloudflarePlugin || hasNitroPlugin
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
973
|
+
...(hasCloudflarePlugin || hasNitroPlugin
|
|
974
|
+
? {}
|
|
975
|
+
: {
|
|
976
|
+
ssr: {
|
|
977
|
+
external: ["react", "react-dom", "react-dom/server"],
|
|
978
|
+
noExternal: true,
|
|
979
|
+
},
|
|
980
|
+
}),
|
|
902
981
|
resolve: {
|
|
903
982
|
alias: nextShimMap,
|
|
904
983
|
// Dedupe React packages to prevent dual-instance errors.
|
|
@@ -906,12 +985,7 @@ export default function vinext(options = {}) {
|
|
|
906
985
|
// brings its own React copy, multiple React instances can load,
|
|
907
986
|
// causing cryptic "Invalid hook call" errors. This is a no-op
|
|
908
987
|
// when only one copy exists.
|
|
909
|
-
dedupe: [
|
|
910
|
-
"react",
|
|
911
|
-
"react-dom",
|
|
912
|
-
"react/jsx-runtime",
|
|
913
|
-
"react/jsx-dev-runtime",
|
|
914
|
-
],
|
|
988
|
+
dedupe: ["react", "react-dom", "react/jsx-runtime", "react/jsx-dev-runtime"],
|
|
915
989
|
},
|
|
916
990
|
// Exclude vinext from dependency optimization so esbuild doesn't
|
|
917
991
|
// scan dist files containing virtual module imports (virtual:vinext-*)
|
|
@@ -954,34 +1028,31 @@ export default function vinext(options = {}) {
|
|
|
954
1028
|
// "Invalid hook call" from duplicate React instances).
|
|
955
1029
|
// The entries must be relative to the project root.
|
|
956
1030
|
const relAppDir = path.relative(root, appDir);
|
|
957
|
-
const appEntries = [
|
|
958
|
-
`${relAppDir}/**/*.{tsx,ts,jsx,js}`,
|
|
959
|
-
];
|
|
1031
|
+
const appEntries = [`${relAppDir}/**/*.{tsx,ts,jsx,js}`];
|
|
960
1032
|
viteConfig.environments = {
|
|
961
1033
|
rsc: {
|
|
962
|
-
...(hasCloudflarePlugin || hasNitroPlugin
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
},
|
|
984
|
-
}),
|
|
1034
|
+
...(hasCloudflarePlugin || hasNitroPlugin
|
|
1035
|
+
? {}
|
|
1036
|
+
: {
|
|
1037
|
+
resolve: {
|
|
1038
|
+
// Externalize native/heavy packages so the RSC environment
|
|
1039
|
+
// loads them natively via Node rather than through Vite's
|
|
1040
|
+
// ESM module evaluator (which can't handle native addons).
|
|
1041
|
+
// Note: Do NOT externalize react/react-dom here — they must
|
|
1042
|
+
// be bundled with the "react-server" condition for RSC.
|
|
1043
|
+
// Skip when targeting bundled runtimes (Cloudflare/Nitro).
|
|
1044
|
+
external: userSsrExternal === true
|
|
1045
|
+
? true
|
|
1046
|
+
: ["satori", "@resvg/resvg-js", "yoga-wasm-web", ...userSsrExternal],
|
|
1047
|
+
// Force all node_modules through Vite's transform pipeline
|
|
1048
|
+
// so non-JS imports (CSS, images) don't hit Node's native
|
|
1049
|
+
// ESM loader. Matches Next.js behavior of bundling everything.
|
|
1050
|
+
// Packages in `external` above take precedence per Vite rules.
|
|
1051
|
+
// When user sets `ssr.external: true`, skip noExternal since
|
|
1052
|
+
// everything is already externalized.
|
|
1053
|
+
...(userSsrExternal === true ? {} : { noExternal: true }),
|
|
1054
|
+
},
|
|
1055
|
+
}),
|
|
985
1056
|
optimizeDeps: {
|
|
986
1057
|
exclude: ["vinext", "@vercel/og"],
|
|
987
1058
|
entries: appEntries,
|
|
@@ -994,17 +1065,19 @@ export default function vinext(options = {}) {
|
|
|
994
1065
|
},
|
|
995
1066
|
},
|
|
996
1067
|
ssr: {
|
|
997
|
-
...(hasCloudflarePlugin || hasNitroPlugin
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1068
|
+
...(hasCloudflarePlugin || hasNitroPlugin
|
|
1069
|
+
? {}
|
|
1070
|
+
: {
|
|
1071
|
+
resolve: {
|
|
1072
|
+
external: userSsrExternal === true ? true : [...userSsrExternal],
|
|
1073
|
+
// Force all node_modules through Vite's transform pipeline
|
|
1074
|
+
// so non-JS imports (CSS, images) don't hit Node's native
|
|
1075
|
+
// ESM loader. Matches Next.js behavior of bundling everything.
|
|
1076
|
+
// When user sets `ssr.external: true`, skip noExternal since
|
|
1077
|
+
// everything is already externalized.
|
|
1078
|
+
...(userSsrExternal === true ? {} : { noExternal: true }),
|
|
1079
|
+
},
|
|
1080
|
+
}),
|
|
1008
1081
|
optimizeDeps: {
|
|
1009
1082
|
exclude: ["vinext", "@vercel/og"],
|
|
1010
1083
|
entries: appEntries,
|
|
@@ -1137,10 +1210,12 @@ export default function vinext(options = {}) {
|
|
|
1137
1210
|
return RESOLVED_SERVER_ENTRY;
|
|
1138
1211
|
if (cleanId === VIRTUAL_CLIENT_ENTRY)
|
|
1139
1212
|
return RESOLVED_CLIENT_ENTRY;
|
|
1140
|
-
if (cleanId.endsWith("/" + VIRTUAL_SERVER_ENTRY) ||
|
|
1213
|
+
if (cleanId.endsWith("/" + VIRTUAL_SERVER_ENTRY) ||
|
|
1214
|
+
cleanId.endsWith("\\" + VIRTUAL_SERVER_ENTRY)) {
|
|
1141
1215
|
return RESOLVED_SERVER_ENTRY;
|
|
1142
1216
|
}
|
|
1143
|
-
if (cleanId.endsWith("/" + VIRTUAL_CLIENT_ENTRY) ||
|
|
1217
|
+
if (cleanId.endsWith("/" + VIRTUAL_CLIENT_ENTRY) ||
|
|
1218
|
+
cleanId.endsWith("\\" + VIRTUAL_CLIENT_ENTRY)) {
|
|
1144
1219
|
return RESOLVED_CLIENT_ENTRY;
|
|
1145
1220
|
}
|
|
1146
1221
|
// App Router virtual modules
|
|
@@ -1150,13 +1225,16 @@ export default function vinext(options = {}) {
|
|
|
1150
1225
|
return RESOLVED_APP_SSR_ENTRY;
|
|
1151
1226
|
if (cleanId === VIRTUAL_APP_BROWSER_ENTRY)
|
|
1152
1227
|
return RESOLVED_APP_BROWSER_ENTRY;
|
|
1153
|
-
if (cleanId.endsWith("/" + VIRTUAL_RSC_ENTRY) ||
|
|
1228
|
+
if (cleanId.endsWith("/" + VIRTUAL_RSC_ENTRY) ||
|
|
1229
|
+
cleanId.endsWith("\\" + VIRTUAL_RSC_ENTRY)) {
|
|
1154
1230
|
return RESOLVED_RSC_ENTRY;
|
|
1155
1231
|
}
|
|
1156
|
-
if (cleanId.endsWith("/" + VIRTUAL_APP_SSR_ENTRY) ||
|
|
1232
|
+
if (cleanId.endsWith("/" + VIRTUAL_APP_SSR_ENTRY) ||
|
|
1233
|
+
cleanId.endsWith("\\" + VIRTUAL_APP_SSR_ENTRY)) {
|
|
1157
1234
|
return RESOLVED_APP_SSR_ENTRY;
|
|
1158
1235
|
}
|
|
1159
|
-
if (cleanId.endsWith("/" + VIRTUAL_APP_BROWSER_ENTRY) ||
|
|
1236
|
+
if (cleanId.endsWith("/" + VIRTUAL_APP_BROWSER_ENTRY) ||
|
|
1237
|
+
cleanId.endsWith("\\" + VIRTUAL_APP_BROWSER_ENTRY)) {
|
|
1160
1238
|
return RESOLVED_APP_BROWSER_ENTRY;
|
|
1161
1239
|
}
|
|
1162
1240
|
},
|
|
@@ -1182,6 +1260,7 @@ export default function vinext(options = {}) {
|
|
|
1182
1260
|
allowedOrigins: nextConfig?.serverActionsAllowedOrigins,
|
|
1183
1261
|
allowedDevOrigins: nextConfig?.allowedDevOrigins,
|
|
1184
1262
|
bodySizeLimit: nextConfig?.serverActionsBodySizeLimit,
|
|
1263
|
+
i18n: nextConfig?.i18n,
|
|
1185
1264
|
}, instrumentationPath);
|
|
1186
1265
|
}
|
|
1187
1266
|
if (id === RESOLVED_APP_SSR_ENTRY && hasAppDir) {
|
|
@@ -1194,6 +1273,8 @@ export default function vinext(options = {}) {
|
|
|
1194
1273
|
},
|
|
1195
1274
|
// Stub node:async_hooks in client builds — see src/plugins/async-hooks-stub.ts
|
|
1196
1275
|
asyncHooksStubPlugin,
|
|
1276
|
+
// Dedup client references from RSC proxy modules — see src/plugins/client-reference-dedup.ts
|
|
1277
|
+
clientReferenceDedupPlugin(),
|
|
1197
1278
|
// Proxy plugin for @mdx-js/rollup. The real MDX plugin is created lazily
|
|
1198
1279
|
// during vinext:config's config() (when MDX files are detected), but
|
|
1199
1280
|
// plugins returned from config() hooks run too late in the pipeline —
|
|
@@ -1448,9 +1529,14 @@ export default function vinext(options = {}) {
|
|
|
1448
1529
|
// loads the module. The gap between them is exactly the Vite
|
|
1449
1530
|
// compile/transform cost.
|
|
1450
1531
|
function _parseTiming(raw) {
|
|
1451
|
-
const [handlerStart, inHandlerCompileMs, renderMs] = String(raw)
|
|
1452
|
-
|
|
1453
|
-
|
|
1532
|
+
const [handlerStart, inHandlerCompileMs, renderMs] = String(raw)
|
|
1533
|
+
.split(",")
|
|
1534
|
+
.map((v) => Number(v));
|
|
1535
|
+
if (!Number.isNaN(handlerStart) &&
|
|
1536
|
+
!Number.isNaN(inHandlerCompileMs) &&
|
|
1537
|
+
inHandlerCompileMs !== -1) {
|
|
1538
|
+
_compileMs =
|
|
1539
|
+
Math.max(0, Math.round(handlerStart - _reqStart)) + inHandlerCompileMs;
|
|
1454
1540
|
}
|
|
1455
1541
|
if (!Number.isNaN(renderMs) && renderMs !== -1) {
|
|
1456
1542
|
_renderMs = renderMs;
|
|
@@ -1496,7 +1582,9 @@ export default function vinext(options = {}) {
|
|
|
1496
1582
|
// matching what Next.js shows for soft navigations.
|
|
1497
1583
|
const resolvedRenderMs = _renderMs !== undefined
|
|
1498
1584
|
? _renderMs
|
|
1499
|
-
:
|
|
1585
|
+
: _compileMs !== undefined
|
|
1586
|
+
? Math.max(0, Math.round(totalMs - _compileMs))
|
|
1587
|
+
: undefined;
|
|
1500
1588
|
logRequest({
|
|
1501
1589
|
method: req.method ?? "GET",
|
|
1502
1590
|
url: logUrl,
|
|
@@ -1633,7 +1721,10 @@ export default function vinext(options = {}) {
|
|
|
1633
1721
|
}
|
|
1634
1722
|
// Normalize trailing slash based on next.config.js trailingSlash setting.
|
|
1635
1723
|
// Redirect to the canonical form if needed.
|
|
1636
|
-
if (nextConfig &&
|
|
1724
|
+
if (nextConfig &&
|
|
1725
|
+
pathname !== "/" &&
|
|
1726
|
+
pathname !== "/api" &&
|
|
1727
|
+
!pathname.startsWith("/api/")) {
|
|
1637
1728
|
const hasTrailing = pathname.endsWith("/");
|
|
1638
1729
|
if (nextConfig.trailingSlash && !hasTrailing) {
|
|
1639
1730
|
// trailingSlash: true — redirect /about → /about/
|
|
@@ -1652,25 +1743,61 @@ export default function vinext(options = {}) {
|
|
|
1652
1743
|
return;
|
|
1653
1744
|
}
|
|
1654
1745
|
}
|
|
1746
|
+
// When @cloudflare/vite-plugin is present, delegate the entire
|
|
1747
|
+
// Pages Router request pipeline to the Worker/miniflare side.
|
|
1748
|
+
// That keeps middleware, headers, redirects, rewrites, API
|
|
1749
|
+
// routes, and rendering in one place instead of mutating the
|
|
1750
|
+
// host request and forwarding post-middleware state downstream.
|
|
1751
|
+
if (hasCloudflarePlugin)
|
|
1752
|
+
return next();
|
|
1753
|
+
// Snapshot of req.headers before middleware runs. Used for both
|
|
1754
|
+
// preMiddlewareReqCtx and the middleware Request itself. Intentionally
|
|
1755
|
+
// captured once here — applyRequestHeadersToNodeRequest() mutates
|
|
1756
|
+
// req.headers later, but by then this Headers object is no longer read.
|
|
1757
|
+
const nodeRequestHeaders = new Headers(Object.fromEntries(Object.entries(req.headers)
|
|
1758
|
+
.filter(([, v]) => v !== undefined)
|
|
1759
|
+
.map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])));
|
|
1760
|
+
const requestOrigin = `http://${req.headers.host || "localhost"}`;
|
|
1761
|
+
const preMiddlewareReqUrl = new URL(url, requestOrigin);
|
|
1762
|
+
const preMiddlewareReqCtx = requestContextFromRequest(new Request(preMiddlewareReqUrl, { headers: nodeRequestHeaders }));
|
|
1763
|
+
// Config redirects run before middleware, but still match against
|
|
1764
|
+
// the original normalized pathname and request headers/cookies.
|
|
1765
|
+
if (nextConfig?.redirects.length) {
|
|
1766
|
+
const redirected = applyRedirects(pathname, res, nextConfig.redirects, preMiddlewareReqCtx, nextConfig.basePath ?? "");
|
|
1767
|
+
if (redirected)
|
|
1768
|
+
return;
|
|
1769
|
+
}
|
|
1770
|
+
const applyRequestHeadersToNodeRequest = (nextRequestHeaders) => {
|
|
1771
|
+
for (const key of Object.keys(req.headers)) {
|
|
1772
|
+
delete req.headers[key];
|
|
1773
|
+
}
|
|
1774
|
+
for (const [key, value] of nextRequestHeaders) {
|
|
1775
|
+
req.headers[key] = value;
|
|
1776
|
+
}
|
|
1777
|
+
};
|
|
1778
|
+
let middlewareRequestHeaders = null;
|
|
1655
1779
|
// Run middleware.ts if present
|
|
1656
1780
|
if (middlewarePath) {
|
|
1657
1781
|
// Only trust X-Forwarded-Proto when behind a trusted proxy
|
|
1658
|
-
const devTrustProxy = process.env.VINEXT_TRUST_PROXY === "1" ||
|
|
1782
|
+
const devTrustProxy = process.env.VINEXT_TRUST_PROXY === "1" ||
|
|
1783
|
+
(process.env.VINEXT_TRUSTED_HOSTS ?? "").split(",").some((h) => h.trim());
|
|
1659
1784
|
const rawProto = devTrustProxy
|
|
1660
|
-
? String(req.headers["x-forwarded-proto"] || "")
|
|
1785
|
+
? String(req.headers["x-forwarded-proto"] || "")
|
|
1786
|
+
.split(",")[0]
|
|
1787
|
+
.trim()
|
|
1661
1788
|
: "";
|
|
1662
1789
|
const mwProto = rawProto === "https" || rawProto === "http" ? rawProto : "http";
|
|
1663
1790
|
const origin = `${mwProto}://${req.headers.host || "localhost"}`;
|
|
1664
1791
|
const middlewareRequest = new Request(new URL(url, origin), {
|
|
1665
1792
|
method: req.method,
|
|
1666
|
-
headers:
|
|
1667
|
-
.filter(([, v]) => v !== undefined)
|
|
1668
|
-
.map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])),
|
|
1793
|
+
headers: nodeRequestHeaders,
|
|
1669
1794
|
});
|
|
1670
|
-
const result = await runMiddleware(getPagesRunner(), middlewarePath, middlewareRequest);
|
|
1795
|
+
const result = await runMiddleware(getPagesRunner(), middlewarePath, middlewareRequest, nextConfig?.i18n);
|
|
1671
1796
|
if (!result.continue) {
|
|
1672
1797
|
if (result.redirectUrl) {
|
|
1673
|
-
const redirectHeaders = {
|
|
1798
|
+
const redirectHeaders = {
|
|
1799
|
+
Location: result.redirectUrl,
|
|
1800
|
+
};
|
|
1674
1801
|
if (result.responseHeaders) {
|
|
1675
1802
|
for (const [key, value] of result.responseHeaders) {
|
|
1676
1803
|
const existing = redirectHeaders[key];
|
|
@@ -1704,13 +1831,21 @@ export default function vinext(options = {}) {
|
|
|
1704
1831
|
// config has/missing conditions and downstream handlers
|
|
1705
1832
|
// see middleware-modified cookies and headers.
|
|
1706
1833
|
if (result.responseHeaders) {
|
|
1707
|
-
const
|
|
1708
|
-
for (const [key, value] of
|
|
1709
|
-
if (
|
|
1710
|
-
|
|
1711
|
-
|
|
1834
|
+
const currentRequestHeaders = new Headers();
|
|
1835
|
+
for (const [key, value] of Object.entries(req.headers)) {
|
|
1836
|
+
if (Array.isArray(value)) {
|
|
1837
|
+
currentRequestHeaders.set(key, value.join(", "));
|
|
1838
|
+
}
|
|
1839
|
+
else if (value !== undefined) {
|
|
1840
|
+
currentRequestHeaders.set(key, value);
|
|
1712
1841
|
}
|
|
1713
|
-
|
|
1842
|
+
}
|
|
1843
|
+
middlewareRequestHeaders = buildRequestHeadersFromMiddlewareResponse(currentRequestHeaders, result.responseHeaders);
|
|
1844
|
+
if (middlewareRequestHeaders && !hasAppDir) {
|
|
1845
|
+
applyRequestHeadersToNodeRequest(middlewareRequestHeaders);
|
|
1846
|
+
}
|
|
1847
|
+
for (const [key, value] of result.responseHeaders) {
|
|
1848
|
+
if (!key.startsWith("x-middleware-")) {
|
|
1714
1849
|
res.appendHeader(key, value);
|
|
1715
1850
|
}
|
|
1716
1851
|
}
|
|
@@ -1732,52 +1867,25 @@ export default function vinext(options = {}) {
|
|
|
1732
1867
|
req.__vinextRewriteStatus = result.rewriteStatus;
|
|
1733
1868
|
}
|
|
1734
1869
|
}
|
|
1735
|
-
// ── Cloudflare Workers dev mode ────────────────────────────
|
|
1736
|
-
// When @cloudflare/vite-plugin is present, ALL rendering runs
|
|
1737
|
-
// inside the miniflare Worker subprocess — both App Router (via
|
|
1738
|
-
// virtual:vinext-rsc-entry) and Pages Router (via
|
|
1739
|
-
// virtual:vinext-server-entry → renderPage/handleApiRoute).
|
|
1740
|
-
//
|
|
1741
|
-
// The Worker entry already handles config redirects, rewrites,
|
|
1742
|
-
// headers, and all routing internally. Running them here too
|
|
1743
|
-
// would duplicate that logic and produce incorrect behaviour
|
|
1744
|
-
// (double redirects, headers set on the wrong object, etc.).
|
|
1745
|
-
//
|
|
1746
|
-
// Middleware.ts is the only thing that belongs in the host connect
|
|
1747
|
-
// handler — it has already run above. Any terminal middleware
|
|
1748
|
-
// result (redirect, block response) has already been sent.
|
|
1749
|
-
// Any rewrite has been written back to req.url above so the
|
|
1750
|
-
// Cloudflare plugin's handler sees the correct path.
|
|
1751
|
-
//
|
|
1752
|
-
// Call next() to hand off to the Cloudflare plugin's connect
|
|
1753
|
-
// handler, which dispatches the request to miniflare.
|
|
1754
|
-
if (hasCloudflarePlugin)
|
|
1755
|
-
return next();
|
|
1756
1870
|
// Build request context once for has/missing condition checks
|
|
1757
|
-
//
|
|
1871
|
+
// for config rules that execute after middleware (rewrites).
|
|
1758
1872
|
// Convert Node.js IncomingMessage headers to a Web Request for
|
|
1759
1873
|
// requestContextFromRequest(), which uses the standard Web API.
|
|
1760
|
-
const reqUrl = new URL(url,
|
|
1761
|
-
const reqCtxHeaders =
|
|
1762
|
-
.filter(([, v]) => v !== undefined)
|
|
1763
|
-
.map(([k, v]) => [k, Array.isArray(v) ? v.join(", ") : String(v)])));
|
|
1874
|
+
const reqUrl = new URL(url, requestOrigin);
|
|
1875
|
+
const reqCtxHeaders = middlewareRequestHeaders ?? nodeRequestHeaders;
|
|
1764
1876
|
const reqCtx = requestContextFromRequest(new Request(reqUrl, { headers: reqCtxHeaders }));
|
|
1765
1877
|
// Apply custom headers from next.config.js
|
|
1878
|
+
// Header matching still uses the original normalized pathname and
|
|
1879
|
+
// pre-middleware request state; middleware response headers win
|
|
1880
|
+
// later because they are already on the outgoing response.
|
|
1766
1881
|
if (nextConfig?.headers.length) {
|
|
1767
|
-
applyHeaders(pathname, res, nextConfig.headers,
|
|
1768
|
-
}
|
|
1769
|
-
// Apply redirects from next.config.js
|
|
1770
|
-
if (nextConfig?.redirects.length) {
|
|
1771
|
-
const redirected = applyRedirects(pathname, res, nextConfig.redirects, reqCtx);
|
|
1772
|
-
if (redirected)
|
|
1773
|
-
return;
|
|
1882
|
+
applyHeaders(pathname, res, nextConfig.headers, preMiddlewareReqCtx);
|
|
1774
1883
|
}
|
|
1775
1884
|
// Apply rewrites from next.config.js (beforeFiles)
|
|
1776
1885
|
let resolvedUrl = url;
|
|
1777
1886
|
if (nextConfig?.rewrites.beforeFiles.length) {
|
|
1778
1887
|
resolvedUrl =
|
|
1779
|
-
applyRewrites(pathname, nextConfig.rewrites.beforeFiles, reqCtx) ??
|
|
1780
|
-
url;
|
|
1888
|
+
applyRewrites(pathname, nextConfig.rewrites.beforeFiles, reqCtx) ?? url;
|
|
1781
1889
|
}
|
|
1782
1890
|
// External rewrite from beforeFiles — proxy to external URL
|
|
1783
1891
|
if (isExternalUrl(resolvedUrl)) {
|
|
@@ -1786,9 +1894,12 @@ export default function vinext(options = {}) {
|
|
|
1786
1894
|
}
|
|
1787
1895
|
// Handle API routes first (pages/api/*)
|
|
1788
1896
|
const resolvedPathname = resolvedUrl.split("?")[0];
|
|
1789
|
-
if (resolvedPathname.startsWith("/api/") ||
|
|
1790
|
-
resolvedPathname === "/api") {
|
|
1897
|
+
if (resolvedPathname.startsWith("/api/") || resolvedPathname === "/api") {
|
|
1791
1898
|
const apiRoutes = await apiRouter(pagesDir, nextConfig?.pageExtensions, fileMatcher);
|
|
1899
|
+
const apiMatch = matchRoute(resolvedUrl, apiRoutes);
|
|
1900
|
+
if (apiMatch && middlewareRequestHeaders) {
|
|
1901
|
+
applyRequestHeadersToNodeRequest(middlewareRequestHeaders);
|
|
1902
|
+
}
|
|
1792
1903
|
const handled = await handleApiRoute(server, req, res, resolvedUrl, apiRoutes);
|
|
1793
1904
|
if (handled)
|
|
1794
1905
|
return;
|
|
@@ -1820,6 +1931,9 @@ export default function vinext(options = {}) {
|
|
|
1820
1931
|
// Try rendering the resolved URL
|
|
1821
1932
|
const match = matchRoute(resolvedUrl.split("?")[0], routes);
|
|
1822
1933
|
if (match) {
|
|
1934
|
+
if (middlewareRequestHeaders) {
|
|
1935
|
+
applyRequestHeadersToNodeRequest(middlewareRequestHeaders);
|
|
1936
|
+
}
|
|
1823
1937
|
await handler(req, res, resolvedUrl, mwStatus);
|
|
1824
1938
|
return;
|
|
1825
1939
|
}
|
|
@@ -1832,6 +1946,13 @@ export default function vinext(options = {}) {
|
|
|
1832
1946
|
await proxyExternalRewriteNode(req, res, fallbackRewrite);
|
|
1833
1947
|
return;
|
|
1834
1948
|
}
|
|
1949
|
+
const fallbackMatch = matchRoute(fallbackRewrite.split("?")[0], routes);
|
|
1950
|
+
if (!fallbackMatch && hasAppDir) {
|
|
1951
|
+
return next();
|
|
1952
|
+
}
|
|
1953
|
+
if (middlewareRequestHeaders) {
|
|
1954
|
+
applyRequestHeadersToNodeRequest(middlewareRequestHeaders);
|
|
1955
|
+
}
|
|
1835
1956
|
await handler(req, res, fallbackRewrite, mwStatus);
|
|
1836
1957
|
return;
|
|
1837
1958
|
}
|
|
@@ -2032,7 +2153,10 @@ export default function vinext(options = {}) {
|
|
|
2032
2153
|
const importMatch = code.match(importRe);
|
|
2033
2154
|
if (!importMatch)
|
|
2034
2155
|
return null;
|
|
2035
|
-
const importedNames = new Set(importMatch[1]
|
|
2156
|
+
const importedNames = new Set(importMatch[1]
|
|
2157
|
+
.split(",")
|
|
2158
|
+
.map((s) => s.trim())
|
|
2159
|
+
.filter(Boolean));
|
|
2036
2160
|
const s = new MagicString(code);
|
|
2037
2161
|
let hasChanges = false;
|
|
2038
2162
|
const cacheDir = this._cacheDir;
|
|
@@ -2058,10 +2182,14 @@ export default function vinext(options = {}) {
|
|
|
2058
2182
|
}
|
|
2059
2183
|
// Build the Google Fonts CSS URL
|
|
2060
2184
|
const weights = options.weight
|
|
2061
|
-
? Array.isArray(options.weight)
|
|
2185
|
+
? Array.isArray(options.weight)
|
|
2186
|
+
? options.weight
|
|
2187
|
+
: [options.weight]
|
|
2062
2188
|
: [];
|
|
2063
2189
|
const styles = options.style
|
|
2064
|
-
? Array.isArray(options.style)
|
|
2190
|
+
? Array.isArray(options.style)
|
|
2191
|
+
? options.style
|
|
2192
|
+
: [options.style]
|
|
2065
2193
|
: [];
|
|
2066
2194
|
const display = options.display ?? "swap";
|
|
2067
2195
|
let spec = family.replace(/\s+/g, "+");
|
|
@@ -2217,7 +2345,9 @@ export default function vinext(options = {}) {
|
|
|
2217
2345
|
return null;
|
|
2218
2346
|
if (!resolvedRscTransformsPath) {
|
|
2219
2347
|
throw new Error("vinext: 'use cache' requires @vitejs/plugin-rsc to be installed.\n" +
|
|
2220
|
-
"Run: " +
|
|
2348
|
+
"Run: " +
|
|
2349
|
+
detectPackageManager(process.cwd()) +
|
|
2350
|
+
" @vitejs/plugin-rsc");
|
|
2221
2351
|
}
|
|
2222
2352
|
const { transformWrapExport, transformHoistInlineDirective } = await import(pathToFileURL(resolvedRscTransformsPath).href);
|
|
2223
2353
|
const ast = parseAst(code);
|
|
@@ -2232,7 +2362,9 @@ export default function vinext(options = {}) {
|
|
|
2232
2362
|
// (they're leaf components). Layout/template defaults are excluded
|
|
2233
2363
|
// because they receive {children} from the framework.
|
|
2234
2364
|
const directiveValue = cacheDirective.expression.value;
|
|
2235
|
-
const variant = directiveValue === "use cache"
|
|
2365
|
+
const variant = directiveValue === "use cache"
|
|
2366
|
+
? ""
|
|
2367
|
+
: directiveValue.replace("use cache:", "").replace("use cache: ", "").trim();
|
|
2236
2368
|
// Only skip default export wrapping for layouts and templates —
|
|
2237
2369
|
// they receive {children} from the framework which requires
|
|
2238
2370
|
// temporary reference handling that registerCachedFunction doesn't
|
|
@@ -2284,7 +2416,9 @@ export default function vinext(options = {}) {
|
|
|
2284
2416
|
directive: /^use cache(:\s*\w+)?$/,
|
|
2285
2417
|
runtime: (value, name, meta) => {
|
|
2286
2418
|
const directiveMatch = meta.directiveMatch[0];
|
|
2287
|
-
const variant = directiveMatch === "use cache"
|
|
2419
|
+
const variant = directiveMatch === "use cache"
|
|
2420
|
+
? ""
|
|
2421
|
+
: directiveMatch.replace("use cache:", "").replace("use cache: ", "").trim();
|
|
2288
2422
|
return `(await import(${JSON.stringify(runtimeModuleUrl2)})).registerCachedFunction(${value}, ${JSON.stringify(id + ":" + name)}, ${JSON.stringify(variant)})`;
|
|
2289
2423
|
},
|
|
2290
2424
|
rejectNonAsyncFunction: false,
|
|
@@ -2429,11 +2563,9 @@ export default function vinext(options = {}) {
|
|
|
2429
2563
|
const content = fs.readFileSync(indexPath, "utf-8");
|
|
2430
2564
|
// The font is inlined as base64 by vinext:og-inline-fetch-assets, so only
|
|
2431
2565
|
// the WASM needs to be present as a file alongside the bundle.
|
|
2432
|
-
const ogAssets = [
|
|
2433
|
-
"resvg.wasm",
|
|
2434
|
-
];
|
|
2566
|
+
const ogAssets = ["resvg.wasm"];
|
|
2435
2567
|
// Only copy if the bundle actually references these files
|
|
2436
|
-
const referencedAssets = ogAssets.filter(asset => content.includes(asset));
|
|
2568
|
+
const referencedAssets = ogAssets.filter((asset) => content.includes(asset));
|
|
2437
2569
|
if (referencedAssets.length === 0)
|
|
2438
2570
|
return;
|
|
2439
2571
|
// Find @vercel/og in node_modules
|
|
@@ -2482,6 +2614,38 @@ export default function vinext(options = {}) {
|
|
|
2482
2614
|
},
|
|
2483
2615
|
},
|
|
2484
2616
|
},
|
|
2617
|
+
// Vite can emit empty SSR manifest entries for modules that Rollup inlines
|
|
2618
|
+
// into another chunk. Pages Router looks up assets by page module path at
|
|
2619
|
+
// runtime, so rebuild those mappings from the emitted client bundle.
|
|
2620
|
+
{
|
|
2621
|
+
name: "vinext:ssr-manifest-backfill",
|
|
2622
|
+
apply: "build",
|
|
2623
|
+
enforce: "post",
|
|
2624
|
+
writeBundle: {
|
|
2625
|
+
sequential: true,
|
|
2626
|
+
order: "post",
|
|
2627
|
+
handler(options, bundle) {
|
|
2628
|
+
const outDir = options.dir;
|
|
2629
|
+
if (!outDir)
|
|
2630
|
+
return;
|
|
2631
|
+
const viteDir = path.join(outDir, ".vite");
|
|
2632
|
+
const ssrManifestPath = path.join(viteDir, "ssr-manifest.json");
|
|
2633
|
+
if (!fs.existsSync(ssrManifestPath))
|
|
2634
|
+
return;
|
|
2635
|
+
try {
|
|
2636
|
+
const ssrManifest = JSON.parse(fs.readFileSync(ssrManifestPath, "utf-8"));
|
|
2637
|
+
const buildRoot = this.environment?.config.root ?? process.cwd();
|
|
2638
|
+
const buildBase = this.environment?.config.base ?? "/";
|
|
2639
|
+
const augmentedManifest = augmentSsrManifestFromBundle(ssrManifest, bundle, buildRoot, buildBase);
|
|
2640
|
+
fs.writeFileSync(ssrManifestPath, JSON.stringify(augmentedManifest, null, 2));
|
|
2641
|
+
}
|
|
2642
|
+
catch (err) {
|
|
2643
|
+
// Leave Vite's manifest untouched if parsing fails.
|
|
2644
|
+
console.warn("[vinext] Failed to augment SSR manifest:", err);
|
|
2645
|
+
}
|
|
2646
|
+
},
|
|
2647
|
+
},
|
|
2648
|
+
},
|
|
2485
2649
|
// Cloudflare Workers production build integration:
|
|
2486
2650
|
// After all environments are built, compute lazy chunks from the client
|
|
2487
2651
|
// build manifest and inject globals into the worker entry.
|
|
@@ -2513,6 +2677,7 @@ export default function vinext(options = {}) {
|
|
|
2513
2677
|
if (!fs.existsSync(distDir))
|
|
2514
2678
|
return;
|
|
2515
2679
|
const clientDir = path.resolve(buildRoot, "dist", "client");
|
|
2680
|
+
const clientBase = envConfig.base ?? "/";
|
|
2516
2681
|
// Read build manifest and compute lazy chunks (only reachable via
|
|
2517
2682
|
// dynamic imports). This runs for BOTH App Router and Pages Router.
|
|
2518
2683
|
// clientEntryFile is only used by the Pages Router path below —
|
|
@@ -2525,15 +2690,17 @@ export default function vinext(options = {}) {
|
|
|
2525
2690
|
const buildManifest = JSON.parse(fs.readFileSync(buildManifestPath, "utf-8"));
|
|
2526
2691
|
for (const [, value] of Object.entries(buildManifest)) {
|
|
2527
2692
|
if (value && value.isEntry && value.file) {
|
|
2528
|
-
clientEntryFile = value.file;
|
|
2693
|
+
clientEntryFile = manifestFileWithBase(value.file, clientBase);
|
|
2529
2694
|
break;
|
|
2530
2695
|
}
|
|
2531
2696
|
}
|
|
2532
|
-
const lazy = computeLazyChunks(buildManifest);
|
|
2697
|
+
const lazy = manifestFilesWithBase(computeLazyChunks(buildManifest), clientBase);
|
|
2533
2698
|
if (lazy.length > 0)
|
|
2534
2699
|
lazyChunksData = lazy;
|
|
2535
2700
|
}
|
|
2536
|
-
catch {
|
|
2701
|
+
catch {
|
|
2702
|
+
/* ignore parse errors */
|
|
2703
|
+
}
|
|
2537
2704
|
}
|
|
2538
2705
|
// Read SSR manifest for per-page CSS/JS injection
|
|
2539
2706
|
let ssrManifestData = null;
|
|
@@ -2542,7 +2709,9 @@ export default function vinext(options = {}) {
|
|
|
2542
2709
|
try {
|
|
2543
2710
|
ssrManifestData = JSON.parse(fs.readFileSync(ssrManifestPath, "utf-8"));
|
|
2544
2711
|
}
|
|
2545
|
-
catch {
|
|
2712
|
+
catch {
|
|
2713
|
+
/* ignore parse errors */
|
|
2714
|
+
}
|
|
2546
2715
|
}
|
|
2547
2716
|
if (hasAppDir) {
|
|
2548
2717
|
// App Router: the RSC plugin handles __VINEXT_CLIENT_ENTRY__
|
|
@@ -2589,9 +2758,10 @@ export default function vinext(options = {}) {
|
|
|
2589
2758
|
const assetsDir = path.join(clientDir, "assets");
|
|
2590
2759
|
if (fs.existsSync(assetsDir)) {
|
|
2591
2760
|
const files = fs.readdirSync(assetsDir);
|
|
2592
|
-
const entry = files.find((f) => (f.includes("vinext-client-entry") || f.includes("vinext-app-browser-entry")) &&
|
|
2761
|
+
const entry = files.find((f) => (f.includes("vinext-client-entry") || f.includes("vinext-app-browser-entry")) &&
|
|
2762
|
+
f.endsWith(".js"));
|
|
2593
2763
|
if (entry)
|
|
2594
|
-
clientEntryFile = "assets/" + entry;
|
|
2764
|
+
clientEntryFile = manifestFileWithBase("assets/" + entry, clientBase);
|
|
2595
2765
|
}
|
|
2596
2766
|
}
|
|
2597
2767
|
// Prepend globals to worker entry
|
|
@@ -2722,7 +2892,7 @@ export { matchConfigPattern } from "./config/config-matchers.js";
|
|
|
2722
2892
|
*/
|
|
2723
2893
|
function stripServerExports(code) {
|
|
2724
2894
|
const SERVER_EXPORTS = new Set(["getServerSideProps", "getStaticProps", "getStaticPaths"]);
|
|
2725
|
-
if (![...SERVER_EXPORTS].some(name => code.includes(name)))
|
|
2895
|
+
if (![...SERVER_EXPORTS].some((name) => code.includes(name)))
|
|
2726
2896
|
return null;
|
|
2727
2897
|
let ast;
|
|
2728
2898
|
try {
|
|
@@ -2773,11 +2943,13 @@ function stripServerExports(code) {
|
|
|
2773
2943
|
// Build replacement: keep non-server specifiers, add stubs for stripped ones
|
|
2774
2944
|
const parts = [];
|
|
2775
2945
|
if (kept.length > 0) {
|
|
2776
|
-
const keptStr = kept
|
|
2946
|
+
const keptStr = kept
|
|
2947
|
+
.map((sp) => {
|
|
2777
2948
|
const local = sp.local.name;
|
|
2778
2949
|
const exported = sp.exported?.name ?? sp.exported?.value;
|
|
2779
2950
|
return local === exported ? local : `${local} as ${exported}`;
|
|
2780
|
-
})
|
|
2951
|
+
})
|
|
2952
|
+
.join(", ");
|
|
2781
2953
|
parts.push(`export { ${keptStr} };`);
|
|
2782
2954
|
}
|
|
2783
2955
|
for (const name of stripped) {
|
|
@@ -2796,11 +2968,13 @@ function stripServerExports(code) {
|
|
|
2796
2968
|
* Apply redirect rules from next.config.js.
|
|
2797
2969
|
* Returns true if a redirect was applied.
|
|
2798
2970
|
*/
|
|
2799
|
-
function applyRedirects(pathname, res, redirects, ctx) {
|
|
2971
|
+
function applyRedirects(pathname, res, redirects, ctx, basePath = "") {
|
|
2800
2972
|
const result = matchRedirect(pathname, redirects, ctx);
|
|
2801
2973
|
if (result) {
|
|
2802
2974
|
// Sanitize to prevent open redirect via protocol-relative URLs
|
|
2803
|
-
const dest = sanitizeDestination(result.destination)
|
|
2975
|
+
const dest = sanitizeDestination(basePath && !isExternalUrl(result.destination) && !hasBasePath(result.destination, basePath)
|
|
2976
|
+
? basePath + result.destination
|
|
2977
|
+
: result.destination);
|
|
2804
2978
|
res.writeHead(result.permanent ? 308 : 307, { Location: dest });
|
|
2805
2979
|
res.end();
|
|
2806
2980
|
return true;
|
|
@@ -2838,9 +3012,7 @@ async function proxyExternalRewriteNode(req, res, externalUrl) {
|
|
|
2838
3012
|
proxyResponse.headers.forEach((value, key) => {
|
|
2839
3013
|
const existing = nodeHeaders[key];
|
|
2840
3014
|
if (existing !== undefined) {
|
|
2841
|
-
nodeHeaders[key] = Array.isArray(existing)
|
|
2842
|
-
? [...existing, value]
|
|
2843
|
-
: [existing, value];
|
|
3015
|
+
nodeHeaders[key] = Array.isArray(existing) ? [...existing, value] : [existing, value];
|
|
2844
3016
|
}
|
|
2845
3017
|
else {
|
|
2846
3018
|
nodeHeaders[key] = value;
|
|
@@ -2968,6 +3140,7 @@ function scanDirForMdx(dir) {
|
|
|
2968
3140
|
export { staticExportPages, staticExportApp } from "./build/static-export.js";
|
|
2969
3141
|
// Exported for CLI and testing
|
|
2970
3142
|
export { clientManualChunks, clientOutputConfig, clientTreeshakeConfig, computeLazyChunks };
|
|
3143
|
+
export { augmentSsrManifestFromBundle as _augmentSsrManifestFromBundle };
|
|
2971
3144
|
export { resolvePostcssStringPlugins as _resolvePostcssStringPlugins };
|
|
2972
3145
|
export { parseStaticObjectLiteral as _parseStaticObjectLiteral };
|
|
2973
3146
|
export { stripServerExports as _stripServerExports };
|