From 1e6d6bcbecba4c0565eab3dae7adbc364f9ac207 Mon Sep 17 00:00:00 2001 From: Divy Srivastava Date: Thu, 23 May 2024 09:36:18 +0530 Subject: [PATCH 1/2] Support 'deno' runtime for server functions --- packages/open-next/src/build.ts | 7 ++-- .../open-next/src/build/createServerBundle.ts | 36 +++++++++++++++++-- packages/open-next/src/types/open-next.ts | 2 +- 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index 746d263d..140fc049 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -677,15 +677,16 @@ async function createCacheAssets(monorepoRoot: string) { /* Server Helper Functions */ /***************************/ -function compileCache() { - const outfile = path.join(options.outputDir, ".build", "cache.cjs"); +export function compileCache(format: "cjs" | "esm" = "cjs") { + const ext = format === "cjs" ? "cjs" : "mjs"; + const outfile = path.join(options.outputDir, ".build", `cache.${ext}`); esbuildSync( { external: ["next", "styled-jsx", "react", "@aws-sdk/*"], entryPoints: [path.join(__dirname, "adapters", "cache.js")], outfile, target: ["node18"], - format: "cjs", + format, banner: { js: [ `globalThis.disableIncrementalCache = ${ diff --git a/packages/open-next/src/build/createServerBundle.ts b/packages/open-next/src/build/createServerBundle.ts index e1e0474c..e12467f1 100644 --- a/packages/open-next/src/build/createServerBundle.ts +++ b/packages/open-next/src/build/createServerBundle.ts @@ -10,6 +10,7 @@ import { } from "types/open-next"; import url from "url"; +import { compileCache } from "../build.js"; import logger from "../logger.js"; import { minifyAll } from "../minimize-js.js"; import { openNextReplacementPlugin } from "../plugins/replacement.js"; @@ -37,6 +38,14 @@ export async function createServerBundle( const defaultFn = config.default; const functions = Object.entries(config.functions ?? {}); + // Recompile cache.ts as ESM if any function is using Deno runtime + if ( + defaultFn.runtime === "deno" || + functions.some(([, fn]) => fn.runtime === "deno") + ) { + compileCache("esm"); + } + const promises = functions.map(async ([name, fnOptions]) => { const routes = fnOptions.routes; routes.forEach((route) => foundRoutes.add(route)); @@ -134,11 +143,16 @@ async function generateBundle( const packagePath = path.relative(monorepoRoot, appBuildOutputPath); fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true }); + const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs"; fs.copyFileSync( - path.join(outputDir, ".build", "cache.cjs"), + path.join(outputDir, ".build", `cache.${ext}`), path.join(outputPath, packagePath, "cache.cjs"), ); + if (fnOptions.runtime === "deno") { + addDenoJson(outputPath, packagePath); + } + // Bundle next server if necessary const isBundled = fnOptions.experimentalBundledNextServer ?? false; if (isBundled) { @@ -227,14 +241,18 @@ async function generateBundle( .join(",")}] for Next version: ${options.nextVersion}`, ); } + + const outfileExt = fnOptions.runtime === "deno" ? "ts" : "mjs"; await esbuildAsync( { entryPoints: [path.join(__dirname, "../adapters", "server-adapter.js")], external: ["next", "./middleware.mjs", "./next-server.runtime.prod.js"], - outfile: path.join(outputPath, packagePath, "index.mjs"), + outfile: path.join(outputPath, packagePath, `index.${outfileExt}`), banner: { js: [ `globalThis.monorepoPackagePath = "${packagePath}";`, + "import process from 'node:process';", + "import { Buffer } from 'node:buffer';", "import { createRequire as topLevelCreateRequire } from 'module';", "const require = topLevelCreateRequire(import.meta.url);", "import bannerUrl from 'url';", @@ -280,6 +298,20 @@ function shouldGenerateDockerfile(options: FunctionOptions) { return options.override?.generateDockerfile ?? false; } +// Add deno.json file to enable "bring your own node_modules" mode. +// TODO: this won't be necessary in Deno 2. See https://github.com/denoland/deno/issues/23151 +function addDenoJson(outputPath: string, packagePath: string) { + const config = { + // Enable "bring your own node_modules" mode + // and allow `__proto__` + unstable: ["byonm", "fs", "unsafe-proto"], + }; + fs.writeFileSync( + path.join(outputPath, packagePath, "deno.json"), + JSON.stringify(config, null, 2), + ); +} + //TODO: check if this PR is still necessary https://github.com/sst/open-next/pull/341 function addMonorepoEntrypoint(outputPath: string, packagePath: string) { // Note: in the monorepo case, the handler file is output to diff --git a/packages/open-next/src/types/open-next.ts b/packages/open-next/src/types/open-next.ts index 89b5fcd2..b8f68323 100644 --- a/packages/open-next/src/types/open-next.ts +++ b/packages/open-next/src/types/open-next.ts @@ -188,7 +188,7 @@ export interface FunctionOptions extends DefaultFunctionOptions { * Runtime used * @default "node" */ - runtime?: "node" | "edge"; + runtime?: "node" | "edge" | "deno"; /** * @default "regional" */ From 5e5bb70dca00af1bf4fcd448643cdc1f205dd9ea Mon Sep 17 00:00:00 2001 From: conico974 Date: Fri, 24 May 2024 13:14:33 +0200 Subject: [PATCH 2/2] Create chilled-horses-drum.md --- .changeset/chilled-horses-drum.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/chilled-horses-drum.md diff --git a/.changeset/chilled-horses-drum.md b/.changeset/chilled-horses-drum.md new file mode 100644 index 00000000..6dd427a6 --- /dev/null +++ b/.changeset/chilled-horses-drum.md @@ -0,0 +1,5 @@ +--- +"open-next": patch +--- + +Add support for 'deno' server functions