vinext 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/build/prerender.d.ts +9 -1
- package/dist/build/prerender.js +41 -12
- package/dist/build/run-prerender.d.ts +10 -2
- package/dist/build/run-prerender.js +15 -1
- package/dist/client/app-nav-failure-handler.d.ts +8 -0
- package/dist/client/app-nav-failure-handler.js +44 -0
- package/dist/client/vinext-next-data.d.ts +18 -1
- package/dist/client/window-next.d.ts +2 -1
- package/dist/client/window-next.js +12 -1
- package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
- package/dist/config/config-matchers.js +73 -14
- package/dist/config/next-config.d.ts +46 -4
- package/dist/config/next-config.js +147 -48
- package/dist/deploy.d.ts +30 -11
- package/dist/deploy.js +180 -99
- package/dist/entries/app-browser-entry.d.ts +9 -3
- package/dist/entries/app-browser-entry.js +21 -3
- package/dist/entries/app-rsc-entry.d.ts +2 -0
- package/dist/entries/app-rsc-entry.js +64 -5
- package/dist/entries/app-rsc-manifest.js +2 -0
- package/dist/entries/app-ssr-entry.js +1 -1
- package/dist/entries/pages-client-entry.js +53 -8
- package/dist/entries/pages-server-entry.js +41 -5
- package/dist/index.js +200 -62
- package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
- package/dist/plugins/extensionless-dynamic-import.js +152 -0
- package/dist/plugins/optimize-imports.d.ts +2 -1
- package/dist/plugins/optimize-imports.js +11 -9
- package/dist/plugins/postcss.js +7 -7
- package/dist/plugins/typeof-window.d.ts +14 -0
- package/dist/plugins/typeof-window.js +150 -0
- package/dist/routing/app-route-graph.d.ts +2 -1
- package/dist/routing/app-route-graph.js +44 -14
- package/dist/routing/file-matcher.d.ts +10 -1
- package/dist/routing/file-matcher.js +22 -1
- package/dist/routing/pages-router.js +3 -3
- package/dist/routing/utils.d.ts +35 -6
- package/dist/routing/utils.js +59 -7
- package/dist/server/api-handler.d.ts +6 -1
- package/dist/server/api-handler.js +21 -15
- package/dist/server/app-browser-action-result.d.ts +19 -6
- package/dist/server/app-browser-action-result.js +19 -10
- package/dist/server/app-browser-entry.js +167 -90
- package/dist/server/app-browser-error.d.ts +10 -6
- package/dist/server/app-browser-error.js +43 -8
- package/dist/server/app-browser-hydration.d.ts +2 -0
- package/dist/server/app-browser-hydration.js +1 -0
- package/dist/server/app-browser-navigation-controller.d.ts +4 -2
- package/dist/server/app-browser-navigation-controller.js +23 -2
- package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
- package/dist/server/app-browser-server-action-navigation.js +9 -0
- package/dist/server/app-browser-stream.js +86 -43
- package/dist/server/app-elements-wire.d.ts +6 -1
- package/dist/server/app-elements-wire.js +14 -4
- package/dist/server/app-elements.d.ts +2 -2
- package/dist/server/app-elements.js +2 -2
- package/dist/server/app-fallback-renderer.d.ts +1 -0
- package/dist/server/app-fallback-renderer.js +3 -1
- package/dist/server/app-optimistic-routing.js +2 -2
- package/dist/server/app-page-boundary-render.d.ts +1 -0
- package/dist/server/app-page-boundary-render.js +27 -14
- package/dist/server/app-page-cache-render.d.ts +53 -0
- package/dist/server/app-page-cache-render.js +91 -0
- package/dist/server/app-page-cache.d.ts +16 -2
- package/dist/server/app-page-cache.js +62 -1
- package/dist/server/app-page-dispatch.d.ts +26 -0
- package/dist/server/app-page-dispatch.js +149 -92
- package/dist/server/app-page-element-builder.d.ts +1 -0
- package/dist/server/app-page-element-builder.js +5 -2
- package/dist/server/app-page-execution.d.ts +6 -1
- package/dist/server/app-page-execution.js +21 -1
- package/dist/server/app-page-probe.d.ts +1 -0
- package/dist/server/app-page-probe.js +4 -0
- package/dist/server/app-page-render-observation.d.ts +3 -1
- package/dist/server/app-page-render-observation.js +17 -1
- package/dist/server/app-page-render.d.ts +12 -1
- package/dist/server/app-page-render.js +42 -4
- package/dist/server/app-page-request.d.ts +2 -0
- package/dist/server/app-page-request.js +2 -1
- package/dist/server/app-page-route-wiring.d.ts +3 -1
- package/dist/server/app-page-route-wiring.js +14 -5
- package/dist/server/app-page-stream.d.ts +15 -3
- package/dist/server/app-page-stream.js +11 -5
- package/dist/server/app-pages-bridge.d.ts +18 -0
- package/dist/server/app-pages-bridge.js +22 -5
- package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
- package/dist/server/app-ppr-fallback-shell-render.js +26 -0
- package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
- package/dist/server/app-ppr-fallback-shell.js +8 -1
- package/dist/server/app-route-handler-dispatch.js +9 -2
- package/dist/server/app-route-handler-policy.d.ts +1 -0
- package/dist/server/app-router-entry.js +5 -0
- package/dist/server/app-rsc-cache-busting.js +2 -0
- package/dist/server/app-rsc-handler.d.ts +25 -0
- package/dist/server/app-rsc-handler.js +154 -54
- package/dist/server/app-rsc-route-matching.d.ts +3 -0
- package/dist/server/app-rsc-route-matching.js +2 -0
- package/dist/server/app-segment-config.d.ts +9 -1
- package/dist/server/app-segment-config.js +12 -3
- package/dist/server/app-server-action-execution.d.ts +1 -0
- package/dist/server/app-server-action-execution.js +42 -13
- package/dist/server/app-ssr-entry.d.ts +2 -0
- package/dist/server/app-ssr-entry.js +83 -10
- package/dist/server/cache-control.js +4 -0
- package/dist/server/dev-server.d.ts +2 -2
- package/dist/server/dev-server.js +244 -51
- package/dist/server/hybrid-route-priority.d.ts +22 -0
- package/dist/server/hybrid-route-priority.js +33 -0
- package/dist/server/image-optimization.d.ts +18 -9
- package/dist/server/image-optimization.js +37 -23
- package/dist/server/implicit-tags.d.ts +2 -1
- package/dist/server/implicit-tags.js +4 -1
- package/dist/server/navigation-planner.d.ts +133 -30
- package/dist/server/navigation-planner.js +114 -0
- package/dist/server/navigation-trace.d.ts +8 -1
- package/dist/server/navigation-trace.js +8 -1
- package/dist/server/pages-api-route.d.ts +6 -0
- package/dist/server/pages-api-route.js +13 -2
- package/dist/server/pages-asset-tags.d.ts +2 -1
- package/dist/server/pages-asset-tags.js +6 -2
- package/dist/server/pages-data-route.d.ts +8 -1
- package/dist/server/pages-data-route.js +11 -2
- package/dist/server/pages-get-initial-props.d.ts +54 -4
- package/dist/server/pages-get-initial-props.js +43 -1
- package/dist/server/pages-node-compat.js +2 -2
- package/dist/server/pages-page-data.d.ts +11 -2
- package/dist/server/pages-page-data.js +204 -33
- package/dist/server/pages-page-handler.d.ts +4 -2
- package/dist/server/pages-page-handler.js +59 -22
- package/dist/server/pages-page-response.d.ts +2 -1
- package/dist/server/pages-page-response.js +7 -4
- package/dist/server/pages-request-pipeline.d.ts +1 -0
- package/dist/server/pages-request-pipeline.js +73 -36
- package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
- package/dist/server/pregenerated-concrete-paths.js +2 -19
- package/dist/server/prerender-manifest.d.ts +33 -0
- package/dist/server/prerender-manifest.js +54 -0
- package/dist/server/prerender-route-params.d.ts +1 -2
- package/dist/server/prod-server.js +9 -3
- package/dist/server/request-pipeline.d.ts +3 -15
- package/dist/server/request-pipeline.js +58 -47
- package/dist/server/rsc-stream-hints.d.ts +5 -1
- package/dist/server/rsc-stream-hints.js +6 -1
- package/dist/server/seed-cache.js +10 -18
- package/dist/shims/app-router-scroll-state.d.ts +3 -1
- package/dist/shims/app-router-scroll-state.js +14 -2
- package/dist/shims/app-router-scroll.d.ts +3 -0
- package/dist/shims/app-router-scroll.js +28 -18
- package/dist/shims/cache-runtime.js +3 -2
- package/dist/shims/cache.d.ts +1 -0
- package/dist/shims/cache.js +1 -1
- package/dist/shims/cdn-cache.d.ts +5 -5
- package/dist/shims/dynamic-preload-chunks.js +6 -4
- package/dist/shims/error-boundary.d.ts +2 -0
- package/dist/shims/error-boundary.js +7 -0
- package/dist/shims/error.js +3 -2
- package/dist/shims/error.react-server.d.ts +9 -0
- package/dist/shims/error.react-server.js +6 -0
- package/dist/shims/fetch-cache.d.ts +3 -1
- package/dist/shims/fetch-cache.js +45 -20
- package/dist/shims/hash-scroll.js +6 -1
- package/dist/shims/headers.js +29 -4
- package/dist/shims/internal/als-registry.js +28 -1
- package/dist/shims/internal/app-route-detection.js +8 -17
- package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
- package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
- package/dist/shims/internal/navigation-untracked.d.ts +35 -0
- package/dist/shims/internal/navigation-untracked.js +55 -0
- package/dist/shims/internal/pages-data-target.d.ts +7 -2
- package/dist/shims/internal/pages-data-target.js +17 -8
- package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
- package/dist/shims/internal/pages-router-accessor.js +13 -0
- package/dist/shims/internal/router-context.d.ts +2 -1
- package/dist/shims/internal/router-context.js +3 -1
- package/dist/shims/link.js +12 -5
- package/dist/shims/navigation.d.ts +8 -2
- package/dist/shims/navigation.js +61 -31
- package/dist/shims/ppr-fallback-shell.d.ts +5 -1
- package/dist/shims/ppr-fallback-shell.js +28 -7
- package/dist/shims/router.d.ts +13 -2
- package/dist/shims/router.js +419 -128
- package/dist/shims/server.d.ts +16 -1
- package/dist/shims/server.js +44 -12
- package/dist/shims/unified-request-context.js +1 -0
- package/dist/utils/built-asset-url.d.ts +4 -0
- package/dist/utils/built-asset-url.js +11 -0
- package/dist/utils/commonjs-loader.d.ts +16 -0
- package/dist/utils/commonjs-loader.js +100 -0
- package/dist/utils/deployment-id.d.ts +8 -0
- package/dist/utils/deployment-id.js +22 -0
- package/dist/utils/html-limited-bots.d.ts +18 -1
- package/dist/utils/html-limited-bots.js +23 -1
- package/dist/utils/parse-cookie.d.ts +13 -0
- package/dist/utils/parse-cookie.js +52 -0
- package/dist/utils/path.d.ts +7 -1
- package/dist/utils/path.js +9 -1
- package/package.json +2 -2
- package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
- package/dist/shims/internal/parse-cookie-header.js +0 -30
package/dist/deploy.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { detectPackageManager as detectPackageManager$1, ensureESModule as ensureESModule$1, findInNodeModules, renameCJSConfigs as renameCJSConfigs$1 } from "./utils/project.js";
|
|
2
2
|
import { parsePositiveIntegerArg } from "./cli-args.js";
|
|
3
|
+
import { escapeRegExp } from "./utils/regex.js";
|
|
3
4
|
import { loadNextConfig, resolveNextConfig } from "./config/next-config.js";
|
|
4
5
|
import { getReactUpgradeDeps } from "./init.js";
|
|
5
6
|
import { runTPR } from "./cloudflare/tpr.js";
|
|
7
|
+
import { buildPregeneratedConcretePathTable, readPrerenderManifest } from "./server/prerender-manifest.js";
|
|
6
8
|
import { runPrerender } from "./build/run-prerender.js";
|
|
7
9
|
import { loadDotenv } from "./config/dotenv.js";
|
|
8
10
|
import { createRequire } from "node:module";
|
|
@@ -123,14 +125,33 @@ function detectProject(root) {
|
|
|
123
125
|
} catch {}
|
|
124
126
|
let projectName = path.basename(root);
|
|
125
127
|
if (pkg?.name && typeof pkg.name === "string") projectName = pkg.name.replace(/^@[^/]+\//, "").toLowerCase().replace(/[^a-z0-9-]/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
|
|
126
|
-
const hasISR = detectISR(root, isAppRouter);
|
|
127
128
|
const hasTypeModule = pkg?.type === "module";
|
|
128
|
-
|
|
129
|
-
|
|
129
|
+
let hasISR = false;
|
|
130
|
+
let hasMDX = detectMDXFromConfig(root);
|
|
131
|
+
if (isAppRouter) {
|
|
132
|
+
const appDir = resolveProjectDir(root, "app");
|
|
133
|
+
if (appDir) {
|
|
134
|
+
const found = scanTreeForDetection(appDir, {
|
|
135
|
+
isr: true,
|
|
136
|
+
mdx: !hasMDX
|
|
137
|
+
});
|
|
138
|
+
hasISR = found.isr;
|
|
139
|
+
hasMDX = hasMDX || found.mdx;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (hasPages && !hasMDX) {
|
|
143
|
+
const pagesDir = resolveProjectDir(root, "pages");
|
|
144
|
+
if (pagesDir) hasMDX = scanTreeForDetection(pagesDir, {
|
|
145
|
+
isr: false,
|
|
146
|
+
mdx: true
|
|
147
|
+
}).mdx;
|
|
148
|
+
}
|
|
149
|
+
const allDeps = {
|
|
130
150
|
...pkg?.dependencies,
|
|
131
151
|
...pkg?.devDependencies
|
|
132
152
|
};
|
|
133
|
-
const
|
|
153
|
+
const hasCodeHike = "codehike" in allDeps;
|
|
154
|
+
const nativeModulesToStub = detectNativeModules(allDeps);
|
|
134
155
|
return {
|
|
135
156
|
root,
|
|
136
157
|
isAppRouter,
|
|
@@ -149,35 +170,69 @@ function detectProject(root) {
|
|
|
149
170
|
nativeModulesToStub
|
|
150
171
|
};
|
|
151
172
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
173
|
+
/** Matches `export const revalidate = …` (ISR opt-in) in App Router source. */
|
|
174
|
+
const ISR_REVALIDATE_PATTERN = /export\s+const\s+revalidate\s*=/;
|
|
175
|
+
/** Source extensions whose contents are scanned for the ISR pattern. */
|
|
176
|
+
const ISR_SCANNABLE_EXTENSION = /\.(ts|tsx|js|jsx)$/;
|
|
177
|
+
/**
|
|
178
|
+
* Resolve a project subdirectory (`app`/`pages`), preferring the root-level
|
|
179
|
+
* location and falling back to the `src/` variant. Returns null when neither
|
|
180
|
+
* exists.
|
|
181
|
+
*/
|
|
182
|
+
function resolveProjectDir(root, name) {
|
|
183
|
+
const rootDir = path.join(root, name);
|
|
184
|
+
if (fs.existsSync(rootDir)) return rootDir;
|
|
185
|
+
const srcDir = path.join(root, "src", name);
|
|
186
|
+
if (fs.existsSync(srcDir)) return srcDir;
|
|
187
|
+
return null;
|
|
162
188
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Recursively walk `dir` once, evaluating the requested detection predicates
|
|
191
|
+
* per entry. Each flag short-circuits independently: an `.mdx` file sets `mdx`;
|
|
192
|
+
* a scannable source file containing `export const revalidate` sets `isr`. The
|
|
193
|
+
* walk stops as soon as every requested flag is satisfied, so callers that only
|
|
194
|
+
* want one signal don't pay for the other.
|
|
195
|
+
*
|
|
196
|
+
* Replaces the previous pair of single-purpose recursive walkers
|
|
197
|
+
* (`scanDirForPattern` + `scanDirForExtension`) that traversed the same tree
|
|
198
|
+
* twice. Detection semantics are unchanged: same dirs skipped (dotfiles,
|
|
199
|
+
* node_modules), same extension and content tests.
|
|
200
|
+
*/
|
|
201
|
+
function scanTreeForDetection(dir, want) {
|
|
202
|
+
const found = {
|
|
203
|
+
isr: false,
|
|
204
|
+
mdx: false
|
|
205
|
+
};
|
|
206
|
+
const walk = (current) => {
|
|
207
|
+
let entries;
|
|
208
|
+
try {
|
|
209
|
+
entries = fs.readdirSync(current, { withFileTypes: true });
|
|
210
|
+
} catch {
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
for (const entry of entries) {
|
|
214
|
+
if ((!want.isr || found.isr) && (!want.mdx || found.mdx)) return;
|
|
215
|
+
const fullPath = path.join(current, entry.name);
|
|
216
|
+
if (entry.isDirectory()) {
|
|
217
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
|
|
218
|
+
walk(fullPath);
|
|
219
|
+
} else if (entry.isFile()) {
|
|
220
|
+
if (want.mdx && !found.mdx && entry.name.endsWith(".mdx")) found.mdx = true;
|
|
221
|
+
if (want.isr && !found.isr && ISR_SCANNABLE_EXTENSION.test(entry.name)) try {
|
|
222
|
+
if (ISR_REVALIDATE_PATTERN.test(fs.readFileSync(fullPath, "utf-8"))) found.isr = true;
|
|
223
|
+
} catch {}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
walk(dir);
|
|
228
|
+
return found;
|
|
175
229
|
}
|
|
176
230
|
/**
|
|
177
|
-
* Detect
|
|
178
|
-
*
|
|
231
|
+
* Detect MDX usage declared in next.config (`pageExtensions` including "mdx" or
|
|
232
|
+
* an `@next/mdx` import). Filesystem `.mdx` detection is handled separately by
|
|
233
|
+
* the shared app/pages tree walk in `detectProject`.
|
|
179
234
|
*/
|
|
180
|
-
function
|
|
235
|
+
function detectMDXFromConfig(root) {
|
|
181
236
|
for (const f of [
|
|
182
237
|
"next.config.ts",
|
|
183
238
|
"next.config.mts",
|
|
@@ -191,26 +246,6 @@ function detectMDX(root, isAppRouter, hasPages) {
|
|
|
191
246
|
if (/pageExtensions.*mdx/i.test(content) || /@next\/mdx/.test(content)) return true;
|
|
192
247
|
} catch {}
|
|
193
248
|
}
|
|
194
|
-
const dirs = [];
|
|
195
|
-
if (isAppRouter) {
|
|
196
|
-
const appDir = fs.existsSync(path.join(root, "app")) ? path.join(root, "app") : path.join(root, "src", "app");
|
|
197
|
-
dirs.push(appDir);
|
|
198
|
-
}
|
|
199
|
-
if (hasPages) {
|
|
200
|
-
const pagesDir = fs.existsSync(path.join(root, "pages")) ? path.join(root, "pages") : path.join(root, "src", "pages");
|
|
201
|
-
dirs.push(pagesDir);
|
|
202
|
-
}
|
|
203
|
-
for (const dir of dirs) if (fs.existsSync(dir) && scanDirForExtension(dir, ".mdx")) return true;
|
|
204
|
-
return false;
|
|
205
|
-
}
|
|
206
|
-
function scanDirForExtension(dir, ext) {
|
|
207
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
208
|
-
for (const entry of entries) {
|
|
209
|
-
const fullPath = path.join(dir, entry.name);
|
|
210
|
-
if (entry.isDirectory() && !entry.name.startsWith(".") && entry.name !== "node_modules") {
|
|
211
|
-
if (scanDirForExtension(fullPath, ext)) return true;
|
|
212
|
-
} else if (entry.isFile() && entry.name.endsWith(ext)) return true;
|
|
213
|
-
}
|
|
214
249
|
return false;
|
|
215
250
|
}
|
|
216
251
|
/** Known native Node modules that can't run in Workers */
|
|
@@ -222,21 +257,12 @@ const NATIVE_MODULES_TO_STUB = [
|
|
|
222
257
|
"sharp"
|
|
223
258
|
];
|
|
224
259
|
/**
|
|
225
|
-
* Detect native Node modules in
|
|
260
|
+
* Detect native Node modules in the project's merged dependency map that need
|
|
261
|
+
* stubbing for Workers. Accepts the already-built `allDeps` (dependencies +
|
|
262
|
+
* devDependencies) so package.json is not re-read or re-parsed.
|
|
226
263
|
*/
|
|
227
|
-
function detectNativeModules(
|
|
228
|
-
|
|
229
|
-
if (!fs.existsSync(pkgPath)) return [];
|
|
230
|
-
try {
|
|
231
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
232
|
-
const allDeps = {
|
|
233
|
-
...pkg.dependencies,
|
|
234
|
-
...pkg.devDependencies
|
|
235
|
-
};
|
|
236
|
-
return NATIVE_MODULES_TO_STUB.filter((mod) => mod in allDeps);
|
|
237
|
-
} catch {
|
|
238
|
-
return [];
|
|
239
|
-
}
|
|
264
|
+
function detectNativeModules(allDeps) {
|
|
265
|
+
return NATIVE_MODULES_TO_STUB.filter((mod) => mod in allDeps);
|
|
240
266
|
}
|
|
241
267
|
/** @see {@link _ensureESModule} */
|
|
242
268
|
const ensureESModule = ensureESModule$1;
|
|
@@ -280,6 +306,17 @@ import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isI
|
|
|
280
306
|
import type { ImageConfig } from "vinext/server/image-optimization";
|
|
281
307
|
import handler from "vinext/server/app-router-entry";
|
|
282
308
|
|
|
309
|
+
const imageConfig: ImageConfig = {
|
|
310
|
+
deviceSizes: JSON.parse(
|
|
311
|
+
process.env.__VINEXT_IMAGE_DEVICE_SIZES ?? JSON.stringify(DEFAULT_DEVICE_SIZES),
|
|
312
|
+
),
|
|
313
|
+
imageSizes: JSON.parse(
|
|
314
|
+
process.env.__VINEXT_IMAGE_SIZES ?? JSON.stringify(DEFAULT_IMAGE_SIZES),
|
|
315
|
+
),
|
|
316
|
+
qualities: JSON.parse(process.env.__VINEXT_IMAGE_QUALITIES ?? "null") ?? undefined,
|
|
317
|
+
dangerouslyAllowSVG: process.env.__VINEXT_IMAGE_DANGEROUSLY_ALLOW_SVG === "true",
|
|
318
|
+
};
|
|
319
|
+
|
|
283
320
|
interface Env {
|
|
284
321
|
ASSETS: Fetcher;
|
|
285
322
|
IMAGES: {
|
|
@@ -310,14 +347,17 @@ export default {
|
|
|
310
347
|
// The parseImageParams validation inside handleImageOptimization
|
|
311
348
|
// normalizes backslashes and validates the origin hasn't changed.
|
|
312
349
|
if (isImageOptimizationPath(url.pathname)) {
|
|
313
|
-
const allowedWidths = [
|
|
350
|
+
const allowedWidths = [
|
|
351
|
+
...(imageConfig.deviceSizes ?? DEFAULT_DEVICE_SIZES),
|
|
352
|
+
...(imageConfig.imageSizes ?? DEFAULT_IMAGE_SIZES),
|
|
353
|
+
];
|
|
314
354
|
return handleImageOptimization(request, {
|
|
315
355
|
fetchAsset: (path) => env.ASSETS.fetch(new Request(new URL(path, request.url))),
|
|
316
356
|
transformImage: async (body, { width, format, quality }) => {
|
|
317
357
|
const result = await env.IMAGES.input(body).transform(width > 0 ? { width } : {}).output({ format, quality });
|
|
318
358
|
return result.response();
|
|
319
359
|
},
|
|
320
|
-
}, allowedWidths);
|
|
360
|
+
}, allowedWidths, imageConfig);
|
|
321
361
|
}
|
|
322
362
|
|
|
323
363
|
// Delegate everything else to vinext, forwarding ctx so that
|
|
@@ -338,7 +378,7 @@ import { runPagesRequest, wrapMiddlewareWithBasePath } from "vinext/server/pages
|
|
|
338
378
|
import type { PagesPipelineDeps } from "vinext/server/pages-request-pipeline";
|
|
339
379
|
import { handleImageOptimization, DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath } from "vinext/server/image-optimization";
|
|
340
380
|
import type { ImageConfig } from "vinext/server/image-optimization";
|
|
341
|
-
import { cloneRequestWithHeaders, filterInternalHeaders, isOpenRedirectShaped } from "vinext/server/request-pipeline";
|
|
381
|
+
import { cloneRequestWithHeaders, cloneRequestWithUrl, filterInternalHeaders, isOpenRedirectShaped } from "vinext/server/request-pipeline";
|
|
342
382
|
import { notFoundStaticAssetResponse } from "vinext/server/http-error-responses";
|
|
343
383
|
import { assetPrefixPathname, isNextStaticPath } from "vinext/utils/asset-prefix";
|
|
344
384
|
import { hasBasePath, stripBasePath } from "vinext/utils/base-path";
|
|
@@ -373,6 +413,7 @@ const configRedirects = vinextConfig?.redirects ?? [];
|
|
|
373
413
|
const configRewrites = vinextConfig?.rewrites ?? { beforeFiles: [], afterFiles: [], fallback: [] };
|
|
374
414
|
const configHeaders = vinextConfig?.headers ?? [];
|
|
375
415
|
const imageConfig: ImageConfig | undefined = vinextConfig?.images ? {
|
|
416
|
+
qualities: vinextConfig.images.qualities,
|
|
376
417
|
dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
|
|
377
418
|
dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
|
|
378
419
|
contentDispositionType: vinextConfig.images.contentDispositionType,
|
|
@@ -430,7 +471,7 @@ export default {
|
|
|
430
471
|
if (stripped !== pathname) {
|
|
431
472
|
const strippedUrl = new URL(request.url);
|
|
432
473
|
strippedUrl.pathname = stripped;
|
|
433
|
-
request =
|
|
474
|
+
request = cloneRequestWithUrl(request, strippedUrl.toString());
|
|
434
475
|
pathname = stripped;
|
|
435
476
|
}
|
|
436
477
|
}
|
|
@@ -438,7 +479,10 @@ export default {
|
|
|
438
479
|
// ── Image optimization via Cloudflare Images binding ──────────
|
|
439
480
|
// Checked after basePath stripping so /<basePath>/_next/image works.
|
|
440
481
|
if (isImageOptimizationPath(pathname)) {
|
|
441
|
-
const allowedWidths = [
|
|
482
|
+
const allowedWidths = [
|
|
483
|
+
...(vinextConfig?.images?.deviceSizes ?? DEFAULT_DEVICE_SIZES),
|
|
484
|
+
...(vinextConfig?.images?.imageSizes ?? DEFAULT_IMAGE_SIZES),
|
|
485
|
+
];
|
|
442
486
|
return handleImageOptimization(request, {
|
|
443
487
|
fetchAsset: (path) => env.ASSETS.fetch(new Request(new URL(path, request.url))),
|
|
444
488
|
transformImage: async (body, { width, format, quality }) => {
|
|
@@ -797,58 +841,97 @@ async function runBuild(info, env) {
|
|
|
797
841
|
await (await createBuilder({ root: info.root })).buildApp();
|
|
798
842
|
});
|
|
799
843
|
}
|
|
844
|
+
function validateWranglerEnvName(env) {
|
|
845
|
+
if (env.includes("\0")) throw new Error("Wrangler environment names cannot contain null bytes.");
|
|
846
|
+
return env;
|
|
847
|
+
}
|
|
800
848
|
function buildWranglerDeployArgs(options) {
|
|
801
849
|
const args = ["deploy"];
|
|
802
850
|
const env = options.env || (options.preview ? "preview" : void 0);
|
|
803
|
-
if (env) args.push("--env", env);
|
|
851
|
+
if (env) args.push("--env", validateWranglerEnvName(env));
|
|
804
852
|
return {
|
|
805
853
|
args,
|
|
806
854
|
env
|
|
807
855
|
};
|
|
808
856
|
}
|
|
809
857
|
/**
|
|
810
|
-
* Resolve
|
|
858
|
+
* Resolve Wrangler's JavaScript CLI entrypoint in node_modules.
|
|
811
859
|
*
|
|
812
|
-
*
|
|
813
|
-
*
|
|
814
|
-
*
|
|
815
|
-
* On Windows, `node_modules/.bin/` contains both a Unix shebang script (no
|
|
816
|
-
* extension) and a `.CMD` shim. Node's `execFileSync` uses CreateProcess(),
|
|
817
|
-
* which only resolves PATHEXT extensions (`.cmd`, `.exe`, ...) — spawning the
|
|
818
|
-
* bare-name shebang file fails with ENOENT even though the file exists. So on
|
|
819
|
-
* Windows we prefer the `.CMD` shim and only fall back to the bare name for a
|
|
820
|
-
* clearer error message if neither is present.
|
|
860
|
+
* Invoking the JavaScript file through `process.execPath` avoids the `.cmd`
|
|
861
|
+
* shim and command shell that package managers create on Windows.
|
|
821
862
|
*/
|
|
822
|
-
function resolveWranglerBin(root,
|
|
823
|
-
|
|
824
|
-
".
|
|
825
|
-
|
|
826
|
-
".
|
|
827
|
-
] : [".bin/wrangler"];
|
|
828
|
-
for (const candidate of candidates) {
|
|
829
|
-
const found = findInNodeModules(root, candidate);
|
|
830
|
-
if (found) return found;
|
|
863
|
+
function resolveWranglerBin(root, resolvePackageJson = (projectRoot) => {
|
|
864
|
+
try {
|
|
865
|
+
return createRequire(path.join(projectRoot, "package.json")).resolve("wrangler/package.json");
|
|
866
|
+
} catch {
|
|
867
|
+
return findInNodeModules(projectRoot, "wrangler/package.json");
|
|
831
868
|
}
|
|
832
|
-
|
|
869
|
+
}) {
|
|
870
|
+
const packageJsonPath = resolvePackageJson(root);
|
|
871
|
+
if (packageJsonPath) {
|
|
872
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf-8"));
|
|
873
|
+
const bin = typeof packageJson.bin === "string" ? packageJson.bin : packageJson.bin?.wrangler;
|
|
874
|
+
if (bin) return path.resolve(path.dirname(packageJsonPath), bin);
|
|
875
|
+
}
|
|
876
|
+
return path.join(root, "node_modules", "wrangler", "bin", "wrangler.js");
|
|
833
877
|
}
|
|
834
|
-
function
|
|
878
|
+
function buildNodeCliInvocation(scriptPath, args, nodeExecutable = process.execPath) {
|
|
879
|
+
return {
|
|
880
|
+
file: nodeExecutable,
|
|
881
|
+
args: [scriptPath, ...args]
|
|
882
|
+
};
|
|
883
|
+
}
|
|
884
|
+
function buildWranglerInvocation(root, options, nodeExecutable = process.execPath) {
|
|
835
885
|
const wranglerBin = resolveWranglerBin(root);
|
|
886
|
+
const { args, env } = buildWranglerDeployArgs(options);
|
|
887
|
+
return {
|
|
888
|
+
...buildNodeCliInvocation(wranglerBin, args, nodeExecutable),
|
|
889
|
+
env
|
|
890
|
+
};
|
|
891
|
+
}
|
|
892
|
+
function runWranglerDeploy(root, options, execute = execFileSync) {
|
|
836
893
|
const execOpts = {
|
|
837
894
|
cwd: root,
|
|
838
895
|
stdio: "pipe",
|
|
839
896
|
encoding: "utf-8",
|
|
840
|
-
shell:
|
|
897
|
+
shell: false
|
|
841
898
|
};
|
|
842
|
-
const { args, env } =
|
|
899
|
+
const { file, args, env } = buildWranglerInvocation(root, options);
|
|
843
900
|
if (env) console.log(`\n Deploying to env: ${env}...`);
|
|
844
901
|
else console.log("\n Deploying to production...");
|
|
845
|
-
const output =
|
|
902
|
+
const output = execute(file, args, execOpts);
|
|
846
903
|
const urlMatch = output.match(/https:\/\/[^\s]+\.workers\.dev[^\s]*/);
|
|
847
904
|
const deployedUrl = urlMatch ? urlMatch[0] : null;
|
|
848
905
|
if (output.trim()) for (const line of output.trim().split("\n")) console.log(` ${line}`);
|
|
849
906
|
return deployedUrl ?? "(URL not detected in wrangler output)";
|
|
850
907
|
}
|
|
908
|
+
const VINEXT_PREGEN_START = "/* __VINEXT_PREGENERATED_CONCRETE_PATHS_START__ */";
|
|
909
|
+
const VINEXT_PREGEN_END = "/* __VINEXT_PREGENERATED_CONCRETE_PATHS_END__ */";
|
|
910
|
+
const VINEXT_PREGEN_RE = new RegExp(`${escapeRegExp(VINEXT_PREGEN_START)}[\\s\\S]*?${escapeRegExp(VINEXT_PREGEN_END)}\\n?`, "g");
|
|
911
|
+
/**
|
|
912
|
+
* Read the prerender manifest and inject pregenerated concrete paths into the
|
|
913
|
+
* App Router Worker bundle so the PPR fallback-shell guard is populated at
|
|
914
|
+
* module init time without calling `seedMemoryCacheFromPrerender`.
|
|
915
|
+
*
|
|
916
|
+
* The paths are injected as `globalThis.__VINEXT_PREGENERATED_CONCRETE_PATHS`
|
|
917
|
+
* wrapped in replaceable marker comments, and consumed by
|
|
918
|
+
* `initPregeneratedPathsFromGlobals` in the generated RSC entry.
|
|
919
|
+
*
|
|
920
|
+
* Idempotent: repeated calls strip the previous injection before writing the
|
|
921
|
+
* new one. If the manifest is missing, corrupt, or empty, any prior injection
|
|
922
|
+
* is stripped and nothing new is written — failing closed to empty.
|
|
923
|
+
*/
|
|
924
|
+
function injectPregeneratedConcretePaths(root) {
|
|
925
|
+
const workerEntry = path.resolve(root, "dist", "server", "index.js");
|
|
926
|
+
if (!fs.existsSync(workerEntry)) return;
|
|
927
|
+
let code = fs.readFileSync(workerEntry, "utf-8");
|
|
928
|
+
code = code.replace(VINEXT_PREGEN_RE, "");
|
|
929
|
+
const table = buildPregeneratedConcretePathTable(readPrerenderManifest(path.join(root, "dist", "server", "vinext-prerender.json")) ?? {});
|
|
930
|
+
if (table.length > 0) code = `${VINEXT_PREGEN_START}\nglobalThis.__VINEXT_PREGENERATED_CONCRETE_PATHS = ${JSON.stringify(table)};\n${VINEXT_PREGEN_END}\n` + code;
|
|
931
|
+
fs.writeFileSync(workerEntry, code);
|
|
932
|
+
}
|
|
851
933
|
async function deploy(options) {
|
|
934
|
+
const deployEnv = validateWranglerEnvName(options.env || (options.preview ? "preview" : "production"));
|
|
852
935
|
const root = path.resolve(options.root);
|
|
853
936
|
loadDotenv({
|
|
854
937
|
root,
|
|
@@ -905,7 +988,7 @@ async function deploy(options) {
|
|
|
905
988
|
console.log("\n Dry run complete. Files generated but no build or deploy performed.\n");
|
|
906
989
|
return;
|
|
907
990
|
}
|
|
908
|
-
if (!options.skipBuild) await runBuild(info,
|
|
991
|
+
if (!options.skipBuild) await runBuild(info, deployEnv === "production" && !options.env ? void 0 : deployEnv);
|
|
909
992
|
else console.log("\n Skipping build (--skip-build)");
|
|
910
993
|
{
|
|
911
994
|
const nextConfig = await resolveNextConfig(await loadNextConfig(info.root), info.root);
|
|
@@ -922,6 +1005,7 @@ async function deploy(options) {
|
|
|
922
1005
|
concurrency: options.prerenderConcurrency
|
|
923
1006
|
});
|
|
924
1007
|
}
|
|
1008
|
+
injectPregeneratedConcretePaths(root);
|
|
925
1009
|
}
|
|
926
1010
|
if (options.experimentalTPR) {
|
|
927
1011
|
console.log();
|
|
@@ -933,13 +1017,10 @@ async function deploy(options) {
|
|
|
933
1017
|
});
|
|
934
1018
|
if (tprResult.skipped) console.log(` TPR: Skipped (${tprResult.skipped})`);
|
|
935
1019
|
}
|
|
936
|
-
const url = runWranglerDeploy(root, {
|
|
937
|
-
preview: options.preview ?? false,
|
|
938
|
-
env: options.env
|
|
939
|
-
});
|
|
1020
|
+
const url = runWranglerDeploy(root, { env: deployEnv === "production" && !options.env ? void 0 : deployEnv });
|
|
940
1021
|
console.log("\n ─────────────────────────────────────────");
|
|
941
1022
|
console.log(` Deployed to: ${url}`);
|
|
942
1023
|
console.log(" ─────────────────────────────────────────\n");
|
|
943
1024
|
}
|
|
944
1025
|
//#endregion
|
|
945
|
-
export { buildWranglerDeployArgs, deploy, detectProject, ensureESModule, formatMissingCacheAdapterError, formatMissingCloudflarePluginError, generateAppRouterViteConfig, generateAppRouterWorkerEntry, generatePagesRouterViteConfig, generatePagesRouterWorkerEntry, generateWranglerConfig, getFilesToGenerate, getMissingDeps, hasWranglerConfig, isPackageResolvable, parseDeployArgs, renameCJSConfigs, resolveWranglerBin, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, withCloudflareEnv, workerEntryHasCacheHandler };
|
|
1026
|
+
export { buildNodeCliInvocation, buildWranglerDeployArgs, buildWranglerInvocation, deploy, detectProject, ensureESModule, formatMissingCacheAdapterError, formatMissingCloudflarePluginError, generateAppRouterViteConfig, generateAppRouterWorkerEntry, generatePagesRouterViteConfig, generatePagesRouterWorkerEntry, generateWranglerConfig, getFilesToGenerate, getMissingDeps, hasWranglerConfig, injectPregeneratedConcretePaths, isPackageResolvable, parseDeployArgs, renameCJSConfigs, resolveWranglerBin, runWranglerDeploy, validateWranglerEnvName, viteConfigHasCacheAdapter, viteConfigHasCloudflarePlugin, withCloudflareEnv, workerEntryHasCacheHandler };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { NextRewrite } from "../config/next-config.js";
|
|
1
2
|
import { AppRoute, RouteManifest } from "../routing/app-route-graph.js";
|
|
2
|
-
import { VinextLinkPrefetchRoute } from "../client/vinext-next-data.js";
|
|
3
|
+
import { VinextLinkPrefetchRoute, VinextPagesLinkPrefetchRoute } from "../client/vinext-next-data.js";
|
|
3
4
|
|
|
4
5
|
//#region src/entries/app-browser-entry.d.ts
|
|
5
6
|
/**
|
|
@@ -9,7 +10,11 @@ import { VinextLinkPrefetchRoute } from "../client/vinext-next-data.js";
|
|
|
9
10
|
* embedded RSC payload and handles client-side navigation by re-fetching
|
|
10
11
|
* RSC streams.
|
|
11
12
|
*/
|
|
12
|
-
declare function generateBrowserEntry(routes?: readonly AppRoute[], routeManifest?: RouteManifest | null
|
|
13
|
+
declare function generateBrowserEntry(routes?: readonly AppRoute[], routeManifest?: RouteManifest | null, pagesPrefetchRoutes?: readonly VinextPagesLinkPrefetchRoute[], rewrites?: {
|
|
14
|
+
afterFiles: NextRewrite[];
|
|
15
|
+
beforeFiles: NextRewrite[];
|
|
16
|
+
fallback: NextRewrite[];
|
|
17
|
+
}): string;
|
|
13
18
|
/**
|
|
14
19
|
* Filter for routes that should appear in the `__VINEXT_LINK_PREFETCH_ROUTES__`
|
|
15
20
|
* manifest. Exported so the Pages Router client entry can reuse it when
|
|
@@ -17,7 +22,8 @@ declare function generateBrowserEntry(routes?: readonly AppRoute[], routeManifes
|
|
|
17
22
|
* `pages-client-entry.ts`.
|
|
18
23
|
*/
|
|
19
24
|
declare function isLinkPrefetchRoute(route: AppRoute): boolean;
|
|
25
|
+
declare function toDocumentOnlyAppRoute(route: AppRoute): VinextLinkPrefetchRoute;
|
|
20
26
|
/** Project an `AppRoute` down to the public `VinextLinkPrefetchRoute` shape. */
|
|
21
27
|
declare function toLinkPrefetchRoute(route: AppRoute): VinextLinkPrefetchRoute;
|
|
22
28
|
//#endregion
|
|
23
|
-
export { generateBrowserEntry, isLinkPrefetchRoute, toLinkPrefetchRoute };
|
|
29
|
+
export { generateBrowserEntry, isLinkPrefetchRoute, toDocumentOnlyAppRoute, toLinkPrefetchRoute };
|
|
@@ -7,13 +7,23 @@ import { resolveClientRuntimeModule, resolveRuntimeEntryModule } from "./runtime
|
|
|
7
7
|
* embedded RSC payload and handles client-side navigation by re-fetching
|
|
8
8
|
* RSC streams.
|
|
9
9
|
*/
|
|
10
|
-
function generateBrowserEntry(routes = [], routeManifest = null
|
|
10
|
+
function generateBrowserEntry(routes = [], routeManifest = null, pagesPrefetchRoutes = [], rewrites = {
|
|
11
|
+
afterFiles: [],
|
|
12
|
+
beforeFiles: [],
|
|
13
|
+
fallback: []
|
|
14
|
+
}) {
|
|
11
15
|
const entryPath = resolveRuntimeEntryModule("app-browser-entry");
|
|
12
16
|
const navigationRuntimePath = resolveClientRuntimeModule("navigation-runtime");
|
|
13
|
-
const prefetchRoutes = routes.
|
|
17
|
+
const prefetchRoutes = routes.map((route) => isLinkPrefetchRoute(route) ? toLinkPrefetchRoute(route) : toDocumentOnlyAppRoute(route));
|
|
14
18
|
return `import { registerNavigationRuntimeBootstrap } from ${JSON.stringify(navigationRuntimePath)};
|
|
15
19
|
|
|
16
20
|
window.__VINEXT_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(prefetchRoutes)};
|
|
21
|
+
// Pages route manifest for hybrid ownership decisions. In a hybrid
|
|
22
|
+
// app+pages build the user can land on an App page, so the App browser
|
|
23
|
+
// entry must also expose the Pages manifest (the Pages client entry does
|
|
24
|
+
// the same — whichever entry runs first emits both globals).
|
|
25
|
+
window.__VINEXT_PAGES_LINK_PREFETCH_ROUTES__ = ${JSON.stringify(pagesPrefetchRoutes)};
|
|
26
|
+
window.__VINEXT_CLIENT_REWRITES__ = ${JSON.stringify(rewrites)};
|
|
17
27
|
registerNavigationRuntimeBootstrap({
|
|
18
28
|
routeManifest: ${buildRouteManifestExpression(routeManifest)}
|
|
19
29
|
});
|
|
@@ -29,6 +39,14 @@ function isLinkPrefetchRoute(route) {
|
|
|
29
39
|
if (route.pagePath !== null) return true;
|
|
30
40
|
return route.routePath === null && route.layouts.length > 0;
|
|
31
41
|
}
|
|
42
|
+
function toDocumentOnlyAppRoute(route) {
|
|
43
|
+
return {
|
|
44
|
+
canPrefetchLoadingShell: false,
|
|
45
|
+
documentOnly: true,
|
|
46
|
+
patternParts: [...route.patternParts],
|
|
47
|
+
isDynamic: route.isDynamic
|
|
48
|
+
};
|
|
49
|
+
}
|
|
32
50
|
/** Project an `AppRoute` down to the public `VinextLinkPrefetchRoute` shape. */
|
|
33
51
|
function toLinkPrefetchRoute(route) {
|
|
34
52
|
return {
|
|
@@ -62,4 +80,4 @@ function buildMapExpression(map) {
|
|
|
62
80
|
return `new Map(${JSON.stringify(Array.from(map.entries()))})`;
|
|
63
81
|
}
|
|
64
82
|
//#endregion
|
|
65
|
-
export { generateBrowserEntry, isLinkPrefetchRoute, toLinkPrefetchRoute };
|
|
83
|
+
export { generateBrowserEntry, isLinkPrefetchRoute, toDocumentOnlyAppRoute, toLinkPrefetchRoute };
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "../config/next-config.js";
|
|
2
2
|
import { AppRoute } from "../routing/app-route-graph.js";
|
|
3
3
|
import { MetadataFileRoute } from "../server/metadata-routes.js";
|
|
4
|
+
import { ImageConfig } from "../server/image-optimization.js";
|
|
4
5
|
|
|
5
6
|
//#region src/entries/app-rsc-entry.d.ts
|
|
6
7
|
/**
|
|
@@ -45,6 +46,7 @@ type AppRouterConfig = {
|
|
|
45
46
|
inlineCss?: boolean; /** Enables Next.js Cache Components semantics for App Router document HTML. */
|
|
46
47
|
cacheComponents?: boolean; /** Internationalization routing config for middleware matcher locale handling. */
|
|
47
48
|
i18n?: NextI18nConfig | null;
|
|
49
|
+
imageConfig?: ImageConfig;
|
|
48
50
|
/**
|
|
49
51
|
* Absolute path to `app/global-not-found.{tsx,ts,js,jsx}` when present.
|
|
50
52
|
* When provided, route-miss 404s render this module standalone (it owns its
|