diff --git a/client/index.js b/client/index.js index 3c205da669..4d27fe69fa 100644 --- a/client/index.js +++ b/client/index.js @@ -1,5 +1,5 @@ var url = require('url'); -var io = require("socket.io-client"); +var SockJS = require("sockjs-client"); var stripAnsi = require('strip-ansi'); var scriptElements = document.getElementsByTagName("script"); @@ -8,70 +8,79 @@ var urlParts = url.parse(typeof __resourceQuery === "string" && __resourceQuery scriptElements[scriptElements.length-1].getAttribute("src").replace(/\/[^\/]+$/, "") ); -io = io.connect( - url.format({ - protocol: urlParts.protocol, - auth: urlParts.auth, - hostname: (urlParts.hostname === '0.0.0.0') ? window.location.hostname : urlParts.hostname, - port: urlParts.port - }), { - path: urlParts.path === '/' ? null : urlParts.path - } -); - +var sock = null; var hot = false; var initial = true; var currentHash = ""; -io.on("hot", function() { - hot = true; - console.log("[WDS] Hot Module Replacement enabled."); -}); - -io.on("invalid", function() { - console.log("[WDS] App updated. Recompiling..."); -}); - -io.on("hash", function(hash) { - currentHash = hash; -}); - -io.on("still-ok", function() { - console.log("[WDS] Nothing changed.") -}); +var onSocketMsg = { + hot: function() { + hot = true; + console.log("[WDS] Hot Module Replacement enabled."); + }, + invalid: function() { + console.log("[WDS] App updated. Recompiling..."); + }, + hash: function(hash) { + currentHash = hash; + }, + "still-ok": function() { + console.log("[WDS] Nothing changed.") + }, + ok: function() { + if(initial) return initial = false; + reloadApp(); + }, + warnings: function(warnings) { + console.log("[WDS] Warnings while compiling."); + for(var i = 0; i < warnings.length; i++) + console.warn(stripAnsi(warnings[i])); + if(initial) return initial = false; + reloadApp(); + }, + errors: function(errors) { + console.log("[WDS] Errors while compiling."); + for(var i = 0; i < errors.length; i++) + console.error(stripAnsi(errors[i])); + if(initial) return initial = false; + reloadApp(); + }, + "proxy-error": function(errors) { + console.log("[WDS] Proxy error."); + for(var i = 0; i < errors.length; i++) + console.error(stripAnsi(errors[i])); + if(initial) return initial = false; + reloadApp(); + } +}; -io.on("ok", function() { - if(initial) return initial = false; - reloadApp(); -}); +var newConnection = function() { + sock = new SockJS(url.format({ + protocol: urlParts.protocol, + auth: urlParts.auth, + hostname: (urlParts.hostname === '0.0.0.0') ? window.location.hostname : urlParts.hostname, + port: urlParts.port, + pathname: urlParts.path === '/' ? "/sockjs-node" : urlParts.path + })); -io.on("warnings", function(warnings) { - console.log("[WDS] Warnings while compiling."); - for(var i = 0; i < warnings.length; i++) - console.warn(stripAnsi(warnings[i])); - if(initial) return initial = false; - reloadApp(); -}); + sock.onclose = function() { + console.error("[WDS] Disconnected!"); -io.on("errors", function(errors) { - console.log("[WDS] Errors while compiling."); - for(var i = 0; i < errors.length; i++) - console.error(stripAnsi(errors[i])); - if(initial) return initial = false; - reloadApp(); -}); + // Try to reconnect. + sock = null; + setTimeout(function () { + newConnection(); + }, 2000); + }; -io.on("proxy-error", function(errors) { - console.log("[WDS] Proxy error."); - for(var i = 0; i < errors.length; i++) - console.error(stripAnsi(errors[i])); - if(initial) return initial = false; - reloadApp(); -}); + sock.onmessage = function(e) { + // This assumes that all data sent via the websocket is JSON. + var msg = JSON.parse(e.data); + onSocketMsg[msg.type](msg.data); + }; +}; -io.on("disconnect", function() { - console.error("[WDS] Disconnected!"); -}); +newConnection(); function reloadApp() { if(hot) { diff --git a/client/live.js b/client/live.js index 46bd9d7493..da0c0da3e2 100644 --- a/client/live.js +++ b/client/live.js @@ -1,8 +1,32 @@ var $ = require("jquery"); -var io = require("socket.io-client"); +var SockJS = require("sockjs-client"); var stripAnsi = require('strip-ansi'); require("./style.css"); +var sock = null; +var hot = false; +var currentHash = ""; + +var newConnection = function(handlers) { + sock = new SockJS('/sockjs-node'); + + sock.onclose = function() { + handlers.close(); + + // Try to reconnect. + sock = null; + setTimeout(function () { + newConnection(handlers); + }, 2000); + }; + + sock.onmessage = function(e) { + // This assumes that all data sent via the websocket is JSON. + var msg = JSON.parse(e.data); + handlers[msg.type](msg.data); + }; +}; + $(function() { var body = $("body").html(require("./page.jade")()); var status = $("#status"); @@ -10,74 +34,67 @@ $(function() { var $errors = $("#errors"); var iframe = $("#iframe"); var header = $(".header"); - var hot = false; - var currentHash = ""; var contentPage = window.location.pathname.substr("/webpack-dev-server".length) + window.location.search; - status.text("Connecting to socket.io server..."); + status.text("Connecting to sockjs server..."); $errors.hide(); iframe.hide(); header.css({borderColor: "#96b5b4"}); - io = io.connect(); - - io.on("hot", function() { - hot = true; - iframe.attr("src", contentPage + window.location.hash); - }); - io.on("invalid", function() { - okness.text(""); - status.text("App updated. Recompiling..."); - header.css({borderColor: "#96b5b4"}); - $errors.hide(); if(!hot) iframe.hide(); - }); - - io.on("hash", function(hash) { - currentHash = hash; - }); - - io.on("still-ok", function() { - okness.text(""); - status.text("App ready."); - header.css({borderColor: ""}); - $errors.hide(); if(!hot) iframe.show(); - }); - - io.on("ok", function() { - okness.text(""); - $errors.hide(); - reloadApp(); - }); - - io.on("warnings", function(warnings) { - okness.text("Warnings while compiling."); - $errors.hide(); - reloadApp(); - }); - - io.on("errors", function(errors) { - status.text("App updated with errors. No reload!"); - okness.text("Errors while compiling."); - $errors.text("\n" + stripAnsi(errors.join("\n\n\n")) + "\n\n"); - header.css({borderColor: "#ebcb8b"}); - $errors.show(); iframe.hide(); - }); - - io.on("proxy-error", function(errors) { - status.text("Could not proxy to content base target!"); - okness.text("Proxy error."); - $errors.text("\n" + stripAnsi(errors.join("\n\n\n")) + "\n\n"); - header.css({borderColor: "#ebcb8b"}); - $errors.show(); iframe.hide(); - }); + var onSocketMsg = { + hot: function() { + hot = true; + iframe.attr("src", contentPage + window.location.hash); + }, + invalid: function() { + okness.text(""); + status.text("App updated. Recompiling..."); + header.css({borderColor: "#96b5b4"}); + $errors.hide(); if(!hot) iframe.hide(); + }, + hash: function(hash) { + currentHash = hash; + }, + "still-ok": function() { + okness.text(""); + status.text("App ready."); + header.css({borderColor: ""}); + $errors.hide(); if(!hot) iframe.show(); + }, + ok: function() { + okness.text(""); + $errors.hide(); + reloadApp(); + }, + warnings: function(warnings) { + okness.text("Warnings while compiling."); + $errors.hide(); + reloadApp(); + }, + errors: function(errors) { + status.text("App updated with errors. No reload!"); + okness.text("Errors while compiling."); + $errors.text("\n" + stripAnsi(errors.join("\n\n\n")) + "\n\n"); + header.css({borderColor: "#ebcb8b"}); + $errors.show(); iframe.hide(); + }, + "proxy-error": function(errors) { + status.text("Could not proxy to content base target!"); + okness.text("Proxy error."); + $errors.text("\n" + stripAnsi(errors.join("\n\n\n")) + "\n\n"); + header.css({borderColor: "#ebcb8b"}); + $errors.show(); iframe.hide(); + }, + close: function() { + status.text(""); + okness.text("Disconnected."); + $errors.text("\n\n\n Lost connection to webpack-dev-server.\n Please restart the server to reestablish connection...\n\n\n\n"); + header.css({borderColor: "#ebcb8b"}); + $errors.show(); iframe.hide(); + } + }; - io.on("disconnect", function() { - status.text(""); - okness.text("Disconnected."); - $errors.text("\n\n\n Lost connection to webpack-dev-server.\n Please restart the server to reestablish connection...\n\n\n\n"); - header.css({borderColor: "#ebcb8b"}); - $errors.show(); iframe.hide(); - }); + newConnection(onSocketMsg); iframe.load(function() { status.text("App ready."); diff --git a/lib/Server.js b/lib/Server.js index 0e43182a08..8f263cc9ac 100644 --- a/lib/Server.js +++ b/lib/Server.js @@ -3,7 +3,7 @@ var path = require("path"); var webpackDevMiddleware = require("webpack-dev-middleware"); var express = require("express"); var compress = require("compression"); -var socketio = require("socket.io"); +var sockjs = require("sockjs"); var StreamCache = require("stream-cache"); var http = require("http"); var https = require("https"); @@ -22,16 +22,16 @@ function Server(compiler, options) { this.hot = options.hot; this.headers = options.headers; + this.sockets = []; // Listening for events var invalidPlugin = function() { - if(this.io) this.io.sockets.emit("invalid"); + this.sockWrite(this.sockets, "invalid"); }.bind(this); compiler.plugin("compile", invalidPlugin); compiler.plugin("invalid", invalidPlugin); compiler.plugin("done", function(stats) { - if(!this.io) return; - this._sendStats(this.io.sockets, stats.toJson()); + this._sendStats(this.sockets, stats.toJson()); this._stats = stats; }.bind(this)); @@ -146,7 +146,7 @@ function Server(compiler, options) { } proxy.web(req, res, proxyOptions, function(err){ var msg = "cannot proxy to " + proxyOptions.target + " (" + err.message + ")"; - this.io.sockets.emit("proxy-error", [msg]); + this.sockWrite(this.sockets, "proxy-error", [msg]); res.statusCode = 502; res.end(); }.bind(this)); @@ -177,7 +177,7 @@ function Server(compiler, options) { app.all("*", function(req, res) { proxy.web(req, res, contentBase, function(err) { var msg = "cannot proxy to " + contentBase.target + " (" + err.message + ")"; - this.io.sockets.emit("proxy-error", [msg]); + this.sockWrite(this.sockets, "proxy-error", [msg]); res.statusCode = 502; res.end(); }.bind(this)); @@ -264,24 +264,52 @@ Server.prototype.setContentHeaders = function(req, res, next) { next(); } -// delegate listen call and init socket.io +// delegate listen call and init sockjs Server.prototype.listen = function() { this.listeningApp.listen.apply(this.listeningApp, arguments); - this.io = socketio.listen(this.listeningApp, { - "log level": 1 + var sockServer = sockjs.createServer({ + // Limit useless logs + log: function(severity, line) { + if (severity === "error") { + console.log(line); + } + } }); - this.io.sockets.on("connection", function(socket) { - if(this.hot) socket.emit("hot"); + sockServer.on("connection", function(conn) { + this.sockets.push(conn); + + // Remove the connection when it's closed + conn.on("close", function() { + var connIndex = this.sockets.indexOf(conn); + if (connIndex >= 0) { + this.sockets.splice(connIndex, 1); + } + }.bind(this)); + + if(this.hot) this.sockWrite([conn], "hot"); if(!this._stats) return; - this._sendStats(socket, this._stats.toJson(), true); + this._sendStats([conn], this._stats.toJson(), true); }.bind(this)); + + sockServer.installHandlers(this.listeningApp, { + prefix: '/sockjs-node' + }); } Server.prototype.close = function() { - this.io.close(); // Will also close listeningApp + this.sockets.forEach(function(sock) { + sock.close(); + }); + this.sockets = []; this.middleware.close(); } +Server.prototype.sockWrite = function(sockets, type, data) { + sockets.forEach(function(sock) { + sock.write(JSON.stringify({type: type, data: data})); + }); +} + Server.prototype.serveMagicHtml = function(req, res, next) { var _path = req.path; try { @@ -297,17 +325,17 @@ Server.prototype.serveMagicHtml = function(req, res, next) { } // send stats to a socket or multiple sockets -Server.prototype._sendStats = function(socket, stats, force) { +Server.prototype._sendStats = function(sockets, stats, force) { if(!force && stats && (!stats.errors || stats.errors.length === 0) && stats.assets && stats.assets.every(function(asset) { return !asset.emitted; - })) return socket.emit("still-ok"); - socket.emit("hash", stats.hash); + })) return this.sockWrite(sockets, "still-ok"); + this.sockWrite(sockets, "hash", stats.hash); if(stats.errors.length > 0) - socket.emit("errors", stats.errors); + this.sockWrite(sockets, "errors", stats.errors); else if(stats.warnings.length > 0) - socket.emit("warnings", stats.warnings); + this.sockWrite(sockets, "warnings", stats.warnings); else - socket.emit("ok"); + this.sockWrite(sockets, "ok"); } Server.prototype.invalidate = function() { diff --git a/package.json b/package.json index d6155d232a..b69a7f2358 100644 --- a/package.json +++ b/package.json @@ -13,8 +13,8 @@ "http-proxy": "^1.11.2", "optimist": "~0.6.0", "serve-index": "^1.7.2", - "socket.io": "^1.3.6", - "socket.io-client": "^1.3.6", + "sockjs": "^0.3.15", + "sockjs-client": "^1.0.3", "stream-cache": "~0.0.1", "strip-ansi": "^3.0.0", "supports-color": "^3.1.1",