Skip to content

Commit b4e28f5

Browse files
committed
Refactor to move implementation to lib/
1 parent a16a694 commit b4e28f5

File tree

3 files changed

+298
-275
lines changed

3 files changed

+298
-275
lines changed

index.js

+10-275
Original file line numberDiff line numberDiff line change
@@ -1,287 +1,22 @@
11
/**
2-
* @typedef {import('hast').Element} Element
3-
* @typedef {import('unist').Parent} Parent
2+
* @typedef {import('./lib/index.js').AssertAnything} AssertAnything
3+
* @typedef {import('./lib/index.js').Test} Test
4+
* @typedef {import('./lib/index.js').TestFunctionAnything} TestFunctionAnything
45
*/
56

67
/**
7-
* @typedef {Array<TestFunctionAnything | string> | TestFunctionAnything | string | null | undefined} Test
8-
* Check for an arbitrary element, unaware of TypeScript inferral.
9-
*
10-
* @callback TestFunctionAnything
11-
* Check if an element passes a test, unaware of TypeScript inferral.
12-
* @param {Element} element
13-
* An element.
14-
* @param {number | null | undefined} [index]
15-
* Position of `node` in `parent`.
16-
* @param {Parent | null | undefined} [parent]
17-
* Parent of `node`.
18-
* @returns {boolean | undefined | void}
19-
* Whether this element passes the test.
8+
* @template {import('hast').Element} T
9+
* @typedef {import('./lib/index.js').AssertPredicate<T>} AssertPredicate
2010
*/
2111

2212
/**
23-
* @template {Element} T
24-
* Element type.
25-
* @typedef {Array<TestFunctionPredicate<T> | T['tagName']> | TestFunctionPredicate<T> | T['tagName']} PredicateTest
26-
* Check for an element that can be inferred by TypeScript.
13+
* @template {import('hast').Element} T
14+
* @typedef {import('./lib/index.js').PredicateTest<T>} PredicateTest
2715
*/
2816

2917
/**
30-
* Check if an element passes a certain node test.
31-
*
32-
* @template {Element} T
33-
* Element type.
34-
* @callback TestFunctionPredicate
35-
* Complex test function for an element that can be inferred by TypeScript.
36-
* @param {Element} element
37-
* An element.
38-
* @param {number | null | undefined} [index]
39-
* Position of `node` in `parent`.
40-
* @param {Parent | null | undefined} [parent]
41-
* Parent of `node`.
42-
* @returns {element is T}
43-
* Whether this element passes the test.
18+
* @template {import('hast').Element} T
19+
* @typedef {import('./lib/index.js').TestFunctionPredicate<T>} TestFunctionPredicate
4420
*/
4521

46-
/**
47-
* @callback AssertAnything
48-
* Check that an arbitrary value is an element, unaware of TypeScript inferral.
49-
* @param {unknown} [node]
50-
* Anything (typically a node).
51-
* @param {number | null | undefined} [index]
52-
* Position of `node` in `parent`.
53-
* @param {Parent | null | undefined} [parent]
54-
* Parent of `node`.
55-
* @returns {boolean}
56-
* Whether this is an element and passes a test.
57-
*/
58-
59-
/**
60-
* Check if a node is an element and passes a certain node test
61-
*
62-
* @template {Element} T
63-
* Element type.
64-
* @callback AssertPredicate
65-
* Check that an arbitrary value is a specific element, aware of TypeScript.
66-
* @param {unknown} [node]
67-
* Anything (typically a node).
68-
* @param {number | null | undefined} [index]
69-
* Position of `node` in `parent`.
70-
* @param {Parent | null | undefined} [parent]
71-
* Parent of `node`.
72-
* @returns {node is T}
73-
* Whether this is an element and passes a test.
74-
*/
75-
76-
/**
77-
* Check if `node` is an `Element` and whether it passes the given test.
78-
*
79-
* @param node
80-
* Thing to check, typically `Node`.
81-
* @param test
82-
* Check for a specific element.
83-
* @param index
84-
* Position of `node` in `parent`.
85-
* @param parent
86-
* Parent of `node`.
87-
* @param context
88-
* Context to call `test` with.
89-
* @returns
90-
* Whether `node` is an element and passes a test.
91-
*/
92-
export const isElement =
93-
// Note: JSDoc overloads don’t support optional type parameters.
94-
/**
95-
* @type {(
96-
* (() => false) &
97-
* (<T extends Element = Element>(node: unknown, test?: PredicateTest<T>, index?: number, parent?: Parent, context?: unknown) => node is T) &
98-
* ((node: unknown, test: Test, index?: number, parent?: Parent, context?: unknown) => boolean)
99-
* )}
100-
*/
101-
(
102-
/**
103-
* @param {unknown} [node]
104-
* @param {Test | undefined} [test]
105-
* @param {number | null | undefined} [index]
106-
* @param {Parent | null | undefined} [parent]
107-
* @param {unknown} [context]
108-
* @returns {boolean}
109-
*/
110-
// eslint-disable-next-line max-params
111-
function (node, test, index, parent, context) {
112-
const check = convertElement(test)
113-
114-
if (
115-
index !== null &&
116-
index !== undefined &&
117-
(typeof index !== 'number' ||
118-
index < 0 ||
119-
index === Number.POSITIVE_INFINITY)
120-
) {
121-
throw new Error('Expected positive finite index for child node')
122-
}
123-
124-
if (
125-
parent !== null &&
126-
parent !== undefined &&
127-
(!parent.type || !parent.children)
128-
) {
129-
throw new Error('Expected parent node')
130-
}
131-
132-
// @ts-expect-error: looks like a node.
133-
if (!node || !node.type || typeof node.type !== 'string') {
134-
return false
135-
}
136-
137-
if (
138-
(parent === null || parent === undefined) !==
139-
(index === null || index === undefined)
140-
) {
141-
throw new Error('Expected both parent and index')
142-
}
143-
144-
return check.call(context, node, index, parent)
145-
}
146-
)
147-
148-
/**
149-
* Generate an assertion from a test.
150-
*
151-
* Useful if you’re going to test many nodes, for example when creating a
152-
* utility where something else passes a compatible test.
153-
*
154-
* The created function is a bit faster because it expects valid input only:
155-
* a `node`, `index`, and `parent`.
156-
*
157-
* @param test
158-
* * When nullish, checks if `node` is an `Element`.
159-
* * When `string`, works like passing `(element) => element.tagName === test`.
160-
* * When `function` checks if function passed the element is true.
161-
* * When `array`, checks any one of the subtests pass.
162-
* @returns
163-
* An assertion.
164-
*/
165-
export const convertElement =
166-
/**
167-
* @type {(
168-
* (<T extends Element>(test: T['tagName'] | TestFunctionPredicate<T>) => AssertPredicate<T>) &
169-
* ((test?: Test) => AssertAnything)
170-
* )}
171-
*/
172-
(
173-
/**
174-
* @param {Test | null | undefined} [test]
175-
* @returns {AssertAnything}
176-
*/
177-
function (test) {
178-
if (test === null || test === undefined) {
179-
return element
180-
}
181-
182-
if (typeof test === 'string') {
183-
return tagNameFactory(test)
184-
}
185-
186-
if (typeof test === 'object') {
187-
return anyFactory(test)
188-
}
189-
190-
if (typeof test === 'function') {
191-
return castFactory(test)
192-
}
193-
194-
throw new Error('Expected function, string, or array as test')
195-
}
196-
)
197-
198-
/**
199-
* Handle multiple tests.
200-
*
201-
* @param {Array<TestFunctionAnything | string>} tests
202-
* @returns {AssertAnything}
203-
*/
204-
function anyFactory(tests) {
205-
/** @type {Array<AssertAnything>} */
206-
const checks = []
207-
let index = -1
208-
209-
while (++index < tests.length) {
210-
checks[index] = convertElement(tests[index])
211-
}
212-
213-
return castFactory(any)
214-
215-
/**
216-
* @this {unknown}
217-
* @param {Array<unknown>} parameters
218-
* @returns {boolean}
219-
*/
220-
function any(...parameters) {
221-
let index = -1
222-
223-
while (++index < checks.length) {
224-
if (checks[index].call(this, ...parameters)) {
225-
return true
226-
}
227-
}
228-
229-
return false
230-
}
231-
}
232-
233-
/**
234-
* Turn a string into a test for an element with a certain tag name.
235-
*
236-
* @param {string} check
237-
* @returns {AssertAnything}
238-
*/
239-
function tagNameFactory(check) {
240-
return tagName
241-
242-
/**
243-
* @param {unknown} node
244-
* @returns {boolean}
245-
*/
246-
function tagName(node) {
247-
return element(node) && node.tagName === check
248-
}
249-
}
250-
251-
/**
252-
* Turn a custom test into a test for an element that passes that test.
253-
*
254-
* @param {TestFunctionAnything} check
255-
* @returns {AssertAnything}
256-
*/
257-
function castFactory(check) {
258-
return assertion
259-
260-
/**
261-
* @this {unknown}
262-
* @param {unknown} node
263-
* @param {Array<unknown>} parameters
264-
* @returns {boolean}
265-
*/
266-
function assertion(node, ...parameters) {
267-
// @ts-expect-error: assume valid parameters.
268-
return element(node) && Boolean(check.call(this, node, ...parameters))
269-
}
270-
}
271-
272-
/**
273-
* Make sure something is an element.
274-
*
275-
* @param {unknown} node
276-
* @returns {node is Element}
277-
*/
278-
function element(node) {
279-
return Boolean(
280-
node &&
281-
typeof node === 'object' &&
282-
'type' in node &&
283-
node.type === 'element' &&
284-
'tagName' in node &&
285-
typeof node.tagName === 'string'
286-
)
287-
}
22+
export {convertElement, isElement} from './lib/index.js'

0 commit comments

Comments
 (0)