From 87f4a57ef8ce598ed954149d14ab93a1a29ddba4 Mon Sep 17 00:00:00 2001 From: Lloyd Brookes Date: Sat, 16 Jul 2016 16:05:07 +0100 Subject: [PATCH] refactor.. added CliView.. removed cli-tool.. --- example/features/vanilla.js | 65 ++++++++++++++ lib/cli-data.js | 2 +- lib/local-web-server.js | 204 ++++++++++++++++++++++++++------------------ package.json | 1 - 4 files changed, 187 insertions(+), 85 deletions(-) create mode 100644 example/features/vanilla.js diff --git a/example/features/vanilla.js b/example/features/vanilla.js new file mode 100644 index 0000000..1617454 --- /dev/null +++ b/example/features/vanilla.js @@ -0,0 +1,65 @@ +'use strict' + +class Yeah { + middleware () { + return function (req, res, next) { + res.end('Yeah?') + next() + } + } +} + +class Logger { + middleware () { + const express = require('express') + const app = express() + app.use((req, res, next) => { + console.log('incoming', req.url) + next() + }) + return app + } +} + +class Header { + middleware () { + return function (req, res, next) { + res.setHeader('x-pointless', 'yeah?') + next() + } + } +} + +class PieHeader { + middleware () { + const Koa = require('koa') + const app = new Koa() + app.use((ctx, next) => { + ctx.set('x-pie', 'steak and kidney') + next() + }) + return app.callback() + } +} + +const http = require('http') +const server = http.createServer() +server.listen(8100) +const yeah = new Yeah() +const logger = new Logger() +const header = new Header() +const pie = new PieHeader() +const stack = [ + logger.middleware(), + header.middleware(), + pie.middleware(), + yeah.middleware() +] +server.on('request', function (req, res) { + let index = 0 + function processNext () { + const mw = stack[index++] + if (mw) mw(req, res, processNext) + } + processNext() +}) diff --git a/lib/cli-data.js b/lib/cli-data.js index c7a3e96..bbe4605 100644 --- a/lib/cli-data.js +++ b/lib/cli-data.js @@ -5,7 +5,7 @@ exports.optionDefinitions = [ }, { name: 'stack', type: String, multiple: true, - description: 'Middleware stack.', group: 'server' + description: 'Feature stack.', group: 'server' }, { name: 'key', type: String, typeLabel: '[underline]{file}', group: 'server', diff --git a/lib/local-web-server.js b/lib/local-web-server.js index be19d98..7672894 100644 --- a/lib/local-web-server.js +++ b/lib/local-web-server.js @@ -1,7 +1,6 @@ #!/usr/bin/env node 'use strict' const path = require('path') -const CommandLineTool = require('command-line-tool') const flatten = require('reduce-flatten') const arrayify = require('array-back') const ansi = require('ansi-escape-sequences') @@ -10,44 +9,68 @@ const ansi = require('ansi-escape-sequences') * @module local-web-server */ -const tool = new CommandLineTool() + +class CliView { + constructor (options) { + this.options = options || {} + } + show (key, value) { + if (key && value) { + const ansi = require('ansi-escape-sequences') + const tableLayout = require('table-layout') + const output = tableLayout({ key: ansi.format(key, 'bold'), value: value}, { + padding: { left: '', right: ' ' }, + columns: [ + { name: 'key', width: 18 }, + { name: 'value', nowrap: true } + ] + }) + process.stderr.write(output) + } else { + console.error(key) + } + } + verbose (key, value) { + if (this.options.verbose) { + this.show(key, value) + } + } + error (msg) { + console.error(ansi.format(msg, 'red')) + } +} /** * @alias module:local-web-server * @extends module:middleware-stack */ class LocalWebServer { + + /** + * @param [options] {object} - Server options + * @param [options.port} {number} - Port + * @param [options.stack} {string[]|Features[]} - Port + */ constructor (initOptions) { initOptions = initOptions || {} const commandLineArgs = require('command-line-args') const commandLineUsage = require('command-line-usage') const cli = require('../lib/cli-data') - const loadConfig = require('config-master') - const stored = loadConfig('local-web-server') + this.view = new CliView() - /* manually scan for any --stack passed, as we may need to display stack options */ - const stackPaths = arrayify(initOptions.stack || stored.stack) || [] - const stackIndex = process.argv.indexOf('--stack') - if (stackIndex > -1) { - for (var i = stackIndex + 1; i < process.argv.length; i++) { - const stackPath = process.argv[i] - if (/^-/.test(stackPath)) { - break - } else { - stackPaths.push(stackPath) - } - } - } + /* get stored config */ + const loadConfig = require('config-master') + const stored = loadConfig('local-web-server') - /* if the user did not supply a stack, use the default */ - if (!stackPaths.length) stackPaths.push(path.resolve(__dirname, '..', 'node_modules', 'local-web-server-default-stack')) + /* read the config and command-line for feature paths */ + const featurePaths = parseFeaturePaths(initOptions.stack || stored.stack) - /* build the stack */ - const stackModules = buildStack(stackPaths, this.onVerbose.bind(this), this.onDebug.bind(this)) + /* load features and build the middleware stack */ + const features = this._buildFeatureStack(featurePaths) - /* gather stack option definitions and parse the command line */ - const middlewareOptionDefinitions = stackModules + /* gather feature optionDefinitions and parse the command line */ + const featureOptionDefinitions = features .filter(mw => mw.optionDefinitions) .map(mw => mw.optionDefinitions()) .reduce(flatten, []) @@ -57,20 +80,23 @@ class LocalWebServer { return def }) - const usage = commandLineUsage(cli.usage(middlewareOptionDefinitions)) + const usage = commandLineUsage(cli.usage(featureOptionDefinitions)) let options = {} - const allOptionDefinitions = cli.optionDefinitions.concat(middlewareOptionDefinitions) + const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions) if (!initOptions.testMode) { try { options = commandLineArgs(allOptionDefinitions) } catch (err) { - tool.printError(err) - tool.printError(allOptionDefinitions.map(def => { - return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}` - }).join('\n')) - console.error(usage) - tool.halt() + this.view.error(err.toString()) + if (err.name === 'DUPLICATE_NAME') { + this.view.error('\nOption Definitions:') + this.view.error(allOptionDefinitions.map(def => { + return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}` + }).join('\n')) + } + this.view.show(usage) + process.exit(1) } } @@ -84,33 +110,38 @@ class LocalWebServer { options.misc ) + this.view.options.verbose = options.verbose + /** * Config * @type {object} */ this.options = options - stackModules + this.features = features + + features .filter(mw => mw.on) .forEach(mw => { - mw.on('verbose', this.onVerbose.bind(this)) - mw.on('debug', this.onDebug.bind(this)) + mw.on('verbose', this.view.verbose.bind(this.view)) + mw.on('debug', this.view.verbose.bind(this.view)) }) /* --config */ if (options.config) { - tool.stop(JSON.stringify(options, null, ' '), 0) + this.view.show(JSON.stringify(options, null, ' ')) + process.exit(0) /* --version */ } else if (options.version) { const pkg = require(path.resolve(__dirname, '..', 'package.json')) - tool.stop(pkg.version) + this.view.show(pkg.version) + process.exit(0) /* --help */ } else if (options.help) { - tool.stop(usage) - } else { - this.stack = stackModules + this.view.show(usage) + process.exit(0) } } @@ -120,7 +151,7 @@ class LocalWebServer { const compose = require('koa-compose') const convert = require('koa-convert') - const middlewareStack = this.stack + const middlewareStack = this.features .filter(mw => mw.middleware) .map(mw => mw.middleware(this.options)) .reduce(flatten, []) @@ -162,31 +193,51 @@ class LocalWebServer { server = http.createServer(app.callback()) } - const tableLayout = require('table-layout') - server.listen(options.port) if (onListening) server.on('listening', onListening) if (!options.testMode) { - server.on('listening', function () { + server.on('listening', () => { const ipList = getIPList() .map(iface => `[underline]{${server.isHttps ? 'https' : 'http'}://${iface.address}:${options.port}}`) .join(', ') - console.error(ansi.format('Serving at', 'bold'), ansi.format(ipList)) + this.view.show('Serving at', ansi.format(ipList)) }) } return server } - onVerbose (title, msg) { - if (this.options.verbose) { - console.error(ansi.format(title, 'bold'), msg) - } - } - onDebug (title, msg) { - if (this.options.debug) { - console.error(ansi.format(title, 'bold'), msg) - } + _buildFeatureStack (featurePaths) { + return featurePaths + .map(featurePath => loadStack(featurePath)) + .map(Feature => new Feature()) + .map(module => { + if (module.stack) { + const featureStack = module.stack() + .map(Feature => new Feature()) + .map(feature => { + if (feature.on) { + feature.on('verbose', this.view.verbose.bind(this.view)) + feature.on('debug', this.view.verbose.bind(this.view)) + } + return feature + }) + + module.optionDefinitions = function () { + return featureStack + .map(feature => feature.optionDefinitions && feature.optionDefinitions()) + .filter(definitions => definitions) + .reduce(flatten, []) + } + module.middleware = function (options) { + return featureStack + .map(feature => feature.middleware(options)) + .reduce(flatten, []) + .filter(mw => mw) + } + } + return module + }) } } @@ -246,37 +297,24 @@ function getIPList () { return ipList } -function buildStack (stackPaths, onVerbose, onDebug) { - return stackPaths - .map(stackPath => loadStack(stackPath)) - .map(Middleware => new Middleware()) - .map(module => { - if (module.stack) { - const featureStack = module.stack() - .map(Feature => new Feature()) - .map(feature => { - if (feature.on) { - feature.on('verbose', onVerbose) - feature.on('debug', onDebug) - } - return feature - }) - - module.optionDefinitions = function () { - return featureStack - .map(feature => feature.optionDefinitions && feature.optionDefinitions()) - .filter(definitions => definitions) - .reduce(flatten, []) - } - module.middleware = function (options) { - return featureStack - .map(feature => feature.middleware(options)) - .reduce(flatten, []) - .filter(mw => mw) - } +/* manually scan for any --stack passed, as we may need to display stack options */ +function parseFeaturePaths (configStack) { + const featurePaths = arrayify(configStack) + const featureIndex = process.argv.indexOf('--stack') + if (featureIndex > -1) { + for (var i = featureIndex + 1; i < process.argv.length; i++) { + const featurePath = process.argv[i] + if (/^-/.test(featurePath)) { + break + } else { + featurePaths.push(featurePath) } - return module - }) + } + } + + /* if the user did not supply a stack, use the default */ + if (!featurePaths.length) featurePaths.push(path.resolve(__dirname, '..', 'node_modules', 'local-web-server-default-stack')) + return featurePaths } module.exports = LocalWebServer diff --git a/package.json b/package.json index 464552a..347e776 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,6 @@ "ansi-escape-sequences": "^2.2.2", "array-back": "^1.0.3", "command-line-args": "^3.0.0", - "command-line-tool": "~0.4.0", "command-line-usage": "^3.0.3", "config-master": "^2.0.3", "koa": "^2.0.0",