Skip to content

Commit 1e6d6bc

Browse files
committed
Support 'deno' runtime for server functions
1 parent 65f7a60 commit 1e6d6bc

File tree

3 files changed

+39
-6
lines changed

3 files changed

+39
-6
lines changed

packages/open-next/src/build.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -677,15 +677,16 @@ async function createCacheAssets(monorepoRoot: string) {
677677
/* Server Helper Functions */
678678
/***************************/
679679

680-
function compileCache() {
681-
const outfile = path.join(options.outputDir, ".build", "cache.cjs");
680+
export function compileCache(format: "cjs" | "esm" = "cjs") {
681+
const ext = format === "cjs" ? "cjs" : "mjs";
682+
const outfile = path.join(options.outputDir, ".build", `cache.${ext}`);
682683
esbuildSync(
683684
{
684685
external: ["next", "styled-jsx", "react", "@aws-sdk/*"],
685686
entryPoints: [path.join(__dirname, "adapters", "cache.js")],
686687
outfile,
687688
target: ["node18"],
688-
format: "cjs",
689+
format,
689690
banner: {
690691
js: [
691692
`globalThis.disableIncrementalCache = ${

packages/open-next/src/build/createServerBundle.ts

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from "types/open-next";
1111
import url from "url";
1212

13+
import { compileCache } from "../build.js";
1314
import logger from "../logger.js";
1415
import { minifyAll } from "../minimize-js.js";
1516
import { openNextReplacementPlugin } from "../plugins/replacement.js";
@@ -37,6 +38,14 @@ export async function createServerBundle(
3738
const defaultFn = config.default;
3839
const functions = Object.entries(config.functions ?? {});
3940

41+
// Recompile cache.ts as ESM if any function is using Deno runtime
42+
if (
43+
defaultFn.runtime === "deno" ||
44+
functions.some(([, fn]) => fn.runtime === "deno")
45+
) {
46+
compileCache("esm");
47+
}
48+
4049
const promises = functions.map(async ([name, fnOptions]) => {
4150
const routes = fnOptions.routes;
4251
routes.forEach((route) => foundRoutes.add(route));
@@ -134,11 +143,16 @@ async function generateBundle(
134143
const packagePath = path.relative(monorepoRoot, appBuildOutputPath);
135144
fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true });
136145

146+
const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs";
137147
fs.copyFileSync(
138-
path.join(outputDir, ".build", "cache.cjs"),
148+
path.join(outputDir, ".build", `cache.${ext}`),
139149
path.join(outputPath, packagePath, "cache.cjs"),
140150
);
141151

152+
if (fnOptions.runtime === "deno") {
153+
addDenoJson(outputPath, packagePath);
154+
}
155+
142156
// Bundle next server if necessary
143157
const isBundled = fnOptions.experimentalBundledNextServer ?? false;
144158
if (isBundled) {
@@ -227,14 +241,18 @@ async function generateBundle(
227241
.join(",")}] for Next version: ${options.nextVersion}`,
228242
);
229243
}
244+
245+
const outfileExt = fnOptions.runtime === "deno" ? "ts" : "mjs";
230246
await esbuildAsync(
231247
{
232248
entryPoints: [path.join(__dirname, "../adapters", "server-adapter.js")],
233249
external: ["next", "./middleware.mjs", "./next-server.runtime.prod.js"],
234-
outfile: path.join(outputPath, packagePath, "index.mjs"),
250+
outfile: path.join(outputPath, packagePath, `index.${outfileExt}`),
235251
banner: {
236252
js: [
237253
`globalThis.monorepoPackagePath = "${packagePath}";`,
254+
"import process from 'node:process';",
255+
"import { Buffer } from 'node:buffer';",
238256
"import { createRequire as topLevelCreateRequire } from 'module';",
239257
"const require = topLevelCreateRequire(import.meta.url);",
240258
"import bannerUrl from 'url';",
@@ -280,6 +298,20 @@ function shouldGenerateDockerfile(options: FunctionOptions) {
280298
return options.override?.generateDockerfile ?? false;
281299
}
282300

301+
// Add deno.json file to enable "bring your own node_modules" mode.
302+
// TODO: this won't be necessary in Deno 2. See https://github.com/denoland/deno/issues/23151
303+
function addDenoJson(outputPath: string, packagePath: string) {
304+
const config = {
305+
// Enable "bring your own node_modules" mode
306+
// and allow `__proto__`
307+
unstable: ["byonm", "fs", "unsafe-proto"],
308+
};
309+
fs.writeFileSync(
310+
path.join(outputPath, packagePath, "deno.json"),
311+
JSON.stringify(config, null, 2),
312+
);
313+
}
314+
283315
//TODO: check if this PR is still necessary https://github.com/sst/open-next/pull/341
284316
function addMonorepoEntrypoint(outputPath: string, packagePath: string) {
285317
// Note: in the monorepo case, the handler file is output to

packages/open-next/src/types/open-next.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ export interface FunctionOptions extends DefaultFunctionOptions {
188188
* Runtime used
189189
* @default "node"
190190
*/
191-
runtime?: "node" | "edge";
191+
runtime?: "node" | "edge" | "deno";
192192
/**
193193
* @default "regional"
194194
*/

0 commit comments

Comments
 (0)