|
1 | 1 | /** |
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 |
31 | 5 | */ |
32 | 6 |
|
33 | 7 | /** |
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 |
46 | 10 | */ |
47 | 11 |
|
48 | 12 | /** |
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 |
61 | 15 | */ |
62 | 16 |
|
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