Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
826401e
Update the `migrate` command to attempt to create an R2 bucket for ca…
dario-piotrowicz Feb 15, 2026
763d1bb
address comments
dario-piotrowicz Feb 18, 2026
5145bc6
remove incorrect todo comment
dario-piotrowicz Feb 18, 2026
c80f21f
use --json
dario-piotrowicz Feb 18, 2026
760a46c
add --json fallback
dario-piotrowicz Feb 18, 2026
a5fbba6
avoid "already exists" check
dario-piotrowicz Feb 18, 2026
1e58888
Apply suggestions from code review
dario-piotrowicz Feb 19, 2026
f208098
Apply suggestions from code review
dario-piotrowicz Feb 19, 2026
f76b735
Update packages/cloudflare/src/cli/utils/open-next-config.ts
dario-piotrowicz Feb 19, 2026
156d0ce
Update packages/cloudflare/src/cli/utils/open-next-config.ts
dario-piotrowicz Feb 19, 2026
8594106
revert types improvement
dario-piotrowicz Feb 19, 2026
84d8b77
Revert "revert types improvement"
dario-piotrowicz Feb 19, 2026
3f73978
remove duplicated open-next config
dario-piotrowicz Feb 19, 2026
c8cc1e9
remove prompt and rely on --json
dario-piotrowicz Feb 19, 2026
29fd7ce
use Cloudflare sdk to create bucket
dario-piotrowicz Feb 19, 2026
9dd13eb
add PackagerOptions type
dario-piotrowicz Feb 19, 2026
3216c2d
use jsonc parser
dario-piotrowicz Feb 19, 2026
766993a
Apply suggestions from code review
dario-piotrowicz Feb 19, 2026
b094ed1
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
3f2030d
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
ef94a52
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
5035c7f
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
43b7c56
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
3a6ac99
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
ee9ebd0
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
7dc4395
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
2b402cd
Update packages/cloudflare/src/cli/utils/wrangler-config.ts
dario-piotrowicz Feb 19, 2026
e95da20
Update packages/cloudflare/src/cli/utils/run-wrangler.ts
dario-piotrowicz Feb 19, 2026
6f8712f
fix broken types
dario-piotrowicz Feb 20, 2026
b5096e4
use ast-grep
dario-piotrowicz Feb 20, 2026
ccc612c
include CF_ACCOUNT_ID as well
dario-piotrowicz Feb 20, 2026
216a36f
update comment
dario-piotrowicz Feb 20, 2026
745768b
use --json for wrangler auth token
dario-piotrowicz Feb 20, 2026
db1e169
use comment-js
dario-piotrowicz Feb 20, 2026
46bdcc7
add account selection
dario-piotrowicz Feb 20, 2026
f069a97
update `logger.error`
dario-piotrowicz Feb 20, 2026
0726a5d
reshuffle utils
vicb Feb 23, 2026
addb5d3
add JSDoc
vicb Feb 23, 2026
e74c6ad
Add tests for createOpenNextConfigFile
vicb Feb 23, 2026
bdaffe6
Update createOpenNextConfigFile
vicb Feb 23, 2026
7de29d6
Add JSDoc to runWrangler
vicb Feb 23, 2026
3467f6a
Add tests for createWranglerConfigFile
vicb Feb 23, 2026
1736cb9
Simplify createWranglerConfigFile
vicb Feb 23, 2026
aadba7d
Add JSDOc
vicb Feb 23, 2026
f477ce9
Merge branch 'main' into dario/DEVX-2491/r2-migrate-caching
vicb Feb 23, 2026
478cee9
tweak wrangler config creation
vicb Feb 23, 2026
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/bright-colts-march.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@opennextjs/cloudflare": minor
---

Update the `migrate` command to attempt to create an R2 bucket for caching as part of the migration process, if that is not possible an application without caching enabled will be generated instead.
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ export async function createOpenNextConfigIfNotExistent(sourceDir: string): Prom
throw new Error("The `open-next.config.ts` file is required, aborting!");
}

return createOpenNextConfigFile(sourceDir);
return createOpenNextConfigFile(sourceDir, false);
}

return openNextConfigPath;
Expand Down
8 changes: 7 additions & 1 deletion packages/cloudflare/src/cli/commands/deploy.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logger from "@opennextjs/aws/logger.js";
import type yargs from "yargs";

import { DEPLOYMENT_MAPPING_ENV_NAME } from "../templates/skew-protection.js";
Expand Down Expand Up @@ -45,7 +46,7 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu

const deploymentMapping = await getDeploymentMapping(buildOpts, config, envVars);

runWrangler(
const result = runWrangler(
buildOpts,
[
"deploy",
Expand All @@ -65,6 +66,11 @@ export async function deployCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu
},
}
);

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}
}

/**
Expand Down
18 changes: 14 additions & 4 deletions packages/cloudflare/src/cli/commands/migrate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,17 @@ async function migrateCommand(args: { forceInstall: boolean }): Promise<void> {
}

printStepTitle("Creating wrangler.jsonc");
await createWranglerConfigFile("./");
const { cachingEnabled } = await createWranglerConfigFile("./");

if (!cachingEnabled) {
logger.warn(
`Failed to set up a caching solution for your project.\n` +
`After the migration completes, please manually create add a caching solution to your wrangler.jsonc and open-next.config.ts files (for more details see: https://opennext.js.org/cloudflare/caching).\n`
);
}

printStepTitle("Creating open-next.config.ts");
await createOpenNextConfigFile("./");
await createOpenNextConfigFile("./", !cachingEnabled);

const devVarsExists = fs.existsSync(".dev.vars");
printStepTitle(`${devVarsExists ? "Updating" : "Creating"} .dev.vars file`);
Expand Down Expand Up @@ -197,7 +204,10 @@ async function migrateCommand(args: { forceInstall: boolean }): Promise<void> {
"🎉 OpenNext Cloudflare adapter complete!\n" +
"\nNext steps:\n" +
`- Run: "${packageManager.run} preview" to build and preview your Cloudflare application locally\n` +
`- Run: "${packageManager.run} deploy" to deploy your application to Cloudflare Workers\n`
`- Run: "${packageManager.run} deploy" to deploy your application to Cloudflare Workers\n` +
(cachingEnabled
? ""
: `- ⚠️ Add caching solution, see https://opennext.js.org/cloudflare/caching for more details\n`)
);
}

Expand All @@ -209,7 +219,7 @@ interface PackageManager {
}

const packageManagers = {
pnpm: { name: "pnpm", install: "pnpm add", installDev: "pnpm add -D", run: "pnpm" },
pnpm: { name: "pnpm", install: "pnpm add", installDev: "pnpm add -D", run: "pnpm run" },
npm: { name: "npm", install: "npm install", installDev: "npm install --save-dev", run: "npm run" },
bun: { name: "bun", install: "bun add", installDev: "bun add -D", run: "bun" },
yarn: { name: "yarn", install: "yarn add", installDev: "yarn add -D", run: "yarn" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe("getCacheAssets", () => {
});

vi.mock("../utils/run-wrangler.js", () => ({
runWrangler: vi.fn(),
runWrangler: vi.fn(() => ({ success: true, stdout: "", stderr: "" })),
}));

vi.mock("./helpers.js", () => ({
Expand Down
21 changes: 18 additions & 3 deletions packages/cloudflare/src/cli/commands/populate-cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ async function populateR2IncrementalCache(
const concurrency = Math.max(1, populateCacheOptions.cacheChunkSize ?? 50);
const jurisdiction = binding.jurisdiction ? `--jurisdiction ${binding.jurisdiction}` : "";

runWrangler(
const result = runWrangler(
buildOpts,
[
"r2 bulk put",
Expand All @@ -268,6 +268,11 @@ async function populateR2IncrementalCache(

fs.rmSync(listFile, { force: true });

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}

logger.info(`Successfully populated cache with ${assets.length} assets`);
}

Expand Down Expand Up @@ -313,7 +318,7 @@ async function populateKVIncrementalCache(

fs.writeFileSync(chunkPath, JSON.stringify(kvMapping));

runWrangler(
const result = runWrangler(
buildOpts,
[
"kv bulk put",
Expand All @@ -330,6 +335,11 @@ async function populateKVIncrementalCache(
);

fs.rmSync(chunkPath, { force: true });

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}
}

logger.info(`Successfully populated cache with ${assets.length} assets`);
Expand All @@ -349,7 +359,7 @@ function populateD1TagCache(
throw new Error(`No D1 binding ${JSON.stringify(D1_TAG_BINDING_NAME)} found!`);
}

runWrangler(
const result = runWrangler(
buildOpts,
[
"d1 execute",
Expand All @@ -365,6 +375,11 @@ function populateD1TagCache(
}
);

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}

logger.info("\nSuccessfully created D1 table");
}

Expand Down
8 changes: 7 additions & 1 deletion packages/cloudflare/src/cli/commands/preview.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logger from "@opennextjs/aws/logger.js";
import type yargs from "yargs";

import { runWrangler } from "../utils/run-wrangler.js";
Expand Down Expand Up @@ -42,7 +43,12 @@ export async function previewCommand(
envVars
);

runWrangler(buildOpts, ["dev", ...args.wranglerArgs], { logging: "all" });
const result = runWrangler(buildOpts, ["dev", ...args.wranglerArgs], { logging: "all" });

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}
}

/**
Expand Down
8 changes: 7 additions & 1 deletion packages/cloudflare/src/cli/commands/upload.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import logger from "@opennextjs/aws/logger.js";
import type yargs from "yargs";

import { DEPLOYMENT_MAPPING_ENV_NAME } from "../templates/skew-protection.js";
Expand Down Expand Up @@ -51,7 +52,7 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu
envVars
);

runWrangler(
const result = runWrangler(
buildOpts,
[
"versions upload",
Expand All @@ -62,6 +63,11 @@ export async function uploadCommand(args: WithWranglerArgs<{ cacheChunkSize?: nu
],
{ logging: "all" }
);

if (!result.success) {
logger.error("Wrangler command failed");
process.exit(1);
}
}

/**
Expand Down
14 changes: 9 additions & 5 deletions packages/cloudflare/src/cli/utils/open-next-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,19 @@ export function findOpenNextConfig(appDir: string): string | undefined {
}

/**
* Creates a `open-next.config.ts` file in the target directory for the project.
* Creates an `open-next.config.ts` file in the target directory by copying
* the appropriate template file from the package templates.
*
* @param appDir The Next application root
* @return The path to the created source file
* @param appDir The Next.js application root directory
* @param noCache Flag indicating whether to not to set up caching
* @returns The full path to the created configuration file
*/
export async function createOpenNextConfigFile(appDir: string): Promise<string> {
export async function createOpenNextConfigFile(appDir: string, noCache: boolean): Promise<string> {
const openNextConfigPath = join(appDir, "open-next.config.ts");

cpSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), openNextConfigPath);
const templateFileToUse = noCache ? "open-next.config.no-cache.ts" : "open-next.config.ts";

cpSync(join(getPackageTemplatesDirPath(), templateFileToUse), openNextConfigPath);

return openNextConfigPath;
}
52 changes: 40 additions & 12 deletions packages/cloudflare/src/cli/utils/run-wrangler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@ import path from "node:path";

import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
import { compareSemver } from "@opennextjs/aws/build/helper.js";
import logger from "@opennextjs/aws/logger.js";

export type WranglerTarget = "local" | "remote";

export type WranglerCommandResult = {
success: boolean;
stdout: string;
stderr: string;
};

type WranglerOptions = {
target?: WranglerTarget;
environment?: string;
configPath?: string;
logging?: "all" | "error";
logging?: "all" | "error" | "none";
env?: Record<string, string>;
};

Expand All @@ -22,7 +27,7 @@ type WranglerOptions = {
* @param options Build options.
* @returns Whether yarn modern is used.
*/
function isYarnModern(options: BuildOptions) {
function isYarnModern(options: Pick<BuildOptions, "monorepoRoot">) {
const packageJson: { packageManager?: string } = JSON.parse(
readFileSync(path.join(options.monorepoRoot, "package.json"), "utf-8")
);
Expand All @@ -43,7 +48,10 @@ function isYarnModern(options: BuildOptions) {
* @param args CLI args.
* @returns Arguments with a passthrough flag injected when needed.
*/
function injectPassthroughFlagForArgs(options: BuildOptions, args: string[]) {
function injectPassthroughFlagForArgs(
options: Pick<BuildOptions, "packager" | "monorepoRoot">,
args: string[]
) {
if (options.packager !== "npm" && (options.packager !== "yarn" || isYarnModern(options))) {
return args;
}
Expand All @@ -56,7 +64,12 @@ function injectPassthroughFlagForArgs(options: BuildOptions, args: string[]) {
return args;
}

export function runWrangler(options: BuildOptions, args: string[], wranglerOpts: WranglerOptions = {}) {
export function runWrangler(
options: Pick<BuildOptions, "packager" | "monorepoRoot">,
args: string[],
wranglerOpts: WranglerOptions = {}
): WranglerCommandResult {
const noLogs = wranglerOpts.logging === "none";
const shouldPipeLogs = wranglerOpts.logging === "error";

const result = spawnSync(
Expand All @@ -77,7 +90,10 @@ export function runWrangler(options: BuildOptions, args: string[], wranglerOpts:
],
{
shell: true,
stdio: shouldPipeLogs ? ["ignore", "pipe", "pipe"] : "inherit",
// Always pipe stderr so that we can capture it for inspection.
// Keep stdin and stdout as "inherit" when not piping logs to maintain TTY detection
// (wrangler checks `process.stdin.isTTY && process.stdout.isTTY` for interactive mode).
stdio: shouldPipeLogs || noLogs ? ["ignore", "pipe", "pipe"] : ["inherit", "inherit", "pipe"],
env: {
...process.env,
...wranglerOpts.env,
Expand All @@ -89,15 +105,27 @@ export function runWrangler(options: BuildOptions, args: string[], wranglerOpts:
}
);

if (result.status !== 0) {
if (shouldPipeLogs) {
process.stdout.write(result.stdout.toString());
process.stderr.write(result.stderr.toString());
const success = result.status === 0;
const stdout = result.stdout?.toString() ?? "";
const stderr = result.stderr?.toString() ?? "";

if (!noLogs) {
// When not piping logs, stderr is captured but should still be visible to the user
if (!shouldPipeLogs && stderr) {
process.stderr.write(stderr);
}

logger.error("Wrangler command failed");
process.exit(1);
if (!success && shouldPipeLogs) {
process.stdout.write(stdout);
process.stderr.write(stderr);
}
}

return {
success,
stdout,
stderr,
};
}

export function isWranglerTarget(v: string | undefined): v is WranglerTarget {
Expand Down
Loading
Loading