-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
/
xast.js
81 lines (73 loc) · 2.14 KB
/
xast.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
import { selectAll, selectOne, is } from 'css-select';
import xastAdaptor from './svgo/css-select-adapter.js';
/**
* @typedef {import('./types.js').XastNode} XastNode
* @typedef {import('./types.js').XastChild} XastChild
* @typedef {import('./types.js').XastParent} XastParent
* @typedef {import('./types.js').Visitor} Visitor
* @typedef {import('./svgo.ts').querySelector} querySelector
* @typedef {import('./svgo.ts').querySelectorAll} querySelectorAll
*/
const cssSelectOptions = {
xmlMode: true,
adapter: xastAdaptor,
};
/**
* @type {querySelectorAll}
*/
export const querySelectorAll = (node, selector) => {
return selectAll(selector, node, cssSelectOptions);
};
/**
* @type {querySelector}
*/
export const querySelector = (node, selector) => {
return selectOne(selector, node, cssSelectOptions);
};
/**
* @type {(node: XastChild, selector: string) => boolean}
*/
export const matches = (node, selector) => {
return is(node, selector, cssSelectOptions);
};
export const visitSkip = Symbol();
/**
* @type {(node: XastNode, visitor: Visitor, parentNode?: any) => void}
*/
export const visit = (node, visitor, parentNode) => {
const callbacks = visitor[node.type];
if (callbacks && callbacks.enter) {
// @ts-ignore hard to infer
const symbol = callbacks.enter(node, parentNode);
if (symbol === visitSkip) {
return;
}
}
// visit root children
if (node.type === 'root') {
// copy children array to not loose cursor when children is spliced
for (const child of node.children) {
visit(child, visitor, node);
}
}
// visit element children if still attached to parent
if (node.type === 'element') {
if (parentNode.children.includes(node)) {
for (const child of node.children) {
visit(child, visitor, node);
}
}
}
if (callbacks && callbacks.exit) {
// @ts-ignore hard to infer
callbacks.exit(node, parentNode);
}
};
/**
* @param {XastChild} node
* @param {XastParent} parentNode
*/
export const detachNodeFromParent = (node, parentNode) => {
// avoid splice to not break for loops
parentNode.children = parentNode.children.filter((child) => child !== node);
};