source: trip-planner-front/node_modules/watchpack/lib/watchEventSource.js@ ceaed42

Last change on this file since ceaed42 was 6a3a178, checked in by Ema <ema_spirova@…>, 3 years ago

initial commit

  • 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 (${
102 this.rootPath
103 }) to all watchers\n`
104 );
105 }
106 for (const w of this.mapWatcherToPath.keys()) {
107 w.emit("change", type);
108 }
109 } else {
110 const dir = path.dirname(filename);
111 const watchers = this.mapPathToWatchers.get(dir);
112 if (recursiveWatcherLogging) {
113 process.stderr.write(
114 `[watchpack] dispatch ${type} event in recursive watcher (${
115 this.rootPath
116 }) for '${filename}' to ${
117 watchers ? watchers.size : 0
118 } watchers\n`
119 );
120 }
121 if (watchers === undefined) return;
122 for (const w of watchers) {
123 w.emit("change", type, path.basename(filename));
124 }
125 }
126 });
127 watcher.on("error", error => {
128 for (const w of this.mapWatcherToPath.keys()) {
129 w.emit("error", error);
130 }
131 });
132 } catch (err) {
133 process.nextTick(() => {
134 for (const w of this.mapWatcherToPath.keys()) {
135 w.emit("error", err);
136 }
137 });
138 }
139 watcherCount++;
140 if (recursiveWatcherLogging) {
141 process.stderr.write(
142 `[watchpack] created recursive watcher at ${rootPath}\n`
143 );
144 }
145 }
146
147 add(filePath, watcher) {
148 underlyingWatcher.set(watcher, this);
149 const subpath = filePath.slice(this.rootPath.length + 1) || ".";
150 this.mapWatcherToPath.set(watcher, subpath);
151 const set = this.mapPathToWatchers.get(subpath);
152 if (set === undefined) {
153 const newSet = new Set();
154 newSet.add(watcher);
155 this.mapPathToWatchers.set(subpath, newSet);
156 } else {
157 set.add(watcher);
158 }
159 }
160
161 remove(watcher) {
162 const subpath = this.mapWatcherToPath.get(watcher);
163 if (!subpath) return;
164 this.mapWatcherToPath.delete(watcher);
165 const set = this.mapPathToWatchers.get(subpath);
166 set.delete(watcher);
167 if (set.size === 0) {
168 this.mapPathToWatchers.delete(subpath);
169 }
170 if (this.mapWatcherToPath.size === 0) {
171 recursiveWatchers.delete(this.rootPath);
172 watcherCount--;
173 if (this.watcher) this.watcher.close();
174 if (recursiveWatcherLogging) {
175 process.stderr.write(
176 `[watchpack] closed recursive watcher at ${this.rootPath}\n`
177 );
178 }
179 }
180 }
181
182 getWatchers() {
183 return this.mapWatcherToPath;
184 }
185}
186
187class Watcher extends EventEmitter {
188 close() {
189 if (pendingWatchers.has(this)) {
190 pendingWatchers.delete(this);
191 return;
192 }
193 const watcher = underlyingWatcher.get(this);
194 watcher.remove(this);
195 underlyingWatcher.delete(this);
196 }
197}
198
199const createDirectWatcher = filePath => {
200 const existing = directWatchers.get(filePath);
201 if (existing !== undefined) return existing;
202 const w = new DirectWatcher(filePath);
203 directWatchers.set(filePath, w);
204 return w;
205};
206
207const createRecursiveWatcher = rootPath => {
208 const existing = recursiveWatchers.get(rootPath);
209 if (existing !== undefined) return existing;
210 const w = new RecursiveWatcher(rootPath);
211 recursiveWatchers.set(rootPath, w);
212 return w;
213};
214
215const execute = () => {
216 /** @type {Map<string, Watcher[] | Watcher>} */
217 const map = new Map();
218 const addWatcher = (watcher, filePath) => {
219 const entry = map.get(filePath);
220 if (entry === undefined) {
221 map.set(filePath, watcher);
222 } else if (Array.isArray(entry)) {
223 entry.push(watcher);
224 } else {
225 map.set(filePath, [entry, watcher]);
226 }
227 };
228 for (const [watcher, filePath] of pendingWatchers) {
229 addWatcher(watcher, filePath);
230 }
231 pendingWatchers.clear();
232
233 // Fast case when we are not reaching the limit
234 if (!SUPPORTS_RECURSIVE_WATCHING || watcherLimit - watcherCount >= map.size) {
235 // Create watchers for all entries in the map
236 for (const [filePath, entry] of map) {
237 const w = createDirectWatcher(filePath);
238 if (Array.isArray(entry)) {
239 for (const item of entry) w.add(item);
240 } else {
241 w.add(entry);
242 }
243 }
244 return;
245 }
246
247 // Reconsider existing watchers to improving watch plan
248 for (const watcher of recursiveWatchers.values()) {
249 for (const [w, subpath] of watcher.getWatchers()) {
250 addWatcher(w, path.join(watcher.rootPath, subpath));
251 }
252 }
253 for (const watcher of directWatchers.values()) {
254 for (const w of watcher.getWatchers()) {
255 addWatcher(w, watcher.filePath);
256 }
257 }
258
259 // Merge map entries to keep watcher limit
260 // Create a 10% buffer to be able to enter fast case more often
261 const plan = reducePlan(map, watcherLimit * 0.9);
262
263 // Update watchers for all entries in the map
264 for (const [filePath, entry] of plan) {
265 if (entry.size === 1) {
266 for (const [watcher, filePath] of entry) {
267 const w = createDirectWatcher(filePath);
268 const old = underlyingWatcher.get(watcher);
269 if (old === w) continue;
270 w.add(watcher);
271 if (old !== undefined) old.remove(watcher);
272 }
273 } else {
274 const filePaths = new Set(entry.values());
275 if (filePaths.size > 1) {
276 const w = createRecursiveWatcher(filePath);
277 for (const [watcher, watcherPath] of entry) {
278 const old = underlyingWatcher.get(watcher);
279 if (old === w) continue;
280 w.add(watcherPath, watcher);
281 if (old !== undefined) old.remove(watcher);
282 }
283 } else {
284 for (const filePath of filePaths) {
285 const w = createDirectWatcher(filePath);
286 for (const watcher of entry.keys()) {
287 const old = underlyingWatcher.get(watcher);
288 if (old === w) continue;
289 w.add(watcher);
290 if (old !== undefined) old.remove(watcher);
291 }
292 }
293 }
294 }
295 }
296};
297
298exports.watch = filePath => {
299 const watcher = new Watcher();
300 // Find an existing watcher
301 const directWatcher = directWatchers.get(filePath);
302 if (directWatcher !== undefined) {
303 directWatcher.add(watcher);
304 return watcher;
305 }
306 let current = filePath;
307 for (;;) {
308 const recursiveWatcher = recursiveWatchers.get(current);
309 if (recursiveWatcher !== undefined) {
310 recursiveWatcher.add(filePath, watcher);
311 return watcher;
312 }
313 const parent = path.dirname(current);
314 if (parent === current) break;
315 current = parent;
316 }
317 // Queue up watcher for creation
318 pendingWatchers.set(watcher, filePath);
319 if (!isBatch) execute();
320 return watcher;
321};
322
323exports.batch = fn => {
324 isBatch = true;
325 try {
326 fn();
327 } finally {
328 isBatch = false;
329 execute();
330 }
331};
332
333exports.getNumberOfWatchers = () => {
334 return watcherCount;
335};
Note: See TracBrowser for help on using the repository browser.