1 | /*
|
---|
2 | MIT License http://www.opensource.org/licenses/mit-license.php
|
---|
3 | Author Tobias Koppers @sokra
|
---|
4 | */
|
---|
5 |
|
---|
6 | "use strict";
|
---|
7 |
|
---|
8 | const path = require("path");
|
---|
9 |
|
---|
10 | /** @typedef {import("../../declarations/WebpackOptions").WatchOptions} WatchOptions */
|
---|
11 | /** @typedef {import("../FileSystemInfo").FileSystemInfoEntry} FileSystemInfoEntry */
|
---|
12 |
|
---|
13 | /**
|
---|
14 | * @typedef {Object} IStats
|
---|
15 | * @property {() => boolean} isFile
|
---|
16 | * @property {() => boolean} isDirectory
|
---|
17 | * @property {() => boolean} isBlockDevice
|
---|
18 | * @property {() => boolean} isCharacterDevice
|
---|
19 | * @property {() => boolean} isSymbolicLink
|
---|
20 | * @property {() => boolean} isFIFO
|
---|
21 | * @property {() => boolean} isSocket
|
---|
22 | * @property {number | bigint} dev
|
---|
23 | * @property {number | bigint} ino
|
---|
24 | * @property {number | bigint} mode
|
---|
25 | * @property {number | bigint} nlink
|
---|
26 | * @property {number | bigint} uid
|
---|
27 | * @property {number | bigint} gid
|
---|
28 | * @property {number | bigint} rdev
|
---|
29 | * @property {number | bigint} size
|
---|
30 | * @property {number | bigint} blksize
|
---|
31 | * @property {number | bigint} blocks
|
---|
32 | * @property {number | bigint} atimeMs
|
---|
33 | * @property {number | bigint} mtimeMs
|
---|
34 | * @property {number | bigint} ctimeMs
|
---|
35 | * @property {number | bigint} birthtimeMs
|
---|
36 | * @property {Date} atime
|
---|
37 | * @property {Date} mtime
|
---|
38 | * @property {Date} ctime
|
---|
39 | * @property {Date} birthtime
|
---|
40 | */
|
---|
41 |
|
---|
42 | /**
|
---|
43 | * @typedef {Object} IDirent
|
---|
44 | * @property {() => boolean} isFile
|
---|
45 | * @property {() => boolean} isDirectory
|
---|
46 | * @property {() => boolean} isBlockDevice
|
---|
47 | * @property {() => boolean} isCharacterDevice
|
---|
48 | * @property {() => boolean} isSymbolicLink
|
---|
49 | * @property {() => boolean} isFIFO
|
---|
50 | * @property {() => boolean} isSocket
|
---|
51 | * @property {string | Buffer} name
|
---|
52 | */
|
---|
53 |
|
---|
54 | /** @typedef {function((NodeJS.ErrnoException | null)=): void} Callback */
|
---|
55 | /** @typedef {function((NodeJS.ErrnoException | null)=, Buffer=): void} BufferCallback */
|
---|
56 | /** @typedef {function((NodeJS.ErrnoException | null)=, Buffer|string=): void} BufferOrStringCallback */
|
---|
57 | /** @typedef {function((NodeJS.ErrnoException | null)=, (string | Buffer)[] | IDirent[]=): void} DirentArrayCallback */
|
---|
58 | /** @typedef {function((NodeJS.ErrnoException | null)=, string=): void} StringCallback */
|
---|
59 | /** @typedef {function((NodeJS.ErrnoException | null)=, number=): void} NumberCallback */
|
---|
60 | /** @typedef {function((NodeJS.ErrnoException | null)=, IStats=): void} StatsCallback */
|
---|
61 | /** @typedef {function((NodeJS.ErrnoException | Error | null)=, any=): void} ReadJsonCallback */
|
---|
62 |
|
---|
63 | /**
|
---|
64 | * @typedef {Object} Watcher
|
---|
65 | * @property {function(): void} close closes the watcher and all underlying file watchers
|
---|
66 | * @property {function(): void} pause closes the watcher, but keeps underlying file watchers alive until the next watch call
|
---|
67 | * @property {function(): Set<string>=} getAggregatedChanges get current aggregated changes that have not yet send to callback
|
---|
68 | * @property {function(): Set<string>=} getAggregatedRemovals get current aggregated removals that have not yet send to callback
|
---|
69 | * @property {function(): Map<string, FileSystemInfoEntry | "ignore">} getFileTimeInfoEntries get info about files
|
---|
70 | * @property {function(): Map<string, FileSystemInfoEntry | "ignore">} getContextTimeInfoEntries get info about directories
|
---|
71 | */
|
---|
72 |
|
---|
73 | /**
|
---|
74 | * @callback WatchMethod
|
---|
75 | * @param {Iterable<string>} files watched files
|
---|
76 | * @param {Iterable<string>} directories watched directories
|
---|
77 | * @param {Iterable<string>} missing watched exitance entries
|
---|
78 | * @param {number} startTime timestamp of start time
|
---|
79 | * @param {WatchOptions} options options object
|
---|
80 | * @param {function(Error=, Map<string, FileSystemInfoEntry | "ignore">, Map<string, FileSystemInfoEntry | "ignore">, Set<string>, Set<string>): void} callback aggregated callback
|
---|
81 | * @param {function(string, number): void} callbackUndelayed callback when the first change was detected
|
---|
82 | * @returns {Watcher} a watcher
|
---|
83 | */
|
---|
84 |
|
---|
85 | /**
|
---|
86 | * @typedef {Object} OutputFileSystem
|
---|
87 | * @property {function(string, Buffer|string, Callback): void} writeFile
|
---|
88 | * @property {function(string, Callback): void} mkdir
|
---|
89 | * @property {function(string, DirentArrayCallback): void=} readdir
|
---|
90 | * @property {function(string, Callback): void=} rmdir
|
---|
91 | * @property {function(string, Callback): void=} unlink
|
---|
92 | * @property {function(string, StatsCallback): void} stat
|
---|
93 | * @property {function(string, BufferOrStringCallback): void} readFile
|
---|
94 | * @property {(function(string, string): string)=} join
|
---|
95 | * @property {(function(string, string): string)=} relative
|
---|
96 | * @property {(function(string): string)=} dirname
|
---|
97 | */
|
---|
98 |
|
---|
99 | /**
|
---|
100 | * @typedef {Object} InputFileSystem
|
---|
101 | * @property {function(string, BufferOrStringCallback): void} readFile
|
---|
102 | * @property {(function(string, ReadJsonCallback): void)=} readJson
|
---|
103 | * @property {function(string, BufferOrStringCallback): void} readlink
|
---|
104 | * @property {function(string, DirentArrayCallback): void} readdir
|
---|
105 | * @property {function(string, StatsCallback): void} stat
|
---|
106 | * @property {(function(string, BufferOrStringCallback): void)=} realpath
|
---|
107 | * @property {(function(string=): void)=} purge
|
---|
108 | * @property {(function(string, string): string)=} join
|
---|
109 | * @property {(function(string, string): string)=} relative
|
---|
110 | * @property {(function(string): string)=} dirname
|
---|
111 | */
|
---|
112 |
|
---|
113 | /**
|
---|
114 | * @typedef {Object} WatchFileSystem
|
---|
115 | * @property {WatchMethod} watch
|
---|
116 | */
|
---|
117 |
|
---|
118 | /**
|
---|
119 | * @typedef {Object} IntermediateFileSystemExtras
|
---|
120 | * @property {function(string): void} mkdirSync
|
---|
121 | * @property {function(string): NodeJS.WritableStream} createWriteStream
|
---|
122 | * @property {function(string, string, NumberCallback): void} open
|
---|
123 | * @property {function(number, Buffer, number, number, number, NumberCallback): void} read
|
---|
124 | * @property {function(number, Callback): void} close
|
---|
125 | * @property {function(string, string, Callback): void} rename
|
---|
126 | */
|
---|
127 |
|
---|
128 | /** @typedef {InputFileSystem & OutputFileSystem & IntermediateFileSystemExtras} IntermediateFileSystem */
|
---|
129 |
|
---|
130 | /**
|
---|
131 | *
|
---|
132 | * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
|
---|
133 | * @param {string} rootPath the root path
|
---|
134 | * @param {string} targetPath the target path
|
---|
135 | * @returns {string} location of targetPath relative to rootPath
|
---|
136 | */
|
---|
137 | const relative = (fs, rootPath, targetPath) => {
|
---|
138 | if (fs && fs.relative) {
|
---|
139 | return fs.relative(rootPath, targetPath);
|
---|
140 | } else if (path.posix.isAbsolute(rootPath)) {
|
---|
141 | return path.posix.relative(rootPath, targetPath);
|
---|
142 | } else if (path.win32.isAbsolute(rootPath)) {
|
---|
143 | return path.win32.relative(rootPath, targetPath);
|
---|
144 | } else {
|
---|
145 | throw new Error(
|
---|
146 | `${rootPath} is neither a posix nor a windows path, and there is no 'relative' method defined in the file system`
|
---|
147 | );
|
---|
148 | }
|
---|
149 | };
|
---|
150 | exports.relative = relative;
|
---|
151 |
|
---|
152 | /**
|
---|
153 | * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
|
---|
154 | * @param {string} rootPath a path
|
---|
155 | * @param {string} filename a filename
|
---|
156 | * @returns {string} the joined path
|
---|
157 | */
|
---|
158 | const join = (fs, rootPath, filename) => {
|
---|
159 | if (fs && fs.join) {
|
---|
160 | return fs.join(rootPath, filename);
|
---|
161 | } else if (path.posix.isAbsolute(rootPath)) {
|
---|
162 | return path.posix.join(rootPath, filename);
|
---|
163 | } else if (path.win32.isAbsolute(rootPath)) {
|
---|
164 | return path.win32.join(rootPath, filename);
|
---|
165 | } else {
|
---|
166 | throw new Error(
|
---|
167 | `${rootPath} is neither a posix nor a windows path, and there is no 'join' method defined in the file system`
|
---|
168 | );
|
---|
169 | }
|
---|
170 | };
|
---|
171 | exports.join = join;
|
---|
172 |
|
---|
173 | /**
|
---|
174 | * @param {InputFileSystem|OutputFileSystem|undefined} fs a file system
|
---|
175 | * @param {string} absPath an absolute path
|
---|
176 | * @returns {string} the parent directory of the absolute path
|
---|
177 | */
|
---|
178 | const dirname = (fs, absPath) => {
|
---|
179 | if (fs && fs.dirname) {
|
---|
180 | return fs.dirname(absPath);
|
---|
181 | } else if (path.posix.isAbsolute(absPath)) {
|
---|
182 | return path.posix.dirname(absPath);
|
---|
183 | } else if (path.win32.isAbsolute(absPath)) {
|
---|
184 | return path.win32.dirname(absPath);
|
---|
185 | } else {
|
---|
186 | throw new Error(
|
---|
187 | `${absPath} is neither a posix nor a windows path, and there is no 'dirname' method defined in the file system`
|
---|
188 | );
|
---|
189 | }
|
---|
190 | };
|
---|
191 | exports.dirname = dirname;
|
---|
192 |
|
---|
193 | /**
|
---|
194 | * @param {OutputFileSystem} fs a file system
|
---|
195 | * @param {string} p an absolute path
|
---|
196 | * @param {function(Error=): void} callback callback function for the error
|
---|
197 | * @returns {void}
|
---|
198 | */
|
---|
199 | const mkdirp = (fs, p, callback) => {
|
---|
200 | fs.mkdir(p, err => {
|
---|
201 | if (err) {
|
---|
202 | if (err.code === "ENOENT") {
|
---|
203 | const dir = dirname(fs, p);
|
---|
204 | if (dir === p) {
|
---|
205 | callback(err);
|
---|
206 | return;
|
---|
207 | }
|
---|
208 | mkdirp(fs, dir, err => {
|
---|
209 | if (err) {
|
---|
210 | callback(err);
|
---|
211 | return;
|
---|
212 | }
|
---|
213 | fs.mkdir(p, err => {
|
---|
214 | if (err) {
|
---|
215 | if (err.code === "EEXIST") {
|
---|
216 | callback();
|
---|
217 | return;
|
---|
218 | }
|
---|
219 | callback(err);
|
---|
220 | return;
|
---|
221 | }
|
---|
222 | callback();
|
---|
223 | });
|
---|
224 | });
|
---|
225 | return;
|
---|
226 | } else if (err.code === "EEXIST") {
|
---|
227 | callback();
|
---|
228 | return;
|
---|
229 | }
|
---|
230 | callback(err);
|
---|
231 | return;
|
---|
232 | }
|
---|
233 | callback();
|
---|
234 | });
|
---|
235 | };
|
---|
236 | exports.mkdirp = mkdirp;
|
---|
237 |
|
---|
238 | /**
|
---|
239 | * @param {IntermediateFileSystem} fs a file system
|
---|
240 | * @param {string} p an absolute path
|
---|
241 | * @returns {void}
|
---|
242 | */
|
---|
243 | const mkdirpSync = (fs, p) => {
|
---|
244 | try {
|
---|
245 | fs.mkdirSync(p);
|
---|
246 | } catch (err) {
|
---|
247 | if (err) {
|
---|
248 | if (err.code === "ENOENT") {
|
---|
249 | const dir = dirname(fs, p);
|
---|
250 | if (dir === p) {
|
---|
251 | throw err;
|
---|
252 | }
|
---|
253 | mkdirpSync(fs, dir);
|
---|
254 | fs.mkdirSync(p);
|
---|
255 | return;
|
---|
256 | } else if (err.code === "EEXIST") {
|
---|
257 | return;
|
---|
258 | }
|
---|
259 | throw err;
|
---|
260 | }
|
---|
261 | }
|
---|
262 | };
|
---|
263 | exports.mkdirpSync = mkdirpSync;
|
---|
264 |
|
---|
265 | /**
|
---|
266 | * @param {InputFileSystem} fs a file system
|
---|
267 | * @param {string} p an absolute path
|
---|
268 | * @param {ReadJsonCallback} callback callback
|
---|
269 | * @returns {void}
|
---|
270 | */
|
---|
271 | const readJson = (fs, p, callback) => {
|
---|
272 | if ("readJson" in fs) return fs.readJson(p, callback);
|
---|
273 | fs.readFile(p, (err, buf) => {
|
---|
274 | if (err) return callback(err);
|
---|
275 | let data;
|
---|
276 | try {
|
---|
277 | data = JSON.parse(buf.toString("utf-8"));
|
---|
278 | } catch (e) {
|
---|
279 | return callback(e);
|
---|
280 | }
|
---|
281 | return callback(null, data);
|
---|
282 | });
|
---|
283 | };
|
---|
284 | exports.readJson = readJson;
|
---|