@eduardbar/drift 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish-vscode.yml +3 -3
- package/.github/workflows/publish.yml +3 -3
- package/.github/workflows/review-pr.yml +98 -6
- package/AGENTS.md +6 -0
- package/README.md +160 -10
- package/ROADMAP.md +6 -5
- package/dist/analyzer.d.ts +2 -2
- package/dist/analyzer.js +420 -159
- package/dist/benchmark.d.ts +2 -0
- package/dist/benchmark.js +185 -0
- package/dist/cli.js +453 -62
- package/dist/diff.js +74 -10
- package/dist/git.js +12 -0
- package/dist/index.d.ts +5 -3
- package/dist/index.js +3 -1
- package/dist/plugins.d.ts +2 -1
- package/dist/plugins.js +177 -28
- package/dist/printer.js +4 -0
- package/dist/review.js +2 -2
- package/dist/rules/comments.js +2 -2
- package/dist/rules/complexity.js +2 -7
- package/dist/rules/nesting.js +3 -13
- package/dist/rules/phase0-basic.js +10 -10
- package/dist/rules/shared.d.ts +2 -0
- package/dist/rules/shared.js +27 -3
- package/dist/saas.d.ts +143 -7
- package/dist/saas.js +478 -37
- package/dist/trust-kpi.d.ts +9 -0
- package/dist/trust-kpi.js +445 -0
- package/dist/trust.d.ts +65 -0
- package/dist/trust.js +571 -0
- package/dist/types.d.ts +154 -0
- package/docs/PRD.md +187 -109
- package/docs/plugin-contract.md +61 -0
- package/docs/trust-core-release-checklist.md +55 -0
- package/package.json +5 -3
- package/src/analyzer.ts +484 -155
- package/src/benchmark.ts +244 -0
- package/src/cli.ts +562 -79
- package/src/diff.ts +75 -10
- package/src/git.ts +16 -0
- package/src/index.ts +48 -0
- package/src/plugins.ts +354 -26
- package/src/printer.ts +4 -0
- package/src/review.ts +2 -2
- package/src/rules/comments.ts +2 -2
- package/src/rules/complexity.ts +2 -7
- package/src/rules/nesting.ts +3 -13
- package/src/rules/phase0-basic.ts +11 -12
- package/src/rules/shared.ts +31 -3
- package/src/saas.ts +641 -43
- package/src/trust-kpi.ts +518 -0
- package/src/trust.ts +774 -0
- package/src/types.ts +171 -0
- package/tests/diff.test.ts +124 -0
- package/tests/new-features.test.ts +71 -0
- package/tests/plugins.test.ts +219 -0
- package/tests/rules.test.ts +23 -1
- package/tests/saas-foundation.test.ts +358 -1
- package/tests/trust-kpi.test.ts +120 -0
- package/tests/trust.test.ts +584 -0
package/src/rules/complexity.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SourceFile, SyntaxKind } from 'ts-morph'
|
|
2
2
|
import type { DriftIssue } from '../types.js'
|
|
3
|
-
import { hasIgnoreComment, getSnippet, type FunctionLike } from './shared.js'
|
|
3
|
+
import { hasIgnoreComment, getSnippet, collectFunctionLikes, type FunctionLike } from './shared.js'
|
|
4
4
|
|
|
5
5
|
const COMPLEXITY_THRESHOLD = 10
|
|
6
6
|
|
|
@@ -31,12 +31,7 @@ function getCyclomaticComplexity(fn: FunctionLike): number {
|
|
|
31
31
|
|
|
32
32
|
export function detectHighComplexity(file: SourceFile): DriftIssue[] {
|
|
33
33
|
const issues: DriftIssue[] = []
|
|
34
|
-
const fns: FunctionLike[] =
|
|
35
|
-
...file.getFunctions(),
|
|
36
|
-
...file.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
37
|
-
...file.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
38
|
-
...file.getClasses().flatMap((c) => c.getMethods()),
|
|
39
|
-
]
|
|
34
|
+
const fns: FunctionLike[] = collectFunctionLikes(file)
|
|
40
35
|
|
|
41
36
|
for (const fn of fns) {
|
|
42
37
|
const complexity = getCyclomaticComplexity(fn)
|
package/src/rules/nesting.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SourceFile, SyntaxKind, Node } from 'ts-morph'
|
|
2
2
|
import type { DriftIssue } from '../types.js'
|
|
3
|
-
import { hasIgnoreComment, getSnippet, type FunctionLike } from './shared.js'
|
|
3
|
+
import { hasIgnoreComment, getSnippet, collectFunctionLikes, type FunctionLike } from './shared.js'
|
|
4
4
|
|
|
5
5
|
const NESTING_THRESHOLD = 3
|
|
6
6
|
const PARAMS_THRESHOLD = 4
|
|
@@ -35,12 +35,7 @@ function getMaxNestingDepth(fn: FunctionLike): number {
|
|
|
35
35
|
|
|
36
36
|
export function detectDeepNesting(file: SourceFile): DriftIssue[] {
|
|
37
37
|
const issues: DriftIssue[] = []
|
|
38
|
-
const fns: FunctionLike[] =
|
|
39
|
-
...file.getFunctions(),
|
|
40
|
-
...file.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
41
|
-
...file.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
42
|
-
...file.getClasses().flatMap((c) => c.getMethods()),
|
|
43
|
-
]
|
|
38
|
+
const fns: FunctionLike[] = collectFunctionLikes(file)
|
|
44
39
|
|
|
45
40
|
for (const fn of fns) {
|
|
46
41
|
const depth = getMaxNestingDepth(fn)
|
|
@@ -62,12 +57,7 @@ export function detectDeepNesting(file: SourceFile): DriftIssue[] {
|
|
|
62
57
|
|
|
63
58
|
export function detectTooManyParams(file: SourceFile): DriftIssue[] {
|
|
64
59
|
const issues: DriftIssue[] = []
|
|
65
|
-
const fns: FunctionLike[] =
|
|
66
|
-
...file.getFunctions(),
|
|
67
|
-
...file.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
68
|
-
...file.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
69
|
-
...file.getClasses().flatMap((c) => c.getMethods()),
|
|
70
|
-
]
|
|
60
|
+
const fns: FunctionLike[] = collectFunctionLikes(file)
|
|
71
61
|
|
|
72
62
|
for (const fn of fns) {
|
|
73
63
|
const paramCount = fn.getParameters().length
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { SourceFile, SyntaxKind } from 'ts-morph'
|
|
2
2
|
import type { DriftIssue } from '../types.js'
|
|
3
|
-
import { hasIgnoreComment, getSnippet, getFunctionLikeLines,
|
|
3
|
+
import { hasIgnoreComment, getSnippet, getFunctionLikeLines, collectFunctionLikes, getFileLines } from './shared.js'
|
|
4
4
|
|
|
5
5
|
const LARGE_FILE_THRESHOLD = 300
|
|
6
6
|
const LARGE_FUNCTION_THRESHOLD = 50
|
|
@@ -26,12 +26,7 @@ export function detectLargeFile(file: SourceFile): DriftIssue[] {
|
|
|
26
26
|
|
|
27
27
|
export function detectLargeFunctions(file: SourceFile): DriftIssue[] {
|
|
28
28
|
const issues: DriftIssue[] = []
|
|
29
|
-
const fns
|
|
30
|
-
...file.getFunctions(),
|
|
31
|
-
...file.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
32
|
-
...file.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
33
|
-
...file.getClasses().flatMap((c) => c.getMethods()),
|
|
34
|
-
]
|
|
29
|
+
const fns = collectFunctionLikes(file)
|
|
35
30
|
|
|
36
31
|
for (const fn of fns) {
|
|
37
32
|
const lines = getFunctionLikeLines(fn)
|
|
@@ -70,7 +65,7 @@ export function detectDebugLeftovers(file: SourceFile): DriftIssue[] {
|
|
|
70
65
|
}
|
|
71
66
|
}
|
|
72
67
|
|
|
73
|
-
const lines = file
|
|
68
|
+
const lines = getFileLines(file)
|
|
74
69
|
lines.forEach((lineContent, i) => {
|
|
75
70
|
if (/\/\/\s*(TODO|FIXME|HACK|XXX|TEMP)\b/i.test(lineContent)) {
|
|
76
71
|
if (hasIgnoreComment(file, i + 1)) return
|
|
@@ -90,14 +85,18 @@ export function detectDebugLeftovers(file: SourceFile): DriftIssue[] {
|
|
|
90
85
|
|
|
91
86
|
export function detectDeadCode(file: SourceFile): DriftIssue[] {
|
|
92
87
|
const issues: DriftIssue[] = []
|
|
88
|
+
const identifierCounts = new Map<string, number>()
|
|
89
|
+
|
|
90
|
+
for (const id of file.getDescendantsOfKind(SyntaxKind.Identifier)) {
|
|
91
|
+
const text = id.getText()
|
|
92
|
+
identifierCounts.set(text, (identifierCounts.get(text) ?? 0) + 1)
|
|
93
|
+
}
|
|
93
94
|
|
|
94
95
|
for (const imp of file.getImportDeclarations()) {
|
|
95
96
|
for (const named of imp.getNamedImports()) {
|
|
96
97
|
const name = named.getName()
|
|
97
|
-
const
|
|
98
|
-
|
|
99
|
-
)
|
|
100
|
-
if (refs.length === 0) {
|
|
98
|
+
const refsCount = Math.max(0, (identifierCounts.get(name) ?? 0) - 1)
|
|
99
|
+
if (refsCount === 0) {
|
|
101
100
|
issues.push({
|
|
102
101
|
rule: 'dead-code',
|
|
103
102
|
severity: 'warning',
|
package/src/rules/shared.ts
CHANGED
|
@@ -5,12 +5,40 @@ import {
|
|
|
5
5
|
ArrowFunction,
|
|
6
6
|
FunctionExpression,
|
|
7
7
|
MethodDeclaration,
|
|
8
|
+
SyntaxKind,
|
|
8
9
|
} from 'ts-morph'
|
|
9
10
|
|
|
10
11
|
export type FunctionLike = FunctionDeclaration | ArrowFunction | FunctionExpression | MethodDeclaration
|
|
11
12
|
|
|
12
|
-
|
|
13
|
+
const fileLinesCache = new WeakMap<SourceFile, string[]>()
|
|
14
|
+
const functionLikesCache = new WeakMap<SourceFile, FunctionLike[]>()
|
|
15
|
+
|
|
16
|
+
export function getFileLines(file: SourceFile): string[] {
|
|
17
|
+
const cached = fileLinesCache.get(file)
|
|
18
|
+
if (cached) return cached
|
|
19
|
+
|
|
13
20
|
const lines = file.getFullText().split('\n')
|
|
21
|
+
fileLinesCache.set(file, lines)
|
|
22
|
+
return lines
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function collectFunctionLikes(file: SourceFile): FunctionLike[] {
|
|
26
|
+
const cached = functionLikesCache.get(file)
|
|
27
|
+
if (cached) return cached
|
|
28
|
+
|
|
29
|
+
const fns: FunctionLike[] = [
|
|
30
|
+
...file.getFunctions(),
|
|
31
|
+
...file.getDescendantsOfKind(SyntaxKind.ArrowFunction),
|
|
32
|
+
...file.getDescendantsOfKind(SyntaxKind.FunctionExpression),
|
|
33
|
+
...file.getClasses().flatMap((c) => c.getMethods()),
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
functionLikesCache.set(file, fns)
|
|
37
|
+
return fns
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function hasIgnoreComment(file: SourceFile, line: number): boolean {
|
|
41
|
+
const lines = getFileLines(file)
|
|
14
42
|
const currentLine = lines[line - 1] ?? ''
|
|
15
43
|
const prevLine = lines[line - 2] ?? ''
|
|
16
44
|
|
|
@@ -20,13 +48,13 @@ export function hasIgnoreComment(file: SourceFile, line: number): boolean {
|
|
|
20
48
|
}
|
|
21
49
|
|
|
22
50
|
export function isFileIgnored(file: SourceFile): boolean {
|
|
23
|
-
const firstLines = file
|
|
51
|
+
const firstLines = getFileLines(file).slice(0, 10).join('\n') // drift-ignore
|
|
24
52
|
return /\/\/\s*drift-ignore-file\b/.test(firstLines)
|
|
25
53
|
}
|
|
26
54
|
|
|
27
55
|
export function getSnippet(node: Node, file: SourceFile): string {
|
|
28
56
|
const startLine = node.getStartLineNumber()
|
|
29
|
-
const lines = file
|
|
57
|
+
const lines = getFileLines(file)
|
|
30
58
|
return lines
|
|
31
59
|
.slice(Math.max(0, startLine - 1), startLine + 1)
|
|
32
60
|
.join('\n')
|