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+
143import escape from 'escape-string-regexp'
244import { visitParents } from 'unist-util-visit-parents'
345import { convert } from 'unist-util-is'
446
547var own = { } . hasOwnProperty
6- var splice = [ ] . splice
748
49+ /**
50+ * @param {Node } tree
51+ * @param {Find|FindAndReplaceSchema|FindAndReplaceList } find
52+ * @param {Replace|Options } [replace]
53+ * @param {Options } [options]
54+ */
855export function findAndReplace ( tree , find , replace , options ) {
56+ /** @type {Options } */
957 var settings
58+ /** @type {FindAndReplaceSchema|FindAndReplaceList } */
1059 var schema
1160
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.
1363 schema = [ [ find , replace ] ]
64+ settings = options
1465 } else {
1566 schema = find
16- options = replace
67+ // @ts -expect-error don’t expect replace twice.
68+ settings = replace
1769 }
1870
19- settings = options || { }
71+ if ( ! settings ) {
72+ settings = { }
73+ }
2074
2175 search ( tree , settings , handlerFactory ( toPairs ( schema ) ) )
2276
2377 return tree
2478
79+ /**
80+ * @param {Pairs } pairs
81+ * @returns {Handler }
82+ */
2583 function handlerFactory ( pairs ) {
2684 var pair = pairs [ 0 ]
2785
2886 return handler
2987
88+ /**
89+ * @type {Handler }
90+ */
3091 function handler ( node , parent ) {
3192 var find = pair [ 0 ]
3293 var replace = pair [ 1 ]
94+ /** @type {Array.<PhrasingContent> } */
3395 var nodes = [ ]
3496 var start = 0
3597 var index = parent . children . indexOf ( node )
98+ /** @type {number } */
3699 var position
100+ /** @type {RegExpMatchArray } */
37101 var match
102+ /** @type {Handler } */
38103 var subhandler
104+ /** @type {PhrasingContent } */
105+ var child
106+ /** @type {Array.<PhrasingContent>|PhrasingContent|string|false|undefined|null } */
39107 var value
40108
41109 find . lastIndex = 0
@@ -44,17 +112,18 @@ export function findAndReplace(tree, find, replace, options) {
44112
45113 while ( match ) {
46114 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+ }
48121
49122 if ( value !== false ) {
50123 if ( start !== position ) {
51124 nodes . push ( { type : 'text' , value : node . value . slice ( start , position ) } )
52125 }
53126
54- if ( typeof value === 'string' && value . length > 0 ) {
55- value = { type : 'text' , value}
56- }
57-
58127 if ( value ) {
59128 nodes = [ ] . concat ( nodes , value )
60129 }
@@ -77,21 +146,20 @@ export function findAndReplace(tree, find, replace, options) {
77146 nodes . push ( { type : 'text' , value : node . value . slice ( start ) } )
78147 }
79148
80- nodes . unshift ( index , 1 )
81- splice . apply ( parent . children , nodes )
149+ parent . children . splice ( index , 1 , ...nodes )
82150 }
83151
84152 if ( pairs . length > 1 ) {
85153 subhandler = handlerFactory ( pairs . slice ( 1 ) )
86154 position = - 1
87155
88156 while ( ++ position < nodes . length ) {
89- node = nodes [ position ]
157+ child = nodes [ position ]
90158
91- if ( node . type === 'text' ) {
92- subhandler ( node , parent )
159+ if ( child . type === 'text' ) {
160+ subhandler ( child , parent )
93161 } else {
94- search ( node , settings , subhandler )
162+ search ( child , settings , subhandler )
95163 }
96164 }
97165 }
@@ -101,25 +169,33 @@ export function findAndReplace(tree, find, replace, options) {
101169 }
102170}
103171
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 || [ ] )
107180
108181 visitParents ( tree , 'text' , visitor )
109182
110- return result
111-
183+ /** @type {import('unist-util-visit-parents').Visitor<Text> } */
112184 function visitor ( node , parents ) {
113185 var index = - 1
186+ /** @type {Parent } */
114187 var parent
188+ /** @type {Parent } */
115189 var grandparent
116190
117191 while ( ++ index < parents . length ) {
192+ // @ts -expect-error mdast vs. unist parent.
118193 parent = parents [ index ]
119194
120195 if (
121196 ignored (
122197 parent ,
198+ // @ts -expect-error mdast vs. unist parent.
123199 grandparent ? grandparent . children . indexOf ( parent ) : undefined ,
124200 grandparent
125201 )
@@ -134,18 +210,22 @@ function search(tree, settings, handler) {
134210 }
135211}
136212
213+ /**
214+ * @param {FindAndReplaceSchema|FindAndReplaceList } schema
215+ * @returns {Pairs }
216+ */
137217function toPairs ( schema ) {
218+ var index = - 1
219+ /** @type {Pairs } */
138220 var result = [ ]
221+ /** @type {string } */
139222 var key
140- var index
141223
142224 if ( typeof schema !== 'object' ) {
143225 throw new TypeError ( 'Expected array or object as schema' )
144226 }
145227
146- if ( 'length' in schema ) {
147- index = - 1
148-
228+ if ( Array . isArray ( schema ) ) {
149229 while ( ++ index < schema . length ) {
150230 result . push ( [
151231 toExpression ( schema [ index ] [ 0 ] ) ,
@@ -163,14 +243,24 @@ function toPairs(schema) {
163243 return result
164244}
165245
246+ /**
247+ * @param {Find } find
248+ * @returns {RegExp }
249+ */
166250function toExpression ( find ) {
167251 return typeof find === 'string' ? new RegExp ( escape ( find ) , 'g' ) : find
168252}
169253
254+ /**
255+ * @param {Replace } replace
256+ * @returns {ReplaceFunction }
257+ */
170258function toFunction ( replace ) {
171259 return typeof replace === 'function' ? replace : returner
172260
261+ /** @type {ReplaceFunction } */
173262 function returner ( ) {
263+ // @ts -expect-error it’s a string.
174264 return replace
175265 }
176266}
0 commit comments