# How much of WPT needs no script, and which JavaScript engine to link Answers two open questions behind `roadmap.md` "Later phases": how far the engine can get through WPT before any JavaScript engine is linked, and which engine to link when one is. Measured on 2026-10-19 at WPT commit `26f417a8c09fbfcc0b4fcf55760f31090ef2b772` (the `wpt.commit` pinned in `oracle/suites/suites.json`). Every number below is in `docs/js-engine-choice.json`; `crates/harness/tests/js_engine_choice.rs` checks that the engine rows match it and that the WPT counts add up. ## Recommendation **Link nothing yet.** 40.1% of WPT test files outside test262 run with no script, and 66.6% of `css` does: 25350 test files, most of them reftests. The layout work has that many tests to pass before a script engine is the limit. `html` (6.4%) and `dom` (0.0%) are where script becomes necessary. **When an engine is linked, link Boa first.** It is the only candidate written in Rust: it builds with no C or C++ compiler and downloads no prebuilt library, so a build on any lane is the same build from crates.io sources; it builds for both WebAssembly targets the engine cares about (`wasm32-unknown-unknown` needs `boa_engine`'s `js` feature); and at 95.6% of test262 it trails V8 and SpiderMonkey by two to three points, about half of them in `intl402`, while QuickJS-NG passes 83.5%. Its cost is size: 12.2 MiB over an empty Rust binary, against QuickJS-NG's 1.2 MiB. What would change it: - **The WebAssembly build has to stay free of JavaScript glue.** `crates/webengine` builds a plain `wasm32-unknown-unknown` module with no `wasm-bindgen`. Boa's `js` feature reaches the platform's random source through `wasm-bindgen` imports. If supplying `getrandom` another way (its custom backend) does not work, QuickJS-NG builds for that target with a wasm-capable clang and no glue, and becomes the first choice. - **Script speed starts to dominate WPT run time.** Boa is a bytecode interpreter with no JIT; QuickJS-NG is a small, quick-starting interpreter. Execution speed was not measured here. A WPT run that is slow because of script points to QuickJS-NG, and a real-site workload to V8 or SpiderMonkey. - **Binary size gets a budget.** At 12.2 MiB, Boa is ten times QuickJS-NG; a budget under that points to QuickJS-NG. - **Conformance at the top end matters more than a Rust-only build.** SpiderMonkey (98.5%) and V8 (97.7%) lead, but both are C++ linked through a prebuilt static library downloaded at build time (or a from-source build of a browser engine), neither builds for either WebAssembly target, and they add 33.2 MiB and 45.6 MiB. - **QuickJS-NG gains Temporal.** Its gap to Boa is mostly the Temporal tests (0 of 6715 pass) and `intl402`; without `intl402` it passes 89.1%. ## Method ### WPT without script `oracle/suites/wpt-script-free.mjs` reads a blobless clone of WPT at the pinned commit in a temporary directory (`git fetch --depth 1 --filter=blob:none`, then `git checkout`), builds WPT's own manifest there (`./wpt manifest`), and classifies every test file the manifest lists. It refuses a clone at any other commit and never writes to `oracle/suites/checkouts/` or `oracle/suites/suites.json`. ```sh node oracle/suites/wpt-script-free.mjs /tmp/wpt-clone > counts.json ``` The rule, the same text as at the top of the script: - A test file is a path the manifest lists under a test type. Support files are not tests. A file with several variants (`?query`, `.any.js` globals) counts once. - testharness, test262, wdspec and aamtest files always need script: their verdict comes from script in the page or from a driver that runs script in it. - reftest, print-reftest, crashtest, visual and manual files need script when the test file, or any reference it names (followed through references that name their own references), contains a `