const path = require('path'); const cheerio = require('cheerio'); const showdown = require('showdown'); const Repository = require('github-api/dist/components/Repository'); const { readdir, readFile, writeFile } = require('graceful-fs'); const imagemin = require('imagemin'); const imageminJpegtran = require('imagemin-jpegtran'); const imageminPngquant = require('imagemin-pngquant'); const CIInfo = require('ci-info'); const converter = new showdown.Converter(); const templateFilePath = './.operations/res/template.html'; const imageminOpts = { plugins: [ imageminJpegtran(), imageminPngquant({ quality: '65-80' }) ] }; console.info(`Working in [${process.cwd()}]`); const { isCI } = CIInfo; const { GITHUB_TOKEN, OWNER_AND_REPO, BRANCH, IS_PR, IS_FORK } = getConfigFromEnv(); readDirPromise('./') .then(async (fileNames) => { const indexFileNames = fileNames.filter(fn => fn.includes('README.') && fn.includes('.md')); for (let fileName of indexFileNames) { const startTime = new Date(); console.info(`Beginning Generate Document [${fileName}] at [${startTime.toISOString()}]`); try { const templateHTML = await readFilePromise(templateFilePath); const processedTemplateHTML = await inlineResources(templateHTML); const outputHTML = await processMDFile(fileName, processedTemplateHTML); console.info(`Completed Generation in [${computeElapsedTime(startTime)}s]`); const outFileName = path.parse(fileName).name + '.html'; const outFilePath = path.join('.operations', 'out', outFileName); console.info(`Writing output to [${outFilePath}]`); await writeFilePromise(outFilePath, outputHTML); if(shouldUpdateGitHubPages()) { const repo = new Repository(OWNER_AND_REPO, { token: GITHUB_TOKEN }); console.info(`Committing HTML file to branch [gh-pages]`); await repo.writeFile('gh-pages', outFileName, outputHTML, ':loudspeaker: :robot: Automatically updating built HTML file', {}); } } catch (err) { console.error(`Failed to generate from [${fileName}] in [${computeElapsedTime(startTime)}s]`, err); process.exit(1); } } }) .then(() => { console.log(`🎉 Finished gen-html 🎉`); }) function getConfigFromEnv() { if (CIInfo.GITHUB_ACTIONS) { return getConfigFromGithubActionEnv() } return process.env; } function getConfigFromGithubActionEnv() { const config = { ...process.env, OWNER_AND_REPO: process.env.GITHUB_REPOSITORY, IS_PR: CIInfo.IS_PR !== null ? CIInfo.IS_PR : process.env.GITHUB_EVENT_NAME === 'pull_request', // We assume we're in PR and and we get the source for the PR BRANCH: process.env.GITHUB_HEAD_REF, }; if(!config.IS_PR) { // GITHUB_REF example: `refs/heads/main` config.BRANCH = process.env.GITHUB_REF.substring('refs/heads/'.length); } return config; } function shouldUpdateGitHubPages() { return isCI && !IS_FORK && !IS_PR && BRANCH === 'master'; } function computeElapsedTime(startTime) { return (Date.now() - startTime) / 1000; } async function processMDFile(filePath = '/', templateHTML = null) { let mdSrc; try { mdSrc = await readFilePromise(filePath); } catch (err) { console.warn(`Failed to read file [${filePath}], does it exist?`); return ''; } const generatedHTML = converter.makeHtml(mdSrc); let nexHTML = generatedHTML; if (templateHTML) { const $ = cheerio.load(templateHTML); $('.content').html(generatedHTML); nexHTML = $.html(); } const fileDir = path.parse(filePath).dir.replace(process.cwd(), '/') || '/'; console.log(`Processing file [${filePath}]`); const outHtml = await ( inlineLocalReferences(nexHTML, fileDir) .then((html) => fixMdReferences(html)) .then((html) => fixHashAs(html)) .then((html) => inlineAssets(html, fileDir)) ); return outHtml; } const internalRefRegExp = /^((?!http)(?!#)(?!\/\/).)*$/; // Doesn't start with 'http', '//', or '#' async function inlineLocalReferences(html, filePath = '/') { const $ = cheerio.load(html); const as = $('a'); const internalAs = as.toArray().filter((a) => internalRefRegExp.test(a.attribs.href) && !a.attribs.href.includes('README')); const processedInternalRefs = await Promise.all( internalAs.map((a) => processMDFile(path.resolve(filePath, a.attribs.href))) ); processedInternalRefs.forEach((processedHTML, index) => { const originalA = $(internalAs[index]); const contentId = originalA.text().replace(/[^A-Za-z0-9]/g, '_'); $('.references').append([ $('