Skip to content

Commit 387964f

Browse files
authoredJan 25, 2021
feat(compiler): allow custom transformers to access internal Program (#2299)
BREAKING CHANGE `ts-jest` custom AST transformer function signature has changed to ``` import type { TsCompilerInstance } from 'ts-jest/dist/types' export function factory(compilerInstance: TsCompilerInstance) { //... } ```
1 parent 59e59ff commit 387964f

16 files changed

+231
-127
lines changed
 

‎e2e/__cases__/ast-transformers/with-extra-options/foo.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
const { LogContexts, LogLevels } = require('bs-logger')
22

3-
function factory(cs, extraOpts = Object.create(null)) {
4-
const logger = cs.logger.child({ namespace: 'dummy-transformer' })
5-
const ts = cs.compilerModule
3+
function factory({ configSet }, extraOpts = Object.create(null)) {
4+
const logger = configSet.logger.child({ namespace: 'dummy-transformer' })
5+
const ts = configSet.compilerModule
66
logger.debug('Dummy transformer with extra options', JSON.stringify(extraOpts))
77

88
function createVisitor(_ctx, _sf) {

‎src/__mocks__/dummy-transformer.js

+5-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
const { LogContexts, LogLevels } = require('bs-logger')
22

3-
function factory(cs) {
4-
const logger = cs.logger.child({ namespace: 'dummy-transformer' })
5-
const ts = cs.compilerModule
3+
function factory(tsCompiler) {
4+
const logger = tsCompiler.configSet.logger.child({ namespace: 'dummy-transformer' })
5+
const ts = tsCompiler.configSet.compilerModule
6+
// eslint-disable-next-line no-console
7+
console.log(tsCompiler.program)
68

79
function createVisitor(_ctx, _) {
810
return (node) => node

‎src/compiler/ts-compiler.spec.ts

+53-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ describe('TsCompiler', () => {
3030
expect(new ProcessedSource(compiledOutput, fileName).outputCodeWithoutMaps).toMatchSnapshot()
3131
})
3232

33-
it('should compile js file for allowJs true', () => {
33+
test('should compile js file for allowJs true', () => {
3434
const fileName = 'foo.js'
3535
const compiler = makeCompiler({
3636
tsJestConfig: { ...baseTsJestConfig, tsconfig: { allowJs: true, outDir: TS_JEST_OUT_DIR } },
@@ -179,6 +179,32 @@ const t: string = f(5)
179179
).not.toThrowError()
180180
})
181181
})
182+
183+
test('should use correct custom AST transformers', () => {
184+
// eslint-disable-next-line no-console
185+
console.log = jest.fn()
186+
const fileName = 'foo.js'
187+
const compiler = makeCompiler({
188+
tsJestConfig: {
189+
...baseTsJestConfig,
190+
tsconfig: {
191+
allowJs: true,
192+
outDir: TS_JEST_OUT_DIR,
193+
},
194+
astTransformers: {
195+
before: ['dummy-transformer'],
196+
after: ['dummy-transformer'],
197+
afterDeclarations: ['dummy-transformer'],
198+
},
199+
},
200+
})
201+
const source = 'export default 42'
202+
203+
compiler.getCompiledOutput(source, fileName, false)
204+
205+
// eslint-disable-next-line no-console
206+
expect(console.log).toHaveBeenCalledTimes(3)
207+
})
182208
})
183209

184210
describe('isolatedModule false', () => {
@@ -452,5 +478,31 @@ const t: string = f(5)
452478
expect(() => compiler.getCompiledOutput(source, fileName, false)).toThrowErrorMatchingSnapshot()
453479
})
454480
})
481+
482+
test('should pass Program instance into custom transformers', () => {
483+
// eslint-disable-next-line no-console
484+
console.log = jest.fn()
485+
const fileName = join(mockFolder, 'thing.spec.ts')
486+
const compiler = makeCompiler(
487+
{
488+
tsJestConfig: {
489+
...baseTsJestConfig,
490+
astTransformers: {
491+
before: ['dummy-transformer'],
492+
after: ['dummy-transformer'],
493+
afterDeclarations: ['dummy-transformer'],
494+
},
495+
},
496+
},
497+
jestCacheFS,
498+
)
499+
500+
compiler.getCompiledOutput(readFileSync(fileName, 'utf-8'), fileName, false)
501+
502+
// eslint-disable-next-line no-console
503+
expect(console.log).toHaveBeenCalled()
504+
// eslint-disable-next-line no-console
505+
expect(((console.log as any) as jest.MockInstance<any, any>).mock.calls[0][0].emit).toBeDefined()
506+
})
455507
})
456508
})

‎src/compiler/ts-compiler.ts

+36-13
Original file line numberDiff line numberDiff line change
@@ -10,11 +10,16 @@ import type {
1010
ResolvedModuleFull,
1111
TranspileOutput,
1212
CompilerOptions,
13+
SourceFile,
14+
Program,
15+
TransformerFactory,
16+
Bundle,
17+
CustomTransformerFactory,
1318
} from 'typescript'
1419

1520
import type { ConfigSet } from '../config/config-set'
1621
import { LINE_FEED } from '../constants'
17-
import type { CompilerInstance, ResolvedModulesMap, StringMap, TTypeScript } from '../types'
22+
import type { ResolvedModulesMap, StringMap, TsCompilerInstance, TTypeScript } from '../types'
1823
import { rootLogger } from '../utils/logger'
1924
import { Errors, interpolate } from '../utils/messages'
2025

@@ -23,23 +28,22 @@ import { updateOutput } from './compiler-utils'
2328
/**
2429
* @internal
2530
*/
26-
export class TsCompiler implements CompilerInstance {
31+
export class TsCompiler implements TsCompilerInstance {
2732
private readonly _logger: Logger
2833
private readonly _ts: TTypeScript
2934
private readonly _parsedTsConfig: ParsedCommandLine
3035
private readonly _compilerCacheFS: Map<string, number> = new Map<string, number>()
31-
private readonly _jestCacheFS: StringMap
3236
private readonly _initialCompilerOptions: CompilerOptions
3337
private _compilerOptions: CompilerOptions
3438
private _cachedReadFile: ((fileName: string) => string | undefined) | undefined
3539
private _projectVersion = 1
3640
private _languageService: LanguageService | undefined
41+
program: Program | undefined
3742

3843
constructor(readonly configSet: ConfigSet, readonly jestCacheFS: StringMap) {
3944
this._ts = configSet.compilerModule
4045
this._logger = rootLogger.child({ namespace: 'ts-compiler' })
4146
this._parsedTsConfig = this.configSet.parsedTsConfig as ParsedCommandLine
42-
this._jestCacheFS = jestCacheFS
4347
this._initialCompilerOptions = { ...this._parsedTsConfig.options }
4448
this._compilerOptions = { ...this._initialCompilerOptions }
4549
if (!this.configSet.isolatedModules) {
@@ -96,13 +100,13 @@ export class TsCompiler implements CompilerInstance {
96100
// Read contents from TypeScript memory cache.
97101
if (!hit) {
98102
const fileContent =
99-
this._jestCacheFS.get(normalizedFileName) ?? this._cachedReadFile?.(normalizedFileName) ?? undefined
103+
this.jestCacheFS.get(normalizedFileName) ?? this._cachedReadFile?.(normalizedFileName) ?? undefined
100104
if (fileContent) {
101-
this._jestCacheFS.set(normalizedFileName, fileContent)
105+
this.jestCacheFS.set(normalizedFileName, fileContent)
102106
this._compilerCacheFS.set(normalizedFileName, 1)
103107
}
104108
}
105-
const contents = this._jestCacheFS.get(normalizedFileName)
109+
const contents = this.jestCacheFS.get(normalizedFileName)
106110

107111
if (contents === undefined) return
108112

@@ -118,7 +122,17 @@ export class TsCompiler implements CompilerInstance {
118122
getCurrentDirectory: () => this.configSet.cwd,
119123
getCompilationSettings: () => this._compilerOptions,
120124
getDefaultLibFileName: () => this._ts.getDefaultLibFilePath(this._compilerOptions),
121-
getCustomTransformers: () => this.configSet.customTransformers,
125+
getCustomTransformers: () => ({
126+
before: this.configSet.resolvedTransformers.before.map((beforeTransformer) =>
127+
beforeTransformer.factory(this, beforeTransformer.options),
128+
) as (TransformerFactory<SourceFile> | CustomTransformerFactory)[],
129+
after: this.configSet.resolvedTransformers.after.map((afterTransformer) =>
130+
afterTransformer.factory(this, afterTransformer.options),
131+
) as (TransformerFactory<SourceFile> | CustomTransformerFactory)[],
132+
afterDeclarations: this.configSet.resolvedTransformers.afterDeclarations.map((afterDeclarations) =>
133+
afterDeclarations.factory(this, afterDeclarations.options),
134+
) as TransformerFactory<SourceFile | Bundle>[],
135+
}),
122136
resolveModuleNames: (moduleNames: string[], containingFile: string): (ResolvedModuleFull | undefined)[] =>
123137
moduleNames.map((moduleName) => {
124138
const { resolvedModule } = this._ts.resolveModuleName(
@@ -136,6 +150,7 @@ export class TsCompiler implements CompilerInstance {
136150
this._logger.debug('created language service')
137151

138152
this._languageService = this._ts.createLanguageService(serviceHost, this._ts.createDocumentRegistry())
153+
this.program = this._languageService.getProgram()
139154
}
140155

141156
getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap {
@@ -197,7 +212,17 @@ export class TsCompiler implements CompilerInstance {
197212

198213
const result: TranspileOutput = this._ts.transpileModule(fileContent, {
199214
fileName,
200-
transformers: this.configSet.customTransformers,
215+
transformers: {
216+
before: this.configSet.resolvedTransformers.before.map((beforeTransformer) =>
217+
beforeTransformer.factory(this, beforeTransformer.options),
218+
) as (TransformerFactory<SourceFile> | CustomTransformerFactory)[],
219+
after: this.configSet.resolvedTransformers.after.map((afterTransformer) =>
220+
afterTransformer.factory(this, afterTransformer.options),
221+
) as (TransformerFactory<SourceFile> | CustomTransformerFactory)[],
222+
afterDeclarations: this.configSet.resolvedTransformers.afterDeclarations.map((afterDeclarations) =>
223+
afterDeclarations.factory(this, afterDeclarations.options),
224+
) as TransformerFactory<SourceFile | Bundle>[],
225+
},
201226
compilerOptions: this._compilerOptions,
202227
reportDiagnostics: this.configSet.shouldReportDiagnostics(fileName),
203228
})
@@ -212,9 +237,7 @@ export class TsCompiler implements CompilerInstance {
212237

213238
private _isFileInCache(fileName: string): boolean {
214239
return (
215-
this._jestCacheFS.has(fileName) &&
216-
this._compilerCacheFS.has(fileName) &&
217-
this._compilerCacheFS.get(fileName) !== 0
240+
this.jestCacheFS.has(fileName) && this._compilerCacheFS.has(fileName) && this._compilerCacheFS.get(fileName) !== 0
218241
)
219242
}
220243

@@ -229,7 +252,7 @@ export class TsCompiler implements CompilerInstance {
229252
shouldIncrementProjectVersion = true
230253
} else {
231254
const prevVersion = this._compilerCacheFS.get(fileName) ?? 0
232-
const previousContents = this._jestCacheFS.get(fileName)
255+
const previousContents = this.jestCacheFS.get(fileName)
233256
// Avoid incrementing cache when nothing has changed.
234257
if (previousContents !== contents) {
235258
this._compilerCacheFS.set(fileName, prevVersion + 1)

‎src/config/__snapshots__/config-set.spec.ts.snap

+46-9
Original file line numberDiff line numberDiff line change
@@ -54,48 +54,85 @@ Array [
5454

5555
exports[`customTransformers should return an object containing all resolved transformers 1`] = `
5656
Object {
57+
"after": Array [],
58+
"afterDeclarations": Array [],
5759
"before": Array [
58-
[Function],
60+
Object {
61+
"factory": [Function],
62+
"name": "hoisting-jest-mock",
63+
"version": 4,
64+
},
5965
],
6066
}
6167
`;
6268

6369
exports[`customTransformers should return an object containing all resolved transformers 2`] = `
6470
Object {
71+
"after": Array [],
72+
"afterDeclarations": Array [],
6573
"before": Array [
66-
[Function],
67-
[Function],
74+
Object {
75+
"factory": [Function],
76+
"name": "hoisting-jest-mock",
77+
"version": 4,
78+
},
79+
Object {
80+
"factory": [Function],
81+
},
6882
],
6983
}
7084
`;
7185

7286
exports[`customTransformers should return an object containing all resolved transformers 3`] = `
7387
Object {
7488
"after": Array [
75-
[Function],
89+
Object {
90+
"factory": [Function],
91+
},
7692
],
93+
"afterDeclarations": Array [],
7794
"before": Array [
78-
[Function],
95+
Object {
96+
"factory": [Function],
97+
"name": "hoisting-jest-mock",
98+
"version": 4,
99+
},
79100
],
80101
}
81102
`;
82103

83104
exports[`customTransformers should return an object containing all resolved transformers 4`] = `
84105
Object {
106+
"after": Array [],
85107
"afterDeclarations": Array [
86-
[Function],
108+
Object {
109+
"factory": [Function],
110+
},
87111
],
88112
"before": Array [
89-
[Function],
113+
Object {
114+
"factory": [Function],
115+
"name": "hoisting-jest-mock",
116+
"version": 4,
117+
},
90118
],
91119
}
92120
`;
93121

94122
exports[`customTransformers should return an object containing all resolved transformers 5`] = `
95123
Object {
124+
"after": Array [],
125+
"afterDeclarations": Array [],
96126
"before": Array [
97-
[Function],
98-
[Function],
127+
Object {
128+
"factory": [Function],
129+
"name": "hoisting-jest-mock",
130+
"version": 4,
131+
},
132+
Object {
133+
"factory": [Function],
134+
"options": Object {},
135+
},
99136
],
100137
}
101138
`;

‎src/config/config-set.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ describe('customTransformers', () => {
169169
resolve: null,
170170
})
171171

172-
expect(cs.customTransformers).toMatchSnapshot()
172+
expect(cs.resolvedTransformers).toMatchSnapshot()
173173
})
174174
})
175175

‎src/config/config-set.ts

+41-45
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,17 @@ import type { Config } from '@jest/types'
1515
import { LogContexts, Logger } from 'bs-logger'
1616
import { globsToMatcher } from 'jest-util'
1717
import json5 from 'json5'
18-
import {
19-
CompilerOptions,
20-
CustomTransformers,
21-
Diagnostic,
22-
FormatDiagnosticsHost,
23-
ParsedCommandLine,
24-
DiagnosticCategory,
25-
ModuleKind,
26-
ScriptTarget,
27-
} from 'typescript'
18+
import type { CompilerOptions, Diagnostic, FormatDiagnosticsHost, ParsedCommandLine } from 'typescript'
2819

2920
import { DEFAULT_JEST_TEST_MATCH, JS_JSX_EXTENSIONS } from '../constants'
30-
import { factory as hoisting } from '../transformers/hoist-jest'
3121
import type {
3222
AstTransformer,
23+
AstTransformerDesc,
3324
BabelConfig,
3425
BabelJestTransformer,
26+
ProjectConfigTsJest,
3527
TsJestDiagnosticsCfg,
3628
TsJestGlobalOptions,
37-
ProjectConfigTsJest,
3829
TTypeScript,
3930
} from '../types'
4031
import { backportJestConfig } from '../utils/backports'
@@ -64,13 +55,6 @@ export const IGNORE_DIAGNOSTIC_CODES = [
6455
*/
6556
export const TS_JEST_OUT_DIR = '$$ts-jest$$'
6657

67-
const TARGET_TO_VERSION_MAPPING: Record<number, string> = {
68-
[ScriptTarget.ES2018]: 'es2018',
69-
[ScriptTarget.ES2019]: 'es2019',
70-
[ScriptTarget.ES2020]: 'es2020',
71-
[ScriptTarget.ESNext]: 'ESNext',
72-
}
73-
7458
/**
7559
* @internal
7660
*/
@@ -106,6 +90,12 @@ const toDiagnosticCodeList = (items: (string | number)[], into: number[] = []):
10690
return into
10791
}
10892

93+
interface TsJestAstTransformer {
94+
before: AstTransformerDesc[]
95+
after: AstTransformerDesc[]
96+
afterDeclarations: AstTransformerDesc[]
97+
}
98+
10999
export class ConfigSet {
110100
/**
111101
* Use by e2e, don't mark as internal
@@ -118,7 +108,11 @@ export class ConfigSet {
118108
readonly rootDir: string
119109
tsCacheDir: string | undefined
120110
parsedTsConfig!: ParsedCommandLine | Record<string, any>
121-
customTransformers: CustomTransformers = Object.create(null)
111+
resolvedTransformers: TsJestAstTransformer = {
112+
before: [],
113+
after: [],
114+
afterDeclarations: [],
115+
}
122116
useESM = false
123117
/**
124118
* @internal
@@ -305,44 +299,40 @@ export class ConfigSet {
305299
this.logger.debug({ tsconfig: this.parsedTsConfig }, 'normalized typescript config via ts-jest option')
306300

307301
// transformers
302+
this.resolvedTransformers.before = [require('../transformers/hoist-jest')]
308303
const { astTransformers } = options
309-
this.customTransformers = {
310-
before: [hoisting(this)],
311-
}
312304
if (astTransformers) {
313-
const resolveTransformers = (transformers: (string | AstTransformer)[]) =>
305+
const resolveTransformers = (transformers: (string | AstTransformer)[]): AstTransformerDesc[] =>
314306
transformers.map((transformer) => {
315-
let transformerPath: string
316307
if (typeof transformer === 'string') {
317-
transformerPath = this.resolvePath(transformer, { nodeResolve: true })
318-
319-
return require(transformerPath).factory(this)
308+
return require(this.resolvePath(transformer, { nodeResolve: true }))
320309
} else {
321-
transformerPath = this.resolvePath(transformer.path, { nodeResolve: true })
322-
323-
return require(transformerPath).factory(this, transformer.options)
310+
return {
311+
...require(this.resolvePath(transformer.path, { nodeResolve: true })),
312+
options: transformer.options,
313+
}
324314
}
325315
})
326316
if (astTransformers.before) {
327317
/* istanbul ignore next (already covered in unit test) */
328-
this.customTransformers.before?.push(...resolveTransformers(astTransformers.before))
318+
this.resolvedTransformers.before?.push(...resolveTransformers(astTransformers.before))
329319
}
330320
if (astTransformers.after) {
331-
this.customTransformers = {
332-
...this.customTransformers,
321+
this.resolvedTransformers = {
322+
...this.resolvedTransformers,
333323
after: resolveTransformers(astTransformers.after),
334324
}
335325
}
336326
if (astTransformers.afterDeclarations) {
337-
this.customTransformers = {
338-
...this.customTransformers,
327+
this.resolvedTransformers = {
328+
...this.resolvedTransformers,
339329
afterDeclarations: resolveTransformers(astTransformers.afterDeclarations),
340330
}
341331
}
342332
}
343333

344334
this.logger.debug(
345-
{ customTransformers: this.customTransformers },
335+
{ customTransformers: this.resolvedTransformers },
346336
'normalized custom AST transformers via ts-jest option',
347337
)
348338

@@ -394,25 +384,25 @@ export class ConfigSet {
394384
const finalOptions = result.options
395385
// Target ES2015 output by default (instead of ES3).
396386
if (finalOptions.target === undefined) {
397-
finalOptions.target = ScriptTarget.ES2015
387+
finalOptions.target = this.compilerModule.ScriptTarget.ES2015
398388
}
399389

400390
// check the module interoperability
401391
const target = finalOptions.target
402392
// compute the default if not set
403-
const defaultModule = [ScriptTarget.ES3, ScriptTarget.ES5].includes(target)
404-
? ModuleKind.CommonJS
405-
: ModuleKind.ESNext
393+
const defaultModule = [this.compilerModule.ScriptTarget.ES3, this.compilerModule.ScriptTarget.ES5].includes(target)
394+
? this.compilerModule.ModuleKind.CommonJS
395+
: this.compilerModule.ModuleKind.ESNext
406396
const moduleValue = finalOptions.module ?? defaultModule
407397
if (
408398
!this.babelConfig &&
409-
moduleValue !== ModuleKind.CommonJS &&
399+
moduleValue !== this.compilerModule.ModuleKind.CommonJS &&
410400
!(finalOptions.esModuleInterop || finalOptions.allowSyntheticDefaultImports)
411401
) {
412402
result.errors.push({
413403
code: DiagnosticCodes.ConfigModuleOption,
414404
messageText: Errors.ConfigNoModuleInterop,
415-
category: DiagnosticCategory.Message,
405+
category: this.compilerModule.DiagnosticCategory.Message,
416406
file: undefined,
417407
start: undefined,
418408
length: undefined,
@@ -443,12 +433,18 @@ export class ConfigSet {
443433
*/
444434
const nodeJsVer = process.version
445435
const compilationTarget = result.options.target
436+
const TARGET_TO_VERSION_MAPPING: Record<number, string> = {
437+
[this.compilerModule.ScriptTarget.ES2018]: 'es2018',
438+
[this.compilerModule.ScriptTarget.ES2019]: 'es2019',
439+
[this.compilerModule.ScriptTarget.ES2020]: 'es2020',
440+
[this.compilerModule.ScriptTarget.ESNext]: 'ESNext',
441+
}
446442
/* istanbul ignore next (cover by e2e) */
447443
if (
448444
compilationTarget &&
449445
!this.babelConfig &&
450-
((nodeJsVer.startsWith('v10') && compilationTarget > ScriptTarget.ES2018) ||
451-
(nodeJsVer.startsWith('v12') && compilationTarget > ScriptTarget.ES2019))
446+
((nodeJsVer.startsWith('v10') && compilationTarget > this.compilerModule.ScriptTarget.ES2018) ||
447+
(nodeJsVer.startsWith('v12') && compilationTarget > this.compilerModule.ScriptTarget.ES2019))
452448
) {
453449
const message = interpolate(Errors.MismatchNodeTargetMapping, {
454450
nodeJsVer: process.version,

‎src/transformers/README.md

+3-3
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,15 @@ See https://dev.doctorevidence.com/how-to-write-a-typescript-transform-plugin-fc
77
```ts
88
import { SourceFile, TransformationContext, Transformer, Visitor } from 'typescript'
99

10-
import { ConfigSet } from '../config/config-set'
10+
import type { TsCompilerInstance } from 'ts-jest/dist/types'
1111

1212
// this is a unique identifier for your transformer
1313
export const name = 'my-transformer'
1414
// increment this each time you change the behavior of your transformer
1515
export const version = 1
1616

17-
export function factory(cs: ConfigSet) {
18-
const ts = cs.compilerModule
17+
export function factory(compilerInstance: TsCompilerInstance) {
18+
const ts = compilerInstance.configSet.compilerModule
1919
function createVisitor(ctx: TransformationContext, sf: SourceFile) {
2020
const visitor: Visitor = node => {
2121
// here we can check each node and potentially return

‎src/transformers/hoist-jest.spec.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
import { testing } from 'bs-logger'
21
import ts from 'typescript'
32

3+
import { createConfigSet } from '../__helpers__/fakers'
4+
import { TsCompiler } from '../compiler/ts-compiler'
5+
46
import * as hoist from './hoist-jest'
57

68
const CODE_WITH_HOISTING_NO_JEST_GLOBALS = `
@@ -115,8 +117,7 @@ const CODE_WITH_HOISTING_HAS_JEST_GLOBALS = `
115117
console.log(it)
116118
`
117119

118-
const logger = testing.createLoggerMock()
119-
const createFactory = () => hoist.factory({ logger, compilerModule: ts } as any)
120+
const createFactory = () => hoist.factory(new TsCompiler(createConfigSet(), new Map()))
120121
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
121122

122123
describe('hoisting', () => {

‎src/transformers/hoist-jest.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type {
1111
Visitor,
1212
} from 'typescript'
1313

14-
import type { ConfigSet } from '../config/config-set'
14+
import type { TsCompilerInstance } from '../types'
1515

1616
/**
1717
* What methods of `jest` we should hoist
@@ -36,13 +36,13 @@ export const version = 4
3636
*
3737
* @internal
3838
*/
39-
export function factory(cs: ConfigSet): (ctx: TransformationContext) => Transformer<SourceFile> {
40-
const logger = cs.logger.child({ namespace: 'ts-hoisting' })
39+
export function factory({ configSet }: TsCompilerInstance): (ctx: TransformationContext) => Transformer<SourceFile> {
40+
const logger = configSet.logger.child({ namespace: 'ts-hoisting' })
4141
/**
4242
* Our compiler (typescript, or a module with typescript-like interface)
4343
* To access Program or TypeChecker, do: cs.tsCompiler.program or cs.tsCompiler.program.getTypeChecker()
4444
*/
45-
const ts = cs.compilerModule
45+
const ts = configSet.compilerModule
4646
const importNames: string[] = []
4747

4848
function shouldHoistExpression(node: Node): boolean {

‎src/transformers/index.spec.ts

-16
This file was deleted.

‎src/transformers/index.ts

-8
This file was deleted.

‎src/transformers/path-mapping.spec.ts

+8-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
import { join } from 'path'
22

33
import { testing } from 'bs-logger'
4-
import tsc from 'typescript'
4+
import ts from 'typescript'
55

66
import { createConfigSet } from '../__helpers__/fakers'
7+
import { TsCompiler } from '../compiler/ts-compiler'
78
import { normalizeSlashes } from '../utils/normalize-slashes'
89

910
import * as pathMapping from './path-mapping'
@@ -59,9 +60,9 @@ describe('path-mapping', () => {
5960
},
6061
logger,
6162
})
62-
const createFactory = () => pathMapping.factory(configSet)
63-
const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } })
64-
jest.spyOn(tsc, 'resolveModuleName').mockReturnValue({
63+
const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map()))
64+
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
65+
jest.spyOn(ts, 'resolveModuleName').mockReturnValue({
6566
resolvedModule: {
6667
resolvedFileName: require.resolve('../utils/json'),
6768
extension: 'ts',
@@ -114,10 +115,10 @@ describe('path-mapping', () => {
114115
},
115116
logger,
116117
})
117-
const createFactory = () => pathMapping.factory(configSet)
118-
const transpile = (source: string) => tsc.transpileModule(source, { transformers: { before: [createFactory()] } })
118+
const createFactory = () => pathMapping.factory(new TsCompiler(configSet, new Map()))
119+
const transpile = (source: string) => ts.transpileModule(source, { transformers: { before: [createFactory()] } })
119120
const resolvedFileNameStub = join('..', `utils/json.${extension}`)
120-
jest.spyOn(tsc, 'resolveModuleName').mockReturnValue({
121+
jest.spyOn(ts, 'resolveModuleName').mockReturnValue({
121122
resolvedModule: {
122123
resolvedFileName: resolvedFileNameStub,
123124
extension,

‎src/transformers/path-mapping.ts

+7-5
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { basename, dirname, isAbsolute, join, normalize, relative } from 'path'
99
import { LogContexts, LogLevels } from 'bs-logger'
1010
import type * as _ts from 'typescript'
1111

12-
import type { ConfigSet } from '../config/config-set'
12+
import type { TsCompilerInstance } from '../types'
1313

1414
/**
1515
* @internal
@@ -26,10 +26,12 @@ const isBaseDir = (base: string, dir: string) => !relative(base, dir)?.startsWit
2626
/**
2727
* The factory of import path alias transformer factory.
2828
*/
29-
export function factory(cs: ConfigSet): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> {
30-
const logger = cs.logger.child({ namespace: 'ts-path-mapping' })
31-
const ts = cs.compilerModule
32-
const compilerOptions = cs.parsedTsConfig.options
29+
export function factory({
30+
configSet,
31+
}: TsCompilerInstance): (ctx: _ts.TransformationContext) => _ts.Transformer<_ts.SourceFile> {
32+
const logger = configSet.logger.child({ namespace: 'ts-path-mapping' })
33+
const ts = configSet.compilerModule
34+
const compilerOptions = configSet.parsedTsConfig.options
3335
const rootDirs = compilerOptions.rootDirs?.filter(isAbsolute)
3436

3537
const isDynamicImport = (node: _ts.Node): node is _ts.CallExpression =>

‎src/ts-jest-transformer.spec.ts

+14-5
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ describe('TsJestTransformer', () => {
3737
() => {
3838
const obj1 = {
3939
config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
40+
cacheFS: new Map(),
4041
}
4142
const obj2 = { ...obj1, config: { ...obj1.config, globals: Object.create(null) } }
4243
// @ts-expect-error testing purpose
@@ -51,6 +52,7 @@ describe('TsJestTransformer', () => {
5152
test('should return the same config set for same values with jest config objects', () => {
5253
const obj1 = {
5354
config: { cwd: process.cwd(), extensionsToTreatAsEsm: [], globals: {}, testMatch: [], testRegex: [] },
55+
cacheFS: new Map(),
5456
}
5557
const obj2 = { ...obj1 }
5658
// @ts-expect-error testing purpose
@@ -135,6 +137,7 @@ describe('TsJestTransformer', () => {
135137
config: { foo: 'bar', testMatch: [], testRegex: [], extensionsToTreatAsEsm: [] } as any,
136138
instrument: false,
137139
rootDir: '/foo',
140+
cacheFS: new Map(),
138141
},
139142
} as any
140143
const transformOptionsWithCache = {
@@ -256,6 +259,7 @@ describe('TsJestTransformer', () => {
256259
testRegex: [],
257260
extensionsToTreatAsEsm: [],
258261
},
262+
cacheFS: new Map(),
259263
} as any
260264
let tr!: TsJestTransformer
261265

@@ -268,6 +272,7 @@ describe('TsJestTransformer', () => {
268272
const filePath = 'foo.html'
269273
const fileContent = '<h1>Hello World</h1>'
270274
const transformOptions = {
275+
...baseTransformOptions,
271276
config: {
272277
...baseTransformOptions.config,
273278
globals: {
@@ -276,7 +281,7 @@ describe('TsJestTransformer', () => {
276281
},
277282
},
278283
},
279-
} as any
284+
}
280285
tr.getCacheKey(fileContent, filePath, transformOptions)
281286

282287
const result = tr.process(fileContent, filePath, transformOptions)
@@ -297,13 +302,14 @@ describe('TsJestTransformer', () => {
297302
const fileContent = 'const foo = 1'
298303
const filePath = 'foo.js'
299304
const transformOptions = {
305+
...baseTransformOptions,
300306
config: {
301307
...baseTransformOptions.config,
302308
globals: {
303309
'ts-jest': { tsconfig: { allowJs: false } },
304310
},
305311
},
306-
} as any
312+
}
307313
tr.getCacheKey(fileContent, filePath, transformOptions)
308314
logTarget.clear()
309315

@@ -333,13 +339,14 @@ describe('TsJestTransformer', () => {
333339
const fileContent = 'const foo = 1'
334340
const output = 'var foo = 1'
335341
const transformOptions = {
342+
...baseTransformOptions,
336343
config: {
337344
...baseTransformOptions.config,
338345
globals: {
339346
'ts-jest': { tsconfig: { allowJs: true } },
340347
},
341348
},
342-
} as any
349+
}
343350
tr.getCacheKey(fileContent, filePath, transformOptions)
344351
logTarget.clear()
345352
jest.spyOn(TsJestCompiler.prototype, 'getCompiledOutput').mockReturnValueOnce(output)
@@ -353,13 +360,14 @@ describe('TsJestTransformer', () => {
353360
const fileContent = 'foo'
354361
const filePath = 'foo.bar'
355362
const transformOptions = {
363+
...baseTransformOptions,
356364
config: {
357365
...baseTransformOptions.config,
358366
globals: {
359367
'ts-jest': { tsconfig: { allowJs: true } },
360368
},
361369
},
362-
} as any
370+
}
363371
tr.getCacheKey(fileContent, filePath, transformOptions)
364372
logTarget.clear()
365373

@@ -375,13 +383,14 @@ describe('TsJestTransformer', () => {
375383
test.each(['foo.bar', 'foo.js'])('should process file with babel-jest', (filePath) => {
376384
const fileContent = 'foo'
377385
const transformOptions = {
386+
...baseTransformOptions,
378387
config: {
379388
...baseTransformOptions.config,
380389
globals: {
381390
'ts-jest': { babelConfig: true },
382391
},
383392
},
384-
} as any
393+
}
385394
tr.getCacheKey(fileContent, filePath, transformOptions)
386395
logTarget.clear()
387396

‎src/types.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -198,14 +198,19 @@ export interface CompilerInstance {
198198
getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap
199199
getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string
200200
}
201+
export interface TsCompilerInstance extends CompilerInstance {
202+
configSet: ConfigSet
203+
program: _ts.Program | undefined
204+
}
201205
/**
202206
* @internal
203207
*/
204208
export interface AstTransformerDesc<T = Record<string, unknown>> {
205209
name: string
206210
version: number
207211
factory(
208-
cs: ConfigSet,
212+
tsCompiler: TsCompilerInstance,
209213
opts?: T,
210214
): _ts.TransformerFactory<_ts.SourceFile> | _ts.TransformerFactory<_ts.Bundle | _ts.SourceFile>
215+
options?: T
211216
}

0 commit comments

Comments
 (0)
Please sign in to comment.