11var fs = require ( 'fs' ) ,
22 util = require ( 'util' ) ,
33 union = require ( 'union' ) ,
4- ecstatic = require ( 'ecstatic' ) ;
4+ sexstatic = require ( 'sexstatic' ) ,
5+ ws = require ( 'ws' ) ,
6+ portfinder = require ( 'portfinder' ) ,
7+ chokidar = require ( 'chokidar' ) ;
58
69var HTTPServer = exports . HTTPServer = function ( options ) {
710 options = options || { } ;
@@ -19,6 +22,96 @@ var HTTPServer = exports.HTTPServer = function (options) {
1922 }
2023 }
2124
25+ var hotReloadScript = ' \
26+ var xhr = new XMLHttpRequest(); \n \
27+ if (typeof(window.reloadOnChange) == "undefined") \n \
28+ window.reloadOnChange = true; \n \
29+ \n \
30+ xhr.onreadystatechange = function() { \n \
31+ if (xhr.readyState == 4) \n \
32+ { \n \
33+ var resp = JSON.parse(xhr.responseText); \n \
34+ var changeSocket = new WebSocket("ws://"+location.hostname+":"+resp.port+"/"); \n \
35+ changeSocket.onopen = function() { console.log("-- change socket opened, observing changes --") } \n \
36+ changeSocket.onmessage = function(ev) { \n \
37+ if (ev.data.indexOf("change") != -1) { \n \
38+ setTimeout(function() { \n \
39+ if (window.reloadOnChange) { \n \
40+ console.log("change detected: reloading --"); \n \
41+ window.location.reload(); \n \
42+ }}, 1000); \n \
43+ } \n \
44+ if (ev.data.indexOf("folder") != -1 && document.querySelector("title").innerText.indexOf("Index of") != -1) { \n \
45+ console.log("folder change, reloading"); \n \
46+ window.location.reload(); \n \
47+ } \n \
48+ } \n \
49+ }} \n \
50+ xhr.open("GET", "ws.json", true); \n\
51+ xhr.send(null); \
52+ ' ;
53+
54+ var mutableExtras = {
55+ 'http-hot-reload.js' : {
56+ 'content-type' : 'text/javascript' ,
57+ 'content' : hotReloadScript
58+ } ,
59+ 'ws.json' : {
60+ 'content-type' : 'text/json' ,
61+ 'content' : JSON . stringify ( {
62+ port : this . wsPort ,
63+ path : this . root ,
64+ additional : "file untouched. the ws server probably isn't running."
65+ } )
66+ }
67+ }
68+
69+ portfinder . basePort = 8086 ;
70+ portfinder . getPort ( function ( err , port ) {
71+ if ( err ) { console . log ( "Error, no open ports available." ) ; console . dir ( err ) ; return ; }
72+
73+ this . wsPort = port ;
74+ console . log ( 'Websocket Server Listening on Port: ' + port ) ;
75+
76+ this . wss = new ws . Server ( { port : port } ) ;
77+ this . wss . broadcast = function broadcast ( data ) {
78+ for ( var i in this . clients ) {
79+ try { this . clients [ i ] . send ( data ) ; } catch ( ex ) { console . log ( 'error sending to client.' ) ; console . dir ( ex ) ; }
80+ }
81+ } ;
82+
83+ //
84+
85+ mutableExtras [ 'ws.json' ] . content = JSON . stringify ( {
86+ port : this . wsPort ,
87+ path : this . root
88+ } ) ;
89+
90+ var watcher_ready = false ;
91+
92+ var watcher = chokidar . watch ( this . root , { ignored : / [ \/ \\ ] \. / , persistent : true } ) ;
93+
94+ watcher . on ( 'ready' , function ( ) { console . log ( 'Scanned working directory. ready for changes..' ) ; watcher_ready = true ; } ) ;
95+ watcher . on ( 'unlinkDir' , function ( path ) {
96+ if ( ! watcher_ready ) return ;
97+ this . wss . broadcast ( 'folder|' + path ) ;
98+ } . bind ( this ) ) ;
99+ watcher . on ( 'addDir' , function ( path ) {
100+ if ( ! watcher_ready ) return ;
101+ this . wss . broadcast ( 'folder|' + path ) ;
102+ } . bind ( this ) ) ;
103+ watcher . on ( 'add' , function ( path ) {
104+ if ( ! watcher_ready ) return ;
105+ this . wss . broadcast ( 'folder|' + path ) ;
106+ } . bind ( this ) ) ;
107+ watcher . on ( 'change' , function ( filename ) {
108+ if ( ! watcher_ready ) return ;
109+ console . log ( 'change detected! telling clients to reload. [' + filename + ']' ) ;
110+ this . wss . broadcast ( 'changed|' + filename ) ;
111+ } . bind ( this ) ) ;
112+ } . bind ( this ) ) ;
113+
114+
22115 if ( options . headers ) {
23116 this . headers = options . headers ;
24117 }
@@ -33,6 +126,15 @@ var HTTPServer = exports.HTTPServer = function (options) {
33126 : options . ext ;
34127 }
35128
129+ function addReloadScript ( src )
130+ {
131+ var index = src . indexOf ( "</body" ) ;
132+ if ( index == - 1 ) return src ;
133+ var out = src . substr ( 0 , index ) ;
134+ out += '<script type="text/javascript" src="http-hot-reload.js"></script>' + src . substr ( index ) ;
135+ return out ;
136+ }
137+
36138 var serverOptions = {
37139 before : ( options . before || [ ] ) . concat ( [
38140 function ( req , res ) {
@@ -42,13 +144,17 @@ var HTTPServer = exports.HTTPServer = function (options) {
42144
43145 res . emit ( 'next' ) ;
44146 } ,
45- ecstatic ( {
147+ sexstatic ( {
46148 root : this . root ,
47149 cache : this . cache ,
48150 showDir : this . showDir ,
49151 autoIndex : this . autoIndex ,
50- defaultExt : this . ext
51- } )
152+ defaultExt : this . ext ,
153+ modifyFunctions : [
154+ addReloadScript
155+ ] ,
156+ extras : mutableExtras
157+ } ) ,
52158 ] ) ,
53159 headers : this . headers || { }
54160 } ;
0 commit comments