diff --git a/lib/note/noteActions.js b/lib/note/noteActions.js
index 964f4505d6..fca032f676 100644
--- a/lib/note/noteActions.js
+++ b/lib/note/noteActions.js
@@ -2,11 +2,11 @@
const fs = require('fs')
const path = require('path')
-const markdownpdf = require('markdown-pdf')
const shortId = require('shortid')
const querystring = require('querystring')
const moment = require('moment')
const { Pandoc } = require('@hackmd/pandoc.js')
+const { convertMarkdownToPDF } = require('../utils/markdown-to-pdf')
const config = require('../config')
const logger = require('../logger')
@@ -64,7 +64,7 @@ function actionInfo (req, res, note) {
res.send(data)
}
-function actionPDF (req, res, note) {
+async function actionPDF (req, res, note) {
const url = config.serverURL || 'http://' + req.get('host')
const body = note.content
const extracted = Note.extractMeta(body)
@@ -78,14 +78,17 @@ function actionPDF (req, res, note) {
}
const pdfPath = config.tmpPath + '/' + Date.now() + '.pdf'
content = content.replace(/\]\(\//g, '](' + url + '/')
- const markdownpdfOptions = {
- highlightCssPath: highlightCssPath
- }
- markdownpdf(markdownpdfOptions).from.string(content).to(pdfPath, function () {
+
+ try {
+ await convertMarkdownToPDF(content, pdfPath, {
+ highlightCssPath: highlightCssPath
+ })
+
if (!fs.existsSync(pdfPath)) {
logger.error('PDF seems to not be generated as expected. File doesn\'t exist: ' + pdfPath)
return errorInternalError(req, res)
}
+
const stream = fs.createReadStream(pdfPath)
let filename = title
// Be careful of special characters
@@ -95,12 +98,33 @@ function actionPDF (req, res, note) {
res.setHeader('Cache-Control', 'private')
res.setHeader('Content-Type', 'application/pdf; charset=UTF-8')
res.setHeader('X-Robots-Tag', 'noindex, nofollow') // prevent crawling
- stream.on('end', () => {
- stream.close()
- fs.unlinkSync(pdfPath)
- })
+
+ // Cleanup file after streaming
+ const cleanup = () => {
+ try {
+ if (fs.existsSync(pdfPath)) {
+ fs.unlinkSync(pdfPath)
+ }
+ } catch (err) {
+ logger.error('Failed to cleanup PDF file:', err)
+ }
+ }
+
+ stream.on('end', cleanup)
+ stream.on('error', cleanup)
stream.pipe(res)
- })
+ } catch (error) {
+ logger.error('PDF generation failed:', error)
+ // Cleanup any partially created file
+ try {
+ if (fs.existsSync(pdfPath)) {
+ fs.unlinkSync(pdfPath)
+ }
+ } catch (cleanupError) {
+ logger.error('Failed to cleanup partial PDF file:', cleanupError)
+ }
+ return errorInternalError(req, res)
+ }
}
const outputFormats = {
diff --git a/lib/utils/markdown-to-pdf.js b/lib/utils/markdown-to-pdf.js
new file mode 100644
index 0000000000..871478dafc
--- /dev/null
+++ b/lib/utils/markdown-to-pdf.js
@@ -0,0 +1,179 @@
+'use strict'
+
+const { chromium } = require('playwright-chromium')
+const markdownit = require('markdown-it')
+const path = require('path')
+const fs = require('fs')
+
+// Configure markdown-it similar to frontend
+function createMarkdownRenderer () {
+ const md = markdownit('default', {
+ html: true,
+ breaks: true,
+ linkify: true,
+ typographer: true,
+ highlight: function (str, lang) {
+ try {
+ const hljs = require('highlight.js')
+ if (lang && hljs.getLanguage(lang)) {
+ return '
' +
+ hljs.highlight(lang, str, true).value +
+ '
'
+ }
+ } catch (error) {
+ // Fall back to no highlighting
+ }
+ return '' + md.utils.escapeHtml(str) + '
'
+ }
+ })
+
+ // Add plugins commonly used in CodiMD
+ try {
+ md.use(require('markdown-it-abbr'))
+ md.use(require('markdown-it-footnote'))
+ md.use(require('markdown-it-deflist'))
+ md.use(require('markdown-it-mark'))
+ md.use(require('markdown-it-ins'))
+ md.use(require('markdown-it-sub'))
+ md.use(require('markdown-it-sup'))
+ } catch (error) {
+ // Some plugins may not be available, continue with basic rendering
+ console.warn('Some markdown-it plugins not available:', error.message)
+ }
+
+ return md
+}
+
+async function convertMarkdownToPDF (markdown, outputPath, options = {}) {
+ const md = createMarkdownRenderer()
+
+ // Convert markdown to HTML
+ const htmlContent = md.render(markdown)
+
+ // Read highlight.js CSS
+ const highlightCssPath = options.highlightCssPath || path.join(__dirname, '../../node_modules/highlight.js/styles/github-gist.css')
+ let highlightCss = ''
+
+ if (fs.existsSync(highlightCssPath)) {
+ highlightCss = fs.readFileSync(highlightCssPath, 'utf8')
+ }
+
+ // Create full HTML document
+ const fullHtml = `
+
+
+
+
+ PDF Export
+
+
+
+ ${htmlContent}
+
+`
+
+ // Launch Playwright Chromium and generate PDF
+ let browser = null
+ try {
+ browser = await chromium.launch({
+ headless: true,
+ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage']
+ })
+
+ const page = await browser.newPage()
+
+ // Set a timeout for page operations
+ page.setDefaultTimeout(30000)
+
+ await page.setContent(fullHtml, {
+ waitUntil: 'networkidle'
+ })
+
+ await page.pdf({
+ path: outputPath,
+ format: 'A4',
+ margin: {
+ top: '20px',
+ right: '20px',
+ bottom: '20px',
+ left: '20px'
+ },
+ printBackground: true
+ })
+
+ return true
+ } catch (error) {
+ throw new Error(`PDF generation failed: ${error.message}`)
+ } finally {
+ if (browser) {
+ try {
+ await browser.close()
+ } catch (closeError) {
+ console.warn('Failed to close browser:', closeError.message)
+ }
+ }
+ }
+}
+
+module.exports = {
+ convertMarkdownToPDF
+}
diff --git a/package-lock.json b/package-lock.json
index 2b32910232..ff8eed5219 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -44,7 +44,6 @@
"lodash": "^4.17.21",
"lutim": "~1.0.2",
"markdown-it": "~10.0.0",
- "markdown-pdf": "~9.0.0",
"method-override": "~3.0.0",
"minimist": "^1.2.8",
"minio": "^7.1.1",
@@ -67,6 +66,7 @@
"passport.socketio": "~3.7.0",
"pg": "~8.8.0",
"pg-hstore": "~2.3.2",
+ "playwright-chromium": "^1.53.0",
"prom-client": "^11.0.0",
"prometheus-api-metrics": "^2.2.5",
"randomcolor": "~0.5.4",
@@ -2620,14 +2620,6 @@
"node": ">= 4.5.0"
}
},
- "node_modules/autolinker": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz",
- "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=",
- "dependencies": {
- "gulp-header": "^1.7.1"
- }
- },
"node_modules/available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -4850,14 +4842,6 @@
"typedarray": "^0.0.6"
}
},
- "node_modules/concat-with-sourcemaps": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
- "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
- "dependencies": {
- "source-map": "^0.6.1"
- }
- },
"node_modules/confbox": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.1.tgz",
@@ -7180,11 +7164,6 @@
"node": ">=0.10"
}
},
- "node_modules/duplexer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
- },
"node_modules/duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -8918,23 +8897,6 @@
"extract-zip": "cli.js"
}
},
- "node_modules/extract-zip/node_modules/fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "dependencies": {
- "pend": "~1.2.0"
- }
- },
- "node_modules/extract-zip/node_modules/yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- },
"node_modules/extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
@@ -9011,6 +8973,15 @@
"url": "https://github.com/sponsors/wooorm"
}
},
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
"node_modules/feature-policy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz",
@@ -10445,17 +10416,6 @@
"node": ">=4.x"
}
},
- "node_modules/gulp-header": {
- "version": "1.8.12",
- "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz",
- "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==",
- "deprecated": "Removed event-stream from gulp-header",
- "dependencies": {
- "concat-with-sourcemaps": "*",
- "lodash.template": "^4.4.0",
- "through2": "^2.0.0"
- }
- },
"node_modules/hachure-fill": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
@@ -10785,6 +10745,7 @@
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.9.tgz",
"integrity": "sha512-M0zZvfLr5p0keDMCAhNBp03XJbKBxUx5AfyfufMdFMEP4N/Xj6dh0IqC75ys7BAzceR34NgcvXjupRVaHBPPVQ==",
"deprecated": "Version no longer supported. Upgrade to @latest",
+ "dev": true,
"engines": {
"node": "*"
}
@@ -11936,7 +11897,8 @@
"node_modules/isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
},
"node_modules/isobject": {
"version": "3.0.1",
@@ -12479,11 +12441,6 @@
"katex": "cli.js"
}
},
- "node_modules/kew": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
- "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s="
- },
"node_modules/keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -12525,14 +12482,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/klaw": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
- "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
- "optionalDependencies": {
- "graceful-fs": "^4.1.9"
- }
- },
"node_modules/koa": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/koa/-/koa-2.14.2.tgz",
@@ -12975,11 +12924,6 @@
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true
},
- "node_modules/lodash._reinterpolate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
- "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
- },
"node_modules/lodash.assignin": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
@@ -13062,23 +13006,6 @@
"resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
"integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
},
- "node_modules/lodash.template": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
- "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
- "dependencies": {
- "lodash._reinterpolate": "^3.0.0",
- "lodash.templatesettings": "^4.0.0"
- }
- },
- "node_modules/lodash.templatesettings": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
- "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
- "dependencies": {
- "lodash._reinterpolate": "^3.0.0"
- }
- },
"node_modules/lodash.union": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
@@ -13329,28 +13256,6 @@
"resolved": "https://registry.npmjs.org/entities/-/entities-2.0.0.tgz",
"integrity": "sha512-D9f7V0JSRwIxlRI2mjMqufDrRDnx8p+eEOz7aUM9SuvF8gsBzra0/6tbjl1m8eQHrZlYj6PxqE00hZ1SAIKPLw=="
},
- "node_modules/markdown-pdf": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/markdown-pdf/-/markdown-pdf-9.0.0.tgz",
- "integrity": "sha512-5Ck+LJzsxfXR4Bjmg5sLfVW9JhfkG/WEUsFUVdYN7FSHRKLEYw4r/O6esrWA8hEb+mV3RvFNUQTp+DpFKMfyYg==",
- "dependencies": {
- "commander": "^2.2.0",
- "duplexer": "^0.1.1",
- "extend": "^3.0.0",
- "highlight.js": "^9.1.0",
- "phantomjs-prebuilt": "^2.1.3",
- "remarkable": "^1.7.1",
- "stream-from-to": "^1.4.2",
- "through2": "^2.0.0",
- "tmp": "0.0.33"
- },
- "bin": {
- "markdown-pdf": "bin/markdown-pdf"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/markdownlint": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.27.0.tgz",
@@ -15584,6 +15489,7 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -16154,7 +16060,8 @@
"node_modules/pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "license": "MIT"
},
"node_modules/performance-now": {
"version": "2.1.0",
@@ -16246,65 +16153,6 @@
"split": "^1.0.0"
}
},
- "node_modules/phantomjs-prebuilt": {
- "version": "2.1.16",
- "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz",
- "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=",
- "deprecated": "this package is now deprecated",
- "hasInstallScript": true,
- "dependencies": {
- "es6-promise": "^4.0.3",
- "extract-zip": "^1.6.5",
- "fs-extra": "^1.0.0",
- "hasha": "^2.2.0",
- "kew": "^0.7.0",
- "progress": "^1.1.8",
- "request": "^2.81.0",
- "request-progress": "^2.0.1",
- "which": "^1.2.10"
- },
- "bin": {
- "phantomjs": "bin/phantomjs"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/fs-extra": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
- "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^2.1.0",
- "klaw": "^1.0.0"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/hasha": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
- "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
- "dependencies": {
- "is-stream": "^1.0.1",
- "pinkie-promise": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/jsonfile": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/phantomjs-prebuilt/node_modules/progress": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
- "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=",
- "engines": {
- "node": ">=0.4.0"
- }
- },
"node_modules/picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -16333,25 +16181,6 @@
"node": ">=4"
}
},
- "node_modules/pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
- "dependencies": {
- "pinkie": "^2.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/pkg-conf": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz",
@@ -16483,6 +16312,32 @@
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
},
+ "node_modules/playwright-chromium": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.53.0.tgz",
+ "integrity": "sha512-wFIOWSc3037Ql9swJrfCQL/SfcVXbl8X944CzzQmkvh4KqCNp1QMBOGPfltu/+URTfCa5I9qc6HW1YEgY1jeNA==",
+ "hasInstallScript": true,
+ "dependencies": {
+ "playwright-core": "1.53.0"
+ },
+ "bin": {
+ "playwright": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/playwright-core": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0.tgz",
+ "integrity": "sha512-mGLg8m0pm4+mmtB7M89Xw/GSqoNC+twivl8ITteqvAndachozYe2ZA7srU6uleV1vEdAHYqjq+SV8SNxRRFYBw==",
+ "bin": {
+ "playwright-core": "cli.js"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/points-on-curve": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
@@ -18424,21 +18279,6 @@
"xtend": "^4.0.1"
}
},
- "node_modules/remarkable": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
- "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==",
- "dependencies": {
- "argparse": "^1.0.10",
- "autolinker": "~0.28.0"
- },
- "bin": {
- "remarkable": "bin/remarkable.js"
- },
- "engines": {
- "node": ">= 0.10.0"
- }
- },
"node_modules/remarkable-katex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/remarkable-katex/-/remarkable-katex-1.2.1.tgz",
@@ -18669,14 +18509,6 @@
"node": ">= 4"
}
},
- "node_modules/request-progress": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
- "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
- "dependencies": {
- "throttleit": "^1.0.0"
- }
- },
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -19177,11 +19009,6 @@
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
"dev": true
},
- "node_modules/series-stream": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/series-stream/-/series-stream-1.0.1.tgz",
- "integrity": "sha1-MRoJxcHVoJFECDLhpICkdADxAF0="
- },
"node_modules/serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
@@ -19622,6 +19449,7 @@
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
"engines": {
"node": ">=0.10.0"
}
@@ -19916,22 +19744,6 @@
"stream-shift": "^1.0.0"
}
},
- "node_modules/stream-from-to": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/stream-from-to/-/stream-from-to-1.4.3.tgz",
- "integrity": "sha1-snBHPrxRTnNhVyfF0vdrIplB35Q=",
- "dependencies": {
- "async": "^1.5.2",
- "concat-stream": "^1.4.7",
- "mkdirp": "^0.5.0",
- "series-stream": "^1.0.1"
- }
- },
- "node_modules/stream-from-to/node_modules/async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- },
"node_modules/stream-http": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
@@ -20609,11 +20421,6 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
- "node_modules/throttleit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
- "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw="
- },
"node_modules/through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -20623,6 +20430,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
"dependencies": {
"readable-stream": "~2.3.6",
"xtend": "~4.0.1"
@@ -20665,6 +20473,7 @@
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
"dependencies": {
"os-tmpdir": "~1.0.2"
},
@@ -23379,6 +23188,7 @@
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
"integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
+ "dev": true,
"dependencies": {
"isexe": "^2.0.0"
},
@@ -23780,6 +23590,16 @@
"node": "6.* || 8.* || >= 10.*"
}
},
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"node_modules/yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
@@ -26012,14 +25832,6 @@
"integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==",
"dev": true
},
- "autolinker": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/autolinker/-/autolinker-0.28.1.tgz",
- "integrity": "sha1-BlK0kYgYefB3XazgzcoyM5QqTkc=",
- "requires": {
- "gulp-header": "^1.7.1"
- }
- },
"available-typed-arrays": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz",
@@ -27970,14 +27782,6 @@
"typedarray": "^0.0.6"
}
},
- "concat-with-sourcemaps": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/concat-with-sourcemaps/-/concat-with-sourcemaps-1.1.0.tgz",
- "integrity": "sha512-4gEjHJFT9e+2W/77h/DS5SGUgwDaOwprX8L/gl5+3ixnzkVJJsZWDSelmN3Oilw3LNDZjZV0yqH1hLG3k6nghg==",
- "requires": {
- "source-map": "^0.6.1"
- }
- },
"confbox": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.1.tgz",
@@ -29914,11 +29718,6 @@
"nan": "^2.14.0"
}
},
- "duplexer": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz",
- "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E="
- },
"duplexer3": {
"version": "0.1.4",
"resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz",
@@ -31331,25 +31130,6 @@
"debug": "^2.6.9",
"mkdirp": "^0.5.4",
"yauzl": "^2.10.0"
- },
- "dependencies": {
- "fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "requires": {
- "pend": "~1.2.0"
- }
- },
- "yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "requires": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- }
}
},
"extsprintf": {
@@ -31405,6 +31185,14 @@
"format": "^0.2.0"
}
},
+ "fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "requires": {
+ "pend": "~1.2.0"
+ }
+ },
"feature-policy": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/feature-policy/-/feature-policy-0.3.0.tgz",
@@ -32538,16 +32326,6 @@
"integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==",
"dev": true
},
- "gulp-header": {
- "version": "1.8.12",
- "resolved": "https://registry.npmjs.org/gulp-header/-/gulp-header-1.8.12.tgz",
- "integrity": "sha512-lh9HLdb53sC7XIZOYzTXM4lFuXElv3EVkSDhsd7DoJBj7hm+Ni7D3qYbb+Rr8DuM8nRanBvkVO9d7askreXGnQ==",
- "requires": {
- "concat-with-sourcemaps": "*",
- "lodash.template": "^4.4.0",
- "through2": "^2.0.0"
- }
- },
"hachure-fill": {
"version": "0.5.2",
"resolved": "https://registry.npmjs.org/hachure-fill/-/hachure-fill-0.5.2.tgz",
@@ -32798,7 +32576,8 @@
"highlight.js": {
"version": "9.15.9",
"resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.15.9.tgz",
- "integrity": "sha512-M0zZvfLr5p0keDMCAhNBp03XJbKBxUx5AfyfufMdFMEP4N/Xj6dh0IqC75ys7BAzceR34NgcvXjupRVaHBPPVQ=="
+ "integrity": "sha512-M0zZvfLr5p0keDMCAhNBp03XJbKBxUx5AfyfufMdFMEP4N/Xj6dh0IqC75ys7BAzceR34NgcvXjupRVaHBPPVQ==",
+ "dev": true
},
"hmac-drbg": {
"version": "1.0.1",
@@ -33688,7 +33467,8 @@
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA="
+ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=",
+ "dev": true
},
"isobject": {
"version": "3.0.1",
@@ -34135,11 +33915,6 @@
"commander": "^2.19.0"
}
},
- "kew": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz",
- "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s="
- },
"keygrip": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz",
@@ -34175,14 +33950,6 @@
"integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
"dev": true
},
- "klaw": {
- "version": "1.3.1",
- "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz",
- "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=",
- "requires": {
- "graceful-fs": "^4.1.9"
- }
- },
"koa": {
"version": "2.14.2",
"resolved": "https://registry.npmjs.org/koa/-/koa-2.14.2.tgz",
@@ -34536,11 +34303,6 @@
"integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==",
"dev": true
},
- "lodash._reinterpolate": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz",
- "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0="
- },
"lodash.assignin": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/lodash.assignin/-/lodash.assignin-4.2.0.tgz",
@@ -34623,23 +34385,6 @@
"resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz",
"integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0="
},
- "lodash.template": {
- "version": "4.5.0",
- "resolved": "https://registry.npmjs.org/lodash.template/-/lodash.template-4.5.0.tgz",
- "integrity": "sha512-84vYFxIkmidUiFxidA/KjjH9pAycqW+h980j7Fuz5qxRtO9pgB7MDFTdys1N7A5mcucRiDyEq4fusljItR1T/A==",
- "requires": {
- "lodash._reinterpolate": "^3.0.0",
- "lodash.templatesettings": "^4.0.0"
- }
- },
- "lodash.templatesettings": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/lodash.templatesettings/-/lodash.templatesettings-4.2.0.tgz",
- "integrity": "sha512-stgLz+i3Aa9mZgnjr/O+v9ruKZsPsndy7qPZOchbqk2cnTU1ZaldKK+v7m54WoKIyxiuMZTKT2H81F8BeAc3ZQ==",
- "requires": {
- "lodash._reinterpolate": "^3.0.0"
- }
- },
"lodash.union": {
"version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
@@ -34869,22 +34614,6 @@
"integrity": "sha1-y5yf+RpSVawI8/09YyhuFd8KH8M=",
"dev": true
},
- "markdown-pdf": {
- "version": "9.0.0",
- "resolved": "https://registry.npmjs.org/markdown-pdf/-/markdown-pdf-9.0.0.tgz",
- "integrity": "sha512-5Ck+LJzsxfXR4Bjmg5sLfVW9JhfkG/WEUsFUVdYN7FSHRKLEYw4r/O6esrWA8hEb+mV3RvFNUQTp+DpFKMfyYg==",
- "requires": {
- "commander": "^2.2.0",
- "duplexer": "^0.1.1",
- "extend": "^3.0.0",
- "highlight.js": "^9.1.0",
- "phantomjs-prebuilt": "^2.1.3",
- "remarkable": "^1.7.1",
- "stream-from-to": "^1.4.2",
- "through2": "^2.0.0",
- "tmp": "0.0.33"
- }
- },
"markdownlint": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/markdownlint/-/markdownlint-0.27.0.tgz",
@@ -36697,7 +36426,8 @@
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
- "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
+ "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
+ "dev": true
},
"p-cancelable": {
"version": "1.1.0",
@@ -37149,7 +36879,7 @@
"pend": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="
},
"performance-now": {
"version": "2.1.0",
@@ -37219,56 +36949,6 @@
"split": "^1.0.0"
}
},
- "phantomjs-prebuilt": {
- "version": "2.1.16",
- "resolved": "https://registry.npmjs.org/phantomjs-prebuilt/-/phantomjs-prebuilt-2.1.16.tgz",
- "integrity": "sha1-79ISpKOWbTZHaE6ouniFSb4q7+8=",
- "requires": {
- "es6-promise": "^4.0.3",
- "extract-zip": "^1.6.5",
- "fs-extra": "^1.0.0",
- "hasha": "^2.2.0",
- "kew": "^0.7.0",
- "progress": "^1.1.8",
- "request": "^2.81.0",
- "request-progress": "^2.0.1",
- "which": "^1.2.10"
- },
- "dependencies": {
- "fs-extra": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-1.0.0.tgz",
- "integrity": "sha1-zTzl9+fLYUWIP8rjGR6Yd/hYeVA=",
- "requires": {
- "graceful-fs": "^4.1.2",
- "jsonfile": "^2.1.0",
- "klaw": "^1.0.0"
- }
- },
- "hasha": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz",
- "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=",
- "requires": {
- "is-stream": "^1.0.1",
- "pinkie-promise": "^2.0.0"
- }
- },
- "jsonfile": {
- "version": "2.4.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz",
- "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=",
- "requires": {
- "graceful-fs": "^4.1.6"
- }
- },
- "progress": {
- "version": "1.1.8",
- "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz",
- "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74="
- }
- }
- },
"picocolors": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
@@ -37288,19 +36968,6 @@
"integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=",
"dev": true
},
- "pinkie": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz",
- "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA="
- },
- "pinkie-promise": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz",
- "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=",
- "requires": {
- "pinkie": "^2.0.0"
- }
- },
"pkg-conf": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/pkg-conf/-/pkg-conf-3.1.0.tgz",
@@ -37409,6 +37076,19 @@
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.5.tgz",
"integrity": "sha512-TuvHS8AOIZNAlE77WUDiR4rySV/VMptyMfcfeoMgs4P8apaZM3JrnbzBiixKUv+XR6i+BXrQh8WAnjaSPFO65Q=="
},
+ "playwright-chromium": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/playwright-chromium/-/playwright-chromium-1.53.0.tgz",
+ "integrity": "sha512-wFIOWSc3037Ql9swJrfCQL/SfcVXbl8X944CzzQmkvh4KqCNp1QMBOGPfltu/+URTfCa5I9qc6HW1YEgY1jeNA==",
+ "requires": {
+ "playwright-core": "1.53.0"
+ }
+ },
+ "playwright-core": {
+ "version": "1.53.0",
+ "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.53.0.tgz",
+ "integrity": "sha512-mGLg8m0pm4+mmtB7M89Xw/GSqoNC+twivl8ITteqvAndachozYe2ZA7srU6uleV1vEdAHYqjq+SV8SNxRRFYBw=="
+ },
"points-on-curve": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/points-on-curve/-/points-on-curve-0.2.0.tgz",
@@ -39001,15 +38681,6 @@
"xtend": "^4.0.1"
}
},
- "remarkable": {
- "version": "1.7.4",
- "resolved": "https://registry.npmjs.org/remarkable/-/remarkable-1.7.4.tgz",
- "integrity": "sha512-e6NKUXgX95whv7IgddywbeN/ItCkWbISmc2DiqHJb0wTrqZIexqdco5b8Z3XZoo/48IdNVKM9ZCvTPJ4F5uvhg==",
- "requires": {
- "argparse": "^1.0.10",
- "autolinker": "~0.28.0"
- }
- },
"remarkable-katex": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/remarkable-katex/-/remarkable-katex-1.2.1.tgz",
@@ -39183,14 +38854,6 @@
"uuid": "^3.3.2"
}
},
- "request-progress": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz",
- "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=",
- "requires": {
- "throttleit": "^1.0.0"
- }
- },
"require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@@ -39606,11 +39269,6 @@
"integrity": "sha512-0Vb/54WJ6k5v8sSWN09S0ora+Hnr+cX40r9F170nT+mSkaxltoE/7R3OrIdBSUv1OoiobH1QoWQbCnAO+e8J1A==",
"dev": true
},
- "series-stream": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/series-stream/-/series-stream-1.0.1.tgz",
- "integrity": "sha1-MRoJxcHVoJFECDLhpICkdADxAF0="
- },
"serve-static": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz",
@@ -40001,7 +39659,8 @@
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true
},
"source-map-resolve": {
"version": "0.5.3",
@@ -40246,24 +39905,6 @@
"stream-shift": "^1.0.0"
}
},
- "stream-from-to": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/stream-from-to/-/stream-from-to-1.4.3.tgz",
- "integrity": "sha1-snBHPrxRTnNhVyfF0vdrIplB35Q=",
- "requires": {
- "async": "^1.5.2",
- "concat-stream": "^1.4.7",
- "mkdirp": "^0.5.0",
- "series-stream": "^1.0.1"
- },
- "dependencies": {
- "async": {
- "version": "1.5.2",
- "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
- "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
- }
- }
- },
"stream-http": {
"version": "2.8.3",
"resolved": "https://registry.npmjs.org/stream-http/-/stream-http-2.8.3.tgz",
@@ -40835,11 +40476,6 @@
"integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=",
"dev": true
},
- "throttleit": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz",
- "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw="
- },
"through": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
@@ -40849,6 +40485,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz",
"integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==",
+ "dev": true,
"requires": {
"readable-stream": "~2.3.6",
"xtend": "~4.0.1"
@@ -40888,6 +40525,7 @@
"version": "0.0.33",
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
+ "dev": true,
"requires": {
"os-tmpdir": "~1.0.2"
}
@@ -43204,6 +42842,7 @@
"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"
}
@@ -43531,6 +43170,15 @@
"decamelize": "^1.2.0"
}
},
+ "yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "requires": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
"yeast": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/yeast/-/yeast-0.1.2.tgz",
diff --git a/package.json b/package.json
index 4ab074735f..e6f77bbaf3 100644
--- a/package.json
+++ b/package.json
@@ -22,6 +22,7 @@
"jsonlint": "find . -type f -not -ipath \"./.devcontainer/*\" -not -ipath \"./node_modules/*\" -not -ipath \"./.vscode/*\" \\( -name \"*.json\" -o -name \"*.json.*\" \\) | xargs -n 1 -I{} -- bash -c 'echo {}; jq . {} > /dev/null;'",
"start": "sequelize db:migrate && node app.js",
"mocha": "mocha --require intelli-espower-loader --exit ./test --recursive",
+ "mocha:cli": "mocha --require intelli-espower-loader --exit",
"mocha:ci": "mocha --no-color -R dot --require intelli-espower-loader --exit ./test --recursive",
"coverage": "nyc mocha --require intelli-espower-loader --exit --recursive ./test",
"coverage:ci": "nyc mocha --no-color -R dot --require intelli-espower-loader --exit --recursive ./test",
@@ -65,7 +66,6 @@
"lodash": "^4.17.21",
"lutim": "~1.0.2",
"markdown-it": "~10.0.0",
- "markdown-pdf": "~9.0.0",
"method-override": "~3.0.0",
"minimist": "^1.2.8",
"minio": "^7.1.1",
@@ -88,6 +88,7 @@
"passport.socketio": "~3.7.0",
"pg": "~8.8.0",
"pg-hstore": "~2.3.2",
+ "playwright-chromium": "^1.53.0",
"prom-client": "^11.0.0",
"prometheus-api-metrics": "^2.2.5",
"randomcolor": "~0.5.4",
diff --git a/test/pdf-generation.test.js b/test/pdf-generation.test.js
new file mode 100644
index 0000000000..66bfd4e594
--- /dev/null
+++ b/test/pdf-generation.test.js
@@ -0,0 +1,123 @@
+/* eslint-env node, mocha */
+'use strict'
+
+const assert = require('assert')
+const fs = require('fs')
+const path = require('path')
+const os = require('os')
+
+describe('PDF Generation', function () {
+ let convertMarkdownToPDF
+
+ before(function () {
+ // Import the PDF conversion function
+ const markdownToPdf = require('../lib/utils/markdown-to-pdf')
+ convertMarkdownToPDF = markdownToPdf.convertMarkdownToPDF
+ })
+
+ describe('Module Structure', function () {
+ it('should export convertMarkdownToPDF function', function () {
+ assert(typeof convertMarkdownToPDF === 'function', 'convertMarkdownToPDF should be a function')
+ })
+
+ it('should have required dependencies', function () {
+ const packagePath = path.join(__dirname, '../package.json')
+ const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'))
+
+ assert(packageJson.dependencies['playwright-chromium'], 'playwright-chromium should be in dependencies')
+ assert(!packageJson.dependencies['markdown-pdf'], 'markdown-pdf should not be in dependencies')
+ })
+ })
+
+ describe('PDF Conversion', function () {
+ const testMarkdown = `# Test Document
+
+This is a **test** document with some content.
+
+## Code Block
+\`\`\`javascript
+console.log('Hello World');
+\`\`\`
+
+- List item 1
+- List item 2
+
+> This is a blockquote
+
+| Column 1 | Column 2 |
+|----------|----------|
+| Value 1 | Value 2 |
+`
+
+ let tempDir
+ let outputPath
+
+ beforeEach(function () {
+ tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'pdf-test-'))
+ outputPath = path.join(tempDir, 'test-output.pdf')
+ })
+
+ afterEach(function () {
+ // Clean up temp files
+ if (fs.existsSync(outputPath)) {
+ fs.unlinkSync(outputPath)
+ }
+ if (fs.existsSync(tempDir)) {
+ fs.rmdirSync(tempDir)
+ }
+ })
+
+ it('should convert markdown to PDF successfully', async function () {
+ this.timeout(30000) // Increase timeout for PDF generation
+
+ const result = await convertMarkdownToPDF(testMarkdown, outputPath)
+
+ assert(result === true, 'convertMarkdownToPDF should return true on success')
+ assert(fs.existsSync(outputPath), 'PDF file should be created')
+
+ const stats = fs.statSync(outputPath)
+ assert(stats.size > 0, 'PDF file should not be empty')
+ })
+
+ it('should handle empty markdown', async function () {
+ this.timeout(30000)
+
+ const result = await convertMarkdownToPDF('', outputPath)
+
+ assert(result === true, 'Should handle empty markdown')
+ assert(fs.existsSync(outputPath), 'PDF file should be created even for empty content')
+ })
+
+ it('should handle markdown with special characters', async function () {
+ this.timeout(30000)
+
+ const specialMarkdown = `# Special Characters
+
+This has **special** characters: & < > " '
+
+\`\`\`html
+Hello & Goodbye
+\`\`\`
+`
+
+ const result = await convertMarkdownToPDF(specialMarkdown, outputPath)
+
+ assert(result === true, 'Should handle special characters')
+ assert(fs.existsSync(outputPath), 'PDF file should be created')
+ })
+
+ it('should throw error for invalid output path', async function () {
+ this.timeout(30000)
+
+ const invalidPath = '/nonexistent/directory/test.pdf'
+
+ try {
+ await convertMarkdownToPDF(testMarkdown, invalidPath)
+ assert.fail('Should throw error for invalid path')
+ } catch (error) {
+ assert(error instanceof Error, 'Should throw an Error')
+ assert(error.message.includes('PDF generation failed'), 'Error should mention PDF generation failure')
+ }
+ })
+ })
+})