-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit for remark-mermaid-dataurl plugin
on-behalf-of: @nqminds <[email protected]>
- Loading branch information
1 parent
022215c
commit 6dd7107
Showing
10 changed files
with
6,812 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
module.exports = { | ||
extends: ["plugin:prettier/recommended", "plugin:node/recommended"], | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
name: Publish NPM Package | ||
|
||
on: | ||
release: | ||
types: [published] | ||
|
||
jobs: | ||
publish: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v2 | ||
# with: | ||
# submodules: true | ||
- name: Setup Node | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: "12.x" | ||
registry-url: "https://registry.npmjs.org" | ||
- name: Install NPM Packages | ||
run: npm ci | ||
- name: Publish Package | ||
run: npm publish | ||
env: | ||
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
name: Test | ||
|
||
on: | ||
push: {} | ||
pull_request: {} | ||
|
||
jobs: | ||
deploy: | ||
runs-on: ubuntu-latest | ||
strategy: | ||
matrix: | ||
node-version: [10.x, 12.x, 14.x] | ||
steps: | ||
- uses: actions/checkout@v2 | ||
# with: | ||
# submodules: true | ||
- name: Cache node modules | ||
uses: actions/cache@v1 | ||
env: | ||
cache-name: cache-node-modules | ||
with: | ||
path: ~/.npm # npm cache files are stored in `~/.npm` on Linux/macOS | ||
key: ${{ runner.os }}-${{ env.cache-name }}-${{ matrix.node-version }}-${{ hashFiles('**/package-lock.json') }} | ||
restore-keys: | | ||
${{ runner.os }}-${{ env.cache-name }}-${{ matrix.node-version }}- | ||
${{ runner.os }}-${{ env.cache-name }}- | ||
${{ runner.os }}- | ||
- name: Setup Node ${{ matrix.node-version }} | ||
uses: actions/setup-node@v1 | ||
with: | ||
node-version: ${{ matrix.node-version }} | ||
- name: Install NPM Packages | ||
run: npm ci | ||
- name: Lint | ||
run: npm run lint | ||
- name: Build | ||
run: npm test |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,34 @@ | ||
# remark-mermaid-dataurl | ||
|
||
A remark plugin for Markdown that replaces mermaid graphs with dataurls | ||
|
||
Designed for use with Docusaurus v2. | ||
|
||
## Usage with Docusaurus | ||
|
||
_see https://v2.docusaurus.io/docs/markdown-features#configuring-plugins for more info_ | ||
|
||
First, install this plugin: | ||
|
||
```bash | ||
npm install --save remark-mermaid-dataurl | ||
``` | ||
|
||
Then, add them to your `@docusaurus/preset-classic` options in `docusaurus.config.js`: | ||
|
||
```js | ||
module.exports = { | ||
// ... | ||
presets: [ | ||
[ | ||
"@docusaurus/preset-classic", | ||
{ | ||
docs: { | ||
// ... | ||
remarkPlugins: [require("remark-mermaid-dataurl")], | ||
}, | ||
}, | ||
], | ||
], | ||
}; | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,156 @@ | ||
const fs = require("fs"); | ||
const childProcess = require("child_process"); | ||
const os = require("os"); | ||
const path = require("path"); | ||
|
||
const visit = require("unist-util-visit"); | ||
const mmdc = require.resolve("@mermaid-js/mermaid-cli/index.bundle.js"); | ||
|
||
const PLUGIN_NAME = "remark-mermaid-dataurl"; | ||
|
||
/** | ||
* Deletes a folder, (essentially does `rmdir(tmpdir, {recursive: true})`) | ||
* | ||
* We can't use rmdir(p, {recursive: true}) since it isn't fully supported | ||
* in Node.Js yet. | ||
* @param {string} tmpdir The directory to delete | ||
*/ | ||
async function cleanup(tmpdir) { | ||
const files = await fs.promises.readdir(tmpdir); | ||
await Promise.all( | ||
files.map((file) => { | ||
return fs.promises.unlink(path.join(tmpdir, file)); | ||
}) | ||
); | ||
await fs.promises.rmdir(tmpdir); | ||
} | ||
|
||
/** | ||
* Calls mmdc (mermaid-cli) with the given keyword args | ||
* @param {{[key: string]: any}} kwargs | ||
* Args passed to mmdc in format `--key value` | ||
* @returns {Promise<void, Error>} | ||
*/ | ||
async function renderMermaidFile(kwargs) { | ||
const argPairs = Object.keys(kwargs).map((key) => [`--${key}`, kwargs[key]]); | ||
const args = [].concat(...argPairs); // flatten | ||
const process = childProcess.fork(mmdc, args, { silent: true }); | ||
|
||
return new Promise((resolve, reject) => { | ||
let exited = false; // stream may error AND exit | ||
process.on("error", (error) => { | ||
exited = true; | ||
reject(error); | ||
}); | ||
process.on("exit", (code, signal) => { | ||
if (exited) { | ||
return; // already resolved Promise | ||
} | ||
if (code) { | ||
reject( | ||
new Error( | ||
`${mmdc} with kwargs ${JSON.stringify( | ||
kwargs | ||
)} failed with error code: ${code}` | ||
) | ||
); | ||
} | ||
if (signal) { | ||
reject( | ||
new Error( | ||
`${mmdc} with kwargs ${JSON.stringify( | ||
kwargs | ||
)} recieved signal ${signal}` | ||
) | ||
); | ||
} | ||
resolve(); | ||
}); | ||
}); | ||
} | ||
|
||
/** | ||
* Creates an SVG from the given mermaid input text. | ||
* @param {string} inputText The mermaid text to render. | ||
* @param {Object} mermaidCliOptions Options to pass to mermaid-cli | ||
* @returns {Promise<string>} The contents of the rendered SVG file. | ||
*/ | ||
async function renderMermaidText(inputText, mermaidCliOptions) { | ||
const tmpdir = await fs.promises.mkdtemp( | ||
path.join(os.tmpdir(), "remark-mermaid-") | ||
); | ||
const inputPath = path.join(tmpdir, "input"); | ||
const outputPath = path.join(tmpdir, "output.svg"); | ||
try { | ||
await fs.promises.writeFile(inputPath, inputText, { encoding: "utf8" }); | ||
await renderMermaidFile({ | ||
...mermaidCliOptions, | ||
input: inputPath, | ||
output: outputPath, | ||
}); | ||
return await fs.promises.readFile(outputPath, { encoding: "utf8" }); | ||
} finally { | ||
await cleanup(tmpdir); | ||
} | ||
} | ||
|
||
/** Converts a string to a base64 string */ | ||
function btoa(string) { | ||
return Buffer.from(string).toString("base64"); | ||
} | ||
|
||
function dataUrl(data, mimeType, base64 = false) { | ||
if (base64) { | ||
return `data:${mimeType};base64,${btoa(data)}`; | ||
} else { | ||
return `data:${mimeType},${encodeURIComponent(data)}`; | ||
} | ||
} | ||
|
||
async function transformMermaidNode(node, file, index, parent, { mermaidCli }) { | ||
const { lang, value, position } = node; | ||
try { | ||
const data = await renderMermaidText(value, mermaidCli); | ||
const newNode = { | ||
type: "image", | ||
title: "Diagram generated via mermaid", | ||
url: dataUrl(data, "image/svg+xml;charset=UTF-8"), | ||
}; | ||
|
||
file.info(`${lang} code block replaced with graph`, position, PLUGIN_NAME); | ||
// replace old node with current node | ||
parent.children[index] = newNode; | ||
} catch (error) { | ||
file.fail(error, position, PLUGIN_NAME); | ||
} | ||
} | ||
|
||
/** | ||
* Remark plugin that converts mermaid codeblocks into self-contained SVG dataurls. | ||
* @param {Object} options | ||
* @param {Obejct} options.mermaidCli Options to pass to mermaid-cli | ||
*/ | ||
function remarkMermaid({ mermaidCli = {} } = {}) { | ||
const options = { mermaidCli }; | ||
/** | ||
* Look for all code nodes that have the language mermaid, | ||
* and replace them with images with data urls. | ||
* | ||
* @param {Node} tree The Markdown Tree | ||
* @param {VFile} file The virtual file. | ||
* @returns {Promise<void>} | ||
*/ | ||
return async function (tree, file) { | ||
const promises = []; // keep track of promises since visit isn't async | ||
visit(tree, "code", (node, index, parent) => { | ||
// If this codeblock is not mermaid, bail. | ||
if (node.lang !== "mermaid") { | ||
return node; | ||
} | ||
promises.push(transformMermaidNode(node, file, index, parent, options)); | ||
}); | ||
await Promise.all(promises); | ||
}; | ||
} | ||
|
||
module.exports = remarkMermaid; |
Oops, something went wrong.