/
index.ts
104 lines (92 loc) · 2.76 KB
/
index.ts
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
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import * as acorn from "acorn";
import jsx, {
JSXAttribute,
JSXIdentifier,
JSXNamespacedName,
JSXOpeningElement,
Literal,
} from "acorn-jsx";
import { extend } from "acorn-jsx-walk";
import * as walk from "acorn-walk";
type NodeState = {
selectors?: string[];
text?: string;
};
extend<NodeState>(walk.base);
/**
* Create function to extract selectors from jsx code
*
* @param options - acorn options
* @returns function to extract selectors from jsx code
*
* @public
*/
function purgeFromJsx(options?: acorn.Options) {
return (content: string): string[] => {
// Will be filled during walk
const state: NodeState = { selectors: [] };
// Parse and walk any JSXElement
walk.recursive<NodeState>(
acorn.Parser.extend(
jsx() as (BaseParser: typeof acorn.Parser) => typeof acorn.Parser
).parse(content, options),
state,
{
JSXOpeningElement(acornNode, state, callback) {
const node = acornNode as JSXOpeningElement;
const nameState: NodeState = {};
callback(node.name, nameState);
if (nameState.text) {
state.selectors?.push(nameState.text);
}
for (let i = 0; i < node.attributes.length; ++i) {
callback(node.attributes[i], state);
}
},
JSXAttribute(acornNode, state, callback) {
const node = acornNode as JSXAttribute;
if (!node.value) {
return;
}
const nameState: NodeState = {};
callback(node.name, nameState);
// node.name is id or className
switch (nameState.text) {
case "id":
case "className":
{
// Get text in node.value
const valueState: NodeState = {};
callback(node.value, valueState);
// node.value is not empty
if (valueState.text) {
state.selectors?.push(...valueState.text.split(" "));
}
}
break;
default:
break;
}
},
JSXIdentifier(acornNode, state) {
const node = acornNode as JSXIdentifier;
state.text = node.name;
},
JSXNamespacedName(acornNode, state) {
const node = acornNode as JSXNamespacedName;
state.text = node.namespace.name + ":" + node.name.name;
},
// Only handle Literal for now, not JSXExpressionContainer | JSXElement
Literal(acornNode, state) {
const node = acornNode as Literal;
if (typeof node.value === "string") {
state.text = node.value;
}
},
},
{ ...walk.base }
);
return state.selectors || [];
};
}
export default purgeFromJsx;