UNPKG

@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
// 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;