UNPKG

makestatic-core

Version:

Generic file processing library

202 lines (166 loc) 5.06 kB
const when = require('when') const sequence = require('when/sequence') const mm = require('micromatch') class Phase { constructor (opts = {}) { this.conf = opts.lifecycle[this.name] } /** * Abstract function to validate a lifecycle phase. */ validate (context = {}) { return when.resolve(context) } /** * Get a human display name for the phase. */ get name () { return this.constructor.name.toLowerCase() } run (key, context, plugins) { // const log = context.log let output = [] // log.info( // '[%s] plugin: %s, files: %s', key, plugins.length, context.files.length) return when.map(context.files, (file) => { if (!file || (file && !file.name)) { throw new Error(`bad file name input`) } // iterate and execute plugins for the file return sequence(when.map(plugins, (plugin) => { // closure to get the plugin promise return () => { // run plugin test patterns, if there is no match exclude the file if (plugin.test && !plugin.test.test(file.name)) { return when.resolve(file) // if the file is explicitly excluded using a pattern in the exclude // array, ignore it } else { if (Array.isArray(plugin.exclude)) { for (let i = 0; i < plugin.exclude.length; i++) { if (plugin.exclude[i].test(file.name)) { return when.resolve(file) } } } } if (typeof plugin.instance.sources === 'function') { // pass context to the plugin // options can be accessed via the context return plugin.instance.sources( file, context, plugin.options) } return when.resolve(file) } })) .then(() => { return context }) }, output) } hooks (key, context, plugins) { plugins = plugins.map((plugin) => { // closure to get the plugin promise return () => { if (typeof plugin.instance[key] === 'function') { // pass context to the plugin // options can be accessed via the context return plugin.instance[key](context, plugin.options) } return when.resolve() } }) return sequence(plugins) } before (context, plugins) { return this.hooks(this.before.name, context, plugins) } sources (context, plugins) { return this.run(this.sources.name, context, plugins) } after (context, plugins) { return this.hooks(this.after.name, context, plugins) } getPlugins (context) { let defs = this.conf let plugins = [] let fields = ['plugin', 'test', 'exclude', 'options'] if (!defs) { return plugins } if (defs && !Array.isArray(defs)) { defs = [defs] } plugins = defs.map((def) => { // wrap function pointers if (typeof def === 'function') { def = {plugin: def} } if (!def.plugin || typeof def.plugin !== 'function') { throw new Error(`plugin constructor function expected`) } def.exclude = def.exclude || def.plugin.exclude def.test = def.test || def.plugin.test || /.*/ def.options = def.options || def.plugin.options || {} // options inheritance shortcut for (let k in def) { if (!~fields.indexOf(k)) { def.options[k] = def[k] } } // explicit list of files to exclude from processing if (Array.isArray(def.exclude)) { def.exclude = def.exclude.map((ex) => { if (!(ex instanceof RegExp) && typeof ex !== 'string') { throw new Error(`exclude pattern must be regexp or string`) } let ptn = ex if (typeof ptn === 'string') { ptn = mm.makeRe(ptn) } return ptn }) } // pre-compile glob patterns for the plugin if (typeof def.test === 'string') { def.test = mm.makeRe(def.test) } def.instance = new def.plugin(context, def.options) // eslint-disable-line return def }) return plugins } process (context, options, name) { let plugins let timer let phase = name.toLowerCase() if (context.options.profile) { timer = context.log.time(`[${phase}]`) } try { plugins = this.getPlugins(context) } catch (e) { return when.reject(e) } // console.log('[%s] %s', this.name, plugins.length); // no plugins for this phase if (!plugins.length) { return when.resolve(context) } // 1. before hooks // 2. file processing // 3. after hooks return when.resolve() .then(() => this.before(context, plugins)) .then(() => this.sources(context, plugins)) .then(() => this.after(context, plugins)) .then(() => { context.lifecycle.phases.push(phase) if (timer) { timer() } }) } } module.exports = Phase