UNPKG

blot

Version:

The DRY documentation builder

260 lines (210 loc) 6.54 kB
#! /usr/bin/env node 'use strict' var blot = require('../lib/blot').default var colors = require('colors') var path = require('path') var program = require('commander') var meta = require('../package.json') var config = {} // CLI Actions and Commands function loadConfig(env, cmd) { // override precedence (starting w/ base): // project -> flags -> object literal (--config) return new Promise(function(resolve, reject) { info('env:load', env) Object.assign(config, cmd) mapJsonConfig(cmd.config) if (blot.env.Config.isProject(env)) { // use environment project config blot.env.Config .loadProject(env, config, false) .then(function(envConfig) { info('env:name', envConfig.name) info('env:host', envConfig.host) mapEnvConfig(envConfig) resolve(envConfig) }) .catch(reject) } else { warn('env', 'no project environment defined') resolve(false) } }) } function mapJsonConfig(configObj) { // ensure JSON configuration object (when provided) overrides CLI flags try { Object.assign(config, JSON.parse(configObj)) } catch (e) { error('config override must be valid JSON: ' + e) } } function mapEnvConfig(envConfig) { // ensure run config matches environment config after processing Object.assign(config, envConfig) // map CLI flags with names different from env config... FIXME config.inFiles = envConfig.docs.src config.outFiles = envConfig.docs.dest blot.env.use(envConfig, true) } function parseFiles(files) { info('glob', 'start') return blot.apib .glob(files) .then(function(blueprints) { info('glob', 'end') blueprints.forEach(function(blueprint) { section(blueprint.compiled.markdown) }) return blueprints }) .catch(error) } function parseText(data) { info('read', 'start') return blot.apib.read(data) .then(function(blueprint) { info('read', 'end') section(blueprint.compiled.markdown) return blueprint }) .catch(error) } function compileInputs() { return new Promise(function(resolve, reject) { var curEnv = blot.env.current() var validFs = config.inFiles && config.inFiles !== 'files' var loadFs = curEnv.isProject && curEnv.docs.src if (validFs || loadFs) { parseFiles(config.inFiles) .then(resolve) .catch(reject) } else if (config.inData) { parseText(config.inData) .then(resolve) .catch(reject) } else { error('malformed command') reject(config) } }) } function renderBlueprints(blueprints) { info('render', 'start') return new Promise(function(resolve, reject) { blot.html.Document .fromBlueprints(blueprints) .then(function(docs) { return Promise.all(docs.map(function(doc) { return doc.processed //.html() })) }) .then(function(docs) { info('render', 'end') if ((docs && !docs.length) || Object.is(docs[0], null)) { warn('render', 'no resulting HTML, check `elements` configuration') } docs.forEach(section) resolve(docs) }) .catch(function(error) { error('failed to render API blueprint as HTML: ' + error) reject(error) }) }) } function exportBlueprints(blueprints) { var curEnv = blot.env.current() var validOut = !config.outFile || config.outFile == 'file' var doExport = curEnv.isProject && curEnv.docs.export && curEnv.docs.dest var outPath = doExport ? curEnv.docs.dest : config.outFile // TODO - cleanup, slop if (!doExport && validOut) return info('export.apib', 'start') blueprints.forEach(function(blueprint) { blot.apib .dest(blueprint, outPath) .then(function(content) { info('export', 'end') }) .catch(function(err) { error('failed to export ' + outPath + ': ' + err) }) }) } function exportHtml(html) { var curEnv = blot.env.current() var doExport = curEnv.isProject && curEnv.view.export && curEnv.view.dest if (!doExport) return info('export:html', 'start') new blot.html.Document(html) .dest() .then(function() { info('export:html', 'end') }) .catch(function(err) { error('failed to export ' + outPath + ': ' + err) }) } // Logging function info(action, msg) { if (config.pretty) { blot.log().info(action.magenta + ' ' + msg.green) } else if (config.log) { blot.log().info(action + ': ' + msg) } } function warn(action, msg) { if (config.pretty) { blot.log().warn(action.yellow + ' ' + msg.green) } else if (config.log) { blot.log().warn(action + ': ' + msg) } } function error(msg) { if (config.pretty || config.log) { blot.log().error(msg) } } function section(msg) { if (config.pretty) { console.log('\n--------------------------------------------------\n'.rainbow) console.log(msg.cyan) console.log('\n--------------------------------------------------\n'.rainbow) } else if (config.log) { blot.log().info(msg) } else if (config.echo) { console.log(msg) } } // Command Bindings program .version(meta.version) .option('-i, --in-files [files]', 'Source documentation files (*.blot or *.apib)', 'files') .option('-d, --in-data [md]', 'Read in plain Markdown (UTF-8)', 'markdown') .option('-o, --out-file [file]', 'Write result to file(s) (*.apib , *.html or *.json)', 'file') .option('-c, --config [obj]', 'Provide a JSON-formatted project configuration override object') .option('-v, --validate', 'Determines if provided content is a valid API Blueprint') .option('-e, --echo', 'Prints compiled result to stdout') .option('-l, --log', 'Enables logging of blot events') .option('-p, --pretty', 'Formats results into visually pleasing output') program .command('compile [env]') .description('process documentation and compiles it into a static format') .action(function(env, cmd) { loadConfig(env, cmd.parent) .then(compileInputs) .then(exportBlueprints) }) program .command('render [env]') .description('processes, compiles, and then renders documentation as HTML') .action(function(env, cmd) { process.env.NO_CACHE = 1 // TODO - remove once aglio theme cache works for NPM3 loadConfig(env, cmd.parent) .then(compileInputs) .then(renderBlueprints) .then(exportHtml) }) // TODO - query command program.parse(process.argv)