@opennextjs/cloudflare 0.3.4 → 0.3.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.
@@ -42,6 +42,7 @@ export async function build(projectOpts) {
42
42
  buildHelper.checkRunningInsideNextjsApp(options);
43
43
  logger.info(`App directory: ${options.appPath}`);
44
44
  buildHelper.printNextjsVersion(options);
45
+ ensureNextjsVersionSupported(options);
45
46
  buildHelper.printOpenNextVersion(options);
46
47
  if (projectOpts.skipNextBuild) {
47
48
  logger.warn("Skipping Next.js build");
@@ -209,3 +210,9 @@ export async function getLatestCompatDate() {
209
210
  /* empty */
210
211
  }
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
+ }
@@ -5,5 +5,13 @@
5
5
  * promises.
6
6
  */
7
7
  export function patchExceptionBubbling(code) {
8
- return code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
8
+ // The code before had: `query._nextBubbleNoFallback = '1'`, that has ben refactored to
9
+ // `addRequestMeta(req, 'bubbleNoFallback', true)` in https://github.com/vercel/next.js/pull/74100
10
+ // we need to support both for backward compatibility, that's why we have the following if statement
11
+ if (code.includes("_nextBubbleNoFallback")) {
12
+ return code.replace('_nextBubbleNoFallback = "1"', "_nextBubbleNoFallback = undefined");
13
+ }
14
+ // The Next.js transpiled code contains something like `(0, _requestmeta.addRequestMeta)(req, "bubbleNoFallback", true);`
15
+ // and we want to update it to `(0, _requestmeta.addRequestMeta)(req, "bubbleNoFallback", false);`
16
+ return code.replace(/\((.*?.addRequestMeta\)\(.*?,\s+"bubbleNoFallback"),\s+true\)/, "($1, false)");
9
17
  }
@@ -1,5 +1,7 @@
1
1
  import { readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import * as ts from "ts-morph";
4
+ import { tsParseFile } from "../../utils/index.js";
3
5
  export function patchWranglerDeps(config) {
4
6
  console.log("# patchWranglerDeps");
5
7
  const distPath = getDistPath(config);
@@ -13,6 +15,7 @@ export function patchWranglerDeps(config) {
13
15
  const pagesRuntimeFile = join(distPath, "compiled", "next-server", "pages.runtime.prod.js");
14
16
  const patchedPagesRuntime = readFileSync(pagesRuntimeFile, "utf-8").replace(`e.exports=require("critters")`, `e.exports={}`);
15
17
  writeFileSync(pagesRuntimeFile, patchedPagesRuntime);
18
+ patchRequireReactDomServerEdge(config);
16
19
  // Patch .next/standalone/node_modules/next/dist/server/lib/trace/tracer.js
17
20
  //
18
21
  // Remove the need for an alias in wrangler.toml:
@@ -52,3 +55,92 @@ function getDistPath(config) {
52
55
  }
53
56
  throw new Error("Unexpected error: unable to detect the node_modules/next/dist directory");
54
57
  }
58
+ /**
59
+ * `react-dom` v>=19 has a `server.edge` export: https://github.com/facebook/react/blob/a160102f3/packages/react-dom/package.json#L79
60
+ * but version of `react-dom` <= 18 do not have this export but have a `server.browser` export instead: https://github.com/facebook/react/blob/8a015b68/packages/react-dom/package.json#L49
61
+ *
62
+ * Next.js also try-catches importing the `server.edge` export:
63
+ * https://github.com/vercel/next.js/blob/6784575/packages/next/src/server/ReactDOMServerPages.js
64
+ *
65
+ * The issue here is that in the `.next/standalone/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js`
66
+ * file for whatever reason there is a non `try-catch`ed require for the `server.edge` export
67
+ *
68
+ * This functions fixes this issue by wrapping the require in a try-catch block in the same way Next.js does it
69
+ * (note: this will make the build succeed but doesn't guarantee that everything will necessarily work at runtime since
70
+ * it's not clear what code and how might be rely on this require call)
71
+ *
72
+ */
73
+ function patchRequireReactDomServerEdge(config) {
74
+ const distPath = getDistPath(config);
75
+ // Patch .next/standalone/node_modules/next/dist/compiled/next-server/pages.runtime.prod.js
76
+ const pagesRuntimeFile = join(distPath, "compiled", "next-server", "pages.runtime.prod.js");
77
+ const code = readFileSync(pagesRuntimeFile, "utf-8");
78
+ const file = tsParseFile(code);
79
+ // we need to update this function: `e=>{"use strict";e.exports=require("react-dom/server.edge")}`
80
+ file.getDescendantsOfKind(ts.SyntaxKind.ArrowFunction).forEach((arrowFunction) => {
81
+ // the function has a single parameter
82
+ const p = arrowFunction.getParameters();
83
+ if (p.length !== 1) {
84
+ return;
85
+ }
86
+ const parameterName = p[0].getName();
87
+ const bodyChildren = arrowFunction.getBody().getChildren();
88
+ if (!(bodyChildren.length === 3 &&
89
+ bodyChildren[0].getFullText() === "{" &&
90
+ bodyChildren[2].getFullText() === "}")) {
91
+ return;
92
+ }
93
+ const bodyStatements = bodyChildren[1]?.getChildren();
94
+ // the function has only two statements: "use strict" and e.exports=require("react-dom/server.edge")
95
+ if (!(bodyStatements?.length === 2 &&
96
+ bodyStatements.every((statement) => statement.isKind(ts.SyntaxKind.ExpressionStatement)))) {
97
+ return;
98
+ }
99
+ const bodyExpressionStatements = bodyStatements;
100
+ const stringLiteralExpression = bodyExpressionStatements[0].getExpressionIfKind(ts.SyntaxKind.StringLiteral);
101
+ // the first statement needs to be "use strict"
102
+ if (stringLiteralExpression?.getText() !== '"use strict"') {
103
+ return;
104
+ }
105
+ // the second statement (e.exports=require("react-dom/server.edge")) needs to be a binary expression
106
+ const binaryExpression = bodyExpressionStatements[1].getExpressionIfKind(ts.SyntaxKind.BinaryExpression);
107
+ if (!binaryExpression?.getOperatorToken().isKind(ts.SyntaxKind.EqualsToken)) {
108
+ return;
109
+ }
110
+ // on the left we have `${parameterName}.exports`
111
+ const binaryLeft = binaryExpression.getLeft();
112
+ if (!binaryLeft.isKind(ts.SyntaxKind.PropertyAccessExpression) ||
113
+ binaryLeft.getExpressionIfKind(ts.SyntaxKind.Identifier)?.getText() !== parameterName ||
114
+ binaryLeft.getName() !== "exports") {
115
+ return;
116
+ }
117
+ // on the right we have `require("react-dom/server.edge")`
118
+ const binaryRight = binaryExpression.getRight();
119
+ if (!binaryRight.isKind(ts.SyntaxKind.CallExpression) ||
120
+ binaryRight.getExpressionIfKind(ts.SyntaxKind.Identifier)?.getText() !== "require") {
121
+ return;
122
+ }
123
+ const requireArgs = binaryRight.getArguments();
124
+ if (requireArgs.length !== 1 || requireArgs[0].getText() !== '"react-dom/server.edge"') {
125
+ return;
126
+ }
127
+ arrowFunction.setBodyText(`
128
+ "use strict";
129
+ let ReactDOMServer;
130
+ try {
131
+ ReactDOMServer = require('react-dom/server.edge');
132
+ } catch (error) {
133
+ if (
134
+ error.code !== 'MODULE_NOT_FOUND' &&
135
+ error.code !== 'ERR_PACKAGE_PATH_NOT_EXPORTED'
136
+ ) {
137
+ throw error;
138
+ }
139
+ ReactDOMServer = require('react-dom/server.browser');
140
+ }
141
+ ${parameterName}.exports = ReactDOMServer;
142
+ }`.replace(/\ns*/g, " "));
143
+ });
144
+ const updatedCode = file.print();
145
+ writeFileSync(pagesRuntimeFile, updatedCode);
146
+ }
@@ -45,9 +45,6 @@ export default {
45
45
  * Note that cloudflare env string values are copied by the middleware handler.
46
46
  */
47
47
  async function populateProcessEnv(url, nextJsEnv) {
48
- if (process.env.__PROCESS_ENV_POPULATED === "1") {
49
- return;
50
- }
51
48
  // @ts-expect-error: resolved by wrangler build
52
49
  const nextEnvVars = await import("./.env.mjs");
53
50
  const mode = nextJsEnv ?? "production";
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.4",
4
+ "version": "0.3.5",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "opennextjs-cloudflare": "dist/cli/index.js"