vinext 0.2.0 → 1.0.0-beta.0
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 +50 -29
- package/dist/build/client-build-config.d.ts +13 -92
- package/dist/build/client-build-config.js +17 -95
- package/dist/build/css-url-assets.d.ts +3 -1
- package/dist/build/css-url-assets.js +18 -1
- package/dist/build/inject-pregenerated-paths.d.ts +3 -0
- package/dist/build/inject-pregenerated-paths.js +4 -1
- package/dist/build/prerender-paths.d.ts +23 -0
- package/dist/build/prerender-paths.js +296 -0
- package/dist/build/prerender-server-entry.d.ts +1 -0
- package/dist/build/prerender-server-entry.js +49 -0
- package/dist/build/prerender-server-pool.d.ts +44 -0
- package/dist/build/prerender-server-pool.js +194 -0
- package/dist/build/prerender.d.ts +12 -1
- package/dist/build/prerender.js +87 -23
- package/dist/build/run-prerender.js +2 -1
- package/dist/cache/cache-adapters-virtual.d.ts +7 -1
- package/dist/cache/cache-adapters-virtual.js +26 -2
- package/dist/check.d.ts +4 -6
- package/dist/check.js +13 -9
- package/dist/cli.js +34 -54
- package/dist/client/vinext-next-data.d.ts +4 -1
- package/dist/config/config-matchers.js +33 -11
- package/dist/config/next-config.d.ts +36 -6
- package/dist/config/next-config.js +17 -4
- package/dist/config/prerender.d.ts +11 -1
- package/dist/config/prerender.js +19 -1
- package/dist/config/tsconfig-paths.js +5 -1
- package/dist/entries/app-browser-entry.js +5 -1
- package/dist/entries/app-rsc-entry.d.ts +3 -2
- package/dist/entries/app-rsc-entry.js +4 -0
- package/dist/entries/pages-client-entry.js +14 -3
- package/dist/entries/pages-server-entry.js +18 -4
- package/dist/image/image-adapters-virtual.js +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +549 -152
- package/dist/init-cloudflare.js +12 -3
- package/dist/init-platform.d.ts +10 -1
- package/dist/init-platform.js +78 -12
- package/dist/init.d.ts +11 -3
- package/dist/init.js +129 -31
- package/dist/plugins/fonts.js +1 -1
- package/dist/plugins/ignore-dynamic-requests.js +1 -1
- package/dist/plugins/og-assets.js +2 -1
- package/dist/plugins/optimize-imports.js +1 -1
- package/dist/plugins/rsc-reference-validation-normalizer.d.ts +12 -0
- package/dist/plugins/rsc-reference-validation-normalizer.js +48 -0
- package/dist/plugins/sass.d.ts +17 -1
- package/dist/plugins/sass.js +74 -1
- package/dist/plugins/styled-jsx.d.ts +16 -0
- package/dist/plugins/styled-jsx.js +149 -0
- package/dist/routing/app-route-graph.js +22 -16
- package/dist/routing/file-matcher.d.ts +8 -1
- package/dist/routing/file-matcher.js +15 -3
- package/dist/server/app-browser-entry.js +44 -14
- package/dist/server/app-browser-navigation-controller.js +5 -2
- package/dist/server/app-browser-server-action-client.js +5 -3
- package/dist/server/app-browser-state.d.ts +1 -0
- package/dist/server/app-browser-state.js +1 -1
- package/dist/server/app-layout-param-observation.d.ts +1 -1
- package/dist/server/app-page-boundary.js +2 -1
- package/dist/server/app-page-cache-finalizer.d.ts +1 -0
- package/dist/server/app-page-cache-finalizer.js +3 -3
- package/dist/server/app-page-cache-render.d.ts +1 -1
- package/dist/server/app-page-cache.js +23 -6
- package/dist/server/app-page-dispatch.d.ts +2 -1
- package/dist/server/app-page-dispatch.js +27 -10
- package/dist/server/app-page-element-builder.js +1 -0
- package/dist/server/app-page-execution.js +2 -1
- package/dist/server/app-page-render-identity.d.ts +1 -0
- package/dist/server/app-page-render-identity.js +2 -1
- package/dist/server/app-page-render.d.ts +3 -1
- package/dist/server/app-page-render.js +67 -16
- package/dist/server/app-page-response.d.ts +7 -0
- package/dist/server/app-page-response.js +16 -4
- package/dist/server/app-page-route-wiring.js +3 -3
- package/dist/server/app-page-stream.d.ts +2 -1
- package/dist/server/app-page-stream.js +1 -1
- package/dist/server/app-route-handler-execution.d.ts +1 -1
- package/dist/server/app-route-tree-prefetch.d.ts +43 -0
- package/dist/server/app-route-tree-prefetch.js +187 -0
- package/dist/server/app-router-entry.js +1 -1
- package/dist/server/app-rsc-cache-busting.d.ts +2 -1
- package/dist/server/app-rsc-cache-busting.js +9 -5
- package/dist/server/app-rsc-handler.d.ts +6 -0
- package/dist/server/app-rsc-handler.js +88 -10
- package/dist/server/app-rsc-render-mode.d.ts +3 -5
- package/dist/server/app-rsc-render-mode.js +5 -12
- package/dist/server/app-rsc-request-normalization.d.ts +3 -4
- package/dist/server/app-rsc-request-normalization.js +4 -5
- package/dist/server/app-server-action-execution.js +6 -5
- package/dist/server/app-ssr-entry.js +1 -1
- package/dist/server/cache-control.d.ts +3 -1
- package/dist/server/cache-control.js +13 -1
- 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-server.d.ts +11 -1
- package/dist/server/dev-server.js +82 -24
- package/dist/server/headers.d.ts +6 -2
- package/dist/server/headers.js +11 -5
- package/dist/server/image-optimization.d.ts +12 -1
- package/dist/server/image-optimization.js +13 -1
- package/dist/server/isr-cache.d.ts +13 -4
- package/dist/server/isr-cache.js +8 -4
- package/dist/server/pages-data-route.d.ts +4 -2
- package/dist/server/pages-data-route.js +18 -4
- package/dist/server/pages-dev-module-url.d.ts +2 -1
- package/dist/server/pages-dev-module-url.js +6 -3
- package/dist/server/pages-node-compat.d.ts +12 -1
- package/dist/server/pages-node-compat.js +50 -1
- package/dist/server/pages-page-data.d.ts +9 -0
- package/dist/server/pages-page-data.js +18 -10
- package/dist/server/pages-page-handler.js +19 -7
- package/dist/server/pages-page-response.d.ts +1 -0
- package/dist/server/pages-page-response.js +5 -4
- package/dist/server/pages-request-pipeline.d.ts +8 -6
- package/dist/server/pages-request-pipeline.js +31 -7
- package/dist/server/pages-router-entry.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 +10 -7
- package/dist/server/prod-server.js +43 -28
- package/dist/server/request-pipeline.js +1 -1
- package/dist/server/static-file-cache.js +1 -1
- package/dist/shims/cache-handler.js +8 -1
- package/dist/shims/cache.d.ts +1 -1
- package/dist/shims/cache.js +3 -0
- package/dist/shims/cdn-cache.d.ts +2 -7
- package/dist/shims/cdn-cache.js +2 -14
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +77 -52
- package/dist/shims/internal/app-prefetch-fetch-queue.d.ts +10 -0
- package/dist/shims/internal/app-prefetch-fetch-queue.js +61 -0
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +2 -1
- package/dist/shims/internal/hybrid-client-route-owner.js +34 -1
- package/dist/shims/internal/pages-data-fetch-dedup.d.ts +2 -1
- package/dist/shims/internal/pages-data-fetch-dedup.js +13 -2
- package/dist/shims/internal/pages-data-target.d.ts +8 -3
- package/dist/shims/internal/pages-data-target.js +9 -4
- package/dist/shims/link.d.ts +1 -1
- package/dist/shims/link.js +179 -42
- package/dist/shims/navigation.d.ts +15 -3
- package/dist/shims/navigation.js +202 -39
- package/dist/shims/request-context.js +18 -0
- package/dist/shims/request-state-types.d.ts +2 -2
- package/dist/shims/router.js +31 -15
- package/dist/shims/unified-request-context.d.ts +1 -1
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/typegen.js +1 -1
- package/dist/utils/middleware-request-headers.js +1 -1
- package/dist/utils/project.d.ts +1 -1
- package/dist/utils/protocol-headers.d.ts +7 -1
- package/dist/utils/protocol-headers.js +7 -1
- package/dist/utils/vite-version.d.ts +4 -12
- package/dist/utils/vite-version.js +39 -21
- package/package.json +31 -13
- 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 -435
- 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 -276
- package/dist/packages/cloudflare/src/tpr.d.ts +0 -45
- package/dist/packages/cloudflare/src/tpr.js +0 -561
- package/dist/packages/cloudflare/src/utils/cache-control-metadata.js +0 -20
package/dist/plugins/sass.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { markCssUrlAssetReferences, rebaseCssUrlAssetReferences } from "../build/css-url-assets.js";
|
|
1
2
|
import { createRequire } from "node:module";
|
|
2
3
|
import fs from "node:fs";
|
|
3
4
|
import path from "node:path";
|
|
@@ -75,6 +76,78 @@ function createSassTildeImporter(root) {
|
|
|
75
76
|
}
|
|
76
77
|
} };
|
|
77
78
|
}
|
|
79
|
+
function sassStylesheetCandidates(importPath) {
|
|
80
|
+
const extension = path.extname(importPath);
|
|
81
|
+
const directory = path.dirname(importPath);
|
|
82
|
+
const basename = path.basename(importPath, extension);
|
|
83
|
+
const candidates = [];
|
|
84
|
+
const extensions = extension ? [extension] : [
|
|
85
|
+
".scss",
|
|
86
|
+
".sass",
|
|
87
|
+
".css"
|
|
88
|
+
];
|
|
89
|
+
for (const candidateExtension of extensions) {
|
|
90
|
+
const basePath = extension ? importPath : `${importPath}${candidateExtension}`;
|
|
91
|
+
candidates.push(basePath);
|
|
92
|
+
candidates.push(path.join(directory, `_${basename}${candidateExtension}`));
|
|
93
|
+
}
|
|
94
|
+
for (const candidateExtension of extensions) {
|
|
95
|
+
candidates.push(path.join(importPath, `index${candidateExtension}`));
|
|
96
|
+
candidates.push(path.join(importPath, `_index${candidateExtension}`));
|
|
97
|
+
}
|
|
98
|
+
return candidates;
|
|
99
|
+
}
|
|
100
|
+
function deriveSassNamespace(importUrl) {
|
|
101
|
+
const parts = importUrl.replaceAll("\\", "/").replace(/\/$/, "").split("/");
|
|
102
|
+
let basename = parts.pop() ?? "stylesheet";
|
|
103
|
+
basename = basename.replace(/^_/, "").replace(/\..*$/, "");
|
|
104
|
+
if (basename === "index" && parts.length > 0) basename = (parts.pop() ?? basename).replace(/^_/, "").replace(/\..*$/, "");
|
|
105
|
+
return basename;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Preserve source-asset provenance for `url()` references inside imported Sass
|
|
109
|
+
* partials. Vite's transform hook only sees the entry stylesheet after Sass has
|
|
110
|
+
* flattened its imports, at which point relative URLs from a partial have lost
|
|
111
|
+
* the partial's source filename. Marking the partial before compilation keeps
|
|
112
|
+
* byte-identical assets associated with their original basenames.
|
|
113
|
+
*/
|
|
114
|
+
function createSassCssUrlAssetImporter() {
|
|
115
|
+
const markedStylesheets = /* @__PURE__ */ new Map();
|
|
116
|
+
const importUrlPrefix = "vinext-css-url-asset:";
|
|
117
|
+
const prepareStylesheet = (filename, entryDirectory) => {
|
|
118
|
+
const contents = fs.readFileSync(filename, "utf8");
|
|
119
|
+
const marked = markCssUrlAssetReferences(rebaseCssUrlAssetReferences(contents, path.dirname(filename), entryDirectory) ?? contents, filename);
|
|
120
|
+
if (marked === null) return null;
|
|
121
|
+
const extension = path.extname(filename).toLowerCase();
|
|
122
|
+
return {
|
|
123
|
+
contents: rewriteImports(marked, filename, entryDirectory),
|
|
124
|
+
syntax: extension === ".sass" ? "indented" : extension === ".css" ? "css" : "scss"
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
const rewriteImports = (source, filename, entryDirectory = path.dirname(filename)) => source.replace(/(@(import|use|forward)\s+)(["'])(\.[^"']+)\3([^;]*;?)/g, (match, prefix, rule, quote, importUrl, suffix) => {
|
|
128
|
+
const importPath = path.resolve(path.dirname(filename), importUrl);
|
|
129
|
+
for (const candidate of sassStylesheetCandidates(importPath)) {
|
|
130
|
+
if (!fs.statSync(candidate, { throwIfNoEntry: false })?.isFile()) continue;
|
|
131
|
+
const prepared = prepareStylesheet(candidate, entryDirectory);
|
|
132
|
+
if (prepared === null) return match;
|
|
133
|
+
const canonicalUrl = pathToFileURL(candidate);
|
|
134
|
+
markedStylesheets.set(canonicalUrl.href, prepared);
|
|
135
|
+
const namespace = rule === "use" && !/\bas\s+(?:\*|[-\w]+)/.test(suffix) ? ` as ${deriveSassNamespace(importUrl)}` : "";
|
|
136
|
+
return `${prefix}${quote}${importUrlPrefix}${encodeURIComponent(candidate)}${quote}${namespace}${suffix}`;
|
|
137
|
+
}
|
|
138
|
+
return match;
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
canonicalize(url) {
|
|
142
|
+
if (!url.startsWith(importUrlPrefix)) return null;
|
|
143
|
+
return pathToFileURL(decodeURIComponent(url.slice(21)));
|
|
144
|
+
},
|
|
145
|
+
load(canonicalUrl) {
|
|
146
|
+
return markedStylesheets.get(canonicalUrl.href) ?? null;
|
|
147
|
+
},
|
|
148
|
+
rewriteImports
|
|
149
|
+
};
|
|
150
|
+
}
|
|
78
151
|
function buildSassPreprocessorOptions(sassOptions) {
|
|
79
152
|
if (!sassOptions || typeof sassOptions !== "object") return void 0;
|
|
80
153
|
const { prependData, additionalData, includePaths, loadPaths, ...rest } = sassOptions;
|
|
@@ -265,4 +338,4 @@ function createSassAwareFileSystemLoader() {
|
|
|
265
338
|
};
|
|
266
339
|
}
|
|
267
340
|
//#endregion
|
|
268
|
-
export { buildSassPreprocessorOptions, createSassAwareFileSystemLoader, createSassTildeImporter };
|
|
341
|
+
export { buildSassPreprocessorOptions, createSassAwareFileSystemLoader, createSassCssUrlAssetImporter, createSassTildeImporter };
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Plugin } from "vite";
|
|
2
|
+
|
|
3
|
+
//#region src/plugins/styled-jsx.d.ts
|
|
4
|
+
type NextSwcModule = {
|
|
5
|
+
loadBindings(): Promise<unknown>;
|
|
6
|
+
transform(source: string, options: Record<string, unknown>): Promise<{
|
|
7
|
+
code: string;
|
|
8
|
+
map?: string;
|
|
9
|
+
}>;
|
|
10
|
+
};
|
|
11
|
+
type StyledJsxPluginOptions = {
|
|
12
|
+
importModule?: (url: string) => Promise<NextSwcModule>;
|
|
13
|
+
};
|
|
14
|
+
declare function createStyledJsxPlugin(initialProjectRoot: string, options?: StyledJsxPluginOptions): Plugin;
|
|
15
|
+
//#endregion
|
|
16
|
+
export { createStyledJsxPlugin };
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parseAst } from "vite";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
//#region src/plugins/styled-jsx.ts
|
|
6
|
+
const STYLED_JSX_IMPORT_RE = /^styled-jsx(?:\/.*)?$/;
|
|
7
|
+
const NODE_MODULES_RE = /[\\/]node_modules[\\/]/;
|
|
8
|
+
const STYLED_JSX_SOURCE_RE = /(?:<style\b|from\s+["']styled-jsx\/css["']|require\s*\(\s*["']styled-jsx\/css["']\s*\))/;
|
|
9
|
+
const STYLED_JSX_CSS_RE = /(?:from\s+["']styled-jsx\/css["']|require\s*\(\s*["']styled-jsx\/css["']\s*\))/;
|
|
10
|
+
function hasStyledJsxTag(source, id) {
|
|
11
|
+
const cleanId = id.split("?")[0];
|
|
12
|
+
const extension = path.extname(cleanId);
|
|
13
|
+
const lang = extension === ".ts" || extension === ".mts" || extension === ".cts" ? "ts" : "tsx";
|
|
14
|
+
let ast;
|
|
15
|
+
try {
|
|
16
|
+
ast = parseAst(source, { lang });
|
|
17
|
+
} catch {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const pending = [ast];
|
|
21
|
+
const visited = /* @__PURE__ */ new Set();
|
|
22
|
+
while (pending.length > 0) {
|
|
23
|
+
const value = pending.pop();
|
|
24
|
+
if (!value || typeof value !== "object" || visited.has(value)) continue;
|
|
25
|
+
visited.add(value);
|
|
26
|
+
const node = value;
|
|
27
|
+
if (node.type === "JSXOpeningElement") {
|
|
28
|
+
const name = node.name;
|
|
29
|
+
if (name?.type === "JSXIdentifier" && name.name === "style") {
|
|
30
|
+
if (node.attributes?.some((attribute) => {
|
|
31
|
+
if (attribute.type !== "JSXAttribute") return false;
|
|
32
|
+
const attributeName = attribute.name;
|
|
33
|
+
return attributeName?.type === "JSXIdentifier" && attributeName.name === "jsx";
|
|
34
|
+
})) return true;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
for (const child of Object.values(node)) if (Array.isArray(child)) pending.push(...child);
|
|
38
|
+
else if (child && typeof child === "object") pending.push(child);
|
|
39
|
+
}
|
|
40
|
+
return false;
|
|
41
|
+
}
|
|
42
|
+
function createProjectRequire(projectRoot) {
|
|
43
|
+
return createRequire(path.join(projectRoot, "package.json"));
|
|
44
|
+
}
|
|
45
|
+
function resolveNextRequire(projectRoot) {
|
|
46
|
+
try {
|
|
47
|
+
return createRequire(createProjectRequire(projectRoot).resolve("next/package.json"));
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function parserOptions(id) {
|
|
53
|
+
const extension = path.extname(id.split("?")[0]);
|
|
54
|
+
if (extension === ".ts" || extension === ".tsx") return {
|
|
55
|
+
syntax: "typescript",
|
|
56
|
+
tsx: extension === ".tsx",
|
|
57
|
+
decorators: true
|
|
58
|
+
};
|
|
59
|
+
return {
|
|
60
|
+
syntax: "ecmascript",
|
|
61
|
+
jsx: true
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function createStyledJsxPlugin(initialProjectRoot, options = {}) {
|
|
65
|
+
let projectRoot = initialProjectRoot;
|
|
66
|
+
let development = false;
|
|
67
|
+
let nextRequire;
|
|
68
|
+
let compilerPromise = null;
|
|
69
|
+
const importModule = options.importModule ?? ((url) => import(url));
|
|
70
|
+
function getNextRequire() {
|
|
71
|
+
nextRequire ??= resolveNextRequire(projectRoot);
|
|
72
|
+
return nextRequire;
|
|
73
|
+
}
|
|
74
|
+
async function getCompiler() {
|
|
75
|
+
if (!compilerPromise) {
|
|
76
|
+
const requireFromNext = getNextRequire();
|
|
77
|
+
if (!requireFromNext) throw new Error("[vinext] styled-jsx requires an installed next package so vinext can use its matching compiler.");
|
|
78
|
+
compilerPromise = importModule(pathToFileURL(requireFromNext.resolve("next/dist/build/swc")).href).then(async (compiler) => {
|
|
79
|
+
await compiler.loadBindings();
|
|
80
|
+
return compiler;
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
return compilerPromise;
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
name: "vinext:styled-jsx",
|
|
87
|
+
enforce: "pre",
|
|
88
|
+
configResolved(config) {
|
|
89
|
+
development = config.command === "serve";
|
|
90
|
+
if (config.root !== projectRoot) {
|
|
91
|
+
projectRoot = config.root;
|
|
92
|
+
nextRequire = void 0;
|
|
93
|
+
compilerPromise = null;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
resolveId: {
|
|
97
|
+
filter: { id: STYLED_JSX_IMPORT_RE },
|
|
98
|
+
handler(source) {
|
|
99
|
+
try {
|
|
100
|
+
return getNextRequire()?.resolve(source) ?? null;
|
|
101
|
+
} catch {}
|
|
102
|
+
try {
|
|
103
|
+
return createProjectRequire(projectRoot).resolve(source);
|
|
104
|
+
} catch {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
transform: {
|
|
110
|
+
filter: {
|
|
111
|
+
id: {
|
|
112
|
+
include: /\.[cm]?[jt]sx?(?:\?.*)?$/,
|
|
113
|
+
exclude: NODE_MODULES_RE
|
|
114
|
+
},
|
|
115
|
+
code: STYLED_JSX_SOURCE_RE
|
|
116
|
+
},
|
|
117
|
+
async handler(source, id) {
|
|
118
|
+
if (NODE_MODULES_RE.test(id.split("?")[0])) return null;
|
|
119
|
+
const hasStyledJsxCss = STYLED_JSX_CSS_RE.test(source);
|
|
120
|
+
const hasStyledJsxElement = !hasStyledJsxCss && hasStyledJsxTag(source, id);
|
|
121
|
+
if (!hasStyledJsxCss && !hasStyledJsxElement) return null;
|
|
122
|
+
if (!getNextRequire()) throw new Error("[vinext] styled-jsx requires an installed next package so vinext can use its matching compiler.");
|
|
123
|
+
const result = await (await getCompiler()).transform(source, {
|
|
124
|
+
filename: id.split("?")[0],
|
|
125
|
+
sourceMaps: true,
|
|
126
|
+
module: { type: "es6" },
|
|
127
|
+
styledJsx: { useLightningcss: false },
|
|
128
|
+
jsc: {
|
|
129
|
+
parser: parserOptions(id),
|
|
130
|
+
transform: {
|
|
131
|
+
react: {
|
|
132
|
+
runtime: "automatic",
|
|
133
|
+
development,
|
|
134
|
+
useBuiltins: true
|
|
135
|
+
},
|
|
136
|
+
optimizer: { simplify: false }
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
code: result.code,
|
|
142
|
+
map: result.map ?? null
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
//#endregion
|
|
149
|
+
export { createStyledJsxPlugin };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { decodeRouteSegment, isInvisibleSegment, sortRoutes } from "./utils.js";
|
|
2
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
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";
|
|
@@ -440,9 +440,9 @@ function validatePageRouteConflicts(routes, appDir) {
|
|
|
440
440
|
}
|
|
441
441
|
}
|
|
442
442
|
function formatAppFilePath(filePath, appDir) {
|
|
443
|
-
const relativePath = path.relative(appDir, filePath)
|
|
443
|
+
const relativePath = normalizePathSeparators(path.relative(appDir, filePath));
|
|
444
444
|
const parsedPath = path.parse(relativePath);
|
|
445
|
-
const withoutExtension = path.join(parsedPath.dir, parsedPath.name)
|
|
445
|
+
const withoutExtension = normalizePathSeparators(path.join(parsedPath.dir, parsedPath.name));
|
|
446
446
|
return withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
|
|
447
447
|
}
|
|
448
448
|
/**
|
|
@@ -1180,9 +1180,7 @@ function findSlotRootPage(slotDir, matcher) {
|
|
|
1180
1180
|
* default component, intercepting route, or nested page-backed sub-route.
|
|
1181
1181
|
*
|
|
1182
1182
|
* `dir` and `appDir` must be forward-slash. The slot directory is built from
|
|
1183
|
-
* `dir` with `path.posix.join
|
|
1184
|
-
* `path.posix.relative(appDir, …)`, which only yields a forward-slash relative
|
|
1185
|
-
* path when both operands already are.
|
|
1183
|
+
* `dir` with `path.posix.join`.
|
|
1186
1184
|
*/
|
|
1187
1185
|
function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = false) {
|
|
1188
1186
|
const entries = readDirEntriesCached(dir, matcher);
|
|
@@ -1197,12 +1195,12 @@ function discoverParallelSlots(dir, appDir, matcher, includeNestedOnlySlots = fa
|
|
|
1197
1195
|
const interceptingRoutes = discoverInterceptingRoutes(slotDir, dir, appDir, matcher);
|
|
1198
1196
|
const hasNestedPages = includeNestedOnlySlots && findSlotSubPages(slotDir, matcher).length > 0;
|
|
1199
1197
|
if (!pagePath && !defaultPath && interceptingRoutes.length === 0 && !hasNestedPages) continue;
|
|
1200
|
-
const ownerSegments = path.
|
|
1198
|
+
const ownerSegments = path.relative(appDir, dir).split(path.sep).filter((segment) => segment.length > 0);
|
|
1201
1199
|
const ownerTreePath = createAppRouteGraphTreePath(ownerSegments, ownerSegments.length);
|
|
1202
1200
|
const configLayoutPaths = findSlotConfigLayoutPaths(slotDir, pagePath, matcher);
|
|
1203
1201
|
slots.push({
|
|
1204
1202
|
id: createAppRouteGraphSlotId(slotName, ownerTreePath),
|
|
1205
|
-
key: `${slotName}@${path.
|
|
1203
|
+
key: `${slotName}@${normalizePathSeparators(path.relative(appDir, slotDir))}`,
|
|
1206
1204
|
name: slotName,
|
|
1207
1205
|
ownerDir: slotDir,
|
|
1208
1206
|
ownerTreePath,
|
|
@@ -1262,8 +1260,7 @@ function isInterceptionMarkerDir(name) {
|
|
|
1262
1260
|
* They intercept navigation to another route and render within the slot instead.
|
|
1263
1261
|
*
|
|
1264
1262
|
* `slotDir`, `routeDir`, and `appDir` must be forward-slash. They are passed
|
|
1265
|
-
* down to `path.posix.join`
|
|
1266
|
-
* intercept page paths and target patterns.
|
|
1263
|
+
* down to `path.posix.join` when building the intercept page paths.
|
|
1267
1264
|
*
|
|
1268
1265
|
* @param slotDir - The parallel slot directory (e.g. app/feed/@modal)
|
|
1269
1266
|
* @param routeDir - The directory of the route that owns this slot (e.g. app/feed)
|
|
@@ -1283,13 +1280,17 @@ function discoverInterceptingRoutes(slotDir, routeDir, appDir, matcher) {
|
|
|
1283
1280
|
* Sibling intercepts use the same conventions and target-computation logic as
|
|
1284
1281
|
* slot intercepts, but their intercepting page replaces the full page response
|
|
1285
1282
|
* (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.*`.
|
|
1286
1287
|
*/
|
|
1287
1288
|
function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
1288
1289
|
const routesByDir = /* @__PURE__ */ new Map();
|
|
1289
1290
|
for (const route of routes) {
|
|
1290
1291
|
const filePath = route.pagePath ?? route.routePath;
|
|
1291
1292
|
if (!filePath) continue;
|
|
1292
|
-
const routeDir =
|
|
1293
|
+
const routeDir = path.posix.dirname(filePath);
|
|
1293
1294
|
if (!routesByDir.has(routeDir)) routesByDir.set(routeDir, route);
|
|
1294
1295
|
}
|
|
1295
1296
|
function walk(dir) {
|
|
@@ -1303,7 +1304,7 @@ function discoverSiblingInterceptingRoutes(routes, appDir, matcher) {
|
|
|
1303
1304
|
if (!entry.isDirectory()) continue;
|
|
1304
1305
|
if (entry.name.startsWith("_")) continue;
|
|
1305
1306
|
if (entry.name.startsWith("@")) continue;
|
|
1306
|
-
const childDir = path.join(dir, entry.name);
|
|
1307
|
+
const childDir = path.posix.join(dir, entry.name);
|
|
1307
1308
|
const marker = matchInterceptConvention(entry.name);
|
|
1308
1309
|
if (marker) {
|
|
1309
1310
|
const restOfName = entry.name.slice(marker.prefix.length);
|
|
@@ -1370,8 +1371,7 @@ function findOwnerRouteForDir(dir, appDir, routes, routesByDir) {
|
|
|
1370
1371
|
* intercepting route directories.
|
|
1371
1372
|
*
|
|
1372
1373
|
* `currentDir`, `routeDir`, and `appDir` must be forward-slash. `currentDir`
|
|
1373
|
-
* descends with `path.posix.join`
|
|
1374
|
-
* `path.posix.relative` calls that build the intercept page paths and patterns.
|
|
1374
|
+
* descends with `path.posix.join` when building the intercept page paths.
|
|
1375
1375
|
*/
|
|
1376
1376
|
function scanForInterceptingPages(currentDir, routeDir, appDir, results, matcher) {
|
|
1377
1377
|
if (!fs.existsSync(currentDir)) return;
|
|
@@ -1397,6 +1397,12 @@ function matchInterceptConvention(name) {
|
|
|
1397
1397
|
/**
|
|
1398
1398
|
* Collect page.tsx files inside an intercepting route directory tree
|
|
1399
1399
|
* 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.
|
|
1400
1406
|
*/
|
|
1401
1407
|
function collectInterceptingPages(currentDir, interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, parentLayoutPaths = []) {
|
|
1402
1408
|
const currentLayoutPath = findFile(currentDir, "layout", matcher);
|
|
@@ -1407,7 +1413,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1407
1413
|
if (targetPattern) {
|
|
1408
1414
|
const sourceMatchPattern = computeInterceptSourceMatchPattern(interceptParentDir, appDir);
|
|
1409
1415
|
results.push({
|
|
1410
|
-
branchSegments: [interceptSegment, ...
|
|
1416
|
+
branchSegments: [interceptSegment, ...path.relative(interceptRoot, path.dirname(page)).split(path.sep).filter(Boolean)],
|
|
1411
1417
|
convention,
|
|
1412
1418
|
layoutPaths: [...layoutPaths],
|
|
1413
1419
|
layoutSegments: layoutPaths.map((layoutPath) => {
|
|
@@ -1426,7 +1432,7 @@ function collectInterceptingPages(currentDir, interceptRoot, convention, interce
|
|
|
1426
1432
|
for (const entry of entries) {
|
|
1427
1433
|
if (!entry.isDirectory()) continue;
|
|
1428
1434
|
if (entry.name.startsWith("_")) continue;
|
|
1429
|
-
collectInterceptingPages(path.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1435
|
+
collectInterceptingPages(path.posix.join(currentDir, entry.name), interceptRoot, convention, interceptSegment, routeDir, appDir, interceptParentDir, results, matcher, layoutPaths);
|
|
1430
1436
|
}
|
|
1431
1437
|
}
|
|
1432
1438
|
/**
|
|
@@ -39,11 +39,18 @@ declare function findFileWithExts(dir: string, name: string, matcher: ValidFileM
|
|
|
39
39
|
* 1. User-configured pageExtensions go first (each prefixed with `.`) so
|
|
40
40
|
* the user's priority wins. e.g. `.platform.tsx` resolves before `.tsx`.
|
|
41
41
|
* 2. Vite's defaults follow, with duplicates removed.
|
|
42
|
+
* 3. `.cjs`/`.cts` go last (lowest priority). Neither Vite's defaults nor the
|
|
43
|
+
* user's pageExtensions include them, but `vinext init` renames CJS config
|
|
44
|
+
* files (e.g. `tailwind.config.js` → `tailwind.config.cjs`) when it adds
|
|
45
|
+
* `"type": "module"`, and app code imports those extensionlessly
|
|
46
|
+
* (`import cfg from "../tailwind.config"`). Without these, the bundle fails
|
|
47
|
+
* with "[UNRESOLVED_IMPORT] Could not resolve '../tailwind.config'".
|
|
42
48
|
*
|
|
43
49
|
* The user's pageExtensions retain their relative order, which is what
|
|
44
50
|
* Next.js / Turbopack do via the `resolveExtensions` config option.
|
|
45
51
|
*
|
|
46
|
-
* See: cloudflare/vinext#1502
|
|
52
|
+
* See: cloudflare/vinext#1502 for page-extension ordering, and
|
|
53
|
+
* cloudflare/vinext#2435 for extensionless `.cjs` config imports.
|
|
47
54
|
*/
|
|
48
55
|
declare function buildViteResolveExtensions(pageExtensions?: readonly string[] | null, viteDefaults?: readonly string[]): string[];
|
|
49
56
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { escapeRegExp } from "../utils/regex.js";
|
|
2
1
|
import { normalizePathSeparators } from "../utils/path.js";
|
|
2
|
+
import { escapeRegExp } from "../utils/regex.js";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
5
|
import { glob } from "node:fs/promises";
|
|
@@ -91,11 +91,18 @@ function findFileWithExts(dir, name, matcher) {
|
|
|
91
91
|
* 1. User-configured pageExtensions go first (each prefixed with `.`) so
|
|
92
92
|
* the user's priority wins. e.g. `.platform.tsx` resolves before `.tsx`.
|
|
93
93
|
* 2. Vite's defaults follow, with duplicates removed.
|
|
94
|
+
* 3. `.cjs`/`.cts` go last (lowest priority). Neither Vite's defaults nor the
|
|
95
|
+
* user's pageExtensions include them, but `vinext init` renames CJS config
|
|
96
|
+
* files (e.g. `tailwind.config.js` → `tailwind.config.cjs`) when it adds
|
|
97
|
+
* `"type": "module"`, and app code imports those extensionlessly
|
|
98
|
+
* (`import cfg from "../tailwind.config"`). Without these, the bundle fails
|
|
99
|
+
* with "[UNRESOLVED_IMPORT] Could not resolve '../tailwind.config'".
|
|
94
100
|
*
|
|
95
101
|
* The user's pageExtensions retain their relative order, which is what
|
|
96
102
|
* Next.js / Turbopack do via the `resolveExtensions` config option.
|
|
97
103
|
*
|
|
98
|
-
* See: cloudflare/vinext#1502
|
|
104
|
+
* See: cloudflare/vinext#1502 for page-extension ordering, and
|
|
105
|
+
* cloudflare/vinext#2435 for extensionless `.cjs` config imports.
|
|
99
106
|
*/
|
|
100
107
|
function buildViteResolveExtensions(pageExtensions, viteDefaults = [
|
|
101
108
|
".mjs",
|
|
@@ -109,7 +116,12 @@ function buildViteResolveExtensions(pageExtensions, viteDefaults = [
|
|
|
109
116
|
const dotted = normalizePageExtensions(pageExtensions).map((ext) => `.${ext}`);
|
|
110
117
|
const seen = /* @__PURE__ */ new Set();
|
|
111
118
|
const result = [];
|
|
112
|
-
for (const ext of [
|
|
119
|
+
for (const ext of [
|
|
120
|
+
...dotted,
|
|
121
|
+
...viteDefaults,
|
|
122
|
+
".cjs",
|
|
123
|
+
".cts"
|
|
124
|
+
]) {
|
|
113
125
|
if (seen.has(ext)) continue;
|
|
114
126
|
seen.add(ext);
|
|
115
127
|
result.push(ext);
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { VINEXT_CLIENT_REUSE_MANIFEST_HEADER, VINEXT_PARAMS_HEADER, VINEXT_RSC_REDIRECT_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";
|
|
@@ -13,7 +12,7 @@ import { DevRecoveryBoundary, GlobalErrorBoundary, RedirectBoundary } from "../s
|
|
|
13
12
|
import { beginAppRouterScrollIntent, consumeAppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
|
|
14
13
|
import { AppRouterScrollCommitProvider } from "../shims/app-router-scroll.js";
|
|
15
14
|
import { BfcacheStateKeyMapContext, ElementsContext, Slot } from "../shims/slot.js";
|
|
16
|
-
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId } from "./app-rsc-cache-busting.js";
|
|
15
|
+
import { VINEXT_RSC_COMPATIBILITY_ID_HEADER, VINEXT_RSC_CONTENT_TYPE, createRscRequestHeaders, createRscRequestUrl, getVinextRscCompatibilityId, stripRscCacheBustingSearchParam } from "./app-rsc-cache-busting.js";
|
|
17
16
|
import { getNavigationRuntime, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions } from "../client/navigation-runtime.js";
|
|
18
17
|
import { notifyAppRouterTransitionStart } from "../client/instrumentation-client-state.js";
|
|
19
18
|
import "../client/instrumentation-client.js";
|
|
@@ -25,7 +24,8 @@ import { readHistoryStatePreviousNextUrl } from "./app-history-state.js";
|
|
|
25
24
|
import { AppBrowserMpaNavigationScheduler } from "./app-browser-mpa-navigation.js";
|
|
26
25
|
import { blockDangerousStreamedRscRedirect } from "./app-browser-rsc-redirect.js";
|
|
27
26
|
import { navigationPlanner } from "./navigation-planner.js";
|
|
28
|
-
import {
|
|
27
|
+
import { resolveHybridClientRewriteHref } from "../shims/internal/hybrid-client-route-owner.js";
|
|
28
|
+
import { DYNAMIC_NAVIGATION_CACHE_TTL, PREFETCH_CACHE_TTL, __basePath, appRouterInstance, commitClientNavigationState, consumePrefetchResponseForNavigation, createCachedRscResponseSnapshot, createClientNavigationRenderSnapshot, createSnapshotPathAndSearch, deletePrefetchResponseSnapshot, getClientNavigationRenderContext, getMountedSlotsHeader, getPrefetchCache, hasPrefetchCacheEntryForNavigation, invalidatePrefetchCache, pushHistoryStateWithoutNotify, replaceClientParamsWithoutNotify, replaceHistoryStateWithoutNotify, resolvePrefetchCacheEntryMountedSlotsHeader, restoreRscResponse, saveScrollPosition, seedPrefetchResponseSnapshot, setClientParams, setMountedSlotsHeader, setPendingPathname, useRouter } from "../shims/navigation.js";
|
|
29
29
|
import { chunksToReadableStream, createProgressiveRscStream, getVinextBrowserGlobal } from "./app-browser-stream.js";
|
|
30
30
|
import { COMMITTED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN, VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN, isCacheRestorableAppPayloadMetadata, isCompleteAppPayloadMetadata, resolveInterceptionContextFromPreviousNextUrl } from "./app-browser-state.js";
|
|
31
31
|
import { clearHardNavigationLoopGuard, createAppBrowserNavigationController, createBasePathStrippedPathAndSearch } from "./app-browser-navigation-controller.js";
|
|
@@ -193,6 +193,21 @@ function clearClientNavigationCaches() {
|
|
|
193
193
|
clearPrefetchState();
|
|
194
194
|
historyController.invalidateRestorableClientState();
|
|
195
195
|
}
|
|
196
|
+
function normalizeBrowserRscUrlForReuse(url) {
|
|
197
|
+
if (!url) return null;
|
|
198
|
+
try {
|
|
199
|
+
const parsed = new URL(url, window.location.origin);
|
|
200
|
+
stripRscCacheBustingSearchParam(parsed);
|
|
201
|
+
return `${parsed.pathname}${parsed.search}`;
|
|
202
|
+
} catch {
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function isAlternatePrefetchResponseUrl(responseUrl, additionalRscUrls) {
|
|
207
|
+
const normalizedResponseUrl = normalizeBrowserRscUrlForReuse(responseUrl);
|
|
208
|
+
if (normalizedResponseUrl === null) return false;
|
|
209
|
+
return additionalRscUrls.some((additionalRscUrl) => normalizeBrowserRscUrlForReuse(additionalRscUrl) === normalizedResponseUrl);
|
|
210
|
+
}
|
|
196
211
|
function isSettledPrefetchCacheEntry(entry) {
|
|
197
212
|
return entry.outcome === "cache-seeded" && entry.pending === void 0 && entry.snapshot !== void 0;
|
|
198
213
|
}
|
|
@@ -242,6 +257,7 @@ async function learnOptimisticRouteTemplatesFromPrefetchCache(options) {
|
|
|
242
257
|
if (optimisticRouteTemplateSources.has(sourceKey)) continue;
|
|
243
258
|
if (optimisticRouteTemplateLearning.has(sourceKey)) continue;
|
|
244
259
|
if (!isSettledPrefetchCacheEntry(entry)) continue;
|
|
260
|
+
if (entry.prefetchKind === "route-tree") continue;
|
|
245
261
|
const promise = learnOptimisticRouteTemplateFromPrefetch({
|
|
246
262
|
cacheKey,
|
|
247
263
|
entry,
|
|
@@ -545,9 +561,17 @@ function BrowserRoot({ hydrationCachePublication, initialElements, initialNaviga
|
|
|
545
561
|
setWindowNextInternalSourcePage(AppElementsWire.readMetadata(treeState.elements).sourcePage);
|
|
546
562
|
}, [treeState.elements]);
|
|
547
563
|
useLayoutEffect(() => {
|
|
548
|
-
|
|
564
|
+
const previousMountedSlotsHeader = getMountedSlotsHeader();
|
|
565
|
+
const nextMountedSlotsHeader = getMountedSlotIdsHeader(stateRef.current.elements);
|
|
566
|
+
setMountedSlotsHeader(nextMountedSlotsHeader);
|
|
549
567
|
removeStylesheetLinksCoveredByInlineCss();
|
|
550
|
-
|
|
568
|
+
if (previousMountedSlotsHeader === nextMountedSlotsHeader) return;
|
|
569
|
+
const pingTimer = window.setTimeout(() => {
|
|
570
|
+
getNavigationRuntime()?.functions.pingVisibleLinks?.();
|
|
571
|
+
}, 0);
|
|
572
|
+
return () => {
|
|
573
|
+
window.clearTimeout(pingTimer);
|
|
574
|
+
};
|
|
551
575
|
}, [treeState.elements]);
|
|
552
576
|
useLayoutEffect(() => {
|
|
553
577
|
if (treeState.renderId !== 0) return;
|
|
@@ -748,14 +772,14 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
748
772
|
...initialRscBootstrap?.dynamicStaleTimeSeconds !== void 0 ? { dynamicStaleTimeSeconds: initialRscBootstrap.dynamicStaleTimeSeconds } : {},
|
|
749
773
|
mountedSlotsHeader,
|
|
750
774
|
paramsHeader: encodeURIComponent(JSON.stringify(initialParams)),
|
|
775
|
+
renderedPathAndSearch: null,
|
|
751
776
|
url: rscUrl
|
|
752
777
|
};
|
|
753
778
|
const fallbackTtlMs = initialRscBootstrap?.initialCacheKind === "static" ? PREFETCH_CACHE_TTL : DYNAMIC_NAVIGATION_CACHE_TTL;
|
|
754
779
|
hydrationCachePublication.publish(() => {
|
|
755
780
|
if (cacheGeneration !== clientNavigationCacheGeneration) return () => {};
|
|
756
781
|
if (isCacheRestorableAppPayloadMetadata(metadata)) return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader);
|
|
757
|
-
|
|
758
|
-
return () => deletePrefetchResponseSnapshot(rscUrl, snapshot, metadata.interceptionContext);
|
|
782
|
+
return storeVisitedResponseSnapshot(rscUrl, metadata.interceptionContext, snapshot, initialParams, fallbackTtlMs, mountedSlotsHeader, elements);
|
|
759
783
|
});
|
|
760
784
|
}).catch(() => {});
|
|
761
785
|
historyController.writeBootstrapHistoryMetadata();
|
|
@@ -863,10 +887,11 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
863
887
|
const shouldBypassNavigationCache = earlyIntentDecision?.kind === "flightNavigation" && earlyIntentDecision.bypassNavigationCache;
|
|
864
888
|
const requestHeaders = createRscRequestHeaders({
|
|
865
889
|
interceptionContext: requestInterceptionContext,
|
|
866
|
-
mountedSlotsHeader
|
|
867
|
-
renderMode: navigationKind === "refresh" ? APP_RSC_RENDER_MODE_REFRESH_PRESERVE_UI : void 0
|
|
890
|
+
mountedSlotsHeader
|
|
868
891
|
});
|
|
869
892
|
const rscUrl = await createRscRequestUrl(url.pathname + url.search, requestHeaders);
|
|
893
|
+
const rewrittenNavigationHref = navigationKind === "navigate" ? resolveHybridClientRewriteHref(currentHref, __basePath) : null;
|
|
894
|
+
const additionalPrefetchRscUrls = rewrittenNavigationHref && rewrittenNavigationHref !== currentHref ? [await createRscRequestUrl(rewrittenNavigationHref, requestHeaders)] : [];
|
|
870
895
|
const visitedResponseCandidate = shouldBypassNavigationCache ? {
|
|
871
896
|
cacheKey: AppElementsWire.encodeCacheKey(rscUrl, requestInterceptionContext),
|
|
872
897
|
entry: null,
|
|
@@ -883,7 +908,10 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
883
908
|
visitedResponse
|
|
884
909
|
});
|
|
885
910
|
let routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
|
|
886
|
-
const hasPrefetchCandidate = prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
911
|
+
const hasPrefetchCandidate = prefetchProbeDecision.kind === "probe" && hasPrefetchCacheEntryForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
912
|
+
additionalRscUrls: additionalPrefetchRscUrls,
|
|
913
|
+
notifyInvalidation: false
|
|
914
|
+
});
|
|
887
915
|
const reuseDecision = navigationPlanner.classifyNavigationReuse({
|
|
888
916
|
bypassNavigationCache: shouldBypassNavigationCache,
|
|
889
917
|
navigationKind,
|
|
@@ -941,12 +969,15 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
941
969
|
let navResponseUrl = null;
|
|
942
970
|
let fallbackReuseDecision = reuseDecision;
|
|
943
971
|
if (reuseDecision.kind === "consumePrefetch") {
|
|
944
|
-
const prefetchedResponse = await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
972
|
+
const prefetchedResponse = await consumePrefetchResponseForNavigation(rscUrl, requestInterceptionContext, mountedSlotsHeader, {
|
|
973
|
+
additionalRscUrls: additionalPrefetchRscUrls,
|
|
974
|
+
shouldConsume: () => browserNavigationController.isCurrentNavigation(navId)
|
|
975
|
+
});
|
|
945
976
|
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
946
977
|
if (prefetchedResponse) {
|
|
947
978
|
navResponse = restoreRscResponse(prefetchedResponse, false);
|
|
948
979
|
navResponseExpiresAt = prefetchedResponse.expiresAt;
|
|
949
|
-
navResponseUrl = prefetchedResponse.url;
|
|
980
|
+
navResponseUrl = isAlternatePrefetchResponseUrl(prefetchedResponse.url, additionalPrefetchRscUrls) ? rscUrl : prefetchedResponse.url;
|
|
950
981
|
}
|
|
951
982
|
if (!navResponse) {
|
|
952
983
|
routeManifest = navigationKind === "navigate" ? getBrowserRouteManifest() : null;
|
|
@@ -1053,7 +1084,6 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1053
1084
|
committedState = state;
|
|
1054
1085
|
if (activeNavigationAbortController === navigationAbortController) activeNavigationAbortController = null;
|
|
1055
1086
|
}, detachedNavigationCommits ? "authoritative" : void 0) !== "committed") return;
|
|
1056
|
-
if (!browserNavigationController.isCurrentNavigation(navId)) return;
|
|
1057
1087
|
try {
|
|
1058
1088
|
const renderedElements = await rscPayload;
|
|
1059
1089
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
@@ -1074,7 +1104,7 @@ function bootstrapHydration(rscStream, devErrorOverlay, initialRscBootstrap) {
|
|
|
1074
1104
|
if (isCacheRestorableAppPayloadMetadata(metadata)) {
|
|
1075
1105
|
if (navigationCacheGeneration !== clientNavigationCacheGeneration) return;
|
|
1076
1106
|
storeVisitedResponseSnapshot(rscUrl, interceptionContext, snapshot, navParams, PREFETCH_CACHE_TTL, mountedSlotsHeader);
|
|
1077
|
-
} else if (committedState !== null
|
|
1107
|
+
} else if (committedState !== null) {
|
|
1078
1108
|
const state = committedState;
|
|
1079
1109
|
const committedElements = {
|
|
1080
1110
|
...state.elements,
|
|
@@ -449,8 +449,11 @@ function createAppBrowserNavigationController(deps = {}) {
|
|
|
449
449
|
return;
|
|
450
450
|
}
|
|
451
451
|
if (latestApproval.approvedCommit) {
|
|
452
|
-
|
|
453
|
-
|
|
452
|
+
const approvedRevalidationCommit = latestApproval.approvedCommit;
|
|
453
|
+
startTransition(() => {
|
|
454
|
+
dispatchSynchronousVisibleCommit(approvedRevalidationCommit);
|
|
455
|
+
});
|
|
456
|
+
syncHistoryStatePreviousNextUrl(approvedRevalidationCommit.previousNextUrl, approvedRevalidationCommit.action.bfcacheIds);
|
|
454
457
|
} else notifyDiscardedServerActionRevalidation(lifecycleOptions);
|
|
455
458
|
} else if (decision.disposition === "no-commit") notifyDiscardedServerActionRevalidation(lifecycleOptions);
|
|
456
459
|
if (returnValue) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hasBasePath } from "../utils/base-path.js";
|
|
1
2
|
import { ACTION_REDIRECT_HEADER, ACTION_REDIRECT_STATUS_HEADER } from "./headers.js";
|
|
2
3
|
import { DANGEROUS_URL_BLOCK_MESSAGE, isDangerousScheme } from "../shims/url-safety.js";
|
|
3
4
|
import { AppElementsWire } from "./app-elements-wire.js";
|
|
@@ -9,7 +10,7 @@ import { applyServerActionResultDecision } from "./app-browser-server-action-nav
|
|
|
9
10
|
import { throwOnServerActionNotFound } from "./server-action-not-found.js";
|
|
10
11
|
import { createFromFetch, createTemporaryReferenceSet, encodeReply } from "@vitejs/plugin-rsc/browser";
|
|
11
12
|
//#region src/server/app-browser-server-action-client.ts
|
|
12
|
-
function resolveActionRedirectTarget(response, performHardNavigation) {
|
|
13
|
+
function resolveActionRedirectTarget(response, basePath, performHardNavigation) {
|
|
13
14
|
const actionRedirect = response.headers.get(ACTION_REDIRECT_HEADER);
|
|
14
15
|
if (!actionRedirect) return null;
|
|
15
16
|
if (isDangerousScheme(actionRedirect)) {
|
|
@@ -25,7 +26,7 @@ function resolveActionRedirectTarget(response, performHardNavigation) {
|
|
|
25
26
|
if (!baseDir.endsWith("/")) baseDir += "/";
|
|
26
27
|
redirectUrl = new URL(actionRedirect, `${baseParsed.origin}${baseDir}${baseParsed.search}`);
|
|
27
28
|
}
|
|
28
|
-
if (redirectUrl.origin !== window.location.origin) {
|
|
29
|
+
if (redirectUrl.origin !== window.location.origin || basePath !== "" && !hasBasePath(redirectUrl.pathname, basePath)) {
|
|
29
30
|
performHardNavigation(actionRedirect);
|
|
30
31
|
return null;
|
|
31
32
|
}
|
|
@@ -60,6 +61,7 @@ async function invokeClientServerAction(id, args, actionInitiation, deps) {
|
|
|
60
61
|
actionId: id,
|
|
61
62
|
basePath: deps.basePath,
|
|
62
63
|
elements: actionInitiation.routerState.elements,
|
|
64
|
+
interceptionContext: actionInitiation.routerState.interception !== null ? actionInitiation.routerState.interceptionContext : null,
|
|
63
65
|
previousNextUrl: actionInitiation.routerState.previousNextUrl
|
|
64
66
|
}).headers;
|
|
65
67
|
const fetchResponse = await fetch(createServerActionRequestUrl(actionInitiation.path), {
|
|
@@ -69,7 +71,7 @@ async function invokeClientServerAction(id, args, actionInitiation, deps) {
|
|
|
69
71
|
});
|
|
70
72
|
throwOnServerActionNotFound(fetchResponse, id);
|
|
71
73
|
const hasActionRedirect = fetchResponse.headers.has(ACTION_REDIRECT_HEADER);
|
|
72
|
-
const actionRedirectTarget = resolveActionRedirectTarget(fetchResponse, (url, historyMode) => deps.performHardNavigation(url, historyMode));
|
|
74
|
+
const actionRedirectTarget = resolveActionRedirectTarget(fetchResponse, deps.basePath, (url, historyMode) => deps.performHardNavigation(url, historyMode));
|
|
73
75
|
if (hasActionRedirect && !actionRedirectTarget) return void 0;
|
|
74
76
|
const actionResultFacts = createServerActionResultFacts({
|
|
75
77
|
actionRedirectHref: actionRedirectTarget?.href ?? null,
|