diff --git a/README.md b/README.md index a838200..70b4bb6 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,12 @@ comment. To make these files strict too, just remove its' ignore comments. ## Configuration -Plugin takes extra, non-mandatory arguments `paths`, `exlude` and `excludePattern`. Args `paths` and +Plugin takes extra, non-mandatory arguments `paths`, `exclude`, `excludePattern` and `convertStrictErrorsToWarnings`. Args `paths` and `exclude` accept an array of relative or absolute paths that should be included (property `paths`) or excluded (property `exclude`). Arg `excludePattern` accepts an array of strings that will be matched with [minimatch](https://github.com/isaacs/minimatch). To add strict mode to files from -ignored paths you can insert `//@ts-strict` comment. +ignored paths you can insert `//@ts-strict` comment. Arg `convertStrictErrorsToWarnings` show errors from strict checking as warnings instead of errors. This allows for showing strict checking as a warning in codebases with a large amount +of strict errors. ```json { @@ -145,6 +146,7 @@ command available in the image +To only show strict erros as warnings in VSCode, set `convertStrictErrorsToWarnings` to true. ## Testing the plugin ### Manually diff --git a/src/plugin/index.ts b/src/plugin/index.ts index 878bdb2..6684f78 100644 --- a/src/plugin/index.ts +++ b/src/plugin/index.ts @@ -7,6 +7,39 @@ import { } from './utils'; import * as ts from 'typescript/lib/tsserverlibrary'; +const isDiagnosticChainEquals = ( + a: ts.Diagnostic['messageText'] | undefined, + b: ts.Diagnostic['messageText'] | undefined, +): boolean => { + if (a === undefined || b === undefined) { + return a === b; + } else if (typeof a === 'string' && typeof b === 'string') { + return a === b; + } else if (typeof a === 'string' || typeof b === 'string') { + return false; + } else { + const allChainsEqual: boolean = + a.next?.every((n, i) => isDiagnosticChainEquals(n, b.next?.[i])) ?? a === b; + return ( + a.category === b.category && + a.code === b.code && + a.messageText === b.messageText && + allChainsEqual + ); + } +}; + +const isDiagnosticEquals = (a: ts.Diagnostic, b: ts.Diagnostic) => { + return ( + a.category == b.category && + a.code === b.code && + a.source === b.source && + a.length === b.length && + a.messageText === b.messageText && + isDiagnosticChainEquals(a.messageText, b.messageText) + ); +}; + const init: ts.server.PluginModuleFactory = () => { function create(info: PluginInfo) { const proxy = setupLanguageServiceProxy(info); @@ -19,7 +52,25 @@ const init: ts.server.PluginModuleFactory = () => { proxy.getSemanticDiagnostics = function (filePath) { const strictFile = new PluginStrictFileChecker(info).isFileStrict(filePath); - if (strictFile) { + const convertStrictErrorsToWarnings = !!info?.config?.convertStrictErrorsToWarnings; + + if (strictFile && convertStrictErrorsToWarnings) { + //To find which errors are strict errors, we check if we find the the same error when we are running nonstrict. + //If we do not, we can be sure that error only occurs when we run with strict + const strictDiagnostics = strictLanguageService.getSemanticDiagnostics(filePath); + const nonStrictDiagnostics = info.languageService.getSemanticDiagnostics(filePath); + + return strictDiagnostics.map((strict) => { + const isInNonStrict = nonStrictDiagnostics.some((nonStrict) => + isDiagnosticEquals(nonStrict, strict), + ); + if (isInNonStrict || strict.category !== ts.DiagnosticCategory.Error) { + return strict; + } else { + return { ...strict, category: ts.DiagnosticCategory.Warning }; + } + }); + } else if (strictFile) { return strictLanguageService.getSemanticDiagnostics(filePath); } else { return info.languageService.getSemanticDiagnostics(filePath);