Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

unist-util-select

Package Overview
Dependencies
Maintainers
2
Versions
21
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unist-util-select - npm Package Compare versions

Comparing version 4.0.3 to 5.0.0

4

index.d.ts

@@ -29,3 +29,3 @@ /**

* Tree to search.
* @returns {Node | null}
* @returns {Node | undefined}
* First node in `tree` that matches `selector` or `null` if nothing is

@@ -39,3 +39,3 @@ * found.

tree?: Node | NodeLike | null | undefined
): Node | null
): Node | undefined
/**

@@ -42,0 +42,0 @@ * Select all nodes that match `selector` in the given `tree`.

@@ -5,8 +5,11 @@ /**

* @typedef {import('./lib/types.js').SelectState} SelectState
*/
/**
* @typedef {Record<string, unknown> & {type: string, position?: Position | undefined}} NodeLike
*/
import {queryToSelectors, walk} from './lib/walk.js'
import {parse} from './lib/parse.js'
import {parent} from './lib/util.js'
import {walk} from './lib/walk.js'

@@ -45,3 +48,3 @@ /**

* Tree to search.
* @returns {Node | null}
* @returns {Node | undefined}
* First node in `tree` that matches `selector` or `null` if nothing is

@@ -56,4 +59,3 @@ * found.

walk(state, tree || undefined)
// To do next major: return `undefined`.
return state.results[0] || null
return state.results[0]
}

@@ -87,2 +89,3 @@

* @returns {SelectState}
* State.
*/

@@ -92,3 +95,3 @@ function createState(selector, tree) {

// State of the query.
rootQuery: queryToSelectors(parse(selector)),
rootQuery: parse(selector),
results: [],

@@ -95,0 +98,0 @@ scopeNodes: tree

/**
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node
* @returns {boolean}
*/
export function attribute(query: Rule, node: Node): boolean
export type Rule = import('./types.js').Rule
export type RuleAttr = import('./types.js').RuleAttr
export function attribute(query: AstRule, node: Node): boolean
export type AstAttribute = import('css-selector-parser').AstAttribute
export type AstRule = import('css-selector-parser').AstRule
export type Node = import('./types.js').Node
/**
* @typedef {import('./types.js').Rule} Rule
* @typedef {import('./types.js').RuleAttr} RuleAttr
* @typedef {import('css-selector-parser').AstAttribute} AstAttribute
* @typedef {import('css-selector-parser').AstRule} AstRule
* @typedef {import('./types.js').Node} Node
*/
import {unreachable} from 'devlop'
import {zwitch} from 'zwitch'
import {indexable} from './util.js'
/** @type {(query: RuleAttr, node: Node) => boolean} */
/** @type {(query: AstAttribute, node: Node) => boolean} */
const handle = zwitch('operator', {

@@ -24,3 +26,3 @@ unknown: unknownOperator,

/**
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node

@@ -32,4 +34,6 @@ * @returns {boolean}

while (++index < query.attrs.length) {
if (!handle(query.attrs[index], node)) return false
if (query.attributes) {
while (++index < query.attributes.length) {
if (!handle(query.attributes[index], node)) return false
}
}

@@ -45,3 +49,3 @@

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -51,3 +55,3 @@ * @returns {boolean}

function exists(query, node) {
// @ts-expect-error: Looks like a record.
indexable(node)
return node[query.name] !== null && node[query.name] !== undefined

@@ -61,3 +65,3 @@ }

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -67,4 +71,5 @@ * @returns {boolean}

function exact(query, node) {
// @ts-expect-error: Looks like a record.
return exists(query, node) && String(node[query.name]) === query.value
const queryValue = attributeValue(query)
indexable(node)
return exists(query, node) && String(node[query.name]) === queryValue
}

@@ -80,3 +85,3 @@

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -86,4 +91,3 @@ * @returns {boolean}

function containsArray(query, node) {
/** @type {unknown} */
// @ts-expect-error: Looks like a record.
indexable(node)
const value = node[query.name]

@@ -93,7 +97,9 @@

// If this is an array, and the query is contained in it, return true.
const queryValue = attributeValue(query)
// If this is an array, and the query is contained in it, return `true`.
// Coverage comment in place because TS turns `Array.isArray(unknown)`
// into `Array<any>` instead of `Array<unknown>`.
// type-coverage:ignore-next-line
if (Array.isArray(value) && value.includes(query.value)) {
if (Array.isArray(value) && value.includes(queryValue)) {
return true

@@ -103,3 +109,3 @@ }

// For all other values, return whether this is an exact match.
return String(value) === query.value
return String(value) === queryValue
}

@@ -112,3 +118,3 @@

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -118,5 +124,5 @@ * @returns {boolean}

function begins(query, node) {
/** @type {unknown} */
// @ts-expect-error: Looks like a record.
indexable(node)
const value = node[query.name]
const queryValue = attributeValue(query)

@@ -126,3 +132,3 @@ return Boolean(

typeof value === 'string' &&
value.slice(0, query.value.length) === query.value
value.slice(0, queryValue.length) === queryValue
)

@@ -136,3 +142,3 @@ }

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -142,5 +148,5 @@ * @returns {boolean}

function ends(query, node) {
/** @type {unknown} */
// @ts-expect-error: Looks like a record.
indexable(node)
const value = node[query.name]
const queryValue = attributeValue(query)

@@ -150,3 +156,3 @@ return Boolean(

typeof value === 'string' &&
value.slice(-query.value.length) === query.value
value.slice(-queryValue.length) === queryValue
)

@@ -160,3 +166,3 @@ }

*
* @param {RuleAttr} query
* @param {AstAttribute} query
* @param {Node} node

@@ -166,7 +172,8 @@ * @returns {boolean}

function containsString(query, node) {
/** @type {unknown} */
// @ts-expect-error: Looks like a record.
indexable(node)
const value = node[query.name]
const queryValue = attributeValue(query)
return Boolean(
query.value && typeof value === 'string' && value.includes(query.value)
typeof value === 'string' && queryValue && value.includes(queryValue)
)

@@ -185,1 +192,17 @@ }

}
/**
* @param {AstAttribute} query
* @returns {string}
*/
function attributeValue(query) {
const queryValue = query.value
/* c8 ignore next 4 -- never happens with our config */
if (!queryValue) unreachable('Attribute values should be defined')
if (queryValue.type === 'Substitution') {
unreachable('Substitutions are not enabled')
}
return queryValue.value
}
/**
* @param {string} selector
* @returns {Selectors | RuleSet | null}
* @returns {AstSelector}
*/
export function parse(selector: string): Selectors | RuleSet | null
export type Selectors = import('./types.js').Selectors
export type RuleSet = import('./types.js').RuleSet
export function parse(selector: string): AstSelector
export type AstSelector = import('css-selector-parser').AstSelector
/**
* @typedef {import('./types.js').Selectors} Selectors
* @typedef {import('./types.js').RuleSet} RuleSet
* @typedef {import('css-selector-parser').AstSelector} AstSelector
*/
import {CssSelectorParser} from 'css-selector-parser'
import {createParser} from 'css-selector-parser'
const parser = new CssSelectorParser()
const cssSelectorParse = createParser({syntax: 'selectors-4'})
parser.registerAttrEqualityMods('~', '^', '$', '*')
parser.registerSelectorPseudos('any', 'matches', 'not', 'has')
parser.registerNestingOperators('>', '+', '~')
/**
* @param {string} selector
* @returns {Selectors | RuleSet | null}
* @returns {AstSelector}
*/

@@ -23,3 +18,3 @@ export function parse(selector) {

return parser.parse(selector)
return cssSelectorParse(selector)
}
/**
* Check whether an node matches pseudo selectors.
*
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node

@@ -12,3 +12,3 @@ * @param {number | undefined} index

export function pseudo(
query: Rule,
query: AstRule,
node: Node,

@@ -20,9 +20,8 @@ index: number | undefined,

export namespace pseudo {
const needsIndex: string[]
let needsIndex: string[]
}
export type Rule = import('./types.js').Rule
export type RulePseudo = import('./types.js').RulePseudo
export type RulePseudoSelector = import('./types.js').RulePseudoSelector
export type Parent = import('./types.js').Parent
export type AstRule = import('css-selector-parser').AstRule
export type AstPseudoClass = import('css-selector-parser').AstPseudoClass
export type Node = import('unist').Node
export type Parent = import('unist').Parent
export type SelectState = import('./types.js').SelectState
export type Node = import('./types.js').Node
/**
* @typedef {import('./types.js').Rule} Rule
* @typedef {import('./types.js').RulePseudo} RulePseudo
* @typedef {import('./types.js').RulePseudoSelector} RulePseudoSelector
* @typedef {import('./types.js').Parent} Parent
* @typedef {import('css-selector-parser').AstRule} AstRule
* @typedef {import('css-selector-parser').AstPseudoClass} AstPseudoClass
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('./types.js').SelectState} SelectState
* @typedef {import('./types.js').Node} Node
*/
import {unreachable} from 'devlop'
import fauxEsmNthCheck from 'nth-check'
import {zwitch} from 'zwitch'
import {parent} from './util.js'
import {queryToSelectors, walk} from './walk.js'
import {walk} from './walk.js'
/** @type {import('nth-check').default} */
// @ts-expect-error
// @ts-expect-error: `nth-check` types are wrong.
const nthCheck = fauxEsmNthCheck.default || fauxEsmNthCheck
/** @type {(rule: Rule | RulePseudo, node: Node, index: number | undefined, parent: Parent | undefined, state: SelectState) => boolean} */
/** @type {(rule: AstPseudoClass, node: Node, index: number | undefined, parent: Parent | undefined, state: SelectState) => boolean} */
const handle = zwitch('name', {
// @ts-expect-error: always known.
unknown: unknownPseudo,
invalid: invalidPseudo,
handlers: {
any: matches,
is,
blank: empty,

@@ -32,3 +33,2 @@ empty,

'last-of-type': lastOfType,
matches,
not,

@@ -52,3 +52,3 @@ 'nth-child': nthChild,

'last-of-type',
'matches',
'is',
'not',

@@ -66,3 +66,3 @@ 'nth-child',

*
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node

@@ -75,7 +75,9 @@ * @param {number | undefined} index

export function pseudo(query, node, index, parent, state) {
const pseudos = query.pseudos
let offset = -1
while (++offset < pseudos.length) {
if (!handle(pseudos[offset], node, index, parent, state)) return false
if (query.pseudoClasses) {
while (++offset < query.pseudoClasses.length) {
if (!handle(query.pseudoClasses[offset], node, index, parent, state))
return false
}
}

@@ -89,3 +91,3 @@

*
* @param {RulePseudo} _1
* @param {AstPseudoClass} _1
* @param {Node} node

@@ -101,3 +103,3 @@ * @returns {boolean}

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -117,3 +119,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -131,3 +133,3 @@ * @param {number | undefined} _2

/**
* @param {RulePseudoSelector} query
* @param {AstPseudoClass} query
* @param {Node} node

@@ -140,2 +142,9 @@ * @param {number | undefined} _1

function has(query, node, _1, _2, state) {
const argument = query.argument
/* c8 ignore next 3 -- never happens with our config */
if (!argument || argument.type !== 'Selector') {
unreachable('`:has` has selectors')
}
const fragment = {type: 'root', children: parent(node) ? node.children : []}

@@ -153,3 +162,3 @@ /** @type {SelectState} */

results: [],
rootQuery: queryToSelectors(query.value)
rootQuery: argument
}

@@ -165,3 +174,3 @@

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -184,3 +193,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -201,5 +210,5 @@ * @param {number | undefined} _2

/**
* Check whether a node `:matches` further selectors.
* Check whether a node `:is` further selectors.
*
* @param {RulePseudoSelector} query
* @param {AstPseudoClass} query
* @param {Node} node

@@ -211,3 +220,10 @@ * @param {number | undefined} _1

*/
function matches(query, node, _1, _2, state) {
function is(query, node, _1, _2, state) {
const argument = query.argument
/* c8 ignore next 3 -- never happens with our config */
if (!argument || argument.type !== 'Selector') {
unreachable('`:is` has selectors')
}
/** @type {SelectState} */

@@ -224,3 +240,3 @@ const childState = {

results: [],
rootQuery: queryToSelectors(query.value)
rootQuery: argument
}

@@ -236,3 +252,3 @@

*
* @param {RulePseudoSelector} query
* @param {AstPseudoClass} query
* @param {Node} node

@@ -245,3 +261,3 @@ * @param {number | undefined} index

function not(query, node, index, parent, state) {
return !matches(query, node, index, parent, state)
return !is(query, node, index, parent, state)
}

@@ -252,3 +268,3 @@

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -269,3 +285,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -290,3 +306,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -311,3 +327,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -328,3 +344,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -344,3 +360,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @param {Node} _1

@@ -360,3 +376,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} _1
* @param {AstPseudoClass} _1
* @param {Node} node

@@ -374,3 +390,3 @@ * @param {number | undefined} _2

*
* @param {RulePseudo} _1
* @param {AstPseudoClass} _1
* @param {Node} node

@@ -393,13 +409,7 @@ * @param {number | undefined} _2

/**
* @param {unknown} query
* @param {AstPseudoClass} query
* @returns {never}
*/
function unknownPseudo(query) {
// @ts-expect-error: indexable.
if (query.name) {
// @ts-expect-error: indexable.
throw new Error('Unknown pseudo-selector `' + query.name + '`')
}
throw new Error('Unexpected pseudo-element or empty pseudo-class')
throw new Error('Unknown pseudo-selector `' + query.name + '`')
}

@@ -409,3 +419,3 @@

* @param {SelectState} state
* @param {RulePseudo} query
* @param {AstPseudoClass} query
*/

@@ -419,3 +429,3 @@ function assertDeep(state, query) {

/**
* @param {RulePseudo} query
* @param {AstPseudoClass} query
* @returns {(value: number) => boolean}

@@ -429,4 +439,10 @@ */

if (!fn) {
// @ts-expect-error: always string.
fn = nthCheck(query.value)
const value = query.argument
/* c8 ignore next 3 -- never happens with our config */
if (!value || value.type !== 'Formula') {
unreachable('`:nth` has a formula')
}
fn = nthCheck(value.a + 'n+' + value.b)
// @ts-expect-error: cache.

@@ -433,0 +449,0 @@ query._cachedFn = fn

/**
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node

@@ -10,3 +10,3 @@ * @param {number | undefined} index

export function test(
query: Rule,
query: AstRule,
node: Node,

@@ -17,5 +17,5 @@ index: number | undefined,

): boolean
export type Rule = import('./types.js').Rule
export type Node = import('./types.js').Node
export type Parent = import('./types.js').Parent
export type AstRule = import('css-selector-parser').AstRule
export type Node = import('unist').Node
export type Parent = import('unist').Parent
export type SelectState = import('./types.js').SelectState
/**
* @typedef {import('./types.js').Rule} Rule
* @typedef {import('./types.js').Node} Node
* @typedef {import('./types.js').Parent} Parent
* @typedef {import('css-selector-parser').AstRule} AstRule
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('./types.js').SelectState} SelectState

@@ -9,7 +9,6 @@ */

import {attribute} from './attribute.js'
import {name} from './name.js'
import {pseudo} from './pseudo.js'
/**
* @param {Rule} query
* @param {AstRule} query
* @param {Node} node

@@ -22,11 +21,16 @@ * @param {number | undefined} index

export function test(query, node, index, parent, state) {
if (query.id) throw new Error('Invalid selector: id')
if (query.ids) throw new Error('Invalid selector: id')
if (query.classNames) throw new Error('Invalid selector: class')
if (query.pseudoElement) {
throw new Error('Invalid selector: `::' + query.pseudoElement + '`')
}
return Boolean(
node &&
(!query.tagName || name(query, node)) &&
(!query.attrs || attribute(query, node)) &&
(!query.pseudos || pseudo(query, node, index, parent, state))
(!query.tag ||
query.tag.type === 'WildcardTag' ||
query.tag.name === node.type) &&
(!query.attributes || attribute(query, node)) &&
(!query.pseudoClasses || pseudo(query, node, index, parent, state))
)
}

@@ -1,68 +0,4 @@

/**
* Any node.
*/
export type AstSelector = import('css-selector-parser').AstSelector
export type Node = import('unist').Node
/**
* Node with children.
*/
export type Parent = import('unist').Parent
/**
* Multiple selectors.
*/
export type Selectors = import('css-selector-parser').Selectors
/**
* One rule.
*/
export type Rule = import('css-selector-parser').Rule
/**
* Multiple rules.
*/
export type RuleSet = import('css-selector-parser').RuleSet
/**
* Pseudo rule.
*/
export type RulePseudo = import('css-selector-parser').RulePseudo
/**
* Attribute value type.
*/
export type AttrValueType = import('css-selector-parser').AttrValueType
/**
* Fix for types from `css-selector-parser`.
*/
export type RuleAttr = {
/**
* Attribute name.
*/
name: string
/**
* Operator, such as `'|='`, missing when for example `[x]`.
*/
operator?: string | undefined
/**
* Attribute value type.
*/
valueType?: AttrValueType | undefined
/**
* Attribute value.
*/
value?: string | undefined
}
/**
* More specific type for registered selector pseudos.
*/
export type RulePseudoSelector = {
/**
* Name of pseudo, such as `'matches'`.
*/
name: string
/**
* Set to `'selector'`, because `value` is a compiled selector.
*/
valueType: 'selector'
/**
* Selector.
*/
value: Selectors | RuleSet
}
/**
* Current state.

@@ -74,3 +10,3 @@ */

*/
rootQuery: Selectors
rootQuery: AstSelector
/**

@@ -77,0 +13,0 @@ * Matches.

/**
* @typedef {import('css-selector-parser').AstSelector} AstSelector
* @typedef {import('unist').Node} Node
* Any node.
* @typedef {import('unist').Parent} Parent
* Node with children.
*
* @typedef {import('css-selector-parser').Selectors} Selectors
* Multiple selectors.
* @typedef {import('css-selector-parser').Rule} Rule
* One rule.
* @typedef {import('css-selector-parser').RuleSet} RuleSet
* Multiple rules.
* @typedef {import('css-selector-parser').RulePseudo} RulePseudo
* Pseudo rule.
* @typedef {import('css-selector-parser').AttrValueType} AttrValueType
* Attribute value type.
*
* @typedef RuleAttr
* Fix for types from `css-selector-parser`.
* @property {string} name
* Attribute name.
* @property {string | undefined} [operator]
* Operator, such as `'|='`, missing when for example `[x]`.
* @property {AttrValueType | undefined} [valueType]
* Attribute value type.
* @property {string | undefined} [value]
* Attribute value.
*
* @typedef RulePseudoSelector
* More specific type for registered selector pseudos.
* @property {string} name
* Name of pseudo, such as `'matches'`.
* @property {'selector'} valueType
* Set to `'selector'`, because `value` is a compiled selector.
* @property {Selectors | RuleSet} value
* Selector.
*
*/
/**
* @typedef SelectState
* Current state.
* @property {Selectors} rootQuery
* @property {AstSelector} rootQuery
* Original root selectors.

@@ -42,0 +11,0 @@ * @property {Array<Node>} results

/**
* @typedef {import('./types.js').Node} Node
* @typedef {import('./types.js').Parent} Parent
* TypeScript helper to check if something is indexable (any object is
* indexable in JavaScript).
*
* @param {unknown} value
* Thing to check.
* @returns {asserts value is Record<string, unknown>}
* Nothing.
* @throws {Error}
* When `value` is not an object.
*/
export function indexable(
value: unknown
): asserts value is Record<string, unknown>
/**

@@ -9,9 +19,4 @@ * @param {Node} node

*/
export function parent(
node: Node
): node is import('unist').Parent<
import('unist').Node<import('unist').Data>,
import('unist').Data
>
export type Node = import('./types.js').Node
export type Parent = import('./types.js').Parent
export function parent(node: Node): node is import('unist').Parent
export type Node = import('unist').Node
export type Parent = import('unist').Parent
/**
* @typedef {import('./types.js').Node} Node
* @typedef {import('./types.js').Parent} Parent
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
*/
import {unreachable} from 'devlop'
/**
* TypeScript helper to check if something is indexable (any object is
* indexable in JavaScript).
*
* @param {unknown} value
* Thing to check.
* @returns {asserts value is Record<string, unknown>}
* Nothing.
* @throws {Error}
* When `value` is not an object.
*/
export function indexable(value) {
// Always called when something is an object, this is just for TS.
/* c8 ignore next 3 */
if (!value || typeof value !== 'object') {
unreachable('Expected object')
}
}
/**
* @param {Node} node

@@ -11,4 +32,4 @@ * @returns {node is Parent}

export function parent(node) {
// @ts-expect-error: looks like a record.
indexable(node)
return Array.isArray(node.children)
}
/**
* Turn a query into a uniform object.
*
* @param {Selectors | RuleSet | null} query
* @returns {Selectors}
*/
export function queryToSelectors(query: Selectors | RuleSet | null): Selectors
/**
* Walk a tree.

@@ -15,7 +8,6 @@ *

export function walk(state: SelectState, tree: Node | undefined): void
export type Node = import('./types.js').Node
export type Parent = import('./types.js').Parent
export type RuleSet = import('./types.js').RuleSet
export type AstRule = import('css-selector-parser').AstRule
export type Node = import('unist').Node
export type Parent = import('unist').Parent
export type SelectState = import('./types.js').SelectState
export type Selectors = import('./types.js').Selectors
/**

@@ -28,15 +20,15 @@ * Rule sets by nesting.

*/
descendant: Array<RuleSet> | undefined
descendant: Array<AstRule> | undefined
/**
* `a > b`
*/
directChild: Array<RuleSet> | undefined
directChild: Array<AstRule> | undefined
/**
* `a + b`
*/
adjacentSibling: Array<RuleSet> | undefined
adjacentSibling: Array<AstRule> | undefined
/**
* `a ~ b`
*/
generalSibling: Array<RuleSet> | undefined
generalSibling: Array<AstRule> | undefined
}

@@ -43,0 +35,0 @@ /**

/**
* @typedef {import('./types.js').Node} Node
* @typedef {import('./types.js').Parent} Parent
* @typedef {import('./types.js').RuleSet} RuleSet
* @typedef {import('css-selector-parser').AstRule} AstRule
* @typedef {import('unist').Node} Node
* @typedef {import('unist').Parent} Parent
* @typedef {import('./types.js').SelectState} SelectState
* @typedef {import('./types.js').Selectors} Selectors
*
* @typedef Nest
* Rule sets by nesting.
* @property {Array<RuleSet> | undefined} descendant
* @property {Array<AstRule> | undefined} descendant
* `a b`
* @property {Array<RuleSet> | undefined} directChild
* @property {Array<AstRule> | undefined} directChild
* `a > b`
* @property {Array<RuleSet> | undefined} adjacentSibling
* @property {Array<AstRule> | undefined} adjacentSibling
* `a + b`
* @property {Array<RuleSet> | undefined} generalSibling
* @property {Array<AstRule> | undefined} generalSibling
* `a ~ b`

@@ -34,20 +33,2 @@ *

/**
* Turn a query into a uniform object.
*
* @param {Selectors | RuleSet | null} query
* @returns {Selectors}
*/
export function queryToSelectors(query) {
if (query === null) {
return {type: 'selectors', selectors: []}
}
if (query.type === 'ruleSet') {
return {type: 'selectors', selectors: [query]}
}
return query
}
/**
* Walk a tree.

@@ -60,3 +41,3 @@ *

if (tree) {
one(state, [], tree, undefined, undefined)
one(state, [], tree, undefined, undefined, tree)
}

@@ -69,9 +50,10 @@ }

* @param {SelectState} state
* @param {Array<RuleSet>} currentRules
* @param {Array<AstRule>} currentRules
* @param {Node} node
* @param {number | undefined} index
* @param {Parent | undefined} parentNode
* @param {Node} tree
* @returns {Nest}
*/
function one(state, currentRules, node, index, parentNode) {
function one(state, currentRules, node, index, parentNode, tree) {
/** @type {Nest} */

@@ -85,6 +67,19 @@ let nestResult = {

let rootRules = state.rootQuery.rules
// Remove direct child rules if this is the root.
// This only happens for a `:has()` rule, which can be like
// `a:has(> b)`.
if (parentNode && parentNode !== tree) {
rootRules = state.rootQuery.rules.filter(
(d) =>
d.combinator === undefined ||
(d.combinator === '>' && parentNode === tree)
)
}
nestResult = applySelectors(
state,
// Try the root rules for this node too.
combine(currentRules, state.rootQuery.selectors),
combine(currentRules, rootRules),
node,

@@ -98,3 +93,3 @@ index,

if (parent(node) && !state.shallow && !(state.one && state.found)) {
all(state, nestResult, node)
all(state, nestResult, node, tree)
}

@@ -111,7 +106,8 @@

* @param {Parent} node
* @returns {void}
* @param {Node} tree
* @returns {undefined}
*/
function all(state, nest, node) {
function all(state, nest, node, tree) {
const fromParent = combine(nest.descendant, nest.directChild)
/** @type {Array<RuleSet> | undefined} */
/** @type {Array<AstRule> | undefined} */
let fromSibling

@@ -149,3 +145,3 @@ let index = -1

const forSibling = combine(fromParent, fromSibling)
const nest = one(state, forSibling, node.children[index], index, node)
const nest = one(state, forSibling, node.children[index], index, node, tree)
fromSibling = combine(nest.generalSibling, nest.adjacentSibling)

@@ -167,3 +163,3 @@

* Current state.
* @param {Array<RuleSet>} rules
* @param {Array<AstRule>} rules
* Rules to apply.

@@ -190,3 +186,3 @@ * @param {Node} node

while (++selectorIndex < rules.length) {
const ruleSet = rules[selectorIndex]
const rule = rules[selectorIndex]

@@ -201,3 +197,3 @@ // We found one thing, and one is enough.

// Might get quite complex though.
if (state.shallow && ruleSet.rule.rule) {
if (state.shallow && rule.nestedRule) {
throw new Error('Expected selector without nesting')

@@ -207,19 +203,17 @@ }

// If this rule matches:
if (test(ruleSet.rule, node, index, parent, state)) {
const nest = ruleSet.rule.rule
if (test(rule, node, index, parent, state)) {
const nest = rule.nestedRule
// Are there more?
if (nest) {
/** @type {RuleSet} */
const rule = {type: 'ruleSet', rule: nest}
/** @type {keyof Nest} */
const label =
nest.nestingOperator === '+'
nest.combinator === '+'
? 'adjacentSibling'
: nest.nestingOperator === '~'
: nest.combinator === '~'
? 'generalSibling'
: nest.nestingOperator === '>'
: nest.combinator === '>'
? 'directChild'
: 'descendant'
add(nestResult, label, rule)
add(nestResult, label, nest)
} else {

@@ -236,10 +230,10 @@ // We have a match!

// Descendant.
if (ruleSet.rule.nestingOperator === null) {
add(nestResult, 'descendant', ruleSet)
if (rule.combinator === undefined) {
add(nestResult, 'descendant', rule)
}
// Adjacent.
else if (ruleSet.rule.nestingOperator === '~') {
add(nestResult, 'generalSibling', ruleSet)
else if (rule.combinator === '~') {
add(nestResult, 'generalSibling', rule)
}
// Drop top-level nesting (`undefined`), direct child (`>`), adjacent sibling (`+`).
// Drop direct child (`>`), adjacent sibling (`+`).
}

@@ -255,5 +249,5 @@

*
* @param {Array<RuleSet> | undefined} left
* @param {Array<RuleSet> | undefined} right
* @returns {Array<RuleSet>}
* @param {Array<AstRule> | undefined} left
* @param {Array<AstRule> | undefined} right
* @returns {Array<AstRule>}
*/

@@ -275,3 +269,3 @@ function combine(left, right) {

* @param {keyof Nest} field
* @param {RuleSet} rule
* @param {AstRule} rule
*/

@@ -294,3 +288,3 @@ function add(nest, field, rule) {

* Node.
* @returns {void}
* @returns {undefined}
* Nothing.

@@ -297,0 +291,0 @@ */

{
"name": "unist-util-select",
"version": "4.0.3",
"version": "5.0.0",
"description": "unist utility to select nodes with CSS-like selectors",

@@ -43,4 +43,3 @@ "license": "MIT",

"type": "module",
"main": "index.js",
"types": "index.d.ts",
"exports": "./index.js",
"files": [

@@ -52,4 +51,5 @@ "lib/",

"dependencies": {
"@types/unist": "^2.0.0",
"css-selector-parser": "^1.0.0",
"@types/unist": "^3.0.0",
"css-selector-parser": "^2.0.0",
"devlop": "^1.1.0",
"nth-check": "^2.0.0",

@@ -59,4 +59,4 @@ "zwitch": "^2.0.0"

"devDependencies": {
"@types/node": "^18.0.0",
"c8": "^7.0.0",
"@types/node": "^20.0.0",
"c8": "^8.0.0",
"prettier": "^2.0.0",

@@ -66,5 +66,5 @@ "remark-cli": "^11.0.0",

"type-coverage": "^2.0.0",
"typescript": "^4.0.0",
"typescript": "^5.0.0",
"unist-builder": "^3.0.0",
"xo": "^0.53.0"
"xo": "^0.54.0"
},

@@ -76,18 +76,25 @@ "scripts": {

"test-api": "node --conditions development test/index.js",
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
"test-coverage": "c8 --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"
},
"prettier": {
"tabWidth": 2,
"useTabs": false,
"singleQuote": true,
"bracketSpacing": false,
"semi": false,
"trailingComma": "none"
"singleQuote": true,
"tabWidth": 2,
"trailingComma": "none",
"useTabs": false
},
"remarkConfig": {
"plugins": [
"remark-preset-wooorm"
]
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"ignoreCatch": true,
"strict": true
},
"xo": {
"prettier": true,
"rules": {
"max-params": "off"
},
"overrides": [

@@ -99,17 +106,13 @@ {

"rules": {
"no-await-in-loop": 0
"import/no-unassigned-import": "off",
"max-nested-callbacks": "off",
"no-await-in-loop": "off"
}
}
]
},
"remarkConfig": {
"plugins": [
"preset-wooorm"
]
},
"typeCoverage": {
"atLeast": 100,
"detail": true,
"strict": true
],
"rules": {
"max-params": "off"
},
"prettier": true
}
}

@@ -57,3 +57,3 @@ # unist-util-select

This package is [ESM only][esm].
In Node.js (version 14.14+, 16.0+), install with [npm][]:
In Node.js (version 16+), install with [npm][]:

@@ -67,3 +67,3 @@ ```sh

```js
import {matches, select, selectAll} from "https://esm.sh/unist-util-select@4"
import {matches, select, selectAll} from "https://esm.sh/unist-util-select@5"
```

@@ -75,3 +75,3 @@

<script type="module">
import {matches, select, selectAll} from "https://esm.sh/unist-util-select@4?bundle"
import {matches, select, selectAll} from "https://esm.sh/unist-util-select@5?bundle"
</script>

@@ -96,3 +96,3 @@ ```

matches('blockquote, list', tree) // => true
console.log(matches('blockquote, list', tree)) // => true

@@ -108,3 +108,4 @@ console.log(select('code ~ :nth-child(even)', tree))

This package exports the identifiers `matches`, `select`, and `selectAll`.
This package exports the identifiers [`matches`][api-matches],
[`select`][api-select], and [`selectAll`][api-select-all].
There is no default export.

@@ -157,3 +158,3 @@

First node in `tree` that matches `selector` or `null` if nothing is found.
First node in `tree` that matches `selector` or `undefined` if nothing is found.

@@ -255,6 +256,4 @@ This could be `tree` itself.

if there’s an array on the tree, otherwise same as attribute equality)
* [x] `:any()` (functional pseudo-class, use `:matches` instead)
* [x] `:has()` (functional pseudo-class)
Relative selectors (`:has(> img)`) are not supported, but `:scope` is
* [x] `:matches()` (functional pseudo-class)
* [x] `:is()` (functional pseudo-class)
* [x] `:has()` (functional pseudo-class; also supports `a:has(> b)`)
* [x] `:not()` (functional pseudo-class)

@@ -281,2 +280,3 @@ * [x] `:blank` (pseudo-class, blank and empty are the same: a parent without

* \* — not supported in `matches`
* `:any()` and `:matches()` are renamed to `:is()` in CSS

@@ -290,7 +290,10 @@ ## Types

Projects maintained by the unified collective are compatible with all maintained
Projects maintained by the unified collective are compatible with maintained
versions of Node.js.
As of now, that is Node.js 14.14+ and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
When we cut a new major release, we drop support for unmaintained versions of
Node.
This means we try to keep the current release line, `unist-util-select@^5`,
compatible with Node.js 16.
## Related

@@ -335,5 +338,5 @@

[size-badge]: https://img.shields.io/bundlephobia/minzip/unist-util-select.svg
[size-badge]: https://img.shields.io/badge/dynamic/json?label=minzipped%20size&query=$.size.compressedSize&url=https://deno.bundlejs.com/?q=unist-util-select
[size]: https://bundlephobia.com/result?p=unist-util-select
[size]: https://bundlejs.com/?q=unist-util-select

@@ -379,1 +382,7 @@ [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg

[hast-util-select]: https://github.com/syntax-tree/hast-util-select
[api-matches]: #matchesselector-node
[api-select]: #selectselector-tree
[api-select-all]: #selectallselector-tree
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc