747 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			747 lines
		
	
	
		
			18 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
|  | 'use strict'; | ||
|  | 
 | ||
|  | Object.defineProperty(exports, '__esModule', { | ||
|  |   value: true | ||
|  | }); | ||
|  | exports.default = void 0; | ||
|  | 
 | ||
|  | function _crypto() { | ||
|  |   const data = _interopRequireDefault(require('crypto')); | ||
|  | 
 | ||
|  |   _crypto = function _crypto() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _path() { | ||
|  |   const data = _interopRequireDefault(require('path')); | ||
|  | 
 | ||
|  |   _path = function _path() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _vm() { | ||
|  |   const data = _interopRequireDefault(require('vm')); | ||
|  | 
 | ||
|  |   _vm = function _vm() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _jestUtil() { | ||
|  |   const data = require('jest-util'); | ||
|  | 
 | ||
|  |   _jestUtil = function _jestUtil() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _gracefulFs() { | ||
|  |   const data = _interopRequireDefault(require('graceful-fs')); | ||
|  | 
 | ||
|  |   _gracefulFs = function _gracefulFs() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _core() { | ||
|  |   const data = require('@babel/core'); | ||
|  | 
 | ||
|  |   _core = function _core() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _babelPluginIstanbul() { | ||
|  |   const data = _interopRequireDefault(require('babel-plugin-istanbul')); | ||
|  | 
 | ||
|  |   _babelPluginIstanbul = function _babelPluginIstanbul() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _convertSourceMap() { | ||
|  |   const data = _interopRequireDefault(require('convert-source-map')); | ||
|  | 
 | ||
|  |   _convertSourceMap = function _convertSourceMap() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _jestHasteMap() { | ||
|  |   const data = _interopRequireDefault(require('jest-haste-map')); | ||
|  | 
 | ||
|  |   _jestHasteMap = function _jestHasteMap() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _fastJsonStableStringify() { | ||
|  |   const data = _interopRequireDefault(require('fast-json-stable-stringify')); | ||
|  | 
 | ||
|  |   _fastJsonStableStringify = function _fastJsonStableStringify() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _slash() { | ||
|  |   const data = _interopRequireDefault(require('slash')); | ||
|  | 
 | ||
|  |   _slash = function _slash() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _writeFileAtomic() { | ||
|  |   const data = _interopRequireDefault(require('write-file-atomic')); | ||
|  | 
 | ||
|  |   _writeFileAtomic = function _writeFileAtomic() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | function _realpathNative() { | ||
|  |   const data = require('realpath-native'); | ||
|  | 
 | ||
|  |   _realpathNative = function _realpathNative() { | ||
|  |     return data; | ||
|  |   }; | ||
|  | 
 | ||
|  |   return data; | ||
|  | } | ||
|  | 
 | ||
|  | var _shouldInstrument = _interopRequireDefault(require('./shouldInstrument')); | ||
|  | 
 | ||
|  | var _enhanceUnexpectedTokenMessage = _interopRequireDefault( | ||
|  |   require('./enhanceUnexpectedTokenMessage') | ||
|  | ); | ||
|  | 
 | ||
|  | function _interopRequireDefault(obj) { | ||
|  |   return obj && obj.__esModule ? obj : {default: obj}; | ||
|  | } | ||
|  | 
 | ||
|  | function _defineProperty(obj, key, value) { | ||
|  |   if (key in obj) { | ||
|  |     Object.defineProperty(obj, key, { | ||
|  |       value: value, | ||
|  |       enumerable: true, | ||
|  |       configurable: true, | ||
|  |       writable: true | ||
|  |     }); | ||
|  |   } else { | ||
|  |     obj[key] = value; | ||
|  |   } | ||
|  |   return obj; | ||
|  | } | ||
|  | 
 | ||
|  | // Use `require` to avoid TS rootDir
 | ||
|  | const _require = require('../package.json'), | ||
|  |   VERSION = _require.version; // This data structure is used to avoid recalculating some data every time that
 | ||
|  | // we need to transform a file. Since ScriptTransformer is instantiated for each
 | ||
|  | // file we need to keep this object in the local scope of this module.
 | ||
|  | 
 | ||
|  | const projectCaches = new WeakMap(); // To reset the cache for specific changesets (rather than package version).
 | ||
|  | 
 | ||
|  | const CACHE_VERSION = '1'; | ||
|  | 
 | ||
|  | class ScriptTransformer { | ||
|  |   constructor(config) { | ||
|  |     _defineProperty(this, '_cache', void 0); | ||
|  | 
 | ||
|  |     _defineProperty(this, '_config', void 0); | ||
|  | 
 | ||
|  |     _defineProperty(this, '_transformCache', void 0); | ||
|  | 
 | ||
|  |     this._config = config; | ||
|  |     this._transformCache = new Map(); | ||
|  |     let projectCache = projectCaches.get(config); | ||
|  | 
 | ||
|  |     if (!projectCache) { | ||
|  |       projectCache = { | ||
|  |         configString: (0, _fastJsonStableStringify().default)(this._config), | ||
|  |         ignorePatternsRegExp: calcIgnorePatternRegExp(this._config), | ||
|  |         transformRegExp: calcTransformRegExp(this._config), | ||
|  |         transformedFiles: new Map() | ||
|  |       }; | ||
|  |       projectCaches.set(config, projectCache); | ||
|  |     } | ||
|  | 
 | ||
|  |     this._cache = projectCache; | ||
|  |   } | ||
|  | 
 | ||
|  |   _getCacheKey(fileData, filename, instrument) { | ||
|  |     const configString = this._cache.configString; | ||
|  | 
 | ||
|  |     const transformer = this._getTransformer(filename); | ||
|  | 
 | ||
|  |     if (transformer && typeof transformer.getCacheKey === 'function') { | ||
|  |       return _crypto() | ||
|  |         .default.createHash('md5') | ||
|  |         .update( | ||
|  |           transformer.getCacheKey(fileData, filename, configString, { | ||
|  |             config: this._config, | ||
|  |             instrument, | ||
|  |             rootDir: this._config.rootDir | ||
|  |           }) | ||
|  |         ) | ||
|  |         .update(CACHE_VERSION) | ||
|  |         .digest('hex'); | ||
|  |     } else { | ||
|  |       return _crypto() | ||
|  |         .default.createHash('md5') | ||
|  |         .update(fileData) | ||
|  |         .update(configString) | ||
|  |         .update(instrument ? 'instrument' : '') | ||
|  |         .update(filename) | ||
|  |         .update(CACHE_VERSION) | ||
|  |         .digest('hex'); | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   _getFileCachePath(filename, content, instrument) { | ||
|  |     const baseCacheDir = _jestHasteMap().default.getCacheFilePath( | ||
|  |       this._config.cacheDirectory, | ||
|  |       'jest-transform-cache-' + this._config.name, | ||
|  |       VERSION | ||
|  |     ); | ||
|  | 
 | ||
|  |     const cacheKey = this._getCacheKey(content, filename, instrument); // Create sub folders based on the cacheKey to avoid creating one
 | ||
|  |     // directory with many files.
 | ||
|  | 
 | ||
|  |     const cacheDir = _path().default.join( | ||
|  |       baseCacheDir, | ||
|  |       cacheKey[0] + cacheKey[1] | ||
|  |     ); | ||
|  | 
 | ||
|  |     const cacheFilenamePrefix = _path() | ||
|  |       .default.basename(filename, _path().default.extname(filename)) | ||
|  |       .replace(/\W/g, ''); | ||
|  | 
 | ||
|  |     const cachePath = (0, _slash().default)( | ||
|  |       _path().default.join(cacheDir, cacheFilenamePrefix + '_' + cacheKey) | ||
|  |     ); | ||
|  |     (0, _jestUtil().createDirectory)(cacheDir); | ||
|  |     return cachePath; | ||
|  |   } | ||
|  | 
 | ||
|  |   _getTransformPath(filename) { | ||
|  |     const transformRegExp = this._cache.transformRegExp; | ||
|  | 
 | ||
|  |     if (!transformRegExp) { | ||
|  |       return undefined; | ||
|  |     } | ||
|  | 
 | ||
|  |     for (let i = 0; i < transformRegExp.length; i++) { | ||
|  |       if (transformRegExp[i][0].test(filename)) { | ||
|  |         return transformRegExp[i][1]; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     return undefined; | ||
|  |   } | ||
|  | 
 | ||
|  |   _getTransformer(filename) { | ||
|  |     let transform = null; | ||
|  | 
 | ||
|  |     if (!this._config.transform || !this._config.transform.length) { | ||
|  |       return null; | ||
|  |     } | ||
|  | 
 | ||
|  |     const transformPath = this._getTransformPath(filename); | ||
|  | 
 | ||
|  |     if (transformPath) { | ||
|  |       const transformer = this._transformCache.get(transformPath); | ||
|  | 
 | ||
|  |       if (transformer != null) { | ||
|  |         return transformer; | ||
|  |       } | ||
|  | 
 | ||
|  |       transform = require(transformPath); | ||
|  | 
 | ||
|  |       if (typeof transform.createTransformer === 'function') { | ||
|  |         transform = transform.createTransformer(); | ||
|  |       } | ||
|  | 
 | ||
|  |       if (typeof transform.process !== 'function') { | ||
|  |         throw new TypeError( | ||
|  |           'Jest: a transform must export a `process` function.' | ||
|  |         ); | ||
|  |       } | ||
|  | 
 | ||
|  |       this._transformCache.set(transformPath, transform); | ||
|  |     } | ||
|  | 
 | ||
|  |     return transform; | ||
|  |   } | ||
|  | 
 | ||
|  |   _instrumentFile(filename, content) { | ||
|  |     const result = (0, _core().transformSync)(content, { | ||
|  |       auxiliaryCommentBefore: ' istanbul ignore next ', | ||
|  |       babelrc: false, | ||
|  |       caller: { | ||
|  |         name: '@jest/transform', | ||
|  |         supportsStaticESM: false | ||
|  |       }, | ||
|  |       configFile: false, | ||
|  |       filename, | ||
|  |       plugins: [ | ||
|  |         [ | ||
|  |           _babelPluginIstanbul().default, | ||
|  |           { | ||
|  |             compact: false, | ||
|  |             // files outside `cwd` will not be instrumented
 | ||
|  |             cwd: this._config.rootDir, | ||
|  |             exclude: [], | ||
|  |             useInlineSourceMaps: false | ||
|  |           } | ||
|  |         ] | ||
|  |       ] | ||
|  |     }); | ||
|  | 
 | ||
|  |     if (result) { | ||
|  |       const code = result.code; | ||
|  | 
 | ||
|  |       if (code) { | ||
|  |         return code; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     return content; | ||
|  |   } | ||
|  | 
 | ||
|  |   _getRealPath(filepath) { | ||
|  |     try { | ||
|  |       return (0, _realpathNative().sync)(filepath) || filepath; | ||
|  |     } catch (err) { | ||
|  |       return filepath; | ||
|  |     } | ||
|  |   } // We don't want to expose transformers to the outside - this function is just
 | ||
|  |   // to warm up `this._transformCache`
 | ||
|  | 
 | ||
|  |   preloadTransformer(filepath) { | ||
|  |     this._getTransformer(filepath); | ||
|  |   } | ||
|  | 
 | ||
|  |   transformSource(filepath, content, instrument) { | ||
|  |     const filename = this._getRealPath(filepath); | ||
|  | 
 | ||
|  |     const transform = this._getTransformer(filename); | ||
|  | 
 | ||
|  |     const cacheFilePath = this._getFileCachePath(filename, content, instrument); | ||
|  | 
 | ||
|  |     let sourceMapPath = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache)
 | ||
|  | 
 | ||
|  |     let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null; | ||
|  |     const shouldCallTransform = transform && this.shouldTransform(filename); // That means that the transform has a custom instrumentation
 | ||
|  |     // logic and will handle it based on `config.collectCoverage` option
 | ||
|  | 
 | ||
|  |     const transformWillInstrument = | ||
|  |       shouldCallTransform && transform && transform.canInstrument; // If we handle the coverage instrumentation, we should try to map code
 | ||
|  |     // coverage against original source with any provided source map
 | ||
|  | 
 | ||
|  |     const mapCoverage = instrument && !transformWillInstrument; | ||
|  | 
 | ||
|  |     if (code) { | ||
|  |       // This is broken: we return the code, and a path for the source map
 | ||
|  |       // directly from the cache. But, nothing ensures the source map actually
 | ||
|  |       // matches that source code. They could have gotten out-of-sync in case
 | ||
|  |       // two separate processes write concurrently to the same cache files.
 | ||
|  |       return { | ||
|  |         code, | ||
|  |         mapCoverage, | ||
|  |         sourceMapPath | ||
|  |       }; | ||
|  |     } | ||
|  | 
 | ||
|  |     let transformed = { | ||
|  |       code: content, | ||
|  |       map: null | ||
|  |     }; | ||
|  | 
 | ||
|  |     if (transform && shouldCallTransform) { | ||
|  |       const processed = transform.process(content, filename, this._config, { | ||
|  |         instrument | ||
|  |       }); | ||
|  | 
 | ||
|  |       if (typeof processed === 'string') { | ||
|  |         transformed.code = processed; | ||
|  |       } else if (processed != null && typeof processed.code === 'string') { | ||
|  |         transformed = processed; | ||
|  |       } else { | ||
|  |         throw new TypeError( | ||
|  |           "Jest: a transform's `process` function must return a string, " + | ||
|  |             'or an object with `code` key containing this string.' | ||
|  |         ); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!transformed.map) { | ||
|  |       //Could be a potential freeze here.
 | ||
|  |       //See: https://github.com/facebook/jest/pull/5177#discussion_r158883570
 | ||
|  |       const inlineSourceMap = _convertSourceMap().default.fromSource( | ||
|  |         transformed.code | ||
|  |       ); | ||
|  | 
 | ||
|  |       if (inlineSourceMap) { | ||
|  |         transformed.map = inlineSourceMap.toJSON(); | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     if (!transformWillInstrument && instrument) { | ||
|  |       code = this._instrumentFile(filename, transformed.code); | ||
|  |     } else { | ||
|  |       code = transformed.code; | ||
|  |     } | ||
|  | 
 | ||
|  |     if (transformed.map) { | ||
|  |       const sourceMapContent = | ||
|  |         typeof transformed.map === 'string' | ||
|  |           ? transformed.map | ||
|  |           : JSON.stringify(transformed.map); | ||
|  |       writeCacheFile(sourceMapPath, sourceMapContent); | ||
|  |     } else { | ||
|  |       sourceMapPath = null; | ||
|  |     } | ||
|  | 
 | ||
|  |     writeCodeCacheFile(cacheFilePath, code); | ||
|  |     return { | ||
|  |       code, | ||
|  |       mapCoverage, | ||
|  |       sourceMapPath | ||
|  |     }; | ||
|  |   } | ||
|  | 
 | ||
|  |   _transformAndBuildScript(filename, options, instrument, fileSource) { | ||
|  |     const isInternalModule = !!(options && options.isInternalModule); | ||
|  |     const isCoreModule = !!(options && options.isCoreModule); | ||
|  |     const content = stripShebang( | ||
|  |       fileSource || _gracefulFs().default.readFileSync(filename, 'utf8') | ||
|  |     ); | ||
|  |     let wrappedCode; | ||
|  |     let sourceMapPath = null; | ||
|  |     let mapCoverage = false; | ||
|  |     const willTransform = | ||
|  |       !isInternalModule && | ||
|  |       !isCoreModule && | ||
|  |       (this.shouldTransform(filename) || instrument); | ||
|  | 
 | ||
|  |     try { | ||
|  |       const extraGlobals = (options && options.extraGlobals) || []; | ||
|  | 
 | ||
|  |       if (willTransform) { | ||
|  |         const transformedSource = this.transformSource( | ||
|  |           filename, | ||
|  |           content, | ||
|  |           instrument | ||
|  |         ); | ||
|  |         wrappedCode = wrap(transformedSource.code, ...extraGlobals); | ||
|  |         sourceMapPath = transformedSource.sourceMapPath; | ||
|  |         mapCoverage = transformedSource.mapCoverage; | ||
|  |       } else { | ||
|  |         wrappedCode = wrap(content, ...extraGlobals); | ||
|  |       } | ||
|  | 
 | ||
|  |       return { | ||
|  |         mapCoverage, | ||
|  |         script: new (_vm()).default.Script(wrappedCode, { | ||
|  |           displayErrors: true, | ||
|  |           filename: isCoreModule ? 'jest-nodejs-core-' + filename : filename | ||
|  |         }), | ||
|  |         sourceMapPath | ||
|  |       }; | ||
|  |     } catch (e) { | ||
|  |       if (e.codeFrame) { | ||
|  |         e.stack = e.message + '\n' + e.codeFrame; | ||
|  |       } | ||
|  | 
 | ||
|  |       if ( | ||
|  |         e instanceof SyntaxError && | ||
|  |         e.message.includes('Unexpected token') && | ||
|  |         !e.message.includes(' expected') | ||
|  |       ) { | ||
|  |         throw (0, _enhanceUnexpectedTokenMessage.default)(e); | ||
|  |       } | ||
|  | 
 | ||
|  |       throw e; | ||
|  |     } | ||
|  |   } | ||
|  | 
 | ||
|  |   transform(filename, options, fileSource) { | ||
|  |     let scriptCacheKey = undefined; | ||
|  |     let instrument = false; | ||
|  | 
 | ||
|  |     if (!options.isCoreModule) { | ||
|  |       instrument = (0, _shouldInstrument.default)( | ||
|  |         filename, | ||
|  |         options, | ||
|  |         this._config | ||
|  |       ); | ||
|  |       scriptCacheKey = getScriptCacheKey(filename, instrument); | ||
|  | 
 | ||
|  |       const result = this._cache.transformedFiles.get(scriptCacheKey); | ||
|  | 
 | ||
|  |       if (result) { | ||
|  |         return result; | ||
|  |       } | ||
|  |     } | ||
|  | 
 | ||
|  |     const result = this._transformAndBuildScript( | ||
|  |       filename, | ||
|  |       options, | ||
|  |       instrument, | ||
|  |       fileSource | ||
|  |     ); | ||
|  | 
 | ||
|  |     if (scriptCacheKey) { | ||
|  |       this._cache.transformedFiles.set(scriptCacheKey, result); | ||
|  |     } | ||
|  | 
 | ||
|  |     return result; | ||
|  |   } | ||
|  | 
 | ||
|  |   transformJson(filename, options, fileSource) { | ||
|  |     const isInternalModule = options.isInternalModule; | ||
|  |     const isCoreModule = options.isCoreModule; | ||
|  |     const willTransform = | ||
|  |       !isInternalModule && !isCoreModule && this.shouldTransform(filename); | ||
|  | 
 | ||
|  |     if (willTransform) { | ||
|  |       const _this$transformSource = this.transformSource( | ||
|  |           filename, | ||
|  |           fileSource, | ||
|  |           false | ||
|  |         ), | ||
|  |         transformedJsonSource = _this$transformSource.code; | ||
|  | 
 | ||
|  |       return transformedJsonSource; | ||
|  |     } | ||
|  | 
 | ||
|  |     return fileSource; | ||
|  |   } | ||
|  |   /** | ||
|  |    * @deprecated use `this.shouldTransform` instead | ||
|  |    */ | ||
|  |   // @ts-ignore: Unused and private - remove in Jest 25
 | ||
|  | 
 | ||
|  |   _shouldTransform(filename) { | ||
|  |     return this.shouldTransform(filename); | ||
|  |   } | ||
|  | 
 | ||
|  |   shouldTransform(filename) { | ||
|  |     const ignoreRegexp = this._cache.ignorePatternsRegExp; | ||
|  |     const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false; | ||
|  |     return ( | ||
|  |       !!this._config.transform && !!this._config.transform.length && !isIgnored | ||
|  |     ); | ||
|  |   } | ||
|  | } | ||
|  | 
 | ||
|  | exports.default = ScriptTransformer; | ||
|  | 
 | ||
|  | _defineProperty(ScriptTransformer, 'EVAL_RESULT_VARIABLE', void 0); | ||
|  | 
 | ||
|  | const removeFile = path => { | ||
|  |   try { | ||
|  |     _gracefulFs().default.unlinkSync(path); | ||
|  |   } catch (e) {} | ||
|  | }; | ||
|  | 
 | ||
|  | const stripShebang = content => { | ||
|  |   // If the file data starts with a shebang remove it. Leaves the empty line
 | ||
|  |   // to keep stack trace line numbers correct.
 | ||
|  |   if (content.startsWith('#!')) { | ||
|  |     return content.replace(/^#!.*/, ''); | ||
|  |   } else { | ||
|  |     return content; | ||
|  |   } | ||
|  | }; | ||
|  | /** | ||
|  |  * This is like `writeCacheFile` but with an additional sanity checksum. We | ||
|  |  * cannot use the same technique for source maps because we expose source map | ||
|  |  * cache file paths directly to callsites, with the expectation they can read | ||
|  |  * it right away. This is not a great system, because source map cache file | ||
|  |  * could get corrupted, out-of-sync, etc. | ||
|  |  */ | ||
|  | 
 | ||
|  | function writeCodeCacheFile(cachePath, code) { | ||
|  |   const checksum = _crypto() | ||
|  |     .default.createHash('md5') | ||
|  |     .update(code) | ||
|  |     .digest('hex'); | ||
|  | 
 | ||
|  |   writeCacheFile(cachePath, checksum + '\n' + code); | ||
|  | } | ||
|  | /** | ||
|  |  * Read counterpart of `writeCodeCacheFile`. We verify that the content of the | ||
|  |  * file matches the checksum, in case some kind of corruption happened. This | ||
|  |  * could happen if an older version of `jest-runtime` writes non-atomically to | ||
|  |  * the same cache, for example. | ||
|  |  */ | ||
|  | 
 | ||
|  | function readCodeCacheFile(cachePath) { | ||
|  |   const content = readCacheFile(cachePath); | ||
|  | 
 | ||
|  |   if (content == null) { | ||
|  |     return null; | ||
|  |   } | ||
|  | 
 | ||
|  |   const code = content.substr(33); | ||
|  | 
 | ||
|  |   const checksum = _crypto() | ||
|  |     .default.createHash('md5') | ||
|  |     .update(code) | ||
|  |     .digest('hex'); | ||
|  | 
 | ||
|  |   if (checksum === content.substr(0, 32)) { | ||
|  |     return code; | ||
|  |   } | ||
|  | 
 | ||
|  |   return null; | ||
|  | } | ||
|  | /** | ||
|  |  * Writing to the cache atomically relies on 'rename' being atomic on most | ||
|  |  * file systems. Doing atomic write reduces the risk of corruption by avoiding | ||
|  |  * two processes to write to the same file at the same time. It also reduces | ||
|  |  * the risk of reading a file that's being overwritten at the same time. | ||
|  |  */ | ||
|  | 
 | ||
|  | const writeCacheFile = (cachePath, fileData) => { | ||
|  |   try { | ||
|  |     _writeFileAtomic().default.sync(cachePath, fileData, { | ||
|  |       encoding: 'utf8' | ||
|  |     }); | ||
|  |   } catch (e) { | ||
|  |     if (cacheWriteErrorSafeToIgnore(e, cachePath)) { | ||
|  |       return; | ||
|  |     } | ||
|  | 
 | ||
|  |     e.message = | ||
|  |       'jest: failed to cache transform results in: ' + | ||
|  |       cachePath + | ||
|  |       '\nFailure message: ' + | ||
|  |       e.message; | ||
|  |     removeFile(cachePath); | ||
|  |     throw e; | ||
|  |   } | ||
|  | }; | ||
|  | /** | ||
|  |  * On Windows, renames are not atomic, leading to EPERM exceptions when two | ||
|  |  * processes attempt to rename to the same target file at the same time. | ||
|  |  * If the target file exists we can be reasonably sure another process has | ||
|  |  * legitimately won a cache write race and ignore the error. | ||
|  |  */ | ||
|  | 
 | ||
|  | const cacheWriteErrorSafeToIgnore = (e, cachePath) => | ||
|  |   process.platform === 'win32' && | ||
|  |   e.code === 'EPERM' && | ||
|  |   _gracefulFs().default.existsSync(cachePath); | ||
|  | 
 | ||
|  | const readCacheFile = cachePath => { | ||
|  |   if (!_gracefulFs().default.existsSync(cachePath)) { | ||
|  |     return null; | ||
|  |   } | ||
|  | 
 | ||
|  |   let fileData; | ||
|  | 
 | ||
|  |   try { | ||
|  |     fileData = _gracefulFs().default.readFileSync(cachePath, 'utf8'); | ||
|  |   } catch (e) { | ||
|  |     e.message = | ||
|  |       'jest: failed to read cache file: ' + | ||
|  |       cachePath + | ||
|  |       '\nFailure message: ' + | ||
|  |       e.message; | ||
|  |     removeFile(cachePath); | ||
|  |     throw e; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (fileData == null) { | ||
|  |     // We must have somehow created the file but failed to write to it,
 | ||
|  |     // let's delete it and retry.
 | ||
|  |     removeFile(cachePath); | ||
|  |   } | ||
|  | 
 | ||
|  |   return fileData; | ||
|  | }; | ||
|  | 
 | ||
|  | const getScriptCacheKey = (filename, instrument) => { | ||
|  |   const mtime = _gracefulFs().default.statSync(filename).mtime; | ||
|  | 
 | ||
|  |   return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : ''); | ||
|  | }; | ||
|  | 
 | ||
|  | const calcIgnorePatternRegExp = config => { | ||
|  |   if ( | ||
|  |     !config.transformIgnorePatterns || | ||
|  |     config.transformIgnorePatterns.length === 0 | ||
|  |   ) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   return new RegExp(config.transformIgnorePatterns.join('|')); | ||
|  | }; | ||
|  | 
 | ||
|  | const calcTransformRegExp = config => { | ||
|  |   if (!config.transform.length) { | ||
|  |     return; | ||
|  |   } | ||
|  | 
 | ||
|  |   const transformRegexp = []; | ||
|  | 
 | ||
|  |   for (let i = 0; i < config.transform.length; i++) { | ||
|  |     transformRegexp.push([ | ||
|  |       new RegExp(config.transform[i][0]), | ||
|  |       config.transform[i][1] | ||
|  |     ]); | ||
|  |   } | ||
|  | 
 | ||
|  |   return transformRegexp; | ||
|  | }; | ||
|  | 
 | ||
|  | const wrap = (content, ...extras) => { | ||
|  |   const globals = new Set([ | ||
|  |     'module', | ||
|  |     'exports', | ||
|  |     'require', | ||
|  |     '__dirname', | ||
|  |     '__filename', | ||
|  |     'global', | ||
|  |     'jest', | ||
|  |     ...extras | ||
|  |   ]); | ||
|  |   return ( | ||
|  |     '({"' + | ||
|  |     ScriptTransformer.EVAL_RESULT_VARIABLE + | ||
|  |     `":function(${Array.from(globals).join(',')}){` + | ||
|  |     content + | ||
|  |     '\n}});' | ||
|  |   ); | ||
|  | }; // TODO: Can this be added to the static property?
 | ||
|  | 
 | ||
|  | ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.<anonymous>'; |