1
+ /**
2
+ * @typedef Options
3
+ * @property {Test } [ignore]
4
+ *
5
+ * @typedef {import('mdast').Text } Text
6
+ * @typedef {import('mdast').Parent } Parent
7
+ * @typedef {import('mdast').Root } Root
8
+ * @typedef {import('mdast').PhrasingContent } PhrasingContent
9
+ * @typedef {Parent['children'][number]|Root } Node
10
+ *
11
+ * @typedef {import('unist-util-visit-parents').Test } Test
12
+ * @typedef {import('unist-util-visit-parents').VisitorResult } VisitorResult
13
+ *
14
+ * @typedef RegExpMatchObject
15
+ * @property {number } index
16
+ * @property {string } input
17
+ *
18
+ * @typedef {string|RegExp } Find
19
+ * @typedef {string|ReplaceFunction } Replace
20
+ *
21
+ * @typedef {[Find, Replace] } FindAndReplaceTuple
22
+ * @typedef {Object.<string, Replace> } FindAndReplaceSchema
23
+ * @typedef {Array.<FindAndReplaceTuple> } FindAndReplaceList
24
+ *
25
+ * @typedef {[RegExp, ReplaceFunction] } Pair
26
+ * @typedef {Array.<Pair> } Pairs
27
+ */
28
+
29
+ /**
30
+ * @callback Handler
31
+ * @param {Text } node
32
+ * @param {Parent } parent
33
+ * @returns {VisitorResult }
34
+ */
35
+
36
+ /**
37
+ * @callback ReplaceFunction
38
+ * @param {...string } parameters
39
+ * @param {RegExpMatchObject } matchObject
40
+ * @returns {Array.<PhrasingContent>|PhrasingContent|string|false|undefined|null }
41
+ */
42
+
1
43
import escape from 'escape-string-regexp'
2
44
import { visitParents } from 'unist-util-visit-parents'
3
45
import { convert } from 'unist-util-is'
4
46
5
47
var own = { } . hasOwnProperty
6
- var splice = [ ] . splice
7
48
49
+ /**
50
+ * @param {Node } tree
51
+ * @param {Find|FindAndReplaceSchema|FindAndReplaceList } find
52
+ * @param {Replace|Options } [replace]
53
+ * @param {Options } [options]
54
+ */
8
55
export function findAndReplace ( tree , find , replace , options ) {
56
+ /** @type {Options } */
9
57
var settings
58
+ /** @type {FindAndReplaceSchema|FindAndReplaceList } */
10
59
var schema
11
60
12
- if ( typeof find === 'string' || ( find && typeof find . exec === 'function' ) ) {
61
+ if ( typeof find === 'string' || find instanceof RegExp ) {
62
+ // @ts -expect-error don’t expect options twice.
13
63
schema = [ [ find , replace ] ]
64
+ settings = options
14
65
} else {
15
66
schema = find
16
- options = replace
67
+ // @ts -expect-error don’t expect replace twice.
68
+ settings = replace
17
69
}
18
70
19
- settings = options || { }
71
+ if ( ! settings ) {
72
+ settings = { }
73
+ }
20
74
21
75
search ( tree , settings , handlerFactory ( toPairs ( schema ) ) )
22
76
23
77
return tree
24
78
79
+ /**
80
+ * @param {Pairs } pairs
81
+ * @returns {Handler }
82
+ */
25
83
function handlerFactory ( pairs ) {
26
84
var pair = pairs [ 0 ]
27
85
28
86
return handler
29
87
88
+ /**
89
+ * @type {Handler }
90
+ */
30
91
function handler ( node , parent ) {
31
92
var find = pair [ 0 ]
32
93
var replace = pair [ 1 ]
94
+ /** @type {Array.<PhrasingContent> } */
33
95
var nodes = [ ]
34
96
var start = 0
35
97
var index = parent . children . indexOf ( node )
98
+ /** @type {number } */
36
99
var position
100
+ /** @type {RegExpMatchArray } */
37
101
var match
102
+ /** @type {Handler } */
38
103
var subhandler
104
+ /** @type {PhrasingContent } */
105
+ var child
106
+ /** @type {Array.<PhrasingContent>|PhrasingContent|string|false|undefined|null } */
39
107
var value
40
108
41
109
find . lastIndex = 0
@@ -44,17 +112,18 @@ export function findAndReplace(tree, find, replace, options) {
44
112
45
113
while ( match ) {
46
114
position = match . index
47
- value = replace ( ...match , { index : match . index , input : match . input } )
115
+ // @ts -expect-error this is perfectly fine, typescript.
116
+ value = replace ( ...match , { index : position , input : match . input } )
117
+
118
+ if ( typeof value === 'string' && value . length > 0 ) {
119
+ value = { type : 'text' , value}
120
+ }
48
121
49
122
if ( value !== false ) {
50
123
if ( start !== position ) {
51
124
nodes . push ( { type : 'text' , value : node . value . slice ( start , position ) } )
52
125
}
53
126
54
- if ( typeof value === 'string' && value . length > 0 ) {
55
- value = { type : 'text' , value}
56
- }
57
-
58
127
if ( value ) {
59
128
nodes = [ ] . concat ( nodes , value )
60
129
}
@@ -77,21 +146,20 @@ export function findAndReplace(tree, find, replace, options) {
77
146
nodes . push ( { type : 'text' , value : node . value . slice ( start ) } )
78
147
}
79
148
80
- nodes . unshift ( index , 1 )
81
- splice . apply ( parent . children , nodes )
149
+ parent . children . splice ( index , 1 , ...nodes )
82
150
}
83
151
84
152
if ( pairs . length > 1 ) {
85
153
subhandler = handlerFactory ( pairs . slice ( 1 ) )
86
154
position = - 1
87
155
88
156
while ( ++ position < nodes . length ) {
89
- node = nodes [ position ]
157
+ child = nodes [ position ]
90
158
91
- if ( node . type === 'text' ) {
92
- subhandler ( node , parent )
159
+ if ( child . type === 'text' ) {
160
+ subhandler ( child , parent )
93
161
} else {
94
- search ( node , settings , subhandler )
162
+ search ( child , settings , subhandler )
95
163
}
96
164
}
97
165
}
@@ -101,25 +169,33 @@ export function findAndReplace(tree, find, replace, options) {
101
169
}
102
170
}
103
171
104
- function search ( tree , settings , handler ) {
105
- var ignored = convert ( settings . ignore || [ ] )
106
- var result = [ ]
172
+ /**
173
+ * @param {Node } tree
174
+ * @param {Options } options
175
+ * @param {Handler } handler
176
+ * @returns {void }
177
+ */
178
+ function search ( tree , options , handler ) {
179
+ var ignored = convert ( options . ignore || [ ] )
107
180
108
181
visitParents ( tree , 'text' , visitor )
109
182
110
- return result
111
-
183
+ /** @type {import('unist-util-visit-parents').Visitor<Text> } */
112
184
function visitor ( node , parents ) {
113
185
var index = - 1
186
+ /** @type {Parent } */
114
187
var parent
188
+ /** @type {Parent } */
115
189
var grandparent
116
190
117
191
while ( ++ index < parents . length ) {
192
+ // @ts -expect-error mdast vs. unist parent.
118
193
parent = parents [ index ]
119
194
120
195
if (
121
196
ignored (
122
197
parent ,
198
+ // @ts -expect-error mdast vs. unist parent.
123
199
grandparent ? grandparent . children . indexOf ( parent ) : undefined ,
124
200
grandparent
125
201
)
@@ -134,18 +210,22 @@ function search(tree, settings, handler) {
134
210
}
135
211
}
136
212
213
+ /**
214
+ * @param {FindAndReplaceSchema|FindAndReplaceList } schema
215
+ * @returns {Pairs }
216
+ */
137
217
function toPairs ( schema ) {
218
+ var index = - 1
219
+ /** @type {Pairs } */
138
220
var result = [ ]
221
+ /** @type {string } */
139
222
var key
140
- var index
141
223
142
224
if ( typeof schema !== 'object' ) {
143
225
throw new TypeError ( 'Expected array or object as schema' )
144
226
}
145
227
146
- if ( 'length' in schema ) {
147
- index = - 1
148
-
228
+ if ( Array . isArray ( schema ) ) {
149
229
while ( ++ index < schema . length ) {
150
230
result . push ( [
151
231
toExpression ( schema [ index ] [ 0 ] ) ,
@@ -163,14 +243,24 @@ function toPairs(schema) {
163
243
return result
164
244
}
165
245
246
+ /**
247
+ * @param {Find } find
248
+ * @returns {RegExp }
249
+ */
166
250
function toExpression ( find ) {
167
251
return typeof find === 'string' ? new RegExp ( escape ( find ) , 'g' ) : find
168
252
}
169
253
254
+ /**
255
+ * @param {Replace } replace
256
+ * @returns {ReplaceFunction }
257
+ */
170
258
function toFunction ( replace ) {
171
259
return typeof replace === 'function' ? replace : returner
172
260
261
+ /** @type {ReplaceFunction } */
173
262
function returner ( ) {
263
+ // @ts -expect-error it’s a string.
174
264
return replace
175
265
}
176
266
}
0 commit comments