UNPKG

live-markdown

Version:

21st century markdown

135 lines (122 loc) 5.21 kB
#!/usr/bin/env node const { readFile, existsSync, writeFile, mkdirSync, createWriteStream } = require("fs"); const puppeteer = require('puppeteer'); const chalk = require('chalk'); const GIFEncoder = require('gifencoder'); const rimraf = require("rimraf"); const PNG = require('png-js'); const args = require('yargs').argv; const VIEWPORT_WIDTH = 1024 const source = args.s; const destination = args.d; const folder = args.f || "readme-pics/"; function uuid() { return Math.random().toString(36).substr(2, 9); } function decode(png) { return new Promise(r => { png.decode(pixels => r(pixels)) }); } async function gifAddFrame(page, encoder, clip) { const pngBuffer = await page.screenshot({ omitBackground: true, clip: { ...clip, width: VIEWPORT_WIDTH } }); const png = new PNG(pngBuffer); await decode(png).then(pixels => encoder.addFrame(pixels)); } if (!source) { console.log(chalk.red("Error: You must define a source md file (livemd -s README.md)")); return; } if (source && !source.includes(".md")) { console.log(chalk.red("Error: Source must be an md file (README.md)")); return; } if (!existsSync(source)) { console.log(chalk.red(`Error: File ${source} not fould`)); return; } if (folder) { if (existsSync(folder)) { rimraf(folder, function () { mkdirSync(folder); GenerateLiveMarkdown(); }); } else { mkdirSync(folder); GenerateLiveMarkdown(); } } function GenerateLiveMarkdown() { readFile(source, 'utf8', (err, data) => { let content = data; let newContent = ""; puppeteer.launch().then(async browser => { const page = await browser.newPage(); await page.setContent(content, { waitUntil: "networkidle0" }); const children = await page.evaluate((folder, uuid) => { let ffolder = ""; if (folder) { ffolder = folder.slice(-1) === "/" ? folder : `${folder}/`; } return Array.from(document.body.children).map(child => { const duration = Number(getComputedStyle(child).animationDuration.split("s")[0]) * 30; const extension = duration ? "gif" : "png"; return ({ path: `${ffolder}${child.tagName.toLowerCase()}${uuid}.${extension}`, outerHtml: child.outerHTML, duration, clip: { width: child.getBoundingClientRect().width, height: child.getBoundingClientRect().height, x: child.getBoundingClientRect().x, y: child.getBoundingClientRect().y, } }) }) }, folder, uuid()); newContent = await page.evaluate(() => { Array.from(document.body.querySelectorAll("style")).map(style => style.remove()); return document.body.innerHTML }); for (const child of children) { if (child.duration) { const encoder = new GIFEncoder(1024, child.clip.height); encoder.createWriteStream().pipe(createWriteStream(child.path)); encoder.start() encoder.setRepeat(0); encoder.setFrameRate((child.duration / 30) * 2); encoder.setTransparent(0x0000FF); encoder.setQuality(10); for (let i = 1; i <= child.duration; i++) { process.stdout.write(`Generating gif: ${chalk.green(child.path + " " + Math.round(i * 100 / child.duration) + "%")}`); process.stdout.clearLine(); process.stdout.cursorTo(0); process.stdout.write(`Generating gif: ${chalk.green(child.path + " " + Math.round(i * 100 / child.duration) + "%")}`); await gifAddFrame(page, encoder, child.clip); } encoder.finish(); } else { process.stdout.write(`Generating png: ${chalk.green(child.path + " 100%")} \n`); await page.screenshot({ path: child.path, omitBackground: true, clip: { ...child.clip, width: VIEWPORT_WIDTH }, }); } newContent = newContent.replace(new RegExp(`${child.outerHtml}`, "gm"), `<img src="${child.path}" />`) await writeFile(`${destination || (source.split(".md")[0] + 1 + ".md")}`, newContent, 'utf8', function (err) { if (err) throw err; }); } await browser.close(); }); }); }