Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(wasm): support output esm imports #1565

Merged
merged 6 commits into from
Aug 11, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions src/presets/cloudflare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,15 @@
JSON.stringify({ lockfileVersion: 1 }, null, 2)
);
},
"rollup:before"(nitro) {
antfu marked this conversation as resolved.
Show resolved Hide resolved
if (nitro.options.experimental?.wasm) {
if (nitro.options.experimental.wasm === true) {
nitro.options.experimental.wasm = {};
}
// Cloudflare Workers requires WASM to be directly imported
// https://community.cloudflare.com/t/fixed-cloudflare-workers-slow-with-moderate-sized-webassembly-bindings/184668/3
nitro.options.experimental.wasm.directImport = true;
antfu marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 33 in src/presets/cloudflare.ts

View check run for this annotation

Codecov / codecov/patch

src/presets/cloudflare.ts#L27-L33

Added lines #L27 - L33 were not covered by tests
},
},
});
14 changes: 10 additions & 4 deletions src/rollup/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@
import { sanitizeFilePath, resolvePath } from "mlly";
import unimportPlugin from "unimport/unplugin";
import { hash } from "ohash";
import type { Nitro, NitroStaticBuildFlags } from "../types";
import type { Nitro, NitroStaticBuildFlags, WasmOptions } from "../types";
import { resolveAliases } from "../utils";
import { runtimeDir } from "../dirs";
import { version } from "../../package.json";
import { replace } from "./plugins/replace";
import { virtual } from "./plugins/virtual";
import { wasmImport } from "./plugins/wasm-import";
import { dynamicRequire } from "./plugins/dynamic-require";
import { NodeExternalsOptions, externals } from "./plugins/externals";
import { externals as legacyExternals } from "./plugins/externals-legacy";
Expand Down Expand Up @@ -150,9 +151,13 @@
// WASM import support
if (nitro.options.experimental.wasm) {
const options = {
...(nitro.options.experimental.wasm as RollupWasmOptions),
...(nitro.options.experimental.wasm as WasmOptions),

Check warning on line 154 in src/rollup/config.ts

View check run for this annotation

Codecov / codecov/patch

src/rollup/config.ts#L154

Added line #L154 was not covered by tests
};
rollupConfig.plugins.push(wasmPlugin(options));
if (options.directImport) {
rollupConfig.plugins.push(wasmImport());
} else {
rollupConfig.plugins.push(wasmPlugin(options));
antfu marked this conversation as resolved.
Show resolved Hide resolved
}

Check warning on line 160 in src/rollup/config.ts

View check run for this annotation

Codecov / codecov/patch

src/rollup/config.ts#L156-L160

Added lines #L156 - L160 were not covered by tests
}

// Build-time environment variables
Expand Down Expand Up @@ -365,7 +370,8 @@
return { id: _resolved, external: false };
}
}
if (!resolved || resolved.external) {
// @ts-expect-error cast
if (!resolved || (resolved.external && !resolved.__nitro_external__)) {
antfu marked this conversation as resolved.
Show resolved Hide resolved
throw new Error(
`Cannot resolve ${JSON.stringify(id)} from ${JSON.stringify(
from
Expand Down
69 changes: 69 additions & 0 deletions src/rollup/plugins/wasm-import.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import { createHash } from "node:crypto";
import { extname, basename } from "node:path";
import { promises as fs } from "node:fs";
import type { Plugin } from "rollup";

const PLUGIN_NAME = "nitro:wasm-import";
const wasmRegex = /\.wasm$/;

export function wasmImport(): Plugin {
const copies = Object.create(null);

return {
name: PLUGIN_NAME,
async resolveId(id: string, importer: string) {
if (copies[id]) {
return {
id: copies[id].publicFilepath,
external: true,
};
}
if (wasmRegex.test(id)) {
const { id: filepath } =
(await this.resolve(id, importer, { skipSelf: true })) || {};
console.log({ filepath });
if (!filepath || filepath === id) {
return null;
}
const buffer = await fs.readFile(filepath);
const hash = createHash("sha1")
.update(buffer)
.digest("hex")
.slice(0, 16);
const ext = extname(filepath);
const name = basename(filepath, ext);

const outputFileName = "wasm/[name]-[hash][extname]"
.replace(/\[hash]/g, hash)
.replace(/\[extname]/g, ext)
.replace(/\[name]/g, name);
antfu marked this conversation as resolved.
Show resolved Hide resolved

const publicFilepath = `./${outputFileName}`;

copies[id] = {
filename: outputFileName,
publicFilepath,
buffer,
};

return {
id: publicFilepath,
external: true,
};
}
},
async generateBundle() {
await Promise.all(
Object.keys(copies).map(async (name) => {
const copy = copies[name];
await this.emitFile({
type: "asset",
source: copy.buffer,
name: "Rollup WASM Asset",
antfu marked this conversation as resolved.
Show resolved Hide resolved
fileName: copy.filename,
});
})
);
},
};
}

Check warning on line 69 in src/rollup/plugins/wasm-import.ts

View check run for this annotation

Codecov / codecov/patch

src/rollup/plugins/wasm-import.ts#L10-L69

Added lines #L10 - L69 were not covered by tests
9 changes: 8 additions & 1 deletion src/types/nitro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,13 @@ export interface NitroRouteRules
proxy?: { to: string } & ProxyOptions;
}

export interface WasmOptions extends RollupWasmOptions {
/**
* Direct import the wasm file instead of bundling, required in Cloudflare Workers
*/
directImport?: boolean;
}

export interface NitroOptions extends PresetOptions {
// Internal
_config: NitroConfig;
Expand Down Expand Up @@ -216,7 +223,7 @@ export interface NitroOptions extends PresetOptions {
serveStatic: boolean | "node" | "deno";
noPublicDir: boolean;
experimental?: {
wasm?: boolean | RollupWasmOptions;
wasm?: boolean | WasmOptions;
legacyExternals?: boolean;
openAPI?: boolean;
/**
Expand Down