1 | # [![unified][logo]][site]
|
2 |
|
3 | [![Build][build-badge]][build]
|
4 | [![Coverage][coverage-badge]][coverage]
|
5 | [![Downloads][downloads-badge]][downloads]
|
6 | [![Size][size-badge]][size]
|
7 | [![Sponsors][sponsors-badge]][collective]
|
8 | [![Backers][backers-badge]][collective]
|
9 | [![Chat][chat-badge]][chat]
|
10 |
|
11 | **unified** lets you inspect and transform content with plugins.
|
12 |
|
13 | ## Contents
|
14 |
|
15 | * [What is this?](#what-is-this)
|
16 | * [When should I use this?](#when-should-i-use-this)
|
17 | * [Install](#install)
|
18 | * [Use](#use)
|
19 | * [Overview](#overview)
|
20 | * [API](#api)
|
21 | * [`processor()`](#processor)
|
22 | * [`processor.compiler`](#processorcompiler)
|
23 | * [`processor.data([key[, value]])`](#processordatakey-value)
|
24 | * [`processor.freeze()`](#processorfreeze)
|
25 | * [`processor.parse(file)`](#processorparsefile)
|
26 | * [`processor.parser`](#processorparser)
|
27 | * [`processor.process(file[, done])`](#processorprocessfile-done)
|
28 | * [`processor.processSync(file)`](#processorprocesssyncfile)
|
29 | * [`processor.run(tree[, file][, done])`](#processorruntree-file-done)
|
30 | * [`processor.runSync(tree[, file])`](#processorrunsynctree-file)
|
31 | * [`processor.stringify(tree[, file])`](#processorstringifytree-file)
|
32 | * [`processor.use(plugin[, options])`](#processoruseplugin-options)
|
33 | * [`CompileResultMap`](#compileresultmap)
|
34 | * [`CompileResults`](#compileresults)
|
35 | * [`Compiler`](#compiler)
|
36 | * [`Data`](#data)
|
37 | * [`Parser`](#parser)
|
38 | * [`Pluggable`](#pluggable)
|
39 | * [`PluggableList`](#pluggablelist)
|
40 | * [`Plugin`](#plugin)
|
41 | * [`PluginTuple`](#plugintuple)
|
42 | * [`Preset`](#preset)
|
43 | * [`ProcessCallback`](#processcallback)
|
44 | * [`Processor`](#processor-1)
|
45 | * [`RunCallback`](#runcallback)
|
46 | * [`Settings`](#settings)
|
47 | * [`TransformCallback`](#transformcallback)
|
48 | * [`Transformer`](#transformer)
|
49 | * [Types](#types)
|
50 | * [Compatibility](#compatibility)
|
51 | * [Contribute](#contribute)
|
52 | * [Sponsor](#sponsor)
|
53 | * [Acknowledgments](#acknowledgments)
|
54 | * [License](#license)
|
55 |
|
56 | ## What is this?
|
57 |
|
58 | unified is two things:
|
59 |
|
60 | * **unified** is a collective of 500+ free and open source packages that work
|
61 | with content as structured data (ASTs)
|
62 | * `unified` (this project) is the core package, used in 1.3m+ projects on GH,
|
63 | to process content with plugins
|
64 |
|
65 | Several ecosystems are built on unified around different kinds of content.
|
66 | Notably, [remark][] (markdown), [rehype][] (HTML), and [retext][] (natural
|
67 | language).
|
68 | These ecosystems can be connected together.
|
69 |
|
70 | * for more about us, see [`unifiedjs.com`][site]
|
71 | * for updates, see [@unifiedjs][twitter] on Twitter
|
72 | * for questions, see [support][]
|
73 | * to help, see [contribute][] and [sponsor][] below
|
74 |
|
75 | ## When should I use this?
|
76 |
|
77 | In some cases, you are already using unified.
|
78 | For example, it’s used in MDX, Gatsby, Docusaurus, etc.
|
79 | In those cases, you don’t need to add `unified` yourself but you can include
|
80 | plugins into those projects.
|
81 |
|
82 | But the real fun (for some) is to get your hands dirty and work with syntax
|
83 | trees and build with it yourself.
|
84 | You can create those projects, or things like Prettier, or your own site
|
85 | generator.
|
86 | You can connect utilities together and make your own plugins that check for
|
87 | problems and transform from one thing to another.
|
88 |
|
89 | When you are dealing with one type of content (such as markdown), you can use
|
90 | the main package of that ecosystem instead (so `remark`).
|
91 | When you are dealing with different kinds of content (such as markdown and
|
92 | HTML), it’s recommended to use `unified` itself, and pick and choose the plugins
|
93 | you need.
|
94 |
|
95 | ## Install
|
96 |
|
97 | This package is [ESM only][esm].
|
98 | In Node.js (version 16+), install with [npm][]:
|
99 |
|
100 | ```sh
|
101 | npm install unified
|
102 | ```
|
103 |
|
104 | In Deno with [`esm.sh`][esmsh]:
|
105 |
|
106 | ```js
|
107 | import {unified} from 'https://esm.sh/unified@11'
|
108 | ```
|
109 |
|
110 | In browsers with [`esm.sh`][esmsh]:
|
111 |
|
112 | ```html
|
113 | <script type="module">
|
114 | import {unified} from 'https://esm.sh/unified@11?bundle'
|
115 | </script>
|
116 | ```
|
117 |
|
118 | ## Use
|
119 |
|
120 | ```js
|
121 | import rehypeDocument from 'rehype-document'
|
122 | import rehypeFormat from 'rehype-format'
|
123 | import rehypeStringify from 'rehype-stringify'
|
124 | import remarkParse from 'remark-parse'
|
125 | import remarkRehype from 'remark-rehype'
|
126 | import {unified} from 'unified'
|
127 | import {reporter} from 'vfile-reporter'
|
128 |
|
129 | const file = await unified()
|
130 | .use(remarkParse)
|
131 | .use(remarkRehype)
|
132 | .use(rehypeDocument, {title: '👋🌍'})
|
133 | .use(rehypeFormat)
|
134 | .use(rehypeStringify)
|
135 | .process('# Hello world!')
|
136 |
|
137 | console.error(reporter(file))
|
138 | console.log(String(file))
|
139 | ```
|
140 |
|
141 | Yields:
|
142 |
|
143 | ```txt
|
144 | no issues found
|
145 | ```
|
146 |
|
147 | ```html
|
148 | <!doctype html>
|
149 | <html lang="en">
|
150 | <head>
|
151 | <meta charset="utf-8">
|
152 | <title>👋🌍</title>
|
153 | <meta name="viewport" content="width=device-width, initial-scale=1">
|
154 | </head>
|
155 | <body>
|
156 | <h1>Hello world!</h1>
|
157 | </body>
|
158 | </html>
|
159 | ```
|
160 |
|
161 |
|
162 |
|
163 | <a name="description"></a>
|
164 |
|
165 | ## Overview
|
166 |
|
167 | `unified` is an interface for processing content with syntax trees.
|
168 | Syntax trees are a representation of content understandable to programs.
|
169 | Those programs, called *[plugins][api-plugin]*, take these trees and inspect and
|
170 | modify them.
|
171 | To get to the syntax tree from text, there is a *[parser][api-parser]*.
|
172 | To get from that back to text, there is a *[compiler][api-compiler]*.
|
173 | This is the *[process][api-process]* of a *processor*.
|
174 |
|
175 | ```ascii
|
176 | | ........................ process ........................... |
|
177 | | .......... parse ... | ... run ... | ... stringify ..........|
|
178 |
|
179 | +--------+ +----------+
|
180 | Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
|
181 | +--------+ | +----------+
|
182 | X
|
183 | |
|
184 | +--------------+
|
185 | | Transformers |
|
186 | +--------------+
|
187 | ```
|
188 |
|
189 | ###### Processors
|
190 |
|
191 | Processors process content.
|
192 | On its own, `unified` (the root processor) doesn’t work.
|
193 | It needs to be configured with plugins to work.
|
194 | For example:
|
195 |
|
196 | ```js
|
197 | const processor = unified()
|
198 | .use(remarkParse)
|
199 | .use(remarkRehype)
|
200 | .use(rehypeDocument, {title: '👋🌍'})
|
201 | .use(rehypeFormat)
|
202 | .use(rehypeStringify)
|
203 | ```
|
204 |
|
205 | That processor can do different things.
|
206 | It can:
|
207 |
|
208 | * …parse markdown (`parse`)
|
209 | * …turn parsed markdown into HTML and format the HTML (`run`)
|
210 | * …compile HTML (`stringify`)
|
211 | * …do all of the above (`process`)
|
212 |
|
213 | Every processor implements another processor.
|
214 | To create a processor, call another processor.
|
215 | The new processor is configured to work the same as its ancestor.
|
216 | But when the descendant processor is configured in the future it does not affect
|
217 | the ancestral processor.
|
218 |
|
219 | When processors are exposed from a module (for example, `unified` itself) they
|
220 | should not be configured directly, as that would change their behavior for all
|
221 | module users.
|
222 | Those processors are *[frozen][api-freeze]* and they should be called to create
|
223 | a new processor before they are used.
|
224 |
|
225 | ###### File
|
226 |
|
227 | When processing a document, metadata is gathered about that document.
|
228 | [`vfile`][vfile] is the file format that stores data, metadata, and messages
|
229 | about files for unified and plugins.
|
230 |
|
231 | There are several [utilities][vfile-utilities] for working with these files.
|
232 |
|
233 | ###### Syntax tree
|
234 |
|
235 | The syntax trees used in unified are [unist][] nodes.
|
236 | A tree represents a whole document and each [node][] is a plain JavaScript
|
237 | object with a `type` field.
|
238 | The semantics of nodes and the format of syntax trees is defined by other
|
239 | projects:
|
240 |
|
241 | * [esast][] — JavaScript
|
242 | * [hast][] — HTML
|
243 | * [mdast][] — markdown
|
244 | * [nlcst][] — natural language
|
245 | * [xast][] — XML
|
246 |
|
247 | There are many utilities for working with trees listed in each aforementioned
|
248 | project and maintained in the [`syntax-tree`][syntax-tree] organization.
|
249 | These utilities are a level lower than unified itself and are building blocks
|
250 | that can be used to make plugins.
|
251 |
|
252 |
|
253 |
|
254 | <a name="list-of-processors"></a>
|
255 |
|
256 | ###### Ecosystems
|
257 |
|
258 | Around each syntax tree is an ecosystem that focusses on that particular kind
|
259 | of content.
|
260 | At their core, they parse text to a tree and compile that tree back to text.
|
261 | They also provide plugins that work with the syntax tree, without requiring
|
262 | that the end user has knowledge about that tree.
|
263 |
|
264 | * [rehype][] (hast) — HTML
|
265 | * [remark][] (mdast) — markdown
|
266 | * [retext][] (nlcst) — natural language
|
267 |
|
268 | <a name="list-of-plugins"></a>
|
269 |
|
270 | ###### Plugins
|
271 |
|
272 | Each aforementioned ecosystem comes with a large set of plugins that you can
|
273 | pick and choose from to do all kinds of things.
|
274 |
|
275 | * [List of remark plugins][remark-plugins] ·
|
276 | [`remarkjs/awesome-remark`][awesome-remark] ·
|
277 | [`remark-plugin` topic][topic-remark-plugin]
|
278 | * [List of rehype plugins][rehype-plugins] ·
|
279 | [`rehypejs/awesome-rehype`][awesome-rehype] ·
|
280 | [`rehype-plugin` topic][topic-rehype-plugin]
|
281 | * [List of retext plugins][retext-plugins] ·
|
282 | [`retextjs/awesome-retext`][awesome-retext] ·
|
283 | [`retext-plugin` topic][topic-retext-plugin]
|
284 |
|
285 | There are also a few plugins that work in any ecosystem:
|
286 |
|
287 | * [`unified-diff`](https://github.com/unifiedjs/unified-diff)
|
288 | — ignore unrelated messages in GitHub Actions and Travis
|
289 | * [`unified-infer-git-meta`](https://github.com/unifiedjs/unified-infer-git-meta)
|
290 | — infer metadata of a document from Git
|
291 | * [`unified-message-control`](https://github.com/unifiedjs/unified-message-control)
|
292 | — enable, disable, and ignore messages from content
|
293 |
|
294 | ###### Configuration
|
295 |
|
296 | Processors are configured with [plugins][api-plugin] or with the
|
297 | [`data`][api-data] method.
|
298 | Most plugins also accept configuration through options.
|
299 | See each plugin’s readme for more info.
|
300 |
|
301 | ###### Integrations
|
302 |
|
303 | unified can integrate with the file system through
|
304 | [`unified-engine`][unified-engine].
|
305 | CLI apps can be created with [`unified-args`][unified-args], Gulp plugins with
|
306 | [`unified-engine-gulp`][unified-engine-gulp], and language servers with
|
307 | [`unified-language-server`][unified-language-server].
|
308 | A streaming interface can be created with [`unified-stream`][unified-stream].
|
309 |
|
310 | ###### Programming interface
|
311 |
|
312 | The [API][] provided by `unified` allows multiple files to be processed and
|
313 | gives access to metadata (such as lint messages):
|
314 |
|
315 | ```js
|
316 | import rehypeStringify from 'rehype-stringify'
|
317 | import remarkParse from 'remark-parse'
|
318 | import remarkPresetLintMarkdownStyleGuide from 'remark-preset-lint-markdown-style-guide'
|
319 | import remarkRehype from 'remark-rehype'
|
320 | import remarkRetext from 'remark-retext'
|
321 | import retextEnglish from 'retext-english'
|
322 | import retextEquality from 'retext-equality'
|
323 | import {unified} from 'unified'
|
324 | import {reporter} from 'vfile-reporter'
|
325 |
|
326 | const file = await unified()
|
327 | .use(remarkParse)
|
328 | .use(remarkPresetLintMarkdownStyleGuide)
|
329 | .use(remarkRetext, unified().use(retextEnglish).use(retextEquality))
|
330 | .use(remarkRehype)
|
331 | .use(rehypeStringify)
|
332 | .process('*Emphasis* and _stress_, you guys!')
|
333 |
|
334 | console.error(reporter(file))
|
335 | console.log(String(file))
|
336 | ```
|
337 |
|
338 | Yields:
|
339 |
|
340 | ```txt
|
341 | 1:16-1:24 warning Emphasis should use `*` as a marker emphasis-marker remark-lint
|
342 | 1:30-1:34 warning `guys` may be insensitive, use `people`, `persons`, `folks` instead gals-man retext-equality
|
343 |
|
344 | ⚠ 2 warnings
|
345 | ```
|
346 |
|
347 | ```html
|
348 | <p><em>Emphasis</em> and <em>stress</em>, you guys!</p>
|
349 | ```
|
350 |
|
351 |
|
352 |
|
353 | <a name="processing-between-syntaxes"></a>
|
354 |
|
355 | ###### Transforming between ecosystems
|
356 |
|
357 | Ecosystems can be combined in two modes.
|
358 |
|
359 | **Bridge** mode transforms the tree from one format (*origin*) to another
|
360 | (*destination*).
|
361 | A different processor runs on the destination tree.
|
362 | Afterwards, the original processor continues with the origin tree.
|
363 |
|
364 | **Mutate** mode also transforms the syntax tree from one format to another.
|
365 | But the original processor continues transforming the destination tree.
|
366 |
|
367 | In the previous example (“Programming interface”), `remark-retext` is used in
|
368 | bridge mode: the origin syntax tree is kept after retext is done; whereas
|
369 | `remark-rehype` is used in mutate mode: it sets a new syntax tree and discards
|
370 | the origin tree.
|
371 |
|
372 | The following plugins lets you combine ecosystems:
|
373 |
|
374 | * [`remark-retext`][remark-retext] — turn markdown into natural language
|
375 | * [`remark-rehype`][remark-rehype] — turn markdown into HTML
|
376 | * [`rehype-retext`][rehype-retext] — turn HTML into natural language
|
377 | * [`rehype-remark`][rehype-remark] — turn HTML into markdown
|
378 |
|
379 | ## API
|
380 |
|
381 | This package exports the identifier `unified` (the root `processor`).
|
382 | There is no default export.
|
383 |
|
384 | ### `processor()`
|
385 |
|
386 | Create a new processor.
|
387 |
|
388 | ###### Returns
|
389 |
|
390 | New *[unfrozen][api-freeze]* processor ([`processor`][api-processor]).
|
391 |
|
392 | This processor is configured to work the same as its ancestor.
|
393 | When the descendant processor is configured in the future it does not affect
|
394 | the ancestral processor.
|
395 |
|
396 | ###### Example
|
397 |
|
398 | This example shows how a new processor can be created (from `remark`) and linked
|
399 | to **stdin**(4) and **stdout**(4).
|
400 |
|
401 | ```js
|
402 | import process from 'node:process'
|
403 | import concatStream from 'concat-stream'
|
404 | import {remark} from 'remark'
|
405 |
|
406 | process.stdin.pipe(
|
407 | concatStream(function (buf) {
|
408 | process.stdout.write(String(remark().processSync(buf)))
|
409 | })
|
410 | )
|
411 | ```
|
412 |
|
413 | ### `processor.compiler`
|
414 |
|
415 | Compiler to use ([`Compiler`][api-compiler], optional).
|
416 |
|
417 | ### `processor.data([key[, value]])`
|
418 |
|
419 | Configure the processor with info available to all plugins.
|
420 | Information is stored in an object.
|
421 |
|
422 | Typically, options can be given to a specific plugin, but sometimes it makes
|
423 | sense to have information shared with several plugins.
|
424 | For example, a list of HTML elements that are self-closing, which is needed
|
425 | during all [phases][overview].
|
426 |
|
427 | > 👉 **Note**: setting information cannot occur on *[frozen][api-freeze]*
|
428 | > processors.
|
429 | > Call the processor first to create a new unfrozen processor.
|
430 |
|
431 | > 👉 **Note**: to register custom data in TypeScript, augment the
|
432 | > [`Data`][api-data] interface.
|
433 |
|
434 | ###### Signatures
|
435 |
|
436 | * `processor = processor.data(key, value)`
|
437 | * `processor = processor.data(dataset)`
|
438 | * `value = processor.data(key)`
|
439 | * `dataset = processor.data()`
|
440 |
|
441 | ###### Parameters
|
442 |
|
443 | * `key` ([`keyof Data`][api-data], optional) — field to get
|
444 | * `value` ([`Data[key]`][api-data]) — value to set
|
445 | * `values` ([`Data`][api-data]) — values to set
|
446 |
|
447 | ###### Returns
|
448 |
|
449 | The current processor when setting ([`processor`][api-processor]), the value at
|
450 | `key` when getting ([`Data[key]`][api-data]), or the entire dataset when
|
451 | getting without key ([`Data`][api-data]).
|
452 |
|
453 | ###### Example
|
454 |
|
455 | This example show how to get and set info:
|
456 |
|
457 | ```js
|
458 | import {unified} from 'unified'
|
459 |
|
460 | const processor = unified().data('alpha', 'bravo')
|
461 |
|
462 | processor.data('alpha') // => 'bravo'
|
463 |
|
464 | processor.data() // => {alpha: 'bravo'}
|
465 |
|
466 | processor.data({charlie: 'delta'})
|
467 |
|
468 | processor.data() // => {charlie: 'delta'}
|
469 | ```
|
470 |
|
471 | ### `processor.freeze()`
|
472 |
|
473 | Freeze a processor.
|
474 |
|
475 | Frozen processors are meant to be extended and not to be configured directly.
|
476 |
|
477 | When a processor is frozen it cannot be unfrozen.
|
478 | New processors working the same way can be created by calling the processor.
|
479 |
|
480 | It’s possible to freeze processors explicitly by calling `.freeze()`.
|
481 | Processors freeze automatically when `.parse()`, `.run()`, `.runSync()`,
|
482 | `.stringify()`, `.process()`, or `.processSync()` are called.
|
483 |
|
484 | ###### Returns
|
485 |
|
486 | The current processor ([`processor`][api-processor]).
|
487 |
|
488 | ###### Example
|
489 |
|
490 | This example, `index.js`, shows how `rehype` prevents extensions to itself:
|
491 |
|
492 | ```js
|
493 | import rehypeParse from 'rehype-parse'
|
494 | import rehypeStringify from 'rehype-stringify'
|
495 | import {unified} from 'unified'
|
496 |
|
497 | export const rehype = unified().use(rehypeParse).use(rehypeStringify).freeze()
|
498 | ```
|
499 |
|
500 | That processor can be used and configured like so:
|
501 |
|
502 | ```js
|
503 | import {rehype} from 'rehype'
|
504 | import rehypeFormat from 'rehype-format'
|
505 | // …
|
506 |
|
507 | rehype()
|
508 | .use(rehypeFormat)
|
509 | // …
|
510 | ```
|
511 |
|
512 | A similar looking example is broken as operates on the frozen interface.
|
513 | If this behavior was allowed it would result in unexpected behavior so an error
|
514 | is thrown.
|
515 | **This is not valid**:
|
516 |
|
517 | ```js
|
518 | import {rehype} from 'rehype'
|
519 | import rehypeFormat from 'rehype-format'
|
520 | // …
|
521 |
|
522 | rehype
|
523 | .use(rehypeFormat)
|
524 | // …
|
525 | ```
|
526 |
|
527 | Yields:
|
528 |
|
529 | ```txt
|
530 | ~/node_modules/unified/index.js:426
|
531 | throw new Error(
|
532 | ^
|
533 |
|
534 | Error: Cannot call `use` on a frozen processor.
|
535 | Create a new processor first, by calling it: use `processor()` instead of `processor`.
|
536 | at assertUnfrozen (~/node_modules/unified/index.js:426:11)
|
537 | at Function.use (~/node_modules/unified/index.js:165:5)
|
538 | …
|
539 | ```
|
540 |
|
541 | ### `processor.parse(file)`
|
542 |
|
543 | Parse text to a syntax tree.
|
544 |
|
545 | > 👉 **Note**: `parse` freezes the processor if not already
|
546 | > *[frozen][api-freeze]*.
|
547 |
|
548 | > 👉 **Note**: `parse` performs the [parse phase][overview], not the run phase
|
549 | > or other phases.
|
550 |
|
551 | ###### Parameters
|
552 |
|
553 | * `file` ([`Compatible`][vfile-compatible]) — file to parse; typically
|
554 | `string` or [`VFile`][vfile]; any value accepted as `x` in `new VFile(x)`
|
555 |
|
556 | ###### Returns
|
557 |
|
558 | Syntax tree representing `file` ([`Node`][node]).
|
559 |
|
560 | ###### Example
|
561 |
|
562 | This example shows how `parse` can be used to create a tree from a file.
|
563 |
|
564 | ```js
|
565 | import remarkParse from 'remark-parse'
|
566 | import {unified} from 'unified'
|
567 |
|
568 | const tree = unified().use(remarkParse).parse('# Hello world!')
|
569 |
|
570 | console.log(tree)
|
571 | ```
|
572 |
|
573 | Yields:
|
574 |
|
575 | ```js
|
576 | {
|
577 | type: 'root',
|
578 | children: [
|
579 | {type: 'heading', depth: 1, children: [Array], position: [Object]}
|
580 | ],
|
581 | position: {
|
582 | start: {line: 1, column: 1, offset: 0},
|
583 | end: {line: 1, column: 15, offset: 14}
|
584 | }
|
585 | }
|
586 | ```
|
587 |
|
588 | ### `processor.parser`
|
589 |
|
590 | Parser to use ([`Parser`][api-parser], optional).
|
591 |
|
592 | ### `processor.process(file[, done])`
|
593 |
|
594 | Process the given file as configured on the processor.
|
595 |
|
596 | > 👉 **Note**: `process` freezes the processor if not already
|
597 | > *[frozen][api-freeze]*.
|
598 |
|
599 | > 👉 **Note**: `process` performs the [parse, run, and stringify
|
600 | > phases][overview].
|
601 |
|
602 | ###### Signatures
|
603 |
|
604 | * `processor.process(file, done)`
|
605 | * `Promise<VFile> = processor.process(file?)`
|
606 |
|
607 | ###### Parameters
|
608 |
|
609 | * `file` ([`Compatible`][vfile-compatible], optional) — file; typically
|
610 | `string` or [`VFile`][vfile]; any value accepted as `x` in `new VFile(x)`
|
611 | * `done` ([`ProcessCallback`][api-process-callback], optional) — callback
|
612 |
|
613 | ###### Returns
|
614 |
|
615 | Nothing if `done` is given (`undefined`).
|
616 | Otherwise a promise, rejected with a fatal error or resolved with the
|
617 | processed file ([`Promise<VFile>`][vfile]).
|
618 |
|
619 | The parsed, transformed, and compiled value is available at `file.value` (see
|
620 | note).
|
621 |
|
622 | > 👉 **Note**: unified typically compiles by serializing: most
|
623 | > compilers return `string` (or `Uint8Array`).
|
624 | > Some compilers, such as the one configured with
|
625 | > [`rehype-react`][rehype-react], return other values (in this case, a React
|
626 | > tree).
|
627 | > If you’re using a compiler that doesn’t serialize, expect different result
|
628 | > values.
|
629 | >
|
630 | > To register custom results in TypeScript, add them to
|
631 | > [`CompileResultMap`][api-compile-result-map].
|
632 |
|
633 | ###### Example
|
634 |
|
635 | This example shows how `process` can be used to process a file:
|
636 |
|
637 | ```js
|
638 | import rehypeDocument from 'rehype-document'
|
639 | import rehypeFormat from 'rehype-format'
|
640 | import rehypeStringify from 'rehype-stringify'
|
641 | import remarkParse from 'remark-parse'
|
642 | import remarkRehype from 'remark-rehype'
|
643 | import {unified} from 'unified'
|
644 |
|
645 | const file = await unified()
|
646 | .use(remarkParse)
|
647 | .use(remarkRehype)
|
648 | .use(rehypeDocument, {title: '👋🌍'})
|
649 | .use(rehypeFormat)
|
650 | .use(rehypeStringify)
|
651 | .process('# Hello world!')
|
652 |
|
653 | console.log(String(file))
|
654 | ```
|
655 |
|
656 | Yields:
|
657 |
|
658 | ```html
|
659 | <!doctype html>
|
660 | <html lang="en">
|
661 | <head>
|
662 | <meta charset="utf-8">
|
663 | <title>👋🌍</title>
|
664 | <meta name="viewport" content="width=device-width, initial-scale=1">
|
665 | </head>
|
666 | <body>
|
667 | <h1>Hello world!</h1>
|
668 | </body>
|
669 | </html>
|
670 | ```
|
671 |
|
672 | ### `processor.processSync(file)`
|
673 |
|
674 | Process the given file as configured on the processor.
|
675 |
|
676 | An error is thrown if asynchronous transforms are configured.
|
677 |
|
678 | > 👉 **Note**: `processSync` freezes the processor if not already
|
679 | > *[frozen][api-freeze]*.
|
680 |
|
681 | > 👉 **Note**: `processSync` performs the [parse, run, and stringify
|
682 | > phases][overview].
|
683 |
|
684 | ###### Parameters
|
685 |
|
686 | * `file` ([`Compatible`][vfile-compatible], optional) — file; typically
|
687 | `string` or [`VFile`][vfile]; any value accepted as `x` in `new VFile(x)`
|
688 |
|
689 | ###### Returns
|
690 |
|
691 | The processed file ([`VFile`][vfile]).
|
692 |
|
693 | The parsed, transformed, and compiled value is available at `file.value` (see
|
694 | note).
|
695 |
|
696 | > 👉 **Note**: unified typically compiles by serializing: most
|
697 | > compilers return `string` (or `Uint8Array`).
|
698 | > Some compilers, such as the one configured with
|
699 | > [`rehype-react`][rehype-react], return other values (in this case, a React
|
700 | > tree).
|
701 | > If you’re using a compiler that doesn’t serialize, expect different result
|
702 | > values.
|
703 | >
|
704 | > To register custom results in TypeScript, add them to
|
705 | > [`CompileResultMap`][api-compile-result-map].
|
706 |
|
707 | ###### Example
|
708 |
|
709 | This example shows how `processSync` can be used to process a file, if all
|
710 | transformers are synchronous.
|
711 |
|
712 | ```js
|
713 | import rehypeDocument from 'rehype-document'
|
714 | import rehypeFormat from 'rehype-format'
|
715 | import rehypeStringify from 'rehype-stringify'
|
716 | import remarkParse from 'remark-parse'
|
717 | import remarkRehype from 'remark-rehype'
|
718 | import {unified} from 'unified'
|
719 |
|
720 | const processor = unified()
|
721 | .use(remarkParse)
|
722 | .use(remarkRehype)
|
723 | .use(rehypeDocument, {title: '👋🌍'})
|
724 | .use(rehypeFormat)
|
725 | .use(rehypeStringify)
|
726 |
|
727 | console.log(String(processor.processSync('# Hello world!')))
|
728 | ```
|
729 |
|
730 | Yields:
|
731 |
|
732 | ```html
|
733 | <!doctype html>
|
734 | <html lang="en">
|
735 | <head>
|
736 | <meta charset="utf-8">
|
737 | <title>👋🌍</title>
|
738 | <meta name="viewport" content="width=device-width, initial-scale=1">
|
739 | </head>
|
740 | <body>
|
741 | <h1>Hello world!</h1>
|
742 | </body>
|
743 | </html>
|
744 | ```
|
745 |
|
746 | ### `processor.run(tree[, file][, done])`
|
747 |
|
748 | Run *[transformers][api-transformer]* on a syntax tree.
|
749 |
|
750 | > 👉 **Note**: `run` freezes the processor if not already
|
751 | > *[frozen][api-freeze]*.
|
752 |
|
753 | > 👉 **Note**: `run` performs the [run phase][overview], not other phases.
|
754 |
|
755 | ###### Signatures
|
756 |
|
757 | * `processor.run(tree, done)`
|
758 | * `processor.run(tree, file, done)`
|
759 | * `Promise<Node> = processor.run(tree, file?)`
|
760 |
|
761 | ###### Parameters
|
762 |
|
763 | * `tree` ([`Node`][node]) — tree to transform and inspect
|
764 | * `file` ([`Compatible`][vfile-compatible], optional) — file associated
|
765 | with `node`; any value accepted as `x` in `new VFile(x)`
|
766 | * `done` ([`RunCallback`][api-run-callback], optional) — callback
|
767 |
|
768 | ###### Returns
|
769 |
|
770 | Nothing if `done` is given (`undefined`).
|
771 | Otherwise, a promise rejected with a fatal error or resolved with the
|
772 | transformed tree ([`Promise<Node>`][node]).
|
773 |
|
774 | ###### Example
|
775 |
|
776 | This example shows how `run` can be used to transform a tree:
|
777 |
|
778 | ```js
|
779 | import remarkReferenceLinks from 'remark-reference-links'
|
780 | import {unified} from 'unified'
|
781 | import {u} from 'unist-builder'
|
782 |
|
783 | const tree = u('root', [
|
784 | u('paragraph', [
|
785 | u('link', {href: 'https://example.com'}, [u('text', 'Example Domain')])
|
786 | ])
|
787 | ])
|
788 |
|
789 | const changedTree = await unified().use(remarkReferenceLinks).run(tree)
|
790 |
|
791 | console.log(changedTree)
|
792 | ```
|
793 |
|
794 | Yields:
|
795 |
|
796 | ```js
|
797 | {
|
798 | type: 'root',
|
799 | children: [
|
800 | {type: 'paragraph', children: [Array]},
|
801 | {type: 'definition', identifier: '1', title: '', url: undefined}
|
802 | ]
|
803 | }
|
804 | ```
|
805 |
|
806 | ### `processor.runSync(tree[, file])`
|
807 |
|
808 | Run *[transformers][api-transformer]* on a syntax tree.
|
809 |
|
810 | An error is thrown if asynchronous transforms are configured.
|
811 |
|
812 | > 👉 **Note**: `runSync` freezes the processor if not already
|
813 | > *[frozen][api-freeze]*.
|
814 |
|
815 | > 👉 **Note**: `runSync` performs the [run phase][overview], not other phases.
|
816 |
|
817 | ###### Parameters
|
818 |
|
819 | * `tree` ([`Node`][node]) — tree to transform and inspect
|
820 | * `file` ([`Compatible`][vfile-compatible], optional) — file associated
|
821 | with `node`; any value accepted as `x` in `new VFile(x)`
|
822 |
|
823 | ###### Returns
|
824 |
|
825 | Transformed tree ([`Node`][node]).
|
826 |
|
827 | ### `processor.stringify(tree[, file])`
|
828 |
|
829 | Compile a syntax tree.
|
830 |
|
831 | > 👉 **Note**: `stringify` freezes the processor if not already
|
832 | > *[frozen][api-freeze]*.
|
833 |
|
834 | > 👉 **Note**: `stringify` performs the [stringify phase][overview], not the run
|
835 | > phase or other phases.
|
836 |
|
837 | ###### Parameters
|
838 |
|
839 | * `tree` ([`Node`][node]) — tree to compile
|
840 | * `file` ([`Compatible`][vfile-compatible], optional) — file associated
|
841 | with `node`; any value accepted as `x` in `new VFile(x)`
|
842 |
|
843 | ###### Returns
|
844 |
|
845 | Textual representation of the tree (`Uint8Array` or `string`, see note).
|
846 |
|
847 | > 👉 **Note**: unified typically compiles by serializing: most compilers
|
848 | > return `string` (or `Uint8Array`).
|
849 | > Some compilers, such as the one configured with
|
850 | > [`rehype-react`][rehype-react], return other values (in this case, a
|
851 | > React tree).
|
852 | > If you’re using a compiler that doesn’t serialize, expect different
|
853 | > result values.
|
854 | >
|
855 | > To register custom results in TypeScript, add them to
|
856 | > [`CompileResultMap`][api-compile-result-map].
|
857 |
|
858 | ###### Example
|
859 |
|
860 | This example shows how `stringify` can be used to serialize a syntax tree:
|
861 |
|
862 | ```js
|
863 | import {h} from 'hastscript'
|
864 | import rehypeStringify from 'rehype-stringify'
|
865 | import {unified} from 'unified'
|
866 |
|
867 | const tree = h('h1', 'Hello world!')
|
868 |
|
869 | const document = unified().use(rehypeStringify).stringify(tree)
|
870 |
|
871 | console.log(document)
|
872 | ```
|
873 |
|
874 | Yields:
|
875 |
|
876 | ```html
|
877 | <h1>Hello world!</h1>
|
878 | ```
|
879 |
|
880 | ### `processor.use(plugin[, options])`
|
881 |
|
882 | Configure the processor to use a plugin, a list of usable values, or a preset.
|
883 |
|
884 | If the processor is already using a plugin, the previous plugin configuration
|
885 | is changed based on the options that are passed in.
|
886 | In other words, the plugin is not added a second time.
|
887 |
|
888 | > 👉 **Note**: `use` cannot be called on [*frozen*][api-freeze] processors.
|
889 | > Call the processor first to create a new unfrozen processor.
|
890 |
|
891 | ###### Signatures
|
892 |
|
893 | * `processor.use(preset?)`
|
894 | * `processor.use(list)`
|
895 | * `processor.use(plugin[, ...parameters])`
|
896 |
|
897 | ###### Parameters
|
898 |
|
899 | * `preset` ([`Preset`][api-preset]) — plugins and settings
|
900 | * `list` ([`PluggableList`][api-pluggable-list]) — list of usable things
|
901 | * `plugin` ([`Plugin`][api-plugin]) — plugin
|
902 | * `parameters` (`Array<unknown>`) — configuration for `plugin`, typically a
|
903 | single options object
|
904 |
|
905 | ###### Returns
|
906 |
|
907 | Current processor ([`processor`][api-processor]).
|
908 |
|
909 | ###### Example
|
910 |
|
911 | There are many ways to pass plugins to `.use()`.
|
912 | This example gives an overview:
|
913 |
|
914 | ```js
|
915 | import {unified} from 'unified'
|
916 |
|
917 | unified()
|
918 | // Plugin with options:
|
919 | .use(pluginA, {x: true, y: true})
|
920 | // Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):
|
921 | .use(pluginA, {y: false, z: true})
|
922 | // Plugins:
|
923 | .use([pluginB, pluginC])
|
924 | // Two plugins, the second with options:
|
925 | .use([pluginD, [pluginE, {}]])
|
926 | // Preset with plugins and settings:
|
927 | .use({plugins: [pluginF, [pluginG, {}]], settings: {position: false}})
|
928 | // Settings only:
|
929 | .use({settings: {position: false}})
|
930 | ```
|
931 |
|
932 | ### `CompileResultMap`
|
933 |
|
934 | Interface of known results from compilers (TypeScript type).
|
935 |
|
936 | Normally, compilers result in text ([`Value`][vfile-value] of `vfile`).
|
937 | When you compile to something else, such as a React node (as in,
|
938 | `rehype-react`), you can augment this interface to include that type.
|
939 |
|
940 | ```ts
|
941 | import type {ReactNode} from 'somewhere'
|
942 |
|
943 | declare module 'unified' {
|
944 | interface CompileResultMap {
|
945 | // Register a new result (value is used, key should match it).
|
946 | ReactNode: ReactNode
|
947 | }
|
948 | }
|
949 |
|
950 | export {} // You may not need this, but it makes sure the file is a module.
|
951 | ```
|
952 |
|
953 | Use [`CompileResults`][api-compile-results] to access the values.
|
954 |
|
955 | ###### Type
|
956 |
|
957 | ```ts
|
958 | interface CompileResultMap {
|
959 | // Note: if `Value` from `VFile` is changed, this should too.
|
960 | Uint8Array: Uint8Array
|
961 | string: string
|
962 | }
|
963 | ```
|
964 |
|
965 | ### `CompileResults`
|
966 |
|
967 | Acceptable results from compilers (TypeScript type).
|
968 |
|
969 | To register custom results, add them to
|
970 | [`CompileResultMap`][api-compile-result-map].
|
971 |
|
972 | ###### Type
|
973 |
|
974 | ```ts
|
975 | type CompileResults = CompileResultMap[keyof CompileResultMap]
|
976 | ```
|
977 |
|
978 | ### `Compiler`
|
979 |
|
980 | A **compiler** handles the compiling of a syntax tree to something else
|
981 | (in most cases, text) (TypeScript type).
|
982 |
|
983 | It is used in the stringify phase and called with a [`Node`][node]
|
984 | and [`VFile`][vfile] representation of the document to compile.
|
985 | It should return the textual representation of the given tree (typically
|
986 | `string`).
|
987 |
|
988 | > 👉 **Note**: unified typically compiles by serializing: most compilers
|
989 | > return `string` (or `Uint8Array`).
|
990 | > Some compilers, such as the one configured with
|
991 | > [`rehype-react`][rehype-react], return other values (in this case, a
|
992 | > React tree).
|
993 | > If you’re using a compiler that doesn’t serialize, expect different
|
994 | > result values.
|
995 | >
|
996 | > To register custom results in TypeScript, add them to
|
997 | > [`CompileResultMap`][api-compile-result-map].
|
998 |
|
999 | ###### Type
|
1000 |
|
1001 | ```ts
|
1002 | type Compiler<
|
1003 | Tree extends Node = Node,
|
1004 | Result extends CompileResults = CompileResults
|
1005 | > = (tree: Tree, file: VFile) => Result
|
1006 | ```
|
1007 |
|
1008 | ### `Data`
|
1009 |
|
1010 | Interface of known data that can be supported by all plugins (TypeScript type).
|
1011 |
|
1012 | Typically, options can be given to a specific plugin, but sometimes it makes
|
1013 | sense to have information shared with several plugins.
|
1014 | For example, a list of HTML elements that are self-closing, which is needed
|
1015 | during all phases.
|
1016 |
|
1017 | To type this, do something like:
|
1018 |
|
1019 | ```ts
|
1020 | declare module 'unified' {
|
1021 | interface Data {
|
1022 | htmlVoidElements?: Array<string> | undefined
|
1023 | }
|
1024 | }
|
1025 |
|
1026 | export {} // You may not need this, but it makes sure the file is a module.
|
1027 | ```
|
1028 |
|
1029 | ###### Type
|
1030 |
|
1031 | ```ts
|
1032 | interface Data {
|
1033 | settings?: Settings | undefined
|
1034 | }
|
1035 | ```
|
1036 |
|
1037 | See [`Settings`][api-settings] for more info.
|
1038 |
|
1039 | ### `Parser`
|
1040 |
|
1041 | A **parser** handles the parsing of text to a syntax tree (TypeScript type).
|
1042 |
|
1043 | It is used in the parse phase and is called with a `string` and
|
1044 | [`VFile`][vfile] of the document to parse.
|
1045 | It must return the syntax tree representation of the given file
|
1046 | ([`Node`][node]).
|
1047 |
|
1048 | ###### Type
|
1049 |
|
1050 | ```ts
|
1051 | type Parser<Tree extends Node = Node> = (document: string, file: VFile) => Tree
|
1052 | ```
|
1053 |
|
1054 | ### `Pluggable`
|
1055 |
|
1056 | Union of the different ways to add plugins and settings (TypeScript type).
|
1057 |
|
1058 | ###### Type
|
1059 |
|
1060 | ```ts
|
1061 | type Pluggable =
|
1062 | | Plugin<Array<any>, any, any>
|
1063 | | PluginTuple<Array<any>, any, any>
|
1064 | | Preset
|
1065 | ```
|
1066 |
|
1067 | See [`Plugin`][api-plugin], [`PluginTuple`][api-plugin-tuple],
|
1068 | and [`Preset`][api-preset] for more info.
|
1069 |
|
1070 | ### `PluggableList`
|
1071 |
|
1072 | List of plugins and presets (TypeScript type).
|
1073 |
|
1074 | ###### Type
|
1075 |
|
1076 | ```ts
|
1077 | type PluggableList = Array<Pluggable>
|
1078 | ```
|
1079 |
|
1080 | See [`Pluggable`][api-pluggable] for more info.
|
1081 |
|
1082 | ### `Plugin`
|
1083 |
|
1084 | Single plugin (TypeScript type).
|
1085 |
|
1086 | Plugins configure the processors they are applied on in the following ways:
|
1087 |
|
1088 | * they change the processor, such as the parser, the compiler, or by
|
1089 | configuring data
|
1090 | * they specify how to handle trees and files
|
1091 |
|
1092 | In practice, they are functions that can receive options and configure the
|
1093 | processor (`this`).
|
1094 |
|
1095 | > 👉 **Note**: plugins are called when the processor is *frozen*, not when they
|
1096 | > are applied.
|
1097 |
|
1098 | ###### Type
|
1099 |
|
1100 | ```ts
|
1101 | type Plugin<
|
1102 | PluginParameters extends unknown[] = [],
|
1103 | Input extends Node | string | undefined = Node,
|
1104 | Output = Input
|
1105 | > = (
|
1106 | this: Processor,
|
1107 | ...parameters: PluginParameters
|
1108 | ) => Input extends string // Parser.
|
1109 | ? Output extends Node | undefined
|
1110 | ? undefined | void
|
1111 | : never
|
1112 | : Output extends CompileResults // Compiler.
|
1113 | ? Input extends Node | undefined
|
1114 | ? undefined | void
|
1115 | : never
|
1116 | : // Inspect/transform.
|
1117 | | Transformer<
|
1118 | Input extends Node ? Input : Node,
|
1119 | Output extends Node ? Output : Node
|
1120 | >
|
1121 | | undefined
|
1122 | | void
|
1123 | ```
|
1124 |
|
1125 | See [`Transformer`][api-transformer] for more info.
|
1126 |
|
1127 | ###### Example
|
1128 |
|
1129 | `move.js`:
|
1130 |
|
1131 | ```js
|
1132 | /**
|
1133 | * @typedef Options
|
1134 | * Configuration (required).
|
1135 | * @property {string} extname
|
1136 | * File extension to use (must start with `.`).
|
1137 | */
|
1138 |
|
1139 | /** @type {import('unified').Plugin<[Options]>} */
|
1140 | export function move(options) {
|
1141 | if (!options || !options.extname) {
|
1142 | throw new Error('Missing `options.extname`')
|
1143 | }
|
1144 |
|
1145 | return function (_, file) {
|
1146 | if (file.extname && file.extname !== options.extname) {
|
1147 | file.extname = options.extname
|
1148 | }
|
1149 | }
|
1150 | }
|
1151 | ```
|
1152 |
|
1153 | `example.md`:
|
1154 |
|
1155 | ```markdown
|
1156 | # Hello, world!
|
1157 | ```
|
1158 |
|
1159 | `example.js`:
|
1160 |
|
1161 | ```js
|
1162 | import rehypeStringify from 'rehype-stringify'
|
1163 | import remarkParse from 'remark-parse'
|
1164 | import remarkRehype from 'remark-rehype'
|
1165 | import {read, write} from 'to-vfile'
|
1166 | import {unified} from 'unified'
|
1167 | import {reporter} from 'vfile-reporter'
|
1168 | import {move} from './move.js'
|
1169 |
|
1170 | const file = await unified()
|
1171 | .use(remarkParse)
|
1172 | .use(remarkRehype)
|
1173 | .use(move, {extname: '.html'})
|
1174 | .use(rehypeStringify)
|
1175 | .process(await read('example.md'))
|
1176 |
|
1177 | console.error(reporter(file))
|
1178 | await write(file) // Written to `example.html`.
|
1179 | ```
|
1180 |
|
1181 | Yields:
|
1182 |
|
1183 | ```txt
|
1184 | example.md: no issues found
|
1185 | ```
|
1186 |
|
1187 | …and in `example.html`:
|
1188 |
|
1189 | ```html
|
1190 | <h1>Hello, world!</h1>
|
1191 | ```
|
1192 |
|
1193 | ### `PluginTuple`
|
1194 |
|
1195 | Tuple of a plugin and its configuration (TypeScript type).
|
1196 |
|
1197 | The first item is a plugin, the rest are its parameters.
|
1198 |
|
1199 | ###### Type
|
1200 |
|
1201 | ```ts
|
1202 | type PluginTuple<
|
1203 | TupleParameters extends unknown[] = [],
|
1204 | Input extends Node | string | undefined = undefined,
|
1205 | Output = undefined
|
1206 | > = [
|
1207 | plugin: Plugin<TupleParameters, Input, Output>,
|
1208 | ...parameters: TupleParameters
|
1209 | ]
|
1210 | ```
|
1211 |
|
1212 | See [`Plugin`][api-plugin] for more info.
|
1213 |
|
1214 | ### `Preset`
|
1215 |
|
1216 | Sharable configuration (TypeScript type).
|
1217 |
|
1218 | They can contain plugins and settings.
|
1219 |
|
1220 | ###### Fields
|
1221 |
|
1222 | * `plugins` ([`PluggableList`][api-pluggable-list], optional)
|
1223 | — list of plugins and presets
|
1224 | * `settings` ([`Data`][api-data], optional)
|
1225 | — shared settings for parsers and compilers
|
1226 |
|
1227 | ###### Example
|
1228 |
|
1229 | `preset.js`:
|
1230 |
|
1231 | ```js
|
1232 | import remarkCommentConfig from 'remark-comment-config'
|
1233 | import remarkLicense from 'remark-license'
|
1234 | import remarkPresetLintConsistent from 'remark-preset-lint-consistent'
|
1235 | import remarkPresetLintRecommended from 'remark-preset-lint-recommended'
|
1236 | import remarkToc from 'remark-toc'
|
1237 |
|
1238 | /** @type {import('unified').Preset} */
|
1239 | const preset = {
|
1240 | plugins: [
|
1241 | remarkPresetLintRecommended,
|
1242 | remarkPresetLintConsistent,
|
1243 | remarkCommentConfig,
|
1244 | [remarkToc, {maxDepth: 3, tight: true}],
|
1245 | remarkLicense
|
1246 | ]
|
1247 | settings: {bullet: '*', emphasis: '*', fences: true},
|
1248 | }
|
1249 |
|
1250 | export default preset
|
1251 | ```
|
1252 |
|
1253 | `example.md`:
|
1254 |
|
1255 | ```markdown
|
1256 | # Hello, world!
|
1257 |
|
1258 | _Emphasis_ and **importance**.
|
1259 |
|
1260 | ## Table of contents
|
1261 |
|
1262 | ## API
|
1263 |
|
1264 | ## License
|
1265 | ```
|
1266 |
|
1267 | `example.js`:
|
1268 |
|
1269 | ```js
|
1270 | import {remark} from 'remark'
|
1271 | import {read, write} from 'to-vfile'
|
1272 | import {reporter} from 'vfile-reporter'
|
1273 | import preset from './preset.js'
|
1274 |
|
1275 | const file = await remark()
|
1276 | .use(preset)
|
1277 | .process(await read('example.md'))
|
1278 |
|
1279 | console.error(reporter(file))
|
1280 | await write(file)
|
1281 | ```
|
1282 |
|
1283 | Yields:
|
1284 |
|
1285 | ```txt
|
1286 | example.md: no issues found
|
1287 | ```
|
1288 |
|
1289 | `example.md` now contains:
|
1290 |
|
1291 | ```markdown
|
1292 | # Hello, world!
|
1293 |
|
1294 | *Emphasis* and **importance**.
|
1295 |
|
1296 | ## Table of contents
|
1297 |
|
1298 | * [API](#api)
|
1299 | * [License](#license)
|
1300 |
|
1301 | ## API
|
1302 |
|
1303 | ## License
|
1304 |
|
1305 | [MIT](license) © [Titus Wormer](https://wooorm.com)
|
1306 | ```
|
1307 |
|
1308 | ### `ProcessCallback`
|
1309 |
|
1310 | Callback called when the process is done (TypeScript type).
|
1311 |
|
1312 | Called with either an error or a result.
|
1313 |
|
1314 | ###### Parameters
|
1315 |
|
1316 | * `error` (`Error`, optional)
|
1317 | — fatal error
|
1318 | * `file` ([`VFile`][vfile], optional)
|
1319 | — processed file
|
1320 |
|
1321 | ###### Returns
|
1322 |
|
1323 | Nothing (`undefined`).
|
1324 |
|
1325 | ###### Example
|
1326 |
|
1327 | This example shows how `process` can be used to process a file with a callback.
|
1328 |
|
1329 | ```js
|
1330 | import remarkGithub from 'remark-github'
|
1331 | import remarkParse from 'remark-parse'
|
1332 | import remarkStringify from 'remark-stringify'
|
1333 | import {unified} from 'unified'
|
1334 | import {reporter} from 'vfile-reporter'
|
1335 |
|
1336 | unified()
|
1337 | .use(remarkParse)
|
1338 | .use(remarkGithub)
|
1339 | .use(remarkStringify)
|
1340 | .process('@unifiedjs', function (error, file) {
|
1341 | if (error) throw error
|
1342 | if (file) {
|
1343 | console.error(reporter(file))
|
1344 | console.log(String(file))
|
1345 | }
|
1346 | })
|
1347 | ```
|
1348 |
|
1349 | Yields:
|
1350 |
|
1351 | ```txt
|
1352 | no issues found
|
1353 | ```
|
1354 |
|
1355 | ```markdown
|
1356 | [**@unifiedjs**](https://github.com/unifiedjs)
|
1357 | ```
|
1358 |
|
1359 | ### `Processor`
|
1360 |
|
1361 | Type of a [`processor`][api-processor] (TypeScript type).
|
1362 |
|
1363 | ### `RunCallback`
|
1364 |
|
1365 | Callback called when transformers are done (TypeScript type).
|
1366 |
|
1367 | Called with either an error or results.
|
1368 |
|
1369 | ###### Parameters
|
1370 |
|
1371 | * `error` (`Error`, optional)
|
1372 | — fatal error
|
1373 | * `tree` ([`Node`][node], optional)
|
1374 | — transformed tree
|
1375 | * `file` ([`VFile`][vfile], optional)
|
1376 | — file
|
1377 |
|
1378 | ###### Returns
|
1379 |
|
1380 | Nothing (`undefined`).
|
1381 |
|
1382 | ### `Settings`
|
1383 |
|
1384 | Interface of known extra options, that can be supported by parser and
|
1385 | compilers.
|
1386 |
|
1387 | This exists so that users can use packages such as `remark`, which configure
|
1388 | both parsers and compilers (in this case `remark-parse` and
|
1389 | `remark-stringify`), and still provide options for them.
|
1390 |
|
1391 | When you make parsers or compilers, that could be packaged up together, you
|
1392 | should support `this.data('settings')` as input and merge it with explicitly
|
1393 | passed `options`.
|
1394 | Then, to type it, using `remark-stringify` as an example, do something like:
|
1395 |
|
1396 | ```ts
|
1397 | declare module 'unified' {
|
1398 | interface Settings {
|
1399 | bullet: '*' | '+' | '-'
|
1400 | // …
|
1401 | }
|
1402 | }
|
1403 |
|
1404 | export {} // You may not need this, but it makes sure the file is a module.
|
1405 | ```
|
1406 |
|
1407 | ###### Type
|
1408 |
|
1409 | ```ts
|
1410 | interface Settings {}
|
1411 | ```
|
1412 |
|
1413 | ### `TransformCallback`
|
1414 |
|
1415 | Callback passed to transforms (TypeScript type).
|
1416 |
|
1417 | If the signature of a `transformer` accepts a third argument, the transformer
|
1418 | may perform asynchronous operations, and must call it.
|
1419 |
|
1420 | ###### Parameters
|
1421 |
|
1422 | * `error` (`Error`, optional)
|
1423 | — fatal error to stop the process
|
1424 | * `tree` ([`Node`][node], optional)
|
1425 | — new, changed, tree
|
1426 | * `file` ([`VFile`][vfile], optional)
|
1427 | — new, changed, file
|
1428 |
|
1429 | ###### Returns
|
1430 |
|
1431 | Nothing (`undefined`).
|
1432 |
|
1433 | ### `Transformer`
|
1434 |
|
1435 | Transformers handle syntax trees and files (TypeScript type).
|
1436 |
|
1437 | They are functions that are called each time a syntax tree and file are
|
1438 | passed through the run phase.
|
1439 | When an error occurs in them (either because it’s thrown, returned,
|
1440 | rejected, or passed to `next`), the process stops.
|
1441 |
|
1442 | The run phase is handled by [`trough`][trough], see its documentation for
|
1443 | the exact semantics of these functions.
|
1444 |
|
1445 | > 👉 **Note**: you should likely ignore `next`: don’t accept it.
|
1446 | > it supports callback-style async work.
|
1447 | > But promises are likely easier to reason about.
|
1448 |
|
1449 | ###### Type
|
1450 |
|
1451 | ```ts
|
1452 | type Transformer<
|
1453 | Input extends Node = Node,
|
1454 | Output extends Node = Input
|
1455 | > = (
|
1456 | tree: Input,
|
1457 | file: VFile,
|
1458 | next: TransformCallback<Output>
|
1459 | ) =>
|
1460 | | Promise<Output | undefined>
|
1461 | | Output
|
1462 | | Error
|
1463 | | undefined
|
1464 | ```
|
1465 |
|
1466 | ## Types
|
1467 |
|
1468 | This package is fully typed with [TypeScript][].
|
1469 | It exports the additional types
|
1470 | [`CompileResultMap`][api-compile-result-map],
|
1471 | [`CompileResults`][api-compile-results],
|
1472 | [`Compiler`][api-compiler],
|
1473 | [`Data`][api-data],
|
1474 | [`Parser`][api-parser],
|
1475 | [`Pluggable`][api-pluggable],
|
1476 | [`PluggableList`][api-pluggable-list],
|
1477 | [`Plugin`][api-plugin],
|
1478 | [`PluginTuple`][api-plugin-tuple],
|
1479 | [`Preset`][api-preset],
|
1480 | [`ProcessCallback`][api-process-callback],
|
1481 | [`Processor`][api-processor],
|
1482 | [`RunCallback`][api-run-callback],
|
1483 | [`Settings`][api-settings],
|
1484 | [`TransformCallback`][api-transform-callback],
|
1485 | and [`Transformer`][api-transformer]
|
1486 |
|
1487 | For TypeScript to work, it is particularly important to type your plugins
|
1488 | correctly.
|
1489 | We strongly recommend using the `Plugin` type with its generics and to use the
|
1490 | node types for the syntax trees provided by our packages (as in,
|
1491 | [`@types/hast`][types-hast], [`@types/mdast`][types-mdast],
|
1492 | [`@types/nlcst`][types-nlcst]).
|
1493 |
|
1494 | ```js
|
1495 | /**
|
1496 | * @typedef {import('hast').Root} HastRoot
|
1497 | * @typedef {import('mdast').Root} MdastRoot
|
1498 | */
|
1499 |
|
1500 | /**
|
1501 | * @typedef Options
|
1502 | * Configuration (optional).
|
1503 | * @property {boolean | null | undefined} [someField]
|
1504 | * Some option (optional).
|
1505 | */
|
1506 |
|
1507 | // To type options:
|
1508 | /** @type {import('unified').Plugin<[(Options | null | undefined)?]>} */
|
1509 | export function myPluginAcceptingOptions(options) {
|
1510 | const settings = options || {}
|
1511 | // `settings` is now `Options`.
|
1512 | }
|
1513 |
|
1514 | // To type a plugin that works on a certain tree, without options:
|
1515 | /** @type {import('unified').Plugin<[], MdastRoot>} */
|
1516 | export function myRemarkPlugin() {
|
1517 | return function (tree, file) {
|
1518 | // `tree` is `MdastRoot`.
|
1519 | }
|
1520 | }
|
1521 |
|
1522 | // To type a plugin that transforms one tree into another:
|
1523 | /** @type {import('unified').Plugin<[], MdastRoot, HastRoot>} */
|
1524 | export function remarkRehype() {
|
1525 | return function (tree) {
|
1526 | // `tree` is `MdastRoot`.
|
1527 | // Result must be `HastRoot`.
|
1528 | }
|
1529 | }
|
1530 |
|
1531 | // To type a plugin that defines a parser:
|
1532 | /** @type {import('unified').Plugin<[], string, MdastRoot>} */
|
1533 | export function remarkParse(options) {}
|
1534 |
|
1535 | // To type a plugin that defines a compiler:
|
1536 | /** @type {import('unified').Plugin<[], HastRoot, string>} */
|
1537 | export function rehypeStringify(options) {}
|
1538 | ```
|
1539 |
|
1540 | ## Compatibility
|
1541 |
|
1542 | Projects maintained by the unified collective are compatible with maintained
|
1543 | versions of Node.js.
|
1544 |
|
1545 | When we cut a new major release, we drop support for unmaintained versions of
|
1546 | Node.
|
1547 | This means we try to keep the current release line, `unified@^11`, compatible
|
1548 | with Node.js 16.
|
1549 |
|
1550 | ## Contribute
|
1551 |
|
1552 | See [`contributing.md`][contributing] in [`unifiedjs/.github`][health] for ways
|
1553 | to get started.
|
1554 | See [`support.md`][support] for ways to get help.
|
1555 |
|
1556 | This project has a [code of conduct][coc].
|
1557 | By interacting with this repository, organization, or community you agree to
|
1558 | abide by its terms.
|
1559 |
|
1560 | For info on how to submit a security report, see our
|
1561 | [security policy][security].
|
1562 |
|
1563 | ## Sponsor
|
1564 |
|
1565 | Support this effort and give back by sponsoring on [OpenCollective][collective]!
|
1566 |
|
1567 | <table>
|
1568 | <tr valign="middle">
|
1569 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1570 | <a href="https://vercel.com">Vercel</a><br><br>
|
1571 | <a href="https://vercel.com"><img src="https://avatars1.githubusercontent.com/u/14985020?s=256&v=4" width="128"></a>
|
1572 | </td>
|
1573 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1574 | <a href="https://motif.land">Motif</a><br><br>
|
1575 | <a href="https://motif.land"><img src="https://avatars1.githubusercontent.com/u/74457950?s=256&v=4" width="128"></a>
|
1576 | </td>
|
1577 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1578 | <a href="https://www.hashicorp.com">HashiCorp</a><br><br>
|
1579 | <a href="https://www.hashicorp.com"><img src="https://avatars1.githubusercontent.com/u/761456?s=256&v=4" width="128"></a>
|
1580 | </td>
|
1581 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1582 | <a href="https://americanexpress.io">American Express</a><br><br>
|
1583 | <a href="https://americanexpress.io"><img src="https://avatars1.githubusercontent.com/u/3853301?s=256&v=4" width="128"></a>
|
1584 | </td>
|
1585 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1586 | <a href="https://www.gitbook.com">GitBook</a><br><br>
|
1587 | <a href="https://www.gitbook.com"><img src="https://avatars1.githubusercontent.com/u/7111340?s=256&v=4" width="128"></a>
|
1588 | </td>
|
1589 | </tr>
|
1590 | <tr valign="middle">
|
1591 | </tr>
|
1592 | <tr valign="middle">
|
1593 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1594 | <a href="https://www.gatsbyjs.org">Gatsby</a><br><br>
|
1595 | <a href="https://www.gatsbyjs.org"><img src="https://avatars1.githubusercontent.com/u/12551863?s=256&v=4" width="128"></a>
|
1596 | </td>
|
1597 | <td width="20%" align="center" rowspan="2" colspan="2">
|
1598 | <a href="https://www.netlify.com">Netlify</a><br><br>
|
1599 |
|
1600 | <a href="https://www.netlify.com"><img src="https://images.opencollective.com/netlify/4087de2/logo/256.png" width="128"></a>
|
1601 | </td>
|
1602 | <td width="10%" align="center">
|
1603 | <a href="https://www.coinbase.com">Coinbase</a><br><br>
|
1604 | <a href="https://www.coinbase.com"><img src="https://avatars1.githubusercontent.com/u/1885080?s=256&v=4" width="64"></a>
|
1605 | </td>
|
1606 | <td width="10%" align="center">
|
1607 | <a href="https://themeisle.com">ThemeIsle</a><br><br>
|
1608 | <a href="https://themeisle.com"><img src="https://avatars1.githubusercontent.com/u/58979018?s=128&v=4" width="64"></a>
|
1609 | </td>
|
1610 | <td width="10%" align="center">
|
1611 | <a href="https://expo.io">Expo</a><br><br>
|
1612 | <a href="https://expo.io"><img src="https://avatars1.githubusercontent.com/u/12504344?s=128&v=4" width="64"></a>
|
1613 | </td>
|
1614 | <td width="10%" align="center">
|
1615 | <a href="https://boostnote.io">Boost Note</a><br><br>
|
1616 | <a href="https://boostnote.io"><img src="https://images.opencollective.com/boosthub/6318083/logo/128.png" width="64"></a>
|
1617 | </td>
|
1618 | <td width="10%" align="center">
|
1619 | <a href="https://markdown.space">Markdown Space</a><br><br>
|
1620 | <a href="https://markdown.space"><img src="https://images.opencollective.com/markdown-space/e1038ed/logo/128.png" width="64"></a>
|
1621 | </td>
|
1622 | <td width="10%" align="center">
|
1623 | <a href="https://www.holloway.com">Holloway</a><br><br>
|
1624 | <a href="https://www.holloway.com"><img src="https://avatars1.githubusercontent.com/u/35904294?s=128&v=4" width="64"></a>
|
1625 | </td>
|
1626 | </tr>
|
1627 | <tr valign="middle">
|
1628 | <td width="100%" align="center" colspan="6">
|
1629 | <br>
|
1630 | <a href="https://opencollective.com/unified"><strong>You?</strong></a>
|
1631 | <br><br>
|
1632 | </td>
|
1633 | </tr>
|
1634 | </table>
|
1635 |
|
1636 | ## Acknowledgments
|
1637 |
|
1638 | Preliminary work for unified was done [in 2014][preliminary] for
|
1639 | **[retext][]** and inspired by [`ware`][ware].
|
1640 | Further incubation happened in **[remark][]**.
|
1641 | The project was finally [externalised][] in 2015 and [published][] as `unified`.
|
1642 | The project was authored by **[@wooorm](https://github.com/wooorm)**.
|
1643 |
|
1644 | Although `unified` since moved its plugin architecture to [`trough`][trough],
|
1645 | thanks to **[@calvinfo](https://github.com/calvinfo)**,
|
1646 | **[@ianstormtaylor](https://github.com/ianstormtaylor)**, and others for their
|
1647 | work on [`ware`][ware], as it was a huge initial inspiration.
|
1648 |
|
1649 | ## License
|
1650 |
|
1651 | [MIT][license] © [Titus Wormer][author]
|
1652 |
|
1653 |
|
1654 |
|
1655 | [logo]: https://raw.githubusercontent.com/unifiedjs/unified/93862e5/logo.svg?sanitize=true
|
1656 |
|
1657 | [build-badge]: https://github.com/unifiedjs/unified/workflows/main/badge.svg
|
1658 |
|
1659 | [build]: https://github.com/unifiedjs/unified/actions
|
1660 |
|
1661 | [coverage-badge]: https://img.shields.io/codecov/c/github/unifiedjs/unified.svg
|
1662 |
|
1663 | [coverage]: https://codecov.io/github/unifiedjs/unified
|
1664 |
|
1665 | [downloads-badge]: https://img.shields.io/npm/dm/unified.svg
|
1666 |
|
1667 | [downloads]: https://www.npmjs.com/package/unified
|
1668 |
|
1669 | [size-badge]: https://img.shields.io/bundlejs/size/unified
|
1670 |
|
1671 | [size]: https://bundlejs.com/?q=unified
|
1672 |
|
1673 | [sponsors-badge]: https://opencollective.com/unified/sponsors/badge.svg
|
1674 |
|
1675 | [backers-badge]: https://opencollective.com/unified/backers/badge.svg
|
1676 |
|
1677 | [collective]: https://opencollective.com/unified
|
1678 |
|
1679 | [chat-badge]: https://img.shields.io/badge/chat-discussions-success.svg
|
1680 |
|
1681 | [chat]: https://github.com/unifiedjs/unified/discussions
|
1682 |
|
1683 | [esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c
|
1684 |
|
1685 | [esmsh]: https://esm.sh
|
1686 |
|
1687 | [typescript]: https://www.typescriptlang.org
|
1688 |
|
1689 | [health]: https://github.com/unifiedjs/.github
|
1690 |
|
1691 | [contributing]: https://github.com/unifiedjs/.github/blob/main/contributing.md
|
1692 |
|
1693 | [support]: https://github.com/unifiedjs/.github/blob/main/support.md
|
1694 |
|
1695 | [coc]: https://github.com/unifiedjs/.github/blob/main/code-of-conduct.md
|
1696 |
|
1697 | [security]: https://github.com/unifiedjs/.github/blob/main/security.md
|
1698 |
|
1699 | [license]: license
|
1700 |
|
1701 | [author]: https://wooorm.com
|
1702 |
|
1703 | [npm]: https://docs.npmjs.com/cli/install
|
1704 |
|
1705 | [site]: https://unifiedjs.com
|
1706 |
|
1707 | [twitter]: https://twitter.com/unifiedjs
|
1708 |
|
1709 | [rehype]: https://github.com/rehypejs/rehype
|
1710 |
|
1711 | [remark]: https://github.com/remarkjs/remark
|
1712 |
|
1713 | [retext]: https://github.com/retextjs/retext
|
1714 |
|
1715 | [syntax-tree]: https://github.com/syntax-tree
|
1716 |
|
1717 | [esast]: https://github.com/syntax-tree/esast
|
1718 |
|
1719 | [hast]: https://github.com/syntax-tree/hast
|
1720 |
|
1721 | [mdast]: https://github.com/syntax-tree/mdast
|
1722 |
|
1723 | [nlcst]: https://github.com/syntax-tree/nlcst
|
1724 |
|
1725 | [unist]: https://github.com/syntax-tree/unist
|
1726 |
|
1727 | [xast]: https://github.com/syntax-tree/xast
|
1728 |
|
1729 | [unified-engine]: https://github.com/unifiedjs/unified-engine
|
1730 |
|
1731 | [unified-args]: https://github.com/unifiedjs/unified-args
|
1732 |
|
1733 | [unified-engine-gulp]: https://github.com/unifiedjs/unified-engine-gulp
|
1734 |
|
1735 | [unified-language-server]: https://github.com/unifiedjs/unified-language-server
|
1736 |
|
1737 | [unified-stream]: https://github.com/unifiedjs/unified-stream
|
1738 |
|
1739 | [rehype-remark]: https://github.com/rehypejs/rehype-remark
|
1740 |
|
1741 | [rehype-retext]: https://github.com/rehypejs/rehype-retext
|
1742 |
|
1743 | [remark-rehype]: https://github.com/remarkjs/remark-rehype
|
1744 |
|
1745 | [remark-retext]: https://github.com/remarkjs/remark-retext
|
1746 |
|
1747 | [node]: https://github.com/syntax-tree/unist#node
|
1748 |
|
1749 | [vfile]: https://github.com/vfile/vfile
|
1750 |
|
1751 | [vfile-compatible]: https://github.com/vfile/vfile#compatible
|
1752 |
|
1753 | [vfile-value]: https://github.com/vfile/vfile#value
|
1754 |
|
1755 | [vfile-utilities]: https://github.com/vfile/vfile#list-of-utilities
|
1756 |
|
1757 | [rehype-react]: https://github.com/rehypejs/rehype-react
|
1758 |
|
1759 | [trough]: https://github.com/wooorm/trough#function-fninput-next
|
1760 |
|
1761 | [rehype-plugins]: https://github.com/rehypejs/rehype/blob/main/doc/plugins.md#list-of-plugins
|
1762 |
|
1763 | [remark-plugins]: https://github.com/remarkjs/remark/blob/main/doc/plugins.md#list-of-plugins
|
1764 |
|
1765 | [retext-plugins]: https://github.com/retextjs/retext/blob/main/doc/plugins.md#list-of-plugins
|
1766 |
|
1767 | [awesome-rehype]: https://github.com/rehypejs/awesome-rehype
|
1768 |
|
1769 | [awesome-remark]: https://github.com/remarkjs/awesome-remark
|
1770 |
|
1771 | [awesome-retext]: https://github.com/retextjs/awesome-retext
|
1772 |
|
1773 | [topic-rehype-plugin]: https://github.com/topics/rehype-plugin
|
1774 |
|
1775 | [topic-remark-plugin]: https://github.com/topics/remark-plugin
|
1776 |
|
1777 | [topic-retext-plugin]: https://github.com/topics/retext-plugin
|
1778 |
|
1779 | [types-hast]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/hast
|
1780 |
|
1781 | [types-mdast]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/mdast
|
1782 |
|
1783 | [types-nlcst]: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/nlcst
|
1784 |
|
1785 | [preliminary]: https://github.com/retextjs/retext/commit/8fcb1f
|
1786 |
|
1787 | [externalised]: https://github.com/remarkjs/remark/commit/9892ec
|
1788 |
|
1789 | [published]: https://github.com/unifiedjs/unified/commit/2ba1cf
|
1790 |
|
1791 | [ware]: https://github.com/segmentio/ware
|
1792 |
|
1793 | [api]: #api
|
1794 |
|
1795 | [contribute]: #contribute
|
1796 |
|
1797 | [overview]: #overview
|
1798 |
|
1799 | [sponsor]: #sponsor
|
1800 |
|
1801 | [api-compile-result-map]: #compileresultmap
|
1802 |
|
1803 | [api-compile-results]: #compileresults
|
1804 |
|
1805 | [api-compiler]: #compiler
|
1806 |
|
1807 | [api-data]: #data
|
1808 |
|
1809 | [api-freeze]: #processorfreeze
|
1810 |
|
1811 | [api-parser]: #parser
|
1812 |
|
1813 | [api-pluggable]: #pluggable
|
1814 |
|
1815 | [api-pluggable-list]: #pluggablelist
|
1816 |
|
1817 | [api-plugin]: #plugin
|
1818 |
|
1819 | [api-plugin-tuple]: #plugintuple
|
1820 |
|
1821 | [api-preset]: #preset
|
1822 |
|
1823 | [api-process]: #processorprocessfile-done
|
1824 |
|
1825 | [api-process-callback]: #processcallback
|
1826 |
|
1827 | [api-processor]: #processor
|
1828 |
|
1829 | [api-run-callback]: #runcallback
|
1830 |
|
1831 | [api-settings]: #settings
|
1832 |
|
1833 | [api-transform-callback]: #transformcallback
|
1834 |
|
1835 | [api-transformer]: #transformer
|