From 9d3484fed68fab1831eb1db739f6253fdeb1b45e Mon Sep 17 00:00:00 2001 From: Lloyd Brookes Date: Sun, 17 Jul 2016 17:05:21 +0100 Subject: [PATCH] server now created on contruction, before views intantiated.. refactor view API --- bin/cli.js | 3 +- doc/api.md | 18 ++++---- doc/visualisation.md | 2 +- lib/cli-view.js | 65 +++++++++++++++++----------- lib/local-web-server.js | 111 ++++++++++++++++++++++++++---------------------- test/test.js | 12 +++--- 6 files changed, 119 insertions(+), 92 deletions(-) diff --git a/bin/cli.js b/bin/cli.js index 35c5a3c..019ce92 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,5 +1,4 @@ #!/usr/bin/env node 'use strict' const LocalWebServer = require('../') -const ws = new LocalWebServer() -ws.getServer() +new LocalWebServer() diff --git a/doc/api.md b/doc/api.md index b476b8d..4b4ccf0 100644 --- a/doc/api.md +++ b/doc/api.md @@ -5,10 +5,10 @@ * [LocalWebServer](#exp_module_local-web-server--LocalWebServer) ⇐ module:middleware-stack ⏏ * [new LocalWebServer([options])](#new_module_local-web-server--LocalWebServer_new) * _instance_ - * [.view](#module_local-web-server--LocalWebServer.LocalWebServer+view) : View * [.features](#module_local-web-server--LocalWebServer.LocalWebServer+features) : Array.<Feature> * [.options](#module_local-web-server--LocalWebServer.LocalWebServer+options) : object - * [.server](#module_local-web-server--LocalWebServer+server) : Server + * [.view](#module_local-web-server--LocalWebServer.LocalWebServer+view) : View + * [.server](#module_local-web-server--LocalWebServer.LocalWebServer+server) : Server * [.getApplication()](#module_local-web-server--LocalWebServer+getApplication) ⇒ function * [.getServer()](#module_local-web-server--LocalWebServer+getServer) ⇒ Server * _inner_ @@ -28,12 +28,6 @@ - .port} number - Port - .stack} Array.<string> | Array.<Features> - Port - - -#### localWebServer.view : View -Current view. - -**Kind**: instance property of [LocalWebServer](#exp_module_local-web-server--LocalWebServer) #### localWebServer.features : Array.<Feature> @@ -46,7 +40,13 @@ Loaded feature modules Config **Kind**: instance property of [LocalWebServer](#exp_module_local-web-server--LocalWebServer) - + + +#### localWebServer.view : View +Current view. + +**Kind**: instance property of [LocalWebServer](#exp_module_local-web-server--LocalWebServer) + #### localWebServer.server : Server Node.js server diff --git a/doc/visualisation.md b/doc/visualisation.md index c621023..c31ce9d 100644 --- a/doc/visualisation.md +++ b/doc/visualisation.md @@ -13,7 +13,7 @@ Then, start the server, outputting `combined` format logs to disk: $ 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 diff --git a/lib/cli-view.js b/lib/cli-view.js index 21fabaa..07503db 100644 --- a/lib/cli-view.js +++ b/lib/cli-view.js @@ -1,31 +1,48 @@ +'use strict' + class CliView { constructor (localWebServer) { - this.options = localWebServer.options + this.localWebServer = localWebServer } - 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')) + write (msg) { + const writeToStdout = [ 'log', 'info' ] + Object.keys(msg).forEach(key => { + if (writeToStdout.includes(key)) { + console.log(msg[key]) + } else if (key === 'config' && msg.config && this.localWebServer.options.verbose) { + printLine(msg.config) + } else if (key === 'error') { + const ansi = require('ansi-escape-sequences') + console.error(ansi.format(msg.error, 'red')) + } + }) } } 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 } + ] + }) +} diff --git a/lib/local-web-server.js b/lib/local-web-server.js index f43b21e..4f8c23c 100644 --- a/lib/local-web-server.js +++ b/lib/local-web-server.js @@ -22,17 +22,10 @@ class LocalWebServer { */ constructor (initOptions) { initOptions = initOptions || {} - const commandLineArgs = require('command-line-args') const commandLineUsage = require('command-line-usage') const CliView = require('./cli-view') const cli = require('../lib/cli-data') - /** - * Current view. - * @type {View} - */ - this.view = new CliView(this) - /* get stored config */ const loadConfig = require('config-master') const stored = loadConfig('local-web-server') @@ -47,35 +40,10 @@ class LocalWebServer { this.features = this._buildFeatureStack(featurePaths) /* gather feature optionDefinitions and parse the command line */ - const featureOptionDefinitions = this.features - .filter(mw => mw.optionDefinitions) - .map(mw => mw.optionDefinitions()) - .reduce(flatten, []) - .filter(def => def) - .map(def => { - def.group = 'middleware' - return def - }) - + const featureOptionDefinitions = gatherOptionDefinitions(this.features) const usage = commandLineUsage(cli.usage(featureOptionDefinitions)) - - let options = {} const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions) - if (!initOptions.testMode) { - 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) - } - } + let options = initOptions.testMode ? {} : parseCommandLineOptions(allOptionDefinitions, this.view) /* combine in stored config */ options = Object.assign( @@ -93,26 +61,41 @@ class LocalWebServer { */ this.options = options - if (options.view) { - const View = loadModule(options.view) - this.view = new View(this) - } + /** + * Current view. + * @type {View} + */ + this.view = null /* --config */ if (options.config) { - this.view.info(JSON.stringify(options, null, ' ')) + console.error(JSON.stringify(options, null, ' ')) process.exit(0) /* --version */ } else if (options.version) { const pkg = require(path.resolve(__dirname, '..', 'package.json')) - this.view.info(pkg.version) + console.error(pkg.version) process.exit(0) /* --help */ } else if (options.help) { - this.view.info(usage) + console.error(usage) 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 {Server} */ - getServer (onListening) { + getServer () { const app = this.getApplication() const options = this.options @@ -173,21 +156,18 @@ class LocalWebServer { } server.listen(options.port) - if (onListening) server.on('listening', onListening) + // if (onListening) server.on('listening', onListening) + + /* on server-up message */ if (!options.testMode) { server.on('listening', () => { const ipList = getIPList() .map(iface => `[underline]{${server.isHttps ? 'https' : 'http'}://${iface.address}:${options.port}}`) .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 } @@ -234,7 +214,7 @@ function loadStack (modulePath) { process.exit(1) } } else { - const msg = `No module found at: \n${tried.join('\n')}` + const msg = `No module found for: ${modulePath}` console.error(msg) process.exit(1) } @@ -299,4 +279,35 @@ function parseFeaturePaths (configStack) { 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 diff --git a/test/test.js b/test/test.js index 68a6d6d..b1ba7ea 100644 --- a/test/test.js +++ b/test/test.js @@ -12,13 +12,13 @@ test('stack', function (t) { port: 8100, testMode: true }) - const server = ws.getServer(() => { + ws.server.on('listening', () => { return request('http://localhost:8100/') .then(c.checkResponse(t, 200, /1234512345/)) - .then(server.close.bind(server)) + .then(ws.server.close.bind(ws.server)) .catch(err => { t.fail(err.message) - server.close() + ws.server.close() }) }) }) @@ -34,13 +34,13 @@ test('https', function (t) { const url = require('url') const reqOptions = url.parse('https://localhost:8100/') reqOptions.rejectUnauthorized = false - const server = ws.getServer(() => { + ws.server.on('listening', () => { return request(reqOptions) .then(c.checkResponse(t, 200, /1234512345/)) - .then(server.close.bind(server)) + .then(ws.server.close.bind(ws.server)) .catch(err => { t.fail(err.message) - server.close() + ws.server.close() }) }) })