From f18a07ba5a329e1ce836dcbe8af7d54a6c00becb Mon Sep 17 00:00:00 2001 From: Lloyd Brookes Date: Mon, 13 Mar 2017 23:44:11 +0000 Subject: [PATCH] switch to using lws.. clean up --- .travis.yml | 4 +- LICENSE | 2 +- README.md | 2 +- bin/cli.js | 3 +- lib/cli-data.js | 87 ------------- lib/feature.js | 47 ------- lib/local-web-server.js | 332 +----------------------------------------------- package.json | 26 +--- ssl/127.0.0.1.crt | 20 --- ssl/127.0.0.1.key | 27 ---- 10 files changed, 15 insertions(+), 535 deletions(-) delete mode 100644 lib/cli-data.js delete mode 100644 lib/feature.js delete mode 100644 ssl/127.0.0.1.crt delete mode 100644 ssl/127.0.0.1.key diff --git a/.travis.yml b/.travis.yml index 56c2d58..fb0b4ac 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,3 @@ language: node_js node_js: - - 4 - - 5 - - 6 + - 7 diff --git a/LICENSE b/LICENSE index 677f7e8..8f01d24 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ The MIT License (MIT) -Copyright (c) 2013-16 Lloyd Brookes <75pound@gmail.com> +Copyright (c) 2013-17 Lloyd Brookes <75pound@gmail.com> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 1b8a224..cc529ac 100644 --- a/README.md +++ b/README.md @@ -102,4 +102,4 @@ $ npm install -g local-web-server ``` * * * -© 2013-16 Lloyd Brookes <75pound@gmail.com>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). +© 2013-17 Lloyd Brookes <75pound@gmail.com>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown). diff --git a/bin/cli.js b/bin/cli.js index 019ce92..f8c6095 100755 --- a/bin/cli.js +++ b/bin/cli.js @@ -1,4 +1,5 @@ #!/usr/bin/env node 'use strict' const LocalWebServer = require('../') -new LocalWebServer() +const localWebServer = new LocalWebServer() +localWebServer.start() diff --git a/lib/cli-data.js b/lib/cli-data.js deleted file mode 100644 index f232d25..0000000 --- a/lib/cli-data.js +++ /dev/null @@ -1,87 +0,0 @@ -exports.optionDefinitions = [ - { - name: 'port', alias: 'p', type: Number, defaultOption: true, - description: 'Web server port.', group: 'server' - }, - { - name: 'stack', type: String, multiple: true, - description: 'Feature stack.', group: 'server' - }, - { - name: 'key', type: String, typeLabel: '[underline]{file}', group: 'server', - description: 'SSL key. Supply along with --cert to launch a https server.' - }, - { - name: 'cert', type: String, typeLabel: '[underline]{file}', group: 'server', - description: 'SSL cert. Supply along with --key to launch a https server.' - }, - { - name: 'https', type: Boolean, group: 'server', - description: 'Enable HTTPS using a built-in key and cert, registered to the domain 127.0.0.1.' - }, - { - name: 'help', alias: 'h', type: Boolean, - description: 'Print these usage instructions.', group: 'misc' - }, - { - name: 'view', type: String, - description: 'Custom view', group: 'misc' - }, - { - name: 'config', type: Boolean, - description: 'Print the stored config.', group: 'misc' - }, - { - name: 'config-file', type: String, - description: 'Config file to use', group: 'misc' - }, - { - name: 'verbose', type: Boolean, alias: 'v', - description: 'Verbose output.', group: 'misc' - }, - { - name: 'debug', type: Boolean, - description: 'Very verbose output, intended for debugging.', group: 'misc' - }, - { - name: 'version', type: Boolean, - description: 'Print the version number.', group: 'misc' - } -] - -function usage (middlewareDefinitions) { - return [ - { - header: 'local-web-server', - content: 'A simple web-server for productive front-end development.' - }, - { - header: 'Synopsis', - content: [ - '$ ws [--verbose] [] []', - '$ ws --config', - '$ ws --help' - ] - }, - { - header: 'Server', - optionList: exports.optionDefinitions, - group: 'server' - }, - { - header: 'Middleware', - optionList: middlewareDefinitions, - group: 'middleware' - }, - { - header: 'Misc', - optionList: exports.optionDefinitions, - group: 'misc' - }, - { - content: 'Project home: [underline]{https://github.com/75lb/local-web-server}' - } - ] -} - -exports.usage = usage diff --git a/lib/feature.js b/lib/feature.js deleted file mode 100644 index d3e7198..0000000 --- a/lib/feature.js +++ /dev/null @@ -1,47 +0,0 @@ -'use strict' - -/** - * Feature interface. - */ -class Feature { - /** - * localWebServer instance passed to constructor in case feature needs access to http server instance. - */ - constructor (localWebServer) {} - - /** - * Return one or more options definitions to collect command-line input - * @returns {OptionDefinition|OptionDefinition[]} - */ - optionDefinitions () {} - - /** - * Return one of more middleware functions with three args (req, res and next). Can be created by express, Koa or hand-rolled. - */ - middleware (options) {} - - expandStack () { - const flatten = require('reduce-flatten') - - if (this.stack) { - const featureStack = this.stack() - .map(Feature => new Feature()) - - this.optionDefinitions = function () { - return featureStack - .map(feature => feature.optionDefinitions && feature.optionDefinitions()) - .filter(definitions => definitions) - .reduce(flatten, []) - } - this.middleware = function (options, view) { - return featureStack - .map(feature => feature.middleware(options, view)) - .reduce(flatten, []) - .filter(mw => mw) - } - } - return this - } -} - -module.exports = Feature diff --git a/lib/local-web-server.js b/lib/local-web-server.js index d2735bc..c477536 100644 --- a/lib/local-web-server.js +++ b/lib/local-web-server.js @@ -1,10 +1,5 @@ -#!/usr/bin/env node 'use strict' - -const path = require('path') -const flatten = require('reduce-flatten') -const arrayify = require('array-back') -const ansi = require('ansi-escape-sequences') +const Lws = require('lws') /** * @module local-web-server @@ -12,329 +7,12 @@ const ansi = require('ansi-escape-sequences') /** * @alias module:local-web-server - * @extends module:middleware-stack - */ -class LocalWebServer { - - /** - * @param [options] {object} - Server options - * @param [options.port} {number} - Port - * @param [options.stack} {string[]|Features[]} - Port - */ - constructor (initOptions) { - initOptions = initOptions || {} - const commandLineUsage = require('command-line-usage') - const CliView = require('./cli-view') - const cli = require('../lib/cli-data') - - /* get stored config */ - const loadConfig = require('config-master') - const stored = loadConfig('local-web-server') - - /* read the config and command-line for feature paths */ - const featurePaths = parseFeaturePaths(initOptions.stack || stored.stack) - - /** - * Loaded feature modules - * @type {Feature[]} - */ - this.features = this._buildFeatureStack(featurePaths) - - /* gather feature optionDefinitions and parse the command line */ - const featureOptionDefinitions = gatherOptionDefinitions(this.features) - const usage = commandLineUsage(cli.usage(featureOptionDefinitions)) - const allOptionDefinitions = cli.optionDefinitions.concat(featureOptionDefinitions) - let options = initOptions.testMode ? {} : parseCommandLineOptions(allOptionDefinitions, this.view) - - /* combine in stored config */ - options = Object.assign( - { port: 8000 }, - initOptions, - stored, - options.server, - options.middleware, - options.misc - ) - - /** - * Config - * @type {object} - */ - this.options = options - - /** - * Current view. - * @type {View} - */ - this.view = null - - /* --config */ - if (options.config) { - console.error(JSON.stringify(options, null, ' ')) - process.exit(0) - - /* --version */ - } else if (options.version) { - const pkg = require(path.resolve(__dirname, '..', 'package.json')) - console.error(pkg.version) - process.exit(0) - - /* --help */ - } else if (options.help) { - 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) - } - - for (const feature of this.features) { - if (feature.ready) { - feature.ready(this) - } - } - } - } - - /** - * 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 () { - const Koa = require('koa') - const app = new Koa() - const compose = require('koa-compose') - const convert = require('koa-convert') - - const middlewareStack = this.features - .filter(mw => mw.middleware) - .map(mw => mw.middleware(this.options, this)) - .reduce(flatten, []) - .filter(mw => mw) - .map(convert) - - app.use(compose(middlewareStack)) - app.on('error', err => { - console.error(ansi.format(err.stack, 'red')) - }) - return app.callback() - } - - /** - * Returns a listening server which processes requests using the middleware supplied. - * @returns {Server} - */ - getServer () { - const app = this.getApplication() - const options = this.options - - let key = options.key - let cert = options.cert - - if (options.https && !(key && cert)) { - key = path.resolve(__dirname, '..', 'ssl', '127.0.0.1.key') - cert = path.resolve(__dirname, '..', 'ssl', '127.0.0.1.crt') - } - - let server = null - if (key && cert) { - const fs = require('fs') - const serverOptions = { - key: fs.readFileSync(key), - cert: fs.readFileSync(cert) - } - - const https = require('https') - server = https.createServer(serverOptions, app) - server.isHttps = true - } else { - const http = require('http') - server = http.createServer(app) - } - - server.listen(options.port) - // 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(', ') - console.error('Serving at', ansi.format(ipList)) - }) - } - - return server - } - - /** - * Returns an array of Feature instances, given their module paths/names. - * @return {feature[]} - */ - _buildFeatureStack (featurePaths) { - const FeatureBase = require('./feature') - return featurePaths - .map(featurePath => loadFeature(featurePath)) - .map(Feature => new Feature(this)) - .map(feature => FeatureBase.prototype.expandStack.call(feature)) - } -} - -/** - * Load a module and verify it's of the correct type - * @returns {Feature} - */ -function loadFeature (modulePath) { - const isModule = module => module.prototype && (module.prototype.middleware || module.prototype.stack || module.prototype.ready) - 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 for: ${modulePath}` - console.error(msg) - process.exit(1) - } - return module -} - -/** - * Returns a module, loaded by the first to succeed from - * - direct path - * - 'node_modules/local-web-server-' + path, from current folder upward - * - 'node_modules/' + path, from current folder upward - * - also search local-web-server project node_modules? (e.g. to search for a feature module without need installing it locally) - * @returns {object} - */ -function loadModule (modulePath) { - let module - const tried = [] - if (modulePath) { - try { - tried.push(path.resolve(modulePath)) - module = require(path.resolve(modulePath)) - } catch (err) { - if (!(err && err.code === 'MODULE_NOT_FOUND')) { - throw err - } - const walkBack = require('walk-back') - const foundPath = walkBack(process.cwd(), path.join('node_modules', 'local-web-server-' + modulePath)) - tried.push('local-web-server-' + modulePath) - if (foundPath) { - module = require(foundPath) - } else { - const foundPath2 = walkBack(process.cwd(), path.join('node_modules', modulePath)) - tried.push(modulePath) - if (foundPath2) { - module = require(foundPath2) - } else { - const foundPath3 = walkBack(path.resolve(__filename, '..'), path.join('node_modules', 'local-web-server-' + modulePath)) - if (foundPath3) { - return require(foundPath3) - } else { - const foundPath4 = walkBack(path.resolve(__filename, '..'), path.join('node_modules', modulePath)) - if (foundPath4) { - return require(foundPath4) - } - } - } - } - } - } - return module -} - -/** - * Returns an array of available IPv4 network interfaces - * @example - * [ { address: 'mbp.local' }, - * { address: '127.0.0.1', - * netmask: '255.0.0.0', - * family: 'IPv4', - * mac: '00:00:00:00:00:00', - * internal: true }, - * { address: '192.168.1.86', - * netmask: '255.255.255.0', - * family: 'IPv4', - * mac: 'd0:a6:37:e9:86:49', - * internal: false } ] */ -function getIPList () { - const flatten = require('reduce-flatten') - const os = require('os') - - let ipList = Object.keys(os.networkInterfaces()) - .map(key => os.networkInterfaces()[key]) - .reduce(flatten, []) - .filter(iface => iface.family === 'IPv4') - ipList.unshift({ address: os.hostname() }) - return ipList -} - -/* manually scan for any --stack passed, as we may need to display stack options */ -function parseFeaturePaths (configStack) { - const featurePaths = arrayify(configStack) - const featureIndex = process.argv.indexOf('--stack') - if (featureIndex > -1) { - for (var i = featureIndex + 1; i < process.argv.length; i++) { - const featurePath = process.argv[i] - if (/^-/.test(featurePath)) { - break - } else { - featurePaths.push(featurePath) - } - } - } - - /* 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 -} - -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 +class LocalWebServer extends Lws { + constructor () { + super({ + stack: [ 'log', 'static' ] }) -} - -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) } } diff --git a/package.json b/package.json index 86358ff..23e2efc 100644 --- a/package.json +++ b/package.json @@ -30,32 +30,16 @@ "ssl" ], "scripts": { - "test": "tape test/*.js", + "test": "test-runner test/*.js", "docs": "jsdoc2md -t jsdoc2md/api.hbs -p list lib/*.js > doc/api.md; echo", "cover": "istanbul cover ./node_modules/.bin/tape test/*.js && cat coverage/lcov.info | coveralls && rm -rf coverage; echo" }, "repository": "https://github.com/75lb/local-web-server", "author": "Lloyd Brookes <75pound@gmail.com>", "dependencies": { - "ansi-escape-sequences": "^3.0.0", - "array-back": "^1.0.4", - "command-line-args": "^4.0.1", - "command-line-usage": "^4.0.0", - "config-master": "^3.0.0", - "koa": "next", - "koa-compose": "^3.1.0", - "koa-convert": "^1.2.0", - "local-web-server-default-stack": "github:local-web-server/default-stack", - "reduce-flatten": "^1.0.1", - "table-layout": "~0.4.0", - "typical": "^2.6.0", - "walk-back": "^2.0.1" + "local-web-server-log": "file:///Users/lloydb/Documents/lws/log", + "local-web-server-static": "file:///Users/lloydb/Documents/lws/static", + "lws": "^0.8.1" }, - "devDependencies": { - "jsdoc-to-markdown": "^3.0.0", - "koa-cache-control": "^1.0.0", - "koa-livereload": "~0.2.0", - "req-then": "~0.5.1", - "tape": "^4.6.3" - } + "devDependencies": {} } diff --git a/ssl/127.0.0.1.crt b/ssl/127.0.0.1.crt deleted file mode 100644 index bb95c77..0000000 --- a/ssl/127.0.0.1.crt +++ /dev/null @@ -1,20 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIDLjCCAhYCCQC3MW7xH6DDyTANBgkqhkiG9w0BAQUFADBZMQswCQYDVQQGEwJH -QjETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0 -cyBQdHkgTHRkMRIwEAYDVQQDEwkxMjcuMC4wLjEwHhcNMTYwMzEwMTAzMTMwWhcN -MTcwMzEwMTAzMTMwWjBZMQswCQYDVQQGEwJHQjETMBEGA1UECBMKU29tZS1TdGF0 -ZTEhMB8GA1UEChMYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRIwEAYDVQQDEwkx -MjcuMC4wLjEwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDIWz+H5P3P -5/Uixviwbj88y112TBCCdhPizqVb8f7EgTgeIA0Jpqe2+RR9siawwUAX9nqRUB1g -vgLZE4NZS+5ICN3JqkC4EysDS6VtIVf2OAuem3kdKaHSLl4JabsmBprgf2Dtze0i -eX5+Pur5Pi2BEAYNCUKzC4OuVaP//3jNWD/Xp6eHBbC76L03EIGPxytYf5wkITbY -wCjIVQw0Mq+WsV9eJRuLT4bnoeefCK+zPeTEQ6o+3SFkTkhqfsTF83sHvgcy1T4u -7f+GZ9TYiaUi/1OVvfUg2FdGDAlKtVVH/t+pAg0M2hGr7vTClSVOg/qiY3ktEaYW -FvcxJa65DyQNAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAFwrxsqXwA6BTFTvRYi1 -s4tqos8loaZxE4eug96mL7qRvYzhDY+nDluiDEjMapACQOQaGIV+uMraOBk9yCUo -BsYqLcBLUTKBZvIMEmYmlUKxZrtFLVo1y6p7CJM9luwUEpbPRivA/Vofk9zlq9B1 -AeVjDtqK/iZbO05qN18sgp7VPZZc4zRLOYUGfiUfX6r+dvDAPx/NBFM3vAEyYSur -Jqa2CdsiUXo08CytgIaxGgF1DJxLqoA4SZagSUWWcuOlDzLSooNlcW/zfEfQfeMQ -h7SbUtD4IJuKNd0BCeWMyVN7rM91zp9tf7713l+skbo5wIJAsNQAa2o8uRIXLjNX -jy4= ------END CERTIFICATE----- diff --git a/ssl/127.0.0.1.key b/ssl/127.0.0.1.key deleted file mode 100644 index 5746322..0000000 --- a/ssl/127.0.0.1.key +++ /dev/null @@ -1,27 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIEpAIBAAKCAQEAyFs/h+T9z+f1Isb4sG4/PMtddkwQgnYT4s6lW/H+xIE4HiAN -CaantvkUfbImsMFAF/Z6kVAdYL4C2RODWUvuSAjdyapAuBMrA0ulbSFX9jgLnpt5 -HSmh0i5eCWm7Jgaa4H9g7c3tInl+fj7q+T4tgRAGDQlCswuDrlWj//94zVg/16en -hwWwu+i9NxCBj8crWH+cJCE22MAoyFUMNDKvlrFfXiUbi0+G56Hnnwivsz3kxEOq -Pt0hZE5Ian7ExfN7B74HMtU+Lu3/hmfU2ImlIv9Tlb31INhXRgwJSrVVR/7fqQIN -DNoRq+70wpUlToP6omN5LRGmFhb3MSWuuQ8kDQIDAQABAoIBAQDFiBkBvQVzxegM -ColDQN597K5PpDyesxV2BnBHTzXzvMZ8BPN1sWYm4jmOl2bH2y96sJo0y/y61Xrv -U+qqzk61nHA1k/JMyTEeBaWqCzay3JywGe51jwcotmgl9aT6n4ZwkYUZz23dEFVi -2FtHskKgvRCKJ7gn19FSvsJ68P/Dyl7H3/XGucj/7S+0JK3tb7BJ/ce68XABF99x -hvvkaWtxv0WNX2LWDyLVwv3T5i+pq4sscd9dmxwwCb1N3Lm3SkAOqH7BINia/qud -BLLJwHamzToWH7NTSWqrM4X9I7mI3zcMfOGeH9yZEFhB3cVu63V4yHfnGGqEiUOk -21fA+iLBAoGBAPXwZskl+nM0Z7yadaOOCqjRMdvPIgHOvQvjKtQJ/E7I4sH3ZBfO -4YPU0pErV4rbOyv6TZcUQwmcHmepK5wcHjj52+vgDQMr+K1wjRai8WdapKgXi39n -5IgPD0y5Hgi7qUJI6w67ybkawgknL8hm6TwtxfbKtVoJ5BVgS1UmFMYRAoGBANCN -e3X685aGqsyuCVU3bXnZVGyromiCDQge3NGuUFqaSCA0uK9/Q4HuStktH7LiRoZo -UwBmdnF0Wa4hMcjBBONv1bc8S43CdoJC3LR6DdFL8j4YarUSXnTFRo+MnKIbNwQh -378E1ws+dsOGrJ+IIqQJHfzsnG+vvb9PUleXgtI9AoGAFOBKKUri/oJ1R8oosDBv -cTMIs2rarSKaY3bt/L+4PgvJS8OvKGI0PFeFZDM0pCHF3Q7LJUbgBeHNpujyPbcZ -TabP5y7Gi/1gh4BlSYWdTjOghHAzNCZifLYii1WvWfhr/qdn5IFGN0MxM0uzP6SU -qboM8sz0JedvB+17l4e6/bECgYBYI0MHJGyns/ghEngtRISG13tfhdXYVwYM5YYr -M4EQGV3cBov610z/b2bAi9p2rjxh91sEs0jhP+vatHqmvjRDrnLiwp+npISTHpDJ -0T9fsboJ1iXaqo2yyeC9MA7OT7QbkflOcEw1m0tz7MmtjkodiyDaUGD4rowBexNw -oz6NfQKBgQCbhTO6MNmdeQrJn/ojR6HipypKqpVXqqqraAgU5BapaH0ZZwXkXDAM -36ldQviX8UnPNFqHj7jzVSNyWsgmKHnXFmdTEBYTd+0b+WEyn9FR/8kBlxHFR7Nc -AcAF7XF79pkJM31e6GCwFymYPbFJEL4TkWSOnPkypGY6IXHp57bKzA== ------END RSA PRIVATE KEY-----