Skip to content

Commit 0d7356c

Browse files
authored
fix: invoke Babel processAsync for babel-jest in ESM mode instead of process (#3430)
1 parent 3dba4ec commit 0d7356c

File tree

2 files changed

+103
-39
lines changed

2 files changed

+103
-39
lines changed

src/ts-jest-transformer.spec.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -463,11 +463,31 @@ describe('TsJestTransformer', () => {
463463
const sourceText = 'const foo = 1'
464464
const sourcePath = 'foo.ts'
465465
const tr = new TsJestTransformer()
466-
tr.process = jest.fn()
466+
// @ts-expect-error `processWithTs` is private
467+
tr.processWithTs = jest.fn().mockReturnValueOnce('var foo = 1')
468+
const transformOptions = {
469+
...baseTransformOptions,
470+
config: {
471+
...baseTransformOptions.config,
472+
globals: {
473+
'ts-jest': {
474+
babelConfig: true,
475+
},
476+
},
477+
},
478+
}
479+
// @ts-expect-error `_configsFor` is private
480+
const babelJest = tr._configsFor(transformOptions).babelJestTransformer!
481+
jest.spyOn(babelJest, 'processAsync').mockResolvedValue('var foo = 1')
467482

468-
await tr.processAsync(sourceText, sourcePath, baseTransformOptions)
483+
const resultFromTs = await tr.processAsync(sourceText, sourcePath, transformOptions)
469484

470-
expect(tr.process).toHaveBeenCalledWith(sourceText, sourcePath, baseTransformOptions)
485+
// @ts-expect-error `processWithTs` is private
486+
expect(tr.processWithTs).toHaveBeenCalledWith(sourceText, sourcePath, transformOptions)
487+
expect(babelJest.processAsync).toHaveBeenCalledWith(resultFromTs, sourcePath, {
488+
...transformOptions,
489+
instrument: false,
490+
})
471491
})
472492
})
473493
})

src/ts-jest-transformer.ts

Lines changed: 80 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -137,40 +137,79 @@ export class TsJestTransformer implements SyncTransformer {
137137
* @public
138138
*/
139139
process(
140-
fileContent: string,
141-
filePath: Config.Path,
140+
sourceText: string,
141+
sourcePath: Config.Path,
142142
transformOptions: TransformOptionsTsJest,
143143
): TransformedSource | string {
144-
this._logger.debug({ fileName: filePath, transformOptions }, 'processing', filePath)
144+
this._logger.debug({ fileName: sourcePath, transformOptions }, 'processing', sourcePath)
145145

146-
let result: string | TransformedSource
147146
const configs = this._configsFor(transformOptions)
148-
const shouldStringifyContent = configs.shouldStringifyContent(filePath)
147+
const shouldStringifyContent = configs.shouldStringifyContent(sourcePath)
149148
const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer
150-
const isDefinitionFile = filePath.endsWith(DECLARATION_TYPE_EXT)
151-
const isJsFile = JS_JSX_REGEX.test(filePath)
152-
const isTsFile = !isDefinitionFile && TS_TSX_REGEX.test(filePath)
153-
let hooksFile = process.env.TS_JEST_HOOKS
154-
let hooks: TsJestHooksMap | undefined
155-
/* istanbul ignore next (cover by e2e) */
156-
if (hooksFile) {
157-
hooksFile = path.resolve(configs.cwd, hooksFile)
158-
hooks = importer.tryTheseOr(hooksFile, {})
149+
let result: TransformedSource | string = this.processWithTs(sourceText, sourcePath, transformOptions)
150+
if (babelJest) {
151+
this._logger.debug({ fileName: sourcePath }, 'calling babel-jest processor')
152+
153+
// do not instrument here, jest will do it anyway afterwards
154+
result = babelJest.process(result, sourcePath, {
155+
...transformOptions,
156+
instrument: false,
157+
})
159158
}
159+
result = this.runTsJestHook(sourcePath, sourceText, transformOptions, result) as string
160+
161+
return result
162+
}
163+
164+
async processAsync(
165+
sourceText: string,
166+
sourcePath: Config.Path,
167+
transformOptions: TransformOptionsTsJest,
168+
): Promise<TransformedSource | string> {
169+
this._logger.debug({ fileName: sourcePath, transformOptions }, 'processing', sourcePath)
170+
171+
return new Promise(async (resolve) => {
172+
const configs = this._configsFor(transformOptions)
173+
const shouldStringifyContent = configs.shouldStringifyContent(sourcePath)
174+
const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer
175+
let result: TransformedSource | string = this.processWithTs(sourceText, sourcePath, transformOptions)
176+
if (babelJest) {
177+
this._logger.debug({ fileName: sourcePath }, 'calling babel-jest processor')
178+
179+
// do not instrument here, jest will do it anyway afterwards
180+
result = await babelJest.processAsync(result, sourcePath, {
181+
...transformOptions,
182+
instrument: false,
183+
})
184+
}
185+
result = this.runTsJestHook(sourcePath, sourceText, transformOptions, result) as string
186+
187+
resolve(result)
188+
})
189+
}
190+
191+
private processWithTs(sourceText: string, sourcePath: string, transformOptions: TransformOptionsTsJest) {
192+
let result: string | TransformedSource
193+
const configs = this._configsFor(transformOptions)
194+
const shouldStringifyContent = configs.shouldStringifyContent(sourcePath)
195+
const babelJest = shouldStringifyContent ? undefined : configs.babelJestTransformer
196+
const isDefinitionFile = sourcePath.endsWith(DECLARATION_TYPE_EXT)
197+
const isJsFile = JS_JSX_REGEX.test(sourcePath)
198+
const isTsFile = !isDefinitionFile && TS_TSX_REGEX.test(sourcePath)
160199
if (shouldStringifyContent) {
161200
// handles here what we should simply stringify
162-
result = `module.exports=${stringify(fileContent)}`
201+
result = `module.exports=${stringify(sourceText)}`
163202
} else if (isDefinitionFile) {
164203
// do not try to compile declaration files
165204
result = ''
166205
} else if (!configs.parsedTsConfig.options.allowJs && isJsFile) {
167206
// we've got a '.js' but the compiler option `allowJs` is not set or set to false
168-
this._logger.warn({ fileName: filePath }, interpolate(Errors.GotJsFileButAllowJsFalse, { path: filePath }))
207+
this._logger.warn({ fileName: sourcePath }, interpolate(Errors.GotJsFileButAllowJsFalse, { path: sourcePath }))
169208

170-
result = fileContent
209+
result = sourceText
171210
} else if (isJsFile || isTsFile) {
172211
// transpile TS code (source maps are included)
173-
result = this._compiler.getCompiledOutput(fileContent, filePath, {
212+
result = this._compiler.getCompiledOutput(sourceText, sourcePath, {
174213
depGraphs: this._depGraphs,
175214
supportsStaticESM: transformOptions.supportsStaticESM,
176215
watchMode: this._watchMode,
@@ -181,36 +220,41 @@ export class TsJestTransformer implements SyncTransformer {
181220
// define the transform value with `babel-jest` for this extension instead
182221
const message = babelJest ? Errors.GotUnknownFileTypeWithBabel : Errors.GotUnknownFileTypeWithoutBabel
183222

184-
this._logger.warn({ fileName: filePath }, interpolate(message, { path: filePath }))
223+
this._logger.warn({ fileName: sourcePath }, interpolate(message, { path: sourcePath }))
185224

186-
result = fileContent
225+
result = sourceText
187226
}
188-
// calling babel-jest transformer
189-
if (babelJest) {
190-
this._logger.debug({ fileName: filePath }, 'calling babel-jest processor')
191227

192-
// do not instrument here, jest will do it anyway afterwards
193-
result = babelJest.process(result, filePath, { ...transformOptions, instrument: false })
228+
return result
229+
}
230+
231+
private runTsJestHook(
232+
sourcePath: string,
233+
sourceText: string,
234+
transformOptions: TransformOptionsTsJest,
235+
compiledOutput: TransformedSource | string,
236+
) {
237+
let hooksFile = process.env.TS_JEST_HOOKS
238+
let hooks: TsJestHooksMap | undefined
239+
/* istanbul ignore next (cover by e2e) */
240+
if (hooksFile) {
241+
hooksFile = path.resolve(this._configsFor(transformOptions).cwd, hooksFile)
242+
hooks = importer.tryTheseOr(hooksFile, {})
194243
}
195244
// This is not supposed to be a public API but we keep it as some people use it
196245
if (hooks?.afterProcess) {
197-
this._logger.debug({ fileName: filePath, hookName: 'afterProcess' }, 'calling afterProcess hook')
246+
this._logger.debug({ fileName: sourcePath, hookName: 'afterProcess' }, 'calling afterProcess hook')
198247

199-
const newResult = hooks.afterProcess([fileContent, filePath, transformOptions.config, transformOptions], result)
248+
const newResult = hooks.afterProcess(
249+
[sourceText, sourcePath, transformOptions.config, transformOptions],
250+
compiledOutput,
251+
)
200252
if (newResult) {
201253
return newResult
202254
}
203255
}
204256

205-
return result
206-
}
207-
208-
async processAsync(
209-
sourceText: string,
210-
sourcePath: Config.Path,
211-
transformOptions: TransformOptionsTsJest,
212-
): Promise<TransformedSource | string> {
213-
return new Promise((resolve) => resolve(this.process(sourceText, sourcePath, transformOptions)))
257+
return compiledOutput
214258
}
215259

216260
/**

0 commit comments

Comments
 (0)