UNPKG

4.58 kBtext/coffeescriptView Raw
1fs = require 'fs'
2path = require 'path'
3ws = require 'ws'
4http = require 'http'
5https = require 'https'
6url = require 'url'
7chokidar = require 'chokidar'
8
9protocol_version = '7'
10defaultPort = 35729
11
12defaultExts = [
13 'html', 'css', 'js', 'png', 'gif', 'jpg',
14 'php', 'php5', 'py', 'rb', 'erb', 'coffee'
15]
16
17defaultExclusions = [/\.git\//, /\.svn\//, /\.hg\//]
18
19class Server
20 constructor: (@config) ->
21 @config ?= {}
22
23 @config.version ?= protocol_version
24 @config.port ?= defaultPort
25
26 @config.exts ?= []
27 @config.exclusions ?= []
28
29 @config.exts = @config.exts.concat defaultExts
30 @config.exclusions = @config.exclusions.concat defaultExclusions
31
32 @config.applyCSSLive ?= true
33
34 @config.originalPath ?= ''
35 @config.overrideURL ?= ''
36
37 @config.usePolling ?= false
38
39 listen: (callback) ->
40 @debug "LiveReload is waiting for browser to connect."
41
42 if @config.server
43 @config.server.listen @config.port
44 @server = new ws.Server({server: @config.server})
45 else
46 @server = new ws.Server({port: @config.port})
47
48 @server.on 'connection', @onConnection.bind @
49 @server.on 'close', @onClose.bind @
50 if callback
51 @server.once 'listening', callback
52
53 onConnection: (socket) ->
54 @debug "Browser connected."
55
56 # Client sends various messages under the key 'command'
57 #
58 # 'hello': the handshake. Must reply with 'hello'
59 # 'info' : info about the client script and any plugins it has enabled
60 #
61 # TODO: handle info messages
62 socket.on 'message', (message) =>
63 @debug "Client message: #{message}"
64
65 request = JSON.parse(message)
66
67 if request.command == "hello"
68 @debug "Client requested handshake..."
69 @debug "Handshaking with client using protocol #{@config.version}..."
70
71 data = JSON.stringify {
72 command: 'hello',
73 protocols: [
74 'http://livereload.com/protocols/official-7',
75 'http://livereload.com/protocols/official-8',
76 'http://livereload.com/protocols/official-9',
77 'http://livereload.com/protocols/2.x-origin-version-negotiation',
78 'http://livereload.com/protocols/2.x-remote-control'],
79 serverName: 'node-livereload'
80 }
81
82 socket.send data
83
84 # handle error events from socket
85 socket.on 'error', (err) =>
86 @debug "Error in client socket: #{err}"
87
88 socket.on 'close', (message) =>
89 @debug "Client closed connection"
90
91
92 onClose: (socket) ->
93 @debug "Socket closed."
94
95 watch: (paths) ->
96 @watcher = chokidar.watch(paths,
97 ignoreInitial: true
98 ignored: @config.exclusions
99 usePolling: @config.usePolling
100 )
101 .on 'add', @filterRefresh.bind(@)
102 .on 'change', @filterRefresh.bind(@)
103 .on 'unlink', @filterRefresh.bind(@)
104
105
106 filterRefresh: (filepath) ->
107 exts = @config.exts
108 fileext = path.extname filepath
109 .substring 1
110
111 # check if file extension is supposed to be watched
112 if (exts.indexOf(fileext) != -1)
113 if @config.delay
114 delayedRefresh = setTimeout(
115 =>
116 clearTimeout(delayedRefresh)
117 @refresh filepath
118 @config.delay
119 )
120 else
121 @refresh filepath
122
123 refresh: (filepath) ->
124 @debug "Refresh: #{filepath}"
125 data = JSON.stringify {
126 command: 'reload',
127 path: filepath,
128 liveCSS: @config.applyCSSLive
129 liveImg: @config.applyImgLive,
130 originalPath: this.config.originalPath,
131 overrideURL: this.config.overrideURL
132 }
133 @sendAllClients data
134
135 alert: (message) ->
136 @debug "Alert: #{message}"
137 data = JSON.stringify {
138 command: 'alert',
139 message: message
140 }
141 @sendAllClients data
142
143 sendAllClients: (data) ->
144 for socket in @server.clients
145 socket.send data, (error) =>
146 if error
147 @debug error
148
149 debug: (str) ->
150 if @config.debug
151 console.log "#{str}\n"
152
153 close: ->
154 @watcher.close()
155 # ensure ws server is closed
156 @server._server.close()
157 @server.close()
158
159exports.createServer = (config = {}, callback) ->
160 requestHandler = ( req, res )->
161 if url.parse(req.url).pathname is '/livereload.js'
162 res.writeHead(200, {'Content-Type': 'text/javascript'})
163 res.end fs.readFileSync __dirname + '/../ext/livereload.js'
164 if !config.https?
165 app = http.createServer requestHandler
166 else
167 app = https.createServer config.https, requestHandler
168
169 config.server ?= app
170
171 server = new Server config
172 unless config.noListen
173 server.listen(callback)
174 server