From 026ed206aacd92bc28fd252f2c6bbc18cd0d5836 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 15 Oct 2024 12:40:22 +0200 Subject: [PATCH 1/7] refactor: consistently retrieve the build folder from the options And a few other minor changes: - rename BuildOptions#tempDir to BuildOptions#buildDir - simplify the default OpenNext config - rename a few arguments for consistency / clarity --- packages/open-next/src/build.ts | 25 ++++++++----------- packages/open-next/src/build/compileConfig.ts | 13 +++------- .../open-next/src/build/createServerBundle.ts | 15 +++++------ .../src/build/edge/createEdgeBundle.ts | 2 +- packages/open-next/src/build/helper.ts | 10 ++++---- 5 files changed, 26 insertions(+), 39 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index 51ef46859..a46b571be 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -191,15 +191,12 @@ function initOutputDir(srcTempDir: string, options: BuildOptions) { ); } fs.rmSync(options.outputDir, { recursive: true, force: true }); - const destTempDir = options.tempDir; - fs.mkdirSync(destTempDir, { recursive: true }); - fs.writeFileSync( - path.join(destTempDir, "open-next.config.mjs"), - openNextConfig, - ); + const { buildDir } = options; + fs.mkdirSync(buildDir, { recursive: true }); + fs.writeFileSync(path.join(buildDir, "open-next.config.mjs"), openNextConfig); if (openNextConfigEdge) { fs.writeFileSync( - path.join(destTempDir, "open-next.config.edge.mjs"), + path.join(buildDir, "open-next.config.edge.mjs"), openNextConfigEdge, ); } @@ -215,7 +212,7 @@ async function createWarmerBundle(options: BuildOptions) { fs.mkdirSync(outputPath, { recursive: true }); // Copy open-next.config.mjs into the bundle - copyOpenNextConfig(options.tempDir, outputPath); + copyOpenNextConfig(options.buildDir, outputPath); // Build Lambda code // note: bundle in OpenNext package b/c the adatper relys on the @@ -258,7 +255,7 @@ async function createRevalidationBundle(options: BuildOptions) { fs.mkdirSync(outputPath, { recursive: true }); //Copy open-next.config.mjs into the bundle - copyOpenNextConfig(options.tempDir, outputPath); + copyOpenNextConfig(options.buildDir, outputPath); // Build Lambda code await esbuildAsync( @@ -297,7 +294,7 @@ async function createImageOptimizationBundle(options: BuildOptions) { fs.mkdirSync(outputPath, { recursive: true }); // Copy open-next.config.mjs into the bundle - copyOpenNextConfig(options.tempDir, outputPath); + copyOpenNextConfig(options.buildDir, outputPath); const plugins = [ openNextResolvePlugin({ @@ -636,7 +633,7 @@ async function createCacheAssets(options: BuildOptions) { ); //Copy open-next.config.mjs into the bundle - copyOpenNextConfig(options.tempDir, providerPath); + copyOpenNextConfig(options.buildDir, providerPath); // TODO: check if metafiles doesn't contain duplicates fs.writeFileSync( @@ -660,7 +657,7 @@ export function compileCache( ) { const { config } = options; const ext = format === "cjs" ? "cjs" : "mjs"; - const outfile = path.join(options.outputDir, ".build", `cache.${ext}`); + const outfile = path.join(options.buildDir, `cache.${ext}`); const isAfter15 = compareSemver(options.nextVersion, "15.0.0") >= 0; @@ -721,7 +718,7 @@ async function createMiddleware(options: BuildOptions) { // Copy open-next.config.mjs copyOpenNextConfig( - options.tempDir, + options.buildDir, outputPath, config.middleware.override?.wrapper === "cloudflare", ); @@ -739,7 +736,7 @@ async function createMiddleware(options: BuildOptions) { } else { await buildEdgeBundle({ entrypoint: path.join(__dirname, "core", "edgeFunctionHandler.js"), - outfile: path.join(outputDir, ".build", "middleware.mjs"), + outfile: path.join(options.buildDir, "middleware.mjs"), ...commonMiddlewareOptions, onlyBuildOnce: true, }); diff --git a/packages/open-next/src/build/compileConfig.ts b/packages/open-next/src/build/compileConfig.ts index eded84ac8..c78387011 100644 --- a/packages/open-next/src/build/compileConfig.ts +++ b/packages/open-next/src/build/compileConfig.ts @@ -7,7 +7,7 @@ import { OpenNextConfig } from "types/open-next.js"; import logger from "../logger.js"; export function compileOpenNextConfigNode( - tempDir: string, + outputDir: string, openNextConfigPath?: string, nodeExternals?: string, ) { @@ -15,20 +15,13 @@ export function compileOpenNextConfigNode( process.cwd(), openNextConfigPath ?? "open-next.config.ts", ); - const outputPath = path.join(tempDir, "open-next.config.mjs"); + const outputPath = path.join(outputDir, "open-next.config.mjs"); //Check if open-next.config.ts exists if (!fs.existsSync(sourcePath)) { //Create a simple open-next.config.mjs file logger.debug("Cannot find open-next.config.ts. Using default config."); - fs.writeFileSync( - outputPath, - [ - "var config = { default: { } };", - "var open_next_config_default = config;", - "export { open_next_config_default as default };", - ].join("\n"), - ); + fs.writeFileSync(outputPath, "export default { default: { } };"); } else { buildSync({ entryPoints: [sourcePath], diff --git a/packages/open-next/src/build/createServerBundle.ts b/packages/open-next/src/build/createServerBundle.ts index dc555f364..8f78bf9bd 100644 --- a/packages/open-next/src/build/createServerBundle.ts +++ b/packages/open-next/src/build/createServerBundle.ts @@ -144,7 +144,7 @@ async function generateBundle( const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs"; fs.copyFileSync( - path.join(outputDir, ".build", `cache.${ext}`), + path.join(options.buildDir, `cache.${ext}`), path.join(outputPath, packagePath, "cache.cjs"), ); @@ -158,24 +158,21 @@ async function generateBundle( await bundleNextServer(path.join(outputPath, packagePath), appPath); } - // // Copy middleware + // Copy middleware if ( !config.middleware?.external && - existsSync(path.join(outputDir, ".build", "middleware.mjs")) + existsSync(path.join(options.buildDir, "middleware.mjs")) ) { fs.copyFileSync( - path.join(outputDir, ".build", "middleware.mjs"), + path.join(options.buildDir, "middleware.mjs"), path.join(outputPath, packagePath, "middleware.mjs"), ); } // Copy open-next.config.mjs - copyOpenNextConfig( - path.join(outputDir, ".build"), - path.join(outputPath, packagePath), - ); + copyOpenNextConfig(options.buildDir, path.join(outputPath, packagePath)); - //Copy env files + // Copy env files copyEnvFile(appBuildOutputPath, packagePath, outputPath); // Copy all necessary traced files diff --git a/packages/open-next/src/build/edge/createEdgeBundle.ts b/packages/open-next/src/build/edge/createEdgeBundle.ts index c8722dbc9..3ddafaba6 100644 --- a/packages/open-next/src/build/edge/createEdgeBundle.ts +++ b/packages/open-next/src/build/edge/createEdgeBundle.ts @@ -179,7 +179,7 @@ export async function generateEdgeBundle( fs.mkdirSync(outputPath, { recursive: true }); // Copy open-next.config.mjs - copyOpenNextConfig(path.join(outputDir, ".build"), outputPath, true); + copyOpenNextConfig(options.buildDir, outputPath, true); // Load middleware manifest const middlewareManifest = JSON.parse( diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index 539f39acc..4fec9d464 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -51,7 +51,7 @@ export function normalizeOptions(config: OpenNextConfig) { openNextVersion: getOpenNextVersion(), outputDir, packager, - tempDir: path.join(outputDir, ".build"), + buildDir: path.join(outputDir, ".build"), }; } @@ -273,17 +273,17 @@ export function compareSemver(v1: string, v2: string): number { } export function copyOpenNextConfig( - tempDir: string, - outputPath: string, + inputDir: string, + outputDir: string, isEdge = false, ) { // Copy open-next.config.mjs fs.copyFileSync( path.join( - tempDir, + inputDir, isEdge ? "open-next.config.edge.mjs" : "open-next.config.mjs", ), - path.join(outputPath, "open-next.config.mjs"), + path.join(outputDir, "open-next.config.mjs"), ); } From 083c316b6c9a0ffeac24c508e661a968663f4e8f Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 15 Oct 2024 13:03:46 +0200 Subject: [PATCH 2/7] refactor: compile the config in a proper temp dir --- packages/open-next/src/build.ts | 50 ++++++++++---------------- packages/open-next/src/build/helper.ts | 8 +++-- 2 files changed, 25 insertions(+), 33 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index a46b571be..ffe70f5d0 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -1,5 +1,5 @@ import cp from "node:child_process"; -import fs, { readFileSync } from "node:fs"; +import fs from "node:fs"; import { createRequire as topLevelCreateRequire } from "node:module"; import os from "node:os"; import path from "node:path"; @@ -47,9 +47,9 @@ export async function build( showWindowsWarning(); // Load open-next.config.ts - const tempDir = initTempDir(); + const tempConfigDir = fs.mkdtempSync("open-next"); let configPath = compileOpenNextConfigNode( - tempDir, + tempConfigDir, openNextConfigPath, nodeExternals, ); @@ -64,10 +64,10 @@ export async function build( } validateConfig(config); - compileOpenNextConfigEdge(tempDir, config, openNextConfigPath); + compileOpenNextConfigEdge(tempConfigDir, config, openNextConfigPath); // Initialize options - const options = normalizeOptions(config); + const options = normalizeOptions(config, tempConfigDir); logger.setLevel(options.debug ? "debug" : "info"); // Pre-build validation @@ -82,7 +82,7 @@ export async function build( // Generate deployable bundle printHeader("Generating bundle"); - initOutputDir(tempDir, options); + initOutputDir(options); // Compile cache.ts compileCache(options); @@ -113,14 +113,6 @@ function showWindowsWarning() { ); } -function initTempDir() { - const dir = path.join(process.cwd(), ".open-next"); - const tempDir = path.join(dir, ".build"); - fs.rmSync(dir, { recursive: true, force: true }); - fs.mkdirSync(tempDir, { recursive: true }); - return tempDir; -} - function checkRunningInsideNextjsApp(options: BuildOptions) { const { appPath } = options; const extension = ["js", "cjs", "mjs", "ts"].find((ext) => @@ -175,30 +167,26 @@ function printOpenNextVersion(options: BuildOptions) { logger.info(`OpenNext v${options.openNextVersion}`); } -function initOutputDir(srcTempDir: string, options: BuildOptions) { +function initOutputDir(options: BuildOptions) { // We need to get the build relative to the cwd to find the compiled config // This is needed for the case where the app is a single-version monorepo and the package.json is in the root of the monorepo // where the build is in the app directory, but the compiled config is in the root of the monorepo. - const openNextConfig = readFileSync( - path.join(srcTempDir, "open-next.config.mjs"), - "utf8", - ); - let openNextConfigEdge: string | null = null; - if (fs.existsSync(path.join(srcTempDir, "open-next.config.edge.mjs"))) { - openNextConfigEdge = readFileSync( - path.join(srcTempDir, "open-next.config.edge.mjs"), - "utf8", - ); - } fs.rmSync(options.outputDir, { recursive: true, force: true }); const { buildDir } = options; fs.mkdirSync(buildDir, { recursive: true }); - fs.writeFileSync(path.join(buildDir, "open-next.config.mjs"), openNextConfig); - if (openNextConfigEdge) { - fs.writeFileSync( + + fs.copyFileSync( + path.join(options.tempConfigDir, "open-next.config.mjs"), + path.join(buildDir, "open-next.config.mjs"), + ); + + try { + fs.copyFileSync( + path.join(options.tempConfigDir, "open-next.config.edge.mjs"), path.join(buildDir, "open-next.config.edge.mjs"), - openNextConfigEdge, ); + } catch { + // The edge config does not always exist. } } @@ -692,7 +680,7 @@ async function createMiddleware(options: BuildOptions) { // Get middleware manifest const middlewareManifest = JSON.parse( - readFileSync( + fs.readFileSync( path.join(appBuildOutputPath, ".next/server/middleware-manifest.json"), "utf8", ), diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index 4fec9d464..3b4091e06 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -17,7 +17,10 @@ const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); export type BuildOptions = ReturnType; -export function normalizeOptions(config: OpenNextConfig) { +export function normalizeOptions( + config: OpenNextConfig, + tempConfigDir: string, +) { const appPath = path.join(process.cwd(), config.appPath || "."); const buildOutputPath = path.join( process.cwd(), @@ -44,6 +47,7 @@ export function normalizeOptions(config: OpenNextConfig) { appPackageJsonPath, appPath, appPublicPath: path.join(appPath, "public"), + buildDir: path.join(outputDir, ".build"), config, debug: Boolean(process.env.OPEN_NEXT_DEBUG) ?? false, monorepoRoot, @@ -51,7 +55,7 @@ export function normalizeOptions(config: OpenNextConfig) { openNextVersion: getOpenNextVersion(), outputDir, packager, - buildDir: path.join(outputDir, ".build"), + tempConfigDir, }; } From 4ee55df27f0b3216288ed463fe5f4b65909e3c88 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 15 Oct 2024 13:07:32 +0200 Subject: [PATCH 3/7] feat: add a warning when no lockfile found --- packages/open-next/src/build/helper.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index 3b4091e06..318bd94fc 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -81,6 +81,7 @@ function findMonorepoRoot(appPath: string) { // note: a lock file (package-lock.json, yarn.lock, or pnpm-lock.yaml) is // not found in the app's directory or any of its parent directories. // We are going to assume that the app is not part of a monorepo. + logger.warn("No lockfile found"); return { root: appPath, packager: "npm" as const }; } From 343a10c6a6de73b11e61de4377961b1ce52823f7 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 15 Oct 2024 16:21:18 +0200 Subject: [PATCH 4/7] fix: correctly handle the temp folder And use it (move) as the build folder --- packages/open-next/src/build.ts | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index ffe70f5d0..36816f54b 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -47,7 +47,9 @@ export async function build( showWindowsWarning(); // Load open-next.config.ts - const tempConfigDir = fs.mkdtempSync("open-next"); + const tempConfigDir = fs.mkdtempSync( + path.join(os.tmpdir(), ".open-next-temp"), + ); let configPath = compileOpenNextConfigNode( tempConfigDir, openNextConfigPath, @@ -174,20 +176,8 @@ function initOutputDir(options: BuildOptions) { fs.rmSync(options.outputDir, { recursive: true, force: true }); const { buildDir } = options; fs.mkdirSync(buildDir, { recursive: true }); - - fs.copyFileSync( - path.join(options.tempConfigDir, "open-next.config.mjs"), - path.join(buildDir, "open-next.config.mjs"), - ); - - try { - fs.copyFileSync( - path.join(options.tempConfigDir, "open-next.config.edge.mjs"), - path.join(buildDir, "open-next.config.edge.mjs"), - ); - } catch { - // The edge config does not always exist. - } + fs.renameSync(options.tempConfigDir, buildDir); + process.exit(0); } async function createWarmerBundle(options: BuildOptions) { From 66b59af5058f7dc8eb822a910588f58cf98eae61 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Tue, 15 Oct 2024 18:45:37 +0200 Subject: [PATCH 5/7] fix: review feedback --- packages/open-next/src/build.ts | 11 +++++------ packages/open-next/src/build/helper.ts | 7 ++----- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index 36816f54b..19e379e28 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -47,11 +47,11 @@ export async function build( showWindowsWarning(); // Load open-next.config.ts - const tempConfigDir = fs.mkdtempSync( + const tempBuildDir = fs.mkdtempSync( path.join(os.tmpdir(), ".open-next-temp"), ); let configPath = compileOpenNextConfigNode( - tempConfigDir, + tempBuildDir, openNextConfigPath, nodeExternals, ); @@ -66,10 +66,10 @@ export async function build( } validateConfig(config); - compileOpenNextConfigEdge(tempConfigDir, config, openNextConfigPath); + compileOpenNextConfigEdge(tempBuildDir, config, openNextConfigPath); // Initialize options - const options = normalizeOptions(config, tempConfigDir); + const options = normalizeOptions(config, tempBuildDir); logger.setLevel(options.debug ? "debug" : "info"); // Pre-build validation @@ -176,8 +176,7 @@ function initOutputDir(options: BuildOptions) { fs.rmSync(options.outputDir, { recursive: true, force: true }); const { buildDir } = options; fs.mkdirSync(buildDir, { recursive: true }); - fs.renameSync(options.tempConfigDir, buildDir); - process.exit(0); + fs.renameSync(options.tempBuildDir, buildDir); } async function createWarmerBundle(options: BuildOptions) { diff --git a/packages/open-next/src/build/helper.ts b/packages/open-next/src/build/helper.ts index 318bd94fc..d3c95d45f 100644 --- a/packages/open-next/src/build/helper.ts +++ b/packages/open-next/src/build/helper.ts @@ -17,10 +17,7 @@ const __dirname = url.fileURLToPath(new URL(".", import.meta.url)); export type BuildOptions = ReturnType; -export function normalizeOptions( - config: OpenNextConfig, - tempConfigDir: string, -) { +export function normalizeOptions(config: OpenNextConfig, tempBuildDir: string) { const appPath = path.join(process.cwd(), config.appPath || "."); const buildOutputPath = path.join( process.cwd(), @@ -55,7 +52,7 @@ export function normalizeOptions( openNextVersion: getOpenNextVersion(), outputDir, packager, - tempConfigDir, + tempBuildDir, }; } From 8f9aa56d4dddf128688ce7d14ee577481965592a Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 16 Oct 2024 06:53:34 +0200 Subject: [PATCH 6/7] fix: use cp over over rename rename does not work across devices and the os tmp might be on a different device --- packages/open-next/src/build.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/packages/open-next/src/build.ts b/packages/open-next/src/build.ts index 19e379e28..f55f3d95d 100755 --- a/packages/open-next/src/build.ts +++ b/packages/open-next/src/build.ts @@ -47,9 +47,7 @@ export async function build( showWindowsWarning(); // Load open-next.config.ts - const tempBuildDir = fs.mkdtempSync( - path.join(os.tmpdir(), ".open-next-temp"), - ); + const tempBuildDir = fs.mkdtempSync(path.join(os.tmpdir(), "open-next-tmp")); let configPath = compileOpenNextConfigNode( tempBuildDir, openNextConfigPath, @@ -176,7 +174,7 @@ function initOutputDir(options: BuildOptions) { fs.rmSync(options.outputDir, { recursive: true, force: true }); const { buildDir } = options; fs.mkdirSync(buildDir, { recursive: true }); - fs.renameSync(options.tempBuildDir, buildDir); + fs.cpSync(options.tempBuildDir, buildDir, { recursive: true }); } async function createWarmerBundle(options: BuildOptions) { From 70f855dd57b53321f99d96fb2011f6e0dab46355 Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Wed, 16 Oct 2024 10:14:03 +0200 Subject: [PATCH 7/7] test: use the latest NPM release --- .github/workflows/e2e.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/e2e.yml b/.github/workflows/e2e.yml index a9a14696f..c109b3e08 100644 --- a/.github/workflows/e2e.yml +++ b/.github/workflows/e2e.yml @@ -33,7 +33,7 @@ jobs: - name: Get Latest Next Version id: get_latest_version run: | - latest_version=$(curl -s https://api.github.com/repos/vercel/next.js/releases/latest | jq -r '.tag_name') + latest_version=$(curl -s https://registry.npmjs.org/-/package/next/dist-tags | jq -r '.latest') echo "Latest version: $latest_version" echo "LATEST_VERSION=$latest_version" >> $GITHUB_ENV echo "LATEST_VERSION=$latest_version" >> $GITHUB_OUTPUT