import exec from "../util/exec"; import fs from "fs"; import { merge } from "merge-anything"; import { mkTempFile } from "../util/tempdir"; import parallel from "../util/parallel"; import path from "path"; import PluginError from "plugin-error"; import { promisify } from "util"; const writeFile = promisify(fs.writeFile); const exists = promisify(fs.exists); const PLUGIN_NAME = "pandoc"; const DEFAULTS = { slideLevel: 2, tocDepth: 2, revealJSURL: "reveal.js", template: path.normalize(__dirname + "/../../templates/revealjs.html"), variables: { width: 960, height: 700, navigationMode: "linear", theme: "inrae", slideNumber: true, history: true, fragmentInURL: true, }, }; /** * @param {string} url * @return {function} */ export function getRevealJSResolver(url) { if (url.match(/^(https?:)?\/\//i)) { return () => url; } return (output) => path.relative(path.dirname(output.relative), url); } /** * @param {import("vinyl")} input * @param {string} path * @return {Promise<void>|never} * @throws PluginError */ async function writeToDisk(input, path) { if (input.isStream()) { const write = fs.createWriteStream(path); const done = new Promise((resolve, reject) => { write.on("close", () => resolve()); input.on("error", reject); write.on("error", reject); }); input.contents.pipe(write); return done; } if (input.isBuffer()) { return writeFile(path, input.contents, {}); } throw new PluginError(PLUGIN_NAME, `cannot handle input ${input.inspect()}`); } /** * @param {import("vinyl")} input * @return {import("vinyl")} * @throws PluginError */ async function onDisk(input) { if (await exists(input.path)) { return input; } const tmpFile = await mkTempFile(input); await writeToDisk(input, tmpFile.path); return tmpFile; } export default function pandoc(options = {}) { const { slideLevel, tocDepth, revealJSURL, template, variables } = merge( {}, DEFAULTS, options ); const revealJSURLResolver = getRevealJSResolver(revealJSURL); return parallel( /** * @param {import("vinyl")} input */ async function (input) { const output = input.clone({ contents: false }); output.extname = ".html"; output.contents = null; const src = await onDisk(input); const args = [ "--from=markdown+backtick_code_blocks+pandoc_title_block+yaml_metadata_block", "--to=revealjs", "--standalone", `--template=${template}`, `--slide-level=${slideLevel}`, "--toc", `--toc-depth=${tocDepth}`, `--variable=revealjs-url:${revealJSURLResolver(output)}`, ...Object.getOwnPropertyNames(variables).map( (name) => `--variable=${name}:${variables[name]}` ), src.path, ]; const { stdout } = await exec(PLUGIN_NAME, "pandoc", args, { encoding: "buffer", cwd: input.cwd, }); output.contents = stdout; this.push(output); } ); }