UNPKG

4.8 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 @debug """
42 Protocol version: #{@config.version}
43 Exclusions: #{@config.exclusions}
44 Extensions: #{@config.exts}
45 Polling: #{@config.usePolling}
46
47 """
48
49 if @config.server
50 @config.server.listen @config.port
51 @server = new ws.Server({server: @config.server})
52 else
53 @server = new ws.Server({port: @config.port})
54
55 @server.on 'connection', @onConnection.bind @
56 @server.on 'close', @onClose.bind @
57 if callback
58 @server.once 'listening', callback
59
60 onConnection: (socket) ->
61 @debug "Browser connected."
62
63 # Client sends various messages under the key 'command'
64 #
65 # 'hello': the handshake. Must reply with 'hello'
66 # 'info' : info about the client script and any plugins it has enabled
67 #
68 # TODO: handle info messages
69 socket.on 'message', (message) =>
70 @debug "Client message: #{message}"
71
72 request = JSON.parse(message)
73
74 if request.command == "hello"
75 @debug "Client requested handshake..."
76 @debug "Handshaking with client using protocol #{@config.version}..."
77
78 data = JSON.stringify {
79 command: 'hello',
80 protocols: [
81 'http://livereload.com/protocols/official-7',
82 'http://livereload.com/protocols/official-8',
83 'http://livereload.com/protocols/official-9',
84 'http://livereload.com/protocols/2.x-origin-version-negotiation',
85 'http://livereload.com/protocols/2.x-remote-control'],
86 serverName: 'node-livereload'
87 }
88
89 socket.send data
90
91 # handle error events from socket
92 socket.on 'error', (err) =>
93 @debug "Error in client socket: #{err}"
94
95 socket.on 'close', (message) =>
96 @debug "Client closed connection"
97
98
99 onClose: (socket) ->
100 @debug "Socket closed."
101
102 watch: (paths) ->
103 @debug "Watching #{paths}..."
104 @watcher = chokidar.watch(paths,
105 ignoreInitial: true
106 ignored: @config.exclusions
107 usePolling: @config.usePolling
108 )
109 .on 'add', @filterRefresh.bind(@)
110 .on 'change', @filterRefresh.bind(@)
111 .on 'unlink', @filterRefresh.bind(@)
112
113
114 filterRefresh: (filepath) ->
115 exts = @config.exts
116 fileext = path.extname filepath
117 .substring 1
118
119 # check if file extension is supposed to be watched
120 if (exts.indexOf(fileext) != -1)
121 if @config.delay
122 delayedRefresh = setTimeout(
123 =>
124 clearTimeout(delayedRefresh)
125 @refresh filepath
126 @config.delay
127 )
128 else
129 @refresh filepath
130
131 refresh: (filepath) ->
132 @debug "Reloading: #{filepath}"
133 data = JSON.stringify {
134 command: 'reload',
135 path: filepath,
136 liveCSS: @config.applyCSSLive,
137 liveImg: @config.applyImgLive,
138 originalPath: this.config.originalPath,
139 overrideURL: this.config.overrideURL
140 }
141 @sendAllClients data
142
143 alert: (message) ->
144 @debug "Alert: #{message}"
145 data = JSON.stringify {
146 command: 'alert',
147 message: message
148 }
149 @sendAllClients data
150
151 sendAllClients: (data) ->
152 for socket in @server.clients
153 socket.send data, (error) =>
154 if error
155 @debug error
156
157 debug: (str) ->
158 if @config.debug
159 console.log "#{str}\n"
160
161 close: ->
162 @watcher.close()
163 # ensure ws server is closed
164 @server._server.close()
165 @server.close()
166
167exports.createServer = (config = {}, callback) ->
168 requestHandler = ( req, res )->
169 if url.parse(req.url).pathname is '/livereload.js'
170 res.writeHead(200, {'Content-Type': 'text/javascript'})
171 res.end fs.readFileSync __dirname + '/../ext/livereload.js'
172 if !config.https?
173 app = http.createServer requestHandler
174 else
175 app = https.createServer config.https, requestHandler
176
177 config.server ?= app
178
179 server = new Server config
180 unless config.noListen
181 server.listen(callback)
182 server