Skip to content

Commit 9f46ace

Browse files
authoredOct 26, 2020
feat(config): allow to override resolve tsconfig behavior (#2063)
This change is needed because some jest transformers which want to reuse/extend our `ConfigSet` but want to change the default behavior when reading and resolving tsconfig. This PR introduces: - Change `_readTsConfig` to `_resolveTsConfig` and set `_resolveTsConfig` to `protected` - Allow to override `_resolveTsConfig` via overload signature - Expose `_transformCfgStr`, `_configSet`, `_overriddenCompilerOptions`
1 parent 681bfef commit 9f46ace

File tree

6 files changed

+84
-92
lines changed

6 files changed

+84
-92
lines changed
 

‎src/compiler/language-service.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ export const initializeLanguageServiceInstance = (configs: ConfigSet, logger: Lo
7575
}
7676
// Initialize memory cache for typescript compiler
7777
configs.parsedTsConfig.fileNames
78-
.filter((fileName) => !configs.isTestFile(fileName))
79-
.forEach((fileName) => {
78+
.filter((fileName: string) => !configs.isTestFile(fileName))
79+
.forEach((fileName: string) => {
8080
memoryCache.files.set(fileName, {
8181
version: 0,
8282
})
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,57 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3+
exports[`_resolveTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and allowSyntheticDefaultImports is false in tsConfig should use correct paths when searching 1`] = `
4+
Array [
5+
Object {
6+
"category": 3,
7+
"code": 151001,
8+
"file": undefined,
9+
"length": undefined,
10+
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
11+
"start": undefined,
12+
},
13+
]
14+
`;
15+
16+
exports[`_resolveTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and allowSyntheticDefaultImports is false in tsConfig should use given tsconfig path 1`] = `
17+
Array [
18+
Object {
19+
"category": 3,
20+
"code": 151001,
21+
"file": undefined,
22+
"length": undefined,
23+
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
24+
"start": undefined,
25+
},
26+
]
27+
`;
28+
29+
exports[`_resolveTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and esModuleInterop is not in tsConfig should use correct paths when searching 1`] = `
30+
Array [
31+
Object {
32+
"category": 3,
33+
"code": 151001,
34+
"file": undefined,
35+
"length": undefined,
36+
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
37+
"start": undefined,
38+
},
39+
]
40+
`;
41+
42+
exports[`_resolveTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and esModuleInterop is not in tsConfig should use given tsconfig path 1`] = `
43+
Array [
44+
Object {
45+
"category": 3,
46+
"code": 151001,
47+
"file": undefined,
48+
"length": undefined,
49+
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
50+
"start": undefined,
51+
},
52+
]
53+
`;
54+
355
exports[`customTransformers should return an object containing all resolved transformers 1`] = `
456
Object {
557
"before": Array [
@@ -55,55 +107,3 @@ exports[`isTestFile should return a boolean value whether the file matches test
55107
exports[`isTestFile should return a boolean value whether the file matches test pattern 3`] = `true`;
56108

57109
exports[`isTestFile should return a boolean value whether the file matches test pattern 4`] = `true`;
58-
59-
exports[`readTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and allowSyntheticDefaultImports is false in tsConfig should use correct paths when searching 1`] = `
60-
Array [
61-
Object {
62-
"category": 3,
63-
"code": 151001,
64-
"file": undefined,
65-
"length": undefined,
66-
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
67-
"start": undefined,
68-
},
69-
]
70-
`;
71-
72-
exports[`readTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and allowSyntheticDefaultImports is false in tsConfig should use given tsconfig path 1`] = `
73-
Array [
74-
Object {
75-
"category": 3,
76-
"code": 151001,
77-
"file": undefined,
78-
"length": undefined,
79-
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
80-
"start": undefined,
81-
},
82-
]
83-
`;
84-
85-
exports[`readTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and esModuleInterop is not in tsConfig should use correct paths when searching 1`] = `
86-
Array [
87-
Object {
88-
"category": 3,
89-
"code": 151001,
90-
"file": undefined,
91-
"length": undefined,
92-
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
93-
"start": undefined,
94-
},
95-
]
96-
`;
97-
98-
exports[`readTsConfig resolve configFileName normally module in tsConfig is not the same as forced module and esModuleInterop is not in tsConfig should use given tsconfig path 1`] = `
99-
Array [
100-
Object {
101-
"category": 3,
102-
"code": 151001,
103-
"file": undefined,
104-
"length": undefined,
105-
"messageText": "If you have issues related to imports, you should consider setting \`esModuleInterop\` to \`true\` in your TypeScript configuration file (usually \`tsconfig.json\`). See https://blogs.msdn.microsoft.com/typescript/2018/01/31/announcing-typescript-2-7/#easier-ecmascript-module-interoperability for more information.",
106-
"start": undefined,
107-
},
108-
]
109-
`;

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -653,7 +653,7 @@ describe('resolvePath', () => {
653653
})
654654
}) // resolvePath
655655

656-
describe('readTsConfig', () => {
656+
describe('_resolveTsConfig', () => {
657657
let findConfig!: jest.SpyInstance<string | undefined>
658658
let readConfig!: jest.SpyInstance<{ config?: any; error?: ts.Diagnostic }>
659659
let parseConfig!: jest.SpyInstance<ts.ParsedCommandLine>

‎src/config/config-set.ts

+11-11
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ export class ConfigSet {
116116
readonly isolatedModules: boolean
117117
readonly cwd: string
118118
tsCacheDir: string | undefined
119-
parsedTsConfig!: ParsedCommandLine
119+
parsedTsConfig!: ParsedCommandLine | Record<string, any>
120120
customTransformers: CustomTransformers = Object.create(null)
121121
readonly rootDir: string
122122
/**
@@ -139,10 +139,7 @@ export class ConfigSet {
139139
* @internal
140140
*/
141141
private _stringifyContentRegExp: RegExp | undefined
142-
/**
143-
* @internal
144-
*/
145-
private _overriddenCompilerOptions: Partial<CompilerOptions> = {
142+
protected _overriddenCompilerOptions: Partial<CompilerOptions> = {
146143
// we handle sourcemaps this way and not another
147144
sourceMap: true,
148145
inlineSourceMap: false,
@@ -282,7 +279,10 @@ export class ConfigSet {
282279
}
283280
const tsconfigOpt = options.tsConfig ?? options.tsconfig
284281
const configFilePath = typeof tsconfigOpt === 'string' ? this.resolvePath(tsconfigOpt) : undefined
285-
this.parsedTsConfig = this._readTsConfig(typeof tsconfigOpt === 'object' ? tsconfigOpt : undefined, configFilePath)
282+
this.parsedTsConfig = this._resolveTsConfig(
283+
typeof tsconfigOpt === 'object' ? tsconfigOpt : undefined,
284+
configFilePath,
285+
)
286286
// throw errors if any matching wanted diagnostics
287287
this.raiseDiagnostics(this.parsedTsConfig.errors, configFilePath)
288288

@@ -389,10 +389,10 @@ export class ConfigSet {
389389
/**
390390
* Load TypeScript configuration. Returns the parsed TypeScript config and
391391
* any `tsConfig` options specified in ts-jest tsConfig
392-
*
393-
* @internal
394392
*/
395-
private _readTsConfig(compilerOptions?: CompilerOptions, resolvedConfigFile?: string): ParsedCommandLine {
393+
protected _resolveTsConfig(compilerOptions?: CompilerOptions, resolvedConfigFile?: string): Record<string, any>
394+
// eslint-disable-next-line no-dupe-class-members
395+
protected _resolveTsConfig(compilerOptions?: CompilerOptions, resolvedConfigFile?: string): ParsedCommandLine {
396396
let config = { compilerOptions: Object.create(null) }
397397
let basePath = normalizeSlashes(this.rootDir)
398398
const ts = this.compilerModule
@@ -402,6 +402,7 @@ export class ConfigSet {
402402
: ts.findConfigFile(normalizeSlashes(this.rootDir), ts.sys.fileExists)
403403
if (configFileName) {
404404
this.logger.debug({ tsConfigFileName: configFileName }, 'readTsConfig(): reading', configFileName)
405+
405406
const result = ts.readConfigFile(configFileName, ts.sys.readFile)
406407
// Return diagnostics.
407408
if (result.error) {
@@ -419,10 +420,8 @@ export class ConfigSet {
419420

420421
// parse json, merge config extending others, ...
421422
const result = ts.parseJsonConfigFileContent(config, ts.sys, basePath, undefined, configFileName)
422-
423423
const { _overriddenCompilerOptions: forcedOptions } = this
424424
const finalOptions = result.options
425-
426425
// Target ES5 output by default (instead of ES3).
427426
if (finalOptions.target === undefined) {
428427
finalOptions.target = ts.ScriptTarget.ES5
@@ -484,6 +483,7 @@ export class ConfigSet {
484483
nodeJsVer: process.version,
485484
compilationTarget: config.compilerOptions.target ?? TARGET_TO_VERSION_MAPPING[compilationTarget],
486485
})
486+
487487
this.logger.warn(message)
488488
}
489489

‎src/constants.ts

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
/**
2-
* @internal
3-
*/
41
export const LINE_FEED = '\n'
52
export const TS_TSX_REGEX = /\.tsx?$/
63
export const JS_JSX_REGEX = /\.jsx?$/
4+
export const DECLARATION_TYPE_EXT = '.d.ts'
75
/**
86
* @internal
97
* See https://jestjs.io/docs/en/configuration#testmatch-arraystring

‎src/ts-jest-transformer.ts

+17-23
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import type { Config } from '@jest/types'
44
import type { Logger } from 'bs-logger'
55

66
import { ConfigSet } from './config/config-set'
7-
import { JS_JSX_REGEX, TS_TSX_REGEX } from './constants'
7+
import { DECLARATION_TYPE_EXT, JS_JSX_REGEX, TS_TSX_REGEX } from './constants'
88
import { stringify } from './utils/json'
99
import { JsonableValue } from './utils/jsonable-value'
1010
import { rootLogger } from './utils/logger'
@@ -24,14 +24,8 @@ export class TsJestTransformer implements Transformer {
2424
*/
2525
private static readonly _cachedConfigSets: CachedConfigSet[] = []
2626
protected readonly logger: Logger
27-
/**
28-
* @internal
29-
*/
30-
private _transformCfgStr!: string
31-
/**
32-
* @internal
33-
*/
34-
private _tsJestCfgSet!: ConfigSet
27+
protected _transformCfgStr!: string
28+
protected _configSet!: ConfigSet
3529

3630
constructor() {
3731
this.logger = rootLogger.child({ namespace: 'ts-jest-transformer' })
@@ -49,10 +43,10 @@ export class TsJestTransformer implements Transformer {
4943

5044
let result: string | TransformedSource
5145
const source: string = input
52-
const { hooks } = this._tsJestCfgSet
53-
const shouldStringifyContent = this._tsJestCfgSet.shouldStringifyContent(filePath)
54-
const babelJest = shouldStringifyContent ? undefined : this._tsJestCfgSet.babelJestTransformer
55-
const isDefinitionFile = filePath.endsWith('.d.ts')
46+
const { hooks } = this._configSet
47+
const shouldStringifyContent = this._configSet.shouldStringifyContent(filePath)
48+
const babelJest = shouldStringifyContent ? undefined : this._configSet.babelJestTransformer
49+
const isDefinitionFile = filePath.endsWith(DECLARATION_TYPE_EXT)
5650
const isJsFile = JS_JSX_REGEX.test(filePath)
5751
const isTsFile = !isDefinitionFile && TS_TSX_REGEX.test(filePath)
5852
if (shouldStringifyContent) {
@@ -61,15 +55,15 @@ export class TsJestTransformer implements Transformer {
6155
} else if (isDefinitionFile) {
6256
// do not try to compile declaration files
6357
result = ''
64-
} else if (!this._tsJestCfgSet.parsedTsConfig.options.allowJs && isJsFile) {
58+
} else if (!this._configSet.parsedTsConfig.options.allowJs && isJsFile) {
6559
// we've got a '.js' but the compiler option `allowJs` is not set or set to false
6660
this.logger.warn({ fileName: filePath }, interpolate(Errors.GotJsFileButAllowJsFalse, { path: filePath }))
6761

6862
result = source
6963
} else if (isJsFile || isTsFile) {
7064
// transpile TS code (source maps are included)
7165
/* istanbul ignore if */
72-
result = this._tsJestCfgSet.tsCompiler.compile(source, filePath)
66+
result = this._configSet.tsCompiler.compile(source, filePath)
7367
} else {
7468
// we should not get called for files with other extension than js[x], ts[x] and d.ts,
7569
// TypeScript will bail if we try to compile, and if it was to call babel, users can
@@ -131,7 +125,7 @@ export class TsJestTransformer implements Transformer {
131125
)
132126
if (ccs) {
133127
this._transformCfgStr = ccs.transformerCfgStr
134-
this._tsJestCfgSet = ccs.configSet
128+
this._configSet = ccs.configSet
135129
} else {
136130
// try to look-it up by stringified version
137131
const serializedJestCfg = stringify(jestConfig)
@@ -144,24 +138,24 @@ export class TsJestTransformer implements Transformer {
144138
// the config, and then it calls the transformer with the proper object
145139
serializedCcs.jestConfig.value = jestConfig
146140
this._transformCfgStr = serializedCcs.transformerCfgStr
147-
this._tsJestCfgSet = serializedCcs.configSet
141+
this._configSet = serializedCcs.configSet
148142
} else {
149143
// create the new record in the index
150144
this.logger.info('no matching config-set found, creating a new one')
151145

152-
this._tsJestCfgSet = new ConfigSet(jestConfig)
146+
this._configSet = new ConfigSet(jestConfig)
153147
this._transformCfgStr = new JsonableValue({
154-
digest: this._tsJestCfgSet.tsJestDigest,
155-
babel: this._tsJestCfgSet.babelConfig,
148+
digest: this._configSet.tsJestDigest,
149+
babel: this._configSet.babelConfig,
156150
...jestConfig,
157151
tsconfig: {
158-
options: this._tsJestCfgSet.parsedTsConfig.options,
159-
raw: this._tsJestCfgSet.parsedTsConfig.raw,
152+
options: this._configSet.parsedTsConfig.options,
153+
raw: this._configSet.parsedTsConfig.raw,
160154
},
161155
}).serialized
162156
TsJestTransformer._cachedConfigSets.push({
163157
jestConfig: new JsonableValue(jestConfig),
164-
configSet: this._tsJestCfgSet,
158+
configSet: this._configSet,
165159
transformerCfgStr: this._transformCfgStr,
166160
})
167161
}

0 commit comments

Comments
 (0)
Please sign in to comment.