- Notifications
You must be signed in to change notification settings - Fork112
☔️ interface for parsing, inspecting, transforming, and serializing content through syntax trees
License
unifiedjs/unified
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
unified lets you inspect and transform content with plugins.
- What is this?
- When should I use this?
- Install
- Use
- Overview
- API
processor()
processor.compiler
processor.data([key[, value]])
processor.freeze()
processor.parse(file)
processor.parser
processor.process(file[, done])
processor.processSync(file)
processor.run(tree[, file][, done])
processor.runSync(tree[, file])
processor.stringify(tree[, file])
processor.use(plugin[, options])
CompileResultMap
CompileResults
Compiler
Data
Parser
Pluggable
PluggableList
Plugin
PluginTuple
Preset
ProcessCallback
Processor
RunCallback
Settings
TransformCallback
Transformer
- Types
- Compatibility
- Contribute
- Sponsor
- Acknowledgments
- License
unified is two things:
- unified is a collective of 500+ free and open source packages that workwith content as structured data (ASTs)
unified
(this project) is the core package, used in 1.3m+ projects on GH,to process content with plugins
Several ecosystems are built on unified around different kinds of content.Notably,remark (markdown),rehype (HTML), andretext (naturallanguage).These ecosystems can be connected together.
- for more about us, see
unifiedjs.com
- for questions, seesupport
- to help, seecontribute andsponsor below
In some cases, you are already using unified.For example, it’s used in MDX, Gatsby, Docusaurus, etc.In those cases, you don’t need to addunified
yourself but you can includeplugins into those projects.
But the real fun (for some) is to get your hands dirty and work with syntaxtrees and build with it yourself.You can create those projects, or things like Prettier, or your own sitegenerator.You can connect utilities together and make your own plugins that check forproblems and transform from one thing to another.
When you are dealing with one type of content (such as markdown), you can usethe main package of that ecosystem instead (soremark
).When you are dealing with different kinds of content (such as markdown andHTML), it’s recommended to useunified
itself, and pick and choose the pluginsyou need.
This package isESM only.In Node.js (version 16+), install withnpm:
npm install unified
In Deno withesm.sh
:
import{unified}from'https://esm.sh/unified@11'
In browsers withesm.sh
:
<scripttype="module">import{unified}from'https://esm.sh/unified@11?bundle'</script>
importrehypeDocumentfrom'rehype-document'importrehypeFormatfrom'rehype-format'importrehypeStringifyfrom'rehype-stringify'importremarkParsefrom'remark-parse'importremarkRehypefrom'remark-rehype'import{unified}from'unified'import{reporter}from'vfile-reporter'constfile=awaitunified().use(remarkParse).use(remarkRehype).use(rehypeDocument,{title:'👋🌍'}).use(rehypeFormat).use(rehypeStringify).process('# Hello world!')console.error(reporter(file))console.log(String(file))
Yields:
no issues found
<!doctype html><htmllang="en"><head><metacharset="utf-8"><title>👋🌍</title><metaname="viewport"content="width=device-width, initial-scale=1"></head><body><h1>Hello world!</h1></body></html>
unified
is an interface for processing content with syntax trees.Syntax trees are a representation of content understandable to programs.Those programs, calledplugins, take these trees and inspect andmodify them.To get to the syntax tree from text, there is aparser.To get from that back to text, there is acompiler.This is theprocess of aprocessor.
| ........................ process ........................... || .......... parse ... | ... run ... | ... stringify ..........| +--------+ +----------+Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output +--------+ | +----------+ X | +--------------+ | Transformers | +--------------+
Processors process content.On its own,unified
(the root processor) doesn’t work.It needs to be configured with plugins to work.For example:
constprocessor=unified().use(remarkParse).use(remarkRehype).use(rehypeDocument,{title:'👋🌍'}).use(rehypeFormat).use(rehypeStringify)
That processor can do different things.It can:
- …parse markdown (
parse
) - …turn parsed markdown into HTML and format the HTML (
run
) - …compile HTML (
stringify
) - …do all of the above (
process
)
Every processor implements another processor.To create a processor, call another processor.The new processor is configured to work the same as its ancestor.But when the descendant processor is configured in the future it does not affectthe ancestral processor.
When processors are exposed from a module (for example,unified
itself) theyshould not be configured directly, as that would change their behavior for allmodule users.Those processors arefrozen and they should be called to createa new processor before they are used.
When processing a document, metadata is gathered about that document.vfile
is the file format that stores data, metadata, and messagesabout files for unified and plugins.
There are severalutilities for working with these files.
The syntax trees used in unified areunist nodes.A tree represents a whole document and eachnode is a plain JavaScriptobject with atype
field.The semantics of nodes and the format of syntax trees is defined by otherprojects:
There are many utilities for working with trees listed in each aforementionedproject and maintained in thesyntax-tree
organization.These utilities are a level lower than unified itself and are building blocksthat can be used to make plugins.
Around each syntax tree is an ecosystem that focusses on that particular kindof content.At their core, they parse text to a tree and compile that tree back to text.They also provide plugins that work with the syntax tree, without requiringthat the end user has knowledge about that tree.
Each aforementioned ecosystem comes with a large set of plugins that you canpick and choose from to do all kinds of things.
- List of remark plugins ·
remarkjs/awesome-remark
·remark-plugin
topic - List of rehype plugins ·
rehypejs/awesome-rehype
·rehype-plugin
topic - List of retext plugins ·
retextjs/awesome-retext
·retext-plugin
topic
There are also a few plugins that work in any ecosystem:
unified-diff
— ignore unrelated messages in GitHub Actions and Travisunified-infer-git-meta
— infer metadata of a document from Gitunified-message-control
— enable, disable, and ignore messages from content
Processors are configured withplugins or with thedata
method.Most plugins also accept configuration through options.See each plugin’s readme for more info.
unified can integrate with the file system throughunified-engine
.CLI apps can be created withunified-args
, Gulp plugins withunified-engine-gulp
, and language servers withunified-language-server
.A streaming interface can be created withunified-stream
.
TheAPI provided byunified
allows multiple files to be processed andgives access to metadata (such as lint messages):
importrehypeStringifyfrom'rehype-stringify'importremarkParsefrom'remark-parse'importremarkPresetLintMarkdownStyleGuidefrom'remark-preset-lint-markdown-style-guide'importremarkRehypefrom'remark-rehype'importremarkRetextfrom'remark-retext'importretextEnglishfrom'retext-english'importretextEqualityfrom'retext-equality'import{unified}from'unified'import{reporter}from'vfile-reporter'constfile=awaitunified().use(remarkParse).use(remarkPresetLintMarkdownStyleGuide).use(remarkRetext,unified().use(retextEnglish).use(retextEquality)).use(remarkRehype).use(rehypeStringify).process('*Emphasis* and _stress_, you guys!')console.error(reporter(file))console.log(String(file))
Yields:
1:16-1:24 warning Emphasis should use `*` as a marker emphasis-marker remark-lint1:30-1:34 warning `guys` may be insensitive, use `people`, `persons`, `folks` instead gals-man retext-equality⚠ 2 warnings
<p><em>Emphasis</em> and<em>stress</em>, you guys!</p>
Ecosystems can be combined in two modes.
Bridge mode transforms the tree from one format (origin) to another(destination).A different processor runs on the destination tree.Afterwards, the original processor continues with the origin tree.
Mutate mode also transforms the syntax tree from one format to another.But the original processor continues transforming the destination tree.
In the previous example (“Programming interface”),remark-retext
is used inbridge mode: the origin syntax tree is kept after retext is done; whereasremark-rehype
is used in mutate mode: it sets a new syntax tree and discardsthe origin tree.
The following plugins lets you combine ecosystems:
remark-retext
— turn markdown into natural languageremark-rehype
— turn markdown into HTMLrehype-retext
— turn HTML into natural languagerehype-remark
— turn HTML into markdown
This package exports the identifierunified
(the rootprocessor
).There is no default export.
Create a new processor.
Newunfrozen processor (processor
).
This processor is configured to work the same as its ancestor.When the descendant processor is configured in the future it does not affectthe ancestral processor.
This example shows how a new processor can be created (fromremark
) and linkedtostdin(4) andstdout(4).
importprocessfrom'node:process'importconcatStreamfrom'concat-stream'import{remark}from'remark'process.stdin.pipe(concatStream(function(buf){process.stdout.write(String(remark().processSync(buf)))}))
Compiler to use (Compiler
, optional).
Configure the processor with info available to all plugins.Information is stored in an object.
Typically, options can be given to a specific plugin, but sometimes it makessense to have information shared with several plugins.For example, a list of HTML elements that are self-closing, which is neededduring allphases.
👉Note: setting information cannot occur onfrozenprocessors.Call the processor first to create a new unfrozen processor.
👉Note: to register custom data in TypeScript, augment the
Data
interface.
processor = processor.data(key, value)
processor = processor.data(dataset)
value = processor.data(key)
dataset = processor.data()
key
(keyof Data
, optional) — field to getvalue
(Data[key]
) — value to setvalues
(Data
) — values to set
The current processor when setting (processor
), the value atkey
when getting (Data[key]
), or the entire dataset whengetting without key (Data
).
This example show how to get and set info:
import{unified}from'unified'constprocessor=unified().data('alpha','bravo')processor.data('alpha')// => 'bravo'processor.data()// => {alpha: 'bravo'}processor.data({charlie:'delta'})processor.data()// => {charlie: 'delta'}
Freeze a processor.
Frozen processors are meant to be extended and not to be configured directly.
When a processor is frozen it cannot be unfrozen.New processors working the same way can be created by calling the processor.
It’s possible to freeze processors explicitly by calling.freeze()
.Processors freeze automatically when.parse()
,.run()
,.runSync()
,.stringify()
,.process()
, or.processSync()
are called.
The current processor (processor
).
This example,index.js
, shows howrehype
prevents extensions to itself:
importrehypeParsefrom'rehype-parse'importrehypeStringifyfrom'rehype-stringify'import{unified}from'unified'exportconstrehype=unified().use(rehypeParse).use(rehypeStringify).freeze()
That processor can be used and configured like so:
import{rehype}from'rehype'importrehypeFormatfrom'rehype-format'// …rehype().use(rehypeFormat)// …
A similar looking example is broken as operates on the frozen interface.If this behavior was allowed it would result in unexpected behavior so an erroris thrown.This is not valid:
import{rehype}from'rehype'importrehypeFormatfrom'rehype-format'// …rehype.use(rehypeFormat)// …
Yields:
~/node_modules/unified/index.js:426 throw new Error(^Error: Cannot call `use` on a frozen processor.Create a new processor first, by calling it: use `processor()` instead of `processor`. at assertUnfrozen (~/node_modules/unified/index.js:426:11) at Function.use (~/node_modules/unified/index.js:165:5) …
Parse text to a syntax tree.
👉Note:
parse
freezes the processor if not alreadyfrozen.
👉Note:
parse
performs theparse phase, not the run phaseor other phases.
file
(Compatible
) — file to parse; typicallystring
orVFile
; any value accepted asx
innew VFile(x)
Syntax tree representingfile
(Node
).
This example shows howparse
can be used to create a tree from a file.
importremarkParsefrom'remark-parse'import{unified}from'unified'consttree=unified().use(remarkParse).parse('# Hello world!')console.log(tree)
Yields:
{type:'root',children:[{type:'heading',depth:1,children:[Array],position:[Object]}],position:{start:{line:1,column:1,offset:0},end:{line:1,column:15,offset:14}}}
Parser to use (Parser
, optional).
Process the given file as configured on the processor.
👉Note:
process
freezes the processor if not alreadyfrozen.
👉Note:
process
performs theparse, run, and stringifyphases.
processor.process(file, done)
Promise<VFile> = processor.process(file?)
file
(Compatible
, optional) — file; typicallystring
orVFile
; any value accepted asx
innew VFile(x)
done
(ProcessCallback
, optional) — callback
Nothing ifdone
is given (undefined
).Otherwise a promise, rejected with a fatal error or resolved with theprocessed file (Promise<VFile>
).
The parsed, transformed, and compiled value is available atfile.value
(seenote).
👉Note: unified typically compiles by serializing: mostcompilers return
string
(orUint8Array
).Some compilers, such as the one configured withrehype-react
, return other values (in this case, a Reacttree).If you’re using a compiler that doesn’t serialize, expect different resultvalues.To register custom results in TypeScript, add them to
CompileResultMap
.
This example shows howprocess
can be used to process a file:
importrehypeDocumentfrom'rehype-document'importrehypeFormatfrom'rehype-format'importrehypeStringifyfrom'rehype-stringify'importremarkParsefrom'remark-parse'importremarkRehypefrom'remark-rehype'import{unified}from'unified'constfile=awaitunified().use(remarkParse).use(remarkRehype).use(rehypeDocument,{title:'👋🌍'}).use(rehypeFormat).use(rehypeStringify).process('# Hello world!')console.log(String(file))
Yields:
<!doctype html><htmllang="en"><head><metacharset="utf-8"><title>👋🌍</title><metaname="viewport"content="width=device-width, initial-scale=1"></head><body><h1>Hello world!</h1></body></html>
Process the given file as configured on the processor.
An error is thrown if asynchronous transforms are configured.
👉Note:
processSync
freezes the processor if not alreadyfrozen.
👉Note:
processSync
performs theparse, run, and stringifyphases.
file
(Compatible
, optional) — file; typicallystring
orVFile
; any value accepted asx
innew VFile(x)
The processed file (VFile
).
The parsed, transformed, and compiled value is available atfile.value
(seenote).
👉Note: unified typically compiles by serializing: mostcompilers return
string
(orUint8Array
).Some compilers, such as the one configured withrehype-react
, return other values (in this case, a Reacttree).If you’re using a compiler that doesn’t serialize, expect different resultvalues.To register custom results in TypeScript, add them to
CompileResultMap
.
This example shows howprocessSync
can be used to process a file, if alltransformers are synchronous.
importrehypeDocumentfrom'rehype-document'importrehypeFormatfrom'rehype-format'importrehypeStringifyfrom'rehype-stringify'importremarkParsefrom'remark-parse'importremarkRehypefrom'remark-rehype'import{unified}from'unified'constprocessor=unified().use(remarkParse).use(remarkRehype).use(rehypeDocument,{title:'👋🌍'}).use(rehypeFormat).use(rehypeStringify)console.log(String(processor.processSync('# Hello world!')))
Yields:
<!doctype html><htmllang="en"><head><metacharset="utf-8"><title>👋🌍</title><metaname="viewport"content="width=device-width, initial-scale=1"></head><body><h1>Hello world!</h1></body></html>
Runtransformers on a syntax tree.
👉Note:
run
freezes the processor if not alreadyfrozen.
👉Note:
run
performs therun phase, not other phases.
processor.run(tree, done)
processor.run(tree, file, done)
Promise<Node> = processor.run(tree, file?)
tree
(Node
) — tree to transform and inspectfile
(Compatible
, optional) — file associatedwithnode
; any value accepted asx
innew VFile(x)
done
(RunCallback
, optional) — callback
Nothing ifdone
is given (undefined
).Otherwise, a promise rejected with a fatal error or resolved with thetransformed tree (Promise<Node>
).
This example shows howrun
can be used to transform a tree:
importremarkReferenceLinksfrom'remark-reference-links'import{unified}from'unified'import{u}from'unist-builder'consttree=u('root',[u('paragraph',[u('link',{href:'https://example.com'},[u('text','Example Domain')])])])constchangedTree=awaitunified().use(remarkReferenceLinks).run(tree)console.log(changedTree)
Yields:
{type:'root',children:[{type:'paragraph',children:[Array]},{type:'definition',identifier:'1',title:'',url:undefined}]}
Runtransformers on a syntax tree.
An error is thrown if asynchronous transforms are configured.
👉Note:
runSync
freezes the processor if not alreadyfrozen.
👉Note:
runSync
performs therun phase, not other phases.
tree
(Node
) — tree to transform and inspectfile
(Compatible
, optional) — file associatedwithnode
; any value accepted asx
innew VFile(x)
Transformed tree (Node
).
Compile a syntax tree.
👉Note:
stringify
freezes the processor if not alreadyfrozen.
👉Note:
stringify
performs thestringify phase, not the runphase or other phases.
tree
(Node
) — tree to compilefile
(Compatible
, optional) — file associatedwithnode
; any value accepted asx
innew VFile(x)
Textual representation of the tree (Uint8Array
orstring
, see note).
👉Note: unified typically compiles by serializing: most compilersreturn
string
(orUint8Array
).Some compilers, such as the one configured withrehype-react
, return other values (in this case, aReact tree).If you’re using a compiler that doesn’t serialize, expect differentresult values.To register custom results in TypeScript, add them to
CompileResultMap
.
This example shows howstringify
can be used to serialize a syntax tree:
import{h}from'hastscript'importrehypeStringifyfrom'rehype-stringify'import{unified}from'unified'consttree=h('h1','Hello world!')constdocument=unified().use(rehypeStringify).stringify(tree)console.log(document)
Yields:
<h1>Hello world!</h1>
Configure the processor to use a plugin, a list of usable values, or a preset.
If the processor is already using a plugin, the previous plugin configurationis changed based on the options that are passed in.In other words, the plugin is not added a second time.
👉Note:
use
cannot be called onfrozen processors.Call the processor first to create a new unfrozen processor.
processor.use(preset?)
processor.use(list)
processor.use(plugin[, ...parameters])
preset
(Preset
) — plugins and settingslist
(PluggableList
) — list of usable thingsplugin
(Plugin
) — pluginparameters
(Array<unknown>
) — configuration forplugin
, typically asingle options object
Current processor (processor
).
There are many ways to pass plugins to.use()
.This example gives an overview:
import{unified}from'unified'unified()// Plugin with options:.use(pluginA,{x:true,y:true})// Passing the same plugin again merges configuration (to `{x: true, y: false, z: true}`):.use(pluginA,{y:false,z:true})// Plugins:.use([pluginB,pluginC])// Two plugins, the second with options:.use([pluginD,[pluginE,{}]])// Preset with plugins and settings:.use({plugins:[pluginF,[pluginG,{}]],settings:{position:false}})// Settings only:.use({settings:{position:false}})
Interface of known results from compilers (TypeScript type).
Normally, compilers result in text (Value
ofvfile
).When you compile to something else, such as a React node (as in,rehype-react
), you can augment this interface to include that type.
importtype{ReactNode}from'somewhere'declare module'unified'{interfaceCompileResultMap{// Register a new result (value is used, key should match it).ReactNode:ReactNode}}export{}// You may not need this, but it makes sure the file is a module.
UseCompileResults
to access the values.
interfaceCompileResultMap{// Note: if `Value` from `VFile` is changed, this should too.Uint8Array:Uint8Arraystring:string}
Acceptable results from compilers (TypeScript type).
To register custom results, add them toCompileResultMap
.
typeCompileResults=CompileResultMap[keyofCompileResultMap]
Acompiler handles the compiling of a syntax tree to something else(in most cases, text) (TypeScript type).
It is used in the stringify phase and called with aNode
andVFile
representation of the document to compile.It should return the textual representation of the given tree (typicallystring
).
👉Note: unified typically compiles by serializing: most compilersreturn
string
(orUint8Array
).Some compilers, such as the one configured withrehype-react
, return other values (in this case, aReact tree).If you’re using a compiler that doesn’t serialize, expect differentresult values.To register custom results in TypeScript, add them to
CompileResultMap
.
typeCompiler<TreeextendsNode=Node,ResultextendsCompileResults=CompileResults>=(tree:Tree,file:VFile)=>Result
Interface of known data that can be supported by all plugins (TypeScript type).
Typically, options can be given to a specific plugin, but sometimes it makessense to have information shared with several plugins.For example, a list of HTML elements that are self-closing, which is neededduring all phases.
To type this, do something like:
declare module'unified'{interfaceData{htmlVoidElements?:Array<string>|undefined}}export{}// You may not need this, but it makes sure the file is a module.
interfaceData{settings?:Settings|undefined}
SeeSettings
for more info.
Aparser handles the parsing of text to a syntax tree (TypeScript type).
It is used in the parse phase and is called with astring
andVFile
of the document to parse.It must return the syntax tree representation of the given file(Node
).
typeParser<TreeextendsNode=Node>=(document:string,file:VFile)=>Tree
Union of the different ways to add plugins and settings (TypeScript type).
typePluggable=|Plugin<Array<any>,any,any>|PluginTuple<Array<any>,any,any>|Preset
SeePlugin
,PluginTuple
,andPreset
for more info.
List of plugins and presets (TypeScript type).
typePluggableList=Array<Pluggable>
SeePluggable
for more info.
Single plugin (TypeScript type).
Plugins configure the processors they are applied on in the following ways:
- they change the processor, such as the parser, the compiler, or byconfiguring data
- they specify how to handle trees and files
In practice, they are functions that can receive options and configure theprocessor (this
).
👉Note: plugins are called when the processor isfrozen, not when theyare applied.
typePlugin<PluginParametersextendsunknown[]=[],InputextendsNode|string|undefined=Node,Output=Input>=(this:Processor, ...parameters:PluginParameters)=>Inputextendsstring// Parser. ?OutputextendsNode|undefined ?undefined|void :never :OutputextendsCompileResults// Compiler. ?InputextendsNode|undefined ?undefined|void :never :// Inspect/transform.|Transformer<InputextendsNode ?Input :Node,OutputextendsNode ?Output :Node>|undefined|void
SeeTransformer
for more info.
move.js
:
/** *@import {Plugin} from 'unified' *//** *@typedef Options * Configuration (required). *@property {string} extname * File extension to use (must start with `.`). *//**@type {Plugin<[Options]>} */exportfunctionmove(options){if(!options||!options.extname){thrownewError('Missing `options.extname`')}returnfunction(_,file){if(file.extname&&file.extname!==options.extname){file.extname=options.extname}}}
example.md
:
#Hello, world!
example.js
:
importrehypeStringifyfrom'rehype-stringify'importremarkParsefrom'remark-parse'importremarkRehypefrom'remark-rehype'import{read,write}from'to-vfile'import{unified}from'unified'import{reporter}from'vfile-reporter'import{move}from'./move.js'constfile=awaitunified().use(remarkParse).use(remarkRehype).use(move,{extname:'.html'}).use(rehypeStringify).process(awaitread('example.md'))console.error(reporter(file))awaitwrite(file)// Written to `example.html`.
Yields:
example.md: no issues found
…and inexample.html
:
<h1>Hello, world!</h1>
Tuple of a plugin and its configuration (TypeScript type).
The first item is a plugin, the rest are its parameters.
typePluginTuple<TupleParametersextendsunknown[]=[],InputextendsNode|string|undefined=undefined,Output=undefined>=[plugin:Plugin<TupleParameters,Input,Output>, ...parameters:TupleParameters]
SeePlugin
for more info.
Sharable configuration (TypeScript type).
They can contain plugins and settings.
plugins
(PluggableList
, optional)— list of plugins and presetssettings
(Data
, optional)— shared settings for parsers and compilers
preset.js
:
/** *@import {Preset} from 'unified' */importremarkCommentConfigfrom'remark-comment-config'importremarkLicensefrom'remark-license'importremarkPresetLintConsistentfrom'remark-preset-lint-consistent'importremarkPresetLintRecommendedfrom'remark-preset-lint-recommended'importremarkTocfrom'remark-toc'/**@type {Preset} */constpreset={plugins:[remarkPresetLintRecommended,remarkPresetLintConsistent,remarkCommentConfig,[remarkToc,{maxDepth:3,tight:true}],remarkLicense],settings:{bullet:'*',emphasis:'*',fences:true},}exportdefaultpreset
example.md
:
#Hello, world!_Emphasis_ and**importance**.##Table of contents##API##License
example.js
:
import{remark}from'remark'import{read,write}from'to-vfile'import{reporter}from'vfile-reporter'importpresetfrom'./preset.js'constfile=awaitremark().use(preset).process(awaitread('example.md'))console.error(reporter(file))awaitwrite(file)
Yields:
example.md: no issues found
example.md
now contains:
#Hello, world!*Emphasis* and**importance**.##Table of contents*[API](#api)*[License](#license)##API##License[MIT](license) ©[Titus Wormer](https://wooorm.com)
Callback called when the process is done (TypeScript type).
Called with either an error or a result.
error
(Error
, optional)— fatal errorfile
(VFile
, optional)— processed file
Nothing (undefined
).
This example shows howprocess
can be used to process a file with a callback.
importremarkGithubfrom'remark-github'importremarkParsefrom'remark-parse'importremarkStringifyfrom'remark-stringify'import{unified}from'unified'import{reporter}from'vfile-reporter'unified().use(remarkParse).use(remarkGithub).use(remarkStringify).process('@unifiedjs',function(error,file){if(error)throwerrorif(file){console.error(reporter(file))console.log(String(file))}})
Yields:
no issues found
[**@unifiedjs**](https://github.com/unifiedjs)
Type of aprocessor
(TypeScript type).
Callback called when transformers are done (TypeScript type).
Called with either an error or results.
error
(Error
, optional)— fatal errortree
(Node
, optional)— transformed treefile
(VFile
, optional)— file
Nothing (undefined
).
Interface of known extra options, that can be supported by parser andcompilers.
This exists so that users can use packages such asremark
, which configureboth parsers and compilers (in this caseremark-parse
andremark-stringify
), and still provide options for them.
When you make parsers or compilers, that could be packaged up together, youshould supportthis.data('settings')
as input and merge it with explicitlypassedoptions
.Then, to type it, usingremark-stringify
as an example, do something like:
declare module'unified'{interfaceSettings{bullet:'*'|'+'|'-'// …}}export{}// You may not need this, but it makes sure the file is a module.
interfaceSettings{}
Callback passed to transforms (TypeScript type).
If the signature of atransformer
accepts a third argument, the transformermay perform asynchronous operations, and must call it.
error
(Error
, optional)— fatal error to stop the processtree
(Node
, optional)— new, changed, treefile
(VFile
, optional)— new, changed, file
Nothing (undefined
).
Transformers handle syntax trees and files (TypeScript type).
They are functions that are called each time a syntax tree and file arepassed through the run phase.When an error occurs in them (either because it’s thrown, returned,rejected, or passed tonext
), the process stops.
The run phase is handled bytrough
, see its documentation forthe exact semantics of these functions.
👉Note: you should likely ignore
next
: don’t accept it.it supports callback-style async work.But promises are likely easier to reason about.
typeTransformer<InputextendsNode=Node,OutputextendsNode=Input>=(tree:Input,file:VFile,next:TransformCallback<Output>)=>|Promise<Output|undefined>|Output|Error|undefined
This package is fully typed withTypeScript.It exports the additional typesCompileResultMap
,CompileResults
,Compiler
,Data
,Parser
,Pluggable
,PluggableList
,Plugin
,PluginTuple
,Preset
,ProcessCallback
,Processor
,RunCallback
,Settings
,TransformCallback
,andTransformer
For TypeScript to work, it is particularly important to type your pluginscorrectly.We strongly recommend using thePlugin
type with its generics and to use thenode types for the syntax trees provided by our packages (as in,@types/hast
,@types/mdast
,@types/nlcst
).
/** *@import {Root as HastRoot} from 'hast' *@import {Root as MdastRoot} from 'mdast' *@import {Plugin} from 'unified' *//** *@typedef Options * Configuration (optional). *@property {boolean | null | undefined} [someField] * Some option (optional). */// To type options:/**@type {Plugin<[(Options | null | undefined)?]>} */exportfunctionmyPluginAcceptingOptions(options){constsettings=options||{}// `settings` is now `Options`.}// To type a plugin that works on a certain tree, without options:/**@type {Plugin<[], MdastRoot>} */exportfunctionmyRemarkPlugin(){returnfunction(tree,file){// `tree` is `MdastRoot`.}}// To type a plugin that transforms one tree into another:/**@type {Plugin<[], MdastRoot, HastRoot>} */exportfunctionremarkRehype(){returnfunction(tree){// `tree` is `MdastRoot`.// Result must be `HastRoot`.}}// To type a plugin that defines a parser:/**@type {Plugin<[], string, MdastRoot>} */exportfunctionremarkParse(options){}// To type a plugin that defines a compiler:/**@type {Plugin<[], HastRoot, string>} */exportfunctionrehypeStringify(options){}
Projects maintained by the unified collective are compatible with maintainedversions of Node.js.
When we cut a new major release, we drop support for unmaintained versions ofNode.This means we try to keep the current release line,unified@^11
, compatiblewith Node.js 16.
Seecontributing.md
inunifiedjs/.github
for waysto get started.Seesupport.md
for ways to get help.
This project has acode of conduct.By interacting with this repository, organization, or community you agree toabide by its terms.
For info on how to submit a security report, see oursecurity policy.
Support this effort and give back by sponsoring onOpenCollective!
Vercel | Motif | HashiCorp | American Express | GitBook | |||||
Gatsby | Netlify | Coinbase | ThemeIsle | Expo | Boost Note | Markdown Space | Holloway | ||
You? |
Preliminary work for unified was donein 2014 forretext and inspired byware
.Further incubation happened inremark.The project was finallyexternalised in 2015 andpublished asunified
.The project was authored by@wooorm.
Althoughunified
since moved its plugin architecture totrough
,thanks to@calvinfo,@ianstormtaylor, and others for theirwork onware
, as it was a huge initial inspiration.
About
☔️ interface for parsing, inspecting, transforming, and serializing content through syntax trees