1 | const
|
2 | minimatch = require('minimatch'),
|
3 | path = require('path'),
|
4 | globWatcher = require('glob-watcher'),
|
5 | sgUtil = require('./util');
|
6 |
|
7 | class 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 |
|
141 | module.exports = Watcher;
|