Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 commits
Select commit Hold shift + click to select a range
a16a300
feat: add migrate command to set up OpenNext.js for Cloudflare
2u841r Nov 24, 2025
80f4543
Rename command from `migrate` to `init` and fix various small issues
dario-piotrowicz Jan 19, 2026
39cd5bc
reuse existing logic to create wrangler.jsonc file
dario-piotrowicz Jan 19, 2026
4d6e7ff
reuse existing logic to create open-next.config.ts file
dario-piotrowicz Jan 20, 2026
356f2d8
remove verbose success logs
dario-piotrowicz Jan 20, 2026
045792a
add missing newline
dario-piotrowicz Jan 20, 2026
1c9c081
fixup! remove verbose success logs
dario-piotrowicz Jan 20, 2026
da82cae
fix gitignore logic
dario-piotrowicz Jan 20, 2026
bdd77f7
add \n
dario-piotrowicz Jan 20, 2026
caefc06
remove \n
dario-piotrowicz Jan 20, 2026
9f256bf
bump version of `@opennextjs/aws`
dario-piotrowicz Jan 21, 2026
5429a7a
automatically select package manager
dario-piotrowicz Jan 21, 2026
573e021
add runStep utility to make code more clean and easy to read
dario-piotrowicz Jan 21, 2026
60d9893
improve next-steps logs
dario-piotrowicz Jan 21, 2026
cdc1506
copy _headers from teamplates directory instead of inlining its conte…
dario-piotrowicz Jan 21, 2026
fcf7ff4
undo unintentional pnpm-lock.yaml update
dario-piotrowicz Jan 22, 2026
ed3444d
Update .changeset/add-init-command.md
dario-piotrowicz Jan 22, 2026
71828d4
fix wrangler template path for _headers
dario-piotrowicz Jan 22, 2026
85017ce
Update .changeset/add-init-command.md
dario-piotrowicz Jan 22, 2026
b53403c
Update packages/cloudflare/src/cli/commands/init.ts
dario-piotrowicz Jan 22, 2026
38d3653
Update packages/cloudflare/src/cli/commands/init.ts
dario-piotrowicz Jan 22, 2026
8f277a0
Update packages/cloudflare/src/cli/commands/init.ts
dario-piotrowicz Jan 22, 2026
ebcad59
rename `projectDir` to `appDir`
dario-piotrowicz Jan 22, 2026
ce00acd
convert `runStep` to `printStepTitle`
dario-piotrowicz Jan 22, 2026
f67c023
replace `console` calls with `logger`
dario-piotrowicz Jan 22, 2026
320903f
move `initCommand` function to the top of the file
dario-piotrowicz Jan 22, 2026
4bf9d58
remove unnecessary comments
dario-piotrowicz Jan 22, 2026
b8121c2
remove manual win32 check
dario-piotrowicz Jan 22, 2026
a23fca4
add JSDoc comment
dario-piotrowicz Jan 22, 2026
163fd56
improve packageJson scripts updating logic
dario-piotrowicz Jan 22, 2026
3f573dc
Update init command text
dario-piotrowicz Jan 22, 2026
e956d60
move wrangler detection logic
dario-piotrowicz Jan 22, 2026
d5e31e2
move `getOpenNextConfigPath` logic
dario-piotrowicz Jan 22, 2026
f5c877e
unify logger.warn calls
dario-piotrowicz Jan 22, 2026
c5a5273
remove unnecessary public check
dario-piotrowicz Jan 22, 2026
2fdda8f
simplify `logger.warn` calls (since they already have a WARN prefix
dario-piotrowicz Jan 23, 2026
842aee5
simplify `logger.error` calls (since they already have an ERROR prefix)
dario-piotrowicz Jan 23, 2026
ce49feb
improve warn messages for edge runtime detection
dario-piotrowicz Jan 23, 2026
840e504
remove slice operation
dario-piotrowicz Jan 23, 2026
9cec15b
add .mts extension
dario-piotrowicz Jan 23, 2026
c4e9ad9
make open-next and wrangler utilities consistent
dario-piotrowicz Jan 23, 2026
dbf053f
rename `createWranglerConfigIfNotExistent` to `createWranglerConfigIf…
dario-piotrowicz Jan 23, 2026
a0b6276
improve Next.js config file detection and gate init command behind it
dario-piotrowicz Jan 23, 2026
5d025d4
remove unnecessary open-next.config.ts exist check
dario-piotrowicz Jan 23, 2026
783f676
use appensFileSync for gitignore editing
dario-piotrowicz Jan 23, 2026
337ef54
update wrong logic in `getWranglerConfigPath`
dario-piotrowicz Jan 23, 2026
f889080
fail command if wrangler config file exists
dario-piotrowicz Jan 23, 2026
90d3ad4
remove outdated code comment
dario-piotrowicz Jan 23, 2026
5e50198
make code cleaner via `createOrAppendToFile` utility
dario-piotrowicz Jan 23, 2026
471cf6f
rename `init` command back to `migrate`
dario-piotrowicz Jan 23, 2026
69cd139
rename `createOpenNextConfig` to `createOpenNextConfigFile` for consi…
dario-piotrowicz Jan 23, 2026
80d0552
use `createOrAppendToFile` function for `_headers` file update
dario-piotrowicz Jan 24, 2026
54cce24
update changeset
dario-piotrowicz Jan 24, 2026
4d52ded
improve path argument
dario-piotrowicz Jan 24, 2026
d52aca4
solve TODO comment
dario-piotrowicz Jan 24, 2026
2e3648a
Apply suggestions from code review
dario-piotrowicz Jan 24, 2026
622dff5
add adapter
dario-piotrowicz Jan 24, 2026
02fb34f
update `createOrAppendToFile` to `conditionalAppendFileSync`
dario-piotrowicz Jan 24, 2026
b01a9b6
unify `logger.info`
dario-piotrowicz Jan 24, 2026
3a6d1c2
add comment about already-existent wrangler.jsonc
dario-piotrowicz Jan 24, 2026
ade604a
avoid `join`
dario-piotrowicz Jan 24, 2026
22bf2c7
update checks of `confitionalAppendFileSync`
dario-piotrowicz Jan 24, 2026
d5882de
remove existence check in `getOpenNextConfigPath`
dario-piotrowicz Jan 24, 2026
dab3a26
Apply suggestions from code review
dario-piotrowicz Jan 26, 2026
8958582
update `getXConfigPath` functions to `findXConfig`
dario-piotrowicz Jan 26, 2026
f39b272
Update packages/cloudflare/src/cli/commands/migrate.ts
dario-piotrowicz Jan 26, 2026
10a3872
use childProcess
dario-piotrowicz Jan 26, 2026
3bb2cde
fix formatting
dario-piotrowicz Jan 26, 2026
35796e9
remove `\n`s from `conditionalAppendFileSync`
dario-piotrowicz Jan 26, 2026
04f331e
use utils from aws prerelease and remove no longer needed code
dario-piotrowicz Jan 26, 2026
b5066ac
Update packages/cloudflare/src/cli/commands/migrate.ts
dario-piotrowicz Jan 26, 2026
050665c
Update .changeset/add-init-command.md
dario-piotrowicz Jan 26, 2026
bbb295d
adapter...
dario-piotrowicz Jan 26, 2026
17bc01c
add `--force-install` flag
dario-piotrowicz Jan 26, 2026
608b497
fix casing
dario-piotrowicz Jan 26, 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
9 changes: 9 additions & 0 deletions .changeset/add-init-command.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@opennextjs/cloudflare": minor
---

feature: add `migrate` command to set up OpenNext for Cloudflare adapter

This command helps users migrate existing Next.js applications to the OpenNext Cloudflare adapter by automatically setting up all necessary configuration files, dependencies, and scripts.

To use the command simply run: `npx opennextjs-cloudflare migrate`
70 changes: 8 additions & 62 deletions packages/cloudflare/src/cli/build/utils/create-config-files.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
import { join } from "node:path";

import { getPackageTemplatesDirPath } from "../../../utils/get-package-templates-dir-path.js";
import type { ProjectOptions } from "../../project-options.js";
import { askConfirmation } from "../../utils/ask-confirmation.js";
import { createOpenNextConfigFile, findOpenNextConfig } from "../../utils/open-next-config.js";
import { createWranglerConfigFile, findWranglerConfig } from "../../utils/wrangler-config.js";

/**
* Creates a `wrangler.jsonc` file for the user if a wrangler config file doesn't already exist,
Expand All @@ -13,12 +11,8 @@ import { askConfirmation } from "../../utils/ask-confirmation.js";
*
* @param projectOpts The options for the project
*/
export async function createWranglerConfigIfNotExistent(projectOpts: ProjectOptions): Promise<void> {
const possibleExts = ["toml", "json", "jsonc"];

const wranglerConfigFileExists = possibleExts.some((ext) =>
existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`))
);
export async function createWranglerConfigIfNonExistent(projectOpts: ProjectOptions): Promise<void> {
const wranglerConfigFileExists = Boolean(findWranglerConfig(projectOpts.sourceDir));
if (wranglerConfigFileExists) {
return;
}
Expand All @@ -36,54 +30,7 @@ export async function createWranglerConfigIfNotExistent(projectOpts: ProjectOpti
return;
}

let wranglerConfig = readFileSync(join(getPackageTemplatesDirPath(), "wrangler.jsonc"), "utf8");

const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name";

wranglerConfig = wranglerConfig.replaceAll('"<WORKER_NAME>"', JSON.stringify(appName.replaceAll("_", "-")));

const compatDate = await getLatestCompatDate();
if (compatDate) {
wranglerConfig = wranglerConfig.replace(
/"compatibility_date": "\d{4}-\d{2}-\d{2}"/,
`"compatibility_date": ${JSON.stringify(compatDate)}`
);
}

writeFileSync(join(projectOpts.sourceDir, "wrangler.jsonc"), wranglerConfig);
}

function getAppNameFromPackageJson(sourceDir: string): string | undefined {
try {
const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8");
const packageJson: Record<string, string> = JSON.parse(packageJsonStr);
if (typeof packageJson.name === "string") return packageJson.name;
} catch {
/* empty */
}
}

export async function getLatestCompatDate(): Promise<string | undefined> {
try {
const resp = await fetch(`https://registry.npmjs.org/workerd`);
const latestWorkerdVersion = (
(await resp.json()) as {
"dist-tags": { latest: string };
}
)["dist-tags"].latest;

// The format of the workerd version is `major.yyyymmdd.patch`.
const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/);

if (match) {
const [, year, month, date] = match;
const compatDate = `${year}-${month}-${date}`;

return compatDate;
}
} catch {
/* empty */
}
await createWranglerConfigFile(projectOpts.sourceDir);
}

/**
Expand All @@ -95,9 +42,8 @@ export async function getLatestCompatDate(): Promise<string | undefined> {
* @return The path to the created source file
*/
export async function createOpenNextConfigIfNotExistent(sourceDir: string): Promise<string> {
const openNextConfigPath = join(sourceDir, "open-next.config.ts");

if (!existsSync(openNextConfigPath)) {
const openNextConfigPath = findOpenNextConfig(sourceDir);
if (!openNextConfigPath) {
const answer = await askConfirmation(
"Missing required `open-next.config.ts` file, do you want to create one?"
);
Expand All @@ -106,7 +52,7 @@ export async function createOpenNextConfigIfNotExistent(sourceDir: string): Prom
throw new Error("The `open-next.config.ts` file is required, aborting!");
}

cpSync(join(getPackageTemplatesDirPath(), "open-next.config.ts"), openNextConfigPath);
return createOpenNextConfigFile(sourceDir);
}

return openNextConfigPath;
Expand Down
23 changes: 23 additions & 0 deletions packages/cloudflare/src/cli/build/utils/files.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import fs from "node:fs";

/**
* Appends text to a file
*
* When the file does not exists, it is always created with the text content.
* When the file exists, the text is appended only when the predicate return `true`.
*
* @param filepath The path to the file.
* @param text The text to append to the file.
* @param condition A function that receives the current file content and returns `true` if the text should be appended to it, the condition is skipped when the file is being created.
*/
export function conditionalAppendFileSync(
filepath: string,
text: string,
condition: (fileContent: string) => boolean
): void {
const fileExists = fs.existsSync(filepath);

if (!fileExists || condition(fs.readFileSync(filepath, "utf8"))) {
fs.appendFileSync(filepath, text);
}
}
4 changes: 2 additions & 2 deletions packages/cloudflare/src/cli/commands/build.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import type yargs from "yargs";

import { build as buildImpl } from "../build/build.js";
import { createWranglerConfigIfNotExistent } from "../build/utils/index.js";
import { createWranglerConfigIfNonExistent } from "../build/utils/index.js";
import type { WithWranglerArgs } from "./utils.js";
import {
compileConfig,
Expand Down Expand Up @@ -38,7 +38,7 @@ async function buildCommand(
// Note: We don't ask when a custom config file is specified via `--config`
// nor when `--skipWranglerConfigCheck` is used.
if (!projectOpts.wranglerConfigPath && !args.skipWranglerConfigCheck) {
await createWranglerConfigIfNotExistent(projectOpts);
await createWranglerConfigIfNonExistent(projectOpts);
}

const wranglerConfig = await readWranglerConfig(args);
Expand Down
Loading
Loading