Skip to content

Commit 10d9580

Browse files
authored
fix(jest-transform): improve runtime errors and warnings (#11998)
1 parent 619f843 commit 10d9580

5 files changed

Lines changed: 130 additions & 28 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
- `[expect]` Allow again `expect.Matchers` generic with single value ([#11986](https://github.com/facebook/jest/pull/11986))
1111
- `[jest-environment-jsdom]` Add `@types/jsdom` dependency ([#11999](https://github.com/facebook/jest/pull/11999))
12+
- `[jest-transform]` Improve error and warning messages ([#11998](https://github.com/facebook/jest/pull/11998))
1213

1314
### Chore & Maintenance
1415

packages/jest-transform/src/ScriptTransformer.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ import {
2525
tryRealpath,
2626
} from 'jest-util';
2727
import handlePotentialSyntaxError from './enhanceUnexpectedTokenMessage';
28+
import {
29+
makeInvalidReturnValueError,
30+
makeInvalidSourceMapWarning,
31+
makeInvalidSyncTransformerError,
32+
makeInvalidTransformerError,
33+
} from './runtimeErrorsAndWarnings';
2834
import shouldInstrument from './shouldInstrument';
2935
import type {
3036
Options,
@@ -258,7 +264,7 @@ class ScriptTransformer {
258264
);
259265

260266
if (!transformer) {
261-
throw new TypeError('Jest: a transform must export something.');
267+
throw new Error(makeInvalidTransformerError(transformPath));
262268
}
263269
if (typeof transformer.createTransformer === 'function') {
264270
transformer = transformer.createTransformer(transformerConfig);
@@ -267,9 +273,7 @@ class ScriptTransformer {
267273
typeof transformer.process !== 'function' &&
268274
typeof transformer.processAsync !== 'function'
269275
) {
270-
throw new TypeError(
271-
'Jest: a transform must export a `process` or `processAsync` function.',
272-
);
276+
throw new Error(makeInvalidTransformerError(transformPath));
273277
}
274278
const res = {transformer, transformerConfig};
275279
this._transformCache.set(transformPath, res);
@@ -373,11 +377,7 @@ class ScriptTransformer {
373377
} else if (processed != null && typeof processed.code === 'string') {
374378
transformed = processed;
375379
} else {
376-
throw new TypeError(
377-
"Jest: a transform's `process` function must return a string, " +
378-
'or an object with `code` key containing this string. ' +
379-
"It's `processAsync` function must return a Promise resolving to it.",
380-
);
380+
throw new Error(makeInvalidReturnValueError());
381381
}
382382
}
383383

@@ -391,11 +391,8 @@ class ScriptTransformer {
391391
}
392392
} catch {
393393
const transformPath = this._getTransformPath(filename);
394-
console.warn(
395-
`jest-transform: The source map produced for the file ${filename} ` +
396-
`by ${transformPath} was invalid. Proceeding without source ` +
397-
'mapping for that file.',
398-
);
394+
invariant(transformPath);
395+
console.warn(makeInvalidSourceMapWarning(filename, transformPath));
399396
}
400397
}
401398

@@ -997,7 +994,7 @@ function assertSyncTransformer(
997994
invariant(name);
998995
invariant(
999996
typeof transformer.process === 'function',
1000-
`Jest: synchronous transformer ${name} must export a "process" function.`,
997+
makeInvalidSyncTransformerError(name),
1001998
);
1002999
}
10031000

packages/jest-transform/src/__tests__/ScriptTransformer.test.ts

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -486,14 +486,14 @@ describe('ScriptTransformer', () => {
486486
await Promise.all([...promisesToReject, ...promisesToResolve]);
487487
});
488488

489-
it('throws an error if neither `process` nor `processAsync is defined', async () => {
489+
it('throws an error if neither `process` nor `processAsync` is defined', async () => {
490490
config = {
491491
...config,
492492
transform: [['\\.js$', 'skipped-required-props-preprocessor', {}]],
493493
};
494-
await expect(() => createScriptTransformer(config)).rejects.toThrow(
495-
'Jest: a transform must export a `process` or `processAsync` function.',
496-
);
494+
await expect(() =>
495+
createScriptTransformer(config),
496+
).rejects.toThrowErrorMatchingSnapshot();
497497
});
498498

499499
it("(in sync mode) throws an error if `process` isn't defined", async () => {
@@ -506,9 +506,7 @@ describe('ScriptTransformer', () => {
506506
const scriptTransformer = await createScriptTransformer(config);
507507
expect(() =>
508508
scriptTransformer.transformSource('sample.js', '', {instrument: false}),
509-
).toThrow(
510-
'Jest: synchronous transformer skipped-required-props-preprocessor-only-async must export a "process" function.',
511-
);
509+
).toThrowErrorMatchingSnapshot();
512510
});
513511

514512
it('(in async mode) handles only sync `process`', async () => {
@@ -537,9 +535,9 @@ describe('ScriptTransformer', () => {
537535
],
538536
],
539537
};
540-
await expect(() => createScriptTransformer(config)).rejects.toThrow(
541-
'Jest: a transform must export a `process` or `processAsync` function.',
542-
);
538+
await expect(() =>
539+
createScriptTransformer(config),
540+
).rejects.toThrowErrorMatchingSnapshot();
543541
});
544542

545543
it("shouldn't throw error without process method. But with correct createTransformer method", async () => {

packages/jest-transform/src/__tests__/__snapshots__/ScriptTransformer.test.ts.snap

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,14 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`ScriptTransformer (in sync mode) throws an error if \`process\` isn't defined 1`] = `
4+
"<red><bold>● Invalid synchronous transformer module:</></>
5+
<red> \\"skipped-required-props-preprocessor-only-async\\" specified in the \\"transform\\" object of Jest configuration</>
6+
<red> must export a \`process\` function.</>
7+
<red> <bold>Code Transformation Documentation:</></>
8+
<red> https://jestjs.io/docs/code-transformation</>
9+
<red></>"
10+
`;
11+
312
exports[`ScriptTransformer in async mode, passes expected transform options to getCacheKey 1`] = `
413
[MockFunction] {
514
"calls": Array [
@@ -126,7 +135,11 @@ const TRANSFORMED = {
126135
127136
exports[`ScriptTransformer in async mode, uses the supplied preprocessor 2`] = `module.exports = "react";`;
128137
129-
exports[`ScriptTransformer in async mode, warns of unparseable inlined source maps from the preprocessor 1`] = `jest-transform: The source map produced for the file /fruits/banana.js by preprocessor-with-sourcemaps was invalid. Proceeding without source mapping for that file.`;
138+
exports[`ScriptTransformer in async mode, warns of unparseable inlined source maps from the preprocessor 1`] = `
139+
● Invalid source map:
140+
 The source map for "/fruits/banana.js" returned by "preprocessor-with-sourcemaps" is invalid.
141+
 Proceeding without source mapping for that file.
142+
`;
130143
131144
exports[`ScriptTransformer passes expected transform options to getCacheKey 1`] = `
132145
[MockFunction] {
@@ -340,6 +353,24 @@ exports[`ScriptTransformer passes expected transform options to getCacheKeyAsync
340353
}
341354
`;
342355
356+
exports[`ScriptTransformer throws an error if createTransformer returns object without \`process\` method 1`] = `
357+
"<red><bold>● Invalid transformer module:</></>
358+
<red> \\"skipped-required-create-transformer-props-preprocessor\\" specified in the \\"transform\\" object of Jest configuration</>
359+
<red> must export a \`process\` or \`processAsync\` or \`createTransformer\` function.</>
360+
<red> <bold>Code Transformation Documentation:</></>
361+
<red> https://jestjs.io/docs/code-transformation</>
362+
<red></>"
363+
`;
364+
365+
exports[`ScriptTransformer throws an error if neither \`process\` nor \`processAsync\` is defined 1`] = `
366+
"<red><bold>● Invalid transformer module:</></>
367+
<red> \\"skipped-required-props-preprocessor\\" specified in the \\"transform\\" object of Jest configuration</>
368+
<red> must export a \`process\` or \`processAsync\` or \`createTransformer\` function.</>
369+
<red> <bold>Code Transformation Documentation:</></>
370+
<red> https://jestjs.io/docs/code-transformation</>
371+
<red></>"
372+
`;
373+
343374
exports[`ScriptTransformer transforms a file async properly 1`] = `
344375
/* istanbul ignore next */
345376
function cov_25u22311x4() {
@@ -688,6 +719,14 @@ const TRANSFORMED = {
688719
689720
exports[`ScriptTransformer uses the supplied preprocessor 2`] = `module.exports = "react";`;
690721
691-
exports[`ScriptTransformer warns of unparseable inlined source maps from the async preprocessor 1`] = `jest-transform: The source map produced for the file /fruits/banana.js by async-preprocessor-with-sourcemaps was invalid. Proceeding without source mapping for that file.`;
722+
exports[`ScriptTransformer warns of unparseable inlined source maps from the async preprocessor 1`] = `
723+
Invalid source map:
724+
 The source map for "/fruits/banana.js" returned by "async-preprocessor-with-sourcemaps" is invalid.
725+
 Proceeding without source mapping for that file.
726+
`;
692727
693-
exports[`ScriptTransformer warns of unparseable inlined source maps from the preprocessor 1`] = `jest-transform: The source map produced for the file /fruits/banana.js by preprocessor-with-sourcemaps was invalid. Proceeding without source mapping for that file.`;
728+
exports[`ScriptTransformer warns of unparseable inlined source maps from the preprocessor 1`] = `
729+
Invalid source map:
730+
 The source map for "/fruits/banana.js" returned by "preprocessor-with-sourcemaps" is invalid.
731+
 Proceeding without source mapping for that file.
732+
`;
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import chalk = require('chalk');
9+
import slash = require('slash');
10+
11+
const BULLET = '\u25cf ';
12+
const DOCUMENTATION_NOTE = ` ${chalk.bold(
13+
'Code Transformation Documentation:',
14+
)}
15+
https://jestjs.io/docs/code-transformation
16+
`;
17+
18+
export const makeInvalidReturnValueError = (): string =>
19+
chalk.red(
20+
[
21+
chalk.bold(BULLET + 'Invalid return value:'),
22+
` Code transformer's \`process\` function must return a string or an object`,
23+
' with `code` key containing a string. If `processAsync` function is implemented,',
24+
' it must return a Promise resolving to one of these values.',
25+
'',
26+
].join('\n') + DOCUMENTATION_NOTE,
27+
);
28+
29+
export const makeInvalidSourceMapWarning = (
30+
filename: string,
31+
transformPath: string,
32+
): string =>
33+
chalk.yellow(
34+
[
35+
chalk.bold(BULLET + 'Invalid source map:'),
36+
` The source map for "${slash(filename)}" returned by "${slash(
37+
transformPath,
38+
)}" is invalid.`,
39+
' Proceeding without source mapping for that file.',
40+
].join('\n'),
41+
);
42+
43+
export const makeInvalidSyncTransformerError = (
44+
transformPath: string,
45+
): string =>
46+
chalk.red(
47+
[
48+
chalk.bold(BULLET + 'Invalid synchronous transformer module:'),
49+
` "${slash(
50+
transformPath,
51+
)}" specified in the "transform" object of Jest configuration`,
52+
' must export a `process` function.',
53+
'',
54+
].join('\n') + DOCUMENTATION_NOTE,
55+
);
56+
57+
export const makeInvalidTransformerError = (transformPath: string): string =>
58+
chalk.red(
59+
[
60+
chalk.bold(BULLET + 'Invalid transformer module:'),
61+
` "${slash(
62+
transformPath,
63+
)}" specified in the "transform" object of Jest configuration`,
64+
' must export a `process` or `processAsync` or `createTransformer` function.',
65+
'',
66+
].join('\n') + DOCUMENTATION_NOTE,
67+
);

0 commit comments

Comments
 (0)