WXT + React 19 browser extension (Bun) that highlights words from the
user's list on any page. The hot path — scanning page text for those words
— is handled solely by a Rust→WASM Aho-Corasick backend
(crates/wasm-matcher). There is no JS fallback: a browser without WASM is
unsupported by design. The loader (src/wasm/matcherLoader.ts) throws on
init failure rather than degrading.
- Module organization: no
mod.rs; use folder-named files (e.g.src/matcher.rs, notsrc/matcher/mod.rs). - Error propagation: use
?; no bareunwrap();expect("reason")when needed; document any forcedunwrap()in///. unsafedisabled by default; document memory-safety guarantees inline if used.- Prefer traits, pattern matching, iterators; avoid unnecessary
Clone. - Format with
cargo fmt --all; passcargo clippy -p wasm-matcher --target wasm32-unknown-unknown -- -D warnings. - Every
.rsfile://!top comment for design intent. Public items:///English doc covering intent and parameters. - The WASM boundary stays thin: only
set_words/find_matches/find_deleted_matchescross it. Keep DOM work in JS.
- The wasm crate requires
--release(browser payload size + runtime perf). Debug builds remain fine forcargo check/tests. - Browser artifacts:
scripts/build-wasm.sh(cargo release wasm32 + wasm-bindgen--target web) →scripts/inline-wasm.tsembeds the.wasmas base64 so the content script is self-contained (no fetch / noweb_accessible_resources). Generated files undersrc/wasm/are gitignored and regenerated bybun run wasm.
wasm32-unknown-unknowntarget +wasm-bindgen-cliare required. Thewasm-bindgencrate is pinned to=0.2.122to match the CLI exactly; the build script verifies this.- On the NixOS host the toolchain is declarative (no rustup); rustup machines
get the target via
rust-toolchain.toml. - Frontend uses bun (not pnpm/npm).
- Atomic commits: one logical, runnable change per Conventional Commit.
- When behavior/workflow changes, update the relevant
README.mdin the same commit. Every code directory has aREADME.md.
- Rust:
bun run check:rust,bun run lint:rust,cargo test -p wasm-matcher. - Web:
bun run dev|build|compile. - wasm:
bun run wasm. - e2e:
bun run test:e2e(Playwright, loads the built extension).
- Headless Chrome (≥ ~128, host has 148) refuses
--load-extensionof an unpacked extension, sotests/e2e/highlight.spec.tsself-skips on a display-less sandbox/CI host. Run it for real with a display:- local desktop: headed system Chrome loads the extension (manual
equivalent:
bun run dev); - CI / headless host:
xvfb-run -a bun run test:e2e.
- local desktop: headed system Chrome loads the extension (manual
equivalent:
tests/e2e/matcher.bench.spec.ts(WASM correctness + perf) does not load the extension and runs everywhere.
- Communicate with the user in Chinese.
- Surface ambiguity before risky or irreversible changes.