-
-
Notifications
You must be signed in to change notification settings - Fork 779
/
extractQuoted.ts
124 lines (116 loc) · 2.83 KB
/
extractQuoted.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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
export type Range = [start: number, end: number]
export interface ExtractStringOptions<Details extends boolean = false> {
deep?: boolean
templateStaticOnly?: boolean
details?: Details
range?: Range
}
export interface DetailString {
value: string
range: Range
quote: '\'' | '"' | '`'
}
export function matchingPair(
text: string,
[left, right]: Iterable<string>,
i: number,
allowEscape?: boolean,
) {
const len = text.length
let stack = +(text[i] === left)
while (++i < len) {
const char = text[i]
if (char === left) {
stack++
}
else if (char === right) {
stack--
if (stack === 0)
return i
}
else if (allowEscape && char === '\\') {
i++
}
}
return -1
}
const QUOTES = ['\'', '"', '`'] as const
export function extractQuoted(str: string, options?: ExtractStringOptions<false>): string[]
export function extractQuoted(str: string, options: ExtractStringOptions<true>): DetailString[]
export function extractQuoted(
str: string,
options: ExtractStringOptions<boolean> = {},
): any[] {
const {
deep = false,
templateStaticOnly = false,
details = false,
range: [rstart, rend] = [0, str.length],
} = options
const result: (string | DetailString)[] = []
let quote: DetailString['quote']
const addResult = (start: number, end: number) => result.push(
details
? {
value: str.slice(start, end),
range: [start, end],
quote,
}
: str.slice(start, end),
)
let i = rstart
while (i < rend) {
const char = str[i]
if ((QUOTES.includes as (c: string) => c is typeof quote)(char)) {
quote = char
const start = i + 1
const isTemplate = quote === '`'
let templateStart = start
let end = start
while (end < rend) {
const nextChar = str[end]
if (nextChar === quote) {
if (isTemplate && templateStaticOnly) {
addResult(templateStart, end)
break
}
addResult(start, end)
break
}
if (nextChar === '\\') {
end += 2
}
else if (isTemplate && nextChar === '$' && str[end + 1] === '{') {
const nestStart = end + 2
end = matchingPair(str, '{}', end + 1, true)
if (templateStaticOnly) {
addResult(
templateStart,
nestStart - 2,
)
}
templateStart = end + 1
if (deep) {
result.push(
...extractQuoted(
str,
{
...options,
range: [nestStart, end],
} as ExtractStringOptions<any>,
),
)
}
}
else {
end += 1
}
}
i = end + 1
}
else {
i++
}
}
return result
}