index.js 35 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.DuplicateError = void 0;
  6. Object.defineProperty(exports, 'ModuleMap', {
  7. enumerable: true,
  8. get: function () {
  9. return _ModuleMap.default;
  10. }
  11. });
  12. exports.default = void 0;
  13. function _child_process() {
  14. const data = require('child_process');
  15. _child_process = function () {
  16. return data;
  17. };
  18. return data;
  19. }
  20. function _crypto() {
  21. const data = require('crypto');
  22. _crypto = function () {
  23. return data;
  24. };
  25. return data;
  26. }
  27. function _events() {
  28. const data = require('events');
  29. _events = function () {
  30. return data;
  31. };
  32. return data;
  33. }
  34. function _os() {
  35. const data = require('os');
  36. _os = function () {
  37. return data;
  38. };
  39. return data;
  40. }
  41. function path() {
  42. const data = _interopRequireWildcard(require('path'));
  43. path = function () {
  44. return data;
  45. };
  46. return data;
  47. }
  48. function _jestRegexUtil() {
  49. const data = require('jest-regex-util');
  50. _jestRegexUtil = function () {
  51. return data;
  52. };
  53. return data;
  54. }
  55. function _jestSerializer() {
  56. const data = _interopRequireDefault(require('jest-serializer'));
  57. _jestSerializer = function () {
  58. return data;
  59. };
  60. return data;
  61. }
  62. function _jestWorker() {
  63. const data = require('jest-worker');
  64. _jestWorker = function () {
  65. return data;
  66. };
  67. return data;
  68. }
  69. var _HasteFS = _interopRequireDefault(require('./HasteFS'));
  70. var _ModuleMap = _interopRequireDefault(require('./ModuleMap'));
  71. var _constants = _interopRequireDefault(require('./constants'));
  72. var _node = _interopRequireDefault(require('./crawlers/node'));
  73. var _watchman = _interopRequireDefault(require('./crawlers/watchman'));
  74. var _getMockName = _interopRequireDefault(require('./getMockName'));
  75. var fastPath = _interopRequireWildcard(require('./lib/fast_path'));
  76. var _getPlatformExtension = _interopRequireDefault(
  77. require('./lib/getPlatformExtension')
  78. );
  79. var _normalizePathSep = _interopRequireDefault(
  80. require('./lib/normalizePathSep')
  81. );
  82. var _FSEventsWatcher = _interopRequireDefault(
  83. require('./watchers/FSEventsWatcher')
  84. );
  85. var _NodeWatcher = _interopRequireDefault(require('./watchers/NodeWatcher'));
  86. var _WatchmanWatcher = _interopRequireDefault(
  87. require('./watchers/WatchmanWatcher')
  88. );
  89. var _worker = require('./worker');
  90. function _interopRequireDefault(obj) {
  91. return obj && obj.__esModule ? obj : {default: obj};
  92. }
  93. function _getRequireWildcardCache(nodeInterop) {
  94. if (typeof WeakMap !== 'function') return null;
  95. var cacheBabelInterop = new WeakMap();
  96. var cacheNodeInterop = new WeakMap();
  97. return (_getRequireWildcardCache = function (nodeInterop) {
  98. return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
  99. })(nodeInterop);
  100. }
  101. function _interopRequireWildcard(obj, nodeInterop) {
  102. if (!nodeInterop && obj && obj.__esModule) {
  103. return obj;
  104. }
  105. if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
  106. return {default: obj};
  107. }
  108. var cache = _getRequireWildcardCache(nodeInterop);
  109. if (cache && cache.has(obj)) {
  110. return cache.get(obj);
  111. }
  112. var newObj = {};
  113. var hasPropertyDescriptor =
  114. Object.defineProperty && Object.getOwnPropertyDescriptor;
  115. for (var key in obj) {
  116. if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
  117. var desc = hasPropertyDescriptor
  118. ? Object.getOwnPropertyDescriptor(obj, key)
  119. : null;
  120. if (desc && (desc.get || desc.set)) {
  121. Object.defineProperty(newObj, key, desc);
  122. } else {
  123. newObj[key] = obj[key];
  124. }
  125. }
  126. }
  127. newObj.default = obj;
  128. if (cache) {
  129. cache.set(obj, newObj);
  130. }
  131. return newObj;
  132. }
  133. function _defineProperty(obj, key, value) {
  134. if (key in obj) {
  135. Object.defineProperty(obj, key, {
  136. value: value,
  137. enumerable: true,
  138. configurable: true,
  139. writable: true
  140. });
  141. } else {
  142. obj[key] = value;
  143. }
  144. return obj;
  145. }
  146. // TypeScript doesn't like us importing from outside `rootDir`, but it doesn't
  147. // understand `require`.
  148. const {version: VERSION} = require('../package.json');
  149. const CHANGE_INTERVAL = 30;
  150. const MAX_WAIT_TIME = 240000;
  151. const NODE_MODULES = path().sep + 'node_modules' + path().sep;
  152. const PACKAGE_JSON = path().sep + 'package.json';
  153. const VCS_DIRECTORIES = ['.git', '.hg']
  154. .map(vcs =>
  155. (0, _jestRegexUtil().escapePathForRegex)(path().sep + vcs + path().sep)
  156. )
  157. .join('|');
  158. const canUseWatchman = (() => {
  159. try {
  160. (0, _child_process().execSync)('watchman --version', {
  161. stdio: ['ignore']
  162. });
  163. return true;
  164. } catch {}
  165. return false;
  166. })();
  167. function invariant(condition, message) {
  168. if (!condition) {
  169. throw new Error(message);
  170. }
  171. }
  172. /**
  173. * HasteMap is a JavaScript implementation of Facebook's haste module system.
  174. *
  175. * This implementation is inspired by https://github.com/facebook/node-haste
  176. * and was built with for high-performance in large code repositories with
  177. * hundreds of thousands of files. This implementation is scalable and provides
  178. * predictable performance.
  179. *
  180. * Because the haste map creation and synchronization is critical to startup
  181. * performance and most tasks are blocked by I/O this class makes heavy use of
  182. * synchronous operations. It uses worker processes for parallelizing file
  183. * access and metadata extraction.
  184. *
  185. * The data structures created by `jest-haste-map` can be used directly from the
  186. * cache without further processing. The metadata objects in the `files` and
  187. * `map` objects contain cross-references: a metadata object from one can look
  188. * up the corresponding metadata object in the other map. Note that in most
  189. * projects, the number of files will be greater than the number of haste
  190. * modules one module can refer to many files based on platform extensions.
  191. *
  192. * type HasteMap = {
  193. * clocks: WatchmanClocks,
  194. * files: {[filepath: string]: FileMetaData},
  195. * map: {[id: string]: ModuleMapItem},
  196. * mocks: {[id: string]: string},
  197. * }
  198. *
  199. * // Watchman clocks are used for query synchronization and file system deltas.
  200. * type WatchmanClocks = {[filepath: string]: string};
  201. *
  202. * type FileMetaData = {
  203. * id: ?string, // used to look up module metadata objects in `map`.
  204. * mtime: number, // check for outdated files.
  205. * size: number, // size of the file in bytes.
  206. * visited: boolean, // whether the file has been parsed or not.
  207. * dependencies: Array<string>, // all relative dependencies of this file.
  208. * sha1: ?string, // SHA-1 of the file, if requested via options.
  209. * };
  210. *
  211. * // Modules can be targeted to a specific platform based on the file name.
  212. * // Example: platform.ios.js and Platform.android.js will both map to the same
  213. * // `Platform` module. The platform should be specified during resolution.
  214. * type ModuleMapItem = {[platform: string]: ModuleMetaData};
  215. *
  216. * //
  217. * type ModuleMetaData = {
  218. * path: string, // the path to look up the file object in `files`.
  219. * type: string, // the module type (either `package` or `module`).
  220. * };
  221. *
  222. * Note that the data structures described above are conceptual only. The actual
  223. * implementation uses arrays and constant keys for metadata storage. Instead of
  224. * `{id: 'flatMap', mtime: 3421, size: 42, visited: true, dependencies: []}` the real
  225. * representation is similar to `['flatMap', 3421, 42, 1, []]` to save storage space
  226. * and reduce parse and write time of a big JSON blob.
  227. *
  228. * The HasteMap is created as follows:
  229. * 1. read data from the cache or create an empty structure.
  230. *
  231. * 2. crawl the file system.
  232. * * empty cache: crawl the entire file system.
  233. * * cache available:
  234. * * if watchman is available: get file system delta changes.
  235. * * if watchman is unavailable: crawl the entire file system.
  236. * * build metadata objects for every file. This builds the `files` part of
  237. * the `HasteMap`.
  238. *
  239. * 3. parse and extract metadata from changed files.
  240. * * this is done in parallel over worker processes to improve performance.
  241. * * the worst case is to parse all files.
  242. * * the best case is no file system access and retrieving all data from
  243. * the cache.
  244. * * the average case is a small number of changed files.
  245. *
  246. * 4. serialize the new `HasteMap` in a cache file.
  247. * Worker processes can directly access the cache through `HasteMap.read()`.
  248. *
  249. */
  250. class HasteMap extends _events().EventEmitter {
  251. static getStatic(config) {
  252. if (config.haste.hasteMapModulePath) {
  253. return require(config.haste.hasteMapModulePath);
  254. }
  255. return HasteMap;
  256. }
  257. static create(options) {
  258. if (options.hasteMapModulePath) {
  259. const CustomHasteMap = require(options.hasteMapModulePath);
  260. return new CustomHasteMap(options);
  261. }
  262. return new HasteMap(options);
  263. }
  264. constructor(options) {
  265. super();
  266. _defineProperty(this, '_buildPromise', void 0);
  267. _defineProperty(this, '_cachePath', void 0);
  268. _defineProperty(this, '_changeInterval', void 0);
  269. _defineProperty(this, '_console', void 0);
  270. _defineProperty(this, '_options', void 0);
  271. _defineProperty(this, '_watchers', void 0);
  272. _defineProperty(this, '_worker', void 0);
  273. this._options = {
  274. cacheDirectory: options.cacheDirectory || (0, _os().tmpdir)(),
  275. computeDependencies:
  276. options.computeDependencies === undefined
  277. ? true
  278. : options.computeDependencies,
  279. computeSha1: options.computeSha1 || false,
  280. dependencyExtractor: options.dependencyExtractor || null,
  281. enableSymlinks: options.enableSymlinks || false,
  282. extensions: options.extensions,
  283. forceNodeFilesystemAPI: !!options.forceNodeFilesystemAPI,
  284. hasteImplModulePath: options.hasteImplModulePath,
  285. maxWorkers: options.maxWorkers,
  286. mocksPattern: options.mocksPattern
  287. ? new RegExp(options.mocksPattern)
  288. : null,
  289. name: options.name,
  290. platforms: options.platforms,
  291. resetCache: options.resetCache,
  292. retainAllFiles: options.retainAllFiles,
  293. rootDir: options.rootDir,
  294. roots: Array.from(new Set(options.roots)),
  295. skipPackageJson: !!options.skipPackageJson,
  296. throwOnModuleCollision: !!options.throwOnModuleCollision,
  297. useWatchman: options.useWatchman == null ? true : options.useWatchman,
  298. watch: !!options.watch
  299. };
  300. this._console = options.console || global.console;
  301. if (options.ignorePattern) {
  302. if (options.ignorePattern instanceof RegExp) {
  303. this._options.ignorePattern = new RegExp(
  304. options.ignorePattern.source.concat('|' + VCS_DIRECTORIES),
  305. options.ignorePattern.flags
  306. );
  307. } else {
  308. throw new Error(
  309. 'jest-haste-map: the `ignorePattern` option must be a RegExp'
  310. );
  311. }
  312. } else {
  313. this._options.ignorePattern = new RegExp(VCS_DIRECTORIES);
  314. }
  315. if (this._options.enableSymlinks && this._options.useWatchman) {
  316. throw new Error(
  317. 'jest-haste-map: enableSymlinks config option was set, but ' +
  318. 'is incompatible with watchman.\n' +
  319. 'Set either `enableSymlinks` to false or `useWatchman` to false.'
  320. );
  321. }
  322. const rootDirHash = (0, _crypto().createHash)('md5')
  323. .update(options.rootDir)
  324. .digest('hex');
  325. let hasteImplHash = '';
  326. let dependencyExtractorHash = '';
  327. if (options.hasteImplModulePath) {
  328. const hasteImpl = require(options.hasteImplModulePath);
  329. if (hasteImpl.getCacheKey) {
  330. hasteImplHash = String(hasteImpl.getCacheKey());
  331. }
  332. }
  333. if (options.dependencyExtractor) {
  334. const dependencyExtractor = require(options.dependencyExtractor);
  335. if (dependencyExtractor.getCacheKey) {
  336. dependencyExtractorHash = String(dependencyExtractor.getCacheKey());
  337. }
  338. }
  339. this._cachePath = HasteMap.getCacheFilePath(
  340. this._options.cacheDirectory,
  341. `haste-map-${this._options.name}-${rootDirHash}`,
  342. VERSION,
  343. this._options.name,
  344. this._options.roots
  345. .map(root => fastPath.relative(options.rootDir, root))
  346. .join(':'),
  347. this._options.extensions.join(':'),
  348. this._options.platforms.join(':'),
  349. this._options.computeSha1.toString(),
  350. options.mocksPattern || '',
  351. (options.ignorePattern || '').toString(),
  352. hasteImplHash,
  353. dependencyExtractorHash,
  354. this._options.computeDependencies.toString()
  355. );
  356. this._buildPromise = null;
  357. this._watchers = [];
  358. this._worker = null;
  359. }
  360. static getCacheFilePath(tmpdir, name, ...extra) {
  361. const hash = (0, _crypto().createHash)('md5').update(extra.join(''));
  362. return path().join(
  363. tmpdir,
  364. name.replace(/\W/g, '-') + '-' + hash.digest('hex')
  365. );
  366. }
  367. static getModuleMapFromJSON(json) {
  368. return _ModuleMap.default.fromJSON(json);
  369. }
  370. getCacheFilePath() {
  371. return this._cachePath;
  372. }
  373. build() {
  374. if (!this._buildPromise) {
  375. this._buildPromise = (async () => {
  376. const data = await this._buildFileMap(); // Persist when we don't know if files changed (changedFiles undefined)
  377. // or when we know a file was changed or deleted.
  378. let hasteMap;
  379. if (
  380. data.changedFiles === undefined ||
  381. data.changedFiles.size > 0 ||
  382. data.removedFiles.size > 0
  383. ) {
  384. hasteMap = await this._buildHasteMap(data);
  385. this._persist(hasteMap);
  386. } else {
  387. hasteMap = data.hasteMap;
  388. }
  389. const rootDir = this._options.rootDir;
  390. const hasteFS = new _HasteFS.default({
  391. files: hasteMap.files,
  392. rootDir
  393. });
  394. const moduleMap = new _ModuleMap.default({
  395. duplicates: hasteMap.duplicates,
  396. map: hasteMap.map,
  397. mocks: hasteMap.mocks,
  398. rootDir
  399. });
  400. const __hasteMapForTest =
  401. (process.env.NODE_ENV === 'test' && hasteMap) || null;
  402. await this._watch(hasteMap);
  403. return {
  404. __hasteMapForTest,
  405. hasteFS,
  406. moduleMap
  407. };
  408. })();
  409. }
  410. return this._buildPromise;
  411. }
  412. /**
  413. * 1. read data from the cache or create an empty structure.
  414. */
  415. read() {
  416. let hasteMap;
  417. try {
  418. hasteMap = _jestSerializer().default.readFileSync(this._cachePath);
  419. } catch {
  420. hasteMap = this._createEmptyMap();
  421. }
  422. return hasteMap;
  423. }
  424. readModuleMap() {
  425. const data = this.read();
  426. return new _ModuleMap.default({
  427. duplicates: data.duplicates,
  428. map: data.map,
  429. mocks: data.mocks,
  430. rootDir: this._options.rootDir
  431. });
  432. }
  433. /**
  434. * 2. crawl the file system.
  435. */
  436. async _buildFileMap() {
  437. let hasteMap;
  438. try {
  439. const read = this._options.resetCache ? this._createEmptyMap : this.read;
  440. hasteMap = await read.call(this);
  441. } catch {
  442. hasteMap = this._createEmptyMap();
  443. }
  444. return this._crawl(hasteMap);
  445. }
  446. /**
  447. * 3. parse and extract metadata from changed files.
  448. */
  449. _processFile(hasteMap, map, mocks, filePath, workerOptions) {
  450. const rootDir = this._options.rootDir;
  451. const setModule = (id, module) => {
  452. let moduleMap = map.get(id);
  453. if (!moduleMap) {
  454. moduleMap = Object.create(null);
  455. map.set(id, moduleMap);
  456. }
  457. const platform =
  458. (0, _getPlatformExtension.default)(
  459. module[_constants.default.PATH],
  460. this._options.platforms
  461. ) || _constants.default.GENERIC_PLATFORM;
  462. const existingModule = moduleMap[platform];
  463. if (
  464. existingModule &&
  465. existingModule[_constants.default.PATH] !==
  466. module[_constants.default.PATH]
  467. ) {
  468. const method = this._options.throwOnModuleCollision ? 'error' : 'warn';
  469. this._console[method](
  470. [
  471. 'jest-haste-map: Haste module naming collision: ' + id,
  472. ' The following files share their name; please adjust your hasteImpl:',
  473. ' * <rootDir>' +
  474. path().sep +
  475. existingModule[_constants.default.PATH],
  476. ' * <rootDir>' + path().sep + module[_constants.default.PATH],
  477. ''
  478. ].join('\n')
  479. );
  480. if (this._options.throwOnModuleCollision) {
  481. throw new DuplicateError(
  482. existingModule[_constants.default.PATH],
  483. module[_constants.default.PATH]
  484. );
  485. } // We do NOT want consumers to use a module that is ambiguous.
  486. delete moduleMap[platform];
  487. if (Object.keys(moduleMap).length === 1) {
  488. map.delete(id);
  489. }
  490. let dupsByPlatform = hasteMap.duplicates.get(id);
  491. if (dupsByPlatform == null) {
  492. dupsByPlatform = new Map();
  493. hasteMap.duplicates.set(id, dupsByPlatform);
  494. }
  495. const dups = new Map([
  496. [module[_constants.default.PATH], module[_constants.default.TYPE]],
  497. [
  498. existingModule[_constants.default.PATH],
  499. existingModule[_constants.default.TYPE]
  500. ]
  501. ]);
  502. dupsByPlatform.set(platform, dups);
  503. return;
  504. }
  505. const dupsByPlatform = hasteMap.duplicates.get(id);
  506. if (dupsByPlatform != null) {
  507. const dups = dupsByPlatform.get(platform);
  508. if (dups != null) {
  509. dups.set(
  510. module[_constants.default.PATH],
  511. module[_constants.default.TYPE]
  512. );
  513. }
  514. return;
  515. }
  516. moduleMap[platform] = module;
  517. };
  518. const relativeFilePath = fastPath.relative(rootDir, filePath);
  519. const fileMetadata = hasteMap.files.get(relativeFilePath);
  520. if (!fileMetadata) {
  521. throw new Error(
  522. 'jest-haste-map: File to process was not found in the haste map.'
  523. );
  524. }
  525. const moduleMetadata = hasteMap.map.get(
  526. fileMetadata[_constants.default.ID]
  527. );
  528. const computeSha1 =
  529. this._options.computeSha1 && !fileMetadata[_constants.default.SHA1]; // Callback called when the response from the worker is successful.
  530. const workerReply = metadata => {
  531. // `1` for truthy values instead of `true` to save cache space.
  532. fileMetadata[_constants.default.VISITED] = 1;
  533. const metadataId = metadata.id;
  534. const metadataModule = metadata.module;
  535. if (metadataId && metadataModule) {
  536. fileMetadata[_constants.default.ID] = metadataId;
  537. setModule(metadataId, metadataModule);
  538. }
  539. fileMetadata[_constants.default.DEPENDENCIES] = metadata.dependencies
  540. ? metadata.dependencies.join(_constants.default.DEPENDENCY_DELIM)
  541. : '';
  542. if (computeSha1) {
  543. fileMetadata[_constants.default.SHA1] = metadata.sha1;
  544. }
  545. }; // Callback called when the response from the worker is an error.
  546. const workerError = error => {
  547. if (typeof error !== 'object' || !error.message || !error.stack) {
  548. error = new Error(error);
  549. error.stack = ''; // Remove stack for stack-less errors.
  550. }
  551. if (!['ENOENT', 'EACCES'].includes(error.code)) {
  552. throw error;
  553. } // If a file cannot be read we remove it from the file list and
  554. // ignore the failure silently.
  555. hasteMap.files.delete(relativeFilePath);
  556. }; // If we retain all files in the virtual HasteFS representation, we avoid
  557. // reading them if they aren't important (node_modules).
  558. if (this._options.retainAllFiles && filePath.includes(NODE_MODULES)) {
  559. if (computeSha1) {
  560. return this._getWorker(workerOptions)
  561. .getSha1({
  562. computeDependencies: this._options.computeDependencies,
  563. computeSha1,
  564. dependencyExtractor: this._options.dependencyExtractor,
  565. filePath,
  566. hasteImplModulePath: this._options.hasteImplModulePath,
  567. rootDir
  568. })
  569. .then(workerReply, workerError);
  570. }
  571. return null;
  572. }
  573. if (
  574. this._options.mocksPattern &&
  575. this._options.mocksPattern.test(filePath)
  576. ) {
  577. const mockPath = (0, _getMockName.default)(filePath);
  578. const existingMockPath = mocks.get(mockPath);
  579. if (existingMockPath) {
  580. const secondMockPath = fastPath.relative(rootDir, filePath);
  581. if (existingMockPath !== secondMockPath) {
  582. const method = this._options.throwOnModuleCollision
  583. ? 'error'
  584. : 'warn';
  585. this._console[method](
  586. [
  587. 'jest-haste-map: duplicate manual mock found: ' + mockPath,
  588. ' The following files share their name; please delete one of them:',
  589. ' * <rootDir>' + path().sep + existingMockPath,
  590. ' * <rootDir>' + path().sep + secondMockPath,
  591. ''
  592. ].join('\n')
  593. );
  594. if (this._options.throwOnModuleCollision) {
  595. throw new DuplicateError(existingMockPath, secondMockPath);
  596. }
  597. }
  598. }
  599. mocks.set(mockPath, relativeFilePath);
  600. }
  601. if (fileMetadata[_constants.default.VISITED]) {
  602. if (!fileMetadata[_constants.default.ID]) {
  603. return null;
  604. }
  605. if (moduleMetadata != null) {
  606. const platform =
  607. (0, _getPlatformExtension.default)(
  608. filePath,
  609. this._options.platforms
  610. ) || _constants.default.GENERIC_PLATFORM;
  611. const module = moduleMetadata[platform];
  612. if (module == null) {
  613. return null;
  614. }
  615. const moduleId = fileMetadata[_constants.default.ID];
  616. let modulesByPlatform = map.get(moduleId);
  617. if (!modulesByPlatform) {
  618. modulesByPlatform = Object.create(null);
  619. map.set(moduleId, modulesByPlatform);
  620. }
  621. modulesByPlatform[platform] = module;
  622. return null;
  623. }
  624. }
  625. return this._getWorker(workerOptions)
  626. .worker({
  627. computeDependencies: this._options.computeDependencies,
  628. computeSha1,
  629. dependencyExtractor: this._options.dependencyExtractor,
  630. filePath,
  631. hasteImplModulePath: this._options.hasteImplModulePath,
  632. rootDir
  633. })
  634. .then(workerReply, workerError);
  635. }
  636. _buildHasteMap(data) {
  637. const {removedFiles, changedFiles, hasteMap} = data; // If any files were removed or we did not track what files changed, process
  638. // every file looking for changes. Otherwise, process only changed files.
  639. let map;
  640. let mocks;
  641. let filesToProcess;
  642. if (changedFiles === undefined || removedFiles.size) {
  643. map = new Map();
  644. mocks = new Map();
  645. filesToProcess = hasteMap.files;
  646. } else {
  647. map = hasteMap.map;
  648. mocks = hasteMap.mocks;
  649. filesToProcess = changedFiles;
  650. }
  651. for (const [relativeFilePath, fileMetadata] of removedFiles) {
  652. this._recoverDuplicates(
  653. hasteMap,
  654. relativeFilePath,
  655. fileMetadata[_constants.default.ID]
  656. );
  657. }
  658. const promises = [];
  659. for (const relativeFilePath of filesToProcess.keys()) {
  660. if (
  661. this._options.skipPackageJson &&
  662. relativeFilePath.endsWith(PACKAGE_JSON)
  663. ) {
  664. continue;
  665. } // SHA-1, if requested, should already be present thanks to the crawler.
  666. const filePath = fastPath.resolve(
  667. this._options.rootDir,
  668. relativeFilePath
  669. );
  670. const promise = this._processFile(hasteMap, map, mocks, filePath);
  671. if (promise) {
  672. promises.push(promise);
  673. }
  674. }
  675. return Promise.all(promises).then(
  676. () => {
  677. this._cleanup();
  678. hasteMap.map = map;
  679. hasteMap.mocks = mocks;
  680. return hasteMap;
  681. },
  682. error => {
  683. this._cleanup();
  684. throw error;
  685. }
  686. );
  687. }
  688. _cleanup() {
  689. const worker = this._worker; // @ts-expect-error
  690. if (worker && typeof worker.end === 'function') {
  691. // @ts-expect-error
  692. worker.end();
  693. }
  694. this._worker = null;
  695. }
  696. /**
  697. * 4. serialize the new `HasteMap` in a cache file.
  698. */
  699. _persist(hasteMap) {
  700. _jestSerializer().default.writeFileSync(this._cachePath, hasteMap);
  701. }
  702. /**
  703. * Creates workers or parses files and extracts metadata in-process.
  704. */
  705. _getWorker(options) {
  706. if (!this._worker) {
  707. if ((options && options.forceInBand) || this._options.maxWorkers <= 1) {
  708. this._worker = {
  709. getSha1: _worker.getSha1,
  710. worker: _worker.worker
  711. };
  712. } else {
  713. // @ts-expect-error: assignment of a worker with custom properties.
  714. this._worker = new (_jestWorker().Worker)(require.resolve('./worker'), {
  715. exposedMethods: ['getSha1', 'worker'],
  716. maxRetries: 3,
  717. numWorkers: this._options.maxWorkers
  718. });
  719. }
  720. }
  721. return this._worker;
  722. }
  723. _crawl(hasteMap) {
  724. const options = this._options;
  725. const ignore = this._ignore.bind(this);
  726. const crawl =
  727. canUseWatchman && this._options.useWatchman
  728. ? _watchman.default
  729. : _node.default;
  730. const crawlerOptions = {
  731. computeSha1: options.computeSha1,
  732. data: hasteMap,
  733. enableSymlinks: options.enableSymlinks,
  734. extensions: options.extensions,
  735. forceNodeFilesystemAPI: options.forceNodeFilesystemAPI,
  736. ignore,
  737. rootDir: options.rootDir,
  738. roots: options.roots
  739. };
  740. const retry = error => {
  741. if (crawl === _watchman.default) {
  742. this._console.warn(
  743. 'jest-haste-map: Watchman crawl failed. Retrying once with node ' +
  744. 'crawler.\n' +
  745. " Usually this happens when watchman isn't running. Create an " +
  746. "empty `.watchmanconfig` file in your project's root folder or " +
  747. 'initialize a git or hg repository in your project.\n' +
  748. ' ' +
  749. error
  750. );
  751. return (0, _node.default)(crawlerOptions).catch(e => {
  752. throw new Error(
  753. 'Crawler retry failed:\n' +
  754. ` Original error: ${error.message}\n` +
  755. ` Retry error: ${e.message}\n`
  756. );
  757. });
  758. }
  759. throw error;
  760. };
  761. try {
  762. return crawl(crawlerOptions).catch(retry);
  763. } catch (error) {
  764. return retry(error);
  765. }
  766. }
  767. /**
  768. * Watch mode
  769. */
  770. _watch(hasteMap) {
  771. if (!this._options.watch) {
  772. return Promise.resolve();
  773. } // In watch mode, we'll only warn about module collisions and we'll retain
  774. // all files, even changes to node_modules.
  775. this._options.throwOnModuleCollision = false;
  776. this._options.retainAllFiles = true; // WatchmanWatcher > FSEventsWatcher > sane.NodeWatcher
  777. const Watcher =
  778. canUseWatchman && this._options.useWatchman
  779. ? _WatchmanWatcher.default
  780. : _FSEventsWatcher.default.isSupported()
  781. ? _FSEventsWatcher.default
  782. : _NodeWatcher.default;
  783. const extensions = this._options.extensions;
  784. const ignorePattern = this._options.ignorePattern;
  785. const rootDir = this._options.rootDir;
  786. let changeQueue = Promise.resolve();
  787. let eventsQueue = []; // We only need to copy the entire haste map once on every "frame".
  788. let mustCopy = true;
  789. const createWatcher = root => {
  790. const watcher = new Watcher(root, {
  791. dot: true,
  792. glob: extensions.map(extension => '**/*.' + extension),
  793. ignored: ignorePattern
  794. });
  795. return new Promise((resolve, reject) => {
  796. const rejectTimeout = setTimeout(
  797. () => reject(new Error('Failed to start watch mode.')),
  798. MAX_WAIT_TIME
  799. );
  800. watcher.once('ready', () => {
  801. clearTimeout(rejectTimeout);
  802. watcher.on('all', onChange);
  803. resolve(watcher);
  804. });
  805. });
  806. };
  807. const emitChange = () => {
  808. if (eventsQueue.length) {
  809. mustCopy = true;
  810. const changeEvent = {
  811. eventsQueue,
  812. hasteFS: new _HasteFS.default({
  813. files: hasteMap.files,
  814. rootDir
  815. }),
  816. moduleMap: new _ModuleMap.default({
  817. duplicates: hasteMap.duplicates,
  818. map: hasteMap.map,
  819. mocks: hasteMap.mocks,
  820. rootDir
  821. })
  822. };
  823. this.emit('change', changeEvent);
  824. eventsQueue = [];
  825. }
  826. };
  827. const onChange = (type, filePath, root, stat) => {
  828. filePath = path().join(root, (0, _normalizePathSep.default)(filePath));
  829. if (
  830. (stat && stat.isDirectory()) ||
  831. this._ignore(filePath) ||
  832. !extensions.some(extension => filePath.endsWith(extension))
  833. ) {
  834. return;
  835. }
  836. const relativeFilePath = fastPath.relative(rootDir, filePath);
  837. const fileMetadata = hasteMap.files.get(relativeFilePath); // The file has been accessed, not modified
  838. if (
  839. type === 'change' &&
  840. fileMetadata &&
  841. stat &&
  842. fileMetadata[_constants.default.MTIME] === stat.mtime.getTime()
  843. ) {
  844. return;
  845. }
  846. changeQueue = changeQueue
  847. .then(() => {
  848. // If we get duplicate events for the same file, ignore them.
  849. if (
  850. eventsQueue.find(
  851. event =>
  852. event.type === type &&
  853. event.filePath === filePath &&
  854. ((!event.stat && !stat) ||
  855. (!!event.stat &&
  856. !!stat &&
  857. event.stat.mtime.getTime() === stat.mtime.getTime()))
  858. )
  859. ) {
  860. return null;
  861. }
  862. if (mustCopy) {
  863. mustCopy = false;
  864. hasteMap = {
  865. clocks: new Map(hasteMap.clocks),
  866. duplicates: new Map(hasteMap.duplicates),
  867. files: new Map(hasteMap.files),
  868. map: new Map(hasteMap.map),
  869. mocks: new Map(hasteMap.mocks)
  870. };
  871. }
  872. const add = () => {
  873. eventsQueue.push({
  874. filePath,
  875. stat,
  876. type
  877. });
  878. return null;
  879. };
  880. const fileMetadata = hasteMap.files.get(relativeFilePath); // If it's not an addition, delete the file and all its metadata
  881. if (fileMetadata != null) {
  882. const moduleName = fileMetadata[_constants.default.ID];
  883. const platform =
  884. (0, _getPlatformExtension.default)(
  885. filePath,
  886. this._options.platforms
  887. ) || _constants.default.GENERIC_PLATFORM;
  888. hasteMap.files.delete(relativeFilePath);
  889. let moduleMap = hasteMap.map.get(moduleName);
  890. if (moduleMap != null) {
  891. // We are forced to copy the object because jest-haste-map exposes
  892. // the map as an immutable entity.
  893. moduleMap = copy(moduleMap);
  894. delete moduleMap[platform];
  895. if (Object.keys(moduleMap).length === 0) {
  896. hasteMap.map.delete(moduleName);
  897. } else {
  898. hasteMap.map.set(moduleName, moduleMap);
  899. }
  900. }
  901. if (
  902. this._options.mocksPattern &&
  903. this._options.mocksPattern.test(filePath)
  904. ) {
  905. const mockName = (0, _getMockName.default)(filePath);
  906. hasteMap.mocks.delete(mockName);
  907. }
  908. this._recoverDuplicates(hasteMap, relativeFilePath, moduleName);
  909. } // If the file was added or changed,
  910. // parse it and update the haste map.
  911. if (type === 'add' || type === 'change') {
  912. invariant(
  913. stat,
  914. 'since the file exists or changed, it should have stats'
  915. );
  916. const fileMetadata = [
  917. '',
  918. stat.mtime.getTime(),
  919. stat.size,
  920. 0,
  921. '',
  922. null
  923. ];
  924. hasteMap.files.set(relativeFilePath, fileMetadata);
  925. const promise = this._processFile(
  926. hasteMap,
  927. hasteMap.map,
  928. hasteMap.mocks,
  929. filePath,
  930. {
  931. forceInBand: true
  932. }
  933. ); // Cleanup
  934. this._cleanup();
  935. if (promise) {
  936. return promise.then(add);
  937. } else {
  938. // If a file in node_modules has changed,
  939. // emit an event regardless.
  940. add();
  941. }
  942. } else {
  943. add();
  944. }
  945. return null;
  946. })
  947. .catch(error => {
  948. this._console.error(
  949. `jest-haste-map: watch error:\n ${error.stack}\n`
  950. );
  951. });
  952. };
  953. this._changeInterval = setInterval(emitChange, CHANGE_INTERVAL);
  954. return Promise.all(this._options.roots.map(createWatcher)).then(
  955. watchers => {
  956. this._watchers = watchers;
  957. }
  958. );
  959. }
  960. /**
  961. * This function should be called when the file under `filePath` is removed
  962. * or changed. When that happens, we want to figure out if that file was
  963. * part of a group of files that had the same ID. If it was, we want to
  964. * remove it from the group. Furthermore, if there is only one file
  965. * remaining in the group, then we want to restore that single file as the
  966. * correct resolution for its ID, and cleanup the duplicates index.
  967. */
  968. _recoverDuplicates(hasteMap, relativeFilePath, moduleName) {
  969. let dupsByPlatform = hasteMap.duplicates.get(moduleName);
  970. if (dupsByPlatform == null) {
  971. return;
  972. }
  973. const platform =
  974. (0, _getPlatformExtension.default)(
  975. relativeFilePath,
  976. this._options.platforms
  977. ) || _constants.default.GENERIC_PLATFORM;
  978. let dups = dupsByPlatform.get(platform);
  979. if (dups == null) {
  980. return;
  981. }
  982. dupsByPlatform = copyMap(dupsByPlatform);
  983. hasteMap.duplicates.set(moduleName, dupsByPlatform);
  984. dups = copyMap(dups);
  985. dupsByPlatform.set(platform, dups);
  986. dups.delete(relativeFilePath);
  987. if (dups.size !== 1) {
  988. return;
  989. }
  990. const uniqueModule = dups.entries().next().value;
  991. if (!uniqueModule) {
  992. return;
  993. }
  994. let dedupMap = hasteMap.map.get(moduleName);
  995. if (dedupMap == null) {
  996. dedupMap = Object.create(null);
  997. hasteMap.map.set(moduleName, dedupMap);
  998. }
  999. dedupMap[platform] = uniqueModule;
  1000. dupsByPlatform.delete(platform);
  1001. if (dupsByPlatform.size === 0) {
  1002. hasteMap.duplicates.delete(moduleName);
  1003. }
  1004. }
  1005. async end() {
  1006. if (this._changeInterval) {
  1007. clearInterval(this._changeInterval);
  1008. }
  1009. if (!this._watchers.length) {
  1010. return;
  1011. }
  1012. await Promise.all(this._watchers.map(watcher => watcher.close()));
  1013. this._watchers = [];
  1014. }
  1015. /**
  1016. * Helpers
  1017. */
  1018. _ignore(filePath) {
  1019. const ignorePattern = this._options.ignorePattern;
  1020. const ignoreMatched =
  1021. ignorePattern instanceof RegExp
  1022. ? ignorePattern.test(filePath)
  1023. : ignorePattern && ignorePattern(filePath);
  1024. return (
  1025. ignoreMatched ||
  1026. (!this._options.retainAllFiles && filePath.includes(NODE_MODULES))
  1027. );
  1028. }
  1029. _createEmptyMap() {
  1030. return {
  1031. clocks: new Map(),
  1032. duplicates: new Map(),
  1033. files: new Map(),
  1034. map: new Map(),
  1035. mocks: new Map()
  1036. };
  1037. }
  1038. }
  1039. exports.default = HasteMap;
  1040. _defineProperty(HasteMap, 'H', _constants.default);
  1041. class DuplicateError extends Error {
  1042. constructor(mockPath1, mockPath2) {
  1043. super('Duplicated files or mocks. Please check the console for more info');
  1044. _defineProperty(this, 'mockPath1', void 0);
  1045. _defineProperty(this, 'mockPath2', void 0);
  1046. this.mockPath1 = mockPath1;
  1047. this.mockPath2 = mockPath2;
  1048. }
  1049. }
  1050. exports.DuplicateError = DuplicateError;
  1051. function copy(object) {
  1052. return Object.assign(Object.create(null), object);
  1053. }
  1054. function copyMap(input) {
  1055. return new Map(input);
  1056. }