1 | fs = require 'fs'
|
2 | path = require 'path'
|
3 | ws = require 'ws'
|
4 | http = require 'http'
|
5 | https = require 'https'
|
6 | url = require 'url'
|
7 | chokidar = require 'chokidar'
|
8 |
|
9 | protocol_version = '7'
|
10 | defaultPort = 35729
|
11 |
|
12 | defaultExts = [
|
13 | 'html', 'css', 'js', 'png', 'gif', 'jpg',
|
14 | 'php', 'php5', 'py', 'rb', 'erb', 'coffee'
|
15 | ]
|
16 |
|
17 | defaultExclusions = [/\.git\//, /\.svn\//, /\.hg\//]
|
18 |
|
19 | class 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 |
|
57 |
|
58 |
|
59 |
|
60 |
|
61 |
|
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 |
|
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 |
|
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 |
|
156 | @server._server.close()
|
157 | @server.close()
|
158 |
|
159 | exports.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
|