[Bug]: Missing docs for SyncTransformer#createTransformer
See original GitHub issueVersion
27.5.1
Steps to reproduce
I created a minimal repro repo before figuring out what was wrong, but the essence is this:
Create a debug-transformer.js that wraps another, like this wrapping babel-jest:
const debug = require('debug')('jest-debug-transform')
const babelJest = require('babel-jest').default
const originalProcess = babelJest.process
const originalProcessAsync = babelJest.processAsync
module.exports = {
...babelJest, // <-- source of confusion: the presence of `createTransformer` will void the effect of implemented methods
process(src, filename, options) {
debug(`Transforming '${filename}'`)
return originalProcess(src, filename, options)
},
processAsync(src, filename, options) {
debug(`Async Transforming '${filename}'`)
return originalProcessAsync(src, filename, options)
},
}
and use it in the jest config:
transform: { '\\.[jt]sx?$': [ './debug-transformer.js', {} ] }
Now, when running DEBUG="jest-debug*" npx jest --no-cache I would have assumed it would print messages such as “Transforming ‘./src/myfile.test.tsx’”, but it never does. Unless I manually add a line where I delete module.exports.createTransformer, that is.
Expected behavior
I would have expected it to use the implemented process and processAsync methods when implemented, as the effects of createTransformer is not documented.
Actual behavior
createTransformer() is being called if present. This is not documented in the transform docs, and makes the interface not really reflect reality. There is no point in implementing process if it is never called.
Additional context
I have been looking into/debugging code transformation related issues in Jest for the last day and a recurring theme is that the SyncTransformer#createTransformer method is a constant source of surprise and it is not really documented why it exists.
The SyncTransformer interface only has a single field one has to implement: process. But it seems that if one implements createTransformer those other methods will not be used: instead Jest seems to create a new transformer using createTransformer, which caused my to lose a few hairs until I figured what was going on. This behaviour does not seem to be documented.
The babel-jest source for Jest 27.
Environment
System:
OS: macOS 12.2
CPU: (8) arm64 Apple M1
Binaries:
Node: 17.4.0 - /usr/local/bin/node
npm: 8.3.1 - /usr/local/bin/npm
npmPackages:
jest: ^27.5.1 => 27.5.1
Issue Analytics
- State:
- Created 2 years ago
- Comments:7 (1 by maintainers)

Top Related StackOverflow Question
Supplying
createTransformermakes the other 2 redundant, yes, as they won’t be used.However, we have both sync (
process) and async (processAsync) code transformation, which both can be provided.requirewill always useprocess, andimportwill useprocessAsyncif it exists, otherwise fall back toprocess. So if you useimportexclusively you don’t needprocess, but in most cases supplying both makes sense. Jest transpiles on demand rather than ahead of time, so the sync one needs to exist.Ish,
createTransformerneeds to returnprocessand/orprocessAsync(plus the cache key variants), but it doesn’t really make sense to implement them at the same level ascreateTransformer.Agreed!
As mentioned,
processis used byrequire,processAsyncbyimport.👍
👎
👍
We’re currently releasing alphas for Jest 28, so breaking changes are fine 🙂
I have no issues in documenting it, I was just not sure what to write, but now I have deep dived into
ScriptTransformerand its likes, so I understand a bit more 🤓Looking into the validation errors and associated tests, it seems the interfaces should be updated as well?
This is what I seem to understand is lacking documentation (as explicit docs or via typing):
process. The code actually requires that one implementscreateTransformerorprocessAsyncorprocess- in that order of precedence. Supplying all three makes no sense.createTransformerthen implementing any of the other methods makes no sense (to me), as they will not be used. To me this sayscreateTransformerdoes not belong inside of theTransformerinterfaces.processAsyncthenprocesswill never be used. To me that says that these fields should never co-exist.Suggested changes
createTransformerfrom theTransformerinterfaceprocessfrom AsyncTransformer andprocessAsyncfrom SyncTransformertype TransformerOrTransformerCreator = Transformer | { createTransformer: TransformerCreator }The last point would potentially be breaking, so for backwards compatibility could be
type TransformerAndOrTransformerCreator = Transformer & { createTransformer?: TransformerCreator } | { createTransformer: TransformerCreator }(head-coded those, so not sure if compiles)👍 / 👎 ?