live-server-maintained 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/.editorconfig ADDED
@@ -0,0 +1,16 @@
1
+ root = true
2
+
3
+ [*]
4
+ indent_style = tab
5
+ indent_size = 4
6
+ end_of_line = lf
7
+ charset = utf-8
8
+ trim_trailing_whitespace = true
9
+ insert_final_newline = true
10
+
11
+ [*.md]
12
+ trim_trailing_whitespace = false
13
+
14
+ [{package,bower}.json]
15
+ indent_style = space
16
+ indent_size = 2
@@ -0,0 +1,19 @@
1
+ Contributing Guidelines
2
+ =======================
3
+
4
+ When implementing a new feature or a bugfix, consider these points:
5
+
6
+ * Is this thing useful to other people, or is it just catering for an obscure corner case resulting from e.g. weirdness in personal dev environment?
7
+ * Should the thing be live-server's responsibility at all, or is it better served by other tools?
8
+ * Am I introducing unnecessary dependencies when the same thing could easily be done using normal JavaScript / node.js features?
9
+ * Does the naming (e.g. command line parameters) make sense and uses same style as other similar ones?
10
+ * Does my code adhere to the project's coding style (observable from surrounding code)?
11
+ * Can backwards compatibility be preserved?
12
+
13
+ A few guiding principles: keep the app simple and small, focusing on what it's meant to provide: live reloading development web server. Avoid extra dependencies and the need to do configuration when possible and it makes sense. Minimize bloat.
14
+
15
+ If you are adding a feature, think about if it could be an extenral middleware instead, possible bundled with `live-server` in its `middleware` folder.
16
+
17
+ **New features should come with test cases!**
18
+
19
+ **Run `npm test` to check that you are not introducing new bugs or style issues!**
@@ -0,0 +1,11 @@
1
+ ###### Before submitting an issue, please, see https://github.com/tapio/live-server#troubleshooting
2
+
3
+ ## Issue description
4
+
5
+ ## Software details
6
+
7
+ * Command line used for launching `live-server`:
8
+ * OS:
9
+ * Browser (if browser related):
10
+ * Node.js version:
11
+ * `live-server` version:
package/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: node_js
3
+ node_js:
4
+ - "node"
5
+ - "lts/*"
package/README.md CHANGED
@@ -1,3 +1,13 @@
1
- # Temporary Holding Version
1
+ # live-server-maintained
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ An updated version of [live-server](https://github.com/tapio/live-server) with refreshed dependencies.
4
+
5
+ The command is shortened from `live-server` to `lvm`:
6
+
7
+ ```bash
8
+ lvm
9
+ ```
10
+
11
+ Thats all you need to know.
12
+
13
+ Original project created by [Tapio Vierros](https://github.com/tapio).
package/index.js ADDED
@@ -0,0 +1,399 @@
1
+ #!/usr/bin/env node
2
+ var fs = require('fs'),
3
+ connect = require('connect'),
4
+ serveIndex = require('serve-index'),
5
+ logger = require('morgan'),
6
+ WebSocket = require('faye-websocket'),
7
+ path = require('path'),
8
+ url = require('url'),
9
+ http = require('http'),
10
+ send = require('send'),
11
+ open = require('opn'),
12
+ es = require("event-stream"),
13
+ os = require('os'),
14
+ chokidar = require('chokidar');
15
+ require('colors');
16
+
17
+ var INJECTED_CODE = fs.readFileSync(path.join(__dirname, "injected.html"), "utf8");
18
+
19
+ var LiveServer = {
20
+ server: null,
21
+ watcher: null,
22
+ logLevel: 2
23
+ };
24
+
25
+ function escape(html){
26
+ return String(html)
27
+ .replace(/&(?!\w+;)/g, '&')
28
+ .replace(/</g, '&lt;')
29
+ .replace(/>/g, '&gt;')
30
+ .replace(/"/g, '&quot;');
31
+ }
32
+
33
+ // Based on connect.static(), but streamlined and with added code injecter
34
+ function staticServer(root) {
35
+ var isFile = false;
36
+ try { // For supporting mounting files instead of just directories
37
+ isFile = fs.statSync(root).isFile();
38
+ } catch (e) {
39
+ if (e.code !== "ENOENT") throw e;
40
+ }
41
+ return function(req, res, next) {
42
+ if (req.method !== "GET" && req.method !== "HEAD") return next();
43
+ var reqpath = isFile ? "" : url.parse(req.url).pathname;
44
+ var hasNoOrigin = !req.headers.origin;
45
+ var injectCandidates = [ new RegExp("</body>", "i"), new RegExp("</svg>"), new RegExp("</head>", "i")];
46
+ var injectTag = null;
47
+
48
+ function directory() {
49
+ var pathname = url.parse(req.originalUrl).pathname;
50
+ res.statusCode = 301;
51
+ res.setHeader('Location', pathname + '/');
52
+ res.end('Redirecting to ' + escape(pathname) + '/');
53
+ }
54
+
55
+ function file(filepath /*, stat*/) {
56
+ var x = path.extname(filepath).toLocaleLowerCase(), match,
57
+ possibleExtensions = [ "", ".html", ".htm", ".xhtml", ".php", ".svg" ];
58
+ if (hasNoOrigin && (possibleExtensions.indexOf(x) > -1)) {
59
+ // TODO: Sync file read here is not nice, but we need to determine if the html should be injected or not
60
+ var contents = fs.readFileSync(filepath, "utf8");
61
+ for (var i = 0; i < injectCandidates.length; ++i) {
62
+ match = injectCandidates[i].exec(contents);
63
+ if (match) {
64
+ injectTag = match[0];
65
+ break;
66
+ }
67
+ }
68
+ if (injectTag === null && LiveServer.logLevel >= 3) {
69
+ console.warn("Failed to inject refresh script!".yellow,
70
+ "Couldn't find any of the tags ", injectCandidates, "from", filepath);
71
+ }
72
+ }
73
+ }
74
+
75
+ function error(err) {
76
+ if (err.status === 404) return next();
77
+ next(err);
78
+ }
79
+
80
+ function inject(stream) {
81
+ if (injectTag) {
82
+ // We need to modify the length given to browser
83
+ var len = INJECTED_CODE.length + res.getHeader('Content-Length');
84
+ res.setHeader('Content-Length', len);
85
+ var originalPipe = stream.pipe;
86
+ stream.pipe = function(resp) {
87
+ originalPipe.call(stream, es.replace(new RegExp(injectTag, "i"), INJECTED_CODE + injectTag)).pipe(resp);
88
+ };
89
+ }
90
+ }
91
+
92
+ send(req, reqpath, { root: root })
93
+ .on('error', error)
94
+ .on('directory', directory)
95
+ .on('file', file)
96
+ .on('stream', inject)
97
+ .pipe(res);
98
+ };
99
+ }
100
+
101
+ /**
102
+ * Rewrite request URL and pass it back to the static handler.
103
+ * @param staticHandler {function} Next handler
104
+ * @param file {string} Path to the entry point file
105
+ */
106
+ function entryPoint(staticHandler, file) {
107
+ if (!file) return function(req, res, next) { next(); };
108
+
109
+ return function(req, res, next) {
110
+ req.url = "/" + file;
111
+ staticHandler(req, res, next);
112
+ };
113
+ }
114
+
115
+ /**
116
+ * Start a live server with parameters given as an object
117
+ * @param host {string} Address to bind to (default: 0.0.0.0)
118
+ * @param port {number} Port number (default: 8080)
119
+ * @param root {string} Path to root directory (default: cwd)
120
+ * @param watch {array} Paths to exclusively watch for changes
121
+ * @param ignore {array} Paths to ignore when watching files for changes
122
+ * @param ignorePattern {regexp} Ignore files by RegExp
123
+ * @param noCssInject Don't inject CSS changes, just reload as with any other file change
124
+ * @param open {(string|string[])} Subpath(s) to open in browser, use false to suppress launch (default: server root)
125
+ * @param mount {array} Mount directories onto a route, e.g. [['/components', './node_modules']].
126
+ * @param logLevel {number} 0 = errors only, 1 = some, 2 = lots
127
+ * @param file {string} Path to the entry point file
128
+ * @param wait {number} Server will wait for all changes, before reloading
129
+ * @param htpasswd {string} Path to htpasswd file to enable HTTP Basic authentication
130
+ * @param middleware {array} Append middleware to stack, e.g. [function(req, res, next) { next(); }].
131
+ */
132
+ LiveServer.start = function(options) {
133
+ options = options || {};
134
+ var host = options.host || '0.0.0.0';
135
+ var port = options.port !== undefined ? options.port : 8080; // 0 means random
136
+ var root = options.root || process.cwd();
137
+ var mount = options.mount || [];
138
+ var watchPaths = options.watch || [root];
139
+ LiveServer.logLevel = options.logLevel === undefined ? 2 : options.logLevel;
140
+ var openPath = (options.open === undefined || options.open === true) ?
141
+ "" : ((options.open === null || options.open === false) ? null : options.open);
142
+ if (options.noBrowser) openPath = null; // Backwards compatibility with 0.7.0
143
+ var file = options.file;
144
+ var staticServerHandler = staticServer(root);
145
+ var wait = options.wait === undefined ? 100 : options.wait;
146
+ var browser = options.browser || null;
147
+ var htpasswd = options.htpasswd || null;
148
+ var cors = options.cors || false;
149
+ var https = options.https || null;
150
+ var proxy = options.proxy || [];
151
+ var middleware = options.middleware || [];
152
+ var noCssInject = options.noCssInject;
153
+ var httpsModule = options.httpsModule;
154
+
155
+ if (httpsModule) {
156
+ try {
157
+ require.resolve(httpsModule);
158
+ } catch (e) {
159
+ console.error(("HTTPS module \"" + httpsModule + "\" you've provided was not found.").red);
160
+ console.error("Did you do", "\"npm install " + httpsModule + "\"?");
161
+ return;
162
+ }
163
+ } else {
164
+ httpsModule = "https";
165
+ }
166
+
167
+ // Setup a web server
168
+ var app = connect();
169
+
170
+ // Add logger. Level 2 logs only errors
171
+ if (LiveServer.logLevel === 2) {
172
+ app.use(logger('dev', {
173
+ skip: function (req, res) { return res.statusCode < 400; }
174
+ }));
175
+ // Level 2 or above logs all requests
176
+ } else if (LiveServer.logLevel > 2) {
177
+ app.use(logger('dev'));
178
+ }
179
+ if (options.spa) {
180
+ middleware.push("spa");
181
+ }
182
+ // Add middleware
183
+ middleware.map(function(mw) {
184
+ if (typeof mw === "string") {
185
+ var ext = path.extname(mw).toLocaleLowerCase();
186
+ if (ext !== ".js") {
187
+ mw = require(path.join(__dirname, "middleware", mw + ".js"));
188
+ } else {
189
+ mw = require(mw);
190
+ }
191
+ }
192
+ app.use(mw);
193
+ });
194
+
195
+ // Use http-auth if configured
196
+ if (htpasswd !== null) {
197
+ var auth = require('http-auth');
198
+ var basic = auth.basic({
199
+ realm: "Please authorize",
200
+ file: htpasswd
201
+ });
202
+ app.use(auth.connect(basic));
203
+ }
204
+ if (cors) {
205
+ app.use(require("cors")({
206
+ origin: true, // reflecting request origin
207
+ credentials: true // allowing requests with credentials
208
+ }));
209
+ }
210
+ mount.forEach(function(mountRule) {
211
+ var mountPath = path.resolve(process.cwd(), mountRule[1]);
212
+ if (!options.watch) // Auto add mount paths to wathing but only if exclusive path option is not given
213
+ watchPaths.push(mountPath);
214
+ app.use(mountRule[0], staticServer(mountPath));
215
+ if (LiveServer.logLevel >= 1)
216
+ console.log('Mapping %s to "%s"', mountRule[0], mountPath);
217
+ });
218
+ proxy.forEach(function(proxyRule) {
219
+ var proxyOpts = url.parse(proxyRule[1]);
220
+ proxyOpts.via = true;
221
+ proxyOpts.preserveHost = true;
222
+ app.use(proxyRule[0], require('proxy-middleware')(proxyOpts));
223
+ if (LiveServer.logLevel >= 1)
224
+ console.log('Mapping %s to "%s"', proxyRule[0], proxyRule[1]);
225
+ });
226
+ app.use(staticServerHandler) // Custom static server
227
+ .use(entryPoint(staticServerHandler, file))
228
+ .use(serveIndex(root, { icons: true }));
229
+
230
+ var server, protocol;
231
+ if (https !== null) {
232
+ var httpsConfig = https;
233
+ if (typeof https === "string") {
234
+ httpsConfig = require(path.resolve(process.cwd(), https));
235
+ }
236
+ server = require(httpsModule).createServer(httpsConfig, app);
237
+ protocol = "https";
238
+ } else {
239
+ server = http.createServer(app);
240
+ protocol = "http";
241
+ }
242
+
243
+ // Handle server startup errors
244
+ server.addListener('error', function(e) {
245
+ if (e.code === 'EADDRINUSE') {
246
+ var serveURL = protocol + '://' + host + ':' + port;
247
+ console.log('%s is already in use. Trying another port.'.yellow, serveURL);
248
+ setTimeout(function() {
249
+ server.listen(0, host);
250
+ }, 1000);
251
+ } else {
252
+ console.error(e.toString().red);
253
+ LiveServer.shutdown();
254
+ }
255
+ });
256
+
257
+ // Handle successful server
258
+ server.addListener('listening', function(/*e*/) {
259
+ LiveServer.server = server;
260
+
261
+ var address = server.address();
262
+ var serveHost = address.address === "0.0.0.0" ? "127.0.0.1" : address.address;
263
+ var openHost = host === "0.0.0.0" ? "127.0.0.1" : host;
264
+
265
+ var serveURL = protocol + '://' + serveHost + ':' + address.port;
266
+ var openURL = protocol + '://' + openHost + ':' + address.port;
267
+
268
+ var serveURLs = [ serveURL ];
269
+ if (LiveServer.logLevel > 2 && address.address === "0.0.0.0") {
270
+ var ifaces = os.networkInterfaces();
271
+ serveURLs = Object.keys(ifaces)
272
+ .map(function(iface) {
273
+ return ifaces[iface];
274
+ })
275
+ // flatten address data, use only IPv4
276
+ .reduce(function(data, addresses) {
277
+ addresses.filter(function(addr) {
278
+ return addr.family === "IPv4";
279
+ }).forEach(function(addr) {
280
+ data.push(addr);
281
+ });
282
+ return data;
283
+ }, [])
284
+ .map(function(addr) {
285
+ return protocol + "://" + addr.address + ":" + address.port;
286
+ });
287
+ }
288
+
289
+ // Output
290
+ if (LiveServer.logLevel >= 1) {
291
+ if (serveURL === openURL)
292
+ if (serveURLs.length === 1) {
293
+ console.log(("Serving \"%s\" at %s").green, root, serveURLs[0]);
294
+ } else {
295
+ console.log(("Serving \"%s\" at\n\t%s").green, root, serveURLs.join("\n\t"));
296
+ }
297
+ else
298
+ console.log(("Serving \"%s\" at %s (%s)").green, root, openURL, serveURL);
299
+ }
300
+
301
+ // Launch browser
302
+ if (openPath !== null)
303
+ if (typeof openPath === "object") {
304
+ openPath.forEach(function(p) {
305
+ open(openURL + p, {app: browser});
306
+ });
307
+ } else {
308
+ open(openURL + openPath, {app: browser});
309
+ }
310
+ });
311
+
312
+ // Setup server to listen at port
313
+ server.listen(port, host);
314
+
315
+ // WebSocket
316
+ var clients = [];
317
+ server.addListener('upgrade', function(request, socket, head) {
318
+ var ws = new WebSocket(request, socket, head);
319
+ ws.onopen = function() { ws.send('connected'); };
320
+
321
+ if (wait > 0) {
322
+ (function() {
323
+ var wssend = ws.send;
324
+ var waitTimeout;
325
+ ws.send = function() {
326
+ var args = arguments;
327
+ if (waitTimeout) clearTimeout(waitTimeout);
328
+ waitTimeout = setTimeout(function(){
329
+ wssend.apply(ws, args);
330
+ }, wait);
331
+ };
332
+ })();
333
+ }
334
+
335
+ ws.onclose = function() {
336
+ clients = clients.filter(function (x) {
337
+ return x !== ws;
338
+ });
339
+ };
340
+
341
+ clients.push(ws);
342
+ });
343
+
344
+ var ignored = [
345
+ function(testPath) { // Always ignore dotfiles (important e.g. because editor hidden temp files)
346
+ return testPath !== "." && /(^[.#]|(?:__|~)$)/.test(path.basename(testPath));
347
+ }
348
+ ];
349
+ if (options.ignore) {
350
+ ignored = ignored.concat(options.ignore);
351
+ }
352
+ if (options.ignorePattern) {
353
+ ignored.push(options.ignorePattern);
354
+ }
355
+ // Setup file watcher
356
+ LiveServer.watcher = chokidar.watch(watchPaths, {
357
+ ignored: ignored,
358
+ ignoreInitial: true
359
+ });
360
+ function handleChange(changePath) {
361
+ var cssChange = path.extname(changePath) === ".css" && !noCssInject;
362
+ if (LiveServer.logLevel >= 1) {
363
+ if (cssChange)
364
+ console.log("CSS change detected".magenta, changePath);
365
+ else console.log("Change detected".cyan, changePath);
366
+ }
367
+ clients.forEach(function(ws) {
368
+ if (ws)
369
+ ws.send(cssChange ? 'refreshcss' : 'reload');
370
+ });
371
+ }
372
+ LiveServer.watcher
373
+ .on("change", handleChange)
374
+ .on("add", handleChange)
375
+ .on("unlink", handleChange)
376
+ .on("addDir", handleChange)
377
+ .on("unlinkDir", handleChange)
378
+ .on("ready", function () {
379
+ if (LiveServer.logLevel >= 1)
380
+ console.log("Ready for changes".cyan);
381
+ })
382
+ .on("error", function (err) {
383
+ console.log("ERROR:".red, err);
384
+ });
385
+
386
+ return server;
387
+ };
388
+
389
+ LiveServer.shutdown = function() {
390
+ var watcher = LiveServer.watcher;
391
+ if (watcher) {
392
+ watcher.close();
393
+ }
394
+ var server = LiveServer.server;
395
+ if (server)
396
+ server.close();
397
+ };
398
+
399
+ module.exports = LiveServer;
package/injected.html ADDED
@@ -0,0 +1,31 @@
1
+ <!-- Code injected by live-server -->
2
+ <script type="text/javascript">
3
+ // <![CDATA[ <-- For SVG support
4
+ if ('WebSocket' in window) {
5
+ (function() {
6
+ function refreshCSS() {
7
+ var sheets = [].slice.call(document.getElementsByTagName("link"));
8
+ var head = document.getElementsByTagName("head")[0];
9
+ for (var i = 0; i < sheets.length; ++i) {
10
+ var elem = sheets[i];
11
+ head.removeChild(elem);
12
+ var rel = elem.rel;
13
+ if (elem.href && typeof rel != "string" || rel.length == 0 || rel.toLowerCase() == "stylesheet") {
14
+ var url = elem.href.replace(/(&|\?)_cacheOverride=\d+/, '');
15
+ elem.href = url + (url.indexOf('?') >= 0 ? '&' : '?') + '_cacheOverride=' + (new Date().valueOf());
16
+ }
17
+ head.appendChild(elem);
18
+ }
19
+ }
20
+ var protocol = window.location.protocol === 'http:' ? 'ws://' : 'wss://';
21
+ var address = protocol + window.location.host + window.location.pathname + '/ws';
22
+ var socket = new WebSocket(address);
23
+ socket.onmessage = function(msg) {
24
+ if (msg.data == 'reload') window.location.reload();
25
+ else if (msg.data == 'refreshcss') refreshCSS();
26
+ };
27
+ console.log('Live reload enabled.');
28
+ })();
29
+ }
30
+ // ]]>
31
+ </script>
package/live-server.js ADDED
@@ -0,0 +1,174 @@
1
+ #!/usr/bin/env node
2
+ var path = require('path');
3
+ var fs = require('fs');
4
+ var assign = require('object-assign');
5
+ var liveServer = require("./index");
6
+
7
+ var opts = {
8
+ host: process.env.IP,
9
+ port: process.env.PORT,
10
+ open: true,
11
+ mount: [],
12
+ proxy: [],
13
+ middleware: [],
14
+ logLevel: 2,
15
+ };
16
+
17
+ var homeDir = process.env[(process.platform === 'win32') ? 'USERPROFILE' : 'HOME'];
18
+ var configPath = path.join(homeDir, '.live-server.json');
19
+ if (fs.existsSync(configPath)) {
20
+ var userConfig = fs.readFileSync(configPath, 'utf8');
21
+ assign(opts, JSON.parse(userConfig));
22
+ if (opts.ignorePattern) opts.ignorePattern = new RegExp(opts.ignorePattern);
23
+ }
24
+
25
+ for (var i = process.argv.length - 1; i >= 2; --i) {
26
+ var arg = process.argv[i];
27
+ if (arg.indexOf("--port=") > -1) {
28
+ var portString = arg.substring(7);
29
+ var portNumber = parseInt(portString, 10);
30
+ if (portNumber === +portString) {
31
+ opts.port = portNumber;
32
+ process.argv.splice(i, 1);
33
+ }
34
+ }
35
+ else if (arg.indexOf("--host=") > -1) {
36
+ opts.host = arg.substring(7);
37
+ process.argv.splice(i, 1);
38
+ }
39
+ else if (arg.indexOf("--open=") > -1) {
40
+ var open = arg.substring(7);
41
+ if (open.indexOf('/') !== 0) {
42
+ open = '/' + open;
43
+ }
44
+ switch (typeof opts.open) {
45
+ case "boolean":
46
+ opts.open = open;
47
+ break;
48
+ case "string":
49
+ opts.open = [opts.open, open];
50
+ break;
51
+ case "object":
52
+ opts.open.push(open);
53
+ break;
54
+ }
55
+ process.argv.splice(i, 1);
56
+ }
57
+ else if (arg.indexOf("--watch=") > -1) {
58
+ // Will be modified later when cwd is known
59
+ opts.watch = arg.substring(8).split(",");
60
+ process.argv.splice(i, 1);
61
+ }
62
+ else if (arg.indexOf("--ignore=") > -1) {
63
+ // Will be modified later when cwd is known
64
+ opts.ignore = arg.substring(9).split(",");
65
+ process.argv.splice(i, 1);
66
+ }
67
+ else if (arg.indexOf("--ignorePattern=") > -1) {
68
+ opts.ignorePattern = new RegExp(arg.substring(16));
69
+ process.argv.splice(i, 1);
70
+ }
71
+ else if (arg === "--no-css-inject") {
72
+ opts.noCssInject = true;
73
+ process.argv.splice(i, 1);
74
+ }
75
+ else if (arg === "--no-browser") {
76
+ opts.open = false;
77
+ process.argv.splice(i, 1);
78
+ }
79
+ else if (arg.indexOf("--browser=") > -1) {
80
+ opts.browser = arg.substring(10).split(",");
81
+ process.argv.splice(i, 1);
82
+ }
83
+ else if (arg.indexOf("--entry-file=") > -1) {
84
+ var file = arg.substring(13);
85
+ if (file.length) {
86
+ opts.file = file;
87
+ process.argv.splice(i, 1);
88
+ }
89
+ }
90
+ else if (arg === "--spa") {
91
+ opts.middleware.push("spa");
92
+ process.argv.splice(i, 1);
93
+ }
94
+ else if (arg === "--quiet" || arg === "-q") {
95
+ opts.logLevel = 0;
96
+ process.argv.splice(i, 1);
97
+ }
98
+ else if (arg === "--verbose" || arg === "-V") {
99
+ opts.logLevel = 3;
100
+ process.argv.splice(i, 1);
101
+ }
102
+ else if (arg.indexOf("--mount=") > -1) {
103
+ // e.g. "--mount=/components:./node_modules" will be ['/components', '<process.cwd()>/node_modules']
104
+ // split only on the first ":", as the path may contain ":" as well (e.g. C:\file.txt)
105
+ var match = arg.substring(8).match(/([^:]+):(.+)$/);
106
+ match[2] = path.resolve(process.cwd(), match[2]);
107
+ opts.mount.push([ match[1], match[2] ]);
108
+ process.argv.splice(i, 1);
109
+ }
110
+ else if (arg.indexOf("--wait=") > -1) {
111
+ var waitString = arg.substring(7);
112
+ var waitNumber = parseInt(waitString, 10);
113
+ if (waitNumber === +waitString) {
114
+ opts.wait = waitNumber;
115
+ process.argv.splice(i, 1);
116
+ }
117
+ }
118
+ else if (arg === "--version" || arg === "-v") {
119
+ var packageJson = require('./package.json');
120
+ console.log(packageJson.name, packageJson.version);
121
+ process.exit();
122
+ }
123
+ else if (arg.indexOf("--htpasswd=") > -1) {
124
+ opts.htpasswd = arg.substring(11);
125
+ process.argv.splice(i, 1);
126
+ }
127
+ else if (arg === "--cors") {
128
+ opts.cors = true;
129
+ process.argv.splice(i, 1);
130
+ }
131
+ else if (arg.indexOf("--https=") > -1) {
132
+ opts.https = arg.substring(8);
133
+ process.argv.splice(i, 1);
134
+ }
135
+ else if (arg.indexOf("--https-module=") > -1) {
136
+ opts.httpsModule = arg.substring(15);
137
+ process.argv.splice(i, 1);
138
+ }
139
+ else if (arg.indexOf("--proxy=") > -1) {
140
+ // split only on the first ":", as the URL will contain ":" as well
141
+ var match = arg.substring(8).match(/([^:]+):(.+)$/);
142
+ opts.proxy.push([ match[1], match[2] ]);
143
+ process.argv.splice(i, 1);
144
+ }
145
+ else if (arg.indexOf("--middleware=") > -1) {
146
+ opts.middleware.push(arg.substring(13));
147
+ process.argv.splice(i, 1);
148
+ }
149
+ else if (arg === "--help" || arg === "-h") {
150
+ console.log('Usage: live-server [-v|--version] [-h|--help] [-q|--quiet] [--port=PORT] [--host=HOST] [--open=PATH] [--no-browser] [--browser=BROWSER] [--ignore=PATH] [--ignorePattern=RGXP] [--no-css-inject] [--entry-file=PATH] [--spa] [--mount=ROUTE:PATH] [--wait=MILLISECONDS] [--htpasswd=PATH] [--cors] [--https=PATH] [--https-module=MODULE_NAME] [--proxy=PATH] [PATH]');
151
+ process.exit();
152
+ }
153
+ else if (arg === "--test") {
154
+ // Hidden param for tests to exit automatically
155
+ setTimeout(liveServer.shutdown, 500);
156
+ process.argv.splice(i, 1);
157
+ }
158
+ }
159
+
160
+ // Patch paths
161
+ var dir = opts.root = process.argv[2] || "";
162
+
163
+ if (opts.watch) {
164
+ opts.watch = opts.watch.map(function(relativePath) {
165
+ return path.join(dir, relativePath);
166
+ });
167
+ }
168
+ if (opts.ignore) {
169
+ opts.ignore = opts.ignore.map(function(relativePath) {
170
+ return path.join(dir, relativePath);
171
+ });
172
+ }
173
+
174
+ liveServer.start(opts);
@@ -0,0 +1,5 @@
1
+ module.exports = function(req, res, next) {
2
+ res.statusCode = 202;
3
+ next();
4
+ }
5
+
@@ -0,0 +1,14 @@
1
+ // Single Page Apps - redirect to /#/ except when a file extension is given
2
+ var path = require('path');
3
+ module.exports = function(req, res, next) {
4
+ if (req.method !== "GET" && req.method !== "HEAD")
5
+ next();
6
+ if (req.url !== '/' && path.extname(req.url) === '') {
7
+ var route = req.url;
8
+ req.url = '/';
9
+ res.statusCode = 302;
10
+ res.setHeader('Location', req.url + '#' + route);
11
+ res.end();
12
+ }
13
+ else next();
14
+ }