@telefonica/markdown-confluence-sync
Version:
Creates/updates/deletes Confluence pages based on markdown files in a directory. Supports Mermaid diagrams and per-page configuration using frontmatter metadata. Works great with Docusaurus
81 lines (80 loc) • 3.08 kB
JavaScript
// SPDX-FileCopyrightText: 2024 Telefónica Innovación Digital
// SPDX-License-Identifier: Apache-2.0
import { spawnSync } from "node:child_process";
import { randomBytes } from "node:crypto";
import { rmSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
import { ensureDirSync } from "fs-extra";
import which from "which";
import { replace } from "../../../../support/unist/unist-util-replace.js";
import { DEPENDENCIES_BIN_PATH, PACKAGE_ROOT } from "../../../../util/paths.js";
const AUTOGENERATED_FILE_PREFIX = "autogenerated-";
/**
* UnifiedPlugin to remove footnotes from the AST.
*/
const remarkReplaceMermaid = function remarkReplaceMermaid(options) {
return function (tree, file) {
const outDir = options.outDir;
replaceMermaidCodeBlocks(tree, file, outDir);
};
};
/**
* FIXME: This function was throwing the error TS2589: Type instantiation is excessively deep and possibly infinite, but it's not throwing anymore.
* Investigate why it was throwing and why it's not throwing anymore.
*/
function replaceMermaidCodeBlocks(tree, file, outDir) {
replace(tree, isMermaidCode, function (node) {
try {
const url = render(outDir, node.value);
return {
type: "image",
url,
};
}
catch (e) {
// FIXME: replace with file.fail(e as Error, node, "remark-replace-mermaid");
// It changes due to lack of time to debug possible issues with mermaid cli.
file.message(e, node, "remark-replace-mermaid");
return node;
}
});
}
function isMermaidCode(node) {
return node.type === "code" && node.lang === "mermaid";
}
function render(dir, code) {
const tempFileName = randomBytes(4).toString("hex");
const mmdTempFileName = `${tempFileName}.mmd`;
const mmdcTempFile = join(dir, mmdTempFileName);
ensureDirSync(dir);
try {
// HACK: which is a Common JS module, so we to use default import here.
const mmdcExec = which.sync("mmdc", {
path: DEPENDENCIES_BIN_PATH,
});
const svgTempFilename = `${AUTOGENERATED_FILE_PREFIX}${tempFileName}.svg`;
const svgTempFile = join(dir, svgTempFilename);
writeFileSync(mmdcTempFile, code, { flag: "w+" });
const puppeteerConfigFile = resolve(join(PACKAGE_ROOT, "config"), "puppeteer-config.json");
const child = spawnSync(mmdcExec, [
"--input",
mmdcTempFile,
"--output",
svgTempFile,
"--backgroundColor",
"transparent",
"--puppeteerConfigFile",
puppeteerConfigFile,
], { stdio: [0, "ignore", "pipe"] });
if (child.status !== 0) {
throw new Error(`mmdc failed with exit code ${child.status}:\n${child.output}`, {
cause: child.error,
});
}
return svgTempFile;
}
finally {
rmSync(mmdcTempFile, { force: true });
}
}
export default remarkReplaceMermaid;