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

transpileToJS feature #46

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,12 @@ rust({
//
// This is useful for libraries which want to export TypeScript types.
typescriptDeclarationDir: null,

// Whether to transpile the wasm output to JS using wasm2js tool.
// Note that this option may slow down your application.
//
// This requires `inlineWasm: true`, `directExports: true` and `synchronous: true`.
transpileToJS: false,
},
})
```
Expand All @@ -225,6 +231,12 @@ rust({

This is necessary because extension files are put into a separate URL namespace, so you must use `chrome.runtime.getURL` to get the correct URL.

If you want to use inline wasm with extensions, the browser will treat it as remote wasm execution and may block it.
To avoid this, you can use the `transpileToJS` option, which will transpile wasm to JS.
Keep in mind that this option may slow down your extension.

```js

### Environment variables

You can use the following environment variables to customize some aspects of this plugin:
Expand Down
98 changes: 50 additions & 48 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,48 +1,50 @@
{
"name": "@wasm-tool/rollup-plugin-rust",
"author": "Pauan <[email protected]>",
"description": "Rollup plugin for bundling and importing Rust crates.",
"version": "2.4.5",
"license": "MIT",
"repository": "github:wasm-tool/rollup-plugin-rust",
"homepage": "https://github.com/wasm-tool/rollup-plugin-rust#readme",
"bugs": "https://github.com/wasm-tool/rollup-plugin-rust/issues",
"main": "src/index.js",
"scripts": {
"test:foo": "cd tests/src/foo && yarn install",
"test": "yarn test:foo && cd tests && rimraf dist/js && rollup --bundleConfigAsCjs --config",
"test:watch": "yarn test:foo && cd tests && rimraf dist/js && rollup --bundleConfigAsCjs --config --watch",
"test:serve": "live-server tests/dist"
},
"directories": {
"example": "example"
},
"keywords": [
"rollup-plugin",
"vite-plugin",
"rust-wasm",
"wasm",
"rust",
"rollup",
"plugin",
"webassembly",
"wasm-bindgen",
"wasm-pack"
],
"dependencies": {
"@iarna/toml": "^2.2.5",
"@rollup/pluginutils": "^5.0.2",
"binaryen": "^111.0.0",
"chalk": "^4.0.0",
"glob": "^10.2.2",
"node-fetch": "^2.0.0",
"rimraf": "^5.0.0",
"tar": "^6.1.11"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-node-resolve": "^15.0.2",
"live-server": "^1.2.1",
"rollup": "^3.21.0"
}
}
{
"name": "@wasm-tool/rollup-plugin-rust",
"author": "Pauan <[email protected]>",
"description": "Rollup plugin for bundling and importing Rust crates.",
"version": "2.4.5",
"license": "MIT",
"repository": "github:wasm-tool/rollup-plugin-rust",
"homepage": "https://github.com/wasm-tool/rollup-plugin-rust#readme",
"bugs": "https://github.com/wasm-tool/rollup-plugin-rust/issues",
"main": "src/index.js",
"types": "src/index.d.ts",
"scripts": {
"test:foo": "cd tests/src/foo && yarn install",
"test": "yarn test:foo && cd tests && rimraf dist/js && rollup --bundleConfigAsCjs --config",
"test:watch": "yarn test:foo && cd tests && rimraf dist/js && rollup --bundleConfigAsCjs --config --watch",
"test:serve": "live-server tests/dist"
},
"directories": {
"example": "example"
},
"keywords": [
"rollup-plugin",
"vite-plugin",
"rust-wasm",
"wasm",
"rust",
"rollup",
"plugin",
"webassembly",
"wasm-bindgen",
"wasm-pack"
],
"dependencies": {
"@iarna/toml": "^2.2.5",
"@rollup/pluginutils": "^5.0.2",
"binaryen": "^111.0.0",
"chalk": "^4.0.0",
"glob": "^10.2.2",
"node-fetch": "^2.0.0",
"replace-in-files": "^3.0.0",
"rimraf": "^5.0.0",
"tar": "^6.1.11"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^24.1.0",
"@rollup/plugin-node-resolve": "^15.2.3",
"live-server": "^1.2.1",
"rollup": "^3.29.4"
}
}
22 changes: 22 additions & 0 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export interface ExperimentalRustOptions {
directExports?: boolean;
synchronous?: boolean;
typescriptDeclarationDir?: string;
transpileToJS?: boolean;
}

export interface RustOptions {
serverPath?: string;
nodejs?: boolean;
debug?: boolean;
verbose?: boolean;
inlineWasm?: boolean;
cargoArgs?: string[];
wasmBindgenArgs?: string[];
wasmOptArgs?: string[];
watchPatterns?: string[];
importHook?: (arg0: string) => string;
experimental?: ExperimentalRustOptions;
}

export default function rust(options?: RustOptions): any;
107 changes: 95 additions & 12 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ const $toml = require("@iarna/toml");
const { createFilter } = require("@rollup/pluginutils");
const { glob, rm, mv, mkdir, read, readString, writeString, exec, spawn, lock, debug, getEnv } = require("./utils");
const { run_wasm_bindgen } = require("./wasm-bindgen");

const replaceInFiles = require("replace-in-files");
const wasm2jsPath = require.resolve('binaryen/bin/wasm2js');
const rollup = require('rollup');
const { nodeResolve } = require("@rollup/plugin-node-resolve");

const PREFIX = "./.__rollup-plugin-rust__";
const ENTRY_SUFFIX = "?rollup-plugin-rust-entry";
Expand Down Expand Up @@ -112,6 +115,55 @@ async function load_wasm(out_dir, options) {
return await read(wasm_path);
}

async function bundleJS(path) {
const bundle = await rollup.rollup({
input: path,
plugins: [ nodeResolve() ],
});

const { output } = await bundle.generate({
format: 'es',
});

return output[0].code;
}

async function run_wasm2js(out_dir, name, options) {
const wasm2js_args = [
$path.join(out_dir, "index_bg.wasm"),
"-o", $path.join(out_dir, name+".js"),
];

if (options.verbose) {
debug(`Running ${wasm2js_args.join(" ")}`);
}

let jsContent;
try {
await spawn(wasm2jsPath, wasm2js_args, { cwd: out_dir, stdio: "inherit" });

await replaceInFiles({
files: [
$path.join(out_dir, "index.js"),
$path.join(out_dir, "index_bg.js"),
],
from: /index_bg\.wasm/g,
to: name + ".js",
});

jsContent = await bundleJS($path.join(out_dir, "index.js"));
} catch (e) {
if (options.verbose) {
throw e;
} else {
const e = new Error("wasm2js failed");
e.stack = null;
throw e;
}
}

return {name, compiledOutput: jsContent.toString(), out_dir};
}

async function compile_rust(cx, dir, id, target_dir, source, options) {
const toml = $toml.parse(source);
Expand Down Expand Up @@ -152,9 +204,13 @@ async function compile_rust(cx, dir, id, target_dir, source, options) {
await run_wasm_opt(cx, out_dir, options);
}

const wasm = await load_wasm(out_dir, options);
const compiledOutput = await load_wasm(out_dir, options);

if(options.experimental.transpileToJS) {
return run_wasm2js(out_dir, name, options);
}

return { name, wasm, out_dir };
return { name, compiledOutput, out_dir };
});

} catch (e) {
Expand Down Expand Up @@ -204,7 +260,7 @@ async function build(cx, state, id, options) {
}


function compile_js_inline(options, import_path, real_path, wasm, is_entry) {
function compile_js_inline(options, import_path, real_path, compiledOutput, is_entry) {
let export_code;

if (!is_entry && options.experimental.directExports) {
Expand All @@ -214,6 +270,23 @@ function compile_js_inline(options, import_path, real_path, wasm, is_entry) {
export_code = "";
}

if(options.experimental.transpileToJS) {
if (!options.experimental.directExports) {
throw new Error("transpileToJS can only be used with experimental.directExports: true");
}
if(!options.experimental.synchronous) {
throw new Error("transpileToJS can only be used with experimental.synchronous: true");
}

return {
code: compiledOutput,
map: { mappings: '' },
moduleSideEffects: true,
meta: {
"rollup-plugin-rust": { root: false, real_path }
},
};
}

let main_code;
let sideEffects;
Expand Down Expand Up @@ -258,7 +331,7 @@ function compile_js_inline(options, import_path, real_path, wasm, is_entry) {
}


const wasm_string = JSON.stringify(wasm.toString("base64"));
const wasm_string = JSON.stringify(compiledOutput.toString("base64"));

const code = `
${export_code}
Expand Down Expand Up @@ -309,6 +382,9 @@ function compile_js_inline(options, import_path, real_path, wasm, is_entry) {

function compile_js_load(cx, state, options, import_path, real_path, name, wasm, is_entry) {
let fileId;
if(options.experimental.transpileToJS) {
throw new Error("transpileToJS can only be used with inlineWasm: true");
}

if (options.outDir == null) {
fileId = cx.emitFile({
Expand Down Expand Up @@ -433,19 +509,19 @@ function compile_js_load(cx, state, options, import_path, real_path, name, wasm,
}


function compile_js(cx, state, name, wasm, is_entry, out_dir, options) {
function compile_js(cx, state, name, compiledOutput, is_entry, out_dir, options) {
const real_path = $path.join(out_dir, "index.js");

// This returns a fake file path, this ensures that the directory is the
// same as the Cargo.toml file, which is necessary in order to make npm
// package imports work correctly.
const import_path = `"${PREFIX}${name}/index.js"`;

if (options.inlineWasm) {
return compile_js_inline(options, import_path, real_path, wasm, is_entry);
if (options.inlineWasm || options.transpileToJS) {
return compile_js_inline(options, import_path, real_path, compiledOutput, is_entry);

} else {
return compile_js_load(cx, state, options, import_path, real_path, name, wasm, is_entry);
return compile_js_load(cx, state, options, import_path, real_path, name, compiledOutput, is_entry);
}
}

Expand Down Expand Up @@ -514,11 +590,14 @@ async function load_cargo_toml(cx, state, id, is_entry, meta, options) {

await compile_dts(cx, state, result.name, result.out_dir, options);

return compile_js(cx, state, result.name, result.wasm, is_entry, result.out_dir, options);
return compile_js(cx, state, result.name, result.compiledOutput, is_entry, result.out_dir, options);
}


/**
* @param {import("./index").RustOptions} options
*/
module.exports = function rust(options = {}) {

// TODO should the filter affect the watching ?
// TODO should the filter affect the Rust compilation ?
const filter = createFilter(options.include, options.exclude);
Expand Down Expand Up @@ -553,7 +632,7 @@ module.exports = function rust(options = {}) {
if (options.inlineWasm == null) {
options.inlineWasm = false;
}

if (options.verbose == null) {
options.verbose = false;
}
Expand All @@ -573,6 +652,10 @@ module.exports = function rust(options = {}) {
if (options.experimental.synchronous == null) {
options.experimental.synchronous = false;
}

if(options.experimental.transpileToJS == null) {
options.experimental.transpileToJS = false;
}

return {
name: "rust",
Expand Down
5 changes: 4 additions & 1 deletion src/wasm-bindgen.js
Original file line number Diff line number Diff line change
Expand Up @@ -101,9 +101,12 @@ async function run_wasm_bindgen(dir, wasm_path, out_dir, options) {
let wasm_bindgen_args = [
"--out-dir", out_dir,
"--out-name", "index",
"--target", "web",
"--omit-default-module-path",
];

if (!options.experimental.transpileToJs) {
wasm_bindgen_args.push("--target", "web");
}

if (options.experimental.typescriptDeclarationDir == null) {
wasm_bindgen_args.push("--no-typescript");
Expand Down