mrkeyoor.com_
Sat 08 Aug 22:00 UTC
npmUtilsupdated 08 Aug 2026

css-select-base-adapter

css-select-base-adapter is a tiny CommonJS factory for people adapting a custom tree structure to the css-select selector engine. You supply six operations that identify elements, read names and attributes, walk parents and children, and collect text. It returns those operations plus default implementations of sibling lookup, attribute checks, subtree searches, and duplicate or descendant removal. It does not parse HTML, implement a DOM, or select anything by itself.

Verdict

Keep it if an existing adapter already depends on its exact behavior. For new code, the eight-year maintenance gap, absent types, and stale contract documentation make a small in-house typed adapter or an existing current adapter easier to trust.

API stability4/5The exported API is one factory, the required six-function input has not changed, and caller-supplied methods intentionally override defaults because Object.assign copies the implementation last. That surface is predictable, but stability here mostly comes from inactivity: there has been no release since 0.1.1 in October 2018, and the README still lists search methods that current css-select types no longer require.
Docs2/5The README clearly lists the six mandatory methods, names every generated method, explains override behavior, and includes a minimal factory example. It does not document argument and return shapes, mutation by removeSubsets, root behavior in getSiblings, the non-tag traversal limitation in existsOne, module interop, TypeScript usage, or which css-select majors were actually tested. The linked css-select README cannot fill in package-specific behavior.
Maintenance1/5GitHub reports the last repository push on 2018-10-24, npm shows version 0.1.1 published the same day, and the latest commits are packaging and license housekeeping rather than compatibility work. The repository is not archived and has no open issues or pull requests, but there is also no evidence of testing against the many css-select major releases since 2018 or of any ongoing release process.
Ecosystem2/5The package recorded 4,975,507 downloads in the measured week and sits near the widely used css-select stack, yet the repository has only 4 stars and the package declares no css-select peer dependency that would communicate compatibility. It supplies one narrow adapter-building function, has no declarations or extension packages, and much of its traffic is likely transitive rather than evidence that teams deliberately choose its API.

Use it if

  • You have a non-DOM tree model and need to supply the repetitive traversal helpers expected by a css-select adapter
  • Your nodes have stable object identity, parent links, child arrays, tag names, attributes, and recursive text access
  • You are maintaining older CommonJS code that already uses the package and want to preserve its adapter behavior
  • You want a dependency-free implementation small enough to audit completely before adopting it
Skip it if

Setup reality

Installation is just npm install css-select-base-adapter, with no dependencies, peer dependencies, native build, credentials, or configuration files. That simplicity hides the actual integration work. The package does not include css-select, a parser, DOM nodes, or TypeScript declarations, so you install and configure those separately. Your implementation must provide exactly six functions before the factory will return: isTag, getAttributeValue, getChildren, getName, getParent, and getText. Miss one and construction throws an Error listing the absent functions. getText must recursively produce text for a whole node, not merely read an element's direct text field. getParent must use null or another falsy value at the root, and child and parent links must remain consistent for sibling and ancestor operations. The included defaults assume object identity, ordinary arrays, synchronous traversal, and an acyclic tree. removeSubsets mutates the array you pass. getSiblings includes the node itself and returns the parent's complete child list, matching css-select's contract but not jQuery's meaning of siblings. The package is CommonJS and uses Object.assign; it has no engines field and no transpiled fallback for very old JavaScript runtimes. With current css-select 7, import interop and the adapter type are your responsibility, and the upstream README is not a compatibility promise because this package's tests do not install or exercise css-select itself.

Patterns

Complete a minimal custom adaptercreate-adapter

const makeAdapter = require('css-select-base-adapter');

const adapter = makeAdapter({
  isTag: (node) => node.type === 'element',
  getAttributeValue: (node, name) => node.attributes[name],
  getChildren: (node) => node.children || [],
  getName: (node) => node.name,
  getParent: (node) => node.parent || null,
  getText: (node) => node.type === 'text'
    ? node.value
    : (node.children || []).map(getText).join(''),
});

function getText(node) {
  return adapter.getText(node);
}

All six named functions are mandatory. The factory validates them immediately and reports every missing function in one error.

Pass the adapter to current css-selectuse-with-css-select

import makeAdapter from 'css-select-base-adapter';
import { selectAll } from 'css-select';

const adapter = makeAdapter(treeImplementation);
const matches = selectAll('article.featured > h2', roots, {
  adapter,
  xmlMode: false,
});

css-select 7 is ESM while this package is CommonJS. Node can provide the default-import interop shown here, but TypeScript needs your own adapter typing because this package has no declarations.

Replace a generated helperoverride-default

const adapter = makeAdapter({
  ...treeImplementation,
  hasAttrib(node, name) {
    return Object.prototype.hasOwnProperty.call(node.attributes, name);
  },
});

Provided methods win over defaults. Override hasAttrib when a present attribute can legitimately map to undefined in your data model.

Find the first matching nodefind-first

const firstMain = adapter.findOne(
  (node) => adapter.isTag(node) && adapter.getAttributeValue(node, 'role') === 'main',
  roots
);

findOne runs the predicate on every array entry, including non-tag nodes, so include an isTag guard if element-only fields are not safe on text nodes.

Collect every matching elementfind-all

const headings = adapter.findAll(
  (node) => /^h[1-6]$/.test(adapter.getName(node)),
  roots
);

findAll only tests nodes accepted by isTag, then recursively walks their children in depth-first document order.

Check whether any matching element existscheck-descendant

const hasLink = adapter.existsOne(
  (node) => adapter.getName(node) === 'a',
  elementRoots
);

Every starting node must satisfy isTag for traversal to continue. Passing a document wrapper that is not a tag can incorrectly produce false without visiting its children.

Read an element's sibling groupread-siblings

const siblingGroup = adapter.getSiblings(node) || [node];
const position = siblingGroup.indexOf(node);

The returned list includes node itself. The default returns a falsy value when node has no parent, despite current css-select types describing an array return.

Test attribute presencecheck-attribute

if (adapter.hasAttrib(node, 'disabled')) {
  console.log('attribute exists even when its value is empty');
}

The default considers any value other than undefined present. Empty strings and null therefore count as present.

Remove duplicate and nested selectionsremove-subsets

const candidates = [section, paragraph, section];
const topLevelOnly = adapter.removeSubsets(candidates);

removeSubsets mutates candidates in place, removes duplicates, and drops a node when one of its ancestors is also present. Copy first if the original array matters.

Remove subsets without changing caller datapreserve-input-array

const topLevelOnly = adapter.removeSubsets([...candidates]);
console.log(candidates.length); // unchanged

A shallow array copy is enough to protect the list, but the nodes themselves are still shared and compared with strict object identity.

Override traversal for a document wrappersupport-root-node

const adapter = makeAdapter({
  ...treeImplementation,
  existsOne(test, nodes) {
    return nodes.some((node) =>
      (this.isTag(node) && test(node)) ||
      this.existsOne(test, this.getChildren(node) || [])
    );
  },
});

The stock existsOne does not descend through non-tag document nodes. This override handles wrappers, but avoid this-dependent methods if your callers detach functions from the adapter.

Fail clearly when a primitive is missingvalidate-implementation

try {
  makeAdapter({
    isTag() {},
    getChildren() {},
  });
} catch (error) {
  console.error(error.message);
  // Expected functions (...) to be implemented
}

Validation checks only that each required property is a function. It cannot verify return types, parent-child consistency, or cycle safety.

Alternatives

PackageRegistryPick it when
css-selectnpmUse its built-in domutils adapter directly when your nodes come from htmlparser2 rather than maintaining a custom adapter
css-select-browser-adapternpmUse a ready-made adapter when the backing nodes are browser DOM elements
domutilsnpmUse the adapter css-select expects by default when you already parse with domhandler or htmlparser2
cheerionpmUse a higher-level HTML parsing and querying API when you do not actually have a custom tree model