source: imaps-frontend/node_modules/watchpack/lib/watchpack.js@ 79a0317

main
Last change on this file since 79a0317 was 79a0317, checked in by stefan toskovski <stefantoska84@…>, 3 days ago

F4 Finalna Verzija

  • Property mode set to 100644
File size: 10.4 KB
Line 
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5"use strict";
6
7const getWatcherManager = require("./getWatcherManager");
8const LinkResolver = require("./LinkResolver");
9const EventEmitter = require("events").EventEmitter;
10const globToRegExp = require("glob-to-regexp");
11const watchEventSource = require("./watchEventSource");
12
13const EMPTY_ARRAY = [];
14const EMPTY_OPTIONS = {};
15
16function addWatchersToSet(watchers, set) {
17 for (const ww of watchers) {
18 const w = ww.watcher;
19 if (!set.has(w.directoryWatcher)) {
20 set.add(w.directoryWatcher);
21 }
22 }
23}
24
25const stringToRegexp = ignored => {
26 if (ignored.length === 0) {
27 return;
28 }
29 const source = globToRegExp(ignored, { globstar: true, extended: true })
30 .source;
31 return source.slice(0, source.length - 1) + "(?:$|\\/)";
32};
33
34const ignoredToFunction = ignored => {
35 if (Array.isArray(ignored)) {
36 const stringRegexps = ignored.map(i => stringToRegexp(i)).filter(Boolean);
37 if (stringRegexps.length === 0) {
38 return () => false;
39 }
40 const regexp = new RegExp(stringRegexps.join("|"));
41 return x => regexp.test(x.replace(/\\/g, "/"));
42 } else if (typeof ignored === "string") {
43 const stringRegexp = stringToRegexp(ignored);
44 if (!stringRegexp) {
45 return () => false;
46 }
47 const regexp = new RegExp(stringRegexp);
48 return x => regexp.test(x.replace(/\\/g, "/"));
49 } else if (ignored instanceof RegExp) {
50 return x => ignored.test(x.replace(/\\/g, "/"));
51 } else if (ignored instanceof Function) {
52 return ignored;
53 } else if (ignored) {
54 throw new Error(`Invalid option for 'ignored': ${ignored}`);
55 } else {
56 return () => false;
57 }
58};
59
60const normalizeOptions = options => {
61 return {
62 followSymlinks: !!options.followSymlinks,
63 ignored: ignoredToFunction(options.ignored),
64 poll: options.poll
65 };
66};
67
68const normalizeCache = new WeakMap();
69const cachedNormalizeOptions = options => {
70 const cacheEntry = normalizeCache.get(options);
71 if (cacheEntry !== undefined) return cacheEntry;
72 const normalized = normalizeOptions(options);
73 normalizeCache.set(options, normalized);
74 return normalized;
75};
76
77class WatchpackFileWatcher {
78 constructor(watchpack, watcher, files) {
79 this.files = Array.isArray(files) ? files : [files];
80 this.watcher = watcher;
81 watcher.on("initial-missing", type => {
82 for (const file of this.files) {
83 if (!watchpack._missing.has(file))
84 watchpack._onRemove(file, file, type);
85 }
86 });
87 watcher.on("change", (mtime, type) => {
88 for (const file of this.files) {
89 watchpack._onChange(file, mtime, file, type);
90 }
91 });
92 watcher.on("remove", type => {
93 for (const file of this.files) {
94 watchpack._onRemove(file, file, type);
95 }
96 });
97 }
98
99 update(files) {
100 if (!Array.isArray(files)) {
101 if (this.files.length !== 1) {
102 this.files = [files];
103 } else if (this.files[0] !== files) {
104 this.files[0] = files;
105 }
106 } else {
107 this.files = files;
108 }
109 }
110
111 close() {
112 this.watcher.close();
113 }
114}
115
116class WatchpackDirectoryWatcher {
117 constructor(watchpack, watcher, directories) {
118 this.directories = Array.isArray(directories) ? directories : [directories];
119 this.watcher = watcher;
120 watcher.on("initial-missing", type => {
121 for (const item of this.directories) {
122 watchpack._onRemove(item, item, type);
123 }
124 });
125 watcher.on("change", (file, mtime, type) => {
126 for (const item of this.directories) {
127 watchpack._onChange(item, mtime, file, type);
128 }
129 });
130 watcher.on("remove", type => {
131 for (const item of this.directories) {
132 watchpack._onRemove(item, item, type);
133 }
134 });
135 }
136
137 update(directories) {
138 if (!Array.isArray(directories)) {
139 if (this.directories.length !== 1) {
140 this.directories = [directories];
141 } else if (this.directories[0] !== directories) {
142 this.directories[0] = directories;
143 }
144 } else {
145 this.directories = directories;
146 }
147 }
148
149 close() {
150 this.watcher.close();
151 }
152}
153
154class Watchpack extends EventEmitter {
155 constructor(options) {
156 super();
157 if (!options) options = EMPTY_OPTIONS;
158 this.options = options;
159 this.aggregateTimeout =
160 typeof options.aggregateTimeout === "number"
161 ? options.aggregateTimeout
162 : 200;
163 this.watcherOptions = cachedNormalizeOptions(options);
164 this.watcherManager = getWatcherManager(this.watcherOptions);
165 this.fileWatchers = new Map();
166 this.directoryWatchers = new Map();
167 this._missing = new Set();
168 this.startTime = undefined;
169 this.paused = false;
170 this.aggregatedChanges = new Set();
171 this.aggregatedRemovals = new Set();
172 this.aggregateTimer = undefined;
173 this._onTimeout = this._onTimeout.bind(this);
174 }
175
176 watch(arg1, arg2, arg3) {
177 let files, directories, missing, startTime;
178 if (!arg2) {
179 ({
180 files = EMPTY_ARRAY,
181 directories = EMPTY_ARRAY,
182 missing = EMPTY_ARRAY,
183 startTime
184 } = arg1);
185 } else {
186 files = arg1;
187 directories = arg2;
188 missing = EMPTY_ARRAY;
189 startTime = arg3;
190 }
191 this.paused = false;
192 const fileWatchers = this.fileWatchers;
193 const directoryWatchers = this.directoryWatchers;
194 const ignored = this.watcherOptions.ignored;
195 const filter = path => !ignored(path);
196 const addToMap = (map, key, item) => {
197 const list = map.get(key);
198 if (list === undefined) {
199 map.set(key, item);
200 } else if (Array.isArray(list)) {
201 list.push(item);
202 } else {
203 map.set(key, [list, item]);
204 }
205 };
206 const fileWatchersNeeded = new Map();
207 const directoryWatchersNeeded = new Map();
208 const missingFiles = new Set();
209 if (this.watcherOptions.followSymlinks) {
210 const resolver = new LinkResolver();
211 for (const file of files) {
212 if (filter(file)) {
213 for (const innerFile of resolver.resolve(file)) {
214 if (file === innerFile || filter(innerFile)) {
215 addToMap(fileWatchersNeeded, innerFile, file);
216 }
217 }
218 }
219 }
220 for (const file of missing) {
221 if (filter(file)) {
222 for (const innerFile of resolver.resolve(file)) {
223 if (file === innerFile || filter(innerFile)) {
224 missingFiles.add(file);
225 addToMap(fileWatchersNeeded, innerFile, file);
226 }
227 }
228 }
229 }
230 for (const dir of directories) {
231 if (filter(dir)) {
232 let first = true;
233 for (const innerItem of resolver.resolve(dir)) {
234 if (filter(innerItem)) {
235 addToMap(
236 first ? directoryWatchersNeeded : fileWatchersNeeded,
237 innerItem,
238 dir
239 );
240 }
241 first = false;
242 }
243 }
244 }
245 } else {
246 for (const file of files) {
247 if (filter(file)) {
248 addToMap(fileWatchersNeeded, file, file);
249 }
250 }
251 for (const file of missing) {
252 if (filter(file)) {
253 missingFiles.add(file);
254 addToMap(fileWatchersNeeded, file, file);
255 }
256 }
257 for (const dir of directories) {
258 if (filter(dir)) {
259 addToMap(directoryWatchersNeeded, dir, dir);
260 }
261 }
262 }
263 // Close unneeded old watchers
264 // and update existing watchers
265 for (const [key, w] of fileWatchers) {
266 const needed = fileWatchersNeeded.get(key);
267 if (needed === undefined) {
268 w.close();
269 fileWatchers.delete(key);
270 } else {
271 w.update(needed);
272 fileWatchersNeeded.delete(key);
273 }
274 }
275 for (const [key, w] of directoryWatchers) {
276 const needed = directoryWatchersNeeded.get(key);
277 if (needed === undefined) {
278 w.close();
279 directoryWatchers.delete(key);
280 } else {
281 w.update(needed);
282 directoryWatchersNeeded.delete(key);
283 }
284 }
285 // Create new watchers and install handlers on these watchers
286 watchEventSource.batch(() => {
287 for (const [key, files] of fileWatchersNeeded) {
288 const watcher = this.watcherManager.watchFile(key, startTime);
289 if (watcher) {
290 fileWatchers.set(key, new WatchpackFileWatcher(this, watcher, files));
291 }
292 }
293 for (const [key, directories] of directoryWatchersNeeded) {
294 const watcher = this.watcherManager.watchDirectory(key, startTime);
295 if (watcher) {
296 directoryWatchers.set(
297 key,
298 new WatchpackDirectoryWatcher(this, watcher, directories)
299 );
300 }
301 }
302 });
303 this._missing = missingFiles;
304 this.startTime = startTime;
305 }
306
307 close() {
308 this.paused = true;
309 if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
310 for (const w of this.fileWatchers.values()) w.close();
311 for (const w of this.directoryWatchers.values()) w.close();
312 this.fileWatchers.clear();
313 this.directoryWatchers.clear();
314 }
315
316 pause() {
317 this.paused = true;
318 if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
319 }
320
321 getTimes() {
322 const directoryWatchers = new Set();
323 addWatchersToSet(this.fileWatchers.values(), directoryWatchers);
324 addWatchersToSet(this.directoryWatchers.values(), directoryWatchers);
325 const obj = Object.create(null);
326 for (const w of directoryWatchers) {
327 const times = w.getTimes();
328 for (const file of Object.keys(times)) obj[file] = times[file];
329 }
330 return obj;
331 }
332
333 getTimeInfoEntries() {
334 const map = new Map();
335 this.collectTimeInfoEntries(map, map);
336 return map;
337 }
338
339 collectTimeInfoEntries(fileTimestamps, directoryTimestamps) {
340 const allWatchers = new Set();
341 addWatchersToSet(this.fileWatchers.values(), allWatchers);
342 addWatchersToSet(this.directoryWatchers.values(), allWatchers);
343 const safeTime = { value: 0 };
344 for (const w of allWatchers) {
345 w.collectTimeInfoEntries(fileTimestamps, directoryTimestamps, safeTime);
346 }
347 }
348
349 getAggregated() {
350 if (this.aggregateTimer) {
351 clearTimeout(this.aggregateTimer);
352 this.aggregateTimer = undefined;
353 }
354 const changes = this.aggregatedChanges;
355 const removals = this.aggregatedRemovals;
356 this.aggregatedChanges = new Set();
357 this.aggregatedRemovals = new Set();
358 return { changes, removals };
359 }
360
361 _onChange(item, mtime, file, type) {
362 file = file || item;
363 if (!this.paused) {
364 this.emit("change", file, mtime, type);
365 if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
366 this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
367 }
368 this.aggregatedRemovals.delete(item);
369 this.aggregatedChanges.add(item);
370 }
371
372 _onRemove(item, file, type) {
373 file = file || item;
374 if (!this.paused) {
375 this.emit("remove", file, type);
376 if (this.aggregateTimer) clearTimeout(this.aggregateTimer);
377 this.aggregateTimer = setTimeout(this._onTimeout, this.aggregateTimeout);
378 }
379 this.aggregatedChanges.delete(item);
380 this.aggregatedRemovals.add(item);
381 }
382
383 _onTimeout() {
384 this.aggregateTimer = undefined;
385 const changes = this.aggregatedChanges;
386 const removals = this.aggregatedRemovals;
387 this.aggregatedChanges = new Set();
388 this.aggregatedRemovals = new Set();
389 this.emit("aggregated", changes, removals);
390 }
391}
392
393module.exports = Watchpack;
Note: See TracBrowser for help on using the repository browser.