@opennextjs/cloudflare 1.14.10 → 1.15.0

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.
@@ -79,9 +79,6 @@ async function ensureNextjsVersionSupported({ nextVersion }) {
79
79
  logger.error("Next.js version unsupported, please upgrade to version 14.2 or greater.");
80
80
  process.exit(1);
81
81
  }
82
- if (buildHelper.compareSemver(nextVersion, ">=", "16")) {
83
- logger.warn("Next.js 16 is not fully supported yet! Some features may not work as expected.");
84
- }
85
82
  const { default: { version: wranglerVersion }, } = await import("wrangler/package.json", { with: { type: "json" } });
86
83
  // We need a version of workerd that has a fix for setImmediate for Next.js 16.1+
87
84
  // See:
@@ -3,6 +3,7 @@ import { readFile, writeFile } from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
  import { getPackagePath } from "@opennextjs/aws/build/helper.js";
6
+ import * as buildHelper from "@opennextjs/aws/build/helper.js";
6
7
  import { ContentUpdater } from "@opennextjs/aws/plugins/content-updater.js";
7
8
  import { build } from "esbuild";
8
9
  import { getOpenNextConfig } from "../../api/config.js";
@@ -20,6 +21,7 @@ import { patchDepdDeprecations } from "./patches/plugins/patch-depd-deprecations
20
21
  import { fixRequire } from "./patches/plugins/require.js";
21
22
  import { shimRequireHook } from "./patches/plugins/require-hook.js";
22
23
  import { patchRouteModules } from "./patches/plugins/route-module.js";
24
+ import { shimReact } from "./patches/plugins/shim-react.js";
23
25
  import { setWranglerExternal } from "./patches/plugins/wrangler-external.js";
24
26
  import { copyPackageCliFiles, needsExperimentalReact, normalizePath } from "./utils/index.js";
25
27
  /** The dist directory of the Cloudflare adapter package */
@@ -46,10 +48,10 @@ export async function bundleServer(buildOpts, projectOpts) {
46
48
  const dotNextPath = path.join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
47
49
  const serverFiles = path.join(dotNextPath, "required-server-files.json");
48
50
  const nextConfig = JSON.parse(fs.readFileSync(serverFiles, "utf-8")).config;
49
- const useTurbopack = fs.existsSync(path.join(dotNextPath, "server/chunks/[turbopack]_runtime.js"));
51
+ const useTurbopack = buildHelper.getBundlerRuntime(buildOpts) === "turbopack";
50
52
  console.log(`\x1b[35m⚙️ Bundling the OpenNext server...\n\x1b[0m`);
51
53
  await patchWebpackRuntime(buildOpts);
52
- patchVercelOgLibrary(buildOpts);
54
+ const useOg = patchVercelOgLibrary(buildOpts);
53
55
  const outputPath = path.join(outputDir, "server-functions", "default");
54
56
  const packagePath = getPackagePath(buildOpts);
55
57
  const openNextServer = path.join(outputPath, packagePath, `index.mjs`);
@@ -79,6 +81,7 @@ export async function bundleServer(buildOpts, projectOpts) {
79
81
  conditions: getOpenNextConfig(buildOpts).cloudflare?.useWorkerdCondition === false ? [] : ["workerd"],
80
82
  plugins: [
81
83
  shimRequireHook(buildOpts),
84
+ shimReact(buildOpts),
82
85
  inlineDynamicRequires(updater, buildOpts),
83
86
  setWranglerExternal(),
84
87
  fixRequire(updater),
@@ -95,7 +98,11 @@ export async function bundleServer(buildOpts, projectOpts) {
95
98
  // Apply updater updates, must be the last plugin
96
99
  updater.plugin,
97
100
  ],
98
- external: ["./middleware/handler.mjs"],
101
+ external: [
102
+ "./middleware/handler.mjs",
103
+ // Do not bundle og when it is not used
104
+ ...(useOg ? [] : ["next/dist/compiled/@vercel/og/index.edge.js"]),
105
+ ],
99
106
  alias: {
100
107
  // Workers have `fetch` so the `node-fetch` polyfill is not needed
101
108
  "next/dist/compiled/node-fetch": path.join(buildOpts.outputDir, "cloudflare-templates/shims/fetch.js"),
@@ -137,7 +137,7 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
137
137
  awsPatches.patchUnstableCacheForISR,
138
138
  awsPatches.patchUseCacheForISR,
139
139
  awsPatches.patchNextServer,
140
- awsPatches.patchEnvVars,
140
+ awsPatches.getEnvVarsPatch(options),
141
141
  awsPatches.patchBackgroundRevalidation,
142
142
  awsPatches.patchNodeEnvironment,
143
143
  // Cloudflare specific patches
@@ -3,5 +3,6 @@ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
3
3
  * Patches the usage of @vercel/og to be compatible with Cloudflare Workers.
4
4
  *
5
5
  * @param buildOpts Build options.
6
+ * @returns Whether the @vercel/og library is used.
6
7
  */
7
- export declare function patchVercelOgLibrary(buildOpts: BuildOptions): void;
8
+ export declare function patchVercelOgLibrary(buildOpts: BuildOptions): boolean;
@@ -8,19 +8,24 @@ import { patchVercelOgFallbackFont, patchVercelOgImport } from "./vercel-og.js";
8
8
  * Patches the usage of @vercel/og to be compatible with Cloudflare Workers.
9
9
  *
10
10
  * @param buildOpts Build options.
11
+ * @returns Whether the @vercel/og library is used.
11
12
  */
12
13
  export function patchVercelOgLibrary(buildOpts) {
13
14
  const { appBuildOutputPath, outputDir } = buildOpts;
14
15
  const functionsPath = path.join(outputDir, "server-functions/default");
15
16
  const packagePath = path.join(functionsPath, getPackagePath(buildOpts));
17
+ let useOg = false;
16
18
  for (const traceInfoPath of globSync(path.join(appBuildOutputPath, ".next/server/**/*.nft.json"), {
17
19
  windowsPathsNoEscape: true,
18
20
  })) {
19
- let edgeFilePatched = false;
21
+ // Look for the Node version of the traced @vercel/og files
20
22
  const traceInfo = JSON.parse(readFileSync(traceInfoPath, { encoding: "utf8" }));
21
23
  const tracedNodePath = traceInfo.files.find((p) => p.endsWith("@vercel/og/index.node.js"));
22
24
  if (!tracedNodePath)
23
25
  continue;
26
+ // If we are here, it means the application is using the @vercel/og library
27
+ // and there is an `index.edge.js` colocated file that we need to copy and patch.
28
+ useOg = true;
24
29
  const outputDir = getOutputDir({ functionsPath, packagePath });
25
30
  const outputEdgePath = path.join(outputDir, "index.edge.js");
26
31
  // Ensure the edge version is available in the OpenNext node_modules.
@@ -28,12 +33,11 @@ export function patchVercelOgLibrary(buildOpts) {
28
33
  const tracedEdgePath = path.join(path.dirname(traceInfoPath), tracedNodePath.replace("index.node.js", "index.edge.js"));
29
34
  copyFileSync(tracedEdgePath, outputEdgePath);
30
35
  }
31
- if (!edgeFilePatched) {
32
- edgeFilePatched = true;
33
- // Change font fetches in the library to use imports.
34
- const node = parseFile(outputEdgePath);
35
- const { edits, matches } = patchVercelOgFallbackFont(node);
36
- writeFileSync(outputEdgePath, node.commitEdits(edits));
36
+ // Change font fetches in the library to use imports.
37
+ {
38
+ const ast = parseFile(outputEdgePath);
39
+ const { edits, matches } = patchVercelOgFallbackFont(ast);
40
+ writeFileSync(outputEdgePath, ast.commitEdits(edits));
37
41
  if (matches.length > 0) {
38
42
  const fontFileName = matches[0].getMatch("PATH").text();
39
43
  renameSync(path.join(outputDir, fontFileName), path.join(outputDir, `${fontFileName}.bin`));
@@ -41,11 +45,14 @@ export function patchVercelOgLibrary(buildOpts) {
41
45
  }
42
46
  // Change node imports for the library to edge imports.
43
47
  // This is only useful when turbopack is not used to bundle the function.
44
- const routeFilePath = traceInfoPath.replace(appBuildOutputPath, packagePath).replace(".nft.json", "");
45
- const node = parseFile(routeFilePath);
46
- const { edits } = patchVercelOgImport(node);
47
- writeFileSync(routeFilePath, node.commitEdits(edits));
48
+ {
49
+ const routeFilePath = traceInfoPath.replace(appBuildOutputPath, packagePath).replace(".nft.json", "");
50
+ const ast = parseFile(routeFilePath);
51
+ const { edits } = patchVercelOgImport(ast);
52
+ writeFileSync(routeFilePath, ast.commitEdits(edits));
53
+ }
48
54
  }
55
+ return useOg;
49
56
  }
50
57
  function getOutputDir(opts) {
51
58
  const vercelOgNodeModulePath = "node_modules/next/dist/compiled/@vercel/og";
@@ -14,6 +14,9 @@ export declare const vercelOgFallbackFontRule = "\nrule:\n kind: variable_decla
14
14
  /**
15
15
  * Patches the default font fetching to use a .bin import.
16
16
  *
17
+ * We use `.bin` extension as they are added as modules in the wrangler bundler.
18
+ * We would need to add a rule to handle `.ttf` otherwise.
19
+ *
17
20
  * @param root Root node.
18
21
  * @returns Results of applying the rule.
19
22
  */
@@ -50,6 +50,9 @@ fix: |-
50
50
  /**
51
51
  * Patches the default font fetching to use a .bin import.
52
52
  *
53
+ * We use `.bin` extension as they are added as modules in the wrangler bundler.
54
+ * We would need to add a rule to handle `.ttf` otherwise.
55
+ *
53
56
  * @param root Root node.
54
57
  * @returns Results of applying the rule.
55
58
  */
@@ -3,9 +3,9 @@ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
3
3
  export function shimRequireHook(options) {
4
4
  const emptyShimPath = join(options.outputDir, "cloudflare-templates/shims/empty.js");
5
5
  return {
6
- name: "replaceRelative",
6
+ name: "require-hook-shim",
7
7
  setup(build) {
8
- // Note: we (empty) shim require-hook modules as they generate problematic code that uses requires
8
+ // We (empty) shim require-hook modules as they generate problematic code that uses requires
9
9
  build.onResolve({ filter: getCrossPlatformPathRegex(String.raw `^\./require-hook$`, { escape: false }) }, () => ({
10
10
  path: emptyShimPath,
11
11
  }));
@@ -0,0 +1,15 @@
1
+ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
2
+ import type { Plugin } from "esbuild";
3
+ /**
4
+ * `react-dom/server.edge` requires:
5
+ * - `react-dom-server.edge.production.js`
6
+ * - `react-dom-server.browser.production.js`
7
+ * - `react-dom-server-legacy.browser.production.js`
8
+ *
9
+ * However only the first one is needed in the Cloudflare Workers environment.
10
+ * The other two can be shimmed to an empty module to reduce the bundle size.
11
+ *
12
+ * @param options Build options
13
+ * @returns An ESBuild plugin that shims unnecessary React modules
14
+ */
15
+ export declare function shimReact(options: BuildOptions): Plugin;
@@ -0,0 +1,27 @@
1
+ import { join } from "node:path";
2
+ import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
3
+ /**
4
+ * `react-dom/server.edge` requires:
5
+ * - `react-dom-server.edge.production.js`
6
+ * - `react-dom-server.browser.production.js`
7
+ * - `react-dom-server-legacy.browser.production.js`
8
+ *
9
+ * However only the first one is needed in the Cloudflare Workers environment.
10
+ * The other two can be shimmed to an empty module to reduce the bundle size.
11
+ *
12
+ * @param options Build options
13
+ * @returns An ESBuild plugin that shims unnecessary React modules
14
+ */
15
+ export function shimReact(options) {
16
+ const emptyShimPath = join(options.outputDir, "cloudflare-templates/shims/empty.js");
17
+ return {
18
+ name: "react-shim",
19
+ setup(build) {
20
+ build.onResolve({
21
+ filter: getCrossPlatformPathRegex(String.raw `(react-dom-server\.browser\.production\.js|react-dom-server-legacy\.browser\.production\.js)$`, { escape: false }),
22
+ }, () => ({
23
+ path: emptyShimPath,
24
+ }));
25
+ },
26
+ };
27
+ }
@@ -56,7 +56,7 @@ export declare function getNormalizedOptions(config: OpenNextConfig, buildDir?:
56
56
  openNextVersion: string;
57
57
  openNextDistDir: string;
58
58
  outputDir: string;
59
- packager: "bun" | "npm" | "yarn" | "pnpm";
59
+ packager: "npm" | "pnpm" | "yarn" | "bun";
60
60
  tempBuildDir: string;
61
61
  };
62
62
  /**
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.14.10",
4
+ "version": "1.15.0",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"
@@ -44,7 +44,7 @@
44
44
  "dependencies": {
45
45
  "@ast-grep/napi": "0.40.0",
46
46
  "@dotenvx/dotenvx": "1.31.0",
47
- "@opennextjs/aws": "3.9.10",
47
+ "@opennextjs/aws": "3.9.11",
48
48
  "cloudflare": "^4.4.1",
49
49
  "enquirer": "^2.4.1",
50
50
  "glob": "^12.0.0",