@opennextjs/cloudflare 1.16.4 → 1.16.5

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.
@@ -34,13 +34,31 @@ const resolver = {
34
34
  type: "core",
35
35
  statusCode: response.status,
36
36
  headers: Object.fromEntries(response.headers.entries()),
37
- // Workers and Node types differ.
38
37
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
39
- body: response.body || new ReadableStream(),
38
+ body: getResponseBody(method, response),
40
39
  isBase64Encoded: false,
41
40
  };
42
41
  },
43
42
  };
43
+ /**
44
+ * Returns the response body for an asset result.
45
+ *
46
+ * HEAD responses must return `null` because `response.body` is `null` per the HTTP spec
47
+ * and the `new ReadableStream()` fallback would create a stream that never closes, hanging the Worker.
48
+ *
49
+ * @param method - The HTTP method of the request.
50
+ * @param response - The response from the ASSETS binding.
51
+ * @returns The body to use in the internal result.
52
+ */
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ function getResponseBody(method, response) {
55
+ if (method === "HEAD") {
56
+ return null;
57
+ }
58
+ // Workers and Node ReadableStream types differ.
59
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
60
+ return response.body || new ReadableStream();
61
+ }
44
62
  /**
45
63
  * @param runWorkerFirst `run_worker_first` config
46
64
  * @param pathname pathname of the request
@@ -6,6 +6,7 @@ import * as buildHelper from "@opennextjs/aws/build/helper.js";
6
6
  import { patchOriginalNextConfig } from "@opennextjs/aws/build/patch/patches/index.js";
7
7
  import { printHeader } from "@opennextjs/aws/build/utils.js";
8
8
  import logger from "@opennextjs/aws/logger.js";
9
+ import { ensureNextjsVersionSupported } from "../commands/utils.js";
9
10
  import { bundleServer } from "./bundle-server.js";
10
11
  import { compileCacheAssetsManifestSqlFile } from "./open-next/compile-cache-assets-manifest.js";
11
12
  import { compileEnvFiles } from "./open-next/compile-env-files.js";
@@ -75,18 +76,3 @@ export async function build(options, config, projectOpts, wranglerConfig, allowU
75
76
  await bundleServer(options, projectOpts);
76
77
  logger.info("OpenNext build complete.");
77
78
  }
78
- async function ensureNextjsVersionSupported({ nextVersion }) {
79
- if (buildHelper.compareSemver(nextVersion, "<", "14.2.0")) {
80
- logger.error("Next.js version unsupported, please upgrade to version 14.2 or greater.");
81
- process.exit(1);
82
- }
83
- const { default: { version: wranglerVersion }, } = await import("wrangler/package.json", { with: { type: "json" } });
84
- // We need a version of workerd that has a fix for setImmediate for Next.js 16.1+
85
- // See:
86
- // - https://github.com/cloudflare/workerd/pull/5869
87
- // - https://github.com/opennextjs/opennextjs-cloudflare/issues/1049
88
- if (buildHelper.compareSemver(nextVersion, ">=", "16.1.0") &&
89
- buildHelper.compareSemver(wranglerVersion, "<", "4.59.2")) {
90
- logger.warn(`Next.js 16.1+ requires wrangler 4.59.2 or greater (${wranglerVersion} detected).`);
91
- }
92
- }
@@ -1,12 +1,14 @@
1
+ import assert from "node:assert";
1
2
  import childProcess from "node:child_process";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
- import { checkRunningInsideNextjsApp, findNextConfig, findPackagerAndRoot, } from "@opennextjs/aws/build/helper.js";
5
+ import { checkRunningInsideNextjsApp, findNextConfig, findPackagerAndRoot, getNextVersion, } from "@opennextjs/aws/build/helper.js";
5
6
  import logger from "@opennextjs/aws/logger.js";
6
7
  import { conditionalAppendFileSync } from "../build/utils/files.js";
8
+ import { askConfirmation } from "../utils/ask-confirmation.js";
7
9
  import { createOpenNextConfigFile, findOpenNextConfig } from "../utils/open-next-config.js";
8
10
  import { createWranglerConfigFile, findWranglerConfig } from "../utils/wrangler-config.js";
9
- import { printHeaders } from "./utils.js";
11
+ import { ensureNextjsVersionSupported, printHeaders } from "./utils.js";
10
12
  /**
11
13
  * Implementation of the `opennextjs-cloudflare migrate` command.
12
14
  *
@@ -16,6 +18,14 @@ async function migrateCommand(args) {
16
18
  printHeaders("migrate");
17
19
  logger.info("🚀 Setting up the OpenNext Cloudflare adapter...\n");
18
20
  const projectDir = process.cwd();
21
+ const nextConfigFileCreated = await maybeCreateNextConfigFileIfMissing(projectDir, args.forceInstall).catch((e) => {
22
+ logger.error(`${e instanceof Error ? e.message : e}\n`);
23
+ process.exit(1);
24
+ });
25
+ if (nextConfigFileCreated === false) {
26
+ logger.error("The next.config file is required, aborting!\n");
27
+ process.exit(1);
28
+ }
19
29
  checkRunningInsideNextjsApp({ appPath: projectDir });
20
30
  const wranglerConfigFilePath = findWranglerConfig(projectDir);
21
31
  if (wranglerConfigFilePath) {
@@ -93,13 +103,14 @@ async function migrateCommand(args) {
93
103
  appendPrefix: "\n",
94
104
  });
95
105
  const nextConfig = findNextConfig({ appPath: projectDir });
96
- if (nextConfig) {
97
- printStepTitle("Updating Next.js config");
98
- conditionalAppendFileSync(nextConfig.path, "import('@opennextjs/cloudflare').then(m => m.initOpenNextCloudflareForDev());\n", {
99
- appendIf: (content) => !content.includes("initOpenNextCloudflareForDev"),
100
- appendPrefix: "\n",
101
- });
102
- }
106
+ // At this point the next config file should exist (it either
107
+ // was part of the original project or we've created it)
108
+ assert(nextConfig, "Next config file unexpectedly missing");
109
+ printStepTitle("Updating Next.js config");
110
+ conditionalAppendFileSync(nextConfig.path, "import('@opennextjs/cloudflare').then(m => m.initOpenNextCloudflareForDev());\n", {
111
+ appendIf: (content) => !content.includes("initOpenNextCloudflareForDev"),
112
+ appendPrefix: "\n",
113
+ });
103
114
  printStepTitle("Checking for edge runtime usage");
104
115
  try {
105
116
  const extensions = [".ts", ".tsx", ".js", ".jsx", ".mjs", ".mts"];
@@ -173,6 +184,53 @@ function findFilesRecursive(dir, extensions, fileList = []) {
173
184
  function printStepTitle(title) {
174
185
  logger.info(`⚙️ ${title}...\n`);
175
186
  }
187
+ /**
188
+ * Creates a plain next.config.ts file
189
+ *
190
+ * @param appDir The directory where the config file should be created
191
+ */
192
+ function createNextConfigFile(appDir) {
193
+ const nextConfigPath = path.join(appDir, "next.config.ts");
194
+ const content = `import type { NextConfig } from "next";
195
+
196
+ const nextConfig: NextConfig = {};
197
+
198
+ export default nextConfig;
199
+ `;
200
+ fs.writeFileSync(nextConfigPath, content);
201
+ }
202
+ /**
203
+ * Creates a next.config.ts file, after asking for the user's confirmation, if missing in the project's directory.
204
+ *
205
+ * To be safe, this function also ensures that the "next" package is installed and its version is compatible with OpenNext.
206
+ *
207
+ * @param projectDir The project directory to check
208
+ * @param skipNextVersionCheck Whether to bypass the "next" version compatibility check
209
+ * @returns A boolean representing whether the user has accepter the creation of the config file, undefined if the file already existed
210
+ * @throws {Error} If "next" is not installed or the Next.js version is incompatible with open-next
211
+ */
212
+ async function maybeCreateNextConfigFileIfMissing(projectDir, skipNextVersionCheck) {
213
+ if (findNextConfig({ appPath: projectDir })) {
214
+ return;
215
+ }
216
+ let nextVersion;
217
+ try {
218
+ nextVersion = getNextVersion(projectDir);
219
+ }
220
+ catch {
221
+ throw new Error("This does not appear to be a Next.js application. The 'next' package is not installed and no next.config file was found.");
222
+ }
223
+ if (!skipNextVersionCheck) {
224
+ await ensureNextjsVersionSupported({ nextVersion });
225
+ }
226
+ const answer = await askConfirmation("Missing required next.config file. Do you want to create one?");
227
+ if (!answer) {
228
+ return false;
229
+ }
230
+ createNextConfigFile(projectDir);
231
+ logger.info("Created next.config.ts\n");
232
+ return true;
233
+ }
176
234
  /**
177
235
  * Add the `migrate` command to yargs configuration.
178
236
  */
@@ -1,3 +1,4 @@
1
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
1
2
  import type yargs from "yargs";
2
3
  import type { OpenNextConfig } from "../../api/config.js";
3
4
  export type WithWranglerArgs<T = unknown> = T & {
@@ -12,6 +13,15 @@ export declare const nextAppDir: string;
12
13
  * @param command
13
14
  */
14
15
  export declare function printHeaders(command: string): void;
16
+ /**
17
+ * Validates that the Next.js version is supported and checks wrangler compatibility.
18
+ *
19
+ * Note: this function assumes that wrangler is installed.
20
+ *
21
+ * @param options.nextVersion The detected Next.js version string
22
+ * @throws {Error} If the Next.js version is unsupported
23
+ */
24
+ export declare function ensureNextjsVersionSupported({ nextVersion, }: Pick<buildHelper.BuildOptions, "nextVersion">): Promise<void>;
15
25
  /**
16
26
  * Compile the OpenNext config.
17
27
  *
@@ -4,6 +4,7 @@ import path from "node:path";
4
4
  import url from "node:url";
5
5
  import { compileOpenNextConfig } from "@opennextjs/aws/build/compileConfig.js";
6
6
  import { normalizeOptions } from "@opennextjs/aws/build/helper.js";
7
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
7
8
  import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
8
9
  import logger from "@opennextjs/aws/logger.js";
9
10
  import { unstable_readConfig } from "wrangler";
@@ -18,6 +19,28 @@ export function printHeaders(command) {
18
19
  printHeader(`Cloudflare ${command}`);
19
20
  showWarningOnWindows();
20
21
  }
22
+ /**
23
+ * Validates that the Next.js version is supported and checks wrangler compatibility.
24
+ *
25
+ * Note: this function assumes that wrangler is installed.
26
+ *
27
+ * @param options.nextVersion The detected Next.js version string
28
+ * @throws {Error} If the Next.js version is unsupported
29
+ */
30
+ export async function ensureNextjsVersionSupported({ nextVersion, }) {
31
+ if (buildHelper.compareSemver(nextVersion, "<", "14.2.0")) {
32
+ throw new Error("Next.js version unsupported, please upgrade to version 14.2 or greater.");
33
+ }
34
+ const { default: { version: wranglerVersion }, } = await import("wrangler/package.json", { with: { type: "json" } });
35
+ // We need a version of workerd that has a fix for setImmediate for Next.js 16.1+
36
+ // See:
37
+ // - https://github.com/cloudflare/workerd/pull/5869
38
+ // - https://github.com/opennextjs/opennextjs-cloudflare/issues/1049
39
+ if (buildHelper.compareSemver(nextVersion, ">=", "16.1.0") &&
40
+ buildHelper.compareSemver(wranglerVersion, "<", "4.59.2")) {
41
+ logger.warn(`Next.js 16.1+ requires wrangler 4.59.2 or greater (${wranglerVersion} detected).`);
42
+ }
43
+ }
21
44
  /**
22
45
  * Compile the OpenNext config.
23
46
  *
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": "1.16.4",
4
+ "version": "1.16.5",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"