| # [![unified][logo]](https://unified.js.org/) |
| |
| [![Travis][build-badge]][build] |
| [![Coverage][coverage-badge]][coverage] |
| [![Downloads][downloads-badge]][downloads] |
| [![Size][size-badge]][size] |
| [![Chat][chat-badge]][chat] |
| |
| **unified** is an interface for processing text using syntax trees. Itโs what |
| powers [**remark**][remark], [**retext**][retext], and [**rehype**][rehype], |
| but it also allows for processing between multiple syntaxes. |
| |
| **unified** enabled new exciting projects like [Gatsby][] to pull in markdown, |
| [MDX][] to embed [JSX][], and [Prettier][] to format it. |
| Itโs used to check code for [Storybook][], [debugger.html][] ([Mozilla][]), |
| and [opensource.guide][] ([GitHub][]). |
| |
| * To read about what weโre up to, follow us on [Medium][] and [Twitter][] |
| * For a less technical and more practical introduction to unified, visit |
| [`unified.js.org`][site] and try its introductory [Guides][] |
| * To help us out, see [`contributing.md`][contributing], or become a backer |
| or sponsor on [Open Collective][collective] |
| |
| * * * |
| |
| <!--lint ignore no-html--> |
| |
| **Announcing the unified collective! ๐ |
| [Read more about it on Medium ยป][medium]** |
| |
| ## Sponsors |
| |
| <!--lint ignore no-html maximum-line-length--> |
| |
| <table> |
| <tr valign="top"> |
| <td width="20%" align="center"> |
| <a href="https://zeit.co"><img src="https://avatars1.githubusercontent.com/u/14985020?s=400&v=4"></a> |
| <br><br>๐ฅ |
| <a href="https://zeit.co">ZEIT</a> |
| </td> |
| <td width="20%" align="center"> |
| <a href="https://www.gatsbyjs.org"><img src="https://avatars1.githubusercontent.com/u/12551863?s=400&v=4"></a> |
| <br><br>๐ฅ |
| <a href="https://www.gatsbyjs.org">Gatsby</a></td> |
| <td width="20%" align="center"> |
| <a href="https://compositor.io"><img src="https://avatars1.githubusercontent.com/u/19245838?s=400&v=4"></a> |
| <br><br>๐ฅ |
| <a href="https://compositor.io">Compositor</a> |
| </td> |
| <td width="20%" align="center"> |
| <a href="https://www.holloway.com"><img src="https://avatars1.githubusercontent.com/u/35904294?s=400&v=4"></a> |
| <br><br> |
| <a href="https://www.holloway.com">Holloway</a> |
| </td> |
| <td width="20%" align="center"> |
| <br><br><br><br> |
| <a href="https://opencollective.com/unified"><strong>You?</strong> |
| </td> |
| </tr> |
| </table> |
| |
| ## Installation |
| |
| [npm][]: |
| |
| ```bash |
| npm install unified |
| ``` |
| |
| ## Usage |
| |
| ```js |
| var unified = require('unified') |
| var markdown = require('remark-parse') |
| var remark2rehype = require('remark-rehype') |
| var doc = require('rehype-document') |
| var format = require('rehype-format') |
| var html = require('rehype-stringify') |
| var report = require('vfile-reporter') |
| |
| unified() |
| .use(markdown) |
| .use(remark2rehype) |
| .use(doc) |
| .use(format) |
| .use(html) |
| .process('# Hello world!', function(err, file) { |
| console.error(report(err || file)) |
| console.log(String(file)) |
| }) |
| ``` |
| |
| Yields: |
| |
| ```html |
| no issues found |
| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| </head> |
| <body> |
| <h1>Hello world!</h1> |
| </body> |
| </html> |
| ``` |
| |
| ## Table of Contents |
| |
| * [Description](#description) |
| * [API](#api) |
| * [processor()](#processor) |
| * [processor.use(plugin\[, options\])](#processoruseplugin-options) |
| * [processor.parse(file|value)](#processorparsefilevalue) |
| * [processor.stringify(node\[, file\])](#processorstringifynode-file) |
| * [processor.run(node\[, file\]\[, done\])](#processorrunnode-file-done) |
| * [processor.runSync(node\[, file\])](#processorrunsyncnode-file) |
| * [processor.process(file|value\[, done\])](#processorprocessfilevalue-done) |
| * [processor.processSync(file|value)](#processorprocesssyncfilevalue) |
| * [processor.data(\[key\[, value\]\])](#processordatakey-value) |
| * [processor.freeze()](#processorfreeze) |
| * [Plugin](#plugin) |
| * [function attacher(\[options\])](#function-attacheroptions) |
| * [function transformer(node, file\[, next\])](#function-transformernode-file-next) |
| * [Preset](#preset) |
| * [Contribute](#contribute) |
| * [Acknowledgments](#acknowledgments) |
| * [License](#license) |
| |
| ## Description |
| |
| **unified** is an interface for processing text using syntax trees. Syntax |
| trees are a representation understandable to programs. Those programs, called |
| [**plugin**][plugin]s, take these trees and modify them, amongst other things. |
| To get to the syntax tree from input text thereโs a [**parser**][parser]. To |
| get from that back to text thereโs a [**compiler**][compiler]. This is the |
| [**process**][process] of a **processor**. |
| |
| ```ascii |
| | ....................... process() ......................... | |
| | ......... parse() ..... | run() | ..... stringify() ....... | |
| |
| +--------+ +----------+ |
| Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output |
| +--------+ | +----------+ |
| X |
| | |
| +--------------+ |
| | Transformers | |
| +--------------+ |
| ``` |
| |
| ###### Processors |
| |
| Every processor implements another processor. To create a new processor invoke |
| another processor. This creates a processor that is configured to function 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 behaviour for all |
| module users. Those processors are [**frozen**][freeze] and they should be |
| invoked to create a new processor before they are used. |
| |
| ###### Node |
| |
| The syntax trees used in **unified** are [**Unist**][unist] nodes: plain |
| JavaScript objects with a `type` property. The semantics of those `type`s are |
| defined by other projects. |
| |
| There are several [utilities][unist-utilities] for working with these nodes. |
| |
| ###### List of Processors |
| |
| The following projects process different syntax trees. They parse text to |
| their respective syntax tree and they compile their syntax trees back to text. |
| These processors can be used as-is, or their parsers and compilers can be mixed |
| and matched with **unified** and other plugins to process between different |
| syntaxes. |
| |
| * [**rehype**][rehype] ([**hast**][hast]) โ HTML |
| * [**remark**][remark] ([**mdast**][mdast]) โ Markdown |
| * [**retext**][retext] ([**nlcst**][nlcst]) โ Natural language |
| |
| ###### List of Plugins |
| |
| The below plugins work with **unified**, unrelated to what flavour the syntax |
| tree is in: |
| |
| * [`unified-diff`](https://github.com/unifiedjs/unified-diff) |
| โ Ignore messages for unchanged lines in Travis |
| |
| See [**remark**][remark-plugins], [**rehype**][rehype-plugins], and |
| [**retext**][retext-plugins] for lists of their plugins. |
| |
| ###### File |
| |
| When processing documents metadata is often gathered about that document. |
| [**VFile**][vfile] is a virtual file format which stores data and handles |
| metadata and messages for **unified** and its plugins. |
| |
| There are several [utilities][vfile-utilities] for working with these files. |
| |
| ###### Configuration |
| |
| To configure a processor invoke its [`use`][use] method, supply it a |
| [**plugin**][plugin], and optionally settings. |
| |
| ###### Integrations |
| |
| **unified** can integrate with the file-system through |
| [`unified-engine`][engine]. On top of that, CLI apps can be created with |
| [`unified-args`][args], Gulp plugins with [`unified-engine-gulp`][gulp], and |
| Atom Linters with [`unified-engine-atom`][atom]. |
| |
| A streaming interface is provided through [`unified-stream`][stream]. |
| |
| ###### Programming interface |
| |
| The API gives access to processing metadata (such as lint messages) and |
| supports multiple passed through files: |
| |
| ```js |
| var unified = require('unified') |
| var markdown = require('remark-parse') |
| var styleGuide = require('remark-preset-lint-markdown-style-guide') |
| var remark2retext = require('remark-retext') |
| var english = require('retext-english') |
| var equality = require('retext-equality') |
| var remark2rehype = require('remark-rehype') |
| var html = require('rehype-stringify') |
| var report = require('vfile-reporter') |
| |
| unified() |
| .use(markdown) |
| .use(styleGuide) |
| .use( |
| remark2retext, |
| unified() |
| .use(english) |
| .use(equality) |
| ) |
| .use(remark2rehype) |
| .use(html) |
| .process('*Emphasis* and _importance_, you guys!', function(err, file) { |
| console.error(report(err || file)) |
| console.log(String(file)) |
| }) |
| ``` |
| |
| Yields: |
| |
| ```txt |
| 1:16-1:28 warning Emphasis should use `*` as a marker emphasis-marker remark-lint |
| 1:34-1:38 warning `guys` may be insensitive, use `people`, `persons`, `folks` instead gals-men retext-equality |
| |
| โ 2 warnings |
| <p><em>Emphasis</em> and <em>importance</em>, you guys!</p> |
| ``` |
| |
| ###### Processing between syntaxes |
| |
| The processors can be combined in two modes. |
| |
| **Bridge** mode transforms the syntax tree from one flavour (the origin) to |
| another (the destination). Then, transformations are applied on that tree. |
| Finally, the origin processor continues transforming the original syntax tree. |
| |
| **Mutate** mode also transforms the syntax tree from one flavour to another. |
| But then the origin processor continues transforming the destination syntax |
| 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 original. |
| |
| * [`remark-retext`][remark-retext] |
| * [`remark-rehype`][remark-rehype] |
| * [`rehype-retext`][rehype-retext] |
| * [`rehype-remark`][rehype-remark] |
| |
| ## API |
| |
| ### `processor()` |
| |
| Object describing how to process text. |
| |
| ###### Returns |
| |
| `Function` โ New [**unfrozen**][freeze] processor which is configured to |
| function the same as its ancestor. But when the descendant processor is |
| configured in the future it does not affect the ancestral processor. |
| |
| ###### Example |
| |
| The following example shows how a new processor can be created (from the remark |
| processor) and linked to **stdin**(4) and **stdout**(4). |
| |
| ```js |
| var remark = require('remark') |
| var concat = require('concat-stream') |
| |
| process.stdin.pipe(concat(onconcat)) |
| |
| function onconcat(buf) { |
| var doc = remark() |
| .processSync(buf) |
| .toString() |
| |
| process.stdout.write(doc) |
| } |
| ``` |
| |
| ### `processor.use(plugin[, options])` |
| |
| Configure the processor to use a [**plugin**][plugin] and optionally configure |
| that plugin with options. |
| |
| ###### Signatures |
| |
| * `processor.use(plugin[, options])` |
| * `processor.use(preset)` |
| * `processor.use(list)` |
| |
| ###### Parameters |
| |
| * `plugin` ([`Plugin`][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 |
| |
| `processor` โ The processor on which `use` is invoked. |
| |
| ###### Note |
| |
| `use` cannot be called on [frozen][freeze] processors. Invoke the processor |
| first to create a new unfrozen processor. |
| |
| ###### Example |
| |
| There are many ways to pass plugins to `.use()`. The below example gives an |
| overview. |
| |
| ```js |
| var unified = require('unified') |
| |
| unified() |
| // Plugin with options: |
| .use(plugin, {}) |
| // Plugins: |
| .use([plugin, pluginB]) |
| // Two plugins, the second with options: |
| .use([plugin, [pluginB, {}]]) |
| // Preset with plugins and settings: |
| .use({plugins: [plugin, [pluginB, {}]], settings: {position: false}}) |
| // Settings only: |
| .use({settings: {position: false}}) |
| |
| function plugin() {} |
| function pluginB() {} |
| ``` |
| |
| ### `processor.parse(file|value)` |
| |
| Parse text to a syntax tree. |
| |
| ###### Parameters |
| |
| * `file` ([`VFile`][file]) |
| โ Or anything which can be given to `vfile()` |
| |
| ###### Returns |
| |
| [`Node`][node] โ Syntax tree representation of input. |
| |
| ###### Note |
| |
| `parse` [freezes][freeze] the processor if not already frozen. |
| |
| `parse` does not apply [transformers from the run phase][description] to the |
| [syntax tree][node]. |
| |
| ###### Example |
| |
| The below example shows how the `parse` function can be used to create a |
| [syntax tree][node] from a [file][]. |
| |
| ```js |
| var unified = require('unified') |
| var markdown = require('remark-parse') |
| |
| var tree = unified() |
| .use(markdown) |
| .parse('# Hello world!') |
| |
| console.log(tree) |
| ``` |
| |
| Yields: |
| |
| ```js |
| { type: 'root', |
| children: |
| [ { type: 'heading', |
| depth: 1, |
| children: [Array], |
| position: [Position] } ], |
| position: |
| { start: { line: 1, column: 1, offset: 0 }, |
| end: { line: 1, column: 15, offset: 14 } } } |
| ``` |
| |
| #### `processor.Parser` |
| |
| Function handling the parsing of text to a syntax tree. Used in the |
| [**parse**][parse] phase in the process and invoked with a `string` and |
| [`VFile`][file] representation of the document to parse. |
| |
| `Parser` can be a normal function in which case it must return a |
| [`Node`][node]: the syntax tree representation of the given file. |
| |
| `Parser` can also be a constructor function (a function with keys in its |
| `prototype`) in which case itโs invoked with `new`. Instances must have a |
| `parse` method which is invoked without arguments and must return a |
| [`Node`][node]. |
| |
| ### `processor.stringify(node[, file])` |
| |
| Compile a syntax tree to text. |
| |
| ###### Parameters |
| |
| * `node` ([`Node`][node]) |
| * `file` ([`VFile`][file], optional); |
| โ Or anything which can be given to `vfile()` |
| |
| ###### Returns |
| |
| `string` โ String representation of the syntax tree file. |
| |
| ###### Note |
| |
| `stringify` [freezes][freeze] the processor if not already frozen. |
| |
| `stringify` does not apply [transformers from the run phase][description] |
| to the [syntax tree][node]. |
| |
| ###### Example |
| |
| The below example shows how the `stringify` function can be used to generate a |
| [file][] from a [syntax tree][node]. |
| |
| ```js |
| var unified = require('unified') |
| var html = require('rehype-stringify') |
| var h = require('hastscript') |
| |
| var tree = h('h1', 'Hello world!') |
| |
| var doc = unified() |
| .use(html) |
| .stringify(tree) |
| |
| console.log(doc) |
| ``` |
| |
| Yields: |
| |
| ```html |
| <h1>Hello world!</h1> |
| ``` |
| |
| #### `processor.Compiler` |
| |
| Function handling the compilation of syntax tree to a text. Used in the |
| [**stringify**][stringify] phase in the process and invoked with a |
| [`Node`][node] and [`VFile`][file] representation of the document to stringify. |
| |
| `Compiler` can be a normal function in which case it must return a `string`: |
| the text representation of the given syntax tree. |
| |
| `Compiler` can also be a constructor function (a function with keys in its |
| `prototype`) in which case itโs invoked with `new`. Instances must have a |
| `compile` method which is invoked without arguments and must return a `string`. |
| |
| ### `processor.run(node[, file][, done])` |
| |
| Transform a syntax tree by applying [**plugin**][plugin]s to it. |
| |
| ###### Parameters |
| |
| * `node` ([`Node`][node]) |
| * `file` ([`VFile`][file], optional) |
| โ Or anything which can be given to `vfile()` |
| * `done` ([`Function`][run-done], optional) |
| |
| ###### Returns |
| |
| [`Promise`][promise] if `done` is not given. Rejected with an error, or |
| resolved with the resulting syntax tree. |
| |
| ###### Note |
| |
| `run` [freezes][freeze] the processor if not already frozen. |
| |
| #### `function done(err[, node, file])` |
| |
| Invoked when transformation is complete. Either invoked with an error or a |
| syntax tree and a file. |
| |
| ###### Parameters |
| |
| * `err` (`Error`) โ Fatal error |
| * `node` ([`Node`][node]) |
| * `file` ([`VFile`][file]) |
| |
| ###### Example |
| |
| The below example shows how the `run` function can be used to transform a |
| [syntax tree][node]. |
| |
| ```js |
| var unified = require('unified') |
| var references = require('remark-reference-links') |
| var u = require('unist-builder') |
| |
| var tree = u('root', [ |
| u('paragraph', [ |
| u('link', {href: 'https://example.com'}, [u('text', 'Example Domain')]) |
| ]) |
| ]) |
| |
| unified() |
| .use(references) |
| .run(tree, function(err, tree) { |
| if (err) throw err |
| console.log(tree) |
| }) |
| ``` |
| |
| Yields: |
| |
| ```js |
| { type: 'root', |
| children: |
| [ { type: 'paragraph', children: [Array] }, |
| { type: 'definition', |
| identifier: '1', |
| title: undefined, |
| url: undefined } ] } |
| ``` |
| |
| ### `processor.runSync(node[, file])` |
| |
| Transform a syntax tree by applying [**plugin**][plugin]s to it. |
| |
| If asynchronous [**plugin**][plugin]s are configured an error is thrown. |
| |
| ###### Parameters |
| |
| * `node` ([`Node`][node]) |
| * `file` ([`VFile`][file], optional) |
| โ Or anything which can be given to `vfile()` |
| |
| ###### Returns |
| |
| [`Node`][node] โ The given syntax tree. |
| |
| ###### Note |
| |
| `runSync` [freezes][freeze] the processor if not already frozen. |
| |
| ### `processor.process(file|value[, done])` |
| |
| Process the given representation of a file as configured on the processor. The |
| process invokes `parse`, `run`, and `stringify` internally. |
| |
| ###### Parameters |
| |
| * `file` ([`VFile`][file]) |
| * `value` (`string`) โ String representation of a file |
| * `done` ([`Function`][process-done], optional) |
| |
| ###### Returns |
| |
| [`Promise`][promise] if `done` is not given. Rejected with an error or |
| resolved with the resulting file. |
| |
| ###### Note |
| |
| `process` [freezes][freeze] the processor if not already frozen. |
| |
| ###### Example |
| |
| The below example shows how the `process` function can be used to process a |
| [file][] whether plugins are asynchronous or not with Promises. |
| |
| ```js |
| var unified = require('unified') |
| var markdown = require('remark-parse') |
| var remark2rehype = require('remark-rehype') |
| var doc = require('rehype-document') |
| var format = require('rehype-format') |
| var html = require('rehype-stringify') |
| |
| unified() |
| .use(markdown) |
| .use(remark2rehype) |
| .use(doc) |
| .use(format) |
| .use(html) |
| .process('# Hello world!') |
| .then( |
| function(file) { |
| console.log(String(file)) |
| }, |
| function(err) { |
| console.error(String(err)) |
| } |
| ) |
| ``` |
| |
| Yields: |
| |
| ```html |
| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| </head> |
| <body> |
| <h1>Hello world!</h1> |
| </body> |
| </html> |
| ``` |
| |
| #### `function done(err, file)` |
| |
| Invoked when the process is complete. Invoked with a fatal error, if any, and |
| the [`VFile`][file]. |
| |
| ###### Parameters |
| |
| * `err` (`Error`, optional) โ Fatal error |
| * `file` ([`VFile`][file]) |
| |
| ###### Example |
| |
| The below example shows how the `process` function can be used to process a |
| [file][] whether plugins are asynchronous or not with a callback. |
| |
| ```js |
| var unified = require('unified') |
| var parse = require('remark-parse') |
| var stringify = require('remark-stringify') |
| var github = require('remark-github') |
| var report = require('vfile-reporter') |
| |
| unified() |
| .use(parse) |
| .use(github) |
| .use(stringify) |
| .process('@mention', function(err, file) { |
| console.error(report(err || file)) |
| console.log(String(file)) |
| }) |
| ``` |
| |
| Yields: |
| |
| ```markdown |
| no issues found |
| [**@mention**](https://github.com/blog/821) |
| ``` |
| |
| ### `processor.processSync(file|value)` |
| |
| Process the given representation of a file as configured on the processor. The |
| process invokes `parse`, `run`, and `stringify` internally. |
| |
| If asynchronous [**plugin**][plugin]s are configured an error is thrown. |
| |
| ###### Parameters |
| |
| * `file` ([`VFile`][file]) |
| * `value` (`string`) โ String representation of a file |
| |
| ###### Returns |
| |
| [`VFile`][file] โ Virtual file with modified [`contents`][vfile-contents]. |
| |
| ###### Note |
| |
| `processSync` [freezes][freeze] the processor if not already frozen. |
| |
| ###### Example |
| |
| The below example shows how the `processSync` function can be used to process a |
| [file][] if all plugins are known to be synchronous. |
| |
| ```js |
| var unified = require('unified') |
| var markdown = require('remark-parse') |
| var remark2rehype = require('remark-rehype') |
| var doc = require('rehype-document') |
| var format = require('rehype-format') |
| var html = require('rehype-stringify') |
| |
| var processor = unified() |
| .use(markdown) |
| .use(remark2rehype) |
| .use(doc) |
| .use(format) |
| .use(html) |
| |
| console.log(processor.processSync('# Hello world!').toString()) |
| ``` |
| |
| Yields: |
| |
| ```html |
| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1"> |
| </head> |
| <body> |
| <h1>Hello world!</h1> |
| </body> |
| </html> |
| ``` |
| |
| ### `processor.data([key[, value]])` |
| |
| Get or set information in an in-memory key-value store accessible to all phases |
| of the process. An example is a list of HTML elements which are self-closing, |
| which is needed when parsing, transforming, and compiling HTML. |
| |
| ###### Parameters |
| |
| * `key` (`string`, optional) โ Identifier |
| * `value` (`*`, optional) โ Value to set. Omit if getting `key` |
| |
| ###### Returns |
| |
| * `processor` โ If setting, the processor on which `data` is invoked |
| * `*` โ If getting, the value at `key` |
| * `object` - Without arguments, the key-value store |
| |
| ###### Note |
| |
| Setting information with `data` cannot occur on [frozen][freeze] processors. |
| Invoke the processor first to create a new unfrozen processor. |
| |
| ###### Example |
| |
| The following example show how to get and set information: |
| |
| ```js |
| var unified = require('unified') |
| |
| var processor = unified() |
| .data('alpha', 'bravo') |
| .data('alpha') // => 'bravo' |
| |
| processor.data() // {alpha: 'bravo'} |
| ``` |
| |
| ### `processor.freeze()` |
| |
| Freeze a processor. Frozen processors are meant to be extended and not to be |
| configured or processed directly. |
| |
| Once a processor is frozen it cannot be unfrozen. New processors functioning |
| just like it can be created by invoking the processor. |
| |
| Itโs possible to freeze processors explicitly, by calling `.freeze()`, but |
| [`.parse()`][parse], [`.run()`][run], [`.stringify()`][stringify], and |
| [`.process()`][process] call `.freeze()` to freeze a processor too. |
| |
| ###### Returns |
| |
| `Processor` โ The processor on which `freeze` is invoked. |
| |
| ###### Example |
| |
| The following example, `index.js`, shows how [**rehype**][rehype] prevents |
| extensions to itself: |
| |
| ```js |
| var unified = require('unified') |
| var parse = require('rehype-parse') |
| var stringify = require('rehype-stringify') |
| |
| module.exports = unified() |
| .use(parse) |
| .use(stringify) |
| .freeze() |
| ``` |
| |
| The below example, `a.js`, shows how that processor can be used and configured. |
| |
| ```js |
| var rehype = require('rehype') |
| var format = require('rehype-format') |
| // ... |
| |
| rehype() |
| .use(format) |
| // ... |
| ``` |
| |
| The below example, `b.js`, shows a similar looking example which operates on |
| the frozen [**rehype**][rehype] interface. If this behaviour was allowed it |
| would result in unexpected behaviour so an error is thrown. **This is |
| invalid**: |
| |
| ```js |
| var rehype = require('rehype') |
| var format = require('rehype-format') |
| // ... |
| |
| rehype |
| .use(format) |
| // ... |
| ``` |
| |
| Yields: |
| |
| ```txt |
| ~/node_modules/unified/index.js:440 |
| throw new Error( |
| ^ |
| |
| Error: Cannot invoke `use` on a frozen processor. |
| Create a new processor first, by invoking it: use `processor()` instead of `processor`. |
| at assertUnfrozen (~/node_modules/unified/index.js:440:11) |
| at Function.use (~/node_modules/unified/index.js:172:5) |
| at Object.<anonymous> (~/b.js:6:4) |
| ``` |
| |
| ## `Plugin` |
| |
| **unified** plugins change the way the applied-on processor works in the |
| following ways: |
| |
| * They modify the [**processor**][processor]: such as changing the parser, |
| the compiler, or linking it to other processors |
| * They transform [**syntax tree**][node] representation of files |
| * They modify metadata of files |
| |
| Plugins are a concept. They materialise as [`attacher`][attacher]s. |
| |
| ###### Example |
| |
| `move.js`: |
| |
| ```js |
| module.exports = move |
| |
| function move(options) { |
| var expected = (options || {}).extname |
| |
| if (!expected) { |
| throw new Error('Missing `extname` in options') |
| } |
| |
| return transformer |
| |
| function transformer(tree, file) { |
| if (file.extname && file.extname !== expected) { |
| file.extname = expected |
| } |
| } |
| } |
| ``` |
| |
| `index.js`: |
| |
| ```js |
| var unified = require('unified') |
| var parse = require('remark-parse') |
| var remark2rehype = require('remark-rehype') |
| var stringify = require('rehype-stringify') |
| var vfile = require('to-vfile') |
| var report = require('vfile-reporter') |
| var move = require('./move') |
| |
| unified() |
| .use(parse) |
| .use(remark2rehype) |
| .use(move, {extname: '.html'}) |
| .use(stringify) |
| .process(vfile.readSync('index.md'), function(err, file) { |
| console.error(report(err || file)) |
| if (file) { |
| vfile.writeSync(file) // Written to `index.html`. |
| } |
| }) |
| ``` |
| |
| ### `function attacher([options])` |
| |
| An attacher is the thing passed to [`use`][use]. It configures the processor |
| and in turn can receive options. |
| |
| Attachers can configure processors, such as by interacting with parsers and |
| compilers, linking them to other processors, or by specifying how the syntax |
| tree is handled. |
| |
| ###### Context |
| |
| The context object is set to the invoked on [`processor`][processor]. |
| |
| ###### Parameters |
| |
| * `options` (`*`, optional) โ Configuration |
| |
| ###### Returns |
| |
| [`transformer`][transformer] โ Optional. |
| |
| ###### Note |
| |
| Attachers are invoked when the processor is [frozen][freeze]: either when |
| `.freeze()` is called explicitly, or when [`.parse()`][parse], [`.run()`][run], |
| [`.stringify()`][stringify], or [`.process()`][process] is called for the first |
| time. |
| |
| ### `function transformer(node, file[, next])` |
| |
| Transformers modify the syntax tree or metadata of a file. A transformer is a |
| function which is invoked each time a file is passed through the transform |
| phase. If an error occurs (either because itโs thrown, returned, rejected, or |
| passed to [`next`][next]), the process stops. |
| |
| The transformation process in **unified** is handled by [`trough`][trough], see |
| itโs documentation for the exact semantics of transformers. |
| |
| ###### Parameters |
| |
| * `node` ([`Node`][node]) |
| * `file` ([`VFile`][file]) |
| * `next` ([`Function`][next], optional) |
| |
| ###### Returns |
| |
| * `Error` โ Can be returned to stop the process |
| * [`Node`][node] โ Can be returned and results in further transformations |
| and `stringify`s to be performed on the new tree |
| * `Promise` โ If a promise is returned, the function is asynchronous, and |
| **must** be resolved (optionally with a [`Node`][node]) or rejected |
| (optionally with an `Error`) |
| |
| #### `function next(err[, tree[, file]])` |
| |
| If the signature of a transformer includes `next` (third argument), the |
| function **may** finish asynchronous, and **must** invoke `next()`. |
| |
| ###### Parameters |
| |
| * `err` (`Error`, optional) โ Stop the process |
| * `node` ([`Node`][node], optional) โ New syntax tree |
| * `file` ([`VFile`][file], optional) โ New virtual file |
| |
| ## `Preset` |
| |
| Presets provide a potentially sharable way to configure processors. They can |
| contain multiple plugins and optionally settings as well. |
| |
| ###### Example |
| |
| `preset.js`: |
| |
| ```js |
| exports.settings = {bullet: '*', fences: true} |
| |
| exports.plugins = [ |
| require('remark-preset-lint-recommended'), |
| require('remark-comment-config'), |
| require('remark-preset-lint-markdown-style-guide'), |
| [require('remark-toc'), {maxDepth: 3, tight: true}], |
| require('remark-github') |
| ] |
| ``` |
| |
| `index.js`: |
| |
| ```js |
| var remark = require('remark') |
| var vfile = require('to-vfile') |
| var report = require('vfile-reporter') |
| var preset = require('./preset') |
| |
| remark() |
| .use(preset) |
| .process(vfile.readSync('index.md'), function(err, file) { |
| console.error(report(err || file)) |
| |
| if (file) { |
| vfile.writeSync(file) |
| } |
| }) |
| ``` |
| |
| ## Contribute |
| |
| **unified** is built by people just like you! |
| Check out [`contributing.md`][contributing] for ways to get started. |
| |
| This project has a [Code of Conduct][coc]. |
| By interacting with this repository, organisation, or community you agree to |
| abide by its terms. |
| |
| Want to chat with the community and contributors? |
| Join us in [spectrum][chat]! |
| |
| Have an idea for a cool new utility or tool? |
| Thatโs great! |
| If you want feedback, help, or just to share it with the world you can do so by |
| creating an issue in the [`unifiedjs/ideas`][ideas] repository! |
| |
| ## Acknowledgments |
| |
| Preliminary work for unified was done [in 2014][preliminary] for |
| [**retext**][retext] and inspired by [`ware`][ware]. Further incubation |
| happened in [**remark**][remark]. The project was finally [externalised][] |
| in 2015 and [published][] as `unified`. The project was authored by |
| [**@wooorm**](https://github.com/wooorm). |
| |
| Although `unified` since moved itโs plugin architecture to [`trough`][trough], |
| thanks to [**@calvinfo**](https://github.com/calvinfo), |
| [**@ianstormtaylor**](https://github.com/ianstormtaylor), and others for their |
| work on [`ware`][ware], which was a huge initial inspiration. |
| |
| ## License |
| |
| [MIT][license] ยฉ [Titus Wormer][author] |
| |
| <!-- Definitions --> |
| |
| [logo]: https://raw.githubusercontent.com/unifiedjs/unified/39917ea/logo.svg?sanitize=true |
| |
| [build-badge]: https://img.shields.io/travis/unifiedjs/unified/master.svg |
| |
| [build]: https://travis-ci.org/unifiedjs/unified |
| |
| [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 |
| |
| [chat-badge]: https://img.shields.io/badge/join%20the%20community-on%20spectrum-7b16ff.svg |
| |
| [chat]: https://spectrum.chat/unified |
| |
| [npm]: https://docs.npmjs.com/cli/install |
| |
| [license]: license |
| |
| [author]: https://wooorm.com |
| |
| [site]: https://unified.js.org |
| |
| [medium]: https://medium.com/unifiedjs |
| |
| [twitter]: https://twitter.com/unifiedjs |
| |
| [collective]: https://opencollective.com/unified |
| |
| [guides]: https://unified.js.org/#guides |
| |
| [rehype]: https://github.com/rehypejs/rehype |
| |
| [remark]: https://github.com/remarkjs/remark |
| |
| [retext]: https://github.com/retextjs/retext |
| |
| [hast]: https://github.com/syntax-tree/hast |
| |
| [mdast]: https://github.com/syntax-tree/mdast |
| |
| [nlcst]: https://github.com/syntax-tree/nlcst |
| |
| [unist]: https://github.com/syntax-tree/unist |
| |
| [engine]: https://github.com/unifiedjs/unified-engine |
| |
| [args]: https://github.com/unifiedjs/unified-args |
| |
| [gulp]: https://github.com/unifiedjs/unified-engine-gulp |
| |
| [atom]: https://github.com/unifiedjs/unified-engine-atom |
| |
| [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 |
| |
| [unist-utilities]: https://github.com/syntax-tree/unist#list-of-utilities |
| |
| [vfile]: https://github.com/vfile/vfile |
| |
| [vfile-contents]: https://github.com/vfile/vfile#vfilecontents |
| |
| [vfile-utilities]: https://github.com/vfile/vfile#related-tools |
| |
| [description]: #description |
| |
| [file]: #file |
| |
| [node]: #node |
| |
| [processor]: #processor |
| |
| [process]: #processorprocessfilevalue-done |
| |
| [parse]: #processorparsefilevalue |
| |
| [parser]: #processorparser |
| |
| [stringify]: #processorstringifynode-file |
| |
| [run]: #processorrunnode-file-done |
| |
| [compiler]: #processorcompiler |
| |
| [use]: #processoruseplugin-options |
| |
| [attacher]: #function-attacheroptions |
| |
| [transformer]: #function-transformernode-file-next |
| |
| [next]: #function-nexterr-tree-file |
| |
| [freeze]: #processorfreeze |
| |
| [plugin]: #plugin |
| |
| [run-done]: #function-doneerr-node-file |
| |
| [process-done]: #function-doneerr-file |
| |
| [trough]: https://github.com/wooorm/trough#function-fninput-next |
| |
| [promise]: https://developer.mozilla.org/Web/JavaScript/Reference/Global_Objects/Promise |
| |
| [remark-plugins]: https://github.com/remarkjs/remark/blob/master/doc/plugins.md#list-of-plugins |
| |
| [rehype-plugins]: https://github.com/rehypejs/rehype/blob/master/doc/plugins.md#list-of-plugins |
| |
| [retext-plugins]: https://github.com/retextjs/retext/blob/master/doc/plugins.md#list-of-plugins |
| |
| [stream]: https://github.com/unifiedjs/unified-stream |
| |
| [contributing]: contributing.md |
| |
| [coc]: code-of-conduct.md |
| |
| [ideas]: https://github.com/unifiedjs/ideas |
| |
| [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 |
| |
| [gatsby]: https://www.gatsbyjs.org |
| |
| [mdx]: https://mdxjs.com |
| |
| [jsx]: https://reactjs.org/docs/jsx-in-depth.html |
| |
| [prettier]: https://prettier.io |
| |
| [storybook]: https://storybook.js.org |
| |
| [debugger.html]: https://github.com/devtools-html/debugger.html |
| |
| [mozilla]: https://www.mozilla.org |
| |
| [opensource.guide]: https://opensource.guide |
| |
| [github]: https://github.com |