UNPKG

5.88 kBJavaScriptView Raw
1/*
2 MIT License http://www.opensource.org/licenses/mit-license.php
3 Author Tobias Koppers @sokra
4*/
5
6"use strict";
7
8const util = require("util");
9const Watchpack = require("watchpack");
10
11/** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
12/** @typedef {import("../FileSystemInfo").FileSystemInfoEntry} FileSystemInfoEntry */
13/** @typedef {import("../util/fs").WatchFileSystem} WatchFileSystem */
14/** @typedef {import("../util/fs").WatchMethod} WatchMethod */
15/** @typedef {import("../util/fs").Watcher} Watcher */
16
17class NodeWatchFileSystem {
18 constructor(inputFileSystem) {
19 this.inputFileSystem = inputFileSystem;
20 this.watcherOptions = {
21 aggregateTimeout: 0
22 };
23 this.watcher = new Watchpack(this.watcherOptions);
24 }
25
26 /**
27 * @param {Iterable<string>} files watched files
28 * @param {Iterable<string>} directories watched directories
29 * @param {Iterable<string>} missing watched exitance entries
30 * @param {number} startTime timestamp of start time
31 * @param {WatchOptions} options options object
32 * @param {function(Error=, Map<string, FileSystemInfoEntry>, Map<string, FileSystemInfoEntry>, Set<string>, Set<string>): void} callback aggregated callback
33 * @param {function(string, number): void} callbackUndelayed callback when the first change was detected
34 * @returns {Watcher} a watcher
35 */
36 watch(
37 files,
38 directories,
39 missing,
40 startTime,
41 options,
42 callback,
43 callbackUndelayed
44 ) {
45 if (!files || typeof files[Symbol.iterator] !== "function") {
46 throw new Error("Invalid arguments: 'files'");
47 }
48 if (!directories || typeof directories[Symbol.iterator] !== "function") {
49 throw new Error("Invalid arguments: 'directories'");
50 }
51 if (!missing || typeof missing[Symbol.iterator] !== "function") {
52 throw new Error("Invalid arguments: 'missing'");
53 }
54 if (typeof callback !== "function") {
55 throw new Error("Invalid arguments: 'callback'");
56 }
57 if (typeof startTime !== "number" && startTime) {
58 throw new Error("Invalid arguments: 'startTime'");
59 }
60 if (typeof options !== "object") {
61 throw new Error("Invalid arguments: 'options'");
62 }
63 if (typeof callbackUndelayed !== "function" && callbackUndelayed) {
64 throw new Error("Invalid arguments: 'callbackUndelayed'");
65 }
66 const oldWatcher = this.watcher;
67 this.watcher = new Watchpack(options);
68
69 if (callbackUndelayed) {
70 this.watcher.once("change", callbackUndelayed);
71 }
72
73 const fetchTimeInfo = () => {
74 const fileTimeInfoEntries = new Map();
75 const contextTimeInfoEntries = new Map();
76 if (this.watcher) {
77 this.watcher.collectTimeInfoEntries(
78 fileTimeInfoEntries,
79 contextTimeInfoEntries
80 );
81 }
82 return { fileTimeInfoEntries, contextTimeInfoEntries };
83 };
84 this.watcher.once("aggregated", (changes, removals) => {
85 // pause emitting events (avoids clearing aggregated changes and removals on timeout)
86 this.watcher.pause();
87
88 if (this.inputFileSystem && this.inputFileSystem.purge) {
89 const fs = this.inputFileSystem;
90 for (const item of changes) {
91 fs.purge(item);
92 }
93 for (const item of removals) {
94 fs.purge(item);
95 }
96 }
97 const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
98 callback(
99 null,
100 fileTimeInfoEntries,
101 contextTimeInfoEntries,
102 changes,
103 removals
104 );
105 });
106
107 this.watcher.watch({ files, directories, missing, startTime });
108
109 if (oldWatcher) {
110 oldWatcher.close();
111 }
112 return {
113 close: () => {
114 if (this.watcher) {
115 this.watcher.close();
116 this.watcher = null;
117 }
118 },
119 pause: () => {
120 if (this.watcher) {
121 this.watcher.pause();
122 }
123 },
124 getAggregatedRemovals: util.deprecate(
125 () => {
126 const items = this.watcher && this.watcher.aggregatedRemovals;
127 if (items && this.inputFileSystem && this.inputFileSystem.purge) {
128 const fs = this.inputFileSystem;
129 for (const item of items) {
130 fs.purge(item);
131 }
132 }
133 return items;
134 },
135 "Watcher.getAggregatedRemovals is deprecated in favor of Watcher.getInfo since that's more performant.",
136 "DEP_WEBPACK_WATCHER_GET_AGGREGATED_REMOVALS"
137 ),
138 getAggregatedChanges: util.deprecate(
139 () => {
140 const items = this.watcher && this.watcher.aggregatedChanges;
141 if (items && this.inputFileSystem && this.inputFileSystem.purge) {
142 const fs = this.inputFileSystem;
143 for (const item of items) {
144 fs.purge(item);
145 }
146 }
147 return items;
148 },
149 "Watcher.getAggregatedChanges is deprecated in favor of Watcher.getInfo since that's more performant.",
150 "DEP_WEBPACK_WATCHER_GET_AGGREGATED_CHANGES"
151 ),
152 getFileTimeInfoEntries: util.deprecate(
153 () => {
154 return fetchTimeInfo().fileTimeInfoEntries;
155 },
156 "Watcher.getFileTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
157 "DEP_WEBPACK_WATCHER_FILE_TIME_INFO_ENTRIES"
158 ),
159 getContextTimeInfoEntries: util.deprecate(
160 () => {
161 return fetchTimeInfo().contextTimeInfoEntries;
162 },
163 "Watcher.getContextTimeInfoEntries is deprecated in favor of Watcher.getInfo since that's more performant.",
164 "DEP_WEBPACK_WATCHER_CONTEXT_TIME_INFO_ENTRIES"
165 ),
166 getInfo: () => {
167 const removals = this.watcher && this.watcher.aggregatedRemovals;
168 const changes = this.watcher && this.watcher.aggregatedChanges;
169 if (this.inputFileSystem && this.inputFileSystem.purge) {
170 const fs = this.inputFileSystem;
171 if (removals) {
172 for (const item of removals) {
173 fs.purge(item);
174 }
175 }
176 if (changes) {
177 for (const item of changes) {
178 fs.purge(item);
179 }
180 }
181 }
182 const { fileTimeInfoEntries, contextTimeInfoEntries } = fetchTimeInfo();
183 return {
184 changes,
185 removals,
186 fileTimeInfoEntries,
187 contextTimeInfoEntries
188 };
189 }
190 };
191 }
192}
193
194module.exports = NodeWatchFileSystem;