UNPKG

4.45 kBJavaScriptView Raw
1const
2 minimatch = require('minimatch'),
3 path = require('path'),
4 globWatcher = require('glob-watcher'),
5 sgUtil = require('./util');
6
7class Watcher {
8 constructor({conf, browserSync, CollectorStore: {getSections, getFiles, updateTimestamp}}) {
9 this.getSections = getSections;
10 this.getFiles = getFiles;
11 this.updateTimestamp = updateTimestamp;
12 this.browserSync = browserSync;
13
14 this.globOptions = conf.get('globOptions');
15 this.htmlExt = conf.get('htmlExt');
16 this.templateExt = conf.get('templateExt');
17 this.dataExt = conf.get('dataExt');
18 this.emitDelay = conf.get('emitDelay', 100);
19 this.logLevel = conf.get('logLevel', 0);
20 this.emitTimeout = null;
21 this.emitQue = {};
22
23 this.watch = this.watch.bind(this);
24 this.start = this.start.bind(this);
25 this.onAddFiles = this.onAddFiles.bind(this);
26 this.onUnlinkFiles = this.onUnlinkFiles.bind(this);
27 this.emitDelayed = this.emitDelayed.bind(this);
28 this.resetEmitQue = this.resetEmitQue.bind(this);
29 this.addToEmitQue = this.addToEmitQue.bind(this);
30 }
31
32 watch(section) {
33 const {watchPattern} = section;
34
35 return globWatcher(watchPattern, {ignoreInitial: true})
36 .on('change', (filename) => this.onChangeFiles(path.resolve(filename), section))
37 .on('add', (filename) => this.onAddFiles(path.resolve(filename), section))
38 .on('unlink', (filename) => this.onUnlinkFiles(path.resolve(filename), section));
39 }
40
41 start() {
42 this.getSections().forEach(this.watch);
43 }
44
45 onAddFiles(filename, section) {
46 if (minimatch(filename, `**/*.${this.templateExt}`)) {
47 if (this.logLevel > 1) {
48 sgUtil.log(`Watcher: file has been added (${filename})`);
49 }
50 const affectedUrls = this.updateTimestamp(filename);
51 this.removeCaches(filename, section);
52 section.setFile({filename, section});
53 this.emitDelayed(filename, affectedUrls, 'onadd');
54 }
55
56 if (minimatch(filename, `**/*.+(${this.dataExt}|js)`)) {
57 this.onChangeFiles(filename, section);
58 }
59 }
60
61 onChangeFiles(filename, section) {
62 if (this.logLevel > 1) {
63 sgUtil.log(`Watcher: file has been updated (${filename})`);
64 }
65
66 if (minimatch(filename, `**/*+(${this.templateExt}|${this.dataExt}|js)`)) {
67 filename = sgUtil.replaceFileExtension(filename, this.templateExt);
68
69 const affectedUrls = this.updateTimestamp(filename);
70
71 section.updateFile(filename);
72 this.removeCaches(filename, section);
73 this.emitDelayed(filename, affectedUrls, 'onchange');
74 }
75 }
76
77 onUnlinkFiles(filename, section) {
78 if (minimatch(filename, `**/*.${this.templateExt}`)) {
79 if (this.logLevel > 1) {
80 sgUtil.log(`Watcher: file has been unlinked (${filename})`);
81 }
82
83 const affectedUrls = this.updateTimestamp(filename);
84
85 section.unsetFile(filename);
86 this.removeCaches(filename, section);
87 this.emitDelayed(filename, affectedUrls, 'onunlink');
88 }
89
90 if (minimatch(filename, `**/*.+(${this.dataExt}|js)`)) {
91 this.onChangeFiles(filename, section);
92 }
93 }
94
95 addToEmitQue({filename, extension}) {
96 if (this.emitQue[extension] === undefined) {
97 this.emitQue[extension] = [];
98 }
99 this.emitQue[extension].push(filename);
100 }
101
102 resetEmitQue() {
103 this.emitQue = {};
104 }
105
106 emitDelayed(filename, affectedUrls, origin) {
107 const
108 {htmlExt, templateExt, dataExt, emitQue, emitDelay, addToEmitQue, resetEmitQue, browserSync} = this,
109 extension = sgUtil.getFileExtension(filename);
110
111 if ([htmlExt, templateExt, dataExt].indexOf(extension) !== -1) {
112
113 addToEmitQue({filename, extension});
114 clearTimeout(this.emitTimeout);
115
116 const extensions = Object.keys(emitQue);
117
118 this.emitTimeout = setTimeout(() => {
119 sgUtil.log(`[atomatic] Emit reload based on file changes`, 'bs');
120 browserSync.sockets.emit('atomatic:reload', affectedUrls);
121 browserSync.emitter.emit('atomatic:change', affectedUrls);
122
123 if (!origin !== 'onchange' || !extensions.indexOf(dataExt)) {
124 browserSync.sockets.emit('atomatic:fetchSection');
125 }
126 resetEmitQue();
127 }, emitDelay);
128 }
129 }
130
131 removeCaches(filename, section) {
132 section._timestamp = 0;
133 const dirname = path.dirname(filename);
134
135 Object.keys(this.globOptions.cache)
136 .filter(key => key.indexOf(dirname) !== -1)
137 .map(key => delete this.globOptions.cache[key]);
138 }
139}
140
141module.exports = Watcher;