'use strict';Object.defineProperty(exports, "__esModule", { value: true });var _crypto; function _load_crypto() {return _crypto = _interopRequireDefault(require('crypto'));}var _path; function _load_path() {return _path = _interopRequireDefault(require('path'));}var _vm; function _load_vm() {return _vm = _interopRequireDefault(require('vm'));}var _jestUtil; function _load_jestUtil() {return _jestUtil = require('jest-util');}var _gracefulFs; function _load_gracefulFs() {return _gracefulFs = _interopRequireDefault(require('graceful-fs'));}var _babelCore; function _load_babelCore() {return _babelCore = require('babel-core');}var _babelPluginIstanbul; function _load_babelPluginIstanbul() {return _babelPluginIstanbul = _interopRequireDefault(require('babel-plugin-istanbul'));}var _convertSourceMap; function _load_convertSourceMap() {return _convertSourceMap = _interopRequireDefault(require('convert-source-map'));}var _jestHasteMap; function _load_jestHasteMap() {return _jestHasteMap = _interopRequireDefault(require('jest-haste-map'));}var _jsonStableStringify; function _load_jsonStableStringify() {return _jsonStableStringify = _interopRequireDefault(require('json-stable-stringify'));}var _slash; function _load_slash() {return _slash = _interopRequireDefault(require('slash'));}var _package; function _load_package() {return _package = require('../package.json');}var _should_instrument; function _load_should_instrument() {return _should_instrument = _interopRequireDefault(require('./should_instrument'));}var _writeFileAtomic; function _load_writeFileAtomic() {return _writeFileAtomic = _interopRequireDefault(require('write-file-atomic'));}var _realpathNative; function _load_realpathNative() {return _realpathNative = require('realpath-native');}function _interopRequireDefault(obj) {return obj && obj.__esModule ? obj : { default: obj };} const cache = new Map(); /** * Copyright (c) 2014-present, Facebook, Inc. All rights reserved. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * * */const configToJsonMap = new Map(); // Cache regular expressions to test whether the file needs to be preprocessed const ignoreCache = new WeakMap(); // To reset the cache for specific changesets (rather than package version). const CACHE_VERSION = '1';class ScriptTransformer { constructor(config) { this._config = config; this._transformCache = new Map(); } _getCacheKey( fileData, filename, instrument, mapCoverage) { if (!configToJsonMap.has(this._config)) { // We only need this set of config options that can likely influence // cached output instead of all config options. configToJsonMap.set(this._config, (0, (_jsonStableStringify || _load_jsonStableStringify()).default)(this._config)); } const configString = configToJsonMap.get(this._config) || ''; const transformer = this._getTransformer(filename); if (transformer && typeof transformer.getCacheKey === 'function') { return (_crypto || _load_crypto()).default. createHash('md5'). update( transformer.getCacheKey(fileData, filename, configString, { instrument, mapCoverage, rootDir: this._config.rootDir })). update(CACHE_VERSION). digest('hex'); } else { return (_crypto || _load_crypto()).default. createHash('md5'). update(fileData). update(configString). update(instrument ? 'instrument' : ''). update(mapCoverage ? 'mapCoverage' : ''). update(CACHE_VERSION). digest('hex'); } } _getFileCachePath( filename, content, instrument, mapCoverage) { const baseCacheDir = (_jestHasteMap || _load_jestHasteMap()).default.getCacheFilePath( this._config.cacheDirectory, 'jest-transform-cache-' + this._config.name, (_package || _load_package()).version); const cacheKey = this._getCacheKey( content, filename, instrument, mapCoverage); // Create sub folders based on the cacheKey to avoid creating one // directory with many files. const cacheDir = (_path || _load_path()).default.join(baseCacheDir, cacheKey[0] + cacheKey[1]); const cachePath = (0, (_slash || _load_slash()).default)( (_path || _load_path()).default.join( cacheDir, (_path || _load_path()).default.basename(filename, (_path || _load_path()).default.extname(filename)) + '_' + cacheKey)); (0, (_jestUtil || _load_jestUtil()).createDirectory)(cacheDir); return cachePath; } _getTransformPath(filename) { for (let i = 0; i < this._config.transform.length; i++) { if (new RegExp(this._config.transform[i][0]).test(filename)) { return this._config.transform[i][1]; } } return null; } _getTransformer(filename) { let transform; 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; } // $FlowFixMe transform = require(transformPath); if (typeof transform.process !== 'function') { throw new TypeError( 'Jest: a transform must export a `process` function.'); } if (typeof transform.createTransformer === 'function') { transform = transform.createTransformer(); } this._transformCache.set(transformPath, transform); } return transform; } _instrumentFile(filename, content) { return (0, (_babelCore || _load_babelCore()).transform)(content, { auxiliaryCommentBefore: ' istanbul ignore next ', babelrc: false, filename, plugins: [ [(_babelPluginIstanbul || _load_babelPluginIstanbul()).default, { // files outside `cwd` will not be instrumented cwd: this._config.rootDir, exclude: [], useInlineSourceMaps: false }]], retainLines: true }). code; } _getRealPath(filepath) { try { return (0, (_realpathNative || _load_realpathNative()).sync)(filepath) || filepath; } catch (err) { return filepath; } } transformSource( filepath, content, instrument, mapCoverage) { const filename = this._getRealPath(filepath); const transform = this._getTransformer(filename); const cacheFilePath = this._getFileCachePath( filename, content, instrument, mapCoverage); let sourceMapPath = cacheFilePath + '.map'; // Ignore cache if `config.cache` is set (--no-cache) let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null; 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, sourceMapPath }; } let transformed = { code: content, map: null }; if (transform && shouldTransform(filename, this._config)) { 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 (mapCoverage) { if (!transformed.map) { const inlineSourceMap = (_convertSourceMap || _load_convertSourceMap()).default.fromSource(transformed.code); if (inlineSourceMap) { transformed.map = inlineSourceMap.toJSON(); } } } // That means that the transform has a custom instrumentation // logic and will handle it based on `config.collectCoverage` option const transformDidInstrument = transform && transform.canInstrument; if (!transformDidInstrument && instrument) { code = this._instrumentFile(filename, transformed.code); } else { code = transformed.code; } if (instrument && mapCoverage && 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, sourceMapPath }; } _transformAndBuildScript( filename, options, instrument, fileSource) { const isInternalModule = !!(options && options.isInternalModule); const isCoreModule = !!(options && options.isCoreModule); const content = stripShebang( fileSource || (_gracefulFs || _load_gracefulFs()).default.readFileSync(filename, 'utf8')); let wrappedCode; let sourceMapPath = null; const willTransform = !isInternalModule && !isCoreModule && ( shouldTransform(filename, this._config) || instrument); try { if (willTransform) { const transformedSource = this.transformSource( filename, content, instrument, !!(options && options.mapCoverage)); wrappedCode = wrap(transformedSource.code); sourceMapPath = transformedSource.sourceMapPath; } else { wrappedCode = wrap(content); } return { script: new (_vm || _load_vm()).default.Script(wrappedCode, { displayErrors: true, filename: isCoreModule ? 'jest-nodejs-core-' + filename : filename }), sourceMapPath }; } catch (e) { if (e.codeFrame) { e.stack = e.codeFrame; } throw e; } } transform( filename, options, fileSource) { let scriptCacheKey = null; let instrument = false; let result = ''; if (!options.isCoreModule) { instrument = (0, (_should_instrument || _load_should_instrument()).default)(filename, options, this._config); scriptCacheKey = getScriptCacheKey(filename, this._config, instrument); result = cache.get(scriptCacheKey); } if (result) { return result; } result = this._transformAndBuildScript( filename, options, instrument, fileSource); if (scriptCacheKey) { cache.set(scriptCacheKey, result); } return result; }}exports.default = ScriptTransformer; const removeFile = path => { try { (_gracefulFs || _load_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 || _load_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 || _load_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 || _load_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) => { return ( process.platform === 'win32' && e.code === 'EPERM' && (_gracefulFs || _load_gracefulFs()).default.existsSync(cachePath)); }; const readCacheFile = cachePath => { if (!(_gracefulFs || _load_gracefulFs()).default.existsSync(cachePath)) { return null; } let fileData; try { fileData = (_gracefulFs || _load_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, config, instrument) => { const mtime = (_gracefulFs || _load_gracefulFs()).default.statSync(filename).mtime; return filename + '_' + mtime.getTime() + (instrument ? '_instrumented' : ''); }; const shouldTransform = (filename, config) => { if (!ignoreCache.has(config)) { if ( !config.transformIgnorePatterns || config.transformIgnorePatterns.length === 0) { ignoreCache.set(config, null); } else { ignoreCache.set( config, new RegExp(config.transformIgnorePatterns.join('|'))); } } const ignoreRegexp = ignoreCache.get(config); const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false; return !!config.transform && !!config.transform.length && !isIgnored; }; const wrap = content => '({"' + ScriptTransformer.EVAL_RESULT_VARIABLE + '":function(module,exports,require,__dirname,__filename,global,jest){' + content + '\n}});'; ScriptTransformer.EVAL_RESULT_VARIABLE = 'Object.';