1 | var sysPath = require('path');
|
2 | var fs = require('fs');
|
3 | var https = require('https');
|
4 | var WebSocketServer = require('ws').Server;
|
5 | var isWorker = require('cluster').isWorker;
|
6 | var isCss = function(file) {
|
7 | return sysPath.extname(file.path) === '.css';
|
8 | };
|
9 | var startingPort = 9485;
|
10 |
|
11 | function AutoReloader(config) {
|
12 | if (config == null) config = {};
|
13 | this.config = config;
|
14 | if (config.autoReload) {
|
15 | throw new Error('Warning: config.autoReload is no longer supported, please move it to config.plugins.autoReload');
|
16 | }
|
17 | var plugins = config.plugins || {};
|
18 | var cfg = plugins.autoReload || {};
|
19 | var ports = [];
|
20 |
|
21 | if (cfg.port == null) {
|
22 | for (var i = 0; i < 11; i++) ports.push(startingPort + i);
|
23 | } else {
|
24 | ports = Array.isArray(cfg.port) ? cfg.port.slice() : [cfg.port];
|
25 | }
|
26 |
|
27 | if (this.config.persistent) {
|
28 | this.enabled = (cfg.enabled == null) ? true : cfg.enabled;
|
29 | }
|
30 | this.delay = cfg.delay;
|
31 |
|
32 | var conns = this.connections = [];
|
33 | var host = cfg.host || '0.0.0.0';
|
34 | var port = this.port = ports.shift();
|
35 |
|
36 | var key, cert;
|
37 | if (cfg.keyPath && cfg.certPath) {
|
38 | this.ssl = true;
|
39 | key = fs.readFileSync(cfg.keyPath);
|
40 | cert = fs.readFileSync(cfg.certPath);
|
41 | if (key && cert) {
|
42 | this.httpsServer = https.createServer({key: key, cert: cert}).listen(port, host);
|
43 | }
|
44 | }
|
45 |
|
46 | var startServer = (function() {
|
47 | this.port = port;
|
48 | var args = this.httpsServer ? {server: this.httpsServer} : {host: host, port: port}
|
49 | var server = this.server = new WebSocketServer(args);
|
50 | server.on('connection', function(conn) {
|
51 | conns.push(conn);
|
52 | conn.on('close', function() {
|
53 | conns.splice(conn, 1);
|
54 | });
|
55 | });
|
56 | server.on('error', function(error) {
|
57 | if (error.toString().match(/EADDRINUSE/)) {
|
58 | if (ports.length) {
|
59 | port = ports.shift();
|
60 | startServer();
|
61 | } else {
|
62 | error = "cannot start because port " + port + " is in use";
|
63 | }
|
64 | }
|
65 |
|
66 | });
|
67 | }).bind(this);
|
68 |
|
69 | if (this.enabled && !isWorker) startServer();
|
70 | }
|
71 |
|
72 | AutoReloader.prototype.brunchPlugin = true;
|
73 | AutoReloader.prototype.type = 'javascript';
|
74 | AutoReloader.prototype.extension = 'js';
|
75 |
|
76 | AutoReloader.prototype.onCompile = function(changedFiles) {
|
77 | var enabled = this.enabled;
|
78 | var conns = this.connections;
|
79 | if (!enabled) return;
|
80 |
|
81 | var didCompile = changedFiles.length > 0;
|
82 | var allCss = didCompile && changedFiles.every(isCss);
|
83 |
|
84 | if (toString.call(enabled) === '[object Object]') {
|
85 | if (!(didCompile || enabled.assets)) return;
|
86 | if (allCss) {
|
87 | if (!enabled.css) return;
|
88 | } else if (didCompile) {
|
89 | var changedExts = changedFiles.map(function(_) {
|
90 | return sysPath.extname(_.path).slice(1);
|
91 | });
|
92 | var wasChanged = !Object.keys(enabled).some(function(_) {
|
93 | return enabled[_] && changedExts.indexOf(_) !== -1;
|
94 | });
|
95 | if (wasChanged) return;
|
96 | }
|
97 | }
|
98 |
|
99 | var message = allCss ? 'stylesheet' : 'page';
|
100 | var sendMessage = function() {
|
101 | conns
|
102 | .filter(function(connection) {
|
103 | return connection.readyState === 1;
|
104 | })
|
105 | .forEach(function(connection) {
|
106 | return connection.send(message);
|
107 | });
|
108 | };
|
109 |
|
110 | this.delay ? setTimeout(sendMessage, this.delay) : sendMessage();
|
111 | };
|
112 |
|
113 | var fileName = 'auto-reload.js';
|
114 | AutoReloader.prototype.include = function() {
|
115 | return this.enabled ?
|
116 | [sysPath.join(__dirname, 'vendor', fileName)] : [];
|
117 | };
|
118 |
|
119 | AutoReloader.prototype.teardown = function() {
|
120 | if (this.server) this.server.close();
|
121 | if (this.httpsServer) this.httpsServer.close();
|
122 | };
|
123 |
|
124 | AutoReloader.prototype.compile = function(params, callback) {
|
125 | if (this.enabled &&
|
126 | this.port !== startingPort &&
|
127 | sysPath.basename(params.path) === fileName) {
|
128 | params.data = params.data.replace(startingPort, this.port);
|
129 | }
|
130 | if (this.enabled && this.ssl) {
|
131 | params.data = params.data.replace('ws://', 'wss://');
|
132 | }
|
133 | return callback(null, params);
|
134 | };
|
135 |
|
136 | module.exports = AutoReloader
|