Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/presets/vercel/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,13 @@ export interface VercelServerlessFunctionConfig {
/**
* The runtime to use. Defaults to the auto-detected Node.js version.
*/
runtime?: "nodejs20.x" | "nodejs22.x" | "bun1.x" | (string & {});
runtime?:
| "nodejs20.x"
| "nodejs22.x"
| "nodejs24.x"
| "nodejs26.x"
| "bun1.x"
| (string & {});

[key: string]: unknown;
}
Expand Down
19 changes: 12 additions & 7 deletions src/presets/vercel/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import { ISR_URL_PARAM } from "./runtime/consts";
// https://vercel.com/docs/build-output-api/configuration

// https://vercel.com/docs/functions/runtimes/node-js/node-js-versions
const SUPPORTED_NODE_VERSIONS = [18, 20, 22, 24];
// https://github.com/vercel/vercel/blob/main/packages/build-utils/src/fs/node-version.ts
const SUPPORTED_NODE_VERSIONS = [22, 24, 26];

const FALLBACK_ROUTE = "/__fallback";

Expand All @@ -39,6 +40,15 @@ function getSystemNodeVersion() {
return Number.isNaN(systemNodeVersion) ? 22 : systemNodeVersion;
}

export function resolveNodeRuntime(
systemNodeVersion = getSystemNodeVersion()
): `nodejs${number}.x` {
const usedNodeVersion =
SUPPORTED_NODE_VERSIONS.find((version) => version >= systemNodeVersion) ??
SUPPORTED_NODE_VERSIONS.at(-1)!;
return `nodejs${usedNodeVersion}.x`;
}

export async function generateFunctionFiles(nitro: Nitro) {
const o11Routes = getObservabilityRoutes(nitro);

Expand All @@ -58,12 +68,7 @@ export async function generateFunctionFiles(nitro: Nitro) {
runtime = `bun${vercelConfig.bunVersion || "1.x"}`;
} else {
// 3. Auto-detect runtime based on system Node.js version
const systemNodeVersion = getSystemNodeVersion();
const usedNodeVersion =
SUPPORTED_NODE_VERSIONS.find(
(version) => version >= systemNodeVersion
) ?? SUPPORTED_NODE_VERSIONS.at(-1);
runtime = `nodejs${usedNodeVersion}.x`;
runtime = resolveNodeRuntime();
}
}

Expand Down
24 changes: 24 additions & 0 deletions test/presets/vercel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,30 @@ describe("nitro:preset:vercel:bun", async () => {
});
});

describe("nitro:preset:vercel:node-runtime", async () => {
const ctx = await setupTest("vercel", {
config: {
preset: "vercel",
vercel: {
functions: {
runtime: "nodejs26.x",
},
},
},
});
// Read now: the other vercel groups build into the same output dir
const config = await fsp
.readFile(
resolve(ctx.outDir, "functions/__fallback.func/.vc-config.json"),
"utf8"
)
.then((r) => JSON.parse(r));

it("should keep an explicit nodejs26.x runtime", () => {
expect(config.runtime).toBe("nodejs26.x");
});
});

describe("nitro:preset:vercel:skew-protection", async () => {
const prevDeploymentId = process.env.VERCEL_DEPLOYMENT_ID;
process.env.VERCEL_DEPLOYMENT_ID = "dpl_test";
Expand Down
49 changes: 49 additions & 0 deletions test/unit/vercel-runtime.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { describe, expect, it } from "vitest";
import { resolveNodeRuntime } from "../../src/presets/vercel/utils";

describe("vercel: resolveNodeRuntime", () => {
it.each([
[22, "nodejs22.x"],
[24, "nodejs24.x"],
[26, "nodejs26.x"],
])(
"uses the runtime of a supported Node.js major (%s)",
(major, expected) => {
expect(resolveNodeRuntime(major)).toBe(expected);
}
);

it.each([
[21, "nodejs22.x"],
[23, "nodejs24.x"],
[25, "nodejs26.x"],
])("uses the next supported major for Node.js %s", (major, expected) => {
expect(resolveNodeRuntime(major)).toBe(expected);
});

it.each([16, 18, 20])("uses the oldest runtime for Node.js %s", (major) => {
expect(resolveNodeRuntime(major)).toBe("nodejs22.x");
});

it.each([27, 28, 30])("uses the newest runtime for Node.js %s", (major) => {
expect(resolveNodeRuntime(major)).toBe("nodejs26.x");
});

it("detects the Node.js major of the running process", () => {
const node = Object.getOwnPropertyDescriptor(process.versions, "node")!;
try {
Object.defineProperty(process.versions, "node", {
value: "26.1.0",
configurable: true,
});
expect(resolveNodeRuntime()).toBe("nodejs26.x");
Object.defineProperty(process.versions, "node", {
value: "24.0.0",
configurable: true,
});
expect(resolveNodeRuntime()).toBe("nodejs24.x");
} finally {
Object.defineProperty(process.versions, "node", node);
}
});
});
Loading