UNPKG

makestatic-core

Version:

Generic file processing library

415 lines (360 loc) 8.37 kB
const path = require('path') const sequence = require('when/sequence') const when = require('when') const Config = require('./lib/config') const Context = require('./lib/context') const Phase = require('./lib/phase') /** * Core library for the processing lifecycle. * * @class LifeCycle */ class LifeCycle { /** * Creates a new LifeCycle. * * @constructor LifeCycle * @param {Object} options the processing options. */ constructor (options = {}) { this._config = new Config(options) } /** * The lifecycle configuration. * * @property {Config} config * @member LifeCycle * @readonly */ get config () { return this._config } /** * The processing context for this lifecycle. * * @property {Context} context * @member LifeCycle * @readonly */ get context () { return this._context } /** * List of standard lifecycle phases. * * @property {Array} main * @member LifeCycle * @readonly */ get main () { return standard } /** * List of deploy lifecycle phases. * * @property {Array} deploy * @member LifeCycle * @readonly */ get deploy () { return [Audit, Deploy] } /** * Builds the list of phases to execute. * * @function getPlugins * @member LifeCycle * @param {Object} argv map of option overrides. * * @returns array list of phase plugins. */ getPlugins (argv = {}) { // configure initial phases let plugins = [Clean, Build, Load] // using pack phase if (argv.pack !== false) { plugins.push(Pack) // if we are packing the webpack plugin is responsible // for executing the rest of the lifecycle return plugins } plugins = plugins.concat(standard) // must write out files plugins.push(Write) // add deploy phases plugins = plugins.concat(this.deploy) return plugins } /** * Abstract method to get a default lifecycle configuration. * * @abstract * @function getLifecycleConfig * @member LifeCycle * @param {Object} opts map of computed options. * @param {Object} argv map of option overrides. * * @returns object map of lifecycle phase configurations. */ getLifecycleConfig (opts = {}/*, argv = {} */) { return opts.lifecycle || {} } /** * Process the entire lifecycle. * * This method will set the computed options on the `config` based on * the passed `argv` options merged with the options specified when * the instance was created. * * It will then merge the result of calling `getLifecycleConfig()` into the * `lifecycle` property of the computed options before retrieving the * list of plugin phases to execute returned by `getPlugins()`. * * Finally it creates a processing `context` for this lifecycle before * deferring execution to the `run()` method. * * @function process * @member LifeCycle * @param {Object} argv map of option overrides. * @param {Array} phases list of phases to execute. * * @returns a promise that resolves when all phases have completed. */ process (argv = {}, phases = null) { // configure options let opts try { opts = this.config.setComputedOptions(argv) } catch (e) { return when.reject(e) } // get the lifecycle configuration let config = this.getLifecycleConfig(opts, argv) opts.lifecycle = config let lifecycle = phases || this.getPlugins(argv) // configure processing context this._context = new Context(this, this.config) this._phases = [] return this.run(lifecycle) } /** * Name of the currently executing phase. * * @property {String} phase * @member LifeCycle * @readonly */ get phase () { return this._phase } /** * List of phases that have already executed. * * @property {Array} phases * @member LifeCycle * @readonly */ get phases () { return this._phases } /** * Run lifecycle processing phases. * * @function run * @member LifeCycle * @param {Array} phases list of phases to execute. * * @returns a promise that resolves when all phases have completed. */ run (phases) { const context = this.context const opts = context.options // execute the phases phases = phases.map((Plugin) => { let plugin = new Plugin(opts) return () => { return plugin.validate(context) .then(() => { let name = plugin.constructor.name this._phase = name.toLowerCase() //console.log(this._phase) return plugin.process(context, opts, name) .catch((e) => { throw e }) }) } }) return sequence(phases) .then(() => context) } } /** * Clean output files. * * @private {class} Clean * @inherits Phase */ class Clean extends Phase {} /** * Build source files. * * @private {class} Build * @inherits Phase */ class Build extends Phase {} /** * Loads source files from disc. * * @private {class} Load * @inherits Phase */ class Load extends Phase { /** * Validates that a `root` directory has been specified. * * @private {function} validate * @member Load */ validate (context) { return when.promise((resolve, reject) => { if (!context.options.root) { return reject(new Error(`root directory is required`)) } resolve() }) } } /** * Package source files, typically this would mean compiling with webpack. * * @private {class} Pack * @inherits Phase */ class Pack extends Phase {} /** * Parse source files to ensure there is an abstract syntax tree. * * @private {class} Parse * @inherits Phase */ class Parse extends Phase {} /** * Create a graph of the site resources for the compiled output files. * * @private {class} Graph * @inherits Phase */ class Graph extends Phase {} /** * Perform transformations on the parsed abstract syntax trees. * * @private {class} Transform * @inherits Phase */ class Transform extends Phase {} /** * Verify document abstract syntax trees. * * @private {class} Verify * @inherits Phase */ class Verify extends Phase {} /** * Resolve the output path for a file and seal the file contents. * * @private {class} Resolve * @inherits Phase */ class Resolve extends Phase {} /** * Validate output files. * * Typically used to validate HTML files but you can add any plugins you want * to this phase. * * @private {class} Validate * @inherits Phase */ class Validate extends Phase {} /** * Optimize compiled assets. * * @private {class} Optimize * @inherits Phase */ class Optimize extends Phase {} /** * Emit additional assets such as static gz files. * * @private {class} Emit * @inherits Phase */ class Emit extends Phase {} /** * Generate file manifest. * * @private {class} Manifest * @inherits Phase */ class Manifest extends Phase { /** * Validates the `manifest` option. * * @private {function} validate * @member Load */ validate (context) { const opts = context.options // configure manifest file output path if (opts.manifest) { let manifest = opts.manifest if (opts.manifest === true) { manifest = 'manifest.json' } if (opts.manifest && typeof manifest !== 'string') { return when.reject( new Error(`manifest file path should be a string`)) } // cannot set path, must be basename as is always written to // the output directory manifest = path.basename(manifest) opts.manifest = manifest } return when.resolve(opts) } } /** * Write files to disc. * * @private {class} Write * @inherits Phase */ class Write extends Phase {} /** * Audit files prior to deployment. * * @private {class} Audit * @inherits Phase */ class Audit extends Phase {} /** * Deploy files to a provider. * * @private {class} Deploy * @inherits Phase */ class Deploy extends Phase {} // standard plugin phases const standard = [ Parse, Graph, Transform, Verify, Resolve, Validate, Optimize, Emit, Manifest ] module.exports = LifeCycle