@opennextjs/cloudflare 1.11.0 → 1.12.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.
- package/dist/cli/build/build.js +9 -3
- package/dist/cli/build/bundle-server.js +6 -6
- package/dist/cli/build/open-next/compileDurableObjects.js +4 -4
- package/dist/cli/build/open-next/createServerBundle.js +2 -0
- package/dist/cli/build/patches/ast/patch-vercel-og-library.js +9 -2
- package/dist/cli/build/patches/plugins/turbopack.d.ts +2 -0
- package/dist/cli/build/patches/plugins/turbopack.js +78 -0
- package/dist/cli/build/utils/middleware.d.ts +8 -0
- package/dist/cli/build/utils/middleware.js +21 -0
- package/dist/cli/commands/helpers.js +12 -0
- package/dist/cli/commands/utils.js +2 -1
- package/package.json +1 -1
package/dist/cli/build/build.js
CHANGED
|
@@ -13,6 +13,7 @@ import { compileInit } from "./open-next/compile-init.js";
|
|
|
13
13
|
import { compileSkewProtection } from "./open-next/compile-skew-protection.js";
|
|
14
14
|
import { compileDurableObjects } from "./open-next/compileDurableObjects.js";
|
|
15
15
|
import { createServerBundle } from "./open-next/createServerBundle.js";
|
|
16
|
+
import { useNodeMiddleware } from "./utils/middleware.js";
|
|
16
17
|
import { getVersion } from "./utils/version.js";
|
|
17
18
|
/**
|
|
18
19
|
* Builds the application in a format that can be passed to workerd
|
|
@@ -45,13 +46,18 @@ export async function build(options, config, projectOpts, wranglerConfig) {
|
|
|
45
46
|
setStandaloneBuildMode(options);
|
|
46
47
|
buildNextjsApp(options);
|
|
47
48
|
}
|
|
49
|
+
// Make sure no Node.js middleware is used
|
|
50
|
+
if (useNodeMiddleware(options)) {
|
|
51
|
+
logger.error("Node.js middleware is not currently supported. Consider switching to Edge Middleware.");
|
|
52
|
+
process.exit(1);
|
|
53
|
+
}
|
|
48
54
|
// Generate deployable bundle
|
|
49
55
|
printHeader("Generating bundle");
|
|
50
56
|
compileCache(options);
|
|
51
57
|
compileEnvFiles(options);
|
|
52
|
-
compileInit(options, wranglerConfig);
|
|
53
|
-
compileImages(options);
|
|
54
|
-
compileSkewProtection(options, config);
|
|
58
|
+
await compileInit(options, wranglerConfig);
|
|
59
|
+
await compileImages(options);
|
|
60
|
+
await compileSkewProtection(options, config);
|
|
55
61
|
// Compile middleware
|
|
56
62
|
await createMiddleware(options, { forceOnlyBuildOnce: true });
|
|
57
63
|
createStaticAssets(options, { useBasePath: true });
|
|
@@ -43,9 +43,10 @@ const optionalDependencies = [
|
|
|
43
43
|
export async function bundleServer(buildOpts, projectOpts) {
|
|
44
44
|
copyPackageCliFiles(packageDistDir, buildOpts);
|
|
45
45
|
const { appPath, outputDir, monorepoRoot, debug } = buildOpts;
|
|
46
|
-
const
|
|
47
|
-
const serverFiles = path.join(
|
|
46
|
+
const dotNextPath = path.join(outputDir, "server-functions/default", getPackagePath(buildOpts), ".next");
|
|
47
|
+
const serverFiles = path.join(dotNextPath, "required-server-files.json");
|
|
48
48
|
const nextConfig = JSON.parse(fs.readFileSync(serverFiles, "utf-8")).config;
|
|
49
|
+
const useTurbopack = fs.existsSync(path.join(dotNextPath, "server/chunks/[turbopack]_runtime.js"));
|
|
49
50
|
console.log(`\x1b[35m⚙️ Bundling the OpenNext server...\n\x1b[0m`);
|
|
50
51
|
await patchWebpackRuntime(buildOpts);
|
|
51
52
|
patchVercelOgLibrary(buildOpts);
|
|
@@ -118,13 +119,12 @@ export async function bundleServer(buildOpts, projectOpts) {
|
|
|
118
119
|
// Note: we need the __non_webpack_require__ variable declared as it is used by next-server:
|
|
119
120
|
// https://github.com/vercel/next.js/blob/be0c3283/packages/next/src/server/next-server.ts#L116-L119
|
|
120
121
|
__non_webpack_require__: "require",
|
|
122
|
+
// The 2 following defines are used to reduce the bundle size by removing unnecessary code
|
|
123
|
+
// Next uses different precompiled renderers (i.e. `app-page.runtime.prod.js`) based on if you use `TURBOPACK` or some experimental React features
|
|
124
|
+
...(useTurbopack ? {} : { "process.env.TURBOPACK": "false" }),
|
|
121
125
|
// We make sure that environment variables that Next.js expects are properly defined
|
|
122
126
|
"process.env.NEXT_RUNTIME": '"nodejs"',
|
|
123
127
|
"process.env.NODE_ENV": '"production"',
|
|
124
|
-
// The 2 following defines are used to reduce the bundle size by removing unnecessary code
|
|
125
|
-
// Next uses different precompiled renderers (i.e. `app-page.runtime.prod.js`) based on if you use `TURBOPACK` or some experimental React features
|
|
126
|
-
// Turbopack is not supported for build at the moment, so we disable it
|
|
127
|
-
"process.env.TURBOPACK": "false",
|
|
128
128
|
// This define should be safe to use for Next 14.2+, earlier versions (13.5 and less) will cause trouble
|
|
129
129
|
"process.env.__NEXT_EXPERIMENTAL_REACT": `${needsExperimentalReact(nextConfig)}`,
|
|
130
130
|
// Fix `res.validate` in Next 15.4 (together with the `route-module` patch)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { loadBuildId, loadPrerenderManifest } from "@opennextjs/aws/adapters/config/util.js";
|
|
4
|
-
import { esbuildSync
|
|
4
|
+
import { esbuildSync } from "@opennextjs/aws/build/helper.js";
|
|
5
5
|
export function compileDurableObjects(buildOpts) {
|
|
6
6
|
const _require = createRequire(import.meta.url);
|
|
7
7
|
const entryPoints = [
|
|
@@ -9,10 +9,10 @@ export function compileDurableObjects(buildOpts) {
|
|
|
9
9
|
_require.resolve("@opennextjs/cloudflare/durable-objects/sharded-tag-cache"),
|
|
10
10
|
_require.resolve("@opennextjs/cloudflare/durable-objects/bucket-cache-purge"),
|
|
11
11
|
];
|
|
12
|
-
const
|
|
13
|
-
const prerenderManifest = loadPrerenderManifest(
|
|
12
|
+
const buildOutputDotNextDir = path.join(buildOpts.appBuildOutputPath, ".next");
|
|
13
|
+
const prerenderManifest = loadPrerenderManifest(buildOutputDotNextDir);
|
|
14
14
|
const previewModeId = prerenderManifest.preview.previewModeId;
|
|
15
|
-
const BUILD_ID = loadBuildId(
|
|
15
|
+
const BUILD_ID = loadBuildId(buildOutputDotNextDir);
|
|
16
16
|
return esbuildSync({
|
|
17
17
|
entryPoints,
|
|
18
18
|
bundle: true,
|
|
@@ -20,6 +20,7 @@ import { openNextResolvePlugin } from "@opennextjs/aws/plugins/resolve.js";
|
|
|
20
20
|
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
21
21
|
import { getOpenNextConfig } from "../../../api/config.js";
|
|
22
22
|
import { patchResRevalidate } from "../patches/plugins/res-revalidate.js";
|
|
23
|
+
import { patchTurbopackRuntime } from "../patches/plugins/turbopack.js";
|
|
23
24
|
import { patchUseCacheIO } from "../patches/plugins/use-cache.js";
|
|
24
25
|
import { normalizePath } from "../utils/index.js";
|
|
25
26
|
import { copyWorkerdPackages } from "../utils/workerd.js";
|
|
@@ -142,6 +143,7 @@ async function generateBundle(name, options, fnOptions, codeCustomization) {
|
|
|
142
143
|
// Cloudflare specific patches
|
|
143
144
|
patchResRevalidate,
|
|
144
145
|
patchUseCacheIO,
|
|
146
|
+
patchTurbopackRuntime,
|
|
145
147
|
...additionalCodePatches,
|
|
146
148
|
]);
|
|
147
149
|
// Build Lambda code
|
|
@@ -16,6 +16,7 @@ export function patchVercelOgLibrary(buildOpts) {
|
|
|
16
16
|
for (const traceInfoPath of globSync(path.join(appBuildOutputPath, ".next/server/**/*.nft.json"), {
|
|
17
17
|
windowsPathsNoEscape: true,
|
|
18
18
|
})) {
|
|
19
|
+
let edgeFilePatched = false;
|
|
19
20
|
const traceInfo = JSON.parse(readFileSync(traceInfoPath, { encoding: "utf8" }));
|
|
20
21
|
const tracedNodePath = traceInfo.files.find((p) => p.endsWith("@vercel/og/index.node.js"));
|
|
21
22
|
if (!tracedNodePath)
|
|
@@ -26,14 +27,20 @@ export function patchVercelOgLibrary(buildOpts) {
|
|
|
26
27
|
if (!existsSync(outputEdgePath)) {
|
|
27
28
|
const tracedEdgePath = path.join(path.dirname(traceInfoPath), tracedNodePath.replace("index.node.js", "index.edge.js"));
|
|
28
29
|
copyFileSync(tracedEdgePath, outputEdgePath);
|
|
30
|
+
}
|
|
31
|
+
if (!edgeFilePatched) {
|
|
32
|
+
edgeFilePatched = true;
|
|
29
33
|
// Change font fetches in the library to use imports.
|
|
30
34
|
const node = parseFile(outputEdgePath);
|
|
31
35
|
const { edits, matches } = patchVercelOgFallbackFont(node);
|
|
32
36
|
writeFileSync(outputEdgePath, node.commitEdits(edits));
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
if (matches.length > 0) {
|
|
38
|
+
const fontFileName = matches[0].getMatch("PATH").text();
|
|
39
|
+
renameSync(path.join(outputDir, fontFileName), path.join(outputDir, `${fontFileName}.bin`));
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
// Change node imports for the library to edge imports.
|
|
43
|
+
// This is only useful when turbopack is not used to bundle the function.
|
|
37
44
|
const routeFilePath = traceInfoPath.replace(appBuildOutputPath, packagePath).replace(".nft.json", "");
|
|
38
45
|
const node = parseFile(routeFilePath);
|
|
39
46
|
const { edits } = patchVercelOgImport(node);
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { patchCode } from "@opennextjs/aws/build/patch/astCodePatcher.js";
|
|
2
|
+
import { getCrossPlatformPathRegex } from "@opennextjs/aws/utils/regex.js";
|
|
3
|
+
const inlineChunksRule = `
|
|
4
|
+
rule:
|
|
5
|
+
kind: call_expression
|
|
6
|
+
pattern: require(resolved)
|
|
7
|
+
fix:
|
|
8
|
+
requireChunk(chunkPath)
|
|
9
|
+
`;
|
|
10
|
+
export const patchTurbopackRuntime = {
|
|
11
|
+
name: "inline-turbopack-chunks",
|
|
12
|
+
patches: [
|
|
13
|
+
{
|
|
14
|
+
versions: ">=15.0.0",
|
|
15
|
+
pathFilter: getCrossPlatformPathRegex(String.raw `\[turbopack\]_runtime\.js$`, {
|
|
16
|
+
escape: false,
|
|
17
|
+
}),
|
|
18
|
+
contentFilter: /loadRuntimeChunkPath/,
|
|
19
|
+
patchCode: async ({ code, tracedFiles }) => {
|
|
20
|
+
let patched = patchCode(code, inlineExternalImportRule);
|
|
21
|
+
patched = patchCode(patched, inlineChunksRule);
|
|
22
|
+
return `${patched}\n${inlineChunksFn(tracedFiles)}`;
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
],
|
|
26
|
+
};
|
|
27
|
+
function getInlinableChunks(tracedFiles) {
|
|
28
|
+
const chunks = new Set();
|
|
29
|
+
for (const file of tracedFiles) {
|
|
30
|
+
if (file === "[turbopack]_runtime.js") {
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (file.includes(".next/server/chunks/")) {
|
|
34
|
+
chunks.add(file);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
return Array.from(chunks);
|
|
38
|
+
}
|
|
39
|
+
function inlineChunksFn(tracedFiles) {
|
|
40
|
+
// From the outputs, we extract every chunks
|
|
41
|
+
const chunks = getInlinableChunks(tracedFiles);
|
|
42
|
+
return `
|
|
43
|
+
function requireChunk(chunkPath) {
|
|
44
|
+
switch(chunkPath) {
|
|
45
|
+
${chunks
|
|
46
|
+
.map((chunk) => ` case "${
|
|
47
|
+
// we only want the path after /path/to/.next/
|
|
48
|
+
chunk.replace(/.*\/\.next\//, "")}": return require("${chunk}");`)
|
|
49
|
+
.join("\n")}
|
|
50
|
+
default:
|
|
51
|
+
throw new Error(\`Not found \${chunkPath}\`);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
`;
|
|
55
|
+
}
|
|
56
|
+
// Turbopack imports `og` via `externalImport`.
|
|
57
|
+
// We patch it to:
|
|
58
|
+
// - add the explicit path so that the file is inlined by wrangler
|
|
59
|
+
// - use the edge version of the module instead of the node version.
|
|
60
|
+
//
|
|
61
|
+
// Modules that are not inlined (no added to the switch), would generate an error similar to:
|
|
62
|
+
// Failed to load external module path/to/module: Error: No such module "path/to/module"
|
|
63
|
+
const inlineExternalImportRule = `
|
|
64
|
+
rule:
|
|
65
|
+
pattern: "$RAW = await import($ID)"
|
|
66
|
+
inside:
|
|
67
|
+
regex: "externalImport"
|
|
68
|
+
kind: function_declaration
|
|
69
|
+
stopBy: end
|
|
70
|
+
fix: |-
|
|
71
|
+
switch ($ID) {
|
|
72
|
+
case "next/dist/compiled/@vercel/og/index.node.js":
|
|
73
|
+
$RAW = await import("next/dist/compiled/@vercel/og/index.edge.js");
|
|
74
|
+
break;
|
|
75
|
+
default:
|
|
76
|
+
$RAW = await import($ID);
|
|
77
|
+
}
|
|
78
|
+
`;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import * as buildHelper from "@opennextjs/aws/build/helper.js";
|
|
2
|
+
/**
|
|
3
|
+
* Returns whether the project is using a Node.js middleware.
|
|
4
|
+
*
|
|
5
|
+
* @param options
|
|
6
|
+
* @returns Whether the project is using a Node.js middleware
|
|
7
|
+
*/
|
|
8
|
+
export declare function useNodeMiddleware(options: buildHelper.BuildOptions): boolean;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { loadFunctionsConfigManifest, loadMiddlewareManifest } from "@opennextjs/aws/adapters/config/util.js";
|
|
3
|
+
/**
|
|
4
|
+
* Returns whether the project is using a Node.js middleware.
|
|
5
|
+
*
|
|
6
|
+
* @param options
|
|
7
|
+
* @returns Whether the project is using a Node.js middleware
|
|
8
|
+
*/
|
|
9
|
+
export function useNodeMiddleware(options) {
|
|
10
|
+
const buildOutputDotNextDir = path.join(options.appBuildOutputPath, ".next");
|
|
11
|
+
// Look for the edge middleware
|
|
12
|
+
const middlewareManifest = loadMiddlewareManifest(buildOutputDotNextDir);
|
|
13
|
+
const edgeMiddleware = middlewareManifest.middleware["/"];
|
|
14
|
+
if (edgeMiddleware) {
|
|
15
|
+
// The app uses an edge middleware
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
// Look for the node middleware
|
|
19
|
+
const functionsConfigManifest = loadFunctionsConfigManifest(buildOutputDotNextDir);
|
|
20
|
+
return Boolean(functionsConfigManifest?.functions["/_middleware"]);
|
|
21
|
+
}
|
|
@@ -71,6 +71,18 @@ export async function getEnvFromPlatformProxy(options, buildOpts) {
|
|
|
71
71
|
* @returns escaped arg
|
|
72
72
|
*/
|
|
73
73
|
export function quoteShellMeta(arg) {
|
|
74
|
+
if (process.platform === "win32") {
|
|
75
|
+
if (arg.length === 0) {
|
|
76
|
+
return '""';
|
|
77
|
+
}
|
|
78
|
+
const needsEscaping = /[&|<>^()%!"]/;
|
|
79
|
+
const needsQuotes = /\s/.test(arg) || needsEscaping.test(arg);
|
|
80
|
+
let escaped = arg.replace(/"/g, '""');
|
|
81
|
+
if (/[&|<>^()%!]/.test(arg)) {
|
|
82
|
+
escaped = escaped.replace(/[&|<>^()%!]/g, "^$&");
|
|
83
|
+
}
|
|
84
|
+
return needsQuotes ? `"${escaped}"` : escaped;
|
|
85
|
+
}
|
|
74
86
|
if (/["\s]/.test(arg) && !/'/.test(arg)) {
|
|
75
87
|
return `'${arg.replace(/(['\\])/g, "\\$1")}'`;
|
|
76
88
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import url from "node:url";
|
|
4
5
|
import { compileOpenNextConfig } from "@opennextjs/aws/build/compileConfig.js";
|
|
5
6
|
import { normalizeOptions } from "@opennextjs/aws/build/helper.js";
|
|
6
7
|
import { printHeader, showWarningOnWindows } from "@opennextjs/aws/build/utils.js";
|
|
@@ -50,7 +51,7 @@ export async function retrieveCompiledConfig() {
|
|
|
50
51
|
logger.error("Could not find compiled Open Next config, did you run the build command?");
|
|
51
52
|
process.exit(1);
|
|
52
53
|
}
|
|
53
|
-
const config = await import(configPath).then((mod) => mod.default);
|
|
54
|
+
const config = await import(url.pathToFileURL(configPath).href).then((mod) => mod.default);
|
|
54
55
|
ensureCloudflareConfig(config);
|
|
55
56
|
return { config };
|
|
56
57
|
}
|