This is an abbreviation: HTML.
*[HTML]: HyperText Markup Language
| import fs from "node:fs/promises"; | |
| import { unified } from "unified"; | |
| import remarkParse from "remark-parse"; | |
| import remarkRehype from "remark-rehype"; | |
| import rehypeStringify from "rehype-stringify"; | |
| import myCustomRemarkAbbrPlugin from "./index.js"; | |
| const file = await unified() | |
| .use(remarkParse) | |
| .use(myCustomRemarkAbbrPlugin) | |
| .use(remarkRehype) | |
| .use(rehypeStringify) | |
| .process(await fs.readFile("example.md")); | |
| console.log(file); |
This is an abbreviation: HTML.
*[HTML]: HyperText Markup Language
| import { visit } from "unist-util-visit"; | |
| import { findAndReplace } from "mdast-util-find-and-replace"; | |
| import escapeStringRegexp from "escape-string-regexp"; | |
| // This plugin does two things: | |
| // a) First, it tells micromark and friends how to parse abbreviation definitions. | |
| // b) Much later, it replaces abbreviations use, in the AST, with custom nodes. | |
| /** @type {import('unified').Plugin<[], import('mdast').Root>} */ | |
| export default function myCustomRemarkAbbr() { | |
| const data = this.data(); | |
| // Register things that deal with parsing. | |
| // Micromark extensions define how to parse syntax. | |
| // This is a micromark extension. | |
| // It would look like <https://github.com/micromark/micromark/blob/main/packages/micromark-core-commonmark/dev/lib/definition.js>. | |
| add("micromarkExtensions", myCustomMicromarkAbbrExtension); | |
| // This is a `mdast-util-from-markdown` extension. | |
| // `mdast-util-from-markdown` extensions define how to handle the resulting tokens/events from micromark extensions and build nodes from them. | |
| // It would look like <https://github.com/syntax-tree/mdast-util-math/blob/4670ae469c47b53d9661e7a116a869493f4b1800/index.js#L24>. | |
| // But instead of math, it would handle the tokens that are added by your custom micromark extension. | |
| // The rest of the codes expects the resulting nodes from your extension to look as follows: | |
| // `{type: 'my-abbr-definition-node-type', id: string, title: string}`. | |
| add("fromMarkdownExtensions", myCustomMdastUtilFromMarkdownAbbrExtension); | |
| // To do: if you need to compile back to markdown again, you need to define how to turn nodes back into a string of markdown. | |
| // If you need this, it would look like <https://github.com/syntax-tree/mdast-util-math/blob/4670ae469c47b53d9661e7a116a869493f4b1800/index.js#L125>. | |
| // But instead of math, it would handle the nodes that are created by your `mdast-util-from-markdown` extension. | |
| // add("toMarkdownExtensions", myCustomMicromarkAbbrToMarkdownExtension); | |
| // This a) finds all defined abbr definitions, b) replaces all abbr uses with what is defined. | |
| return function transform(tree) { | |
| /** @type {Record<string, string>} */ | |
| const definitionToTitle = Object.create(null); | |
| visit(tree, "my-abbr-definition-node-type", (node) => { | |
| // To do: prefer the first of duplicates? | |
| definitionToTitle[node.id] = node.title; | |
| }); | |
| // Create a regex to look for ID use. | |
| let search = new RegExp( | |
| Object.keys(definitionToTitle).map(escapeStringRegexp).join("|"), | |
| "g" | |
| ); | |
| // Replace ID use with custom abbr use nodes. | |
| findAndReplace(tree, search, (/** @type {string} */ $0) => { | |
| let title = definitionToTitle; | |
| return { | |
| type: "my-abbr-use-node-type", | |
| value: $0, | |
| // We are in markdown, but we want to define how our custom nodes get turned into HTML. | |
| // For more on this, see: <https://github.com/remarkjs/remark-rehype#what-is-this>. | |
| // For more on these fields, see: <https://github.com/syntax-tree/mdast-util-to-hast#fields-on-nodes>. | |
| data: { | |
| hTagName: "abbr", | |
| hProperties: { | |
| title, | |
| }, | |
| }, | |
| }; | |
| }); | |
| }; | |
| /** | |
| * @param {string} field | |
| * @param {unknown} value | |
| */ | |
| function add(field, value) { | |
| const list = /** @type {unknown[]} */ ( | |
| // Other extensions | |
| data[field] ? data[field] : (data[field] = []) | |
| ); | |
| list.push(value); | |
| } | |
| } |
| { | |
| "name": "example", | |
| "version": "1.0.0", | |
| "description": "", | |
| "main": "index.js", | |
| "scripts": { | |
| "test": "echo \"Error: no test specified\" && exit 1" | |
| }, | |
| "keywords": [], | |
| "author": "", | |
| "license": "ISC", | |
| "devDependencies": { | |
| "rehype-stringify": "^9.0.3", | |
| "remark-parse": "^10.0.1", | |
| "remark-rehype": "^10.1.0", | |
| "unified": "^10.1.2" | |
| }, | |
| "dependencies": { | |
| "@types/mdast": "^3.0.10", | |
| "escape-string-regexp": "^5.0.0", | |
| "mdast-util-find-and-replace": "^2.2.1", | |
| "unist-util-visit": "^4.1.0" | |
| } | |
| } |
You are commenting on a pseudocode example that explains in the comments how to do that?
Re extending markdown? You most likely shouldn't: https://github.com/micromark/micromark#extending-markdown
I’m not sure how you found this gist, it was I think privately helping someone with a ton of legacy markdown.
I recommend going with directives if you can!
Thank you very much for quick reply. I will check it out.
Also found it on Google 😄
In case it's helpful to anyone, here are a few tips if you want to implement this thanks to remark-directive:
facebook/docusaurus#10242 (reply in thread)
Oh no! 😅
Anyway, good answer, slorber!
Hi, @wooorm,
How can I find the
myCustomMicromarkAbbrExtensionandmyCustomMdastUtilFromMarkdownAbbrExtensionI couldn't find any remark abbreviation plugin which works, so far. I need help.