@opennextjs/cloudflare 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,4 +7,3 @@ import type { ProjectOptions } from "../config.js";
7
7
  * @param projectOpts The options for the project
8
8
  */
9
9
  export declare function build(projectOpts: ProjectOptions): Promise<void>;
10
- export declare function getLatestCompatDate(): Promise<string | undefined>;
@@ -0,0 +1,89 @@
1
+ import { cpSync } from "node:fs";
2
+ import { createRequire } from "node:module";
3
+ import { dirname, join } from "node:path";
4
+ import { buildNextjsApp, setStandaloneBuildMode } from "@opennextjs/aws/build/buildNextApp.js";
5
+ import { compileCache } from "@opennextjs/aws/build/compileCache.js";
6
+ import { compileOpenNextConfig } from "@opennextjs/aws/build/compileConfig.js";
7
+ import { createCacheAssets, createStaticAssets } from "@opennextjs/aws/build/createAssets.js";
8
+ import { createMiddleware } from "@opennextjs/aws/build/createMiddleware.js";
9
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
10
+ import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
11
+ import logger from "@opennextjs/aws/logger.js";
12
+ import { containsDotNextDir, getConfig } from "../config.js";
13
+ import { bundleServer } from "./bundle-server.js";
14
+ import { compileEnvFiles } from "./open-next/compile-env-files.js";
15
+ import { copyCacheAssets } from "./open-next/copyCacheAssets.js";
16
+ import { createServerBundle } from "./open-next/createServerBundle.js";
17
+ import { createOpenNextConfigIfNotExistent, createWranglerConfigIfNotExistent, ensureCloudflareConfig, } from "./utils/index.js";
18
+ /**
19
+ * Builds the application in a format that can be passed to workerd
20
+ *
21
+ * It saves the output in a `.worker-next` directory
22
+ *
23
+ * @param projectOpts The options for the project
24
+ */
25
+ export async function build(projectOpts) {
26
+ printHeader("Cloudflare build");
27
+ showWarningOnWindows();
28
+ const baseDir = projectOpts.sourceDir;
29
+ const require = createRequire(import.meta.url);
30
+ const openNextDistDir = dirname(require.resolve("@opennextjs/aws/index.js"));
31
+ await createOpenNextConfigIfNotExistent(projectOpts);
32
+ const { config, buildDir } = await compileOpenNextConfig(baseDir);
33
+ ensureCloudflareConfig(config);
34
+ // Initialize options
35
+ const options = buildHelper.normalizeOptions(config, openNextDistDir, buildDir);
36
+ logger.setLevel(options.debug ? "debug" : "info");
37
+ // Do not minify the code so that we can apply string replacement patch.
38
+ // Note that wrangler will still minify the bundle.
39
+ options.minify = false;
40
+ // Pre-build validation
41
+ buildHelper.checkRunningInsideNextjsApp(options);
42
+ logger.info(`App directory: ${options.appPath}`);
43
+ buildHelper.printNextjsVersion(options);
44
+ ensureNextjsVersionSupported(options);
45
+ buildHelper.printOpenNextVersion(options);
46
+ if (projectOpts.skipNextBuild) {
47
+ logger.warn("Skipping Next.js build");
48
+ }
49
+ else {
50
+ // Build the next app
51
+ printHeader("Building Next.js app");
52
+ setStandaloneBuildMode(options);
53
+ buildNextjsApp(options);
54
+ }
55
+ if (!containsDotNextDir(projectOpts.sourceDir)) {
56
+ throw new Error(`.next folder not found in ${projectOpts.sourceDir}`);
57
+ }
58
+ // Generate deployable bundle
59
+ printHeader("Generating bundle");
60
+ buildHelper.initOutputDir(options);
61
+ // Compile cache.ts
62
+ compileCache(options);
63
+ // Compile .env files
64
+ compileEnvFiles(options);
65
+ // Compile middleware
66
+ await createMiddleware(options, { forceOnlyBuildOnce: true });
67
+ createStaticAssets(options);
68
+ if (config.dangerous?.disableIncrementalCache !== true) {
69
+ createCacheAssets(options);
70
+ copyCacheAssets(options);
71
+ }
72
+ await createServerBundle(options);
73
+ // TODO: drop this copy.
74
+ // Copy the .next directory to the output directory so it can be mutated.
75
+ cpSync(join(projectOpts.sourceDir, ".next"), join(projectOpts.outputDir, ".next"), { recursive: true });
76
+ const projConfig = getConfig(projectOpts);
77
+ // TODO: rely on options only.
78
+ await bundleServer(projConfig, options);
79
+ if (!projectOpts.skipWranglerConfigCheck) {
80
+ await createWranglerConfigIfNotExistent(projectOpts);
81
+ }
82
+ logger.info("OpenNext build complete.");
83
+ }
84
+ function ensureNextjsVersionSupported(options) {
85
+ if (buildHelper.compareSemver(options.nextVersion, "14.0.0") < 0) {
86
+ logger.error("Next.js version unsupported, please upgrade to version 14 or greater.");
87
+ process.exit(1);
88
+ }
89
+ }
@@ -4,7 +4,7 @@ import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { build } from "esbuild";
6
6
  import * as patches from "./patches/index.js";
7
- import { normalizePath } from "./utils/index.js";
7
+ import { normalizePath, patchCodeWithValidations } from "./utils/index.js";
8
8
  /** The dist directory of the Cloudflare adapter package */
9
9
  const packageDistDir = path.join(path.dirname(fileURLToPath(import.meta.url)), "../..");
10
10
  /**
@@ -162,29 +162,6 @@ function createFixRequiresESBuildPlugin(config) {
162
162
  },
163
163
  };
164
164
  }
165
- /**
166
- * Applies multiple code patches in order to a given piece of code, at each step it validates that the code
167
- * has actually been patched/changed, if not an error is thrown
168
- *
169
- * @param code the code to apply the patches to
170
- * @param patches array of tuples, containing a string indicating the target of the patching (for logging) and
171
- * a patching function that takes a string (pre-patch code) and returns a string (post-patch code)
172
- * @returns the patched code
173
- */
174
- async function patchCodeWithValidations(code, patches) {
175
- console.log(`Applying code patches:`);
176
- let patchedCode = code;
177
- for (const [target, patchFunction, opts] of patches) {
178
- console.log(` - patching ${target}`);
179
- const prePatchCode = patchedCode;
180
- patchedCode = await patchFunction(patchedCode);
181
- if (!opts?.isOptional && prePatchCode === patchedCode) {
182
- throw new Error(`Failed to patch ${target}`);
183
- }
184
- }
185
- console.log(`All ${patches.length} patches applied\n`);
186
- return patchedCode;
187
- }
188
165
  /**
189
166
  * Gets the path of the worker.js file generated by the build process
190
167
  *
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Applies multiple code patches in order to a given piece of code, at each step it validates that the code
3
+ * has actually been patched/changed, if not an error is thrown
4
+ *
5
+ * @param code the code to apply the patches to
6
+ * @param patches array of tuples, containing a string indicating the target of the patching (for logging) and
7
+ * a patching function that takes a string (pre-patch code) and returns a string (post-patch code)
8
+ * @returns the patched code
9
+ */
10
+ export declare function patchCodeWithValidations(code: string, patches: [string, (code: string) => string | Promise<string>, opts?: {
11
+ isOptional?: boolean;
12
+ }][]): Promise<string>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Applies multiple code patches in order to a given piece of code, at each step it validates that the code
3
+ * has actually been patched/changed, if not an error is thrown
4
+ *
5
+ * @param code the code to apply the patches to
6
+ * @param patches array of tuples, containing a string indicating the target of the patching (for logging) and
7
+ * a patching function that takes a string (pre-patch code) and returns a string (post-patch code)
8
+ * @returns the patched code
9
+ */
10
+ export async function patchCodeWithValidations(code, patches) {
11
+ console.log(`Applying code patches:`);
12
+ let patchedCode = code;
13
+ for (const [target, patchFunction, opts] of patches) {
14
+ console.log(` - patching ${target}`);
15
+ const prePatchCode = patchedCode;
16
+ patchedCode = await patchFunction(patchedCode);
17
+ if (!opts?.isOptional && prePatchCode === patchedCode) {
18
+ throw new Error(`Failed to patch ${target}`);
19
+ }
20
+ }
21
+ console.log(`All ${patches.length} patches applied\n`);
22
+ return patchedCode;
23
+ }
@@ -0,0 +1,24 @@
1
+ import type { ProjectOptions } from "../../config.js";
2
+ /**
3
+ * Creates a `wrangler.json` file for the user if a wrangler config file doesn't already exist,
4
+ * but only after asking for the user's confirmation.
5
+ *
6
+ * If the user refuses a warning is shown (which offers ways to opt out of this check to the user).
7
+ *
8
+ * Note: we generate a wrangler.json file with comments instead of using the jsonc extension,
9
+ * we decided to do that since json is more common than jsonc, wrangler also parses
10
+ * them in the same way and we also expect developers to associate `wrangler.json`
11
+ * files to the jsonc language
12
+ *
13
+ * @param projectOpts The options for the project
14
+ */
15
+ export declare function createWranglerConfigIfNotExistent(projectOpts: ProjectOptions): Promise<void>;
16
+ export declare function getLatestCompatDate(): Promise<string | undefined>;
17
+ /**
18
+ * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation.
19
+ *
20
+ * If the user refuses an error is thrown (since the file is mandatory).
21
+ *
22
+ * @param projectOpts The options for the project
23
+ */
24
+ export declare function createOpenNextConfigIfNotExistent(projectOpts: ProjectOptions): Promise<void>;
@@ -0,0 +1,85 @@
1
+ import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { getPackageTemplatesDirPath } from "../../../utils/get-package-templates-dir-path.js";
4
+ import { askConfirmation } from "../../utils/ask-confirmation.js";
5
+ /**
6
+ * Creates a `wrangler.json` file for the user if a wrangler config file doesn't already exist,
7
+ * but only after asking for the user's confirmation.
8
+ *
9
+ * If the user refuses a warning is shown (which offers ways to opt out of this check to the user).
10
+ *
11
+ * Note: we generate a wrangler.json file with comments instead of using the jsonc extension,
12
+ * we decided to do that since json is more common than jsonc, wrangler also parses
13
+ * them in the same way and we also expect developers to associate `wrangler.json`
14
+ * files to the jsonc language
15
+ *
16
+ * @param projectOpts The options for the project
17
+ */
18
+ export async function createWranglerConfigIfNotExistent(projectOpts) {
19
+ const possibleExts = ["toml", "json", "jsonc"];
20
+ const wranglerConfigFileExists = possibleExts.some((ext) => existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`)));
21
+ if (wranglerConfigFileExists) {
22
+ return;
23
+ }
24
+ const answer = await askConfirmation("No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?");
25
+ if (!answer) {
26
+ console.warn("No Wrangler config file created" +
27
+ "\n" +
28
+ "(to avoid this check use the `--skipWranglerConfigCheck` flag or set a `SKIP_WRANGLER_CONFIG_CHECK` environment variable to `yes`)");
29
+ return;
30
+ }
31
+ let wranglerConfig = readFileSync(join(getPackageTemplatesDirPath(), "defaults", "wrangler.json"), "utf8");
32
+ const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name";
33
+ if (appName) {
34
+ wranglerConfig = wranglerConfig.replace('"app-name"', JSON.stringify(appName.replaceAll("_", "-")));
35
+ }
36
+ const compatDate = await getLatestCompatDate();
37
+ if (compatDate) {
38
+ wranglerConfig = wranglerConfig.replace(/"compatibility_date": "\d{4}-\d{2}-\d{2}"/, `"compatibility_date": ${JSON.stringify(compatDate)}`);
39
+ }
40
+ writeFileSync(join(projectOpts.sourceDir, "wrangler.json"), wranglerConfig);
41
+ }
42
+ function getAppNameFromPackageJson(sourceDir) {
43
+ try {
44
+ const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8");
45
+ const packageJson = JSON.parse(packageJsonStr);
46
+ if (typeof packageJson.name === "string")
47
+ return packageJson.name;
48
+ }
49
+ catch {
50
+ /* empty */
51
+ }
52
+ }
53
+ export async function getLatestCompatDate() {
54
+ try {
55
+ const resp = await fetch(`https://registry.npmjs.org/workerd`);
56
+ const latestWorkerdVersion = (await resp.json())["dist-tags"].latest;
57
+ // The format of the workerd version is `major.yyyymmdd.patch`.
58
+ const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/);
59
+ if (match) {
60
+ const [, year, month, date] = match;
61
+ const compatDate = `${year}-${month}-${date}`;
62
+ return compatDate;
63
+ }
64
+ }
65
+ catch {
66
+ /* empty */
67
+ }
68
+ }
69
+ /**
70
+ * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation.
71
+ *
72
+ * If the user refuses an error is thrown (since the file is mandatory).
73
+ *
74
+ * @param projectOpts The options for the project
75
+ */
76
+ export async function createOpenNextConfigIfNotExistent(projectOpts) {
77
+ const openNextConfigPath = join(projectOpts.sourceDir, "open-next.config.ts");
78
+ if (!existsSync(openNextConfigPath)) {
79
+ const answer = await askConfirmation("Missing required `open-next.config.ts` file, do you want to create one?");
80
+ if (!answer) {
81
+ throw new Error("The `open-next.config.ts` file is required, aborting!");
82
+ }
83
+ cpSync(join(getPackageTemplatesDirPath(), "defaults", "open-next.config.ts"), openNextConfigPath);
84
+ }
85
+ }
@@ -0,0 +1,7 @@
1
+ import type { OpenNextConfig } from "@opennextjs/aws/types/open-next.js";
2
+ /**
3
+ * Ensures open next is configured for cloudflare.
4
+ *
5
+ * @param config OpenNext configuration.
6
+ */
7
+ export declare function ensureCloudflareConfig(config: OpenNextConfig): void;
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Ensures open next is configured for cloudflare.
3
+ *
4
+ * @param config OpenNext configuration.
5
+ */
6
+ export function ensureCloudflareConfig(config) {
7
+ const requirements = {
8
+ dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
9
+ dftUseEdgeConverter: config.default?.override?.converter === "edge",
10
+ dftMaybeUseCache: config.default?.override?.incrementalCache === "dummy" ||
11
+ typeof config.default?.override?.incrementalCache === "function",
12
+ dftUseDummyTagCacheAndQueue: config.default?.override?.tagCache === "dummy" && config.default?.override?.queue === "dummy",
13
+ disableCacheInterception: config.dangerous?.enableCacheInterception !== true,
14
+ mwIsMiddlewareExternal: config.middleware?.external == true,
15
+ mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
16
+ mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
17
+ mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
18
+ };
19
+ if (Object.values(requirements).some((satisfied) => !satisfied)) {
20
+ throw new Error("The `open-next.config.ts` should have a default export like this:\n\n" +
21
+ `{
22
+ default: {
23
+ override: {
24
+ wrapper: "cloudflare-node",
25
+ converter: "edge",
26
+ incrementalCache: "dummy" | function,
27
+ tagCache: "dummy",
28
+ queue: "dummy",
29
+ },
30
+ },
31
+
32
+ middleware: {
33
+ external: true,
34
+ override: {
35
+ wrapper: "cloudflare-edge",
36
+ converter: "edge",
37
+ proxyExternalRequest: "fetch",
38
+ },
39
+ },
40
+
41
+ "dangerous": {
42
+ "enableCacheInterception": false
43
+ },
44
+ }\n\n`.replace(/^ {8}/gm, ""));
45
+ }
46
+ }
@@ -1,7 +1,7 @@
1
1
  import { appendFileSync, writeFileSync } from "node:fs";
2
2
  import mockFs from "mock-fs";
3
3
  import { afterEach, beforeEach, describe, expect, it } from "vitest";
4
- import { extractProjectEnvVars } from "./extract-project-env-vars";
4
+ import { extractProjectEnvVars } from "./extract-project-env-vars.js";
5
5
  const options = { monorepoRoot: "", appPath: "" };
6
6
  describe("extractProjectEnvVars", () => {
7
7
  beforeEach(() => {
@@ -1,3 +1,6 @@
1
+ export * from "./apply-patches.js";
2
+ export * from "./create-config-files.js";
3
+ export * from "./ensure-cf-config.js";
1
4
  export * from "./extract-project-env-vars.js";
2
5
  export * from "./normalize-path.js";
3
6
  export * from "./ts-parse-file.js";
@@ -1,3 +1,6 @@
1
+ export * from "./apply-patches.js";
2
+ export * from "./create-config-files.js";
3
+ export * from "./ensure-cf-config.js";
1
4
  export * from "./extract-project-env-vars.js";
2
5
  export * from "./normalize-path.js";
3
6
  export * from "./ts-parse-file.js";
package/dist/cli/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { resolve } from "node:path";
3
3
  import { getArgs } from "./args.js";
4
- import { build } from "./build/index.js";
4
+ import { build } from "./build/build.js";
5
5
  const nextAppDir = process.cwd();
6
6
  const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify } = getArgs();
7
7
  await build({
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@opennextjs/cloudflare",
3
3
  "description": "Cloudflare builder for next apps",
4
- "version": "0.3.8",
4
+ "version": "0.3.9",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"
@@ -42,7 +42,7 @@
42
42
  },
43
43
  "homepage": "https://github.com/opennextjs/opennextjs-cloudflare",
44
44
  "devDependencies": {
45
- "@cloudflare/workers-types": "^4.20240925.0",
45
+ "@cloudflare/workers-types": "^4.20241230.0",
46
46
  "@eslint/js": "^9.11.1",
47
47
  "@tsconfig/strictest": "^2.0.5",
48
48
  "@types/mock-fs": "^4.13.4",
@@ -62,13 +62,13 @@
62
62
  },
63
63
  "dependencies": {
64
64
  "@dotenvx/dotenvx": "1.31.0",
65
- "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@695",
65
+ "@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@704",
66
66
  "glob": "^11.0.0",
67
67
  "ts-morph": "^23.0.0",
68
68
  "enquirer": "^2.4.1"
69
69
  },
70
70
  "peerDependencies": {
71
- "wrangler": "^3.99.0"
71
+ "wrangler": "^3.101.0"
72
72
  },
73
73
  "scripts": {
74
74
  "clean": "rimraf dist",
@@ -1,218 +0,0 @@
1
- import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
2
- import { createRequire } from "node:module";
3
- import { dirname, join } from "node:path";
4
- import { buildNextjsApp, setStandaloneBuildMode } from "@opennextjs/aws/build/buildNextApp.js";
5
- import { compileCache } from "@opennextjs/aws/build/compileCache.js";
6
- import { compileOpenNextConfig } from "@opennextjs/aws/build/compileConfig.js";
7
- import { createCacheAssets, createStaticAssets } from "@opennextjs/aws/build/createAssets.js";
8
- import { createMiddleware } from "@opennextjs/aws/build/createMiddleware.js";
9
- import * as buildHelper from "@opennextjs/aws/build/helper.js";
10
- import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
11
- import logger from "@opennextjs/aws/logger.js";
12
- import { getPackageTemplatesDirPath } from "../../utils/get-package-templates-dir-path.js";
13
- import { containsDotNextDir, getConfig } from "../config.js";
14
- import { askConfirmation } from "../utils/ask-confirmation.js";
15
- import { bundleServer } from "./bundle-server.js";
16
- import { compileEnvFiles } from "./open-next/compile-env-files.js";
17
- import { copyCacheAssets } from "./open-next/copyCacheAssets.js";
18
- import { createServerBundle } from "./open-next/createServerBundle.js";
19
- /**
20
- * Builds the application in a format that can be passed to workerd
21
- *
22
- * It saves the output in a `.worker-next` directory
23
- *
24
- * @param projectOpts The options for the project
25
- */
26
- export async function build(projectOpts) {
27
- printHeader("Cloudflare build");
28
- showWarningOnWindows();
29
- const baseDir = projectOpts.sourceDir;
30
- const require = createRequire(import.meta.url);
31
- const openNextDistDir = dirname(require.resolve("@opennextjs/aws/index.js"));
32
- await createOpenNextConfigIfNotExistent(projectOpts);
33
- const { config, buildDir } = await compileOpenNextConfig(baseDir);
34
- ensureCloudflareConfig(config);
35
- // Initialize options
36
- const options = buildHelper.normalizeOptions(config, openNextDistDir, buildDir);
37
- logger.setLevel(options.debug ? "debug" : "info");
38
- // Do not minify the code so that we can apply string replacement patch.
39
- // Note that wrangler will still minify the bundle.
40
- options.minify = false;
41
- // Pre-build validation
42
- buildHelper.checkRunningInsideNextjsApp(options);
43
- logger.info(`App directory: ${options.appPath}`);
44
- buildHelper.printNextjsVersion(options);
45
- ensureNextjsVersionSupported(options);
46
- buildHelper.printOpenNextVersion(options);
47
- if (projectOpts.skipNextBuild) {
48
- logger.warn("Skipping Next.js build");
49
- }
50
- else {
51
- // Build the next app
52
- printHeader("Building Next.js app");
53
- setStandaloneBuildMode(options);
54
- buildNextjsApp(options);
55
- }
56
- if (!containsDotNextDir(projectOpts.sourceDir)) {
57
- throw new Error(`.next folder not found in ${projectOpts.sourceDir}`);
58
- }
59
- // Generate deployable bundle
60
- printHeader("Generating bundle");
61
- buildHelper.initOutputDir(options);
62
- // Compile cache.ts
63
- compileCache(options);
64
- // Compile .env files
65
- compileEnvFiles(options);
66
- // Compile middleware
67
- await createMiddleware(options, { forceOnlyBuildOnce: true });
68
- createStaticAssets(options);
69
- if (config.dangerous?.disableIncrementalCache !== true) {
70
- createCacheAssets(options);
71
- copyCacheAssets(options);
72
- }
73
- await createServerBundle(options);
74
- // TODO: drop this copy.
75
- // Copy the .next directory to the output directory so it can be mutated.
76
- cpSync(join(projectOpts.sourceDir, ".next"), join(projectOpts.outputDir, ".next"), { recursive: true });
77
- const projConfig = getConfig(projectOpts);
78
- // TODO: rely on options only.
79
- await bundleServer(projConfig, options);
80
- if (!projectOpts.skipWranglerConfigCheck) {
81
- await createWranglerConfigIfNotExistent(projectOpts);
82
- }
83
- logger.info("OpenNext build complete.");
84
- }
85
- /**
86
- * Creates a `open-next.config.ts` file for the user if it doesn't exist, but only after asking for the user's confirmation.
87
- *
88
- * If the user refuses an error is thrown (since the file is mandatory).
89
- *
90
- * @param projectOpts The options for the project
91
- */
92
- async function createOpenNextConfigIfNotExistent(projectOpts) {
93
- const openNextConfigPath = join(projectOpts.sourceDir, "open-next.config.ts");
94
- if (!existsSync(openNextConfigPath)) {
95
- const answer = await askConfirmation("Missing required `open-next.config.ts` file, do you want to create one?");
96
- if (!answer) {
97
- throw new Error("The `open-next.config.ts` file is required, aborting!");
98
- }
99
- cpSync(join(getPackageTemplatesDirPath(), "defaults", "open-next.config.ts"), openNextConfigPath);
100
- }
101
- }
102
- /**
103
- * Ensures open next is configured for cloudflare.
104
- *
105
- * @param config OpenNext configuration.
106
- */
107
- function ensureCloudflareConfig(config) {
108
- const requirements = {
109
- dftUseCloudflareWrapper: config.default?.override?.wrapper === "cloudflare-node",
110
- dftUseEdgeConverter: config.default?.override?.converter === "edge",
111
- dftMaybeUseCache: config.default?.override?.incrementalCache === "dummy" ||
112
- typeof config.default?.override?.incrementalCache === "function",
113
- dftUseDummyTagCacheAndQueue: config.default?.override?.tagCache === "dummy" && config.default?.override?.queue === "dummy",
114
- disableCacheInterception: config.dangerous?.enableCacheInterception !== true,
115
- mwIsMiddlewareExternal: config.middleware?.external == true,
116
- mwUseCloudflareWrapper: config.middleware?.override?.wrapper === "cloudflare-edge",
117
- mwUseEdgeConverter: config.middleware?.override?.converter === "edge",
118
- mwUseFetchProxy: config.middleware?.override?.proxyExternalRequest === "fetch",
119
- };
120
- if (Object.values(requirements).some((satisfied) => !satisfied)) {
121
- throw new Error("The `open-next.config.ts` should have a default export like this:\n\n" +
122
- `{
123
- default: {
124
- override: {
125
- wrapper: "cloudflare-node",
126
- converter: "edge",
127
- incrementalCache: "dummy" | function,
128
- tagCache: "dummy",
129
- queue: "dummy",
130
- },
131
- },
132
-
133
- middleware: {
134
- external: true,
135
- override: {
136
- wrapper: "cloudflare-edge",
137
- converter: "edge",
138
- proxyExternalRequest: "fetch",
139
- },
140
- },
141
-
142
- "dangerous": {
143
- "enableCacheInterception": false
144
- },
145
- }\n\n`.replace(/^ {8}/gm, ""));
146
- }
147
- }
148
- /**
149
- * Creates a `wrangler.json` file for the user if a wrangler config file doesn't already exist,
150
- * but only after asking for the user's confirmation.
151
- *
152
- * If the user refuses a warning is shown (which offers ways to opt out of this check to the user).
153
- *
154
- * Note: we generate a wrangler.json file with comments instead of using the jsonc extension,
155
- * we decided to do that since json is more common than jsonc, wrangler also parses
156
- * them in the same way and we also expect developers to associate `wrangler.json`
157
- * files to the jsonc language
158
- *
159
- * @param projectOpts The options for the project
160
- */
161
- async function createWranglerConfigIfNotExistent(projectOpts) {
162
- const possibleExts = ["toml", "json", "jsonc"];
163
- const wranglerConfigFileExists = possibleExts.some((ext) => existsSync(join(projectOpts.sourceDir, `wrangler.${ext}`)));
164
- if (wranglerConfigFileExists) {
165
- return;
166
- }
167
- const answer = await askConfirmation("No `wrangler.(toml|json|jsonc)` config file found, do you want to create one?");
168
- if (!answer) {
169
- console.warn("No Wrangler config file created" +
170
- "\n" +
171
- "(to avoid this check use the `--skipWranglerConfigCheck` flag or set a `SKIP_WRANGLER_CONFIG_CHECK` environment variable to `yes`)");
172
- return;
173
- }
174
- const wranglerConfigTemplate = readFileSync(join(getPackageTemplatesDirPath(), "defaults", "wrangler.jsonc"), "utf8");
175
- let wranglerConfigContent = wranglerConfigTemplate;
176
- const appName = getAppNameFromPackageJson(projectOpts.sourceDir) ?? "app-name";
177
- if (appName) {
178
- wranglerConfigContent = wranglerConfigContent.replace('"app-name"', JSON.stringify(appName.replaceAll("_", "-")));
179
- }
180
- const compatDate = await getLatestCompatDate();
181
- if (compatDate) {
182
- wranglerConfigContent = wranglerConfigContent.replace(/"compatibility_date": "\d{4}-\d{2}-\d{2}"/, `"compatibility_date": ${JSON.stringify(compatDate)}`);
183
- }
184
- writeFileSync(join(projectOpts.sourceDir, "wrangler.json"), wranglerConfigContent);
185
- }
186
- function getAppNameFromPackageJson(sourceDir) {
187
- try {
188
- const packageJsonStr = readFileSync(join(sourceDir, "package.json"), "utf8");
189
- const packageJson = JSON.parse(packageJsonStr);
190
- if (typeof packageJson.name === "string")
191
- return packageJson.name;
192
- }
193
- catch {
194
- /* empty */
195
- }
196
- }
197
- export async function getLatestCompatDate() {
198
- try {
199
- const resp = await fetch(`https://registry.npmjs.org/workerd`);
200
- const latestWorkerdVersion = (await resp.json())["dist-tags"].latest;
201
- // The format of the workerd version is `major.yyyymmdd.patch`.
202
- const match = latestWorkerdVersion.match(/\d+\.(\d{4})(\d{2})(\d{2})\.\d+/);
203
- if (match) {
204
- const [, year, month, date] = match;
205
- const compatDate = `${year}-${month}-${date}`;
206
- return compatDate;
207
- }
208
- }
209
- catch {
210
- /* empty */
211
- }
212
- }
213
- function ensureNextjsVersionSupported(options) {
214
- if (buildHelper.compareSemver(options.nextVersion, "14.0.0") < 0) {
215
- logger.error("Next.js version unsupported, please upgrade to version 14 or greater.");
216
- process.exit(1);
217
- }
218
- }
@@ -1,7 +0,0 @@
1
- /**
2
- * Recursively reads all file paths in a directory.
3
- *
4
- * @param dir Directory to recursively read from.
5
- * @returns Array of all paths for all files in a directory.
6
- */
7
- export declare function readPathsRecursively(dir: string): string[];
@@ -1,20 +0,0 @@
1
- import { readdirSync } from "node:fs";
2
- import { join } from "node:path";
3
- /**
4
- * Recursively reads all file paths in a directory.
5
- *
6
- * @param dir Directory to recursively read from.
7
- * @returns Array of all paths for all files in a directory.
8
- */
9
- export function readPathsRecursively(dir) {
10
- try {
11
- const files = readdirSync(dir, { withFileTypes: true });
12
- return files.flatMap((file) => {
13
- const filePath = join(dir, file.name);
14
- return file.isDirectory() ? readPathsRecursively(filePath) : filePath;
15
- });
16
- }
17
- catch {
18
- return [];
19
- }
20
- }