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" | |
} | |
} |
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!
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!