From cdaaa318b4e7b93cb3132a7a00476f0701f672a8 Mon Sep 17 00:00:00 2001 From: yeskiy Date: Mon, 29 Jun 2026 05:05:12 +0300 Subject: [PATCH] docs: document typed .rs imports Add a TypeScript types guide covering the ambient floor, the precise `.d.rs.ts` sidecars, and the three ways to generate them (the editor Language Service plugin, the build-time `types` option, and the `gen-types` CLI), plus the tsconfig requirements and the VS Code workspace-TypeScript step. Document the `types` option in the API reference and note typed imports in the overview and README. Refs: RL-67 --- README.md | 1 + docs/docs/api/index.md | 4 + docs/docs/examples/index.md | 4 + docs/docs/examples/typed-imports.md | 159 +++++++++++++++++++++++++ docs/docs/getting-started/overview.mdx | 2 +- docs/sidebars.js | 1 + 6 files changed, 170 insertions(+), 1 deletion(-) create mode 100644 docs/docs/examples/typed-imports.md diff --git a/README.md b/README.md index 10b36a9..cfef8b2 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ compilation and the glue code. - Works with `wasm_bindgen` exports and with plain functions. - Finds the nearest `Cargo.toml` by walking up from the `.rs` file, so you do not configure the crate path by hand. - Builds for `web` and `node` targets, and across the bundlers listed above. +- Types `.rs` imports in TypeScript: an ambient floor keeps imports valid, and generated `.d.rs.ts` sidecars (plus an editor Language Service plugin) give the exact `#[wasm_bindgen]` signatures. You write computation-heavy code in Rust, pull in crates from the Rust ecosystem, and call the result from JavaScript without managing a separate compile-and-link pipeline. diff --git a/docs/docs/api/index.md b/docs/docs/api/index.md index 2f5e24e..7d05ae9 100644 --- a/docs/docs/api/index.md +++ b/docs/docs/api/index.md @@ -32,6 +32,7 @@ interface LoaderOptions { | `web.wasmPathModifier` | `string[]` | `["/"]` | Modify WASM request path | | `node.bundle` | `boolean` | `false` | Bundle WASM in JS file | | `logLevel` | `string` | `"info"` | Logging verbosity | +| `types` | `boolean` | `false` | Write `.d.rs.ts` type sidecars on build | ## Target environments @@ -130,6 +131,9 @@ plugin(loader.bun({ |------------|-------------------------------------------------------|------------------|--------------------------------| | `target` | `"web" \| "node"` | webpack `target` | Override the build target | | `logLevel` | `"verbose" \| "info" \| "warn" \| "error" \| "quiet"` | `"info"` | Control logging verbosity | +| `types` | `boolean` | `false` | Also write the `.d.rs.ts` type sidecar during the build | + +`types` is available on every surface (the loader, the Vite/Rollup/esbuild/Bun plugins, and the Next.js helper). It is one of three ways to generate the type sidecars, alongside the `gen-types` CLI and the editor Language Service plugin. See [TypeScript types](../examples/typed-imports.md) for the full setup. ### Bun options diff --git a/docs/docs/examples/index.md b/docs/docs/examples/index.md index d078c45..2c55deb 100644 --- a/docs/docs/examples/index.md +++ b/docs/docs/examples/index.md @@ -22,3 +22,7 @@ Each example here is a complete project: the setup steps, the config files, and ## Desktop applications - [Electron](./electron.md) - main and renderer processes with Webpack (`electron-main` uses the `node` strategy, `electron-renderer` uses `web`), with the wasm bytes inlined into both. + +## TypeScript + +- [TypeScript types](./typed-imports.md) - type `.rs` imports with the ambient floor, the `gen-types` CLI, the build-time `types` option, and the editor Language Service plugin. diff --git a/docs/docs/examples/typed-imports.md b/docs/docs/examples/typed-imports.md new file mode 100644 index 0000000..377050c --- /dev/null +++ b/docs/docs/examples/typed-imports.md @@ -0,0 +1,159 @@ +--- +sidebar_position: 10 +--- + +# TypeScript types + +A `.rs` import can be a fully typed module in TypeScript, ESLint, and your editor. This page shows how to make `import lib from "./math.rs"` resolve, and how to get the exact signatures of your `#[wasm_bindgen]` exports. + +## How it works + +Types come in two layers: + +- **The floor** - the package ships an ambient `declare module "*.rs"`. Once you reference it, every `.rs` import is valid and loosely typed (a record of callables), so the import never errors even before anything is generated. +- **Precise sidecars** - for each `.rs`, a `.d.rs.ts` file carries the exact signatures, generated from wasm-bindgen's own types. TypeScript resolves it through `allowArbitraryExtensions` and it overrides the floor for that file. + +wasm-bindgen already knows the types; the loader normally discards them. The pieces below keep them and reshape them to match what a `.rs` import actually exposes at runtime. + +## Make `.rs` imports valid + +Reference the shipped floor from your `tsconfig.json`: + +```json title="tsconfig.json" +{ + "files": ["node_modules/rust-wasmpack-loader/types/rs.d.ts"], + "include": ["src"] +} +``` + +That alone clears the `Cannot find module './math.rs'` error. The import is typed as `Record any>` until precise types are generated. + +## Get precise types + +### 1. Turn on arbitrary-extension resolution + +```json title="tsconfig.json" +{ + "compilerOptions": { + "moduleResolution": "bundler", + "allowArbitraryExtensions": true + } +} +``` + +`allowArbitraryExtensions` (TypeScript 5.0+) is what lets `import "./math.rs"` resolve to a `math.d.rs.ts` sidecar. `moduleResolution` must be `bundler`, `node16`, or `nodenext`. + +### 2. Generate the sidecars + +The sidecars are generated from the Rust. Pick whichever path fits your workflow; they all produce the same `.d.rs.ts` and can be combined. + +#### In the editor - the Language Service plugin + +Add the plugin to `tsconfig.json` and your editor types `.rs` imports live, with nothing to run: + +```json title="tsconfig.json" +{ + "compilerOptions": { + "plugins": [{ "name": "rust-wasmpack-loader/tsserver" }] + } +} +``` + +The plugin loads in any tsserver-based editor (VS Code, JetBrains, Neovim) with no editor extension. In VS Code you must use the workspace TypeScript once: run **"TypeScript: Select TypeScript Version"** and choose **"Use Workspace Version"** (a `.vscode/settings.json` with `"typescript.tsdk": "node_modules/typescript/lib"` prompts for it). + +Because the types come from a wasm-pack build, a freshly opened `.rs` shows the loose floor for the second or two the first build takes, then the precise types appear. Editing a `.rs` refreshes on save. + +#### On build - the `types` option + +Set `types: true` on the loader (or any plugin) and a normal build also writes the sidecar next to each `.rs`, reusing the build it already runs: + +```javascript title="webpack.config.js" +{ + test: /\.rs$/, + use: { + loader: "rust-wasmpack-loader", + options: { types: true }, + }, +} +``` + +The same `types: true` option works on the Vite, Rollup, esbuild, and Bun plugins, and on the `withRustWasm` Next.js helper. It is off by default, so it never writes files unless you ask. + +#### In CI or on demand - the CLI + +The package ships a `gen-types` command that writes the sidecars for matched `.rs` files. Wire it into a typecheck or CI step: + +```json title="package.json" +{ + "scripts": { + "typecheck": "rust-wasmpack-loader gen-types && tsc --noEmit" + } +} +``` + +```bash +# generate for specific files, or pass globs; --watch regenerates on change +npx rust-wasmpack-loader gen-types src/math.rs --watch +``` + +### 3. Ignore the generated sidecars + +The sidecars are build output. Add them to `.gitignore`: + +```gitignore title=".gitignore" +*.d.rs.ts +``` + +`tsc`, ESLint, and the editor read whatever sidecar is present; the floor covers anything not generated yet, so a fresh clone or a CI run that has not generated never errors. + +## Example + +The [`example/typed-imports`](https://github.com/yeskiy/rustwasm-loader/tree/main/example/typed-imports) project wires all of this together. + +```rust title="math.rs" +use wasm_bindgen::prelude::*; + +#[wasm_bindgen] +pub fn fibonacci(n: i32) -> i32 { + match n { + 0 => 0, + 1 => 1, + _ => fibonacci(n - 1) + fibonacci(n - 2), + } +} + +#[wasm_bindgen] +pub fn cap(s: &str) -> String { + s[0..1].to_uppercase() + &s[1..] +} +``` + +```typescript title="src/index.ts" +import lib from "../math.rs"; + +// `lib.fibonacci` is typed `(n: number) => number`, `lib.cap` is +// `(s: string) => string`. A typo like `lib.fib()` is a compile error. +export const fib10 = lib.fibonacci(10); +export const capped = lib.cap("hello"); +``` + +```json title="tsconfig.json" +{ + "compilerOptions": { + "module": "esnext", + "moduleResolution": "bundler", + "allowArbitraryExtensions": true, + "strict": true, + "types": [], + "plugins": [{ "name": "rust-wasmpack-loader/tsserver" }] + }, + "files": ["node_modules/rust-wasmpack-loader/types/rs.d.ts"], + "include": ["src/index.ts"] +} +``` + +## Notes + +- **Functions are typed; classes are not yet.** A `#[wasm_bindgen]` function gets its exact signature. An exported `struct`/class stays on the loose floor for now, since the loader does not yet expose wasm-bindgen classes on the default export. +- **The plugin is editor-only.** `tsc` on the command line never loads a Language Service plugin, so it reads the on-disk sidecar instead. Generate it with `types: true` or the CLI for `tsc` and CI; the plugin keeps the editor live. +- **`typescript` is a dependency** of the loader (the generator and the plugin use the compiler API), so it is installed for you. diff --git a/docs/docs/getting-started/overview.mdx b/docs/docs/getting-started/overview.mdx index 51fd41c..cbdf2ab 100644 --- a/docs/docs/getting-started/overview.mdx +++ b/docs/docs/getting-started/overview.mdx @@ -30,7 +30,7 @@ graph LR - Import `.rs` files like any other module, with no separate build step to wire up - Hot reload during development -- TypeScript integration through generated type definitions +- Typed `.rs` imports: an ambient floor for valid imports, generated `.d.rs.ts` sidecars for the exact `#[wasm_bindgen]` signatures, and an editor Language Service plugin for live types - Webpack 5+ with explicit targets (web, node) - Rspack through the same Webpack-compatible loader - Bun runtime support diff --git a/docs/sidebars.js b/docs/sidebars.js index c977754..6428f1b 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -26,6 +26,7 @@ const sidebars = { "docs/examples/vite", "docs/examples/next", "docs/examples/electron", + "docs/examples/typed-imports", ], }, {