import crypto from 'crypto' import scopeCss from '@patarapolw/scope-css' import cheerio from 'cheerio' import ejs from 'ejs' import jieba from 'nodejieba' import showdown from 'showdown' export class MakeHTML { $ = cheerio.load('') mdConverter = new showdown.Converter({ noHeaderId: true, parseImgDimensions: true, simplifiedAutoLink: true, // literalMidWordUnderscores: true, strikethrough: true, tables: true, tasklists: true, // simpleLineBreaks: true, openLinksInNewWindow: true, backslashEscapesHTMLTags: true, emoji: true, underline: true, metadata: true, }) constructor() { /** * ! For some reasons default underscore italic fails to render... */ this.mdConverter.addExtension( { type: 'lang', regex: /([^\\])_{1}(.*?[^\\])_{1}/g, replace: '$1$2', }, 'underscore-italic' ) } async parse( filename: string ): Promise<{ $: cheerio.Cheerio html(): string clean(): string }> { const id = 'md-' + crypto .createHash('sha256') .update(filename) .digest() .toString('hex') .substr(8) const $div = this.$(`
`) $div.html(await this.getHTML(filename)) $div.find('style').each((_, el) => { const $el = this.$(el) const html = $el.html() || '' if (html.trim()) { $el.html(scopeCss(html, `#${id}`)) } }) return { $: $div, html() { return $div.html() || '' }, clean() { return jieba.cutForSearch($div.text() || '').join(' ') }, } } private async getHTML(filename: string) { return this.mdConverter.makeHtml( await ejs.renderFile(filename, { youtube: (id: string) => this.$('
') .html( /* html */ `
` ) .html(), }) ) } }