You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

199 lines
6.0 KiB

10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
  1. 'use strict'
  2. const path = require('path')
  3. const url = require('url')
  4. /**
  5. * @module local-web-server
  6. */
  7. module.exports = localWebServer
  8. /**
  9. * Returns a Koa application you can launch or mix into an existing app.
  10. *
  11. * @param [options] {object} - options
  12. * @param [options.static] {object} - koa-static config
  13. * @param [options.static.root=.] {string} - root directory
  14. * @param [options.static.options] {string} - [options](https://github.com/koajs/static#options)
  15. * @param [options.serveIndex] {object} - koa-serve-index config
  16. * @param [options.serveIndex.path=.] {string} - root directory
  17. * @param [options.serveIndex.options] {string} - [options](https://github.com/expressjs/serve-index#options)
  18. * @param [options.forbid] {string[]} - A list of forbidden routes, each route being an [express route-path](http://expressjs.com/guide/routing.html#route-paths).
  19. * @param [options.spa] {string} - specify an SPA file to catch requests for everything but static assets.
  20. * @param [options.log] {object} - [morgan](https://github.com/expressjs/morgan) config
  21. * @param [options.log.format] {string} - [log format](https://github.com/expressjs/morgan#predefined-formats)
  22. * @param [options.log.options] {object} - [options](https://github.com/expressjs/morgan#options)
  23. * @param [options.compress] {boolean} - Serve gzip-compressed resources, where applicable
  24. * @param [options.mime] {object} - A list of mime-type overrides, passed directly to [mime.define()](https://github.com/broofa/node-mime#mimedefine)
  25. * @param [options.rewrite] {module:local-web-server~rewriteRule[]} - One or more rewrite rules
  26. * @param [options.verbose] {boolean} - Print detailed output, useful for debugging
  27. *
  28. * @alias module:local-web-server
  29. * @return {external:KoaApplication}
  30. * @example
  31. * const localWebServer = require('local-web-server')
  32. * localWebServer().listen(8000)
  33. */
  34. function localWebServer (options) {
  35. options = Object.assign({
  36. static: {},
  37. serveIndex: {},
  38. spa: null,
  39. log: {},
  40. compress: false,
  41. mime: {},
  42. forbid: [],
  43. rewrite: [],
  44. verbose: false
  45. }, options)
  46. if (options.verbose) {
  47. process.env.DEBUG = '*'
  48. }
  49. const debug = require('debug')('local-web-server')
  50. const Koa = require('koa')
  51. const convert = require('koa-convert')
  52. const cors = require('kcors')
  53. const _ = require('koa-route')
  54. const json = require('koa-json')
  55. const bodyParser = require('koa-bodyparser')
  56. const mw = require('./middleware')
  57. const log = options.log
  58. log.options = log.options || {}
  59. const app = new Koa()
  60. const _use = app.use
  61. app.use = x => _use.call(app, convert(x))
  62. if (options.verbose && !log.format) {
  63. log.format = 'none'
  64. }
  65. /* CORS: allow from any origin */
  66. app.use(cors())
  67. /* pretty print JSON */
  68. app.use(json())
  69. /* request body parser */
  70. app.use(bodyParser())
  71. /* rewrite rules */
  72. if (options.rewrite && options.rewrite.length) {
  73. options.rewrite.forEach(route => {
  74. if (route.to) {
  75. if (url.parse(route.to).host) {
  76. debug('proxy rewrite', `${route.from} -> ${route.to}`)
  77. app.use(_.all(route.from, mw.proxyRequest(route, app)))
  78. } else {
  79. const rewrite = require('koa-rewrite')
  80. const rmw = rewrite(route.from, route.to)
  81. rmw._name = 'rewrite'
  82. app.use(rmw)
  83. }
  84. }
  85. })
  86. }
  87. /* path blacklist */
  88. if (options.forbid.length) {
  89. debug('forbid', options.forbid.join(', '))
  90. app.use(mw.blacklist(options.forbid))
  91. }
  92. /* Cache */
  93. if (!options['no-cache']) {
  94. const conditional = require('koa-conditional-get')
  95. const etag = require('koa-etag')
  96. app.use(conditional())
  97. app.use(etag())
  98. }
  99. /* mime-type overrides */
  100. if (options.mime) {
  101. debug('mime override', JSON.stringify(options.mime))
  102. app.use(mw.mime(options.mime))
  103. }
  104. /* compress response */
  105. if (options.compress) {
  106. const compress = require('koa-compress')
  107. debug('compression', 'enabled')
  108. app.use(compress())
  109. }
  110. /* Logging */
  111. if (log.format !== 'none') {
  112. const morgan = require('koa-morgan')
  113. if (!log.format) {
  114. const streamLogStats = require('stream-log-stats')
  115. log.options.stream = streamLogStats({ refreshRate: 500 })
  116. app.use(morgan.middleware('common', log.options))
  117. } else if (log.format === 'logstalgia') {
  118. morgan.token('date', logstalgiaDate)
  119. app.use(morgan.middleware('combined', log.options))
  120. } else {
  121. app.use(morgan.middleware(log.format, log.options))
  122. }
  123. }
  124. /* Mock Responses */
  125. app.use(mw.mockResponses({ root: options.static.root }))
  126. /* serve static files */
  127. if (options.static.root) {
  128. const serve = require('koa-static')
  129. app.use(serve(options.static.root, options.static.options))
  130. }
  131. /* serve directory index */
  132. if (options.serveIndex.path) {
  133. const serveIndex = require('koa-serve-index')
  134. app.use(serveIndex(options.serveIndex.path, options.serveIndex.options))
  135. }
  136. /* for any URL not matched by static (e.g. `/search`), serve the SPA */
  137. if (options.spa) {
  138. const send = require('koa-send')
  139. debug('SPA', options.spa)
  140. app.use(_.all('*', function * () {
  141. yield send(this, options.spa, { root: path.resolve(options.static.root) || process.cwd() })
  142. }))
  143. }
  144. return app
  145. }
  146. function logstalgiaDate () {
  147. var d = new Date()
  148. return (`${d.getDate()}/${d.getUTCMonth()}/${d.getFullYear()}:${d.toTimeString()}`).replace('GMT', '').replace(' (BST)', '')
  149. }
  150. process.on('unhandledRejection', (reason, p) => {
  151. throw reason
  152. })
  153. /**
  154. * The `from` and `to` routes are specified using [express route-paths](http://expressjs.com/guide/routing.html#route-paths)
  155. *
  156. * @example
  157. * ```json
  158. * {
  159. * "rewrite": [
  160. * { "from": "/css/*", "to": "/build/styles/$1" },
  161. * { "from": "/npm/*", "to": "http://registry.npmjs.org/$1" },
  162. * { "from": "/:user/repos/:name", "to": "https://api.github.com/repos/:user/:name" }
  163. * ]
  164. * }
  165. * ```
  166. *
  167. * @typedef rewriteRule
  168. * @property from {string} - request route
  169. * @property to {string} - target route
  170. */
  171. /**
  172. * @external KoaApplication
  173. * @see https://github.com/koajs/koa/blob/master/docs/api/index.md#application
  174. */