Skip to content

Commit 40871fe

Browse files
committedJul 8, 2023
Change API to accept only a tuple, or list of tuples
1 parent 35d35b1 commit 40871fe

File tree

4 files changed

+139
-189
lines changed

4 files changed

+139
-189
lines changed
 

‎index.js

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44
* @typedef {import('./lib/index.js').Find} Find
55
* @typedef {import('./lib/index.js').Replace} Replace
66
* @typedef {import('./lib/index.js').ReplaceFunction} ReplaceFunction
7-
* @typedef {import('./lib/index.js').FindAndReplaceTuple} FindAndReplaceTuple
8-
* @typedef {import('./lib/index.js').FindAndReplaceSchema} FindAndReplaceSchema
97
* @typedef {import('./lib/index.js').FindAndReplaceList} FindAndReplaceList
8+
* @typedef {import('./lib/index.js').FindAndReplaceTuple} FindAndReplaceTuple
109
*/
1110

1211
export {findAndReplace} from './lib/index.js'

‎lib/index.js

Lines changed: 21 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,10 @@
2626
* @typedef {Array<FindAndReplaceTuple>} FindAndReplaceList
2727
* Several find and replaces, in array form.
2828
*
29-
* @typedef {Record<string, Replace>} FindAndReplaceSchema
30-
* Several find and replaces, in object form.
31-
*
32-
* @typedef {[Find, Replace]} FindAndReplaceTuple
29+
* @typedef {[Find, Replace?]} FindAndReplaceTuple
3330
* Find and replace in tuple form.
3431
*
35-
* @typedef {ReplaceFunction | string} Replace
32+
* @typedef {ReplaceFunction | string | null | undefined} Replace
3633
* Thing to replace with.
3734
*
3835
* @callback ReplaceFunction
@@ -67,62 +64,26 @@ import escape from 'escape-string-regexp'
6764
import {visitParents} from 'unist-util-visit-parents'
6865
import {convert} from 'unist-util-is'
6966

70-
const own = {}.hasOwnProperty
71-
7267
/**
7368
* Find patterns in a tree and replace them.
7469
*
7570
* The algorithm searches the tree in *preorder* for complete values in `Text`
7671
* nodes.
7772
* Partial matches are not supported.
7873
*
79-
* @overload
80-
* @param {Nodes} tree
81-
* @param {Find} find
82-
* @param {Replace | null | undefined} [replace]
83-
* @param {Options | null | undefined} [options]
84-
* @returns {undefined}
85-
*
86-
* @overload
87-
* @param {Nodes} tree
88-
* @param {FindAndReplaceSchema | FindAndReplaceList} schema
89-
* @param {Options | null | undefined} [options]
90-
* @returns {undefined}
91-
*
9274
* @param {Nodes} tree
9375
* Tree to change.
94-
* @param {Find | FindAndReplaceList | FindAndReplaceSchema} find
76+
* @param {FindAndReplaceList | FindAndReplaceTuple} list
9577
* Patterns to find.
96-
* @param {Options | Replace | null | undefined} [replace]
97-
* Things to replace with (when `find` is `Find`) or configuration.
9878
* @param {Options | null | undefined} [options]
9979
* Configuration (when `find` is not `Find`).
10080
* @returns {undefined}
10181
* Nothing.
10282
*/
103-
// To do: next major: remove `find` & `replace` combo, remove schema.
104-
export function findAndReplace(tree, find, replace, options) {
105-
/** @type {Options | null | undefined} */
106-
let settings
107-
/** @type {FindAndReplaceList | FindAndReplaceSchema} */
108-
let schema
109-
110-
if (typeof find === 'string' || find instanceof RegExp) {
111-
// @ts-expect-error don’t expect options twice.
112-
schema = [[find, replace]]
113-
settings = options
114-
} else {
115-
schema = find
116-
// @ts-expect-error don’t expect replace twice.
117-
settings = replace
118-
}
119-
120-
if (!settings) {
121-
settings = {}
122-
}
123-
83+
export function findAndReplace(tree, list, options) {
84+
const settings = options || {}
12485
const ignored = convert(settings.ignore || [])
125-
const pairs = toPairs(schema)
86+
const pairs = toPairs(list)
12687
let pairIndex = -1
12788

12889
while (++pairIndex < pairs.length) {
@@ -239,39 +200,33 @@ export function findAndReplace(tree, find, replace, options) {
239200
}
240201

241202
/**
242-
* Turn a schema into pairs.
203+
* Turn a tuple or a list of tuples into pairs.
243204
*
244-
* @param {FindAndReplaceList | FindAndReplaceSchema} schema
205+
* @param {FindAndReplaceList | FindAndReplaceTuple} tupleOrList
245206
* Schema.
246207
* @returns {Pairs}
247208
* Clean pairs.
248209
*/
249-
function toPairs(schema) {
210+
function toPairs(tupleOrList) {
250211
/** @type {Pairs} */
251212
const result = []
252213

253-
if (typeof schema !== 'object') {
254-
throw new TypeError('Expected array or object as schema')
214+
if (!Array.isArray(tupleOrList)) {
215+
throw new TypeError('Expected find and replace tuple or list of tuples')
255216
}
256217

257-
if (Array.isArray(schema)) {
258-
let index = -1
218+
/** @type {FindAndReplaceList} */
219+
// @ts-expect-error: correct.
220+
const list =
221+
!tupleOrList[0] || Array.isArray(tupleOrList[0])
222+
? tupleOrList
223+
: [tupleOrList]
259224

260-
while (++index < schema.length) {
261-
result.push([
262-
toExpression(schema[index][0]),
263-
toFunction(schema[index][1])
264-
])
265-
}
266-
} else {
267-
/** @type {string} */
268-
let key
225+
let index = -1
269226

270-
for (key in schema) {
271-
if (own.call(schema, key)) {
272-
result.push([toExpression(key), toFunction(schema[key])])
273-
}
274-
}
227+
while (++index < list.length) {
228+
const tuple = list[index]
229+
result.push([toExpression(tuple[0]), toFunction(tuple[1])])
275230
}
276231

277232
return result

‎readme.md

Lines changed: 7 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,9 @@
1717
* [Install](#install)
1818
* [Use](#use)
1919
* [API](#api)
20-
* [`findAndReplace(tree, find, replace[, options])`](#findandreplacetree-find-replace-options)
20+
* [`findAndReplace(tree, list[, options])`](#findandreplacetree-list-options)
2121
* [`Find`](#find)
2222
* [`FindAndReplaceList`](#findandreplacelist)
23-
* [`FindAndReplaceSchema`](#findandreplaceschema)
2423
* [`FindAndReplaceTuple`](#findandreplacetuple)
2524
* [`Options`](#options)
2625
* [`RegExpMatchObject`](#regexpmatchobject)
@@ -123,30 +122,21 @@ paragraph[8]
123122
This package exports the identifier [`findAndReplace`][api-findandreplace].
124123
There is no default export.
125124

126-
### `findAndReplace(tree, find, replace[, options])`
125+
### `findAndReplace(tree, list[, options])`
127126

128127
Find patterns in a tree and replace them.
129128

130129
The algorithm searches the tree in *[preorder][]* for complete values in
131130
[`Text`][text] nodes.
132131
Partial matches are not supported.
133132

134-
###### Signatures
135-
136-
* `findAndReplace(tree, find, replace[, options])`
137-
* `findAndReplace(tree, search[, options])`
138-
139133
###### Parameters
140134

141135
* `tree` ([`Node`][node])
142136
— tree to change
143-
* `find` ([`Find`][api-find])
144-
— value to find and remove
145-
* `replace` ([`Replace`][api-replace])
146-
— thing to replace with
147-
* `search` ([`FindAndReplaceSchema`][api-findandreplaceschema] or
148-
[`FindAndReplaceList`][api-findandreplacelist])
149-
— several find and replaces
137+
* `list` ([`FindAndReplaceList`][api-findandreplacelist] or
138+
[`FindAndReplaceTuple`][api-findandreplacetuple])
139+
— one or more find-and-replace pairs
150140
* `options` ([`Options`][api-options])
151141
— configuration
152142

@@ -178,26 +168,14 @@ type FindAndReplaceList = Array<FindAndReplaceTuple>
178168
179169
See [`FindAndReplaceTuple`][api-findandreplacetuple].
180170
181-
### `FindAndReplaceSchema`
182-
183-
Several find and replaces, in object form (TypeScript type).
184-
185-
###### Type
186-
187-
```ts
188-
type FindAndReplaceSchema = Record<string, Replace>
189-
```
190-
191-
See [`Replace`][api-replace].
192-
193171
### `FindAndReplaceTuple`
194172
195173
Find and replace in tuple form (TypeScript type).
196174
197175
###### Type
198176
199177
```ts
200-
type FindAndReplaceTuple = [Find, Replace]
178+
type FindAndReplaceTuple = [Find, Replace?]
201179
```
202180
203181
See [`Find`][api-find] and [`Replace`][api-replace].
@@ -265,7 +243,6 @@ Thing to replace with:
265243
This package is fully typed with [TypeScript][].
266244
It exports the additional types [`Find`][api-find],
267245
[`FindAndReplaceList`][api-findandreplacelist],
268-
[`FindAndReplaceSchema`][api-findandreplaceschema],
269246
[`FindAndReplaceTuple`][api-findandreplacetuple],
270247
[`Options`][api-options],
271248
[`RegExpMatchObject`][api-regexpmatchobject],
@@ -371,7 +348,7 @@ abide by its terms.
371348
372349
[hast-util-find-and-replace]: https://github.com/syntax-tree/hast-util-find-and-replace
373350
374-
[api-findandreplace]: #findandreplacetree-find-replace-options
351+
[api-findandreplace]: #findandreplacetree-list-options
375352
376353
[api-options]: #options
377354
@@ -383,8 +360,6 @@ abide by its terms.
383360
384361
[api-findandreplacelist]: #findandreplacelist
385362
386-
[api-findandreplaceschema]: #findandreplaceschema
387-
388363
[api-findandreplacetuple]: #findandreplacetuple
389364
390365
[api-regexpmatchobject]: #regexpmatchobject

‎test.js

Lines changed: 110 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@ test('findAndReplace', async function (t) {
1616
assert.throws(function () {
1717
// @ts-expect-error: check that the runtime throws an error.
1818
findAndReplace(create(), true)
19-
}, /^TypeError: Expected array or object as schema$/)
19+
}, /Expected find and replace tuple or list of tuples/)
2020
}
2121
)
2222

2323
await t.test('should remove without `replace`', async function () {
2424
const tree = create()
25-
findAndReplace(tree, 'emphasis')
25+
26+
findAndReplace(tree, ['emphasis'])
27+
2628
assert.deepEqual(
2729
tree,
2830
u('paragraph', [
@@ -38,10 +40,10 @@ test('findAndReplace', async function (t) {
3840
})
3941

4042
await t.test(
41-
'should work when given `find` and `replace`',
43+
'should work when given a find-and-replace tuple',
4244
async function () {
4345
const tree = create()
44-
findAndReplace(tree, 'emphasis', '!!!')
46+
findAndReplace(tree, ['emphasis', '!!!'])
4547
assert.deepEqual(
4648
tree,
4749
u('paragraph', [
@@ -62,13 +64,12 @@ test('findAndReplace', async function (t) {
6264
async function () {
6365
const tree = create()
6466

65-
findAndReplace(
66-
tree,
67+
findAndReplace(tree, [
6768
/em(\w+)is/,
6869
function (/** @type {string} */ _, /** @type {string} */ $1) {
6970
return '[' + $1 + ']'
7071
}
71-
)
72+
])
7273

7374
assert.deepEqual(
7475
tree,
@@ -90,9 +91,12 @@ test('findAndReplace', async function (t) {
9091
async function () {
9192
const tree = create()
9293

93-
findAndReplace(tree, 'emphasis', function () {
94-
return ''
95-
})
94+
findAndReplace(tree, [
95+
'emphasis',
96+
function () {
97+
return ''
98+
}
99+
])
96100

97101
assert.deepEqual(
98102
tree,
@@ -114,9 +118,12 @@ test('findAndReplace', async function (t) {
114118
async function () {
115119
const tree = create()
116120

117-
findAndReplace(tree, 'emphasis', function () {
118-
return u('delete', [u('break')])
119-
})
121+
findAndReplace(tree, [
122+
'emphasis',
123+
function () {
124+
return u('delete', [u('break')])
125+
}
126+
])
120127

121128
assert.deepEqual(
122129
tree,
@@ -138,9 +145,12 @@ test('findAndReplace', async function (t) {
138145
async function () {
139146
const tree = create()
140147

141-
findAndReplace(tree, 'emphasis', function () {
142-
return [u('delete', []), u('break')]
143-
})
148+
findAndReplace(tree, [
149+
'emphasis',
150+
function () {
151+
return [u('delete', []), u('break')]
152+
}
153+
])
144154

145155
assert.deepEqual(
146156
tree,
@@ -157,59 +167,43 @@ test('findAndReplace', async function (t) {
157167
}
158168
)
159169

160-
await t.test(
161-
'should work when given `search` as an matrix of strings',
162-
async function () {
163-
const tree = create()
170+
await t.test('should work when given a list of tuples', async function () {
171+
const tree = create()
164172

165-
findAndReplace(tree, [
166-
['emphasis', '!!!'],
167-
['importance', '???']
168-
])
173+
findAndReplace(tree, [
174+
['emphasis', '!!!'],
175+
['importance', '???']
176+
])
169177

170-
assert.deepEqual(
171-
tree,
172-
u('paragraph', [
173-
u('text', 'Some '),
174-
u('emphasis', [u('text', '!!!')]),
175-
u('text', ', '),
176-
u('strong', [u('text', '???')]),
177-
u('text', ', and '),
178-
u('inlineCode', 'code'),
179-
u('text', '.')
180-
])
181-
)
182-
}
183-
)
178+
assert.deepEqual(
179+
tree,
180+
u('paragraph', [
181+
u('text', 'Some '),
182+
u('emphasis', [u('text', '!!!')]),
183+
u('text', ', '),
184+
u('strong', [u('text', '???')]),
185+
u('text', ', and '),
186+
u('inlineCode', 'code'),
187+
u('text', '.')
188+
])
189+
)
190+
})
184191

185192
await t.test(
186-
'should work when given `search` as an object of strings',
193+
'should work when given an empty list of tuples',
187194
async function () {
188195
const tree = create()
189196

190-
findAndReplace(tree, {emp: 'hacks', ',': '!'})
197+
findAndReplace(tree, [])
191198

192-
assert.deepEqual(
193-
tree,
194-
u('paragraph', [
195-
u('text', 'Some '),
196-
u('emphasis', [u('text', 'hacks'), u('text', 'hasis')]),
197-
u('text', '!'),
198-
u('text', ' '),
199-
u('strong', [u('text', 'importance')]),
200-
u('text', '!'),
201-
u('text', ' and '),
202-
u('inlineCode', 'code'),
203-
u('text', '.')
204-
])
205-
)
199+
assert.deepEqual(tree, create())
206200
}
207201
)
208202

209203
await t.test('should work on partial matches', async function () {
210204
const tree = create()
211205

212-
findAndReplace(tree, /\Bmp\B/, '[MP]')
206+
findAndReplace(tree, [/\Bmp\B/, '[MP]'])
213207

214208
assert.deepEqual(
215209
tree,
@@ -228,12 +222,15 @@ test('findAndReplace', async function (t) {
228222
await t.test('should find-and-replace recursively', async function () {
229223
const tree = create()
230224

231-
findAndReplace(tree, {
232-
emphasis() {
233-
return u('link', {url: 'x'}, [u('text', 'importance')])
234-
},
235-
importance: 'something else'
236-
})
225+
findAndReplace(tree, [
226+
[
227+
'emphasis',
228+
function () {
229+
return u('link', {url: 'x'}, [u('text', 'importance')])
230+
}
231+
],
232+
['importance', 'something else']
233+
])
237234

238235
assert.deepEqual(
239236
tree,
@@ -259,7 +256,7 @@ test('findAndReplace', async function (t) {
259256
u('text', '.')
260257
])
261258

262-
findAndReplace(tree, 'importance', '!!!', {ignore: 'strong'})
259+
findAndReplace(tree, ['importance', '!!!'], {ignore: 'strong'})
263260

264261
assert.deepEqual(
265262
tree,
@@ -278,17 +275,26 @@ test('findAndReplace', async function (t) {
278275
u('text', 'Some emphasis, importance, and code.')
279276
])
280277

281-
findAndReplace(tree, {
282-
importance(/** @type {string} */ value) {
283-
return u('strong', [u('text', value)])
284-
},
285-
code(/** @type {string} */ value) {
286-
return u('inlineCode', value)
287-
},
288-
emphasis(/** @type {string} */ value) {
289-
return u('emphasis', [u('text', value)])
290-
}
291-
})
278+
findAndReplace(tree, [
279+
[
280+
'importance',
281+
function (/** @type {string} */ value) {
282+
return u('strong', [u('text', value)])
283+
}
284+
],
285+
[
286+
'code',
287+
function (/** @type {string} */ value) {
288+
return u('inlineCode', value)
289+
}
290+
],
291+
[
292+
'emphasis',
293+
function (/** @type {string} */ value) {
294+
return u('emphasis', [u('text', value)])
295+
}
296+
]
297+
])
292298

293299
assert.deepEqual(tree, create())
294300
})
@@ -346,9 +352,12 @@ test('findAndReplace', async function (t) {
346352
await t.test('should not replace when returning false', async function () {
347353
const tree = create()
348354

349-
findAndReplace(tree, 'emphasis', function () {
350-
return false
351-
})
355+
findAndReplace(tree, [
356+
'emphasis',
357+
function () {
358+
return false
359+
}
360+
])
352361

353362
assert.deepEqual(
354363
tree,
@@ -367,9 +376,12 @@ test('findAndReplace', async function (t) {
367376
await t.test('should not recurse into a replaced value', async function () {
368377
const tree = u('paragraph', [u('text', 'asd.')])
369378

370-
findAndReplace(tree, 'asd', function (/** @type {string} */ d) {
371-
return d
372-
})
379+
findAndReplace(tree, [
380+
'asd',
381+
function (/** @type {string} */ d) {
382+
return d
383+
}
384+
])
373385

374386
assert.deepEqual(tree, u('paragraph', [u('text', 'asd'), u('text', '.')]))
375387
})
@@ -379,9 +391,12 @@ test('findAndReplace', async function (t) {
379391
async function () {
380392
const tree = u('paragraph', [u('text', 'asd.')])
381393

382-
findAndReplace(tree, 'asd', function (/** @type {string} */ d) {
383-
return u('emphasis', [u('text', d)])
384-
})
394+
findAndReplace(tree, [
395+
'asd',
396+
function (/** @type {string} */ d) {
397+
return u('emphasis', [u('text', d)])
398+
}
399+
])
385400

386401
assert.deepEqual(
387402
tree,
@@ -395,9 +410,12 @@ test('findAndReplace', async function (t) {
395410
async function () {
396411
const tree = u('paragraph', [u('text', '.asd')])
397412

398-
findAndReplace(tree, 'asd', function (/** @type {string} */ d) {
399-
return u('emphasis', [u('text', d)])
400-
})
413+
findAndReplace(tree, [
414+
'asd',
415+
function (/** @type {string} */ d) {
416+
return u('emphasis', [u('text', d)])
417+
}
418+
])
401419

402420
assert.deepEqual(
403421
tree,
@@ -411,9 +429,12 @@ test('findAndReplace', async function (t) {
411429
async function () {
412430
const tree = u('paragraph', [u('text', 'asd')])
413431

414-
findAndReplace(tree, 'asd', function (/** @type {string} */ d) {
415-
return u('emphasis', [u('text', d)])
416-
})
432+
findAndReplace(tree, [
433+
'asd',
434+
function (/** @type {string} */ d) {
435+
return u('emphasis', [u('text', d)])
436+
}
437+
])
417438

418439
assert.deepEqual(
419440
tree,
@@ -425,7 +446,7 @@ test('findAndReplace', async function (t) {
425446
await t.test('security: replacer as string (safe)', async function () {
426447
const tree = create()
427448

428-
findAndReplace(tree, 'and', 'alert(1)')
449+
findAndReplace(tree, ['and', 'alert(1)'])
429450

430451
assert.deepEqual(
431452
tree,

0 commit comments

Comments
 (0)
Please sign in to comment.