From 2bce9acbfa504fbb275858dafc6276995654c68f Mon Sep 17 00:00:00 2001 From: Trivernis Date: Thu, 1 Aug 2019 20:31:46 +0200 Subject: [PATCH] More utility stuff - added book config to extend from - added metadata to mdconfig - added mapping of metadata to global variables - added global variable wordcount - added better error handling of puppeteer errors - added style to utility classes and tables --- CHANGELOG.md | 2 ++ README.md | 55 +++++++++++++++++++++++++++++----- src/configs/book-mdconfig.json | 23 ++++++++++++++ src/index.ts | 9 ++++-- src/lib/MarkdownConfig.ts | 15 +++++++++- src/lib/PreFormatter.ts | 9 ++++-- src/lib/Renderer.ts | 3 +- src/lib/globvars.ts | 14 +++++++++ src/lib/utils.ts | 12 ++++++++ src/styles/default.sass | 32 ++++++++++++++++++++ src/styles/includes/vars.sass | 1 + 11 files changed, 162 insertions(+), 13 deletions(-) create mode 100644 src/configs/book-mdconfig.json diff --git a/CHANGELOG.md b/CHANGELOG.md index d766712..f123a04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,4 +16,6 @@ - support for `mdconf.json`config file - MathJax script to html - `mdconfig.json` extensions with `extends: "name"` +- `mdconfig.json` metadata under `meta` that is available as global variables - variables prefixed with `$` and system variables prefixed with `$$` +- default config `book` diff --git a/README.md b/README.md index 889854b..50d6625 100644 --- a/README.md +++ b/README.md @@ -76,7 +76,7 @@ You can include your own stylesheet. It is applied after the default style. The ## Variables -Variables are prefixed with `$`. +Variables are prefixed with `$`. They are case sensitive. You can define and use variables like this: Defining: @@ -93,12 +93,47 @@ I'm eating an $fruit. There are system variables that are prefixed with `$$`. Currently you can use -variable | value | example value ----------|-----------------|-------------- -$now | current datetime| 31.07.2019 21:03:47 -$date | current date | 31.07.2019 -$time | current time | 21:03:47 +variable | value | example value +------------|--------------------------|-------------- +$$now | current datetime | 31.07.2019 21:03:47 +$$date | current date | 31.07.2019 +$$time | current time | 21:03:47 +$$wordcount | words in the document | 200 +All keys defined in the meta part of the `mdconfig.json` are also made +available as system variables as long as they don't overide existing variables. + +## Environments + +These environments need the `div` plugin to work. + +``` +:::multiline +One linebreak in here +Is one linebreak in the document. +::: + +:::centered +Text is centered in here. +::: + +:::page +This is rendered on one or more extra pages. +::: + +:::tabular + | | +--------------|-----------------|---------- +only the body | of the table is | displayed +and without | borders | +::: + +:::title +Title page environment +All text is bigger +and is kept on one page (if possible). +::: +``` ## Configuration file @@ -115,7 +150,13 @@ You can also define plugins, stylesheets and other stuff by using a `mdconf.json ], "stylesheets": [ "customstyle.css" - ] + ], + "meta": { // all values defined here are also global variables + "author": "John Doe", + "title": "Markdown is great", + "subtitle": "Or is it?", + "hey": "Vsauce, Michael here!" + } } ``` diff --git a/src/configs/book-mdconfig.json b/src/configs/book-mdconfig.json new file mode 100644 index 0000000..ec413ff --- /dev/null +++ b/src/configs/book-mdconfig.json @@ -0,0 +1,23 @@ +{ + "plugins": [ + "footnote", + "anchor", + "mark", + "sub", + "attrs", + "abbr", + "imsize", + "highlightjs", + "smartarrows", + "math", + "div", + "multimd-table", + "toc-done-right", + "center-text", + "header-sections", + "task-checkbox", + "underline", + "implicit-figures", + "inline-comments" + ] +} diff --git a/src/index.ts b/src/index.ts index 340fc7c..b09b722 100644 --- a/src/index.ts +++ b/src/index.ts @@ -81,8 +81,13 @@ async function main() { } else if (args.pdf) { let outputFile = noExt(args.file) + '.pdf'; - await renderer.renderPdf(args.file, outputFile); - console.log(`File stored as ${outputFile}`); + try { + await renderer.renderPdf(args.file, outputFile); + console.log(`File stored as ${outputFile}`); + } catch (err) { + logger.error(err); + console.error('Failed to render pdf.'); + } } else { diff --git a/src/lib/MarkdownConfig.ts b/src/lib/MarkdownConfig.ts index 7d1ba7d..a40b2fc 100644 --- a/src/lib/MarkdownConfig.ts +++ b/src/lib/MarkdownConfig.ts @@ -2,6 +2,7 @@ import * as fsx from 'fs-extra'; import * as path from 'path'; import {getMarkdownPlugin} from "./utils"; import {logger} from "./logger"; +import {addGlobalVar, globalVariables} from "./globvars"; const confName = 'mdconfig.json'; const confDir = `${__dirname}../../configs`; @@ -10,7 +11,8 @@ const confDir = `${__dirname}../../configs`; * Configs that can be extended */ const configs: any = { - "full": `${confDir}/full-${confName}` + "full": `${confDir}/full-${confName}`, + "book": `${confDir}/book-${confName}` }; /** @@ -53,6 +55,17 @@ export class MarkdownConfig { this.plugins = new Set(configData.plugins.map(getMarkdownPlugin)); if (configData.stylesheets) this.stylesheets = new Set(configData.stylesheets); + if (configData.meta) { + let meta: any = configData.meta; + // adding metadata as global variables + let definedGlobals = Object.keys(globalVariables); + for (let [key, value] of Object.entries(meta)) { + if (!definedGlobals.includes(key)) { + // @ts-ignore + addGlobalVar(key, value); + } + } + } } /** diff --git a/src/lib/PreFormatter.ts b/src/lib/PreFormatter.ts index e9a341b..1d057bc 100644 --- a/src/lib/PreFormatter.ts +++ b/src/lib/PreFormatter.ts @@ -6,7 +6,7 @@ import {pageFormats} from "./formats"; import {PDFFormat} from "puppeteer"; import {getMarkdownPlugin} from "./utils"; import {logger} from "./logger"; -import {globalVariables} from "./globvars"; +import {addGlobalVar, globalVariables} from "./globvars"; export class PreFormatter { public projectFiles: string[]; @@ -20,7 +20,7 @@ export class PreFormatter { this.projectFiles = []; this.resolvePath = []; this.stylesheets = []; - this.variables = Object.assign({}, globalVariables); + this.variables = {}; } async processCommands(doc: string, docpath: string, renderer: Renderer) { @@ -99,6 +99,11 @@ export class PreFormatter { } let documentContent = outputLines.join('\n'); + addGlobalVar('wordcount', () => { + let doc = documentContent.replace(/\[.*]/g, '').replace(/\s+/, ' '); + return doc.split(' ').length; + }); + Object.assign(this.variables, globalVariables); // assign globals right before replacing // replacing all variables with their values. for (let [key, value] of Object.entries(this.variables)) { let varReplacer: RegExp = new RegExp(`\\$${key}`, "gi"); diff --git a/src/lib/Renderer.ts b/src/lib/Renderer.ts index b71e2a6..db42389 100644 --- a/src/lib/Renderer.ts +++ b/src/lib/Renderer.ts @@ -8,7 +8,7 @@ import {PreFormatter} from "./PreFormatter"; import {EventEmitter} from "events"; import {PDFFormat} from "puppeteer"; import {MarkdownConfig} from "./MarkdownConfig"; -import {bundleImages, delay, includeMathJax} from "./utils"; +import {bundleImages, delay, includeMathJax, replaceMultilineBreaks} from "./utils"; import {logger} from "./logger"; export class Renderer extends EventEmitter { @@ -191,6 +191,7 @@ export class Renderer extends EventEmitter { return dom; }); this.useAfter(includeMathJax); + this.useAfter(replaceMultilineBreaks); // include all images as base64 if (this.config.bundle) this.useAfter(bundleImages); diff --git a/src/lib/globvars.ts b/src/lib/globvars.ts index e5c2231..632da16 100644 --- a/src/lib/globvars.ts +++ b/src/lib/globvars.ts @@ -4,7 +4,21 @@ const dateTimeFormat = "dd.MM.yyyy hh:mm:ss"; const dateFormat = "dd.MM.yyyy"; const timeFormat = "hh:mm:ss"; +/** + * Adds a global variable + * @param name - name of the variable + * @param value - value of the variable + */ +export function addGlobalVar(name: string, value: string|Function) { + name = name.replace(/\W/g, ''); // only allow word characters because it is interpreted as regex + globalVariables[`\\$${name}`] = value; +} + +/** + * All global variables that can be used in the document. + */ export const globalVariables: any = { + "\\$wordcount": 0, // will be replaced in PreFormatter "\\$now"() { return format(dateTimeFormat, new Date()); }, diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 085330a..c053b96 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -53,6 +53,18 @@ export function includeMathJax(dom: JSDOM): JSDOM { return dom; } +/** + * Replaces all linebreaks with
tags in a .multiline div (:::multiline:::) + * @param dom + */ +export function replaceMultilineBreaks(dom: JSDOM): JSDOM { + let document = dom.window.document; + let multis = document.querySelectorAll('.multiline p'); + for (let multi of multis) + multi.innerHTML = multi.innerHTML.replace(/\n/g, '
'); + return dom; +} + /** * Returns the markdown plugin associated with the pluginName * The plugin is first searched in the plugins definition. diff --git a/src/styles/default.sass b/src/styles/default.sass index 259f541..03f7f77 100644 --- a/src/styles/default.sass +++ b/src/styles/default.sass @@ -36,6 +36,15 @@ figcaption margin: 0.5em font-style: italic +table + border-collapse: collapse + td, th + padding: 0.2em + th + border-bottom: 2px solid $primary + td + border-bottom: 1px solid $inactive + .footnote-backref display: none @@ -44,8 +53,31 @@ figcaption page-break-after: always page-break-inside: avoid +.page.title + page-break-before: avoid + .newpage page-break-after: always .text-align-center text-align: center + +.centered + text-align: center + table + margin: auto + +.tabular + thead + display: none + td + border: none + +.title + font-size: x-large + h1 + font-size: 1.8em + h2 + font-size: 1.3em + table + font-size: x-large diff --git a/src/styles/includes/vars.sass b/src/styles/includes/vars.sass index a67d81d..e403a87 100644 --- a/src/styles/includes/vars.sass +++ b/src/styles/includes/vars.sass @@ -1,2 +1,3 @@ $backgroundSecondary: #eee $inactive: #444 +$primary: #000