refactor.. added CliView.. removed cli-tool..
This commit is contained in:
65
example/features/vanilla.js
Normal file
65
example/features/vanilla.js
Normal file
@ -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()
|
||||||
|
})
|
@ -5,7 +5,7 @@ exports.optionDefinitions = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'stack', type: String, multiple: true,
|
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',
|
name: 'key', type: String, typeLabel: '[underline]{file}', group: 'server',
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict'
|
'use strict'
|
||||||
const path = require('path')
|
const path = require('path')
|
||||||
const CommandLineTool = require('command-line-tool')
|
|
||||||
const flatten = require('reduce-flatten')
|
const flatten = require('reduce-flatten')
|
||||||
const arrayify = require('array-back')
|
const arrayify = require('array-back')
|
||||||
const ansi = require('ansi-escape-sequences')
|
const ansi = require('ansi-escape-sequences')
|
||||||
@ -10,44 +9,68 @@ const ansi = require('ansi-escape-sequences')
|
|||||||
* @module local-web-server
|
* @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
|
* @alias module:local-web-server
|
||||||
* @extends module:middleware-stack
|
* @extends module:middleware-stack
|
||||||
*/
|
*/
|
||||||
class LocalWebServer {
|
class LocalWebServer {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param [options] {object} - Server options
|
||||||
|
* @param [options.port} {number} - Port
|
||||||
|
* @param [options.stack} {string[]|Features[]} - Port
|
||||||
|
*/
|
||||||
constructor (initOptions) {
|
constructor (initOptions) {
|
||||||
initOptions = initOptions || {}
|
initOptions = initOptions || {}
|
||||||
const commandLineArgs = require('command-line-args')
|
const commandLineArgs = require('command-line-args')
|
||||||
const commandLineUsage = require('command-line-usage')
|
const commandLineUsage = require('command-line-usage')
|
||||||
const cli = require('../lib/cli-data')
|
const cli = require('../lib/cli-data')
|
||||||
const loadConfig = require('config-master')
|
|
||||||
|
|
||||||
|
this.view = new CliView()
|
||||||
|
|
||||||
|
/* get stored config */
|
||||||
|
const loadConfig = require('config-master')
|
||||||
const stored = loadConfig('local-web-server')
|
const stored = loadConfig('local-web-server')
|
||||||
|
|
||||||
/* manually scan for any --stack passed, as we may need to display stack options */
|
/* read the config and command-line for feature paths */
|
||||||
const stackPaths = arrayify(initOptions.stack || stored.stack) || []
|
const featurePaths = parseFeaturePaths(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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* if the user did not supply a stack, use the default */
|
/* load features and build the middleware stack */
|
||||||
if (!stackPaths.length) stackPaths.push(path.resolve(__dirname, '..', 'node_modules', 'local-web-server-default-stack'))
|
const features = this._buildFeatureStack(featurePaths)
|
||||||
|
|
||||||
/* build the stack */
|
/* gather feature optionDefinitions and parse the command line */
|
||||||
const stackModules = buildStack(stackPaths, this.onVerbose.bind(this), this.onDebug.bind(this))
|
const featureOptionDefinitions = features
|
||||||
|
|
||||||
/* gather stack option definitions and parse the command line */
|
|
||||||
const middlewareOptionDefinitions = stackModules
|
|
||||||
.filter(mw => mw.optionDefinitions)
|
.filter(mw => mw.optionDefinitions)
|
||||||
.map(mw => mw.optionDefinitions())
|
.map(mw => mw.optionDefinitions())
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
@ -57,20 +80,23 @@ class LocalWebServer {
|
|||||||
return def
|
return def
|
||||||
})
|
})
|
||||||
|
|
||||||
const usage = commandLineUsage(cli.usage(middlewareOptionDefinitions))
|
const usage = commandLineUsage(cli.usage(featureOptionDefinitions))
|
||||||
|
|
||||||
let options = {}
|
let options = {}
|
||||||
const allOptionDefinitions = cli.optionDefinitions.concat(middlewareOptionDefinitions)
|
const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions)
|
||||||
if (!initOptions.testMode) {
|
if (!initOptions.testMode) {
|
||||||
try {
|
try {
|
||||||
options = commandLineArgs(allOptionDefinitions)
|
options = commandLineArgs(allOptionDefinitions)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
tool.printError(err)
|
this.view.error(err.toString())
|
||||||
tool.printError(allOptionDefinitions.map(def => {
|
if (err.name === 'DUPLICATE_NAME') {
|
||||||
return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}`
|
this.view.error('\nOption Definitions:')
|
||||||
}).join('\n'))
|
this.view.error(allOptionDefinitions.map(def => {
|
||||||
console.error(usage)
|
return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}`
|
||||||
tool.halt()
|
}).join('\n'))
|
||||||
|
}
|
||||||
|
this.view.show(usage)
|
||||||
|
process.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,33 +110,38 @@ class LocalWebServer {
|
|||||||
options.misc
|
options.misc
|
||||||
)
|
)
|
||||||
|
|
||||||
|
this.view.options.verbose = options.verbose
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config
|
* Config
|
||||||
* @type {object}
|
* @type {object}
|
||||||
*/
|
*/
|
||||||
this.options = options
|
this.options = options
|
||||||
|
|
||||||
stackModules
|
this.features = features
|
||||||
|
|
||||||
|
features
|
||||||
.filter(mw => mw.on)
|
.filter(mw => mw.on)
|
||||||
.forEach(mw => {
|
.forEach(mw => {
|
||||||
mw.on('verbose', this.onVerbose.bind(this))
|
mw.on('verbose', this.view.verbose.bind(this.view))
|
||||||
mw.on('debug', this.onDebug.bind(this))
|
mw.on('debug', this.view.verbose.bind(this.view))
|
||||||
})
|
})
|
||||||
|
|
||||||
/* --config */
|
/* --config */
|
||||||
if (options.config) {
|
if (options.config) {
|
||||||
tool.stop(JSON.stringify(options, null, ' '), 0)
|
this.view.show(JSON.stringify(options, null, ' '))
|
||||||
|
process.exit(0)
|
||||||
|
|
||||||
/* --version */
|
/* --version */
|
||||||
} else if (options.version) {
|
} else if (options.version) {
|
||||||
const pkg = require(path.resolve(__dirname, '..', 'package.json'))
|
const pkg = require(path.resolve(__dirname, '..', 'package.json'))
|
||||||
tool.stop(pkg.version)
|
this.view.show(pkg.version)
|
||||||
|
process.exit(0)
|
||||||
|
|
||||||
/* --help */
|
/* --help */
|
||||||
} else if (options.help) {
|
} else if (options.help) {
|
||||||
tool.stop(usage)
|
this.view.show(usage)
|
||||||
} else {
|
process.exit(0)
|
||||||
this.stack = stackModules
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -120,7 +151,7 @@ class LocalWebServer {
|
|||||||
const compose = require('koa-compose')
|
const compose = require('koa-compose')
|
||||||
const convert = require('koa-convert')
|
const convert = require('koa-convert')
|
||||||
|
|
||||||
const middlewareStack = this.stack
|
const middlewareStack = this.features
|
||||||
.filter(mw => mw.middleware)
|
.filter(mw => mw.middleware)
|
||||||
.map(mw => mw.middleware(this.options))
|
.map(mw => mw.middleware(this.options))
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
@ -162,31 +193,51 @@ class LocalWebServer {
|
|||||||
server = http.createServer(app.callback())
|
server = http.createServer(app.callback())
|
||||||
}
|
}
|
||||||
|
|
||||||
const tableLayout = require('table-layout')
|
|
||||||
|
|
||||||
server.listen(options.port)
|
server.listen(options.port)
|
||||||
if (onListening) server.on('listening', onListening)
|
if (onListening) server.on('listening', onListening)
|
||||||
if (!options.testMode) {
|
if (!options.testMode) {
|
||||||
server.on('listening', function () {
|
server.on('listening', () => {
|
||||||
const ipList = getIPList()
|
const ipList = getIPList()
|
||||||
.map(iface => `[underline]{${server.isHttps ? 'https' : 'http'}://${iface.address}:${options.port}}`)
|
.map(iface => `[underline]{${server.isHttps ? 'https' : 'http'}://${iface.address}:${options.port}}`)
|
||||||
.join(', ')
|
.join(', ')
|
||||||
console.error(ansi.format('Serving at', 'bold'), ansi.format(ipList))
|
this.view.show('Serving at', ansi.format(ipList))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
onVerbose (title, msg) {
|
_buildFeatureStack (featurePaths) {
|
||||||
if (this.options.verbose) {
|
return featurePaths
|
||||||
console.error(ansi.format(title, 'bold'), msg)
|
.map(featurePath => loadStack(featurePath))
|
||||||
}
|
.map(Feature => new Feature())
|
||||||
}
|
.map(module => {
|
||||||
onDebug (title, msg) {
|
if (module.stack) {
|
||||||
if (this.options.debug) {
|
const featureStack = module.stack()
|
||||||
console.error(ansi.format(title, 'bold'), msg)
|
.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
|
return ipList
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildStack (stackPaths, onVerbose, onDebug) {
|
/* manually scan for any --stack passed, as we may need to display stack options */
|
||||||
return stackPaths
|
function parseFeaturePaths (configStack) {
|
||||||
.map(stackPath => loadStack(stackPath))
|
const featurePaths = arrayify(configStack)
|
||||||
.map(Middleware => new Middleware())
|
const featureIndex = process.argv.indexOf('--stack')
|
||||||
.map(module => {
|
if (featureIndex > -1) {
|
||||||
if (module.stack) {
|
for (var i = featureIndex + 1; i < process.argv.length; i++) {
|
||||||
const featureStack = module.stack()
|
const featurePath = process.argv[i]
|
||||||
.map(Feature => new Feature())
|
if (/^-/.test(featurePath)) {
|
||||||
.map(feature => {
|
break
|
||||||
if (feature.on) {
|
} else {
|
||||||
feature.on('verbose', onVerbose)
|
featurePaths.push(featurePath)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
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
|
module.exports = LocalWebServer
|
||||||
|
@ -40,7 +40,6 @@
|
|||||||
"ansi-escape-sequences": "^2.2.2",
|
"ansi-escape-sequences": "^2.2.2",
|
||||||
"array-back": "^1.0.3",
|
"array-back": "^1.0.3",
|
||||||
"command-line-args": "^3.0.0",
|
"command-line-args": "^3.0.0",
|
||||||
"command-line-tool": "~0.4.0",
|
|
||||||
"command-line-usage": "^3.0.3",
|
"command-line-usage": "^3.0.3",
|
||||||
"config-master": "^2.0.3",
|
"config-master": "^2.0.3",
|
||||||
"koa": "^2.0.0",
|
"koa": "^2.0.0",
|
||||||
|
Reference in New Issue
Block a user