feat: init
This commit is contained in:
3
node_modules/nitropack/dist/presets/vercel/preset.d.ts
generated
vendored
Normal file
3
node_modules/nitropack/dist/presets/vercel/preset.d.ts
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
export type { VercelOptions as PresetOptions } from "./types";
|
||||
declare const _default: readonly [any, any, any];
|
||||
export default _default;
|
||||
118
node_modules/nitropack/dist/presets/vercel/preset.mjs
generated
vendored
Normal file
118
node_modules/nitropack/dist/presets/vercel/preset.mjs
generated
vendored
Normal file
@@ -0,0 +1,118 @@
|
||||
import { defineNitroPreset } from "nitropack/kit";
|
||||
import {
|
||||
deprecateSWR,
|
||||
generateEdgeFunctionFiles,
|
||||
generateFunctionFiles,
|
||||
generateStaticFiles
|
||||
} from "./utils.mjs";
|
||||
import { builtnNodeModules } from "../_unenv/node-compat/vercel.mjs";
|
||||
const vercel = defineNitroPreset(
|
||||
{
|
||||
extends: "node",
|
||||
entry: "./runtime/vercel",
|
||||
vercel: {
|
||||
skewProtection: !!process.env.VERCEL_SKEW_PROTECTION_ENABLED
|
||||
},
|
||||
output: {
|
||||
dir: "{{ rootDir }}/.vercel/output",
|
||||
serverDir: "{{ output.dir }}/functions/__fallback.func",
|
||||
publicDir: "{{ output.dir }}/static/{{ baseURL }}"
|
||||
},
|
||||
commands: {
|
||||
preview: "",
|
||||
deploy: "npx vercel deploy --prebuilt"
|
||||
},
|
||||
hooks: {
|
||||
"rollup:before": (nitro) => {
|
||||
deprecateSWR(nitro);
|
||||
},
|
||||
async compiled(nitro) {
|
||||
await generateFunctionFiles(nitro);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "vercel",
|
||||
stdName: "vercel",
|
||||
url: import.meta.url
|
||||
}
|
||||
);
|
||||
const vercelEdge = defineNitroPreset(
|
||||
{
|
||||
extends: "base-worker",
|
||||
entry: "./runtime/vercel-edge",
|
||||
exportConditions: ["edge-light"],
|
||||
output: {
|
||||
dir: "{{ rootDir }}/.vercel/output",
|
||||
serverDir: "{{ output.dir }}/functions/__fallback.func",
|
||||
publicDir: "{{ output.dir }}/static/{{ baseURL }}"
|
||||
},
|
||||
commands: {
|
||||
preview: "",
|
||||
deploy: "npx vercel deploy --prebuilt"
|
||||
},
|
||||
unenv: {
|
||||
external: builtnNodeModules.flatMap((m) => `node:${m}`),
|
||||
alias: {
|
||||
...Object.fromEntries(
|
||||
builtnNodeModules.flatMap((m) => [
|
||||
[m, `node:${m}`],
|
||||
[`node:${m}`, `node:${m}`]
|
||||
])
|
||||
)
|
||||
}
|
||||
},
|
||||
rollupConfig: {
|
||||
output: {
|
||||
format: "module"
|
||||
}
|
||||
},
|
||||
wasm: {
|
||||
lazy: true,
|
||||
esmImport: false
|
||||
},
|
||||
hooks: {
|
||||
"rollup:before": (nitro) => {
|
||||
deprecateSWR(nitro);
|
||||
},
|
||||
async compiled(nitro) {
|
||||
await generateEdgeFunctionFiles(nitro);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "vercel-edge",
|
||||
url: import.meta.url
|
||||
}
|
||||
);
|
||||
const vercelStatic = defineNitroPreset(
|
||||
{
|
||||
extends: "static",
|
||||
vercel: {
|
||||
skewProtection: !!process.env.VERCEL_SKEW_PROTECTION_ENABLED
|
||||
},
|
||||
output: {
|
||||
dir: "{{ rootDir }}/.vercel/output",
|
||||
publicDir: "{{ output.dir }}/static/{{ baseURL }}"
|
||||
},
|
||||
commands: {
|
||||
preview: "npx serve {{ output.publicDir }}",
|
||||
deploy: "npx vercel deploy --prebuilt"
|
||||
},
|
||||
hooks: {
|
||||
"rollup:before": (nitro) => {
|
||||
deprecateSWR(nitro);
|
||||
},
|
||||
async compiled(nitro) {
|
||||
await generateStaticFiles(nitro);
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
name: "vercel-static",
|
||||
stdName: "vercel",
|
||||
static: true,
|
||||
url: import.meta.url
|
||||
}
|
||||
);
|
||||
export default [vercel, vercelEdge, vercelStatic];
|
||||
1
node_modules/nitropack/dist/presets/vercel/runtime/consts.d.ts
generated
vendored
Normal file
1
node_modules/nitropack/dist/presets/vercel/runtime/consts.d.ts
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export declare const ISR_URL_PARAM = "__isr_route";
|
||||
1
node_modules/nitropack/dist/presets/vercel/runtime/consts.mjs
generated
vendored
Normal file
1
node_modules/nitropack/dist/presets/vercel/runtime/consts.mjs
generated
vendored
Normal file
@@ -0,0 +1 @@
|
||||
export const ISR_URL_PARAM = "__isr_route";
|
||||
2
node_modules/nitropack/dist/presets/vercel/runtime/vercel-edge.d.ts
generated
vendored
Normal file
2
node_modules/nitropack/dist/presets/vercel/runtime/vercel-edge.d.ts
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
import "#nitro-internal-pollyfills";
|
||||
export default function handleEvent(request: Request, event: any): Promise<any>;
|
||||
24
node_modules/nitropack/dist/presets/vercel/runtime/vercel-edge.mjs
generated
vendored
Normal file
24
node_modules/nitropack/dist/presets/vercel/runtime/vercel-edge.mjs
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
import "#nitro-internal-pollyfills";
|
||||
import { useNitroApp } from "nitropack/runtime";
|
||||
const nitroApp = useNitroApp();
|
||||
export default async function handleEvent(request, event) {
|
||||
const url = new URL(request.url);
|
||||
let body;
|
||||
if (request.body) {
|
||||
body = await request.arrayBuffer();
|
||||
}
|
||||
return nitroApp.localFetch(url.pathname + url.search, {
|
||||
host: url.hostname,
|
||||
protocol: url.protocol,
|
||||
headers: request.headers,
|
||||
method: request.method,
|
||||
body,
|
||||
context: {
|
||||
_platform: {
|
||||
vercel: {
|
||||
event
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
4
node_modules/nitropack/dist/presets/vercel/runtime/vercel.d.ts
generated
vendored
Normal file
4
node_modules/nitropack/dist/presets/vercel/runtime/vercel.d.ts
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
import "#nitro-internal-pollyfills";
|
||||
import { type NodeListener } from "h3";
|
||||
declare const listener: NodeListener;
|
||||
export default listener;
|
||||
36
node_modules/nitropack/dist/presets/vercel/runtime/vercel.mjs
generated
vendored
Normal file
36
node_modules/nitropack/dist/presets/vercel/runtime/vercel.mjs
generated
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
import "#nitro-internal-pollyfills";
|
||||
import { useNitroApp } from "nitropack/runtime";
|
||||
import { getRouteRulesForPath } from "nitropack/runtime/internal/index";
|
||||
import { toNodeListener } from "h3";
|
||||
import { parseQuery, withQuery } from "ufo";
|
||||
import { ISR_URL_PARAM } from "./consts.mjs";
|
||||
const nitroApp = useNitroApp();
|
||||
const handler = toNodeListener(nitroApp.h3App);
|
||||
const listener = function(req, res) {
|
||||
const isrRoute = req.headers["x-now-route-matches"];
|
||||
if (isrRoute) {
|
||||
const { [ISR_URL_PARAM]: url } = parseQuery(isrRoute);
|
||||
if (url && typeof url === "string") {
|
||||
const routeRules = getRouteRulesForPath(url);
|
||||
if (routeRules.isr) {
|
||||
req.url = url;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
const queryIndex = req.url.indexOf("?");
|
||||
const urlQueryIndex = queryIndex === -1 ? -1 : req.url.indexOf(`${ISR_URL_PARAM}=`, queryIndex);
|
||||
if (urlQueryIndex !== -1) {
|
||||
const { [ISR_URL_PARAM]: url, ...params } = parseQuery(
|
||||
req.url.slice(queryIndex)
|
||||
);
|
||||
if (url && typeof url === "string") {
|
||||
const routeRules = getRouteRulesForPath(url);
|
||||
if (routeRules.isr) {
|
||||
req.url = withQuery(url, params);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return handler(req, res);
|
||||
};
|
||||
export default listener;
|
||||
136
node_modules/nitropack/dist/presets/vercel/types.d.ts
generated
vendored
Normal file
136
node_modules/nitropack/dist/presets/vercel/types.d.ts
generated
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
/**
|
||||
* Vercel Build Output Configuration
|
||||
* @see https://vercel.com/docs/build-output-api/v3
|
||||
*/
|
||||
export interface VercelBuildConfigV3 {
|
||||
version: 3;
|
||||
routes?: ({
|
||||
src: string;
|
||||
headers: {
|
||||
"cache-control": string;
|
||||
};
|
||||
continue: boolean;
|
||||
} | {
|
||||
handle: string;
|
||||
} | {
|
||||
src: string;
|
||||
dest: string;
|
||||
})[];
|
||||
images?: {
|
||||
sizes: number[];
|
||||
domains: string[];
|
||||
remotePatterns?: {
|
||||
protocol?: "http" | "https";
|
||||
hostname: string;
|
||||
port?: string;
|
||||
pathname?: string;
|
||||
}[];
|
||||
minimumCacheTTL?: number;
|
||||
formats?: ("image/avif" | "image/webp")[];
|
||||
dangerouslyAllowSVG?: boolean;
|
||||
contentSecurityPolicy?: string;
|
||||
};
|
||||
wildcard?: Array<{
|
||||
domain: string;
|
||||
value: string;
|
||||
}>;
|
||||
overrides?: Record<string, {
|
||||
path?: string;
|
||||
contentType?: string;
|
||||
}>;
|
||||
cache?: string[];
|
||||
bypassToken?: string;
|
||||
crons?: {
|
||||
path: string;
|
||||
schedule: string;
|
||||
}[];
|
||||
}
|
||||
/**
|
||||
* https://vercel.com/docs/build-output-api/primitives#serverless-function-configuration
|
||||
* https://vercel.com/docs/build-output-api/primitives#node.js-config
|
||||
*/
|
||||
export interface VercelServerlessFunctionConfig {
|
||||
/**
|
||||
* Amount of memory (RAM in MB) that will be allocated to the Serverless Function.
|
||||
*/
|
||||
memory?: number;
|
||||
/**
|
||||
* Specifies the instruction set "architecture" the Vercel Function supports.
|
||||
*
|
||||
* Either `x86_64` or `arm64`. The default value is `x86_64`
|
||||
*/
|
||||
architecture?: "x86_64" | "arm64";
|
||||
/**
|
||||
* Maximum execution duration (in seconds) that will be allowed for the Serverless Function.
|
||||
*/
|
||||
maxDuration?: number;
|
||||
/**
|
||||
* Map of additional environment variables that will be available to the Vercel Function,
|
||||
* in addition to the env vars specified in the Project Settings.
|
||||
*/
|
||||
environment?: Record<string, string>;
|
||||
/**
|
||||
* List of Vercel Regions where the Vercel Function will be deployed to.
|
||||
*/
|
||||
regions?: string[];
|
||||
/**
|
||||
* True if a custom runtime has support for Lambda runtime wrappers.
|
||||
*/
|
||||
supportsWrapper?: boolean;
|
||||
/**
|
||||
* When true, the Serverless Function will stream the response to the client.
|
||||
*/
|
||||
supportsResponseStreaming?: boolean;
|
||||
/**
|
||||
* Enables source map generation.
|
||||
*/
|
||||
shouldAddSourcemapSupport?: boolean;
|
||||
/**
|
||||
* The runtime to use. Defaults to the auto-detected Node.js version.
|
||||
*/
|
||||
runtime?: "nodejs20.x" | "nodejs22.x" | "bun1.x" | (string & {});
|
||||
[key: string]: unknown;
|
||||
}
|
||||
export interface VercelOptions {
|
||||
config: VercelBuildConfigV3;
|
||||
/**
|
||||
* If you have enabled skew protection in the Vercel dashboard, it will
|
||||
* be enabled by default.
|
||||
*
|
||||
* You can disable the Nitro integration by setting this option to `false`.
|
||||
*/
|
||||
skewProtection?: boolean;
|
||||
/**
|
||||
* If you are using `vercel-edge`, you can specify the region(s) for your edge function.
|
||||
* @see https://vercel.com/docs/concepts/functions/edge-functions#edge-function-regions
|
||||
*/
|
||||
regions?: string[];
|
||||
functions?: VercelServerlessFunctionConfig;
|
||||
}
|
||||
/**
|
||||
* https://vercel.com/docs/build-output-api/v3/primitives#prerender-configuration-file
|
||||
*/
|
||||
export type PrerenderFunctionConfig = {
|
||||
/**
|
||||
* Expiration time (in seconds) before the cached asset will be re-generated by invoking the Serverless Function. Setting the value to `false` means it will never expire.
|
||||
*/
|
||||
expiration: number | false;
|
||||
/**
|
||||
* Option group number of the asset. Prerender assets with the same group number will all be re-validated at the same time.
|
||||
*/
|
||||
group?: number;
|
||||
/** Random token assigned to the `__prerender_bypass` cookie when Draft Mode is enabled, in order to safely bypass the Edge Network cache */
|
||||
bypassToken?: string;
|
||||
/**
|
||||
* Name of the optional fallback file relative to the configuration file.
|
||||
*/
|
||||
fallback?: string;
|
||||
/**
|
||||
* List of query string parameter names that will be cached independently. If an empty array, query values are not considered for caching. If undefined each unique query value is cached independently
|
||||
*/
|
||||
allowQuery?: string[];
|
||||
/**
|
||||
* When `true`, the query string will be present on the `request` argument passed to the invoked function. The `allowQuery` filter still applies.
|
||||
*/
|
||||
passQuery?: boolean;
|
||||
};
|
||||
0
node_modules/nitropack/dist/presets/vercel/types.mjs
generated
vendored
Normal file
0
node_modules/nitropack/dist/presets/vercel/types.mjs
generated
vendored
Normal file
9
node_modules/nitropack/dist/presets/vercel/utils.d.ts
generated
vendored
Normal file
9
node_modules/nitropack/dist/presets/vercel/utils.d.ts
generated
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import type { Nitro } from "nitropack/types";
|
||||
export declare function generateFunctionFiles(nitro: Nitro): Promise<void>;
|
||||
export declare function generateEdgeFunctionFiles(nitro: Nitro): Promise<void>;
|
||||
export declare function generateStaticFiles(nitro: Nitro): Promise<void>;
|
||||
export declare function deprecateSWR(nitro: Nitro): void;
|
||||
export interface VercelConfig {
|
||||
bunVersion?: string;
|
||||
}
|
||||
export declare function readVercelConfig(rootDir: string): Promise<VercelConfig>;
|
||||
349
node_modules/nitropack/dist/presets/vercel/utils.mjs
generated
vendored
Normal file
349
node_modules/nitropack/dist/presets/vercel/utils.mjs
generated
vendored
Normal file
@@ -0,0 +1,349 @@
|
||||
import fsp from "node:fs/promises";
|
||||
import { defu } from "defu";
|
||||
import { writeFile } from "nitropack/kit";
|
||||
import { dirname, relative, resolve } from "pathe";
|
||||
import { joinURL, withLeadingSlash, withoutLeadingSlash } from "ufo";
|
||||
import { isTest } from "std-env";
|
||||
import { createRouter as createRadixRouter, toRouteMatcher } from "radix3";
|
||||
import { ISR_URL_PARAM } from "./runtime/consts.mjs";
|
||||
const SUPPORTED_NODE_VERSIONS = [18, 20, 22];
|
||||
const FALLBACK_ROUTE = "/__fallback";
|
||||
const ISR_SUFFIX = "-isr";
|
||||
const SAFE_FS_CHAR_RE = /[^a-zA-Z0-9_.[\]/]/g;
|
||||
function getSystemNodeVersion() {
|
||||
const systemNodeVersion = Number.parseInt(
|
||||
process.versions.node.split(".")[0]
|
||||
);
|
||||
return Number.isNaN(systemNodeVersion) ? 22 : systemNodeVersion;
|
||||
}
|
||||
export async function generateFunctionFiles(nitro) {
|
||||
const o11Routes = getObservabilityRoutes(nitro);
|
||||
const buildConfigPath = resolve(nitro.options.output.dir, "config.json");
|
||||
const buildConfig = generateBuildConfig(nitro, o11Routes);
|
||||
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2));
|
||||
let runtime = nitro.options.vercel?.functions?.runtime;
|
||||
if (!runtime) {
|
||||
const vercelConfig = await readVercelConfig(nitro.options.rootDir);
|
||||
if (vercelConfig.bunVersion || "Bun" in globalThis) {
|
||||
runtime = `bun${vercelConfig.bunVersion || "1.x"}`;
|
||||
} else {
|
||||
const systemNodeVersion = getSystemNodeVersion();
|
||||
const usedNodeVersion = SUPPORTED_NODE_VERSIONS.find(
|
||||
(version) => version >= systemNodeVersion
|
||||
) ?? SUPPORTED_NODE_VERSIONS.at(-1);
|
||||
runtime = `nodejs${usedNodeVersion}.x`;
|
||||
}
|
||||
}
|
||||
const functionConfigPath = resolve(
|
||||
nitro.options.output.serverDir,
|
||||
".vc-config.json"
|
||||
);
|
||||
const functionConfig = {
|
||||
runtime,
|
||||
...nitro.options.vercel?.functions,
|
||||
handler: "index.mjs",
|
||||
launcherType: "Nodejs",
|
||||
shouldAddHelpers: false,
|
||||
supportsResponseStreaming: true
|
||||
};
|
||||
await writeFile(functionConfigPath, JSON.stringify(functionConfig, null, 2));
|
||||
for (const [key, value] of Object.entries(nitro.options.routeRules)) {
|
||||
if (!value.isr) {
|
||||
continue;
|
||||
}
|
||||
const funcPrefix = resolve(
|
||||
nitro.options.output.serverDir,
|
||||
"..",
|
||||
normalizeRouteDest(key) + ISR_SUFFIX
|
||||
);
|
||||
await fsp.mkdir(dirname(funcPrefix), { recursive: true });
|
||||
await fsp.symlink(
|
||||
"./" + relative(dirname(funcPrefix), nitro.options.output.serverDir),
|
||||
funcPrefix + ".func",
|
||||
"junction"
|
||||
);
|
||||
await writePrerenderConfig(
|
||||
funcPrefix + ".prerender-config.json",
|
||||
value.isr,
|
||||
nitro.options.vercel?.config?.bypassToken
|
||||
);
|
||||
}
|
||||
if (o11Routes.length === 0) {
|
||||
return;
|
||||
}
|
||||
const _routeRulesMatcher = toRouteMatcher(
|
||||
createRadixRouter({ routes: nitro.options.routeRules })
|
||||
);
|
||||
const _getRouteRules = (path) => defu({}, ..._routeRulesMatcher.matchAll(path).reverse());
|
||||
for (const route of o11Routes) {
|
||||
const routeRules = _getRouteRules(route.src);
|
||||
if (routeRules.isr) {
|
||||
continue;
|
||||
}
|
||||
const funcPrefix = resolve(
|
||||
nitro.options.output.serverDir,
|
||||
"..",
|
||||
route.dest
|
||||
);
|
||||
await fsp.mkdir(dirname(funcPrefix), { recursive: true });
|
||||
await fsp.symlink(
|
||||
"./" + relative(dirname(funcPrefix), nitro.options.output.serverDir),
|
||||
funcPrefix + ".func",
|
||||
"junction"
|
||||
);
|
||||
}
|
||||
}
|
||||
export async function generateEdgeFunctionFiles(nitro) {
|
||||
const buildConfigPath = resolve(nitro.options.output.dir, "config.json");
|
||||
const buildConfig = generateBuildConfig(nitro);
|
||||
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2));
|
||||
const functionConfigPath = resolve(
|
||||
nitro.options.output.serverDir,
|
||||
".vc-config.json"
|
||||
);
|
||||
const functionConfig = {
|
||||
runtime: "edge",
|
||||
entrypoint: "index.mjs",
|
||||
regions: nitro.options.vercel?.regions
|
||||
};
|
||||
await writeFile(functionConfigPath, JSON.stringify(functionConfig, null, 2));
|
||||
}
|
||||
export async function generateStaticFiles(nitro) {
|
||||
const buildConfigPath = resolve(nitro.options.output.dir, "config.json");
|
||||
const buildConfig = generateBuildConfig(nitro);
|
||||
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2));
|
||||
}
|
||||
function generateBuildConfig(nitro, o11Routes) {
|
||||
const rules = Object.entries(nitro.options.routeRules).sort(
|
||||
(a, b) => b[0].split(/\/(?!\*)/).length - a[0].split(/\/(?!\*)/).length
|
||||
);
|
||||
const config = defu(nitro.options.vercel?.config, {
|
||||
version: 3,
|
||||
overrides: {
|
||||
// Nitro static prerendered route overrides
|
||||
...Object.fromEntries(
|
||||
(nitro._prerenderedRoutes?.filter((r) => r.fileName !== r.route) || []).map(({ route, fileName }) => [
|
||||
withoutLeadingSlash(fileName),
|
||||
{ path: route.replace(/^\//, "") }
|
||||
])
|
||||
)
|
||||
},
|
||||
routes: [
|
||||
// Redirect and header rules
|
||||
...rules.filter(([_, routeRules]) => routeRules.redirect || routeRules.headers).map(([path, routeRules]) => {
|
||||
let route = {
|
||||
src: path.replace("/**", "/(.*)")
|
||||
};
|
||||
if (routeRules.redirect) {
|
||||
route = defu(route, {
|
||||
status: routeRules.redirect.statusCode,
|
||||
headers: {
|
||||
Location: routeRules.redirect.to.replace("/**", "/$1")
|
||||
}
|
||||
});
|
||||
}
|
||||
if (routeRules.headers) {
|
||||
route = defu(route, { headers: routeRules.headers });
|
||||
}
|
||||
return route;
|
||||
}),
|
||||
// Skew protection
|
||||
...nitro.options.vercel?.skewProtection && process.env.VERCEL_DEPLOYMENT_ID ? [
|
||||
{
|
||||
src: "/.*",
|
||||
has: [
|
||||
{
|
||||
type: "header",
|
||||
key: "Sec-Fetch-Dest",
|
||||
value: "document"
|
||||
}
|
||||
],
|
||||
headers: {
|
||||
"Set-Cookie": `__vdpl=${process.env.VERCEL_DEPLOYMENT_ID}; Path=${nitro.options.baseURL}; SameSite=Strict; Secure; HttpOnly`
|
||||
},
|
||||
continue: true
|
||||
}
|
||||
] : [],
|
||||
// Public asset rules
|
||||
...nitro.options.publicAssets.filter((asset) => !asset.fallthrough).map((asset) => joinURL(nitro.options.baseURL, asset.baseURL || "/")).map((baseURL) => ({
|
||||
src: baseURL + "(.*)",
|
||||
headers: {
|
||||
"cache-control": "public,max-age=31536000,immutable"
|
||||
},
|
||||
continue: true
|
||||
})),
|
||||
{ handle: "filesystem" }
|
||||
]
|
||||
});
|
||||
if (nitro.options.static) {
|
||||
return config;
|
||||
}
|
||||
config.routes.push(
|
||||
...nitro.options.routeRules["/"]?.isr ? [
|
||||
{
|
||||
src: `(?<${ISR_URL_PARAM}>/)`,
|
||||
dest: `/index${ISR_SUFFIX}?${ISR_URL_PARAM}=$${ISR_URL_PARAM}`
|
||||
}
|
||||
] : [],
|
||||
...rules.filter(([key, value]) => value.isr !== void 0 && key !== "/").map(([key, value]) => {
|
||||
const src = `(?<${ISR_URL_PARAM}>${normalizeRouteSrc(key)})`;
|
||||
if (value.isr === false) {
|
||||
return {
|
||||
src,
|
||||
dest: FALLBACK_ROUTE
|
||||
};
|
||||
}
|
||||
return {
|
||||
src,
|
||||
dest: nitro.options.preset === "vercel-edge" ? FALLBACK_ROUTE + `?${ISR_URL_PARAM}=$${ISR_URL_PARAM}` : withLeadingSlash(
|
||||
normalizeRouteDest(key) + ISR_SUFFIX + `?${ISR_URL_PARAM}=$${ISR_URL_PARAM}`
|
||||
)
|
||||
};
|
||||
}),
|
||||
...(o11Routes || []).map((route) => ({
|
||||
src: joinURL(nitro.options.baseURL, route.src),
|
||||
dest: withLeadingSlash(route.dest)
|
||||
})),
|
||||
...nitro.options.routeRules["/**"]?.isr ? [] : [
|
||||
{
|
||||
src: "/(.*)",
|
||||
dest: FALLBACK_ROUTE
|
||||
}
|
||||
]
|
||||
);
|
||||
return config;
|
||||
}
|
||||
export function deprecateSWR(nitro) {
|
||||
if (nitro.options.future.nativeSWR) {
|
||||
return;
|
||||
}
|
||||
let hasLegacyOptions = false;
|
||||
for (const [key, value] of Object.entries(nitro.options.routeRules)) {
|
||||
if (_hasProp(value, "isr")) {
|
||||
continue;
|
||||
}
|
||||
if (value.cache === false) {
|
||||
value.isr = false;
|
||||
}
|
||||
if (_hasProp(value, "static")) {
|
||||
value.isr = !value.static;
|
||||
hasLegacyOptions = true;
|
||||
}
|
||||
if (value.cache && _hasProp(value.cache, "swr")) {
|
||||
value.isr = value.cache.swr;
|
||||
hasLegacyOptions = true;
|
||||
}
|
||||
}
|
||||
if (hasLegacyOptions && !isTest) {
|
||||
nitro.logger.warn(
|
||||
"Nitro now uses `isr` option to configure ISR behavior on Vercel. Backwards-compatible support for `static` and `swr` options within the Vercel Build Options API will be removed in the future versions. Set `future.nativeSWR: true` nitro config disable this warning."
|
||||
);
|
||||
}
|
||||
}
|
||||
export async function readVercelConfig(rootDir) {
|
||||
const vercelConfigPath = resolve(rootDir, "vercel.json");
|
||||
const vercelConfig = await fsp.readFile(vercelConfigPath).then((config) => JSON.parse(config.toString())).catch(() => ({}));
|
||||
return vercelConfig;
|
||||
}
|
||||
function _hasProp(obj, prop) {
|
||||
return obj && typeof obj === "object" && prop in obj;
|
||||
}
|
||||
function getObservabilityRoutes(nitro) {
|
||||
const compatDate = nitro.options.compatibilityDate.vercel || nitro.options.compatibilityDate.default;
|
||||
if (compatDate < "2025-07-15") {
|
||||
return [];
|
||||
}
|
||||
const routePatterns = [
|
||||
.../* @__PURE__ */ new Set([
|
||||
...nitro.options.ssrRoutes || [],
|
||||
...[...nitro.scannedHandlers, ...nitro.options.handlers].filter((h) => !h.middleware && h.route).map((h) => h.route)
|
||||
])
|
||||
];
|
||||
const staticRoutes = [];
|
||||
const dynamicRoutes = [];
|
||||
const catchAllRoutes = [];
|
||||
for (const route of routePatterns) {
|
||||
if (route.includes("**")) {
|
||||
catchAllRoutes.push(route);
|
||||
} else if (route.includes(":") || route.includes("*")) {
|
||||
dynamicRoutes.push(route);
|
||||
} else {
|
||||
staticRoutes.push(route);
|
||||
}
|
||||
}
|
||||
const prerendered = nitro._prerenderedRoutes || [];
|
||||
return [
|
||||
...normalizeRoutes(staticRoutes),
|
||||
...normalizeRoutes(dynamicRoutes),
|
||||
...normalizeRoutes(catchAllRoutes)
|
||||
].filter((route) => {
|
||||
return !prerendered.some((r) => route.src === r.route);
|
||||
});
|
||||
}
|
||||
function normalizeRoutes(routes) {
|
||||
return routes.sort(
|
||||
(a, b) => (
|
||||
// a.split("/").length - b.split("/").length ||
|
||||
b.localeCompare(a)
|
||||
)
|
||||
).map((route) => ({
|
||||
src: normalizeRouteSrc(route),
|
||||
dest: normalizeRouteDest(route)
|
||||
}));
|
||||
}
|
||||
function normalizeRouteSrc(route) {
|
||||
let idCtr = 0;
|
||||
return route.split("/").map((segment) => {
|
||||
if (segment.startsWith("**")) {
|
||||
return segment === "**" ? "(?:.*)" : `?(?<${namedGroup(segment.slice(3))}>.+)`;
|
||||
}
|
||||
if (segment === "*") {
|
||||
return `(?<_${idCtr++}>[^/]*)`;
|
||||
}
|
||||
if (segment.includes(":")) {
|
||||
return segment.replace(/:(\w+)/g, (_, id) => `(?<${namedGroup(id)}>[^/]+)`).replace(/\./g, String.raw`\.`);
|
||||
}
|
||||
return segment;
|
||||
}).join("/");
|
||||
}
|
||||
function namedGroup(input = "") {
|
||||
if (/\d/.test(input[0])) {
|
||||
input = `_${input}`;
|
||||
}
|
||||
return input.replace(/[^a-zA-Z0-9_]/g, "") || "_";
|
||||
}
|
||||
function normalizeRouteDest(route) {
|
||||
return route.split("/").slice(1).map((segment) => {
|
||||
if (segment.startsWith("**")) {
|
||||
return `[...${segment.replace(/[*:]/g, "")}]`;
|
||||
}
|
||||
if (segment === "*") {
|
||||
return "[-]";
|
||||
}
|
||||
if (segment.startsWith(":")) {
|
||||
return `[${segment.slice(1)}]`;
|
||||
}
|
||||
if (segment.includes(":")) {
|
||||
return `[${segment.replace(/:/g, "_")}]`;
|
||||
}
|
||||
return segment;
|
||||
}).map((segment) => segment.replace(SAFE_FS_CHAR_RE, "-")).join("/") || "index";
|
||||
}
|
||||
async function writePrerenderConfig(filename, isrConfig, bypassToken) {
|
||||
if (typeof isrConfig === "number") {
|
||||
isrConfig = { expiration: isrConfig };
|
||||
} else if (isrConfig === true) {
|
||||
isrConfig = { expiration: false };
|
||||
} else {
|
||||
isrConfig = { ...isrConfig };
|
||||
}
|
||||
const prerenderConfig = {
|
||||
expiration: isrConfig.expiration ?? false,
|
||||
bypassToken,
|
||||
...isrConfig
|
||||
};
|
||||
if (prerenderConfig.allowQuery && !prerenderConfig.allowQuery.includes(ISR_URL_PARAM)) {
|
||||
prerenderConfig.allowQuery.push(ISR_URL_PARAM);
|
||||
}
|
||||
await writeFile(filename, JSON.stringify(prerenderConfig, null, 2));
|
||||
}
|
||||
Reference in New Issue
Block a user