diff --git a/index.js b/index.js index b382033..bce534d 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,13 @@ /** * @typedef {import('unist').Position} Position * @typedef {import('unist').Node} Node - * @typedef {Record & {type: string, position?: Position|undefined}} NodeLike + * @typedef {import('./lib/types.js').SelectState} SelectState + * @typedef {Record & {type: string, position?: Position | undefined}} NodeLike */ import {any} from './lib/any.js' import {parse} from './lib/parse.js' +import {root} from './lib/util.js' /** * Check that the given `node` matches `selector`. @@ -17,13 +19,17 @@ import {parse} from './lib/parse.js' * * @param {string} selector * CSS selector, such as (`heading`, `link, linkReference`). - * @param {Node | NodeLike | undefined} [node] + * @param {Node | NodeLike | null | undefined} [node] * Node that might match `selector`. * @returns {boolean} * Whether `node` matches `selector`. */ export function matches(selector, node) { - return Boolean(any(parse(selector), node, {one: true, shallow: true, any})[0]) + const state = createState(node) + state.one = true + state.shallow = true + const result = any(parse(selector), node || undefined, state) + return result.length > 0 } /** @@ -33,7 +39,7 @@ export function matches(selector, node) { * * @param {string} selector * CSS selector, such as (`heading`, `link, linkReference`). - * @param {Node | NodeLike | undefined} [tree] + * @param {Node | NodeLike | null | undefined} [tree] * Tree to search. * @returns {Node | null} * First node in `tree` that matches `selector` or `null` if nothing is @@ -42,7 +48,11 @@ export function matches(selector, node) { * This could be `tree` itself. */ export function select(selector, tree) { - return any(parse(selector), tree, {one: true, any})[0] || null + const state = createState(tree) + state.one = true + const result = any(parse(selector), tree || undefined, state) + // To do next major: return `undefined`. + return result[0] || null } /** @@ -52,7 +62,7 @@ export function select(selector, tree) { * * @param {string} selector * CSS selector, such as (`heading`, `link, linkReference`). - * @param {Node | NodeLike | undefined} [tree] + * @param {Node | NodeLike | null | undefined} [tree] * Tree to search. * @returns {Array} * Nodes in `tree` that match `selector`. @@ -60,5 +70,26 @@ export function select(selector, tree) { * This could include `tree` itself. */ export function selectAll(selector, tree) { - return any(parse(selector), tree, {any}) + const state = createState(tree) + return any(parse(selector), tree || undefined, state) +} + +/** + * @param {Node | null | undefined} tree + * @returns {SelectState} + */ +function createState(tree) { + return { + any, + iterator: undefined, + scopeNodes: tree ? (root(tree) ? tree.children : [tree]) : [], + one: false, + shallow: false, + index: false, + found: false, + typeIndex: undefined, + nodeIndex: undefined, + typeCount: undefined, + nodeCount: undefined + } } diff --git a/lib/any.js b/lib/any.js index eb45886..662e487 100644 --- a/lib/any.js +++ b/lib/any.js @@ -22,10 +22,10 @@ const type = zwitch('type', { }) /** - * @param {Selectors|RuleSet|Rule} query - * @param {Node|undefined} node + * @param {Selectors | RuleSet | Rule} query + * @param {Node | undefined} node * @param {SelectState} state - * @returns {Array.} + * @returns {Array} */ export function any(query, node, state) { // @ts-expect-error: fine. @@ -73,7 +73,7 @@ function rule(query, tree, state) { query, tree, 0, - null, + undefined, configure(query, { any: state.any, iterator, @@ -124,7 +124,7 @@ function configure(query, state) { return state } -// Shouldn’t be invoked, all data is handled. +// Shouldn’t be called, all data is handled. /* c8 ignore next 6 */ /** * @param {{[x: string]: unknown, type: string}} query @@ -133,17 +133,17 @@ function unknownType(query) { throw new Error('Unknown type `' + query.type + '`') } -// Shouldn’t be invoked, parser gives correct data. +// Shouldn’t be called, parser gives correct data. /* c8 ignore next 3 */ function invalidType() { throw new Error('Invalid type') } /** - * @param {boolean|undefined} one + * @param {boolean | undefined} one */ function collector(one) { - /** @type {Array.} */ + /** @type {Array} */ const result = [] /** @type {boolean} */ let found @@ -155,7 +155,7 @@ function collector(one) { /** * Append nodes to array, filtering out duplicates. * - * @param {Node|Array.} node + * @param {Node | Array} node */ function collect(node) { let index = -1 diff --git a/lib/attribute.js b/lib/attribute.js index ec9f173..5a14c39 100644 --- a/lib/attribute.js +++ b/lib/attribute.js @@ -76,7 +76,7 @@ function containsArray(query, node) { // 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.` instead of `Array.`. + // into `Array` instead of `Array`. // type-coverage:ignore-next-line if (Array.isArray(value) && value.includes(query.value)) { return true @@ -135,7 +135,7 @@ function containsString(query, node) { return query.value && typeof value === 'string' && value.includes(query.value) } -// Shouldn’t be invoked, Parser throws an error instead. +// Shouldn’t be called, parser throws an error instead. /* c8 ignore next 6 */ /** * @param {{[x: string]: unknown, type: string}} query diff --git a/lib/nest.js b/lib/nest.js index 5ddf556..0cb406b 100644 --- a/lib/nest.js +++ b/lib/nest.js @@ -32,7 +32,7 @@ const handle = zwitch('nestingOperator', { /** @type {Handler} */ export const nest = handle -// Shouldn’t be invoked, parser gives correct data. +// Shouldn’t be called, parser gives correct data. /* c8 ignore next 6 */ /** * @param {{[x: string]: unknown, type: string}} query @@ -93,7 +93,7 @@ function child(query, node, _1, _2, state) { if (!parent(node)) return if (node.children.length === 0) return - new WalkIterator(query, node, state).each().done() + new WalkIterator(query, node, state).each(undefined, undefined).done() } /** @type {Handler} */ @@ -111,7 +111,7 @@ function adjacentSibling(query, _, index, parent, state) { new WalkIterator(query, parent, state) .prefillTypeIndex(0, ++index) .each(index, ++index) - .prefillTypeIndex(index) + .prefillTypeIndex(index, undefined) .done() } @@ -129,7 +129,7 @@ function generalSibling(query, _, index, parent, state) { new WalkIterator(query, parent, state) .prefillTypeIndex(0, ++index) - .each(index) + .each(index, undefined) .done() } @@ -148,15 +148,15 @@ class WalkIterator { this.parent = parent /** @type {SelectState} */ this.state = state - /** @type {TypeIndex|undefined} */ + /** @type {TypeIndex | undefined} */ this.typeIndex = state.index ? new TypeIndex() : undefined - /** @type {Array.} */ + /** @type {Array} */ this.delayed = [] } /** - * @param {number|null|undefined} [x] - * @param {number|null|undefined} [y] + * @param {number | undefined} x + * @param {number | undefined} y * @returns {this} */ prefillTypeIndex(x, y) { @@ -173,8 +173,8 @@ class WalkIterator { } /** - * @param {number|null|undefined} [x] - * @param {number|null|undefined} [y] + * @param {number | undefined} x + * @param {number | undefined} y * @returns {this} */ each(x, y) { @@ -246,13 +246,13 @@ class WalkIterator { } /** - * @param {number|null|undefined} [start] - * @param {number|null|undefined} [end] + * @param {number | undefined} start + * @param {number | undefined} end * @returns {[number, number]} */ defaults(start, end) { - if (start === null || start === undefined || start < 0) start = 0 - if (end === null || end === undefined || end > this.parent.children.length) + if (start === undefined || start < 0) start = 0 + if (end === undefined || end > this.parent.children.length) end = this.parent.children.length return [start, end] } @@ -260,7 +260,7 @@ class WalkIterator { class TypeIndex { constructor() { - /** @type {Object.} */ + /** @type {Record} */ this.counts = {} /** @type {number} */ this.nodes = 0 @@ -283,7 +283,7 @@ class TypeIndex { /** * @param {Node} node - * @returns {number|undefined} + * @returns {number | undefined} */ count(node) { return this.counts[node.type] diff --git a/lib/parse.js b/lib/parse.js index 95ec356..42cf111 100644 --- a/lib/parse.js +++ b/lib/parse.js @@ -70,7 +70,7 @@ function ruleSet(query) { function rule(query) { const pseudos = query.pseudos || [] let index = -1 - /** @type {RulePseudo|RulePseudoNth} */ + /** @type {RulePseudo | RulePseudoNth} */ let pseudo while (++index < pseudos.length) { diff --git a/lib/pseudo.js b/lib/pseudo.js index 16f2992..33b834d 100644 --- a/lib/pseudo.js +++ b/lib/pseudo.js @@ -79,8 +79,8 @@ pseudo.needsIndex = [ /** * @param {Rule} query * @param {Node} node - * @param {number|null} index - * @param {Parent|null} parent + * @param {number | undefined} index + * @param {Parent | undefined} parent * @param {SelectState} state * @returns {boolean} */ @@ -98,8 +98,8 @@ export function pseudo(query, node, index, parent, state) { /** * @param {RulePseudoSelector} query * @param {Node} node - * @param {number|null} _1 - * @param {Parent|null} _2 + * @param {number | undefined} _1 + * @param {Parent | undefined} _2 * @param {SelectState} state * @returns {boolean} */ @@ -121,8 +121,8 @@ function matches(query, node, _1, _2, state) { /** * @param {RulePseudoSelector} query * @param {Node} node - * @param {number|null} index - * @param {Parent|null} parent + * @param {number | undefined} index + * @param {Parent | undefined} parent * @param {SelectState} state * @returns {boolean} */ @@ -133,8 +133,8 @@ function not(query, node, index, parent, state) { /** * @param {RulePseudo} _1 * @param {Node} node - * @param {number|null} _2 - * @param {Parent|null} parent + * @param {number | undefined} _2 + * @param {Parent | undefined} parent * @returns {boolean} */ function root(_1, node, _2, parent) { @@ -144,8 +144,8 @@ function root(_1, node, _2, parent) { /** * @param {RulePseudo} _1 * @param {Node} node - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -169,8 +169,8 @@ function empty(_1, node) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -182,8 +182,8 @@ function firstChild(query, _1, _2, _3, state) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -198,8 +198,8 @@ function lastChild(query, _1, _2, _3, state) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -211,8 +211,8 @@ function onlyChild(query, _1, _2, _3, state) { /** * @param {RulePseudoNth} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -224,8 +224,8 @@ function nthChild(query, _1, _2, _3, state) { /** * @param {RulePseudoNth} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -241,8 +241,8 @@ function nthLastChild(query, _1, _2, _3, state) { /** * @param {RulePseudoNth} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -254,8 +254,8 @@ function nthOfType(query, _1, _2, _3, state) { /** * @param {RulePseudoNth} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -271,8 +271,8 @@ function nthLastOfType(query, _1, _2, _3, state) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -284,8 +284,8 @@ function firstOfType(query, _1, _2, _3, state) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -300,8 +300,8 @@ function lastOfType(query, _1, _2, _3, state) { /** * @param {RulePseudo} query * @param {Node} _1 - * @param {number|null} _2 - * @param {Parent|null} _3 + * @param {number | undefined} _2 + * @param {Parent | undefined} _3 * @param {SelectState} state * @returns {boolean} */ @@ -310,7 +310,7 @@ function onlyOfType(query, _1, _2, _3, state) { return state.typeCount === 1 } -// Shouldn’t be invoked, parser gives correct data. +// Shouldn’t be called, parser gives correct data. /* c8 ignore next 3 */ function invalidPseudo() { throw new Error('Invalid pseudo-selector') @@ -330,7 +330,7 @@ function unknownPseudo(query) { /** * @param {SelectState} state - * @param {RulePseudo|RulePseudoNth} query + * @param {RulePseudo | RulePseudoNth} query */ function assertDeep(state, query) { if (state.shallow) { @@ -341,8 +341,8 @@ function assertDeep(state, query) { /** * @param {RulePseudoSelector} query * @param {Node} node - * @param {number|null} _1 - * @param {Parent|null} _2 + * @param {number | undefined} _1 + * @param {Parent | undefined} _2 * @param {SelectState} state * @returns {boolean} */ diff --git a/lib/test.js b/lib/test.js index 18f6938..d239c22 100644 --- a/lib/test.js +++ b/lib/test.js @@ -12,8 +12,8 @@ import {pseudo} from './pseudo.js' /** * @param {Rule} query * @param {Node} node - * @param {number|null} index - * @param {Parent|null} parent + * @param {number | undefined} index + * @param {Parent | undefined} parent * @param {SelectState} state * @returns {boolean} */ diff --git a/lib/types.js b/lib/types.js index 7ac74e7..eecd883 100644 --- a/lib/types.js +++ b/lib/types.js @@ -8,20 +8,27 @@ * @typedef {import('css-selector-parser').Rule} Rule * @typedef {import('css-selector-parser').RulePseudo} RulePseudo * @typedef {import('css-selector-parser').AttrValueType} AttrValueType - * @typedef {Selector|Rule|RulePseudo} Query + * @typedef {Selector | Rule | RulePseudo} Query * * @typedef RuleAttr * Fix for types from `css-selector-parser`. * @property {string} name - * @property {string} [operator] - * @property {AttrValueType} [valueType] - * @property {string} [value] + * 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 - * @property {Selector} value + * Set to `'selector'`, because `value` is a compiled selector. + * @property {Selectors | RuleSet} value + * Selector. * * @typedef RulePseudoNth * Overwrite to compile nth-checks once. @@ -30,20 +37,20 @@ * @property {(index: number) => boolean} value * * @typedef SelectState - * @property {(query: Selectors|RuleSet|Rule, node: Node|undefined, state: SelectState) => Node[]} any - * @property {SelectIterator|null|undefined} [iterator] - * @property {Array} [scopeNodes] - * @property {boolean} [one=false] - * @property {boolean} [shallow=false] - * @property {boolean} [index=false] - * @property {boolean} [found=false] - * @property {number} [typeIndex] + * @property {(query: Selectors | RuleSet | Rule, node: Node | undefined, state: SelectState) => Array} any + * @property {SelectIterator | undefined} iterator + * @property {Array} scopeNodes + * @property {boolean} one + * @property {boolean} shallow + * @property {boolean} index + * @property {boolean} found + * @property {number | undefined} typeIndex * Track siblings: this current node has `n` nodes with its type before it. - * @property {number} [nodeIndex] + * @property {number | undefined} nodeIndex * Track siblings: this current node has `n` nodes before it. - * @property {number} [typeCount] + * @property {number | undefined} typeCount * Track siblings: there are `n` siblings with this node’s type. - * @property {number} [nodeCount] + * @property {number | undefined} nodeCount * Track siblings: there are `n` siblings. */ @@ -52,13 +59,13 @@ * @param {Rule} query * @param {Node} node * @param {number} index - * @param {Parent|null} parent + * @param {Parent | undefined} parent * @param {SelectState} state */ /** * @typedef {( - * ((query: Rule, node: Node, index: number|null, parent: Parent|null, state: SelectState) => void) + * ((query: Rule, node: Node, index: number | undefined, parent: Parent | undefined, state: SelectState) => void) * )} Handler */