From a698f5d69763801528b3b528e23c36558a5668c5 Mon Sep 17 00:00:00 2001 From: Elliot Blackburn Date: Tue, 26 Dec 2023 22:29:09 +0000 Subject: [PATCH] Start some upgrades --- README.md | 2 +- examples/api/md-file.md | 2 +- examples/footers/md-file.md | 2 +- examples/headers/md-file.md | 2 +- package.json | 20 ++++----- src/index.js | 84 ++++++++++++++++++------------------- src/utils.js | 22 +++++----- tests/index.js | 47 ++++++++++----------- tests/utils.js | 12 +++--- 9 files changed, 95 insertions(+), 98 deletions(-) diff --git a/README.md b/README.md index 3a5180d..231142c 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ In text emoji's are also supported, but there are a few instances of shorthand w ## Programatic API -The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise generated by the [Bluebird.js](bluebirdjs.com) library. For a full example see the [bin/index.js](./bin/index.js)! +The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise. For a full example see the [bin/index.js](./bin/index.js)! ### Example API usage diff --git a/examples/api/md-file.md b/examples/api/md-file.md index ff978f0..13e3c0a 100644 --- a/examples/api/md-file.md +++ b/examples/api/md-file.md @@ -32,7 +32,7 @@ In text emoji's are also supported, but there are a few instances of shorthand w ## Programatic API -The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise generated by the [Bluebird.js](bluebirdjs.com) library. For a full example see the [bin/index.js](./bin/index.js)! +The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise. For a full example see the [bin/index.js](./bin/index.js)! ### Example API Usage diff --git a/examples/footers/md-file.md b/examples/footers/md-file.md index aacfafd..a2fb2fe 100644 --- a/examples/footers/md-file.md +++ b/examples/footers/md-file.md @@ -32,7 +32,7 @@ In text emoji's are also supported, but there are a few instances of shorthand w ## Programatic API -The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise generated by the [Bluebird.js](bluebirdjs.com) library. For a full example see the [bin/index.js](./bin/index.js)! +The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise. For a full example see the [bin/index.js](./bin/index.js)! ### Example API Usage diff --git a/examples/headers/md-file.md b/examples/headers/md-file.md index aacfafd..a2fb2fe 100644 --- a/examples/headers/md-file.md +++ b/examples/headers/md-file.md @@ -32,7 +32,7 @@ In text emoji's are also supported, but there are a few instances of shorthand w ## Programatic API -The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise generated by the [Bluebird.js](bluebirdjs.com) library. For a full example see the [bin/index.js](./bin/index.js)! +The API is very straight forward with a single function `convert()` which takes some options. The convert method uses a promise. For a full example see the [bin/index.js](./bin/index.js)! ### Example API Usage diff --git a/package.json b/package.json index e6023ea..4c2396f 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,7 @@ "name": "mdpdf", "version": "2.0.4", "description": "Markdown to PDF command line converter", + "type": "module", "main": "./src/index.js", "scripts": { "lint": "prettier --trailing-comma es5 --single-quote --list-different '{src,tests,bin}/**/*.js'", @@ -36,20 +37,19 @@ }, "homepage": "https://github.com/bluehatbrit/mdpdf#readme", "dependencies": { - "bluebird": "^3.4.7", "cheerio": "^0.22.0", - "file-url": "^2.0.2", - "handlebars": "^4.1.2", + "file-url": "^4.0.0", + "handlebars": "^4.7.8", "loophole": "^1.1.0", "meow": "^3.7.0", - "prettier": "^1.15.3", - "puppeteer": "^1.3.0", - "showdown": "^1.9.0", - "showdown-emoji": "^1.0.3" + "prettier": "^3.1.1", + "puppeteer": "^21.6.1", + "showdown": "^2.1.0", + "showdown-emoji": "^3.0.0" }, "devDependencies": { - "execa": "^0.6.3", - "mocha": "^5.2.0", - "should": "^11.2.1" + "execa": "^8.0.1", + "mocha": "^10.2.0", + "should": "^13.2.3" } } diff --git a/src/index.js b/src/index.js index 4dd1894..4239d62 100644 --- a/src/index.js +++ b/src/index.js @@ -1,25 +1,25 @@ -const fs = require('fs'); -const path = require('path'); -const Promise = require('bluebird'); -const showdown = require('showdown'); -const showdownEmoji = require('showdown-emoji'); -const puppeteer = require('puppeteer'); -const Handlebars = require('handlebars'); -const loophole = require('loophole'); -const utils = require('./utils'); -const puppeteerHelper = require('./puppeteer-helper'); - -const readFile = Promise.promisify(fs.readFile); -const writeFile = Promise.promisify(fs.writeFile); +import { readFile as _readFile, writeFile as _writeFile, renameSync, unlinkSync } from 'fs'; +import { join, dirname, resolve } from 'path'; +import { promisify } from 'util'; +import { setFlavor, Converter } from 'showdown'; +import showdownEmoji from 'showdown-emoji'; +import { launch } from 'puppeteer'; +import { SafeString, compile } from 'handlebars'; +import { allowUnsafeNewFunction } from 'loophole'; +import { getStyles, getStyleBlock, qualifyImgSources } from './utils'; +import { getOptions } from './puppeteer-helper'; + +const readFile = promisify(_readFile); +const writeFile = promisify(_writeFile); // Main layout template -const layoutPath = path.join(__dirname, '/layouts/doc-body.hbs'); -const headerLayoutPath = path.join(__dirname, '/layouts/header.hbs'); -const footerLayoutPath = path.join(__dirname, '/layouts/footer.hbs'); +const layoutPath = join(__dirname, '/layouts/doc-body.hbs'); +const headerLayoutPath = join(__dirname, '/layouts/header.hbs'); +const footerLayoutPath = join(__dirname, '/layouts/footer.hbs'); // Syntax highlighting const highlightJs = - 'file://' + path.join(__dirname, '/assets/highlight/highlight.pack.js'); + 'file://' + join(__dirname, '/assets/highlight/highlight.pack.js'); function getAllStyles(options) { const cssStyleSheets = []; @@ -27,17 +27,17 @@ function getAllStyles(options) { // GitHub Markdown Style if (options.ghStyle) { cssStyleSheets.push( - path.join(__dirname, '/assets/github-markdown-css.css') + join(__dirname, '/assets/github-markdown-css.css') ); } // Highlight CSS cssStyleSheets.push( - path.join(__dirname, '/assets/highlight/styles/github.css') + join(__dirname, '/assets/highlight/styles/github.css') ); // Some additional defaults such as margins if (options.defaultStyle) { - cssStyleSheets.push(path.join(__dirname, '/assets/default.css')); + cssStyleSheets.push(join(__dirname, '/assets/default.css')); } // Optional user given CSS @@ -46,13 +46,13 @@ function getAllStyles(options) { } return { - styles: utils.getStyles(cssStyleSheets), - styleBlock: utils.getStyleBlock(cssStyleSheets), + styles: getStyles(cssStyleSheets), + styleBlock: getStyleBlock(cssStyleSheets), }; } function parseMarkdownToHtml(markdown, convertEmojis) { - showdown.setFlavor('github'); + setFlavor('github'); const options = { prefixHeaderId: false, ghCompatibleHeaderId: true, @@ -64,7 +64,7 @@ function parseMarkdownToHtml(markdown, convertEmojis) { options.extensions = [showdownEmoji]; } - const converter = new showdown.Converter(options); + const converter = new Converter(options); return converter.makeHtml(markdown); } @@ -79,11 +79,11 @@ function convert(options) { throw new Error('Destination path must be provided'); } - options.assetDir = path.dirname(path.resolve(options.source)); + options.assetDir = dirname(resolve(options.source)); let template = {}; const styles = getAllStyles(options); - let css = new Handlebars.SafeString(styles.styleBlock); + let css = new SafeString(styles.styleBlock); const local = { highlightJs, css: css, @@ -104,7 +104,7 @@ function convert(options) { return readFile(layoutPath, 'utf8'); }) .then(layout => { - template = Handlebars.compile(layout); + template = compile(layout); // Pull in the document source markdown return readFile(options.source, 'utf8'); @@ -113,11 +113,11 @@ function convert(options) { // Compile the main document let content = parseMarkdownToHtml(mdDoc, !options.noEmoji); - content = utils.qualifyImgSources(content, options); + content = qualifyImgSources(content, options); - local.body = new Handlebars.SafeString(content); + local.body = new SafeString(content); // Use loophole for this body template to avoid issues with editor extensions - const html = loophole.allowUnsafeNewFunction(() => template(local)); + const html = allowUnsafeNewFunction(() => template(local)); return createPdf(html, options); }); @@ -130,18 +130,18 @@ function prepareHeader(options, css) { // Get the hbs layout return readFile(headerLayoutPath, 'utf8') .then(headerLayout => { - headerTemplate = Handlebars.compile(headerLayout); + headerTemplate = compile(headerLayout); // Get the header html return readFile(options.header, 'utf8'); }) .then(headerContent => { - const preparedHeader = utils.qualifyImgSources(headerContent, options); + const preparedHeader = qualifyImgSources(headerContent, options); // Compile the header template const headerHtml = headerTemplate({ - content: new Handlebars.SafeString(preparedHeader), - css: new Handlebars.SafeString(css.replace(/"/gm, "'")), + content: new SafeString(preparedHeader), + css: new SafeString(css.replace(/"/gm, "'")), }); return headerHtml; @@ -154,7 +154,7 @@ function prepareHeader(options, css) { function prepareFooter(options) { if (options.footer) { return readFile(options.footer, 'utf8').then(footerContent => { - const preparedFooter = utils.qualifyImgSources(footerContent, options); + const preparedFooter = qualifyImgSources(footerContent, options); return preparedFooter; }); @@ -168,14 +168,14 @@ function createPdf(html, options) { let browser; let page; - const tempHtmlPath = path.join( - path.dirname(options.destination), + const tempHtmlPath = join( + dirname(options.destination), '_temp.html' ); return writeFile(tempHtmlPath, html) .then(() => { - return puppeteer.launch({ headless: true }); + return launch({ headless: true }); }) .then(newBrowser => { browser = newBrowser; @@ -187,7 +187,7 @@ function createPdf(html, options) { return page.goto('file:' + tempHtmlPath, { waitUntil: 'networkidle2' }); }) .then(() => { - const puppetOptions = puppeteerHelper.getOptions(options); + const puppetOptions = getOptions(options); return page.pdf(puppetOptions); }) @@ -196,15 +196,15 @@ function createPdf(html, options) { }) .then(() => { if (options.debug) { - fs.renameSync(tempHtmlPath, options.debug); + renameSync(tempHtmlPath, options.debug); } else { - fs.unlinkSync(tempHtmlPath); + unlinkSync(tempHtmlPath); } return options.destination; }); } -module.exports = { +export default { convert, }; diff --git a/src/utils.js b/src/utils.js index c273544..46677d8 100644 --- a/src/utils.js +++ b/src/utils.js @@ -1,8 +1,8 @@ -const path = require('path'); -const fs = require('fs'); -const url = require('url'); -const fileUrl = require('file-url'); -const cheerio = require('cheerio'); +import { resolve } from 'path'; +import { readFileSync } from 'fs'; +import { parse } from 'url'; +import fileUrl from 'file-url'; +import { load } from 'cheerio'; function getStyleBlock(stylesheets) { // Read in all stylesheets and format them into HTML to @@ -11,7 +11,7 @@ function getStyleBlock(stylesheets) { let styleHtml = ''; for (const i in stylesheets) { if (Object.prototype.hasOwnProperty.call(stylesheets, i)) { - const style = fs.readFileSync(stylesheets[i], 'utf8'); + const style = readFileSync(stylesheets[i], 'utf8'); styleHtml += ''; } } @@ -23,7 +23,7 @@ function getStyles(stylesheets) { let styleHtml = ''; for (const i in stylesheets) { if (Object.prototype.hasOwnProperty.call(stylesheets, i)) { - const style = fs.readFileSync(stylesheets[i], 'utf8'); + const style = readFileSync(stylesheets[i], 'utf8'); styleHtml += style; } } @@ -34,7 +34,7 @@ function getStyles(stylesheets) { function hasAcceptableProtocol(src) { const acceptableProtocols = ['http:', 'https:'].join('|'); - const theUrl = url.parse(src); + const theUrl = parse(src); if (!theUrl.protocol) { return false; @@ -49,12 +49,12 @@ function processSrc(src, options) { } // We need to convert it - const resolvedSrc = path.resolve(options.assetDir, src); + const resolvedSrc = resolve(options.assetDir, src); return fileUrl(resolvedSrc); } function qualifyImgSources(html, options) { - const $ = cheerio.load(html); + const $ = load(html); $('img').each((i, img) => { img.attribs.src = processSrc(img.attribs.src, options); @@ -63,7 +63,7 @@ function qualifyImgSources(html, options) { return $.html(); } -module.exports = { +export default { getStyleBlock, getStyles, hasAcceptableProtocol, diff --git a/tests/index.js b/tests/index.js index 0608fd1..3f99e1f 100644 --- a/tests/index.js +++ b/tests/index.js @@ -1,16 +1,16 @@ -const fs = require('fs'); -const should = require('should'); -const execa = require('execa'); -const mdpdf = require('../'); -const utils = require('./utils'); +import { exists as _exists, unlinkSync, existsSync } from 'fs'; +import should from 'should'; +import execa from 'execa'; +import { convert } from '../'; +import { createOptions } from './utils'; function clean() { const filesToRemove = ['./README.pdf', './README.html', './output.pdf', './test-img-output.pdf']; filesToRemove.forEach(file => { - fs.exists(file, exists => { + _exists(file, exists => { if (exists) { - fs.unlinkSync(file); + unlinkSync(file); } }); }); @@ -27,7 +27,7 @@ describe('Convert CLI', function() { execa('./bin/index.js', ['./README.md']) .then(result => { const stdout = result.stdout; - const pdfExists = fs.existsSync('./README.pdf'); + const pdfExists = existsSync('./README.pdf'); pdfExists.should.be.true(); stdout.should.endWith('README.pdf'); @@ -43,8 +43,8 @@ describe('Convert CLI', function() { execa('./bin/index.js', ['./README.md', '--debug']) .then(result => { const stdout = result.stdout; - const pdfExists = fs.existsSync('./README.pdf'); - const htmlExists = fs.existsSync('./README.html'); + const pdfExists = existsSync('./README.pdf'); + const htmlExists = existsSync('./README.html'); pdfExists.should.be.true(); htmlExists.should.be.true(); @@ -61,7 +61,7 @@ describe('Convert CLI', function() { execa('./bin/index.js', ['./README.md', 'output.pdf']) .then(result => { const stdout = result.stdout; - const pdfExists = fs.existsSync('./output.pdf'); + const pdfExists = existsSync('./output.pdf'); pdfExists.should.be.true(); stdout.should.endWith('output.pdf'); @@ -77,7 +77,7 @@ describe('Convert CLI', function() { execa('./bin/index.js', ['./tests/test.md', './test-img-output.pdf']) .then(result => { const stdout = result.stdout; - const pdfExists = fs.existsSync('./test-img-output.pdf'); + const pdfExists = existsSync('./test-img-output.pdf'); pdfExists.should.be.true(); stdout.should.endWith('test-img-output.pdf'); @@ -97,13 +97,12 @@ describe('Convert API', function() { context('when given a markdown source', () => { it('creates a pdf', done => { - const options = utils.createOptions({ + const options = createOptions({ source: 'README.md', }); - mdpdf - .convert(options) + convert(options) .then(pdfPath => { - const pdfExists = fs.existsSync('./README.pdf'); + const pdfExists = existsSync('./README.pdf'); pdfExists.should.be.true(); @@ -115,15 +114,14 @@ describe('Convert API', function() { context('when debug is true', () => { it('creates a html file', done => { - const options = utils.createOptions({ + const options = createOptions({ source: 'README.md', debug: true, }); - mdpdf - .convert(options) + convert(options) .then(pdfPath => { - const pdfExists = fs.existsSync('./README.pdf'); - const htmlExists = fs.existsSync('./README.html'); + const pdfExists = existsSync('./README.pdf'); + const htmlExists = existsSync('./README.html'); pdfExists.should.be.true(); htmlExists.should.be.true(); @@ -136,14 +134,13 @@ describe('Convert API', function() { context('when destination is set', () => { it('creates a pdf at the destination', done => { - const options = utils.createOptions({ + const options = createOptions({ source: 'README.md', destination: 'output.pdf', }); - mdpdf - .convert(options) + convert(options) .then(pdfPath => { - const pdfExists = fs.existsSync('./output.pdf'); + const pdfExists = existsSync('./output.pdf'); pdfExists.should.be.true(); diff --git a/tests/utils.js b/tests/utils.js index 2cadbd1..b072ec9 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -1,4 +1,4 @@ -const path = require('path'); +import { resolve, dirname, join } from 'path'; const createOptions = function(options) { const source = options.source; @@ -9,16 +9,16 @@ const createOptions = function(options) { return { ghStyle: true, defaultStyle: true, - source: path.resolve(source), - destination: path.resolve(destination), - assetDir: path.dirname(path.resolve(source)), + source: resolve(source), + destination: resolve(destination), + assetDir: dirname(resolve(source)), styles: null, header: null, debug: debug ? source.slice(0, source.indexOf('.md')) + '.html' : null, pdf: { format: 'A4', orientation: 'portrait', - base: path.join('file://', __dirname, '/assets/'), + base: join('file://', __dirname, '/assets/'), header: { height: null, }, @@ -32,6 +32,6 @@ const createOptions = function(options) { }; }; -module.exports = { +export default { createOptions, };