metro-file-map 0.73.3 → 0.73.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "metro-file-map",
3
- "version": "0.73.3",
3
+ "version": "0.73.4",
4
4
  "description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
5
5
  "main": "src/index.js",
6
6
  "repository": {
@@ -8,14 +8,20 @@
8
8
  * @flow strict-local
9
9
  */
10
10
 
11
- import type {FileData, FileMetaData, Glob, Path} from './flow-types';
11
+ import type {
12
+ FileData,
13
+ FileMetaData,
14
+ FileSystem,
15
+ Glob,
16
+ Path,
17
+ } from './flow-types';
12
18
 
13
19
  import H from './constants';
14
20
  import * as fastPath from './lib/fast_path';
15
21
  import * as path from 'path';
16
22
  import {globsToMatcher, replacePathSepForGlob} from 'jest-util';
17
23
 
18
- export default class HasteFS {
24
+ export default class HasteFS implements FileSystem {
19
25
  +_rootDir: Path;
20
26
  +_files: FileData;
21
27
 
package/src/ModuleMap.js CHANGED
@@ -7,6 +7,8 @@ exports.default = void 0;
7
7
 
8
8
  var _constants = _interopRequireDefault(require("./constants"));
9
9
 
10
+ var _DuplicateHasteCandidatesError = require("./lib/DuplicateHasteCandidatesError");
11
+
10
12
  var fastPath = _interopRequireWildcard(require("./lib/fast_path"));
11
13
 
12
14
  function _getRequireWildcardCache(nodeInterop) {
@@ -69,32 +71,6 @@ const EMPTY_OBJ = {};
69
71
  const EMPTY_MAP = new Map();
70
72
 
71
73
  class ModuleMap {
72
- // $FlowFixMe[unclear-type] - Refactor away this function
73
- static _mapToArrayRecursive(map) {
74
- let arr = Array.from(map);
75
-
76
- if (arr[0] && arr[0][1] instanceof Map) {
77
- arr = arr.map(
78
- // $FlowFixMe[unclear-type] - Refactor away this function
79
- (el) => [el[0], this._mapToArrayRecursive(el[1])]
80
- );
81
- }
82
-
83
- return arr;
84
- }
85
-
86
- static _mapFromArrayRecursive(arr) { // $FlowFixMe[unclear-type] - Refactor away this function
87
- if (arr[0] && Array.isArray(arr[1])) {
88
- // $FlowFixMe[reassign-const] - Refactor away this function
89
- arr = arr.map((el) => [
90
- el[0], // $FlowFixMe[unclear-type] - Refactor away this function
91
- this._mapFromArrayRecursive(el[1]), // $FlowFixMe[unclear-type] - Refactor away this function
92
- ]);
93
- }
94
-
95
- return new Map(arr);
96
- }
97
-
98
74
  constructor(raw) {
99
75
  this._raw = raw;
100
76
  }
@@ -129,7 +105,8 @@ class ModuleMap {
129
105
  return mockPath != null
130
106
  ? fastPath.resolve(this._raw.rootDir, mockPath)
131
107
  : null;
132
- }
108
+ } // FIXME: This is only used by Meta-internal validation and should be
109
+ // removed or replaced with a less leaky API.
133
110
 
134
111
  getRawModuleMap() {
135
112
  return {
@@ -139,30 +116,6 @@ class ModuleMap {
139
116
  rootDir: this._raw.rootDir,
140
117
  };
141
118
  }
142
-
143
- toJSON() {
144
- if (!this._json) {
145
- this._json = {
146
- duplicates: ModuleMap._mapToArrayRecursive(this._raw.duplicates),
147
- map: Array.from(this._raw.map),
148
- mocks: Array.from(this._raw.mocks),
149
- rootDir: this._raw.rootDir,
150
- };
151
- }
152
-
153
- return this._json;
154
- }
155
-
156
- static fromJSON(serializableModuleMap) {
157
- return new ModuleMap({
158
- duplicates: ModuleMap._mapFromArrayRecursive(
159
- serializableModuleMap.duplicates
160
- ),
161
- map: new Map(serializableModuleMap.map),
162
- mocks: new Map(serializableModuleMap.mocks),
163
- rootDir: serializableModuleMap.rootDir,
164
- });
165
- }
166
119
  /**
167
120
  * When looking up a module's data, we walk through each eligible platform for
168
121
  * the query. For each platform, we want to check if there are known
@@ -229,7 +182,7 @@ class ModuleMap {
229
182
  duplicates.set(duplicatePath, type);
230
183
  }
231
184
 
232
- throw new DuplicateHasteCandidatesError(
185
+ throw new _DuplicateHasteCandidatesError.DuplicateHasteCandidatesError(
233
186
  name,
234
187
  platform,
235
188
  supportsNativePlatform,
@@ -248,49 +201,3 @@ class ModuleMap {
248
201
  }
249
202
 
250
203
  exports.default = ModuleMap;
251
-
252
- class DuplicateHasteCandidatesError extends Error {
253
- constructor(name, platform, supportsNativePlatform, duplicatesSet) {
254
- const platformMessage = getPlatformMessage(platform);
255
- super(
256
- `The name \`${name}\` was looked up in the Haste module map. It ` +
257
- "cannot be resolved, because there exists several different " +
258
- "files, or packages, that provide a module for " +
259
- `that particular name and platform. ${platformMessage} You must ` +
260
- "delete or exclude files until there remains only one of these:\n\n" +
261
- Array.from(duplicatesSet)
262
- .map(
263
- ([dupFilePath, dupFileType]) =>
264
- ` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n`
265
- )
266
- .sort()
267
- .join("")
268
- );
269
- this.hasteName = name;
270
- this.platform = platform;
271
- this.supportsNativePlatform = supportsNativePlatform;
272
- this.duplicatesSet = duplicatesSet;
273
- }
274
- }
275
-
276
- function getPlatformMessage(platform) {
277
- if (platform === _constants.default.GENERIC_PLATFORM) {
278
- return "The platform is generic (no extension).";
279
- }
280
-
281
- return `The platform extension is \`${platform}\`.`;
282
- }
283
-
284
- function getTypeMessage(type) {
285
- switch (type) {
286
- case _constants.default.MODULE:
287
- return "module";
288
-
289
- case _constants.default.PACKAGE:
290
- return "package";
291
- }
292
-
293
- return "unknown";
294
- }
295
-
296
- ModuleMap.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
@@ -16,48 +16,17 @@ import type {
16
16
  ModuleMetaData,
17
17
  Path,
18
18
  RawModuleMap,
19
- SerializableModuleMap,
20
19
  } from './flow-types';
21
20
 
22
21
  import H from './constants';
22
+ import {DuplicateHasteCandidatesError} from './lib/DuplicateHasteCandidatesError';
23
23
  import * as fastPath from './lib/fast_path';
24
24
 
25
25
  const EMPTY_OBJ: {[string]: ModuleMetaData} = {};
26
26
  const EMPTY_MAP = new Map<'g' | 'native' | string, ?DuplicatesSet>();
27
27
 
28
- export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
29
- static DuplicateHasteCandidatesError: Class<DuplicateHasteCandidatesError>;
28
+ export default class ModuleMap implements IModuleMap {
30
29
  +_raw: RawModuleMap;
31
- _json: ?SerializableModuleMap;
32
-
33
- // $FlowFixMe[unclear-type] - Refactor away this function
34
- static _mapToArrayRecursive(map: Map<string, any>): Array<[string, any]> {
35
- let arr = Array.from(map);
36
- if (arr[0] && arr[0][1] instanceof Map) {
37
- arr = arr.map(
38
- // $FlowFixMe[unclear-type] - Refactor away this function
39
- el => ([el[0], this._mapToArrayRecursive(el[1])]: [string, any]),
40
- );
41
- }
42
- return arr;
43
- }
44
-
45
- static _mapFromArrayRecursive(
46
- // $FlowFixMe[unclear-type] - Refactor away this function
47
- arr: $ReadOnlyArray<[string, any]>,
48
- // $FlowFixMe[unclear-type] - Refactor away this function
49
- ): Map<string, any> {
50
- if (arr[0] && Array.isArray(arr[1])) {
51
- // $FlowFixMe[reassign-const] - Refactor away this function
52
- arr = (arr.map(el => [
53
- el[0],
54
- // $FlowFixMe[unclear-type] - Refactor away this function
55
- this._mapFromArrayRecursive((el[1]: Array<[string, any]>)),
56
- // $FlowFixMe[unclear-type] - Refactor away this function
57
- ]): Array<[string, any]>);
58
- }
59
- return new Map(arr);
60
- }
61
30
 
62
31
  constructor(raw: RawModuleMap) {
63
32
  this._raw = raw;
@@ -97,6 +66,8 @@ export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
97
66
  : null;
98
67
  }
99
68
 
69
+ // FIXME: This is only used by Meta-internal validation and should be
70
+ // removed or replaced with a less leaky API.
100
71
  getRawModuleMap(): RawModuleMap {
101
72
  return {
102
73
  duplicates: this._raw.duplicates,
@@ -106,31 +77,6 @@ export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
106
77
  };
107
78
  }
108
79
 
109
- toJSON(): SerializableModuleMap {
110
- if (!this._json) {
111
- this._json = {
112
- duplicates: (ModuleMap._mapToArrayRecursive(
113
- this._raw.duplicates,
114
- ): SerializableModuleMap['duplicates']),
115
- map: Array.from(this._raw.map),
116
- mocks: Array.from(this._raw.mocks),
117
- rootDir: this._raw.rootDir,
118
- };
119
- }
120
- return this._json;
121
- }
122
-
123
- static fromJSON(serializableModuleMap: SerializableModuleMap): ModuleMap {
124
- return new ModuleMap({
125
- duplicates: (ModuleMap._mapFromArrayRecursive(
126
- serializableModuleMap.duplicates,
127
- ): RawModuleMap['duplicates']),
128
- map: new Map(serializableModuleMap.map),
129
- mocks: new Map(serializableModuleMap.mocks),
130
- rootDir: serializableModuleMap.rootDir,
131
- });
132
- }
133
-
134
80
  /**
135
81
  * When looking up a module's data, we walk through each eligible platform for
136
82
  * the query. For each platform, we want to check if there are known
@@ -215,56 +161,3 @@ export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
215
161
  });
216
162
  }
217
163
  }
218
-
219
- class DuplicateHasteCandidatesError extends Error {
220
- hasteName: string;
221
- platform: string | null;
222
- supportsNativePlatform: boolean;
223
- duplicatesSet: DuplicatesSet;
224
-
225
- constructor(
226
- name: string,
227
- platform: string,
228
- supportsNativePlatform: boolean,
229
- duplicatesSet: DuplicatesSet,
230
- ) {
231
- const platformMessage = getPlatformMessage(platform);
232
- super(
233
- `The name \`${name}\` was looked up in the Haste module map. It ` +
234
- 'cannot be resolved, because there exists several different ' +
235
- 'files, or packages, that provide a module for ' +
236
- `that particular name and platform. ${platformMessage} You must ` +
237
- 'delete or exclude files until there remains only one of these:\n\n' +
238
- Array.from(duplicatesSet)
239
- .map(
240
- ([dupFilePath, dupFileType]) =>
241
- ` * \`${dupFilePath}\` (${getTypeMessage(dupFileType)})\n`,
242
- )
243
- .sort()
244
- .join(''),
245
- );
246
- this.hasteName = name;
247
- this.platform = platform;
248
- this.supportsNativePlatform = supportsNativePlatform;
249
- this.duplicatesSet = duplicatesSet;
250
- }
251
- }
252
-
253
- function getPlatformMessage(platform: string) {
254
- if (platform === H.GENERIC_PLATFORM) {
255
- return 'The platform is generic (no extension).';
256
- }
257
- return `The platform extension is \`${platform}\`.`;
258
- }
259
-
260
- function getTypeMessage(type: number) {
261
- switch (type) {
262
- case H.MODULE:
263
- return 'module';
264
- case H.PACKAGE:
265
- return 'package';
266
- }
267
- return 'unknown';
268
- }
269
-
270
- ModuleMap.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
package/src/Watcher.js CHANGED
@@ -25,6 +25,8 @@ var fs = _interopRequireWildcard(require("fs"));
25
25
 
26
26
  var _common = require("./watchers/common");
27
27
 
28
+ var _events = _interopRequireDefault(require("events"));
29
+
28
30
  var _perf_hooks = require("perf_hooks");
29
31
 
30
32
  var _nullthrows = _interopRequireDefault(require("nullthrows"));
@@ -90,12 +92,13 @@ const debug = require("debug")("Metro:Watcher");
90
92
  const MAX_WAIT_TIME = 240000;
91
93
  let nextInstanceId = 0;
92
94
 
93
- class Watcher {
95
+ class Watcher extends _events.default {
94
96
  _backends = [];
95
97
  _nextHealthCheckId = 0;
96
98
  _pendingHealthChecks = new Map();
97
99
 
98
100
  constructor(options) {
101
+ super();
99
102
  this._options = options;
100
103
  this._instanceId = nextInstanceId++;
101
104
  }
@@ -114,21 +117,26 @@ class Watcher {
114
117
  path.basename(filePath).startsWith(this._options.healthCheckFilePrefix);
115
118
 
116
119
  const crawl = options.useWatchman ? _watchman.default : _node.default;
120
+ let crawler = crawl === _watchman.default ? "watchman" : "node";
117
121
  const crawlerOptions = {
118
122
  abortSignal: options.abortSignal,
119
123
  computeSha1: options.computeSha1,
120
- data: options.initialData,
121
124
  enableSymlinks: options.enableSymlinks,
122
125
  extensions: options.extensions,
123
126
  forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
124
127
  ignore,
128
+ onStatus: (status) => {
129
+ this.emit("status", status);
130
+ },
125
131
  perfLogger: options.perfLogger,
132
+ previousState: options.previousState,
126
133
  rootDir: options.rootDir,
127
134
  roots: options.roots,
128
135
  };
129
136
 
130
137
  const retry = (error) => {
131
138
  if (crawl === _watchman.default) {
139
+ crawler = "node";
132
140
  options.console.warn(
133
141
  "metro-file-map: Watchman crawl failed. Retrying once with node " +
134
142
  "crawler.\n" +
@@ -150,16 +158,30 @@ class Watcher {
150
158
  throw error;
151
159
  };
152
160
 
153
- const logEnd = (result) => {
154
- var _this$_options$perfLo2;
155
-
161
+ const logEnd = (delta) => {
162
+ var _delta$clocks$size, _delta$clocks, _this$_options$perfLo2;
163
+
164
+ debug(
165
+ 'Crawler "%s" returned %d added/modified, %d removed, %d clock(s).',
166
+ crawler,
167
+ delta.changedFiles.size,
168
+ delta.removedFiles.size,
169
+ (_delta$clocks$size =
170
+ (_delta$clocks = delta.clocks) === null || _delta$clocks === void 0
171
+ ? void 0
172
+ : _delta$clocks.size) !== null && _delta$clocks$size !== void 0
173
+ ? _delta$clocks$size
174
+ : 0
175
+ );
156
176
  (_this$_options$perfLo2 = this._options.perfLogger) === null ||
157
177
  _this$_options$perfLo2 === void 0
158
178
  ? void 0
159
179
  : _this$_options$perfLo2.point("crawl_end");
160
- return result;
180
+ return delta;
161
181
  };
162
182
 
183
+ debug('Beginning crawl with "%s".', crawler);
184
+
163
185
  try {
164
186
  return crawl(crawlerOptions).catch(retry).then(logEnd);
165
187
  } catch (error) {
@@ -12,9 +12,9 @@ import type {
12
12
  Console,
13
13
  CrawlerOptions,
14
14
  FileData,
15
- InternalData,
16
15
  Path,
17
16
  PerfLogger,
17
+ WatchmanClocks,
18
18
  } from './flow-types';
19
19
  import type {WatcherOptions as WatcherBackendOptions} from './watchers/common';
20
20
  import type {Stats} from 'fs';
@@ -28,6 +28,7 @@ import NodeWatcher from './watchers/NodeWatcher';
28
28
  import * as path from 'path';
29
29
  import * as fs from 'fs';
30
30
  import {ADD_EVENT, CHANGE_EVENT} from './watchers/common';
31
+ import EventEmitter from 'events';
31
32
  import {performance} from 'perf_hooks';
32
33
  import nullthrows from 'nullthrows';
33
34
 
@@ -35,6 +36,12 @@ const debug = require('debug')('Metro:Watcher');
35
36
 
36
37
  const MAX_WAIT_TIME = 240000;
37
38
 
39
+ type CrawlResult = {
40
+ changedFiles: FileData,
41
+ clocks?: WatchmanClocks,
42
+ removedFiles: FileData,
43
+ };
44
+
38
45
  type WatcherOptions = {
39
46
  abortSignal: AbortSignal,
40
47
  computeSha1: boolean,
@@ -45,7 +52,7 @@ type WatcherOptions = {
45
52
  healthCheckFilePrefix: string,
46
53
  ignore: string => boolean,
47
54
  ignorePattern: RegExp,
48
- initialData: InternalData,
55
+ previousState: CrawlerOptions['previousState'],
49
56
  perfLogger: ?PerfLogger,
50
57
  roots: $ReadOnlyArray<string>,
51
58
  rootDir: string,
@@ -66,7 +73,7 @@ export type HealthCheckResult =
66
73
  | {type: 'success', timeout: number, timeElapsed: number, watcher: ?string}
67
74
  | {type: 'timeout', timeout: number, watcher: ?string, pauseReason: ?string};
68
75
 
69
- export class Watcher {
76
+ export class Watcher extends EventEmitter {
70
77
  _options: WatcherOptions;
71
78
  _backends: $ReadOnlyArray<WatcherBackend> = [];
72
79
  _instanceId: number;
@@ -76,18 +83,12 @@ export class Watcher {
76
83
  _activeWatcher: ?string;
77
84
 
78
85
  constructor(options: WatcherOptions) {
86
+ super();
79
87
  this._options = options;
80
88
  this._instanceId = nextInstanceId++;
81
89
  }
82
90
 
83
- async crawl(): Promise<?(
84
- | Promise<{
85
- changedFiles?: FileData,
86
- hasteMap: InternalData,
87
- removedFiles: FileData,
88
- }>
89
- | {changedFiles?: FileData, hasteMap: InternalData, removedFiles: FileData}
90
- )> {
91
+ async crawl(): Promise<CrawlResult> {
91
92
  this._options.perfLogger?.point('crawl_start');
92
93
 
93
94
  const options = this._options;
@@ -95,21 +96,26 @@ export class Watcher {
95
96
  options.ignore(filePath) ||
96
97
  path.basename(filePath).startsWith(this._options.healthCheckFilePrefix);
97
98
  const crawl = options.useWatchman ? watchmanCrawl : nodeCrawl;
99
+ let crawler = crawl === watchmanCrawl ? 'watchman' : 'node';
98
100
  const crawlerOptions: CrawlerOptions = {
99
101
  abortSignal: options.abortSignal,
100
102
  computeSha1: options.computeSha1,
101
- data: options.initialData,
102
103
  enableSymlinks: options.enableSymlinks,
103
104
  extensions: options.extensions,
104
105
  forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
105
106
  ignore,
107
+ onStatus: status => {
108
+ this.emit('status', status);
109
+ },
106
110
  perfLogger: options.perfLogger,
111
+ previousState: options.previousState,
107
112
  rootDir: options.rootDir,
108
113
  roots: options.roots,
109
114
  };
110
115
 
111
116
  const retry = (error: Error) => {
112
117
  if (crawl === watchmanCrawl) {
118
+ crawler = 'node';
113
119
  options.console.warn(
114
120
  'metro-file-map: Watchman crawl failed. Retrying once with node ' +
115
121
  'crawler.\n' +
@@ -131,11 +137,19 @@ export class Watcher {
131
137
  throw error;
132
138
  };
133
139
 
134
- const logEnd = <T>(result: T): T => {
140
+ const logEnd = (delta: CrawlResult): CrawlResult => {
141
+ debug(
142
+ 'Crawler "%s" returned %d added/modified, %d removed, %d clock(s).',
143
+ crawler,
144
+ delta.changedFiles.size,
145
+ delta.removedFiles.size,
146
+ delta.clocks?.size ?? 0,
147
+ );
135
148
  this._options.perfLogger?.point('crawl_end');
136
- return result;
149
+ return delta;
137
150
  };
138
151
 
152
+ debug('Beginning crawl with "%s".', crawler);
139
153
  try {
140
154
  return crawl(crawlerOptions).catch(retry).then(logEnd);
141
155
  } catch (error) {
@@ -246,7 +246,7 @@ function findNative(roots, extensions, ignore, enableSymlinks, callback) {
246
246
 
247
247
  module.exports = async function nodeCrawl(options) {
248
248
  const {
249
- data,
249
+ previousState,
250
250
  extensions,
251
251
  forceNodeFilesystemAPI,
252
252
  ignore,
@@ -261,28 +261,29 @@ module.exports = async function nodeCrawl(options) {
261
261
  const useNativeFind = await hasNativeFindSupport(forceNodeFilesystemAPI);
262
262
  return new Promise((resolve) => {
263
263
  const callback = (list) => {
264
- const files = new Map();
265
- const removedFiles = new Map(data.files);
266
- list.forEach((fileData) => {
264
+ const changedFiles = new Map();
265
+ const removedFiles = new Map(previousState.files);
266
+
267
+ for (const fileData of list) {
267
268
  const [filePath, mtime, size] = fileData;
268
269
  const relativeFilePath = fastPath.relative(rootDir, filePath);
269
- const existingFile = data.files.get(relativeFilePath);
270
+ const existingFile = previousState.files.get(relativeFilePath);
271
+ removedFiles.delete(relativeFilePath);
270
272
 
271
- if (existingFile && existingFile[_constants.default.MTIME] === mtime) {
272
- files.set(relativeFilePath, existingFile);
273
- } else {
273
+ if (
274
+ existingFile == null ||
275
+ existingFile[_constants.default.MTIME] !== mtime
276
+ ) {
274
277
  // See ../constants.js; SHA-1 will always be null and fulfilled later.
275
- files.set(relativeFilePath, ["", mtime, size, 0, "", null]);
278
+ changedFiles.set(relativeFilePath, ["", mtime, size, 0, "", null]);
276
279
  }
280
+ }
277
281
 
278
- removedFiles.delete(relativeFilePath);
279
- });
280
- data.files = files;
281
282
  perfLogger === null || perfLogger === void 0
282
283
  ? void 0
283
284
  : perfLogger.point("nodeCrawl_end");
284
285
  resolve({
285
- hasteMap: data,
286
+ changedFiles,
286
287
  removedFiles,
287
288
  });
288
289
  };
@@ -10,12 +10,7 @@
10
10
  */
11
11
 
12
12
  import type {Path, FileMetaData} from '../flow-types';
13
- import type {
14
- CrawlerOptions,
15
- FileData,
16
- IgnoreMatcher,
17
- InternalData,
18
- } from '../flow-types';
13
+ import type {CrawlerOptions, FileData, IgnoreMatcher} from '../flow-types';
19
14
 
20
15
  import H from '../constants';
21
16
  import * as fastPath from '../lib/fast_path';
@@ -208,10 +203,10 @@ function findNative(
208
203
 
209
204
  module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
210
205
  removedFiles: FileData,
211
- hasteMap: InternalData,
206
+ changedFiles: FileData,
212
207
  }> {
213
208
  const {
214
- data,
209
+ previousState,
215
210
  extensions,
216
211
  forceNodeFilesystemAPI,
217
212
  ignore,
@@ -225,25 +220,22 @@ module.exports = async function nodeCrawl(options: CrawlerOptions): Promise<{
225
220
 
226
221
  return new Promise(resolve => {
227
222
  const callback = (list: Result) => {
228
- const files = new Map<Path, FileMetaData>();
229
- const removedFiles = new Map(data.files);
230
- list.forEach(fileData => {
223
+ const changedFiles = new Map<Path, FileMetaData>();
224
+ const removedFiles = new Map(previousState.files);
225
+ for (const fileData of list) {
231
226
  const [filePath, mtime, size] = fileData;
232
227
  const relativeFilePath = fastPath.relative(rootDir, filePath);
233
- const existingFile = data.files.get(relativeFilePath);
234
- if (existingFile && existingFile[H.MTIME] === mtime) {
235
- files.set(relativeFilePath, existingFile);
236
- } else {
228
+ const existingFile = previousState.files.get(relativeFilePath);
229
+ removedFiles.delete(relativeFilePath);
230
+ if (existingFile == null || existingFile[H.MTIME] !== mtime) {
237
231
  // See ../constants.js; SHA-1 will always be null and fulfilled later.
238
- files.set(relativeFilePath, ['', mtime, size, 0, '', null]);
232
+ changedFiles.set(relativeFilePath, ['', mtime, size, 0, '', null]);
239
233
  }
240
- removedFiles.delete(relativeFilePath);
241
- });
242
- data.files = files;
234
+ }
243
235
 
244
236
  perfLogger?.point('nodeCrawl_end');
245
237
  resolve({
246
- hasteMap: data,
238
+ changedFiles,
247
239
  removedFiles,
248
240
  });
249
241
  };