diff --git a/CHANGELOG.md b/CHANGELOG.md index 1530ba153658..cd8e4b88e90f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,8 @@ ### Features +- `[babel-jest]` Support passing `supportsDynamicImport` and `supportsStaticESM` ([#9766](https://github.com/facebook/jest/pull/9766)) + ### Fixes ### Chore & Maintenance diff --git a/e2e/__tests__/__snapshots__/transform.test.ts.snap b/e2e/__tests__/__snapshots__/transform.test.ts.snap index 195359dad96c..be43c143d49a 100644 --- a/e2e/__tests__/__snapshots__/transform.test.ts.snap +++ b/e2e/__tests__/__snapshots__/transform.test.ts.snap @@ -6,7 +6,7 @@ FAIL __tests__/ignoredFile.test.js babel-jest: Babel ignores __tests__/ignoredFile.test.js - make sure to include the file in Jest's transformIgnorePatterns as well. - at loadBabelConfig (../../../packages/babel-jest/build/index.js:191:13) + at loadBabelConfig (../../../packages/babel-jest/build/index.js:227:13) `; exports[`babel-jest instruments only specific files and collects coverage 1`] = ` diff --git a/packages/babel-jest/package.json b/packages/babel-jest/package.json index 7b89bbf98e76..f4ba4805d801 100644 --- a/packages/babel-jest/package.json +++ b/packages/babel-jest/package.json @@ -20,7 +20,7 @@ "dependencies": { "@jest/transform": "^25.2.6", "@jest/types": "^25.2.6", - "@types/babel__core": "^7.1.0", + "@types/babel__core": "^7.1.7", "babel-plugin-istanbul": "^6.0.0", "babel-preset-jest": "^25.2.6", "chalk": "^3.0.0", diff --git a/packages/babel-jest/src/__tests__/index.ts b/packages/babel-jest/src/__tests__/index.ts index d817c65f73ba..7bbc4f7c84d1 100644 --- a/packages/babel-jest/src/__tests__/index.ts +++ b/packages/babel-jest/src/__tests__/index.ts @@ -5,9 +5,18 @@ * LICENSE file in the root directory of this source tree. */ -import babelJest from '../index'; +import babelJest = require('../index'); +import {loadPartialConfig} from '../loadBabelConfig'; import {makeProjectConfig} from '../../../../TestUtils'; +jest.mock('../loadBabelConfig', () => { + const actual = jest.requireActual('@babel/core'); + + return { + loadPartialConfig: jest.fn((...args) => actual.loadPartialConfig(...args)), + }; +}); + //Mock data for all the tests const sourceString = ` const sum = (a, b) => a+b; @@ -21,7 +30,11 @@ const customMultiply = (obj, mul) => { customMultiply({a: 32, dummy: "test"}, 2); `; -test(`Returns source string with inline maps when no transformOptions is passed`, () => { +beforeEach(() => { + jest.clearAllMocks(); +}); + +test('Returns source string with inline maps when no transformOptions is passed', () => { const result = babelJest.process( sourceString, 'dummy_path.js', @@ -32,6 +45,55 @@ test(`Returns source string with inline maps when no transformOptions is passed` expect(result.map).toBeDefined(); expect(result.code).toMatch('//# sourceMappingURL'); expect(result.code).toMatch('customMultiply'); - expect(result.map.sources).toEqual(['dummy_path.js']); - expect(JSON.stringify(result.map.sourcesContent)).toMatch('customMultiply'); + expect(result.map!.sources).toEqual(['dummy_path.js']); + expect(JSON.stringify(result.map!.sourcesContent)).toMatch('customMultiply'); +}); + +describe('caller option correctly merges from defaults and options', () => { + test.each([ + [ + { + supportsDynamicImport: true, + supportsStaticESM: true, + }, + { + supportsDynamicImport: true, + supportsStaticESM: true, + }, + ], + [ + { + supportsDynamicImport: false, + supportsStaticESM: false, + }, + { + supportsDynamicImport: false, + supportsStaticESM: false, + }, + ], + [ + {supportsStaticESM: false}, + { + supportsDynamicImport: false, + supportsStaticESM: false, + }, + ], + [ + {supportsDynamicImport: true}, + { + supportsDynamicImport: true, + supportsStaticESM: false, + }, + ], + ])('%j -> %j', (input, output) => { + babelJest.process(sourceString, 'dummy_path.js', makeProjectConfig(), { + instrument: false, + ...input, + }); + + expect(loadPartialConfig).toHaveBeenCalledTimes(1); + expect(loadPartialConfig).toHaveBeenCalledWith( + expect.objectContaining({caller: {name: 'babel-jest', ...output}}), + ); + }); }); diff --git a/packages/babel-jest/src/index.ts b/packages/babel-jest/src/index.ts index 5d1cc9fa169b..12d2ee07a3f6 100644 --- a/packages/babel-jest/src/index.ts +++ b/packages/babel-jest/src/index.ts @@ -8,14 +8,19 @@ import {createHash} from 'crypto'; import * as fs from 'fs'; import * as path from 'path'; -import type {Transformer} from '@jest/transform'; +import type { + TransformOptions as JestTransformOptions, + Transformer, +} from '@jest/transform'; import type {Config} from '@jest/types'; import { PartialConfig, + PluginItem, + TransformCaller, TransformOptions, transformSync as babelTransform, - loadPartialConfig, } from '@babel/core'; +import {loadPartialConfig} from './loadBabelConfig'; import chalk = require('chalk'); import slash = require('slash'); @@ -27,28 +32,51 @@ const babelIstanbulPlugin = require.resolve('babel-plugin-istanbul'); interface BabelJestTransformer extends Transformer { canInstrument: true; } +interface BabelJestTransformOptions extends TransformOptions { + caller: TransformCaller; + compact: false; + plugins: Array; + presets: Array; + sourceMaps: 'both'; +} const createTransformer = ( - options: TransformOptions = {}, + inputOptions: TransformOptions = {}, ): BabelJestTransformer => { - options = { - ...options, + const options: BabelJestTransformOptions = { + ...inputOptions, caller: { name: 'babel-jest', + supportsDynamicImport: false, supportsStaticESM: false, + ...inputOptions.caller, }, compact: false, - plugins: (options && options.plugins) || [], - presets: ((options && options.presets) || []).concat(jestPresetPath), + plugins: inputOptions.plugins ?? [], + presets: (inputOptions.presets ?? []).concat(jestPresetPath), sourceMaps: 'both', }; function loadBabelConfig( cwd: Config.Path, filename: Config.Path, + transformOptions?: JestTransformOptions, ): PartialConfig { // `cwd` first to allow incoming options to override it - const babelConfig = loadPartialConfig({cwd, ...options, filename}); + const babelConfig = loadPartialConfig({ + cwd, + ...options, + caller: { + ...options.caller, + supportsDynamicImport: + transformOptions?.supportsDynamicImport ?? + options.caller.supportsDynamicImport, + supportsStaticESM: + transformOptions?.supportsStaticESM ?? + options.caller.supportsStaticESM, + }, + filename, + }); if (!babelConfig) { throw new Error( @@ -65,13 +93,14 @@ const createTransformer = ( return { canInstrument: true, - getCacheKey( - fileData, - filename, - configString, - {config, instrument, rootDir}, - ) { - const babelOptions = loadBabelConfig(config.cwd, filename); + getCacheKey(fileData, filename, configString, cacheKeyOptions) { + const {config, instrument, rootDir} = cacheKeyOptions; + + const babelOptions = loadBabelConfig( + config.cwd, + filename, + cacheKeyOptions, + ); const configPath = [ babelOptions.config || '', babelOptions.babelrc || '', @@ -98,9 +127,11 @@ const createTransformer = ( .digest('hex'); }, process(src, filename, config, transformOptions) { - const babelOptions = {...loadBabelConfig(config.cwd, filename).options}; + const babelOptions = { + ...loadBabelConfig(config.cwd, filename, transformOptions).options, + }; - if (transformOptions && transformOptions.instrument) { + if (transformOptions?.instrument) { babelOptions.auxiliaryCommentBefore = ' istanbul ignore next '; // Copied from jest-runtime transform.js babelOptions.plugins = (babelOptions.plugins || []).concat([ diff --git a/packages/babel-jest/src/loadBabelConfig.ts b/packages/babel-jest/src/loadBabelConfig.ts new file mode 100644 index 000000000000..91bbd1725980 --- /dev/null +++ b/packages/babel-jest/src/loadBabelConfig.ts @@ -0,0 +1,9 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. 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. + */ + +// this is a separate file so it can be mocked in tests +export {loadPartialConfig} from '@babel/core'; diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index cfef2ebf1704..55c06ff81ad2 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -497,11 +497,7 @@ class Runtime { ): TransformationOptions { return { ...options, - changedFiles: this._coverageOptions.changedFiles, - collectCoverage: this._coverageOptions.collectCoverage, - collectCoverageFrom: this._coverageOptions.collectCoverageFrom, - collectCoverageOnlyFrom: this._coverageOptions.collectCoverageOnlyFrom, - coverageProvider: this._coverageOptions.coverageProvider, + ...this._coverageOptions, }; } diff --git a/packages/jest-runtime/tsconfig.json b/packages/jest-runtime/tsconfig.json index 23c5279939a3..08abc7a9ddfc 100644 --- a/packages/jest-runtime/tsconfig.json +++ b/packages/jest-runtime/tsconfig.json @@ -17,6 +17,7 @@ {"path": "../jest-snapshot"}, {"path": "../jest-source-map"}, {"path": "../jest-test-result"}, + {"path": "../jest-transform"}, {"path": "../jest-types"}, {"path": "../jest-util"}, {"path": "../jest-validate"}, diff --git a/packages/jest-transform/src/ScriptTransformer.ts b/packages/jest-transform/src/ScriptTransformer.ts index d522d114c059..3e595580436e 100644 --- a/packages/jest-transform/src/ScriptTransformer.ts +++ b/packages/jest-transform/src/ScriptTransformer.ts @@ -101,6 +101,8 @@ export default class ScriptTransformer { config: this._config, instrument, rootDir: this._config.rootDir, + supportsDynamicImport: false, + supportsStaticESM: false, }), ) .update(CACHE_VERSION) @@ -285,6 +287,8 @@ export default class ScriptTransformer { if (transform && shouldCallTransform) { const processed = transform.process(content, filename, this._config, { instrument, + supportsDynamicImport: false, + supportsStaticESM: false, }); if (typeof processed === 'string') { diff --git a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap index 0200ad87482c..19c563f8e376 100644 --- a/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap +++ b/packages/jest-transform/src/__tests__/__snapshots__/script_transformer.test.js.snap @@ -71,6 +71,8 @@ Object { }, "instrument": true, "rootDir": "/", + "supportsDynamicImport": false, + "supportsStaticESM": false, } `; diff --git a/packages/jest-transform/src/index.ts b/packages/jest-transform/src/index.ts index 8b953d59a1a5..a104f5197968 100644 --- a/packages/jest-transform/src/index.ts +++ b/packages/jest-transform/src/index.ts @@ -15,6 +15,7 @@ export type { Transformer, ShouldInstrumentOptions, Options as TransformationOptions, + TransformOptions, TransformResult, TransformedSource, } from './types'; diff --git a/packages/jest-transform/src/types.ts b/packages/jest-transform/src/types.ts index ff280e883afb..30075298bc26 100644 --- a/packages/jest-transform/src/types.ts +++ b/packages/jest-transform/src/types.ts @@ -22,6 +22,8 @@ export type Options = ShouldInstrumentOptions & Partial<{ isCoreModule: boolean; isInternalModule: boolean; + supportsDynamicImport: boolean; + supportsStaticESM: boolean; }>; // extends directly after https://github.com/sandersn/downlevel-dts/issues/33 is fixed @@ -38,15 +40,18 @@ export type TransformedSource = export type TransformResult = TransformTypes.TransformResult; -export type TransformOptions = { +export interface TransformOptions { instrument: boolean; -}; + // names are copied from babel + supportsDynamicImport?: boolean; + supportsStaticESM?: boolean; +} -export type CacheKeyOptions = { +// TODO: For Jest 26 we should combine these into one options shape +export interface CacheKeyOptions extends TransformOptions { config: Config.ProjectConfig; - instrument: boolean; rootDir: string; -}; +} export interface Transformer { canInstrument?: boolean; diff --git a/yarn.lock b/yarn.lock index 3137e4fe9b59..c026650daa0f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2137,10 +2137,10 @@ resolved "https://registry.yarnpkg.com/@types/babel__code-frame/-/babel__code-frame-7.0.1.tgz#baf2529c4abbfb5e4008c845efcfe39a187e2f99" integrity sha512-FFfbQozKxYmOnCKFYV+EQprjBI7u2yaNc2ly/K9AhzyC8MzXtCtSRqptpw+HUJxhwCOo5mLwf1ATmzyhOaVbDg== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.0.4", "@types/babel__core@^7.1.0": - version "7.1.6" - resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.6.tgz#16ff42a5ae203c9af1c6e190ed1f30f83207b610" - integrity sha512-tTnhWszAqvXnhW7m5jQU9PomXSiKXk2sFxpahXvI20SZKu9ylPi8WtIxueZ6ehDWikPT0jeFujMj3X4ZHuf3Tg== +"@types/babel__core@^7.0.0", "@types/babel__core@^7.0.4", "@types/babel__core@^7.1.0", "@types/babel__core@^7.1.7": + version "7.1.7" + resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.1.7.tgz#1dacad8840364a57c98d0dd4855c6dd3752c6b89" + integrity sha512-RL62NqSFPCDK2FM1pSDH0scHpJvsXtZNiYlMB73DgPBaG1E38ZYVL+ei5EkWRbr+KC4YNiAUNBnRj+bgwpgjMw== dependencies: "@babel/parser" "^7.1.0" "@babel/types" "^7.0.0"