Skip to content

Commit 064bf3a

Browse files
authoredFeb 25, 2021
fix(compiler): type check correctly in watch mode when a file content itself has changed (#2405)
Closes #2118
1 parent 8615306 commit 064bf3a

6 files changed

+198
-107
lines changed
 

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

+31-7
Original file line numberDiff line numberDiff line change
@@ -374,16 +374,16 @@ const t: string = f(5)
374374
})
375375
})
376376

377-
describe('getResolvedModulesMap', () => {
378-
const fileName = 'foo.ts'
377+
describe('getResolvedModules', () => {
378+
const fileName = join(__dirname, '..', '__mocks__', 'thing.spec.ts')
379379
const fileContent = 'const foo = 1'
380380

381381
test('should return undefined when file name is not known to compiler', () => {
382382
const compiler = makeCompiler({
383383
tsJestConfig: baseTsJestConfig,
384384
})
385385

386-
expect(compiler.getResolvedModulesMap(fileContent, fileName)).toBeUndefined()
386+
expect(compiler.getResolvedModules(fileContent, fileName, new Map())).toEqual([])
387387
})
388388

389389
test('should return undefined when it is isolatedModules true', () => {
@@ -394,7 +394,7 @@ const t: string = f(5)
394394
},
395395
})
396396

397-
expect(compiler.getResolvedModulesMap(fileContent, fileName)).toBeUndefined()
397+
expect(compiler.getResolvedModules(fileContent, fileName, new Map())).toEqual([])
398398
})
399399

400400
test('should return undefined when file has no resolved modules', () => {
@@ -407,12 +407,12 @@ const t: string = f(5)
407407
jestCacheFS,
408408
)
409409

410-
expect(compiler.getResolvedModulesMap(fileContent, fileName)).toBeUndefined()
410+
expect(compiler.getResolvedModules(fileContent, fileName, new Map())).toEqual([])
411411
})
412412

413413
test('should return resolved modules when file has resolved modules', () => {
414414
const jestCacheFS = new Map<string, string>()
415-
const fileContentWithModules = readFileSync(join(__dirname, '..', '__mocks__', 'thing.spec.ts'), 'utf-8')
415+
const fileContentWithModules = readFileSync(fileName, 'utf-8')
416416
jestCacheFS.set(fileName, fileContentWithModules)
417417
const compiler = makeCompiler(
418418
{
@@ -421,7 +421,7 @@ const t: string = f(5)
421421
jestCacheFS,
422422
)
423423

424-
expect(compiler.getResolvedModulesMap(fileContentWithModules, fileName)).toBeDefined()
424+
expect(compiler.getResolvedModules(fileContentWithModules, fileName, new Map())).not.toEqual([])
425425
})
426426
})
427427

@@ -476,6 +476,30 @@ const t: string = f(5)
476476

477477
expect(() => compiler.getCompiledOutput(source, fileName, false)).toThrowErrorMatchingSnapshot()
478478
})
479+
480+
test('should report correct diagnostics when file content has changed', () => {
481+
const compiler = makeCompiler(
482+
{
483+
tsJestConfig: baseTsJestConfig,
484+
},
485+
jestCacheFS,
486+
)
487+
const fileName = join(process.cwd(), 'src', '__mocks__', 'thing.spec.ts')
488+
const oldSource = `
489+
foo.split('-');
490+
`
491+
const newSource = `
492+
const foo = 'bla-bla'
493+
foo.split('-');
494+
`
495+
jestCacheFS.set(fileName, oldSource)
496+
497+
expect(() => compiler.getCompiledOutput(oldSource, fileName, false)).toThrowError()
498+
499+
jestCacheFS.set(fileName, newSource)
500+
501+
expect(() => compiler.getCompiledOutput(newSource, fileName, false)).not.toThrowError()
502+
})
479503
})
480504

481505
test('should pass Program instance into custom transformers', () => {

‎src/compiler/ts-compiler.ts

+108-43
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,13 @@ import type {
1616
Bundle,
1717
CustomTransformerFactory,
1818
CustomTransformers,
19+
ModuleResolutionHost,
20+
ModuleResolutionCache,
1921
} from 'typescript'
2022

2123
import { ConfigSet, TS_JEST_OUT_DIR } from '../config/config-set'
2224
import { LINE_FEED } from '../constants'
23-
import type { ResolvedModulesMap, StringMap, TsCompilerInstance, TsJestAstTransformer, TTypeScript } from '../types'
25+
import type { StringMap, TsCompilerInstance, TsJestAstTransformer, TTypeScript } from '../types'
2426
import { rootLogger } from '../utils/logger'
2527
import { Errors, interpolate } from '../utils/messages'
2628

@@ -31,18 +33,26 @@ export class TsCompiler implements TsCompilerInstance {
3133
protected readonly _ts: TTypeScript
3234
protected readonly _initialCompilerOptions: CompilerOptions
3335
protected _compilerOptions: CompilerOptions
36+
/**
37+
* @private
38+
*/
39+
private _runtimeCacheFS: StringMap
40+
/**
41+
* @private
42+
*/
43+
private _fileContentCache: StringMap | undefined
3444
/**
3545
* @internal
3646
*/
3747
private readonly _parsedTsConfig: ParsedCommandLine
3848
/**
3949
* @internal
4050
*/
41-
private readonly _compilerCacheFS: Map<string, number> = new Map<string, number>()
51+
private readonly _fileVersionCache: Map<string, number> | undefined
4252
/**
4353
* @internal
4454
*/
45-
private _cachedReadFile: ((fileName: string) => string | undefined) | undefined
55+
private readonly _cachedReadFile: ((fileName: string) => string | undefined) | undefined
4656
/**
4757
* @internal
4858
*/
@@ -51,15 +61,50 @@ export class TsCompiler implements TsCompilerInstance {
5161
* @internal
5262
*/
5363
private _languageService: LanguageService | undefined
64+
/**
65+
* @internal
66+
*/
67+
private readonly _moduleResolutionHost: ModuleResolutionHost | undefined
68+
/**
69+
* @internal
70+
*/
71+
private readonly _moduleResolutionCache: ModuleResolutionCache | undefined
72+
5473
program: Program | undefined
5574

56-
constructor(readonly configSet: ConfigSet, readonly jestCacheFS: StringMap) {
75+
constructor(readonly configSet: ConfigSet, readonly runtimeCacheFS: StringMap) {
5776
this._ts = configSet.compilerModule
5877
this._logger = rootLogger.child({ namespace: 'ts-compiler' })
5978
this._parsedTsConfig = this.configSet.parsedTsConfig as ParsedCommandLine
6079
this._initialCompilerOptions = { ...this._parsedTsConfig.options }
6180
this._compilerOptions = { ...this._initialCompilerOptions }
81+
this._runtimeCacheFS = runtimeCacheFS
6282
if (!this.configSet.isolatedModules) {
83+
this._fileContentCache = new Map<string, string>()
84+
this._fileVersionCache = new Map<string, number>()
85+
this._cachedReadFile = this._logger.wrap(
86+
{
87+
namespace: 'ts:serviceHost',
88+
call: null,
89+
[LogContexts.logLevel]: LogLevels.trace,
90+
},
91+
'readFile',
92+
memoize(this._ts.sys.readFile),
93+
)
94+
/* istanbul ignore next */
95+
this._moduleResolutionHost = {
96+
fileExists: memoize(this._ts.sys.fileExists),
97+
readFile: this._cachedReadFile,
98+
directoryExists: memoize(this._ts.sys.directoryExists),
99+
getCurrentDirectory: () => this.configSet.cwd,
100+
realpath: this._ts.sys.realpath && memoize(this._ts.sys.realpath),
101+
getDirectories: memoize(this._ts.sys.getDirectories),
102+
}
103+
this._moduleResolutionCache = this._ts.createModuleResolutionCache(
104+
this.configSet.cwd,
105+
(x) => x,
106+
this._compilerOptions,
107+
)
63108
this._createLanguageService()
64109
}
65110
}
@@ -68,11 +113,6 @@ export class TsCompiler implements TsCompilerInstance {
68113
* @internal
69114
*/
70115
private _createLanguageService(): void {
71-
const serviceHostTraceCtx = {
72-
namespace: 'ts:serviceHost',
73-
call: null,
74-
[LogContexts.logLevel]: LogLevels.trace,
75-
}
76116
// Initialize memory cache for typescript compiler
77117
this._parsedTsConfig.fileNames
78118
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -81,29 +121,17 @@ export class TsCompiler implements TsCompilerInstance {
81121
!this.configSet.isTestFile(fileName) &&
82122
!fileName.includes(this._parsedTsConfig.options.outDir ?? TS_JEST_OUT_DIR),
83123
)
84-
.forEach((fileName) => this._compilerCacheFS.set(fileName, 0))
85-
this._cachedReadFile = this._logger.wrap(serviceHostTraceCtx, 'readFile', memoize(this._ts.sys.readFile))
86-
/* istanbul ignore next */
87-
const moduleResolutionHost = {
88-
fileExists: memoize(this._ts.sys.fileExists),
89-
readFile: this._cachedReadFile,
90-
directoryExists: memoize(this._ts.sys.directoryExists),
91-
getCurrentDirectory: () => this.configSet.cwd,
92-
realpath: this._ts.sys.realpath && memoize(this._ts.sys.realpath),
93-
getDirectories: memoize(this._ts.sys.getDirectories),
94-
}
95-
const moduleResolutionCache = this._ts.createModuleResolutionCache(
96-
this.configSet.cwd,
97-
(x) => x,
98-
this._compilerOptions,
99-
)
124+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
125+
.forEach((fileName) => this._fileVersionCache!.set(fileName, 0))
100126
/* istanbul ignore next */
101127
const serviceHost: LanguageServiceHost = {
102128
getProjectVersion: () => String(this._projectVersion),
103-
getScriptFileNames: () => [...this._compilerCacheFS.keys()],
129+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
130+
getScriptFileNames: () => [...this._fileVersionCache!.keys()],
104131
getScriptVersion: (fileName: string) => {
105132
const normalizedFileName = normalize(fileName)
106-
const version = this._compilerCacheFS.get(normalizedFileName)
133+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
134+
const version = this._fileVersionCache!.get(normalizedFileName)
107135

108136
// We need to return `undefined` and not a string here because TypeScript will use
109137
// `getScriptVersion` and compare against their own version - which can be `undefined`.
@@ -122,13 +150,20 @@ export class TsCompiler implements TsCompilerInstance {
122150
// Read contents from TypeScript memory cache.
123151
if (!hit) {
124152
const fileContent =
125-
this.jestCacheFS.get(normalizedFileName) ?? this._cachedReadFile?.(normalizedFileName) ?? undefined
153+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
154+
this._fileContentCache!.get(normalizedFileName) ??
155+
this._runtimeCacheFS.get(normalizedFileName) ??
156+
this._cachedReadFile?.(normalizedFileName) ??
157+
undefined
126158
if (fileContent) {
127-
this.jestCacheFS.set(normalizedFileName, fileContent)
128-
this._compilerCacheFS.set(normalizedFileName, 1)
159+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
160+
this._fileContentCache!.set(normalizedFileName, fileContent)
161+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
162+
this._fileVersionCache!.set(normalizedFileName, 1)
129163
}
130164
}
131-
const contents = this.jestCacheFS.get(normalizedFileName)
165+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
166+
const contents = this._fileContentCache!.get(normalizedFileName)
132167

133168
if (contents === undefined) return
134169

@@ -151,8 +186,10 @@ export class TsCompiler implements TsCompilerInstance {
151186
moduleName,
152187
containingFile,
153188
this._compilerOptions,
154-
moduleResolutionHost,
155-
moduleResolutionCache,
189+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
190+
this._moduleResolutionHost!,
191+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
192+
this._moduleResolutionCache!,
156193
)
157194

158195
return resolvedModule
@@ -165,12 +202,29 @@ export class TsCompiler implements TsCompilerInstance {
165202
this.program = this._languageService.getProgram()
166203
}
167204

168-
getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap {
169-
this._updateMemoryCache(fileContent, fileName)
205+
getResolvedModules(fileContent: string, fileName: string, runtimeCacheFS: StringMap): string[] {
206+
// In watch mode, it is possible that the initial cacheFS becomes empty
207+
if (!this.runtimeCacheFS.size) {
208+
this._runtimeCacheFS = runtimeCacheFS
209+
}
210+
211+
return this._ts
212+
.preProcessFile(fileContent, true, true)
213+
.importedFiles.map((importedFile) => {
214+
const { resolvedModule } = this._ts.resolveModuleName(
215+
importedFile.fileName,
216+
fileName,
217+
this._compilerOptions,
218+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
219+
this._moduleResolutionHost!,
220+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
221+
this._moduleResolutionCache!,
222+
)
170223

171-
// See https://github.com/microsoft/TypeScript/blob/master/src/compiler/utilities.ts#L164
172-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
173-
return (this._languageService?.getProgram()?.getSourceFile(fileName) as any)?.resolvedModules
224+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
225+
return resolvedModule?.resolvedFileName ?? ''
226+
})
227+
.filter((resolvedFileName) => !!resolvedFileName)
174228
}
175229

176230
getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string {
@@ -261,7 +315,12 @@ export class TsCompiler implements TsCompilerInstance {
261315
*/
262316
private _isFileInCache(fileName: string): boolean {
263317
return (
264-
this.jestCacheFS.has(fileName) && this._compilerCacheFS.has(fileName) && this._compilerCacheFS.get(fileName) !== 0
318+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
319+
this._fileContentCache!.has(fileName) &&
320+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
321+
this._fileVersionCache!.has(fileName) &&
322+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
323+
this._fileVersionCache!.get(fileName) !== 0
265324
)
266325
}
267326

@@ -275,14 +334,20 @@ export class TsCompiler implements TsCompilerInstance {
275334
let shouldIncrementProjectVersion = false
276335
const hit = this._isFileInCache(fileName)
277336
if (!hit) {
278-
this._compilerCacheFS.set(fileName, 1)
337+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
338+
this._fileVersionCache!.set(fileName, 1)
279339
shouldIncrementProjectVersion = true
280340
} else {
281-
const prevVersion = this._compilerCacheFS.get(fileName) ?? 0
282-
const previousContents = this.jestCacheFS.get(fileName)
341+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
342+
const prevVersion = this._fileVersionCache!.get(fileName) ?? 0
343+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
344+
const previousContents = this._fileContentCache!.get(fileName)
283345
// Avoid incrementing cache when nothing has changed.
284346
if (previousContents !== contents) {
285-
this._compilerCacheFS.set(fileName, prevVersion + 1)
347+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
348+
this._fileVersionCache!.set(fileName, prevVersion + 1)
349+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
350+
this._fileContentCache!.set(fileName, contents)
286351
// Only bump project version when file is modified in cache, not when discovered for the first time
287352
if (hit) shouldIncrementProjectVersion = true
288353
}

‎src/compiler/ts-jest-compiler.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { ConfigSet } from '../config/config-set'
2-
import type { CompilerInstance, ResolvedModulesMap, StringMap } from '../types'
2+
import type { CompilerInstance, StringMap } from '../types'
33

44
import { TsCompiler } from './ts-compiler'
55

@@ -9,13 +9,13 @@ import { TsCompiler } from './ts-compiler'
99
export class TsJestCompiler implements CompilerInstance {
1010
private readonly _compilerInstance: CompilerInstance
1111

12-
constructor(readonly configSet: ConfigSet, readonly jestCacheFS: StringMap) {
12+
constructor(configSet: ConfigSet, runtimeCacheFS: StringMap) {
1313
// Later we can add swc/esbuild or other typescript compiler instance here
14-
this._compilerInstance = new TsCompiler(configSet, jestCacheFS)
14+
this._compilerInstance = new TsCompiler(configSet, runtimeCacheFS)
1515
}
1616

17-
getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap {
18-
return this._compilerInstance.getResolvedModulesMap(fileContent, fileName)
17+
getResolvedModules(fileContent: string, fileName: string, runtimeCacheFS: StringMap): string[] {
18+
return this._compilerInstance.getResolvedModules(fileContent, fileName, runtimeCacheFS)
1919
}
2020

2121
getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string {

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

+44-34
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { join } from 'path'
44
import { Logger, LogLevels } from 'bs-logger'
55
import { removeSync, writeFileSync } from 'fs-extra'
66
import mkdirp from 'mkdirp'
7-
import { Extension, ResolvedModuleFull } from 'typescript'
87

98
import { createConfigSet } from './__helpers__/fakers'
109
import { logTargetMock } from './__helpers__/mocks'
@@ -13,19 +12,13 @@ import { TsCompiler } from './compiler/ts-compiler'
1312
import { TsJestCompiler } from './compiler/ts-jest-compiler'
1413
import { ConfigSet } from './config/config-set'
1514
import { CACHE_KEY_EL_SEPARATOR, TsJestTransformer } from './ts-jest-transformer'
16-
import type { ProjectConfigTsJest, ResolvedModulesMap, StringMap } from './types'
15+
import type { DepGraphInfo, ProjectConfigTsJest, StringMap } from './types'
1716
import { stringify } from './utils/json'
1817
import { sha1 } from './utils/sha1'
1918
import { VersionCheckers } from './utils/version-checkers'
2019

2120
const logTarget = logTargetMock()
2221
const cacheDir = join(process.cwd(), 'tmp')
23-
const resolvedModule = {
24-
resolvedFileName: join(__dirname, '__mocks__', 'thing.ts'),
25-
extension: Extension.Ts,
26-
isExternalLibraryImport: false,
27-
packageId: undefined,
28-
}
2922

3023
beforeEach(() => {
3124
logTarget.clear()
@@ -126,8 +119,11 @@ Array [
126119
})
127120
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
128121
const tsCacheDir = cs.tsCacheDir!
129-
const depGraphs: ResolvedModulesMap = new Map<string, ResolvedModuleFull | undefined>()
130-
depGraphs.set(fileName, resolvedModule)
122+
const depGraphs: Map<string, DepGraphInfo> = new Map<string, DepGraphInfo>()
123+
depGraphs.set(fileName, {
124+
fileContent: 'const foo = 1',
125+
resolvedModuleNames: [],
126+
})
131127
const resolvedModulesCacheDir = join(tsCacheDir, sha1('ts-jest-resolved-modules', CACHE_KEY_EL_SEPARATOR))
132128
mkdirp.sync(tsCacheDir)
133129
writeFileSync(resolvedModulesCacheDir, stringify([...depGraphs]))
@@ -165,10 +161,8 @@ Array [
165161
cacheDirectory: cacheDir,
166162
},
167163
}
168-
const depGraphs: ResolvedModulesMap = new Map<string, ResolvedModuleFull | undefined>()
169164

170165
beforeEach(() => {
171-
depGraphs.clear()
172166
// @ts-expect-error testing purpose
173167
TsJestTransformer._cachedConfigSets = []
174168
tr = new TsJestTransformer()
@@ -202,20 +196,22 @@ Array [
202196
})
203197

204198
test('should be the same with the same file content', () => {
205-
depGraphs.set(input.fileName, resolvedModule)
206-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
199+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
207200

208201
const cacheKey1 = tr.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
209202
const cacheKey2 = tr.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
210203

211204
expect(cacheKey1).toEqual(cacheKey2)
212-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledTimes(1)
213-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledWith(input.fileContent, input.fileName)
205+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledTimes(1)
206+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledWith(
207+
input.fileContent,
208+
input.fileName,
209+
new Map(),
210+
)
214211
})
215212

216213
test('should be different between isolatedModules true and isolatedModules false', () => {
217-
depGraphs.set(input.fileName, resolvedModule)
218-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
214+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
219215

220216
const cacheKey1 = tr.getCacheKey(input.fileContent, input.fileName, {
221217
...input.transformOptions,
@@ -225,47 +221,61 @@ Array [
225221
},
226222
})
227223

228-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
224+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
229225
const tr1 = new TsJestTransformer()
230226
const cacheKey2 = tr1.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
231227

232-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledTimes(1)
233-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledWith(input.fileContent, input.fileName)
228+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledTimes(1)
229+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledWith(
230+
input.fileContent,
231+
input.fileName,
232+
new Map(),
233+
)
234234
expect(cacheKey1).not.toEqual(cacheKey2)
235235
})
236236

237237
test('should be different with different file content for the same file', () => {
238-
depGraphs.set(input.fileName, resolvedModule)
239-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
238+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
240239

241240
const cacheKey1 = tr.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
242241

243-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
242+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
244243
const newFileContent = 'const foo = 1'
245244
const cacheKey2 = tr.getCacheKey(newFileContent, input.fileName, transformOptionsWithCache)
246245

247246
expect(cacheKey1).not.toEqual(cacheKey2)
248-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledTimes(2)
249-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenNthCalledWith(
247+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledTimes(2)
248+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenNthCalledWith(
250249
1,
251250
input.fileContent,
252251
input.fileName,
252+
new Map(),
253+
)
254+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenNthCalledWith(
255+
2,
256+
newFileContent,
257+
input.fileName,
258+
new Map(),
253259
)
254-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenNthCalledWith(2, newFileContent, input.fileName)
255260
})
256261

257262
test('should be different with non existed imported modules', () => {
258-
depGraphs.set(input.fileName, resolvedModule)
259-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(depGraphs)
263+
jest
264+
.spyOn(TsJestCompiler.prototype, 'getResolvedModules')
265+
.mockReturnValueOnce([join(process.cwd(), 'src', '__mocks__', 'thing.ts')])
260266

261267
const cacheKey1 = tr.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
262268

263269
jest.spyOn(fs, 'existsSync').mockReturnValueOnce(false)
264270
const cacheKey2 = tr.getCacheKey(input.fileContent, input.fileName, transformOptionsWithCache)
265271

266272
expect(cacheKey1).not.toEqual(cacheKey2)
267-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledTimes(1)
268-
expect(TsJestCompiler.prototype.getResolvedModulesMap).toHaveBeenCalledWith(input.fileContent, input.fileName)
273+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledTimes(1)
274+
expect(TsJestCompiler.prototype.getResolvedModules).toHaveBeenCalledWith(
275+
input.fileContent,
276+
input.fileName,
277+
new Map(),
278+
)
269279
})
270280
})
271281

@@ -282,7 +292,7 @@ Array [
282292

283293
beforeEach(() => {
284294
tr = new TsJestTransformer()
285-
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModulesMap').mockReturnValueOnce(new Map())
295+
jest.spyOn(TsJestCompiler.prototype, 'getResolvedModules').mockReturnValueOnce([])
286296
})
287297

288298
test('should process input as stringified content with content matching stringifyContentPathRegex option', () => {
@@ -424,8 +434,8 @@ Array [
424434

425435
describe('subclass extends TsJestTransformer', () => {
426436
class MyTsCompiler extends TsCompiler {
427-
constructor(readonly configSet: ConfigSet, readonly jestCacheFS: StringMap) {
428-
super(configSet, jestCacheFS)
437+
constructor(readonly configSet: ConfigSet, readonly runtimeCacheFS: StringMap) {
438+
super(configSet, runtimeCacheFS)
429439
}
430440
}
431441

‎src/ts-jest-transformer.ts

+4-15
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import mkdirp from 'mkdirp'
99
import { TsJestCompiler } from './compiler/ts-jest-compiler'
1010
import { ConfigSet } from './config/config-set'
1111
import { DECLARATION_TYPE_EXT, JS_JSX_REGEX, TS_TSX_REGEX } from './constants'
12-
import type { ProjectConfigTsJest, TransformOptionsTsJest } from './types'
12+
import type { DepGraphInfo, ProjectConfigTsJest, TransformOptionsTsJest } from './types'
1313
import { importer } from './utils/importer'
1414
import { parse, stringify } from './utils/json'
1515
import { JsonableValue } from './utils/jsonable-value'
@@ -32,11 +32,6 @@ interface TsJestHooksMap {
3232
afterProcess?(args: any[], result: string | TransformedSource): string | TransformedSource | void
3333
}
3434

35-
interface DepGraphInfo {
36-
fileContent: string
37-
resolveModuleNames: string[]
38-
}
39-
4035
/**
4136
* @internal
4237
*/
@@ -236,24 +231,18 @@ export class TsJestTransformer implements Transformer {
236231
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
237232
resolvedModuleNames = this._depGraphs
238233
.get(filePath)!
239-
.resolveModuleNames.filter((moduleName) => existsSync(moduleName))
234+
.resolvedModuleNames.filter((moduleName) => existsSync(moduleName))
240235
} else {
241236
this._logger.debug(
242237
{ fileName: filePath, transformOptions },
243238
'getting resolved modules from TypeScript API for',
244239
filePath,
245240
)
246241

247-
const resolvedModuleMap = this._compiler.getResolvedModulesMap(fileContent, filePath)
248-
resolvedModuleNames = resolvedModuleMap
249-
? [...resolvedModuleMap.values()]
250-
.filter((resolvedModule) => resolvedModule !== undefined)
251-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
252-
.map((resolveModule) => resolveModule!.resolvedFileName)
253-
: []
242+
resolvedModuleNames = this._compiler.getResolvedModules(fileContent, filePath, transformOptions.cacheFS)
254243
this._depGraphs.set(filePath, {
255244
fileContent,
256-
resolveModuleNames: resolvedModuleNames,
245+
resolvedModuleNames,
257246
})
258247
writeFileSync(this._tsResolvedModulesCachePath, stringify([...this._depGraphs]))
259248
}

‎src/types.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -204,15 +204,18 @@ export interface InitialOptionsTsJest extends Config.InitialOptions {
204204
globals?: GlobalConfigTsJest
205205
}
206206

207-
export type ResolvedModulesMap = Map<string, _ts.ResolvedModuleFull | undefined> | undefined
208-
209207
/**
210208
* @internal
211209
*/
212210
export type StringMap = Map<string, string>
213211

212+
export interface DepGraphInfo {
213+
fileContent: string
214+
resolvedModuleNames: string[]
215+
}
216+
214217
export interface CompilerInstance {
215-
getResolvedModulesMap(fileContent: string, fileName: string): ResolvedModulesMap
218+
getResolvedModules(fileContent: string, fileName: string, runtimeCacheFS: StringMap): string[]
216219
getCompiledOutput(fileContent: string, fileName: string, supportsStaticESM: boolean): string
217220
}
218221
export interface TsCompilerInstance extends CompilerInstance {

0 commit comments

Comments
 (0)
Please sign in to comment.