Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transform): pass config options through to transformer #10926

Merged
merged 2 commits into from Dec 7, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Expand Up @@ -13,6 +13,7 @@
- `[jest-runner]` [**BREAKING**] Run transforms over `runnner` ([#8823](https://github.com/facebook/jest/pull/8823))
- `[jest-runner]` [**BREAKING**] Run transforms over `testRunnner` ([#8823](https://github.com/facebook/jest/pull/8823))
- `[jest-runtime, jest-transform]` share `cacheFS` between runtime and transformer ([#10901](https://github.com/facebook/jest/pull/10901))
- `[jest-transform]` Pass config options defined in Jest's config to transformer's `process` and `getCacheKey` functions ([#10926](https://github.com/facebook/jest/pull/10926))

### Fixes

Expand Down Expand Up @@ -45,6 +46,7 @@
- `[*]` [**BREAKING**] Only support Node LTS releases and Node 15 ([#10685](https://github.com/facebook/jest/pull/10685))
- `[*]` [**BREAKING**] Add `exports` field to all `package.json`s ([#9921](https://github.com/facebook/jest/pull/9921))
- `[*]` Make it easier for Jest's packages to use the VM escape hatch ([#10824](https://github.com/facebook/jest/pull/10824))
- `[*]` [**BREAKING**] Remove deprecated `mapCoverage` ([#9968](https://github.com/facebook/jest/pull/9968))
- `[jest-config]` [**BREAKING**] Remove `enabledTestsMap` config, use `filter` instead ([#10787](https://github.com/facebook/jest/pull/10787))
- `[jest-console]` [**BREAKING**] Move `root` into `config` and take `GlobalConfig` as mandatory parameter for `getConsoleOutput` ([#10126](https://github.com/facebook/jest/pull/10126))
- `[jest-fake-timers]` Clarify global behavior of `jest.useFakeTimers` and `jest.useRealTimers` ([#10867](https://github.com/facebook/jest/pull/10867))
Expand All @@ -59,7 +61,6 @@
- `[jest-runtime]` [**BREAKING**] Remove deprecated and unnused `getSourceMapInfo` from Runtime ([#9969](https://github.com/facebook/jest/pull/9969))
- `[jest-util]` No longer checking `enumerable` when adding `process.domain` ([#10862](https://github.com/facebook/jest/pull/10862))
- `[jest-validate]` [**BREAKING**] Remove `recursiveBlacklist ` option in favor of previously introduced `recursiveDenylist` ([#10650](https://github.com/facebook/jest/pull/10650))
- `[*]` [**BREAKING**] Remove deprecated `mapCoverage` ([#9968](https://github.com/facebook/jest/pull/9968))

### Performance

Expand Down
8 changes: 5 additions & 3 deletions docs/CodeTransformation.md
Expand Up @@ -35,17 +35,17 @@ interface Transformer<OptionType = unknown> {
getCacheKey?: (
sourceText: string,
sourcePath: string,
options: TransformOptions,
options: TransformOptions<OptionType>,
) => string;

process: (
sourceText: string,
sourcePath: string,
options: TransformOptions,
options: TransformOptions<OptionType>,
) => TransformedSource;
}

interface TransformOptions {
interface TransformOptions<OptionType> {
/**
* If a transformer does module resolution and reads files, it should populate `cacheFS` so that
* Jest avoids reading the same files again, improving performance. `cacheFS` stores entries of
Expand All @@ -61,6 +61,8 @@ interface TransformOptions {
supportsExportNamespaceFrom: boolean;
supportsStaticESM: boolean;
supportsTopLevelAwait: boolean;
/** the options passed through Jest's config by the user */
transformerConfig: OptionType;
}

type TransformedSource =
Expand Down
2 changes: 1 addition & 1 deletion e2e/__tests__/__snapshots__/transform.test.ts.snap
Expand Up @@ -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:201:13)
at loadBabelConfig (../../../packages/babel-jest/build/index.js:190:13)
`;

exports[`babel-jest instruments only specific files and collects coverage 1`] = `
Expand Down
10 changes: 5 additions & 5 deletions packages/babel-jest/src/index.ts
Expand Up @@ -50,7 +50,7 @@ const createTransformer: CreateTransformer = userOptions => {
function loadBabelConfig(
cwd: Config.Path,
filename: Config.Path,
transformOptions?: JestTransformOptions,
transformOptions: JestTransformOptions,
): PartialConfig {
// `cwd` first to allow incoming options to override it
const babelConfig = loadPartialConfig({
Expand All @@ -59,16 +59,16 @@ const createTransformer: CreateTransformer = userOptions => {
caller: {
...options.caller,
supportsDynamicImport:
transformOptions?.supportsDynamicImport ??
transformOptions.supportsDynamicImport ??
options.caller.supportsDynamicImport,
supportsExportNamespaceFrom:
transformOptions?.supportsExportNamespaceFrom ??
transformOptions.supportsExportNamespaceFrom ??
options.caller.supportsExportNamespaceFrom,
supportsStaticESM:
transformOptions?.supportsStaticESM ??
transformOptions.supportsStaticESM ??
options.caller.supportsStaticESM,
supportsTopLevelAwait:
transformOptions?.supportsTopLevelAwait ??
transformOptions.supportsTopLevelAwait ??
options.caller.supportsTopLevelAwait,
},
filename,
Expand Down
3 changes: 3 additions & 0 deletions packages/jest-repl/src/cli/repl.ts
Expand Up @@ -15,6 +15,7 @@ import type {Transformer} from '@jest/transform';
import type {Config} from '@jest/types';

let transformer: Transformer;
let transformerConfig: unknown;

const evalCommand: repl.REPLEval = (
cmd: string,
Expand All @@ -37,6 +38,7 @@ const evalCommand: repl.REPLEval = (
supportsExportNamespaceFrom: false,
supportsStaticESM: false,
supportsTopLevelAwait: false,
transformerConfig,
},
);
cmd =
Expand Down Expand Up @@ -69,6 +71,7 @@ if (jestProjectConfig.transform) {
for (let i = 0; i < jestProjectConfig.transform.length; i++) {
if (new RegExp(jestProjectConfig.transform[i][0]).test('foobar.js')) {
transformerPath = jestProjectConfig.transform[i][1];
transformerConfig = jestProjectConfig.transform[i][2];
break;
}
}
Expand Down
47 changes: 28 additions & 19 deletions packages/jest-transform/src/ScriptTransformer.ts
Expand Up @@ -67,7 +67,10 @@ export default class ScriptTransformer {
private readonly _cache: ProjectCache;
private readonly _cacheFS: StringMap;
private readonly _config: Config.ProjectConfig;
private readonly _transformCache: Map<Config.Path, Transformer>;
private readonly _transformCache: Map<
Config.Path,
{transformer: Transformer; transformerConfig: unknown}
>;
private readonly _transformConfigCache: Map<Config.Path, unknown>;

constructor(
Expand Down Expand Up @@ -102,7 +105,8 @@ export default class ScriptTransformer {
options: ReducedTransformOptions,
): string {
const configString = this._cache.configString;
const transformer = this._getTransformer(filename);
const {transformer, transformerConfig = {}} =
this._getTransformer(filename) || {};

if (transformer && typeof transformer.getCacheKey === 'function') {
return createHash('md5')
Expand All @@ -112,6 +116,7 @@ export default class ScriptTransformer {
cacheFS: this._cacheFS,
config: this._config,
configString,
transformerConfig,
}),
)
.update(CACHE_VERSION)
Expand Down Expand Up @@ -181,28 +186,30 @@ export default class ScriptTransformer {
return null;
}

const transformer = this._transformCache.get(transformPath);
if (transformer) {
return transformer;
const cached = this._transformCache.get(transformPath);
if (cached) {
return cached;
}

let transform: Transformer = require(transformPath);
let transformer: Transformer = require(transformPath);

if (!transform) {
if (!transformer) {
throw new TypeError('Jest: a transform must export something.');
}
const transformerConfig = this._transformConfigCache.get(transformPath);
if (typeof transform.createTransformer === 'function') {
transform = transform.createTransformer(transformerConfig);
const transformerConfig =
this._transformConfigCache.get(transformPath) || {};
if (typeof transformer.createTransformer === 'function') {
transformer = transformer.createTransformer(transformerConfig);
}
if (typeof transform.process !== 'function') {
if (typeof transformer.process !== 'function') {
throw new TypeError(
'Jest: a transform must export a `process` function.',
);
}
this._transformCache.set(transformPath, transform);
const res = {transformer, transformerConfig};
this._transformCache.set(transformPath, res);

return transform;
return res;
}

private _instrumentFile(
Expand Down Expand Up @@ -262,18 +269,19 @@ export default class ScriptTransformer {
options: ReducedTransformOptions,
): TransformResult {
const filename = tryRealpath(filepath);
const transform = this._getTransformer(filename);
const {transformer, transformerConfig = {}} =
this._getTransformer(filename) || {};
const cacheFilePath = this._getFileCachePath(filename, content, options);
let sourceMapPath: Config.Path | null = cacheFilePath + '.map';
// Ignore cache if `config.cache` is set (--no-cache)
let code = this._config.cache ? readCodeCacheFile(cacheFilePath) : null;

const shouldCallTransform = transform && this.shouldTransform(filename);
const shouldCallTransform = transformer && this.shouldTransform(filename);

// That means that the transform has a custom instrumentation
// logic and will handle it based on `config.collectCoverage` option
const transformWillInstrument =
shouldCallTransform && transform && transform.canInstrument;
shouldCallTransform && transformer && transformer.canInstrument;

if (code) {
// This is broken: we return the code, and a path for the source map
Expand All @@ -292,12 +300,13 @@ export default class ScriptTransformer {
map: null,
};

if (transform && shouldCallTransform) {
const processed = transform.process(content, filename, {
if (transformer && shouldCallTransform) {
const processed = transformer.process(content, filename, {
...options,
cacheFS: this._cacheFS,
config: this._config,
configString: this._cache.configString,
transformerConfig,
});

if (typeof processed === 'string') {
Expand Down Expand Up @@ -343,7 +352,7 @@ export default class ScriptTransformer {
*
*/
const shouldEmitSourceMaps =
(transform != null && map != null) || transform == null;
(transformer != null && map != null) || transformer == null;

const instrumented = this._instrumentFile(
filename,
Expand Down
Expand Up @@ -663,7 +663,10 @@ describe('ScriptTransformer', () => {
});

it('passes expected transform options to getCacheKey', () => {
config = {...config, transform: [['\\.js$', 'test_preprocessor', {}]]};
config = {
...config,
transform: [['\\.js$', 'test_preprocessor', {configKey: 'configValue'}]],
};
const scriptTransformer = new ScriptTransformer(config);

scriptTransformer.transform(
Expand Down