ScriptTransformer.js 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.createTranspilingRequire = createTranspilingRequire;
  6. exports.default = void 0;
  7. function _crypto() {
  8. const data = require('crypto');
  9. _crypto = function () {
  10. return data;
  11. };
  12. return data;
  13. }
  14. function path() {
  15. const data = _interopRequireWildcard(require('path'));
  16. path = function () {
  17. return data;
  18. };
  19. return data;
  20. }
  21. function _core() {
  22. const data = require('@babel/core');
  23. _core = function () {
  24. return data;
  25. };
  26. return data;
  27. }
  28. function _babelPluginIstanbul() {
  29. const data = _interopRequireDefault(require('babel-plugin-istanbul'));
  30. _babelPluginIstanbul = function () {
  31. return data;
  32. };
  33. return data;
  34. }
  35. function _convertSourceMap() {
  36. const data = require('convert-source-map');
  37. _convertSourceMap = function () {
  38. return data;
  39. };
  40. return data;
  41. }
  42. function _fastJsonStableStringify() {
  43. const data = _interopRequireDefault(require('fast-json-stable-stringify'));
  44. _fastJsonStableStringify = function () {
  45. return data;
  46. };
  47. return data;
  48. }
  49. function fs() {
  50. const data = _interopRequireWildcard(require('graceful-fs'));
  51. fs = function () {
  52. return data;
  53. };
  54. return data;
  55. }
  56. function _pirates() {
  57. const data = require('pirates');
  58. _pirates = function () {
  59. return data;
  60. };
  61. return data;
  62. }
  63. function _slash() {
  64. const data = _interopRequireDefault(require('slash'));
  65. _slash = function () {
  66. return data;
  67. };
  68. return data;
  69. }
  70. function _writeFileAtomic() {
  71. const data = require('write-file-atomic');
  72. _writeFileAtomic = function () {
  73. return data;
  74. };
  75. return data;
  76. }
  77. function _jestHasteMap() {
  78. const data = _interopRequireDefault(require('jest-haste-map'));
  79. _jestHasteMap = function () {
  80. return data;
  81. };
  82. return data;
  83. }
  84. function _jestUtil() {
  85. const data = require('jest-util');
  86. _jestUtil = function () {
  87. return data;
  88. };
  89. return data;
  90. }
  91. var _enhanceUnexpectedTokenMessage = _interopRequireDefault(
  92. require('./enhanceUnexpectedTokenMessage')
  93. );
  94. var _shouldInstrument = _interopRequireDefault(require('./shouldInstrument'));
  95. function _interopRequireDefault(obj) {
  96. return obj && obj.__esModule ? obj : {default: obj};
  97. }
  98. function _getRequireWildcardCache() {
  99. if (typeof WeakMap !== 'function') return null;
  100. var cache = new WeakMap();
  101. _getRequireWildcardCache = function () {
  102. return cache;
  103. };
  104. return cache;
  105. }
  106. function _interopRequireWildcard(obj) {
  107. if (obj && obj.__esModule) {
  108. return obj;
  109. }
  110. if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
  111. return {default: obj};
  112. }
  113. var cache = _getRequireWildcardCache();
  114. if (cache && cache.has(obj)) {
  115. return cache.get(obj);
  116. }
  117. var newObj = {};
  118. var hasPropertyDescriptor =
  119. Object.defineProperty && Object.getOwnPropertyDescriptor;
  120. for (var key in obj) {
  121. if (Object.prototype.hasOwnProperty.call(obj, key)) {
  122. var desc = hasPropertyDescriptor
  123. ? Object.getOwnPropertyDescriptor(obj, key)
  124. : null;
  125. if (desc && (desc.get || desc.set)) {
  126. Object.defineProperty(newObj, key, desc);
  127. } else {
  128. newObj[key] = obj[key];
  129. }
  130. }
  131. }
  132. newObj.default = obj;
  133. if (cache) {
  134. cache.set(obj, newObj);
  135. }
  136. return newObj;
  137. }
  138. function _defineProperty(obj, key, value) {
  139. if (key in obj) {
  140. Object.defineProperty(obj, key, {
  141. value: value,
  142. enumerable: true,
  143. configurable: true,
  144. writable: true
  145. });
  146. } else {
  147. obj[key] = value;
  148. }
  149. return obj;
  150. }
  151. // Use `require` to avoid TS rootDir
  152. const {version: VERSION} = require('../package.json');
  153. // This data structure is used to avoid recalculating some data every time that
  154. // we need to transform a file. Since ScriptTransformer is instantiated for each
  155. // file we need to keep this object in the local scope of this module.
  156. const projectCaches = new Map(); // To reset the cache for specific changesets (rather than package version).
  157. const CACHE_VERSION = '1';
  158. async function waitForPromiseWithCleanup(promise, cleanup) {
  159. try {
  160. await promise;
  161. } finally {
  162. cleanup();
  163. }
  164. }
  165. class ScriptTransformer {
  166. constructor(config) {
  167. _defineProperty(this, '_cache', void 0);
  168. _defineProperty(this, '_config', void 0);
  169. _defineProperty(this, '_transformCache', void 0);
  170. _defineProperty(this, '_transformConfigCache', void 0);
  171. this._config = config;
  172. this._transformCache = new Map();
  173. this._transformConfigCache = new Map();
  174. const configString = (0, _fastJsonStableStringify().default)(this._config);
  175. let projectCache = projectCaches.get(configString);
  176. if (!projectCache) {
  177. projectCache = {
  178. configString,
  179. ignorePatternsRegExp: calcIgnorePatternRegExp(this._config),
  180. transformRegExp: calcTransformRegExp(this._config),
  181. transformedFiles: new Map()
  182. };
  183. projectCaches.set(configString, projectCache);
  184. }
  185. this._cache = projectCache;
  186. }
  187. _getCacheKey(
  188. fileData,
  189. filename,
  190. instrument,
  191. supportsDynamicImport,
  192. supportsStaticESM
  193. ) {
  194. const configString = this._cache.configString;
  195. const transformer = this._getTransformer(filename);
  196. if (transformer && typeof transformer.getCacheKey === 'function') {
  197. return (0, _crypto().createHash)('md5')
  198. .update(
  199. transformer.getCacheKey(fileData, filename, configString, {
  200. config: this._config,
  201. instrument,
  202. rootDir: this._config.rootDir,
  203. supportsDynamicImport,
  204. supportsStaticESM
  205. })
  206. )
  207. .update(CACHE_VERSION)
  208. .digest('hex');
  209. } else {
  210. return (0, _crypto().createHash)('md5')
  211. .update(fileData)
  212. .update(configString)
  213. .update(instrument ? 'instrument' : '')
  214. .update(filename)
  215. .update(CACHE_VERSION)
  216. .digest('hex');
  217. }
  218. }
  219. _getFileCachePath(
  220. filename,
  221. content,
  222. instrument,
  223. supportsDynamicImport,
  224. supportsStaticESM
  225. ) {
  226. const baseCacheDir = _jestHasteMap().default.getCacheFilePath(
  227. this._config.cacheDirectory,
  228. 'jest-transform-cache-' + this._config.name,
  229. VERSION
  230. );
  231. const cacheKey = this._getCacheKey(
  232. content,
  233. filename,
  234. instrument,
  235. supportsDynamicImport,
  236. supportsStaticESM
  237. ); // Create sub folders based on the cacheKey to avoid creating one
  238. // directory with many files.
  239. const cacheDir = path().join(baseCacheDir, cacheKey[0] + cacheKey[1]);
  240. const cacheFilenamePrefix = path()
  241. .basename(filename, path().extname(filename))
  242. .replace(/\W/g, '');
  243. const cachePath = (0, _slash().default)(
  244. path().join(cacheDir, cacheFilenamePrefix + '_' + cacheKey)
  245. );
  246. (0, _jestUtil().createDirectory)(cacheDir);
  247. return cachePath;
  248. }
  249. _getTransformPath(filename) {
  250. const transformRegExp = this._cache.transformRegExp;
  251. if (!transformRegExp) {
  252. return undefined;
  253. }
  254. for (let i = 0; i < transformRegExp.length; i++) {
  255. if (transformRegExp[i][0].test(filename)) {
  256. const transformPath = transformRegExp[i][1];
  257. this._transformConfigCache.set(transformPath, transformRegExp[i][2]);
  258. return transformPath;
  259. }
  260. }
  261. return undefined;
  262. }
  263. _getTransformer(filename) {
  264. if (!this._config.transform || !this._config.transform.length) {
  265. return null;
  266. }
  267. const transformPath = this._getTransformPath(filename);
  268. if (!transformPath) {
  269. return null;
  270. }
  271. const transformer = this._transformCache.get(transformPath);
  272. if (transformer) {
  273. return transformer;
  274. }
  275. let transform = require(transformPath);
  276. if (!transform) {
  277. throw new TypeError('Jest: a transform must export something.');
  278. }
  279. const transformerConfig = this._transformConfigCache.get(transformPath);
  280. if (typeof transform.createTransformer === 'function') {
  281. transform = transform.createTransformer(transformerConfig);
  282. }
  283. if (typeof transform.process !== 'function') {
  284. throw new TypeError(
  285. 'Jest: a transform must export a `process` function.'
  286. );
  287. }
  288. this._transformCache.set(transformPath, transform);
  289. return transform;
  290. }
  291. _instrumentFile(
  292. filename,
  293. input,
  294. supportsDynamicImport,
  295. supportsStaticESM,
  296. canMapToInput
  297. ) {
  298. const inputCode = typeof input === 'string' ? input : input.code;
  299. const inputMap = typeof input === 'string' ? null : input.map;
  300. const result = (0, _core().transformSync)(inputCode, {
  301. auxiliaryCommentBefore: ' istanbul ignore next ',
  302. babelrc: false,
  303. caller: {
  304. name: '@jest/transform',
  305. supportsDynamicImport,
  306. supportsStaticESM
  307. },
  308. configFile: false,
  309. filename,
  310. plugins: [
  311. [
  312. _babelPluginIstanbul().default,
  313. {
  314. compact: false,
  315. // files outside `cwd` will not be instrumented
  316. cwd: this._config.rootDir,
  317. exclude: [],
  318. extension: false,
  319. inputSourceMap: inputMap,
  320. useInlineSourceMaps: false
  321. }
  322. ]
  323. ],
  324. sourceMaps: canMapToInput ? 'both' : false
  325. });
  326. if (result && result.code) {
  327. return result;
  328. }
  329. return input;
  330. } // We don't want to expose transformers to the outside - this function is just
  331. // to warm up `this._transformCache`
  332. preloadTransformer(filepath) {
  333. this._getTransformer(filepath);
  334. } // TODO: replace third argument with TransformOptions in Jest 26
  335. transformSource(
  336. filepath,
  337. content,
  338. instrument,
  339. supportsDynamicImport = false,
  340. supportsStaticESM = false
  341. ) {
  342. const filename = (0, _jestUtil().tryRealpath)(filepath);
  343. const transform = this._getTransformer(filename);
  344. const cacheFilePath = this._getFileCachePath(
  345. filename,
  346. content,
  347. instrument,
  348. supportsDynamicImport,
  349. supportsStaticESM
  350. );
  351. let sourceMapPath = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache)
  352. let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null;
  353. const shouldCallTransform = transform && this.shouldTransform(filename); // That means that the transform has a custom instrumentation
  354. // logic and will handle it based on `config.collectCoverage` option
  355. const transformWillInstrument =
  356. shouldCallTransform && transform && transform.canInstrument;
  357. if (code) {
  358. // This is broken: we return the code, and a path for the source map
  359. // directly from the cache. But, nothing ensures the source map actually
  360. // matches that source code. They could have gotten out-of-sync in case
  361. // two separate processes write concurrently to the same cache files.
  362. return {
  363. code,
  364. originalCode: content,
  365. sourceMapPath
  366. };
  367. }
  368. let transformed = {
  369. code: content,
  370. map: null
  371. };
  372. if (transform && shouldCallTransform) {
  373. const processed = transform.process(content, filename, this._config, {
  374. instrument,
  375. supportsDynamicImport,
  376. supportsStaticESM
  377. });
  378. if (typeof processed === 'string') {
  379. transformed.code = processed;
  380. } else if (processed != null && typeof processed.code === 'string') {
  381. transformed = processed;
  382. } else {
  383. throw new TypeError(
  384. "Jest: a transform's `process` function must return a string, " +
  385. 'or an object with `code` key containing this string.'
  386. );
  387. }
  388. }
  389. if (!transformed.map) {
  390. try {
  391. //Could be a potential freeze here.
  392. //See: https://github.com/facebook/jest/pull/5177#discussion_r158883570
  393. const inlineSourceMap = (0, _convertSourceMap().fromSource)(
  394. transformed.code
  395. );
  396. if (inlineSourceMap) {
  397. transformed.map = inlineSourceMap.toObject();
  398. }
  399. } catch {
  400. const transformPath = this._getTransformPath(filename);
  401. console.warn(
  402. `jest-transform: The source map produced for the file ${filename} ` +
  403. `by ${transformPath} was invalid. Proceeding without source ` +
  404. 'mapping for that file.'
  405. );
  406. }
  407. } // Apply instrumentation to the code if necessary, keeping the instrumented code and new map
  408. let map = transformed.map;
  409. if (!transformWillInstrument && instrument) {
  410. /**
  411. * We can map the original source code to the instrumented code ONLY if
  412. * - the process of transforming the code produced a source map e.g. ts-jest
  413. * - we did not transform the source code
  414. *
  415. * Otherwise we cannot make any statements about how the instrumented code corresponds to the original code,
  416. * and we should NOT emit any source maps
  417. *
  418. */
  419. const shouldEmitSourceMaps =
  420. (transform != null && map != null) || transform == null;
  421. const instrumented = this._instrumentFile(
  422. filename,
  423. transformed,
  424. supportsDynamicImport,
  425. supportsStaticESM,
  426. shouldEmitSourceMaps
  427. );
  428. code =
  429. typeof instrumented === 'string' ? instrumented : instrumented.code;
  430. map = typeof instrumented === 'string' ? null : instrumented.map;
  431. } else {
  432. code = transformed.code;
  433. }
  434. if (map) {
  435. const sourceMapContent =
  436. typeof map === 'string' ? map : JSON.stringify(map);
  437. writeCacheFile(sourceMapPath, sourceMapContent);
  438. } else {
  439. sourceMapPath = null;
  440. }
  441. writeCodeCacheFile(cacheFilePath, code);
  442. return {
  443. code,
  444. originalCode: content,
  445. sourceMapPath
  446. };
  447. }
  448. _transformAndBuildScript(filename, options, instrument, fileSource) {
  449. const {
  450. isCoreModule,
  451. isInternalModule,
  452. supportsDynamicImport,
  453. supportsStaticESM
  454. } = options;
  455. const content = stripShebang(
  456. fileSource || fs().readFileSync(filename, 'utf8')
  457. );
  458. let code = content;
  459. let sourceMapPath = null;
  460. const willTransform =
  461. !isInternalModule &&
  462. !isCoreModule &&
  463. (this.shouldTransform(filename) || instrument);
  464. try {
  465. if (willTransform) {
  466. const transformedSource = this.transformSource(
  467. filename,
  468. content,
  469. instrument,
  470. supportsDynamicImport,
  471. supportsStaticESM
  472. );
  473. code = transformedSource.code;
  474. sourceMapPath = transformedSource.sourceMapPath;
  475. }
  476. return {
  477. code,
  478. originalCode: content,
  479. sourceMapPath
  480. };
  481. } catch (e) {
  482. throw (0, _enhanceUnexpectedTokenMessage.default)(e);
  483. }
  484. }
  485. transform(filename, options, fileSource) {
  486. let scriptCacheKey = undefined;
  487. let instrument = false;
  488. if (!options.isCoreModule) {
  489. instrument =
  490. options.coverageProvider === 'babel' &&
  491. (0, _shouldInstrument.default)(filename, options, this._config);
  492. scriptCacheKey = getScriptCacheKey(filename, instrument);
  493. const result = this._cache.transformedFiles.get(scriptCacheKey);
  494. if (result) {
  495. return result;
  496. }
  497. }
  498. const result = this._transformAndBuildScript(
  499. filename,
  500. options,
  501. instrument,
  502. fileSource
  503. );
  504. if (scriptCacheKey) {
  505. this._cache.transformedFiles.set(scriptCacheKey, result);
  506. }
  507. return result;
  508. }
  509. transformJson(filename, options, fileSource) {
  510. const {
  511. isCoreModule,
  512. isInternalModule,
  513. supportsDynamicImport,
  514. supportsStaticESM
  515. } = options;
  516. const willTransform =
  517. !isInternalModule && !isCoreModule && this.shouldTransform(filename);
  518. if (willTransform) {
  519. const {code: transformedJsonSource} = this.transformSource(
  520. filename,
  521. fileSource,
  522. false,
  523. supportsDynamicImport,
  524. supportsStaticESM
  525. );
  526. return transformedJsonSource;
  527. }
  528. return fileSource;
  529. }
  530. requireAndTranspileModule(moduleName, callback) {
  531. // Load the transformer to avoid a cycle where we need to load a
  532. // transformer in order to transform it in the require hooks
  533. this.preloadTransformer(moduleName);
  534. let transforming = false;
  535. const revertHook = (0, _pirates().addHook)(
  536. (code, filename) => {
  537. try {
  538. transforming = true;
  539. return (
  540. // we might wanna do `supportsDynamicImport` at some point
  541. this.transformSource(filename, code, false, false, false).code ||
  542. code
  543. );
  544. } finally {
  545. transforming = false;
  546. }
  547. },
  548. {
  549. exts: this._config.moduleFileExtensions.map(ext => `.${ext}`),
  550. ignoreNodeModules: false,
  551. matcher: filename => {
  552. if (transforming) {
  553. // Don't transform any dependency required by the transformer itself
  554. return false;
  555. }
  556. return this.shouldTransform(filename);
  557. }
  558. }
  559. );
  560. const module = require(moduleName);
  561. if (!callback) {
  562. revertHook();
  563. return module;
  564. }
  565. try {
  566. const cbResult = callback(module);
  567. if ((0, _jestUtil().isPromise)(cbResult)) {
  568. return waitForPromiseWithCleanup(cbResult, revertHook).then(
  569. () => module
  570. );
  571. }
  572. } finally {
  573. revertHook();
  574. }
  575. return module;
  576. }
  577. /**
  578. * @deprecated use `this.shouldTransform` instead
  579. */
  580. // @ts-expect-error: Unused and private - remove in Jest 25
  581. _shouldTransform(filename) {
  582. return this.shouldTransform(filename);
  583. }
  584. shouldTransform(filename) {
  585. const ignoreRegexp = this._cache.ignorePatternsRegExp;
  586. const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false;
  587. return (
  588. !!this._config.transform && !!this._config.transform.length && !isIgnored
  589. );
  590. }
  591. } // TODO: do we need to define the generics twice?
  592. exports.default = ScriptTransformer;
  593. function createTranspilingRequire(config) {
  594. const transformer = new ScriptTransformer(config);
  595. return function requireAndTranspileModule(
  596. resolverPath,
  597. applyInteropRequireDefault = false
  598. ) {
  599. const transpiledModule = transformer.requireAndTranspileModule(
  600. resolverPath
  601. );
  602. return applyInteropRequireDefault
  603. ? (0, _jestUtil().interopRequireDefault)(transpiledModule).default
  604. : transpiledModule;
  605. };
  606. }
  607. const removeFile = path => {
  608. try {
  609. fs().unlinkSync(path);
  610. } catch {}
  611. };
  612. const stripShebang = content => {
  613. // If the file data starts with a shebang remove it. Leaves the empty line
  614. // to keep stack trace line numbers correct.
  615. if (content.startsWith('#!')) {
  616. return content.replace(/^#!.*/, '');
  617. } else {
  618. return content;
  619. }
  620. };
  621. /**
  622. * This is like `writeCacheFile` but with an additional sanity checksum. We
  623. * cannot use the same technique for source maps because we expose source map
  624. * cache file paths directly to callsites, with the expectation they can read
  625. * it right away. This is not a great system, because source map cache file
  626. * could get corrupted, out-of-sync, etc.
  627. */
  628. function writeCodeCacheFile(cachePath, code) {
  629. const checksum = (0, _crypto().createHash)('md5').update(code).digest('hex');
  630. writeCacheFile(cachePath, checksum + '\n' + code);
  631. }
  632. /**
  633. * Read counterpart of `writeCodeCacheFile`. We verify that the content of the
  634. * file matches the checksum, in case some kind of corruption happened. This
  635. * could happen if an older version of `jest-runtime` writes non-atomically to
  636. * the same cache, for example.
  637. */
  638. function readCodeCacheFile(cachePath) {
  639. const content = readCacheFile(cachePath);
  640. if (content == null) {
  641. return null;
  642. }
  643. const code = content.substr(33);
  644. const checksum = (0, _crypto().createHash)('md5').update(code).digest('hex');
  645. if (checksum === content.substr(0, 32)) {
  646. return code;
  647. }
  648. return null;
  649. }
  650. /**
  651. * Writing to the cache atomically relies on 'rename' being atomic on most
  652. * file systems. Doing atomic write reduces the risk of corruption by avoiding
  653. * two processes to write to the same file at the same time. It also reduces
  654. * the risk of reading a file that's being overwritten at the same time.
  655. */
  656. const writeCacheFile = (cachePath, fileData) => {
  657. try {
  658. (0, _writeFileAtomic().sync)(cachePath, fileData, {
  659. encoding: 'utf8',
  660. fsync: false
  661. });
  662. } catch (e) {
  663. if (cacheWriteErrorSafeToIgnore(e, cachePath)) {
  664. return;
  665. }
  666. e.message =
  667. 'jest: failed to cache transform results in: ' +
  668. cachePath +
  669. '\nFailure message: ' +
  670. e.message;
  671. removeFile(cachePath);
  672. throw e;
  673. }
  674. };
  675. /**
  676. * On Windows, renames are not atomic, leading to EPERM exceptions when two
  677. * processes attempt to rename to the same target file at the same time.
  678. * If the target file exists we can be reasonably sure another process has
  679. * legitimately won a cache write race and ignore the error.
  680. */
  681. const cacheWriteErrorSafeToIgnore = (e, cachePath) =>
  682. process.platform === 'win32' &&
  683. e.code === 'EPERM' &&
  684. fs().existsSync(cachePath);
  685. const readCacheFile = cachePath => {
  686. if (!fs().existsSync(cachePath)) {
  687. return null;
  688. }
  689. let fileData;
  690. try {
  691. fileData = fs().readFileSync(cachePath, 'utf8');
  692. } catch (e) {
  693. e.message =
  694. 'jest: failed to read cache file: ' +
  695. cachePath +
  696. '\nFailure message: ' +
  697. e.message;
  698. removeFile(cachePath);
  699. throw e;
  700. }
  701. if (fileData == null) {
  702. // We must have somehow created the file but failed to write to it,
  703. // let's delete it and retry.
  704. removeFile(cachePath);
  705. }
  706. return fileData;
  707. };
  708. const getScriptCacheKey = (filename, instrument) => {
  709. const mtime = fs().statSync(filename).mtime;
  710. return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : '');
  711. };
  712. const calcIgnorePatternRegExp = config => {
  713. if (
  714. !config.transformIgnorePatterns ||
  715. config.transformIgnorePatterns.length === 0
  716. ) {
  717. return undefined;
  718. }
  719. return new RegExp(config.transformIgnorePatterns.join('|'));
  720. };
  721. const calcTransformRegExp = config => {
  722. if (!config.transform.length) {
  723. return undefined;
  724. }
  725. const transformRegexp = [];
  726. for (let i = 0; i < config.transform.length; i++) {
  727. transformRegexp.push([
  728. new RegExp(config.transform[i][0]),
  729. config.transform[i][1],
  730. config.transform[i][2]
  731. ]);
  732. }
  733. return transformRegexp;
  734. };