server now created on contruction, before views intantiated.. refactor view API
This commit is contained in:
@ -1,5 +1,4 @@
|
|||||||
#!/usr/bin/env node
|
#!/usr/bin/env node
|
||||||
'use strict'
|
'use strict'
|
||||||
const LocalWebServer = require('../')
|
const LocalWebServer = require('../')
|
||||||
const ws = new LocalWebServer()
|
new LocalWebServer()
|
||||||
ws.getServer()
|
|
||||||
|
18
doc/api.md
18
doc/api.md
@ -5,10 +5,10 @@
|
|||||||
* [LocalWebServer](#exp_module_local-web-server--LocalWebServer) ⇐ <code>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)
|
* [new LocalWebServer([options])](#new_module_local-web-server--LocalWebServer_new)
|
||||||
* _instance_
|
* _instance_
|
||||||
* [.view](#module_local-web-server--LocalWebServer.LocalWebServer+view) : <code>View</code>
|
|
||||||
* [.features](#module_local-web-server--LocalWebServer.LocalWebServer+features) : <code>Array.<Feature></code>
|
* [.features](#module_local-web-server--LocalWebServer.LocalWebServer+features) : <code>Array.<Feature></code>
|
||||||
* [.options](#module_local-web-server--LocalWebServer.LocalWebServer+options) : <code>object</code>
|
* [.options](#module_local-web-server--LocalWebServer.LocalWebServer+options) : <code>object</code>
|
||||||
* [.server](#module_local-web-server--LocalWebServer+server) : <code>Server</code>
|
* [.view](#module_local-web-server--LocalWebServer.LocalWebServer+view) : <code>View</code>
|
||||||
|
* [.server](#module_local-web-server--LocalWebServer.LocalWebServer+server) : <code>Server</code>
|
||||||
* [.getApplication()](#module_local-web-server--LocalWebServer+getApplication) ⇒ <code>function</code>
|
* [.getApplication()](#module_local-web-server--LocalWebServer+getApplication) ⇒ <code>function</code>
|
||||||
* [.getServer()](#module_local-web-server--LocalWebServer+getServer) ⇒ <code>Server</code>
|
* [.getServer()](#module_local-web-server--LocalWebServer+getServer) ⇒ <code>Server</code>
|
||||||
* _inner_
|
* _inner_
|
||||||
@ -28,12 +28,6 @@
|
|||||||
- .port} <code>number</code> - Port
|
- .port} <code>number</code> - Port
|
||||||
- .stack} <code>Array.<string></code> | <code>Array.<Features></code> - Port
|
- .stack} <code>Array.<string></code> | <code>Array.<Features></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>
|
<a name="module_local-web-server--LocalWebServer.LocalWebServer+features"></a>
|
||||||
|
|
||||||
#### localWebServer.features : <code>Array.<Feature></code>
|
#### localWebServer.features : <code>Array.<Feature></code>
|
||||||
@ -46,7 +40,13 @@ Loaded feature modules
|
|||||||
Config
|
Config
|
||||||
|
|
||||||
**Kind**: instance property of <code>[LocalWebServer](#exp_module_local-web-server--LocalWebServer)</code>
|
**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.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+server"></a>
|
||||||
|
|
||||||
#### localWebServer.server : <code>Server</code>
|
#### localWebServer.server : <code>Server</code>
|
||||||
Node.js server
|
Node.js server
|
||||||
|
@ -13,7 +13,7 @@ Then, start the server, outputting `combined` format logs to disk:
|
|||||||
$ ws -f combined > web.log
|
$ ws -f combined > web.log
|
||||||
```
|
```
|
||||||
|
|
||||||
In a separate tab, point goaccess at `web.log` and it will display statistics in real time:
|
In a separate terminal, point goaccess at `web.log` and it will display statistics in real time:
|
||||||
|
|
||||||
```
|
```
|
||||||
$ goaccess -p ~/.goaccessrc -f web.log
|
$ goaccess -p ~/.goaccessrc -f web.log
|
||||||
|
@ -1,31 +1,48 @@
|
|||||||
|
'use strict'
|
||||||
|
|
||||||
class CliView {
|
class CliView {
|
||||||
constructor (localWebServer) {
|
constructor (localWebServer) {
|
||||||
this.options = localWebServer.options
|
this.localWebServer = localWebServer
|
||||||
}
|
}
|
||||||
info (key, value) {
|
write (msg) {
|
||||||
if (key && value) {
|
const writeToStdout = [ 'log', 'info' ]
|
||||||
const ansi = require('ansi-escape-sequences')
|
Object.keys(msg).forEach(key => {
|
||||||
const tableLayout = require('table-layout')
|
if (writeToStdout.includes(key)) {
|
||||||
const output = tableLayout({ key: ansi.format(key, 'bold'), value: value}, {
|
console.log(msg[key])
|
||||||
padding: { left: '', right: ' ' },
|
} else if (key === 'config' && msg.config && this.localWebServer.options.verbose) {
|
||||||
columns: [
|
printLine(msg.config)
|
||||||
{ name: 'key', width: 18 },
|
} else if (key === 'error') {
|
||||||
{ name: 'value', nowrap: true }
|
const ansi = require('ansi-escape-sequences')
|
||||||
]
|
console.error(ansi.format(msg.error, 'red'))
|
||||||
})
|
}
|
||||||
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
|
module.exports = CliView
|
||||||
|
|
||||||
|
function printLine (config) {
|
||||||
|
const output = objectToTable(config)
|
||||||
|
process.stderr.write(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
function objectToTable (object) {
|
||||||
|
const ansi = require('ansi-escape-sequences')
|
||||||
|
const tableLayout = require('table-layout')
|
||||||
|
const t = require('typical')
|
||||||
|
|
||||||
|
const data = Object.keys(object).map(key => {
|
||||||
|
if (t.isObject(object[key])) {
|
||||||
|
return { key: ansi.format(key, 'bold'), value: objectToTable(object[key]) }
|
||||||
|
} else {
|
||||||
|
return { key: ansi.format(key, 'bold'), value: object[key] }
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return tableLayout(data, {
|
||||||
|
padding: { left: '', right: ' ' },
|
||||||
|
columns: [
|
||||||
|
// { name: 'key', width: 18 },
|
||||||
|
// { name: 'value', nowrap: true }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
@ -22,17 +22,10 @@ class LocalWebServer {
|
|||||||
*/
|
*/
|
||||||
constructor (initOptions) {
|
constructor (initOptions) {
|
||||||
initOptions = initOptions || {}
|
initOptions = initOptions || {}
|
||||||
const commandLineArgs = require('command-line-args')
|
|
||||||
const commandLineUsage = require('command-line-usage')
|
const commandLineUsage = require('command-line-usage')
|
||||||
const CliView = require('./cli-view')
|
const CliView = require('./cli-view')
|
||||||
const cli = require('../lib/cli-data')
|
const cli = require('../lib/cli-data')
|
||||||
|
|
||||||
/**
|
|
||||||
* 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')
|
||||||
const stored = loadConfig('local-web-server')
|
const stored = loadConfig('local-web-server')
|
||||||
@ -47,35 +40,10 @@ class LocalWebServer {
|
|||||||
this.features = this._buildFeatureStack(featurePaths)
|
this.features = this._buildFeatureStack(featurePaths)
|
||||||
|
|
||||||
/* gather feature optionDefinitions and parse the command line */
|
/* gather feature optionDefinitions and parse the command line */
|
||||||
const featureOptionDefinitions = this.features
|
const featureOptionDefinitions = gatherOptionDefinitions(this.features)
|
||||||
.filter(mw => mw.optionDefinitions)
|
|
||||||
.map(mw => mw.optionDefinitions())
|
|
||||||
.reduce(flatten, [])
|
|
||||||
.filter(def => def)
|
|
||||||
.map(def => {
|
|
||||||
def.group = 'middleware'
|
|
||||||
return def
|
|
||||||
})
|
|
||||||
|
|
||||||
const usage = commandLineUsage(cli.usage(featureOptionDefinitions))
|
const usage = commandLineUsage(cli.usage(featureOptionDefinitions))
|
||||||
|
|
||||||
let options = {}
|
|
||||||
const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions)
|
const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions)
|
||||||
if (!initOptions.testMode) {
|
let options = initOptions.testMode ? {} : parseCommandLineOptions(allOptionDefinitions, this.view)
|
||||||
try {
|
|
||||||
options = commandLineArgs(allOptionDefinitions)
|
|
||||||
} catch (err) {
|
|
||||||
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.info(usage)
|
|
||||||
process.exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* combine in stored config */
|
/* combine in stored config */
|
||||||
options = Object.assign(
|
options = Object.assign(
|
||||||
@ -93,26 +61,41 @@ class LocalWebServer {
|
|||||||
*/
|
*/
|
||||||
this.options = options
|
this.options = options
|
||||||
|
|
||||||
if (options.view) {
|
/**
|
||||||
const View = loadModule(options.view)
|
* Current view.
|
||||||
this.view = new View(this)
|
* @type {View}
|
||||||
}
|
*/
|
||||||
|
this.view = null
|
||||||
|
|
||||||
/* --config */
|
/* --config */
|
||||||
if (options.config) {
|
if (options.config) {
|
||||||
this.view.info(JSON.stringify(options, null, ' '))
|
console.error(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.info(pkg.version)
|
console.error(pkg.version)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
|
||||||
/* --help */
|
/* --help */
|
||||||
} else if (options.help) {
|
} else if (options.help) {
|
||||||
this.view.info(usage)
|
console.error(usage)
|
||||||
process.exit(0)
|
process.exit(0)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
/**
|
||||||
|
* Node.js server
|
||||||
|
* @type {Server}
|
||||||
|
*/
|
||||||
|
this.server = this.getServer()
|
||||||
|
|
||||||
|
if (options.view) {
|
||||||
|
const View = loadModule(options.view)
|
||||||
|
this.view = new View(this)
|
||||||
|
} else {
|
||||||
|
this.view = new CliView(this)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,7 +127,7 @@ class LocalWebServer {
|
|||||||
* Returns a listening server which processes requests using the middleware supplied.
|
* Returns a listening server which processes requests using the middleware supplied.
|
||||||
* @returns {Server}
|
* @returns {Server}
|
||||||
*/
|
*/
|
||||||
getServer (onListening) {
|
getServer () {
|
||||||
const app = this.getApplication()
|
const app = this.getApplication()
|
||||||
const options = this.options
|
const options = this.options
|
||||||
|
|
||||||
@ -173,21 +156,18 @@ class LocalWebServer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
server.listen(options.port)
|
server.listen(options.port)
|
||||||
if (onListening) server.on('listening', onListening)
|
// if (onListening) server.on('listening', onListening)
|
||||||
|
|
||||||
|
/* on server-up message */
|
||||||
if (!options.testMode) {
|
if (!options.testMode) {
|
||||||
server.on('listening', () => {
|
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(', ')
|
||||||
this.view.info('Serving at', ansi.format(ipList))
|
console.error('Serving at', ansi.format(ipList))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Node.js server
|
|
||||||
* @type {Server}
|
|
||||||
*/
|
|
||||||
this.server = server
|
|
||||||
return server
|
return server
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -234,7 +214,7 @@ function loadStack (modulePath) {
|
|||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const msg = `No module found at: \n${tried.join('\n')}`
|
const msg = `No module found for: ${modulePath}`
|
||||||
console.error(msg)
|
console.error(msg)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
}
|
}
|
||||||
@ -299,4 +279,35 @@ function parseFeaturePaths (configStack) {
|
|||||||
return featurePaths
|
return featurePaths
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function gatherOptionDefinitions (features) {
|
||||||
|
return features
|
||||||
|
.filter(mw => mw.optionDefinitions)
|
||||||
|
.map(mw => mw.optionDefinitions())
|
||||||
|
.reduce(flatten, [])
|
||||||
|
.filter(def => def)
|
||||||
|
.map(def => {
|
||||||
|
def.group = 'middleware'
|
||||||
|
return def
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseCommandLineOptions (allOptionDefinitions) {
|
||||||
|
const commandLineArgs = require('command-line-args')
|
||||||
|
try {
|
||||||
|
return commandLineArgs(allOptionDefinitions)
|
||||||
|
} catch (err) {
|
||||||
|
console.error(err)
|
||||||
|
|
||||||
|
/* handle duplicate option names */
|
||||||
|
if (err.name === 'DUPLICATE_NAME') {
|
||||||
|
console.error('\nOption Definitions:')
|
||||||
|
console.error(allOptionDefinitions.map(def => {
|
||||||
|
return `name: ${def.name}${def.alias ? ', alias: ' + def.alias : ''}`
|
||||||
|
}).join('\n'))
|
||||||
|
}
|
||||||
|
console.error(usage)
|
||||||
|
process.exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = LocalWebServer
|
module.exports = LocalWebServer
|
||||||
|
12
test/test.js
12
test/test.js
@ -12,13 +12,13 @@ test('stack', function (t) {
|
|||||||
port: 8100,
|
port: 8100,
|
||||||
testMode: true
|
testMode: true
|
||||||
})
|
})
|
||||||
const server = ws.getServer(() => {
|
ws.server.on('listening', () => {
|
||||||
return request('http://localhost:8100/')
|
return request('http://localhost:8100/')
|
||||||
.then(c.checkResponse(t, 200, /1234512345/))
|
.then(c.checkResponse(t, 200, /1234512345/))
|
||||||
.then(server.close.bind(server))
|
.then(ws.server.close.bind(ws.server))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
t.fail(err.message)
|
t.fail(err.message)
|
||||||
server.close()
|
ws.server.close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
@ -34,13 +34,13 @@ test('https', function (t) {
|
|||||||
const url = require('url')
|
const url = require('url')
|
||||||
const reqOptions = url.parse('https://localhost:8100/')
|
const reqOptions = url.parse('https://localhost:8100/')
|
||||||
reqOptions.rejectUnauthorized = false
|
reqOptions.rejectUnauthorized = false
|
||||||
const server = ws.getServer(() => {
|
ws.server.on('listening', () => {
|
||||||
return request(reqOptions)
|
return request(reqOptions)
|
||||||
.then(c.checkResponse(t, 200, /1234512345/))
|
.then(c.checkResponse(t, 200, /1234512345/))
|
||||||
.then(server.close.bind(server))
|
.then(ws.server.close.bind(ws.server))
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
t.fail(err.message)
|
t.fail(err.message)
|
||||||
server.close()
|
ws.server.close()
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
Reference in New Issue
Block a user