Skip to content

Commit

Permalink
Add support for null as input in types
Browse files Browse the repository at this point in the history
  • Loading branch information
wooorm committed Dec 31, 2022
1 parent d68e907 commit 5e05a61
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 89 deletions.
45 changes: 38 additions & 7 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
/**
* @typedef {import('unist').Position} Position
* @typedef {import('unist').Node} Node
* @typedef {Record<string, unknown> & {type: string, position?: Position|undefined}} NodeLike
* @typedef {import('./lib/types.js').SelectState} SelectState
* @typedef {Record<string, unknown> & {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`.
Expand All @@ -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
}

/**
Expand All @@ -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
Expand All @@ -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
}

/**
Expand All @@ -52,13 +62,34 @@ 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<Node>}
* Nodes in `tree` that match `selector`.
*
* 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
}
}
18 changes: 9 additions & 9 deletions lib/any.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.<Node>}
* @returns {Array<Node>}
*/
export function any(query, node, state) {
// @ts-expect-error: fine.
Expand Down Expand Up @@ -73,7 +73,7 @@ function rule(query, tree, state) {
query,
tree,
0,
null,
undefined,
configure(query, {
any: state.any,
iterator,
Expand Down Expand Up @@ -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
Expand All @@ -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.<Node>} */
/** @type {Array<Node>} */
const result = []
/** @type {boolean} */
let found
Expand All @@ -155,7 +155,7 @@ function collector(one) {
/**
* Append nodes to array, filtering out duplicates.
*
* @param {Node|Array.<Node>} node
* @param {Node | Array<Node>} node
*/
function collect(node) {
let index = -1
Expand Down
4 changes: 2 additions & 2 deletions lib/attribute.js
Original file line number Diff line number Diff line change
Expand Up @@ -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.<any>` instead of `Array.<unknown>`.
// into `Array<any>` instead of `Array<unknown>`.
// type-coverage:ignore-next-line
if (Array.isArray(value) && value.includes(query.value)) {
return true
Expand Down Expand Up @@ -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
Expand Down
32 changes: 16 additions & 16 deletions lib/nest.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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} */
Expand All @@ -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()
}

Expand All @@ -129,7 +129,7 @@ function generalSibling(query, _, index, parent, state) {

new WalkIterator(query, parent, state)
.prefillTypeIndex(0, ++index)
.each(index)
.each(index, undefined)
.done()
}

Expand All @@ -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.<Function>} */
/** @type {Array<Function>} */
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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -246,21 +246,21 @@ 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]
}
}

class TypeIndex {
constructor() {
/** @type {Object.<string, number>} */
/** @type {Record<string, number>} */
this.counts = {}
/** @type {number} */
this.nodes = 0
Expand All @@ -283,7 +283,7 @@ class TypeIndex {

/**
* @param {Node} node
* @returns {number|undefined}
* @returns {number | undefined}
*/
count(node) {
return this.counts[node.type]
Expand Down
2 changes: 1 addition & 1 deletion lib/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
Loading

0 comments on commit 5e05a61

Please sign in to comment.