From 8bdd6de8648382e7212e15dfec9d3ed7562583bc Mon Sep 17 00:00:00 2001 From: James Date: Sun, 31 Aug 2025 17:57:07 +0100 Subject: [PATCH 1/5] feat: support for a custom OpenNext config path --- .changeset/real-bats-sniff.md | 5 +++++ .../my-opennext-config.ts} | 0 .../overrides/r2-incremental-cache/package.json | 2 +- .../src/cli/build/utils/create-config-files.ts | 14 +++++++++----- packages/cloudflare/src/cli/commands/build.ts | 8 +++++++- packages/cloudflare/src/cli/commands/utils.ts | 6 +++--- 6 files changed, 25 insertions(+), 10 deletions(-) create mode 100644 .changeset/real-bats-sniff.md rename examples/overrides/r2-incremental-cache/{open-next.config.ts => .custom-config/my-opennext-config.ts} (100%) diff --git a/.changeset/real-bats-sniff.md b/.changeset/real-bats-sniff.md new file mode 100644 index 00000000..9fd2f1ce --- /dev/null +++ b/.changeset/real-bats-sniff.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": minor +--- + +feat: support for a custom OpenNext config path with the `--openNextConfigPath` flag diff --git a/examples/overrides/r2-incremental-cache/open-next.config.ts b/examples/overrides/r2-incremental-cache/.custom-config/my-opennext-config.ts similarity index 100% rename from examples/overrides/r2-incremental-cache/open-next.config.ts rename to examples/overrides/r2-incremental-cache/.custom-config/my-opennext-config.ts diff --git a/examples/overrides/r2-incremental-cache/package.json b/examples/overrides/r2-incremental-cache/package.json index c0c74322..26a9fb23 100644 --- a/examples/overrides/r2-incremental-cache/package.json +++ b/examples/overrides/r2-incremental-cache/package.json @@ -7,7 +7,7 @@ "build": "next build", "start": "next start", "lint": "next lint", - "build:worker": "pnpm opennextjs-cloudflare build", + "build:worker": "pnpm opennextjs-cloudflare build --openNextConfigPath=./.custom-config/my-opennext-config.ts", "preview:worker": "pnpm opennextjs-cloudflare preview", "preview": "pnpm build:worker && pnpm preview:worker", "e2e": "playwright test -c e2e/playwright.config.ts" diff --git a/packages/cloudflare/src/cli/build/utils/create-config-files.ts b/packages/cloudflare/src/cli/build/utils/create-config-files.ts index 4b00989d..9b8777ca 100644 --- a/packages/cloudflare/src/cli/build/utils/create-config-files.ts +++ b/packages/cloudflare/src/cli/build/utils/create-config-files.ts @@ -88,22 +88,26 @@ export async function getLatestCompatDate(): Promise { } /** - * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation. + * Creates a config file for the user if it doesn't exist, but only after asking for the user's confirmation. * * If the user refuses an error is thrown (since the file is mandatory). * + * @param configPath The path to the config file, relative to the source directory * @param sourceDir The source directory for the project */ -export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise { - const openNextConfigPath = join(sourceDir, "open-next.config.ts"); +export async function createOpenNextConfigIfNotExistent( + configPath: string, + sourceDir: string +): Promise { + const openNextConfigPath = join(sourceDir, configPath); if (!existsSync(openNextConfigPath)) { const answer = await askConfirmation( - "Missing required `open-next.config.ts` file, do you want to create one?" + `Missing required \`${configPath}\` file, do you want to create one?` ); if (!answer) { - throw new Error("The `open-next.config.ts` file is required, aborting!"); + throw new Error(`The \`${configPath}\` file is required, aborting!`); } cpSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), openNextConfigPath); diff --git a/packages/cloudflare/src/cli/commands/build.ts b/packages/cloudflare/src/cli/commands/build.ts index efb035d0..3e38fcc4 100644 --- a/packages/cloudflare/src/cli/commands/build.ts +++ b/packages/cloudflare/src/cli/commands/build.ts @@ -22,11 +22,12 @@ async function buildCommand( skipNextBuild: boolean; noMinify: boolean; skipWranglerConfigCheck: boolean; + openNextConfigPath: string; }> ): Promise { printHeaders("build"); - const { config, buildDir } = await compileConfig(); + const { config, buildDir } = await compileConfig(args.openNextConfigPath); const options = getNormalizedOptions(config, buildDir); const wranglerConfig = readWranglerConfig(args); @@ -65,6 +66,11 @@ export function addBuildCommand(y: T) { type: "boolean", default: ["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)), desc: "Skip checking for a Wrangler config", + }) + .option("openNextConfigPath", { + type: "string", + default: "open-next.config.ts", + desc: "Path to OpenNext configuration file, relative to the source directory", }), (args) => buildCommand(withWranglerPassthroughArgs(args)) ); diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils.ts index ae6afb4d..e39b6341 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -37,10 +37,10 @@ export function printHeaders(command: string) { * * @returns OpenNext config. */ -export async function compileConfig() { - await createOpenNextConfigIfNotExistent(nextAppDir); +export async function compileConfig(configPath: string) { + await createOpenNextConfigIfNotExistent(configPath, nextAppDir); - const { config, buildDir } = await compileOpenNextConfig(nextAppDir, undefined, { compileEdge: true }); + const { config, buildDir } = await compileOpenNextConfig(nextAppDir, configPath, { compileEdge: true }); ensureCloudflareConfig(config); return { config, buildDir }; From f81a23cd0d253aa72068f33535b25e320d03ccae Mon Sep 17 00:00:00 2001 From: James Date: Tue, 2 Sep 2025 19:59:38 +0100 Subject: [PATCH 2/5] deprecate `--configPath` --- .changeset/lucky-places-occur.md | 5 +++ packages/cloudflare/src/cli/commands/utils.ts | 35 ++++++++++++------- 2 files changed, 28 insertions(+), 12 deletions(-) create mode 100644 .changeset/lucky-places-occur.md diff --git a/.changeset/lucky-places-occur.md b/.changeset/lucky-places-occur.md new file mode 100644 index 00000000..ab821f73 --- /dev/null +++ b/.changeset/lucky-places-occur.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +fix: deprecate usage of the `--configPath` flag for the Wrangler config, in favour of the `--config` flag. diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils.ts index e39b6341..c2184e9e 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -16,6 +16,7 @@ export type WithWranglerArgs = T & { // Array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. wranglerArgs: string[]; configPath: string | undefined; + config: string | undefined; env: string | undefined; }; @@ -97,12 +98,17 @@ export function readWranglerConfig(args: WithWranglerArgs) { */ export function withWranglerOptions(args: T) { return args - .options("configPath", { + .option("config", { type: "string", - alias: ["config", "c"], + alias: "c", desc: "Path to Wrangler configuration file", }) - .options("env", { + .option("configPath", { + type: "string", + desc: "Path to Wrangler configuration file", + deprecated: true, + }) + .option("env", { type: "string", alias: "e", desc: "Wrangler environment to use for operations", @@ -114,13 +120,21 @@ export function withWranglerOptions(args: T) { * @param args * @returns An array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. */ -function getWranglerArgs(args: { - _: (string | number)[]; - configPath: string | undefined; - env: string | undefined; -}): string[] { +function getWranglerArgs(args: Omit, "wranglerArgs">): string[] { + if (args.configPath) { + logger.warn("The `--configPath` flag is deprecated, please use `--config` instead."); + + if (args.config) { + logger.error( + "Multiple config flags found. Please use the `--config` flag for your Wrangler config path." + ); + process.exit(1); + } + } + return [ ...(args.configPath ? ["--config", args.configPath] : []), + ...(args.config ? ["--config", args.config] : []), ...(args.env ? ["--env", args.env] : []), // Note: the first args in `_` will be the commands. ...args._.slice(args._[0] === "populateCache" ? 2 : 1).map((a) => `${a}`), @@ -133,10 +147,7 @@ function getWranglerArgs(args: { * @returns The inputted args, and an array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. */ export function withWranglerPassthroughArgs< - T extends yargs.ArgumentsCamelCase<{ - configPath: string | undefined; - env: string | undefined; - }>, + T extends yargs.ArgumentsCamelCase>, >(args: T) { return { ...args, wranglerArgs: getWranglerArgs(args) }; } From b7e76ae1163c7799e2dd8447616ec5a71f255c5b Mon Sep 17 00:00:00 2001 From: James Date: Wed, 3 Sep 2025 09:02:45 +0100 Subject: [PATCH 3/5] rename internal var to wranglerConfigPath --- .../cloudflare/src/cli/commands/deploy.ts | 4 +-- .../src/cli/commands/populate-cache.ts | 10 ++++---- .../cloudflare/src/cli/commands/preview.ts | 2 +- .../cloudflare/src/cli/commands/upload.ts | 4 +-- packages/cloudflare/src/cli/commands/utils.ts | 25 +++++++++++++------ 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/packages/cloudflare/src/cli/commands/deploy.ts b/packages/cloudflare/src/cli/commands/deploy.ts index 29625926..033336c6 100644 --- a/packages/cloudflare/src/cli/commands/deploy.ts +++ b/packages/cloudflare/src/cli/commands/deploy.ts @@ -28,7 +28,7 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize: num const wranglerConfig = readWranglerConfig(args); const envVars = await getEnvFromPlatformProxy({ - configPath: args.configPath, + configPath: args.wranglerConfigPath, environment: args.env, }); @@ -37,7 +37,7 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize: num await populateCache(options, config, wranglerConfig, { target: "remote", environment: args.env, - configPath: args.configPath, + wranglerConfigPath: args.wranglerConfigPath, cacheChunkSize: args.cacheChunkSize, }); diff --git a/packages/cloudflare/src/cli/commands/populate-cache.ts b/packages/cloudflare/src/cli/commands/populate-cache.ts index 416e02f5..f4fcd110 100644 --- a/packages/cloudflare/src/cli/commands/populate-cache.ts +++ b/packages/cloudflare/src/cli/commands/populate-cache.ts @@ -107,7 +107,7 @@ export function getCacheAssets(opts: BuildOptions): CacheAsset[] { type PopulateCacheOptions = { target: WranglerTarget; environment?: string; - configPath?: string; + wranglerConfigPath?: string; cacheChunkSize?: number; }; @@ -148,7 +148,7 @@ async function populateR2IncrementalCache( ], { target: populateCacheOptions.target, - configPath: populateCacheOptions.configPath, + configPath: populateCacheOptions.wranglerConfigPath, // R2 does not support the environment flag and results in the following error: // Incorrect type for the 'cacheExpiry' field on 'HttpMetadata': the provided value is not of type 'date'. environment: undefined, @@ -200,7 +200,7 @@ async function populateKVIncrementalCache( runWrangler(options, ["kv bulk put", quoteShellMeta(chunkPath), `--binding ${KV_CACHE_BINDING_NAME}`], { target: populateCacheOptions.target, environment: populateCacheOptions.environment, - configPath: populateCacheOptions.configPath, + configPath: populateCacheOptions.wranglerConfigPath, logging: "error", }); @@ -232,7 +232,7 @@ function populateD1TagCache( { target: populateCacheOptions.target, environment: populateCacheOptions.environment, - configPath: populateCacheOptions.configPath, + configPath: populateCacheOptions.wranglerConfigPath, logging: "error", } ); @@ -313,7 +313,7 @@ async function populateCacheCommand( await populateCache(options, config, wranglerConfig, { target, environment: args.env, - configPath: args.configPath, + wranglerConfigPath: args.wranglerConfigPath, cacheChunkSize: args.cacheChunkSize, }); } diff --git a/packages/cloudflare/src/cli/commands/preview.ts b/packages/cloudflare/src/cli/commands/preview.ts index 50cd8010..fbcc44ee 100644 --- a/packages/cloudflare/src/cli/commands/preview.ts +++ b/packages/cloudflare/src/cli/commands/preview.ts @@ -27,7 +27,7 @@ export async function previewCommand(args: WithWranglerArgs<{ cacheChunkSize: nu await populateCache(options, config, wranglerConfig, { target: "local", environment: args.env, - configPath: args.configPath, + wranglerConfigPath: args.wranglerConfigPath, cacheChunkSize: args.cacheChunkSize, }); diff --git a/packages/cloudflare/src/cli/commands/upload.ts b/packages/cloudflare/src/cli/commands/upload.ts index 51c799ae..8c35fb92 100644 --- a/packages/cloudflare/src/cli/commands/upload.ts +++ b/packages/cloudflare/src/cli/commands/upload.ts @@ -28,7 +28,7 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize: num const wranglerConfig = readWranglerConfig(args); const envVars = await getEnvFromPlatformProxy({ - configPath: args.configPath, + configPath: args.wranglerConfigPath, environment: args.env, }); @@ -37,7 +37,7 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize: num await populateCache(options, config, wranglerConfig, { target: "remote", environment: args.env, - configPath: args.configPath, + wranglerConfigPath: args.wranglerConfigPath, cacheChunkSize: args.cacheChunkSize, }); diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils.ts index c2184e9e..f4a51c3f 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -15,8 +15,7 @@ import { createOpenNextConfigIfNotExistent, ensureCloudflareConfig } from "../bu export type WithWranglerArgs = T & { // Array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. wranglerArgs: string[]; - configPath: string | undefined; - config: string | undefined; + wranglerConfigPath: string | undefined; env: string | undefined; }; @@ -90,7 +89,7 @@ export function getNormalizedOptions(config: OpenNextConfig, buildDir = nextAppD * @returns Wrangler config. */ export function readWranglerConfig(args: WithWranglerArgs) { - return unstable_readConfig({ env: args.env, config: args.configPath }); + return unstable_readConfig({ env: args.env, config: args.wranglerConfigPath }); } /** @@ -115,12 +114,18 @@ export function withWranglerOptions(args: T) { }); } +type WranglerInputArgs = { + configPath: string | undefined; + config: string | undefined; + env: string | undefined; +}; + /** * * @param args * @returns An array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. */ -function getWranglerArgs(args: Omit, "wranglerArgs">): string[] { +function getWranglerArgs(args: WranglerInputArgs & { _: (string | number)[] }): string[] { if (args.configPath) { logger.warn("The `--configPath` flag is deprecated, please use `--config` instead."); @@ -146,8 +151,12 @@ function getWranglerArgs(args: Omit * @param args * @returns The inputted args, and an array of arguments that can be given to wrangler commands, including the `--config` and `--env` args. */ -export function withWranglerPassthroughArgs< - T extends yargs.ArgumentsCamelCase>, ->(args: T) { - return { ...args, wranglerArgs: getWranglerArgs(args) }; +export function withWranglerPassthroughArgs>( + args: T +): WithWranglerArgs { + return { + ...args, + wranglerConfigPath: args.config ?? args.configPath, + wranglerArgs: getWranglerArgs(args), + }; } From 4c798da20b809da7ab6230b99c3276e036033ace Mon Sep 17 00:00:00 2001 From: Victor Berchet Date: Thu, 4 Sep 2025 21:47:18 +0200 Subject: [PATCH 4/5] fixup! proposal --- .changeset/lucky-places-occur.md | 2 +- .../cli/build/utils/create-config-files.ts | 17 +++++++-------- packages/cloudflare/src/cli/commands/build.ts | 5 ++--- packages/cloudflare/src/cli/commands/utils.ts | 21 +++++++++++++++---- 4 files changed, 28 insertions(+), 17 deletions(-) diff --git a/.changeset/lucky-places-occur.md b/.changeset/lucky-places-occur.md index ab821f73..12821981 100644 --- a/.changeset/lucky-places-occur.md +++ b/.changeset/lucky-places-occur.md @@ -2,4 +2,4 @@ "@opennextjs/cloudflare": patch --- -fix: deprecate usage of the `--configPath` flag for the Wrangler config, in favour of the `--config` flag. +refactor: deprecate usage of the `--configPath` flag for the Wrangler config, in favour of the `--config` flag. diff --git a/packages/cloudflare/src/cli/build/utils/create-config-files.ts b/packages/cloudflare/src/cli/build/utils/create-config-files.ts index 9b8777ca..8089207e 100644 --- a/packages/cloudflare/src/cli/build/utils/create-config-files.ts +++ b/packages/cloudflare/src/cli/build/utils/create-config-files.ts @@ -88,28 +88,27 @@ export async function getLatestCompatDate(): Promise { } /** - * Creates a config file for the user if it doesn't exist, but only after asking for the user's confirmation. + * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation. * * If the user refuses an error is thrown (since the file is mandatory). * - * @param configPath The path to the config file, relative to the source directory * @param sourceDir The source directory for the project + * @return The path to the created source file */ -export async function createOpenNextConfigIfNotExistent( - configPath: string, - sourceDir: string -): Promise { - const openNextConfigPath = join(sourceDir, configPath); +export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise { + const openNextConfigPath = join(sourceDir, "open-next.config.ts"); if (!existsSync(openNextConfigPath)) { const answer = await askConfirmation( - `Missing required \`${configPath}\` file, do you want to create one?` + "Missing required `open-next.config.ts` file, do you want to create one?" ); if (!answer) { - throw new Error(`The \`${configPath}\` file is required, aborting!`); + throw new Error("The `open-next.config.ts` file is required, aborting!"); } cpSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), openNextConfigPath); } + + return openNextConfigPath; } diff --git a/packages/cloudflare/src/cli/commands/build.ts b/packages/cloudflare/src/cli/commands/build.ts index 3e38fcc4..4b8dea6b 100644 --- a/packages/cloudflare/src/cli/commands/build.ts +++ b/packages/cloudflare/src/cli/commands/build.ts @@ -22,7 +22,7 @@ async function buildCommand( skipNextBuild: boolean; noMinify: boolean; skipWranglerConfigCheck: boolean; - openNextConfigPath: string; + openNextConfigPath: string | undefined; }> ): Promise { printHeaders("build"); @@ -69,8 +69,7 @@ export function addBuildCommand(y: T) { }) .option("openNextConfigPath", { type: "string", - default: "open-next.config.ts", - desc: "Path to OpenNext configuration file, relative to the source directory", + desc: "Path to the OpenNext configuration file", }), (args) => buildCommand(withWranglerPassthroughArgs(args)) ); diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils.ts index f4a51c3f..1f4e4839 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -33,14 +33,27 @@ export function printHeaders(command: string) { } /** - * Compile the OpenNext config, and ensure it is for Cloudflare. + * Compile the OpenNext config. * + * When users do not specify a custom config file (using `----openNextConfigPath`), + * the CLI will offer to create one. + * + * When users specify a custom config file but it doesn't exist, we throw an Error. + * + * @param configPath Optional path to the config file. Absolute or relative to cwd. * @returns OpenNext config. */ -export async function compileConfig(configPath: string) { - await createOpenNextConfigIfNotExistent(configPath, nextAppDir); +export async function compileConfig(configPath: string | undefined) { + if (configPath && !existsSync(configPath)) { + throw new Error(`Custom config file not found at ${configPath}`); + } + + if (!configPath) { + configPath = await createOpenNextConfigIfNotExistent(nextAppDir); + } - const { config, buildDir } = await compileOpenNextConfig(nextAppDir, configPath, { compileEdge: true }); + // TODO: remove the hack passing the `configPath` as the `baseDir` when https://github.com/opennextjs/opennextjs-aws/pull/972 is merged + const { config, buildDir } = await compileOpenNextConfig(configPath, "", { compileEdge: true }); ensureCloudflareConfig(config); return { config, buildDir }; From 2da636d10f7303dedb5c0aeee66e6049442087ef Mon Sep 17 00:00:00 2001 From: James Anderson Date: Fri, 5 Sep 2025 11:33:15 +0100 Subject: [PATCH 5/5] Update packages/cloudflare/src/cli/commands/utils.ts --- packages/cloudflare/src/cli/commands/utils.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cloudflare/src/cli/commands/utils.ts b/packages/cloudflare/src/cli/commands/utils.ts index 1f4e4839..903191a0 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -35,7 +35,7 @@ export function printHeaders(command: string) { /** * Compile the OpenNext config. * - * When users do not specify a custom config file (using `----openNextConfigPath`), + * When users do not specify a custom config file (using `--openNextConfigPath`), * the CLI will offer to create one. * * When users specify a custom config file but it doesn't exist, we throw an Error.