metro-file-map 0.73.7 → 0.73.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/watchers/FSEventsWatcher.js +45 -43
- package/src/watchers/FSEventsWatcher.js.flow +42 -51
- package/src/watchers/NodeWatcher.js +84 -62
- package/src/watchers/NodeWatcher.js.flow +85 -63
- package/src/watchers/WatchmanWatcher.js +16 -7
- package/src/watchers/WatchmanWatcher.js.flow +15 -7
- package/src/watchers/common.js +12 -9
- package/src/watchers/common.js.flow +10 -7
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _anymatch = _interopRequireDefault(require("anymatch"));
|
|
8
8
|
var _events = _interopRequireDefault(require("events"));
|
|
9
|
-
var
|
|
9
|
+
var _fs = require("fs");
|
|
10
10
|
var path = _interopRequireWildcard(require("path"));
|
|
11
11
|
var _walker = _interopRequireDefault(require("walker"));
|
|
12
12
|
var _common = require("./common");
|
|
@@ -69,8 +69,6 @@ function _interopRequireDefault(obj) {
|
|
|
69
69
|
|
|
70
70
|
// $FlowFixMe[untyped-import] - walker
|
|
71
71
|
|
|
72
|
-
// $FlowFixMe[untyped-import] - micromatch
|
|
73
|
-
const micromatch = require("micromatch");
|
|
74
72
|
const debug = require("debug")("Metro:FSEventsWatcher");
|
|
75
73
|
let fsevents = null;
|
|
76
74
|
try {
|
|
@@ -101,6 +99,7 @@ class FSEventsWatcher extends _events.default {
|
|
|
101
99
|
dir,
|
|
102
100
|
dirCallback,
|
|
103
101
|
fileCallback,
|
|
102
|
+
symlinkCallback,
|
|
104
103
|
// $FlowFixMe[unclear-type] Add types for callback
|
|
105
104
|
endCallback,
|
|
106
105
|
// $FlowFixMe[unclear-type] Add types for callback
|
|
@@ -113,6 +112,7 @@ class FSEventsWatcher extends _events.default {
|
|
|
113
112
|
)
|
|
114
113
|
.on("dir", FSEventsWatcher._normalizeProxy(dirCallback))
|
|
115
114
|
.on("file", FSEventsWatcher._normalizeProxy(fileCallback))
|
|
115
|
+
.on("symlink", FSEventsWatcher._normalizeProxy(symlinkCallback))
|
|
116
116
|
.on("error", errorCallback)
|
|
117
117
|
.on("end", () => {
|
|
118
118
|
endCallback();
|
|
@@ -132,25 +132,25 @@ class FSEventsWatcher extends _events.default {
|
|
|
132
132
|
this.dot = opts.dot || false;
|
|
133
133
|
this.ignored = opts.ignored;
|
|
134
134
|
this.glob = Array.isArray(opts.glob) ? opts.glob : [opts.glob];
|
|
135
|
-
this.hasIgnore =
|
|
136
|
-
Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0);
|
|
137
135
|
this.doIgnore = opts.ignored
|
|
138
136
|
? (0, _anymatch.default)(opts.ignored)
|
|
139
137
|
: () => false;
|
|
140
138
|
this.root = path.resolve(dir);
|
|
141
|
-
this.fsEventsWatchStopper = fsevents.watch(this.root, (path) =>
|
|
142
|
-
this._handleEvent(path)
|
|
143
|
-
|
|
139
|
+
this.fsEventsWatchStopper = fsevents.watch(this.root, (path) => {
|
|
140
|
+
this._handleEvent(path).catch((error) => {
|
|
141
|
+
this.emit("error", error);
|
|
142
|
+
});
|
|
143
|
+
});
|
|
144
144
|
debug(`Watching ${this.root}`);
|
|
145
145
|
this._tracked = new Set();
|
|
146
|
+
const trackPath = (filePath) => {
|
|
147
|
+
this._tracked.add(filePath);
|
|
148
|
+
};
|
|
146
149
|
FSEventsWatcher._recReaddir(
|
|
147
150
|
this.root,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
(filepath) => {
|
|
152
|
-
this._tracked.add(filepath);
|
|
153
|
-
},
|
|
151
|
+
trackPath,
|
|
152
|
+
trackPath,
|
|
153
|
+
trackPath,
|
|
154
154
|
// $FlowFixMe[method-unbinding] - Refactor
|
|
155
155
|
this.emit.bind(this, "ready"),
|
|
156
156
|
// $FlowFixMe[method-unbinding] - Refactor
|
|
@@ -170,39 +170,27 @@ class FSEventsWatcher extends _events.default {
|
|
|
170
170
|
process.nextTick(callback.bind(null, null, true));
|
|
171
171
|
}
|
|
172
172
|
}
|
|
173
|
-
|
|
174
|
-
if (this.doIgnore(relativePath)) {
|
|
175
|
-
return false;
|
|
176
|
-
}
|
|
177
|
-
return this.glob.length
|
|
178
|
-
? micromatch([relativePath], this.glob, {
|
|
179
|
-
dot: this.dot,
|
|
180
|
-
}).length > 0
|
|
181
|
-
: this.dot || micromatch([relativePath], "**/*").length > 0;
|
|
182
|
-
}
|
|
183
|
-
_handleEvent(filepath) {
|
|
173
|
+
async _handleEvent(filepath) {
|
|
184
174
|
const relativePath = path.relative(this.root, filepath);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
fs.lstat(filepath, (error, stat) => {
|
|
189
|
-
if (error && error.code !== "ENOENT") {
|
|
190
|
-
this.emit("error", error);
|
|
191
|
-
return;
|
|
192
|
-
}
|
|
193
|
-
if (error) {
|
|
194
|
-
// Ignore files that aren't tracked and don't exist.
|
|
195
|
-
if (!this._tracked.has(filepath)) {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
this._emit(DELETE_EVENT, relativePath);
|
|
199
|
-
this._tracked.delete(filepath);
|
|
200
|
-
return;
|
|
201
|
-
}
|
|
175
|
+
try {
|
|
176
|
+
const stat = await _fs.promises.lstat(filepath);
|
|
202
177
|
const type = (0, _common.typeFromStat)(stat);
|
|
178
|
+
|
|
179
|
+
// Ignore files of an unrecognized type
|
|
203
180
|
if (!type) {
|
|
204
181
|
return;
|
|
205
182
|
}
|
|
183
|
+
if (
|
|
184
|
+
!(0, _common.isIncluded)(
|
|
185
|
+
type,
|
|
186
|
+
this.glob,
|
|
187
|
+
this.dot,
|
|
188
|
+
this.doIgnore,
|
|
189
|
+
relativePath
|
|
190
|
+
)
|
|
191
|
+
) {
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
206
194
|
const metadata = {
|
|
207
195
|
type,
|
|
208
196
|
modifiedTime: stat.mtime.getTime(),
|
|
@@ -214,7 +202,21 @@ class FSEventsWatcher extends _events.default {
|
|
|
214
202
|
this._tracked.add(filepath);
|
|
215
203
|
this._emit(ADD_EVENT, relativePath, metadata);
|
|
216
204
|
}
|
|
217
|
-
})
|
|
205
|
+
} catch (error) {
|
|
206
|
+
if (
|
|
207
|
+
(error === null || error === void 0 ? void 0 : error.code) !== "ENOENT"
|
|
208
|
+
) {
|
|
209
|
+
this.emit("error", error);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Ignore files that aren't tracked and don't exist.
|
|
214
|
+
if (!this._tracked.has(filepath)) {
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
this._emit(DELETE_EVENT, relativePath);
|
|
218
|
+
this._tracked.delete(filepath);
|
|
219
|
+
}
|
|
218
220
|
}
|
|
219
221
|
|
|
220
222
|
/**
|
|
@@ -9,20 +9,18 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import type {ChangeEventMetadata} from '../flow-types';
|
|
12
|
+
import type {Stats} from 'fs';
|
|
12
13
|
// $FlowFixMe[cannot-resolve-module] - Optional, Darwin only
|
|
13
14
|
import type {FSEvents} from 'fsevents';
|
|
14
15
|
|
|
15
16
|
// $FlowFixMe[untyped-import] - anymatch
|
|
16
17
|
import anymatch from 'anymatch';
|
|
17
18
|
import EventEmitter from 'events';
|
|
18
|
-
import
|
|
19
|
+
import {promises as fsPromises} from 'fs';
|
|
19
20
|
import * as path from 'path';
|
|
20
21
|
// $FlowFixMe[untyped-import] - walker
|
|
21
22
|
import walker from 'walker';
|
|
22
|
-
import {typeFromStat} from './common';
|
|
23
|
-
|
|
24
|
-
// $FlowFixMe[untyped-import] - micromatch
|
|
25
|
-
const micromatch = require('micromatch');
|
|
23
|
+
import {isIncluded, typeFromStat} from './common';
|
|
26
24
|
|
|
27
25
|
const debug = require('debug')('Metro:FSEventsWatcher');
|
|
28
26
|
|
|
@@ -56,7 +54,6 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
56
54
|
+ignored: ?Matcher;
|
|
57
55
|
+glob: $ReadOnlyArray<string>;
|
|
58
56
|
+dot: boolean;
|
|
59
|
-
+hasIgnore: boolean;
|
|
60
57
|
+doIgnore: (path: string) => boolean;
|
|
61
58
|
+fsEventsWatchStopper: () => Promise<void>;
|
|
62
59
|
_tracked: Set<string>;
|
|
@@ -66,17 +63,18 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
66
63
|
}
|
|
67
64
|
|
|
68
65
|
static _normalizeProxy(
|
|
69
|
-
callback: (normalizedPath: string, stats:
|
|
66
|
+
callback: (normalizedPath: string, stats: Stats) => void,
|
|
70
67
|
// $FlowFixMe[cannot-resolve-name]
|
|
71
68
|
): (filepath: string, stats: Stats) => void {
|
|
72
|
-
return (filepath: string, stats:
|
|
69
|
+
return (filepath: string, stats: Stats): void =>
|
|
73
70
|
callback(path.normalize(filepath), stats);
|
|
74
71
|
}
|
|
75
72
|
|
|
76
73
|
static _recReaddir(
|
|
77
74
|
dir: string,
|
|
78
|
-
dirCallback: (normalizedPath: string, stats:
|
|
79
|
-
fileCallback: (normalizedPath: string, stats:
|
|
75
|
+
dirCallback: (normalizedPath: string, stats: Stats) => void,
|
|
76
|
+
fileCallback: (normalizedPath: string, stats: Stats) => void,
|
|
77
|
+
symlinkCallback: (normalizedPath: string, stats: Stats) => void,
|
|
80
78
|
// $FlowFixMe[unclear-type] Add types for callback
|
|
81
79
|
endCallback: Function,
|
|
82
80
|
// $FlowFixMe[unclear-type] Add types for callback
|
|
@@ -89,6 +87,7 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
89
87
|
)
|
|
90
88
|
.on('dir', FSEventsWatcher._normalizeProxy(dirCallback))
|
|
91
89
|
.on('file', FSEventsWatcher._normalizeProxy(fileCallback))
|
|
90
|
+
.on('symlink', FSEventsWatcher._normalizeProxy(symlinkCallback))
|
|
92
91
|
.on('error', errorCallback)
|
|
93
92
|
.on('end', () => {
|
|
94
93
|
endCallback();
|
|
@@ -116,28 +115,27 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
116
115
|
this.dot = opts.dot || false;
|
|
117
116
|
this.ignored = opts.ignored;
|
|
118
117
|
this.glob = Array.isArray(opts.glob) ? opts.glob : [opts.glob];
|
|
119
|
-
|
|
120
|
-
this.hasIgnore =
|
|
121
|
-
Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0);
|
|
122
118
|
this.doIgnore = opts.ignored ? anymatch(opts.ignored) : () => false;
|
|
123
119
|
|
|
124
120
|
this.root = path.resolve(dir);
|
|
125
121
|
|
|
126
|
-
this.fsEventsWatchStopper = fsevents.watch(this.root, path =>
|
|
127
|
-
this._handleEvent(path)
|
|
128
|
-
|
|
122
|
+
this.fsEventsWatchStopper = fsevents.watch(this.root, path => {
|
|
123
|
+
this._handleEvent(path).catch(error => {
|
|
124
|
+
this.emit('error', error);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
129
127
|
|
|
130
128
|
debug(`Watching ${this.root}`);
|
|
131
129
|
|
|
132
130
|
this._tracked = new Set();
|
|
131
|
+
const trackPath = (filePath: string) => {
|
|
132
|
+
this._tracked.add(filePath);
|
|
133
|
+
};
|
|
133
134
|
FSEventsWatcher._recReaddir(
|
|
134
135
|
this.root,
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
(filepath: string) => {
|
|
139
|
-
this._tracked.add(filepath);
|
|
140
|
-
},
|
|
136
|
+
trackPath,
|
|
137
|
+
trackPath,
|
|
138
|
+
trackPath,
|
|
141
139
|
// $FlowFixMe[method-unbinding] - Refactor
|
|
142
140
|
this.emit.bind(this, 'ready'),
|
|
143
141
|
// $FlowFixMe[method-unbinding] - Refactor
|
|
@@ -158,42 +156,22 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
158
156
|
}
|
|
159
157
|
}
|
|
160
158
|
|
|
161
|
-
|
|
162
|
-
if (this.doIgnore(relativePath)) {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
165
|
-
return this.glob.length
|
|
166
|
-
? micromatch([relativePath], this.glob, {dot: this.dot}).length > 0
|
|
167
|
-
: this.dot || micromatch([relativePath], '**/*').length > 0;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
_handleEvent(filepath: string) {
|
|
159
|
+
async _handleEvent(filepath: string) {
|
|
171
160
|
const relativePath = path.relative(this.root, filepath);
|
|
172
|
-
if (!this._isFileIncluded(relativePath)) {
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
fs.lstat(filepath, (error, stat) => {
|
|
177
|
-
if (error && error.code !== 'ENOENT') {
|
|
178
|
-
this.emit('error', error);
|
|
179
|
-
return;
|
|
180
|
-
}
|
|
181
161
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
return;
|
|
186
|
-
}
|
|
162
|
+
try {
|
|
163
|
+
const stat = await fsPromises.lstat(filepath);
|
|
164
|
+
const type = typeFromStat(stat);
|
|
187
165
|
|
|
188
|
-
|
|
189
|
-
|
|
166
|
+
// Ignore files of an unrecognized type
|
|
167
|
+
if (!type) {
|
|
190
168
|
return;
|
|
191
169
|
}
|
|
192
170
|
|
|
193
|
-
|
|
194
|
-
if (!type) {
|
|
171
|
+
if (!isIncluded(type, this.glob, this.dot, this.doIgnore, relativePath)) {
|
|
195
172
|
return;
|
|
196
173
|
}
|
|
174
|
+
|
|
197
175
|
const metadata: ChangeEventMetadata = {
|
|
198
176
|
type,
|
|
199
177
|
modifiedTime: stat.mtime.getTime(),
|
|
@@ -206,7 +184,20 @@ export default class FSEventsWatcher extends EventEmitter {
|
|
|
206
184
|
this._tracked.add(filepath);
|
|
207
185
|
this._emit(ADD_EVENT, relativePath, metadata);
|
|
208
186
|
}
|
|
209
|
-
})
|
|
187
|
+
} catch (error) {
|
|
188
|
+
if (error?.code !== 'ENOENT') {
|
|
189
|
+
this.emit('error', error);
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Ignore files that aren't tracked and don't exist.
|
|
194
|
+
if (!this._tracked.has(filepath)) {
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
this._emit(DELETE_EVENT, relativePath);
|
|
199
|
+
this._tracked.delete(filepath);
|
|
200
|
+
}
|
|
210
201
|
}
|
|
211
202
|
|
|
212
203
|
/**
|
|
@@ -20,6 +20,7 @@ const { EventEmitter } = require("events");
|
|
|
20
20
|
const fs = require("fs");
|
|
21
21
|
const platform = require("os").platform();
|
|
22
22
|
const path = require("path");
|
|
23
|
+
const fsPromises = fs.promises;
|
|
23
24
|
const CHANGE_EVENT = common.CHANGE_EVENT;
|
|
24
25
|
const DELETE_EVENT = common.DELETE_EVENT;
|
|
25
26
|
const ADD_EVENT = common.ADD_EVENT;
|
|
@@ -46,7 +47,10 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
46
47
|
this._watchdir(dir);
|
|
47
48
|
},
|
|
48
49
|
(filename) => {
|
|
49
|
-
this._register(filename);
|
|
50
|
+
this._register(filename, "f");
|
|
51
|
+
},
|
|
52
|
+
(symlink) => {
|
|
53
|
+
this._register(symlink, "l");
|
|
50
54
|
},
|
|
51
55
|
() => {
|
|
52
56
|
this.emit("ready");
|
|
@@ -70,7 +74,7 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
70
74
|
*
|
|
71
75
|
* Return false if ignored or already registered.
|
|
72
76
|
*/
|
|
73
|
-
_register(filepath) {
|
|
77
|
+
_register(filepath, type) {
|
|
74
78
|
const dir = path.dirname(filepath);
|
|
75
79
|
const filename = path.basename(filepath);
|
|
76
80
|
if (this._dirRegistry[dir] && this._dirRegistry[dir][filename]) {
|
|
@@ -78,7 +82,8 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
78
82
|
}
|
|
79
83
|
const relativePath = path.relative(this.root, filepath);
|
|
80
84
|
if (
|
|
81
|
-
|
|
85
|
+
type === "f" &&
|
|
86
|
+
!common.isIncluded("f", this.globs, this.dot, this.doIgnore, relativePath)
|
|
82
87
|
) {
|
|
83
88
|
return false;
|
|
84
89
|
}
|
|
@@ -147,7 +152,7 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
147
152
|
this.watched[dir] = watcher;
|
|
148
153
|
watcher.on("error", this._checkedEmitError);
|
|
149
154
|
if (this.root !== dir) {
|
|
150
|
-
this._register(dir);
|
|
155
|
+
this._register(dir, "d");
|
|
151
156
|
}
|
|
152
157
|
return true;
|
|
153
158
|
};
|
|
@@ -216,91 +221,108 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
216
221
|
if (!file) {
|
|
217
222
|
this._detectChangedFile(dir, event, (actualFile) => {
|
|
218
223
|
if (actualFile) {
|
|
219
|
-
this._processChange(dir, event, actualFile)
|
|
224
|
+
this._processChange(dir, event, actualFile).catch((error) =>
|
|
225
|
+
this.emit("error", error)
|
|
226
|
+
);
|
|
220
227
|
}
|
|
221
228
|
});
|
|
222
229
|
} else {
|
|
223
|
-
this._processChange(dir, event, path.normalize(file))
|
|
230
|
+
this._processChange(dir, event, path.normalize(file)).catch((error) =>
|
|
231
|
+
this.emit("error", error)
|
|
232
|
+
);
|
|
224
233
|
}
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
/**
|
|
228
237
|
* Process changes.
|
|
229
238
|
*/
|
|
230
|
-
_processChange(dir, event, file) {
|
|
239
|
+
async _processChange(dir, event, file) {
|
|
231
240
|
const fullPath = path.join(dir, file);
|
|
232
241
|
const relativePath = path.join(path.relative(this.root, dir), file);
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
242
|
+
const registered = this._registered(fullPath);
|
|
243
|
+
try {
|
|
244
|
+
const stat = await fsPromises.lstat(fullPath);
|
|
245
|
+
if (stat.isDirectory()) {
|
|
246
|
+
// win32 emits usless change events on dirs.
|
|
237
247
|
if (event === "change") {
|
|
238
|
-
// win32 emits usless change events on dirs.
|
|
239
248
|
return;
|
|
240
249
|
}
|
|
241
250
|
if (
|
|
242
|
-
|
|
243
|
-
|
|
251
|
+
!common.isIncluded(
|
|
252
|
+
"d",
|
|
244
253
|
this.globs,
|
|
245
254
|
this.dot,
|
|
246
255
|
this.doIgnore,
|
|
247
256
|
relativePath
|
|
248
257
|
)
|
|
249
258
|
) {
|
|
250
|
-
|
|
251
|
-
path.resolve(this.root, relativePath),
|
|
252
|
-
(dir, stats) => {
|
|
253
|
-
if (this._watchdir(dir)) {
|
|
254
|
-
this._emitEvent(ADD_EVENT, path.relative(this.root, dir), {
|
|
255
|
-
modifiedTime: stats.mtime.getTime(),
|
|
256
|
-
size: stats.size,
|
|
257
|
-
type: "d",
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
},
|
|
261
|
-
(file, stats) => {
|
|
262
|
-
if (this._register(file)) {
|
|
263
|
-
this._emitEvent(ADD_EVENT, path.relative(this.root, file), {
|
|
264
|
-
modifiedTime: stats.mtime.getTime(),
|
|
265
|
-
size: stats.size,
|
|
266
|
-
type: "f",
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
},
|
|
270
|
-
function endCallback() {},
|
|
271
|
-
this._checkedEmitError,
|
|
272
|
-
this.ignored
|
|
273
|
-
);
|
|
259
|
+
return;
|
|
274
260
|
}
|
|
261
|
+
common.recReaddir(
|
|
262
|
+
path.resolve(this.root, relativePath),
|
|
263
|
+
(dir, stats) => {
|
|
264
|
+
if (this._watchdir(dir)) {
|
|
265
|
+
this._emitEvent(ADD_EVENT, path.relative(this.root, dir), {
|
|
266
|
+
modifiedTime: stats.mtime.getTime(),
|
|
267
|
+
size: stats.size,
|
|
268
|
+
type: "d",
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
},
|
|
272
|
+
(file, stats) => {
|
|
273
|
+
if (this._register(file, "f")) {
|
|
274
|
+
this._emitEvent(ADD_EVENT, path.relative(this.root, file), {
|
|
275
|
+
modifiedTime: stats.mtime.getTime(),
|
|
276
|
+
size: stats.size,
|
|
277
|
+
type: "f",
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
(symlink, stats) => {
|
|
282
|
+
if (this._register(symlink, "l")) {
|
|
283
|
+
this._rawEmitEvent(ADD_EVENT, path.relative(this.root, symlink), {
|
|
284
|
+
modifiedTime: stats.mtime.getTime(),
|
|
285
|
+
size: stats.size,
|
|
286
|
+
type: "l",
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
function endCallback() {},
|
|
291
|
+
this._checkedEmitError,
|
|
292
|
+
this.ignored
|
|
293
|
+
);
|
|
275
294
|
} else {
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
295
|
+
const type = common.typeFromStat(stat);
|
|
296
|
+
if (type == null) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const metadata = {
|
|
300
|
+
modifiedTime: stat.mtime.getTime(),
|
|
301
|
+
size: stat.size,
|
|
302
|
+
type,
|
|
303
|
+
};
|
|
304
|
+
if (registered) {
|
|
305
|
+
this._emitEvent(CHANGE_EVENT, relativePath, metadata);
|
|
284
306
|
} else {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
return;
|
|
288
|
-
}
|
|
289
|
-
const metadata = {
|
|
290
|
-
modifiedTime: stat.mtime.getTime(),
|
|
291
|
-
size: stat.size,
|
|
292
|
-
type,
|
|
293
|
-
};
|
|
294
|
-
if (registered) {
|
|
295
|
-
this._emitEvent(CHANGE_EVENT, relativePath, metadata);
|
|
296
|
-
} else {
|
|
297
|
-
if (this._register(fullPath)) {
|
|
298
|
-
this._emitEvent(ADD_EVENT, relativePath, metadata);
|
|
299
|
-
}
|
|
307
|
+
if (this._register(fullPath, type)) {
|
|
308
|
+
this._emitEvent(ADD_EVENT, relativePath, metadata);
|
|
300
309
|
}
|
|
301
310
|
}
|
|
302
311
|
}
|
|
303
|
-
})
|
|
312
|
+
} catch (error) {
|
|
313
|
+
if (
|
|
314
|
+
(error === null || error === void 0 ? void 0 : error.code) !== "ENOENT"
|
|
315
|
+
) {
|
|
316
|
+
this.emit("error", error);
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this._unregister(fullPath);
|
|
320
|
+
this._stopWatching(fullPath);
|
|
321
|
+
this._unregisterDir(fullPath);
|
|
322
|
+
if (registered) {
|
|
323
|
+
this._emitEvent(DELETE_EVENT, relativePath);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
304
326
|
}
|
|
305
327
|
|
|
306
328
|
/**
|
|
@@ -25,6 +25,8 @@ const fs = require('fs');
|
|
|
25
25
|
const platform = require('os').platform();
|
|
26
26
|
const path = require('path');
|
|
27
27
|
|
|
28
|
+
const fsPromises = fs.promises;
|
|
29
|
+
|
|
28
30
|
const CHANGE_EVENT = common.CHANGE_EVENT;
|
|
29
31
|
const DELETE_EVENT = common.DELETE_EVENT;
|
|
30
32
|
const ADD_EVENT = common.ADD_EVENT;
|
|
@@ -46,7 +48,6 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
46
48
|
doIgnore: string => boolean;
|
|
47
49
|
dot: boolean;
|
|
48
50
|
globs: $ReadOnlyArray<string>;
|
|
49
|
-
hasIgnore: boolean;
|
|
50
51
|
ignored: ?(boolean | RegExp);
|
|
51
52
|
root: string;
|
|
52
53
|
watched: {[key: string]: FSWatcher, __proto__: null};
|
|
@@ -68,7 +69,10 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
68
69
|
this._watchdir(dir);
|
|
69
70
|
},
|
|
70
71
|
filename => {
|
|
71
|
-
this._register(filename);
|
|
72
|
+
this._register(filename, 'f');
|
|
73
|
+
},
|
|
74
|
+
symlink => {
|
|
75
|
+
this._register(symlink, 'l');
|
|
72
76
|
},
|
|
73
77
|
() => {
|
|
74
78
|
this.emit('ready');
|
|
@@ -92,7 +96,7 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
92
96
|
*
|
|
93
97
|
* Return false if ignored or already registered.
|
|
94
98
|
*/
|
|
95
|
-
_register(filepath: string): boolean {
|
|
99
|
+
_register(filepath: string, type: ChangeEventMetadata['type']): boolean {
|
|
96
100
|
const dir = path.dirname(filepath);
|
|
97
101
|
const filename = path.basename(filepath);
|
|
98
102
|
if (this._dirRegistry[dir] && this._dirRegistry[dir][filename]) {
|
|
@@ -101,7 +105,8 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
101
105
|
|
|
102
106
|
const relativePath = path.relative(this.root, filepath);
|
|
103
107
|
if (
|
|
104
|
-
|
|
108
|
+
type === 'f' &&
|
|
109
|
+
!common.isIncluded('f', this.globs, this.dot, this.doIgnore, relativePath)
|
|
105
110
|
) {
|
|
106
111
|
return false;
|
|
107
112
|
}
|
|
@@ -171,7 +176,7 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
171
176
|
watcher.on('error', this._checkedEmitError);
|
|
172
177
|
|
|
173
178
|
if (this.root !== dir) {
|
|
174
|
-
this._register(dir);
|
|
179
|
+
this._register(dir, 'd');
|
|
175
180
|
}
|
|
176
181
|
return true;
|
|
177
182
|
};
|
|
@@ -243,92 +248,109 @@ module.exports = class NodeWatcher extends EventEmitter {
|
|
|
243
248
|
if (!file) {
|
|
244
249
|
this._detectChangedFile(dir, event, actualFile => {
|
|
245
250
|
if (actualFile) {
|
|
246
|
-
this._processChange(dir, event, actualFile)
|
|
251
|
+
this._processChange(dir, event, actualFile).catch(error =>
|
|
252
|
+
this.emit('error', error),
|
|
253
|
+
);
|
|
247
254
|
}
|
|
248
255
|
});
|
|
249
256
|
} else {
|
|
250
|
-
this._processChange(dir, event, path.normalize(file))
|
|
257
|
+
this._processChange(dir, event, path.normalize(file)).catch(error =>
|
|
258
|
+
this.emit('error', error),
|
|
259
|
+
);
|
|
251
260
|
}
|
|
252
261
|
}
|
|
253
262
|
|
|
254
263
|
/**
|
|
255
264
|
* Process changes.
|
|
256
265
|
*/
|
|
257
|
-
_processChange(dir: string, event: string, file: string) {
|
|
266
|
+
async _processChange(dir: string, event: string, file: string) {
|
|
258
267
|
const fullPath = path.join(dir, file);
|
|
259
268
|
const relativePath = path.join(path.relative(this.root, dir), file);
|
|
260
269
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
const registered = this._registered(fullPath);
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const stat = await fsPromises.lstat(fullPath);
|
|
274
|
+
if (stat.isDirectory()) {
|
|
275
|
+
// win32 emits usless change events on dirs.
|
|
265
276
|
if (event === 'change') {
|
|
266
|
-
// win32 emits usless change events on dirs.
|
|
267
277
|
return;
|
|
268
278
|
}
|
|
279
|
+
|
|
269
280
|
if (
|
|
270
|
-
|
|
271
|
-
|
|
281
|
+
!common.isIncluded(
|
|
282
|
+
'd',
|
|
272
283
|
this.globs,
|
|
273
284
|
this.dot,
|
|
274
285
|
this.doIgnore,
|
|
275
286
|
relativePath,
|
|
276
287
|
)
|
|
277
288
|
) {
|
|
278
|
-
|
|
279
|
-
path.resolve(this.root, relativePath),
|
|
280
|
-
(dir, stats) => {
|
|
281
|
-
if (this._watchdir(dir)) {
|
|
282
|
-
this._emitEvent(ADD_EVENT, path.relative(this.root, dir), {
|
|
283
|
-
modifiedTime: stats.mtime.getTime(),
|
|
284
|
-
size: stats.size,
|
|
285
|
-
type: 'd',
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
},
|
|
289
|
-
(file, stats) => {
|
|
290
|
-
if (this._register(file)) {
|
|
291
|
-
this._emitEvent(ADD_EVENT, path.relative(this.root, file), {
|
|
292
|
-
modifiedTime: stats.mtime.getTime(),
|
|
293
|
-
size: stats.size,
|
|
294
|
-
type: 'f',
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
},
|
|
298
|
-
function endCallback() {},
|
|
299
|
-
this._checkedEmitError,
|
|
300
|
-
this.ignored,
|
|
301
|
-
);
|
|
289
|
+
return;
|
|
302
290
|
}
|
|
291
|
+
common.recReaddir(
|
|
292
|
+
path.resolve(this.root, relativePath),
|
|
293
|
+
(dir, stats) => {
|
|
294
|
+
if (this._watchdir(dir)) {
|
|
295
|
+
this._emitEvent(ADD_EVENT, path.relative(this.root, dir), {
|
|
296
|
+
modifiedTime: stats.mtime.getTime(),
|
|
297
|
+
size: stats.size,
|
|
298
|
+
type: 'd',
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
},
|
|
302
|
+
(file, stats) => {
|
|
303
|
+
if (this._register(file, 'f')) {
|
|
304
|
+
this._emitEvent(ADD_EVENT, path.relative(this.root, file), {
|
|
305
|
+
modifiedTime: stats.mtime.getTime(),
|
|
306
|
+
size: stats.size,
|
|
307
|
+
type: 'f',
|
|
308
|
+
});
|
|
309
|
+
}
|
|
310
|
+
},
|
|
311
|
+
(symlink, stats) => {
|
|
312
|
+
if (this._register(symlink, 'l')) {
|
|
313
|
+
this._rawEmitEvent(ADD_EVENT, path.relative(this.root, symlink), {
|
|
314
|
+
modifiedTime: stats.mtime.getTime(),
|
|
315
|
+
size: stats.size,
|
|
316
|
+
type: 'l',
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
function endCallback() {},
|
|
321
|
+
this._checkedEmitError,
|
|
322
|
+
this.ignored,
|
|
323
|
+
);
|
|
303
324
|
} else {
|
|
304
|
-
const
|
|
305
|
-
if (
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
325
|
+
const type = common.typeFromStat(stat);
|
|
326
|
+
if (type == null) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const metadata = {
|
|
330
|
+
modifiedTime: stat.mtime.getTime(),
|
|
331
|
+
size: stat.size,
|
|
332
|
+
type,
|
|
333
|
+
};
|
|
334
|
+
if (registered) {
|
|
335
|
+
this._emitEvent(CHANGE_EVENT, relativePath, metadata);
|
|
312
336
|
} else {
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const metadata = {
|
|
318
|
-
modifiedTime: stat.mtime.getTime(),
|
|
319
|
-
size: stat.size,
|
|
320
|
-
type,
|
|
321
|
-
};
|
|
322
|
-
if (registered) {
|
|
323
|
-
this._emitEvent(CHANGE_EVENT, relativePath, metadata);
|
|
324
|
-
} else {
|
|
325
|
-
if (this._register(fullPath)) {
|
|
326
|
-
this._emitEvent(ADD_EVENT, relativePath, metadata);
|
|
327
|
-
}
|
|
337
|
+
if (this._register(fullPath, type)) {
|
|
338
|
+
this._emitEvent(ADD_EVENT, relativePath, metadata);
|
|
328
339
|
}
|
|
329
340
|
}
|
|
330
341
|
}
|
|
331
|
-
})
|
|
342
|
+
} catch (error) {
|
|
343
|
+
if (error?.code !== 'ENOENT') {
|
|
344
|
+
this.emit('error', error);
|
|
345
|
+
return;
|
|
346
|
+
}
|
|
347
|
+
this._unregister(fullPath);
|
|
348
|
+
this._stopWatching(fullPath);
|
|
349
|
+
this._unregisterDir(fullPath);
|
|
350
|
+
if (registered) {
|
|
351
|
+
this._emitEvent(DELETE_EVENT, relativePath);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
332
354
|
}
|
|
333
355
|
|
|
334
356
|
/**
|
|
@@ -258,14 +258,25 @@ class WatchmanWatcher extends _events.default {
|
|
|
258
258
|
size,
|
|
259
259
|
} = changeDescriptor;
|
|
260
260
|
debug(
|
|
261
|
-
"Handling change to: %s (new: %s, exists: %s)",
|
|
261
|
+
"Handling change to: %s (new: %s, exists: %s, type: %s)",
|
|
262
262
|
relativePath,
|
|
263
263
|
isNew,
|
|
264
|
-
exists
|
|
264
|
+
exists,
|
|
265
|
+
type
|
|
265
266
|
);
|
|
267
|
+
|
|
268
|
+
// Ignore files of an unrecognized type
|
|
269
|
+
if (type != null && !(type === "f" || type === "d" || type === "l")) {
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
266
272
|
if (
|
|
267
|
-
|
|
268
|
-
|
|
273
|
+
!common.isIncluded(
|
|
274
|
+
type,
|
|
275
|
+
this.globs,
|
|
276
|
+
this.dot,
|
|
277
|
+
this.doIgnore,
|
|
278
|
+
relativePath
|
|
279
|
+
)
|
|
269
280
|
) {
|
|
270
281
|
return;
|
|
271
282
|
}
|
|
@@ -283,10 +294,8 @@ class WatchmanWatcher extends _events.default {
|
|
|
283
294
|
size
|
|
284
295
|
);
|
|
285
296
|
if (
|
|
286
|
-
type === "f" ||
|
|
287
|
-
type === "l" ||
|
|
288
297
|
// Change event on dirs are mostly useless.
|
|
289
|
-
(type === "d" && eventType
|
|
298
|
+
!(type === "d" && eventType === CHANGE_EVENT)
|
|
290
299
|
) {
|
|
291
300
|
self._emitEvent(eventType, relativePath, self.root, {
|
|
292
301
|
modifiedTime: Number(mtime_ms),
|
|
@@ -46,7 +46,6 @@ export default class WatchmanWatcher extends EventEmitter {
|
|
|
46
46
|
dot: boolean;
|
|
47
47
|
doIgnore: string => boolean;
|
|
48
48
|
globs: $ReadOnlyArray<string>;
|
|
49
|
-
hasIgnore: boolean;
|
|
50
49
|
root: string;
|
|
51
50
|
subscriptionName: string;
|
|
52
51
|
watchProjectInfo: ?$ReadOnly<{
|
|
@@ -248,15 +247,26 @@ export default class WatchmanWatcher extends EventEmitter {
|
|
|
248
247
|
} = changeDescriptor;
|
|
249
248
|
|
|
250
249
|
debug(
|
|
251
|
-
'Handling change to: %s (new: %s, exists: %s)',
|
|
250
|
+
'Handling change to: %s (new: %s, exists: %s, type: %s)',
|
|
252
251
|
relativePath,
|
|
253
252
|
isNew,
|
|
254
253
|
exists,
|
|
254
|
+
type,
|
|
255
255
|
);
|
|
256
256
|
|
|
257
|
+
// Ignore files of an unrecognized type
|
|
258
|
+
if (type != null && !(type === 'f' || type === 'd' || type === 'l')) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
|
|
257
262
|
if (
|
|
258
|
-
|
|
259
|
-
|
|
263
|
+
!common.isIncluded(
|
|
264
|
+
type,
|
|
265
|
+
this.globs,
|
|
266
|
+
this.dot,
|
|
267
|
+
this.doIgnore,
|
|
268
|
+
relativePath,
|
|
269
|
+
)
|
|
260
270
|
) {
|
|
261
271
|
return;
|
|
262
272
|
}
|
|
@@ -276,10 +286,8 @@ export default class WatchmanWatcher extends EventEmitter {
|
|
|
276
286
|
);
|
|
277
287
|
|
|
278
288
|
if (
|
|
279
|
-
type === 'f' ||
|
|
280
|
-
type === 'l' ||
|
|
281
289
|
// Change event on dirs are mostly useless.
|
|
282
|
-
(type === 'd' && eventType
|
|
290
|
+
!(type === 'd' && eventType === CHANGE_EVENT)
|
|
283
291
|
) {
|
|
284
292
|
self._emitEvent(eventType, relativePath, self.root, {
|
|
285
293
|
modifiedTime: Number(mtime_ms),
|
package/src/watchers/common.js
CHANGED
|
@@ -25,7 +25,7 @@ exports.assignOptions =
|
|
|
25
25
|
exports.ALL_EVENT =
|
|
26
26
|
exports.ADD_EVENT =
|
|
27
27
|
void 0;
|
|
28
|
-
exports.
|
|
28
|
+
exports.isIncluded = isIncluded;
|
|
29
29
|
exports.recReaddir = recReaddir;
|
|
30
30
|
exports.typeFromStat = typeFromStat;
|
|
31
31
|
// $FlowFixMe[untyped-import] - Write libdefs for `anymatch`
|
|
@@ -72,8 +72,6 @@ const assignOptions = function (watcher, opts) {
|
|
|
72
72
|
if (!Array.isArray(watcher.globs)) {
|
|
73
73
|
watcher.globs = [watcher.globs];
|
|
74
74
|
}
|
|
75
|
-
watcher.hasIgnore =
|
|
76
|
-
Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0);
|
|
77
75
|
watcher.doIgnore =
|
|
78
76
|
opts.ignored != null && opts.ignored !== false
|
|
79
77
|
? anymatch(opts.ignored)
|
|
@@ -88,15 +86,18 @@ const assignOptions = function (watcher, opts) {
|
|
|
88
86
|
* Checks a file relative path against the globs array.
|
|
89
87
|
*/
|
|
90
88
|
exports.assignOptions = assignOptions;
|
|
91
|
-
function
|
|
89
|
+
function isIncluded(type, globs, dot, doIgnore, relativePath) {
|
|
92
90
|
if (doIgnore(relativePath)) {
|
|
93
91
|
return false;
|
|
94
92
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
// For non-regular files or if there are no glob matchers, just respect the
|
|
94
|
+
// `dot` option to filter dotfiles if dot === false.
|
|
95
|
+
if (globs.length === 0 || type !== "f") {
|
|
96
|
+
return dot || micromatch.some(relativePath, "**/*");
|
|
97
|
+
}
|
|
98
|
+
return micromatch.some(relativePath, globs, {
|
|
99
|
+
dot,
|
|
100
|
+
});
|
|
100
101
|
}
|
|
101
102
|
|
|
102
103
|
/**
|
|
@@ -106,6 +107,7 @@ function recReaddir(
|
|
|
106
107
|
dir,
|
|
107
108
|
dirCallback,
|
|
108
109
|
fileCallback,
|
|
110
|
+
symlinkCallback,
|
|
109
111
|
endCallback,
|
|
110
112
|
errorCallback,
|
|
111
113
|
ignored
|
|
@@ -114,6 +116,7 @@ function recReaddir(
|
|
|
114
116
|
.filterDir((currentDir) => !anymatch(ignored, currentDir))
|
|
115
117
|
.on("dir", normalizeProxy(dirCallback))
|
|
116
118
|
.on("file", normalizeProxy(fileCallback))
|
|
119
|
+
.on("symlink", normalizeProxy(symlinkCallback))
|
|
117
120
|
.on("error", errorCallback)
|
|
118
121
|
.on("end", () => {
|
|
119
122
|
if (platform === "win32") {
|
|
@@ -49,7 +49,6 @@ interface Watcher {
|
|
|
49
49
|
doIgnore: string => boolean;
|
|
50
50
|
dot: boolean;
|
|
51
51
|
globs: $ReadOnlyArray<string>;
|
|
52
|
-
hasIgnore: boolean;
|
|
53
52
|
ignored?: ?(boolean | RegExp);
|
|
54
53
|
watchmanDeferStates: $ReadOnlyArray<string>;
|
|
55
54
|
watchmanPath?: ?string;
|
|
@@ -75,8 +74,6 @@ export const assignOptions = function (
|
|
|
75
74
|
if (!Array.isArray(watcher.globs)) {
|
|
76
75
|
watcher.globs = [watcher.globs];
|
|
77
76
|
}
|
|
78
|
-
watcher.hasIgnore =
|
|
79
|
-
Boolean(opts.ignored) && !(Array.isArray(opts) && opts.length > 0);
|
|
80
77
|
watcher.doIgnore =
|
|
81
78
|
opts.ignored != null && opts.ignored !== false
|
|
82
79
|
? anymatch(opts.ignored)
|
|
@@ -92,7 +89,8 @@ export const assignOptions = function (
|
|
|
92
89
|
/**
|
|
93
90
|
* Checks a file relative path against the globs array.
|
|
94
91
|
*/
|
|
95
|
-
export function
|
|
92
|
+
export function isIncluded(
|
|
93
|
+
type: ?('f' | 'l' | 'd'),
|
|
96
94
|
globs: $ReadOnlyArray<string>,
|
|
97
95
|
dot: boolean,
|
|
98
96
|
doIgnore: string => boolean,
|
|
@@ -101,9 +99,12 @@ export function isFileIncluded(
|
|
|
101
99
|
if (doIgnore(relativePath)) {
|
|
102
100
|
return false;
|
|
103
101
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
// For non-regular files or if there are no glob matchers, just respect the
|
|
103
|
+
// `dot` option to filter dotfiles if dot === false.
|
|
104
|
+
if (globs.length === 0 || type !== 'f') {
|
|
105
|
+
return dot || micromatch.some(relativePath, '**/*');
|
|
106
|
+
}
|
|
107
|
+
return micromatch.some(relativePath, globs, {dot});
|
|
107
108
|
}
|
|
108
109
|
|
|
109
110
|
/**
|
|
@@ -113,6 +114,7 @@ export function recReaddir(
|
|
|
113
114
|
dir: string,
|
|
114
115
|
dirCallback: (string, Stats) => void,
|
|
115
116
|
fileCallback: (string, Stats) => void,
|
|
117
|
+
symlinkCallback: (string, Stats) => void,
|
|
116
118
|
endCallback: () => void,
|
|
117
119
|
errorCallback: Error => void,
|
|
118
120
|
ignored: ?(boolean | RegExp),
|
|
@@ -121,6 +123,7 @@ export function recReaddir(
|
|
|
121
123
|
.filterDir(currentDir => !anymatch(ignored, currentDir))
|
|
122
124
|
.on('dir', normalizeProxy(dirCallback))
|
|
123
125
|
.on('file', normalizeProxy(fileCallback))
|
|
126
|
+
.on('symlink', normalizeProxy(symlinkCallback))
|
|
124
127
|
.on('error', errorCallback)
|
|
125
128
|
.on('end', () => {
|
|
126
129
|
if (platform === 'win32') {
|