UNPKG

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