A standalone HTML/XML DOM library with a Cheerio-shaped API.
GroveDOM parses, queries, modifies and serializes documents using a C/Lexbor kernel compiled to WebAssembly. Use familiar selectors and chainable operations from JavaScript, with no runtime dependencies or native installation step.
Getting started · Browser usage · Compatibility · Architecture
Experimental: 0.1.1 is published on npm.
This release is a compatibility bug-fix patch, including
documented result and extraction type-inference corrections.
GroveDOM implements a substantial subset of Cheerio, not a complete drop-in
replacement. Check the compatibility guide before migrating.
Requires Node.js 22 or later. Node 22 and 24 are tested in CI.
The package provides ES modules; save the example as example.mjs and run
node example.mjs, or use a project with "type": "module".
npm install grovedomimport { load } from 'grovedom';
const $ = load(`
<main>
<h1>Hello</h1>
<a href="/guide">Read the guide</a>
<p class="advert">Advertisement</p>
</main>
`);
try {
$('h1').text('Hello from GroveDOM');
$('a').attr('target', '_blank').addClass('external');
$('.advert').remove();
console.log($('a').attr('href')); // /guide
console.log($.html());
} finally {
$.dispose();
}load() parses an HTML document by default, adding implied html, head and
body elements. Use load(markup, {}, false) for fragments or
load(xml, { xml: true }) for XML. DOM calls are synchronous.
Always call $.dispose() when finished. It releases the document's resources
and invalidates its selections and node handles. Handles belong to their
document; garbage collection is only a cleanup fallback.
- CSS selection, traversal and chainable collections.
- Attribute, text, HTML, class, property and data operations.
- Insertion, removal, cloning, wrapping and callback-based updates.
- HTML templates, XML, form serialization and extraction helpers.
- Independent documents in caller-managed Node worker threads.
- Bundled TypeScript declarations that do not require Cheerio or its types.
The API is partial. Unsupported options are rejected; GroveDOM does not silently fall back to Cheerio. Parser recovery and some selector/DOM edge cases differ. The compatibility guide describes supported, best-effort and unsupported behavior; tests track selected Cheerio, jQuery and Web Platform Test expectations and known exclusions.
The Node entry initializes its bundled Wasm automatically on the first DOM call.
For custom settings, call init() before load, contains or merge:
import { init } from 'grovedom';
init({
heap: 'pool',
poolSize: 8,
poolMaxBytes: 16 * 1024 * 1024,
// wasm: new URL('./assets/grovedom.wasm', import.meta.url),
});These limits apply to idle instances and their combined linear memory, not
active documents or total process memory. wasm can be a local path, file URL,
byte array, ArrayBuffer or compiled WebAssembly.Module. A custom binary must
match its JavaScript build. Settings become fixed after successful initialization;
repeated calls throw. Runtime configuration uses this API, with no environment
variables. See initialization details.
Browsers use the same Wasm binary as Node, with asynchronous initialization:
import { init, load } from 'grovedom/browser';
await init({ wasm: '/assets/grovedom.wasm' });
const $ = load('<p>Hello, browser</p>');
try {
console.log($('p').text());
} finally {
$.dispose();
}With a bundler, copy node_modules/grovedom/grovedom.wasm to a served asset
directory and pass its deployed URL to init; adjust the example URL for
deployments under a subpath. For unbundled ESM, serve the package files over HTTP and
import its src/browser.js; await init() finds the adjacent packaged binary.
Await the first initialization; concurrent or repeated browser calls reject.
DOM calls remain synchronous and occupy the calling thread. GroveDOM parses its own
documents; it does not manipulate the page's live DOM or execute scripts.
Browser support is best-effort. Reported Firefox 150 and Chromium 140 correctness-page runs each pass 810 cases with 35 documented skips and no failures. WebKit and broader browser API coverage remain unqualified. No SIMD, shared memory, threads or cross-origin isolation is required.
The browser benchmark shares the local and CI authored workload, using balanced paired blocks against CDN-loaded Cheerio 1.2.0 with default and htmlparser2 parsers and an identical-Wasm control, verifying output equality on every replay. The hosted demo serves 0.1.1 through the GitHub Pages workflow.
| Package / entry | Status | Scope |
|---|---|---|
| grovedom | Primary | Node 22/24 on Linux; Node 24 on macOS and Windows |
grovedom/browser |
Best-effort | Async ESM entry, same Wasm binary |
| grovedom-native | Secondary | Node-API; Linux x64 with glibc 2.35+ |
Install grovedom-native separately to use the native backend, then import
load from it instead. Both packages have independent state and handle identity.
Neither has runtime or peer dependencies; both include standalone TypeScript
declarations. GroveDOM creates no threads and fetches no document resources.
The native package has no macOS, Windows, ARM64 or musl/Alpine binary and does
not automatically fall back to Wasm. Each linked package README includes its
installation, initialization and lifetime instructions.
Scoped Node and browser measurements show gains on selected workloads, including bindings and disposal. Reported Firefox and Chromium ratios differ substantially. Current full-scope non-regression and production adoption remain unverified. Benchmarks, controls and limits preserve the historical results and unsuccessful comparisons. Measure your complete workload against the best compatible Cheerio configuration before choosing a backend.
CI covers multiple Node versions and operating systems, selected upstream correctness cases, worker/document lifetimes, native sanitizers, seeded fuzzing and isolated package installations. Production adoption and broader browser validation remain open work.
The main branch targets 0.1.2 (unreleased).
See build and package setup for the pinned Lexbor/WASI inputs and required Node, Clang/LLVM, CMake and Ninja tools. After configuring those inputs and output directories:
npm ci
npm run build:wasm
npm run build:native
npm test
npm run test:typesArchitecture explains the ESM facade and shared C kernel. Testing describes targeted checks, and releasing covers verified tarballs, npm publication and Pages. Pushes and pull requests run CI; releases require manual dispatch.
MIT, the same license as Cheerio. Bundled libraries retain their third-party licenses and notices.