metro-file-map 0.71.3 → 0.72.2

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.71.3",
3
+ "version": "0.72.2",
4
4
  "description": "[Experimental] - 🚇 File crawling, watching and mapping for Metro",
5
5
  "main": "src/index.js",
6
6
  "repository": {
package/src/HasteFS.js CHANGED
@@ -9,6 +9,8 @@ var _constants = _interopRequireDefault(require("./constants"));
9
9
 
10
10
  var fastPath = _interopRequireWildcard(require("./lib/fast_path"));
11
11
 
12
+ var path = _interopRequireWildcard(require("path"));
13
+
12
14
  var _jestUtil = require("jest-util");
13
15
 
14
16
  function _getRequireWildcardCache(nodeInterop) {
@@ -66,7 +68,6 @@ function _interopRequireDefault(obj) {
66
68
  * @format
67
69
  *
68
70
  */
69
- // $FlowFixMe[untyped-import] - jest-util
70
71
  class HasteFS {
71
72
  constructor({ rootDir, files }) {
72
73
  this._rootDir = rootDir;
@@ -141,6 +142,42 @@ class HasteFS {
141
142
 
142
143
  return files;
143
144
  }
145
+ /**
146
+ * Given a search context, return a list of file paths matching the query.
147
+ * The query matches against normalized paths which start with `./`,
148
+ * for example: `a/b.js` -> `./a/b.js`
149
+ */
150
+
151
+ matchFilesWithContext(root, context) {
152
+ const files = [];
153
+ const prefix = "./";
154
+
155
+ for (const file of this.getAbsoluteFileIterator()) {
156
+ const filePath = fastPath.relative(root, file);
157
+ const isUnderRoot = filePath && !filePath.startsWith(".."); // Ignore everything outside of the provided `root`.
158
+
159
+ if (!isUnderRoot) {
160
+ continue;
161
+ } // Prevent searching in child directories during a non-recursive search.
162
+
163
+ if (!context.recursive && filePath.includes(path.sep)) {
164
+ continue;
165
+ }
166
+
167
+ if (
168
+ context.filter.test(
169
+ // NOTE(EvanBacon): Ensure files start with `./` for matching purposes
170
+ // this ensures packages work across Metro and Webpack (ex: Storybook for React DOM / React Native).
171
+ // `a/b.js` -> `./a/b.js`
172
+ prefix + filePath.replace(/\\/g, "/")
173
+ )
174
+ ) {
175
+ files.push(file);
176
+ }
177
+ }
178
+
179
+ return files;
180
+ }
144
181
 
145
182
  matchFilesWithGlob(globs, root) {
146
183
  const files = new Set();
@@ -8,16 +8,13 @@
8
8
  * @flow strict-local
9
9
  */
10
10
 
11
- import type {FileData, Path} from './flow-types';
11
+ import type {FileData, FileMetaData, Glob, Path} from './flow-types';
12
12
 
13
13
  import H from './constants';
14
14
  import * as fastPath from './lib/fast_path';
15
- // $FlowFixMe[untyped-import] - jest-util
15
+ import * as path from 'path';
16
16
  import {globsToMatcher, replacePathSepForGlob} from 'jest-util';
17
17
 
18
- // $FlowFixMe[unclear-type] - Check TS Config.Glob
19
- type Glob = any;
20
-
21
18
  export default class HasteFS {
22
19
  +_rootDir: Path;
23
20
  +_files: FileData;
@@ -84,6 +81,52 @@ export default class HasteFS {
84
81
  return files;
85
82
  }
86
83
 
84
+ /**
85
+ * Given a search context, return a list of file paths matching the query.
86
+ * The query matches against normalized paths which start with `./`,
87
+ * for example: `a/b.js` -> `./a/b.js`
88
+ */
89
+ matchFilesWithContext(
90
+ root: Path,
91
+ context: $ReadOnly<{
92
+ /* Should search for files recursively. */
93
+ recursive: boolean,
94
+ /* Filter relative paths against a pattern. */
95
+ filter: RegExp,
96
+ }>,
97
+ ): Array<Path> {
98
+ const files = [];
99
+ const prefix = './';
100
+
101
+ for (const file of this.getAbsoluteFileIterator()) {
102
+ const filePath = fastPath.relative(root, file);
103
+
104
+ const isUnderRoot = filePath && !filePath.startsWith('..');
105
+ // Ignore everything outside of the provided `root`.
106
+ if (!isUnderRoot) {
107
+ continue;
108
+ }
109
+
110
+ // Prevent searching in child directories during a non-recursive search.
111
+ if (!context.recursive && filePath.includes(path.sep)) {
112
+ continue;
113
+ }
114
+
115
+ if (
116
+ context.filter.test(
117
+ // NOTE(EvanBacon): Ensure files start with `./` for matching purposes
118
+ // this ensures packages work across Metro and Webpack (ex: Storybook for React DOM / React Native).
119
+ // `a/b.js` -> `./a/b.js`
120
+ prefix + filePath.replace(/\\/g, '/'),
121
+ )
122
+ ) {
123
+ files.push(file);
124
+ }
125
+ }
126
+
127
+ return files;
128
+ }
129
+
87
130
  matchFilesWithGlob(globs: $ReadOnlyArray<Glob>, root: ?Path): Set<Path> {
88
131
  const files = new Set<string>();
89
132
  const matcher = globsToMatcher(globs);
@@ -97,7 +140,7 @@ export default class HasteFS {
97
140
  return files;
98
141
  }
99
142
 
100
- _getFileData(file: Path) {
143
+ _getFileData(file: Path): void | FileMetaData {
101
144
  const relativePath = fastPath.relative(this._rootDir, file);
102
145
  return this._files.get(relativePath);
103
146
  }
@@ -184,7 +184,7 @@ export default class ModuleMap implements IModuleMap<SerializableModuleMap> {
184
184
  platform: string,
185
185
  supportsNativePlatform: boolean,
186
186
  relativePathSet: ?DuplicatesSet,
187
- ) {
187
+ ): void {
188
188
  if (relativePathSet == null) {
189
189
  return;
190
190
  }
@@ -30,7 +30,6 @@ function _interopRequireDefault(obj) {
30
30
  *
31
31
  * @format
32
32
  */
33
- // $FlowFixMe[missing-export] - serialize and deserialize missing typedefs
34
33
  const DEFAULT_PREFIX = "metro-file-map";
35
34
  const DEFAULT_DIRECTORY = (0, _os.tmpdir)();
36
35
 
@@ -19,7 +19,6 @@ import rootRelativeCacheKeys from '../lib/rootRelativeCacheKeys';
19
19
  import {readFileSync, writeFileSync} from 'graceful-fs';
20
20
  import {tmpdir} from 'os';
21
21
  import path from 'path';
22
- // $FlowFixMe[missing-export] - serialize and deserialize missing typedefs
23
22
  import {deserialize, serialize} from 'v8';
24
23
 
25
24
  type DiskCacheConfig = {
@@ -91,8 +91,12 @@ async function capabilityCheck(client, caps) {
91
91
  // @ts-expect-error: incorrectly typed
92
92
  caps,
93
93
  (error, response) => {
94
- if (error) {
95
- reject(error);
94
+ if (error != null || response == null) {
95
+ reject(
96
+ error !== null && error !== void 0
97
+ ? error
98
+ : new Error("capabilityCheck: Response missing")
99
+ );
96
100
  } else {
97
101
  resolve(response);
98
102
  }
@@ -279,7 +283,7 @@ module.exports = async function watchmanCrawl(options) {
279
283
  * directories. Therefore `glob` < `suffix`.
280
284
  */
281
285
 
282
- let queryGenerator = undefined;
286
+ let queryGenerator;
283
287
 
284
288
  if (since != null) {
285
289
  // Use the `since` generator and filter by both path and extension.
@@ -352,6 +356,7 @@ module.exports = async function watchmanCrawl(options) {
352
356
  const changedFiles = new Map();
353
357
  let results;
354
358
  let isFresh = false;
359
+ let queryError;
355
360
 
356
361
  try {
357
362
  const watchmanRoots = await getWatchmanRoots(roots);
@@ -365,15 +370,56 @@ module.exports = async function watchmanCrawl(options) {
365
370
  }
366
371
 
367
372
  results = watchmanFileResults.results;
368
- } finally {
369
- client.end();
373
+ } catch (e) {
374
+ queryError = e;
370
375
  }
371
376
 
372
- if (clientError) {
377
+ client.end();
378
+
379
+ if (results == null) {
380
+ var _ref, _queryError;
381
+
382
+ if (clientError) {
383
+ var _clientError$message;
384
+
385
+ perfLogger === null || perfLogger === void 0
386
+ ? void 0
387
+ : perfLogger.annotate({
388
+ string: {
389
+ "watchmanCrawl/client_error":
390
+ (_clientError$message = clientError.message) !== null &&
391
+ _clientError$message !== void 0
392
+ ? _clientError$message
393
+ : "[message missing]",
394
+ },
395
+ });
396
+ }
397
+
398
+ if (queryError) {
399
+ var _queryError$message;
400
+
401
+ perfLogger === null || perfLogger === void 0
402
+ ? void 0
403
+ : perfLogger.annotate({
404
+ string: {
405
+ "watchmanCrawl/query_error":
406
+ (_queryError$message = queryError.message) !== null &&
407
+ _queryError$message !== void 0
408
+ ? _queryError$message
409
+ : "[message missing]",
410
+ },
411
+ });
412
+ }
413
+
373
414
  perfLogger === null || perfLogger === void 0
374
415
  ? void 0
375
416
  : perfLogger.point("watchmanCrawl_end");
376
- throw clientError;
417
+ throw (_ref =
418
+ (_queryError = queryError) !== null && _queryError !== void 0
419
+ ? _queryError
420
+ : clientError) !== null && _ref !== void 0
421
+ ? _ref
422
+ : new Error("Watchman file results missing");
377
423
  }
378
424
 
379
425
  perfLogger === null || perfLogger === void 0
@@ -91,8 +91,8 @@ async function capabilityCheck(
91
91
  // @ts-expect-error: incorrectly typed
92
92
  caps,
93
93
  (error, response) => {
94
- if (error) {
95
- reject(error);
94
+ if (error != null || response == null) {
95
+ reject(error ?? new Error('capabilityCheck: Response missing'));
96
96
  } else {
97
97
  resolve(response);
98
98
  }
@@ -269,11 +269,7 @@ module.exports = async function watchmanCrawl(
269
269
  * repo but Haste map projects are focused on a handful of
270
270
  * directories. Therefore `glob` < `suffix`.
271
271
  */
272
- let queryGenerator: ?(
273
- | $TEMPORARY$string<'glob'>
274
- | $TEMPORARY$string<'since'>
275
- | $TEMPORARY$string<'suffix'>
276
- ) = undefined;
272
+ let queryGenerator: ?string;
277
273
  if (since != null) {
278
274
  // Use the `since` generator and filter by both path and extension.
279
275
  query.since = since;
@@ -340,6 +336,7 @@ module.exports = async function watchmanCrawl(
340
336
  const changedFiles = new Map();
341
337
  let results: Map<string, WatchmanQueryResponse>;
342
338
  let isFresh = false;
339
+ let queryError: ?Error;
343
340
  try {
344
341
  const watchmanRoots = await getWatchmanRoots(roots);
345
342
  const watchmanFileResults = await queryWatchmanForDirs(watchmanRoots);
@@ -353,13 +350,32 @@ module.exports = async function watchmanCrawl(
353
350
  }
354
351
 
355
352
  results = watchmanFileResults.results;
356
- } finally {
357
- client.end();
353
+ } catch (e) {
354
+ queryError = e;
358
355
  }
359
-
360
- if (clientError) {
356
+ client.end();
357
+
358
+ if (results == null) {
359
+ if (clientError) {
360
+ perfLogger?.annotate({
361
+ string: {
362
+ 'watchmanCrawl/client_error':
363
+ clientError.message ?? '[message missing]',
364
+ },
365
+ });
366
+ }
367
+ if (queryError) {
368
+ perfLogger?.annotate({
369
+ string: {
370
+ 'watchmanCrawl/query_error':
371
+ queryError.message ?? '[message missing]',
372
+ },
373
+ });
374
+ }
361
375
  perfLogger?.point('watchmanCrawl_end');
362
- throw clientError;
376
+ throw (
377
+ queryError ?? clientError ?? new Error('Watchman file results missing')
378
+ );
363
379
  }
364
380
 
365
381
  perfLogger?.point('watchmanCrawl/processResults_start');
@@ -126,6 +126,8 @@ export type FileMetaData = [
126
126
  /* sha1 */ ?string,
127
127
  ];
128
128
 
129
+ export type Glob = string;
130
+
129
131
  export interface IModuleMap<S = SerializableModuleMap> {
130
132
  getModule(
131
133
  name: string,
package/src/index.js CHANGED
@@ -144,7 +144,7 @@ exports.DuplicateHasteCandidatesError = DuplicateHasteCandidatesError;
144
144
  // This should be bumped whenever a code change to `metro-file-map` itself
145
145
  // would cause a change to the cache data structure and/or content (for a given
146
146
  // filesystem state and build parameters).
147
- const CACHE_BREAKER = "1";
147
+ const CACHE_BREAKER = "2";
148
148
  const CHANGE_INTERVAL = 30;
149
149
  const MAX_WAIT_TIME = 240000;
150
150
  const NODE_MODULES = path.sep + "node_modules" + path.sep;
@@ -246,7 +246,7 @@ const canUseWatchman = (() => {
246
246
  class HasteMap extends _events.default {
247
247
  static create(options) {
248
248
  return new HasteMap(options);
249
- }
249
+ } // $FlowFixMe[missing-local-annot]
250
250
 
251
251
  constructor(options) {
252
252
  var _options$dependencyEx, _options$watchmanDefe, _this$_options$perfLo;
@@ -988,7 +988,12 @@ class HasteMap extends _events.default {
988
988
  const createWatcher = (root) => {
989
989
  const watcher = new WatcherImpl(root, {
990
990
  dot: true,
991
- glob: extensions.map((extension) => "**/*." + extension),
991
+ glob: [
992
+ // Ensure we always include package.json files, which are crucial for
993
+ /// module resolution.
994
+ "**/package.json",
995
+ ...extensions.map((extension) => "**/*." + extension),
996
+ ],
992
997
  ignored: ignorePattern,
993
998
  watchmanDeferStates: this._options.watchmanDeferStates,
994
999
  });
package/src/index.js.flow CHANGED
@@ -135,7 +135,7 @@ export type {
135
135
  // This should be bumped whenever a code change to `metro-file-map` itself
136
136
  // would cause a change to the cache data structure and/or content (for a given
137
137
  // filesystem state and build parameters).
138
- const CACHE_BREAKER = '1';
138
+ const CACHE_BREAKER = '2';
139
139
 
140
140
  const CHANGE_INTERVAL = 30;
141
141
  const MAX_WAIT_TIME = 240000;
@@ -245,6 +245,7 @@ export default class HasteMap extends EventEmitter {
245
245
  return new HasteMap(options);
246
246
  }
247
247
 
248
+ // $FlowFixMe[missing-local-annot]
248
249
  constructor(options: InputOptions) {
249
250
  if (options.perfLogger) {
250
251
  options.perfLogger?.point('constructor_start');
@@ -778,7 +779,14 @@ export default class HasteMap extends EventEmitter {
778
779
  return this._worker;
779
780
  }
780
781
 
781
- _crawl(hasteMap: InternalData) {
782
+ _crawl(hasteMap: InternalData): Promise<?(
783
+ | Promise<{
784
+ changedFiles?: FileData,
785
+ hasteMap: InternalData,
786
+ removedFiles: FileData,
787
+ }>
788
+ | {changedFiles?: FileData, hasteMap: InternalData, removedFiles: FileData}
789
+ )> {
782
790
  this._options.perfLogger?.point('crawl_start');
783
791
  const options = this._options;
784
792
  const ignore = (filePath: string) => this._ignore(filePath);
@@ -867,7 +875,12 @@ export default class HasteMap extends EventEmitter {
867
875
  const createWatcher = (root: Path): Promise<Watcher> => {
868
876
  const watcher = new WatcherImpl(root, {
869
877
  dot: true,
870
- glob: extensions.map(extension => '**/*.' + extension),
878
+ glob: [
879
+ // Ensure we always include package.json files, which are crucial for
880
+ /// module resolution.
881
+ '**/package.json',
882
+ ...extensions.map(extension => '**/*.' + extension),
883
+ ],
871
884
  ignored: ignorePattern,
872
885
  watchmanDeferStates: this._options.watchmanDeferStates,
873
886
  });
@@ -123,7 +123,10 @@ class FSEventsWatcher extends _events.default {
123
123
  });
124
124
  }
125
125
 
126
- constructor(dir, opts) {
126
+ constructor(
127
+ dir,
128
+ opts // $FlowFixMe[missing-local-annot]
129
+ ) {
127
130
  if (!fsevents) {
128
131
  throw new Error(
129
132
  "`fsevents` unavailable (this watcher can only be used on Darwin)"
@@ -61,7 +61,8 @@ export default class FSEventsWatcher extends EventEmitter {
61
61
 
62
62
  static _normalizeProxy(
63
63
  callback: (normalizedPath: string, stats: fs.Stats) => void,
64
- ) {
64
+ // $FlowFixMe[cannot-resolve-name]
65
+ ): (filepath: string, stats: Stats) => void {
65
66
  return (filepath: string, stats: fs.Stats): void =>
66
67
  callback(path.normalize(filepath), stats);
67
68
  }
@@ -96,6 +97,7 @@ export default class FSEventsWatcher extends EventEmitter {
96
97
  dot: boolean,
97
98
  ...
98
99
  }>,
100
+ // $FlowFixMe[missing-local-annot]
99
101
  ) {
100
102
  if (!fsevents) {
101
103
  throw new Error(
@@ -149,7 +151,7 @@ export default class FSEventsWatcher extends EventEmitter {
149
151
  }
150
152
  }
151
153
 
152
- _isFileIncluded(relativePath: string) {
154
+ _isFileIncluded(relativePath: string): boolean {
153
155
  if (this.doIgnore(relativePath)) {
154
156
  return false;
155
157
  }