Skip to content

Commit 76204a8

Browse files
committed
Refactor code-style
* Add more docs to JSDoc * Add support for `null` in input of API types
1 parent 07c7b88 commit 76204a8

File tree

2 files changed

+99
-44
lines changed

2 files changed

+99
-44
lines changed

Diff for: lib/index.js

+98-44
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,65 @@
11
/**
2-
* @typedef Options
3-
* Configuration (optional).
4-
* @property {Test} [ignore]
5-
* `unist-util-is` test used to assert parents
6-
*
2+
* @typedef {import('mdast').Parent} MdastParent
73
* @typedef {import('mdast').Root} Root
84
* @typedef {import('mdast').Content} Content
95
* @typedef {import('mdast').PhrasingContent} PhrasingContent
106
* @typedef {import('mdast').Text} Text
11-
* @typedef {Content|Root} Node
12-
* @typedef {Exclude<Extract<Node, import('mdast').Parent>, Root>} Parent
13-
*
147
* @typedef {import('unist-util-visit-parents').Test} Test
158
* @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult
9+
*/
10+
11+
/**
12+
* @typedef {Content | Root} Node
13+
* @typedef {Extract<Node, MdastParent>} Parent
14+
* @typedef {Exclude<Parent, Root>} ContentParent
1615
*
1716
* @typedef RegExpMatchObject
17+
* Info on the match.
1818
* @property {number} index
19+
* The index of the search at which the result was found.
1920
* @property {string} input
20-
* @property {[Root, ...Array<Parent>, Text]} stack
21+
* A copy of the search string in the text node.
22+
* @property {[Root, ...Array<ContentParent>, Text]} stack
23+
* All ancestors of the text node, where the last node is the text itself.
2124
*
22-
* @typedef {string|RegExp} Find
23-
* @typedef {string|ReplaceFunction} Replace
25+
* @callback ReplaceFunction
26+
* Callback called when a search matches.
27+
* @param {...any} parameters
28+
* The parameters are the result of corresponding search expression:
2429
*
25-
* @typedef {[Find, Replace]} FindAndReplaceTuple
26-
* @typedef {Record<string, Replace>} FindAndReplaceSchema
27-
* @typedef {Array<FindAndReplaceTuple>} FindAndReplaceList
30+
* * `value` (`string`) — whole match
31+
* * `...capture` (`Array<string>`) — matches from regex capture groups
32+
* * `match` (`RegExpMatchObject`) — info on the match
33+
* @returns {Array<PhrasingContent> | PhrasingContent | string | false | undefined | null}
34+
* Thing to replace with.
35+
*
36+
* * when `null`, `undefined`, `''`, remove the match
37+
* * …or when `false`, do not replace at all
38+
* * …or when `string`, replace with a text node of that value
39+
* * …or when `Node` or `Array<Node>`, replace with those nodes
2840
*
41+
* @typedef {string | RegExp} Find
42+
* Pattern to find.
43+
*
44+
* Strings are escaped and then turned into global expressions.
45+
*
46+
* @typedef {Array<FindAndReplaceTuple>} FindAndReplaceList
47+
* Several find and replaces, in array form.
48+
* @typedef {Record<string, Replace>} FindAndReplaceSchema
49+
* Several find and replaces, in object form.
50+
* @typedef {[Find, Replace]} FindAndReplaceTuple
51+
* Find and replace in tuple form.
52+
* @typedef {string | ReplaceFunction} Replace
53+
* Thing to replace with.
2954
* @typedef {[RegExp, ReplaceFunction]} Pair
55+
* Normalized find and replace.
3056
* @typedef {Array<Pair>} Pairs
31-
*/
32-
33-
/**
34-
* @callback ReplaceFunction
35-
* @param {...any} parameters
36-
* @returns {Array<PhrasingContent>|PhrasingContent|string|false|undefined|null}
57+
* All find and replaced.
58+
*
59+
* @typedef Options
60+
* Configuration.
61+
* @property {Test | null | undefined} [ignore]
62+
* Test for which nodes to ignore.
3763
*/
3864

3965
import escape from 'escape-string-regexp'
@@ -43,31 +69,42 @@ import {convert} from 'unist-util-is'
4369
const own = {}.hasOwnProperty
4470

4571
/**
46-
* @param tree mdast tree
47-
* @param find Value to find and remove. When `string`, escaped and made into a global `RegExp`
48-
* @param [replace] Value to insert.
49-
* * When `string`, turned into a Text node.
50-
* * When `Function`, called with the results of calling `RegExp.exec` as
51-
* arguments, in which case it can return a single or a list of `Node`,
52-
* a `string` (which is wrapped in a `Text` node), or `false` to not replace
53-
* @param [options] Configuration.
72+
* Find patterns in a tree and replace them.
73+
*
74+
* The algorithm searches the tree in *preorder* for complete values in `Text`
75+
* nodes.
76+
* Partial matches are not supported.
77+
*
78+
* @param tree
79+
* Tree to change.
80+
* @param find
81+
* Patterns to find.
82+
* @param replace
83+
* Things to replace with (when `find` is `Find`) or configuration.
84+
* @param options
85+
* Configuration (when `find` is not `Find`).
86+
* @returns
87+
* Given, modified, tree.
5488
*/
89+
// To do: next major: remove `find` & `replace` combo, remove schema.
5590
export const findAndReplace =
5691
/**
5792
* @type {(
58-
* ((tree: Node, find: Find, replace?: Replace, options?: Options) => Node) &
59-
* ((tree: Node, schema: FindAndReplaceSchema|FindAndReplaceList, options?: Options) => Node)
93+
* (<Tree extends Node>(tree: Tree, find: Find, replace?: Replace | null | undefined, options?: Options | null | undefined) => Tree) &
94+
* (<Tree extends Node>(tree: Tree, schema: FindAndReplaceSchema | FindAndReplaceList, options?: Options | null | undefined) => Tree)
6095
* )}
6196
**/
6297
(
6398
/**
64-
* @param {Node} tree
65-
* @param {Find|FindAndReplaceSchema|FindAndReplaceList} find
66-
* @param {Replace|Options} [replace]
67-
* @param {Options} [options]
99+
* @template {Node} Tree
100+
* @param {Tree} tree
101+
* @param {Find | FindAndReplaceSchema | FindAndReplaceList} find
102+
* @param {Replace | Options | null | undefined} [replace]
103+
* @param {Options | null | undefined} [options]
104+
* @returns {Tree}
68105
*/
69106
function (tree, find, replace, options) {
70-
/** @type {Options|undefined} */
107+
/** @type {Options | null | undefined} */
71108
let settings
72109
/** @type {FindAndReplaceSchema|FindAndReplaceList} */
73110
let schema
@@ -94,21 +131,22 @@ export const findAndReplace =
94131
visitParents(tree, 'text', visitor)
95132
}
96133

134+
// To do next major: don’t return the given tree.
97135
return tree
98136

99137
/** @type {import('unist-util-visit-parents/complex-types.js').BuildVisitor<Root, 'text'>} */
100138
function visitor(node, parents) {
101139
let index = -1
102-
/** @type {Parent|undefined} */
140+
/** @type {Parent | undefined} */
103141
let grandparent
104142

105143
while (++index < parents.length) {
106-
const parent = /** @type {Parent} */ (parents[index])
144+
const parent = parents[index]
107145

108146
if (
109147
ignored(
110148
parent,
111-
// @ts-expect-error mdast vs. unist parent.
149+
// @ts-expect-error: TS doesn’t understand but it’s perfect.
112150
grandparent ? grandparent.children.indexOf(parent) : undefined,
113151
grandparent
114152
)
@@ -120,15 +158,19 @@ export const findAndReplace =
120158
}
121159

122160
if (grandparent) {
123-
// @ts-expect-error: stack is fine.
124161
return handler(node, parents)
125162
}
126163
}
127164

128165
/**
166+
* Handle a text node which is not in an ignored parent.
167+
*
129168
* @param {Text} node
130-
* @param {[Root, ...Array<Parent>]} parents
169+
* Text node.
170+
* @param {Array<Parent>} parents
171+
* Parents.
131172
* @returns {VisitorResult}
173+
* Result.
132174
*/
133175
function handler(node, parents) {
134176
const parent = parents[parents.length - 1]
@@ -140,19 +182,18 @@ export const findAndReplace =
140182
let change = false
141183
/** @type {Array<PhrasingContent>} */
142184
let nodes = []
143-
/** @type {number|undefined} */
144-
let position
145185

146186
find.lastIndex = 0
147187

148188
let match = find.exec(node.value)
149189

150190
while (match) {
151-
position = match.index
191+
const position = match.index
152192
/** @type {RegExpMatchObject} */
153193
const matchObject = {
154194
index: match.index,
155195
input: match.input,
196+
// @ts-expect-error: stack is fine.
156197
stack: [...parents, node]
157198
}
158199
let value = replace(...match, matchObject)
@@ -161,6 +202,7 @@ export const findAndReplace =
161202
value = value.length > 0 ? {type: 'text', value} : undefined
162203
}
163204

205+
// It wasn’t a match after all.
164206
if (value !== false) {
165207
if (start !== position) {
166208
nodes.push({
@@ -202,8 +244,12 @@ export const findAndReplace =
202244
)
203245

204246
/**
205-
* @param {FindAndReplaceSchema|FindAndReplaceList} schema
247+
* Turn a schema into pairs.
248+
*
249+
* @param {FindAndReplaceSchema | FindAndReplaceList} schema
250+
* Schema.
206251
* @returns {Pairs}
252+
* Clean pairs.
207253
*/
208254
function toPairs(schema) {
209255
/** @type {Pairs} */
@@ -237,16 +283,24 @@ function toPairs(schema) {
237283
}
238284

239285
/**
286+
* Turn a find into an expression.
287+
*
240288
* @param {Find} find
289+
* Find.
241290
* @returns {RegExp}
291+
* Expression.
242292
*/
243293
function toExpression(find) {
244294
return typeof find === 'string' ? new RegExp(escape(find), 'g') : find
245295
}
246296

247297
/**
298+
* Turn a replace into a function.
299+
*
248300
* @param {Replace} replace
301+
* Replace.
249302
* @returns {ReplaceFunction}
303+
* Function.
250304
*/
251305
function toFunction(replace) {
252306
return typeof replace === 'function' ? replace : () => replace

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"index.js"
3434
],
3535
"dependencies": {
36+
"@types/mdast": "^3.0.0",
3637
"escape-string-regexp": "^5.0.0",
3738
"unist-util-is": "^5.0.0",
3839
"unist-util-visit-parents": "^5.0.0"

0 commit comments

Comments
 (0)