123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619 |
- '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 _fastJsonStableStringify;
- function _load_fastJsonStableStringify() {
- return (_fastJsonStableStringify = _interopRequireDefault(
- require('fast-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'));
- }
- var _helpers;
- function _load_helpers() {
- return (_helpers = require('./helpers'));
- }
- 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) {
- 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,
- (_fastJsonStableStringify || _load_fastJsonStableStringify()).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: instrument,
- 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(CACHE_VERSION)
- .digest('hex');
- }
- }
- _getFileCachePath(filename, content, instrument) {
- 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);
- // 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.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) {
- return (0, (_babelCore || _load_babelCore()).transform)(content, {
- auxiliaryCommentBefore: ' istanbul ignore next ',
- babelrc: false,
- filename: filename,
- plugins: [
- [
- (_babelPluginIstanbul || _load_babelPluginIstanbul()).default,
- {
- compact: false,
- // files outside `cwd` will not be instrumented
- cwd: this._config.rootDir,
- exclude: [],
- useInlineSourceMaps: false
- }
- ]
- ]
- }).code;
- }
- _getRealPath(filepath) {
- try {
- return (
- (0, (_realpathNative || _load_realpathNative()).sync)(filepath) ||
- filepath
- );
- } catch (err) {
- return 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 && shouldTransform(filename, this._config);
- // 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: code,
- mapCoverage: mapCoverage,
- sourceMapPath: sourceMapPath
- };
- }
- let transformed = {
- code: content,
- map: null
- };
- if (transform && shouldCallTransform) {
- const processed = transform.process(content, filename, this._config, {
- instrument: 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 || _load_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: code,
- mapCoverage: mapCoverage,
- sourceMapPath: 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;
- let mapCoverage = false;
- const willTransform =
- !isInternalModule &&
- !isCoreModule &&
- (shouldTransform(filename, this._config) || instrument);
- try {
- if (willTransform) {
- const transformedSource = this.transformSource(
- filename,
- content,
- instrument
- );
- wrappedCode = wrap(transformedSource.code);
- sourceMapPath = transformedSource.sourceMapPath;
- mapCoverage = transformedSource.mapCoverage;
- } else {
- wrappedCode = wrap(content);
- }
- return {
- mapCoverage: mapCoverage,
- script: new (_vm || _load_vm()).default.Script(wrappedCode, {
- displayErrors: true,
- filename: isCoreModule ? 'jest-nodejs-core-' + filename : filename
- }),
- sourceMapPath: sourceMapPath
- };
- } catch (e) {
- if (e.codeFrame) {
- e.stack = e.codeFrame;
- }
- if (
- e instanceof SyntaxError &&
- e.message.includes('Unexpected token') &&
- !e.message.includes(' expected')
- ) {
- throw (0, (_helpers || _load_helpers()).enhanceUnexpectedTokenMessage)(
- e
- );
- }
- 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, 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) =>
- 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, 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.<anonymous>';
|