@opennextjs/cloudflare 0.3.3 → 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.
- package/dist/cli/args.d.ts +1 -0
- package/dist/cli/args.js +7 -1
- package/dist/cli/build/index.d.ts +1 -0
- package/dist/cli/build/index.js +76 -1
- package/dist/cli/build/patches/to-investigate/patch-exception-bubbling.js +9 -1
- package/dist/cli/build/patches/to-investigate/wrangler-deps.js +92 -0
- package/dist/cli/build/utils/extract-project-env-vars.d.ts +1 -1
- package/dist/cli/build/utils/extract-project-env-vars.js +2 -2
- package/dist/cli/build/utils/extract-project-env-vars.spec.js +10 -0
- package/dist/cli/config.d.ts +1 -0
- package/dist/cli/index.js +2 -1
- package/dist/cli/templates/worker.js +0 -3
- package/package.json +2 -2
- package/templates/defaults/wrangler.jsonc +18 -0
package/dist/cli/args.d.ts
CHANGED
package/dist/cli/args.js
CHANGED
|
@@ -2,7 +2,7 @@ import { mkdirSync, statSync } from "node:fs";
|
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
3
|
import { parseArgs } from "node:util";
|
|
4
4
|
export function getArgs() {
|
|
5
|
-
const { skipBuild, output, noMinify } = parseArgs({
|
|
5
|
+
const { skipBuild, skipWranglerConfigCheck, output, noMinify } = parseArgs({
|
|
6
6
|
options: {
|
|
7
7
|
skipBuild: {
|
|
8
8
|
type: "boolean",
|
|
@@ -17,6 +17,10 @@ export function getArgs() {
|
|
|
17
17
|
type: "boolean",
|
|
18
18
|
default: false,
|
|
19
19
|
},
|
|
20
|
+
skipWranglerConfigCheck: {
|
|
21
|
+
type: "boolean",
|
|
22
|
+
default: false,
|
|
23
|
+
},
|
|
20
24
|
},
|
|
21
25
|
allowPositionals: false,
|
|
22
26
|
}).values;
|
|
@@ -27,6 +31,8 @@ export function getArgs() {
|
|
|
27
31
|
return {
|
|
28
32
|
outputDir,
|
|
29
33
|
skipNextBuild: skipBuild || ["1", "true", "yes"].includes(String(process.env.SKIP_NEXT_APP_BUILD)),
|
|
34
|
+
skipWranglerConfigCheck: skipWranglerConfigCheck ||
|
|
35
|
+
["1", "true", "yes"].includes(String(process.env.SKIP_WRANGLER_CONFIG_CHECK)),
|
|
30
36
|
minify: !noMinify,
|
|
31
37
|
};
|
|
32
38
|
}
|
package/dist/cli/build/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cpSync, existsSync } from "node:fs";
|
|
1
|
+
import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import { dirname, join } from "node:path";
|
|
4
4
|
import { buildNextjsApp, setStandaloneBuildMode } from "@opennextjs/aws/build/buildNextApp.js";
|
|
@@ -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");
|
|
@@ -76,6 +77,9 @@ export async function build(projectOpts) {
|
|
|
76
77
|
const projConfig = getConfig(projectOpts);
|
|
77
78
|
// TODO: rely on options only.
|
|
78
79
|
await bundleServer(projConfig, options);
|
|
80
|
+
if (!projectOpts.skipWranglerConfigCheck) {
|
|
81
|
+
await createWranglerConfigIfNotExistent(projectOpts);
|
|
82
|
+
}
|
|
79
83
|
logger.info("OpenNext build complete.");
|
|
80
84
|
}
|
|
81
85
|
/**
|
|
@@ -141,3 +145,74 @@ function ensureCloudflareConfig(config) {
|
|
|
141
145
|
}\n\n`.replace(/^ {8}/gm, ""));
|
|
142
146
|
}
|
|
143
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
|
+
}
|
|
@@ -5,5 +5,13 @@
|
|
|
5
5
|
* promises.
|
|
6
6
|
*/
|
|
7
7
|
export function patchExceptionBubbling(code) {
|
|
8
|
-
|
|
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
|
+
}
|
|
@@ -6,7 +6,7 @@ import type { BuildOptions } from "@opennextjs/aws/build/helper.js";
|
|
|
6
6
|
*
|
|
7
7
|
* Merged variables respect the following priority order.
|
|
8
8
|
* 1. `.env.{mode}.local`
|
|
9
|
-
* 2. `.env.local`
|
|
9
|
+
* 2. `.env.local` (when mode is not equal to `test`)
|
|
10
10
|
* 3. `.env.{mode}`
|
|
11
11
|
* 4. `.env`
|
|
12
12
|
*
|
|
@@ -13,7 +13,7 @@ function readEnvFile(filePath) {
|
|
|
13
13
|
*
|
|
14
14
|
* Merged variables respect the following priority order.
|
|
15
15
|
* 1. `.env.{mode}.local`
|
|
16
|
-
* 2. `.env.local`
|
|
16
|
+
* 2. `.env.local` (when mode is not equal to `test`)
|
|
17
17
|
* 3. `.env.{mode}`
|
|
18
18
|
* 4. `.env`
|
|
19
19
|
*
|
|
@@ -23,7 +23,7 @@ function readEnvFile(filePath) {
|
|
|
23
23
|
* the env files at the root of the monorepo.
|
|
24
24
|
*/
|
|
25
25
|
export function extractProjectEnvVars(mode, { monorepoRoot, appPath }) {
|
|
26
|
-
return [".env", `.env.${mode}`, ".env.local", `.env.${mode}.local`]
|
|
26
|
+
return [".env", `.env.${mode}`, ...(mode !== "test" ? [".env.local"] : []), `.env.${mode}.local`]
|
|
27
27
|
.flatMap((fileName) => [
|
|
28
28
|
...(monorepoRoot !== appPath ? [readEnvFile(path.join(monorepoRoot, fileName))] : []),
|
|
29
29
|
readEnvFile(path.join(appPath, fileName)),
|
|
@@ -8,6 +8,8 @@ describe("extractProjectEnvVars", () => {
|
|
|
8
8
|
mockFs({
|
|
9
9
|
".env": "ENV_VAR=value",
|
|
10
10
|
".env.local": "ENV_LOCAL_VAR=value",
|
|
11
|
+
".env.test": "ENV_TEST_VAR=value",
|
|
12
|
+
".env.test.local": "ENV_TEST_LOCAL_VAR=value",
|
|
11
13
|
".env.development": "ENV_DEV_VAR=value",
|
|
12
14
|
".env.development.local": "ENV_DEV_LOCAL_VAR=value",
|
|
13
15
|
".env.production": "ENV_PROD_VAR=value",
|
|
@@ -54,4 +56,12 @@ describe("extractProjectEnvVars", () => {
|
|
|
54
56
|
ENV_VAR: "value",
|
|
55
57
|
});
|
|
56
58
|
});
|
|
59
|
+
it("should exclude .env.local files when extracting test env vars", () => {
|
|
60
|
+
const result = extractProjectEnvVars("test", options);
|
|
61
|
+
expect(result).toEqual({
|
|
62
|
+
ENV_TEST_LOCAL_VAR: "value",
|
|
63
|
+
ENV_TEST_VAR: "value",
|
|
64
|
+
ENV_VAR: "value",
|
|
65
|
+
});
|
|
66
|
+
});
|
|
57
67
|
});
|
package/dist/cli/config.d.ts
CHANGED
package/dist/cli/index.js
CHANGED
|
@@ -3,10 +3,11 @@ import { resolve } from "node:path";
|
|
|
3
3
|
import { getArgs } from "./args.js";
|
|
4
4
|
import { build } from "./build/index.js";
|
|
5
5
|
const nextAppDir = process.cwd();
|
|
6
|
-
const { skipNextBuild, outputDir, minify } = getArgs();
|
|
6
|
+
const { skipNextBuild, skipWranglerConfigCheck, outputDir, minify } = getArgs();
|
|
7
7
|
await build({
|
|
8
8
|
sourceDir: nextAppDir,
|
|
9
9
|
outputDir: resolve(outputDir ?? nextAppDir, ".open-next"),
|
|
10
10
|
skipNextBuild,
|
|
11
|
+
skipWranglerConfigCheck,
|
|
11
12
|
minify,
|
|
12
13
|
});
|
|
@@ -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
|
+
"version": "0.3.5",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"opennextjs-cloudflare": "dist/cli/index.js"
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
},
|
|
62
62
|
"dependencies": {
|
|
63
63
|
"@dotenvx/dotenvx": "1.31.0",
|
|
64
|
-
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@
|
|
64
|
+
"@opennextjs/aws": "https://pkg.pr.new/@opennextjs/aws@688",
|
|
65
65
|
"glob": "^11.0.0",
|
|
66
66
|
"rimraf": "^6.0.1",
|
|
67
67
|
"ts-morph": "^23.0.0",
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"main": ".open-next/worker.js",
|
|
3
|
+
"name": "app-name",
|
|
4
|
+
"compatibility_date": "2024-12-30",
|
|
5
|
+
"compatibility_flags": ["nodejs_compat"],
|
|
6
|
+
"assets": {
|
|
7
|
+
"directory": ".open-next/assets",
|
|
8
|
+
"binding": "ASSETS"
|
|
9
|
+
},
|
|
10
|
+
"kv_namespaces": [
|
|
11
|
+
// Create a KV binding with the binding name "NEXT_CACHE_WORKERS_KV"
|
|
12
|
+
// to enable the KV based caching:
|
|
13
|
+
// {
|
|
14
|
+
// "binding": "NEXT_CACHE_WORKERS_KV",
|
|
15
|
+
// "id": "<BINDING_ID>"
|
|
16
|
+
// }
|
|
17
|
+
]
|
|
18
|
+
}
|