Skip to content

Commit 085bdf5

Browse files
authored
feat(config): show a warning message when TypeScript target version doesn't match with recommended NodeJs version (#1678)
1 parent 9c6f98e commit 085bdf5

11 files changed

Lines changed: 170 additions & 53 deletions

File tree

e2e/__tests__/__snapshots__/logger.test.ts.snap

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`TS_JEST_LOG should pass and create log file when using template "default" 1`] = `
3+
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "default" 1`] = `
44
Array [
55
"[level:20] creating jest presets not handling JavaScript files",
66
"[level:20] creating Importer singleton",
@@ -37,7 +37,7 @@ Array [
3737
]
3838
`;
3939
40-
exports[`TS_JEST_LOG should pass and create log file when using template "with-babel-7" 1`] = `
40+
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "with-babel-7" 1`] = `
4141
Array [
4242
"[level:20] creating jest presets not handling JavaScript files",
4343
"[level:20] creating Importer singleton",
@@ -80,7 +80,7 @@ Array [
8080
]
8181
`;
8282
83-
exports[`TS_JEST_LOG should pass and create log file when using template "with-babel-7-string-config" 1`] = `
83+
exports[`ts-jest logging TS_JEST_LOG should pass and create log file when using template "with-babel-7-string-config" 1`] = `
8484
Array [
8585
"[level:20] creating jest presets not handling JavaScript files",
8686
"[level:20] creating Importer singleton",
@@ -124,7 +124,7 @@ Array [
124124
]
125125
`;
126126
127-
exports[`With unsupported version test should pass using template "with-unsupported-version" 1`] = `
127+
exports[`ts-jest logging with unsupported version test should pass using template "with-unsupported-version" 1`] = `
128128
√ jest
129129
↳ exit code: 0
130130
===[ STDOUT ]===================================================================
@@ -142,3 +142,10 @@ exports[`With unsupported version test should pass using template "with-unsuppor
142142
Ran all test suites.
143143
================================================================================
144144
`;
145+
146+
exports[`ts-jest logging typescript target is higher than es2019 for NodeJs 12 should pass using template "default" 1`] = `
147+
Array [
148+
"[level:40] There is a mismatch between your NodeJs version v12.16.3 and your TypeScript target es2020. This might lead to some unexpected errors when running tests with \`ts-jest\`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping",
149+
"[level:40] message TS151001: 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.",
150+
]
151+
`;

e2e/__tests__/logger.test.ts

Lines changed: 57 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -4,35 +4,68 @@ import { existsSync } from 'fs'
44
import { PackageSets, allValidPackageSets } from '../__helpers__/templates'
55
import { configureTestCase } from '../__helpers__/test-case'
66

7-
describe('With unsupported version test', () => {
8-
const testCase = configureTestCase('simple')
7+
describe('ts-jest logging', () => {
8+
describe('with unsupported version test', () => {
9+
const testCase = configureTestCase('simple')
910

10-
testCase.runWithTemplates([PackageSets.unsupportedVersion], 0, (runTest, { testLabel }) => {
11-
it(testLabel, () => {
12-
const result = runTest()
13-
expect(result.status).toBe(0)
14-
expect(result).toMatchSnapshot()
11+
testCase.runWithTemplates([PackageSets.unsupportedVersion], 0, (runTest, { testLabel }) => {
12+
it(testLabel, () => {
13+
const result = runTest()
14+
expect(result.status).toBe(0)
15+
expect(result).toMatchSnapshot()
16+
})
1517
})
1618
})
17-
})
1819

19-
describe('TS_JEST_LOG', () => {
20-
const testCase = configureTestCase('simple', {
21-
env: { TS_JEST_LOG: 'ts-jest.log' },
22-
noCache: true,
23-
})
20+
describe('TS_JEST_LOG', () => {
21+
const testCase = configureTestCase('simple', {
22+
env: { TS_JEST_LOG: 'ts-jest.log' },
23+
noCache: true,
24+
})
2425

25-
testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { templateName }) => {
26-
it(`should pass and create log file when using template "${templateName}"`, () => {
27-
const result = runTest()
28-
expect(result.status).toBe(0)
29-
expect(existsSync(result.logFilePath)).toBe(true)
30-
const filteredEntries = result.logFileEntries
31-
// keep only debug and above
32-
.filter(m => (m.context[LogContexts.logLevel] || 0) >= LogLevels.debug)
33-
// simplify entires
34-
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
35-
expect(filteredEntries).toMatchSnapshot()
26+
testCase.runWithTemplates(allValidPackageSets, 0, (runTest, { templateName }) => {
27+
it(`should pass and create log file when using template "${templateName}"`, () => {
28+
const result = runTest()
29+
expect(result.status).toBe(0)
30+
expect(existsSync(result.logFilePath)).toBe(true)
31+
const filteredEntries = result.logFileEntries
32+
// keep only debug and above
33+
.filter(m => (m.context[LogContexts.logLevel] || 0) >= LogLevels.debug)
34+
// simplify entires
35+
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
36+
expect(filteredEntries).toMatchSnapshot()
37+
})
3638
})
3739
})
40+
41+
/**
42+
* Since we only run e2e for node 12 so we need this if here. We follow latest LTS Node version so once latest LTS version
43+
* changes, we also need to change this test.
44+
*/
45+
if (process.version.startsWith('v12')) {
46+
describe('typescript target is higher than es2019 for NodeJs 12', () => {
47+
const testCase = configureTestCase('simple', {
48+
env: { TS_JEST_LOG: 'ts-jest.log' },
49+
noCache: true,
50+
tsJestConfig: {
51+
tsConfig: {
52+
target: 'es2020'
53+
}
54+
}
55+
})
56+
57+
testCase.runWithTemplates([PackageSets.default], 0, (runTest, { testLabel }) => {
58+
it(testLabel, () => {
59+
const result = runTest()
60+
expect(result.status).toBe(0)
61+
const filteredEntries = result.logFileEntries
62+
// keep only debug and above
63+
.filter(m => (m.context[LogContexts.logLevel] || 0) === LogLevels.warn)
64+
// simplify entires
65+
.map(e => result.normalize(`[level:${e.context[LogContexts.logLevel]}] ${e.message}`))
66+
expect(filteredEntries).toMatchSnapshot()
67+
})
68+
})
69+
})
70+
}
3871
})

e2e/jest.config.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1+
const jestBaseConfig = require('../jest-base')
2+
3+
/** @type {import('@jest/types').Config.InitialOptions} */
14
module.exports = {
5+
...jestBaseConfig,
26
rootDir: '..',
3-
transform: {
4-
'\\.ts$': '<rootDir>/dist/index.js',
5-
},
67
testMatch: ['<rootDir>/e2e/__tests__/**/*.test.ts'],
7-
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
8-
testEnvironment: 'node',
98
snapshotSerializers: [
109
'<rootDir>/e2e/__serializers__/run-result.ts',
1110
'<rootDir>/e2e/__serializers__/processed-source.ts',

jest-base.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
/** @type {import('@jest/types').Config.InitialOptions} */
2+
module.exports = {
3+
globals: {
4+
'ts-jest': {
5+
tsConfig: 'tsconfig.spec.json',
6+
},
7+
},
8+
transform: {
9+
'\\.ts$': '<rootDir>/dist/index.js',
10+
},
11+
testEnvironment: 'node',
12+
}

jest.config.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1+
const baseConfig = require('./jest-base')
2+
3+
/** @type {import('@jest/types').Config.InitialOptions} */
14
module.exports = {
5+
...baseConfig,
26
rootDir: '.',
37
setupFilesAfterEnv: ['<rootDir>/src/__helpers__/setup.ts'],
4-
transform: {
5-
'\\.ts$': '<rootDir>/dist/index.js',
6-
},
78
testMatch: ['<rootDir>/src/**/*.spec.ts'],
89
testPathIgnorePatterns: ['<rootDir>/src/__mocks__/*'],
910
collectCoverageFrom: [
@@ -14,8 +15,6 @@ module.exports = {
1415
'!<rootDir>/src/**/__*__/*',
1516
'!<rootDir>/src/util/testing.ts',
1617
],
17-
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'],
18-
testEnvironment: 'node',
1918
snapshotSerializers: ['<rootDir>/src/__serializers__/processed-source.ts'],
2019
cacheDirectory: '<rootDir>/.cache/unit',
2120
}

src/config/config-set.spec.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable jest/no-mocks-import */
22
import { Transformer } from '@jest/transform'
33
import { Config } from '@jest/types'
4-
import { testing } from 'bs-logger'
4+
import { LogLevels, testing } from 'bs-logger'
55
import { readFileSync } from 'fs'
66
import json5 = require('json5')
77
import { resolve } from 'path'
@@ -792,6 +792,59 @@ describe('readTsConfig', () => {
792792
})
793793
})
794794
})
795+
796+
describe('mismatch nodejs version and typescript target', () => {
797+
const logTarget = logTargetMock()
798+
799+
beforeEach(() => {
800+
logTarget.clear()
801+
cs = createConfigSet({ jestConfig: { rootDir: '/root', cwd: '/cwd' } as any })
802+
findConfig.mockImplementation((p) => `${p}/tsconfig.json`)
803+
})
804+
805+
afterEach(() => {
806+
findConfig.mockClear()
807+
})
808+
809+
function mismatchTestCaseContent(tsTarget: string, scriptTarget: ts.ScriptTarget) {
810+
parseConfig.mockImplementation((conf: any) => ({
811+
options: {
812+
...conf,
813+
target: scriptTarget,
814+
},
815+
fileNames: [],
816+
errors: [],
817+
}))
818+
readConfig.mockImplementation((p) => ({ config: { path: p, compilerOptions: { target: tsTarget } } }))
819+
820+
cs.readTsConfig()
821+
822+
// expect.toEqual gives weird result here so toContain is workaround for it.
823+
expect(logTarget.filteredLines(LogLevels.warn, Infinity)[0]).toContain(
824+
'[level:40] There is a mismatch between your ' +
825+
`NodeJs version ${process.version} and your TypeScript target ${tsTarget}. This might lead to some unexpected errors ` +
826+
'when running tests with `ts-jest`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping',
827+
)
828+
829+
parseConfig.mockClear()
830+
readConfig.mockClear()
831+
}
832+
833+
/**
834+
* It seems like not possible to mock process.version so the condition here is needed
835+
*/
836+
if (process.version.startsWith('v10')) {
837+
// eslint-disable-next-line jest/expect-expect
838+
it('should show warning message when nodejs version is 10 and typescript target is higher than es2018', () => {
839+
mismatchTestCaseContent('es2019', ts.ScriptTarget.ES2019)
840+
})
841+
} else {
842+
// eslint-disable-next-line jest/expect-expect
843+
it('should show warning message when nodejs version is 12 and typescript target is higher than es2019', () => {
844+
mismatchTestCaseContent('es2020', ts.ScriptTarget.ES2020)
845+
})
846+
}
847+
})
795848
}) // readTsConfig
796849

797850
describe('versions', () => {

src/config/config-set.ts

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
DiagnosticCategory,
2121
FormatDiagnosticsHost,
2222
ParsedCommandLine,
23+
ScriptTarget,
2324
SourceFile,
2425
} from 'typescript'
2526

@@ -31,11 +32,11 @@ import {
3132
AstTransformerDesc,
3233
BabelConfig,
3334
BabelJestTransformer,
34-
TTypeScript,
3535
TsCompiler,
3636
TsJestConfig,
3737
TsJestGlobalOptions,
3838
TsJestHooksMap,
39+
TTypeScript,
3940
} from '../types'
4041
import { backportJestConfig } from '../util/backports'
4142
import { getPackageVersion } from '../util/get-package-version'
@@ -723,7 +724,7 @@ export class ConfigSet {
723724
resolvedConfigFile?: string | null,
724725
noProject?: boolean | null,
725726
): ParsedCommandLine {
726-
let config = { compilerOptions: {} }
727+
let config = { compilerOptions: Object.create(null) }
727728
let basePath = normalizeSlashes(this.rootDir)
728729
let configFileName: string | undefined
729730
const ts = this.compilerModule
@@ -800,6 +801,22 @@ export class ConfigSet {
800801
finalOptions[key] = val
801802
}
802803
}
804+
/**
805+
* See https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping
806+
* Every time this page is updated, we also need to update here. Here we only show warning message for Node LTS versions
807+
*/
808+
const nodeJsVer = process.version
809+
const compilationTarget = result.options.target!
810+
if (
811+
(nodeJsVer.startsWith('v10') && compilationTarget > ScriptTarget.ES2018) ||
812+
(nodeJsVer.startsWith('v12') && compilationTarget > ScriptTarget.ES2019)
813+
) {
814+
const message = interpolate(Errors.MismatchNodeTargetMapping, {
815+
nodeJsVer: process.version,
816+
compilationTarget: config.compilerOptions.target,
817+
})
818+
logger.warn(message)
819+
}
803820

804821
return result
805822
}

src/util/messages.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export const enum Errors {
1818
GotUnknownFileTypeWithBabel = 'Got a unknown file type to compile (file: {{path}}). To fix this, in your Jest config change the `transform` key which value is `ts-jest` so that it does not match this kind of files anymore. If you still want Babel to process it, add another entry to the `transform` option with value `babel-jest` which key matches this type of files.',
1919
ConfigNoModuleInterop = '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.',
2020
UnableToFindProjectRoot = 'Unable to find the root of the project where ts-jest has been installed.',
21+
MismatchNodeTargetMapping = 'There is a mismatch between your NodeJs version {{nodeJsVer}} and your TypeScript target {{compilationTarget}}. This might lead to some unexpected errors when running tests with `ts-jest`. To fix this, you can check https://github.com/microsoft/TypeScript/wiki/Node-Target-Mapping',
2122
}
2223

2324
/**

src/util/version-checkers.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const logger = rootLogger.child({ namespace: 'versions' })
99
/**
1010
* @internal
1111
*/
12-
export const enum ExpectedVersions {
12+
const enum ExpectedVersions {
1313
Jest = '>=26 <27',
1414
TypeScript = '>=3.8 <4',
1515
BabelJest = '>=26 <27',
@@ -49,6 +49,7 @@ function checkVersion(
4949
): boolean | never {
5050
const version = getPackageVersion(name)
5151
const success = !!version && satisfies(version, expectedRange)
52+
5253
logger.debug(
5354
{
5455
actualVersion: version,
@@ -58,6 +59,7 @@ function checkVersion(
5859
name,
5960
success ? 'OK' : 'NOT OK',
6061
)
62+
6163
if (!action || success) return success
6264

6365
const message = interpolate(version ? Errors.UntestedDependencyVersion : Errors.MissingDependency, {

tsconfig.json

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -30,15 +30,5 @@
3030
"node",
3131
"react"
3232
]
33-
},
34-
"include": [
35-
"e2e/__helpers__",
36-
"e2e/__serializers__",
37-
"e2e/__tests__",
38-
"scripts/",
39-
"src/",
40-
"utils/",
41-
"presets",
42-
"./*.js"
43-
]
33+
}
4434
}

0 commit comments

Comments
 (0)