UNPKG

roosevelt

Version:

🧸 MVC web framework for Node.js designed to make Express easier to use.

108 lines (93 loc) 5 kB
// static page generator require('@colors/colors') const process = require('process') const fs = require('fs-extra') const path = require('path') const { walk } = require('@nodelib/fs.walk/promises') const htmlMinifier = require('html-minifier-terser').minify const gitignoreScanner = require('./tools/gitignoreScanner') const wildcardMatch = require('./tools/wildcardMatch') module.exports = async app => { const params = app.get('params') const appName = app.get('appName') const htmlPath = app.get('htmlPath') const htmlRenderedOutput = app.get('htmlRenderedOutput') const expressValidator = require('express-html-validator')(params.htmlValidator) const minifyOptions = app.get('params').html.minifier.options const logger = app.get('logger') const fsr = require('./tools/fsr')(app) const gitignoreFiles = gitignoreScanner(path.join(app.get('appDir'), '.gitignore')) const allowlist = app.get('params').html.allowlist const blocklist = app.get('params').html.blocklist // skip parsing static pages if feature is disabled or makeBuildArtifacts is false if (!params.html.sourcePath || !params.makeBuildArtifacts) return // change process directory to the statics pages directory so that templates located there can reference other templates using relative paths const oldDir = process.cwd() if (fs.pathExistsSync(params.html.sourcePath)) process.chdir(params.html.sourcePath) // process each html file if (fs.existsSync(htmlPath)) { for (let file of await walk(htmlPath)) { // handle cases where file is an object provided by fsWalk file = file.path || file // filter out irrelevant files if (file !== '.' && file !== '..' && !gitignoreFiles.includes(path.basename(file)) && !gitignoreFiles.includes(file) && !fs.lstatSync(file).isDirectory()) { try { const baseFile = file.replace(app.get('params').html.sourcePath + path.sep, '') if ((allowlist && allowlist.length > 0 && !wildcardMatch(baseFile, allowlist)) || (blocklist && blocklist.length > 0 && wildcardMatch(baseFile, blocklist))) { // skip this file if it's not on the allowlist // but only if an allowlist exists // also skip it if it's on the blocklist continue } let extension = file.split('.') extension = extension[extension.length - 1] if (extension === 'js' || extension === 'json') continue const renderer = app.get('view: ' + extension) if (!renderer) { logger.error(`${appName} failed to parse ${file}. There is no view engine for file type "${extension}" registered with the app.`) continue } let modelFile = file.slice(0, file.length - extension.length) + 'js' if (!fs.pathExistsSync(modelFile)) modelFile = file.slice(0, file.length - extension.length) + 'json' let modelData = app.get('htmlModels')[path.normalize(file.replace(app.get('htmlPath') + path.sep, ''))] if (!modelData && fs.pathExistsSync(modelFile)) { modelData = require(modelFile) if (typeof modelData === 'function') modelData = modelData(app) else logger.error(`${appName} failed to load ${modelFile} model. Please ensure that it is coded correctly.`) } const model = modelData || {} let newHtml = renderer(baseFile, model) // construct destination for rendered html const { name, dir } = path.parse(file) let content const outpath = path.join(htmlRenderedOutput, dir.replace(htmlPath, ''), `${name}.html`) // minify the html if minification is enabled if (params.minify && params.html.minifier.enable) { newHtml = await htmlMinifier(newHtml, minifyOptions) } // validate the html if the validator is enabled if (params.htmlValidator.enable) { newHtml = await expressValidator(newHtml) } // check if html file already exists fsr.ensureDirSync(htmlRenderedOutput) if (fs.pathExistsSync(outpath)) { content = fs.readFileSync(outpath, 'utf8') } // check existing file for matching content before writing if (newHtml !== '' && (!content || content !== newHtml)) { fsr.writeFileSync(outpath, newHtml, ['📝', `${appName} writing new HTML file ${outpath}`.green]) if (params.htmlValidator.enable && newHtml.includes('<title>HTML did not pass validation</title>') && newHtml.includes('<code class="validatorErrors">')) { logger.error(`↳ The file has HTML validation errors. Open ${outpath} in your browser to see the details.`) } } continue } catch (e) { logger.error(`${appName} failed to parse ${file}. Please ensure that it is coded correctly.`) logger.error(e) } } } } process.chdir(oldDir) }