Skip to content

Commit ae207be

Browse files
QuiiBzpi0
authored andcommitted
refactor(vercel): support bun runtime (#3678)
1 parent 50c61c8 commit ae207be

4 files changed

Lines changed: 137 additions & 11 deletions

File tree

docs/2.deploy/20.providers/vercel.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,31 @@ export default defineNuxtConfig({
4444

4545
Framework integrations can use the `ssrRoutes` configuration to declare SSR routes. For more information, see [#3475](https://github.com/nitrojs/nitro/pull/3475).
4646

47+
## Bun runtime
48+
49+
:read-more{title="Vercel" to="https://vercel.com/docs/functions/runtimes/bun"}
50+
51+
You can use [Bun](https://bun.com) instead of Node.js by specifying the runtime using the `vercel.functions` key inside `nitro.config`:
52+
53+
```ts [nitro.config.ts]
54+
export default defineNitroConfig({
55+
vercel: {
56+
functions: {
57+
runtime: "bun1.x"
58+
}
59+
}
60+
})
61+
```
62+
63+
<!-- Alternatively, Nitro also detects Bun automatically if you specify a `bunVersion` property in your `vercel.json`:
64+
65+
```json [vercel.json]
66+
{
67+
"$schema": "https://openapi.vercel.sh/vercel.json",
68+
"bunVersion": "1.x"
69+
}
70+
``` -->
71+
4772
## Custom build output configuration
4873

4974
You can provide additional [build output configuration](https://vercel.com/docs/build-output-api/v3) using `vercel.config` key inside `nitro.config`. It will be merged with built-in auto-generated config.

src/presets/vercel/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ export interface VercelServerlessFunctionConfig {
101101
*/
102102
shouldAddSourcemapSupport?: boolean;
103103

104+
/**
105+
* The runtime to use. Defaults to the auto-detected Node.js version.
106+
*/
107+
runtime?: "nodejs20.x" | "nodejs22.x" | "bun1.x" | (string & {});
108+
104109
[key: string]: unknown;
105110
}
106111

src/presets/vercel/utils.ts

Lines changed: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,23 +38,38 @@ export async function generateFunctionFiles(nitro: Nitro) {
3838
const buildConfig = generateBuildConfig(nitro, o11Routes);
3939
await writeFile(buildConfigPath, JSON.stringify(buildConfig, null, 2));
4040

41-
const systemNodeVersion = getSystemNodeVersion();
42-
const usedNodeVersion =
43-
SUPPORTED_NODE_VERSIONS.find((version) => version >= systemNodeVersion) ??
44-
SUPPORTED_NODE_VERSIONS.at(-1);
41+
// Runtime
42+
// 1. Respect explicit runtime from nitro config
43+
let runtime: VercelServerlessFunctionConfig["runtime"] =
44+
nitro.options.vercel?.functions?.runtime;
45+
// 2. Read runtime from vercel.json if specified
46+
if (!runtime) {
47+
const vercelConfig = await readVercelConfig(nitro.options.rootDir);
48+
// Use bun runtime if bunVersion is specified or bun used to build
49+
if (vercelConfig.bunVersion || "Bun" in globalThis) {
50+
runtime = `bun${vercelConfig.bunVersion || "1.x"}`;
51+
} else {
52+
// 3. Auto-detect runtime based on system Node.js version
53+
const systemNodeVersion = getSystemNodeVersion();
54+
const usedNodeVersion =
55+
SUPPORTED_NODE_VERSIONS.find(
56+
(version) => version >= systemNodeVersion
57+
) ?? SUPPORTED_NODE_VERSIONS.at(-1);
58+
runtime = `nodejs${usedNodeVersion}.x`;
59+
}
60+
}
4561

46-
const runtimeVersion = `nodejs${usedNodeVersion}.x`;
4762
const functionConfigPath = resolve(
4863
nitro.options.output.serverDir,
4964
".vc-config.json"
5065
);
5166
const functionConfig: VercelServerlessFunctionConfig = {
52-
runtime: runtimeVersion,
67+
runtime,
68+
...nitro.options.vercel?.functions,
5369
handler: "index.mjs",
5470
launcherType: "Nodejs",
5571
shouldAddHelpers: false,
5672
supportsResponseStreaming: true,
57-
...nitro.options.vercel?.functions,
5873
};
5974
await writeFile(functionConfigPath, JSON.stringify(functionConfig, null, 2));
6075

@@ -273,6 +288,23 @@ export function deprecateSWR(nitro: Nitro) {
273288
}
274289
}
275290

291+
// --- vercel.json ---
292+
293+
// https://vercel.com/docs/project-configuration
294+
// https://openapi.vercel.sh/vercel.json
295+
export interface VercelConfig {
296+
bunVersion?: string;
297+
}
298+
299+
export async function readVercelConfig(rootDir: string): Promise<VercelConfig> {
300+
const vercelConfigPath = resolve(rootDir, "vercel.json");
301+
const vercelConfig = await fsp
302+
.readFile(vercelConfigPath)
303+
.then((config) => JSON.parse(config.toString()))
304+
.catch(() => ({}));
305+
return vercelConfig as VercelConfig;
306+
}
307+
276308
function _hasProp(obj: any, prop: string) {
277309
return obj && typeof obj === "object" && prop in obj;
278310
}

test/presets/vercel.test.ts

Lines changed: 68 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { promises as fsp } from "node:fs";
2-
import { resolve, join, relative, basename } from "pathe";
3-
import { describe, expect, it } from "vitest";
4-
import { setupTest, startServer, testNitro } from "../tests";
5-
import { readlink } from "node:fs/promises";
2+
import { resolve, join, basename } from "pathe";
3+
import { describe, expect, it, afterAll } from "vitest";
4+
import { setupTest, startServer, testNitro, fixtureDir } from "../tests";
65

76
describe("nitro:preset:vercel", async () => {
87
const ctx = await setupTest("vercel");
@@ -591,3 +590,68 @@ describe("nitro:preset:vercel", async () => {
591590
}
592591
);
593592
});
593+
594+
describe("nitro:preset:vercel:bun", async () => {
595+
const ctx = await setupTest("vercel", {
596+
config: {
597+
preset: "vercel",
598+
vercel: {
599+
functions: {
600+
runtime: "bun1.x",
601+
},
602+
},
603+
},
604+
});
605+
606+
it("should generate function config with bun runtime", async () => {
607+
const config = await fsp
608+
.readFile(
609+
resolve(ctx.outDir, "functions/__fallback.func/.vc-config.json"),
610+
"utf8"
611+
)
612+
.then((r) => JSON.parse(r));
613+
expect(config).toMatchInlineSnapshot(`
614+
{
615+
"handler": "index.mjs",
616+
"launcherType": "Nodejs",
617+
"runtime": "bun1.x",
618+
"shouldAddHelpers": false,
619+
"supportsResponseStreaming": true,
620+
}
621+
`);
622+
});
623+
});
624+
625+
describe("nitro:preset:vercel:bun-verceljson", async () => {
626+
const vercelJsonPath = join(fixtureDir, "vercel.json");
627+
// Need to make sure vercel.json is created before setupTest is called
628+
await fsp.writeFile(vercelJsonPath, JSON.stringify({ bunVersion: "1.x" }));
629+
630+
const ctx = await setupTest("vercel", {
631+
config: {
632+
preset: "vercel",
633+
},
634+
});
635+
636+
afterAll(async () => {
637+
await fsp.unlink(vercelJsonPath).catch(() => {});
638+
});
639+
640+
it("should detect bun runtime from vercel.json", async () => {
641+
const config = await fsp
642+
.readFile(
643+
resolve(ctx.outDir, "functions/__fallback.func/.vc-config.json"),
644+
"utf8"
645+
)
646+
.then((r) => JSON.parse(r));
647+
expect(config).toMatchInlineSnapshot(`
648+
{
649+
"handler": "index.mjs",
650+
"launcherType": "Nodejs",
651+
"runtime": "bun1.x",
652+
"shouldAddHelpers": false,
653+
"supportsResponseStreaming": true,
654+
}
655+
`);
656+
});
657+
});

0 commit comments

Comments
 (0)