From a56e309f8096f6e4e4b23242df21a8f85d569b94 Mon Sep 17 00:00:00 2001 From: Titus Date: Tue, 26 Oct 2021 09:13:37 +0200 Subject: [PATCH] Update docs for newcomers Related-to: unifiedjs/unified#168. Closes GH-6. Reviewed-by: Remco Haszing Reviewed-by: Jeff Caldwell --- readme.md | 290 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 249 insertions(+), 41 deletions(-) diff --git a/readme.md b/readme.md index 22c34b2..a554fd2 100644 --- a/readme.md +++ b/readme.md @@ -12,24 +12,77 @@ (`:cite[smith04]`, `::youtube[Video of a cat in a box]{v=01ab2cd3efg}`, and such). -## Important! +## Contents -This plugin is made for the new parser in remark -([`micromark`](https://github.com/micromark/micromark), -see [`remarkjs/remark#536`](https://github.com/remarkjs/remark/pull/536)). -Use this plugin for remark 13+. +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`unified().use(remarkDirective)`](#unifieduseremarkdirective) +* [Examples](#examples) + * [Example: YouTube](#example-youtube) + * [Example: Styled blocks](#example-styled-blocks) +* [Syntax](#syntax) +* [Syntax tree](#syntax-tree) +* [Types](#types) +* [Compatibility](#compatibility) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package is a [unified][] ([remark][]) plugin to add support for directives: +one syntax for arbitrary extensions in markdown. +You can use this with some more code to match your specific needs, to allow for +anything from callouts, citations, styled blocks, forms, embeds, spoilers, etc. + +unified is an AST (abstract syntax tree) based transform project. +**remark** is everything unified that relates to markdown. +The layer under remark is called mdast, which is only concerned with syntax +trees. +Another layer underneath is micromark, which is only concerned with parsing. +This package is a small wrapper to integrate all of these. + +## When should I use this? + +Directives are one of the four ways to extend markdown: an arbitrary extension +syntax (see [Extending markdown](https://github.com/micromark/micromark#extending-markdown) +in micromark’s docs for the alternatives and more info). +This mechanism works well when you control the content: who authors it, what +tools handle it, and where it’s displayed. +When authors can read a guide on how to embed a tweet but are not expected to +know the ins and outs of HTML or JavaScript. +Directives don’t work well if you don’t know who authors content, what tools +handle it, and where it ends up. +Example use cases are a docs website for a project or product, or blogging tools +and static site generators. ## Install -This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c): -Node 12+ is needed to use it and it must be `import`ed instead of `require`d. - -[npm][]: +This package is [ESM only](https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c). +In Node.js (12.20+, 14.14+, 16.0+), install with [npm][]: ```sh npm install remark-directive ``` +In Deno with [Skypack][]: + +```js +import remarkDirective from 'https://cdn.skypack.dev/remark-directive@2?dts' +``` + +In browsers with [Skypack][]: + +```html + +``` + ## Use Say we have the following file, `example.md`: @@ -50,8 +103,7 @@ A :i[lovely] language know as :abbr[HTML]{title="HyperText Markup Language"}. And our module, `example.js`, looks as follows: ```js -import {readSync} from 'to-vfile' -import {reporter} from 'vfile-reporter' +import {read} from 'to-vfile' import {unified} from 'unified' import remarkParse from 'remark-parse' import remarkDirective from 'remark-directive' @@ -61,23 +113,26 @@ import rehypeStringify from 'rehype-stringify' import {visit} from 'unist-util-visit' import {h} from 'hastscript' -const file = readSync('example.md') +main() -unified() - .use(remarkParse) - .use(remarkDirective) - .use(customPlugin) - .use(remarkRehype) - .use(rehypeFormat) - .use(rehypeStringify) - .process(file) - .then((file) => { - console.error(reporter(file)) - console.log(String(file)) - }) +async function main() { + const file = await unified() + .use(remarkParse) + .use(remarkDirective) + .use(myRemarkPlugin) + .use(remarkRehype) + .use(rehypeFormat) + .use(rehypeStringify) + .process(await read('example.md')) -// This plugin is just an example! You can handle directives however you please! -function customPlugin() { + console.log(String(file)) +} + +// This plugin is an example to let users write HTML with directives. +// It’s informative but rather useless. +// See below for others examples. +/** @type {import('unified').Plugin<[], import('mdast').Root>} */ +function myRemarkPlugin() { return (tree) => { visit(tree, (node) => { if ( @@ -98,12 +153,7 @@ function customPlugin() { Now, running `node example` yields: -```txt -example.md: no issues found -``` - ```html -example.md: no issues found

Lorem
ipsum.


@@ -121,8 +171,163 @@ The default export is `remarkDirective`. Configures remark so that it can parse and serialize directives. Doesn’t handle the directives: [create your own plugin][create-plugin] to do that. -See the [micromark extension for the syntax][syntax] and the -[mdast utility for the syntax tree][syntax-tree]. + +## Examples + +### Example: YouTube + +This example shows how directives can be used for YouTube embeds. +It’s based on the example in Use above. +If `myRemarkPlugin` was replaced with this function: + +```js +// This plugin is an example to turn `::youtube` into iframes. +/** @type {import('unified').Plugin<[], import('mdast').Root>} */ +function myRemarkPlugin() { + return (tree, file) => { + visit(tree, (node) => { + if ( + node.type === 'textDirective' || + node.type === 'leafDirective' || + node.type === 'containerDirective' + ) { + if (node.name !== 'youtube') return + + const data = node.data || (node.data = {}) + const attributes = node.attributes || {} + const id = attributes.id + + if (node.type === 'textDirective') file.fail('Text directives for `youtube` not supported', node) + if (!id) file.fail('Missing video id', node) + + data.hName = 'iframe' + data.hProperties = { + src: 'https://www.youtube.com/embed/' + id, + width: 200, + height: 200, + frameBorder: 0, + allow: 'picture-in-picture', + allowFullScreen: true + } + } + }) + } +} +``` + +…and `example.md` contains: + +```markdown +# Cat videos + +::youtube[Video of a cat in a box]{#01ab2cd3efg} +``` + +…then running `node example` yields: + +```html +

Cat videos

+ +``` + +### Example: Styled blocks + +Note: This is sometimes called admonitions, callouts, etc. + +This example shows how directives can be used to style blocks. +It’s based on the example in Use above. +If `myRemarkPlugin` was replaced with this function: + +```js +// This plugin is an example to turn `::note` into divs, passing arbitrary +// attributes. +/** @type {import('unified').Plugin<[], import('mdast').Root>} */ +function myRemarkPlugin() { + return (tree) => { + visit(tree, (node) => { + if ( + node.type === 'textDirective' || + node.type === 'leafDirective' || + node.type === 'containerDirective' + ) { + if (node.name !== 'note') return + + const data = node.data || (node.data = {}) + const tagName = node.type === 'textDirective' ? 'span' : 'div' + + data.hName = tagName + data.hProperties = h(tagName, node.attributes).properties + } + }) + } +} +``` + +…and `example.md` contains: + +```markdown +# How to use xxx + +You can use xxx. + +:::note{.warning} +if you chose xxx, you should also use yyy somewhere… +::: +``` + +…then running `node example` yields: + +```html +

How to use xxx

+

You can use xxx.

+
+

if you chose xxx, you should also use yyy somewhere…

+
+``` + +## Syntax + +This plugin applies a micromark extensions to parse the syntax. +See its readme for parse details: + +* [`micromark-extension-directive`](https://github.com/micromark/micromark-extension-directive#syntax) + +## Syntax tree + +This plugin applies one mdast utility to build and serialize the AST. +See its readme for the node types supported in the tree: + +* [`mdast-util-directive`](https://github.com/syntax-tree/mdast-util-directive#syntax-tree) + +## Types + +This package is fully typed with [TypeScript][]. +If you’re working with the syntax tree, make sure to import this plugin +somewhere in your types, as that registers the new node types in the tree. + +```js +/** @typedef {import('remark-directive')} */ + +import {visit} from 'unist-util-visit' + +/** @type {import('unified').Plugin<[], import('mdast').Root>} */ +export default function myRemarkPlugin() => { + return (tree) => { + visit(tree, (node) => { + // `node` can now be one of the nodes for directives. + }) + } +} +``` + +## Compatibility + +Projects maintained by the unified collective are compatible with all maintained +versions of Node.js. +As of now, that is Node.js 12.20+, 14.14+, and 16.0+. +Our projects sometimes work with older versions, but this is not guaranteed. + +This plugin works with unified 9+ and remark 14+. ## Security @@ -133,13 +338,14 @@ scripting (XSS)][xss] attacks. ## Related * [`remark-gfm`](https://github.com/remarkjs/remark-gfm) - — GFM + — support GFM (autolink literals, strikethrough, tables, tasklists) * [`remark-github`](https://github.com/remarkjs/remark-github) - — Autolink references like in GitHub issues, PRs, and comments + — link references to commits, issues, pull-requests, and users, like on + GitHub * [`remark-frontmatter`](https://github.com/remarkjs/remark-frontmatter) - — Frontmatter (YAML, TOML, and more) + — support frontmatter (YAML, TOML, and more) * [`remark-math`](https://github.com/remarkjs/remark-math) - — Math + — support math ## Contribute @@ -185,6 +391,8 @@ abide by its terms. [npm]: https://docs.npmjs.com/cli/install +[skypack]: https://www.skypack.dev + [health]: https://github.com/remarkjs/.github [contributing]: https://github.com/remarkjs/.github/blob/HEAD/contributing.md @@ -197,10 +405,14 @@ abide by its terms. [author]: https://wooorm.com +[unified]: https://github.com/unifiedjs/unified + [remark]: https://github.com/remarkjs/remark [xss]: https://en.wikipedia.org/wiki/Cross-site_scripting +[typescript]: https://www.typescriptlang.org + [rehype]: https://github.com/rehypejs/rehype [hast]: https://github.com/syntax-tree/hast @@ -208,7 +420,3 @@ abide by its terms. [prop]: https://talk.commonmark.org/t/generic-directives-plugins-syntax/444 [create-plugin]: https://unifiedjs.com/learn/guide/create-a-plugin/ - -[syntax]: https://github.com/micromark/micromark-extension-directive#syntax - -[syntax-tree]: https://github.com/syntax-tree/mdast-util-directive#syntax-tree