Skip to content

Commit 8392e22

Browse files
authored
fix(middleware): Support wasm in bundled middleware (#802)
1 parent 86f4b44 commit 8392e22

File tree

4 files changed

+81
-42
lines changed

4 files changed

+81
-42
lines changed

.changeset/perfect-cobras-move.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@opennextjs/aws": patch
3+
---
4+
5+
fix(middleware): copy wasm files for bundled middleware

packages/open-next/src/build/createServerBundle.ts

+22-9
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import path from "node:path";
33

44
import type { FunctionOptions, SplittedFunctionOptions } from "types/open-next";
55

6+
import { loadMiddlewareManifest } from "config/util.js";
67
import type { Plugin } from "esbuild";
78
import logger from "../logger.js";
89
import { minifyAll } from "../minimize-js.js";
@@ -13,7 +14,10 @@ import { getCrossPlatformPathRegex } from "../utils/regex.js";
1314
import { bundleNextServer } from "./bundleNextServer.js";
1415
import { compileCache } from "./compileCache.js";
1516
import { copyTracedFiles } from "./copyTracedFiles.js";
16-
import { generateEdgeBundle } from "./edge/createEdgeBundle.js";
17+
import {
18+
copyMiddlewareResources,
19+
generateEdgeBundle,
20+
} from "./edge/createEdgeBundle.js";
1721
import * as buildHelper from "./helper.js";
1822
import { installDependencies } from "./installDeps.js";
1923
import { type CodePatcher, applyCodePatches } from "./patch/codePatcher.js";
@@ -135,12 +139,14 @@ async function generateBundle(
135139
// `.next/standalone/package/path` (ie. `.next`, `server.js`).
136140
// We need to output the handler file inside the package path.
137141
const packagePath = buildHelper.getPackagePath(options);
138-
fs.mkdirSync(path.join(outputPath, packagePath), { recursive: true });
142+
const outPackagePath = path.join(outputPath, packagePath);
143+
144+
fs.mkdirSync(outPackagePath, { recursive: true });
139145

140146
const ext = fnOptions.runtime === "deno" ? "mjs" : "cjs";
141147
fs.copyFileSync(
142148
path.join(options.buildDir, `cache.${ext}`),
143-
path.join(outputPath, packagePath, "cache.cjs"),
149+
path.join(outPackagePath, "cache.cjs"),
144150
);
145151

146152
if (fnOptions.runtime === "deno") {
@@ -150,7 +156,7 @@ async function generateBundle(
150156
// Bundle next server if necessary
151157
const isBundled = fnOptions.experimentalBundledNextServer ?? false;
152158
if (isBundled) {
153-
await bundleNextServer(path.join(outputPath, packagePath), appPath, {
159+
await bundleNextServer(outPackagePath, appPath, {
154160
minify: options.minify,
155161
});
156162
}
@@ -159,15 +165,22 @@ async function generateBundle(
159165
if (!config.middleware?.external) {
160166
fs.copyFileSync(
161167
path.join(options.buildDir, "middleware.mjs"),
162-
path.join(outputPath, packagePath, "middleware.mjs"),
168+
path.join(outPackagePath, "middleware.mjs"),
169+
);
170+
171+
const middlewareManifest = loadMiddlewareManifest(
172+
path.join(options.appBuildOutputPath, ".next"),
173+
);
174+
175+
copyMiddlewareResources(
176+
options,
177+
middlewareManifest.middleware["/"],
178+
outPackagePath,
163179
);
164180
}
165181

166182
// Copy open-next.config.mjs
167-
buildHelper.copyOpenNextConfig(
168-
options.buildDir,
169-
path.join(outputPath, packagePath),
170-
);
183+
buildHelper.copyOpenNextConfig(options.buildDir, outPackagePath);
171184

172185
// Copy env files
173186
buildHelper.copyEnvFile(appBuildOutputPath, packagePath, outputPath);

packages/open-next/src/build/edge/createEdgeBundle.ts

+29-22
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export async function buildEdgeBundle({
5757
name,
5858
additionalPlugins: additionalPluginsFn,
5959
}: BuildEdgeBundleOptions) {
60-
const isInCloudfare = await isEdgeRuntime(overrides);
60+
const isInCloudflare = await isEdgeRuntime(overrides);
6161
function override<T extends keyof Override>(target: T) {
6262
return typeof overrides?.[target] === "string"
6363
? overrides[target]
@@ -103,7 +103,7 @@ export async function buildEdgeBundle({
103103
openNextEdgePlugins({
104104
middlewareInfo,
105105
nextDir: path.join(options.appBuildOutputPath, ".next"),
106-
isInCloudfare,
106+
isInCloudflare,
107107
}),
108108
...additionalPlugins,
109109
// The content updater plugin must be the last plugin
@@ -146,7 +146,7 @@ Object.defineProperty = function(o, p, a) {
146146
};
147147
148148
${
149-
isInCloudfare
149+
isInCloudflare
150150
? ""
151151
: `
152152
const require = (await import("node:module")).createRequire(import.meta.url);
@@ -209,25 +209,7 @@ export async function generateEdgeBundle(
209209
}
210210
const middlewareInfo = functions[0];
211211

212-
//Copy wasm files
213-
const wasmFiles = middlewareInfo.wasm;
214-
mkdirSync(path.join(outputDir, "wasm"), { recursive: true });
215-
for (const wasmFile of wasmFiles) {
216-
fs.copyFileSync(
217-
path.join(buildOutputDotNextDir, wasmFile.filePath),
218-
path.join(outputDir, `wasm/${wasmFile.name}.wasm`),
219-
);
220-
}
221-
222-
// Copy assets
223-
const assets = middlewareInfo.assets;
224-
mkdirSync(path.join(outputDir, "assets"), { recursive: true });
225-
for (const asset of assets) {
226-
fs.copyFileSync(
227-
path.join(buildOutputDotNextDir, asset.filePath),
228-
path.join(outputDir, `assets/${asset.name}`),
229-
);
230-
}
212+
copyMiddlewareResources(options, middlewareInfo, outputDir);
231213

232214
await buildEdgeBundle({
233215
middlewareInfo,
@@ -240,3 +222,28 @@ export async function generateEdgeBundle(
240222
additionalPlugins,
241223
});
242224
}
225+
226+
/**
227+
* Copy wasm files and assets into the destDir.
228+
*/
229+
export function copyMiddlewareResources(
230+
options: BuildOptions,
231+
middlewareInfo: MiddlewareInfo | undefined,
232+
destDir: string,
233+
) {
234+
mkdirSync(path.join(destDir, "wasm"), { recursive: true });
235+
for (const file of middlewareInfo?.wasm ?? []) {
236+
fs.copyFileSync(
237+
path.join(options.appBuildOutputPath, ".next", file.filePath),
238+
path.join(destDir, `wasm/${file.name}.wasm`),
239+
);
240+
}
241+
242+
mkdirSync(path.join(destDir, "assets"), { recursive: true });
243+
for (const file of middlewareInfo?.assets ?? []) {
244+
fs.copyFileSync(
245+
path.join(options.appBuildOutputPath, ".next", file.filePath),
246+
path.join(destDir, `assets/${file.name}`),
247+
);
248+
}
249+
}

packages/open-next/src/plugins/edge.ts

+25-11
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,19 @@ import { getCrossPlatformPathRegex } from "../utils/regex.js";
2424
export interface IPluginSettings {
2525
nextDir: string;
2626
middlewareInfo?: MiddlewareInfo;
27-
isInCloudfare?: boolean;
27+
isInCloudflare?: boolean;
2828
}
2929

3030
/**
3131
* @param opts.nextDir - The path to the .next directory
3232
* @param opts.middlewareInfo - Information about the middleware
33-
* @param opts.isInCloudfare - Whether the code runs on the cloudflare runtime
33+
* @param opts.isInCloudflare - Whether the code runs on the cloudflare runtime
3434
* @returns
3535
*/
3636
export function openNextEdgePlugins({
3737
nextDir,
3838
middlewareInfo,
39-
isInCloudfare,
39+
isInCloudflare,
4040
}: IPluginSettings): Plugin {
4141
const entryFiles =
4242
middlewareInfo?.files.map((file: string) => path.join(nextDir, file)) ?? [];
@@ -94,7 +94,7 @@ globalThis.self = globalThis;
9494
globalThis._ROUTES = ${JSON.stringify(routes)};
9595
9696
${
97-
isInCloudfare
97+
isInCloudflare
9898
? ""
9999
: `
100100
import {readFileSync} from "node:fs";
@@ -138,16 +138,11 @@ if (!globalThis.URLPattern) {
138138
}
139139
`
140140
}
141-
${wasmFiles
142-
.map((file) =>
143-
isInCloudfare
144-
? `import ${file.name} from './wasm/${file.name}.wasm';`
145-
: `const ${file.name} = readFileSync(path.join(__dirname,'/wasm/${file.name}.wasm'));`,
146-
)
147-
.join("\n")}
141+
${importWasm(wasmFiles, { isInCloudflare })}
148142
${entryFiles.map((file) => `require("${file}");`).join("\n")}
149143
${contents}
150144
`;
145+
151146
return {
152147
contents,
153148
};
@@ -202,3 +197,22 @@ ${contents}
202197
},
203198
};
204199
}
200+
201+
function importWasm(
202+
files: MiddlewareInfo["wasm"],
203+
{ isInCloudflare }: { isInCloudflare?: boolean },
204+
) {
205+
return files
206+
.map(({ name }) => {
207+
if (isInCloudflare) {
208+
// As `.next/server/src/middleware.js` references the name,
209+
// using `import ${name} from '...'` would cause ESBuild to rename the import.
210+
// We use `globalThis.${name}` to make sure `middleware.js` reference name will match.
211+
return `import __onw_${name}__ from './wasm/${name}.wasm'
212+
globalThis.${name} = __onw_${name}__`;
213+
}
214+
215+
return `const ${name} = readFileSync(path.join(__dirname,'/wasm/${name}.wasm'));`;
216+
})
217+
.join("\n");
218+
}

0 commit comments

Comments
 (0)