UNPKG

3.32 kBJavaScriptView Raw
1var Tinylr = require('tiny-lr')
2var unyield = require('unyield')
3var chokidar = require('chokidar')
4
5var debounce = require('./debounce')
6var hashFiles = require('./hashfile').hashFiles
7var isAsset = require('./helpers').isAsset
8var diffHashes = require('./helpers').diffHashes
9var filterFiles = require('./helpers').filterFiles
10
11/*
12 * injects livereload into connect() server, and starts a livereload server at
13 * a random port
14 */
15
16exports.spawnLR = unyield(function * (port) {
17 var lrServer = new Tinylr()
18 lrServer.listen(port)
19 return lrServer
20})
21
22/*
23 * returns a watcher to update tinyLR
24 */
25
26exports.watchLR = function (root, lrServer, onChange) {
27 var hashes = {}
28
29 var update = unyield(function * (argsList) {
30 // Get a list of paths that have been 'change'd or 'create'd.
31 // If it's been deleted, mark it off.
32 var paths = argsList.reduce(function (list, args) {
33 var fname = args[1]
34 if (!isAsset(fname)) return list
35
36 if (args[0] === 'delete') {
37 delete hashes[fname]
38 } else {
39 list.push(fname)
40 }
41 return list
42 }, [])
43
44 // Get rid of any non-files (directories)
45 paths = yield filterFiles(root, paths)
46 if (paths.length === 0) return
47
48 // Get their hashes
49 var newHashes = yield hashFiles(root, paths)
50
51 // Compare with old
52 var files = diffHashes(hashes, newHashes)
53 if (files.length === 0) return
54
55 // Call the callback
56 if (onChange) onChange(files)
57
58 files = files.map(escape)
59 lrServer.changed({
60 body: { files: files }
61 })
62 })
63
64 var uupdate = function (argsList) {
65 update(argsList, function (err) { if (err) throw err })
66 }
67
68 return chokidar.watch(root, {
69 ignoreInitial: true,
70 cwd: root
71 })
72 .on('all', debounce(uupdate, 50))
73}
74
75/*
76 * connect() middleware for injecting the livereload snippet
77 * thanks to http://npmjs.com/serveur
78 */
79
80exports.injectLR = function (port) {
81 var snippet = getSnippet(port)
82
83 return function injectLR (req, res, next) {
84 var write = res.write
85 res.write = function (string, encoding) {
86 if (!isHtmlResponse(res)) return write.call(res, string, encoding)
87
88 var body = string instanceof Buffer ? string.toString() : string
89 if (~body.indexOf('</body>')) {
90 body = body.replace(/<\/body>/, function (w) {
91 return snippet + w
92 })
93 } else {
94 body += snippet
95 }
96 if (string instanceof Buffer) {
97 string = new Buffer(body)
98 } else {
99 string = body
100 }
101 if (!this.headersSent) {
102 this.setHeader('content-length', Buffer.byteLength(body))
103 this._implicitHeader()
104 }
105 write.call(res, string, encoding)
106 }
107 next()
108 }
109}
110
111/*
112 * checks if the response is supposed to be an HTML document
113 */
114
115function isHtmlResponse (res) {
116 return res._headers &&
117 res._headers['content-type'] &&
118 res._headers['content-type'].indexOf('text/html') > -1
119}
120
121/*
122 * returns the html snippet to be inserted before the closing body tag
123 */
124
125function getSnippet (port) {
126 return [
127 '<!-- livereload -->',
128 "<script>document.write('<script src=\"http://'",
129 "+(location.host||'localhost').split(':')[0]",
130 "+':" + port + "/livereload.js?snipver=1\"><\\/script>')",
131 '</script>'
132 ].join('') + '\n'
133}
134
135exports.getSnippet = getSnippet