UNPKG

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