UNPKG

cache-pug-templates

Version:

Cache Pug templates for Lad/Koa/Express/Connect

187 lines (159 loc) 5.07 kB
const fs = require('fs'); const path = require('path'); const pug = require('pug'); const debug = require('debug')('cache-pug-templates'); const _ = require('lodash'); const rateLimit = require('function-rate-limit'); // <https://github.com/pugjs/pug/issues/3065> for (const [key, value] of Object.entries(pug.runtime)) { pug[key] = value; } class CachePugTemplates { constructor(config) { this.config = { app: false, views: false, logger: console, callback: false, cache: true, concurrency: 1, interval: 500, ...config }; this.queuedFiles = []; // legacy support for `views` being a String if (_.isString(this.config.views)) this.config.views = [this.config.views]; // // detect if we're using koa or express/connect // if (this.config.app) { // koa if (_.isObject(this.config.app.context)) { debug('detected koa'); // ensure that views is defined and is a string if (_.isArray(this.config.views) && _.isEmpty(this.config.views)) throw new Error( '`views` directory argument must be provided for koa' ); } else { // // express/connect // debug('detected express/connect'); // only continue if pug is the view engine if (this.config.app.get('view engine') !== 'pug') { const errorMessage = `view engine was "${this.config.app.get( 'view engine' )}" and needs to be set to "pug"`; throw new Error(errorMessage); } // views is always defined (defaults to `/views`) if (!_.isArray(this.config.views)) this.config.views = []; this.config.views.push(this.config.app.get('views')); } } this.config.views = _.uniq(this.config.views); debug(this.config); this.writeCache = this.writeCache.bind(this); this.cacheFile = this.cacheFile.bind(this); this.getFilename = this.getFilename.bind(this); this.cacheDirectory = this.cacheDirectory.bind(this); this.start = this.start.bind(this); this.rateLimitedCacheFile = rateLimit( this.config.concurrency, this.config.interval, this.cacheFile.bind(this) ); } writeCache(filename) { debug(`compiling template located at ${filename}`); fs.readFile(filename, 'utf8', (err, string) => { if (err) return this.config.logger.error(err); const options = { cache: true, filename }; if (pug.cache[filename]) return this.config.logger.warn( `${filename} was already cached in pug.cache` ); // <https://github.com/pugjs/pug/issues/2206> setImmediate(() => { const template = pug.compile(string, options); if (this.config.cache) { debug(`caching ${filename}`); try { pug.cache[filename] = template; } catch (err) { this.config.logger.error(err); } } else { debug( `not caching ${filename} since \`cache\` option was set to \`false\`` ); } if (_.isFunction(this.config.callback)) this.config.callback(filename, template); }); }); } cacheFile(filename) { fs.stat(filename, (err, stat) => { if (err) return this.config.logger.error(err); if (stat.isDirectory()) { setImmediate(() => { this.cacheDirectory(filename); }); return; } debug(`checking ${filename}`); if (path.extname(filename) !== '.pug') { debug(`${filename} did not have ".pug" extension`); return; } if (pug.cache[filename]) { debug(`${filename} was already cached in pug.cache`); return; } setImmediate(() => { this.writeCache(filename); }); }); } getFilename(dir) { return (file) => path.join(dir, file); } cacheDirectory(dir) { fs.readdir(dir, (err, files) => { if (err) return this.config.logger.error(err); for (const file of files) { setImmediate(() => { const filename = this.getFilename(dir)(file); this.rateLimitedCacheFile(filename); }); } }); } start() { debug('pre-caching pug views'); if (this.config.app) { // koa if (_.isObject(this.config.app.context)) { // only continue if `env` is production // or if `app.cacheViews = true` is set if (this.config.app.env !== 'production' && !this.config.app.cache) { debug('koa env was not production and app.cache not set'); return; } } else if (!this.config.app.enabled('view cache')) { // only continue if caching is enabled debug('view cache was not enabled'); return; } } // cache directories for (let i = 0; i < this.config.views.length; i++) { setImmediate(() => { this.cacheDirectory(this.config.views[i]); }); } } } module.exports = CachePugTemplates;