NWSAPI finds DOM elements that match CSS selectors. It works in browsers and with DOM libraries in Node.js. The core engine has no external dependencies.
NWSAPI builds on NWMATCHER and aims to support Selectors Level 4. See the supported selectors and features.
pnpm add nwsapiUse nwsapi ≥ 2.3.0 with jsdom ≥ 27. The adapter replaces jsdom's selector engine for queries and stylesheet matching.
Set up the dependency and override
Add the adapter's css-tree peer dependency to package.json:
{
"dependencies": {
"css-tree": "^3.2.1"
}
}Replace <version> with the published nwsapi version you want to use.
-
npm (
package.json):{ "overrides": { "@asamuzakjp/dom-selector": "npm:nwsapi@<version>" } } -
pnpm (
pnpm-workspace.yaml):overrides: '@asamuzakjp/dom-selector': 'npm:nwsapi@<version>'
Install dependencies after the change. The override does not change the NWSAPI factory API or add selector support.
Use the factory in Node.js
Node.js does not provide a DOM. This example creates one with jsdom.
pnpm add nwsapi jsdomconst { JSDOM } = require('jsdom')
const createNwsapi = require('nwsapi')
const { window } = new JSDOM('<p class="item">Hello</p>')
const nw = createNwsapi(window)
const items = nw.select('.item', window.document)
window.close()This example calls NWSAPI directly. It does not replace jsdom's selector engine.
Copy src/nwsapi.js from the package into your project. Set the script path to that file.
<script src="nwsapi.js"></script>
<script>
const items = NW.Dom.select('.item', document)
const firstItem = NW.Dom.first('.item', document)
</script>Replace native selector methods
install() changes selector methods such as querySelectorAll() and matches() for the page.
Use it only when you want those methods to call NWSAPI.
NW.Dom.install()
// Restore the original methods when they are no longer needed.
NW.Dom.uninstall()Use NW.Dom in a browser or the engine returned by the Node.js factory.
Pass a CSS selector as selector and a DOM node as context.
| Method | Result |
|---|---|
ancestor(selector, element) |
Returns the nearest match, starting with the element, or null. |
first(selector, context) |
Returns the first matching descendant, or null. |
match(selector, element) |
Returns true if the element matches, or false. |
select(selector, context) |
Returns an array of matching descendants, or [] if none match. |
These methods accept an optional third argument, callback, which runs for matching elements.
Find elements by ID, tag, or class
Pass the search context as the second argument. These helpers return arrays by default.
NW.Dom.byClass('item', document)
NW.Dom.byId('content', document)
NW.Dom.byTag('p', document)byId() can return multiple elements when the document contains duplicate IDs.
Configure the engine
NW.Dom.configure({ LOGERRORS: false, IDS_DUPES: false })
const options = NW.Dom.configure()| Option | Default | Effect |
|---|---|---|
FORGIVING |
true |
Allows invalid items in forgiving selector lists such as :is() and :where(). |
IDS_DUPES |
true |
Allows duplicate IDs when finding elements. |
LEGACY |
false |
Enables feature checks and fallbacks for older environments. |
LOGERRORS |
true |
Logs errors when exception throwing is disabled. |
NODE_LIST |
false |
Uses NodeList-style results instead of arrays where supported. |
VERBOSITY |
true |
Throws exceptions for invalid selectors. |
[!IMPORTANT] Set
LEGACYbefore the first query when the environment needs compatibility fallbacks.
Add selector extensions
Extensions use JavaScript source strings to define matching behavior. Register only trusted code.
registerCombinator(symbol, resolver) adds a relationship between elements:
NW.Dom.registerCombinator('^', 'e.parentElement')registerOperator(symbol, resolver) adds an attribute operator:
NW.Dom.registerOperator('!=', { p1: '^', p2: '$', p3: 'false' })registerSelector(name, pattern, compile) adds a selector. The compile function returns matching code and a success flag.
NW.Dom.registerSelector('Controls', /^:(control)(.*)/i, (match, source) => ({
source: 'if(/^(button|input|select|textarea)$/i.test(e.nodeName)){' + source + '}',
status: true,
}))The engine compiles selectors into JavaScript functions and caches those functions for later queries.
See the contributor requirements in package.json.
pnpm install
pnpm testThe install sets up WPT and Chromium for browser tests. It needs Git and network access. Node tests do not use the browser or WPT checkout.
Check changes before a push
pnpm run check
pnpm run test:packageRun pnpm run fix to apply lint fixes, format files, and check the result.
Run pnpm run test:watch to repeat Node tests while you edit files.
Run pnpm run ci:local to test the GitHub Actions workflow locally.
It needs Docker and GitHub CLI authentication. It pauses when a step fails.
CI uses one Node.js 26 job.
Run browser tests and measure coverage
See upstream testing for system requirements and setup recovery. WPT means Web Platform Tests.
pnpm run test:browser
pnpm run test:upstream
pnpm run test:coverageCoverage uses WPT in Chromium for the engine and Node tests for the adapter.
The coverage command checks the minimums in .config/coverage.config.mts and updates the badge.
CI also creates HTML reports. Known WPT failures remain visible in test results.
Build the package and update dependencies
Rolldown builds JavaScript from the .mts source files and creates the minified browser file.
Run pnpm run build to build the files. Run pnpm run clean to remove generated JavaScript.
pnpm pack and pnpm publish build the package first.
Published files keep their existing paths, CommonJS API, browser and AMD support, and extension modules.
The package does not include TypeScript source files or development tools.
Pin development dependencies in the pnpm-workspace.yaml catalog. Update pnpm-lock.yaml when dependencies change.
Run pnpm run update --check to preview dependency updates.
Run pnpm run update to apply updates and refresh the lockfile.
Compiler tool versions need a separate compatibility review.
New dependency versions have a one-day release delay. Dependency scripts need explicit approval.
Use pnpm to install this repository; npm cannot install its catalog references.
CI reads Node.js and package manager versions from .config/external-tools.json.
Sponsorship helps fund maintenance, testing, and selector support.
Sponsorship and donation options
Use GitHub Sponsors, Open Collective, or Patreon for ongoing support.
You can also use Ko-fi, Buy Me a Coffee, or Liberapay. Use IssueHunt to fund issues.
Corporate sponsors can ask about custom licensing, dedicated support, or priority fixes.