Socket
Socket
Sign inDemoInstall

mdast-util-find-and-replace

Package Overview
Dependencies
4
Maintainers
2
Versions
11
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 2.1.0 to 2.2.0

lib/index.d.ts

69

index.d.ts

@@ -1,60 +0,9 @@

/**
* @param tree mdast tree
* @param find Value to find and remove. When `string`, escaped and made into a global `RegExp`
* @param [replace] Value to insert.
* * When `string`, turned into a Text node.
* * When `Function`, called with the results of calling `RegExp.exec` as
* arguments, in which case it can return a single or a list of `Node`,
* a `string` (which is wrapped in a `Text` node), or `false` to not replace
* @param [options] Configuration.
*/
export const findAndReplace: ((
tree: Node,
find: Find,
replace?: Replace | undefined,
options?: Options | undefined
) => Node) &
((
tree: Node,
schema: FindAndReplaceSchema | FindAndReplaceList,
options?: Options | undefined
) => Node)
/**
* Configuration.
*/
export type Options = {
/**
* `unist-util-is` test used to assert parents
*/
ignore?: Test
}
export type Root = import('mdast').Root
export type Content = import('mdast').Content
export type PhrasingContent = import('mdast').PhrasingContent
export type Text = import('mdast').Text
export type Node = Content | Root
export type Parent = Extract<Node, import('mdast').Parent>
export type Test = import('unist-util-visit-parents').Test
export type VisitorResult = import('unist-util-visit-parents').VisitorResult
export type RegExpMatchObject = {
index: number
input: string
}
export type Find = string | RegExp
export type Replace = string | ReplaceFunction
export type FindAndReplaceTuple = [Find, Replace]
export type FindAndReplaceSchema = {
[x: string]: Replace
}
export type FindAndReplaceList = Array<[Find, Replace]>
export type Pair = [RegExp, ReplaceFunction]
export type Pairs = Array<[RegExp, ReplaceFunction]>
export type ReplaceFunction = (
...parameters: any[]
) =>
| Array<PhrasingContent>
| PhrasingContent
| string
| false
| undefined
| null
export {findAndReplace} from './lib/index.js'
export type Options = import('./lib/index.js').Options
export type RegExpMatchObject = import('./lib/index.js').RegExpMatchObject
export type Find = import('./lib/index.js').Find
export type Replace = import('./lib/index.js').Replace
export type ReplaceFunction = import('./lib/index.js').ReplaceFunction
export type FindAndReplaceTuple = import('./lib/index.js').FindAndReplaceTuple
export type FindAndReplaceSchema = import('./lib/index.js').FindAndReplaceSchema
export type FindAndReplaceList = import('./lib/index.js').FindAndReplaceList
/**
* @typedef Options Configuration.
* @property {Test} [ignore] `unist-util-is` test used to assert parents
*
* @typedef {import('mdast').Root} Root
* @typedef {import('mdast').Content} Content
* @typedef {import('mdast').PhrasingContent} PhrasingContent
* @typedef {import('mdast').Text} Text
* @typedef {Content|Root} Node
* @typedef {Extract<Node, import('mdast').Parent>} Parent
*
* @typedef {import('unist-util-visit-parents').Test} Test
* @typedef {import('unist-util-visit-parents').VisitorResult} VisitorResult
*
* @typedef RegExpMatchObject
* @property {number} index
* @property {string} input
*
* @typedef {string|RegExp} Find
* @typedef {string|ReplaceFunction} Replace
*
* @typedef {[Find, Replace]} FindAndReplaceTuple
* @typedef {Object.<string, Replace>} FindAndReplaceSchema
* @typedef {Array.<FindAndReplaceTuple>} FindAndReplaceList
*
* @typedef {[RegExp, ReplaceFunction]} Pair
* @typedef {Array.<Pair>} Pairs
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').RegExpMatchObject} RegExpMatchObject
* @typedef {import('./lib/index.js').Find} Find
* @typedef {import('./lib/index.js').Replace} Replace
* @typedef {import('./lib/index.js').ReplaceFunction} ReplaceFunction
* @typedef {import('./lib/index.js').FindAndReplaceTuple} FindAndReplaceTuple
* @typedef {import('./lib/index.js').FindAndReplaceSchema} FindAndReplaceSchema
* @typedef {import('./lib/index.js').FindAndReplaceList} FindAndReplaceList
*/
/**
* @callback ReplaceFunction
* @param {...any} parameters
* @returns {Array.<PhrasingContent>|PhrasingContent|string|false|undefined|null}
*/
import escape from 'escape-string-regexp'
import {visitParents} from 'unist-util-visit-parents'
import {convert} from 'unist-util-is'
const own = {}.hasOwnProperty
/**
* @param tree mdast tree
* @param find Value to find and remove. When `string`, escaped and made into a global `RegExp`
* @param [replace] Value to insert.
* * When `string`, turned into a Text node.
* * When `Function`, called with the results of calling `RegExp.exec` as
* arguments, in which case it can return a single or a list of `Node`,
* a `string` (which is wrapped in a `Text` node), or `false` to not replace
* @param [options] Configuration.
*/
export const findAndReplace =
/**
* @type {(
* ((tree: Node, find: Find, replace?: Replace, options?: Options) => Node) &
* ((tree: Node, schema: FindAndReplaceSchema|FindAndReplaceList, options?: Options) => Node)
* )}
**/
(
/**
* @param {Node} tree
* @param {Find|FindAndReplaceSchema|FindAndReplaceList} find
* @param {Replace|Options} [replace]
* @param {Options} [options]
*/
function (tree, find, replace, options) {
/** @type {Options|undefined} */
let settings
/** @type {FindAndReplaceSchema|FindAndReplaceList} */
let schema
if (typeof find === 'string' || find instanceof RegExp) {
// @ts-expect-error don’t expect options twice.
schema = [[find, replace]]
settings = options
} else {
schema = find
// @ts-expect-error don’t expect replace twice.
settings = replace
}
if (!settings) {
settings = {}
}
const ignored = convert(settings.ignore || [])
const pairs = toPairs(schema)
let pairIndex = -1
while (++pairIndex < pairs.length) {
visitParents(tree, 'text', visitor)
}
return tree
/** @type {import('unist-util-visit-parents').Visitor<Text>} */
function visitor(node, parents) {
let index = -1
/** @type {Parent|undefined} */
let grandparent
while (++index < parents.length) {
const parent = /** @type {Parent} */ (parents[index])
if (
ignored(
parent,
// @ts-expect-error mdast vs. unist parent.
grandparent ? grandparent.children.indexOf(parent) : undefined,
grandparent
)
) {
return
}
grandparent = parent
}
if (grandparent) {
return handler(node, grandparent)
}
}
/**
* @param {Text} node
* @param {Parent} parent
* @returns {VisitorResult}
*/
function handler(node, parent) {
const find = pairs[pairIndex][0]
const replace = pairs[pairIndex][1]
let start = 0
// @ts-expect-error: TS is wrong, some of these children can be text.
let index = parent.children.indexOf(node)
/** @type {Array.<PhrasingContent>} */
let nodes = []
/** @type {number|undefined} */
let position
find.lastIndex = 0
let match = find.exec(node.value)
while (match) {
position = match.index
// @ts-expect-error this is perfectly fine, typescript.
let value = replace(...match, {
index: match.index,
input: match.input
})
if (typeof value === 'string') {
value = value.length > 0 ? {type: 'text', value} : undefined
}
if (value !== false) {
if (start !== position) {
nodes.push({
type: 'text',
value: node.value.slice(start, position)
})
}
if (Array.isArray(value)) {
nodes.push(...value)
} else if (value) {
nodes.push(value)
}
start = position + match[0].length
}
if (!find.global) {
break
}
match = find.exec(node.value)
}
if (position === undefined) {
nodes = [node]
index--
} else {
if (start < node.value.length) {
nodes.push({type: 'text', value: node.value.slice(start)})
}
parent.children.splice(index, 1, ...nodes)
}
return index + nodes.length + 1
}
}
)
/**
* @param {FindAndReplaceSchema|FindAndReplaceList} schema
* @returns {Pairs}
*/
function toPairs(schema) {
/** @type {Pairs} */
const result = []
if (typeof schema !== 'object') {
throw new TypeError('Expected array or object as schema')
}
if (Array.isArray(schema)) {
let index = -1
while (++index < schema.length) {
result.push([
toExpression(schema[index][0]),
toFunction(schema[index][1])
])
}
} else {
/** @type {string} */
let key
for (key in schema) {
if (own.call(schema, key)) {
result.push([toExpression(key), toFunction(schema[key])])
}
}
}
return result
}
/**
* @param {Find} find
* @returns {RegExp}
*/
function toExpression(find) {
return typeof find === 'string' ? new RegExp(escape(find), 'g') : find
}
/**
* @param {Replace} replace
* @returns {ReplaceFunction}
*/
function toFunction(replace) {
return typeof replace === 'function' ? replace : () => replace
}
export {findAndReplace} from './lib/index.js'
{
"name": "mdast-util-find-and-replace",
"version": "2.1.0",
"version": "2.2.0",
"description": "mdast utility to find and replace text in a tree",

@@ -31,2 +31,3 @@ "license": "MIT",

"files": [
"lib/",
"index.d.ts",

@@ -38,3 +39,3 @@ "index.js"

"unist-util-is": "^5.0.0",
"unist-util-visit-parents": "^4.0.0"
"unist-util-visit-parents": "^5.0.0"
},

@@ -45,4 +46,4 @@ "devDependencies": {

"prettier": "^2.0.0",
"remark-cli": "^9.0.0",
"remark-preset-wooorm": "^8.0.0",
"remark-cli": "^10.0.0",
"remark-preset-wooorm": "^9.0.0",
"rimraf": "^3.0.0",

@@ -53,10 +54,10 @@ "tape": "^5.0.0",

"unist-builder": "^3.0.0",
"xo": "^0.42.0"
"xo": "^0.49.0"
},
"scripts": {
"prepack": "npm run build && npm run format",
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
"build": "rimraf \"lib/**/*.d.ts\" \"*.d.ts\" && tsc && type-coverage",
"format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix",
"test-api": "node test.js",
"test-coverage": "c8 --check-coverage --branches 100 --functions 100 --lines 100 --statements 100 --reporter lcov node test.js",
"test-api": "node --conditions development test.js",
"test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api",
"test": "npm run build && npm run format && npm run test-coverage"

@@ -85,5 +86,5 @@ },

"ignoreFiles": [
"index.d.ts"
"lib/index.d.ts"
]
}
}

@@ -11,11 +11,38 @@ # mdast-util-find-and-replace

[**mdast**][mdast] utility to find and replace text in a [*tree*][tree].
[mdast][] utility to find and replace things.
## Contents
* [What is this?](#what-is-this)
* [When should I use this?](#when-should-i-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`findAndReplace(tree, find, replace[, options])`](#findandreplacetree-find-replace-options)
* [Types](#types)
* [Compatibility](#compatibility)
* [Security](#security)
* [Related](#related)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package is a utility that lets you find patterns (`string`, `RegExp`) in
text and replace them with nodes.
## When should I use this?
This utility is typically useful when you have regexes and want to modify mdast.
One example is when you have some form of “mentions” (such as
`/@([a-z][_a-z0-9])\b/gi`) and want to create links to persons from them.
A similar package, [`hast-util-find-and-replace`][hast-util-find-and-replace]
does the same but on [hast][].
## Install
This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c):
Node 12+ is needed to use it and it must be `import`ed instead of `require`d.
This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:
[npm][]:
```sh

@@ -25,2 +52,16 @@ npm install mdast-util-find-and-replace

In Deno with [`esm.sh`][esmsh]:
```js
import {findAndReplace} from 'https://esm.sh/mdast-util-find-and-replace@2'
```
In browsers with [`esm.sh`][esmsh]:
```html
<script type="module">
import {findAndReplace} from 'https://esm.sh/mdast-util-find-and-replace@2?bundle'
</script>
```
## Use

@@ -41,12 +82,14 @@

findAndReplace(tree, 'and', 'or')
findAndReplace(tree, [
[/and/gi, 'or'],
[/emphasis/gi, 'em'],
[/importance/gi, 'strong'],
[
/Some/g,
function ($0) {
return u('link', {url: '//example.com#' + $0}, [u('text', $0)])
}
]
])
findAndReplace(tree, {emphasis: 'em', importance: 'strong'})
findAndReplace(tree, {
Some: function ($0) {
return u('link', {url: '//example.com#' + $0}, [u('text', $0)])
}
})
console.log(inspect(tree))

@@ -59,13 +102,14 @@ ```

paragraph[8]
├─ link[1] [url="//example.com#Some"]
│ └─ text: "Some"
├─ text: " "
├─ emphasis[1]
│ └─ text: "em"
├─ text: " "
├─ text: "or"
├─ text: " "
├─ strong[1]
│ └─ text: "strong"
└─ text: "."
├─0 link[1]
│ │ url: "//example.com#Some"
│ └─0 text "Some"
├─1 text " "
├─2 emphasis[1]
│ └─0 text "em"
├─3 text " "
├─4 text "or"
├─5 text " "
├─6 strong[1]
│ └─0 text "strong"
└─7 text "."
```

@@ -75,10 +119,10 @@

This package exports the following identifiers: `findAndReplace`.
This package exports the identifier `findAndReplace`.
There is no default export.
### `findAndReplace(tree, find[, replace][, options])`
### `findAndReplace(tree, find, replace[, options])`
Find and replace text in [**mdast**][mdast] [*tree*][tree]s.
The algorithm searches the tree in [*preorder*][preorder] for complete values
in [`Text`][text] nodes.
Find patterns in a tree and replace them.
The algorithm searches the tree in *[preorder][]* for complete values in
[`Text`][text] nodes.
Partial matches are not supported.

@@ -88,3 +132,3 @@

* `findAndReplace(tree, find, replace?[, options])`
* `findAndReplace(tree, find, replace[, options])`
* `findAndReplace(tree, search[, options])`

@@ -95,30 +139,41 @@

* `tree` ([`Node`][node])
— [**mdast**][mdast] [*tree*][tree]
* `find` (`string` or `RegExp`)
— Value to find and remove.
When `string`, escaped and made into a global `RegExp`
— value to find and remove (`string`s are escaped and turned into a global
`RegExp`)
* `replace` (`string` or `Function`)
— Value to insert.
When `string`, turned into a [`Text`][text] node.
When `Function`, invoked with the results of calling `RegExp.exec` as
arguments, in which case it can return a single or a list of [`Node`][node],
a `string` (which is wrapped in a [`Text`][text] node), or `false` to not
replace
* `search` (`Object` or `Array`)
— Perform multiple find-and-replaces.
When `Array`, each entry is a tuple (`Array`) of a `find` (at `0`) and
`replace` (at `1`).
When `Object`, each key is a `find` (in string form) and each value is a
`replace`
— value to insert.
`string`s are turned into a [`Text`][text] node,
`Function`s are called with the results of calling `RegExp.exec` as
arguments, and they can return a [`Node`][node], a `string` (which is
wrapped in a [`Text`][text] node), or `false` to not replace
* `search` (`Array` or `Object`)
— perform multiple find-and-replaces.
Either an `Array` of tuples (`Array`s) with `find` (at `0`) and `replace`
(at `1`), or an `Object` where each key is `find` and each value is
the corresponding `replace`
* `options.ignore` (`Test`, default: `[]`)
— Any [`unist-util-is`][test] compatible test.
— any [`unist-util-is`][test] compatible test.
###### Returns
The given, modified, `tree`.
The given `tree` ([`Node`][node]).
## Types
This package is fully typed with [TypeScript][].
It exports the types `Find`, `Replace`, `ReplaceFunction`,
`FindAndReplaceTuple`, `FindAndReplaceSchema`, `FindAndReplaceList`,
`RegExpMatchObject`, and `Options`.
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 12.20+, 14.14+, and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
## Security
Use of `mdast-util-find-and-replace` does not involve [**hast**][hast] or user
content so there are no openings for [cross-site scripting (XSS)][xss] attacks.
Use of `mdast-util-find-and-replace` does not involve [hast][] or user content
so there are no openings for [cross-site scripting (XSS)][xss] attacks.

@@ -128,3 +183,5 @@ ## Related

* [`hast-util-find-and-replace`](https://github.com/syntax-tree/hast-util-find-and-replace)
— hast utility to find and replace text
— find and replace in hast
* [`hast-util-select`](https://github.com/syntax-tree/hast-util-select)
— `querySelector`, `querySelectorAll`, and `matches`
* [`unist-util-select`](https://github.com/syntax-tree/unist-util-select)

@@ -135,8 +192,8 @@ — select unist nodes with CSS-like selectors

See [`contributing.md` in `syntax-tree/.github`][contributing] for ways to get
started.
See [`contributing.md`][contributing] in [`syntax-tree/.github`][health] for
ways to get started.
See [`support.md`][support] for ways to get help.
This project has a [code of conduct][coc].
By interacting with this repository, organization, or community you agree to
By interacting with this repository, organisation, or community you agree to
abide by its terms.

@@ -178,2 +235,8 @@

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
[esmsh]: https://esm.sh
[typescript]: https://www.typescriptlang.org
[license]: license

@@ -183,8 +246,10 @@

[contributing]: https://github.com/syntax-tree/.github/blob/HEAD/contributing.md
[health]: https://github.com/syntax-tree/.github
[support]: https://github.com/syntax-tree/.github/blob/HEAD/support.md
[contributing]: https://github.com/syntax-tree/.github/blob/main/contributing.md
[coc]: https://github.com/syntax-tree/.github/blob/HEAD/code-of-conduct.md
[support]: https://github.com/syntax-tree/.github/blob/main/support.md
[coc]: https://github.com/syntax-tree/.github/blob/main/code-of-conduct.md
[hast]: https://github.com/syntax-tree/hast

@@ -196,4 +261,2 @@

[tree]: https://github.com/syntax-tree/unist#tree
[preorder]: https://github.com/syntax-tree/unist#preorder

@@ -206,1 +269,3 @@

[test]: https://github.com/syntax-tree/unist-util-is#api
[hast-util-find-and-replace]: https://github.com/syntax-tree/hast-util-find-and-replace
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc