[
  {
    "path": ".gitignore",
    "content": "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",
    "content": "nodeLinker: node-modules\n"
  },
  {
    "path": "README.md",
    "content": "# rollup-plugin-rust\n\nRollup plugin for bundling and importing Rust crates.\n\nThis plugin internally uses [`wasm-bindgen`](https://rustwasm.github.io/docs/wasm-bindgen/).\n\n`wasm-bindgen` is automatically installed, you do not need to install it separately.\n\n\n## Installation\n\nFirst, make sure that [rustup](https://rustup.rs/) is installed.\n\nIf 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).\n\nLastly, run this:\n\n```sh\nyarn add --dev @wasm-tool/rollup-plugin-rust binaryen\n```\n\nOr if you're using npm you can use this instead:\n\n```sh\nnpm install --save-dev @wasm-tool/rollup-plugin-rust binaryen\n```\n\n\n## Usage\n\nAdd the plugin to your `rollup.config.js`, and now you can use `Cargo.toml` files as entries:\n\n```js\nimport rust from \"@wasm-tool/rollup-plugin-rust\";\n\nexport default {\n    format: \"es\",\n    input: {\n        foo: \"Cargo.toml\",\n    },\n    plugins: [\n        rust(),\n    ],\n};\n```\n\nYou can import as many different `Cargo.toml` files as you want, each one will be compiled separately.\n\nSee 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.\n\n\n### Importing `Cargo.toml` within `.js`\n\nIt is also possible to import a `Cargo.toml` file inside of a `.js` file, like this:\n\n```js\nimport { foo, bar } from \"./path/to/Cargo.toml\";\n\n// Use functions which were exported from Rust...\n```\n\n----\n\n## Extra Tips\n\n\n### Nightly\n\nIt 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.\n\nYou can use nightly by creating a `rust-toolchain.toml` file in your project directory:\n\n```toml\n[toolchain]\nchannel = \"nightly-2025-11-20\"\ncomponents = [ \"rust-std\", \"rust-src\", \"rustfmt\", \"clippy\" ]\ntargets = [ \"wasm32-unknown-unknown\" ]\n```\n\nYou can change the `channel` to upgrade to the latest nightly version (or downgrade to a past nightly version).\n\nAfter changing the `rust-toolchain.toml` file, you might need to run `rustup show` in order to download the correct Rust version.\n\n\n### Workspaces\n\nWhen 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.\n\nCreate a `Cargo.toml` file in the root of your project which lists out the sub-crates that are a part of the workspace:\n\n```toml\n[workspace]\nmembers = [\n    \"src/foo\",\n    \"src/bar\",\n]\n```\n\n\n### Optimizing for size\n\nBy default the Rust compiler optimizes for maximum runtime performance, but this comes at the cost of a bigger file size.\n\nOn the web it is desirable to have a small file size, because a smaller `.wasm` file will download faster.\n\nThis plugin automatically optimizes for smaller file size, but you can reduce the size even further by adding this into your `Cargo.toml`:\n\n```toml\n[profile.release]\nopt-level = \"z\"\n```\n\nYou can also try `opt-level = \"s\"` which in some cases might produce a smaller file size.\n\nIf you're using workspaces, make sure to add that into your *workspace* `Cargo.toml`, not the sub-crates.\n\n\n### Usage with Vite\n\nThis 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.\n\nThis can cause errors when loading Wasm files, so you need to disable SSR when loading the Wasm:\n\n```js\nasync function loadWasm() {\n    // This code will only run in the browser\n    if (!import.meta.env.SSR) {\n        const { foo, bar } = await import(\"./path/to/Cargo.toml\");\n\n        // Use functions which were exported from Rust...\n    }\n}\n```\n\n\n### Customizing the Wasm loading\n\nFor very advanced use cases, you might want to manually initialize the `.wasm` code.\n\nIf you add `?custom` when importing a `Cargo.toml` file, it will give you:\n\n* `module` which is the `URL` of the `.wasm` file, or a `Uint8Array` if using `inlineWasm: true`.\n\n* `init` which is a function that initializes the Wasm and returns a Promise. The `init` function accepts these options:\n\n   * `module` which is a `URL` or `Uint8Array` or `WebAssembly.Module` for the `.wasm` code.\n   * `memory` which is a `WebAssembly.Memory` that will be used as the memory for the Wasm.\n\n* `initSync` which is a function that *synchronously* initializes the Wasm.\n\n   The `initSync` function should be avoided as much as possible, strongly prefer using `init` instead.\n\n   The `initSync` function accepts these options:\n\n   * `module` which is a `Uint8Array` or `WebAssembly.Module` for the `.wasm` code.\n   * `memory` which is a `WebAssembly.Memory` that will be used as the memory for the Wasm.\n\n```js\nimport { module, init } from \"./path/to/Cargo.toml?custom\";\n\nasync function loadWasm() {\n    const { foo, bar } = await init({\n        // The URL or Uint8Array which will be initialized.\n        module: module,\n\n        // The WebAssembly.Memory which will be used for the Wasm.\n        //\n        // If this is undefined then it will automatically create a new memory.\n        //\n        // This is useful for doing multi-threading with multiple Workers sharing the same SharedArrayBuffer.\n        memory: undefined,\n    });\n\n    // Use functions which were exported from Rust...\n}\n```\n\n----\n\n## Build options\n\nThe default options are good for most use cases, so you generally shouldn't need to change them.\n\nThese are the default options:\n\n```js\nrust({\n    // Whether the code will be run in Node.js or not.\n    //\n    // This is needed because Node.js does not support `fetch`.\n    nodejs: false,\n\n    // Whether to inline the `.wasm` file into the `.js` file.\n    //\n    // This is slower and it increases the file size by ~33%,\n    // but it does not require a separate `.wasm` file.\n    inlineWasm: false,\n\n    // Whether to display extra compilation information in the console.\n    verbose: false,\n\n    extraArgs: {\n        // Extra arguments passed to `cargo`.\n        cargo: [],\n\n        // Extra arguments passed to `rustc`, this is equivalent to `RUSTFLAGS`.\n        rustc: [],\n\n        // Extra arguments passed to `wasm-bindgen`.\n        wasmBindgen: [],\n\n        // Extra arguments passed to `wasm-opt`.\n        wasmOpt: [\"-O\", \"--enable-threads\", \"--enable-bulk-memory\", \"--enable-bulk-memory-opt\"],\n    },\n\n    optimize: {\n        // Whether to build in release mode.\n        //\n        // In watch mode this defaults to false.\n        release: true,\n\n        // Whether to run wasm-opt.\n        //\n        // In watch mode this defaults to false.\n        wasmOpt: true,\n\n        // Whether to use optimized rustc settings.\n        //\n        // This slows down compilation but significantly reduces the file size.\n        //\n        // If you use the nightly toolchain, this will reduce the file size even more.\n        rustc: true,\n\n        // These options default to false in watch mode.\n        strip: {\n            // Removes location information, resulting in lower file size but worse stace traces.\n            // Currently this only works on nightly.\n            location: true,\n\n            // Removes debug formatting from strings, such as `format!(\"{:?}\", ...)`\n            // Currently this only works on nightly.\n            formatDebug: true,\n        },\n    },\n\n    // Which files it should watch in watch mode. This is relative to the Cargo.toml file.\n    // Supports all of the glob syntax: https://www.npmjs.com/package/glob\n    watchPatterns: [\"src/**\"],\n\n    // These options should not be relied upon, they can change or disappear in future versions.\n    experimental: {\n        // Compiles with atomics and enables multi-threading.\n        // Currently this only works on nightly.\n        atomics: false,\n\n        // Whether the Wasm will be initialized synchronously or not.\n        //\n        // In the browser you can only use synchronous loading inside of Workers.\n        //\n        // This requires `inlineWasm: true`.\n        synchronous: false,\n\n        // Creates a `.d.ts` file for each `Cargo.toml` crate and places them\n        // into this directory.\n        //\n        // This is useful for libraries which want to export TypeScript types.\n        typescriptDeclarationDir: null,\n    },\n})\n```\n\n\n### Environment variables\n\nYou can use the following environment variables to customize some aspects of this plugin:\n\n* `CARGO_BIN` is the path to the `cargo` executable.\n* `WASM_BINDGEN_BIN` is the path to the `wasm-bindgen` executable.\n* `WASM_OPT_BIN` is the path to the `wasm-opt` executable.\n\nIf not specified, they will use a good default value, so you shouldn't need to change them, this is for advanced uses only.\n"
  },
  {
    "path": "example/.gitignore",
    "content": "node_modules\nyarn-error.log\n/target\n/dist/js\n"
  },
  {
    "path": "example/Cargo.toml",
    "content": "[package]\nname = \"example\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition = \"2018\"\ncategories = [\"wasm\"]\n\n[dependencies]\nwasm-bindgen = \"0.2.58\"\nconsole_error_panic_hook = \"0.1.6\"\n\n[dependencies.web-sys]\nversion = \"0.3.35\"\nfeatures = [\n    \"console\",\n]\n"
  },
  {
    "path": "example/dist/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n  </head>\n  <body>\n    <script src=\"js/example.js\" type=\"module\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "example/package.json",
    "content": "{\n  \"private\": true,\n  \"type\": \"module\",\n  \"name\": \"example\",\n  \"author\": \"You <you@example.com>\",\n  \"version\": \"0.1.0\",\n  \"scripts\": {\n    \"build\": \"rimraf dist/js && rollup --config\",\n    \"watch\": \"rimraf dist/js && rollup --config --watch\"\n  },\n  \"devDependencies\": {\n    \"@wasm-tool/rollup-plugin-rust\": \"portal:..\",\n    \"binaryen\": \"^125.0.0\",\n    \"rimraf\": \"^6.1.2\",\n    \"rollup\": \"^4.53.3\",\n    \"rollup-plugin-livereload\": \"^2.0.5\",\n    \"rollup-plugin-serve\": \"^3.0.0\",\n    \"rollup-plugin-terser\": \"^7.0.2\"\n  }\n}\n"
  },
  {
    "path": "example/rollup.config.js",
    "content": "import rust from \"@wasm-tool/rollup-plugin-rust\";\nimport serve from \"rollup-plugin-serve\";\nimport livereload from \"rollup-plugin-livereload\";\nimport { terser } from \"rollup-plugin-terser\";\n\nconst is_watch = !!process.env.ROLLUP_WATCH;\n\nexport default {\n    input: {\n        example: \"Cargo.toml\",\n    },\n    output: {\n        dir: \"dist/js\",\n        format: \"es\",\n        sourcemap: true,\n    },\n    plugins: [\n        rust(),\n\n        is_watch && serve({\n            contentBase: \"dist\",\n            open: true,\n        }),\n\n        is_watch && livereload(\"dist\"),\n\n        !is_watch && terser(),\n    ],\n};\n"
  },
  {
    "path": "example/rust-toolchain.toml",
    "content": "[toolchain]\nchannel = \"nightly-2025-11-20\"\ncomponents = [ \"rust-std\", \"rust-src\", \"rustfmt\", \"clippy\" ]\ntargets = [ \"wasm32-unknown-unknown\" ]\n"
  },
  {
    "path": "example/src/lib.rs",
    "content": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(start)]\npub fn main_js() {\n    console_error_panic_hook::set_once();\n\n    console::log_1(&JsValue::from(\"Hello world!\"));\n}\n"
  },
  {
    "path": "package.json",
    "content": "{\r\n  \"name\": \"@wasm-tool/rollup-plugin-rust\",\r\n  \"author\": \"Pauan <pauanyu+github@pm.me>\",\r\n  \"description\": \"Rollup plugin for bundling and importing Rust crates.\",\r\n  \"version\": \"3.1.5\",\r\n  \"license\": \"MIT\",\r\n  \"repository\": \"github:wasm-tool/rollup-plugin-rust\",\r\n  \"homepage\": \"https://github.com/wasm-tool/rollup-plugin-rust#readme\",\r\n  \"bugs\": \"https://github.com/wasm-tool/rollup-plugin-rust/issues\",\r\n  \"type\": \"module\",\r\n  \"main\": \"src/index.js\",\r\n  \"scripts\": {\r\n    \"test:foo\": \"cd tests/src/foo && yarn install\",\r\n    \"test\": \"yarn test:foo && cd tests && rimraf dist/js && rollup --config\",\r\n    \"test:watch\": \"yarn test:foo && cd tests && rimraf dist/js && rollup --config --watch\",\r\n    \"test:serve\": \"live-server tests/dist\"\r\n  },\r\n  \"directories\": {\r\n    \"example\": \"example\"\r\n  },\r\n  \"keywords\": [\r\n    \"rollup-plugin\",\r\n    \"vite-plugin\",\r\n    \"rust-wasm\",\r\n    \"wasm\",\r\n    \"rust\",\r\n    \"rollup\",\r\n    \"plugin\",\r\n    \"webassembly\",\r\n    \"wasm-bindgen\",\r\n    \"wasm-pack\"\r\n  ],\r\n  \"dependencies\": {\r\n    \"@iarna/toml\": \"^2.2.5\",\r\n    \"@rollup/pluginutils\": \"^5.3.0\",\r\n    \"chalk\": \"^5.6.2\",\r\n    \"glob\": \"^13.0.0\",\r\n    \"node-fetch\": \"^3.3.2\",\r\n    \"rimraf\": \"^6.1.2\",\r\n    \"tar\": \"^7.5.2\"\r\n  },\r\n  \"devDependencies\": {\r\n    \"@rollup/plugin-commonjs\": \"^29.0.0\",\r\n    \"@rollup/plugin-node-resolve\": \"^16.0.3\",\r\n    \"binaryen\": \"^125.0.0\",\r\n    \"live-server\": \"^1.2.2\",\r\n    \"rollup\": \"^4.53.3\"\r\n  },\r\n  \"peerDependencies\": {\r\n    \"binaryen\": \"*\"\r\n  }\r\n}\r\n"
  },
  {
    "path": "src/cargo.js",
    "content": "import { getEnv, exec, debug, spawn, Lock } from \"./utils.js\";\n\n\nconst GLOBAL_LOCK = new Lock();\n\n\nexport class Nightly {\n    constructor(year, month, day) {\n        this.year = year;\n        this.month = month;\n        this.day = day;\n    }\n\n    greaterThan(other) {\n        if (this.year > other.year) {\n            return true;\n\n        } else if (this.year === other.year) {\n            if (this.month > other.month) {\n                return true;\n\n            } else if (this.month === other.month) {\n                return this.day > other.day;\n\n            } else {\n                return false;\n            }\n\n        } else {\n            return false;\n        }\n    }\n\n    supportsImmediateAbort() {\n        return this.greaterThan(new Nightly(2025, 10, 4));\n    }\n}\n\n\nexport async function getNightly(dir) {\n    const bin = getEnv(\"CARGO_BIN\", \"cargo\");\n\n    // TODO make this faster somehow ?\n    const version = await exec(`${bin} --version`, { cwd: dir });\n\n    const a = /\\-nightly \\([^ ]+ ([0-9]+)\\-([0-9]+)\\-([0-9]+)\\)/.exec(version);\n\n    if (a) {\n        return new Nightly(+a[1], +a[2], +a[3]);\n\n    } else {\n        return null;\n    }\n}\n\n\nexport async function getTargetDir(dir) {\n    const bin = getEnv(\"CARGO_BIN\", \"cargo\");\n\n    // TODO make this faster somehow ?\n    const metadata = await exec(`${bin} metadata --format-version 1 --no-deps --color never`, { cwd: dir });\n\n    return JSON.parse(metadata)[\"target_directory\"];\n}\n\n\nexport async function getVersion(dir, name) {\n    const bin = getEnv(\"CARGO_BIN\", \"cargo\");\n    const spec = await exec(`${bin} pkgid ${name}`, { cwd: dir });\n\n    const version = /([\\d\\.]+)[\\r\\n]*$/.exec(spec);\n\n    if (version) {\n        return version[1];\n\n    } else {\n        throw new Error(`Could not determine ${name} version`);\n    }\n}\n\n\nexport async function run({ dir, verbose, cargoArgs, rustcArgs, release, optimize, nightly, atomics, strip }) {\n    const cargoBin = getEnv(\"CARGO_BIN\", \"cargo\");\n\n    let args = [\n        \"rustc\",\n        \"--lib\",\n        \"--target\", \"wasm32-unknown-unknown\",\n        \"--crate-type\", \"cdylib\", // Needed for wasm-bindgen to work\n    ];\n\n    let rustflags = [];\n\n    if (atomics) {\n        rustflags.push(\n            \"-C\", \"target-feature=+atomics,+bulk-memory,+mutable-globals\",\n            \"-C\", \"link-args=--shared-memory\",\n            \"-C\", \"link-args=--import-memory\",\n\n            \"-C\", \"link-args=--export=__wasm_init_tls\",\n            \"-C\", \"link-args=--export=__tls_size\",\n            \"-C\", \"link-args=--export=__tls_align\",\n            \"-C\", \"link-args=--export=__tls_base\",\n        );\n\n        args.push(\"-Z\", \"build-std=panic_abort,core,std,alloc,proc_macro\");\n    }\n\n    // https://doc.rust-lang.org/cargo/reference/profiles.html#release\n    if (release) {\n        args.push(\"--release\");\n\n        if (nightly) {\n            if (strip.location.get()) {\n                rustflags.push(\"-Z\", \"location-detail=none\");\n            }\n\n            if (strip.formatDebug.get()) {\n                rustflags.push(\"-Z\", \"fmt-debug=none\");\n            }\n        }\n\n        if (optimize) {\n            // Wasm doesn't support unwind, so we abort instead\n            if (nightly && nightly.supportsImmediateAbort()) {\n                // Reduces file size by removing panic strings\n                args.push(\"--config\");\n                args.push(\"profile.release.panic=\\\"immediate-abort\\\"\");\n\n            } else {\n                args.push(\"--config\");\n                args.push(\"profile.release.panic=\\\"abort\\\"\");\n            }\n\n            // Improves runtime performance and file size\n            args.push(\"--config\");\n            args.push(\"profile.release.lto=true\");\n\n            // Improves runtime performance\n            args.push(\"--config\");\n            args.push(\"profile.release.codegen-units=1\");\n\n            // Reduces file size\n            args.push(\"--config\");\n            args.push(\"profile.release.strip=true\");\n\n            // Reduces file size by removing panic strings\n            if (nightly) {\n                if (nightly.supportsImmediateAbort()) {\n                    args.push(\"-Z\", \"panic-immediate-abort\");\n                }\n\n                args.push(\"-Z\", \"build-std=panic_abort,core,std,alloc,proc_macro\");\n                args.push(\"-Z\", \"build-std-features=optimize_for_size\");\n            }\n        }\n\n    // https://doc.rust-lang.org/cargo/reference/profiles.html#dev\n    } else {\n        if (optimize) {\n            // Wasm doesn't support unwind\n            args.push(\"--config\");\n            args.push(\"profile.dev.panic=\\\"abort\\\"\");\n\n            args.push(\"--config\");\n            args.push(\"profile.dev.lto=\\\"off\\\"\");\n\n            // Speeds up compilation\n            // https://github.com/MoonZoon/MoonZoon/issues/170\n            args.push(\"--config\");\n            args.push(\"profile.dev.debug=false\");\n        }\n    }\n\n    rustflags = rustflags.concat(rustcArgs);\n\n    if (rustflags.length > 0) {\n        args.push(\"--config\", \"build.rustflags=\" + JSON.stringify(rustflags));\n    }\n\n    args = args.concat(cargoArgs);\n\n    await GLOBAL_LOCK.withLock(async () => {\n        if (verbose) {\n            debug(`Running cargo ${args.join(\" \")}`);\n        }\n\n        try {\n            await spawn(cargoBin, args, { cwd: dir, stdio: \"inherit\" });\n\n        } catch (e) {\n            if (verbose) {\n                throw e;\n\n            } else {\n                const e = new Error(\"Rust compilation failed\");\n                e.stack = null;\n                throw e;\n            }\n        }\n    });\n}\n"
  },
  {
    "path": "src/index.js",
    "content": "import * as $path from \"node:path\";\nimport * as $toml from \"@iarna/toml\";\nimport { createFilter } from \"@rollup/pluginutils\";\nimport { glob, rm, read, readString, debug, getEnv, isObject, eachObject, copyObject } from \"./utils.js\";\nimport * as $wasmBindgen from \"./wasm-bindgen.js\";\nimport * as $cargo from \"./cargo.js\";\nimport * as $wasmOpt from \"./wasm-opt.js\";\nimport * as $typescript from \"./typescript.js\";\n\n\nconst PREFIX = \"./.__rollup-plugin-rust__\";\nconst INLINE_ID = \"\\0__rollup-plugin-rust-inlineWasm__\";\n\n\nfunction stripPath(path) {\n    return path.replace(/\\?[^\\?]*$/, \"\");\n}\n\n\nclass Option {\n    constructor(value) {\n        this.value = value;\n        this.isDefault = true;\n    }\n\n    get() {\n        return this.value;\n    }\n\n    getOr(fallback) {\n        if (this.isDefault) {\n            return fallback;\n\n        } else {\n            return this.value;\n        }\n    }\n\n    set(value) {\n        this.value = value;\n        this.isDefault = false;\n    }\n}\n\n\nclass State {\n    constructor() {\n        // Whether the plugin is running in Vite or not\n        this.vite = false;\n\n        // Whether we're in watch mode or not\n        this.watch = false;\n\n        // Whether the options have been processed or not\n        this.processed = false;\n\n        this.fileIds = new Set();\n\n        this.defaults = {\n            watchPatterns: [\"src/**\"],\n\n            inlineWasm: false,\n\n            verbose: false,\n\n            nodejs: false,\n\n            optimize: {\n                release: true,\n\n                wasmOpt: true,\n\n                rustc: true,\n\n                strip: {\n                    location: true,\n\n                    formatDebug: true,\n                },\n            },\n\n            extraArgs: {\n                cargo: [],\n\n                rustc: [],\n\n                wasmBindgen: [],\n\n                // TODO figure out better optimization options ?\n                wasmOpt: [\"-O\", \"--enable-threads\", \"--enable-bulk-memory\", \"--enable-bulk-memory-opt\"],\n            },\n\n            experimental: {\n                atomics: false,\n\n                synchronous: false,\n\n                typescriptDeclarationDir: null,\n            },\n        };\n\n        // Make a copy of the default settings\n        this.options = copyObject(this.defaults, (value) => new Option(value));\n\n        this.deprecations = {\n            debug: (cx, value) => {\n                cx.warn(\"The `debug` option has been changed to `optimize.release`\");\n                this.options.optimize.release.set(!value);\n            },\n\n            cargoArgs: (cx, value) => {\n                cx.warn(\"The `cargoArgs` option has been changed to `extraArgs.cargo`\");\n                this.options.extraArgs.cargo.set(value);\n            },\n\n            wasmBindgenArgs: (cx, value) => {\n                cx.warn(\"The `wasmBindgenArgs` option has been changed to `extraArgs.wasmBindgen`\");\n                this.options.extraArgs.wasmBindgen.set(value);\n            },\n\n            wasmOptArgs: (cx, value) => {\n                cx.warn(\"The `wasmOptArgs` option has been changed to `extraArgs.wasmOpt`\");\n                this.options.extraArgs.wasmOpt.set(value);\n            },\n\n            serverPath: (cx, value) => {\n                cx.warn(\"The `serverPath` option is deprecated and no longer works\");\n            },\n\n            importHook: (cx, value) => {\n                cx.warn(\"The `importHook` option is deprecated and no longer works\");\n            },\n\n            experimental: {\n                directExports: (cx, value) => {\n                    cx.warn(\"The `experimental.directExports` option is deprecated and no longer works\");\n                },\n            },\n        };\n\n        this.cache = {\n            nightly: {},\n            targetDir: {},\n            wasmBindgen: {},\n            build: {},\n        };\n    }\n\n\n    reset() {\n        this.fileIds.clear();\n\n        this.cache.nightly = {};\n        this.cache.targetDir = {};\n        this.cache.wasmBindgen = {};\n        this.cache.build = {};\n    }\n\n\n    processOptions(cx, oldOptions) {\n        if (!this.processed) {\n            this.processed = true;\n\n            // Overwrite the default settings with the user-provided settings\n            this.setOptions(cx, [], oldOptions, this.options, this.defaults, this.deprecations);\n        }\n    }\n\n    setOptions(cx, path, oldOptions, options, defaults, deprecations) {\n        if (oldOptions != null) {\n            if (isObject(oldOptions)) {\n                eachObject(oldOptions, (key, value) => {\n                    const newPath = path.concat([key]);\n\n                    // If the option is deprecated, call the function\n                    if (deprecations != null && key in deprecations) {\n                        const deprecation = deprecations[key];\n\n                        if (isObject(deprecation)) {\n                            this.setOptions(cx, newPath, value, options?.[key], defaults?.[key], deprecation);\n\n                        } else {\n                            deprecation(cx, value);\n                        }\n\n                    // If the option has a default, apply it\n                    } else if (defaults != null && key in defaults) {\n                        const def = defaults[key];\n\n                        if (isObject(def)) {\n                            this.setOptions(cx, newPath, value, options?.[key], def, deprecations?.[key]);\n\n                        } else if (value != null) {\n                            if (options[key].isDefault) {\n                                options[key].set(value);\n                            }\n                        }\n\n                    // The option doesn't exist\n                    } else {\n                        throw new Error(`The \\`${newPath.join(\".\")}\\` option does not exist`);\n                    }\n                });\n\n            } else if (path.length > 0) {\n                throw new Error(`The \\`${path.join(\".\")}\\` option must be an object`);\n\n            } else {\n                throw new Error(`Options must be an object`);\n            }\n        }\n    }\n\n\n    async watchFiles(cx, dir) {\n        if (this.watch) {\n            const matches = await Promise.all(this.options.watchPatterns.get().map((pattern) => glob(pattern, dir)));\n\n            // TODO deduplicate matches ?\n            matches.forEach(function (files) {\n                files.forEach(function (file) {\n                    cx.addWatchFile(file);\n                });\n            });\n        }\n    }\n\n\n    async getNightly(dir) {\n        let nightly = this.cache.nightly[dir];\n\n        if (nightly == null) {\n            nightly = this.cache.nightly[dir] = $cargo.getNightly(dir);\n        }\n\n        return await nightly;\n    }\n\n\n    async getTargetDir(dir) {\n        let targetDir = this.cache.targetDir[dir];\n\n        if (targetDir == null) {\n            targetDir = this.cache.targetDir[dir] = $cargo.getTargetDir(dir);\n        }\n\n        return await targetDir;\n    }\n\n\n    async getWasmBindgen(dir) {\n        let bin = getEnv(\"WASM_BINDGEN_BIN\", null);\n\n        if (bin == null) {\n            bin = this.cache.wasmBindgen[dir];\n\n            if (bin == null) {\n                bin = this.cache.wasmBindgen[dir] = $wasmBindgen.download(dir, this.options.verbose.get());\n            }\n\n            return await bin;\n\n        } else {\n            return bin;\n        }\n    }\n\n\n    async loadWasm(outDir) {\n        const wasmPath = $path.join(outDir, \"index_bg.wasm\");\n\n        if (this.options.verbose.get()) {\n            debug(`Looking for wasm at ${wasmPath}`);\n        }\n\n        return await read(wasmPath);\n    }\n\n\n    async compileTypescript(name, outDir) {\n        if (this.options.experimental.typescriptDeclarationDir.get() != null) {\n            await $typescript.write(\n                name,\n                this.options.experimental.typescriptDeclarationDir.get(),\n                outDir,\n            );\n        }\n    }\n\n\n    async compileTypescriptCustom(name, isCustom) {\n        if (isCustom && this.options.experimental.typescriptDeclarationDir.get() != null) {\n            await $typescript.writeCustom(\n                name,\n                this.options.experimental.typescriptDeclarationDir.get(),\n                this.options.inlineWasm.get(),\n                this.options.experimental.synchronous.get(),\n            );\n        }\n    }\n\n\n    async wasmOpt(cx, outDir) {\n        if (this.options.optimize.wasmOpt.getOr(!this.watch)) {\n            const result = await $wasmOpt.run({\n                dir: outDir,\n                input: \"index_bg.wasm\",\n                output: \"wasm_opt.wasm\",\n                extraArgs: this.options.extraArgs.wasmOpt.get(),\n                verbose: this.options.verbose.get(),\n            });\n\n            if (result !== null) {\n                cx.warn(\"wasm-opt failed: \" + result.message);\n            }\n        }\n    }\n\n\n    compileInlineWasm(build) {\n        const wasmString = JSON.stringify(build.wasm.toString(\"base64\"));\n\n        const code = `\n            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];\n\n            function getBase64Code(charCode) {\n                return base64codes[charCode - 43];\n            }\n\n            function base64Decode(str) {\n                let missingOctets = str.endsWith(\"==\") ? 2 : str.endsWith(\"=\") ? 1 : 0;\n                let n = str.length;\n                let result = new Uint8Array(3 * (n / 4));\n                let buffer;\n\n                for (let i = 0, j = 0; i < n; i += 4, j += 3) {\n                    buffer =\n                        getBase64Code(str.charCodeAt(i)) << 18 |\n                        getBase64Code(str.charCodeAt(i + 1)) << 12 |\n                        getBase64Code(str.charCodeAt(i + 2)) << 6 |\n                        getBase64Code(str.charCodeAt(i + 3));\n                    result[j] = buffer >> 16;\n                    result[j + 1] = (buffer >> 8) & 0xFF;\n                    result[j + 2] = buffer & 0xFF;\n                }\n\n                return result.subarray(0, result.length - missingOctets);\n            }\n\n            export default base64Decode(${wasmString});\n        `;\n\n        return {\n            code,\n            map: { mappings: '' },\n            moduleSideEffects: false,\n        };\n    }\n\n\n    compileJsInline(build, isCustom) {\n        let mainCode;\n        let sideEffects;\n\n        if (this.options.experimental.synchronous.get()) {\n            if (isCustom) {\n                sideEffects = false;\n\n                mainCode = `export { module };\n\n                export function init(options) {\n                    exports.initSync({\n                        module: options.module,\n                        memory: options.memory,\n                    });\n                    return exports;\n                }\n\n                export const initSync = init;`\n\n            } else {\n                sideEffects = true;\n\n                mainCode = `\n                    exports.initSync({ module });\n                    export * from ${build.importPath};\n                `;\n            }\n\n        } else {\n            if (isCustom) {\n                sideEffects = false;\n\n                mainCode = `export { module };\n\n                export async function init(options) {\n                    await exports.default({\n                        module_or_path: await options.module,\n                        memory: options.memory,\n                    });\n                    return exports;\n                }\n\n                export function initSync(options) {\n                    exports.initSync({\n                        module: options.module,\n                        memory: options.memory,\n                    });\n                    return exports;\n                }`;\n\n            } else {\n                sideEffects = true;\n\n                mainCode = `\n                    await exports.default({ module_or_path: module });\n                    export * from ${build.importPath};\n                `;\n            }\n        }\n\n\n        const wasmString = JSON.stringify(build.wasm.toString(\"base64\"));\n\n        const code = `\n            import * as exports from ${build.importPath};\n\n            import module from \"${INLINE_ID}\";\n\n            ${mainCode}\n        `;\n\n        return {\n            code,\n            map: { mappings: '' },\n            moduleSideEffects: sideEffects,\n            meta: {\n                \"rollup-plugin-rust\": { root: false, realPath: build.realPath }\n            },\n        };\n    }\n\n\n    compileJsNormal(build, isCustom) {\n        let wasmPath = `import.meta.ROLLUP_FILE_URL_${build.fileId}`;\n\n        let prelude;\n\n        if (this.options.nodejs.get()) {\n            prelude = `function loadFile(url) {\n                return new Promise((resolve, reject) => {\n                    require(\"node:fs\").readFile(url, (err, data) => {\n                        if (err) {\n                            reject(err);\n\n                        } else {\n                            resolve(data);\n                        }\n                    });\n                });\n            }\n\n            const module = loadFile(${wasmPath});`;\n\n        } else {\n            prelude = `const module = ${wasmPath};`;\n        }\n\n\n        let mainCode;\n        let sideEffects;\n\n        if (this.options.experimental.synchronous.get()) {\n            throw new Error(\"synchronous option can only be used with inlineWasm: true\");\n\n        } else {\n            if (isCustom) {\n                sideEffects = false;\n\n                mainCode = `export { module };\n\n                export async function init(options) {\n                    await exports.default({\n                        module_or_path: await options.module,\n                        memory: options.memory,\n                    });\n                    return exports;\n                }\n\n                export function initSync(options) {\n                    exports.initSync({\n                        module: options.module,\n                        memory: options.memory,\n                    });\n                    return exports;\n                }`;\n\n            } else {\n                sideEffects = true;\n\n                mainCode = `\n                    await exports.default({ module_or_path: module });\n                    export * from ${build.importPath};\n                `;\n            }\n        }\n\n        return {\n            code: `\n                import * as exports from ${build.importPath};\n\n                ${prelude}\n                ${mainCode}\n            `,\n            map: { mappings: '' },\n            moduleSideEffects: sideEffects,\n            meta: {\n                \"rollup-plugin-rust\": { root: false, realPath: build.realPath }\n            },\n        };\n    }\n\n\n    compileJs(build, isCustom) {\n        if (this.options.inlineWasm.get()) {\n            return this.compileJsInline(build, isCustom);\n\n        } else {\n            return this.compileJsNormal(build, isCustom);\n        }\n    }\n\n\n    async getInfo(dir, id) {\n        const [targetDir, source] = await Promise.all([\n            this.getTargetDir(dir),\n            readString(id),\n        ]);\n\n        const toml = $toml.parse(source);\n\n        // TODO make this faster somehow\n        // TODO does it need to do more transformations on the name ?\n        const name = toml.package.name.replace(/\\-/g, \"_\");\n\n        const wasmPath = $path.resolve($path.join(\n            targetDir,\n            \"wasm32-unknown-unknown\",\n            (this.options.optimize.release.getOr(!this.watch) ? \"release\" : \"debug\"),\n            name + \".wasm\"\n        ));\n\n        const outDir = $path.resolve($path.join(targetDir, \"rollup-plugin-rust\", name));\n\n        if (this.options.verbose.get()) {\n            debug(`Using target directory ${targetDir}`);\n            debug(`Using rustc output ${wasmPath}`);\n            debug(`Using output directory ${outDir}`);\n        }\n\n        await rm(outDir);\n\n        return { name, wasmPath, outDir };\n    }\n\n\n    async buildCargo(dir) {\n        const nightly = await this.getNightly(dir);\n\n        await $cargo.run({\n            dir,\n            nightly,\n            verbose: this.options.verbose.get(),\n            cargoArgs: this.options.extraArgs.cargo.get(),\n            rustcArgs: this.options.extraArgs.rustc.get(),\n            release: this.options.optimize.release.getOr(!this.watch),\n            optimize: this.options.optimize.rustc.get(),\n            strip: this.options.optimize.strip,\n            atomics: this.options.experimental.atomics.get(),\n        });\n    }\n\n\n    async buildWasm(cx, dir, bin, name, wasmPath, outDir) {\n        await $wasmBindgen.run({\n            bin,\n            dir,\n            wasmPath,\n            outDir,\n            typescript: this.options.experimental.typescriptDeclarationDir.get() != null,\n            extraArgs: this.options.extraArgs.wasmBindgen.get(),\n            verbose: this.options.verbose.get(),\n        });\n\n        const [wasm] = await Promise.all([\n            this.wasmOpt(cx, outDir).then(() => {\n                return this.loadWasm(outDir);\n            }),\n\n            this.compileTypescript(name, outDir),\n        ]);\n\n        let fileId;\n\n        if (!this.options.inlineWasm.get()) {\n            fileId = cx.emitFile({\n                type: \"asset\",\n                source: wasm,\n                name: name + \".wasm\"\n            });\n\n            this.fileIds.add(fileId);\n        }\n\n        const realPath = $path.join(outDir, \"index.js\");\n\n        // This returns a fake file path, this ensures that the directory is the\n        // same as the Cargo.toml file, which is necessary in order to make npm\n        // package imports work correctly.\n        const importPath = `\"${PREFIX}${name}/index.js\"`;\n\n        return { name, outDir, importPath, realPath, wasm, fileId };\n    }\n\n\n    async build(cx, dir, id) {\n        if (this.options.verbose.get()) {\n            debug(`Compiling ${id}`);\n        }\n\n        await this.buildCargo(dir);\n\n        const [bin, { name, wasmPath, outDir }] = await Promise.all([\n            this.getWasmBindgen(dir),\n            this.getInfo(dir, id),\n        ]);\n\n        return await this.buildWasm(cx, dir, bin, name, wasmPath, outDir);\n    }\n\n\n    async load(cx, oldId) {\n        try {\n            const id = stripPath(oldId);\n\n            let promise = this.cache.build[id];\n\n            if (promise == null) {\n                const dir = $path.dirname(id);\n\n                promise = this.cache.build[id] = Promise.all([\n                    this.build(cx, dir, id),\n                    this.watchFiles(cx, dir),\n                ]);\n            }\n\n            const [build] = await promise;\n\n            if (oldId.endsWith(\"?inline\")) {\n                return this.compileInlineWasm(build);\n\n            } else {\n                const isCustom = oldId.endsWith(\"?custom\");\n\n                const [result] = await Promise.all([\n                    this.compileJs(build, isCustom),\n                    this.compileTypescriptCustom(build.name, isCustom),\n                ]);\n\n                return result;\n            }\n\n        } catch (e) {\n            if (!this.options.verbose.get()) {\n                e.stack = null;\n            }\n\n            throw e;\n        }\n    }\n}\n\n\nexport default function rust(options = {}) {\n    // TODO should the filter affect the watching ?\n    // TODO should the filter affect the Rust compilation ?\n    const filter = createFilter(options.include, options.exclude);\n\n    const state = new State();\n\n    return {\n        name: \"rust\",\n\n        // Vite-specific hook\n        configResolved(config) {\n            state.vite = true;\n\n            if (config.command !== \"build\") {\n                // We have to force inlineWasm during dev because Vite doesn't support emitFile\n                // https://github.com/vitejs/vite/issues/7029\n                state.options.inlineWasm.set(true);\n            }\n        },\n\n        buildStart(rollup) {\n            state.reset();\n\n            state.processOptions(this, options);\n\n            state.watch = this.meta.watchMode || rollup.watch;\n        },\n\n        // This is only compatible with Rollup 2.78.0 and higher\n        resolveId: {\n            order: \"pre\",\n            handler(id, importer, info) {\n                if (id === INLINE_ID) {\n                    return {\n                        id: stripPath(importer) + \"?inline\",\n                        meta: {\n                            \"rollup-plugin-rust\": { root: true }\n                        }\n                    };\n\n                } else {\n                    const name = $path.basename(id);\n\n                    const normal = (name === \"Cargo.toml\");\n                    const custom = (name === \"Cargo.toml?custom\");\n\n                    if ((normal || custom) && filter(id)) {\n                        const path = (importer ? $path.resolve($path.dirname(importer), id) : $path.resolve(id));\n\n                        return {\n                            id: path,\n                            moduleSideEffects: !custom,\n                            meta: {\n                                \"rollup-plugin-rust\": { root: true }\n                            }\n                        };\n\n                    // Rewrites the fake file paths to real file paths.\n                    } else if (importer && id[0] === \".\") {\n                        const info = this.getModuleInfo(importer);\n\n                        if (info && info.meta) {\n                            const meta = info.meta[\"rollup-plugin-rust\"];\n\n                            if (meta && !meta.root) {\n                                // TODO maybe use resolve ?\n                                const path = $path.join($path.dirname(importer), id);\n\n                                const realPath = (id.startsWith(PREFIX)\n                                    ? meta.realPath\n                                    : $path.join($path.dirname(meta.realPath), id));\n\n                                return {\n                                    id: path,\n                                    meta: {\n                                        \"rollup-plugin-rust\": {\n                                            root: false,\n                                            realPath,\n                                        }\n                                    }\n                                };\n                            }\n                        }\n                    }\n                }\n\n                return null;\n            },\n        },\n\n        load(id, loadState) {\n            const info = this.getModuleInfo(id);\n\n            if (info && info.meta) {\n                const meta = info.meta[\"rollup-plugin-rust\"];\n\n                if (meta) {\n                    if (meta.root) {\n                        // This causes Vite to load a noop module during SSR\n                        if (state.vite && loadState && loadState.ssr) {\n                            return {\n                                code: `export {};`,\n                                map: { mappings: '' },\n                                moduleSideEffects: false,\n                            };\n\n                        // This compiles the Cargo.toml\n                        } else {\n                            return state.load(this, id);\n                        }\n\n                    } else {\n                        if (state.options.verbose.get()) {\n                            debug(`Loading file ${meta.realPath}`);\n                        }\n\n                        // This maps the fake path to a real path on disk and loads it\n                        return readString(meta.realPath);\n                    }\n                }\n            }\n\n            return null;\n        },\n\n        resolveFileUrl(info) {\n            if (state.fileIds.has(info.referenceId)) {\n                return `new URL(${JSON.stringify(info.relativePath)}, import.meta.url)`;\n\n            } else {\n                return null;\n            }\n        },\n    };\n};\n"
  },
  {
    "path": "src/typescript.js",
    "content": "import * as $path from \"node:path\";\nimport { writeString, readString, mkdir } from \"./utils.js\";\n\n\nfunction trim(s) {\n    return s.replace(/\\n\\n\\n+/g, \"\\n\\n\").replace(/\\n\\n+\\}/g, \"\\n}\").trim();\n}\n\n\nfunction parse(declaration) {\n    declaration = declaration.replace(/export type InitInput = [\\s\\S]*/g, \"\");\n    return trim(declaration);\n}\n\n\nexport async function writeCustom(name, typescriptDir, inline, synchronous) {\n    const outPath = $path.join(typescriptDir, name + \"_custom.d.ts\");\n\n    let output;\n\n    if (synchronous) {\n        output = `export type Module = BufferSource | WebAssembly.Module;\n\nexport type InitOutput = typeof import(\"./${name}\");\n\nexport interface InitOptions {\n    module: Module;\n\n    memory?: WebAssembly.Memory;\n}\n\nexport const module: Uint8Array;\n\nexport function init(options: InitOptions): InitOutput;\n`;\n\n    } else {\n        output = `export type Module = RequestInfo | URL | Response | BufferSource | WebAssembly.Module;\n\nexport type InitOutput = typeof import(\"./${name}\");\n\nexport interface InitOptions {\n    module: Module | Promise<Module>;\n\n    memory?: WebAssembly.Memory;\n}\n\nexport const module: ${inline ? \"Uint8Array\" : \"URL\"};\n\nexport function init(options: InitOptions): Promise<InitOutput>;\n`;\n    }\n\n    await writeString(outPath, output);\n}\n\n\nexport async function write(name, typescriptDir, outDir) {\n    const realPath = $path.join(outDir, \"index.d.ts\");\n\n    const [declaration] = await Promise.all([\n        readString(realPath),\n\n        mkdir(typescriptDir),\n    ]);\n\n    await writeString($path.join(typescriptDir, name + \".d.ts\"), parse(declaration));\n}\n"
  },
  {
    "path": "src/utils.js",
    "content": "import * as $path from \"node:path\";\nimport * as $stream from \"node:stream\";\nimport * as $fs from \"node:fs\";\nimport * as $os from \"node:os\";\nimport * as $child from \"node:child_process\";\n\nimport * as $glob from \"glob\";\nimport * as $rimraf from \"rimraf\";\nimport * as $tar from \"tar\";\nimport $chalk from \"chalk\";\n\n\nexport function getCacheDir(name) {\n    switch (process.platform) {\n    case \"win32\":\n        const localAppData = process.env.LOCALAPPDATA || $path.join($os.homedir(), \"AppData\", \"Local\");\n        return $path.join(localAppData, name, \"Cache\");\n\n    case \"darwin\":\n        return $path.join($os.homedir(), \"Library\", \"Caches\", name);\n\n    // https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html\n    default:\n        const cacheDir = process.env.XDG_CACHE_HOME || $path.join($os.homedir(), \".cache\");\n        return $path.join(cacheDir, name);\n    }\n}\n\n\nexport function posixPath(path) {\n    return path.replace(/\\\\/g, $path.posix.sep);\n}\n\n\nexport function debug(s) {\n    console.debug($chalk.blue(\"> \" + s + \"\\n\"));\n}\n\n\nexport function info(s) {\n    console.info($chalk.yellow(s));\n}\n\n\nexport function isObject(value) {\n    return Object.prototype.toString.call(value) === \"[object Object]\";\n}\n\n\nexport function eachObject(object, f) {\n    Object.keys(object).forEach((key) => {\n        f(key, object[key]);\n    });\n}\n\n\nexport function copyObject(object, f) {\n    const output = {};\n\n    eachObject(object, (key, value) => {\n        if (isObject(value)) {\n            output[key] = copyObject(value, f);\n\n        } else {\n            output[key] = f(value);\n        }\n    });\n\n    return output;\n}\n\n\nexport function glob(pattern, cwd) {\n    return $glob.glob(pattern, {\n        cwd: cwd,\n        strict: true,\n        absolute: true,\n        nodir: true\n    });\n}\n\n\nexport function rm(path) {\n    return $rimraf.rimraf(path, { glob: false });\n}\n\n\nexport function mv(from, to) {\n    return new Promise((resolve, reject) => {\n        $fs.rename(from, to, (err) => {\n            if (err) {\n                reject(err);\n            } else {\n                resolve();\n            }\n        });\n    });\n}\n\n\nexport function mkdir(path) {\n    return new Promise((resolve, reject) => {\n        $fs.mkdir(path, { recursive: true }, (err) => {\n            if (err) {\n                reject(err);\n            } else {\n                resolve();\n            }\n        });\n    });\n}\n\n\nexport function exists(path) {\n    return new Promise((resolve, reject) => {\n        $fs.access(path, (err) => {\n            if (err) {\n                resolve(false);\n            } else {\n                resolve(true);\n            }\n        });\n    });\n}\n\n\nexport function read(path) {\n    return new Promise(function (resolve, reject) {\n        $fs.readFile(path, function (err, file) {\n            if (err) {\n                reject(err);\n\n            } else {\n                resolve(file);\n            }\n        });\n    });\n}\n\n\nexport function readString(path) {\n    return new Promise(function (resolve, reject) {\n        $fs.readFile(path, { encoding: \"utf8\" }, function (err, file) {\n            if (err) {\n                reject(err);\n\n            } else {\n                resolve(file);\n            }\n        });\n    });\n}\n\n\nexport function writeString(path, value) {\n    return new Promise(function (resolve, reject) {\n        $fs.writeFile(path, value, { encoding: \"utf8\" }, function (err) {\n            if (err) {\n                reject(err);\n\n            } else {\n                resolve();\n            }\n        });\n    });\n}\n\n\nexport function getEnv(name, fallback) {\n    const value = process.env[name];\n\n    if (value == null) {\n        return fallback;\n\n    } else {\n        return value;\n    }\n}\n\n\nexport function exec(cmd, options) {\n    return new Promise((resolve, reject) => {\n        $child.exec(cmd, options, (err, stdout, stderr) => {\n            if (err) {\n                reject(err);\n\n            } else if (stderr.length > 0) {\n                reject(new Error(stderr));\n\n            } else {\n                resolve(stdout);\n            }\n        });\n    });\n}\n\n\nexport function spawn(command, args, options) {\n    return wait($child.spawn(command, args, options));\n}\n\n\nexport function wait(p) {\n    return new Promise((resolve, reject) => {\n        p.on(\"close\", (code) => {\n            if (code === 0) {\n                resolve();\n\n            } else {\n                reject(new Error(\"Command `\" + p.spawnargs.join(\" \") + \"` failed with error code: \" + code));\n            }\n        });\n\n        p.on(\"error\", reject);\n    });\n}\n\n\nexport function tar(stream, options) {\n    return new Promise((resolve, reject) => {\n        $stream.pipeline(\n            stream,\n            $tar.x({\n                cwd: options.cwd,\n                strict: true,\n            }, options.files),\n            (err) => {\n                if (err) {\n                    reject(err);\n                } else {\n                    resolve();\n                }\n            },\n        );\n    });\n}\n\n\nexport class Lock {\n    constructor() {\n        this.locked = false;\n        this.pending = [];\n    }\n\n    async withLock(f) {\n        await this.lock();\n\n        try {\n            return await f();\n\n        } finally {\n            this.unlock();\n        }\n    }\n\n    async lock() {\n        if (this.locked) {\n            await new Promise((resolve, reject) => {\n                this.pending.push(resolve);\n            });\n\n            if (this.locked) {\n                throw new Error(\"Invalid lock state\");\n            }\n        }\n\n        this.locked = true;\n    }\n\n    unlock() {\n        this.locked = false;\n\n        if (this.pending.length !== 0) {\n            const resolve = this.pending.shift();\n            // Wake up pending task\n            resolve();\n        }\n    }\n}\n"
  },
  {
    "path": "src/wasm-bindgen.js",
    "content": "import * as $path from \"node:path\";\nimport * as $tar from \"tar\";\nimport $fetch from \"node-fetch\";\nimport { getVersion } from \"./cargo.js\";\nimport { exec, mkdir, getCacheDir, tar, exists, spawn, info, debug, getEnv } from \"./utils.js\";\n\n\nconst WASM_BINDGEN_CACHE = {};\n\n\nfunction getName(version) {\n    switch (process.platform) {\n    case \"win32\":\n        return `wasm-bindgen-${version}-x86_64-pc-windows-msvc`;\n    case \"darwin\":\n        switch (process.arch) {\n        case \"arm64\":\n            return `wasm-bindgen-${version}-aarch64-apple-darwin`;\n        default:\n            return `wasm-bindgen-${version}-x86_64-apple-darwin`;\n        }\n    default:\n        switch (process.arch) {\n        case \"arm64\":\n            return `wasm-bindgen-${version}-aarch64-unknown-linux-gnu`;\n        default:\n            return `wasm-bindgen-${version}-x86_64-unknown-linux-musl`;\n        }\n    }\n}\n\n\nfunction getUrl(version, name) {\n    return `https://github.com/rustwasm/wasm-bindgen/releases/download/${version}/${name}.tar.gz`;\n}\n\n\nfunction getPath(dir) {\n    if (process.platform === \"win32\") {\n        return $path.join(dir, \"wasm-bindgen.exe\");\n    } else {\n        return $path.join(dir, \"wasm-bindgen\");\n    }\n}\n\n\nasync function fetchBin(dir, version, name, path) {\n    await mkdir(dir);\n\n    if (!(await exists(path))) {\n        info(`Downloading wasm-bindgen version ${version}`);\n\n        const response = await $fetch(getUrl(version, name));\n\n        if (!response.ok) {\n            throw new Error(`Could not download wasm-bindgen: ${response.statusText}`);\n        }\n\n        await tar(response.body, {\n            cwd: dir,\n        });\n    }\n}\n\n\nexport async function download(dir, verbose) {\n    const version = await getVersion(dir, \"wasm-bindgen\");\n    const name = getName(version);\n\n    const cache = getCacheDir(\"rollup-plugin-rust\");\n\n    const path = getPath($path.join(cache, name));\n\n    if (verbose) {\n        debug(`Searching for wasm-bindgen at ${path}`);\n    }\n\n    let promise = WASM_BINDGEN_CACHE[path];\n\n    if (promise == null) {\n        promise = WASM_BINDGEN_CACHE[path] = fetchBin(cache, version, name, path);\n    }\n\n    await promise;\n\n    return path;\n}\n\n\nexport async function run({ bin, dir, wasmPath, outDir, typescript, extraArgs, verbose }) {\n    // TODO what about --debug --no-demangle --keep-debug ?\n    let args = [\n        \"--out-dir\", outDir,\n        \"--out-name\", \"index\",\n        \"--target\", \"web\",\n        \"--omit-default-module-path\",\n    ];\n\n    if (!typescript) {\n        args.push(\"--no-typescript\");\n    }\n\n    args.push(wasmPath);\n\n    args = args.concat(extraArgs);\n\n    if (verbose) {\n        debug(`Running wasm-bindgen ${args.join(\" \")}`);\n    }\n\n    await spawn(bin, args, { cwd: dir, stdio: \"inherit\" });\n}\n"
  },
  {
    "path": "src/wasm-opt.js",
    "content": "import * as $path from \"node:path\";\nimport { getEnv, debug, spawn, mv } from \"./utils.js\";\n\n\n// Replace with @webassemblyjs/wasm-opt ?\nexport async function run({ dir, input, output, extraArgs, verbose }) {\n    const isWindows = (process.platform === \"win32\");\n\n    // Needed to make wasm-opt work on Windows\n    const bin = getEnv(\"WASM_OPT_BIN\", (isWindows ? \"wasm-opt.cmd\" : \"wasm-opt\"));\n\n    const args = [input, \"--output\", output].concat(extraArgs);\n\n    if (verbose) {\n        debug(`Running ${bin} ${args.join(\" \")}`);\n    }\n\n    try {\n        await spawn(bin, args, { cwd: dir, shell: isWindows, stdio: \"inherit\" });\n\n    } catch (e) {\n        return e;\n    }\n\n    await mv($path.join(dir, output), $path.join(dir, input));\n\n    return null;\n}\n"
  },
  {
    "path": "tests/.gitignore",
    "content": "node_modules\nyarn-error.log\nCargo.lock\nyarn.lock\n/target\n/dist/js\n"
  },
  {
    "path": "tests/Cargo.toml",
    "content": "[workspace]\nmembers = [\n    \"src/bar\",\n    \"src/foo\",\n]\nresolver = \"2\"\n\n[workspace.package]\nedition = \"2018\"\n"
  },
  {
    "path": "tests/dist/index.html",
    "content": "<!DOCTYPE html>\n<html>\n  <head>\n    <meta charset=\"utf-8\" />\n  </head>\n  <body>\n    <script type=\"module\" src=\"js/foo.js\"></script>\n    <script type=\"module\" src=\"js/bar.js\"></script>\n    <script type=\"module\" src=\"js/qux.js\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "tests/rollup.config.js",
    "content": "import rust from \"../src/index.js\";\nimport { nodeResolve } from \"@rollup/plugin-node-resolve\";\nimport commonjs from \"@rollup/plugin-commonjs\";\n\nexport default {\n    input: {\n        foo: \"./src/foo.js\",\n        bar: \"./src/bar.js\",\n        qux: \"./src/foo/Cargo.toml\",\n    },\n    output: {\n        dir: \"dist/js\",\n        format: \"es\",\n        sourcemap: true,\n    },\n    plugins: [\n        nodeResolve(),\n\n        commonjs(),\n\n        rust({\n            extraArgs: {\n                wasmBindgen: [\"--debug\", \"--keep-debug\"],\n            },\n            verbose: true,\n        }),\n    ],\n};\n"
  },
  {
    "path": "tests/src/bar/Cargo.toml",
    "content": "[package]\nname = \"bar\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition.workspace = true\ncategories = [\"wasm\"]\n\n[dependencies]\nwasm-bindgen = \"0.2.58\"\nconsole_error_panic_hook = \"0.1.6\"\n\n[dependencies.web-sys]\nversion = \"0.3.35\"\nfeatures = [\n    \"console\",\n]\n"
  },
  {
    "path": "tests/src/bar/src/lib.rs",
    "content": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(start)]\npub fn main_js() {\n    console_error_panic_hook::set_once();\n\n    console::log_1(&JsValue::from(\"Bar!\"));\n}\n"
  },
  {
    "path": "tests/src/bar.js",
    "content": "import \"./bar/Cargo.toml\";\n"
  },
  {
    "path": "tests/src/foo/Cargo.toml",
    "content": "[package]\nname = \"foo\"\nversion = \"0.1.0\"\nauthors = [\"You <you@example.com>\"]\nedition.workspace = true\ncategories = [\"wasm\"]\n\n[dependencies]\nwasm-bindgen = \"0.2.58\"\nconsole_error_panic_hook = \"0.1.6\"\n\n[dependencies.web-sys]\nversion = \"0.3.35\"\nfeatures = [\n    \"console\",\n]\n"
  },
  {
    "path": "tests/src/foo/package.json",
    "content": "{\n  \"private\": true,\n  \"name\": \"foo\",\n  \"author\": \"You <you@example.com>\",\n  \"version\": \"0.1.0\",\n  \"devDependencies\": {\n    \"nop\": \"^1.0.0\"\n  }\n}\n"
  },
  {
    "path": "tests/src/foo/src/lib.rs",
    "content": "use wasm_bindgen::prelude::*;\nuse web_sys::console;\n\n\n#[wasm_bindgen(inline_js = \"export function foo() { return 5; }\")]\nextern \"C\" {\n    fn foo() -> u32;\n}\n\n\n#[wasm_bindgen(module = \"nop\")]\nextern \"C\" {\n    #[wasm_bindgen(js_name = default)]\n    fn nop();\n}\n\n\n#[wasm_bindgen(start)]\npub fn main_js() {\n    console_error_panic_hook::set_once();\n\n    console::log_1(&JsValue::from(format!(\"Foo! {:?} {}\", nop(), foo())));\n}\n"
  },
  {
    "path": "tests/src/foo.js",
    "content": "import * as foo1 from \"./foo/Cargo.toml?custom\";\nimport * as foo2 from \"./foo/Cargo.toml?custom\";\n\nawait foo1.init({ module: foo1.module });\nawait foo2.init({ module: foo2.module });\n"
  }
]