1 |
|
- |
// Assert if `test` passes for `node`. |
2 |
|
- |
// When a `parent` node is known the `index` of node should also be given. |
3 |
|
- |
// eslint-disable-next-line max-params |
4 |
|
- |
export function is(node, test, index, parent, context) { |
5 |
|
- |
var check = convert(test) |
6 |
|
- |
|
7 |
|
- |
if ( |
8 |
|
- |
index !== undefined && |
9 |
|
- |
index !== null && |
10 |
|
- |
(typeof index !== 'number' || |
11 |
|
- |
index < 0 || |
12 |
|
- |
index === Number.POSITIVE_INFINITY) |
13 |
|
- |
) { |
14 |
|
- |
throw new Error('Expected positive finite index') |
15 |
|
- |
} |
16 |
|
- |
|
17 |
|
- |
if ( |
18 |
|
- |
parent !== undefined && |
19 |
|
- |
parent !== null && |
20 |
|
- |
(!is(parent) || !parent.children) |
21 |
|
- |
) { |
22 |
|
- |
throw new Error('Expected parent node') |
23 |
|
- |
} |
24 |
|
- |
|
25 |
|
- |
if ( |
26 |
|
- |
(parent === undefined || parent === null) !== |
27 |
|
- |
(index === undefined || index === null) |
28 |
|
- |
) { |
29 |
|
- |
throw new Error('Expected both parent and index') |
30 |
|
- |
} |
31 |
|
- |
|
32 |
|
- |
return node && node.type && typeof node.type === 'string' |
33 |
|
- |
? Boolean(check.call(context, node, index, parent)) |
34 |
|
- |
: false |
35 |
|
- |
} |
36 |
|
- |
|
37 |
|
- |
export function convert(test) { |
38 |
|
- |
if (test === undefined || test === null) { |
39 |
|
- |
return ok |
40 |
|
- |
} |
|
1 |
+ |
/** |
|
2 |
+ |
* @typedef {import('unist').Node} Node |
|
3 |
+ |
* @typedef {import('unist').Parent} Parent |
|
4 |
+ |
* |
|
5 |
+ |
* @typedef {string} Type |
|
6 |
+ |
* @typedef {Object<string, unknown>} Props |
|
7 |
+ |
*/ |
|
8 |
+ |
|
|
9 |
+ |
/** |
|
10 |
+ |
* Check if a node passes a test |
|
11 |
+ |
* |
|
12 |
+ |
* @callback TestFunctionAnything |
|
13 |
+ |
* @param {Node} node |
|
14 |
+ |
* @param {number} [index] |
|
15 |
+ |
* @param {Parent} [parent] |
|
16 |
+ |
* @returns {boolean|void} |
|
17 |
+ |
*/ |
|
18 |
+ |
|
|
19 |
+ |
/** |
|
20 |
+ |
* Check if a node passes a certain node test |
|
21 |
+ |
* |
|
22 |
+ |
* @template {Node} X |
|
23 |
+ |
* @callback TestFunctionPredicate |
|
24 |
+ |
* @param {Node} node |
|
25 |
+ |
* @param {number} [index] |
|
26 |
+ |
* @param {Parent} [parent] |
|
27 |
+ |
* @returns {node is X} |
|
28 |
+ |
*/ |
|
29 |
+ |
|
|
30 |
+ |
/** |
|
31 |
+ |
* @callback AssertAnything |
|
32 |
+ |
* @param {unknown} [node] |
|
33 |
+ |
* @param {number} [index] |
|
34 |
+ |
* @param {Parent} [parent] |
|
35 |
+ |
* @returns {boolean} |
|
36 |
+ |
*/ |
|
37 |
+ |
|
|
38 |
+ |
/** |
|
39 |
+ |
* Check if a node passes a certain node test |
|
40 |
+ |
* |
|
41 |
+ |
* @template {Node} Y |
|
42 |
+ |
* @callback AssertPredicate |
|
43 |
+ |
* @param {unknown} [node] |
|
44 |
+ |
* @param {number} [index] |
|
45 |
+ |
* @param {Parent} [parent] |
|
46 |
+ |
* @returns {node is Y} |
|
47 |
+ |
*/ |
|
48 |
+ |
|
|
49 |
+ |
export var is = |
|
50 |
+ |
/** |
|
51 |
+ |
* Check if a node passes a test. |
|
52 |
+ |
* When a `parent` node is known the `index` of node should also be given. |
|
53 |
+ |
* |
|
54 |
+ |
* @type {( |
|
55 |
+ |
* (<T extends Node>(node: unknown, test: T['type']|Partial<T>|TestFunctionPredicate<T>|Array.<T['type']|Partial<T>|TestFunctionPredicate<T>>, index?: number, parent?: Parent, context?: unknown) => node is T) & |
|
56 |
+ |
* ((node?: unknown, test?: null|undefined|Type|Props|TestFunctionAnything|Array.<Type|Props|TestFunctionAnything>, index?: number, parent?: Parent, context?: unknown) => boolean) |
|
57 |
+ |
* )} |
|
58 |
+ |
*/ |
|
59 |
+ |
( |
|
60 |
+ |
/** |
|
61 |
+ |
* Check if a node passes a test. |
|
62 |
+ |
* When a `parent` node is known the `index` of node should also be given. |
|
63 |
+ |
* |
|
64 |
+ |
* @param {unknown} [node] Node to check |
|
65 |
+ |
* @param {null|undefined|Type|Props|TestFunctionAnything|Array.<Type|Props|TestFunctionAnything>} [test] |
|
66 |
+ |
* When nullish, checks if `node` is a `Node`. |
|
67 |
+ |
* When `string`, works like passing `function (node) {return node.type === test}`. |
|
68 |
+ |
* When `function` checks if function passed the node is true. |
|
69 |
+ |
* When `object`, checks that all keys in test are in node, and that they have (strictly) equal values. |
|
70 |
+ |
* When `array`, checks any one of the subtests pass. |
|
71 |
+ |
* @param {number} [index] Position of `node` in `parent` |
|
72 |
+ |
* @param {Parent} [parent] Parent of `node` |
|
73 |
+ |
* @param {unknown} [context] Context object to invoke `test` with |
|
74 |
+ |
* @returns {boolean} Whether test passed and `node` is a `Node` (object with `type` set to non-empty `string`). |
|
75 |
+ |
*/ |
|
76 |
+ |
// eslint-disable-next-line max-params |
|
77 |
+ |
function is(node, test, index, parent, context) { |
|
78 |
+ |
var check = convert(test) |
|
79 |
+ |
|
|
80 |
+ |
if ( |
|
81 |
+ |
index !== undefined && |
|
82 |
+ |
index !== null && |
|
83 |
+ |
(typeof index !== 'number' || |
|
84 |
+ |
index < 0 || |
|
85 |
+ |
index === Number.POSITIVE_INFINITY) |
|
86 |
+ |
) { |
|
87 |
+ |
throw new Error('Expected positive finite index') |
|
88 |
+ |
} |
41 |
89 |
|
|
42 |
|
- |
if (typeof test === 'string') { |
43 |
|
- |
return typeFactory(test) |
44 |
|
- |
} |
|
90 |
+ |
if ( |
|
91 |
+ |
parent !== undefined && |
|
92 |
+ |
parent !== null && |
|
93 |
+ |
(!is(parent) || !parent.children) |
|
94 |
+ |
) { |
|
95 |
+ |
throw new Error('Expected parent node') |
|
96 |
+ |
} |
45 |
97 |
|
|
46 |
|
- |
if (typeof test === 'object') { |
47 |
|
- |
return 'length' in test ? anyFactory(test) : allFactory(test) |
48 |
|
- |
} |
|
98 |
+ |
if ( |
|
99 |
+ |
(parent === undefined || parent === null) !== |
|
100 |
+ |
(index === undefined || index === null) |
|
101 |
+ |
) { |
|
102 |
+ |
throw new Error('Expected both parent and index') |
|
103 |
+ |
} |
49 |
104 |
|
|
50 |
|
- |
if (typeof test === 'function') { |
51 |
|
- |
return test |
52 |
|
- |
} |
|
105 |
+ |
// @ts-ignore Looks like a node. |
|
106 |
+ |
return node && node.type && typeof node.type === 'string' |
|
107 |
+ |
? Boolean(check.call(context, node, index, parent)) |
|
108 |
+ |
: false |
|
109 |
+ |
} |
|
110 |
+ |
) |
|
111 |
+ |
|
|
112 |
+ |
export var convert = |
|
113 |
+ |
/** |
|
114 |
+ |
* @type {( |
|
115 |
+ |
* (<T extends Node>(test: T['type']|Partial<T>|TestFunctionPredicate<T>) => AssertPredicate<T>) & |
|
116 |
+ |
* ((test?: null|undefined|Type|Props|TestFunctionAnything|Array.<Type|Props|TestFunctionAnything>) => AssertAnything) |
|
117 |
+ |
* )} |
|
118 |
+ |
*/ |
|
119 |
+ |
( |
|
120 |
+ |
/** |
|
121 |
+ |
* Generate an assertion from a check. |
|
122 |
+ |
* @param {null|undefined|Type|Props|TestFunctionAnything|Array.<Type|Props|TestFunctionAnything>} [test] |
|
123 |
+ |
* When nullish, checks if `node` is a `Node`. |
|
124 |
+ |
* When `string`, works like passing `function (node) {return node.type === test}`. |
|
125 |
+ |
* When `function` checks if function passed the node is true. |
|
126 |
+ |
* When `object`, checks that all keys in test are in node, and that they have (strictly) equal values. |
|
127 |
+ |
* When `array`, checks any one of the subtests pass. |
|
128 |
+ |
* @returns {AssertAnything} |
|
129 |
+ |
*/ |
|
130 |
+ |
function (test) { |
|
131 |
+ |
if (test === undefined || test === null) { |
|
132 |
+ |
return ok |
|
133 |
+ |
} |
53 |
134 |
|
|
54 |
|
- |
throw new Error('Expected function, string, or object as test') |
55 |
|
- |
} |
|
135 |
+ |
if (typeof test === 'string') { |
|
136 |
+ |
return typeFactory(test) |
|
137 |
+ |
} |
56 |
138 |
|
|
57 |
|
- |
// Utility to assert each property in `test` is represented in `node`, and each |
58 |
|
- |
// values are strictly equal. |
59 |
|
- |
function allFactory(test) { |
60 |
|
- |
return all |
|
139 |
+ |
if (typeof test === 'object') { |
|
140 |
+ |
// @ts-ignore looks like a list of tests / partial test object. |
|
141 |
+ |
return 'length' in test ? anyFactory(test) : propsFactory(test) |
|
142 |
+ |
} |
61 |
143 |
|
|
62 |
|
- |
function all(node) { |
63 |
|
- |
var key |
|
144 |
+ |
if (typeof test === 'function') { |
|
145 |
+ |
return castFactory(test) |
|
146 |
+ |
} |
64 |
147 |
|
|
65 |
|
- |
for (key in test) { |
66 |
|
- |
if (node[key] !== test[key]) return false |
|
148 |
+ |
throw new Error('Expected function, string, or object as test') |
67 |
149 |
|
} |
68 |
|
- |
|
69 |
|
- |
return true |
70 |
|
- |
} |
71 |
|
- |
} |
72 |
|
- |
|
|
150 |
+ |
) |
|
151 |
+ |
/** |
|
152 |
+ |
* @param {Array.<Type|Props|TestFunctionAnything>} tests |
|
153 |
+ |
* @returns {AssertAnything} |
|
154 |
+ |
*/ |
73 |
155 |
|
function anyFactory(tests) { |
|
156 |
+ |
/** @type {Array.<AssertAnything>} */ |
74 |
157 |
|
var checks = [] |
75 |
158 |
|
var index = -1 |
76 |
159 |
|
|