Skip to content

Commit e614881

Browse files
committed
Refactor to move implementation to lib/
1 parent 9a8ef81 commit e614881

File tree

3 files changed

+299
-281
lines changed

3 files changed

+299
-281
lines changed

index.js

Lines changed: 8 additions & 281 deletions
Original file line numberDiff line numberDiff line change
@@ -1,290 +1,17 @@
11
/**
2-
* @typedef {import('unist').Node} Node
3-
* @typedef {import('unist').Parent} Parent
4-
* @typedef {string} Type
5-
* @typedef {Record<string, unknown>} Props
6-
* @typedef {null|undefined|Type|Props|TestFunctionAnything|Array<Type|Props|TestFunctionAnything>} Test
7-
*
8-
* @callback TestFunctionAnything
9-
* Arbitrary function to define whether a node passes.
10-
* @param {unknown} this
11-
* The to `is` given `context`
12-
* @param {Node} node
13-
* Node to check.
14-
* @param {number|null|undefined} [index]
15-
* Index of `node` in `parent`.
16-
* @param {Parent|null|undefined} [parent]
17-
* Parent of `node`.
18-
* @returns {boolean|void}
19-
* Whether `node` matches.
20-
*
21-
* @callback AssertAnything
22-
* Check if a node is an element and passes a certain node test.
23-
* @param {unknown} [node]
24-
* Thing to check and check that it’s a node.
25-
* @param {number|null|undefined} [index]
26-
* Index of `node` in `parent`.
27-
* @param {Parent|null|undefined} [parent]
28-
* Parent of `node`.
29-
* @returns {boolean}
30-
* Whether `node` matches.
2+
* @typedef {import('./lib/index.js').Test} Test
3+
* @typedef {import('./lib/index.js').TestFunctionAnything} TestFunctionAnything
4+
* @typedef {import('./lib/index.js').AssertAnything} AssertAnything
315
*/
326

337
/**
34-
* @template {Node} Kind
35-
* @callback TestFunctionPredicate
36-
* Arbitrary function to define whether a node passes, using a TypeScript type
37-
* predicate.
38-
* @param {Node} node
39-
* Node to check.
40-
* @param {number|null|undefined} [index]
41-
* Index of `node` in `parent`.
42-
* @param {Parent|null|undefined} [parent]
43-
* Parent of `node`.
44-
* @returns {node is Kind}
45-
* Whether `node` matches.
8+
* @template {import('unist').Node} Kind
9+
* @typedef {import('./lib/index.js').TestFunctionPredicate<Kind>} TestFunctionPredicate
4610
*/
4711

4812
/**
49-
* @template {Node} Kind
50-
* @callback AssertPredicate
51-
* Check if a node is an element and passes a certain node test, using a
52-
* TypeScript type predicate.
53-
* @param {unknown} [node]
54-
* Thing to check and check that it’s a node.
55-
* @param {number|null|undefined} [index]
56-
* Index of `node` in `parent`.
57-
* @param {Parent|null|undefined} [parent]
58-
* Parent of `node`.
59-
* @returns {node is Kind}
60-
* Whether `node` matches.
13+
* @template {import('unist').Node} Kind
14+
* @typedef {import('./lib/index.js').AssertPredicate<Kind>} AssertPredicate
6115
*/
6216

63-
/**
64-
* Check if `node` passes a test.
65-
* When a `parent` node is given the `index` of node should also be given.
66-
*
67-
* @param node
68-
* Node to check, can be anything.
69-
* @param test
70-
* * when nullish, checks if `node` is a `Node`.
71-
* * when `string`, works like passing `(node) => node.type === test`.
72-
* * when `array`, checks any one of the subtests pass.
73-
* * when `object`, checks that all keys in test are in node, and that they have (strictly) equal values.
74-
* * when `function` checks if function passed the node is true.
75-
* @param index
76-
* Position of `node` in `parent`, must be a number if `parent` is also given.
77-
* @param parent
78-
* Parent of `node`, must be given if `index` is also given.
79-
* @param context
80-
* Context object to call `test` with
81-
* @returns
82-
* Whether test passed and `node` is a `Node` (object with `type` set to
83-
* non-empty `string`).
84-
*/
85-
export const is =
86-
/**
87-
* @type {(
88-
* (<Kind extends Node>(node: unknown, test: Kind['type']|Partial<Kind>|TestFunctionPredicate<Kind>|Array<Kind['type']|Partial<Kind>|TestFunctionPredicate<Kind>>, index: number, parent: Parent, context?: unknown) => node is Kind) &
89-
* (<Kind extends Node>(node: unknown, test: Kind['type']|Partial<Kind>|TestFunctionPredicate<Kind>|Array<Kind['type']|Partial<Kind>|TestFunctionPredicate<Kind>>, index?: null|undefined, parent?: null|undefined, context?: unknown) => node is Kind) &
90-
* ((node: unknown, test: Test, index: number, parent: Parent, context?: unknown) => boolean) &
91-
* ((node?: unknown, test?: Test, index?: null|undefined, parent?: null|undefined, context?: unknown) => boolean)
92-
* )}
93-
*/
94-
(
95-
/**
96-
* @param {unknown} [node]
97-
* @param {Test} [test]
98-
* @param {number|null|undefined} [index]
99-
* @param {Parent|null|undefined} [parent]
100-
* @param {unknown} [context]
101-
* @returns {boolean}
102-
*/
103-
// eslint-disable-next-line max-params
104-
function is(node, test, index, parent, context) {
105-
const check = convert(test)
106-
107-
if (
108-
index !== undefined &&
109-
index !== null &&
110-
(typeof index !== 'number' ||
111-
index < 0 ||
112-
index === Number.POSITIVE_INFINITY)
113-
) {
114-
throw new Error('Expected positive finite index')
115-
}
116-
117-
if (
118-
parent !== undefined &&
119-
parent !== null &&
120-
(!is(parent) || !parent.children)
121-
) {
122-
throw new Error('Expected parent node')
123-
}
124-
125-
if (
126-
(parent === undefined || parent === null) !==
127-
(index === undefined || index === null)
128-
) {
129-
throw new Error('Expected both parent and index')
130-
}
131-
132-
// @ts-expect-error Looks like a node.
133-
return node && node.type && typeof node.type === 'string'
134-
? Boolean(check.call(context, node, index, parent))
135-
: false
136-
}
137-
)
138-
139-
/**
140-
* Create a test function from `test` that can later be called with a `node`,
141-
* `index`, and `parent`.
142-
* Useful if you’re going to test many nodes, for example when creating a
143-
* utility where something else passes an is-compatible test.
144-
*
145-
* The created function is slightly faster that using `is` because it expects
146-
* valid input only.
147-
* Therefore, passing invalid input yields unexpected results.
148-
*
149-
* @param test
150-
* * when nullish, checks if `node` is a `Node`.
151-
* * when `string`, works like passing `(node) => node.type === test`.
152-
* * when `array`, checks any one of the subtests pass.
153-
* * when `object`, checks that all keys in test are in node, and that they have (strictly) equal values.
154-
* * when `function` checks if function passed the node is true.
155-
* @returns
156-
* Check function that can be called as `check(node, index, parent)`.
157-
*/
158-
export const convert =
159-
/**
160-
* @type {(
161-
* (<Kind extends Node>(test: Kind['type']|Partial<Kind>|TestFunctionPredicate<Kind>) => AssertPredicate<Kind>) &
162-
* ((test?: Test) => AssertAnything)
163-
* )}
164-
*/
165-
(
166-
/**
167-
* @param {Test} [test]
168-
* @returns {AssertAnything}
169-
*/
170-
function (test) {
171-
if (test === undefined || test === null) {
172-
return ok
173-
}
174-
175-
if (typeof test === 'string') {
176-
return typeFactory(test)
177-
}
178-
179-
if (typeof test === 'object') {
180-
return Array.isArray(test) ? anyFactory(test) : propsFactory(test)
181-
}
182-
183-
if (typeof test === 'function') {
184-
return castFactory(test)
185-
}
186-
187-
throw new Error('Expected function, string, or object as test')
188-
}
189-
)
190-
191-
/**
192-
* @param {Array<Type|Props|TestFunctionAnything>} tests
193-
* @returns {AssertAnything}
194-
*/
195-
function anyFactory(tests) {
196-
/** @type {Array<AssertAnything>} */
197-
const checks = []
198-
let index = -1
199-
200-
while (++index < tests.length) {
201-
checks[index] = convert(tests[index])
202-
}
203-
204-
return castFactory(any)
205-
206-
/**
207-
* @this {unknown}
208-
* @param {Array<unknown>} parameters
209-
* @returns {boolean}
210-
*/
211-
function any(...parameters) {
212-
let index = -1
213-
214-
while (++index < checks.length) {
215-
if (checks[index].call(this, ...parameters)) return true
216-
}
217-
218-
return false
219-
}
220-
}
221-
222-
/**
223-
* Assert each field in `test` is present in `node` and each values are strictly
224-
* equal.
225-
*
226-
* @param {Props} check
227-
* @returns {AssertAnything}
228-
*/
229-
function propsFactory(check) {
230-
return castFactory(all)
231-
232-
/**
233-
* @param {Node} node
234-
* @returns {boolean}
235-
*/
236-
function all(node) {
237-
/** @type {string} */
238-
let key
239-
240-
for (key in check) {
241-
// @ts-expect-error: hush, it sure works as an index.
242-
if (node[key] !== check[key]) return false
243-
}
244-
245-
return true
246-
}
247-
}
248-
249-
/**
250-
* Convert a string into a function which checks a given node’s type
251-
* for said string.
252-
*
253-
* @param {Type} check
254-
* @returns {AssertAnything}
255-
*/
256-
function typeFactory(check) {
257-
return castFactory(type)
258-
259-
/**
260-
* @param {Node} node
261-
*/
262-
function type(node) {
263-
return node && node.type === check
264-
}
265-
}
266-
267-
/**
268-
* Convert a string into a function which checks a given node’s type
269-
* for said string.
270-
*
271-
* @param {TestFunctionAnything} check
272-
* @returns {AssertAnything}
273-
*/
274-
function castFactory(check) {
275-
return assertion
276-
277-
/**
278-
* @this {unknown}
279-
* @param {Array<unknown>} parameters
280-
* @returns {boolean}
281-
*/
282-
function assertion(...parameters) {
283-
// @ts-expect-error: spreading is fine.
284-
return Boolean(check.call(this, ...parameters))
285-
}
286-
}
287-
288-
function ok() {
289-
return true
290-
}
17+
export {is, convert} from './lib/index.js'

0 commit comments

Comments
 (0)