Skip to content

Add a jsdom selector-engine adapter - #211

Merged
jdalton merged 1 commit into
dperini:masterfrom
jdalton:feat/jsdom-adapter
Sep 5, 2026
Merged

Add a jsdom selector-engine adapter#211
jdalton merged 1 commit into
dperini:masterfrom
jdalton:feat/jsdom-adapter

Conversation

@jdalton

@jdalton jdalton commented Sep 5, 2026

Copy link
Copy Markdown
Collaborator

Use nwsapi inside jsdom 30

Add a DOMSelector export alongside the existing CommonJS factory. It lets npm replace jsdom 30's @asamuzakjp/dom-selector dependency with nwsapi without patching jsdom or its DOM prototypes.

The adapter converts jsdom's internal nodes to public DOM nodes and routes queries, matches, and closest calls through nwsapi. It also supplies the CSS syntax trees that jsdom needs for stylesheet specificity. css-tree is an optional peer dependency, already supplied by jsdom 30; ordinary nwsapi callers do not load it.

The adapter loads css-tree only on the first stylesheet check(). DOM-only calls do not request it. A 256-entry cache reuses parsed selectors and generated branch text, never match results. DOM mutations stay visible, and clear(true) drops the syntax cache. The adapter checks all stylesheet rules without a subject prefilter. Existing selector bugs remain nwsapi's responsibility; this does not replace #178 or the other fixes.

Tests

On Node 26.5.0, all 11 integration tests pass both locally and through a fresh npm install of the packed adapter. Coverage includes DOM wrappers, static NodeLists, ordering, scoped and detached queries, fragments, XML, mutations, errors, stylesheet specificity, lazy loading, missing peers, and bounded syntax caching.

A local microbenchmark of 10,000 warm stylesheet checks fell from 28.54 ms to 3.00 ms (median of seven runs). This measures repeated syntax reuse, not end-to-end jsdom performance. jsdom can also load css-tree independently.

Run node --test test/jsdom-adapter.test.cjs for local checks. Run node test/jsdom-adapter-package.cjs for the real npm override check. The latter packs this checkout, installs jsdom 30.0.1 in a temporary consumer, overrides its selector dependency with that tarball, and runs the same suite without a module-cache substitution.

The original reentry path also remains observable through this installed adapter. Across 300 real Element.matches() calls, master reaches the depth-21 safety limit. With #178's factory, the same test creates one engine, makes 301 engine calls, reaches depth two, and never hits the limit. We can therefore move reentry coverage to jsdom 30 and remove the jsdom 26 alias from #210.

Scope

This is one commit targeting master. The engine change is only the lazy CommonJS export. The adapter, tests, package inclusion, optional peer declaration, and usage documentation are separate from selector fixes. The README shows an override to the published npm package, using the version of a future release containing the adapter. The existing npm release does not yet contain it. The package test uses a temporary tarball only to verify the adapter before publication.

@jdalton
jdalton force-pushed the feat/jsdom-adapter branch 11 times, most recently from a285d19 to 5975102 Compare September 5, 2026 23:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant