Skip to content

Commit

Permalink
fix: support mermaid v9.1.6
Browse files Browse the repository at this point in the history
Simplifies the code by calling the new mermaid-cli Node.JS API
directly, which was introduced in Mermaid v9.1.6.

In the future, it should be possible to optimise the plugin
by reusing the same browser instance too.
  • Loading branch information
aloisklink committed Aug 23, 2022
1 parent 1b2bfc9 commit fc2f31e
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 320 deletions.
3 changes: 3 additions & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
module.exports = {
extends: ["plugin:prettier/recommended", "plugin:node/recommended"],
parserOptions: {
ecmaVersion: 2020,
},
};
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Fixed

- Fixed support for mermaid-cli `^9.1.6`

### Dependencies

- Updated `mermaid-cli` version to `^9.1.6`
- This increases the minimum version of `mermaid` to `^9.1.6`, see
https://github.com/mermaid-js/mermaid/releases/tag/9.1.6
- Removed unneeded dependency on `memfs`
- Added dependency on puppeteer `^16.0.0`
- puppeteer was previously a transitive dependency from `mermaid-cli`

## [2.0.1] - 2022-07-21

### Fixed
Expand Down
121 changes: 22 additions & 99 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,12 @@
const { Volume } = require("memfs");
const childProcess = require("child_process");
const path = require("path");
const { readFile } = require("fs/promises");
const puppeteer = require("puppeteer");

const visit = require("unist-util-visit");
const mmdc = require.resolve("@mermaid-js/mermaid-cli/index.bundle.js");

const { setSvgBbox, validSVG } = require("./src/svg.js");

const PLUGIN_NAME = "remark-mermaid-dataurl";

const createVolume = () => {
const volume = new Volume();
volume.mkdirSync(process.cwd(), { recursive: true });
return volume;
};

/**
* Calls mmdc (mermaid-cli) with the given keyword args.
*
Expand All @@ -29,104 +21,35 @@ const createVolume = () => {
* @throws {Error} If mmdc fails in anyways.
* @returns {Promise<string>} Returns the rendered mermaid code as an SVG.
*/
function renderMermaidFile(kwargs, input) {
const volume = createVolume();
const cwd = process.cwd();
const outputPath = path.join(cwd, "output.svg");
const inputPath = path.join(cwd, "input");
volume.writeFileSync(inputPath, input, "utf8");

if (kwargs.configFile && typeof kwargs.configFile === "object") {
const configFilePath = path.join(cwd, "config.json");
volume.writeFileSync(
configFilePath,
JSON.stringify(kwargs.configFile),
"utf8"
async function renderMermaidFile(kwargs, input) {
let configFile = kwargs.configFile ?? {};
if (kwargs.configFile && typeof kwargs.configFile !== "object") {
configFile = JSON.parse(
await readFile(kwargs.configFile, { encoding: "utf8" })
);
kwargs = { ...kwargs, configFile: configFilePath };
}
let puppeteerConfigFile = kwargs.puppeteerConfigFile ?? {};
if (
kwargs.puppeteerConfigFile &&
typeof kwargs.puppeteerConfigFile === "object"
typeof kwargs.puppeteerConfigFile !== "object"
) {
const puppeteerConfigFilePath = path.join(cwd, "puppeteerConfigFile.json");
volume.writeFileSync(
puppeteerConfigFilePath,
JSON.stringify(kwargs.puppeteerConfigFile),
"utf8"
puppeteerConfigFile = JSON.parse(
await readFile(kwargs.puppeteerConfigFile, { encoding: "utf8" })
);
kwargs = { ...kwargs, puppeteerConfigFile: puppeteerConfigFilePath };
}

// Array.flatMap() was only added in NodeJS v11
const args = [].concat(
...Object.entries({
...kwargs,
input: inputPath,
output: outputPath,
}).map(([key, value]) => [`--${key}`, value])
);
const child_process = childProcess.fork(
require.resolve("./mermaid_hook"),
args,
{
// store stderr for errors, and ipc for piping memfs info
stdio: ["ignore", "ignore", "pipe", "ipc"],
}
);

const stderrChunks = [];
child_process.send(volume.toJSON());

child_process.stderr.on("data", (chunk) => stderrChunks.push(chunk));

return new Promise((resolve, reject) => {
let exited = false; // stream may error AND exit
child_process.on("message", (message) => {
exited = true;
child_process.kill();
volume.fromJSON(message);
resolve(volume.promises.readFile(outputPath, { encoding: "utf8" }));
});
child_process.on("error", (error) => {
exited = true;
reject(error);
});
child_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} and stderr: ${Buffer.concat(
stderrChunks
).toString("utf-8")}`
)
);
} else if (signal) {
reject(
new Error(
`${mmdc} with kwargs ${JSON.stringify(
kwargs
)} recieved signal ${signal}`
)
);
} else {
// Mermaid-CLI throws unhandledRejection warnings
// which sometimes return exit code 0
reject(
new Error(
`${mmdc} with kwargs ${JSON.stringify(
kwargs
)} exited without returning created SVG.`
)
);
}
// eslint-disable-next-line node/no-unsupported-features/es-syntax, node/no-missing-import
const { parseMMD } = await import("@mermaid-js/mermaid-cli");
const browser = await puppeteer.launch(puppeteerConfigFile);
try {
const outputSvg = await parseMMD(browser, input, "svg", {
mermaidConfig: configFile,
viewport: { width: 800, height: 600 },
});
});
return outputSvg.toString("utf8");
} finally {
await browser.close();
}
}

/** Converts a string to a base64 string */
Expand Down
4 changes: 4 additions & 0 deletions jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,8 @@ module.exports = {
],
// work around for https://github.com/nodejs/node/issues/35889#issuecomment-1129293091
runner: "jest-light-runner",
// from https://github.com/chalk/chalk/issues/532
"moduleNameMapper": {
"#(.*)": "<rootDir>/node_modules/$1"
},
};
61 changes: 0 additions & 61 deletions mermaid_hook.js

This file was deleted.

Loading

0 comments on commit fc2f31e

Please sign in to comment.