Skip to content

Commit

Permalink
Custom Unified.js Plugins (Admonitions + Type Links) (#154)
Browse files Browse the repository at this point in the history
* Replace `remark-admonitions` with a custom `remark-rehype` handler to render directives from `remark-directive` with our own Docusaurus V3 Admonition component

* Added backwards compatibility for Docusaurus V2 Admonition syntax

* Fixed support for type links by adding a new Remark plugin to add the syntax back

* Added back support for GFM which was no longer included by default in Remark
  • Loading branch information
YetAnotherClown authored Oct 28, 2024
1 parent ed901c7 commit 93fe073
Show file tree
Hide file tree
Showing 7 changed files with 1,714 additions and 1,502 deletions.
2 changes: 1 addition & 1 deletion cli/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2,860 changes: 1,411 additions & 1,449 deletions docusaurus-plugin-moonwave/package-lock.json

Large diffs are not rendered by default.

8 changes: 6 additions & 2 deletions docusaurus-plugin-moonwave/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,12 @@
"rehype-sanitize": "^6.0.0",
"rehype-stringify": "^10.0.0",
"remark-parse": "^11.0.0",
"remark-react": "^9.0.0",
"remark-rehype": "^11.1.0",
"unified": "^11.0.4"
"remark-gfm": "^4.0.0",
"unified": "^11.0.4",
"unist-util-visit": "^5.0.0",
"remark-directive": "^3.0.0",
"hast-util-from-html": "^2.0.3",
"micromark-util-symbol": "^2.0.0"
}
}
157 changes: 108 additions & 49 deletions docusaurus-plugin-moonwave/src/components/Admonition.js
Original file line number Diff line number Diff line change
@@ -1,66 +1,125 @@
import React from "react"
const ICONS = {
note: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="16"
viewBox="0 0 14 16"
>
<path
fillRule="evenodd"
d="M6.3 5.69a.942.942 0 0 1-.28-.7c0-.28.09-.52.28-.7.19-.18.42-.28.7-.28.28 0 .52.09.7.28.18.19.28.42.28.7 0 .28-.09.52-.28.7a1 1 0 0 1-.7.3c-.28 0-.52-.11-.7-.3zM8 7.99c-.02-.25-.11-.48-.31-.69-.2-.19-.42-.3-.69-.31H6c-.27.02-.48.13-.69.31-.2.2-.3.44-.31.69h1v3c.02.27.11.5.31.69.2.2.42.31.69.31h1c.27 0 .48-.11.69-.31.2-.19.3-.42.31-.69H8V7.98v.01zM7 2.3c-3.14 0-5.7 2.54-5.7 5.68 0 3.14 2.56 5.7 5.7 5.7s5.7-2.55 5.7-5.7c0-3.15-2.56-5.69-5.7-5.69v.01zM7 .98c3.86 0 7 3.14 7 7s-3.14 7-7 7-7-3.12-7-7 3.14-7 7-7z"
></path>
</svg>
),
info: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="14"
height="16"
viewBox="0 0 14 16"
>
<path
fillRule="evenodd"
d="M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"
></path>
</svg>
),
success: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="16"
viewBox="0 0 12 16"
>
<path
fillRule="evenodd"
d="M6.5 0C3.48 0 1 2.19 1 5c0 .92.55 2.25 1 3 1.34 2.25 1.78 2.78 2 4v1h5v-1c.22-1.22.66-1.75 2-4 .45-.75 1-2.08 1-3 0-2.81-2.48-5-5.5-5zm3.64 7.48c-.25.44-.47.8-.67 1.11-.86 1.41-1.25 2.06-1.45 3.23-.02.05-.02.11-.02.17H5c0-.06 0-.13-.02-.17-.2-1.17-.59-1.83-1.45-3.23-.2-.31-.42-.67-.67-1.11C2.44 6.78 2 5.65 2 5c0-2.2 2.02-4 4.5-4 1.22 0 2.36.42 3.22 1.19C10.55 2.94 11 3.94 11 5c0 .66-.44 1.78-.86 2.48zM4 14h5c-.23 1.14-1.3 2-2.5 2s-2.27-.86-2.5-2z"
></path>
</svg>
),
warning: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
>
<path
fillRule="evenodd"
d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"
></path>
</svg>
),
danger: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="16"
viewBox="0 0 12 16"
>
<path
fillRule="evenodd"
d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"
></path>
</svg>
),
}

const VARIATIONS = {
danger: {
className: "admonition-danger alert--danger",
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="12"
height="16"
viewBox="0 0 12 16"
>
<path
fillRule="evenodd"
d="M5.05.31c.81 2.17.41 3.38-.52 4.31C3.55 5.67 1.98 6.45.9 7.98c-1.45 2.05-1.7 6.53 3.53 7.7-2.2-1.16-2.67-4.52-.3-6.61-.61 2.03.53 3.33 1.94 2.86 1.39-.47 2.3.53 2.27 1.67-.02.78-.31 1.44-1.13 1.81 3.42-.59 4.78-3.42 4.78-5.56 0-2.84-2.53-3.22-1.25-5.61-1.52.13-2.03 1.13-1.89 2.75.09 1.08-1.02 1.8-1.86 1.33-.67-.41-.66-1.19-.06-1.78C8.18 5.31 8.68 2.45 5.05.32L5.03.3l.02.01z"
></path>
</svg>
),
note: {
className: "admonition admonition-note alert alert--secondary",
icon: ICONS.note,
},
info: {
className: "admonition admonition-info alert alert--info",
icon: ICONS.info,
},
tip: {
className: "admonition admonition-success alert alert--success",
icon: ICONS.success,
},
warning: {
className: "admonition admonition-warning alert alert--warning",
icon: ICONS.warning,
},
caution: {
className: "admonition-caution alert--warning",
icon: (
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
>
<path
fillRule="evenodd"
d="M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"
></path>
</svg>
),
className: "admonition admonition-caution alert alert--warning",
icon: ICONS.warning,
},
danger: {
className: "admonition admonition-danger alert alert--danger",
icon: ICONS.danger,
},
}

export default function Admonition({ variation, title, children }) {
if (!VARIATIONS[variation]) {
return (
<p
style={{
background: "red",
color: "white",
fontStyle: "italic",
}}
>
This Admonition variation is not supported
</p>
<div class="alert alert--danger" role="alert">
<strong>
This Admonition variation `{variation}` is not supported. Try `note`,
`info`, `tip`, `warning`, or `danger`.
</strong>
</div>
)
}

const { className, icon } = VARIATIONS[variation]

return (
<div className={`admonition alert ${VARIATIONS[variation].className}`}>
<div className="admonition-heading">
<h5>
<span className="admonition-icon">{VARIATIONS[variation].icon}</span>
{title}
</h5>
</div>
<div className="admonition-content">
<p>{children}</p>
<>
<div className={className}>
<div className="admonition-heading">
<h5>
<span className="admonition-icon">{icon}</span>
<span className="admonition-title">
{title || variation.toUpperCase()}
</span>
</h5>
</div>
<div className="admonition-content">{children}</div>
</div>
</div>
</>
)
}
25 changes: 24 additions & 1 deletion docusaurus-plugin-moonwave/src/components/Markdown.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ import React, { useContext } from "react"
import format from "rehype-format"
import sanitize from "rehype-sanitize"
import html from "rehype-stringify"
import directives from "remark-directive"
import remarkGfm from "remark-gfm"
import remarkRehypeAdmonitions from "../remark/remarkRehypeAdmonitions"
import remarkExtendedLinkReferences from "../remark/remarkExtendedLinkReferences"
import parse from "remark-parse"
import remark2rehype from "remark-rehype"
import { unified } from "unified"
Expand Down Expand Up @@ -68,14 +72,33 @@ const autoLinkReferences = (typeLinks, baseUrl) => (node) => {
node.children = node.children.map(replaceLinkRefs)
}

// Backwards compatibility for Docusaurus V2 Admonitions
function convertAdmonitions(content) {
const blocksToConvert =
/:::(\w+)(?:[ \t]+([^\[\]{}\n]+))?\n((?:[ \t]*\n?(?:(?!:::).)*\n?)+):::/gm

return content.replace(blocksToConvert, (_, name, label, innerContent) => {
label = label ? `[${label}]` : ""

return `:::${name}${label}\n${innerContent}\n:::`
})
}

export default function Markdown({ content, inline }) {
const { siteConfig } = useDocusaurusContext()
const typeLinks = useContext(TypeLinksContext)

content = convertAdmonitions(content)

const markdownHtml = unified()
.use(parse)
.use(remarkExtendedLinkReferences)
.use(remarkGfm)
.use(directives)
.use(() => autoLinkReferences(typeLinks, siteConfig.baseUrl))
.use(remark2rehype)
.use(remark2rehype, {
handlers: { ...remarkRehypeAdmonitions },
})
.use(() => linkTransformer(siteConfig.baseUrl))
.use(rehypePrism)
.use(format)
Expand Down
111 changes: 111 additions & 0 deletions docusaurus-plugin-moonwave/src/remark/remarkExtendedLinkReferences.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { codes } from "micromark-util-symbol"

function fromSyntax() {
function afterTokenize(_, ok, notOk) {
return (code) => {
if (
code === codes.colon ||
code === codes.leftParenthesis ||
code === codes.leftCurlyBrace ||
code === codes.leftSquareBracket
) {
return notOk(code)
}

return ok(code)
}
}

function tokenize(effects, ok, notOk) {
function start(code) {
effects.enter("extendedLinkReference")
effects.enter("extendedLinkReferenceMarker")
effects.consume(code)
effects.exit("extendedLinkReferenceMarker")
effects.enter("extendedLinkReferenceLabel")
return begin
}

function begin(code) {
return code === codes.rightSquareBracket ? notOk(code) : inside(code)
}

function inside(code) {
if (code === -5 || code === -4 || code === -3 || code === null) {
return notOk(code)
}

if (code == codes.rightSquareBracket) {
effects.exit("extendedLinkReferenceLabel")
effects.enter("extendedLinkReferenceMarker")
effects.consume(code)
effects.exit("extendedLinkReferenceMarker")
effects.exit("extendedLinkReference")

return effects.check({ tokenize: afterTokenize }, ok, notOk)
}

effects.consume(code)

return inside
}

return start
}

return {
text: {
[codes.leftSquareBracket]: {
name: "extendedLinkReference",
tokenize: tokenize,
},
},
}
}

function fromMarkdown() {
function enter(token) {
this.enter(
{
type: "linkReference",
referenceType: "shortcut",
},
token
)
}

function exit(token) {
this.exit(token)
}

function exitLabel(token) {
const label = this.sliceSerialize(token)
const node = this.stack[this.stack.length - 1]
node.identifier = label
node.label = label
node.children = [{ type: "text", value: label }]
}

return {
enter: {
extendedLinkReference: enter,
},
exit: {
extendedLinkReference: exit,
extendedLinkReferenceLabel: exitLabel,
},
}
}

export default function remarkExtendedLinkReferences() {
const data = this.data()

const micromarkExtensions =
data.micromarkExtensions || (data.micromarkExtensions = [])

const fromMarkdownExtensions =
data.fromMarkdownExtensions || (data.fromMarkdownExtensions = [])

micromarkExtensions.push(fromSyntax())
fromMarkdownExtensions.push(fromMarkdown())
}
Loading

0 comments on commit 93fe073

Please sign in to comment.