npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

selery

v0.2.0

Published

A CSS selector parser

Downloads

46

Readme

selery

Selery is a small, handwritten CSS selector parser and DOM query engine.

It aims to be compliant with the relevant specifications (CSS Syntax Module Level 3, CSS Selectors Level 4, and others), while remaining compact and understandable so that it can be used as a starting point to experiment with new CSS syntax.

An online playground is available at danburzo.ro/selery/.

Getting started

selery on npm selery on bundlephobia

You can install Selery as an npm package:

npm install selery

API reference

tokenize(selector)

Takes a string selector and returns an array of tokens.

let { tokenize } = require('selery');

tokenize('article a[href="#"]');

A token is a plain object having a type property, along with other optional properties, which are documented in the CSS token reference. For the sample selector 'article a[href="#"]' mentioned above, the resulting token array is:

[
	{ type: 'ident', value: 'article', start: 0, end: 6 },
	{ type: 'whitespace', start: 7, end: 7 },
	{ type: 'ident', value: 'a', start: 8, end: 8 },
	{ type: '[', start: 9, end: 9 },
	{ type: 'ident', value: 'href', start: 10, end: 13 },
	{ type: 'delim', value: '=', start: 14, end: 14 },
	{ type: 'string', value: '#', start: 15, end: 17 },
	{ type: ']', start: 18, end: 18 }
];

The function will throw an erorr if the selector supplied does not follow generally valid CSS syntax.

parse(input, options)

Accepts an input argument, which can be either an array of tokens obtained from the tokenize() function or, more conveniently, a string representing a selector. The latter is passed through tokenize() internally.

It produces an abstract syntax tree (AST), also called a parse tree, for the provided input.

let { parse } = require('selery');

let tree = parse('div > span:nth-child(3)');

Available options:

syntaxes (Object) — provide custom microsyntaxes to various pseudo-classes and pseudo-elements. By default, the argument of :nth-*() pseudo-classes is parsed with the An+B microsyntax, while for the :is(), :where(), :not(), and :has(), the argument is parsed as a SelectorList.

The keys to the syntaxes object are the identifier for the pseudo-class (prefixed by :) or pseudo-element (prefixed by ::), and the values are either strings (one of None, AnPlusB, or SelectorList) or functions. Function values will receive an array of tokens and can return anything suitable for storing in the AST node's argument key.

parse(':nth-child(3)', {
	syntaxes: {
		/* Change the microsyntax of a pseudo-class */
		':nth-child': 'None',

		/* A microsyntax defined as a function */
		':magic': tokens => tokens.map(t => t.value).join('★')
	}
});

serialize(input)

Converts the input back into a string. The input argument can be either an array of tokens, or an object representing a parse tree.

DOM API shims

Shims for selector-accepting DOM methods using simpler DOM primitives.

Across these methods:

  • the selector argument can be a string (as with their native DOM counterparts), an array of tokens, or an object representing a parse tree;
  • the options object accepts the following keys:
    • root (Element) — an optional scoping root;
    • scope (Element | Array) — an optional set of :scope elements.

matches(element, selector, options)

See the Element.matches DOM method.

closest(element, selector, options)

See the Element.closest DOM method.

querySelector(element, selector, options)

See the Element.querySelector DOM method.

querySelectorAll(element, selector, options)

See the Element.querySelectorAll DOM method. While the native DOM method return a NodeList, our implementation of querySelectorAll returns an Array.

CSS token reference

The tokenize() function returns an Array of tokens with a type property. The list of type values is below:

export const Tokens = {
	AtKeyword: 'at-keyword',
	BadString: 'bad-string',
	BadUrl: 'bad-url',
	BraceClose: '}',
	BraceOpen: '{',
	BracketClose: ']',
	BracketOpen: '[',
	CDC: 'cdc',
	CDO: 'cdo',
	Colon: 'colon',
	Comma: 'comma',
	Delim: 'delim',
	Dimension: 'dimension',
	Function: 'function',
	Hash: 'hash',
	Ident: 'ident',
	Number: 'number',
	ParenClose: ')',
	ParenOpen: '(',
	Percentage: 'percentage',
	Semicolon: 'semicolon',
	String: 'string',
	UnicodeRange: 'unicode',
	Url: 'url',
	Whitespace: 'whitespace'
};

The following token types include a value property: at-keyword, bad-string, bad-url, delim, dimension, function, hash, ident, number, percentage, string, unicode, url.

Some token types may include specific properties:

  • number and percentage include a sign property;
  • dimension includes sign and unit properties;

All tokens include the positional start and end properties that delimit the token’s locarion in the input string.

CSS selector AST reference

All nodes in the AST contain a type property, and additional properties for each specific type, listed below.

All nodes also include the positional start and end properties that delimit the selector’s location in the input string.

SelectorList

The topmost node in the AST.

  • selectors — an array of (possibly complex) selectors.

ComplexSelector

A complex selector represents a pair of selectors stringed together with combinators, such as article > p.

  • left — the left-side (possibly complex, or compound) selector; null when the selector is relative, such as the > img in a:has(> img);
  • right — the right-side (possibly complex, compound) selector;
  • combinator — one of , >, ~, +, ||

Longer sequences of selectors are represented with nested ComplexSelector elements in the AST. For example, article > p span is represented as:

{
	type: 'SelectorList',
	selectors: [{
		type: 'ComplexSelector',
		left: {
			type: 'ComplexSelector',
			left: {
				type: 'TypeSelector',
				identifier: 'article'
			},
			right: {
				type: 'TypeSelector',
				identifier: 'p'
			},
			combinator: ' ',
		},
		right: {
			type: 'TypeSelector',
			identifier: 'span'
		},
		combinator: ' '
	}]
}

CompoundSelector

A compound selector is a combination of simple selectors, all of which impose conditions on a single element, such as a.external[href$=".pdf"].

  • selectors — an array of simple selectors.

TypeSelector

Represents a type selector, such as article.

  • identifier (String) — the element type to match; can be * in the case of the universal selector;
  • namespace (String) — the namespace, if provided with the namespace|type syntax; an empty string corresponds to the |type syntax.

IdSelector

Represents an ID selector, such as #main.

  • identifier (String) — the ID to match;

ClassSelector

Represents a class selector, such as .primary.

  • identifier (String) — the class name to match;

AttributeSelector

Represents an attribute selector, such as [href^="http"].

  • identifier (String) — the attribute to match;
  • value (String) — the value to match against;
  • quotes (Boolean) — true if the value is a string; otherwise absent for brevity;
  • matcher (String) — one of =, ^=, $=, *=, ~=, |=;
  • modifier (String) — either s or i, if any.

PseudoClassSelector and PseudoElementSelector

Represents a pseudo-class selector (such as :visited or :is(a, b, c)) or a pseudo-element (such as ::before), respectively.

Both types of nodes share a common structure:

  • identifier (String) — the pseudo-class or pseudo-element;
  • argument (Anything) — the argument to the pseudo-class / pseudo-element;

In CSS, there is more than one way to interpret the argument passed to pseudo-classes and pseudo-elements which expressed with the function notation. Some pseudo-classes, such as :nth-*(), use the An+B microsyntax, others accept a list of selectors.

You can control how the microsyntaxes get applied to the pseudo-classes and pseudo-elements with the syntax option on the parse() method.

Supported selectors

  • [x] Logical combinations with :has(), :not(), :is(), :where() (and their legacy counterparts);
  • [x] Combinators A B, A > B, A + B, A ~ B, A || B, plus any custom combinators passed to parse();

See also

Selery is planned to power qsx, the query language based on CSS selectors, and hred, the command-line tool to extract data from HTML and XML.

You may also want to check out these other CSS parsing projects:

Acknowledgements

Selery’s tokenizer is much more robust thanks to the test suite imported from parse-css.