[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 GitHub Comments
Supplying
createTransformer
makes 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.require
will always useprocess
, andimport
will useprocessAsync
if it exists, otherwise fall back toprocess
. So if you useimport
exclusively 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,
createTransformer
needs to returnprocess
and/orprocessAsync
(plus the cache key variants), but it doesn’t really make sense to implement them at the same level ascreateTransformer
.Agreed!
As mentioned,
process
is used byrequire
,processAsync
byimport
.👍
👎
👍
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
ScriptTransformer
and 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 implementscreateTransformer
orprocessAsync
orprocess
- in that order of precedence. Supplying all three makes no sense.createTransformer
then implementing any of the other methods makes no sense (to me), as they will not be used. To me this sayscreateTransformer
does not belong inside of theTransformer
interfaces.processAsync
thenprocess
will never be used. To me that says that these fields should never co-exist.Suggested changes
createTransformer
from theTransformer
interfaceprocess
from AsyncTransformer andprocessAsync
from 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)👍 / 👎 ?