From a6bf77494f1a787894b5511fae40fed32441e18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Guillaume=20Perr=C3=A9al?= <guillaume.perreal@inrae.fr> Date: Mon, 4 May 2020 09:27:24 +0200 Subject: [PATCH] Rend optionels drawio et wkhtmltopdf. --- gulpfile.esm.js | 4 +-- lib/drawio.js | 86 +++++++++++++++++++++++++--------------------- lib/index.js | 17 +++------ lib/optional.js | 29 ++++++++++++++++ lib/wkhtmltopdf.js | 80 +++++++++++++++++++++++++----------------- package-lock.json | 27 +++++++++++++-- package.json | 3 +- 7 files changed, 156 insertions(+), 90 deletions(-) create mode 100644 lib/optional.js diff --git a/gulpfile.esm.js b/gulpfile.esm.js index c03d605..c2f73ef 100644 --- a/gulpfile.esm.js +++ b/gulpfile.esm.js @@ -17,12 +17,12 @@ const PREZ_GLOB = `${SRC_DIR}/**/index.md`; const GRAPH_GLOB = `${SRC_DIR}/**/*.drawio`; const PDF_GLOB = `${DEST_DIR}/**/index.html`; -export const clean = () => del(`${DEST_DIR}/**`); +export const clean = () => del(`${DEST_DIR}/**`, { force: true }); export const assets = () => src(ASSET_GLOB).pipe(dest(DEST_DIR)); -export const graphs = () => src(GRAPH_GLOB).pipe(drawio()).pipe(dest(DEST_DIR)); export const prez = () => src(PREZ_GLOB).pipe(index()).pipe(pandoc()).pipe(dest(DEST_DIR)); +export const graphs = () => src(GRAPH_GLOB).pipe(drawio()).pipe(dest(DEST_DIR)); export const pdf = () => src(PDF_GLOB).pipe(wkhtmltopdf()).pipe(dest(DEST_DIR)); export const build = series(clean, parallel(assets, prez, graphs), pdf); diff --git a/lib/drawio.js b/lib/drawio.js index 5851a28..873c43f 100644 --- a/lib/drawio.js +++ b/lib/drawio.js @@ -5,47 +5,55 @@ import logger from "gulplog"; import { mkTempFile } from "./tempdir"; import { obj } from "through2"; import PluginError from "plugin-error"; +import { withBinary } from "./optional"; const PLUGIN_NAME = "drawio"; -const DRAWIO_BINARY = process.env.DRAWIO_BINARY || "drawio"; - const execFile = promisify(child_process.execFile); -export default function drawio() { - return obj( - callbackify(async function (input) { - try { - const output = await mkTempFile(input); - output.extname = ".svg"; - - const args = [ - "--export", - "--format", - "svg", - "--width", - "1024", - "--output", - output.path, - input.path, - ]; - - const { stdout, stderr } = await execFile(DRAWIO_BINARY, args); - logger.info("%s: generated %s", PLUGIN_NAME, output.relative); - if (stdout.length > 0) { - logger.debug(PLUGIN_NAME, "stdout:", stdout); - } - if (stderr.length > 0) { - logger.info(PLUGIN_NAME, "stderr:", stderr); - } - - output.contents = fs.createReadStream(output.path, { - encoding: "UTF-8", - }); - this.push(output); - } catch (error) { - throw new PluginError(PLUGIN_NAME, error); - } - }) - ); -} +const drawio = withBinary( + "DRAWIO_BINARY", + "drawio", + (DRAWIO_BINARY) => + function drawio() { + return obj( + callbackify(async function (input) { + try { + const output = await mkTempFile(input); + output.extname = ".svg"; + + const args = [ + "--export", + "--format", + "svg", + "--width", + "1024", + "--output", + output.path, + input.path, + ]; + + const { stdout, stderr } = await execFile(DRAWIO_BINARY, args); + if (stdout.length > 0) { + logger.info(PLUGIN_NAME, "stdout:", stdout); + } + if (stderr.length > 0) { + logger.info(PLUGIN_NAME, "command:", DRAWIO_BINARY, args); + logger.info(PLUGIN_NAME, "stderr:", stderr); + } + + output.contents = fs.createReadStream(output.path, { + encoding: "UTF-8", + }); + logger.info("%s: generated %s", PLUGIN_NAME, output.relative); + + this.push(output); + } catch (error) { + throw new PluginError(PLUGIN_NAME, error); + } + }) + ); + } +); + +export default drawio; diff --git a/lib/index.js b/lib/index.js index 3447e07..23ac171 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1,5 +1,6 @@ import { callbackify } from "util"; import File from "vinyl"; +import { getPDFOutput } from "./wkhtmltopdf"; import logger from "gulplog"; import { obj } from "through2"; import PluginError from "plugin-error"; @@ -75,18 +76,6 @@ function getPrezFilename(input) { return prez.relative; } -/** - * @param {File} input - * @return {string} - */ -function getPdfFilename(input) { - const parent = new File({ path: input.dirname }); - const pdf = input.clone({ deep: false, contents: false }); - pdf.stem = parent.stem; - pdf.extname = ".pdf"; - return pdf.relative; -} - function renderIndex(inputs) { return `% Présentations % Dev@Science @@ -108,7 +97,9 @@ ${inputs .map( (input) => `- [${input.titleBlock.title}](${getPrezFilename(input)})` + - `[<img class="plain icon" src="file_pdf.png"/>](${getPdfFilename(input)})` + `[<img class="plain icon" src="file_pdf.png"/>](${ + getPDFOutput(input).relative + })` ) .join("\n")}`; } diff --git a/lib/optional.js b/lib/optional.js new file mode 100644 index 0000000..8c8e33d --- /dev/null +++ b/lib/optional.js @@ -0,0 +1,29 @@ +import logger from "gulplog"; +import { obj } from "through2"; +import which from "which"; + +function noop() { + return obj((_file, _enc, done) => done()); +} + +/** + * + * @param {string} envName + * @param {any} + * @param {*} resolve + */ +export function withBinary(envName, execName, resolve) { + const setting = process.env[envName] || execName; + const binary = which.sync(setting, { nothrow: true }); + if (!binary) { + logger.info("%s not found (configure with %s)", execName, envName); + return noop; + } + logger.info( + "Using `%s` for %s (configure with %s)", + binary, + execName, + envName + ); + return resolve(binary); +} diff --git a/lib/wkhtmltopdf.js b/lib/wkhtmltopdf.js index b0f520a..33febcd 100644 --- a/lib/wkhtmltopdf.js +++ b/lib/wkhtmltopdf.js @@ -1,11 +1,12 @@ import { callbackify, promisify } from "util"; import child_process from "child_process"; import { createReadStream } from "fs"; -import File from "vinyl"; import logger from "gulplog"; import { mkTempFile } from "./tempdir"; import { obj } from "through2"; +import path from "path"; import PluginError from "plugin-error"; +import { withBinary } from "./optional"; const execFile = promisify(child_process.execFile); @@ -32,37 +33,52 @@ const DEFAULT_OPTIONS = { ], }; -const WKHTMLTOPDF_BINARY = process.env.WKHTMLTOPDF_BINARY || "wkhtmltopdf"; +/** + * @param {File} input + * @return {string} + */ +export function getPDFOutput(input) { + const output = input.clone({ deep: false, contents: false }); + output.basename = path.basename(input.dirname, ".md") + ".pdf"; + return output; +} -export default function wkhtmltopdf(options = {}) { - const { args } = Object.assign({}, DEFAULT_OPTIONS, options); - return obj( - callbackify(async function (input) { - try { - const output = await mkTempFile(input); - const parent = new File({ path: input.dirname }); - output.stem = parent.stem; - output.extname = ".pdf"; - const execArgs = [ - ...args, - `file://${input.path}?print-pdf`, - output.path, - ]; +export const wkhtmltopdf = withBinary( + "WKHTMLTOPDF_BINARY", + "wkhtmltopdf", + (WKHTMLTOPDF_BINARY) => + function wkhtmltopdf(options = {}) { + const { args } = Object.assign({}, DEFAULT_OPTIONS, options); + return obj( + callbackify(async function (input) { + try { + const output = await mkTempFile(getPDFOutput(input)); + const execArgs = [ + ...args, + `file://${input.path}?print-pdf`, + output.path, + ]; - const { stdout, stderr } = await execFile(WKHTMLTOPDF_BINARY, execArgs); - logger.info("%s: generated %s", PLUGIN_NAME, output.relative); - if (stdout.length > 0) { - logger.debug(PLUGIN_NAME, "stdout:", stdout); - } - if (stderr.length > 0) { - logger.debug(PLUGIN_NAME, "stderr:", stderr); - } + const { stdout, stderr } = await execFile( + WKHTMLTOPDF_BINARY, + execArgs + ); + logger.info("%s: generated %s", PLUGIN_NAME, output.relative); + if (stdout.length > 0) { + logger.debug(PLUGIN_NAME, "stdout:", stdout); + } + if (stderr.length > 0) { + logger.debug(PLUGIN_NAME, "stderr:", stderr); + } - output.contents = createReadStream(output.path); - this.push(output); - } catch (err) { - throw new PluginError(PLUGIN_NAME, err, { filename: input.path }); - } - }) - ); -} + output.contents = createReadStream(output.path); + this.push(output); + } catch (err) { + throw new PluginError(PLUGIN_NAME, err, { filename: input.path }); + } + }) + ); + } +); + +export default wkhtmltopdf; diff --git a/package-lock.json b/package-lock.json index f9483d3..3ccac86 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1033,6 +1033,17 @@ "semver": "^5.5.0", "shebang-command": "^1.2.0", "which": "^1.2.9" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + } } }, "currently-unhandled": { @@ -2836,6 +2847,16 @@ "ini": "^1.3.4", "is-windows": "^1.0.1", "which": "^1.2.14" + }, + "dependencies": { + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "requires": { + "isexe": "^2.0.0" + } + } } }, "globals": { @@ -6870,9 +6891,9 @@ } }, "which": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", - "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "requires": { "isexe": "^2.0.0" } diff --git a/package.json b/package.json index ec1f010..474dcd1 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ "gulplog": "^1.0.0", "plugin-error": "^1.0.1", "through2": "^3.0.1", - "vinyl": "^2.2.0" + "vinyl": "^2.2.0", + "which": "^2.0.2" }, "devDependencies": { "eslint": "^6.8.0", -- GitLab