Browse Source

rewrite.. examples.. docs.. refactor

master
Lloyd Brookes 9 years ago
parent
commit
28e1cfdae9
  1. 13
      README.md
  2. 8
      bin/cli.js
  3. 5
      example/rewrite/.local-web-server.json
  4. 10
      example/rewrite/index.html
  5. 7
      example/rewrite/styles/style.css
  6. 12
      jsdoc2md/README.hbs
  7. 8
      lib/cli-options.js
  8. 110
      lib/local-web-server.js

13
README.md

@ -37,11 +37,11 @@ $ ws --spa index.html
By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Found` as there is no file at that location on disk. By marking `index.html` as the SPA you create this rule: By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Found` as there is no file at that location on disk. By marking `index.html` as the SPA you create this rule:
*if a file with that url exists (e.g. `/css/style.css`) then serve it, if it does not (e.g. `/login`) then pass it to the SPA.*
*if a static file at the requested path exists (e.g. `/css/style.css`) then serve it, if it does not (e.g. `/login`) then serve the SPA.*
### Access Control ### Access Control
Access to all files is allowed, beside those you forbid (e.g. config files):
Access to all files is allowed, beside those in the forbidden list (e.g. config files):
```sh ```sh
$ ws --forbid .json .yml $ ws --forbid .json .yml
serving at http://localhost:8000 serving at http://localhost:8000
@ -54,6 +54,8 @@ When urls don't map to your directory structure, rewrite:
$ ws --rewrite /css=>/build/css $ ws --rewrite /css=>/build/css
``` ```
### Proxy
Rewrite to remote servers (proxy): Rewrite to remote servers (proxy):
```sh ```sh
$ ws --rewrite "/api => http://api.example.com/api" \ $ ws --rewrite "/api => http://api.example.com/api" \
@ -61,9 +63,6 @@ $ ws --rewrite "/api => http://api.example.com/api" \
"/user/:project/repo -> https://api.github.com/repos/:project" "/user/:project/repo -> https://api.github.com/repos/:project"
``` ```
### Mock Responses
*Coming soon*.
### Stored config ### Stored config
Always use this port and blacklist? Persist it to the config: Always use this port and blacklist? Persist it to the config:
@ -87,6 +86,9 @@ serving at http://localhost:8000
::1 - - [16/Nov/2015:11:16:52 +0000] "GET / HTTP/1.1" 200 12290 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2562.0 Safari/537.36" ::1 - - [16/Nov/2015:11:16:52 +0000] "GET / HTTP/1.1" 200 12290 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2562.0 Safari/537.36"
``` ```
### Mock Responses
*Coming soon*.
### Other features ### Other features
Compression, caching, simple statistics view, log, override mime types. Compression, caching, simple statistics view, log, override mime types.
@ -208,6 +210,7 @@ Returns a Koa application
**Example** **Example**
```js ```js
const localWebServer = require('local-web-server') const localWebServer = require('local-web-server')
localWebServer().listen(8000)
``` ```
## Composition ## Composition

8
bin/cli.js

@ -43,7 +43,8 @@ localWebServer({
forbid: options.server.forbid.map(regexp => RegExp(regexp, "i")), forbid: options.server.forbid.map(regexp => RegExp(regexp, "i")),
proxyRoutes: options.server.proxyRoutes, proxyRoutes: options.server.proxyRoutes,
spa: options.server.spa, spa: options.server.spa,
'no-cache': options.server['no-cache']
'no-cache': options.server['no-cache'],
rewrite: options.server.rewrite
}).listen(options.server.port, onServerUp) }).listen(options.server.port, onServerUp)
function halt (err) { function halt (err) {
@ -74,10 +75,9 @@ function collectOptions () {
const builtIn = { const builtIn = {
port: 8000, port: 8000,
root: process.cwd(), // root dir when using multiple static dirs
directory: process.cwd(), directory: process.cwd(),
proxyRoutes: [],
forbid: []
forbid: [],
proxyRoutes: []
} }
/* override built-in defaults with stored config and then command line args */ /* override built-in defaults with stored config and then command line args */

5
example/rewrite/.local-web-server.json

@ -0,0 +1,5 @@
{
"rewrite": [
{ "from": "/css/*", "to": "/styles/$1" }
]
}

10
example/rewrite/index.html

@ -0,0 +1,10 @@
<head>
<link rel="stylesheet" href="css/style.css">
</head>
<h1>Amazing Page</h1>
<p>
With a freaky triangle..
</p>
<svg width="500" height="500">
<polygon points="250,0 0,500 500,500"></polygon>
</svg>

7
example/rewrite/styles/style.css

@ -0,0 +1,7 @@
body {
background-color: #AA3939;
color: #FFE2E2
}
svg {
fill: #000
}

12
jsdoc2md/README.hbs

@ -37,11 +37,11 @@ $ ws --spa index.html
By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Found` as there is no file at that location on disk. By marking `index.html` as the SPA you create this rule: By default, typical SPA urls (e.g. `/user/1`, `/login`) would return `404 Not Found` as there is no file at that location on disk. By marking `index.html` as the SPA you create this rule:
*if a file with that url exists (e.g. `/css/style.css`) then serve it, if it does not (e.g. `/login`) then pass it to the SPA.*
*if a static file at the requested path exists (e.g. `/css/style.css`) then serve it, if it does not (e.g. `/login`) then serve the SPA.*
### Access Control ### Access Control
Access to all files is allowed, beside those you forbid (e.g. config files):
Access to all files is allowed, beside those in the forbidden list (e.g. config files):
```sh ```sh
$ ws --forbid .json .yml $ ws --forbid .json .yml
serving at http://localhost:8000 serving at http://localhost:8000
@ -54,6 +54,8 @@ When urls don't map to your directory structure, rewrite:
$ ws --rewrite /css=>/build/css $ ws --rewrite /css=>/build/css
``` ```
### Proxy
Rewrite to remote servers (proxy): Rewrite to remote servers (proxy):
```sh ```sh
$ ws --rewrite "/api => http://api.example.com/api" \ $ ws --rewrite "/api => http://api.example.com/api" \
@ -61,9 +63,6 @@ $ ws --rewrite "/api => http://api.example.com/api" \
"/user/:project/repo -> https://api.github.com/repos/:project" "/user/:project/repo -> https://api.github.com/repos/:project"
``` ```
### Mock Responses
*Coming soon*.
### Stored config ### Stored config
Always use this port and blacklist? Persist it to the config: Always use this port and blacklist? Persist it to the config:
@ -87,6 +86,9 @@ serving at http://localhost:8000
::1 - - [16/Nov/2015:11:16:52 +0000] "GET / HTTP/1.1" 200 12290 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2562.0 Safari/537.36" ::1 - - [16/Nov/2015:11:16:52 +0000] "GET / HTTP/1.1" 200 12290 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2562.0 Safari/537.36"
``` ```
### Mock Responses
*Coming soon*.
### Other features ### Other features
Compression, caching, simple statistics view, log, override mime types. Compression, caching, simple statistics view, log, override mime types.

8
lib/cli-options.js

@ -5,7 +5,7 @@ module.exports = {
description: 'Web server port', group: 'server' description: 'Web server port', group: 'server'
}, },
{ {
name: 'log-format', alias: 'l', type: String,
name: 'log-format', alias: 'f', type: String,
description: "If a format is supplied an access log is written to stdout. If not, a statistics view is displayed. Use a preset ('none', 'dev','combined', 'short', 'tiny' or 'logstalgia') or supply a custom format (e.g. ':method -> :url').", group: 'server' description: "If a format is supplied an access log is written to stdout. If not, a statistics view is displayed. Use a preset ('none', 'dev','combined', 'short', 'tiny' or 'logstalgia') or supply a custom format (e.g. ':method -> :url').", group: 'server'
}, },
{ {
@ -17,7 +17,7 @@ module.exports = {
description: 'Enable gzip compression, reduces bandwidth.', group: 'server' description: 'Enable gzip compression, reduces bandwidth.', group: 'server'
}, },
{ {
name: 'forbid', alias: 'f', type: String, multiple: true, typeLabel: '[underline]{regexp} ...',
name: 'forbid', alias: 'b', type: String, multiple: true, typeLabel: '[underline]{regexp} ...',
description: 'A list of forbidden routes', group: 'server' description: 'A list of forbidden routes', group: 'server'
}, },
{ {
@ -25,6 +25,10 @@ module.exports = {
description: 'Disable etag-based caching - forces loading from disk each request.', group: 'server' description: 'Disable etag-based caching - forces loading from disk each request.', group: 'server'
}, },
{ {
name: 'rewrite', alias: 'r', type: String, multiple: true, typeLabel: '[underline]{expression} ...',
description: 'A list of URL rewrite rules', group: 'server'
},
{
name: 'help', alias: 'h', type: Boolean, name: 'help', alias: 'h', type: Boolean,
description: 'Print these usage instructions', group: 'misc' description: 'Print these usage instructions', group: 'misc'
}, },

110
lib/local-web-server.js

@ -3,20 +3,11 @@ const path = require('path')
const http = require('http') const http = require('http')
const url = require('url') const url = require('url')
const Koa = require('koa') const Koa = require('koa')
const serve = require('koa-static')
const convert = require('koa-convert') const convert = require('koa-convert')
const serveIndex = require('koa-serve-index')
const morgan = require('koa-morgan')
const compress = require('koa-compress')
const streamLogStats = require('stream-log-stats')
const cors = require('kcors') const cors = require('kcors')
const conditional = require('koa-conditional-get');
const etag = require('koa-etag');
const _ = require('koa-route') const _ = require('koa-route')
const mount = require('koa-mount') const mount = require('koa-mount')
const httpProxy = require('http-proxy')
const pathToRegexp = require('path-to-regexp') const pathToRegexp = require('path-to-regexp')
const send = require('koa-send')
/** /**
* @module local-web-server * @module local-web-server
@ -31,6 +22,7 @@ module.exports = localWebServer
* @alias module:local-web-server * @alias module:local-web-server
* @example * @example
* const localWebServer = require('local-web-server') * const localWebServer = require('local-web-server')
* localWebServer().listen(8000)
*/ */
function localWebServer (options) { function localWebServer (options) {
options = Object.assign({ options = Object.assign({
@ -40,7 +32,8 @@ function localWebServer (options) {
compress: false, compress: false,
forbid: [], forbid: [],
directories: [], directories: [],
proxyRoutes: []
proxyRoutes: [],
rewrite: []
}, options) }, options)
const log = options.log const log = options.log
@ -50,33 +43,44 @@ function localWebServer (options) {
const _use = app.use const _use = app.use
app.use = x => _use.call(app, convert(x)) app.use = x => _use.call(app, convert(x))
const proxy = httpProxy.createProxyServer({
changeOrigin: true
})
/* Proxy routes */ /* Proxy routes */
options.proxyRoutes.forEach(route => {
app.use(_.all(route.from, function * () {
const keys = []
route.re = pathToRegexp(route.from, keys)
route.new = route.to
this.response = false
keys.forEach((key, index) => {
const re = {
token: RegExp('\\$\\{' + key.name + '\\}', 'g'),
index: RegExp('\\$\\{' + index + '\\}', 'g')
}
route.new = route.new
.replace(re.token, arguments[index] || '')
.replace(re.index, arguments[index] || '')
})
proxy.once('proxyReq', function (proxyReq) {
proxyReq.path = url.parse(route.new).path;
})
proxy.web(this.req, this.res, { target: route.new })
}))
})
if (options.proxyRoutes.length) {
const httpProxy = require('http-proxy')
const proxy = httpProxy.createProxyServer({
changeOrigin: true
})
options.proxyRoutes.forEach(route => {
app.use(_.all(route.from, function * () {
const keys = []
route.re = pathToRegexp(route.from, keys)
route.new = route.to
this.response = false
keys.forEach((key, index) => {
const re = {
token: RegExp('\\$\\{' + key.name + '\\}', 'g'),
index: RegExp('\\$\\{' + index + '\\}', 'g')
}
route.new = route.new
.replace(re.token, arguments[index] || '')
.replace(re.index, arguments[index] || '')
})
proxy.once('proxyReq', function (proxyReq) {
proxyReq.path = url.parse(route.new).path;
})
proxy.web(this.req, this.res, { target: route.new })
}))
})
}
/* Rewrite rules */
if (options.rewrite && options.rewrite.length) {
const rewrite = require('koa-rewrite')
options.rewrite.forEach(rule => {
app.use(rewrite(rule.from, rule.to))
})
}
/* CORS: allow from any origin */ /* CORS: allow from any origin */
app.use(cors()) app.use(cors())
@ -94,6 +98,8 @@ function localWebServer (options) {
/* Cache */ /* Cache */
if (!options['no-cache']) { if (!options['no-cache']) {
const conditional = require('koa-conditional-get');
const etag = require('koa-etag');
app.use(conditional()) app.use(conditional())
app.use(etag()) app.use(etag())
} }
@ -113,45 +119,41 @@ function localWebServer (options) {
/* compress response */ /* compress response */
if (options.compress) { if (options.compress) {
const compress = require('koa-compress')
app.use(compress()) app.use(compress())
} }
/* special case log formats */
if (log.format) {
if (log.format === 'none'){
log.format = undefined
/* Logging */
if (log.format !== 'none') {
const morgan = require('koa-morgan')
if (!log.format) {
const streamLogStats = require('stream-log-stats')
log.options.stream = streamLogStats({ refreshRate: 500 })
app.use(morgan.middleware('common', log.options))
} else if (log.format === 'logstalgia') { } else if (log.format === 'logstalgia') {
morgan.token('date', logstalgiaDate) morgan.token('date', logstalgiaDate)
log.format = 'combined'
app.use(morgan.middleware('combined', log.options))
} else {
app.use(morgan.middleware(log.format, log.options))
} }
/* if no specific log format was requested, show log stats */
} else {
log.format = 'common'
log.options.stream = streamLogStats({ refreshRate: 500 })
} }
if (log.format) app.use(morgan.middleware(log.format, log.options))
// options.static.root = [
// { route: '/one', root: 'lib' },
// { route: '/two', root: 'node_modules' }
// ]
/* serve static files */ /* serve static files */
if (options.static.root) { if (options.static.root) {
const serve = require('koa-static')
app.use(serve(options.static.root, options.static.options)) app.use(serve(options.static.root, options.static.options))
// options.static.root.forEach(config => {
// app.use(mount(config.route, serve(config.root)))
// app.use(mount(config.route, serveIndex(config.root)))
// })
} }
/* serve directory index */ /* serve directory index */
if (options.serveIndex.path) { if (options.serveIndex.path) {
const serveIndex = require('koa-serve-index')
app.use(serveIndex(options.serveIndex.path, options.serveIndex.options)) app.use(serveIndex(options.serveIndex.path, options.serveIndex.options))
} }
/* for any URL not matched by static (e.g. `/search`), serve the SPA */ /* for any URL not matched by static (e.g. `/search`), serve the SPA */
if (options.spa) { if (options.spa) {
const send = require('koa-send')
app.use(_.all('*', function * () { app.use(_.all('*', function * () {
yield send(this, options.spa, { root: process.cwd() }) yield send(this, options.spa, { root: process.cwd() })
})) }))

Loading…
Cancel
Save