UNPKG

30.8 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6exports.createScriptTransformer = createScriptTransformer;
7exports.createTranspilingRequire = createTranspilingRequire;
8function _crypto() {
9 const data = require('crypto');
10 _crypto = function () {
11 return data;
12 };
13 return data;
14}
15function path() {
16 const data = _interopRequireWildcard(require('path'));
17 path = function () {
18 return data;
19 };
20 return data;
21}
22function _core() {
23 const data = require('@babel/core');
24 _core = function () {
25 return data;
26 };
27 return data;
28}
29function _babelPluginIstanbul() {
30 const data = _interopRequireDefault(require('babel-plugin-istanbul'));
31 _babelPluginIstanbul = function () {
32 return data;
33 };
34 return data;
35}
36function _convertSourceMap() {
37 const data = require('convert-source-map');
38 _convertSourceMap = function () {
39 return data;
40 };
41 return data;
42}
43function _fastJsonStableStringify() {
44 const data = _interopRequireDefault(require('fast-json-stable-stringify'));
45 _fastJsonStableStringify = function () {
46 return data;
47 };
48 return data;
49}
50function fs() {
51 const data = _interopRequireWildcard(require('graceful-fs'));
52 fs = function () {
53 return data;
54 };
55 return data;
56}
57function _pirates() {
58 const data = require('pirates');
59 _pirates = function () {
60 return data;
61 };
62 return data;
63}
64function _slash() {
65 const data = _interopRequireDefault(require('slash'));
66 _slash = function () {
67 return data;
68 };
69 return data;
70}
71function _writeFileAtomic() {
72 const data = require('write-file-atomic');
73 _writeFileAtomic = function () {
74 return data;
75 };
76 return data;
77}
78function _jestHasteMap() {
79 const data = _interopRequireDefault(require('jest-haste-map'));
80 _jestHasteMap = function () {
81 return data;
82 };
83 return data;
84}
85function _jestUtil() {
86 const data = require('jest-util');
87 _jestUtil = function () {
88 return data;
89 };
90 return data;
91}
92var _enhanceUnexpectedTokenMessage = _interopRequireDefault(
93 require('./enhanceUnexpectedTokenMessage')
94);
95var _runtimeErrorsAndWarnings = require('./runtimeErrorsAndWarnings');
96var _shouldInstrument = _interopRequireDefault(require('./shouldInstrument'));
97function _interopRequireDefault(obj) {
98 return obj && obj.__esModule ? obj : {default: obj};
99}
100function _getRequireWildcardCache(nodeInterop) {
101 if (typeof WeakMap !== 'function') return null;
102 var cacheBabelInterop = new WeakMap();
103 var cacheNodeInterop = new WeakMap();
104 return (_getRequireWildcardCache = function (nodeInterop) {
105 return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
106 })(nodeInterop);
107}
108function _interopRequireWildcard(obj, nodeInterop) {
109 if (!nodeInterop && obj && obj.__esModule) {
110 return obj;
111 }
112 if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
113 return {default: obj};
114 }
115 var cache = _getRequireWildcardCache(nodeInterop);
116 if (cache && cache.has(obj)) {
117 return cache.get(obj);
118 }
119 var newObj = {};
120 var hasPropertyDescriptor =
121 Object.defineProperty && Object.getOwnPropertyDescriptor;
122 for (var key in obj) {
123 if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
124 var desc = hasPropertyDescriptor
125 ? Object.getOwnPropertyDescriptor(obj, key)
126 : null;
127 if (desc && (desc.get || desc.set)) {
128 Object.defineProperty(newObj, key, desc);
129 } else {
130 newObj[key] = obj[key];
131 }
132 }
133 }
134 newObj.default = obj;
135 if (cache) {
136 cache.set(obj, newObj);
137 }
138 return newObj;
139}
140/**
141 * Copyright (c) Meta Platforms, Inc. and affiliates.
142 *
143 * This source code is licensed under the MIT license found in the
144 * LICENSE file in the root directory of this source tree.
145 */
146
147// @ts-expect-error: should just be `require.resolve`, but the tests mess that up
148
149// Use `require` to avoid TS rootDir
150const {version: VERSION} = require('../package.json');
151// This data structure is used to avoid recalculating some data every time that
152// we need to transform a file. Since ScriptTransformer is instantiated for each
153// file we need to keep this object in the local scope of this module.
154const projectCaches = new Map();
155
156// To reset the cache for specific changesets (rather than package version).
157const CACHE_VERSION = '1';
158async function waitForPromiseWithCleanup(promise, cleanup) {
159 try {
160 await promise;
161 } finally {
162 cleanup();
163 }
164}
165
166// type predicate
167function isTransformerFactory(t) {
168 return typeof t.createTransformer === 'function';
169}
170class ScriptTransformer {
171 _cache;
172 _transformCache = new Map();
173 _transformsAreLoaded = false;
174 constructor(_config, _cacheFS) {
175 this._config = _config;
176 this._cacheFS = _cacheFS;
177 const configString = (0, _fastJsonStableStringify().default)(this._config);
178 let projectCache = projectCaches.get(configString);
179 if (!projectCache) {
180 projectCache = {
181 configString,
182 ignorePatternsRegExp: calcIgnorePatternRegExp(this._config),
183 transformRegExp: calcTransformRegExp(this._config),
184 transformedFiles: new Map()
185 };
186 projectCaches.set(configString, projectCache);
187 }
188 this._cache = projectCache;
189 }
190 _buildCacheKeyFromFileInfo(
191 fileData,
192 filename,
193 transformOptions,
194 transformerCacheKey
195 ) {
196 if (transformerCacheKey != null) {
197 return (0, _crypto().createHash)('sha1')
198 .update(transformerCacheKey)
199 .update(CACHE_VERSION)
200 .digest('hex')
201 .substring(0, 32);
202 }
203 return (0, _crypto().createHash)('sha1')
204 .update(fileData)
205 .update(transformOptions.configString)
206 .update(transformOptions.instrument ? 'instrument' : '')
207 .update(filename)
208 .update(CACHE_VERSION)
209 .digest('hex')
210 .substring(0, 32);
211 }
212 _buildTransformCacheKey(pattern, filepath) {
213 return pattern + filepath;
214 }
215 _getCacheKey(fileData, filename, options) {
216 const configString = this._cache.configString;
217 const {transformer, transformerConfig = {}} =
218 this._getTransformer(filename) ?? {};
219 let transformerCacheKey = undefined;
220 const transformOptions = {
221 ...options,
222 cacheFS: this._cacheFS,
223 config: this._config,
224 configString,
225 transformerConfig
226 };
227 if (typeof transformer?.getCacheKey === 'function') {
228 transformerCacheKey = transformer.getCacheKey(
229 fileData,
230 filename,
231 transformOptions
232 );
233 }
234 return this._buildCacheKeyFromFileInfo(
235 fileData,
236 filename,
237 transformOptions,
238 transformerCacheKey
239 );
240 }
241 async _getCacheKeyAsync(fileData, filename, options) {
242 const configString = this._cache.configString;
243 const {transformer, transformerConfig = {}} =
244 this._getTransformer(filename) ?? {};
245 let transformerCacheKey = undefined;
246 const transformOptions = {
247 ...options,
248 cacheFS: this._cacheFS,
249 config: this._config,
250 configString,
251 transformerConfig
252 };
253 if (transformer) {
254 const getCacheKey =
255 transformer.getCacheKeyAsync ?? transformer.getCacheKey;
256 if (typeof getCacheKey === 'function') {
257 transformerCacheKey = await getCacheKey(
258 fileData,
259 filename,
260 transformOptions
261 );
262 }
263 }
264 return this._buildCacheKeyFromFileInfo(
265 fileData,
266 filename,
267 transformOptions,
268 transformerCacheKey
269 );
270 }
271 _createCachedFilename(filename, cacheKey) {
272 const HasteMapClass = _jestHasteMap().default.getStatic(this._config);
273 const baseCacheDir = HasteMapClass.getCacheFilePath(
274 this._config.cacheDirectory,
275 `jest-transform-cache-${this._config.id}`,
276 VERSION
277 );
278 // Create sub folders based on the cacheKey to avoid creating one
279 // directory with many files.
280 const cacheDir = path().join(baseCacheDir, cacheKey[0] + cacheKey[1]);
281 const cacheFilenamePrefix = path()
282 .basename(filename, path().extname(filename))
283 .replace(/\W/g, '');
284 return (0, _slash().default)(
285 path().join(cacheDir, `${cacheFilenamePrefix}_${cacheKey}`)
286 );
287 }
288 _getFileCachePath(filename, content, options) {
289 const cacheKey = this._getCacheKey(content, filename, options);
290 return this._createCachedFilename(filename, cacheKey);
291 }
292 async _getFileCachePathAsync(filename, content, options) {
293 const cacheKey = await this._getCacheKeyAsync(content, filename, options);
294 return this._createCachedFilename(filename, cacheKey);
295 }
296 _getTransformPatternAndPath(filename) {
297 const transformEntry = this._cache.transformRegExp;
298 if (transformEntry == null) {
299 return undefined;
300 }
301 for (let i = 0; i < transformEntry.length; i++) {
302 const [transformRegExp, transformPath] = transformEntry[i];
303 if (transformRegExp.test(filename)) {
304 return [transformRegExp.source, transformPath];
305 }
306 }
307 return undefined;
308 }
309 _getTransformPath(filename) {
310 const transformInfo = this._getTransformPatternAndPath(filename);
311 if (!Array.isArray(transformInfo)) {
312 return undefined;
313 }
314 return transformInfo[1];
315 }
316 async loadTransformers() {
317 await Promise.all(
318 this._config.transform.map(
319 async ([transformPattern, transformPath, transformerConfig], i) => {
320 let transformer = await (0, _jestUtil().requireOrImportModule)(
321 transformPath
322 );
323 if (transformer == null) {
324 throw new Error(
325 (0, _runtimeErrorsAndWarnings.makeInvalidTransformerError)(
326 transformPath
327 )
328 );
329 }
330 if (isTransformerFactory(transformer)) {
331 transformer = await transformer.createTransformer(
332 transformerConfig
333 );
334 }
335 if (
336 typeof transformer.process !== 'function' &&
337 typeof transformer.processAsync !== 'function'
338 ) {
339 throw new Error(
340 (0, _runtimeErrorsAndWarnings.makeInvalidTransformerError)(
341 transformPath
342 )
343 );
344 }
345 const res = {
346 transformer,
347 transformerConfig
348 };
349 const transformCacheKey = this._buildTransformCacheKey(
350 this._cache.transformRegExp?.[i]?.[0].source ??
351 new RegExp(transformPattern).source,
352 transformPath
353 );
354 this._transformCache.set(transformCacheKey, res);
355 }
356 )
357 );
358 this._transformsAreLoaded = true;
359 }
360 _getTransformer(filename) {
361 if (!this._transformsAreLoaded) {
362 throw new Error(
363 'Jest: Transformers have not been loaded yet - make sure to run `loadTransformers` and wait for it to complete before starting to transform files'
364 );
365 }
366 if (this._config.transform.length === 0) {
367 return null;
368 }
369 const transformPatternAndPath = this._getTransformPatternAndPath(filename);
370 if (!Array.isArray(transformPatternAndPath)) {
371 return null;
372 }
373 const [transformPattern, transformPath] = transformPatternAndPath;
374 const transformCacheKey = this._buildTransformCacheKey(
375 transformPattern,
376 transformPath
377 );
378 const transformer = this._transformCache.get(transformCacheKey);
379 if (transformer !== undefined) {
380 return transformer;
381 }
382 throw new Error(
383 `Jest was unable to load the transformer defined for ${filename}. This is a bug in Jest, please open up an issue`
384 );
385 }
386 _instrumentFile(filename, input, canMapToInput, options) {
387 const inputCode = typeof input === 'string' ? input : input.code;
388 const inputMap = typeof input === 'string' ? null : input.map;
389 const result = (0, _core().transformSync)(inputCode, {
390 auxiliaryCommentBefore: ' istanbul ignore next ',
391 babelrc: false,
392 caller: {
393 name: '@jest/transform',
394 supportsDynamicImport: options.supportsDynamicImport,
395 supportsExportNamespaceFrom: options.supportsExportNamespaceFrom,
396 supportsStaticESM: options.supportsStaticESM,
397 supportsTopLevelAwait: options.supportsTopLevelAwait
398 },
399 configFile: false,
400 filename,
401 plugins: [
402 [
403 _babelPluginIstanbul().default,
404 {
405 compact: false,
406 // files outside `cwd` will not be instrumented
407 cwd: this._config.rootDir,
408 exclude: [],
409 extension: false,
410 inputSourceMap: inputMap,
411 useInlineSourceMaps: false
412 }
413 ]
414 ],
415 sourceMaps: canMapToInput ? 'both' : false
416 });
417 if (result?.code != null) {
418 return result;
419 }
420 return input;
421 }
422 _buildTransformResult(
423 filename,
424 cacheFilePath,
425 content,
426 transformer,
427 shouldCallTransform,
428 options,
429 processed,
430 sourceMapPath
431 ) {
432 let transformed = {
433 code: content,
434 map: null
435 };
436 if (transformer && shouldCallTransform) {
437 if (processed != null && typeof processed.code === 'string') {
438 transformed = processed;
439 } else {
440 const transformPath = this._getTransformPath(filename);
441 (0, _jestUtil().invariant)(transformPath);
442 throw new Error(
443 (0, _runtimeErrorsAndWarnings.makeInvalidReturnValueError)(
444 transformPath
445 )
446 );
447 }
448 }
449 if (transformed.map == null || transformed.map === '') {
450 try {
451 //Could be a potential freeze here.
452 //See: https://github.com/jestjs/jest/pull/5177#discussion_r158883570
453 const inlineSourceMap = (0, _convertSourceMap().fromSource)(
454 transformed.code
455 );
456 if (inlineSourceMap) {
457 transformed.map = inlineSourceMap.toObject();
458 }
459 } catch {
460 const transformPath = this._getTransformPath(filename);
461 (0, _jestUtil().invariant)(transformPath);
462 console.warn(
463 (0, _runtimeErrorsAndWarnings.makeInvalidSourceMapWarning)(
464 filename,
465 transformPath
466 )
467 );
468 }
469 }
470
471 // That means that the transform has a custom instrumentation
472 // logic and will handle it based on `config.collectCoverage` option
473 const transformWillInstrument =
474 shouldCallTransform && transformer && transformer.canInstrument;
475
476 // Apply instrumentation to the code if necessary, keeping the instrumented code and new map
477 let map = transformed.map;
478 let code;
479 if (transformWillInstrument !== true && options.instrument) {
480 /**
481 * We can map the original source code to the instrumented code ONLY if
482 * - the process of transforming the code produced a source map e.g. ts-jest
483 * - we did not transform the source code
484 *
485 * Otherwise we cannot make any statements about how the instrumented code corresponds to the original code,
486 * and we should NOT emit any source maps
487 *
488 */
489 const shouldEmitSourceMaps =
490 (transformer != null && map != null) || transformer == null;
491 const instrumented = this._instrumentFile(
492 filename,
493 transformed,
494 shouldEmitSourceMaps,
495 options
496 );
497 code =
498 typeof instrumented === 'string' ? instrumented : instrumented.code;
499 map = typeof instrumented === 'string' ? null : instrumented.map;
500 } else {
501 code = transformed.code;
502 }
503 if (map != null) {
504 const sourceMapContent =
505 typeof map === 'string' ? map : JSON.stringify(map);
506 (0, _jestUtil().invariant)(
507 sourceMapPath,
508 'We should always have default sourceMapPath'
509 );
510 writeCacheFile(sourceMapPath, sourceMapContent);
511 } else {
512 sourceMapPath = null;
513 }
514 writeCodeCacheFile(cacheFilePath, code);
515 return {
516 code,
517 originalCode: content,
518 sourceMapPath
519 };
520 }
521 transformSource(filepath, content, options) {
522 const filename = (0, _jestUtil().tryRealpath)(filepath);
523 const {transformer, transformerConfig = {}} =
524 this._getTransformer(filename) ?? {};
525 const cacheFilePath = this._getFileCachePath(filename, content, options);
526 const sourceMapPath = `${cacheFilePath}.map`;
527 // Ignore cache if `config.cache` is set (--no-cache)
528 const code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null;
529 if (code != null) {
530 // This is broken: we return the code, and a path for the source map
531 // directly from the cache. But, nothing ensures the source map actually
532 // matches that source code. They could have gotten out-of-sync in case
533 // two separate processes write concurrently to the same cache files.
534 return {
535 code,
536 originalCode: content,
537 sourceMapPath
538 };
539 }
540 let processed = null;
541 let shouldCallTransform = false;
542 if (transformer && this.shouldTransform(filename)) {
543 shouldCallTransform = true;
544 assertSyncTransformer(transformer, this._getTransformPath(filename));
545 processed = transformer.process(content, filename, {
546 ...options,
547 cacheFS: this._cacheFS,
548 config: this._config,
549 configString: this._cache.configString,
550 transformerConfig
551 });
552 }
553 (0, _jestUtil().createDirectory)(path().dirname(cacheFilePath));
554 return this._buildTransformResult(
555 filename,
556 cacheFilePath,
557 content,
558 transformer,
559 shouldCallTransform,
560 options,
561 processed,
562 sourceMapPath
563 );
564 }
565 async transformSourceAsync(filepath, content, options) {
566 const filename = (0, _jestUtil().tryRealpath)(filepath);
567 const {transformer, transformerConfig = {}} =
568 this._getTransformer(filename) ?? {};
569 const cacheFilePath = await this._getFileCachePathAsync(
570 filename,
571 content,
572 options
573 );
574 const sourceMapPath = `${cacheFilePath}.map`;
575 // Ignore cache if `config.cache` is set (--no-cache)
576 const code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null;
577 if (code != null) {
578 // This is broken: we return the code, and a path for the source map
579 // directly from the cache. But, nothing ensures the source map actually
580 // matches that source code. They could have gotten out-of-sync in case
581 // two separate processes write concurrently to the same cache files.
582 return {
583 code,
584 originalCode: content,
585 sourceMapPath
586 };
587 }
588 let processed = null;
589 let shouldCallTransform = false;
590 if (transformer && this.shouldTransform(filename)) {
591 shouldCallTransform = true;
592 const process = transformer.processAsync ?? transformer.process;
593
594 // This is probably dead code since `_getTransformerAsync` already asserts this
595 (0, _jestUtil().invariant)(
596 typeof process === 'function',
597 'A transformer must always export either a `process` or `processAsync`'
598 );
599 processed = await process(content, filename, {
600 ...options,
601 cacheFS: this._cacheFS,
602 config: this._config,
603 configString: this._cache.configString,
604 transformerConfig
605 });
606 }
607 (0, _jestUtil().createDirectory)(path().dirname(cacheFilePath));
608 return this._buildTransformResult(
609 filename,
610 cacheFilePath,
611 content,
612 transformer,
613 shouldCallTransform,
614 options,
615 processed,
616 sourceMapPath
617 );
618 }
619 async _transformAndBuildScriptAsync(
620 filename,
621 options,
622 transformOptions,
623 fileSource
624 ) {
625 const {isInternalModule} = options;
626 let fileContent = fileSource ?? this._cacheFS.get(filename);
627 if (fileContent == null) {
628 fileContent = fs().readFileSync(filename, 'utf8');
629 this._cacheFS.set(filename, fileContent);
630 }
631 const content = stripShebang(fileContent);
632 let code = content;
633 let sourceMapPath = null;
634 const willTransform =
635 isInternalModule !== true &&
636 (transformOptions.instrument || this.shouldTransform(filename));
637 try {
638 if (willTransform) {
639 const transformedSource = await this.transformSourceAsync(
640 filename,
641 content,
642 transformOptions
643 );
644 code = transformedSource.code;
645 sourceMapPath = transformedSource.sourceMapPath;
646 }
647 return {
648 code,
649 originalCode: content,
650 sourceMapPath
651 };
652 } catch (e) {
653 if (!(e instanceof Error)) {
654 throw e;
655 }
656 throw (0, _enhanceUnexpectedTokenMessage.default)(e);
657 }
658 }
659 _transformAndBuildScript(filename, options, transformOptions, fileSource) {
660 const {isInternalModule} = options;
661 let fileContent = fileSource ?? this._cacheFS.get(filename);
662 if (fileContent == null) {
663 fileContent = fs().readFileSync(filename, 'utf8');
664 this._cacheFS.set(filename, fileContent);
665 }
666 const content = stripShebang(fileContent);
667 let code = content;
668 let sourceMapPath = null;
669 const willTransform =
670 isInternalModule !== true &&
671 (transformOptions.instrument || this.shouldTransform(filename));
672 try {
673 if (willTransform) {
674 const transformedSource = this.transformSource(
675 filename,
676 content,
677 transformOptions
678 );
679 code = transformedSource.code;
680 sourceMapPath = transformedSource.sourceMapPath;
681 }
682 return {
683 code,
684 originalCode: content,
685 sourceMapPath
686 };
687 } catch (e) {
688 if (!(e instanceof Error)) {
689 throw e;
690 }
691 throw (0, _enhanceUnexpectedTokenMessage.default)(e);
692 }
693 }
694 async transformAsync(filename, options, fileSource) {
695 const instrument =
696 options.coverageProvider === 'babel' &&
697 (0, _shouldInstrument.default)(filename, options, this._config);
698 const scriptCacheKey = getScriptCacheKey(filename, instrument);
699 let result = this._cache.transformedFiles.get(scriptCacheKey);
700 if (result) {
701 return result;
702 }
703 result = await this._transformAndBuildScriptAsync(
704 filename,
705 options,
706 {
707 ...options,
708 instrument
709 },
710 fileSource
711 );
712 if (scriptCacheKey) {
713 this._cache.transformedFiles.set(scriptCacheKey, result);
714 }
715 return result;
716 }
717 transform(filename, options, fileSource) {
718 const instrument =
719 options.coverageProvider === 'babel' &&
720 (0, _shouldInstrument.default)(filename, options, this._config);
721 const scriptCacheKey = getScriptCacheKey(filename, instrument);
722 let result = this._cache.transformedFiles.get(scriptCacheKey);
723 if (result) {
724 return result;
725 }
726 result = this._transformAndBuildScript(
727 filename,
728 options,
729 {
730 ...options,
731 instrument
732 },
733 fileSource
734 );
735 if (scriptCacheKey) {
736 this._cache.transformedFiles.set(scriptCacheKey, result);
737 }
738 return result;
739 }
740 transformJson(filename, options, fileSource) {
741 const {isInternalModule} = options;
742 const willTransform =
743 isInternalModule !== true && this.shouldTransform(filename);
744 if (willTransform) {
745 const {code: transformedJsonSource} = this.transformSource(
746 filename,
747 fileSource,
748 {
749 ...options,
750 instrument: false
751 }
752 );
753 return transformedJsonSource;
754 }
755 return fileSource;
756 }
757 async requireAndTranspileModule(
758 moduleName,
759 callback,
760 options = {
761 applyInteropRequireDefault: true,
762 instrument: false,
763 supportsDynamicImport: false,
764 supportsExportNamespaceFrom: false,
765 supportsStaticESM: false,
766 supportsTopLevelAwait: false
767 }
768 ) {
769 let transforming = false;
770 const {applyInteropRequireDefault, ...transformOptions} = options;
771 const revertHook = (0, _pirates().addHook)(
772 (code, filename) => {
773 try {
774 transforming = true;
775 return (
776 this.transformSource(filename, code, transformOptions).code || code
777 );
778 } finally {
779 transforming = false;
780 }
781 },
782 {
783 // Exclude `mjs` extension when addHook because pirates don't support hijack es module
784 exts: this._config.moduleFileExtensions
785 .filter(ext => ext !== 'mjs')
786 .map(ext => `.${ext}`),
787 ignoreNodeModules: false,
788 matcher: filename => {
789 if (transforming) {
790 // Don't transform any dependency required by the transformer itself
791 return false;
792 }
793 return this.shouldTransform(filename);
794 }
795 }
796 );
797 try {
798 const module = await (0, _jestUtil().requireOrImportModule)(
799 moduleName,
800 applyInteropRequireDefault
801 );
802 if (!callback) {
803 revertHook();
804 return module;
805 }
806 const cbResult = callback(module);
807 if ((0, _jestUtil().isPromise)(cbResult)) {
808 return await waitForPromiseWithCleanup(cbResult, revertHook).then(
809 () => module
810 );
811 }
812 return module;
813 } finally {
814 revertHook();
815 }
816 }
817 shouldTransform(filename) {
818 const ignoreRegexp = this._cache.ignorePatternsRegExp;
819 const isIgnored = ignoreRegexp ? ignoreRegexp.test(filename) : false;
820 return this._config.transform.length !== 0 && !isIgnored;
821 }
822}
823
824// TODO: do we need to define the generics twice?
825async function createTranspilingRequire(config) {
826 const transformer = await createScriptTransformer(config);
827 return async function requireAndTranspileModule(
828 resolverPath,
829 applyInteropRequireDefault = false
830 ) {
831 const transpiledModule = await transformer.requireAndTranspileModule(
832 resolverPath,
833 // eslint-disable-next-line @typescript-eslint/no-empty-function
834 () => {},
835 {
836 applyInteropRequireDefault,
837 instrument: false,
838 supportsDynamicImport: false,
839 // this might be true, depending on node version.
840 supportsExportNamespaceFrom: false,
841 supportsStaticESM: false,
842 supportsTopLevelAwait: false
843 }
844 );
845 return transpiledModule;
846 };
847}
848const removeFile = path => {
849 try {
850 fs().unlinkSync(path);
851 } catch {}
852};
853const stripShebang = content => {
854 // If the file data starts with a shebang remove it. Leaves the empty line
855 // to keep stack trace line numbers correct.
856 if (content.startsWith('#!')) {
857 return content.replace(/^#!.*/, '');
858 } else {
859 return content;
860 }
861};
862
863/**
864 * This is like `writeCacheFile` but with an additional sanity checksum. We
865 * cannot use the same technique for source maps because we expose source map
866 * cache file paths directly to callsites, with the expectation they can read
867 * it right away. This is not a great system, because source map cache file
868 * could get corrupted, out-of-sync, etc.
869 */
870function writeCodeCacheFile(cachePath, code) {
871 const checksum = (0, _crypto().createHash)('sha1')
872 .update(code)
873 .digest('hex')
874 .substring(0, 32);
875 writeCacheFile(cachePath, `${checksum}\n${code}`);
876}
877
878/**
879 * Read counterpart of `writeCodeCacheFile`. We verify that the content of the
880 * file matches the checksum, in case some kind of corruption happened. This
881 * could happen if an older version of `jest-runtime` writes non-atomically to
882 * the same cache, for example.
883 */
884function readCodeCacheFile(cachePath) {
885 const content = readCacheFile(cachePath);
886 if (content == null) {
887 return null;
888 }
889 const code = content.substring(33);
890 const checksum = (0, _crypto().createHash)('sha1')
891 .update(code)
892 .digest('hex')
893 .substring(0, 32);
894 if (checksum === content.substring(0, 32)) {
895 return code;
896 }
897 return null;
898}
899
900/**
901 * Writing to the cache atomically relies on 'rename' being atomic on most
902 * file systems. Doing atomic write reduces the risk of corruption by avoiding
903 * two processes to write to the same file at the same time. It also reduces
904 * the risk of reading a file that's being overwritten at the same time.
905 */
906const writeCacheFile = (cachePath, fileData) => {
907 try {
908 (0, _writeFileAtomic().sync)(cachePath, fileData, {
909 encoding: 'utf8',
910 fsync: false
911 });
912 } catch (e) {
913 if (!(e instanceof Error)) {
914 throw e;
915 }
916 if (cacheWriteErrorSafeToIgnore(e, cachePath)) {
917 return;
918 }
919 e.message = `jest: failed to cache transform results in: ${cachePath}\nFailure message: ${e.message}`;
920 removeFile(cachePath);
921 throw e;
922 }
923};
924
925/**
926 * On Windows, renames are not atomic, leading to EPERM exceptions when two
927 * processes attempt to rename to the same target file at the same time.
928 * If the target file exists we can be reasonably sure another process has
929 * legitimately won a cache write race and ignore the error.
930 */
931const cacheWriteErrorSafeToIgnore = (e, cachePath) =>
932 process.platform === 'win32' &&
933 e.code === 'EPERM' &&
934 fs().existsSync(cachePath);
935const readCacheFile = cachePath => {
936 if (!fs().existsSync(cachePath)) {
937 return null;
938 }
939 let fileData;
940 try {
941 fileData = fs().readFileSync(cachePath, 'utf8');
942 } catch (e) {
943 if (!(e instanceof Error)) {
944 throw e;
945 }
946 // on windows write-file-atomic is not atomic which can
947 // result in this error
948 if (e.code === 'ENOENT' && process.platform === 'win32') {
949 return null;
950 }
951 e.message = `jest: failed to read cache file: ${cachePath}\nFailure message: ${e.message}`;
952 removeFile(cachePath);
953 throw e;
954 }
955 if (fileData == null) {
956 // We must have somehow created the file but failed to write to it,
957 // let's delete it and retry.
958 removeFile(cachePath);
959 }
960 return fileData;
961};
962const getScriptCacheKey = (filename, instrument) => {
963 const mtime = fs().statSync(filename).mtime;
964 return `${filename}_${mtime.getTime()}${instrument ? '_instrumented' : ''}`;
965};
966const calcIgnorePatternRegExp = config => {
967 if (
968 config.transformIgnorePatterns == null ||
969 config.transformIgnorePatterns.length === 0
970 ) {
971 return undefined;
972 }
973 return new RegExp(config.transformIgnorePatterns.join('|'));
974};
975const calcTransformRegExp = config => {
976 if (!config.transform.length) {
977 return undefined;
978 }
979 const transformRegexp = [];
980 for (let i = 0; i < config.transform.length; i++) {
981 transformRegexp.push([
982 new RegExp(config.transform[i][0]),
983 config.transform[i][1],
984 config.transform[i][2]
985 ]);
986 }
987 return transformRegexp;
988};
989function assertSyncTransformer(transformer, name) {
990 (0, _jestUtil().invariant)(name);
991 (0, _jestUtil().invariant)(
992 typeof transformer.process === 'function',
993 (0, _runtimeErrorsAndWarnings.makeInvalidSyncTransformerError)(name)
994 );
995}
996async function createScriptTransformer(config, cacheFS = new Map()) {
997 const transformer = new ScriptTransformer(config, cacheFS);
998 await transformer.loadTransformers();
999 return transformer;
1000}