Skip to content

Commit 4b6a50b

Browse files
create a wrangler.toml file for the user in case one is not already present (#220)
create a wrangler config file for the user in case one is not already present (alongside ways to opt out of this)
1 parent 7654867 commit 4b6a50b

File tree

8 files changed

+147
-4
lines changed

8 files changed

+147
-4
lines changed

.changeset/lazy-balloons-report.md

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@opennextjs/cloudflare": patch
3+
---
4+
5+
check and create a `wrangler.jsonc` file for the user in case a `wrangler.(toml|json|jsonc)` file is not already present
6+
7+
also introduce a new `--skipWranglerConfigCheck` cli flag and a `SKIP_WRANGLER_CONFIG_CHECK`
8+
environment variable that allows users to opt out of the above check (since developers might
9+
want to use custom locations for their config files)

.prettierrc

+10-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,14 @@
44
"semi": true,
55
"useTabs": false,
66
"tabWidth": 2,
7-
"trailingComma": "es5"
7+
"trailingComma": "es5",
8+
"overrides": [
9+
{
10+
"// comment": "wrangler doesn't seem to accept wrangler.jsonc with trailing commas",
11+
"files": ["**/wrangler.jsonc"],
12+
"options": {
13+
"trailingComma": "none"
14+
}
15+
}
16+
]
817
}

packages/cloudflare/env.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ declare global {
33
interface ProcessEnv {
44
__NEXT_PRIVATE_STANDALONE_CONFIG?: string;
55
SKIP_NEXT_APP_BUILD?: string;
6+
SKIP_WRANGLER_CONFIG_CHECK?: string;
67
NEXT_PRIVATE_DEBUG_CACHE?: string;
78
OPEN_NEXT_ORIGIN: string;
89
NODE_ENV?: string;

packages/cloudflare/src/cli/args.ts

+9-1
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,11 @@ import { parseArgs } from "node:util";
44

55
export function getArgs(): {
66
skipNextBuild: boolean;
7+
skipWranglerConfigCheck: boolean;
78
outputDir?: string;
89
minify: boolean;
910
} {
10-
const { skipBuild, output, noMinify } = parseArgs({
11+
const { skipBuild, skipWranglerConfigCheck, output, noMinify } = parseArgs({
1112
options: {
1213
skipBuild: {
1314
type: "boolean",
@@ -22,6 +23,10 @@ export function getArgs(): {
2223
type: "boolean",
2324
default: false,
2425
},
26+
skipWranglerConfigCheck: {
27+
type: "boolean",
28+
default: false,
29+
},
2530
},
2631
allowPositionals: false,
2732
}).values;
@@ -35,6 +40,9 @@ export function getArgs(): {
3540
return {
3641
outputDir,
3742
skipNextBuild: skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
43+
skipWranglerConfigCheck:
44+
skipWranglerConfigCheck ||
45+
["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)),
3846
minify: !noMinify,
3947
};
4048
}

packages/cloudflare/src/cli/build/index.ts

+96-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { cpSync, existsSync } from "node:fs";
1+
import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
22
import { createRequire } from "node:module";
33
import { dirname, join } from "node:path";
44

@@ -101,6 +101,10 @@ export async function build(projectOpts: ProjectOptions): Promise<void> {
101101
// TODO: rely on options only.
102102
await bundleServer(projConfig, options);
103103

104+
if (!projectOpts.skipWranglerConfigCheck) {
105+
await createWranglerConfigIfNotExistent(projectOpts);
106+
}
107+
104108
logger.info("OpenNext build complete.");
105109
}
106110

@@ -178,3 +182,94 @@ function ensureCloudflareConfig(config: OpenNextConfig) {
178182
);
179183
}
180184
}
185+
186+
/**
187+
* Creates a `wrangler.jsonc` file for the user if a wrangler config file doesn't already exist,
188+
* but only after asking for the user's confirmation.
189+
*
190+
* If the user refuses a warning is shown (which offers ways to opt out of this check to the user).
191+
*
192+
* @param projectOpts The options for the project
193+
*/
194+
async function createWranglerConfigIfNotExistent(projectOpts: ProjectOptions): Promise<void> {
195+
const possibleExts = ["toml", "json", "jsonc"];
196+
197+
const wranglerConfigFileExists = possibleExts.some((ext) =>
198+
existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`))
199+
);
200+
if (wranglerConfigFileExists) {
201+
return;
202+
}
203+
204+
const wranglerConfigPath = join(projectOpts.sourceDir, "wrangler.jsonc");
205+
206+
const answer = await askConfirmation(
207+
"No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?"
208+
);
209+
210+
if (!answer) {
211+
console.warn(
212+
"No Wrangler config file created" +
213+
"\n" +
214+
"(to avoid this check use the `--skipWranglerConfigCheck` flag or set a `SKIP_WRANGLER_CONFIG_CHECK` environment variable to `yes`)"
215+
);
216+
return;
217+
}
218+
219+
const wranglerConfigTemplate = readFileSync(
220+
join(getPackageTemplatesDirPath(), "defaults", "wrangler.jsonc"),
221+
"utf8"
222+
);
223+
let wranglerConfigContent = wranglerConfigTemplate;
224+
225+
const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name";
226+
if (appName) {
227+
wranglerConfigContent = wranglerConfigContent.replace(
228+
'"app-name"',
229+
JSON.stringify(appName.replaceAll("_", "-"))
230+
);
231+
}
232+
233+
const compatDate = await getLatestCompatDate();
234+
if (compatDate) {
235+
wranglerConfigContent = wranglerConfigContent.replace(
236+
/"compatibility_date": "\d{4}-\d{2}-\d{2}"/,
237+
`"compatibility_date": ${JSON.stringify(compatDate)}`
238+
);
239+
}
240+
241+
writeFileSync(wranglerConfigPath, wranglerConfigContent);
242+
}
243+
244+
function getAppNameFromPackageJson(sourceDir: string): string | undefined {
245+
try {
246+
const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8");
247+
const packageJson: Record<string, string> = JSON.parse(packageJsonStr);
248+
if (typeof packageJson.name === "string") return packageJson.name;
249+
} catch {
250+
/* empty */
251+
}
252+
}
253+
254+
export async function getLatestCompatDate(): Promise<string | undefined> {
255+
try {
256+
const resp = await fetch(`https://registry.npmjs.org/workerd`);
257+
const latestWorkerdVersion = (
258+
(await resp.json()) as {
259+
"dist-tags": { latest: string };
260+
}
261+
)["dist-tags"].latest;
262+
263+
// The format of the workerd version is `major.yyyymmdd.patch`.
264+
const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/);
265+
266+
if (match) {
267+
const [, year, month, date] = match;
268+
const compatDate = `${year}-${month}-${date}`;
269+
270+
return compatDate;
271+
}
272+
} catch {
273+
/* empty */
274+
}
275+
}

packages/cloudflare/src/cli/config.ts

+2
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,8 @@ export type ProjectOptions = {
114114
outputDir: string;
115115
// Whether the Next.js build should be skipped (i.e. if the `.next` dir is already built)
116116
skipNextBuild: boolean;
117+
// Whether the check to see if a wrangler config file exists should be skipped
118+
skipWranglerConfigCheck: boolean;
117119
// Whether minification of the worker should be enabled
118120
minify: boolean;
119121
};

packages/cloudflare/src/cli/index.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,12 @@ import { build } from "./build/index.js";
66

77
const nextAppDir = process.cwd();
88

9-
const { skipNextBuild, outputDir, minify } = getArgs();
9+
const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify } = getArgs();
1010

1111
await build({
1212
sourceDir: nextAppDir,
1313
outputDir: resolve(outputDir ?? nextAppDir, ".open-next"),
1414
skipNextBuild,
15+
skipWranglerConfigCheck,
1516
minify,
1617
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"main": ".open-next/worker.js",
3+
"name": "app-name",
4+
"compatibility_date": "2024-12-30",
5+
"compatibility_flags": ["nodejs_compat"],
6+
"assets": {
7+
"directory": ".open-next/assets",
8+
"binding": "ASSETS"
9+
},
10+
"kv_namespaces": [
11+
// Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV"
12+
// to enable the KV based caching:
13+
// {
14+
// "binding": "NEXT_CACHE_WORKERS_KV",
15+
// "id": "<BINDING_ID>"
16+
// }
17+
]
18+
}

0 commit comments

Comments
 (0)