Socket
Socket
Sign inDemoInstall

mdast-util-heading-range

Package Overview
Dependencies
Maintainers
2
Versions
16
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mdast-util-heading-range - npm Package Compare versions

Comparing version 3.1.0 to 3.1.1

lib/index.d.ts

38

index.d.ts

@@ -0,32 +1,10 @@

export {headingRange} from './lib/index.js'
export type Handler = import('./lib/index.js').Handler
export type Options = import('./lib/index.js').Options
export type TestFunction = import('./lib/index.js').TestFunction
export type Test = import('./lib/index.js').Test
export type Info = import('./lib/index.js').Info
/**
* Search `node` with `options` and invoke `callback`.
*
* @param {Node} node
* @param {Test|Options} options
* @param {Handler} handler
* Deprecated: use `Info`
*/
export function headingRange(
node: Node,
options: Test | Options,
handler: Handler
): void
export type Parent = import('unist').Parent
export type Node = import('mdast').Root | import('mdast').Content
export type Heading = import('mdast').Heading
export type TestFunction = (value: string, node: Heading) => boolean
export type Test = string | RegExp | TestFunction
export type Options = {
test: Test
ignoreFinalDefinitions?: boolean | undefined
}
export type ZoneInfo = {
start: number
end: number
parent: Parent | null
}
export type Handler = (
start: Heading | undefined,
between: Array<Node>,
end: Node | undefined,
info: ZoneInfo
) => any
export type ZoneInfo = Info
/**
* @typedef {import('unist').Parent} Parent
* @typedef {import('mdast').Root|import('mdast').Content} Node
* @typedef {import('mdast').Heading} Heading
*
* @typedef {(value: string, node: Heading) => boolean} TestFunction
* @typedef {string|RegExp|TestFunction} Test
*
* @typedef Options
* @property {Test} test
* @property {boolean} [ignoreFinalDefinitions=false]
*
* @typedef ZoneInfo
* @property {number} start
* @property {number} end
* @property {Parent|null} parent
*
* @callback Handler
* @param {Heading|undefined} start
* @param {Array.<Node>} between
* @param {Node|undefined} end
* @param {ZoneInfo} info
* @typedef {import('./lib/index.js').Handler} Handler
* @typedef {import('./lib/index.js').Options} Options
* @typedef {import('./lib/index.js').TestFunction} TestFunction
* @typedef {import('./lib/index.js').Test} Test
* @typedef {import('./lib/index.js').Info} Info
*/
import {toString} from 'mdast-util-to-string'
// To do: next major: remove.
/**
* Search `node` with `options` and invoke `callback`.
*
* @param {Node} node
* @param {Test|Options} options
* @param {Handler} handler
* @typedef {Info} ZoneInfo
* Deprecated: use `Info`
*/
// eslint-disable-next-line complexity
export function headingRange(node, options, handler) {
let test = options
/** @type {Array.<Node>} */
const children = 'children' in node ? node.children : []
/** @type {boolean|undefined} */
let ignoreFinalDefinitions
// Object, not regex.
if (test && typeof test === 'object' && !('exec' in test)) {
ignoreFinalDefinitions = test.ignoreFinalDefinitions
test = test.test
}
// Transform a string into an applicable expression.
if (typeof test === 'string') {
test = new RegExp('^(' + test + ')$', 'i')
}
// Regex.
if (test && 'exec' in test) {
test = wrapExpression(test)
}
if (typeof test !== 'function') {
throw new TypeError(
'Expected `string`, `regexp`, or `function` for `test`, not `' +
test +
'`'
)
}
let index = -1
/** @type {number|undefined} */
let start
/** @type {number|undefined} */
let end
/** @type {number|undefined} */
let depth
// Find the range.
while (++index < children.length) {
const child = children[index]
if (child.type === 'heading') {
if (depth && child.depth <= depth) {
end = index
break
}
if (!depth && test(toString(child), child)) {
depth = child.depth
start = index
// Assume no end heading is found.
end = children.length
}
}
}
// When we have a starting heading.
if (depth && end !== undefined && start !== undefined) {
if (ignoreFinalDefinitions) {
while (
children[end - 1].type === 'definition' ||
children[end - 1].type === 'footnoteDefinition'
) {
end--
}
}
/** @type {Array.<Node>} */
const nodes = handler(
// @ts-expect-error `start` points to a heading.
children[start],
children.slice(start + 1, end),
children[end],
{parent: node, start, end: children[end] ? end : null}
)
if (nodes) {
// Ensure no empty nodes are inserted.
// This could be the case if `end` is in `nodes` but no `end` node exists.
/** @type {Array.<Node>} */
const result = []
let index = -1
while (++index < nodes.length) {
if (nodes[index]) result.push(nodes[index])
}
children.splice(start, end - start + 1, ...result)
}
}
}
/**
* Wrap an expression into an assertion function.
* @param {RegExp} expression
* @returns {(value: string) => boolean}
*/
function wrapExpression(expression) {
return assertion
/**
* Assert `value` matches the bound `expression`.
* @param {string} value
* @returns {boolean}
*/
function assertion(value) {
return expression.test(value)
}
}
export {headingRange} from './lib/index.js'
{
"name": "mdast-util-heading-range",
"version": "3.1.0",
"version": "3.1.1",
"description": "mdast utility to use headings as ranges in mdast",

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

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

@@ -42,20 +43,20 @@ "index.js"

"devDependencies": {
"@types/tape": "^4.0.0",
"@types/node": "^18.0.0",
"c8": "^7.0.0",
"mdast-util-from-markdown": "^1.0.0",
"mdast-util-to-markdown": "^1.0.0",
"prettier": "^2.0.0",
"remark": "^13.0.0",
"remark-cli": "^9.0.0",
"remark-preset-wooorm": "^8.0.0",
"rimraf": "^3.0.0",
"remark-cli": "^11.0.0",
"remark-preset-wooorm": "^9.0.0",
"tape": "^5.0.0",
"type-coverage": "^2.0.0",
"typescript": "^4.0.0",
"xo": "^0.42.0"
"xo": "^0.53.0"
},
"scripts": {
"prepack": "npm run build && npm run format",
"build": "rimraf \"*.d.ts\" && tsc && type-coverage",
"build": "tsc --build --clean && tsc --build && 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"

@@ -76,3 +77,3 @@ },

"plugins": [
"preset-wooorm"
"remark-preset-wooorm"
]

@@ -79,0 +80,0 @@ },

@@ -11,11 +11,46 @@ # mdast-util-heading-range

[**mdast**][mdast] utility to use headings as ranges.
[mdast][] utility to find headings and replace the content in their section.
## Contents
* [What is this?](#what-is-this)
* [When should I use this?](#when-should-i-use-this)
* [Install](#install)
* [Use](#use)
* [API](#api)
* [`headingRange(tree, test|options, handler)`](#headingrangetree-testoptions-handler)
* [`Handler`](#handler)
* [`Info`](#info)
* [`Options`](#options)
* [`Test`](#test)
* [`TestFunction`](#testfunction)
* [Types](#types)
* [Compatibility](#compatibility)
* [Security](#security)
* [Related](#related)
* [Contribute](#contribute)
* [License](#license)
## What is this?
This package is a utility that lets you find a certain heading, then takes the
content in their section (from it to the next heading of the same or lower
depth), and calls a given handler with the result, so that you can change or
replace things.
## When should I use this?
This utility is typically useful when you have certain sections that can be
generated.
For example, this utility is used by [`remark-toc`][remark-toc] to update the
above `Contents` heading.
A similar package, [`mdast-zone`][mdast-zone], does the same but uses comments
to mark the start and end of sections.
## 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 14.14+ and 16.0+), install with [npm][]:
[npm][]:
```sh

@@ -25,2 +60,16 @@ npm install mdast-util-heading-range

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

@@ -38,19 +87,17 @@

And our script, `example.js`, looks as follows:
…and a module `example.js`:
```js
import {readSync} from 'to-vfile'
import {read} from 'to-vfile'
import {remark} from 'remark'
import {headingRange} from 'mdast-util-heading-range'
const file = readSync('example.md')
const file = await remark()
.use(myPluginThatReplacesFoo)
.process(await read('example.md'))
remark()
.use(plugin)
.process(file)
.then((file) => {
console.log(String(file))
})
console.log(String(file))
function plugin() {
/** @type {import('unified').Plugin<[], import('mdast').Root>} */
function myPluginThatReplacesFoo() {
return (tree) => {

@@ -66,3 +113,3 @@ headingRange(tree, 'foo', (start, nodes, end) => [

Now, running `node example` yields:
…now running `node example.js` yields:

@@ -79,3 +126,3 @@ ```markdown

This package exports the following identifiers: `headingRange`.
This package exports the identifier [`headingRange`][api-headingrange].
There is no default export.

@@ -85,56 +132,115 @@

Search `tree` ([`Node`][node]) and transform a section without affecting other
parts with `handler` ([`Function`][handler]).
A “section” is a heading that passes `test`, until the next heading of the same
or lower depth, or the end of the document.
Search `tree` for a heading matching `test` and change its “section” with
`handler`.
A “section” ranges from the matched heading until the next heading of the
same or lower depth, or the end of the document.
If `ignoreFinalDefinitions: true`, final definitions “in” the section are
excluded.
##### `options`
###### Parameters
###### `options.test`
* `tree` ([`Node`][node])
— tree to change
* `test` ([`Test`][api-test])
— same as passing `{test: Test}`
* `options` ([`Options`][api-options])
— configuration
* `handler` ([`Handler`][api-handler])
— handle a section
Heading to look for (`string`, `RegExp`, [`Function`][test]).
When `string`, wrapped in `new RegExp('^(' + value + ')$', 'i')`;
when `RegExp`, wrapped in `function (value) {expression.test(value)}`
###### Returns
###### `options.ignoreFinalDefinitions`
Nothing (`void`).
Ignore final definitions otherwise in the section (`boolean`, default: `false`).
### `Handler`
#### `function test(value, node)`
Callback called when a section is found (TypeScript type).
Function invoked for each heading with its content (`string`) and `node`
itself ([`Heading`][heading]) to check if it’s the one to look for.
###### Parameters
* `start` ([`Heading`][heading])
— start of section (a heading node matching `test`)
* `nodes` ([`Array<Node>`][node])
— nodes between `start` and `end`
* `end` ([`Node`][node] or `undefined`)
— end of section, if any
* `info` ([`Info`][api-info])
— extra info
### `Info`
Extra info (TypeScript type).
###### Fields
* `parent` ([`Node`][node])
— parent of the section
* `start` (`number`)
— index of `start` in `parent`
* `end` (`number` or `null`)
— index of `end` in `parent`
###### Returns
`Boolean?`, `true` if this is the heading to use.
Results (`Array<Node | null | undefined>`, optional).
#### `function handler(start, nodes, end?, scope)`
If nothing is returned, nothing will be changed.
If an array of nodes (can include `null` and `undefined`) is returned, the
original section will be replaced by those nodes.
Callback invoked when a range is found.
### `Options`
##### Parameters
Configuration (TypeScript type).
###### `start`
###### Fields
Start of range ([`Heading`][heading]).
* `test` ([`Test`][api-test])
— test for a heading
* `ignoreFinalDefinitions` (`boolean`, default: `false`)
— ignore final definitions otherwise in the section
###### `nodes`
### `Test`
Nodes between `start` and `end` ([`Array.<Node>`][node]).
Test for a heading (TypeScript type).
###### `end`
When `string`, wrapped in `new RegExp('^(' + value + ')$', 'i')`;
when `RegExp`, wrapped in `(value) => expression.test(value)`
End of range, if any ([`Node?`][node]).
###### Type
###### `scope`
```ts
export type Test = string | RegExp | TestFunction
```
Extra info (`Object`):
### `TestFunction`
* `parent` ([`Node`][node]) — Parent of the range
* `start` (`number`) — Index of `start` in `parent`
* `end` (`number?`) — Index of `end` in `parent`
Check if a node matches (TypeScript type).
###### Parameters
* `value` (`string`)
— plain-text heading
* `node` ([`Heading`][heading])
— heading node
###### Returns
Whether this is the heading that is searched for (`boolean`, optional).
## Types
This package is fully typed with [TypeScript][].
This package exports the types [`Handler`][api-handler],
[`Info`][api-info], [`Options`][api-options], [`Test`][api-test],
and [`TestFunction`][api-testfunction].
## Compatibility
Projects maintained by the unified collective are compatible with all maintained
versions of Node.js.
As of now, that is Node.js 14.14+ and 16.0+.
Our projects sometimes work with older versions, but this is not guaranteed.
## Security

@@ -149,4 +255,5 @@

```js
/** @type {import('mdast-util-heading-range').Handler} */
function handler(start, nodes, end) {
return [start, {type: 'html', value: 'alert(1)'}, end]
return [start, {type: 'html', value: '<script>alert(1)</script>'}, end]
}

@@ -166,3 +273,3 @@ ```

Either do not use user input in `handler` or use
[`hast-util-santize`][sanitize].
[`hast-util-santize`][hast-util-sanitize].

@@ -172,12 +279,12 @@ ## Related

* [`mdast-zone`](https://github.com/syntax-tree/mdast-zone)
— comments as ranges or markers instead of headings
— similar but uses comments to mark the start and end of sections
## Contribute
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.

@@ -219,2 +326,8 @@

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

@@ -224,8 +337,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
[mdast]: https://github.com/syntax-tree/mdast

@@ -235,8 +350,4 @@

[handler]: #function-handlerstart-nodes-end-scope
[heading]: https://github.com/syntax-tree/mdast#heading
[test]: #function-testvalue-node
[xss]: https://en.wikipedia.org/wiki/Cross-site_scripting

@@ -246,2 +357,18 @@

[sanitize]: https://github.com/syntax-tree/hast-util-sanitize
[mdast-zone]: https://github.com/syntax-tree/mdast-zone
[hast-util-sanitize]: https://github.com/syntax-tree/hast-util-sanitize
[remark-toc]: https://github.com/remarkjs/remark-toc
[api-headingrange]: #headingrangetree-testoptions-handler
[api-handler]: #handler
[api-options]: #options
[api-test]: #test
[api-testfunction]: #testfunction
[api-info]: #info
SocketSocket SOC 2 Logo

Product

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

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc