diff --git a/.changeset/lucky-places-occur.md b/.changeset/lucky-places-occur.md new file mode 100644 index 00000000..12821981 --- /dev/null +++ b/.changeset/lucky-places-occur.md @@ -0,0 +1,5 @@ +--- +"@opennextjs/cloudflare": patch +--- + +refactor: deprecate usage of the `--configPath` flag for the Wrangler config, in favour of the `--config` flag. 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..8089207e 100644 --- a/packages/cloudflare/src/cli/build/utils/create-config-files.ts +++ b/packages/cloudflare/src/cli/build/utils/create-config-files.ts @@ -93,8 +93,9 @@ export async function getLatestCompatDate(): Promise { * If the user refuses an error is thrown (since the file is mandatory). * * @param sourceDir The source directory for the project + * @return The path to the created source file */ -export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise { +export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise { const openNextConfigPath = join(sourceDir, "open-next.config.ts"); if (!existsSync(openNextConfigPath)) { @@ -108,4 +109,6 @@ export async function createOpenNextConfigIfNotExistent(sourceDir: string): Prom 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 efb035d0..4b8dea6b 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 | undefined; }> ): 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,10 @@ 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", + desc: "Path to the OpenNext configuration file", }), (args) => buildCommand(withWranglerPassthroughArgs(args)) ); 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 ae6afb4d..903191a0 100644 --- a/packages/cloudflare/src/cli/commands/utils.ts +++ b/packages/cloudflare/src/cli/commands/utils.ts @@ -15,7 +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; + wranglerConfigPath: string | undefined; env: string | undefined; }; @@ -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() { - await createOpenNextConfigIfNotExistent(nextAppDir); +export async function compileConfig(configPath: string | undefined) { + if (configPath && !existsSync(configPath)) { + throw new Error(`Custom config file not found at ${configPath}`); + } - const { config, buildDir } = await compileOpenNextConfig(nextAppDir, undefined, { compileEdge: true }); + if (!configPath) { + configPath = await createOpenNextConfigIfNotExistent(nextAppDir); + } + + // 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 }; @@ -89,7 +102,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 }); } /** @@ -97,30 +110,49 @@ export function readWranglerConfig(args: WithWranglerArgs) { */ export function withWranglerOptions(args: T) { return args - .options("configPath", { + .option("config", { + type: "string", + alias: "c", + desc: "Path to Wrangler configuration file", + }) + .option("configPath", { type: "string", - alias: ["config", "c"], desc: "Path to Wrangler configuration file", + deprecated: true, }) - .options("env", { + .option("env", { type: "string", alias: "e", desc: "Wrangler environment to use for operations", }); } +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: { - _: (string | number)[]; - configPath: string | undefined; - env: string | undefined; -}): string[] { +function getWranglerArgs(args: WranglerInputArgs & { _: (string | number)[] }): 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}`), @@ -132,11 +164,12 @@ function getWranglerArgs(args: { * @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<{ - configPath: string | undefined; - env: string | undefined; - }>, ->(args: T) { - return { ...args, wranglerArgs: getWranglerArgs(args) }; +export function withWranglerPassthroughArgs>( + args: T +): WithWranglerArgs { + return { + ...args, + wranglerConfigPath: args.config ?? args.configPath, + wranglerArgs: getWranglerArgs(args), + }; }