Unified

interface for parsing, inspecting, transforming, and serializing content th...

README

[![unified][logo]][site]

[![Build][build-badge]][build] [![Coverage][coverage-badge]][coverage] [![Downloads][downloads-badge]][downloads] [![Size][size-badge]][size] [![Sponsors][sponsors-badge]][collective] [![Backers][backers-badge]][collective] [![Chat][chat-badge]][chat]

unified lets you inspect and transform content with plugins.

Contents


  Use
  API
      [processor()](#processor)
      [processor.use(plugin[, options])](#processoruseplugin-options)
      [processor.parse(file)](#processorparsefile)
      [processor.stringify(tree[, file])](#processorstringifytree-file)
      [processor.run(tree[, file][, done])](#processorruntree-file-done)
      [processor.runSync(tree[, file])](#processorrunsynctree-file)
      [processor.process(file[, done])](#processorprocessfile-done)
      [processor.processSync(file)](#processorprocesssyncfile)
      [processor.data([key[, value]])](#processordatakey-value)
      [processor.freeze()](#processorfreeze)
  [Plugin](#plugin)
      [function attacher(options?)](#function-attacheroptions)
      [function transformer(tree, file[, next])](#function-transformertree-file-next)
  [Preset](#preset)
  Types

What is this?


unified is two things:

  unified is a collective of 500+ free and open source packages that work
    with content as structured data (ASTs)
  unified (this project) is the core package, used in 800k+ projects on GH,
    to process content with plugins

Several ecosystems are built on unified around different kinds of content.
Notably, [remark][] (markdown), [rehype][] (HTML), and [retext][] (natural
language).
These ecosystems can be connected together.

  for more about us, see [unifiedjs.com][site]
  for updates, see [@unifiedjs][twitter] on Twitter
  for questions, see [support][]
  to help, see [contribute][] and [sponsor][] below

When should I use this?


In some cases, you are already using unified.
For example, it’s used in MDX, Gatsby, Docusaurus, etc.
In those cases, you don’t need to add unified yourself but you can include
plugins into those projects.

But the real fun (for some) is to get your hands dirty and work with syntax
trees and build with it yourself.
You can create those projects, or things like Prettier, or your own site
generator.
You can connect utilities together and make your own plugins that check for
problems and transform from one thing to another.

When you are dealing with one type of content (such as markdown), it’s
recommended to use the main package of that ecosystem instead (so remark).
When you are dealing with different kinds of content (such as markdown and
HTML), it’s recommended to use unified itself, and pick and choose the plugins
you need.

Install


This package is [ESM only][esm].
In Node.js (version 12.20+, 14.14+, or 16.0+), install with [npm][]:

  1. ```sh
  2. npm install unified
  3. ```

In Deno with [esm.sh][esmsh]:

  1. ```js
  2. import {unified} from 'https://esm.sh/unified@10'
  3. ```

In browsers with [esm.sh][esmsh]:

  1. ```html
  2. <script type="module">
  3.   import {unified} from 'https://esm.sh/unified@10?bundle'
  4. </script>
  5. ```

Use


  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'
  4. import remarkRehype from 'remark-rehype'
  5. import rehypeDocument from 'rehype-document'
  6. import rehypeFormat from 'rehype-format'
  7. import rehypeStringify from 'rehype-stringify'
  8. import {reporter} from 'vfile-reporter'

  9. const file = await unified()
  10.   .use(remarkParse)
  11.   .use(remarkRehype)
  12.   .use(rehypeDocument, {title: '👋🌍'})
  13.   .use(rehypeFormat)
  14.   .use(rehypeStringify)
  15.   .process('# Hello world!')

  16. console.error(reporter(file))
  17. console.log(String(file))
  18. ```

Yields:

  1. ```txt
  2. no issues found
  3. ```

  1. ```html
  2. <!doctype html>
  3. <html lang="en">
  4.   <head>
  5.     <meta charset="utf-8">
  6.     <title>👋🌍</title>
  7.     <meta name="viewport" content="width=device-width, initial-scale=1">
  8.   </head>
  9.   <body>
  10.     <h1>Hello world!</h1>
  11.   </body>
  12. </html>
  13. ```


Overview


unified is an interface for processing content with syntax trees.
Syntax trees are a representation of content understandable to programs.
Those programs, called [plugins][plugin], take these trees and inspect and
modify them.
To get to the syntax tree from text, there is a [parser][].
To get from that back to text, there is a [compiler][].
This is the [process][] of a processor.

  1. ```ascii
  2. | ........................ process ........................... |
  3. | .......... parse ... | ... run ... | ... stringify ..........|

  4.           +--------+                     +----------+
  5. Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
  6.           +--------+          |          +----------+
  7.                               X
  8.                               |
  9.                        +--------------+
  10.                        | Transformers |
  11.                        +--------------+
  12. ```

Processors

Processors process content.
On its own, unified (the root processor) doesn’t work.
It needs to be configured with plugins to work.
For example:

  1. ```js
  2. const processor = unified()
  3.   .use(remarkParse)
  4.   .use(remarkRehype)
  5.   .use(rehypeDocument, {title: '👋🌍'})
  6.   .use(rehypeFormat)
  7.   .use(rehypeStringify)
  8. ```

That processor can do different things.
It can:

  …parse markdown (parse)
  …turn parsed markdown into HTML and format the HTML (run)
  …compile HTML (stringify)
  …do all of the above (process)

Every processor implements another processor.
To create a processor, call another processor.
The new processor is configured to work the same as its ancestor.
But when the descendant processor is configured in the future it does not affect
the ancestral processor.

When processors are exposed from a module (for example, unified itself) they
should not be configured directly, as that would change their behavior for all
module users.
Those processors are [frozen][freeze] and they should be called to create a
new processor before they are used.

File

When processing a document, metadata is gathered about that document.
[vfile][vfile] is the file format that stores data, metadata, and messages
about files for unified and plugins.

There are several [utilities][vfile-utilities] for working with these files.

Syntax tree

The syntax trees used in unified are [unist][] nodes.
A tree represents a whole document and each [node][] is a plain JavaScript
object with a type field.
The semantics of nodes and the format of syntax trees is defined by other
projects:

  [esast][] — JavaScript
  [hast][] — HTML
  [mdast][] — markdown
  [nlcst][] — natural language
  [xast][] — XML

There are many utilities for working with trees listed in each aforementioned
project and maintained in the [syntax-tree][syntax-tree] organization.
These utilities are a level lower than unified itself and are building blocks
that can be used to make plugins.


Ecosystems

Around each syntax tree is an ecosystem that focusses on that particular kind
of content.
At their core, they parse text to a tree and compile that tree back to text.
They also provide plugins that work with the syntax tree, without requiring
that the end user has knowledge about that tree.

  [rehype][] (hast) — HTML
  [remark][] (mdast) — markdown
  [retext][] (nlcst) — natural language


Plugins

Each aforementioned ecosystem comes with a large set of plugins that you can
pick and choose from to do all kinds of things.

  [List of remark plugins][remark-plugins] ·
    [remarkjs/awesome-remark][awesome-remark] ·
    [remark-plugin topic][topic-remark-plugin]
  [List of rehype plugins][rehype-plugins] ·
    [rehypejs/awesome-rehype][awesome-rehype] ·
    [rehype-plugin topic][topic-rehype-plugin]
  [List of retext plugins][retext-plugins] ·
    [retextjs/awesome-retext][awesome-retext] ·
    [retext-plugin topic][topic-retext-plugin]

There are also a few plugins that work in any ecosystem:

  [unified-diff](https://github.com/unifiedjs/unified-diff)
    — ignore unrelated messages in GitHub Actions and Travis
  [unified-infer-git-meta](https://github.com/unifiedjs/unified-infer-git-meta)
    — infer metadata of a document from Git
  [unified-message-control](https://github.com/unifiedjs/unified-message-control)
    — enable, disable, and ignore messages from content

Configuration

Processors are configured with [plugins][plugin] or with the [data][data]
method.
Most plugins also accept configuration through options.
See each plugin’s readme for more info.

Integrations

unified can integrate with the file system through
[unified-engine][unified-engine].
CLI apps can be created with [unified-args][unified-args], Gulp plugins with
[unified-engine-gulp][unified-engine-gulp], and language servers with
[unified-language-server][unified-language-server].
A streaming interface can be created with [unified-stream][unified-stream].

Programming interface

The [API][] provided by unified allows multiple files to be processed and
gives access to metadata (such as lint messages):

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'
  4. import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide'
  5. import remarkRetext from 'remark-retext'
  6. import retextEnglish from 'retext-english'
  7. import retextEquality from 'retext-equality'
  8. import remarkRehype from 'remark-rehype'
  9. import rehypeStringify from 'rehype-stringify'
  10. import {reporter} from 'vfile-reporter'

  11. const file = await unified()
  12.   .use(remarkParse)
  13.   .use(remarkPresetLintMarkdownStyleGuide)
  14.   .use(remarkRetext, unified().use(retextEnglish).use(retextEquality))
  15.   .use(remarkRehype)
  16.   .use(rehypeStringify)
  17.   .process('*Emphasis* and _stress_, you guys!')

  18. console.error(reporter(file))
  19. console.log(String(file))
  20. ```

Yields:

  1. ```txt
  2.   1:16-1:24  warning  Emphasis should use `*` as a marker                                  emphasis-marker  remark-lint
  3.   1:30-1:34  warning  `guys` may be insensitive, use `people`, `persons`, `folks` instead  gals-man         retext-equality

  4. 2 warnings
  5. ```

  1. ```html
  2. <p><em>Emphasis</em> and <em>stress</em>, you guys!</p>
  3. ```


Transforming between ecosystems

Ecosystems can be combined in two modes.

Bridge mode transforms the tree from one format (origin) to another
(destination).
A different processor runs on the destination tree.
Afterwards, the original processor continues with the origin tree.

Mutate mode also transforms the syntax tree from one format to another.
But the original processor continues transforming the destination tree.

In the previous example (“Programming interface”), remark-retext is used in
bridge mode: the origin syntax tree is kept after retext is done; whereas
remark-rehype is used in mutate mode: it sets a new syntax tree and discards
the origin tree.

The following plugins lets you combine ecosystems:

  [remark-retext][remark-retext] — turn markdown into natural language
  [remark-rehype][remark-rehype] — turn markdown into HTML
  [rehype-retext][rehype-retext] — turn HTML into natural language
  [rehype-remark][rehype-remark] — turn HTML into markdown

API


This package exports the identifier unified (the root processor).
There is no default export.

processor()


Create a processor.

Returns

New [unfrozen][freeze] processor (processor) that is configured to work the
same as its ancestor.
When the descendant processor is configured in the future it does not affect the
ancestral processor.

Example

This example shows how a new processor can be created (from remark) and linked
to stdin(4) and stdout(4).

  1. ```js
  2. import process from 'node:process'
  3. import concatStream from 'concat-stream'
  4. import {remark} from 'remark'

  5. process.stdin.pipe(
  6.   concatStream((buf) => {
  7.     process.stdout.write(String(remark().processSync(buf)))
  8.   })
  9. )
  10. ```

processor.use(plugin[, options])


Configure the processor to use a plugin and optionally configure that plugin
with options.

If the processor is already using a plugin, the previous plugin configuration
is changed based on the options that are passed in.
In other words, the plugin is not added a second time.

👉 Note: use cannot be called on [frozen][freeze] processors.

Call the processor first to create a new unfrozen processor.


Signatures

  processor.use(plugin[, options])
  processor.use(preset)
  processor.use(list)

Parameters

  plugin ([Attacher][plugin])
  options (*, optional) — configuration for plugin
  preset (Object) — object with an optional plugins (set to list),
    and/or an optional settings object
  list (Array) — list of plugins, presets, and pairs (plugin and
    options in an array)

Returns

The processor that use was called on (processor).

Example

There are many ways to pass plugins to .use().
This example gives an overview:

  1. ```js
  2. import {unified} from 'unified'

  3. unified()
  4.   // Plugin with options:
  5.   .use(pluginA, {x: true, y: true})
  6.   // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):
  7.   .use(pluginA, {y: false, z: true})
  8.   // Plugins:
  9.   .use([pluginB, pluginC])
  10.   // Two plugins, the second with options:
  11.   .use([pluginD, [pluginE, {}]])
  12.   // Preset with plugins and settings:
  13.   .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}})
  14.   // Settings only:
  15.   .use({settings: {position: false}})
  16. ```

processor.parse(file)


Parse text to a syntax tree.

👉 Note: parse freezes the processor if not already [frozen][freeze].


👉 Note: parse performs the [parse phase][overview], not the run phase

or other phases.


Parameters

  file ([VFile][vfile]) — any value accepted  as x in new VFile(x)

Returns

Syntax tree representing file ([Node][node]).

Example

This example shows how parse can be used to create a tree from a file.

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'

  4. const tree = unified().use(remarkParse).parse('# Hello world!')

  5. console.log(tree)
  6. ```

Yields:

  1. ```js
  2. {
  3.   type: 'root',
  4.   children: [
  5.     {type: 'heading', depth: 1, children: [Array], position: [Position]}
  6.   ],
  7.   position: {
  8.     start: {line: 1, column: 1, offset: 0},
  9.     end: {line: 1, column: 15, offset: 14}
  10.   }
  11. }
  12. ```

processor.Parser


A parser handles the parsing of text to a syntax tree.
It is used in the [parse phase][overview] and is called with a string and
[VFile][vfile] of the document to parse.

Parser can be a normal function, in which case it must return the syntax
tree representation of the given file ([Node][node]).

Parser can also be a constructor function (a function with a parse field, or
other fields, in its prototype), in which case it is constructed with new.
Instances must have a parse method that is called without arguments and must
return a [Node][node].

processor.stringify(tree[, file])


Compile a syntax tree.

👉 Note: stringify freezes the processor if not already

[frozen][freeze].


👉 Note: stringify performs the [stringify phase][overview], not the run

phase or other phases.


Parameters

  tree ([Node][node]) — tree to compile
  file ([VFile][vfile], optional) — any value accepted as x in
    new VFile(x)

Returns

Textual representation of the tree (string or Buffer, see note).

👉 Note: unified typically compiles by serializing: most

[compilers][compiler] return string (or Buffer).

Some compilers, such as the one configured with

[rehype-react][rehype-react], return other values (in this case, a React

tree).

If you’re using a compiler that doesn’t serialize, expect different result

values.


Example

This example shows how stringify can be used to serialize a syntax tree:

  1. ```js
  2. import {unified} from 'unified'
  3. import rehypeStringify from 'rehype-stringify'
  4. import {h} from 'hastscript'

  5. const tree = h('h1', 'Hello world!')

  6. const doc = unified().use(rehypeStringify).stringify(tree)

  7. console.log(doc)
  8. ```

Yields:

  1. ```html
  2. <h1>Hello world!</h1>
  3. ```

processor.Compiler


A compiler handles the compiling of a syntax tree to something else (in
most cases, text).
It is used in the [stringify phase][overview] and called with a [Node][node]
and [VFile][file] representation of the document to compile.

Compiler can be a normal function, in which case it should return the textual
representation of the given tree (string).

Compiler can also be a constructor function (a function with a compile
field, or other fields, in its prototype), in which case it is constructed
with new.
Instances must have a compile method that is called without arguments and
should return a string.

👉 Note: unified typically compiles by serializing: most compilers

return string (or Buffer).

Some compilers, such as the one configured with

[rehype-react][rehype-react], return other values (in this case, a React

tree).

If you’re using a compiler that doesn’t serialize, expect different result

values.


processor.run(tree[, file][, done])


Run [transformers][transformer] on a syntax tree.

👉 Note: run freezes the processor if not already [frozen][freeze].


👉 Note: run performs the [run phase][overview], not other phases.


Parameters

  tree ([Node][node]) — tree to transform and inspect
  file ([VFile][vfile], optional) — any value accepted as x in
    new VFile(x)
  done ([Function][run-done], optional) — callback

Returns

Nothing if done is given (void).
A [Promise][promise] otherwise.
The promise is rejected with a fatal error or resolved with the transformed
tree ([Node][node]).

Example

This example shows how run can be used to transform a tree:

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkReferenceLinks from 'remark-reference-links'
  4. import {u} from 'unist-builder'

  5. const tree = u('root', [
  6.   u('paragraph', [
  7.     u('link', {href: 'https://example.com'}, [u('text', 'Example Domain')])
  8.   ])
  9. ])

  10. const changedTree = await unified().use(remarkReferenceLinks).run(tree)

  11. console.log(changedTree)
  12. ```

Yields:

  1. ```js
  2. {
  3.   type: 'root',
  4.   children: [
  5.     {type: 'paragraph', children: [Array]},
  6.     {type: 'definition', identifier: '1', title: undefined, url: undefined}
  7.   ]
  8. }
  9. ```

function done(err[, tree, file])


Callback called when transformers are done.
Called with either an error or results.

Parameters

  err (Error, optional) — fatal error
  tree ([Node][node], optional) — transformed tree
  file ([VFile][vfile], optional) — file

processor.runSync(tree[, file])


Run [transformers][transformer] on a syntax tree.
An error is thrown if asynchronous transforms are configured.

👉 Note: runSync freezes the processor if not already

[frozen][freeze].


👉 Note: runSync performs the [run phase][overview], not other phases.


Parameters

  tree ([Node][node]) — tree to transform and inspect
  file ([VFile][vfile], optional) — any value accepted as x in
    new VFile(x)

Returns

Transformed tree ([Node][node]).

processor.process(file[, done])


Process the given file as configured on the processor.

👉 Note: process freezes the processor if not already

[frozen][freeze].


👉 Note: process performs the [parse, run, and stringify

phases][overview].


Parameters

  file ([VFile][vfile]) — any value accepted as x in new VFile(x)
  done ([Function][process-done], optional) — callback

Returns

Nothing if done is given (void).
A [Promise][promise] otherwise.
The promise is rejected with a fatal error or resolved with the processed
file ([VFile][vfile]).

The parsed, transformed, and compiled value is available at
[file.value][vfile-value] (see note).

👉 Note: unified typically compiles by serializing: most

[compilers][compiler] return string (or Buffer).

Some compilers, such as the one configured with

[rehype-react][rehype-react], result in other values (in this case, a React

tree).

If you’re using a compiler that does not serialize, the result is available

at file.result.


Example

This example shows how process can be used to process a file:

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'
  4. import remarkRehype from 'remark-rehype'
  5. import rehypeDocument from 'rehype-document'
  6. import rehypeFormat from 'rehype-format'
  7. import rehypeStringify from 'rehype-stringify'

  8. const file = await unified()
  9.   .use(remarkParse)
  10.   .use(remarkRehype)
  11.   .use(rehypeDocument, {title: '👋🌍'})
  12.   .use(rehypeFormat)
  13.   .use(rehypeStringify)
  14.   .process('# Hello world!')

  15. console.log(String(file))
  16. ```

Yields:

  1. ```html
  2. <!doctype html>
  3. <html lang="en">
  4.   <head>
  5.     <meta charset="utf-8">
  6.     <title>👋🌍</title>
  7.     <meta name="viewport" content="width=device-width, initial-scale=1">
  8.   </head>
  9.   <body>
  10.     <h1>Hello world!</h1>
  11.   </body>
  12. </html>
  13. ```

function done(err, file)


Callback called when the process is done.
Called with either an error or a result.

Parameters

  err (Error, optional) — fatal error
  file ([VFile][vfile]) — processed file

Example

This example shows how process can be used to process a file with a callback.

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'
  4. import remarkGithub from 'remark-github'
  5. import remarkStringify from 'remark-stringify'
  6. import {reporter} from 'vfile-reporter'

  7. unified()
  8.   .use(remarkParse)
  9.   .use(remarkGithub)
  10.   .use(remarkStringify)
  11.   .process('@unifiedjs', function (error, file) {
  12.     console.error(reporter(error || file))
  13.     if (file) {
  14.       console.log(String(file))
  15.     }
  16.   })
  17. ```

Yields:

  1. ```txt
  2. no issues found
  3. ```

  1. ```markdown
  2. [**@unifiedjs**](https://github.com/unifiedjs)
  3. ```

processor.processSync(file)


Process the given file as configured on the processor.
An error is thrown if asynchronous transforms are configured.

👉 Note: processSync freezes the processor if not already

[frozen][freeze].


👉 Note: processSync performs the [parse, run, and stringify

phases][overview].


Parameters

  file ([VFile][vfile]) — any value accepted as x in new VFile(x)

Returns

The processed file ([VFile][vfile]).

The parsed, transformed, and compiled value is available at
[file.value][vfile-value] (see note).

👉 Note: unified typically compiles by serializing: most

[compilers][compiler] return string (or Buffer).

Some compilers, such as the one configured with

[rehype-react][rehype-react], result in other values (in this case, a React

tree).

If you’re using a compiler that does not serialize, the result is available

at file.result.


Example

This example shows how processSync can be used to process a file, if all
transformers are synchronous.

  1. ```js
  2. import {unified} from 'unified'
  3. import remarkParse from 'remark-parse'
  4. import remarkRehype from 'remark-rehype'
  5. import rehypeDocument from 'rehype-document'
  6. import rehypeFormat from 'rehype-format'
  7. import rehypeStringify from 'rehype-stringify'

  8. const processor = unified()
  9.   .use(remarkParse)
  10.   .use(remarkRehype)
  11.   .use(rehypeDocument, {title: '👋🌍'})
  12.   .use(rehypeFormat)
  13.   .use(rehypeStringify)

  14. console.log(String(processor.processSync('# Hello world!')))
  15. ```

Yields:

  1. ```html
  2. <!doctype html>
  3. <html lang="en">
  4.   <head>
  5.     <meta charset="utf-8">
  6.     <title>👋🌍</title>
  7.     <meta name="viewport" content="width=device-width, initial-scale=1">
  8.   </head>
  9.   <body>
  10.     <h1>Hello world!</h1>
  11.   </body>
  12. </html>
  13. ```

processor.data([key[, value]])


Configure the processor with info available to all plugins.
Information is stored in an object.

Typically, options can be given to a specific plugin, but sometimes it makes
sense to have information shared with several plugins.
For example, a list of HTML elements that are self-closing, which is needed
during all [phases][overview].

👉 Note: setting information cannot occur on [frozen][freeze]

processors.

Call the processor first to create a new unfrozen processor.


Signatures

  processor = processor.data(key, value)
  processor = processor.data(values)
  value = processor.data(key)
  info = processor.data()

Parameters

  key (string, optional) — identifier
  value (*, optional) — value to set
  values (Object, optional) — values to set

Returns

  processor — when setting, the processor that data is called on
  value (*) — when getting, the value at key
  info (Object) — without arguments, the key-value store

Example

This example show how to get and set info:

  1. ```js
  2. import {unified} from 'unified'

  3. const processor = unified().data('alpha', 'bravo')

  4. processor.data('alpha') // => 'bravo'

  5. processor.data() // => {alpha: 'bravo'}

  6. processor.data({charlie: 'delta'})

  7. processor.data() // => {charlie: 'delta'}
  8. ```

processor.freeze()


Freeze a processor.
Frozen processors are meant to be extended and not to be configured directly.

When a processor is frozen it cannot be unfrozen.
New processors working the same way can be created by calling the processor.

It’s possible to freeze processors explicitly by calling .freeze().
Processors freeze automatically when [.parse()][parse], [.run()][run],
[.runSync()][run-sync], [.stringify()][stringify], [.process()][process],
or [.processSync()][process-sync] are called.

Returns

The processor that freeze was called on (processor).

Example

This example, index.js, shows how rehype prevents extensions to itself:

  1. ```js
  2. import {unified} from 'unified'
  3. import rehypeParse from 'rehype-parse'
  4. import rehypeStringify from 'rehype-stringify'

  5. export const rehype = unified().use(rehypeParse).use(rehypeStringify).freeze()
  6. ```

That processor can be used and configured like so:

  1. ```js
  2. import {rehype} from 'rehype'
  3. import rehypeFormat from 'rehype-format'
  4. // …

  5. rehype()
  6.   .use(rehypeFormat)
  7.   // …
  8. ```

A similar looking example is broken as operates on the frozen interface.
If this behavior was allowed it would result in unexpected behavior so an error
is thrown.
This is not valid:

  1. ```js
  2. import {rehype} from 'rehype'
  3. import rehypeFormat from 'rehype-format'
  4. // …

  5. rehype
  6.   .use(rehypeFormat)
  7.   // …
  8. ```

Yields:

  1. ```txt
  2. ~/node_modules/unified/index.js:426
  3.     throw new Error(
  4.     ^

  5. Error: Cannot call `use` on a frozen processor.
  6. Create a new processor first, by calling it: use `processor()` instead of `processor`.
  7.     at assertUnfrozen (~/node_modules/unified/index.js:426:11)
  8.     at Function.use (~/node_modules/unified/index.js:165:5)
  9.     
  10. ```

Plugin


Plugins configure the processors they are applied on in the following ways:

  they change the processor, such as the [parser][], the [compiler][], or by
    configuring [data][]
  they specify how to handle trees and files

Plugins are a concept.
They materialize as [Attachers][attacher].

Example

move.js:

  1. ```js
  2. /**
  3. * @typedef Options
  4. *   Configuration (required).
  5. * @property {string} extname
  6. *   File extension to use (must start with `.`).
  7. */

  8. /** @type {import('unified').Plugin<[Options]>} */
  9. export function move(options) {
  10.   if (!options || !options.extname) {
  11.     throw new Error('Missing `options.extname`')
  12.   }

  13.   return function (tree, file) {
  14.     if (file.extname && file.extname !== options.extname) {
  15.       file.extname = options.extname
  16.     }
  17.   }
  18. }
  19. ```

index.md:

  1. ```markdown
  2. # Hello, world!
  3. ```

index.js:

  1. ```js
  2. import {read, write} from 'to-vfile'
  3. import {reporter} from 'vfile-reporter'
  4. import {unified} from 'unified'
  5. import remarkParse from 'remark-parse'
  6. import remarkRehype from 'remark-rehype'
  7. import rehypeStringify from 'rehype-stringify'
  8. import {move} from './move.js'

  9. const file = await unified()
  10.   .use(remarkParse)
  11.   .use(remarkRehype)
  12.   .use(move, {extname: '.html'})
  13.   .use(rehypeStringify)
  14.   .process(await read('index.md'))

  15. console.error(reporter(file))
  16. await write(file) // Written to `index.html`.

  17. ```

Yields:

  1. ```txt
  2. index.md: no issues found
  3. ```

…and in index.html:

  1. ```html
  2. <h1>Hello, world!</h1>
  3. ```

function attacher(options?)


Attachers are materialized plugins.
They are functions that can receive options and configure the processor.

Attachers change the processor, such as the [parser][], the [compiler][],
by configuring [data][], or by specifying how the tree and file are handled.

👉 Note: attachers are called when the processor is [frozen][freeze],

not when they are applied.


Parameters

  this (processor) — processor the attacher is applied to
  options (*, optional) — configuration

Returns

Optional transform ([Transformer][transformer]).

function transformer(tree, file[, next])


Transformers handle syntax trees and files.
They are functions that are called each time a syntax tree and file are passed
through the [run phase][overview].
When an error occurs in them (either because it’s thrown, returned, rejected,
or passed to [next][next]), the process stops.

The run phase is handled by [trough][trough], see its documentation for the
exact semantics of these functions.

Parameters

  tree ([Node][node]) — tree to handle
  file ([VFile][vfile]) —file to handle
  next ([Function][next], optional)

Returns

  void — the next transformer keeps using same tree
  Error — fatal error to stop the process
  [Node][node] — new, changed, tree
* `Promise` — resolved with a new, changed, tree or rejected with an
    Error

function next(err[, tree[, file]])


If the signature of a transformer accepts a third argument, the transformer
may perform asynchronous operations, and must call next().

Parameters

  err (Error, optional) — fatal error to stop the process
  tree ([Node][node], optional) — new, changed, tree
  file ([VFile][vfile], optional) — new, changed, file

Preset


Presets are sharable configuration.
They can contain plugins and settings.

Example

preset.js:

  1. ```js
  2. import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
  3. import remarkPresetLintConsistent from 'remark-preset-lint-consistent'
  4. import remarkCommentConfig from 'remark-comment-config'
  5. import remarkToc from 'remark-toc'
  6. import remarkLicense from 'remark-license'

  7. export const preset = {
  8.   settings: {bullet: '*', emphasis: '*', fences: true},
  9.   plugins: [
  10.     remarkPresetLintRecommended,
  11.     remarkPresetLintConsistent,
  12.     remarkCommentConfig,
  13.     [remarkToc, {maxDepth: 3, tight: true}],
  14.     remarkLicense
  15.   ]
  16. }
  17. ```

example.md:

  1. ```markdown
  2. # Hello, world!

  3. _Emphasis_ and **importance**.

  4. ## Table of contents

  5. ## API

  6. ## License
  7. ```

index.js:

  1. ```js
  2. import {read, write} from 'to-vfile'
  3. import {remark} from 'remark'
  4. import {reporter} from 'vfile-reporter'
  5. import {preset} from './preset.js'

  6. const file = await remark()
  7.   .use(preset)
  8.   .process(await read('example.md'))

  9. console.error(reporter(file))
  10. await write(file)
  11. ```

Yields:

  1. ```txt
  2. example.md: no issues found
  3. ```

example.md now contains:

  1. ```markdown
  2. # Hello, world!

  3. *Emphasis* and **importance**.

  4. ## Table of contents

  5. *   [API](#api)
  6. *   [License](#license)

  7. ## API

  8. ## License

  9. [MIT](license) © [Titus Wormer](https://wooorm.com)
  10. ```

Types


This package is fully typed with [TypeScript][].
It exports the following additional types:

* `Processor`
    — processor, where ParseTree is the tree that the parser creates,
    CurrentTree the tree that the current plugin yields, CompileTree the
    tree that the compiler accepts, and CompileResult the thing that the
    compiler yields
* `FrozenProcessor`
    — like Processor but frozen
* `Plugin`
    — plugin, where PluginParameters are the accepted arguments, Input the
    input value, and Output the output value (see below)
* `Pluggable`
  Preset
    — preset
* `PluginTuple`
    — plugin tuple
* `Pluggable`
    — any usable value, where PluginParameters are the accepted arguments
  PluggableList
    — list of plugins and presets
* `Transformer`
    — transformer, where Input and Output are the input/output trees
  TransformCallback
    — third argument of a transformer
* `Parser`
    — parser as a class or normal function, where Tree is the resulting tree
* `ParserClass`
    — parser class
* `ParserFunction`
    — parser function
* `Compiler`
    — compiler as a class or normal function, where Tree is the accepted tree
    and Result the thing that the compiler yields
* `CompilerClass`
    — compiler class
* `CompilerFunction`
    — compiler function
* `RunCallback`
    — callback to run, where Tree is the resulting tree
* `ProcessCallback`
    — callback to process, where File is the resulting file

For TypeScript to work, it is particularly important to type your plugins
correctly.
We strongly recommend using the Plugin type with its generics and to use the
node types for the syntax trees provided by our packages (as in,
[@types/hast][types-hast], [@types/mdast][types-mdast],
[@types/nlcst][types-nlcst]).

  1. ```js
  2. /**
  3. * @typedef {import('mdast').Root} MdastRoot
  4. * @typedef {import('hast').Root} HastRoot
  5. *
  6. * @typedef Options
  7. *   Configuration (optional).
  8. * @property {boolean} [someField]
  9. *   Some option.
  10. */

  11. // To type options:
  12. /** @type {import('unified').Plugin<[Options?]>} */
  13. export function myPluginAcceptingOptions(options) {
  14.   // `options` is `Options?`.
  15. }

  16. // To type a plugin that works on a certain tree:
  17. /** @type {import('unified').Plugin<[], MdastRoot>} */
  18. export function myRemarkPlugin() {
  19.   return function (tree, file) {
  20.     // `tree` is `MdastRoot`.
  21.   }
  22. }

  23. // To type a plugin that transforms one tree into another:
  24. /** @type {import('unified').Plugin<[], MdastRoot, HastRoot>} */
  25. export function remarkRehype() {
  26.   return function (tree) {
  27.     // `tree` is `MdastRoot`.
  28.     // Result must be `HastRoot`.
  29.   }
  30. }

  31. // To type a plugin that defines a parser:
  32. /** @type {import('unified').Plugin<[], string, MdastRoot>} */
  33. export function remarkParse(options) {}

  34. // To type a plugin that defines a compiler:
  35. /** @type {import('unified').Plugin<[], HastRoot, string>} */
  36. export function rehypeStringify(options) {}
  37. ```

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.

Contribute


See [contributing.md][contributing] in [unifiedjs/.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
abide by its terms.

For info on how to submit a security report, see our
[security policy][security].

Sponsor


Support this effort and give back by sponsoring on [OpenCollective][collective]!

Vercel

Motif

HashiCorp

American Express

GitBook

Gatsby

Netlify

Coinbase

ThemeIsle

Expo

Boost Note

Holloway


You?


Acknowledgments


Preliminary work for unified was done [in 2014][preliminary] for
[retext][] and inspired by [ware][ware].
Further incubation happened in [remark][].
The project was finally [externalised][] in 2015 and [published][] as unified.
The project was authored by @wooorm.

Although unified since moved its plugin architecture to [trough][trough],
thanks to @calvinfo,
@ianstormtaylor, and others for their
work on [ware][ware], as it was a huge initial inspiration.

License


[MIT][license] © [Titus Wormer][author]


[logo]: https://raw.githubusercontent.com/unifiedjs/unified/93862e5/logo.svg?sanitize=true

[build-badge]: https://github.com/unifiedjs/unified/workflows/main/badge.svg

[build]: https://github.com/unifiedjs/unified/actions

[coverage-badge]: https://img.shields.io/codecov/c/github/unifiedjs/unified.svg

[coverage]: https://codecov.io/github/unifiedjs/unified

[downloads-badge]: https://img.shields.io/npm/dm/unified.svg

[downloads]: https://www.npmjs.com/package/unified

[size-badge]: https://img.shields.io/bundlephobia/minzip/unified.svg

[size]: https://bundlephobia.com/result?p=unified

[sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg

[backers-badge]: https://opencollective.com/unified/backers/badge.svg

[collective]: https://opencollective.com/unified

[chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg

[chat]: https://github.com/unifiedjs/unified/discussions

[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c

[esmsh]: https://esm.sh

[typescript]: https://www.typescriptlang.org

[health]: https://github.com/unifiedjs/.github

[contributing]: https://github.com/unifiedjs/.github/blob/main/contributing.md

[support]: https://github.com/unifiedjs/.github/blob/main/support.md

[coc]: https://github.com/unifiedjs/.github/blob/main/code-of-conduct.md

[security]: https://github.com/unifiedjs/.github/blob/main/security.md

[license]: license

[author]: https://wooorm.com

[npm]: https://docs.npmjs.com/cli/install

[site]: https://unifiedjs.com

[twitter]: https://twitter.com/unifiedjs

[rehype]: https://github.com/rehypejs/rehype

[remark]: https://github.com/remarkjs/remark

[retext]: https://github.com/retextjs/retext

[syntax-tree]: https://github.com/syntax-tree

[esast]: https://github.com/syntax-tree/esast

[hast]: https://github.com/syntax-tree/hast

[mdast]: https://github.com/syntax-tree/mdast

[nlcst]: https://github.com/syntax-tree/nlcst

[xast]: https://github.com/syntax-tree/xast

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

[unified-engine]: https://github.com/unifiedjs/unified-engine

[unified-args]: https://github.com/unifiedjs/unified-args

[unified-engine-gulp]: https://github.com/unifiedjs/unified-engine-gulp

[unified-language-server]: https://github.com/unifiedjs/unified-language-server

[unified-stream]: https://github.com/unifiedjs/unified-stream

[remark-rehype]: https://github.com/remarkjs/remark-rehype

[remark-retext]: https://github.com/remarkjs/remark-retext

[rehype-retext]: https://github.com/rehypejs/rehype-retext

[rehype-remark]: https://github.com/rehypejs/rehype-remark

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

[vfile]: https://github.com/vfile/vfile

[vfile-value]: https://github.com/vfile/vfile#vfilevalue

[vfile-utilities]: https://github.com/vfile/vfile#list-of-utilities

[overview]: #overview

[file]: #file

[api]: #api

[process]: #processorprocessfile-done

[process-sync]: #processorprocesssyncfile

[parse]: #processorparsefile

[parser]: #processorparser

[stringify]: #processorstringifytree-file

[run]: #processorruntree-file-done

[run-sync]: #processorrunsynctree-file

[compiler]: #processorcompiler

[data]: #processordatakey-value

[attacher]: #function-attacheroptions

[transformer]: #function-transformertree-file-next

[next]: #function-nexterr-tree-file

[freeze]: #processorfreeze

[plugin]: #plugin

[run-done]: #function-doneerr-tree-file

[process-done]: #function-doneerr-file

[contribute]: #contribute

[sponsor]: #sponsor

[rehype-react]: https://github.com/rehypejs/rehype-react

[trough]: https://github.com/wooorm/trough#function-fninput-next

[promise]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise

[remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins

[rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins

[retext-plugins]: https://github.com/retextjs/retext/blob/main/doc/plugins.md#list-of-plugins

[awesome-remark]: https://github.com/remarkjs/awesome-remark

[awesome-rehype]: https://github.com/rehypejs/awesome-rehype

[awesome-retext]: https://github.com/retextjs/awesome-retext

[topic-remark-plugin]: https://github.com/topics/remark-plugin

[topic-rehype-plugin]: https://github.com/topics/rehype-plugin

[topic-retext-plugin]: https://github.com/topics/retext-plugin

[types-hast]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/hast

[types-mdast]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mdast

[types-nlcst]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nlcst

[preliminary]: https://github.com/retextjs/retext/commit/8fcb1f#diff-168726dbe96b3ce427e7fedce31bb0bc

[externalised]: https://github.com/remarkjs/remark/commit/9892ec#diff-168726dbe96b3ce427e7fedce31bb0bc

[published]: https://github.com/unifiedjs/unified/commit/2ba1cf

[ware]: https://github.com/segmentio/ware