diff --git a/packages/next/build/webpack/loaders/next-middleware-wasm-loader.ts b/packages/next/build/webpack/loaders/next-middleware-wasm-loader.ts index 5b1842c85203..08ad8c8727c6 100644 --- a/packages/next/build/webpack/loaders/next-middleware-wasm-loader.ts +++ b/packages/next/build/webpack/loaders/next-middleware-wasm-loader.ts @@ -10,20 +10,18 @@ const MiddlewareWasmLoader: webpack.loader.Loader = function ( source, _sourceMap ) { - const cb = this.async() - if (!cb) throw new Error('async() must be used with wasm-loader') - const name = `wasm_${sha1(source)}` const file = `middleware-chunks/${name}.wasm` const binding: WasmBinding = { file, name } this._module.buildInfo.nextWasmMiddlewareBinding = binding - this.emitFile(`/${file}`, source, undefined) - - cb(null, `module.exports = globalThis[${JSON.stringify(name)}];`) + this.emitFile(`/${file}`, source, null) + return `module.exports = globalThis[${JSON.stringify(name)}];` } +export const raw = true + +export default MiddlewareWasmLoader + function sha1(source: string | Buffer) { return crypto.createHash('sha1').update(source).digest('hex') } - -export default MiddlewareWasmLoader diff --git a/packages/next/server/next-server.ts b/packages/next/server/next-server.ts index a19c4477d793..2a6d89fe6810 100644 --- a/packages/next/server/next-server.ts +++ b/packages/next/server/next-server.ts @@ -1262,6 +1262,7 @@ export default class NextNodeServer extends BaseServer { name: middlewareInfo.name, paths: middlewareInfo.paths, env: middlewareInfo.env, + wasmBindings: middlewareInfo.wasmBindings, request: { headers: params.request.headers, method, @@ -1347,3 +1348,13 @@ export default class NextNodeServer extends BaseServer { } } } + +function loadWasmBindings( + wasmBindings: { file: string; name: string }[] +): Record { + const wasmBindingsMap: Record = {} + for (const { file, name } of wasmBindings) { + wasmBindingsMap[name] = WebAssembly.compile(fs.readFileSync(file)) + } + return wasmBindingsMap +} diff --git a/packages/next/server/require.ts b/packages/next/server/require.ts index 9c34b8538300..1e1293c69e50 100644 --- a/packages/next/server/require.ts +++ b/packages/next/server/require.ts @@ -85,7 +85,12 @@ export function getMiddlewareInfo(params: { distDir: string page: string serverless: boolean -}): { name: string; paths: string[]; env: string[] } { +}): { + name: string + paths: string[] + env: string[] + wasmBindings: { file: string; name: string }[] +} { const serverBuildPath = join( params.distDir, params.serverless && !params.dev ? SERVERLESS_DIRECTORY : SERVER_DIRECTORY @@ -113,5 +118,9 @@ export function getMiddlewareInfo(params: { name: pageInfo.name, paths: pageInfo.files.map((file) => join(params.distDir, file)), env: pageInfo.env ?? [], + wasmBindings: (pageInfo.wasmBindings ?? []).map((binding) => ({ + ...binding, + file: join(params.distDir, binding.file), + })), } } diff --git a/packages/next/server/web/sandbox/context.ts b/packages/next/server/web/sandbox/context.ts index 42f480bb6a31..02c5e9e102bd 100644 --- a/packages/next/server/web/sandbox/context.ts +++ b/packages/next/server/web/sandbox/context.ts @@ -52,18 +52,19 @@ const caches = new Map< * run in within the context. It may or may not use a cache depending on * the parameters. */ -export function getModuleContext(options: { +export async function getModuleContext(options: { module: string onWarning: (warn: Error) => void useCache: boolean env: string[] + wasmBindings: WasmBinding[] }) { let moduleCache = options.useCache ? caches.get(options.module) - : createModuleContext(options) + : await createModuleContext(options) if (!moduleCache) { - moduleCache = createModuleContext(options) + moduleCache = await createModuleContext(options) caches.set(options.module, moduleCache) } @@ -95,10 +96,11 @@ export function getModuleContext(options: { * 2. Dependencies that require runtime globals such as Blob. * 3. Dependencies that are scoped for the provided parameters. */ -function createModuleContext(options: { +async function createModuleContext(options: { onWarning: (warn: Error) => void module: string env: string[] + wasmBindings: WasmBinding[] }) { const requireCache = new Map([ [require.resolve('next/dist/compiled/cookie'), { exports: cookie }], @@ -166,6 +168,8 @@ function createModuleContext(options: { return fetch(String(input), init) } + Object.assign(context, await loadWasmBindings(options.wasmBindings)) + return moduleCache } @@ -260,3 +264,16 @@ function buildEnvironmentVariablesFrom( const pairs = keys.map((key) => [key, process.env[key]]) return Object.fromEntries(pairs) } + +async function loadWasmBindings( + wasmBindings: WasmBinding[] +): Promise> { + const modules: Record = {} + for (const binding of wasmBindings) { + const module = await WebAssembly.compile(readFileSync(binding.file)) + modules[binding.name] = module + } + return modules +} + +export type WasmBinding = { file: string; name: string } diff --git a/packages/next/server/web/sandbox/sandbox.ts b/packages/next/server/web/sandbox/sandbox.ts index 576e8be7580c..4a039a8f4550 100644 --- a/packages/next/server/web/sandbox/sandbox.ts +++ b/packages/next/server/web/sandbox/sandbox.ts @@ -1,5 +1,5 @@ import type { RequestData, FetchEventResult } from '../types' -import { getModuleContext } from './context' +import { getModuleContext, WasmBinding } from './context' export async function run(params: { name: string @@ -8,12 +8,14 @@ export async function run(params: { paths: string[] request: RequestData useCache: boolean + wasmBindings: WasmBinding[] }): Promise { - const { runInContext, context } = getModuleContext({ + const { runInContext, context } = await getModuleContext({ module: params.name, onWarning: params.onWarning, useCache: params.useCache !== false, env: params.env, + wasmBindings: params.wasmBindings, }) for (const paramPath of params.paths) {