1 |
|
2 |
|
3 |
|
4 | var fs = require('fs')
|
5 | , debug = require('debug')('koa-liveload')
|
6 | , path = require('path');
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 | var is = (function(ret) {
|
13 | ['file', 'dir'].forEach(function(method) {
|
14 | ret[method] = function(fpath) {
|
15 | var suffix = ({file: 'File', dir: 'Directory'})[method];
|
16 | if (fs.existsSync(fpath)) {
|
17 | return fs.statSync(fpath)['is' + suffix]();
|
18 | }
|
19 | return false;
|
20 | }
|
21 | });
|
22 | return ret;
|
23 | }({}));
|
24 |
|
25 |
|
26 |
|
27 |
|
28 |
|
29 | var sub = function(parent, cb) {
|
30 | if (is.dir(parent)) {
|
31 | fs.readdir(parent, function(err, all) {
|
32 | all && all.forEach(function(f) {
|
33 | var sdir = path.join(parent, f)
|
34 | if (is.dir(sdir)) {
|
35 | cb.call(null, sdir)
|
36 | }
|
37 | });
|
38 | });
|
39 | }
|
40 | };
|
41 |
|
42 |
|
43 |
|
44 |
|
45 |
|
46 | var memo = (function(memo) {
|
47 | return {
|
48 | push: function(name, type) {
|
49 | memo[name] = type;
|
50 | },
|
51 | has: function(name) {
|
52 | return memo.hasOwnProperty(name) ? true : false;
|
53 | },
|
54 | update: function(name) {
|
55 | if (!is.file(name) && !is.dir(name)) {
|
56 | delete memo[name];
|
57 | }
|
58 | return true;
|
59 | }
|
60 | };
|
61 | }({}));
|
62 |
|
63 |
|
64 |
|
65 |
|
66 |
|
67 | var fileNameCache = (function(cache) {
|
68 | return {
|
69 | push: function(name) {
|
70 | cache[name] = 1;
|
71 | return this;
|
72 | },
|
73 | each: function() {
|
74 | var temp = Object.keys(cache).filter(function(name){
|
75 | if (memo.has(name)) {
|
76 | memo.update(name);
|
77 | }
|
78 | return true;
|
79 | });
|
80 | temp.forEach.apply(temp, arguments);
|
81 | return this;
|
82 | },
|
83 | clear: function(){
|
84 | cache = {};
|
85 | return this;
|
86 | }
|
87 | };
|
88 | }({}));
|
89 |
|
90 |
|
91 |
|
92 |
|
93 |
|
94 | var worker = (function() {
|
95 | var free = true;
|
96 | return {
|
97 | busydoing: function(cb) {
|
98 | if (free) {
|
99 | free = false;
|
100 | cb.call();
|
101 | }
|
102 | },
|
103 | free: function() {
|
104 | free = true;
|
105 | }
|
106 | }
|
107 | }());
|
108 |
|
109 |
|
110 |
|
111 |
|
112 |
|
113 |
|
114 |
|
115 |
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 | function watchDir(fpath, opts, cb){
|
124 | if(typeof(opts) == "function"){
|
125 | cb = opts;
|
126 | opts = {};
|
127 | }
|
128 | opts.includes = opts.includes || ['js'];
|
129 | opts.excludes = opts.excludes || ['node_modules'];
|
130 | opts.ignoreHidden = opts.ignoreHidden || true;
|
131 | watch(fpath, cb);
|
132 |
|
133 | var normalizeCall = function(fname, cb) {
|
134 | debug('changed file: ' + fname);
|
135 |
|
136 | fileNameCache.push(fname);
|
137 |
|
138 | worker.busydoing(function() {
|
139 |
|
140 | setTimeout(function() {
|
141 |
|
142 |
|
143 |
|
144 | fileNameCache
|
145 | .each(function(f) {
|
146 |
|
147 | if (!memo.has(f) && is.dir(f)) {
|
148 | watch(f, cb);
|
149 | } else if (is.file(f)) {
|
150 | cb.call(null, f);
|
151 | }
|
152 | }).clear();
|
153 |
|
154 | worker.free();
|
155 |
|
156 | }, 100);
|
157 | })
|
158 | }
|
159 |
|
160 | function watch(fpath, cb) {
|
161 | var basename = path.basename(fpath);
|
162 | if(!is.dir(fpath)) return;
|
163 | if(opts.ignoreHidden && /^\./.test(basename)) return;
|
164 | if(opts.excludes && opts.excludes.indexOf(basename) !== -1) return;
|
165 |
|
166 | debug('watching directory: ' + fpath);
|
167 | memo.push(fpath, 'dir');
|
168 | fs.watch(fpath, function(err, fname) {
|
169 |
|
170 | if(!fname) return;
|
171 | var ext = path.extname(fname).slice(1);
|
172 | var f = path.join(fpath, fname);
|
173 | if (is.file(f) && opts.includes.indexOf(ext) === -1) return;
|
174 | normalizeCall(f, cb);
|
175 | });
|
176 |
|
177 | sub(fpath, function(dir) {
|
178 | watch(dir, cb);
|
179 | });
|
180 | }
|
181 | }
|
182 |
|
183 |
|
184 |
|
185 | module.exports = watchDir; |
\ | No newline at end of file |