source: imaps-frontend/node_modules/watchpack/lib/watchEventSource.js

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

F4 Finalna Verzija

  • Property mode set to 100644
File size: 8.3 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 fs = require("fs");
8const path = require("path");
9const { EventEmitter } = require("events");
10const reducePlan = require("./reducePlan");
11
12const IS_OSX = require("os").platform() === "darwin";
13const IS_WIN = require("os").platform() === "win32";
14const SUPPORTS_RECURSIVE_WATCHING = IS_OSX || IS_WIN;
15
16const watcherLimit =
17 +process.env.WATCHPACK_WATCHER_LIMIT || (IS_OSX ? 2000 : 10000);
18
19const recursiveWatcherLogging = !!process.env
20 .WATCHPACK_RECURSIVE_WATCHER_LOGGING;
21
22let isBatch = false;
23let watcherCount = 0;
24
25/** @type {Map<Watcher, string>} */
26const pendingWatchers = new Map();
27
28/** @type {Map<string, RecursiveWatcher>} */
29const recursiveWatchers = new Map();
30
31/** @type {Map<string, DirectWatcher>} */
32const directWatchers = new Map();
33
34/** @type {Map<Watcher, RecursiveWatcher | DirectWatcher>} */
35const underlyingWatcher = new Map();
36
37class DirectWatcher {
38 constructor(filePath) {
39 this.filePath = filePath;
40 this.watchers = new Set();
41 this.watcher = undefined;
42 try {
43 const watcher = fs.watch(filePath);
44 this.watcher = watcher;
45 watcher.on("change", (type, filename) => {
46 for (const w of this.watchers) {
47 w.emit("change", type, filename);
48 }
49 });
50 watcher.on("error", error => {
51 for (const w of this.watchers) {
52 w.emit("error", error);
53 }
54 });
55 } catch (err) {
56 process.nextTick(() => {
57 for (const w of this.watchers) {
58 w.emit("error", err);
59 }
60 });
61 }
62 watcherCount++;
63 }
64
65 add(watcher) {
66 underlyingWatcher.set(watcher, this);
67 this.watchers.add(watcher);
68 }
69
70 remove(watcher) {
71 this.watchers.delete(watcher);
72 if (this.watchers.size === 0) {
73 directWatchers.delete(this.filePath);
74 watcherCount--;
75 if (this.watcher) this.watcher.close();
76 }
77 }
78
79 getWatchers() {
80 return this.watchers;
81 }
82}
83
84class RecursiveWatcher {
85 constructor(rootPath) {
86 this.rootPath = rootPath;
87 /** @type {Map<Watcher, string>} */
88 this.mapWatcherToPath = new Map();
89 /** @type {Map<string, Set<Watcher>>} */
90 this.mapPathToWatchers = new Map();
91 this.watcher = undefined;
92 try {
93 const watcher = fs.watch(rootPath, {
94 recursive: true
95 });
96 this.watcher = watcher;
97 watcher.on("change", (type, filename) => {
98 if (!filename) {
99 if (recursiveWatcherLogging) {
100 process.stderr.write(
101 `[watchpack] dispatch ${type} event in recursive watcher (${this.rootPath}) to all watchers\n`
102 );
103 }
104 for (const w of this.mapWatcherToPath.keys()) {
105 w.emit("change", type);
106 }
107 } else {
108 const dir = path.dirname(filename);
109 const watchers = this.mapPathToWatchers.get(dir);
110 if (recursiveWatcherLogging) {
111 process.stderr.write(
112 `[watchpack] dispatch ${type} event in recursive watcher (${
113 this.rootPath
114 }) for '${filename}' to ${
115 watchers ? watchers.size : 0
116 } watchers\n`
117 );
118 }
119 if (watchers === undefined) return;
120 for (const w of watchers) {
121 w.emit("change", type, path.basename(filename));
122 }
123 }
124 });
125 watcher.on("error", error => {
126 for (const w of this.mapWatcherToPath.keys()) {
127 w.emit("error", error);
128 }
129 });
130 } catch (err) {
131 process.nextTick(() => {
132 for (const w of this.mapWatcherToPath.keys()) {
133 w.emit("error", err);
134 }
135 });
136 }
137 watcherCount++;
138 if (recursiveWatcherLogging) {
139 process.stderr.write(
140 `[watchpack] created recursive watcher at ${rootPath}\n`
141 );
142 }
143 }
144
145 add(filePath, watcher) {
146 underlyingWatcher.set(watcher, this);
147 const subpath = filePath.slice(this.rootPath.length + 1) || ".";
148 this.mapWatcherToPath.set(watcher, subpath);
149 const set = this.mapPathToWatchers.get(subpath);
150 if (set === undefined) {
151 const newSet = new Set();
152 newSet.add(watcher);
153 this.mapPathToWatchers.set(subpath, newSet);
154 } else {
155 set.add(watcher);
156 }
157 }
158
159 remove(watcher) {
160 const subpath = this.mapWatcherToPath.get(watcher);
161 if (!subpath) return;
162 this.mapWatcherToPath.delete(watcher);
163 const set = this.mapPathToWatchers.get(subpath);
164 set.delete(watcher);
165 if (set.size === 0) {
166 this.mapPathToWatchers.delete(subpath);
167 }
168 if (this.mapWatcherToPath.size === 0) {
169 recursiveWatchers.delete(this.rootPath);
170 watcherCount--;
171 if (this.watcher) this.watcher.close();
172 if (recursiveWatcherLogging) {
173 process.stderr.write(
174 `[watchpack] closed recursive watcher at ${this.rootPath}\n`
175 );
176 }
177 }
178 }
179
180 getWatchers() {
181 return this.mapWatcherToPath;
182 }
183}
184
185class Watcher extends EventEmitter {
186 close() {
187 if (pendingWatchers.has(this)) {
188 pendingWatchers.delete(this);
189 return;
190 }
191 const watcher = underlyingWatcher.get(this);
192 watcher.remove(this);
193 underlyingWatcher.delete(this);
194 }
195}
196
197const createDirectWatcher = filePath => {
198 const existing = directWatchers.get(filePath);
199 if (existing !== undefined) return existing;
200 const w = new DirectWatcher(filePath);
201 directWatchers.set(filePath, w);
202 return w;
203};
204
205const createRecursiveWatcher = rootPath => {
206 const existing = recursiveWatchers.get(rootPath);
207 if (existing !== undefined) return existing;
208 const w = new RecursiveWatcher(rootPath);
209 recursiveWatchers.set(rootPath, w);
210 return w;
211};
212
213const execute = () => {
214 /** @type {Map<string, Watcher[] | Watcher>} */
215 const map = new Map();
216 const addWatcher = (watcher, filePath) => {
217 const entry = map.get(filePath);
218 if (entry === undefined) {
219 map.set(filePath, watcher);
220 } else if (Array.isArray(entry)) {
221 entry.push(watcher);
222 } else {
223 map.set(filePath, [entry, watcher]);
224 }
225 };
226 for (const [watcher, filePath] of pendingWatchers) {
227 addWatcher(watcher, filePath);
228 }
229 pendingWatchers.clear();
230
231 // Fast case when we are not reaching the limit
232 if (!SUPPORTS_RECURSIVE_WATCHING || watcherLimit - watcherCount >= map.size) {
233 // Create watchers for all entries in the map
234 for (const [filePath, entry] of map) {
235 const w = createDirectWatcher(filePath);
236 if (Array.isArray(entry)) {
237 for (const item of entry) w.add(item);
238 } else {
239 w.add(entry);
240 }
241 }
242 return;
243 }
244
245 // Reconsider existing watchers to improving watch plan
246 for (const watcher of recursiveWatchers.values()) {
247 for (const [w, subpath] of watcher.getWatchers()) {
248 addWatcher(w, path.join(watcher.rootPath, subpath));
249 }
250 }
251 for (const watcher of directWatchers.values()) {
252 for (const w of watcher.getWatchers()) {
253 addWatcher(w, watcher.filePath);
254 }
255 }
256
257 // Merge map entries to keep watcher limit
258 // Create a 10% buffer to be able to enter fast case more often
259 const plan = reducePlan(map, watcherLimit * 0.9);
260
261 // Update watchers for all entries in the map
262 for (const [filePath, entry] of plan) {
263 if (entry.size === 1) {
264 for (const [watcher, filePath] of entry) {
265 const w = createDirectWatcher(filePath);
266 const old = underlyingWatcher.get(watcher);
267 if (old === w) continue;
268 w.add(watcher);
269 if (old !== undefined) old.remove(watcher);
270 }
271 } else {
272 const filePaths = new Set(entry.values());
273 if (filePaths.size > 1) {
274 const w = createRecursiveWatcher(filePath);
275 for (const [watcher, watcherPath] of entry) {
276 const old = underlyingWatcher.get(watcher);
277 if (old === w) continue;
278 w.add(watcherPath, watcher);
279 if (old !== undefined) old.remove(watcher);
280 }
281 } else {
282 for (const filePath of filePaths) {
283 const w = createDirectWatcher(filePath);
284 for (const watcher of entry.keys()) {
285 const old = underlyingWatcher.get(watcher);
286 if (old === w) continue;
287 w.add(watcher);
288 if (old !== undefined) old.remove(watcher);
289 }
290 }
291 }
292 }
293 }
294};
295
296exports.watch = filePath => {
297 const watcher = new Watcher();
298 // Find an existing watcher
299 const directWatcher = directWatchers.get(filePath);
300 if (directWatcher !== undefined) {
301 directWatcher.add(watcher);
302 return watcher;
303 }
304 let current = filePath;
305 for (;;) {
306 const recursiveWatcher = recursiveWatchers.get(current);
307 if (recursiveWatcher !== undefined) {
308 recursiveWatcher.add(filePath, watcher);
309 return watcher;
310 }
311 const parent = path.dirname(current);
312 if (parent === current) break;
313 current = parent;
314 }
315 // Queue up watcher for creation
316 pendingWatchers.set(watcher, filePath);
317 if (!isBatch) execute();
318 return watcher;
319};
320
321exports.batch = fn => {
322 isBatch = true;
323 try {
324 fn();
325 } finally {
326 isBatch = false;
327 execute();
328 }
329};
330
331exports.getNumberOfWatchers = () => {
332 return watcherCount;
333};
Note: See TracBrowser for help on using the repository browser.