Skip to content

Commit 2c840c2

Browse files
committed
feat(diagnostics): add option to enable/disable first-TS-error-throws
1 parent 66633d5 commit 2c840c2

13 files changed

Lines changed: 109 additions & 40 deletions

File tree

docs/user/config/diagnostics.md

Lines changed: 47 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,22 +9,23 @@ A diagnostic can be:
99
- syntax errors in some of your TypeScript files (source or tests)
1010
- type/semantic errors, what TypeScript has actually been made for 😁
1111

12-
If a diagnostic is not filtered out, it'll fail the compilatin within TSJest, and so will your related test.
12+
If a diagnostic is not filtered out, it'll fail the compilation within TSJest, and so will your related test.
1313

1414
### Disabling/enabling
1515

16-
By default all diagnostic are enabled. This is the same as setting the `diagnostics` option to `true`. To disable all diagnostics, set `diagnostics` to `false` (you might experience slightly better performence as well, especially if you disabled Jest cache).
16+
By default all diagnostic are enabled. This is the same as setting the `diagnostics` option to `true`. To disable all diagnostics, set `diagnostics` to `false` (you might experience slightly better performance as well, especially if you disabled Jest cache).
1717

1818
### Advanced configuration
1919

20-
The option's value can also accpet an object for more advanced configuration. Each config. key is optional:
20+
The option's value can also accept an object for more advanced configuration. Each config. key is optional:
2121

22-
- **`pretty`**: Enables/disable colorful and pretty output of errors (default: _enabled_).
22+
- **`warnOnly`**: If specified and `true`, diagnostics will be reported but won't stop compilation (default: _disabled_).
2323
- **`ignoreCodes`**: List of TypeScript error codes to ignore. Complete list can be found [there](https://github.com/Microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json). By default here are the ones ignored:
2424
- `6059`: _'rootDir' is expected to contain all source files._
25-
- `18002`: _The 'files' list in config file is empty._ (it is strongly recommanded to include this one)
25+
- `18002`: _The 'files' list in config file is empty._ (it is strongly recommended to include this one)
2626
- `18003`: _No inputs were found in config file._
2727
- **`pathRegex`**: If specified, diagnostics of source files which path does **not** match will be ignored.
28+
- **`pretty`**: Enables/disables colorful and pretty output of errors (default: _enabled_).
2829

2930
### Examples
3031

@@ -77,7 +78,7 @@ module.exports = {
7778
globals: {
7879
'ts-jest': {
7980
diagnostics: {
80-
pathRegex: /\.(spec|test).ts$/
81+
pathRegex: /\.(spec|test)\.ts$/
8182
}
8283
}
8384
}
@@ -104,6 +105,46 @@ module.exports = {
104105

105106
</div></div>
106107

108+
##### Do not fail on first error
109+
110+
While some diagnostics are stop-blockers for the compilation, most of them are not. If you want the compilation (and so your tests) to continue when encountering those, set the `warnOnly` to `true`:
111+
112+
<div class="row"><div class="col-md-6" markdown="block">
113+
114+
```js
115+
// jest.config.js
116+
module.exports = {
117+
// [...]
118+
globals: {
119+
'ts-jest': {
120+
diagnostics: {
121+
warnOnly: true
122+
}
123+
}
124+
}
125+
};
126+
```
127+
128+
</div><div class="col-md-6" markdown="block">
129+
130+
```js
131+
// OR package.json
132+
{
133+
// [...]
134+
"jest": {
135+
"globals": {
136+
"ts-jest": {
137+
"diagnostics": {
138+
"warnOnly": true
139+
}
140+
}
141+
}
142+
}
143+
}
144+
```
145+
146+
</div></div>
147+
107148
##### Ignoring some error codes:
108149

109150
All TypeScript error codes can be found [there](https://github.com/Microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json). The `ignoreCodes` option accepts this values:

e2e/__tests__/__snapshots__/type-checking.test.ts.snap

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ exports[`With type checking should fail using template "default" 1`] = `
99
FAIL ./main.spec.ts
1010
● Test suite failed to run
1111
12-
Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
12+
Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
1313
main.ts:2:3 - error TS2322: Type 'string' is not assignable to type 'number'.
1414
1515
2 return input
@@ -32,7 +32,7 @@ exports[`With type checking should fail using template "with-babel-6" 1`] = `
3232
FAIL ./main.spec.ts
3333
● Test suite failed to run
3434
35-
Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
35+
Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
3636
main.ts:2:3 - error TS2322: Type 'string' is not assignable to type 'number'.
3737
3838
2 return input
@@ -55,7 +55,7 @@ exports[`With type checking should fail using template "with-babel-7" 1`] = `
5555
FAIL ./main.spec.ts
5656
● Test suite failed to run
5757
58-
Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
58+
Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
5959
main.ts:2:3 - error TS2322: Type 'string' is not assignable to type 'number'.
6060
6161
2 return input
@@ -78,7 +78,7 @@ exports[`With type checking should fail using template "with-jest-22" 1`] = `
7878
FAIL ./main.spec.ts
7979
● Test suite failed to run
8080
81-
Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
81+
Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
8282
main.ts:2:3 - error TS2322: Type 'string' is not assignable to type 'number'.
8383
8484
2 return input
@@ -101,7 +101,7 @@ exports[`With type checking should fail using template "with-typescript-2-7" 1`]
101101
FAIL ./main.spec.ts
102102
● Test suite failed to run
103103
104-
Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
104+
Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
105105
main.ts:2:3 - error TS2322: Type 'string' is not assignable to type 'number'.
106106
107107
2 return input

src/__helpers__/fakers.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ export function tsJestConfig(options?: Partial<TsJestConfig>): TsJestConfig {
6161
babelConfig: undefined,
6262
tsConfig: undefined,
6363
stringifyContentPathRegex: undefined,
64-
diagnostics: { ignoreCodes: [], pretty: false },
64+
diagnostics: { ignoreCodes: [], pretty: false, throws: true },
6565
...options,
6666
}
6767
}

src/compiler.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const v: boolean = t
4747
'foo.ts',
4848
),
4949
).toThrowErrorMatchingInlineSnapshot(`
50-
"Unable to compile TypeScript (add code(s) in \`[jest-config].globals.ts-jest.diagnostics.ignoreCodes\` to ignore):
50+
"Unable to compile TypeScript (customize using \`[jest-config].globals.ts-jest.diagnostics\` option):
5151
foo.ts(3,7): error TS2322: Type 'number' is not assignable to type 'string'.
5252
foo.ts(4,7): error TS2322: Type 'string' is not assignable to type 'boolean'."
5353
`)

src/compiler.ts

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,7 @@ export function createCompiler(configs: ConfigSet): TsCompiler {
100100
reportDiagnostics: configs.shouldReportDiagnostic(fileName),
101101
})
102102

103-
const diagnosticList = result.diagnostics ? configs.filterDiagnostics(result.diagnostics) : []
104-
105-
if (diagnosticList.length) {
106-
throw configs.createTsError(diagnosticList)
107-
}
103+
if (result.diagnostics) configs.raiseDiagnostics(result.diagnostics, fileName, logger)
108104

109105
return [result.outputText, result.sourceMapText as string]
110106
}
@@ -190,11 +186,8 @@ export function createCompiler(configs: ConfigSet): TsCompiler {
190186
.concat(service.getSyntacticDiagnostics(fileName))
191187
.concat(service.getSemanticDiagnostics(fileName))
192188

193-
const diagnosticList = configs.filterDiagnostics(diagnostics)
194-
195-
if (diagnosticList.length) {
196-
throw configs.createTsError(diagnosticList)
197-
}
189+
// will raise or just warn diagnostics depending on config
190+
configs.raiseDiagnostics(diagnostics, fileName, logger)
198191
}
199192

200193
if (output.emitSkipped) {

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ Object {
2929
18003,
3030
],
3131
"pretty": true,
32+
"throws": true,
3233
},
3334
"isolatedModules": false,
3435
"stringifyContentPathRegex": undefined,

src/config/config-set.spec.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ describe('tsJest', () => {
129129
const EXPECTED = {
130130
ignoreCodes: IGNORE_DIAGNOSTIC_CODES,
131131
pretty: true,
132+
throws: true,
132133
}
133134
expect(get().diagnostics).toEqual(EXPECTED)
134135
expect(get({ diagnostics: true }).diagnostics).toEqual(EXPECTED)
@@ -139,6 +140,7 @@ describe('tsJest', () => {
139140
ignoreCodes: IGNORE_DIAGNOSTIC_CODES,
140141
pretty: true,
141142
pathRegex: MATCH_NOTHING.source,
143+
throws: false,
142144
}
143145
expect(get({ diagnostics: false }).diagnostics).toEqual(EXPECTED)
144146
})
@@ -148,6 +150,7 @@ describe('tsJest', () => {
148150
ignoreCodes: [...IGNORE_DIAGNOSTIC_CODES, 10, 25],
149151
pretty: false,
150152
pathRegex: '\\.test\\.ts',
153+
throws: true,
151154
}
152155
expect(
153156
get({
@@ -168,6 +171,14 @@ describe('tsJest', () => {
168171
}).diagnostics,
169172
).toEqual(EXPECTED)
170173
})
174+
it('should have correct throws value', () => {
175+
const EXPECTED = {
176+
ignoreCodes: IGNORE_DIAGNOSTIC_CODES,
177+
pretty: true,
178+
}
179+
expect(get({ diagnostics: { warnOnly: true } }).diagnostics).toEqual({ ...EXPECTED, throws: false })
180+
expect(get({ diagnostics: { warnOnly: false } }).diagnostics).toEqual({ ...EXPECTED, throws: true })
181+
})
171182
}) // diagnostics
172183

173184
describe('stringifyContentPathRegex', () => {

src/config/config-set.ts

Lines changed: 29 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
* version of the `jest.ProjectConfig`, and then later it calls `process()`
99
* with the complete, object version of it.
1010
*/
11-
import { Logger } from 'bs-logger'
11+
import { LogContexts, Logger } from 'bs-logger'
1212
import { existsSync, readFileSync } from 'fs'
1313
import json5 from 'json5'
1414
import { dirname, isAbsolute, join, resolve } from 'path'
@@ -123,7 +123,7 @@ export class ConfigSet {
123123
readonly parentOptions?: TsJestGlobalOptions,
124124
parentLogger?: Logger,
125125
) {
126-
this.logger = parentLogger ? parentLogger.child({ namespace: 'config' }) : logger
126+
this.logger = parentLogger ? parentLogger.child({ [LogContexts.namespace]: 'config' }) : logger
127127
}
128128

129129
@Memoize()
@@ -187,9 +187,10 @@ export class ConfigSet {
187187
const ignoreList: any[] = [IGNORE_DIAGNOSTIC_CODES, process.env.TS_JEST_IGNORE_DIAGNOSTICS]
188188

189189
if (diagnosticsOpt === true || diagnosticsOpt == null) {
190-
diagnostics = { ignoreCodes: [], pretty: true }
190+
diagnostics = { ignoreCodes: [], pretty: true, throws: true }
191191
} else if (diagnosticsOpt === false) {
192192
diagnostics = {
193+
throws: false,
193194
pretty: true,
194195
ignoreCodes: [],
195196
pathRegex: MATCH_NOTHING.source, // matches nothing
@@ -200,9 +201,10 @@ export class ConfigSet {
200201
pretty: diagnosticsOpt.pretty == null ? true : !!diagnosticsOpt.pretty,
201202
ignoreCodes: [],
202203
pathRegex: normalizeRegex(diagnosticsOpt.pathRegex),
204+
throws: !diagnosticsOpt.warnOnly,
203205
}
204206
}
205-
// now we clean and flaten the list
207+
// now we clean and flatten the list
206208
diagnostics.ignoreCodes = toDiagnosticCodeList(ignoreList)
207209

208210
// stringifyContentPathRegex option
@@ -250,21 +252,37 @@ export class ConfigSet {
250252
const {
251253
tsJest: { tsConfig },
252254
} = this
255+
const configFilePath = tsConfig && tsConfig.kind === 'file' ? tsConfig.value : undefined
253256
const result = this.readTsConfig(
254257
tsConfig && tsConfig.kind === 'inline' ? tsConfig.value : undefined,
255-
tsConfig && tsConfig.kind === 'file' ? tsConfig.value : undefined,
258+
configFilePath,
256259
tsConfig == null,
257260
)
258261
// throw errors if any matching wanted diagnostics
259-
const configDiagnosticList = this.filterDiagnostics(result.resolved.errors)
260-
if (configDiagnosticList.length) {
261-
throw this.createTsError(configDiagnosticList)
262-
}
262+
this.raiseDiagnostics(result.resolved.errors, configFilePath)
263263

264264
this.logger.debug({ tsconfig: result }, 'normalized typescript config')
265265
return result
266266
}
267267

268+
@Memoize()
269+
get raiseDiagnostics() {
270+
const {
271+
createTsError,
272+
filterDiagnostics,
273+
tsJest: {
274+
diagnostics: { throws },
275+
},
276+
} = this
277+
return (diagnostics: Diagnostic[], filePath?: string, logger: Logger = this.logger): void | never => {
278+
const filteredDiagnostics = filterDiagnostics(diagnostics, filePath)
279+
if (filteredDiagnostics.length === 0) return
280+
const error = createTsError(filteredDiagnostics)
281+
if (throws) throw error
282+
logger.warn({ error }, error.message)
283+
}
284+
}
285+
268286
@Memoize()
269287
get babel(): BabelConfig | undefined {
270288
const {
@@ -416,7 +434,7 @@ export class ConfigSet {
416434
logger.debug('file caching disabled')
417435
return
418436
}
419-
const cacheSufix = sha1(
437+
const cacheSuffix = sha1(
420438
stringify({
421439
version: this.compilerModule.version,
422440
compiler: this.tsJest.compiler,
@@ -425,7 +443,7 @@ export class ConfigSet {
425443
ignoreDiagnostics: this.tsJest.diagnostics.ignoreCodes,
426444
}),
427445
)
428-
const res = join(this.jest.cacheDirectory, `ts-jest-${cacheSufix}`)
446+
const res = join(this.jest.cacheDirectory, `ts-jest-${cacheSuffix}`)
429447
logger.debug({ cacheDirectory: res }, `will use file caching`)
430448
return res
431449
}

src/config/paths-to-module-name-mapper.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ Object {
6161
Array [
6262
"[level:40] Not mapping \\"no-target\\" because it has no target.
6363
",
64-
"[level:40] Mapping only to first target of \\"too-many-target\\" becuase it has more than one (2).
64+
"[level:40] Mapping only to first target of \\"too-many-target\\" because it has more than one (2).
6565
",
6666
"[level:40] Not mapping \\"too/*/many/*/stars\\" because it has more than one star (\`*\`).
6767
",

src/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ export interface TsJestGlobalOptions {
4848
pretty?: boolean
4949
ignoreCodes?: number | string | Array<number | string>
5050
pathRegex?: RegExp | string
51+
warnOnly?: boolean
5152
}
5253

5354
/**
@@ -81,6 +82,7 @@ interface TsJestConfig$diagnostics {
8182
pretty: boolean
8283
ignoreCodes: number[]
8384
pathRegex?: string | undefined
85+
throws: boolean
8486
}
8587
interface TsJestConfig$babelConfig$file {
8688
kind: 'file'

0 commit comments

Comments
 (0)