webpack-dev-server 1.15.1 → 1.16.2

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/lib/Server.js CHANGED
@@ -1,409 +1,416 @@
1
- var fs = require("fs");
2
- var path = require("path");
3
- var webpackDevMiddleware = require("webpack-dev-middleware");
4
- var express = require("express");
5
- var compress = require("compression");
6
- var sockjs = require("sockjs");
7
- var StreamCache = require("stream-cache");
8
- var http = require("http");
9
- var https = require("https");
10
- var httpProxyMiddleware = require("http-proxy-middleware");
11
- var serveIndex = require("serve-index");
12
- var historyApiFallback = require("connect-history-api-fallback");
13
-
14
- function Server(compiler, options) {
15
- // Default options
16
- if(!options) options = {};
17
-
18
- if(options.lazy && !options.filename) {
19
- throw new Error("'filename' option must be set in lazy mode.");
20
- }
21
-
22
- this.hot = options.hot;
23
- this.headers = options.headers;
24
- this.sockets = [];
25
-
26
- // Listening for events
27
- var invalidPlugin = function() {
28
- this.sockWrite(this.sockets, "invalid");
29
- }.bind(this);
30
- compiler.plugin("compile", invalidPlugin);
31
- compiler.plugin("invalid", invalidPlugin);
32
- compiler.plugin("done", function(stats) {
33
- this._sendStats(this.sockets, stats.toJson());
34
- this._stats = stats;
35
- }.bind(this));
36
-
37
- // Prepare live html page
38
- var livePage = this.livePage = new StreamCache();
39
- fs.createReadStream(path.join(__dirname, "..", "client", "live.html")).pipe(livePage);
40
-
41
- // Prepare the live js file
42
- var liveJs = new StreamCache();
43
- fs.createReadStream(path.join(__dirname, "..", "client", "live.bundle.js")).pipe(liveJs);
44
-
45
- // Prepare the inlined js file
46
- var inlinedJs = new StreamCache();
47
- fs.createReadStream(path.join(__dirname, "..", "client", "index.bundle.js")).pipe(inlinedJs);
48
-
49
- // Init express server
50
- var app = this.app = new express();
51
-
52
- // middleware for serving webpack bundle
53
- this.middleware = webpackDevMiddleware(compiler, options);
54
-
55
- app.get("/__webpack_dev_server__/live.bundle.js", function(req, res) {
56
- res.setHeader("Content-Type", "application/javascript");
57
- liveJs.pipe(res);
58
- });
59
-
60
- app.get("/webpack-dev-server.js", function(req, res) {
61
- res.setHeader("Content-Type", "application/javascript");
62
- inlinedJs.pipe(res);
63
- });
64
-
65
- app.get("/webpack-dev-server/*", function(req, res) {
66
- res.setHeader("Content-Type", "text/html");
67
- this.livePage.pipe(res);
68
- }.bind(this));
69
-
70
- app.get("/webpack-dev-server", function(req, res) {
71
- res.setHeader("Content-Type", "text/html");
72
- res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>');
73
- var path = this.middleware.getFilenameFromUrl(options.publicPath || "/");
74
- var fs = this.middleware.fileSystem;
75
-
76
- function writeDirectory(baseUrl, basePath) {
77
- var content = fs.readdirSync(basePath);
78
- res.write("<ul>");
79
- content.forEach(function(item) {
80
- var p = basePath + "/" + item;
81
- if(fs.statSync(p).isFile()) {
82
- res.write('<li><a href="');
83
- res.write(baseUrl + item);
84
- res.write('">');
85
- res.write(item);
86
- res.write('</a></li>');
87
- if(/\.js$/.test(item)) {
88
- var htmlItem = item.substr(0, item.length - 3);
89
- res.write('<li><a href="');
90
- res.write(baseUrl + htmlItem);
91
- res.write('">');
92
- res.write(htmlItem);
93
- res.write('</a> (magic html for ');
94
- res.write(item);
95
- res.write(') (<a href="');
96
- res.write(baseUrl.replace(/(^(https?:\/\/[^\/]+)?\/)/, "$1webpack-dev-server/") + htmlItem);
97
- res.write('">webpack-dev-server</a>)</li>');
98
- }
99
- } else {
100
- res.write('<li>');
101
- res.write(item);
102
- res.write('<br>');
103
- writeDirectory(baseUrl + item + "/", p);
104
- res.write('</li>');
105
- }
106
- });
107
- res.write("</ul>");
108
- }
109
- writeDirectory(options.publicPath || "/", path);
110
- res.end('</body></html>');
111
- }.bind(this));
112
-
113
- var features = {
114
- compress: function() {
115
- if(options.compress) {
116
- // Enable gzip compression.
117
- app.use(compress());
118
- }
119
- },
120
-
121
- proxy: function() {
122
- if(options.proxy) {
123
- /**
124
- * Assume a proxy configuration specified as:
125
- * proxy: 'a url'
126
- */
127
- if(typeof options.proxy === 'string') {
128
- options.proxy = [{
129
- context: options.proxy
130
- }];
131
- }
132
-
133
- /**
134
- * Assume a proxy configuration specified as:
135
- * proxy: {
136
- * 'context': { options }
137
- * }
138
- * OR
139
- * proxy: {
140
- * 'context': 'target'
141
- * }
142
- */
143
- if(!Array.isArray(options.proxy)) {
144
- options.proxy = Object.keys(options.proxy).map(function(context) {
145
- var proxyOptions;
146
- // For backwards compatibility reasons.
147
- var correctedContext = context.replace(/^\*$/, "**").replace(/\/\*$/, "");
148
-
149
- if(typeof options.proxy[context] === 'string') {
150
- proxyOptions = {
151
- context: correctedContext,
152
- target: options.proxy[context]
153
- };
154
- } else {
155
- proxyOptions = options.proxy[context];
156
- proxyOptions.context = correctedContext;
157
- }
158
-
159
- return proxyOptions;
160
- });
161
- }
162
-
163
- /**
164
- * Assume a proxy configuration specified as:
165
- * proxy: [
166
- * {
167
- * context: ...,
168
- * ...options...
169
- * }
170
- * ]
171
- */
172
- options.proxy.forEach(function(proxyConfig) {
173
- var bypass = typeof proxyConfig.bypass === 'function';
174
- var context = proxyConfig.context || proxyConfig.path;
175
- var proxyMiddleware;
176
- // It is possible to use the `bypass` method without a `target`.
177
- // However, the proxy middleware has no use in this case, and will fail to instantiate.
178
- if(proxyConfig.target) {
179
- proxyMiddleware = httpProxyMiddleware(context, proxyConfig);
180
- }
181
-
182
- app.use(function(req, res, next) {
183
- var bypassUrl = bypass && proxyConfig.bypass(req, res, proxyConfig) || false;
184
-
185
- if(bypassUrl) {
186
- req.url = bypassUrl;
187
- next();
188
- } else if(proxyMiddleware) {
189
- return proxyMiddleware(req, res, next);
190
- }
191
- });
192
- });
193
- }
194
- },
195
-
196
- historyApiFallback: function() {
197
- if(options.historyApiFallback) {
198
- // Fall back to /index.html if nothing else matches.
199
- app.use(historyApiFallback(typeof options.historyApiFallback === 'object' ? options.historyApiFallback : null));
200
- }
201
- },
202
-
203
- contentBase: function() {
204
- if(options.contentBase !== false) {
205
- var contentBase = options.contentBase || process.cwd();
206
-
207
- if(Array.isArray(contentBase)) {
208
- contentBase.forEach(function(item) {
209
- app.get("*", express.static(item));
210
- });
211
- contentBase.forEach(function(item) {
212
- app.get("*", serveIndex(item));
213
- });
214
- } else if(typeof contentBase === "object") {
215
- console.log('Using contentBase as a proxy is deprecated and will be removed in the next major version. Please use the proxy option instead.\n\nTo update remove the contentBase option from webpack.config.js and add this:');
216
- console.log('proxy: {\n\t"*": <your current contentBase configuration>\n}');
217
- // Proxy every request to contentBase.target
218
- app.all("*", function(req, res) {
219
- proxy.web(req, res, contentBase, function(err) {
220
- var msg = "cannot proxy to " + contentBase.target + " (" + err.message + ")";
221
- this.sockWrite(this.sockets, "proxy-error", [msg]);
222
- res.statusCode = 502;
223
- res.end();
224
- }.bind(this));
225
- }.bind(this));
226
- } else if(/^(https?:)?\/\//.test(contentBase)) {
227
- // Redirect every request to contentBase
228
- app.get("*", function(req, res) {
229
- res.writeHead(302, {
230
- 'Location': contentBase + req.path + (req._parsedUrl.search || "")
231
- });
232
- res.end();
233
- });
234
- } else if(typeof contentBase === "number") {
235
- // Redirect every request to the port contentBase
236
- app.get("*", function(req, res) {
237
- res.writeHead(302, {
238
- 'Location': "//localhost:" + contentBase + req.path + (req._parsedUrl.search || "")
239
- });
240
- res.end();
241
- });
242
- } else {
243
- // route content request
244
- app.get("*", express.static(contentBase, options.staticOptions), serveIndex(contentBase));
245
- }
246
- }
247
- }.bind(this),
248
-
249
- middleware: function() {
250
- // include our middleware to ensure it is able to handle '/index.html' request after redirect
251
- app.use(this.middleware);
252
- }.bind(this),
253
-
254
- headers: function() {
255
- app.all("*", this.setContentHeaders.bind(this));
256
- }.bind(this),
257
-
258
- magicHtml: function() {
259
- app.get("*", this.serveMagicHtml.bind(this));
260
- }.bind(this),
261
-
262
- setup: function() {
263
- if(typeof options.setup === "function")
264
- options.setup(app, this);
265
- }.bind(this)
266
- };
267
-
268
- var defaultFeatures = ["setup", "headers", "middleware"];
269
- if(options.proxy)
270
- defaultFeatures.push("proxy");
271
- if(options.historyApiFallback)
272
- defaultFeatures.push("historyApiFallback", "middleware");
273
- defaultFeatures.push("magicHtml");
274
- if(options.contentBase !== false)
275
- defaultFeatures.push("contentBase");
276
- // compress is placed last and uses unshift so that it will be the first middleware used
277
- if(options.compress)
278
- defaultFeatures.unshift("compress");
279
-
280
- (options.features || defaultFeatures).forEach(function(feature) {
281
- features[feature]();
282
- }, this);
283
-
284
- if(options.https) {
285
- // for keep supporting CLI parameters
286
- if(typeof options.https === 'boolean') {
287
- options.https = {
288
- key: options.key,
289
- cert: options.cert,
290
- ca: options.ca
291
- };
292
- }
293
-
294
- // using built-in self-signed certificate if no certificate was configured
295
- options.https.key = options.https.key || fs.readFileSync(path.join(__dirname, "../ssl/server.key"));
296
- options.https.cert = options.https.cert || fs.readFileSync(path.join(__dirname, "../ssl/server.crt"));
297
- options.https.ca = options.https.ca || fs.readFileSync(path.join(__dirname, "../ssl/ca.crt"));
298
-
299
- this.listeningApp = https.createServer(options.https, app);
300
- } else {
301
- this.listeningApp = http.createServer(app);
302
- }
303
- }
304
-
305
- Server.prototype.use = function() {
306
- this.app.use.apply(this.app, arguments);
307
- }
308
-
309
- Server.prototype.setContentHeaders = function(req, res, next) {
310
- if(this.headers) {
311
- for(var name in this.headers) {
312
- res.setHeader(name, this.headers[name]);
313
- }
314
- }
315
-
316
- next();
317
- }
318
-
319
- // delegate listen call and init sockjs
320
- Server.prototype.listen = function() {
321
- this.listeningApp.listen.apply(this.listeningApp, arguments);
322
- var sockServer = sockjs.createServer({
323
- // Limit useless logs
324
- log: function(severity, line) {
325
- if(severity === "error") {
326
- console.log(line);
327
- }
328
- }
329
- });
330
- sockServer.on("connection", function(conn) {
331
- this.sockets.push(conn);
332
-
333
- // Remove the connection when it's closed
334
- conn.on("close", function() {
335
- var connIndex = this.sockets.indexOf(conn);
336
- if(connIndex >= 0) {
337
- this.sockets.splice(connIndex, 1);
338
- }
339
- }.bind(this));
340
-
341
- if(this.hot) this.sockWrite([conn], "hot");
342
- if(!this._stats) return;
343
- this._sendStats([conn], this._stats.toJson(), true);
344
- }.bind(this));
345
-
346
- sockServer.installHandlers(this.listeningApp, {
347
- prefix: '/sockjs-node'
348
- });
349
- }
350
-
351
- Server.prototype.close = function() {
352
- this.sockets.forEach(function(sock) {
353
- sock.close();
354
- });
355
- this.sockets = [];
356
- this.middleware.close();
357
- this.listeningApp.close();
358
- }
359
-
360
- Server.prototype.sockWrite = function(sockets, type, data) {
361
- sockets.forEach(function(sock) {
362
- sock.write(JSON.stringify({
363
- type: type,
364
- data: data
365
- }));
366
- });
367
- }
368
-
369
- Server.prototype.serveMagicHtml = function(req, res, next) {
370
- var _path = req.path;
371
- try {
372
- if(!this.middleware.fileSystem.statSync(this.middleware.getFilenameFromUrl(_path + ".js")).isFile())
373
- return next();
374
- // Serve a page that executes the javascript
375
- res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body><script type="text/javascript" charset="utf-8" src="');
376
- res.write(_path);
377
- res.write('.js');
378
- res.write(req._parsedUrl.search || "");
379
- res.end('"></script></body></html>');
380
- } catch(e) {
381
- return next();
382
- }
383
- }
384
-
385
- // send stats to a socket or multiple sockets
386
- Server.prototype._sendStats = function(sockets, stats, force) {
387
- if(!force &&
388
- stats &&
389
- (!stats.errors || stats.errors.length === 0) &&
390
- stats.assets &&
391
- stats.assets.every(function(asset) {
392
- return !asset.emitted;
393
- })
394
- )
395
- return this.sockWrite(sockets, "still-ok");
396
- this.sockWrite(sockets, "hash", stats.hash);
397
- if(stats.errors.length > 0)
398
- this.sockWrite(sockets, "errors", stats.errors);
399
- else if(stats.warnings.length > 0)
400
- this.sockWrite(sockets, "warnings", stats.warnings);
401
- else
402
- this.sockWrite(sockets, "ok");
403
- }
404
-
405
- Server.prototype.invalidate = function() {
406
- if(this.middleware) this.middleware.invalidate();
407
- }
408
-
409
- module.exports = Server;
1
+ var fs = require("fs");
2
+ var path = require("path");
3
+ var webpackDevMiddleware = require("webpack-dev-middleware");
4
+ var express = require("express");
5
+ var compress = require("compression");
6
+ var sockjs = require("sockjs");
7
+ var StreamCache = require("stream-cache");
8
+ var http = require("http");
9
+ var https = require("https");
10
+ var httpProxyMiddleware = require("http-proxy-middleware");
11
+ var serveIndex = require("serve-index");
12
+ var historyApiFallback = require("connect-history-api-fallback");
13
+
14
+ function Server(compiler, options) {
15
+ // Default options
16
+ if(!options) options = {};
17
+
18
+ if(options.lazy && !options.filename) {
19
+ throw new Error("'filename' option must be set in lazy mode.");
20
+ }
21
+
22
+ this.hot = options.hot;
23
+ this.headers = options.headers;
24
+ this.clientLogLevel = options.clientLogLevel;
25
+ this.sockets = [];
26
+
27
+ // Listening for events
28
+ var invalidPlugin = function() {
29
+ this.sockWrite(this.sockets, "invalid");
30
+ }.bind(this);
31
+ compiler.plugin("compile", invalidPlugin);
32
+ compiler.plugin("invalid", invalidPlugin);
33
+ compiler.plugin("done", function(stats) {
34
+ this._sendStats(this.sockets, stats.toJson());
35
+ this._stats = stats;
36
+ }.bind(this));
37
+
38
+ // Prepare live html page
39
+ var livePage = this.livePage = new StreamCache();
40
+ fs.createReadStream(path.join(__dirname, "..", "client", "live.html")).pipe(livePage);
41
+
42
+ // Prepare the live js file
43
+ var liveJs = new StreamCache();
44
+ fs.createReadStream(path.join(__dirname, "..", "client", "live.bundle.js")).pipe(liveJs);
45
+
46
+ // Prepare the inlined js file
47
+ var inlinedJs = new StreamCache();
48
+ fs.createReadStream(path.join(__dirname, "..", "client", "index.bundle.js")).pipe(inlinedJs);
49
+
50
+ // Init express server
51
+ var app = this.app = new express();
52
+
53
+ // middleware for serving webpack bundle
54
+ this.middleware = webpackDevMiddleware(compiler, options);
55
+
56
+ app.get("/__webpack_dev_server__/live.bundle.js", function(req, res) {
57
+ res.setHeader("Content-Type", "application/javascript");
58
+ liveJs.pipe(res);
59
+ });
60
+
61
+ app.get("/webpack-dev-server.js", function(req, res) {
62
+ res.setHeader("Content-Type", "application/javascript");
63
+ inlinedJs.pipe(res);
64
+ });
65
+
66
+ app.get("/webpack-dev-server/*", function(req, res) {
67
+ res.setHeader("Content-Type", "text/html");
68
+ this.livePage.pipe(res);
69
+ }.bind(this));
70
+
71
+ app.get("/webpack-dev-server", function(req, res) {
72
+ res.setHeader("Content-Type", "text/html");
73
+ res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body>');
74
+ var path = this.middleware.getFilenameFromUrl(options.publicPath || "/");
75
+ var fs = this.middleware.fileSystem;
76
+
77
+ function writeDirectory(baseUrl, basePath) {
78
+ var content = fs.readdirSync(basePath);
79
+ res.write("<ul>");
80
+ content.forEach(function(item) {
81
+ var p = basePath + "/" + item;
82
+ if(fs.statSync(p).isFile()) {
83
+ res.write('<li><a href="');
84
+ res.write(baseUrl + item);
85
+ res.write('">');
86
+ res.write(item);
87
+ res.write('</a></li>');
88
+ if(/\.js$/.test(item)) {
89
+ var htmlItem = item.substr(0, item.length - 3);
90
+ res.write('<li><a href="');
91
+ res.write(baseUrl + htmlItem);
92
+ res.write('">');
93
+ res.write(htmlItem);
94
+ res.write('</a> (magic html for ');
95
+ res.write(item);
96
+ res.write(') (<a href="');
97
+ res.write(baseUrl.replace(/(^(https?:\/\/[^\/]+)?\/)/, "$1webpack-dev-server/") + htmlItem);
98
+ res.write('">webpack-dev-server</a>)</li>');
99
+ }
100
+ } else {
101
+ res.write('<li>');
102
+ res.write(item);
103
+ res.write('<br>');
104
+ writeDirectory(baseUrl + item + "/", p);
105
+ res.write('</li>');
106
+ }
107
+ });
108
+ res.write("</ul>");
109
+ }
110
+ writeDirectory(options.publicPath || "/", path);
111
+ res.end('</body></html>');
112
+ }.bind(this));
113
+
114
+ var features = {
115
+ compress: function() {
116
+ if(options.compress) {
117
+ // Enable gzip compression.
118
+ app.use(compress());
119
+ }
120
+ },
121
+
122
+ proxy: function() {
123
+ if(options.proxy) {
124
+ /**
125
+ * Assume a proxy configuration specified as:
126
+ * proxy: 'a url'
127
+ */
128
+ if(typeof options.proxy === 'string') {
129
+ options.proxy = [{
130
+ context: options.proxy
131
+ }];
132
+ }
133
+
134
+ /**
135
+ * Assume a proxy configuration specified as:
136
+ * proxy: {
137
+ * 'context': { options }
138
+ * }
139
+ * OR
140
+ * proxy: {
141
+ * 'context': 'target'
142
+ * }
143
+ */
144
+ if(!Array.isArray(options.proxy)) {
145
+ options.proxy = Object.keys(options.proxy).map(function(context) {
146
+ var proxyOptions;
147
+ // For backwards compatibility reasons.
148
+ var correctedContext = context.replace(/^\*$/, "**").replace(/\/\*$/, "");
149
+
150
+ if(typeof options.proxy[context] === 'string') {
151
+ proxyOptions = {
152
+ context: correctedContext,
153
+ target: options.proxy[context]
154
+ };
155
+ } else {
156
+ proxyOptions = options.proxy[context];
157
+ proxyOptions.context = correctedContext;
158
+ }
159
+
160
+ return proxyOptions;
161
+ });
162
+ }
163
+
164
+ /**
165
+ * Assume a proxy configuration specified as:
166
+ * proxy: [
167
+ * {
168
+ * context: ...,
169
+ * ...options...
170
+ * }
171
+ * ]
172
+ */
173
+ options.proxy.forEach(function(proxyConfig) {
174
+ var bypass = typeof proxyConfig.bypass === 'function';
175
+ var context = proxyConfig.context || proxyConfig.path;
176
+ var proxyMiddleware;
177
+ // It is possible to use the `bypass` method without a `target`.
178
+ // However, the proxy middleware has no use in this case, and will fail to instantiate.
179
+ if(proxyConfig.target) {
180
+ proxyMiddleware = httpProxyMiddleware(context, proxyConfig);
181
+ }
182
+
183
+ app.use(function(req, res, next) {
184
+ var bypassUrl = bypass && proxyConfig.bypass(req, res, proxyConfig) || false;
185
+
186
+ if(bypassUrl) {
187
+ req.url = bypassUrl;
188
+ next();
189
+ } else if(proxyMiddleware) {
190
+ return proxyMiddleware(req, res, next);
191
+ }
192
+ });
193
+ });
194
+ }
195
+ },
196
+
197
+ historyApiFallback: function() {
198
+ if(options.historyApiFallback) {
199
+ // Fall back to /index.html if nothing else matches.
200
+ app.use(historyApiFallback(typeof options.historyApiFallback === 'object' ? options.historyApiFallback : null));
201
+ }
202
+ },
203
+
204
+ contentBase: function() {
205
+ if(options.contentBase !== false) {
206
+ var contentBase = options.contentBase || process.cwd();
207
+
208
+ if(Array.isArray(contentBase)) {
209
+ contentBase.forEach(function(item) {
210
+ app.get("*", express.static(item));
211
+ });
212
+ contentBase.forEach(function(item) {
213
+ app.get("*", serveIndex(item));
214
+ });
215
+ } else if(typeof contentBase === "object") {
216
+ console.log('Using contentBase as a proxy is deprecated and will be removed in the next major version. Please use the proxy option instead.\n\nTo update remove the contentBase option from webpack.config.js and add this:');
217
+ console.log('proxy: {\n\t"*": <your current contentBase configuration>\n}');
218
+ // Proxy every request to contentBase.target
219
+ app.all("*", function(req, res) {
220
+ proxy.web(req, res, contentBase, function(err) {
221
+ var msg = "cannot proxy to " + contentBase.target + " (" + err.message + ")";
222
+ this.sockWrite(this.sockets, "proxy-error", [msg]);
223
+ res.statusCode = 502;
224
+ res.end();
225
+ }.bind(this));
226
+ }.bind(this));
227
+ } else if(/^(https?:)?\/\//.test(contentBase)) {
228
+ // Redirect every request to contentBase
229
+ app.get("*", function(req, res) {
230
+ res.writeHead(302, {
231
+ 'Location': contentBase + req.path + (req._parsedUrl.search || "")
232
+ });
233
+ res.end();
234
+ });
235
+ } else if(typeof contentBase === "number") {
236
+ // Redirect every request to the port contentBase
237
+ app.get("*", function(req, res) {
238
+ res.writeHead(302, {
239
+ 'Location': "//localhost:" + contentBase + req.path + (req._parsedUrl.search || "")
240
+ });
241
+ res.end();
242
+ });
243
+ } else {
244
+ // route content request
245
+ app.get("*", express.static(contentBase, options.staticOptions), serveIndex(contentBase));
246
+ }
247
+ }
248
+ }.bind(this),
249
+
250
+ middleware: function() {
251
+ // include our middleware to ensure it is able to handle '/index.html' request after redirect
252
+ app.use(this.middleware);
253
+ }.bind(this),
254
+
255
+ headers: function() {
256
+ app.all("*", this.setContentHeaders.bind(this));
257
+ }.bind(this),
258
+
259
+ magicHtml: function() {
260
+ app.get("*", this.serveMagicHtml.bind(this));
261
+ }.bind(this),
262
+
263
+ setup: function() {
264
+ if(typeof options.setup === "function")
265
+ options.setup(app, this);
266
+ }.bind(this)
267
+ };
268
+
269
+ var defaultFeatures = ["setup", "headers", "middleware"];
270
+ if(options.proxy)
271
+ defaultFeatures.push("proxy", "middleware");
272
+ if(options.historyApiFallback)
273
+ defaultFeatures.push("historyApiFallback", "middleware");
274
+ defaultFeatures.push("magicHtml");
275
+ if(options.contentBase !== false)
276
+ defaultFeatures.push("contentBase");
277
+ // compress is placed last and uses unshift so that it will be the first middleware used
278
+ if(options.compress)
279
+ defaultFeatures.unshift("compress");
280
+
281
+ (options.features || defaultFeatures).forEach(function(feature) {
282
+ features[feature]();
283
+ }, this);
284
+
285
+ if(options.https) {
286
+ // for keep supporting CLI parameters
287
+ if(typeof options.https === 'boolean') {
288
+ options.https = {
289
+ key: options.key,
290
+ cert: options.cert,
291
+ ca: options.ca,
292
+ pfx: options.pfx,
293
+ passphrase: options.pfxPassphrase
294
+ };
295
+ }
296
+
297
+ // using built-in self-signed certificate if no certificate was configured
298
+ options.https.key = options.https.key || fs.readFileSync(path.join(__dirname, "../ssl/server.key"));
299
+ options.https.cert = options.https.cert || fs.readFileSync(path.join(__dirname, "../ssl/server.crt"));
300
+ options.https.ca = options.https.ca || fs.readFileSync(path.join(__dirname, "../ssl/ca.crt"));
301
+
302
+ this.listeningApp = https.createServer(options.https, app);
303
+ } else {
304
+ this.listeningApp = http.createServer(app);
305
+ }
306
+ }
307
+
308
+ Server.prototype.use = function() {
309
+ this.app.use.apply(this.app, arguments);
310
+ }
311
+
312
+ Server.prototype.setContentHeaders = function(req, res, next) {
313
+ if(this.headers) {
314
+ for(var name in this.headers) {
315
+ res.setHeader(name, this.headers[name]);
316
+ }
317
+ }
318
+
319
+ next();
320
+ }
321
+
322
+ // delegate listen call and init sockjs
323
+ Server.prototype.listen = function() {
324
+ this.listeningApp.listen.apply(this.listeningApp, arguments);
325
+ var sockServer = sockjs.createServer({
326
+ // Limit useless logs
327
+ log: function(severity, line) {
328
+ if(severity === "error") {
329
+ console.log(line);
330
+ }
331
+ }
332
+ });
333
+ sockServer.on("connection", function(conn) {
334
+ if(!conn) return;
335
+ this.sockets.push(conn);
336
+
337
+ conn.on("close", function() {
338
+ var connIndex = this.sockets.indexOf(conn);
339
+ if(connIndex >= 0) {
340
+ this.sockets.splice(connIndex, 1);
341
+ }
342
+ }.bind(this));
343
+
344
+ if(this.clientLogLevel)
345
+ this.sockWrite([conn], "log-level", this.clientLogLevel);
346
+
347
+ if(this.hot) this.sockWrite([conn], "hot");
348
+
349
+ if(!this._stats) return;
350
+ this._sendStats([conn], this._stats.toJson(), true);
351
+ }.bind(this));
352
+
353
+ sockServer.installHandlers(this.listeningApp, {
354
+ prefix: '/sockjs-node'
355
+ });
356
+ }
357
+
358
+ Server.prototype.close = function() {
359
+ this.sockets.forEach(function(sock) {
360
+ sock.close();
361
+ });
362
+ this.sockets = [];
363
+ this.middleware.close();
364
+ this.listeningApp.close();
365
+ }
366
+
367
+ Server.prototype.sockWrite = function(sockets, type, data) {
368
+ sockets.forEach(function(sock) {
369
+ sock.write(JSON.stringify({
370
+ type: type,
371
+ data: data
372
+ }));
373
+ });
374
+ }
375
+
376
+ Server.prototype.serveMagicHtml = function(req, res, next) {
377
+ var _path = req.path;
378
+ try {
379
+ if(!this.middleware.fileSystem.statSync(this.middleware.getFilenameFromUrl(_path + ".js")).isFile())
380
+ return next();
381
+ // Serve a page that executes the javascript
382
+ res.write('<!DOCTYPE html><html><head><meta charset="utf-8"/></head><body><script type="text/javascript" charset="utf-8" src="');
383
+ res.write(_path);
384
+ res.write('.js');
385
+ res.write(req._parsedUrl.search || "");
386
+ res.end('"></script></body></html>');
387
+ } catch(e) {
388
+ return next();
389
+ }
390
+ }
391
+
392
+ // send stats to a socket or multiple sockets
393
+ Server.prototype._sendStats = function(sockets, stats, force) {
394
+ if(!force &&
395
+ stats &&
396
+ (!stats.errors || stats.errors.length === 0) &&
397
+ stats.assets &&
398
+ stats.assets.every(function(asset) {
399
+ return !asset.emitted;
400
+ })
401
+ )
402
+ return this.sockWrite(sockets, "still-ok");
403
+ this.sockWrite(sockets, "hash", stats.hash);
404
+ if(stats.errors.length > 0)
405
+ this.sockWrite(sockets, "errors", stats.errors);
406
+ else if(stats.warnings.length > 0)
407
+ this.sockWrite(sockets, "warnings", stats.warnings);
408
+ else
409
+ this.sockWrite(sockets, "ok");
410
+ }
411
+
412
+ Server.prototype.invalidate = function() {
413
+ if(this.middleware) this.middleware.invalidate();
414
+ }
415
+
416
+ module.exports = Server;