Skip to content

Commit

Permalink
Add support for 'deno' server functions (#419)
Browse files Browse the repository at this point in the history
* Support 'deno' runtime for server functions

* Create chilled-horses-drum.md

---------

Co-authored-by: conico974 <[email protected]>
  • Loading branch information
littledivy and conico974 authored May 24, 2024
1 parent 1b3c6fe commit f83d636
Show file tree
Hide file tree
Showing 4 changed files with 44 additions and 6 deletions.
5 changes: 5 additions & 0 deletions .changeset/chilled-horses-drum.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"open-next": patch
---

Add support for 'deno' server functions
7 changes: 4 additions & 3 deletions packages/open-next/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = ${
Expand Down
36 changes: 34 additions & 2 deletions packages/open-next/src/build/createServerBundle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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';",
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion packages/open-next/src/types/open-next.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ export interface FunctionOptions extends DefaultFunctionOptions {
* Runtime used
* @default "node"
*/
runtime?: "node" | "edge";
runtime?: "node" | "edge" | "deno";
/**
* @default "regional"
*/
Expand Down

0 comments on commit f83d636

Please sign in to comment.