Repository: wasm-tool/rollup-plugin-rust
Branch: master
Commit: 6b08aa0bfe50
Files: 28
Total size: 54.2 KB
Directory structure:
gitextract_n_az4ztq/
├── .gitignore
├── .yarnrc.yml
├── README.md
├── example/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── dist/
│ │ └── index.html
│ ├── package.json
│ ├── rollup.config.js
│ ├── rust-toolchain.toml
│ └── src/
│ └── lib.rs
├── package.json
├── src/
│ ├── cargo.js
│ ├── index.js
│ ├── typescript.js
│ ├── utils.js
│ ├── wasm-bindgen.js
│ └── wasm-opt.js
└── tests/
├── .gitignore
├── Cargo.toml
├── dist/
│ └── index.html
├── rollup.config.js
└── src/
├── bar/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── bar.js
├── foo/
│ ├── Cargo.toml
│ ├── package.json
│ └── src/
│ └── lib.rs
└── foo.js
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitignore
================================================
node_modules
Cargo.lock
yarn.lock
package-lock.json
yarn-error.log
/typescript
/example-multi
.DS_Store
.yarn
================================================
FILE: .yarnrc.yml
================================================
nodeLinker: node-modules
================================================
FILE: README.md
================================================
# rollup-plugin-rust
Rollup plugin for bundling and importing Rust crates.
This plugin internally uses [`wasm-bindgen`](https://rustwasm.github.io/docs/wasm-bindgen/).
`wasm-bindgen` is automatically installed, you do not need to install it separately.
## Installation
First, make sure that [rustup](https://rustup.rs/) is installed.
If you are on Windows, then you also need to install the [Visual Studio build tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools&rel=16) (make sure to enable the "C++ build tools" option).
Lastly, run this:
```sh
yarn add --dev @wasm-tool/rollup-plugin-rust binaryen
```
Or if you're using npm you can use this instead:
```sh
npm install --save-dev @wasm-tool/rollup-plugin-rust binaryen
```
## Usage
Add the plugin to your `rollup.config.js`, and now you can use `Cargo.toml` files as entries:
```js
import rust from "@wasm-tool/rollup-plugin-rust";
export default {
format: "es",
input: {
foo: "Cargo.toml",
},
plugins: [
rust(),
],
};
```
You can import as many different `Cargo.toml` files as you want, each one will be compiled separately.
See the [example folder](/example) for a simple working example. First run `yarn install`, and then `yarn watch` for development. Use `yarn build` to build for production.
### Importing `Cargo.toml` within `.js`
It is also possible to import a `Cargo.toml` file inside of a `.js` file, like this:
```js
import { foo, bar } from "./path/to/Cargo.toml";
// Use functions which were exported from Rust...
```
----
## Extra Tips
### Nightly
It is recommended to use the [nightly toolchain](https://rust-lang.github.io/rustup/overrides.html#the-toolchain-file) because it significantly reduces the size of the `.wasm` file.
You can use nightly by creating a `rust-toolchain.toml` file in your project directory:
```toml
[toolchain]
channel = "nightly-2025-11-20"
components = [ "rust-std", "rust-src", "rustfmt", "clippy" ]
targets = [ "wasm32-unknown-unknown" ]
```
You can change the `channel` to upgrade to the latest nightly version (or downgrade to a past nightly version).
After changing the `rust-toolchain.toml` file, you might need to run `rustup show` in order to download the correct Rust version.
### Workspaces
When compiling multiple crates it is highly recommended to use a [workspace](https://doc.rust-lang.org/cargo/reference/manifest.html#the-workspace-section) to improve compile times.
Create a `Cargo.toml` file in the root of your project which lists out the sub-crates that are a part of the workspace:
```toml
[workspace]
members = [
"src/foo",
"src/bar",
]
```
### Optimizing for size
By default the Rust compiler optimizes for maximum runtime performance, but this comes at the cost of a bigger file size.
On the web it is desirable to have a small file size, because a smaller `.wasm` file will download faster.
This plugin automatically optimizes for smaller file size, but you can reduce the size even further by adding this into your `Cargo.toml`:
```toml
[profile.release]
opt-level = "z"
```
You can also try `opt-level = "s"` which in some cases might produce a smaller file size.
If you're using workspaces, make sure to add that into your *workspace* `Cargo.toml`, not the sub-crates.
### Usage with Vite
This plugin works out of the box with Vite, however Vite has SSR, which means that it runs your code on both the server and browser.
This can cause errors when loading Wasm files, so you need to disable SSR when loading the Wasm:
```js
async function loadWasm() {
// This code will only run in the browser
if (!import.meta.env.SSR) {
const { foo, bar } = await import("./path/to/Cargo.toml");
// Use functions which were exported from Rust...
}
}
```
### Customizing the Wasm loading
For very advanced use cases, you might want to manually initialize the `.wasm` code.
If you add `?custom` when importing a `Cargo.toml` file, it will give you:
* `module` which is the `URL` of the `.wasm` file, or a `Uint8Array` if using `inlineWasm: true`.
* `init` which is a function that initializes the Wasm and returns a Promise. The `init` function accepts these options:
* `module` which is a `URL` or `Uint8Array` or `WebAssembly.Module` for the `.wasm` code.
* `memory` which is a `WebAssembly.Memory` that will be used as the memory for the Wasm.
* `initSync` which is a function that *synchronously* initializes the Wasm.
The `initSync` function should be avoided as much as possible, strongly prefer using `init` instead.
The `initSync` function accepts these options:
* `module` which is a `Uint8Array` or `WebAssembly.Module` for the `.wasm` code.
* `memory` which is a `WebAssembly.Memory` that will be used as the memory for the Wasm.
```js
import { module, init } from "./path/to/Cargo.toml?custom";
async function loadWasm() {
const { foo, bar } = await init({
// The URL or Uint8Array which will be initialized.
module: module,
// The WebAssembly.Memory which will be used for the Wasm.
//
// If this is undefined then it will automatically create a new memory.
//
// This is useful for doing multi-threading with multiple Workers sharing the same SharedArrayBuffer.
memory: undefined,
});
// Use functions which were exported from Rust...
}
```
----
## Build options
The default options are good for most use cases, so you generally shouldn't need to change them.
These are the default options:
```js
rust({
// Whether the code will be run in Node.js or not.
//
// This is needed because Node.js does not support `fetch`.
nodejs: false,
// Whether to inline the `.wasm` file into the `.js` file.
//
// This is slower and it increases the file size by ~33%,
// but it does not require a separate `.wasm` file.
inlineWasm: false,
// Whether to display extra compilation information in the console.
verbose: false,
extraArgs: {
// Extra arguments passed to `cargo`.
cargo: [],
// Extra arguments passed to `rustc`, this is equivalent to `RUSTFLAGS`.
rustc: [],
// Extra arguments passed to `wasm-bindgen`.
wasmBindgen: [],
// Extra arguments passed to `wasm-opt`.
wasmOpt: ["-O", "--enable-threads", "--enable-bulk-memory", "--enable-bulk-memory-opt"],
},
optimize: {
// Whether to build in release mode.
//
// In watch mode this defaults to false.
release: true,
// Whether to run wasm-opt.
//
// In watch mode this defaults to false.
wasmOpt: true,
// Whether to use optimized rustc settings.
//
// This slows down compilation but significantly reduces the file size.
//
// If you use the nightly toolchain, this will reduce the file size even more.
rustc: true,
// These options default to false in watch mode.
strip: {
// Removes location information, resulting in lower file size but worse stace traces.
// Currently this only works on nightly.
location: true,
// Removes debug formatting from strings, such as `format!("{:?}", ...)`
// Currently this only works on nightly.
formatDebug: true,
},
},
// Which files it should watch in watch mode. This is relative to the Cargo.toml file.
// Supports all of the glob syntax: https://www.npmjs.com/package/glob
watchPatterns: ["src/**"],
// These options should not be relied upon, they can change or disappear in future versions.
experimental: {
// Compiles with atomics and enables multi-threading.
// Currently this only works on nightly.
atomics: false,
// Whether the Wasm will be initialized synchronously or not.
//
// In the browser you can only use synchronous loading inside of Workers.
//
// This requires `inlineWasm: true`.
synchronous: false,
// Creates a `.d.ts` file for each `Cargo.toml` crate and places them
// into this directory.
//
// This is useful for libraries which want to export TypeScript types.
typescriptDeclarationDir: null,
},
})
```
### Environment variables
You can use the following environment variables to customize some aspects of this plugin:
* `CARGO_BIN` is the path to the `cargo` executable.
* `WASM_BINDGEN_BIN` is the path to the `wasm-bindgen` executable.
* `WASM_OPT_BIN` is the path to the `wasm-opt` executable.
If not specified, they will use a good default value, so you shouldn't need to change them, this is for advanced uses only.
================================================
FILE: example/.gitignore
================================================
node_modules
yarn-error.log
/target
/dist/js
================================================
FILE: example/Cargo.toml
================================================
[package]
name = "example"
version = "0.1.0"
authors = ["You <you@example.com>"]
edition = "2018"
categories = ["wasm"]
[dependencies]
wasm-bindgen = "0.2.58"
console_error_panic_hook = "0.1.6"
[dependencies.web-sys]
version = "0.3.35"
features = [
"console",
]
================================================
FILE: example/dist/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<script src="js/example.js" type="module"></script>
</body>
</html>
================================================
FILE: example/package.json
================================================
{
"private": true,
"type": "module",
"name": "example",
"author": "You <you@example.com>",
"version": "0.1.0",
"scripts": {
"build": "rimraf dist/js && rollup --config",
"watch": "rimraf dist/js && rollup --config --watch"
},
"devDependencies": {
"@wasm-tool/rollup-plugin-rust": "portal:..",
"binaryen": "^125.0.0",
"rimraf": "^6.1.2",
"rollup": "^4.53.3",
"rollup-plugin-livereload": "^2.0.5",
"rollup-plugin-serve": "^3.0.0",
"rollup-plugin-terser": "^7.0.2"
}
}
================================================
FILE: example/rollup.config.js
================================================
import rust from "@wasm-tool/rollup-plugin-rust";
import serve from "rollup-plugin-serve";
import livereload from "rollup-plugin-livereload";
import { terser } from "rollup-plugin-terser";
const is_watch = !!process.env.ROLLUP_WATCH;
export default {
input: {
example: "Cargo.toml",
},
output: {
dir: "dist/js",
format: "es",
sourcemap: true,
},
plugins: [
rust(),
is_watch && serve({
contentBase: "dist",
open: true,
}),
is_watch && livereload("dist"),
!is_watch && terser(),
],
};
================================================
FILE: example/rust-toolchain.toml
================================================
[toolchain]
channel = "nightly-2025-11-20"
components = [ "rust-std", "rust-src", "rustfmt", "clippy" ]
targets = [ "wasm32-unknown-unknown" ]
================================================
FILE: example/src/lib.rs
================================================
use wasm_bindgen::prelude::*;
use web_sys::console;
#[wasm_bindgen(start)]
pub fn main_js() {
console_error_panic_hook::set_once();
console::log_1(&JsValue::from("Hello world!"));
}
================================================
FILE: package.json
================================================
{
"name": "@wasm-tool/rollup-plugin-rust",
"author": "Pauan <pauanyu+github@pm.me>",
"description": "Rollup plugin for bundling and importing Rust crates.",
"version": "3.1.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",
"type": "module",
"main": "src/index.js",
"scripts": {
"test:foo": "cd tests/src/foo && yarn install",
"test": "yarn test:foo && cd tests && rimraf dist/js && rollup --config",
"test:watch": "yarn test:foo && cd tests && rimraf dist/js && rollup --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.3.0",
"chalk": "^5.6.2",
"glob": "^13.0.0",
"node-fetch": "^3.3.2",
"rimraf": "^6.1.2",
"tar": "^7.5.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^29.0.0",
"@rollup/plugin-node-resolve": "^16.0.3",
"binaryen": "^125.0.0",
"live-server": "^1.2.2",
"rollup": "^4.53.3"
},
"peerDependencies": {
"binaryen": "*"
}
}
================================================
FILE: src/cargo.js
================================================
import { getEnv, exec, debug, spawn, Lock } from "./utils.js";
const GLOBAL_LOCK = new Lock();
export class Nightly {
constructor(year, month, day) {
this.year = year;
this.month = month;
this.day = day;
}
greaterThan(other) {
if (this.year > other.year) {
return true;
} else if (this.year === other.year) {
if (this.month > other.month) {
return true;
} else if (this.month === other.month) {
return this.day > other.day;
} else {
return false;
}
} else {
return false;
}
}
supportsImmediateAbort() {
return this.greaterThan(new Nightly(2025, 10, 4));
}
}
export async function getNightly(dir) {
const bin = getEnv("CARGO_BIN", "cargo");
// TODO make this faster somehow ?
const version = await exec(`${bin} --version`, { cwd: dir });
const a = /\-nightly \([^ ]+ ([0-9]+)\-([0-9]+)\-([0-9]+)\)/.exec(version);
if (a) {
return new Nightly(+a[1], +a[2], +a[3]);
} else {
return null;
}
}
export async function getTargetDir(dir) {
const bin = getEnv("CARGO_BIN", "cargo");
// TODO make this faster somehow ?
const metadata = await exec(`${bin} metadata --format-version 1 --no-deps --color never`, { cwd: dir });
return JSON.parse(metadata)["target_directory"];
}
export async function getVersion(dir, name) {
const bin = getEnv("CARGO_BIN", "cargo");
const spec = await exec(`${bin} pkgid ${name}`, { cwd: dir });
const version = /([\d\.]+)[\r\n]*$/.exec(spec);
if (version) {
return version[1];
} else {
throw new Error(`Could not determine ${name} version`);
}
}
export async function run({ dir, verbose, cargoArgs, rustcArgs, release, optimize, nightly, atomics, strip }) {
const cargoBin = getEnv("CARGO_BIN", "cargo");
let args = [
"rustc",
"--lib",
"--target", "wasm32-unknown-unknown",
"--crate-type", "cdylib", // Needed for wasm-bindgen to work
];
let rustflags = [];
if (atomics) {
rustflags.push(
"-C", "target-feature=+atomics,+bulk-memory,+mutable-globals",
"-C", "link-args=--shared-memory",
"-C", "link-args=--import-memory",
"-C", "link-args=--export=__wasm_init_tls",
"-C", "link-args=--export=__tls_size",
"-C", "link-args=--export=__tls_align",
"-C", "link-args=--export=__tls_base",
);
args.push("-Z", "build-std=panic_abort,core,std,alloc,proc_macro");
}
// https://doc.rust-lang.org/cargo/reference/profiles.html#release
if (release) {
args.push("--release");
if (nightly) {
if (strip.location.get()) {
rustflags.push("-Z", "location-detail=none");
}
if (strip.formatDebug.get()) {
rustflags.push("-Z", "fmt-debug=none");
}
}
if (optimize) {
// Wasm doesn't support unwind, so we abort instead
if (nightly && nightly.supportsImmediateAbort()) {
// Reduces file size by removing panic strings
args.push("--config");
args.push("profile.release.panic=\"immediate-abort\"");
} else {
args.push("--config");
args.push("profile.release.panic=\"abort\"");
}
// Improves runtime performance and file size
args.push("--config");
args.push("profile.release.lto=true");
// Improves runtime performance
args.push("--config");
args.push("profile.release.codegen-units=1");
// Reduces file size
args.push("--config");
args.push("profile.release.strip=true");
// Reduces file size by removing panic strings
if (nightly) {
if (nightly.supportsImmediateAbort()) {
args.push("-Z", "panic-immediate-abort");
}
args.push("-Z", "build-std=panic_abort,core,std,alloc,proc_macro");
args.push("-Z", "build-std-features=optimize_for_size");
}
}
// https://doc.rust-lang.org/cargo/reference/profiles.html#dev
} else {
if (optimize) {
// Wasm doesn't support unwind
args.push("--config");
args.push("profile.dev.panic=\"abort\"");
args.push("--config");
args.push("profile.dev.lto=\"off\"");
// Speeds up compilation
// https://github.com/MoonZoon/MoonZoon/issues/170
args.push("--config");
args.push("profile.dev.debug=false");
}
}
rustflags = rustflags.concat(rustcArgs);
if (rustflags.length > 0) {
args.push("--config", "build.rustflags=" + JSON.stringify(rustflags));
}
args = args.concat(cargoArgs);
await GLOBAL_LOCK.withLock(async () => {
if (verbose) {
debug(`Running cargo ${args.join(" ")}`);
}
try {
await spawn(cargoBin, args, { cwd: dir, stdio: "inherit" });
} catch (e) {
if (verbose) {
throw e;
} else {
const e = new Error("Rust compilation failed");
e.stack = null;
throw e;
}
}
});
}
================================================
FILE: src/index.js
================================================
import * as $path from "node:path";
import * as $toml from "@iarna/toml";
import { createFilter } from "@rollup/pluginutils";
import { glob, rm, read, readString, debug, getEnv, isObject, eachObject, copyObject } from "./utils.js";
import * as $wasmBindgen from "./wasm-bindgen.js";
import * as $cargo from "./cargo.js";
import * as $wasmOpt from "./wasm-opt.js";
import * as $typescript from "./typescript.js";
const PREFIX = "./.__rollup-plugin-rust__";
const INLINE_ID = "\0__rollup-plugin-rust-inlineWasm__";
function stripPath(path) {
return path.replace(/\?[^\?]*$/, "");
}
class Option {
constructor(value) {
this.value = value;
this.isDefault = true;
}
get() {
return this.value;
}
getOr(fallback) {
if (this.isDefault) {
return fallback;
} else {
return this.value;
}
}
set(value) {
this.value = value;
this.isDefault = false;
}
}
class State {
constructor() {
// Whether the plugin is running in Vite or not
this.vite = false;
// Whether we're in watch mode or not
this.watch = false;
// Whether the options have been processed or not
this.processed = false;
this.fileIds = new Set();
this.defaults = {
watchPatterns: ["src/**"],
inlineWasm: false,
verbose: false,
nodejs: false,
optimize: {
release: true,
wasmOpt: true,
rustc: true,
strip: {
location: true,
formatDebug: true,
},
},
extraArgs: {
cargo: [],
rustc: [],
wasmBindgen: [],
// TODO figure out better optimization options ?
wasmOpt: ["-O", "--enable-threads", "--enable-bulk-memory", "--enable-bulk-memory-opt"],
},
experimental: {
atomics: false,
synchronous: false,
typescriptDeclarationDir: null,
},
};
// Make a copy of the default settings
this.options = copyObject(this.defaults, (value) => new Option(value));
this.deprecations = {
debug: (cx, value) => {
cx.warn("The `debug` option has been changed to `optimize.release`");
this.options.optimize.release.set(!value);
},
cargoArgs: (cx, value) => {
cx.warn("The `cargoArgs` option has been changed to `extraArgs.cargo`");
this.options.extraArgs.cargo.set(value);
},
wasmBindgenArgs: (cx, value) => {
cx.warn("The `wasmBindgenArgs` option has been changed to `extraArgs.wasmBindgen`");
this.options.extraArgs.wasmBindgen.set(value);
},
wasmOptArgs: (cx, value) => {
cx.warn("The `wasmOptArgs` option has been changed to `extraArgs.wasmOpt`");
this.options.extraArgs.wasmOpt.set(value);
},
serverPath: (cx, value) => {
cx.warn("The `serverPath` option is deprecated and no longer works");
},
importHook: (cx, value) => {
cx.warn("The `importHook` option is deprecated and no longer works");
},
experimental: {
directExports: (cx, value) => {
cx.warn("The `experimental.directExports` option is deprecated and no longer works");
},
},
};
this.cache = {
nightly: {},
targetDir: {},
wasmBindgen: {},
build: {},
};
}
reset() {
this.fileIds.clear();
this.cache.nightly = {};
this.cache.targetDir = {};
this.cache.wasmBindgen = {};
this.cache.build = {};
}
processOptions(cx, oldOptions) {
if (!this.processed) {
this.processed = true;
// Overwrite the default settings with the user-provided settings
this.setOptions(cx, [], oldOptions, this.options, this.defaults, this.deprecations);
}
}
setOptions(cx, path, oldOptions, options, defaults, deprecations) {
if (oldOptions != null) {
if (isObject(oldOptions)) {
eachObject(oldOptions, (key, value) => {
const newPath = path.concat([key]);
// If the option is deprecated, call the function
if (deprecations != null && key in deprecations) {
const deprecation = deprecations[key];
if (isObject(deprecation)) {
this.setOptions(cx, newPath, value, options?.[key], defaults?.[key], deprecation);
} else {
deprecation(cx, value);
}
// If the option has a default, apply it
} else if (defaults != null && key in defaults) {
const def = defaults[key];
if (isObject(def)) {
this.setOptions(cx, newPath, value, options?.[key], def, deprecations?.[key]);
} else if (value != null) {
if (options[key].isDefault) {
options[key].set(value);
}
}
// The option doesn't exist
} else {
throw new Error(`The \`${newPath.join(".")}\` option does not exist`);
}
});
} else if (path.length > 0) {
throw new Error(`The \`${path.join(".")}\` option must be an object`);
} else {
throw new Error(`Options must be an object`);
}
}
}
async watchFiles(cx, dir) {
if (this.watch) {
const matches = await Promise.all(this.options.watchPatterns.get().map((pattern) => glob(pattern, dir)));
// TODO deduplicate matches ?
matches.forEach(function (files) {
files.forEach(function (file) {
cx.addWatchFile(file);
});
});
}
}
async getNightly(dir) {
let nightly = this.cache.nightly[dir];
if (nightly == null) {
nightly = this.cache.nightly[dir] = $cargo.getNightly(dir);
}
return await nightly;
}
async getTargetDir(dir) {
let targetDir = this.cache.targetDir[dir];
if (targetDir == null) {
targetDir = this.cache.targetDir[dir] = $cargo.getTargetDir(dir);
}
return await targetDir;
}
async getWasmBindgen(dir) {
let bin = getEnv("WASM_BINDGEN_BIN", null);
if (bin == null) {
bin = this.cache.wasmBindgen[dir];
if (bin == null) {
bin = this.cache.wasmBindgen[dir] = $wasmBindgen.download(dir, this.options.verbose.get());
}
return await bin;
} else {
return bin;
}
}
async loadWasm(outDir) {
const wasmPath = $path.join(outDir, "index_bg.wasm");
if (this.options.verbose.get()) {
debug(`Looking for wasm at ${wasmPath}`);
}
return await read(wasmPath);
}
async compileTypescript(name, outDir) {
if (this.options.experimental.typescriptDeclarationDir.get() != null) {
await $typescript.write(
name,
this.options.experimental.typescriptDeclarationDir.get(),
outDir,
);
}
}
async compileTypescriptCustom(name, isCustom) {
if (isCustom && this.options.experimental.typescriptDeclarationDir.get() != null) {
await $typescript.writeCustom(
name,
this.options.experimental.typescriptDeclarationDir.get(),
this.options.inlineWasm.get(),
this.options.experimental.synchronous.get(),
);
}
}
async wasmOpt(cx, outDir) {
if (this.options.optimize.wasmOpt.getOr(!this.watch)) {
const result = await $wasmOpt.run({
dir: outDir,
input: "index_bg.wasm",
output: "wasm_opt.wasm",
extraArgs: this.options.extraArgs.wasmOpt.get(),
verbose: this.options.verbose.get(),
});
if (result !== null) {
cx.warn("wasm-opt failed: " + result.message);
}
}
}
compileInlineWasm(build) {
const wasmString = JSON.stringify(build.wasm.toString("base64"));
const code = `
const base64codes = [62,0,0,0,63,52,53,54,55,56,57,58,59,60,61,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,0,0,0,0,0,0,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51];
function getBase64Code(charCode) {
return base64codes[charCode - 43];
}
function base64Decode(str) {
let missingOctets = str.endsWith("==") ? 2 : str.endsWith("=") ? 1 : 0;
let n = str.length;
let result = new Uint8Array(3 * (n / 4));
let buffer;
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
buffer =
getBase64Code(str.charCodeAt(i)) << 18 |
getBase64Code(str.charCodeAt(i + 1)) << 12 |
getBase64Code(str.charCodeAt(i + 2)) << 6 |
getBase64Code(str.charCodeAt(i + 3));
result[j] = buffer >> 16;
result[j + 1] = (buffer >> 8) & 0xFF;
result[j + 2] = buffer & 0xFF;
}
return result.subarray(0, result.length - missingOctets);
}
export default base64Decode(${wasmString});
`;
return {
code,
map: { mappings: '' },
moduleSideEffects: false,
};
}
compileJsInline(build, isCustom) {
let mainCode;
let sideEffects;
if (this.options.experimental.synchronous.get()) {
if (isCustom) {
sideEffects = false;
mainCode = `export { module };
export function init(options) {
exports.initSync({
module: options.module,
memory: options.memory,
});
return exports;
}
export const initSync = init;`
} else {
sideEffects = true;
mainCode = `
exports.initSync({ module });
export * from ${build.importPath};
`;
}
} else {
if (isCustom) {
sideEffects = false;
mainCode = `export { module };
export async function init(options) {
await exports.default({
module_or_path: await options.module,
memory: options.memory,
});
return exports;
}
export function initSync(options) {
exports.initSync({
module: options.module,
memory: options.memory,
});
return exports;
}`;
} else {
sideEffects = true;
mainCode = `
await exports.default({ module_or_path: module });
export * from ${build.importPath};
`;
}
}
const wasmString = JSON.stringify(build.wasm.toString("base64"));
const code = `
import * as exports from ${build.importPath};
import module from "${INLINE_ID}";
${mainCode}
`;
return {
code,
map: { mappings: '' },
moduleSideEffects: sideEffects,
meta: {
"rollup-plugin-rust": { root: false, realPath: build.realPath }
},
};
}
compileJsNormal(build, isCustom) {
let wasmPath = `import.meta.ROLLUP_FILE_URL_${build.fileId}`;
let prelude;
if (this.options.nodejs.get()) {
prelude = `function loadFile(url) {
return new Promise((resolve, reject) => {
require("node:fs").readFile(url, (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
}
const module = loadFile(${wasmPath});`;
} else {
prelude = `const module = ${wasmPath};`;
}
let mainCode;
let sideEffects;
if (this.options.experimental.synchronous.get()) {
throw new Error("synchronous option can only be used with inlineWasm: true");
} else {
if (isCustom) {
sideEffects = false;
mainCode = `export { module };
export async function init(options) {
await exports.default({
module_or_path: await options.module,
memory: options.memory,
});
return exports;
}
export function initSync(options) {
exports.initSync({
module: options.module,
memory: options.memory,
});
return exports;
}`;
} else {
sideEffects = true;
mainCode = `
await exports.default({ module_or_path: module });
export * from ${build.importPath};
`;
}
}
return {
code: `
import * as exports from ${build.importPath};
${prelude}
${mainCode}
`,
map: { mappings: '' },
moduleSideEffects: sideEffects,
meta: {
"rollup-plugin-rust": { root: false, realPath: build.realPath }
},
};
}
compileJs(build, isCustom) {
if (this.options.inlineWasm.get()) {
return this.compileJsInline(build, isCustom);
} else {
return this.compileJsNormal(build, isCustom);
}
}
async getInfo(dir, id) {
const [targetDir, source] = await Promise.all([
this.getTargetDir(dir),
readString(id),
]);
const toml = $toml.parse(source);
// TODO make this faster somehow
// TODO does it need to do more transformations on the name ?
const name = toml.package.name.replace(/\-/g, "_");
const wasmPath = $path.resolve($path.join(
targetDir,
"wasm32-unknown-unknown",
(this.options.optimize.release.getOr(!this.watch) ? "release" : "debug"),
name + ".wasm"
));
const outDir = $path.resolve($path.join(targetDir, "rollup-plugin-rust", name));
if (this.options.verbose.get()) {
debug(`Using target directory ${targetDir}`);
debug(`Using rustc output ${wasmPath}`);
debug(`Using output directory ${outDir}`);
}
await rm(outDir);
return { name, wasmPath, outDir };
}
async buildCargo(dir) {
const nightly = await this.getNightly(dir);
await $cargo.run({
dir,
nightly,
verbose: this.options.verbose.get(),
cargoArgs: this.options.extraArgs.cargo.get(),
rustcArgs: this.options.extraArgs.rustc.get(),
release: this.options.optimize.release.getOr(!this.watch),
optimize: this.options.optimize.rustc.get(),
strip: this.options.optimize.strip,
atomics: this.options.experimental.atomics.get(),
});
}
async buildWasm(cx, dir, bin, name, wasmPath, outDir) {
await $wasmBindgen.run({
bin,
dir,
wasmPath,
outDir,
typescript: this.options.experimental.typescriptDeclarationDir.get() != null,
extraArgs: this.options.extraArgs.wasmBindgen.get(),
verbose: this.options.verbose.get(),
});
const [wasm] = await Promise.all([
this.wasmOpt(cx, outDir).then(() => {
return this.loadWasm(outDir);
}),
this.compileTypescript(name, outDir),
]);
let fileId;
if (!this.options.inlineWasm.get()) {
fileId = cx.emitFile({
type: "asset",
source: wasm,
name: name + ".wasm"
});
this.fileIds.add(fileId);
}
const realPath = $path.join(outDir, "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 importPath = `"${PREFIX}${name}/index.js"`;
return { name, outDir, importPath, realPath, wasm, fileId };
}
async build(cx, dir, id) {
if (this.options.verbose.get()) {
debug(`Compiling ${id}`);
}
await this.buildCargo(dir);
const [bin, { name, wasmPath, outDir }] = await Promise.all([
this.getWasmBindgen(dir),
this.getInfo(dir, id),
]);
return await this.buildWasm(cx, dir, bin, name, wasmPath, outDir);
}
async load(cx, oldId) {
try {
const id = stripPath(oldId);
let promise = this.cache.build[id];
if (promise == null) {
const dir = $path.dirname(id);
promise = this.cache.build[id] = Promise.all([
this.build(cx, dir, id),
this.watchFiles(cx, dir),
]);
}
const [build] = await promise;
if (oldId.endsWith("?inline")) {
return this.compileInlineWasm(build);
} else {
const isCustom = oldId.endsWith("?custom");
const [result] = await Promise.all([
this.compileJs(build, isCustom),
this.compileTypescriptCustom(build.name, isCustom),
]);
return result;
}
} catch (e) {
if (!this.options.verbose.get()) {
e.stack = null;
}
throw e;
}
}
}
export default 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);
const state = new State();
return {
name: "rust",
// Vite-specific hook
configResolved(config) {
state.vite = true;
if (config.command !== "build") {
// We have to force inlineWasm during dev because Vite doesn't support emitFile
// https://github.com/vitejs/vite/issues/7029
state.options.inlineWasm.set(true);
}
},
buildStart(rollup) {
state.reset();
state.processOptions(this, options);
state.watch = this.meta.watchMode || rollup.watch;
},
// This is only compatible with Rollup 2.78.0 and higher
resolveId: {
order: "pre",
handler(id, importer, info) {
if (id === INLINE_ID) {
return {
id: stripPath(importer) + "?inline",
meta: {
"rollup-plugin-rust": { root: true }
}
};
} else {
const name = $path.basename(id);
const normal = (name === "Cargo.toml");
const custom = (name === "Cargo.toml?custom");
if ((normal || custom) && filter(id)) {
const path = (importer ? $path.resolve($path.dirname(importer), id) : $path.resolve(id));
return {
id: path,
moduleSideEffects: !custom,
meta: {
"rollup-plugin-rust": { root: true }
}
};
// Rewrites the fake file paths to real file paths.
} else if (importer && id[0] === ".") {
const info = this.getModuleInfo(importer);
if (info && info.meta) {
const meta = info.meta["rollup-plugin-rust"];
if (meta && !meta.root) {
// TODO maybe use resolve ?
const path = $path.join($path.dirname(importer), id);
const realPath = (id.startsWith(PREFIX)
? meta.realPath
: $path.join($path.dirname(meta.realPath), id));
return {
id: path,
meta: {
"rollup-plugin-rust": {
root: false,
realPath,
}
}
};
}
}
}
}
return null;
},
},
load(id, loadState) {
const info = this.getModuleInfo(id);
if (info && info.meta) {
const meta = info.meta["rollup-plugin-rust"];
if (meta) {
if (meta.root) {
// This causes Vite to load a noop module during SSR
if (state.vite && loadState && loadState.ssr) {
return {
code: `export {};`,
map: { mappings: '' },
moduleSideEffects: false,
};
// This compiles the Cargo.toml
} else {
return state.load(this, id);
}
} else {
if (state.options.verbose.get()) {
debug(`Loading file ${meta.realPath}`);
}
// This maps the fake path to a real path on disk and loads it
return readString(meta.realPath);
}
}
}
return null;
},
resolveFileUrl(info) {
if (state.fileIds.has(info.referenceId)) {
return `new URL(${JSON.stringify(info.relativePath)}, import.meta.url)`;
} else {
return null;
}
},
};
};
================================================
FILE: src/typescript.js
================================================
import * as $path from "node:path";
import { writeString, readString, mkdir } from "./utils.js";
function trim(s) {
return s.replace(/\n\n\n+/g, "\n\n").replace(/\n\n+\}/g, "\n}").trim();
}
function parse(declaration) {
declaration = declaration.replace(/export type InitInput = [\s\S]*/g, "");
return trim(declaration);
}
export async function writeCustom(name, typescriptDir, inline, synchronous) {
const outPath = $path.join(typescriptDir, name + "_custom.d.ts");
let output;
if (synchronous) {
output = `export type Module = BufferSource | WebAssembly.Module;
export type InitOutput = typeof import("./${name}");
export interface InitOptions {
module: Module;
memory?: WebAssembly.Memory;
}
export const module: Uint8Array;
export function init(options: InitOptions): InitOutput;
`;
} else {
output = `export type Module = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;
export type InitOutput = typeof import("./${name}");
export interface InitOptions {
module: Module | Promise<Module>;
memory?: WebAssembly.Memory;
}
export const module: ${inline ? "Uint8Array" : "URL"};
export function init(options: InitOptions): Promise<InitOutput>;
`;
}
await writeString(outPath, output);
}
export async function write(name, typescriptDir, outDir) {
const realPath = $path.join(outDir, "index.d.ts");
const [declaration] = await Promise.all([
readString(realPath),
mkdir(typescriptDir),
]);
await writeString($path.join(typescriptDir, name + ".d.ts"), parse(declaration));
}
================================================
FILE: src/utils.js
================================================
import * as $path from "node:path";
import * as $stream from "node:stream";
import * as $fs from "node:fs";
import * as $os from "node:os";
import * as $child from "node:child_process";
import * as $glob from "glob";
import * as $rimraf from "rimraf";
import * as $tar from "tar";
import $chalk from "chalk";
export function getCacheDir(name) {
switch (process.platform) {
case "win32":
const localAppData = process.env.LOCALAPPDATA || $path.join($os.homedir(), "AppData", "Local");
return $path.join(localAppData, name, "Cache");
case "darwin":
return $path.join($os.homedir(), "Library", "Caches", name);
// https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html
default:
const cacheDir = process.env.XDG_CACHE_HOME || $path.join($os.homedir(), ".cache");
return $path.join(cacheDir, name);
}
}
export function posixPath(path) {
return path.replace(/\\/g, $path.posix.sep);
}
export function debug(s) {
console.debug($chalk.blue("> " + s + "\n"));
}
export function info(s) {
console.info($chalk.yellow(s));
}
export function isObject(value) {
return Object.prototype.toString.call(value) === "[object Object]";
}
export function eachObject(object, f) {
Object.keys(object).forEach((key) => {
f(key, object[key]);
});
}
export function copyObject(object, f) {
const output = {};
eachObject(object, (key, value) => {
if (isObject(value)) {
output[key] = copyObject(value, f);
} else {
output[key] = f(value);
}
});
return output;
}
export function glob(pattern, cwd) {
return $glob.glob(pattern, {
cwd: cwd,
strict: true,
absolute: true,
nodir: true
});
}
export function rm(path) {
return $rimraf.rimraf(path, { glob: false });
}
export function mv(from, to) {
return new Promise((resolve, reject) => {
$fs.rename(from, to, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
export function mkdir(path) {
return new Promise((resolve, reject) => {
$fs.mkdir(path, { recursive: true }, (err) => {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
export function exists(path) {
return new Promise((resolve, reject) => {
$fs.access(path, (err) => {
if (err) {
resolve(false);
} else {
resolve(true);
}
});
});
}
export function read(path) {
return new Promise(function (resolve, reject) {
$fs.readFile(path, function (err, file) {
if (err) {
reject(err);
} else {
resolve(file);
}
});
});
}
export function readString(path) {
return new Promise(function (resolve, reject) {
$fs.readFile(path, { encoding: "utf8" }, function (err, file) {
if (err) {
reject(err);
} else {
resolve(file);
}
});
});
}
export function writeString(path, value) {
return new Promise(function (resolve, reject) {
$fs.writeFile(path, value, { encoding: "utf8" }, function (err) {
if (err) {
reject(err);
} else {
resolve();
}
});
});
}
export function getEnv(name, fallback) {
const value = process.env[name];
if (value == null) {
return fallback;
} else {
return value;
}
}
export function exec(cmd, options) {
return new Promise((resolve, reject) => {
$child.exec(cmd, options, (err, stdout, stderr) => {
if (err) {
reject(err);
} else if (stderr.length > 0) {
reject(new Error(stderr));
} else {
resolve(stdout);
}
});
});
}
export function spawn(command, args, options) {
return wait($child.spawn(command, args, options));
}
export function wait(p) {
return new Promise((resolve, reject) => {
p.on("close", (code) => {
if (code === 0) {
resolve();
} else {
reject(new Error("Command `" + p.spawnargs.join(" ") + "` failed with error code: " + code));
}
});
p.on("error", reject);
});
}
export function tar(stream, options) {
return new Promise((resolve, reject) => {
$stream.pipeline(
stream,
$tar.x({
cwd: options.cwd,
strict: true,
}, options.files),
(err) => {
if (err) {
reject(err);
} else {
resolve();
}
},
);
});
}
export class Lock {
constructor() {
this.locked = false;
this.pending = [];
}
async withLock(f) {
await this.lock();
try {
return await f();
} finally {
this.unlock();
}
}
async lock() {
if (this.locked) {
await new Promise((resolve, reject) => {
this.pending.push(resolve);
});
if (this.locked) {
throw new Error("Invalid lock state");
}
}
this.locked = true;
}
unlock() {
this.locked = false;
if (this.pending.length !== 0) {
const resolve = this.pending.shift();
// Wake up pending task
resolve();
}
}
}
================================================
FILE: src/wasm-bindgen.js
================================================
import * as $path from "node:path";
import * as $tar from "tar";
import $fetch from "node-fetch";
import { getVersion } from "./cargo.js";
import { exec, mkdir, getCacheDir, tar, exists, spawn, info, debug, getEnv } from "./utils.js";
const WASM_BINDGEN_CACHE = {};
function getName(version) {
switch (process.platform) {
case "win32":
return `wasm-bindgen-${version}-x86_64-pc-windows-msvc`;
case "darwin":
switch (process.arch) {
case "arm64":
return `wasm-bindgen-${version}-aarch64-apple-darwin`;
default:
return `wasm-bindgen-${version}-x86_64-apple-darwin`;
}
default:
switch (process.arch) {
case "arm64":
return `wasm-bindgen-${version}-aarch64-unknown-linux-gnu`;
default:
return `wasm-bindgen-${version}-x86_64-unknown-linux-musl`;
}
}
}
function getUrl(version, name) {
return `https://github.com/rustwasm/wasm-bindgen/releases/download/${version}/${name}.tar.gz`;
}
function getPath(dir) {
if (process.platform === "win32") {
return $path.join(dir, "wasm-bindgen.exe");
} else {
return $path.join(dir, "wasm-bindgen");
}
}
async function fetchBin(dir, version, name, path) {
await mkdir(dir);
if (!(await exists(path))) {
info(`Downloading wasm-bindgen version ${version}`);
const response = await $fetch(getUrl(version, name));
if (!response.ok) {
throw new Error(`Could not download wasm-bindgen: ${response.statusText}`);
}
await tar(response.body, {
cwd: dir,
});
}
}
export async function download(dir, verbose) {
const version = await getVersion(dir, "wasm-bindgen");
const name = getName(version);
const cache = getCacheDir("rollup-plugin-rust");
const path = getPath($path.join(cache, name));
if (verbose) {
debug(`Searching for wasm-bindgen at ${path}`);
}
let promise = WASM_BINDGEN_CACHE[path];
if (promise == null) {
promise = WASM_BINDGEN_CACHE[path] = fetchBin(cache, version, name, path);
}
await promise;
return path;
}
export async function run({ bin, dir, wasmPath, outDir, typescript, extraArgs, verbose }) {
// TODO what about --debug --no-demangle --keep-debug ?
let args = [
"--out-dir", outDir,
"--out-name", "index",
"--target", "web",
"--omit-default-module-path",
];
if (!typescript) {
args.push("--no-typescript");
}
args.push(wasmPath);
args = args.concat(extraArgs);
if (verbose) {
debug(`Running wasm-bindgen ${args.join(" ")}`);
}
await spawn(bin, args, { cwd: dir, stdio: "inherit" });
}
================================================
FILE: src/wasm-opt.js
================================================
import * as $path from "node:path";
import { getEnv, debug, spawn, mv } from "./utils.js";
// Replace with @webassemblyjs/wasm-opt ?
export async function run({ dir, input, output, extraArgs, verbose }) {
const isWindows = (process.platform === "win32");
// Needed to make wasm-opt work on Windows
const bin = getEnv("WASM_OPT_BIN", (isWindows ? "wasm-opt.cmd" : "wasm-opt"));
const args = [input, "--output", output].concat(extraArgs);
if (verbose) {
debug(`Running ${bin} ${args.join(" ")}`);
}
try {
await spawn(bin, args, { cwd: dir, shell: isWindows, stdio: "inherit" });
} catch (e) {
return e;
}
await mv($path.join(dir, output), $path.join(dir, input));
return null;
}
================================================
FILE: tests/.gitignore
================================================
node_modules
yarn-error.log
Cargo.lock
yarn.lock
/target
/dist/js
================================================
FILE: tests/Cargo.toml
================================================
[workspace]
members = [
"src/bar",
"src/foo",
]
resolver = "2"
[workspace.package]
edition = "2018"
================================================
FILE: tests/dist/index.html
================================================
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
</head>
<body>
<script type="module" src="js/foo.js"></script>
<script type="module" src="js/bar.js"></script>
<script type="module" src="js/qux.js"></script>
</body>
</html>
================================================
FILE: tests/rollup.config.js
================================================
import rust from "../src/index.js";
import { nodeResolve } from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
input: {
foo: "./src/foo.js",
bar: "./src/bar.js",
qux: "./src/foo/Cargo.toml",
},
output: {
dir: "dist/js",
format: "es",
sourcemap: true,
},
plugins: [
nodeResolve(),
commonjs(),
rust({
extraArgs: {
wasmBindgen: ["--debug", "--keep-debug"],
},
verbose: true,
}),
],
};
================================================
FILE: tests/src/bar/Cargo.toml
================================================
[package]
name = "bar"
version = "0.1.0"
authors = ["You <you@example.com>"]
edition.workspace = true
categories = ["wasm"]
[dependencies]
wasm-bindgen = "0.2.58"
console_error_panic_hook = "0.1.6"
[dependencies.web-sys]
version = "0.3.35"
features = [
"console",
]
================================================
FILE: tests/src/bar/src/lib.rs
================================================
use wasm_bindgen::prelude::*;
use web_sys::console;
#[wasm_bindgen(start)]
pub fn main_js() {
console_error_panic_hook::set_once();
console::log_1(&JsValue::from("Bar!"));
}
================================================
FILE: tests/src/bar.js
================================================
import "./bar/Cargo.toml";
================================================
FILE: tests/src/foo/Cargo.toml
================================================
[package]
name = "foo"
version = "0.1.0"
authors = ["You <you@example.com>"]
edition.workspace = true
categories = ["wasm"]
[dependencies]
wasm-bindgen = "0.2.58"
console_error_panic_hook = "0.1.6"
[dependencies.web-sys]
version = "0.3.35"
features = [
"console",
]
================================================
FILE: tests/src/foo/package.json
================================================
{
"private": true,
"name": "foo",
"author": "You <you@example.com>",
"version": "0.1.0",
"devDependencies": {
"nop": "^1.0.0"
}
}
================================================
FILE: tests/src/foo/src/lib.rs
================================================
use wasm_bindgen::prelude::*;
use web_sys::console;
#[wasm_bindgen(inline_js = "export function foo() { return 5; }")]
extern "C" {
fn foo() -> u32;
}
#[wasm_bindgen(module = "nop")]
extern "C" {
#[wasm_bindgen(js_name = default)]
fn nop();
}
#[wasm_bindgen(start)]
pub fn main_js() {
console_error_panic_hook::set_once();
console::log_1(&JsValue::from(format!("Foo! {:?} {}", nop(), foo())));
}
================================================
FILE: tests/src/foo.js
================================================
import * as foo1 from "./foo/Cargo.toml?custom";
import * as foo2 from "./foo/Cargo.toml?custom";
await foo1.init({ module: foo1.module });
await foo2.init({ module: foo2.module });
gitextract_n_az4ztq/
├── .gitignore
├── .yarnrc.yml
├── README.md
├── example/
│ ├── .gitignore
│ ├── Cargo.toml
│ ├── dist/
│ │ └── index.html
│ ├── package.json
│ ├── rollup.config.js
│ ├── rust-toolchain.toml
│ └── src/
│ └── lib.rs
├── package.json
├── src/
│ ├── cargo.js
│ ├── index.js
│ ├── typescript.js
│ ├── utils.js
│ ├── wasm-bindgen.js
│ └── wasm-opt.js
└── tests/
├── .gitignore
├── Cargo.toml
├── dist/
│ └── index.html
├── rollup.config.js
└── src/
├── bar/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── bar.js
├── foo/
│ ├── Cargo.toml
│ ├── package.json
│ └── src/
│ └── lib.rs
└── foo.js
SYMBOL INDEX (82 symbols across 9 files)
FILE: example/src/lib.rs
function main_js (line 6) | pub fn main_js() {
FILE: src/cargo.js
constant GLOBAL_LOCK (line 4) | const GLOBAL_LOCK = new Lock();
class Nightly (line 7) | class Nightly {
method constructor (line 8) | constructor(year, month, day) {
method greaterThan (line 14) | greaterThan(other) {
method supportsImmediateAbort (line 34) | supportsImmediateAbort() {
function getNightly (line 40) | async function getNightly(dir) {
function getTargetDir (line 57) | async function getTargetDir(dir) {
function getVersion (line 67) | async function getVersion(dir, name) {
function run (line 82) | async function run({ dir, verbose, cargoArgs, rustcArgs, release, optimi...
FILE: src/index.js
constant PREFIX (line 11) | const PREFIX = "./.__rollup-plugin-rust__";
constant INLINE_ID (line 12) | const INLINE_ID = "\0__rollup-plugin-rust-inlineWasm__";
function stripPath (line 15) | function stripPath(path) {
class Option (line 20) | class Option {
method constructor (line 21) | constructor(value) {
method get (line 26) | get() {
method getOr (line 30) | getOr(fallback) {
method set (line 39) | set(value) {
class State (line 46) | class State {
method constructor (line 47) | constructor() {
method reset (line 150) | reset() {
method processOptions (line 160) | processOptions(cx, oldOptions) {
method setOptions (line 169) | setOptions(cx, path, oldOptions, options, defaults, deprecations) {
method watchFiles (line 215) | async watchFiles(cx, dir) {
method getNightly (line 229) | async getNightly(dir) {
method getTargetDir (line 240) | async getTargetDir(dir) {
method getWasmBindgen (line 251) | async getWasmBindgen(dir) {
method loadWasm (line 269) | async loadWasm(outDir) {
method compileTypescript (line 280) | async compileTypescript(name, outDir) {
method compileTypescriptCustom (line 291) | async compileTypescriptCustom(name, isCustom) {
method wasmOpt (line 303) | async wasmOpt(cx, outDir) {
method compileInlineWasm (line 320) | compileInlineWasm(build) {
method compileJsInline (line 361) | compileJsInline(build, isCustom) {
method compileJsNormal (line 444) | compileJsNormal(build, isCustom) {
method compileJs (line 524) | compileJs(build, isCustom) {
method getInfo (line 534) | async getInfo(dir, id) {
method buildCargo (line 567) | async buildCargo(dir) {
method buildWasm (line 584) | async buildWasm(cx, dir, bin, name, wasmPath, outDir) {
method build (line 626) | async build(cx, dir, id) {
method load (line 642) | async load(cx, oldId) {
function rust (line 684) | function rust(options = {}) {
FILE: src/typescript.js
function trim (line 5) | function trim(s) {
function parse (line 10) | function parse(declaration) {
function writeCustom (line 16) | async function writeCustom(name, typescriptDir, inline, synchronous) {
function write (line 58) | async function write(name, typescriptDir, outDir) {
FILE: src/utils.js
function getCacheDir (line 13) | function getCacheDir(name) {
function posixPath (line 30) | function posixPath(path) {
function debug (line 35) | function debug(s) {
function info (line 40) | function info(s) {
function isObject (line 45) | function isObject(value) {
function eachObject (line 50) | function eachObject(object, f) {
function copyObject (line 57) | function copyObject(object, f) {
function glob (line 73) | function glob(pattern, cwd) {
function rm (line 83) | function rm(path) {
function mv (line 88) | function mv(from, to) {
function mkdir (line 101) | function mkdir(path) {
function exists (line 114) | function exists(path) {
function read (line 127) | function read(path) {
function readString (line 141) | function readString(path) {
function writeString (line 155) | function writeString(path, value) {
function getEnv (line 169) | function getEnv(name, fallback) {
function exec (line 181) | function exec(cmd, options) {
function spawn (line 198) | function spawn(command, args, options) {
function wait (line 203) | function wait(p) {
function tar (line 219) | function tar(stream, options) {
class Lock (line 239) | class Lock {
method constructor (line 240) | constructor() {
method withLock (line 245) | async withLock(f) {
method lock (line 256) | async lock() {
method unlock (line 270) | unlock() {
FILE: src/wasm-bindgen.js
constant WASM_BINDGEN_CACHE (line 8) | const WASM_BINDGEN_CACHE = {};
function getName (line 11) | function getName(version) {
function getUrl (line 33) | function getUrl(version, name) {
function getPath (line 38) | function getPath(dir) {
function fetchBin (line 47) | async function fetchBin(dir, version, name, path) {
function download (line 66) | async function download(dir, verbose) {
function run (line 90) | async function run({ bin, dir, wasmPath, outDir, typescript, extraArgs, ...
FILE: src/wasm-opt.js
function run (line 6) | async function run({ dir, input, output, extraArgs, verbose }) {
FILE: tests/src/bar/src/lib.rs
function main_js (line 6) | pub fn main_js() {
FILE: tests/src/foo/src/lib.rs
function foo (line 7) | fn foo() -> u32;
function nop (line 14) | fn nop();
function main_js (line 19) | pub fn main_js() {
Condensed preview — 28 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (60K chars).
[
{
"path": ".gitignore",
"chars": 110,
"preview": "node_modules\nCargo.lock\nyarn.lock\npackage-lock.json\nyarn-error.log\n/typescript\n/example-multi\n.DS_Store\n.yarn\n"
},
{
"path": ".yarnrc.yml",
"chars": 25,
"preview": "nodeLinker: node-modules\n"
},
{
"path": "README.md",
"chars": 8822,
"preview": "# rollup-plugin-rust\n\nRollup plugin for bundling and importing Rust crates.\n\nThis plugin internally uses [`wasm-bindgen`"
},
{
"path": "example/.gitignore",
"chars": 45,
"preview": "node_modules\nyarn-error.log\n/target\n/dist/js\n"
},
{
"path": "example/Cargo.toml",
"chars": 268,
"preview": "[package]\nname = \"example\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition = \"2018\"\ncategories = [\"wasm\"]\n"
},
{
"path": "example/dist/index.html",
"chars": 154,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n </head>\n <body>\n <script src=\"js/example.js\" type=\"mo"
},
{
"path": "example/package.json",
"chars": 520,
"preview": "{\n \"private\": true,\n \"type\": \"module\",\n \"name\": \"example\",\n \"author\": \"You <you@example.com>\",\n \"version\": \"0.1.0\","
},
{
"path": "example/rollup.config.js",
"chars": 608,
"preview": "import rust from \"@wasm-tool/rollup-plugin-rust\";\nimport serve from \"rollup-plugin-serve\";\nimport livereload from \"rollu"
},
{
"path": "example/rust-toolchain.toml",
"chars": 143,
"preview": "[toolchain]\nchannel = \"nightly-2025-11-20\"\ncomponents = [ \"rust-std\", \"rust-src\", \"rustfmt\", \"clippy\" ]\ntargets = [ \"was"
},
{
"path": "example/src/lib.rs",
"chars": 193,
"preview": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(start)]\npub fn main_js() {\n console_error_panic_"
},
{
"path": "package.json",
"chars": 1473,
"preview": "{\r\n \"name\": \"@wasm-tool/rollup-plugin-rust\",\r\n \"author\": \"Pauan <pauanyu+github@pm.me>\",\r\n \"description\": \"Rollup plu"
},
{
"path": "src/cargo.js",
"chars": 5539,
"preview": "import { getEnv, exec, debug, spawn, Lock } from \"./utils.js\";\n\n\nconst GLOBAL_LOCK = new Lock();\n\n\nexport class Nightly "
},
{
"path": "src/index.js",
"chars": 24149,
"preview": "import * as $path from \"node:path\";\nimport * as $toml from \"@iarna/toml\";\nimport { createFilter } from \"@rollup/pluginut"
},
{
"path": "src/typescript.js",
"chars": 1614,
"preview": "import * as $path from \"node:path\";\nimport { writeString, readString, mkdir } from \"./utils.js\";\n\n\nfunction trim(s) {\n "
},
{
"path": "src/utils.js",
"chars": 5800,
"preview": "import * as $path from \"node:path\";\nimport * as $stream from \"node:stream\";\nimport * as $fs from \"node:fs\";\nimport * as "
},
{
"path": "src/wasm-bindgen.js",
"chars": 2766,
"preview": "import * as $path from \"node:path\";\nimport * as $tar from \"tar\";\nimport $fetch from \"node-fetch\";\nimport { getVersion } "
},
{
"path": "src/wasm-opt.js",
"chars": 754,
"preview": "import * as $path from \"node:path\";\nimport { getEnv, debug, spawn, mv } from \"./utils.js\";\n\n\n// Replace with @webassembl"
},
{
"path": "tests/.gitignore",
"chars": 66,
"preview": "node_modules\nyarn-error.log\nCargo.lock\nyarn.lock\n/target\n/dist/js\n"
},
{
"path": "tests/Cargo.toml",
"chars": 109,
"preview": "[workspace]\nmembers = [\n \"src/bar\",\n \"src/foo\",\n]\nresolver = \"2\"\n\n[workspace.package]\nedition = \"2018\"\n"
},
{
"path": "tests/dist/index.html",
"chars": 254,
"preview": "<!DOCTYPE html>\n<html>\n <head>\n <meta charset=\"utf-8\" />\n </head>\n <body>\n <script type=\"module\" src=\"js/foo.js"
},
{
"path": "tests/rollup.config.js",
"chars": 590,
"preview": "import rust from \"../src/index.js\";\nimport { nodeResolve } from \"@rollup/plugin-node-resolve\";\nimport commonjs from \"@ro"
},
{
"path": "tests/src/bar/Cargo.toml",
"chars": 272,
"preview": "[package]\nname = \"bar\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition.workspace = true\ncategories = [\"was"
},
{
"path": "tests/src/bar/src/lib.rs",
"chars": 185,
"preview": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(start)]\npub fn main_js() {\n console_error_panic_"
},
{
"path": "tests/src/bar.js",
"chars": 27,
"preview": "import \"./bar/Cargo.toml\";\n"
},
{
"path": "tests/src/foo/Cargo.toml",
"chars": 272,
"preview": "[package]\nname = \"foo\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition.workspace = true\ncategories = [\"was"
},
{
"path": "tests/src/foo/package.json",
"chars": 146,
"preview": "{\n \"private\": true,\n \"name\": \"foo\",\n \"author\": \"You <you@example.com>\",\n \"version\": \"0.1.0\",\n \"devDependencies\": {\n"
},
{
"path": "tests/src/foo/src/lib.rs",
"chars": 423,
"preview": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(inline_js = \"export function foo() { return 5; }\")]"
},
{
"path": "tests/src/foo.js",
"chars": 183,
"preview": "import * as foo1 from \"./foo/Cargo.toml?custom\";\nimport * as foo2 from \"./foo/Cargo.toml?custom\";\n\nawait foo1.init({ mod"
}
]
About this extraction
This page contains the full source code of the wasm-tool/rollup-plugin-rust GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 28 files (54.2 KB), approximately 13.4k tokens, and a symbol index with 82 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.