Skip to content

Commit

Permalink
feat: add file any info kind, add configuration changed
Browse files Browse the repository at this point in the history
  • Loading branch information
plantain-00 committed Sep 13, 2020
1 parent 27dec21 commit 2500db6
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 18 deletions.
17 changes: 16 additions & 1 deletion README.md
Expand Up @@ -138,7 +138,7 @@ const result = await lint('.', { strict: true })

```ts
export function lint(project: string, options?: Partial<LintOptions>): Promise<FileTypeCheckResult & { program: ts.Program }>
export function lintSync(compilerOptions: ts.CompilerOptions, rootNames: string[], options?: Partial<LintOptions>): Promise<FileTypeCheckResult & { program: ts.Program }> // Added in `v2.12`
export function lintSync(compilerOptions: ts.CompilerOptions, rootNames: string[], options?: Partial<LintOptions>): FileTypeCheckResult & { program: ts.Program } // Added in `v2.12`

export interface LintOptions {
debug: boolean,
Expand All @@ -163,6 +163,21 @@ export interface FileTypeCheckResult {
}[]
}

export interface FileAnyInfo {
line: number
character: number
text: string
kind: FileAnyInfoKind // Added in v2.13
}

export const enum FileAnyInfoKind {
any = 1, // any
containsAny = 2, // Promise<any>
unsafeAs = 3, // foo as string
unsafeTypeAssertion = 4, // <string>foo
unsafeNonNull = 5, // foo!
}

export type ProccessAny = (node: ts.Node, context: FileContext) => boolean
```
Expand Down
21 changes: 11 additions & 10 deletions packages/core/src/checker.ts
@@ -1,8 +1,8 @@
import ts from 'typescript'

import { FileContext } from './interfaces'
import { FileAnyInfoKind, FileContext } from './interfaces'

function collectAny(node: ts.Node, context: FileContext) {
function collectAny(node: ts.Node, context: FileContext, kind: FileAnyInfoKind) {
const { file, sourceFile, typeCheckResult, ingoreMap, debug, processAny } = context
if (processAny !== undefined) {
return processAny(node, context)
Expand All @@ -12,9 +12,9 @@ function collectAny(node: ts.Node, context: FileContext) {
return false
}
if (debug) {
console.log(`type === any: ${file}:${line + 1}:${character + 1}: ${node.getText(sourceFile)}`)
console.log(`type === any(${kind}): ${file}:${line + 1}:${character + 1}: ${node.getText(sourceFile)}`)
} else {
typeCheckResult.anys.push({ line, character, text: node.getText(sourceFile) })
typeCheckResult.anys.push({ line, character, text: node.getText(sourceFile), kind })
}
return true
}
Expand All @@ -41,7 +41,8 @@ function collectData(node: ts.Node, context: FileContext) {
if (types.length > 0) {
context.typeCheckResult.totalCount++
if (types.every((t) => typeIsStrictAny(t, context.strict))) {
const success = collectAny(node, context)
const kind = types.every((t) => typeIsStrictAny(t, false)) ? FileAnyInfoKind.any : FileAnyInfoKind.containsAny
const success = collectAny(node, context, kind)
if (!success) {
collectNotAny(node, context, type)
}
Expand Down Expand Up @@ -74,7 +75,7 @@ function checkNodes(nodes: ts.NodeArray<ts.Node> | undefined, context: FileConte
}
}

function checkTypeAssertion(node: ts.Node, context: FileContext) {
function checkTypeAssertion(node: ts.Node, context: FileContext, kind: FileAnyInfoKind) {
if (context.strict) {
if ((ts.isAsExpression(node) || ts.isTypeAssertion(node))) {
// exclude `foo as const` and `<const>foo`
Expand All @@ -94,7 +95,7 @@ function checkTypeAssertion(node: ts.Node, context: FileContext) {
return
}
}
const success = collectAny(node, context)
const success = collectAny(node, context, kind)
if (success) {
context.typeCheckResult.totalCount++
}
Expand Down Expand Up @@ -313,7 +314,7 @@ export function checkNode(node: ts.Node | undefined, context: FileContext): void
return
}
if (ts.isTypeAssertion(node)) {
checkTypeAssertion(node, context)
checkTypeAssertion(node, context, FileAnyInfoKind.unsafeTypeAssertion)
checkNode(node.expression, context)
checkNode(node.type, context)
return
Expand Down Expand Up @@ -374,13 +375,13 @@ export function checkNode(node: ts.Node | undefined, context: FileContext): void
return
}
if (ts.isAsExpression(node)) {
checkTypeAssertion(node, context)
checkTypeAssertion(node, context, FileAnyInfoKind.unsafeAs)
checkNode(node.expression, context)
checkNode(node.type, context)
return
}
if (ts.isNonNullExpression(node)) {
checkTypeAssertion(node, context)
checkTypeAssertion(node, context, FileAnyInfoKind.unsafeNonNull)
checkNode(node.expression, context)
return
}
Expand Down
14 changes: 13 additions & 1 deletion packages/core/src/interfaces.ts
Expand Up @@ -13,10 +13,22 @@ export interface AnyInfo extends FileAnyInfo {
file: string
}

interface FileAnyInfo {
/**
* @public
*/
export interface FileAnyInfo {
line: number
character: number
text: string
kind: FileAnyInfoKind
}

export const enum FileAnyInfoKind {
any = 1, // any
containsAny = 2, // Promise<any>
unsafeAs = 3, // foo as string
unsafeTypeAssertion = 4, // <string>foo
unsafeNonNull = 5, // foo!
}

/**
Expand Down
29 changes: 23 additions & 6 deletions packages/plugin/src/index.ts
@@ -1,11 +1,11 @@
import * as tsserverlibrary from 'typescript/lib/tsserverlibrary'
import { lintSync, LintOptions } from 'type-coverage-core'
import { lintSync, LintOptions, FileAnyInfoKind } from 'type-coverage-core'

function init(modules: { typescript: typeof tsserverlibrary }) {
let oldProgram: ts.Program | undefined
let lintOptions: LintOptions | undefined

function create(info: ts.server.PluginCreateInfo) {
const lintOptions = info.config as LintOptions
const proxy: tsserverlibrary.LanguageService = {
...info.languageService,
getSemanticDiagnostics(fileName) {
Expand All @@ -14,21 +14,33 @@ function init(modules: { typescript: typeof tsserverlibrary }) {
info.project.getCompilerOptions(),
info.project.getRootFiles(),
{
...lintOptions,
...(lintOptions || info.config as LintOptions),
files: [fileName],
oldProgram,
},
)
oldProgram = result.program
for (const anyObject of result.anys) {
let messageText: string
if (anyObject.kind === FileAnyInfoKind.containsAny) {
messageText = `The type of '${anyObject.text}' contains 'any'`
} else if (anyObject.kind === FileAnyInfoKind.unsafeAs) {
messageText = `The '${anyObject.text}' has unsafe 'as' type assertion`
} else if (anyObject.kind === FileAnyInfoKind.unsafeNonNull) {
messageText = `The '${anyObject.text}' has unsafe '!' type assertion`
} else if (anyObject.kind === FileAnyInfoKind.unsafeTypeAssertion) {
messageText = `The '${anyObject.text}' has unsafe '<>' type assertion`
} else {
messageText = `The type of '${anyObject.text}' is 'any'`
}
prior.push({
category: modules.typescript.DiagnosticCategory.Warning,
code: 1,
code: anyObject.kind,
source: 'ts-plugin-type-coverage',
file: anyObject.sourceFile,
start: modules.typescript.getPositionOfLineAndCharacter(anyObject.sourceFile, anyObject.line, anyObject.character),
length: anyObject.text.length,
messageText: `The type of '${anyObject.text}' is 'any'`,
messageText,
})
}
return prior
Expand All @@ -37,7 +49,12 @@ function init(modules: { typescript: typeof tsserverlibrary }) {
return proxy
}

return { create };
return {
create,
onConfigurationChanged(config: LintOptions) {
lintOptions = config
}
};
}

export = init;

0 comments on commit 2500db6

Please sign in to comment.