vinext 0.2.1 → 1.0.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -30
- package/dist/build/assets-ignore.js +1 -1
- package/dist/build/clean-output.js +1 -1
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +23 -99
- package/dist/build/css-url-assets.js +3 -3
- package/dist/build/google-fonts/fallback-metrics.js +1 -1
- package/dist/build/google-fonts/font-metadata.js +1 -1
- package/dist/build/inject-pregenerated-paths.js +1 -1
- package/dist/build/inline-css.js +2 -5
- package/dist/build/module-dependency-cache.d.ts +4 -0
- package/dist/build/module-dependency-cache.js +13 -0
- package/dist/build/next-client-runtime-manifests.js +1 -1
- package/dist/build/pages-client-assets-module.js +1 -1
- package/dist/build/precompress.js +7 -7
- package/dist/build/prerender-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +291 -0
- package/dist/build/prerender-server-pool.js +2 -2
- package/dist/build/prerender.d.ts +12 -1
- package/dist/build/prerender.js +2 -2
- package/dist/build/preview-credentials.d.ts +11 -0
- package/dist/build/preview-credentials.js +19 -0
- package/dist/build/report.d.ts +0 -3
- package/dist/build/report.js +2 -4
- package/dist/build/run-prerender.d.ts +2 -11
- package/dist/build/run-prerender.js +2 -10
- package/dist/build/server-manifest.js +1 -1
- package/dist/build/ssr-manifest.d.ts +4 -0
- package/dist/build/ssr-manifest.js +10 -6
- package/dist/build/standalone.js +37 -37
- package/dist/cache/cache-adapters-virtual.d.ts +7 -17
- package/dist/cache/cache-adapters-virtual.js +35 -2
- package/dist/check.d.ts +0 -16
- package/dist/check.js +17 -34
- package/dist/cli.js +86 -101
- package/dist/client/instrumentation-client-inject.js +1 -1
- package/dist/client/pages-router-link-navigation.js +1 -1
- package/dist/config/config-matchers.js +2 -2
- package/dist/config/dotenv.js +1 -1
- package/dist/config/next-config.d.ts +4 -1
- package/dist/config/next-config.js +82 -16
- package/dist/config/prerender.d.ts +12 -2
- package/dist/config/prerender.js +22 -12
- package/dist/config/server-external-packages.js +1 -1
- package/dist/config/tsconfig-paths.js +7 -3
- package/dist/{node_modules/.pnpm/am-i-vibing@0.5.0/node_modules → deps/.pnpm/am-i-vibing@0.5.0/deps}/am-i-vibing/dist/detector-1yx2Hoe0.js +2 -2
- package/dist/deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js +50 -0
- package/dist/entries/app-rsc-entry.js +12 -5
- package/dist/entries/app-rsc-manifest.js +4 -4
- package/dist/entries/pages-client-entry.js +6 -5
- package/dist/entries/pages-server-entry.js +15 -26
- package/dist/entries/runtime-entry-module.js +2 -2
- package/dist/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +500 -338
- package/dist/init-cloudflare.js +13 -4
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +79 -13
- package/dist/init.d.ts +11 -3
- package/dist/init.js +133 -35
- package/dist/plugins/ast-utils.js +1 -1
- package/dist/plugins/dynamic-preload-metadata.js +7 -7
- package/dist/plugins/extensionless-dynamic-import.js +1 -1
- package/dist/plugins/fonts.js +3 -3
- package/dist/plugins/ignore-dynamic-requests.js +13 -8
- package/dist/plugins/import-meta-url.js +16 -17
- package/dist/plugins/middleware-export-validation.js +1 -1
- package/dist/plugins/middleware-server-only.js +2 -2
- package/dist/plugins/og-asset-ownership.js +7 -6
- package/dist/plugins/og-assets.js +4 -5
- package/dist/plugins/optimize-imports.js +12 -13
- package/dist/plugins/postcss.js +1 -1
- package/dist/plugins/remove-console.js +1 -1
- package/dist/plugins/require-context.js +1 -1
- package/dist/plugins/sass.js +2 -2
- package/dist/plugins/server-externals-manifest.js +1 -1
- package/dist/plugins/strip-server-exports.js +1 -1
- package/dist/plugins/styled-jsx.js +3 -2
- package/dist/plugins/typeof-window.js +1 -1
- package/dist/routing/app-route-graph.d.ts +3 -10
- package/dist/routing/app-route-graph.js +28 -76
- package/dist/routing/app-router.d.ts +0 -5
- package/dist/routing/app-router.js +0 -5
- package/dist/routing/file-matcher.d.ts +2 -6
- package/dist/routing/file-matcher.js +10 -11
- package/dist/routing/pages-router.js +3 -3
- package/dist/server/api-handler.js +41 -33
- package/dist/server/app-browser-entry.js +15 -11
- package/dist/server/app-browser-navigation-controller.js +9 -4
- package/dist/server/app-browser-server-action-client.js +1 -1
- package/dist/server/app-elements-wire.d.ts +2 -1
- package/dist/server/app-fallback-renderer.d.ts +8 -0
- package/dist/server/app-fallback-renderer.js +13 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-page-boundary-render.d.ts +14 -1
- package/dist/server/app-page-boundary-render.js +107 -54
- package/dist/server/app-page-cache-render.d.ts +1 -1
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +29 -40
- package/dist/server/app-page-element-builder.d.ts +2 -1
- package/dist/server/app-page-element-builder.js +3 -3
- package/dist/server/app-page-execution.d.ts +3 -1
- package/dist/server/app-page-execution.js +31 -39
- package/dist/server/app-page-render.d.ts +1 -1
- package/dist/server/app-page-request.js +1 -1
- package/dist/server/app-page-route-wiring.js +4 -4
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-tree-prefetch.js +1 -1
- package/dist/server/app-rsc-cache-busting.js +1 -2
- package/dist/server/app-rsc-handler.js +2 -2
- package/dist/server/app-rsc-redirect-flight.d.ts +66 -0
- package/dist/server/app-rsc-redirect-flight.js +71 -0
- package/dist/server/app-rsc-render-mode.d.ts +2 -5
- package/dist/server/app-rsc-render-mode.js +2 -12
- package/dist/server/app-segment-config.js +2 -2
- package/dist/server/app-server-action-execution.js +3 -3
- package/dist/server/app-ssr-entry.js +4 -1
- package/dist/server/app-ssr-stream.d.ts +2 -1
- package/dist/server/app-ssr-stream.js +4 -1
- package/dist/server/cache-proof.js +1 -1
- package/dist/server/default-global-error-module.d.ts +3 -1
- package/dist/server/default-not-found-module.d.ts +3 -1
- package/dist/server/dev-lockfile.js +2 -3
- package/dist/server/dev-module-runner.js +1 -1
- package/dist/server/dev-origin-check.d.ts +2 -2
- package/dist/server/dev-origin-check.js +2 -2
- package/dist/server/dev-route-files.js +1 -1
- package/dist/server/dev-server.js +256 -164
- package/dist/server/dev-stack-sourcemap.js +4 -3
- package/dist/server/headers.d.ts +3 -1
- package/dist/server/headers.js +3 -1
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +15 -3
- package/dist/server/implicit-tags.js +2 -1
- package/dist/server/instrumentation.js +2 -2
- package/dist/server/isr-cache.d.ts +12 -4
- package/dist/server/isr-cache.js +3 -3
- package/dist/server/metadata-routes.js +4 -4
- package/dist/server/middleware-response-headers.js +1 -1
- package/dist/server/middleware-runtime.js +1 -1
- package/dist/server/middleware.d.ts +1 -0
- package/dist/server/middleware.js +1 -1
- package/dist/server/navigation-planner.d.ts +2 -0
- package/dist/server/navigation-planner.js +8 -2
- package/dist/server/next-error-digest.d.ts +9 -24
- package/dist/server/next-error-digest.js +23 -15
- package/dist/server/operation-token.js +1 -1
- package/dist/server/pages-body-parser-config.js +2 -1
- package/dist/server/pages-dev-hydration.d.ts +14 -0
- package/dist/server/pages-dev-hydration.js +83 -0
- package/dist/server/pages-dev-module-url.js +2 -2
- package/dist/server/pages-node-compat.d.ts +13 -6
- package/dist/server/pages-node-compat.js +44 -45
- package/dist/server/pages-page-data.d.ts +1 -1
- package/dist/server/pages-page-data.js +5 -4
- package/dist/server/pages-page-handler.js +61 -29
- package/dist/server/pages-page-response.d.ts +2 -2
- package/dist/server/pages-page-response.js +2 -2
- package/dist/server/pages-preview.d.ts +26 -0
- package/dist/server/pages-preview.js +165 -0
- package/dist/server/pages-readiness.d.ts +2 -1
- package/dist/server/pages-readiness.js +4 -2
- package/dist/server/pages-request-pipeline.js +1 -1
- package/dist/server/prerender-manifest.d.ts +15 -1
- package/dist/server/prerender-manifest.js +29 -1
- package/dist/server/prod-server.d.ts +6 -5
- package/dist/server/prod-server.js +15 -15
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/seed-cache.js +1 -1
- package/dist/server/socket-error-backstop.js +1 -1
- package/dist/server/static-file-cache.js +8 -9
- package/dist/server/worker-utils.js +1 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +2 -1
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/constants.js +1 -1
- package/dist/shims/error-boundary.d.ts +4 -4
- package/dist/shims/error.d.ts +21 -5
- package/dist/shims/error.js +52 -14
- package/dist/shims/fetch-cache.js +1 -1
- package/dist/shims/font-utils.js +1 -1
- package/dist/shims/form.d.ts +1 -1
- package/dist/shims/head.js +3 -3
- package/dist/shims/headers.js +6 -4
- package/dist/shims/internal/pages-data-fetch-dedup.js +5 -3
- package/dist/shims/layout-segment-context.d.ts +1 -1
- package/dist/shims/link.d.ts +3 -1
- package/dist/shims/link.js +6 -3
- package/dist/shims/navigation-errors.d.ts +2 -1
- package/dist/shims/navigation-errors.js +9 -13
- package/dist/shims/navigation.d.ts +1 -1
- package/dist/shims/navigation.js +5 -5
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router-state.d.ts +1 -0
- package/dist/shims/router.d.ts +2 -1
- package/dist/shims/router.js +18 -11
- package/dist/shims/script.js +1 -1
- package/dist/shims/server.js +2 -2
- package/dist/shims/slot.d.ts +1 -1
- package/dist/shims/thenable-params.js +1 -1
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/typegen.js +9 -11
- package/dist/utils/client-build-manifest.js +2 -2
- package/dist/utils/client-entry-manifest.js +1 -1
- package/dist/utils/client-runtime-metadata.js +1 -1
- package/dist/utils/commonjs-loader.js +3 -3
- package/dist/utils/mdx-scan.js +1 -1
- package/dist/utils/path.d.ts +1 -13
- package/dist/utils/path.js +1 -15
- package/dist/utils/plugin-options.d.ts +4 -0
- package/dist/utils/plugin-options.js +8 -0
- package/dist/utils/project.d.ts +1 -7
- package/dist/utils/project.js +2 -8
- package/dist/utils/public-routes.js +2 -2
- package/dist/utils/react-version.js +1 -1
- package/dist/utils/redirect-digest.d.ts +9 -0
- package/dist/utils/redirect-digest.js +25 -0
- package/dist/utils/vinext-root.js +1 -1
- package/dist/utils/vite-version.d.ts +6 -13
- package/dist/utils/vite-version.js +68 -34
- package/package.json +33 -14
- package/dist/cloudflare/index.d.ts +0 -3
- package/dist/cloudflare/index.js +0 -3
- package/dist/packages/cloudflare/src/cache/cdn-adapter.runtime.js +0 -102
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +0 -126
- package/dist/packages/cloudflare/src/cache/kv-data-adapter.runtime.js +0 -442
- package/dist/packages/cloudflare/src/deploy-config.js +0 -150
- package/dist/packages/cloudflare/src/deploy-help.js +0 -55
- package/dist/packages/cloudflare/src/deploy.js +0 -272
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -582
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
- /package/dist/build/google-fonts/{fallback-metrics-data.js → fallback-metrics-data.json.js} +0 -0
- /package/dist/build/google-fonts/{font-data.js → font-data.json.js} +0 -0
- /package/dist/{node_modules/.pnpm/process-ancestry@0.1.0/node_modules → deps/.pnpm/process-ancestry@0.1.0/deps}/process-ancestry/dist/index.js +0 -0
|
@@ -1,10 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
3
3
|
import { findFileWithExts, scanWithExtensions } from "./file-matcher.js";
|
|
4
4
|
import { validateRoutePatterns } from "./route-validation.js";
|
|
5
5
|
import { compareStrings } from "../utils/compare.js";
|
|
6
6
|
import fs from "node:fs";
|
|
7
|
-
import path from "node:path";
|
|
8
7
|
import { createHash } from "node:crypto";
|
|
9
8
|
//#region src/routing/app-route-graph.ts
|
|
10
9
|
/**
|
|
@@ -363,10 +362,6 @@ function createRouteManifestGraphVersion(segmentGraph) {
|
|
|
363
362
|
}
|
|
364
363
|
/**
|
|
365
364
|
* Build the App Router route graph by scanning `appDir`.
|
|
366
|
-
*
|
|
367
|
-
* `appDir` must be forward-slash. Every path in the graph is derived from it
|
|
368
|
-
* with `path.posix.*` and `findFile`, so a native appDir would produce mixed
|
|
369
|
-
* separators on Windows. Production callers normalize it at their entry.
|
|
370
365
|
*/
|
|
371
366
|
async function buildAppRouteGraph(appDir, matcher) {
|
|
372
367
|
const routes = [];
|
|
@@ -384,8 +379,8 @@ async function buildAppRouteGraph(appDir, matcher) {
|
|
|
384
379
|
const routePatterns = new Set(routes.map((route) => route.pattern));
|
|
385
380
|
const ghostParentRoutes = [];
|
|
386
381
|
for await (const file of scanWithExtensions("**/layout", appDir, scanMatcher.extensions, excludeDir)) {
|
|
387
|
-
const dir = path.
|
|
388
|
-
const routeDir = dir === "." ? appDir : path.
|
|
382
|
+
const dir = path.dirname(file);
|
|
383
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
389
384
|
if (!hasParallelSlotDirectory(routeDir)) continue;
|
|
390
385
|
if (discoverParallelSlots(routeDir, appDir, scanMatcher, true).length === 0) continue;
|
|
391
386
|
const route = directoryToAppRoute(dir, appDir, scanMatcher, null, null, true);
|
|
@@ -440,9 +435,9 @@ function validatePageRouteConflicts(routes, appDir) {
|
|
|
440
435
|
}
|
|
441
436
|
}
|
|
442
437
|
function formatAppFilePath(filePath, appDir) {
|
|
443
|
-
const relativePath =
|
|
438
|
+
const relativePath = path.relative(appDir, filePath);
|
|
444
439
|
const parsedPath = path.parse(relativePath);
|
|
445
|
-
const withoutExtension =
|
|
440
|
+
const withoutExtension = path.join(parsedPath.dir, parsedPath.name);
|
|
446
441
|
return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
|
|
447
442
|
}
|
|
448
443
|
/**
|
|
@@ -704,27 +699,15 @@ function findCatchAllPage(dir, matcher) {
|
|
|
704
699
|
}
|
|
705
700
|
/**
|
|
706
701
|
* Convert a file path relative to app/ into an AppRoute.
|
|
707
|
-
*
|
|
708
|
-
* `file` and `appDir` must be forward-slash. `file` comes from
|
|
709
|
-
* `scanWithExtensions` (already forward-slash) and is joined onto `appDir` with
|
|
710
|
-
* `path.posix.join` to form the page/route path, so a native input would
|
|
711
|
-
* produce a mixed separator on Windows.
|
|
712
702
|
*/
|
|
713
703
|
function fileToAppRoute(file, appDir, type, matcher) {
|
|
714
|
-
let dir = path.
|
|
715
|
-
if (type === "page" && dir !== "." && path.
|
|
716
|
-
const parent = path.
|
|
704
|
+
let dir = path.dirname(file);
|
|
705
|
+
if (type === "page" && dir !== "." && path.basename(dir) === "@children") {
|
|
706
|
+
const parent = path.dirname(dir);
|
|
717
707
|
dir = parent === "" || parent === "." ? "." : parent;
|
|
718
708
|
}
|
|
719
|
-
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.
|
|
709
|
+
return directoryToAppRoute(dir, appDir, matcher, type === "page" ? path.join(appDir, file) : null, type === "route" ? path.join(appDir, file) : null);
|
|
720
710
|
}
|
|
721
|
-
/**
|
|
722
|
-
* `dir`, `appDir`, `pagePath`, and `routePath` must all be forward-slash. `dir`
|
|
723
|
-
* is split on `path.posix.sep` and joined onto `appDir` with `path.posix.join`.
|
|
724
|
-
* `appDir` is threaded to the layout/slot/boundary discovery below, which builds
|
|
725
|
-
* paths the same way. `pagePath` and `routePath` are stored on the route node as
|
|
726
|
-
* canonical ids that get compared and re-joined downstream.
|
|
727
|
-
*/
|
|
728
711
|
function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeNestedOnlySlots = false) {
|
|
729
712
|
const segments = dir === "." ? [] : dir.split("/");
|
|
730
713
|
const params = [];
|
|
@@ -743,7 +726,7 @@ function directoryToAppRoute(dir, appDir, matcher, pagePath, routePath, includeN
|
|
|
743
726
|
const errorEntries = discoverSegmentErrors(segments, appDir, matcher);
|
|
744
727
|
const errorPaths = errorEntries.map((entry) => entry.path);
|
|
745
728
|
const errorTreePositions = errorEntries.map((entry) => entry.treePosition);
|
|
746
|
-
const routeDir = dir === "." ? appDir : path.
|
|
729
|
+
const routeDir = dir === "." ? appDir : path.join(appDir, dir);
|
|
747
730
|
const effectivePagePath = pagePath ?? (routePath ? null : findFile(routeDir, "default", matcher));
|
|
748
731
|
const loadingPath = findFile(routeDir, "loading", matcher);
|
|
749
732
|
const errorPath = findFile(routeDir, "error", matcher);
|
|
@@ -850,8 +833,9 @@ function convertTreePathToRouteParts(treePath) {
|
|
|
850
833
|
function computeLayoutTreePositions(appDir, layouts) {
|
|
851
834
|
return layouts.map((layoutPath) => {
|
|
852
835
|
const layoutDir = path.dirname(layoutPath);
|
|
853
|
-
|
|
854
|
-
|
|
836
|
+
const relative = path.relative(appDir, layoutDir);
|
|
837
|
+
if (relative === "") return 0;
|
|
838
|
+
return relative.split(path.sep).length;
|
|
855
839
|
});
|
|
856
840
|
}
|
|
857
841
|
/**
|
|
@@ -976,10 +960,6 @@ function discoverBoundaryFilePerLayout(layouts, fileName, matcher) {
|
|
|
976
960
|
* Walk from appDir through each segment to the route's directory. At each level
|
|
977
961
|
* that has @slot dirs, collect them. Slots at the route's own directory level
|
|
978
962
|
* use page.tsx; slots at ancestor levels use default.tsx only.
|
|
979
|
-
*
|
|
980
|
-
* `appDir` and `routeDir` must be forward-slash — `currentDir` descends from
|
|
981
|
-
* `appDir` via `path.posix.join`, and the `dir === routeDir` active-level test
|
|
982
|
-
* below only matches when both share the canonical separator.
|
|
983
963
|
*/
|
|
984
964
|
function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, includeNestedOnlySlots = false) {
|
|
985
965
|
const slotMap = /* @__PURE__ */ new Map();
|
|
@@ -992,7 +972,7 @@ function discoverInheritedParallelSlots(segments, appDir, routeDir, matcher, inc
|
|
|
992
972
|
segmentIndex: 0
|
|
993
973
|
});
|
|
994
974
|
for (let i = 0; i < segments.length; i++) {
|
|
995
|
-
currentDir = path.
|
|
975
|
+
currentDir = path.join(currentDir, segments[i]);
|
|
996
976
|
if (findFile(currentDir, "layout", matcher)) layoutIdx++;
|
|
997
977
|
dirsToCheck.push({
|
|
998
978
|
dir: currentDir,
|
|
@@ -1155,9 +1135,6 @@ function patternsStructurallyEquivalent(a, b) {
|
|
|
1155
1135
|
*
|
|
1156
1136
|
* Returns the absolute page path, or null if no root-level page is found.
|
|
1157
1137
|
*
|
|
1158
|
-
* `slotDir` must be forward-slash: the `path.posix.join` descent stays a
|
|
1159
|
-
* canonical id only when the base already is.
|
|
1160
|
-
*
|
|
1161
1138
|
* Only descends into route-group directories (those whose name starts with `(`
|
|
1162
1139
|
* and ends with `)`). Dynamic segments, regular named dirs, and `@slot` dirs
|
|
1163
1140
|
* are not transparent and are therefore not searched.
|
|
@@ -1169,7 +1146,7 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1169
1146
|
for (const entry of entries) {
|
|
1170
1147
|
if (!entry.isDirectory()) continue;
|
|
1171
1148
|
if (!entry.name.startsWith("(") || !entry.name.endsWith(")")) continue;
|
|
1172
|
-
const found = findSlotRootPage(path.
|
|
1149
|
+
const found = findSlotRootPage(path.join(slotDir, entry.name), matcher);
|
|
1173
1150
|
if (found) return found;
|
|
1174
1151
|
}
|
|
1175
1152
|
return null;
|
|
@@ -1178,9 +1155,6 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1178
1155
|
* Discover parallel route slots (@team, @analytics, etc.) in a directory.
|
|
1179
1156
|
* Returns a ParallelSlot for each @-prefixed subdirectory that has a page,
|
|
1180
1157
|
* default component, intercepting route, or nested page-backed sub-route.
|
|
1181
|
-
*
|
|
1182
|
-
* `dir` and `appDir` must be forward-slash. The slot directory is built from
|
|
1183
|
-
* `dir` with `path.posix.join`.
|
|
1184
1158
|
*/
|
|
1185
1159
|
function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
|
|
1186
1160
|
const entries = readDirEntriesCached(dir, matcher);
|
|
@@ -1189,7 +1163,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1189
1163
|
if (!entry.isDirectory() || !entry.name.startsWith("@")) continue;
|
|
1190
1164
|
if (entry.name === "@children") continue;
|
|
1191
1165
|
const slotName = entry.name.slice(1);
|
|
1192
|
-
const slotDir = path.
|
|
1166
|
+
const slotDir = path.join(dir, entry.name);
|
|
1193
1167
|
const pagePath = findSlotRootPage(slotDir, matcher);
|
|
1194
1168
|
const defaultPath = findFile(slotDir, "default", matcher);
|
|
1195
1169
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
@@ -1200,7 +1174,7 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1200
1174
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1201
1175
|
slots.push({
|
|
1202
1176
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1203
|
-
key: `${slotName}@${
|
|
1177
|
+
key: `${slotName}@${path.relative(appDir, slotDir)}`,
|
|
1204
1178
|
name: slotName,
|
|
1205
1179
|
ownerDir: slotDir,
|
|
1206
1180
|
ownerTreePath,
|
|
@@ -1259,9 +1233,6 @@ function isInterceptionMarkerDir(name) {
|
|
|
1259
1233
|
* Intercepting routes use conventions like (.)photo, (..)feed, (...), etc.
|
|
1260
1234
|
* They intercept navigation to another route and render within the slot instead.
|
|
1261
1235
|
*
|
|
1262
|
-
* `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
|
|
1263
|
-
* down to `path.posix.join` when building the intercept page paths.
|
|
1264
|
-
*
|
|
1265
1236
|
* @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
|
|
1266
1237
|
* @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
|
|
1267
1238
|
* @param appDir - The root app directory
|
|
@@ -1280,17 +1251,13 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
|
1280
1251
|
* Sibling intercepts use the same conventions and target-computation logic as
|
|
1281
1252
|
* slot intercepts, but their intercepting page replaces the full page response
|
|
1282
1253
|
* (not a slot) during soft navigation.
|
|
1283
|
-
*
|
|
1284
|
-
* `appDir` and each route's `pagePath` / `routePath` must be forward-slash. The
|
|
1285
|
-
* owner directories are derived from `pagePath` / `routePath` and matched
|
|
1286
|
-
* against `appDir`-relative paths built with `path.posix.*`.
|
|
1287
1254
|
*/
|
|
1288
1255
|
function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
1289
1256
|
const routesByDir = /* @__PURE__ */ new Map();
|
|
1290
1257
|
for (const route of routes) {
|
|
1291
1258
|
const filePath = route.pagePath ?? route.routePath;
|
|
1292
1259
|
if (!filePath) continue;
|
|
1293
|
-
const routeDir = path.
|
|
1260
|
+
const routeDir = path.dirname(filePath);
|
|
1294
1261
|
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1295
1262
|
}
|
|
1296
1263
|
function walk(dir) {
|
|
@@ -1304,7 +1271,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1304
1271
|
if (!entry.isDirectory()) continue;
|
|
1305
1272
|
if (entry.name.startsWith("_")) continue;
|
|
1306
1273
|
if (entry.name.startsWith("@")) continue;
|
|
1307
|
-
const childDir = path.
|
|
1274
|
+
const childDir = path.join(dir, entry.name);
|
|
1308
1275
|
const marker = matchInterceptConvention(entry.name);
|
|
1309
1276
|
if (marker) {
|
|
1310
1277
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
@@ -1335,18 +1302,15 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1335
1302
|
* no sibling pages at all (e.g. `deep/path/(...)target` with no
|
|
1336
1303
|
* `deep/path/page.tsx`).
|
|
1337
1304
|
*
|
|
1338
|
-
*
|
|
1339
|
-
*
|
|
1340
|
-
*
|
|
1341
|
-
* backslashes on Windows. Without normalizing, the `current === appDir`
|
|
1342
|
-
* termination never fires there and the walk overshoots the app root.
|
|
1343
|
-
* `routesByDir` keys must be forward-slash dirnames of the route file paths.
|
|
1305
|
+
* Comparisons happen in forward-slash space — inputs go through `toSlash` so
|
|
1306
|
+
* callers still holding native-separator paths hit the `current === appDir`
|
|
1307
|
+
* termination instead of overshooting the app root.
|
|
1344
1308
|
*
|
|
1345
1309
|
* Exported for tests.
|
|
1346
1310
|
*/
|
|
1347
1311
|
function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
1348
|
-
const appRoot =
|
|
1349
|
-
let current =
|
|
1312
|
+
const appRoot = toSlash(appDir);
|
|
1313
|
+
let current = toSlash(dir);
|
|
1350
1314
|
while (true) {
|
|
1351
1315
|
const exact = routesByDir.get(current);
|
|
1352
1316
|
if (exact) return exact;
|
|
@@ -1355,7 +1319,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1355
1319
|
for (const route of routes) {
|
|
1356
1320
|
const filePath = route.pagePath ?? route.routePath;
|
|
1357
1321
|
if (!filePath) continue;
|
|
1358
|
-
if (!
|
|
1322
|
+
if (!toSlash(filePath).startsWith(currentWithSep)) continue;
|
|
1359
1323
|
if (!best || route.patternParts.length < best.patternParts.length) best = route;
|
|
1360
1324
|
}
|
|
1361
1325
|
if (best) return best;
|
|
@@ -1369,9 +1333,6 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1369
1333
|
/**
|
|
1370
1334
|
* Recursively scan a directory tree for page.tsx files that are inside
|
|
1371
1335
|
* intercepting route directories.
|
|
1372
|
-
*
|
|
1373
|
-
* `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
|
|
1374
|
-
* descends with `path.posix.join` when building the intercept page paths.
|
|
1375
1336
|
*/
|
|
1376
1337
|
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1377
1338
|
if (!fs.existsSync(currentDir)) return;
|
|
@@ -1380,7 +1341,7 @@ function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher
|
|
|
1380
1341
|
if (!entry.isDirectory()) continue;
|
|
1381
1342
|
if (entry.name.startsWith("_")) continue;
|
|
1382
1343
|
const interceptMatch = matchInterceptConvention(entry.name);
|
|
1383
|
-
const interceptDir = path.
|
|
1344
|
+
const interceptDir = path.join(currentDir, entry.name);
|
|
1384
1345
|
if (interceptMatch) {
|
|
1385
1346
|
const restOfName = entry.name.slice(interceptMatch.prefix.length);
|
|
1386
1347
|
collectInterceptingPages(interceptDir, interceptDir, interceptMatch.convention, restOfName, routeDir, appDir, currentDir, results, matcher);
|
|
@@ -1397,12 +1358,6 @@ function matchInterceptConvention(name) {
|
|
|
1397
1358
|
/**
|
|
1398
1359
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1399
1360
|
* and compute their target URL patterns.
|
|
1400
|
-
*
|
|
1401
|
-
* `currentDir`, `interceptRoot`, `routeDir`, `appDir`, and `interceptParentDir`
|
|
1402
|
-
* must all be forward-slash. `currentDir` descends with `path.posix.join` and
|
|
1403
|
-
* its `findFile` results become the stored layout/page paths. The others are
|
|
1404
|
-
* relativized against `appDir` (and each other) to derive the intercept page
|
|
1405
|
-
* segments and URL patterns.
|
|
1406
1361
|
*/
|
|
1407
1362
|
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
|
|
1408
1363
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
@@ -1422,7 +1377,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1422
1377
|
targetPattern: targetPattern.pattern,
|
|
1423
1378
|
sourceMatchPattern,
|
|
1424
1379
|
pagePath: page,
|
|
1425
|
-
sourcePageSegments:
|
|
1380
|
+
sourcePageSegments: path.relative(appDir, path.dirname(page)).split("/").filter(Boolean),
|
|
1426
1381
|
params: targetPattern.params
|
|
1427
1382
|
});
|
|
1428
1383
|
}
|
|
@@ -1432,7 +1387,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1432
1387
|
for (const entry of entries) {
|
|
1433
1388
|
if (!entry.isDirectory()) continue;
|
|
1434
1389
|
if (entry.name.startsWith("_")) continue;
|
|
1435
|
-
collectInterceptingPages(path.
|
|
1390
|
+
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1436
1391
|
}
|
|
1437
1392
|
}
|
|
1438
1393
|
/**
|
|
@@ -1552,9 +1507,6 @@ const findFileProbeCache = /* @__PURE__ */ new WeakMap();
|
|
|
1552
1507
|
* registered per-scan cache; otherwise falls back to a direct probe (identical
|
|
1553
1508
|
* result). The `null` "not found" outcome is cached too, so repeated misses on
|
|
1554
1509
|
* shared ancestors cost a single set of `existsSync` calls per scan.
|
|
1555
|
-
*
|
|
1556
|
-
* `dir` must be forward-slash. The returned path comes from `findFileWithExts`,
|
|
1557
|
-
* so it is forward-slash too.
|
|
1558
1510
|
*/
|
|
1559
1511
|
function findFile(dir, name, matcher) {
|
|
1560
1512
|
const cache = findFileProbeCache.get(matcher);
|
|
@@ -12,16 +12,11 @@ declare function invalidateAppRouteCache(): void;
|
|
|
12
12
|
* TODO(#726): Layer 4 should consume this read model directly once the
|
|
13
13
|
* navigation planner owns route graph facts.
|
|
14
14
|
*
|
|
15
|
-
* `appDir` must be forward-slash — callers normalize it at their entry, and it
|
|
16
|
-
* flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
|
|
17
|
-
*
|
|
18
15
|
* @internal
|
|
19
16
|
*/
|
|
20
17
|
declare function appRouteGraph(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraph>;
|
|
21
18
|
/**
|
|
22
19
|
* Scan the app/ directory and return a list of routes.
|
|
23
|
-
*
|
|
24
|
-
* `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
|
|
25
20
|
*/
|
|
26
21
|
declare function appRouter(appDir: string, pageExtensions?: readonly string[], matcher?: ValidFileMatcher): Promise<AppRouteGraphRoute[]>;
|
|
27
22
|
/**
|
|
@@ -30,9 +30,6 @@ function invalidateAppRouteCache() {
|
|
|
30
30
|
* TODO(#726): Layer 4 should consume this read model directly once the
|
|
31
31
|
* navigation planner owns route graph facts.
|
|
32
32
|
*
|
|
33
|
-
* `appDir` must be forward-slash — callers normalize it at their entry, and it
|
|
34
|
-
* flows into `buildAppRouteGraph`, which builds every path with `path.posix.*`.
|
|
35
|
-
*
|
|
36
33
|
* @internal
|
|
37
34
|
*/
|
|
38
35
|
async function appRouteGraph(appDir, pageExtensions, matcher) {
|
|
@@ -47,8 +44,6 @@ async function appRouteGraph(appDir, pageExtensions, matcher) {
|
|
|
47
44
|
}
|
|
48
45
|
/**
|
|
49
46
|
* Scan the app/ directory and return a list of routes.
|
|
50
|
-
*
|
|
51
|
-
* `appDir` must be forward-slash — it is forwarded to `appRouteGraph`.
|
|
52
47
|
*/
|
|
53
48
|
async function appRouter(appDir, pageExtensions, matcher) {
|
|
54
49
|
return (await appRouteGraph(appDir, pageExtensions, matcher)).routes;
|
|
@@ -21,9 +21,6 @@ declare function findFileWithExtensions(basePath: string, matcher: ValidFileMatc
|
|
|
21
21
|
/**
|
|
22
22
|
* Find a file by basename and configured page extension in a directory.
|
|
23
23
|
* Returns the first matching absolute path, or null if not found.
|
|
24
|
-
*
|
|
25
|
-
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
26
|
-
* so it is forward-slash too.
|
|
27
24
|
*/
|
|
28
25
|
declare function findFileWithExts(dir: string, name: string, matcher: ValidFileMatcher): string | null;
|
|
29
26
|
/**
|
|
@@ -66,9 +63,8 @@ declare function normalizeViteResolveExtensions(extensions: readonly string[]):
|
|
|
66
63
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
67
64
|
*
|
|
68
65
|
* Yields forward-slash relative paths: node's glob emits native (backslash)
|
|
69
|
-
* separators on Windows, so each match
|
|
70
|
-
*
|
|
71
|
-
* forward-slash ids.
|
|
66
|
+
* separators on Windows, so each match goes through `toSlash` — this is the
|
|
67
|
+
* boundary where external fs output enters the canonical forward-slash space.
|
|
72
68
|
*/
|
|
73
69
|
declare function scanWithExtensions(stem: string, cwd: string, extensions: readonly string[], exclude?: (name: string) => boolean): AsyncGenerator<string>;
|
|
74
70
|
//#endregion
|
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
2
2
|
import { escapeRegExp } from "../utils/regex.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
|
-
import path from "node:path";
|
|
5
4
|
import { glob } from "node:fs/promises";
|
|
6
5
|
//#region src/routing/file-matcher.ts
|
|
7
6
|
const DEFAULT_PAGE_EXTENSIONS = [
|
|
@@ -19,6 +18,10 @@ function buildExtensionGlob(stem, extensions) {
|
|
|
19
18
|
if (extensions.length === 1) return `${stem}.${extensions[0]}`;
|
|
20
19
|
return `${stem}.{${extensions.join(",")}}`;
|
|
21
20
|
}
|
|
21
|
+
function includeDotDirectoryMatches(pattern) {
|
|
22
|
+
if (!pattern.startsWith("**/")) return pattern;
|
|
23
|
+
return `{**,**/.*/**}/${pattern.slice(3)}`;
|
|
24
|
+
}
|
|
22
25
|
/**
|
|
23
26
|
* Ported in spirit from Next.js createValidFileMatcher:
|
|
24
27
|
* packages/next/src/server/lib/find-page-file.ts
|
|
@@ -67,13 +70,10 @@ function findFileWithExtensions(basePath, matcher) {
|
|
|
67
70
|
/**
|
|
68
71
|
* Find a file by basename and configured page extension in a directory.
|
|
69
72
|
* Returns the first matching absolute path, or null if not found.
|
|
70
|
-
*
|
|
71
|
-
* `dir` must be forward-slash. The returned path is built with `path.posix.join`,
|
|
72
|
-
* so it is forward-slash too.
|
|
73
73
|
*/
|
|
74
74
|
function findFileWithExts(dir, name, matcher) {
|
|
75
75
|
for (const ext of matcher.dottedExtensions) {
|
|
76
|
-
const filePath = path.
|
|
76
|
+
const filePath = path.join(dir, name + ext);
|
|
77
77
|
if (existsSync(filePath)) return filePath;
|
|
78
78
|
}
|
|
79
79
|
return null;
|
|
@@ -153,16 +153,15 @@ function normalizeViteResolveExtensions(extensions) {
|
|
|
153
153
|
* Use function-form exclude for Node < 22.14 compatibility.
|
|
154
154
|
*
|
|
155
155
|
* Yields forward-slash relative paths: node's glob emits native (backslash)
|
|
156
|
-
* separators on Windows, so each match
|
|
157
|
-
*
|
|
158
|
-
* forward-slash ids.
|
|
156
|
+
* separators on Windows, so each match goes through `toSlash` — this is the
|
|
157
|
+
* boundary where external fs output enters the canonical forward-slash space.
|
|
159
158
|
*/
|
|
160
159
|
async function* scanWithExtensions(stem, cwd, extensions, exclude) {
|
|
161
|
-
const pattern = buildExtensionGlob(stem, extensions);
|
|
160
|
+
const pattern = includeDotDirectoryMatches(buildExtensionGlob(stem, extensions));
|
|
162
161
|
for await (const file of glob(pattern, {
|
|
163
162
|
cwd,
|
|
164
163
|
...exclude ? { exclude } : {}
|
|
165
|
-
})) yield
|
|
164
|
+
})) yield toSlash(file);
|
|
166
165
|
}
|
|
167
166
|
//#endregion
|
|
168
167
|
export { buildViteResolveExtensions, createValidFileMatcher, findFileWithExtensions, findFileWithExts, normalizePageExtensions, normalizeViteResolveExtensions, scanWithExtensions };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
+
import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
|
|
1
2
|
import { decodeRouteSegment, sortRoutes } from "./utils.js";
|
|
2
3
|
import { createValidFileMatcher, scanWithExtensions } from "./file-matcher.js";
|
|
3
4
|
import { patternToNextFormat, validateRoutePatterns } from "./route-validation.js";
|
|
4
5
|
import { createRouteTrieCache, matchRouteWithTrie } from "./route-matching.js";
|
|
5
|
-
import path from "node:path";
|
|
6
6
|
//#region src/routing/pages-router.ts
|
|
7
7
|
/** Next.js special pages that should not produce routes. */
|
|
8
|
-
const RESERVED_PAGE_NAMES = new Set([
|
|
8
|
+
const RESERVED_PAGE_NAMES = /* @__PURE__ */ new Set([
|
|
9
9
|
"_app",
|
|
10
10
|
"_document",
|
|
11
11
|
"_error"
|
|
@@ -152,7 +152,7 @@ async function scanApiRoutes(pagesDir, matcher) {
|
|
|
152
152
|
}
|
|
153
153
|
const routes = [];
|
|
154
154
|
for (const file of files) {
|
|
155
|
-
const route = fileToRoute(path.
|
|
155
|
+
const route = fileToRoute(path.join("api", file), pagesDir, matcher);
|
|
156
156
|
if (route) routes.push(route);
|
|
157
157
|
}
|
|
158
158
|
validateRoutePatterns(routes.map((route) => route.pattern));
|
|
@@ -8,7 +8,7 @@ import { DEFAULT_PAGES_API_BODY_SIZE_LIMIT, resolveBodyParserConfig } from "./pa
|
|
|
8
8
|
import { PagesBodyParseError, getMediaType, isJsonMediaType } from "./pages-media-type.js";
|
|
9
9
|
import { resolveRequestHost, resolveRequestProtocol } from "./proxy-trust.js";
|
|
10
10
|
import { performOnDemandRevalidate } from "./pages-revalidate.js";
|
|
11
|
-
import { attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
11
|
+
import { attachPagesPreviewApi, attachPagesRequestCookies } from "./pages-node-compat.js";
|
|
12
12
|
import { isEdgeApiRuntime } from "./edge-api-runtime.js";
|
|
13
13
|
import { decode } from "node:querystring";
|
|
14
14
|
import { Buffer } from "node:buffer";
|
|
@@ -166,41 +166,49 @@ function enhanceApiObjects(req, res, query, body) {
|
|
|
166
166
|
query
|
|
167
167
|
});
|
|
168
168
|
attachPagesRequestCookies(apiReq);
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
169
|
+
const apiRes = Object.assign(res, {
|
|
170
|
+
status(code) {
|
|
171
|
+
this.statusCode = code;
|
|
172
|
+
return this;
|
|
173
|
+
},
|
|
174
|
+
json(data) {
|
|
175
|
+
this.setHeader("Content-Type", "application/json");
|
|
176
|
+
this.end(JSON.stringify(data));
|
|
177
|
+
},
|
|
178
|
+
send(data) {
|
|
179
|
+
if (Buffer.isBuffer(data)) {
|
|
180
|
+
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "application/octet-stream");
|
|
181
|
+
this.setHeader("Content-Length", String(data.length));
|
|
182
|
+
this.end(data);
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
if (typeof data === "object" && data !== null) {
|
|
177
186
|
this.setHeader("Content-Type", "application/json");
|
|
178
187
|
this.end(JSON.stringify(data));
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
this.setHeader("Content-Type", "application/json");
|
|
189
|
-
this.end(JSON.stringify(data));
|
|
190
|
-
} else {
|
|
191
|
-
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
|
|
192
|
-
this.end(String(data));
|
|
193
|
-
}
|
|
194
|
-
},
|
|
195
|
-
redirect(statusOrUrl, url) {
|
|
196
|
-
if (typeof statusOrUrl === "string") this.writeHead(307, { Location: statusOrUrl });
|
|
197
|
-
else this.writeHead(statusOrUrl, { Location: url ?? "" });
|
|
198
|
-
this.end();
|
|
199
|
-
},
|
|
200
|
-
async revalidate(urlPath, opts) {
|
|
201
|
-
await performOnDemandRevalidate(req, urlPath, opts);
|
|
188
|
+
} else {
|
|
189
|
+
if (!this.getHeader("Content-Type")) this.setHeader("Content-Type", "text/plain");
|
|
190
|
+
this.end(String(data));
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
redirect(statusOrUrl, url) {
|
|
194
|
+
if (typeof statusOrUrl === "string") {
|
|
195
|
+
url = statusOrUrl;
|
|
196
|
+
statusOrUrl = 307;
|
|
202
197
|
}
|
|
203
|
-
|
|
198
|
+
if (typeof statusOrUrl !== "number" || typeof url !== "string") throw new Error("Invalid redirect arguments. Please use a single argument URL, e.g. res.redirect('/destination') or use a status code and URL, e.g. res.redirect(307, '/destination').");
|
|
199
|
+
this.writeHead(statusOrUrl, { Location: url });
|
|
200
|
+
this.write(url);
|
|
201
|
+
this.end();
|
|
202
|
+
return this;
|
|
203
|
+
},
|
|
204
|
+
async revalidate(urlPath, opts) {
|
|
205
|
+
await performOnDemandRevalidate(req, urlPath, opts);
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
attachPagesPreviewApi(apiReq, apiRes);
|
|
209
|
+
return {
|
|
210
|
+
apiReq,
|
|
211
|
+
apiRes
|
|
204
212
|
};
|
|
205
213
|
}
|
|
206
214
|
/**
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_HEADER } from "./headers.js";
|
|
1
|
+
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_HEADER, VINEXT_RSC_REDIRECT_TYPE_HEADER } from "./headers.js";
|
|
2
2
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
3
|
-
import { APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI } from "./app-rsc-render-mode.js";
|
|
4
3
|
import { getMountedSlotIdsHeader, resolveVisitedResponseInterceptionContext } from "./app-elements.js";
|
|
5
4
|
import { AppRouterContext } from "../shims/internal/app-router-context.js";
|
|
6
5
|
import { installWindowNext, setWindowNextInternalSourcePage } from "../client/window-next.js";
|
|
@@ -598,9 +597,10 @@ function BrowserRoot({ hydrationCachePublication, initialElements, initialNaviga
|
|
|
598
597
|
resetKey: treeState.renderId,
|
|
599
598
|
onCatch: handleDevRecoveryBoundaryCatch
|
|
600
599
|
}, innerTree) : innerTree;
|
|
600
|
+
const scrollScopedTree = createElement(AppRouterScrollCommitProvider, { commitId: treeState.renderId }, committedTree);
|
|
601
601
|
const rootErrorTree = createElement(GlobalErrorBoundary, {
|
|
602
602
|
fallback: DEFAULT_GLOBAL_ERROR_COMPONENT,
|
|
603
|
-
children:
|
|
603
|
+
children: scrollScopedTree
|
|
604
604
|
});
|
|
605
605
|
const ClientNavigationRenderContext = getClientNavigationRenderContext();
|
|
606
606
|
if (!ClientNavigationRenderContext) return rootErrorTree;
|
|
@@ -764,7 +764,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
764
764
|
const metadata = AppElementsWire.readMetadata(elements);
|
|
765
765
|
if (!isCompleteAppPayloadMetadata(metadata)) return;
|
|
766
766
|
const mountedSlotsHeader = getMountedSlotIdsHeader(elements);
|
|
767
|
-
const
|
|
767
|
+
const headers = createRscRequestHeaders({ mountedSlotsHeader });
|
|
768
|
+
const rscUrl = await createRscRequestUrl(initialPathAndSearch, headers);
|
|
768
769
|
if (cacheGeneration !== clientNavigationCacheGeneration) return;
|
|
769
770
|
const snapshot = {
|
|
770
771
|
compatibilityIdHeader: CLIENT_RSC_COMPATIBILITY_ID,
|
|
@@ -852,9 +853,9 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
852
853
|
let redirectCount = redirectDepth;
|
|
853
854
|
let detachedNavigationCommits = false;
|
|
854
855
|
const activeTraversalIntent = navigationKind === "traverse" ? traversalIntent ?? historyController.resolveTraversalIntent(window.history.state) : null;
|
|
855
|
-
const performHardNavigationForScrollIntent = (targetHref) => {
|
|
856
|
+
const performHardNavigationForScrollIntent = (targetHref, mode) => {
|
|
856
857
|
consumeAppRouterScrollIntent(scrollIntent ?? null);
|
|
857
|
-
const didNavigate = browserNavigationController.performHardNavigation(targetHref);
|
|
858
|
+
const didNavigate = browserNavigationController.performHardNavigation(targetHref, mode);
|
|
858
859
|
if (!didNavigate) clearAppNavigationFailureTarget(targetHref);
|
|
859
860
|
return didNavigate;
|
|
860
861
|
};
|
|
@@ -888,8 +889,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
888
889
|
const shouldBypassNavigationCache = earlyIntentDecision?.kind === "flightNavigation" && earlyIntentDecision.bypassNavigationCache;
|
|
889
890
|
const requestHeaders = createRscRequestHeaders({
|
|
890
891
|
interceptionContext: requestInterceptionContext,
|
|
891
|
-
mountedSlotsHeader
|
|
892
|
-
renderMode: navigationKind === "refresh" ? APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI : void 0
|
|
892
|
+
mountedSlotsHeader
|
|
893
893
|
});
|
|
894
894
|
const rscUrl = await createRscRequestUrl(url.pathname + url.search, requestHeaders);
|
|
895
895
|
const rewrittenNavigationHref = navigationKind === "navigate" ? resolveHybridClientRewriteHref(currentHref, __basePath) : null;
|
|
@@ -1035,6 +1035,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1035
1035
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1036
1036
|
const navContentType = navResponse.headers.get("content-type") ?? "";
|
|
1037
1037
|
const streamedRedirectTarget = navResponse.headers.get(VINEXT_RSC_REDIRECT_HEADER);
|
|
1038
|
+
const streamedRedirectTypeHeader = navResponse.headers.get(VINEXT_RSC_REDIRECT_TYPE_HEADER);
|
|
1039
|
+
const streamedRedirectType = streamedRedirectTypeHeader === "push" || streamedRedirectTypeHeader === "replace" ? streamedRedirectTypeHeader : null;
|
|
1038
1040
|
if (blockDangerousStreamedRscRedirect(navResponse, streamedRedirectTarget)) return;
|
|
1039
1041
|
const liveFetchDecision = navigationPlanner.classifyRscFetchResult({
|
|
1040
1042
|
clientCompatibilityId: CLIENT_RSC_COMPATIBILITY_ID,
|
|
@@ -1049,13 +1051,14 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1049
1051
|
responseOk: navResponse.ok,
|
|
1050
1052
|
responseUrl: navResponseUrl ?? navResponse.url,
|
|
1051
1053
|
source: "live",
|
|
1052
|
-
streamedRedirectTarget
|
|
1054
|
+
streamedRedirectTarget,
|
|
1055
|
+
streamedRedirectType
|
|
1053
1056
|
});
|
|
1054
1057
|
if (liveFetchDecision.kind === "hardNavigate") {
|
|
1055
1058
|
if (liveFetchDecision.discardBody) navResponse.body?.cancel().catch(() => {});
|
|
1056
1059
|
if (liveFetchDecision.reason === "redirectDepthExhausted") console.error("[vinext] Too many RSC redirects — aborting navigation to prevent infinite loop.");
|
|
1057
1060
|
if (liveFetchDecision.reason === "streamedRedirectLoop") console.error("[vinext] RSC streamed redirect resolved to the current URL — aborting navigation to prevent infinite loop.");
|
|
1058
|
-
performHardNavigationForScrollIntent(liveFetchDecision.url);
|
|
1061
|
+
performHardNavigationForScrollIntent(liveFetchDecision.url, liveFetchDecision.hardNavigationMode);
|
|
1059
1062
|
return;
|
|
1060
1063
|
}
|
|
1061
1064
|
if (liveFetchDecision.kind === "followRedirect") {
|
|
@@ -1206,7 +1209,8 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1206
1209
|
}
|
|
1207
1210
|
};
|
|
1208
1211
|
import.meta.hot.on("rsc:update", () => {
|
|
1209
|
-
|
|
1212
|
+
const updateId = ++latestRscHmrUpdateId;
|
|
1213
|
+
handleRscUpdate(updateId);
|
|
1210
1214
|
});
|
|
1211
1215
|
}
|
|
1212
1216
|
}
|
|
@@ -259,12 +259,14 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
259
259
|
}
|
|
260
260
|
if (visibleCommitMode === "synchronous") {
|
|
261
261
|
flushSync(() => {
|
|
262
|
-
|
|
262
|
+
const committedState = captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
|
|
263
|
+
setter(committedState);
|
|
263
264
|
});
|
|
264
265
|
return;
|
|
265
266
|
}
|
|
266
267
|
startTransition(() => {
|
|
267
|
-
|
|
268
|
+
const committedState = captureCandidateState(applyApprovedVisibleCommit(getBrowserRouterState(), commit));
|
|
269
|
+
setter(committedState);
|
|
268
270
|
});
|
|
269
271
|
}
|
|
270
272
|
function dispatchSynchronousVisibleCommit(commit) {
|
|
@@ -449,8 +451,11 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
449
451
|
return;
|
|
450
452
|
}
|
|
451
453
|
if (latestApproval.approvedCommit) {
|
|
452
|
-
|
|
453
|
-
|
|
454
|
+
const approvedRevalidationCommit = latestApproval.approvedCommit;
|
|
455
|
+
startTransition(() => {
|
|
456
|
+
dispatchSynchronousVisibleCommit(approvedRevalidationCommit);
|
|
457
|
+
});
|
|
458
|
+
syncHistoryStatePreviousNextUrl(approvedRevalidationCommit.previousNextUrl, approvedRevalidationCommit.action.bfcacheIds);
|
|
454
459
|
} else notifyDiscardedServerActionRevalidation(lifecycleOptions);
|
|
455
460
|
} else if (decision.disposition === "no-commit") notifyDiscardedServerActionRevalidation(lifecycleOptions);
|
|
456
461
|
if (returnValue) {
|
|
@@ -49,7 +49,7 @@ var ServerActionRedirectError = class extends Error {
|
|
|
49
49
|
const redirectUrl = new URL(target.href, window.location.href);
|
|
50
50
|
const redirectHref = redirectUrl.pathname + redirectUrl.search + redirectUrl.hash;
|
|
51
51
|
const redirectType = target.type === "push" ? "push" : "replace";
|
|
52
|
-
this.digest = `NEXT_REDIRECT;${redirectType};${
|
|
52
|
+
this.digest = `NEXT_REDIRECT;${redirectType};${redirectHref};${target.status};`;
|
|
53
53
|
}
|
|
54
54
|
};
|
|
55
55
|
async function invokeClientServerAction(id, args, actionInitiation, deps) {
|