Browse Source

docs.. view work.. refactor

master
Lloyd Brookes 9 years ago
parent
commit
f11cbee820
  1. 2
      README.md
  2. 68
      doc/api.md
  3. 4
      lib/cli-data.js
  4. 31
      lib/cli-view.js
  5. 148
      lib/local-web-server.js

2
README.md

@ -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

@ -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.&lt;Feature&gt;</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>
<a name=""></a>
**Extends:** <code>module:middleware-stack</code>
<a name="new_module_local-web-server--LocalWebServer_new"></a>
#### localWebServer.add(middleware) ↩︎
**Kind**: instance method of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
**Chainable**
#### new LocalWebServer([options])
**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.&lt;string&gt;</code> | <code>Array.&lt;Features&gt;</code> - Port
<a name="module_local-web-server--LocalWebServer.LocalWebServer+view"></a>
#### localWebServer.view : <code>View</code>
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.&lt;Feature&gt;</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>
<a name="module_local-web-server--LocalWebServer..collectUserOptions"></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~collectUserOptions()
Return default, stored and command-line options combined
#### 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>

4
lib/cli-data.js

@ -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

@ -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

148
lib/local-web-server.js

@ -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
features
.filter(mw => mw.on)
.forEach(mw => {
mw.on('verbose', this.view.verbose.bind(this.view))
mw.on('debug', this.view.verbose.bind(this.view))
})
if (options.view) {
const View = loadModule(options.view)
this.view = new View(this)
}
/* --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 => {
if (module.stack) {
const featureStack = module.stack()
.map(feature => {
if (feature.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')

Loading…
Cancel
Save