Understanding remark
The tool for working with Markdown as ASTs.
What is remark?
- An “ecosystem” of plugins that work with markdown as structured data
- Does this by converting the markdown into an abstract syntax tree (AST)
- Allows “programs” to easily inspect and process / adjust the markdown
- These “programs” are the
remarkplugins - They inspect and change the trees
- These “programs” are the
- Uses
unifiedas the core project that transforms content with ASTsremarkadds support for markdown syntax to unified viamdast, the markdown AST package
- Uses the
micromarkpackage for the actual markdown parsing- Per the
micromarkGithub: “The smallest CommonMark compliant markdown parser”
- Per the
- Github repo for
remarkis actually a monorepo with several packages:remark-parse: a plugin to take markdown as input and turn it into an AST- Uses
mdastunder-the-hood
- Uses
remark-stringify: a plugin to take a the markdown AST (mdast) and turn it into a markdown stringremark: the combination ofunified,remark-parse, andremark-stringifyall together- This is useful to use when the input and output are markdown
remark-cli: CLI that usesremarkto inspect and format markdown in scripts
What is unified?
- Can reference 2 different things:
- The unified collective: 500+ FOS packages that work with ASTs
unified(the library): the core package, used to process content with plugins
- Several different “ecosystems” are built on
unifiedremarkrehype: Similar toremarkbut for working with HTML as an ASTretext: Same, but for natural language- This seems interesting!
- Important parts or plugins for a program built on
unified(see the graphic at the top of this section for a visual)
Overview of unified “runtime”
| ........................ process ........................... |
| .......... parse ... | ... run ... | ... stringify ..........|
+--------+ +----------+
Input ->- | Parser | ->- Syntax Tree ->- | Compiler | ->- Output
+--------+ | +----------+
X
|
+--------------+
| Transformers |
+--------------+
When should I use which package?
Most code examples that I’ve seen end up using remark-parse and remark-stringify as plugins to the core unified library. For example, the following code is in the remark README as a small example of a custom plugin to automatically indent markdown headings:
import remarkParse from "remark-parse";
import remarkStringify from "remark-stringify";
import { unified } from "unified";
import { visit } from "unist-util-visit";
const file = await unified()
.use(remarkParse)
.use(myRemarkPluginToIncreaseHeadings)
.use(remarkStringify)
.process("# Hi, Saturn!");
console.log(String(file)); // => '## Hi, Saturn!'
function myRemarkPluginToIncreaseHeadings() {
/**
* @param {import('mdast').Root} tree
*/
return function (tree) {
visit(tree, function (node) {
if (node.type === "heading") {
node.depth++;
}
});
};
}
However, here’s what the Github repo says about the various packages included:
- If the input is markdown, you can use
remark-parsewithunified(as seen above) - If the output is markdown, you can use
remark-stringifywithunified(as seen above) - If both the input and output are markdown, you can use
remarkon its own.- See the example below
- When you want to inspect and format markdown files in a project, you can use
remark-cli
Example of Just Remark + Plugins
Here’s a quick example of using remark instead of unified, plus some additional plugins. This example includes the use of plugins that check markdown code style:
import { remark } from "remark";
import remarkPresetLintConsistent from "remark-preset-lint-consistent";
import remarkPresetLintRecommended from "remark-preset-lint-recommended";
import { reporter } from "vfile-reporter";
const file = await remark()
.use(remarkPresetLintConsistent)
.use(remarkPresetLintRecommended)
.process("1) Hello, _Jupiter_ and *Neptune*!");
console.error(reporter(file));
This code yields the following in the terminal:
warning Missing newline character at end of file final-newline remark-lint
1:1-1:35 warning Marker style should be `.` ordered-list-marker-style remark-lint
1:4 warning Incorrect list-item indent: add 1 space list-item-indent remark-lint
1:25-1:34 warning Emphasis should use `_` as a marker emphasis-marker remark-lint
⚠ 4 warnings
remark Plugins
There’s still a lot that I have to learn about remark plugins, but here are some quick highlights based on the small amount of research that I’ve done:
- Plugins configure the processors (the overall “content pipeline” that you’re building using
unified) they are applied on in the following ways- Change the processor, such as the parser, compiler, or by configuring data
- They specify how to handle trees and files
- They are functions that can receive options and configure the actual processor (
this) - Plugins are called when the processor is frozen, not when they are applied
- Open Question: I don’t know what this means exactly…
Additional Resources
- remark’s Github Repo
- unified’s Github Repo
- remark Plugins: Concepts - A portion of the
unifiedREADME
- remark Plugins: Concepts - A portion of the
- unified.js Website
- mdast’s Github Repo
- micromark’s Github Repo