Skip to content

add command to build and run OpenNext locally with dev overrides #853

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
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
5 changes: 5 additions & 0 deletions .changeset/seven-dodos-draw.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/aws": patch
---

feature: add cli commands to build and run OpenNext locally with dev overrides
32 changes: 32 additions & 0 deletions packages/open-next/src/build/constant.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,35 @@
//TODO: Move all other manifest path here as well
export const MIDDLEWARE_TRACE_FILE = "server/middleware.js.nft.json";
export const INSTRUMENTATION_TRACE_FILE = "server/instrumentation.js.nft.json";

export const LOCAL_CONFIG_PATH = "./open-next.config.local.ts";
/**
* https://opennext.js.org/aws/contribute/local_run
* This is an OpenNext config to run the default server function locally
* Be aware that this will not work the same way as in production.
* Its mostly used for debugging and development purposes.
*/
export const LOCAL_CONFIG = `export default {
default: {
override: {
wrapper: "express-dev",
converter: "node",
incrementalCache: "fs-dev",
queue: "direct",
tagCache: "fs-dev",
},
},
imageOptimization: {
override: {
wrapper: "dummy",
converter: "dummy",
},
loader: "fs-dev",
// This part is not needed on ARM Linux as it will be installed by default
// Remember to change this depending on your arch and system
install: {
arch: "x64",
packages: ["sharp"],
},
},
}`;
106 changes: 96 additions & 10 deletions packages/open-next/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,34 @@
#!/usr/bin/env node
import { spawnSync } from "node:child_process";
import { existsSync, writeFileSync } from "node:fs";
import path from "node:path";

import { build } from "./build.js";
import { LOCAL_CONFIG, LOCAL_CONFIG_PATH } from "./build/constant.js";
import { printHeader } from "./build/utils.js";

const command = process.argv[2];
if (command !== "build") printHelp();
const stage = process.argv[3];
const validCommand =
command === "build" ||
command === "preview" ||
(command === "generate" && stage === "local");

if (!validCommand) {
printHelp();
}

const args = parseArgs();
if (Object.keys(args).includes("--help")) printHelp();

await build(args["--config-path"], args["--node-externals"]);

if (command === "build") {
await build(args["--config-path"], args["--node-externals"]);
} else if (command === "generate" && stage === "local") {
generateLocalConfig();
} else if (command === "preview") {
await buildLocalConfig();
runLocally();
}
function parseArgs() {
return process.argv.slice(2).reduce(
(acc, key, ind, self) => {
Expand All @@ -29,20 +48,87 @@ function parseArgs() {
);
}

async function buildLocalConfig() {
if (!existsSync(LOCAL_CONFIG_PATH)) {
console.error(
"open-next.config.local.ts does not exist. You can run `generate local` first to generate it.",
);
process.exit(1);
}

// Build OpenNext with dev overrides
printHeader("Building OpenNext with open-next.config.local.ts");
await build(LOCAL_CONFIG_PATH, args["--node-externals"]);
}

function runLocally() {
const handlerPath = path.join(
".open-next",
"server-functions",
"default",
"index.mjs",
);
if (!existsSync(handlerPath)) {
console.error(
"OpenNext server function not found. Please build it before running this command.",
);
process.exit(1);
}
printHeader("Running OpenNext locally");
spawnSync("node", [handlerPath], {
stdio: "inherit",
shell: true,
});
}

function generateLocalConfig() {
if (existsSync(LOCAL_CONFIG_PATH)) {
console.error(
"open-next.config.local.ts already exists. Please remove it before running this command.",
);
process.exit(1);
}

try {
writeFileSync(LOCAL_CONFIG_PATH, LOCAL_CONFIG);
} catch (e) {
console.error("Error writing open-next.config.local.ts", e);
}
}

function printHelp() {
console.log("Unknown command");
console.log("");
console.log("╔══════════════════════════════════════════╗");
console.log("║ Unknown Command ║");
console.log("╚══════════════════════════════════════════╝\n");

console.log("Usage:");
console.log(" npx open-next build");
console.log("You can use a custom config path here");
console.log(" Build the project with OpenNext.\n");

console.log("Options:");
console.log(" --config-path <path>");
console.log(" Use a custom config path.");
console.log(
" npx open-next build --config-path ./path/to/open-next.config.ts",
" Usage: npx open-next build --config-path ./path/to/open-next.config.ts\n",
);

console.log(" --node-externals <modules>");
console.log(" Configure externals for the esbuild compilation of the");
console.log(" open-next.config.ts file.");
console.log(
" Usage: npx open-next build --node-externals aws-sdk,sharp,sqlite3\n",
);

console.log("Other commands:");
console.log(" preview");
console.log(
" Build and run OpenNext locally with open-next.config.local.ts",
);

console.log(" generate local");
console.log(
"You can configure externals for the esbuild compilation of the open-next.config.ts file",
" Generate a config file with dev overrides for OpenNext in open-next.config.local.ts",
);
console.log(" npx open-next build --node-externals aws-sdk,sharp,sqlite3");
console.log("");

process.exit(1);
}
Loading