🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

selderee

Package Overview
Dependencies
Maintainers
1
Versions
9
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

selderee - npm Package Compare versions

Comparing version
0.11.0
to
0.12.0
+289
lib/selderee.d.cts
/**
* Decision tree node definitions.
*
* @packageDocumentation
*/
/**
* Specificity as defined by Selectors spec.
*
* {@link https://www.w3.org/TR/selectors/#specificity}
*
* Three levels: for id, class, tag (type).
*
* Extra level(s) used in HTML styling don't fit the scope of this package
* and no space reserved for them.
*/
type Specificity = [number, number, number];
/**
* Container for the associated value,
* selector specificity and position in the selectors collection.
*
* @typeParam V - the type of the associated value.
*/
type ValueContainer<V> = {
index: number;
specificity: Specificity;
value: V;
};
/**
* When reached a terminal node, decision tree adds
* the value container to the list of successful matches.
*/
type TerminalNode<V> = {
type: 'terminal';
valueContainer: ValueContainer<V>;
};
/**
* Tag name has to be checked.
* Underlying variants can be assembled
* into a dictionary key check.
*/
type TagNameNode<V> = {
type: 'tagName';
variants: VariantNode<V>[];
};
/**
* String value variant.
*/
type VariantNode<V> = {
type: 'variant';
value: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the presence of an element attribute
* with the given name.
*/
type AttrPresenceNode<V> = {
type: 'attrPresence';
name: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the value of an element attribute
* with the given name.
* It usually requires to run all underlying matchers
* one after another.
*/
type AttrValueNode<V> = {
type: 'attrValue';
name: string;
matchers: MatcherNode<V>[];
};
/**
* String value matcher.
* Contains the predicate so no need to reimplement it
* from descriptive parameters.
*/
type MatcherNode<V> = {
type: 'matcher';
matcher: '=' | '~=' | '|=' | '^=' | '$=' | '*=';
modifier: 'i' | 's' | null;
value: string;
predicate: (prop: string) => boolean;
cont: DecisionTreeNode<V>[];
};
/**
* Simple pseudo-class condition has to be checked.
*/
type PseudoClassNode<V> = {
type: 'pseudoClass';
name: string;
cont: DecisionTreeNode<V>[];
};
/**
* Push next element on the stack, defined by the combinator.
* Only `>` and `+` are expected to be supported.
*
* All checks are performed on the element on top of the stack.
*/
type PushElementNode<V> = {
type: 'pushElement';
combinator: '>' | '+';
cont: DecisionTreeNode<V>[];
};
/**
* Remove the top element from the stack -
* following checks are performed on the previous element.
*/
type PopElementNode<V> = {
type: 'popElement';
cont: DecisionTreeNode<V>[];
};
type DecisionTreeNode<V> = TerminalNode<V> | TagNameNode<V> | AttrPresenceNode<V> | AttrValueNode<V> | PseudoClassNode<V> | PushElementNode<V> | PopElementNode<V>;
type Ast_AttrPresenceNode<V> = AttrPresenceNode<V>;
type Ast_AttrValueNode<V> = AttrValueNode<V>;
type Ast_DecisionTreeNode<V> = DecisionTreeNode<V>;
type Ast_MatcherNode<V> = MatcherNode<V>;
type Ast_PopElementNode<V> = PopElementNode<V>;
type Ast_PseudoClassNode<V> = PseudoClassNode<V>;
type Ast_PushElementNode<V> = PushElementNode<V>;
type Ast_Specificity = Specificity;
type Ast_TagNameNode<V> = TagNameNode<V>;
type Ast_TerminalNode<V> = TerminalNode<V>;
type Ast_ValueContainer<V> = ValueContainer<V>;
type Ast_VariantNode<V> = VariantNode<V>;
declare namespace Ast {
export type { Ast_AttrPresenceNode as AttrPresenceNode, Ast_AttrValueNode as AttrValueNode, Ast_DecisionTreeNode as DecisionTreeNode, Ast_MatcherNode as MatcherNode, Ast_PopElementNode as PopElementNode, Ast_PseudoClassNode as PseudoClassNode, Ast_PushElementNode as PushElementNode, Ast_Specificity as Specificity, Ast_TagNameNode as TagNameNode, Ast_TerminalNode as TerminalNode, Ast_ValueContainer as ValueContainer, Ast_VariantNode as VariantNode };
}
/**
* Function types for builders and matchers.
*
* @packageDocumentation
*/
/**
* A function that turn a decision tree into a callable form.
*
* To be implemented by builder plugins.
*
* @typeParam V - the type of associated value.
*
* @typeParam R - return type for this builder
* (Consider using {@link Picker}.)
*/
type BuilderFunction<V, R> = (nodes: DecisionTreeNode<V>[]) => R;
/**
* Recommended matcher function shape to implement in builders.
*
* The elements stack is represented as the arguments array.
*
* @typeParam L - the type of elements in a particular DOM AST.
* @typeParam V - the type of associated value.
*/
type MatcherFunction<L, V> = (el: L, ...tail: L[]) => ValueContainer<V>[];
type Types_BuilderFunction<V, R> = BuilderFunction<V, R>;
type Types_MatcherFunction<L, V> = MatcherFunction<L, V>;
declare namespace Types {
export type { Types_BuilderFunction as BuilderFunction, Types_MatcherFunction as MatcherFunction };
}
/**
* Basic {@link BuilderFunction} implementation
* for decision tree visualization.
*
* @packageDocumentation
*/
/**
* A {@link BuilderFunction} implementation.
*
* Produces a string representation of the tree
* for testing and debug purposes.
*
* Only accepts `string` as the associated value type.
* Map your input collection before creating a {@link DecisionTree}
* if you want to use it with a different type -
* the decision on how to stringify the value is up to you.
*
* @param nodes - nodes from the root level of the decision tree.
* @returns the string representation of the tree.
*/
declare function treeify(nodes: DecisionTreeNode<string>[]): string;
declare const TreeifyBuilder_treeify: typeof treeify;
declare namespace TreeifyBuilder {
export {
TreeifyBuilder_treeify as treeify,
};
}
/**
* Options for DecisionTree construction.
*/
type DecisionTreeOptions = {
/**
* Attribute names whose values should be case-insensitive by default.
*
* Only affects attribute value selectors (`[name="value"]`) with no explicit case-sensitivity modifier (`i` or `s`).
*/
attributesWithNormalizedValues?: string[];
};
/**
* CSS selectors decision tree.
* Data structure that weaves similar selectors together
* in order to minimize the number of checks required
* to find the ones matching a given HTML element.
*
* Converted into a functioning implementation via plugins
* tailored for specific DOM ASTs.
*
* @typeParam V - the type of values associated with selectors.
*/
declare class DecisionTree<V> {
private readonly branches;
/**
* Create new DecisionTree object.
*
* @param input - an array containing all selectors
* paired with associated values.
* @param options - optional configuration for the decision tree.
*
* @typeParam V - the type of values associated with selectors.
*/
constructor(input: [string, V][], options?: DecisionTreeOptions);
/**
* Turn this decision tree into a callable form.
*
* @typeParam R - return type defined by the builder function.
*
* @param builder - the builder function.
*
* @returns the decision tree in a form ready for use.
*/
build<R>(builder: BuilderFunction<V, R>): R;
}
/**
* Simple wrapper around the matcher function.
* Recommended return type for builder plugins.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*/
declare class Picker<L, V> {
private f;
/**
* Create new Picker object.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*
* @param f - the function that matches an element
* and returns all associated values.
*/
constructor(f: MatcherFunction<L, V>);
/**
* Run the selectors decision tree against one HTML Element
* and return all matched associated values
* along with selector specificities.
*
* Client code then decides how to further process them
* (sort, filter, etc).
*
* @param el - an HTML Element.
*
* @returns all associated values along with
* selector specificities for all matched selectors.
*/
pickAll(el: L): ValueContainer<V>[];
/**
* Run the selectors decision tree against one HTML Element
* and choose the value from the most specific matched selector.
*
* @param el - an HTML Element.
*
* @param preferFirst - option to define which value to choose
* when there are multiple matches with equal specificity.
*
* @returns the value from the most specific matched selector
* or `null` if nothing matched.
*/
pick1(el: L, preferFirst?: boolean): V | null;
}
export { Ast, DecisionTree, Picker, TreeifyBuilder as Treeify, Types };
export type { DecisionTreeOptions };
/**
* Decision tree node definitions.
*
* @packageDocumentation
*/
/**
* Specificity as defined by Selectors spec.
*
* {@link https://www.w3.org/TR/selectors/#specificity}
*
* Three levels: for id, class, tag (type).
*
* Extra level(s) used in HTML styling don't fit the scope of this package
* and no space reserved for them.
*/
type Specificity = [number, number, number];
/**
* Container for the associated value,
* selector specificity and position in the selectors collection.
*
* @typeParam V - the type of the associated value.
*/
type ValueContainer<V> = {
index: number;
specificity: Specificity;
value: V;
};
/**
* When reached a terminal node, decision tree adds
* the value container to the list of successful matches.
*/
type TerminalNode<V> = {
type: 'terminal';
valueContainer: ValueContainer<V>;
};
/**
* Tag name has to be checked.
* Underlying variants can be assembled
* into a dictionary key check.
*/
type TagNameNode<V> = {
type: 'tagName';
variants: VariantNode<V>[];
};
/**
* String value variant.
*/
type VariantNode<V> = {
type: 'variant';
value: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the presence of an element attribute
* with the given name.
*/
type AttrPresenceNode<V> = {
type: 'attrPresence';
name: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the value of an element attribute
* with the given name.
* It usually requires to run all underlying matchers
* one after another.
*/
type AttrValueNode<V> = {
type: 'attrValue';
name: string;
matchers: MatcherNode<V>[];
};
/**
* String value matcher.
* Contains the predicate so no need to reimplement it
* from descriptive parameters.
*/
type MatcherNode<V> = {
type: 'matcher';
matcher: '=' | '~=' | '|=' | '^=' | '$=' | '*=';
modifier: 'i' | 's' | null;
value: string;
predicate: (prop: string) => boolean;
cont: DecisionTreeNode<V>[];
};
/**
* Simple pseudo-class condition has to be checked.
*/
type PseudoClassNode<V> = {
type: 'pseudoClass';
name: string;
cont: DecisionTreeNode<V>[];
};
/**
* Push next element on the stack, defined by the combinator.
* Only `>` and `+` are expected to be supported.
*
* All checks are performed on the element on top of the stack.
*/
type PushElementNode<V> = {
type: 'pushElement';
combinator: '>' | '+';
cont: DecisionTreeNode<V>[];
};
/**
* Remove the top element from the stack -
* following checks are performed on the previous element.
*/
type PopElementNode<V> = {
type: 'popElement';
cont: DecisionTreeNode<V>[];
};
type DecisionTreeNode<V> = TerminalNode<V> | TagNameNode<V> | AttrPresenceNode<V> | AttrValueNode<V> | PseudoClassNode<V> | PushElementNode<V> | PopElementNode<V>;
type Ast_AttrPresenceNode<V> = AttrPresenceNode<V>;
type Ast_AttrValueNode<V> = AttrValueNode<V>;
type Ast_DecisionTreeNode<V> = DecisionTreeNode<V>;
type Ast_MatcherNode<V> = MatcherNode<V>;
type Ast_PopElementNode<V> = PopElementNode<V>;
type Ast_PseudoClassNode<V> = PseudoClassNode<V>;
type Ast_PushElementNode<V> = PushElementNode<V>;
type Ast_Specificity = Specificity;
type Ast_TagNameNode<V> = TagNameNode<V>;
type Ast_TerminalNode<V> = TerminalNode<V>;
type Ast_ValueContainer<V> = ValueContainer<V>;
type Ast_VariantNode<V> = VariantNode<V>;
declare namespace Ast {
export type { Ast_AttrPresenceNode as AttrPresenceNode, Ast_AttrValueNode as AttrValueNode, Ast_DecisionTreeNode as DecisionTreeNode, Ast_MatcherNode as MatcherNode, Ast_PopElementNode as PopElementNode, Ast_PseudoClassNode as PseudoClassNode, Ast_PushElementNode as PushElementNode, Ast_Specificity as Specificity, Ast_TagNameNode as TagNameNode, Ast_TerminalNode as TerminalNode, Ast_ValueContainer as ValueContainer, Ast_VariantNode as VariantNode };
}
/**
* Function types for builders and matchers.
*
* @packageDocumentation
*/
/**
* A function that turn a decision tree into a callable form.
*
* To be implemented by builder plugins.
*
* @typeParam V - the type of associated value.
*
* @typeParam R - return type for this builder
* (Consider using {@link Picker}.)
*/
type BuilderFunction<V, R> = (nodes: DecisionTreeNode<V>[]) => R;
/**
* Recommended matcher function shape to implement in builders.
*
* The elements stack is represented as the arguments array.
*
* @typeParam L - the type of elements in a particular DOM AST.
* @typeParam V - the type of associated value.
*/
type MatcherFunction<L, V> = (el: L, ...tail: L[]) => ValueContainer<V>[];
type Types_BuilderFunction<V, R> = BuilderFunction<V, R>;
type Types_MatcherFunction<L, V> = MatcherFunction<L, V>;
declare namespace Types {
export type { Types_BuilderFunction as BuilderFunction, Types_MatcherFunction as MatcherFunction };
}
/**
* Basic {@link BuilderFunction} implementation
* for decision tree visualization.
*
* @packageDocumentation
*/
/**
* A {@link BuilderFunction} implementation.
*
* Produces a string representation of the tree
* for testing and debug purposes.
*
* Only accepts `string` as the associated value type.
* Map your input collection before creating a {@link DecisionTree}
* if you want to use it with a different type -
* the decision on how to stringify the value is up to you.
*
* @param nodes - nodes from the root level of the decision tree.
* @returns the string representation of the tree.
*/
declare function treeify(nodes: DecisionTreeNode<string>[]): string;
declare const TreeifyBuilder_treeify: typeof treeify;
declare namespace TreeifyBuilder {
export {
TreeifyBuilder_treeify as treeify,
};
}
/**
* Options for DecisionTree construction.
*/
type DecisionTreeOptions = {
/**
* Attribute names whose values should be case-insensitive by default.
*
* Only affects attribute value selectors (`[name="value"]`) with no explicit case-sensitivity modifier (`i` or `s`).
*/
attributesWithNormalizedValues?: string[];
};
/**
* CSS selectors decision tree.
* Data structure that weaves similar selectors together
* in order to minimize the number of checks required
* to find the ones matching a given HTML element.
*
* Converted into a functioning implementation via plugins
* tailored for specific DOM ASTs.
*
* @typeParam V - the type of values associated with selectors.
*/
declare class DecisionTree<V> {
private readonly branches;
/**
* Create new DecisionTree object.
*
* @param input - an array containing all selectors
* paired with associated values.
* @param options - optional configuration for the decision tree.
*
* @typeParam V - the type of values associated with selectors.
*/
constructor(input: [string, V][], options?: DecisionTreeOptions);
/**
* Turn this decision tree into a callable form.
*
* @typeParam R - return type defined by the builder function.
*
* @param builder - the builder function.
*
* @returns the decision tree in a form ready for use.
*/
build<R>(builder: BuilderFunction<V, R>): R;
}
/**
* Simple wrapper around the matcher function.
* Recommended return type for builder plugins.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*/
declare class Picker<L, V> {
private f;
/**
* Create new Picker object.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*
* @param f - the function that matches an element
* and returns all associated values.
*/
constructor(f: MatcherFunction<L, V>);
/**
* Run the selectors decision tree against one HTML Element
* and return all matched associated values
* along with selector specificities.
*
* Client code then decides how to further process them
* (sort, filter, etc).
*
* @param el - an HTML Element.
*
* @returns all associated values along with
* selector specificities for all matched selectors.
*/
pickAll(el: L): ValueContainer<V>[];
/**
* Run the selectors decision tree against one HTML Element
* and choose the value from the most specific matched selector.
*
* @param el - an HTML Element.
*
* @param preferFirst - option to define which value to choose
* when there are multiple matches with equal specificity.
*
* @returns the value from the most specific matched selector
* or `null` if nothing matched.
*/
pick1(el: L, preferFirst?: boolean): V | null;
}
export { Ast, DecisionTree, Picker, TreeifyBuilder as Treeify, Types };
export type { DecisionTreeOptions };
+90
-79
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var parseley = require('parseley');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
function _interopNamespaceDefault(e) {
var n = Object.create(null);

@@ -21,17 +18,19 @@ if (e) {

}
n["default"] = e;
n.default = e;
return Object.freeze(n);
}
var parseley__namespace = /*#__PURE__*/_interopNamespace(parseley);
var parseley__namespace = /*#__PURE__*/_interopNamespaceDefault(parseley);
var Ast = /*#__PURE__*/Object.freeze({
var Ast$1 = /*#__PURE__*/Object.freeze({
__proto__: null
});
var Types = /*#__PURE__*/Object.freeze({
var Types$1 = /*#__PURE__*/Object.freeze({
__proto__: null
});
const treeify = (nodes) => '▽\n' + treeifyArray(nodes, thinLines);
function treeify(nodes) {
return '▽\n' + treeifyArray(nodes, thinLines);
}
const thinLines = [['├─', '│ '], ['└─', ' ']];

@@ -55,2 +54,4 @@ const heavyLines = [['┠─', '┃ '], ['┖─', ' ']];

return `◨ Attr presence: ${node.name}\n${treeifyArray(node.cont)}`;
case 'pseudoClass':
return `◪ Pseudo-class: ${node.name}\n${treeifyArray(node.cont)}`;
case 'pushElement':

@@ -82,4 +83,5 @@ return `◉ Push element: ${node.combinator}\n${treeifyArray(node.cont, thinLines)}`;

class DecisionTree {
constructor(input) {
this.branches = weave(toAstTerminalPairs(input));
branches;
constructor(input, options = {}) {
this.branches = weave(toAstTerminalPairs(input, options));
}

@@ -90,3 +92,3 @@ build(builder) {

}
function toAstTerminalPairs(array) {
function toAstTerminalPairs(array, options) {
const len = array.length;

@@ -96,3 +98,9 @@ const results = new Array(len);

const [selectorString, val] = array[i];
const ast = preprocess(parseley__namespace.parse1(selectorString));
const ast = parseley__namespace.parse1(selectorString);
reduceSelectorVariants(ast);
parseley__namespace.normalize(ast, {
mode: 'html',
attributesWithNormalizedValues: options.attributesWithNormalizedValues ?? [],
allowUnspecifiedCaseSensitivityForAttributes: false,
});
results[i] = {

@@ -102,4 +110,4 @@ ast: ast,

type: 'terminal',
valueContainer: { index: i, value: val, specificity: ast.specificity }
}
valueContainer: { index: i, value: val, specificity: ast.specificity },
},
};

@@ -109,10 +117,5 @@ }

}
function preprocess(ast) {
reduceSelectorVariants(ast);
parseley__namespace.normalize(ast);
return ast;
}
function reduceSelectorVariants(ast) {
const newList = [];
ast.list.forEach(sel => {
ast.list.forEach((sel) => {
switch (sel.type) {

@@ -136,3 +139,3 @@ case 'class':

namespace: null,
specificity: sel.specificity,
specificity: [0, 1, 0],
type: 'attrValue',

@@ -158,5 +161,5 @@ value: sel.name,

while (items.length) {
const topKind = findTopKey(items, (sel) => true, getSelectorKind);
const { matches, nonmatches, empty } = breakByKind(items, topKind);
items = nonmatches;
const topKind = findTopKey(items, (_sel) => true, getSelectorKind);
const { matches, nonMatches, empty } = breakByKind(items, topKind);
items = nonMatches;
if (matches.length) {

@@ -180,3 +183,3 @@ branches.push(branchOfKind(topKind, matches));

const { matches, rest } = partition(terminal.cont, (node) => node.type === 'terminal');
matches.forEach((node) => results.push(node));
matches.forEach(node => results.push(node));
if (rest.length) {

@@ -192,9 +195,9 @@ terminal.cont = rest;

const matches = [];
const nonmatches = [];
const nonMatches = [];
const empty = [];
for (const item of items) {
const simpsels = item.ast.list;
if (simpsels.length) {
const isMatch = simpsels.some(node => getSelectorKind(node) === selectedKind);
(isMatch ? matches : nonmatches).push(item);
const simpleSelectors = item.ast.list;
if (simpleSelectors.length) {
const isMatch = simpleSelectors.some(node => getSelectorKind(node) === selectedKind);
(isMatch ? matches : nonMatches).push(item);
}

@@ -205,3 +208,3 @@ else {

}
return { matches, nonmatches, empty };
return { matches, nonMatches, empty };
}

@@ -214,2 +217,4 @@ function getSelectorKind(sel) {

return `attrValue ${sel.name}`;
case 'pc':
return `pc ${sel.name}`;
case 'combinator':

@@ -231,2 +236,5 @@ return `combinator ${sel.combinator}`;

}
if (kind.startsWith('pc ')) {
return pseudoClassBranch(kind.substring(3), items);
}
if (kind === 'combinator >') {

@@ -241,11 +249,11 @@ return combinatorBranch('>', items);

function tagNameBranch(items) {
const groups = spliceAndGroup(items, (x) => x.type === 'tag', (x) => x.name);
const groups = spliceAndGroup(items, (x) => x.type === 'tag', x => x.name);
const variants = Object.entries(groups).map(([name, group]) => ({
type: 'variant',
value: name,
cont: weave(group.items)
cont: weave(group.items),
}));
return {
type: 'tagName',
variants: variants
variants: variants,
};

@@ -260,12 +268,10 @@ }

name: name,
cont: weave(items)
cont: weave(items),
};
}
function attrValueBranch(name, items) {
const groups = spliceAndGroup(items, (x) => (x.type === 'attrValue') && (x.name === name), (x) => `${x.matcher} ${x.modifier || ''} ${x.value}`);
const groups = spliceAndGroup(items, (x) => (x.type === 'attrValue') && (x.name === name), x => `${x.matcher} ${x.modifier || ''} ${x.value}`);
const matchers = [];
for (const group of Object.values(groups)) {
const sel = group.oneSimpleSelector;
const predicate = getAttrPredicate(sel);
const continuation = weave(group.items);
matchers.push({

@@ -276,4 +282,4 @@ type: 'matcher',

value: sel.value,
predicate: predicate,
cont: continuation
predicate: getAttrValuePredicate(sel),
cont: weave(group.items),
});

@@ -284,6 +290,6 @@ }

name: name,
matchers: matchers
matchers: matchers,
};
}
function getAttrPredicate(sel) {
function getAttrValuePredicate(sel) {
if (sel.modifier === 'i') {

@@ -293,11 +299,11 @@ const expected = sel.value.toLowerCase();

case '=':
return (actual) => expected === actual.toLowerCase();
return actual => expected === actual.toLowerCase();
case '~=':
return (actual) => actual.toLowerCase().split(/[ \t]+/).includes(expected);
return actual => actual.toLowerCase().split(/[ \t]+/).includes(expected);
case '^=':
return (actual) => actual.toLowerCase().startsWith(expected);
return actual => actual.toLowerCase().startsWith(expected);
case '$=':
return (actual) => actual.toLowerCase().endsWith(expected);
return actual => actual.toLowerCase().endsWith(expected);
case '*=':
return (actual) => actual.toLowerCase().includes(expected);
return actual => actual.toLowerCase().includes(expected);
case '|=':

@@ -314,18 +320,35 @@ return (actual) => {

case '=':
return (actual) => expected === actual;
return actual => expected === actual;
case '~=':
return (actual) => actual.split(/[ \t]+/).includes(expected);
return actual => actual.split(/[ \t]+/).includes(expected);
case '^=':
return (actual) => actual.startsWith(expected);
return actual => actual.startsWith(expected);
case '$=':
return (actual) => actual.endsWith(expected);
return actual => actual.endsWith(expected);
case '*=':
return (actual) => actual.includes(expected);
return actual => actual.includes(expected);
case '|=':
return (actual) => (expected === actual) || (actual.startsWith(expected) && actual[expected.length] === '-');
return actual => (expected === actual) || (actual.startsWith(expected) && actual[expected.length] === '-');
}
}
}
function pseudoClassBranch(name, items) {
if (name !== 'empty'
&& name !== 'only-child'
&& name !== 'first-child'
&& name !== 'last-child'
&& name !== 'any-link') {
throw new Error(`Unsupported pseudo-class: :${name}`);
}
for (const item of items) {
spliceSimpleSelector(item, (x) => (x.type === 'pc') && (x.name === name));
}
return {
type: 'pseudoClass',
name: name,
cont: weave(items),
};
}
function combinatorBranch(combinator, items) {
const groups = spliceAndGroup(items, (x) => (x.type === 'combinator') && (x.combinator === combinator), (x) => parseley__namespace.serialize(x.left));
const groups = spliceAndGroup(items, (x) => (x.type === 'combinator') && (x.combinator === combinator), x => parseley__namespace.serialize(x.left));
const leftItems = [];

@@ -337,3 +360,3 @@ for (const group of Object.values(groups)) {

ast: leftAst,
terminal: { type: 'popElement', cont: rightCont }
terminal: { type: 'popElement', cont: rightCont },
});

@@ -344,3 +367,3 @@ }

combinator: combinator,
cont: weave(leftItems)
cont: weave(leftItems),
};

@@ -354,3 +377,3 @@ }

const hasBestKeyPredicate = (item) => item.ast.list.some(bestKeyPredicate);
const { matches, rest } = partition1(items, hasBestKeyPredicate);
const { matches, rest } = partition(items, hasBestKeyPredicate);
let oneSimpleSelector = null;

@@ -372,7 +395,7 @@ for (const item of matches) {

function spliceSimpleSelector(item, predicate) {
const simpsels = item.ast.list;
const matches = new Array(simpsels.length);
const simpleSelectors = item.ast.list;
const matches = new Array(simpleSelectors.length);
let firstIndex = -1;
for (let i = simpsels.length; i-- > 0;) {
if (predicate(simpsels[i])) {
for (let i = simpleSelectors.length; i-- > 0;) {
if (predicate(simpleSelectors[i])) {
matches[i] = true;

@@ -385,4 +408,4 @@ firstIndex = i;

}
const result = simpsels[firstIndex];
item.ast.list = simpsels.filter((sel, i) => !matches[i]);
const result = simpleSelectors[firstIndex];
item.ast.list = simpleSelectors.filter((_, i) => !matches[i]);
return result;

@@ -429,17 +452,5 @@ }

}
function partition1(src, predicate) {
const matches = [];
const rest = [];
for (const x of src) {
if (predicate(x)) {
matches.push(x);
}
else {
rest.push(x);
}
}
return { matches, rest };
}
class Picker {
f;
constructor(f) {

@@ -482,6 +493,6 @@ this.f = f;

exports.Ast = Ast;
exports.Ast = Ast$1;
exports.DecisionTree = DecisionTree;
exports.Picker = Picker;
exports.Treeify = TreeifyBuilder;
exports.Types = Types;
exports.Types = Types$1;
import * as parseley from 'parseley';
import { compareSpecificity } from 'parseley';
var Ast = /*#__PURE__*/Object.freeze({
var Ast$1 = /*#__PURE__*/Object.freeze({
__proto__: null
});
var Types = /*#__PURE__*/Object.freeze({
var Types$1 = /*#__PURE__*/Object.freeze({
__proto__: null
});
const treeify = (nodes) => '▽\n' + treeifyArray(nodes, thinLines);
function treeify(nodes) {
return '▽\n' + treeifyArray(nodes, thinLines);
}
const thinLines = [['├─', '│ '], ['└─', ' ']];

@@ -31,2 +33,4 @@ const heavyLines = [['┠─', '┃ '], ['┖─', ' ']];

return `◨ Attr presence: ${node.name}\n${treeifyArray(node.cont)}`;
case 'pseudoClass':
return `◪ Pseudo-class: ${node.name}\n${treeifyArray(node.cont)}`;
case 'pushElement':

@@ -58,4 +62,5 @@ return `◉ Push element: ${node.combinator}\n${treeifyArray(node.cont, thinLines)}`;

class DecisionTree {
constructor(input) {
this.branches = weave(toAstTerminalPairs(input));
branches;
constructor(input, options = {}) {
this.branches = weave(toAstTerminalPairs(input, options));
}

@@ -66,3 +71,3 @@ build(builder) {

}
function toAstTerminalPairs(array) {
function toAstTerminalPairs(array, options) {
const len = array.length;

@@ -72,3 +77,9 @@ const results = new Array(len);

const [selectorString, val] = array[i];
const ast = preprocess(parseley.parse1(selectorString));
const ast = parseley.parse1(selectorString);
reduceSelectorVariants(ast);
parseley.normalize(ast, {
mode: 'html',
attributesWithNormalizedValues: options.attributesWithNormalizedValues ?? [],
allowUnspecifiedCaseSensitivityForAttributes: false,
});
results[i] = {

@@ -78,4 +89,4 @@ ast: ast,

type: 'terminal',
valueContainer: { index: i, value: val, specificity: ast.specificity }
}
valueContainer: { index: i, value: val, specificity: ast.specificity },
},
};

@@ -85,10 +96,5 @@ }

}
function preprocess(ast) {
reduceSelectorVariants(ast);
parseley.normalize(ast);
return ast;
}
function reduceSelectorVariants(ast) {
const newList = [];
ast.list.forEach(sel => {
ast.list.forEach((sel) => {
switch (sel.type) {

@@ -112,3 +118,3 @@ case 'class':

namespace: null,
specificity: sel.specificity,
specificity: [0, 1, 0],
type: 'attrValue',

@@ -134,5 +140,5 @@ value: sel.name,

while (items.length) {
const topKind = findTopKey(items, (sel) => true, getSelectorKind);
const { matches, nonmatches, empty } = breakByKind(items, topKind);
items = nonmatches;
const topKind = findTopKey(items, (_sel) => true, getSelectorKind);
const { matches, nonMatches, empty } = breakByKind(items, topKind);
items = nonMatches;
if (matches.length) {

@@ -156,3 +162,3 @@ branches.push(branchOfKind(topKind, matches));

const { matches, rest } = partition(terminal.cont, (node) => node.type === 'terminal');
matches.forEach((node) => results.push(node));
matches.forEach(node => results.push(node));
if (rest.length) {

@@ -168,9 +174,9 @@ terminal.cont = rest;

const matches = [];
const nonmatches = [];
const nonMatches = [];
const empty = [];
for (const item of items) {
const simpsels = item.ast.list;
if (simpsels.length) {
const isMatch = simpsels.some(node => getSelectorKind(node) === selectedKind);
(isMatch ? matches : nonmatches).push(item);
const simpleSelectors = item.ast.list;
if (simpleSelectors.length) {
const isMatch = simpleSelectors.some(node => getSelectorKind(node) === selectedKind);
(isMatch ? matches : nonMatches).push(item);
}

@@ -181,3 +187,3 @@ else {

}
return { matches, nonmatches, empty };
return { matches, nonMatches, empty };
}

@@ -190,2 +196,4 @@ function getSelectorKind(sel) {

return `attrValue ${sel.name}`;
case 'pc':
return `pc ${sel.name}`;
case 'combinator':

@@ -207,2 +215,5 @@ return `combinator ${sel.combinator}`;

}
if (kind.startsWith('pc ')) {
return pseudoClassBranch(kind.substring(3), items);
}
if (kind === 'combinator >') {

@@ -217,11 +228,11 @@ return combinatorBranch('>', items);

function tagNameBranch(items) {
const groups = spliceAndGroup(items, (x) => x.type === 'tag', (x) => x.name);
const groups = spliceAndGroup(items, (x) => x.type === 'tag', x => x.name);
const variants = Object.entries(groups).map(([name, group]) => ({
type: 'variant',
value: name,
cont: weave(group.items)
cont: weave(group.items),
}));
return {
type: 'tagName',
variants: variants
variants: variants,
};

@@ -236,12 +247,10 @@ }

name: name,
cont: weave(items)
cont: weave(items),
};
}
function attrValueBranch(name, items) {
const groups = spliceAndGroup(items, (x) => (x.type === 'attrValue') && (x.name === name), (x) => `${x.matcher} ${x.modifier || ''} ${x.value}`);
const groups = spliceAndGroup(items, (x) => (x.type === 'attrValue') && (x.name === name), x => `${x.matcher} ${x.modifier || ''} ${x.value}`);
const matchers = [];
for (const group of Object.values(groups)) {
const sel = group.oneSimpleSelector;
const predicate = getAttrPredicate(sel);
const continuation = weave(group.items);
matchers.push({

@@ -252,4 +261,4 @@ type: 'matcher',

value: sel.value,
predicate: predicate,
cont: continuation
predicate: getAttrValuePredicate(sel),
cont: weave(group.items),
});

@@ -260,6 +269,6 @@ }

name: name,
matchers: matchers
matchers: matchers,
};
}
function getAttrPredicate(sel) {
function getAttrValuePredicate(sel) {
if (sel.modifier === 'i') {

@@ -269,11 +278,11 @@ const expected = sel.value.toLowerCase();

case '=':
return (actual) => expected === actual.toLowerCase();
return actual => expected === actual.toLowerCase();
case '~=':
return (actual) => actual.toLowerCase().split(/[ \t]+/).includes(expected);
return actual => actual.toLowerCase().split(/[ \t]+/).includes(expected);
case '^=':
return (actual) => actual.toLowerCase().startsWith(expected);
return actual => actual.toLowerCase().startsWith(expected);
case '$=':
return (actual) => actual.toLowerCase().endsWith(expected);
return actual => actual.toLowerCase().endsWith(expected);
case '*=':
return (actual) => actual.toLowerCase().includes(expected);
return actual => actual.toLowerCase().includes(expected);
case '|=':

@@ -290,18 +299,35 @@ return (actual) => {

case '=':
return (actual) => expected === actual;
return actual => expected === actual;
case '~=':
return (actual) => actual.split(/[ \t]+/).includes(expected);
return actual => actual.split(/[ \t]+/).includes(expected);
case '^=':
return (actual) => actual.startsWith(expected);
return actual => actual.startsWith(expected);
case '$=':
return (actual) => actual.endsWith(expected);
return actual => actual.endsWith(expected);
case '*=':
return (actual) => actual.includes(expected);
return actual => actual.includes(expected);
case '|=':
return (actual) => (expected === actual) || (actual.startsWith(expected) && actual[expected.length] === '-');
return actual => (expected === actual) || (actual.startsWith(expected) && actual[expected.length] === '-');
}
}
}
function pseudoClassBranch(name, items) {
if (name !== 'empty'
&& name !== 'only-child'
&& name !== 'first-child'
&& name !== 'last-child'
&& name !== 'any-link') {
throw new Error(`Unsupported pseudo-class: :${name}`);
}
for (const item of items) {
spliceSimpleSelector(item, (x) => (x.type === 'pc') && (x.name === name));
}
return {
type: 'pseudoClass',
name: name,
cont: weave(items),
};
}
function combinatorBranch(combinator, items) {
const groups = spliceAndGroup(items, (x) => (x.type === 'combinator') && (x.combinator === combinator), (x) => parseley.serialize(x.left));
const groups = spliceAndGroup(items, (x) => (x.type === 'combinator') && (x.combinator === combinator), x => parseley.serialize(x.left));
const leftItems = [];

@@ -313,3 +339,3 @@ for (const group of Object.values(groups)) {

ast: leftAst,
terminal: { type: 'popElement', cont: rightCont }
terminal: { type: 'popElement', cont: rightCont },
});

@@ -320,3 +346,3 @@ }

combinator: combinator,
cont: weave(leftItems)
cont: weave(leftItems),
};

@@ -330,3 +356,3 @@ }

const hasBestKeyPredicate = (item) => item.ast.list.some(bestKeyPredicate);
const { matches, rest } = partition1(items, hasBestKeyPredicate);
const { matches, rest } = partition(items, hasBestKeyPredicate);
let oneSimpleSelector = null;

@@ -348,7 +374,7 @@ for (const item of matches) {

function spliceSimpleSelector(item, predicate) {
const simpsels = item.ast.list;
const matches = new Array(simpsels.length);
const simpleSelectors = item.ast.list;
const matches = new Array(simpleSelectors.length);
let firstIndex = -1;
for (let i = simpsels.length; i-- > 0;) {
if (predicate(simpsels[i])) {
for (let i = simpleSelectors.length; i-- > 0;) {
if (predicate(simpleSelectors[i])) {
matches[i] = true;

@@ -361,4 +387,4 @@ firstIndex = i;

}
const result = simpsels[firstIndex];
item.ast.list = simpsels.filter((sel, i) => !matches[i]);
const result = simpleSelectors[firstIndex];
item.ast.list = simpleSelectors.filter((_, i) => !matches[i]);
return result;

@@ -405,17 +431,5 @@ }

}
function partition1(src, predicate) {
const matches = [];
const rest = [];
for (const x of src) {
if (predicate(x)) {
matches.push(x);
}
else {
rest.push(x);
}
}
return { matches, rest };
}
class Picker {
f;
constructor(f) {

@@ -458,2 +472,2 @@ this.f = f;

export { Ast, DecisionTree, Picker, TreeifyBuilder as Treeify, Types };
export { Ast$1 as Ast, DecisionTree, Picker, TreeifyBuilder as Treeify, Types$1 as Types };
MIT License
Copyright (c) 2021-2022 KillyMXI <killy@mxii.eu.org>
Copyright (c) 2021-2026 KillyMXI <killy@mxii.eu.org>

@@ -5,0 +5,0 @@ Permission is hereby granted, free of charge, to any person obtaining a copy

{
"name": "selderee",
"version": "0.11.0",
"version": "0.12.0",
"description": "Selectors decision tree - choose matching selectors, fast",

@@ -20,7 +20,16 @@ "keywords": [

"author": "KillyMXI",
"funding": "https://ko-fi.com/killymxi",
"funding": "https://github.com/sponsors/KillyMXI",
"license": "MIT",
"exports": {
"import": "./lib/selderee.mjs",
"require": "./lib/selderee.cjs"
".": {
"import": {
"types": "./lib/selderee.d.mts",
"default": "./lib/selderee.mjs"
},
"require": {
"types": "./lib/selderee.d.cts",
"default": "./lib/selderee.cjs"
}
},
"./package.json": "./package.json"
},

@@ -30,4 +39,3 @@ "type": "module",

"module": "./lib/selderee.mjs",
"types": "./lib/selderee.d.ts",
"typedocMain": "./src/selderee.ts",
"types": "./lib/selderee.d.cts",
"files": [

@@ -44,4 +52,4 @@ "lib"

"dependencies": {
"parseley": "^0.12.0"
"parseley": "~0.13.1"
}
}
+18
-18

@@ -26,4 +26,8 @@ # selderee

- Pseudo-classes and pseudo-elements are not supported by the underlying library [parseley](https://github.com/mxxii/parseley) (yet?);
- General siblings (`~`), descendants (` `) and same column combinators (`||`) are also not supported.
- Only selected combinators are supported - child (`>`) and adjacent siblings (`+`);
- General siblings (`~`), descendants (` `) and same column (`||`) combinators are not supported;
- (0.12.x+) Only selected pseudo-classes are currently supported - `:empty`, `:first-child`, `:last-child`, `:only-child` and `:any-link`;
- Other pseudo-classes and pseudo-elements are not supported here or in the underlying library [parseley](https://github.com/mxxii/parseley) yet;
- Case sensitivity model of attribute value selectors is simplified: matching is case-sensitive by default (if no case-sensitivity flag is specified);
- (0.12.x+) It is left to client code to specify what attributes have to be case-insensitive by default - via `DecisionTreeOptions.attributesWithNormalizedValues`.

@@ -83,3 +87,6 @@

// Make a tree structure from all given selectors.
const selectorsDecisionTree = new DecisionTree(selectorValuePairs);
const selectorsDecisionTree = new DecisionTree(
selectorValuePairs,
{ attributesWithNormalizedValues: [] }, // optional, default is `[]`
);

@@ -110,3 +117,3 @@ // `treeify` builder produces a string output for testing and debug purposes.

<details><summary>Example output</summary>
Output of the example:

@@ -118,3 +125,3 @@ ```text

│ ║ ┠─▣ Attr value: class
│ ║ ┃ ╙─◈ ~= "foo"
│ ║ ┃ ╙─◈ ~= "foo"s
│ ║ ┃ ┠─◨ Attr presence: bar

@@ -134,11 +141,13 @@ │ ║ ┃ ┃ ┖─◁ #1 [0,2,1] B

│ ┖─▣ Attr value: class
│ ╙─◈ ~= "foo"
│ ╙─◈ ~= "foo"s
│ ┖─◁ #3 [0,1,1] D
└─▣ Attr value: id
╙─◈ = "baz"
╙─◈ = "baz"s
┖─◁ #6 [1,0,0] G
[ { index: 2, value: 'C', specificity: [ 0, 1, 1 ] },
[
{ index: 2, value: 'C', specificity: [ 0, 1, 1 ] },
{ index: 4, value: 'E', specificity: [ 0, 1, 2 ] },
{ index: 0, value: 'A', specificity: [ 0, 0, 1 ] },
{ index: 5, value: 'F', specificity: [ 0, 0, 2 ] } ]
{ index: 5, value: 'F', specificity: [ 0, 0, 2 ] }
]
Best matched value: E

@@ -148,10 +157,1 @@ ```

*Some gotcha: you may notice the check for `#baz` has to be performed every time the decision tree is called. If it happens to be `p#baz` or `div#baz` or even `.foo#baz` - it would be much better to write it like this. Deeper, narrower tree means less checks on average. (in case of `.foo#baz` the class check might finally outweigh the tag name check and rebalance the tree.)*
</details>
## Development
Targeting Node.js version >=14.
Monorepo uses NPM v7 workspaces (make sure v7 is installed when used with Node.js v14.)
# Changelog
## Version 0.11.0
* Bump `parseley` dependency to version 0.12.0 ([changelog](https://github.com/mxxii/parseley/blob/main/CHANGELOG.md)). Escape sequences in selectors.
## Version 0.10.0
* Targeting Node.js version 14 and ES2020;
* Bump dependencies.
## Version 0.9.0
* Bump dependencies - fix "./core module cannot be found" issue.
## Version 0.8.1
* Bump `parseley` dependency to version 0.9.1 ([changelog](https://github.com/mxxii/parseley/blob/main/CHANGELOG.md)). Now all dependencies are TypeScript, dual CommonJS/ES module packages;
* Use `rollup-plugin-cleanup` to condition published files;
* Package is marked as free of side effects.
## Version 0.7.0
* Drop Node.js version 10 support. At least 12.22.x is required.
## Version 0.6.0
* Give priority to more common attribute values (Previously the first matching simple selector was taken instead);
* Repeated simple selectors should not affect the tree balance and should not produce extra tree nodes (But they affect the specificity).
## Version 0.5.0
Initial release.
Aiming at Node.js version 10 and up.
/**
* Specificity as defined by Selectors spec.
*
* {@link https://www.w3.org/TR/selectors/#specificity}
*
* Three levels: for id, class, tag (type).
*
* Extra level(s) used in HTML styling don't fit the scope of this package
* and no space reserved for them.
*/
export declare type Specificity = [number, number, number];
/**
* Container for the associated value,
* selector specificity and position in the selectors collection.
*
* @typeParam V - the type of the associated value.
*/
export declare type ValueContainer<V> = {
index: number;
specificity: Specificity;
value: V;
};
/**
* When reached a terminal node, decision tree adds
* the value container to the list of successful matches.
*/
export declare type TerminalNode<V> = {
type: 'terminal';
valueContainer: ValueContainer<V>;
};
/**
* Tag name has to be checked.
* Underlying variants can be assembled
* into a dictionary key check.
*/
export declare type TagNameNode<V> = {
type: 'tagName';
variants: VariantNode<V>[];
};
/**
* String value variant.
*/
export declare type VariantNode<V> = {
type: 'variant';
value: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the presence of an element attribute
* with the given name.
*/
export declare type AttrPresenceNode<V> = {
type: 'attrPresence';
name: string;
cont: DecisionTreeNode<V>[];
};
/**
* Have to check the value of an element attribute
* with the given name.
* It usually requires to run all underlying matchers
* one after another.
*/
export declare type AttrValueNode<V> = {
type: 'attrValue';
name: string;
matchers: MatcherNode<V>[];
};
/**
* String value matcher.
* Contains the predicate so no need to reimplement it
* from descriptive parameters.
*/
export declare type MatcherNode<V> = {
type: 'matcher';
matcher: '=' | '~=' | '|=' | '^=' | '$=' | '*=';
modifier: 'i' | 's' | null;
value: string;
predicate: (prop: string) => boolean;
cont: DecisionTreeNode<V>[];
};
/**
* Push next element on the stack, defined by the combinator.
* Only `>` and `+` are expected to be supported.
*
* All checks are performed on the element on top of the stack.
*/
export declare type PushElementNode<V> = {
type: 'pushElement';
combinator: '>' | '+';
cont: DecisionTreeNode<V>[];
};
/**
* Remove the top element from the stack -
* following checks are performed on the previous element.
*/
export declare type PopElementNode<V> = {
type: 'popElement';
cont: DecisionTreeNode<V>[];
};
export declare type DecisionTreeNode<V> = TerminalNode<V> | TagNameNode<V> | AttrPresenceNode<V> | AttrValueNode<V> | PushElementNode<V> | PopElementNode<V>;
import { BuilderFunction } from './Types';
/**
* CSS selectors decision tree.
* Data structure that weaves similar selectors together
* in order to minimize the number of checks required
* to find the ones matching a given HTML element.
*
* Converted into a functioning implementation via plugins
* tailored for specific DOM ASTs.
*
* @typeParam V - the type of values associated with selectors.
*/
export declare class DecisionTree<V> {
private readonly branches;
/**
* Create new DecisionTree object.
*
* @param input - an array containing all selectors
* paired with associated values.
*
* @typeParam V - the type of values associated with selectors.
*/
constructor(input: [string, V][]);
/**
* Turn this decision tree into a usable form.
*
* @typeParam R - return type defined by the builder function.
*
* @param builder - the builder function.
*
* @returns the decision tree in a form ready for use.
*/
build<R>(builder: BuilderFunction<V, R>): R;
}
import { ValueContainer } from './Ast';
import { MatcherFunction } from './Types';
/**
* Simple wrapper around the matcher function.
* Recommended return type for builder plugins.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*/
export declare class Picker<L, V> {
private f;
/**
* Create new Picker object.
*
* @typeParam L - the type of HTML Element in the targeted DOM AST.
* @typeParam V - the type of associated values.
*
* @param f - the function that matches an element
* and returns all associated values.
*/
constructor(f: MatcherFunction<L, V>);
/**
* Run the selectors decision tree against one HTML Element
* and return all matched associated values
* along with selector specificities.
*
* Client code then decides how to further process them
* (sort, filter, etc).
*
* @param el - an HTML Element.
*
* @returns all associated values along with
* selector specificities for all matched selectors.
*/
pickAll(el: L): ValueContainer<V>[];
/**
* Run the selectors decision tree against one HTML Element
* and choose the value from the most specific matched selector.
*
* @param el - an HTML Element.
*
* @param preferFirst - option to define which value to choose
* when there are multiple matches with equal specificity.
*
* @returns the value from the most specific matched selector
* or `null` if nothing matched.
*/
pick1(el: L, preferFirst?: boolean): V | null;
}
export * as Ast from './Ast';
export * as Types from './Types';
export * as Treeify from './TreeifyBuilder';
export * from './DecisionTree';
export * from './Picker';
import { BuilderFunction } from './Types';
/**
* A {@link BuilderFunction} implementation.
*
* Produces a string representation of the tree
* for testing and debug purposes.
*
* Only accepts `string` as the associated value type.
* Map your input collection before creating a {@link DecisionTree}
* if you want to use it with a different type -
* the decision on how to stringify the value is up to you.
*
* @param nodes - nodes from the root level of the decision tree.
* @returns the string representation of the tree.
*/
export declare const treeify: BuilderFunction<string, string>;
import { DecisionTreeNode, ValueContainer } from './Ast';
/**
* A function that turn a decision tree into a usable form.
*
* @typeParam V - the type of associated value.
*
* @typeParam R - return type for this builder
* (Consider using {@link Picker}.)
*/
export declare type BuilderFunction<V, R> = (nodes: DecisionTreeNode<V>[]) => R;
/**
* Recommended matcher function shape to implement
* in builders.
*
* The elements stack is represented as the arguments array.
*
* @typeParam L - the type of elements in a particular DOM AST.
* @typeParam V - the type of associated value.
*/
export declare type MatcherFunction<L, V> = (el: L, ...tail: L[]) => ValueContainer<V>[];