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.

544 lines
17 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
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
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
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. [![view on npm](http://img.shields.io/npm/v/local-web-server.svg)](https://www.npmjs.org/package/local-web-server)
  2. [![npm module downloads](http://img.shields.io/npm/dt/local-web-server.svg)](https://www.npmjs.org/package/local-web-server)
  3. [![Build Status](https://travis-ci.org/75lb/local-web-server.svg?branch=master)](https://travis-ci.org/75lb/local-web-server)
  4. [![Dependency Status](https://david-dm.org/75lb/local-web-server.svg)](https://david-dm.org/75lb/local-web-server)
  5. [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](https://github.com/feross/standard)
  6. ***Requires node v4.0.0 or higher. Install the [previous release](https://github.com/75lb/local-web-server/tree/prev) for older node support.***
  7. # local-web-server
  8. A simple web-server for productive front-end development. Typical use cases:
  9. * Front-end Development
  10. * Static or Single Page App development
  11. * Re-route paths to local or remote resources
  12. * Efficient, predictable, entity-tag-powered conditional requests (no need to 'Disable Cache' in DevTools, slowing page-load down)
  13. * Bundle with your front-end project
  14. * Very little configuration, just a few options
  15. * Outputs a dynamic statistics view to the terminal
  16. * Configurable log output, compatible with [Goaccess, Logstalgia and glTail](https://github.com/75lb/local-web-server/blob/master/doc/visualisation.md)
  17. * Back-end service mocking
  18. * Prototype a web service, microservice, REST API etc.
  19. * Mocks are defined with config (static), or code (dynamic).
  20. * CORS-friendly, all origins allowed by default.
  21. * Proxy server
  22. * Map local routes to remote servers. Removes CORS pain when consuming remote services.
  23. * HTTPS server
  24. * HTTPS is strictly required by some modern techs (ServiceWorker, Media Capture and Streams etc.)
  25. * File sharing
  26. ## Synopsis
  27. local-web-server is a simple command-line tool. To use it, from your project directory run `ws`.
  28. <pre><code>$ ws --help
  29. <strong>local-web-server</strong>
  30. A simple web-server for productive front-end development.
  31. <strong>Synopsis</strong>
  32. $ ws [&lt;server options&gt;]
  33. $ ws --config
  34. $ ws --help
  35. <strong>Server</strong>
  36. -p, --port number Web server port.
  37. -d, --directory path Root directory, defaults to the current directory.
  38. -f, --log-format string If a format is supplied an access log is written to stdout. If
  39. not, a dynamic statistics view is displayed. Use a preset ('none',
  40. 'dev','combined', 'short', 'tiny' or 'logstalgia') or supply a
  41. custom format (e.g. ':method -> :url').
  42. -r, --rewrite expression ... A list of URL rewrite rules. For each rule, separate the 'from'
  43. and 'to' routes with '->'. Whitespace surrounded the routes is
  44. ignored. E.g. '/from -> /to'.
  45. -s, --spa file Path to a Single Page App, e.g. app.html.
  46. -c, --compress Serve gzip-compressed resources, where applicable.
  47. -b, --forbid path ... A list of forbidden routes.
  48. -n, --no-cache Disable etag-based caching -forces loading from disk each request.
  49. --key file SSL key. Supply along with --cert to launch a https server.
  50. --cert file SSL cert. Supply along with --key to launch a https server.
  51. --verbose Verbose output, useful for debugging.
  52. <strong>Misc</strong>
  53. -h, --help Print these usage instructions.
  54. --config Print the stored config.
  55. Project home: https://github.com/75lb/local-web-server
  56. </code></pre>
  57. ## Examples
  58. For the examples below, we assume we're in a project directory looking like this:
  59. ```sh
  60. .
  61. ├── css
  62. │   └── style.css
  63. ├── index.html
  64. └── package.json
  65. ```
  66. All paths/routes are specified using [express syntax](http://expressjs.com/guide/routing.html#route-paths). To run the example projects linked below, clone the project, move into the example directory specified, run `ws`.
  67. ### Static site
  68. Fire up your static site on the default port:
  69. ```sh
  70. $ ws
  71. serving at http://localhost:8000
  72. ```
  73. [Example](https://github.com/75lb/local-web-server/tree/master/example/simple).
  74. ### Single Page Application
  75. You're building a web app with client-side routing, so mark `index.html` as the SPA.
  76. ```sh
  77. $ ws --spa index.html
  78. ```
  79. By default, typical SPA paths (e.g. `/user/1`, `/login`) would return `404 Not Found` as a file does not exist with that path. By marking `index.html` as the SPA you create this rule:
  80. *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 specified SPA and handle the route client-side.*
  81. [Example](https://github.com/75lb/local-web-server/tree/master/example/spa).
  82. ### URL rewriting
  83. Your application requested `/css/style.css` but it's stored at `/build/css/style.css`. To avoid a 404 you need a rewrite rule:
  84. ```sh
  85. $ ws --rewrite '/css/style.css -> /build/css/style.css'
  86. ```
  87. Or, more generally (matching any stylesheet under `/css`):
  88. ```sh
  89. $ ws --rewrite '/css/:stylesheet -> /build/css/:stylesheet'
  90. ```
  91. With a deep CSS directory structure it may be easier to mount the entire contents of `/build/css` to the `/css` path:
  92. ```sh
  93. $ ws --rewrite '/css/* -> /build/css/$1'
  94. ```
  95. this rewrites `/css/a` as `/build/css/a`, `/css/a/b/c` as `/build/css/a/b/c` etc.
  96. #### Proxied requests
  97. If the `to` URL contains a remote host, local-web-server will act as a proxy - fetching and responding with the remote resource.
  98. Mount the npm registry locally:
  99. ```sh
  100. $ ws --rewrite '/npm/* -> http://registry.npmjs.org/$1'
  101. ```
  102. Map local requests for repo data to the Github API:
  103. ```sh
  104. $ ws --rewrite '/:user/repos/:name -> https://api.github.com/repos/:user/:name'
  105. ```
  106. [Example](https://github.com/75lb/local-web-server/tree/master/example/rewrite).
  107. ### Mock Responses
  108. Mocks give you full control over the response headers and body returned to the client. They can be used to return anything from a simple html string to a resourceful REST API. Typically, they're used to mock services but can be used for anything.
  109. In the config, define an array called `mocks`. Each mock definition maps a <code>[route](http://expressjs.com/guide/routing.html#route-paths)</code> to a `response`. A simple home page:
  110. ```json
  111. {
  112. "mocks": [
  113. {
  114. "route": "/",
  115. "response": {
  116. "body": "<h1>Welcome to the Mock Responses example</h1>"
  117. }
  118. }
  119. ]
  120. }
  121. ```
  122. Under the hood, the property values from the `response` object are written onto the underlying [koa response object](https://github.com/koajs/koa/blob/master/docs/api/response.md). You can set any valid koa response properies, for example [type](https://github.com/koajs/koa/blob/master/docs/api/response.md#responsetype-1):
  123. ```json
  124. {
  125. "mocks": [
  126. {
  127. "route": "/",
  128. "response": {
  129. "type": "text/plain",
  130. "body": "<h1>Welcome to the Mock Responses example</h1>"
  131. }
  132. }
  133. ]
  134. }
  135. ```
  136. #### Conditional Response
  137. To define a conditional response, set a `request` object on the mock definition. The `request` value acts as a query - the response defined will only be returned if each property of the `request` query matches. For example, return an XML response *only* if the request headers include `accept: application/xml`, else return 404 Not Found.
  138. ```json
  139. {
  140. "mocks": [
  141. {
  142. "route": "/two",
  143. "request": { "accepts": "xml" },
  144. "response": {
  145. "body": "<result id='2' name='whatever' />"
  146. }
  147. }
  148. ]
  149. }
  150. ```
  151. #### Multiple Potential Responses
  152. To specify multiple potential responses, set an array of mock definitions to the `responses` property. The first response with a matching request query will be sent. In this example, the client will get one of two responses depending on the request method:
  153. ```json
  154. {
  155. "mocks": [
  156. {
  157. "route": "/three",
  158. "responses": [
  159. {
  160. "request": { "method": "GET" },
  161. "response": {
  162. "body": "<h1>Mock response for 'GET' request on /three</h1>"
  163. }
  164. },
  165. {
  166. "request": { "method": "POST" },
  167. "response": {
  168. "status": 400,
  169. "body": { "message": "That method is not allowed." }
  170. }
  171. }
  172. ]
  173. }
  174. ]
  175. }
  176. ```
  177. #### Dynamic Response
  178. The examples above all returned static data. To define a dynamic response, create a mock module. Specify its path in the `module` property:
  179. ```json
  180. {
  181. "mocks": [
  182. {
  183. "route": "/four",
  184. "module": "/mocks/stream-self.js"
  185. }
  186. ]
  187. }
  188. ```
  189. Here's what the `stream-self` module looks like. The module should export a mock definition (an object, or array of objects, each with a `response` and optional `request`). In this example, the module simply streams itself to the response but you could set `body` to *any* [valid value](https://github.com/koajs/koa/blob/master/docs/api/response.md#responsebody-1).
  190. ```js
  191. const fs = require('fs')
  192. module.exports = {
  193. response: {
  194. body: fs.createReadStream(__filename)
  195. }
  196. }
  197. ```
  198. #### Response function
  199. For more power, define the response as a function. It will receive the [koa context](https://github.com/koajs/koa/blob/master/docs/api/context.md) as its first argument. Now you have full programmatic control over the response returned.
  200. ```js
  201. module.exports = {
  202. response: function (ctx) {
  203. ctx.body = '<h1>I can do anything i want.</h1>'
  204. }
  205. }
  206. ```
  207. If the route contains tokens, their values are passed to the response. For example, with this mock...
  208. ```json
  209. {
  210. "mocks": [
  211. {
  212. "route": "/players/:id",
  213. "module": "/mocks/players.js"
  214. }
  215. ]
  216. }
  217. ```
  218. ...the `id` value is passed to the `response` function. For example, a path of `/players/10?name=Lionel` would pass `10` to the response function. Additional, the value `Lionel` would be available on `ctx.query.name`:
  219. ```js
  220. module.exports = {
  221. response: function (ctx, id) {
  222. ctx.body = `<h1>id: ${id}, name: ${ctx.query.name}</h1>`
  223. }
  224. }
  225. ```
  226. #### RESTful Resource example
  227. Here's an example of a REST collection (users). We'll create two routes, one for actions on the resource collection, one for individual resource actions.
  228. ```json
  229. {
  230. "mocks": [
  231. { "route": "/users", "module": "/mocks/users.js" },
  232. { "route": "/users/:id", "module": "/mocks/user.js" }
  233. ]
  234. }
  235. ```
  236. Define a module (`users.json`) defining seed data:
  237. ```json
  238. [
  239. { "id": 1, "name": "Lloyd", "age": 40, "nationality": "English" },
  240. { "id": 2, "name": "Mona", "age": 34, "nationality": "Palestinian" },
  241. { "id": 3, "name": "Francesco", "age": 24, "nationality": "Italian" }
  242. ]
  243. ```
  244. The collection module:
  245. ```js
  246. const users = require('./users.json')
  247. /* responses for /users */
  248. const mockResponses = [
  249. /* Respond with 400 Bad Request for PUT and DELETE - inappropriate on a collection */
  250. { request: { method: 'PUT' }, response: { status: 400 } },
  251. { request: { method: 'DELETE' }, response: { status: 400 } },
  252. {
  253. /* for GET requests return a subset of data, optionally filtered on 'minAge' and 'nationality' */
  254. request: { method: 'GET' },
  255. response: function (ctx) {
  256. ctx.body = users.filter(user => {
  257. const meetsMinAge = (user.age || 1000) >= (Number(ctx.query.minAge) || 0)
  258. const requiredNationality = user.nationality === (ctx.query.nationality || user.nationality)
  259. return meetsMinAge && requiredNationality
  260. })
  261. }
  262. },
  263. {
  264. /* for POST requests, create a new user and return the path to the new resource */
  265. request: { method: 'POST' },
  266. response: function (ctx) {
  267. const newUser = ctx.request.body
  268. users.push(newUser)
  269. newUser.id = users.length
  270. ctx.status = 201
  271. ctx.response.set('Location', `/users/${newUser.id}`)
  272. }
  273. }
  274. ]
  275. module.exports = mockResponses
  276. ```
  277. The individual resource module:
  278. ```js
  279. const users = require('./users.json')
  280. /* responses for /users/:id */
  281. const mockResponses = [
  282. /* don't support POST here */
  283. { request: { method: 'POST' }, response: { status: 400 } },
  284. /* for GET requests, return a particular user */
  285. {
  286. request: { method: 'GET' },
  287. response: function (ctx, id) {
  288. ctx.body = users.find(user => user.id === Number(id))
  289. }
  290. },
  291. /* for PUT requests, update the record */
  292. {
  293. request: { method: 'PUT' },
  294. response: function (ctx, id) {
  295. const updatedUser = ctx.request.body
  296. const existingUserIndex = users.findIndex(user => user.id === Number(id))
  297. users.splice(existingUserIndex, 1, updatedUser)
  298. ctx.status = 200
  299. }
  300. },
  301. /* DELETE request: remove the record */
  302. {
  303. request: { method: 'DELETE' },
  304. response: function (ctx, id) {
  305. const existingUserIndex = users.findIndex(user => user.id === Number(id))
  306. users.splice(existingUserIndex, 1)
  307. ctx.status = 200
  308. }
  309. }
  310. ]
  311. module.exports = mockResponses
  312. ```
  313. [Example](https://github.com/75lb/local-web-server/tree/master/example/mock).
  314. ### HTTPS Server
  315. Some modern techs (ServiceWorker, any `MediaDevices.getUserMedia()` request etc.) *must* be served from a secure origin (HTTPS). To launch an HTTPS server, supply a `--key` and `--cert` to local-web-server, for example:
  316. ```
  317. $ ws --key assets/localhost.key --cert assets/localhost.crt
  318. ```
  319. Follow [this guide](https://devcenter.heroku.com/articles/ssl-certificate-self) to create a key and self-signed certificate. Important: you must put the correct FQDN (typically `127.0.0.1`, `localhost`, `dev-server.local` etc.) into the `Common Name` field.
  320. ### Stored config
  321. Use the same options every time? Persist then to `package.json`:
  322. ```json
  323. {
  324. "name": "example",
  325. "version": "1.0.0",
  326. "local-web-server": {
  327. "port": 8100,
  328. "forbid": "*.json"
  329. }
  330. }
  331. ```
  332. or `.local-web-server.json`
  333. ```json
  334. {
  335. "port": 8100,
  336. "forbid": "*.json"
  337. }
  338. ```
  339. local-web-server will merge and use all config found, searching from the current directory upward. In the case both `package.json` and `.local-web-server.json` config is found in the same directory, `.local-web-server.json` will take precedence. Options set on the command line take precedence over all.
  340. To inspect stored config, run:
  341. ```sh
  342. $ ws --config
  343. ```
  344. ### Logging
  345. By default, local-web-server outputs a simple, dynamic statistics view. To see traditional web server logs, use `--log-format`:
  346. ```sh
  347. $ ws --log-format combined
  348. serving at http://localhost:8000
  349. ::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"
  350. ```
  351. The format value supplied is passed directly to [morgan](https://github.com/expressjs/morgan). The exception is `--log-format none` which disables all output.
  352. ### Access Control
  353. By default, access to all files is allowed (including dot files). Use `--forbid` to establish a blacklist:
  354. ```sh
  355. $ ws --forbid '*.json' '*.yml'
  356. serving at http://localhost:8000
  357. ```
  358. [Example](https://github.com/75lb/local-web-server/tree/master/example/forbid).
  359. ### Other usage
  360. #### Debugging
  361. Prints information about loaded middleware, arguments, remote proxy fetches etc.
  362. ```sh
  363. $ ws --verbose
  364. ```
  365. #### Compression
  366. Serve gzip-compressed resources, where applicable
  367. ```sh
  368. $ ws --compress
  369. ```
  370. #### Disable caching
  371. Disable etag response headers, forcing resources to be served in full every time.
  372. ```sh
  373. $ ws --no-cache
  374. ```
  375. #### mime-types
  376. You can set additional mime-type/extension mappings, or override the defaults by setting a `mime` value in the stored config. This value is passed directly to [mime.define()](https://github.com/broofa/node-mime#mimedefine). Example:
  377. ```json
  378. {
  379. "mime": {
  380. "text/plain": [ "php", "pl" ]
  381. }
  382. }
  383. ```
  384. [Example](https://github.com/75lb/local-web-server/tree/master/example/mime-override).
  385. #### Log Visualisation
  386. Instructions for how to visualise log output using goaccess, logstalgia or gltail [here](https://github.com/75lb/local-web-server/blob/master/doc/visualisation.md).
  387. ## Install
  388. Ensure [node.js](http://nodejs.org) is installed first. Linux/Mac users may need to run the following commands with `sudo`.
  389. ```sh
  390. $ npm install -g local-web-server
  391. ```
  392. This will install the `ws` tool globally. To see the available options, run:
  393. ```sh
  394. $ ws --help
  395. ```
  396. ## Distribute with your project
  397. The standard convention with client-server applications is to add an `npm start` command to launch the server component.
  398. 1\. Install the server as a dev dependency
  399. ```sh
  400. $ npm install local-web-server --save-dev
  401. ```
  402. 2\. Add a `start` command to your `package.json`:
  403. ```json
  404. {
  405. "name": "example",
  406. "version": "1.0.0",
  407. "local-web-server": {
  408. "port": 8100,
  409. "forbid": "*.json"
  410. },
  411. "scripts": {
  412. "start": "ws"
  413. }
  414. }
  415. ```
  416. 3\. Document how to build and launch your site
  417. ```sh
  418. $ npm install
  419. $ npm start
  420. serving at http://localhost:8100
  421. ```
  422. ## API Reference
  423. {{#module name="local-web-server"}}
  424. {{>body~}}
  425. {{>member-index~}}
  426. {{>separator~}}
  427. {{>members~}}
  428. {{/module}}
  429. * * *
  430. &copy; 2015 Lloyd Brookes <75pound@gmail.com>. Documented by [jsdoc-to-markdown](https://github.com/jsdoc2md/jsdoc-to-markdown).