docs.. view work.. refactor
This commit is contained in:
@ -8,7 +8,7 @@
|
|||||||
***Requires node v4.0.0 or higher. Install the [previous release](https://github.com/75lb/local-web-server/tree/prev) for older node support.***
|
***Requires node v4.0.0 or higher. Install the [previous release](https://github.com/75lb/local-web-server/tree/prev) for older node support.***
|
||||||
|
|
||||||
# local-web-server
|
# local-web-server
|
||||||
At its core, local-web-server is an application shell for building a specialised command-line web server to support productive Web Platform engineers. It comes bundled with a middleware stack covering common requirements but any arbitrary stack can be specified from the command line or config.
|
At its core, local-web-server is an application shell for building a specialised command-line web server to support productive Web Platform engineers. When combined with built-in and custom features it's in intended to by a powerful tool in helping build and debug Web applications. It comes bundled with a middleware stack covering common requirements but any arbitrary stack can be specified from the command line or config.
|
||||||
|
|
||||||
Being an npm module, it is trivial is bundle and distribute/deploy with your web application.
|
Being an npm module, it is trivial is bundle and distribute/deploy with your web application.
|
||||||
|
|
||||||
|
68
doc/api.md
68
doc/api.md
@ -2,29 +2,71 @@
|
|||||||
|
|
||||||
|
|
||||||
* [local-web-server](#module_local-web-server)
|
* [local-web-server](#module_local-web-server)
|
||||||
* [LocalWebServer](#exp_module_local-web-server--LocalWebServer) ⇐ <code>[middleware-stack](#module_middleware-stack)</code> ⏏
|
* [LocalWebServer](#exp_module_local-web-server--LocalWebServer) ⇐ <code>module:middleware-stack</code> ⏏
|
||||||
|
* [new LocalWebServer([options])](#new_module_local-web-server--LocalWebServer_new)
|
||||||
* _instance_
|
* _instance_
|
||||||
* [.add(middleware)](#) ↩︎
|
* [.view](#module_local-web-server--LocalWebServer.LocalWebServer+view) : <code>View</code>
|
||||||
|
* [.features](#module_local-web-server--LocalWebServer.LocalWebServer+features) : <code>Array.<Feature></code>
|
||||||
|
* [.options](#module_local-web-server--LocalWebServer.LocalWebServer+options) : <code>object</code>
|
||||||
|
* [.server](#module_local-web-server--LocalWebServer+server) : <code>Server</code>
|
||||||
|
* [.getApplication()](#module_local-web-server--LocalWebServer+getApplication) ⇒ <code>function</code>
|
||||||
|
* [.getServer()](#module_local-web-server--LocalWebServer+getServer) ⇒ <code>Server</code>
|
||||||
* _inner_
|
* _inner_
|
||||||
* [~collectUserOptions()](#module_local-web-server--LocalWebServer..collectUserOptions)
|
* [~loadStack()](#module_local-web-server--LocalWebServer..loadStack) ⇒ <code>object</code>
|
||||||
|
|
||||||
<a name="exp_module_local-web-server--LocalWebServer"></a>
|
<a name="exp_module_local-web-server--LocalWebServer"></a>
|
||||||
|
|
||||||
### LocalWebServer ⇐ <code>[middleware-stack](#module_middleware-stack)</code> ⏏
|
### LocalWebServer ⇐ <code>module:middleware-stack</code> ⏏
|
||||||
**Kind**: Exported class
|
**Kind**: Exported class
|
||||||
**Extends:** <code>[middleware-stack](#module_middleware-stack)</code>
|
**Extends:** <code>module:middleware-stack</code>
|
||||||
<a name=""></a>
|
<a name="new_module_local-web-server--LocalWebServer_new"></a>
|
||||||
|
|
||||||
#### localWebServer.add(middleware) ↩︎
|
#### new LocalWebServer([options])
|
||||||
**Kind**: instance method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
|
||||||
**Chainable**
|
|
||||||
**Params**
|
**Params**
|
||||||
|
|
||||||
- middleware <code>[middleware](#module_middleware-stack--MiddlewareStack..middleware)</code>
|
- [options] <code>object</code> - Server options
|
||||||
|
- .port} <code>number</code> - Port
|
||||||
|
- .stack} <code>Array.<string></code> | <code>Array.<Features></code> - Port
|
||||||
|
|
||||||
<a name="module_local-web-server--LocalWebServer..collectUserOptions"></a>
|
<a name="module_local-web-server--LocalWebServer.LocalWebServer+view"></a>
|
||||||
|
|
||||||
#### LocalWebServer~collectUserOptions()
|
#### localWebServer.view : <code>View</code>
|
||||||
Return default, stored and command-line options combined
|
Current view.
|
||||||
|
|
||||||
|
**Kind**: instance property of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer.LocalWebServer+features"></a>
|
||||||
|
|
||||||
|
#### localWebServer.features : <code>Array.<Feature></code>
|
||||||
|
Loaded feature modules
|
||||||
|
|
||||||
|
**Kind**: instance property of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer.LocalWebServer+options"></a>
|
||||||
|
|
||||||
|
#### localWebServer.options : <code>object</code>
|
||||||
|
Config
|
||||||
|
|
||||||
|
**Kind**: instance property of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer+server"></a>
|
||||||
|
|
||||||
|
#### localWebServer.server : <code>Server</code>
|
||||||
|
Node.js server
|
||||||
|
|
||||||
|
**Kind**: instance property of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer+getApplication"></a>
|
||||||
|
|
||||||
|
#### localWebServer.getApplication() ⇒ <code>function</code>
|
||||||
|
Returns a middleware application suitable for passing to `http.createServer`. The application is a function with three args (req, res and next) which can be created by express, Koa or hand-rolled.
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer+getServer"></a>
|
||||||
|
|
||||||
|
#### localWebServer.getServer() ⇒ <code>Server</code>
|
||||||
|
Returns a listening server which processes requests using the middleware supplied.
|
||||||
|
|
||||||
|
**Kind**: instance method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
<a name="module_local-web-server--LocalWebServer..loadStack"></a>
|
||||||
|
|
||||||
|
#### LocalWebServer~loadStack() ⇒ <code>object</code>
|
||||||
|
Loads a module by either path or name.
|
||||||
|
|
||||||
**Kind**: inner method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
**Kind**: inner method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
||||||
|
@ -24,6 +24,10 @@ exports.optionDefinitions = [
|
|||||||
description: 'Print these usage instructions.', group: 'misc'
|
description: 'Print these usage instructions.', group: 'misc'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
name: 'view', type: String,
|
||||||
|
description: 'Custom view', group: 'misc'
|
||||||
|
},
|
||||||
|
{
|
||||||
name: 'config', type: Boolean,
|
name: 'config', type: Boolean,
|
||||||
description: 'Print the stored config.', group: 'misc'
|
description: 'Print the stored config.', group: 'misc'
|
||||||
},
|
},
|
||||||
|
31
lib/cli-view.js
Normal file
31
lib/cli-view.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
class CliView {
|
||||||
|
constructor (localWebServer) {
|
||||||
|
this.options = localWebServer.options
|
||||||
|
}
|
||||||
|
info (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.info(key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
error (msg) {
|
||||||
|
console.error(ansi.format(msg, 'red'))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = CliView
|
@ -9,37 +9,6 @@ const ansi = require('ansi-escape-sequences')
|
|||||||
* @module local-web-server
|
* @module local-web-server
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
@ -55,9 +24,14 @@ class LocalWebServer {
|
|||||||
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 CliView = require('./cli-view')
|
||||||
const cli = require('../lib/cli-data')
|
const cli = require('../lib/cli-data')
|
||||||
|
|
||||||
this.view = new CliView()
|
/**
|
||||||
|
* Current view.
|
||||||
|
* @type {View}
|
||||||
|
*/
|
||||||
|
this.view = new CliView(this)
|
||||||
|
|
||||||
/* get stored config */
|
/* get stored config */
|
||||||
const loadConfig = require('config-master')
|
const loadConfig = require('config-master')
|
||||||
@ -66,11 +40,14 @@ class LocalWebServer {
|
|||||||
/* read the config and command-line for feature paths */
|
/* read the config and command-line for feature paths */
|
||||||
const featurePaths = parseFeaturePaths(initOptions.stack || stored.stack)
|
const featurePaths = parseFeaturePaths(initOptions.stack || stored.stack)
|
||||||
|
|
||||||
/* load features and build the middleware stack */
|
/**
|
||||||
const features = this._buildFeatureStack(featurePaths)
|
* Loaded feature modules
|
||||||
|
* @type {Feature[]}
|
||||||
|
*/
|
||||||
|
this.features = this._buildFeatureStack(featurePaths)
|
||||||
|
|
||||||
/* gather feature optionDefinitions and parse the command line */
|
/* gather feature optionDefinitions and parse the command line */
|
||||||
const featureOptionDefinitions = features
|
const featureOptionDefinitions = this.features
|
||||||
.filter(mw => mw.optionDefinitions)
|
.filter(mw => mw.optionDefinitions)
|
||||||
.map(mw => mw.optionDefinitions())
|
.map(mw => mw.optionDefinitions())
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
@ -95,7 +72,7 @@ class LocalWebServer {
|
|||||||
return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}`
|
return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}`
|
||||||
}).join('\n'))
|
}).join('\n'))
|
||||||
}
|
}
|
||||||
this.view.show(usage)
|
this.view.info(usage)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,41 +87,39 @@ class LocalWebServer {
|
|||||||
options.misc
|
options.misc
|
||||||
)
|
)
|
||||||
|
|
||||||
this.view.options.verbose = options.verbose
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Config
|
* Config
|
||||||
* @type {object}
|
* @type {object}
|
||||||
*/
|
*/
|
||||||
this.options = options
|
this.options = options
|
||||||
|
|
||||||
this.features = features
|
if (options.view) {
|
||||||
|
const View = loadModule(options.view)
|
||||||
features
|
this.view = new View(this)
|
||||||
.filter(mw => mw.on)
|
}
|
||||||
.forEach(mw => {
|
|
||||||
mw.on('verbose', this.view.verbose.bind(this.view))
|
|
||||||
mw.on('debug', this.view.verbose.bind(this.view))
|
|
||||||
})
|
|
||||||
|
|
||||||
/* --config */
|
/* --config */
|
||||||
if (options.config) {
|
if (options.config) {
|
||||||
this.view.show(JSON.stringify(options, null, ' '))
|
this.view.info(JSON.stringify(options, null, ' '))
|
||||||
process.exit(0)
|
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'))
|
||||||
this.view.show(pkg.version)
|
this.view.info(pkg.version)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
|
||||||
/* --help */
|
/* --help */
|
||||||
} else if (options.help) {
|
} else if (options.help) {
|
||||||
this.view.show(usage)
|
this.view.info(usage)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a middleware application suitable for passing to `http.createServer`. The application is a function with three args (req, res and next) which can be created by express, Koa or hand-rolled.
|
||||||
|
* @returns {function}
|
||||||
|
*/
|
||||||
getApplication () {
|
getApplication () {
|
||||||
const Koa = require('koa')
|
const Koa = require('koa')
|
||||||
const app = new Koa()
|
const app = new Koa()
|
||||||
@ -153,7 +128,7 @@ class LocalWebServer {
|
|||||||
|
|
||||||
const middlewareStack = this.features
|
const middlewareStack = this.features
|
||||||
.filter(mw => mw.middleware)
|
.filter(mw => mw.middleware)
|
||||||
.map(mw => mw.middleware(this.options))
|
.map(mw => mw.middleware(this.options, this))
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
.filter(mw => mw)
|
.filter(mw => mw)
|
||||||
.map(convert)
|
.map(convert)
|
||||||
@ -162,9 +137,13 @@ class LocalWebServer {
|
|||||||
app.on('error', err => {
|
app.on('error', err => {
|
||||||
console.error(ansi.format(err.stack, 'red'))
|
console.error(ansi.format(err.stack, 'red'))
|
||||||
})
|
})
|
||||||
return app
|
return app.callback()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a listening server which processes requests using the middleware supplied.
|
||||||
|
* @returns {Server}
|
||||||
|
*/
|
||||||
getServer (onListening) {
|
getServer (onListening) {
|
||||||
const app = this.getApplication()
|
const app = this.getApplication()
|
||||||
const options = this.options
|
const options = this.options
|
||||||
@ -186,11 +165,11 @@ class LocalWebServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const https = require('https')
|
const https = require('https')
|
||||||
server = https.createServer(serverOptions, app.callback())
|
server = https.createServer(serverOptions, app)
|
||||||
server.isHttps = true
|
server.isHttps = true
|
||||||
} else {
|
} else {
|
||||||
const http = require('http')
|
const http = require('http')
|
||||||
server = http.createServer(app.callback())
|
server = http.createServer(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
server.listen(options.port)
|
server.listen(options.port)
|
||||||
@ -200,10 +179,15 @@ class LocalWebServer {
|
|||||||
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(', ')
|
||||||
this.view.show('Serving at', ansi.format(ipList))
|
this.view.info('Serving at', ansi.format(ipList))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Node.js server
|
||||||
|
* @type {Server}
|
||||||
|
*/
|
||||||
|
this.server = server
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,32 +195,25 @@ class LocalWebServer {
|
|||||||
return featurePaths
|
return featurePaths
|
||||||
.map(featurePath => loadStack(featurePath))
|
.map(featurePath => loadStack(featurePath))
|
||||||
.map(Feature => new Feature())
|
.map(Feature => new Feature())
|
||||||
.map(module => {
|
.map(feature => {
|
||||||
if (module.stack) {
|
if (feature.stack) {
|
||||||
const featureStack = module.stack()
|
const featureStack = feature.stack()
|
||||||
.map(Feature => new Feature())
|
.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 () {
|
feature.optionDefinitions = function () {
|
||||||
return featureStack
|
return featureStack
|
||||||
.map(feature => feature.optionDefinitions && feature.optionDefinitions())
|
.map(feature => feature.optionDefinitions && feature.optionDefinitions())
|
||||||
.filter(definitions => definitions)
|
.filter(definitions => definitions)
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
}
|
}
|
||||||
module.middleware = function (options) {
|
feature.middleware = function (options, view) {
|
||||||
return featureStack
|
return featureStack
|
||||||
.map(feature => feature.middleware(options))
|
.map(feature => feature.middleware(options, view))
|
||||||
.reduce(flatten, [])
|
.reduce(flatten, [])
|
||||||
.filter(mw => mw)
|
.filter(mw => mw)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return module
|
return feature
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,8 +223,26 @@ class LocalWebServer {
|
|||||||
* @returns {object}
|
* @returns {object}
|
||||||
*/
|
*/
|
||||||
function loadStack (modulePath) {
|
function loadStack (modulePath) {
|
||||||
let module
|
const isModule = module => module.prototype && (module.prototype.middleware || module.prototype.stack)
|
||||||
if (isModule(modulePath)) return modulePath
|
if (isModule(modulePath)) return modulePath
|
||||||
|
const module = loadModule(modulePath)
|
||||||
|
if (module) {
|
||||||
|
if (!isModule(module)) {
|
||||||
|
const insp = require('util').inspect(module, { depth: 3, colors: true })
|
||||||
|
const msg = `Not valid Middleware at: ${insp}`
|
||||||
|
console.error(msg)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const msg = `No module found at: \n${tried.join('\n')}`
|
||||||
|
console.error(msg)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
return module
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadModule (modulePath) {
|
||||||
|
let module
|
||||||
const tried = []
|
const tried = []
|
||||||
if (modulePath) {
|
if (modulePath) {
|
||||||
try {
|
try {
|
||||||
@ -268,22 +263,9 @@ function loadStack (modulePath) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (module) {
|
|
||||||
if (!isModule(module)) {
|
|
||||||
const insp = require('util').inspect(module, { depth: 3, colors: true })
|
|
||||||
const msg = `Not valid Middleware at: ${insp}`
|
|
||||||
tool.halt(new Error(msg))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
const msg = `No module found at: \n${tried.join('\n')}`
|
|
||||||
tool.halt(new Error(msg))
|
|
||||||
}
|
|
||||||
return module
|
return module
|
||||||
}
|
}
|
||||||
|
|
||||||
function isModule (module) {
|
|
||||||
return module.prototype && (module.prototype.middleware || module.prototype.stack)
|
|
||||||
}
|
|
||||||
|
|
||||||
function getIPList () {
|
function getIPList () {
|
||||||
const flatten = require('reduce-flatten')
|
const flatten = require('reduce-flatten')
|
||||||
|
Reference in New Issue
Block a user