Skip to content

Commit

Permalink
create a wrangler.toml file for the user in case one is not already p…
Browse files Browse the repository at this point in the history
…resent (#220)

create a wrangler config file for the user in case one is not already present (alongside ways to opt out of this)
  • Loading branch information
dario-piotrowicz authored Jan 2, 2025
1 parent 7654867 commit 4b6a50b
Show file tree
Hide file tree
Showing 8 changed files with 147 additions and 4 deletions.
9 changes: 9 additions & 0 deletions .changeset/lazy-balloons-report.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
"@opennextjs/cloudflare": patch
---

check and create a `wrangler.jsonc` file for the user in case a `wrangler.(toml|json|jsonc)` file is not already present

also introduce a new `--skipWranglerConfigCheck` cli flag and a `SKIP_WRANGLER_CONFIG_CHECK`
environment variable that allows users to opt out of the above check (since developers might
want to use custom locations for their config files)
11 changes: 10 additions & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,14 @@
"semi": true,
"useTabs": false,
"tabWidth": 2,
"trailingComma": "es5"
"trailingComma": "es5",
"overrides": [
{
"// comment": "wrangler doesn't seem to accept wrangler.jsonc with trailing commas",
"files": ["**/wrangler.jsonc"],
"options": {
"trailingComma": "none"
}
}
]
}
1 change: 1 addition & 0 deletions packages/cloudflare/env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ declare global {
interface ProcessEnv {
__NEXT_PRIVATE_STANDALONE_CONFIG?: string;
SKIP_NEXT_APP_BUILD?: string;
SKIP_WRANGLER_CONFIG_CHECK?: string;
NEXT_PRIVATE_DEBUG_CACHE?: string;
OPEN_NEXT_ORIGIN: string;
NODE_ENV?: string;
Expand Down
10 changes: 9 additions & 1 deletion packages/cloudflare/src/cli/args.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { parseArgs } from "node:util";

export function getArgs(): {
skipNextBuild: boolean;
skipWranglerConfigCheck: boolean;
outputDir?: string;
minify: boolean;
} {
const { skipBuild, output, noMinify } = parseArgs({
const { skipBuild, skipWranglerConfigCheck, output, noMinify } = parseArgs({
options: {
skipBuild: {
type: "boolean",
Expand All @@ -22,6 +23,10 @@ export function getArgs(): {
type: "boolean",
default: false,
},
skipWranglerConfigCheck: {
type: "boolean",
default: false,
},
},
allowPositionals: false,
}).values;
Expand All @@ -35,6 +40,9 @@ export function getArgs(): {
return {
outputDir,
skipNextBuild: skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
skipWranglerConfigCheck:
skipWranglerConfigCheck ||
["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)),
minify: !noMinify,
};
}
Expand Down
97 changes: 96 additions & 1 deletion packages/cloudflare/src/cli/build/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { cpSync, existsSync } from "node:fs";
import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
import { createRequire } from "node:module";
import { dirname, join } from "node:path";

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

if (!projectOpts.skipWranglerConfigCheck) {
await createWranglerConfigIfNotExistent(projectOpts);
}

logger.info("OpenNext build complete.");
}

Expand Down Expand Up @@ -178,3 +182,94 @@ function ensureCloudflareConfig(config: OpenNextConfig) {
);
}
}

/**
* Creates a `wrangler.jsonc` file for the user if a wrangler config file doesn't already exist,
* but only after asking for the user's confirmation.
*
* If the user refuses a warning is shown (which offers ways to opt out of this check to the user).
*
* @param projectOpts The options for the project
*/
async function createWranglerConfigIfNotExistent(projectOpts: ProjectOptions): Promise<void> {
const possibleExts = ["toml", "json", "jsonc"];

const wranglerConfigFileExists = possibleExts.some((ext) =>
existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`))
);
if (wranglerConfigFileExists) {
return;
}

const wranglerConfigPath = join(projectOpts.sourceDir, "wrangler.jsonc");

const answer = await askConfirmation(
"No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?"
);

if (!answer) {
console.warn(
"No Wrangler config file created" +
"\n" +
"(to avoid this check use the `--skipWranglerConfigCheck` flag or set a `SKIP_WRANGLER_CONFIG_CHECK` environment variable to `yes`)"
);
return;
}

const wranglerConfigTemplate = readFileSync(
join(getPackageTemplatesDirPath(), "defaults", "wrangler.jsonc"),
"utf8"
);
let wranglerConfigContent = wranglerConfigTemplate;

const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name";
if (appName) {
wranglerConfigContent = wranglerConfigContent.replace(
'"app-name"',
JSON.stringify(appName.replaceAll("_", "-"))
);
}

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

writeFileSync(wranglerConfigPath, wranglerConfigContent);
}

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 */
}
}
2 changes: 2 additions & 0 deletions packages/cloudflare/src/cli/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,8 @@ export type ProjectOptions = {
outputDir: string;
// Whether the Next.js build should be skipped (i.e. if the `.next` dir is already built)
skipNextBuild: boolean;
// Whether the check to see if a wrangler config file exists should be skipped
skipWranglerConfigCheck: boolean;
// Whether minification of the worker should be enabled
minify: boolean;
};
Expand Down
3 changes: 2 additions & 1 deletion packages/cloudflare/src/cli/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ import { build } from "./build/index.js";

const nextAppDir = process.cwd();

const { skipNextBuild, outputDir, minify } = getArgs();
const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify } = getArgs();

await build({
sourceDir: nextAppDir,
outputDir: resolve(outputDir ?? nextAppDir, ".open-next"),
skipNextBuild,
skipWranglerConfigCheck,
minify,
});
18 changes: 18 additions & 0 deletions packages/cloudflare/templates/defaults/wrangler.jsonc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{

This comment has been minimized.

Copy link
@vicb

vicb Jan 4, 2025

Contributor

Can this file be wrangler.json instead?

"main": ".open-next/worker.js",
"name": "app-name",
"compatibility_date": "2024-12-30",
"compatibility_flags": ["nodejs_compat"],
"assets": {
"directory": ".open-next/assets",
"binding": "ASSETS"
},
"kv_namespaces": [
// Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV"
// to enable the KV based caching:
// {
// "binding": "NEXT_CACHE_WORKERS_KV",
// "id": "<BINDING_ID>"
// }
]
}

0 comments on commit 4b6a50b

Please sign in to comment.