@eduardbar/drift 0.9.1 → 1.0.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 +76 -0
- package/AGENTS.md +30 -12
- package/README.md +1 -1
- package/ROADMAP.md +130 -98
- package/dist/analyzer.d.ts +4 -38
- package/dist/analyzer.js +85 -1543
- package/dist/cli.js +47 -4
- package/dist/config.js +1 -1
- package/dist/fix.d.ts +13 -0
- package/dist/fix.js +120 -0
- package/dist/git/blame.d.ts +22 -0
- package/dist/git/blame.js +227 -0
- package/dist/git/helpers.d.ts +36 -0
- package/dist/git/helpers.js +152 -0
- package/dist/git/trend.d.ts +21 -0
- package/dist/git/trend.js +80 -0
- package/dist/git.d.ts +0 -4
- package/dist/git.js +2 -2
- package/dist/report.js +620 -293
- package/dist/rules/phase0-basic.d.ts +11 -0
- package/dist/rules/phase0-basic.js +176 -0
- package/dist/rules/phase1-complexity.d.ts +31 -0
- package/dist/rules/phase1-complexity.js +277 -0
- package/dist/rules/phase2-crossfile.d.ts +27 -0
- package/dist/rules/phase2-crossfile.js +122 -0
- package/dist/rules/phase3-arch.d.ts +31 -0
- package/dist/rules/phase3-arch.js +148 -0
- package/dist/rules/phase5-ai.d.ts +8 -0
- package/dist/rules/phase5-ai.js +262 -0
- package/dist/rules/phase8-semantic.d.ts +22 -0
- package/dist/rules/phase8-semantic.js +109 -0
- package/dist/rules/shared.d.ts +7 -0
- package/dist/rules/shared.js +27 -0
- package/package.json +8 -3
- package/packages/vscode-drift/.vscodeignore +9 -0
- package/packages/vscode-drift/LICENSE +21 -0
- package/packages/vscode-drift/README.md +64 -0
- package/packages/vscode-drift/images/icon.png +0 -0
- package/packages/vscode-drift/images/icon.svg +30 -0
- package/packages/vscode-drift/package-lock.json +485 -0
- package/packages/vscode-drift/package.json +119 -0
- package/packages/vscode-drift/src/analyzer.ts +38 -0
- package/packages/vscode-drift/src/diagnostics.ts +55 -0
- package/packages/vscode-drift/src/extension.ts +111 -0
- package/packages/vscode-drift/src/statusbar.ts +47 -0
- package/packages/vscode-drift/src/treeview.ts +108 -0
- package/packages/vscode-drift/tsconfig.json +18 -0
- package/packages/vscode-drift/vscode-drift-0.1.0.vsix +0 -0
- package/packages/vscode-drift/vscode-drift-0.1.1.vsix +0 -0
- package/src/analyzer.ts +124 -1773
- package/src/cli.ts +53 -4
- package/src/config.ts +1 -1
- package/src/fix.ts +154 -0
- package/src/git/blame.ts +279 -0
- package/src/git/helpers.ts +198 -0
- package/src/git/trend.ts +116 -0
- package/src/git.ts +2 -2
- package/src/report.ts +631 -296
- package/src/rules/phase0-basic.ts +187 -0
- package/src/rules/phase1-complexity.ts +302 -0
- package/src/rules/phase2-crossfile.ts +149 -0
- package/src/rules/phase3-arch.ts +179 -0
- package/src/rules/phase5-ai.ts +292 -0
- package/src/rules/phase8-semantic.ts +132 -0
- package/src/rules/shared.ts +39 -0
- package/tests/helpers.ts +45 -0
- package/tests/rules.test.ts +1269 -0
- package/vitest.config.ts +15 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "vscode-drift",
|
|
3
|
+
"displayName": "drift — Technical Debt Detector",
|
|
4
|
+
"description": "Detect technical debt in TypeScript & JavaScript. AST-based score 0–100: complexity, dead code, coupling, nesting and more.",
|
|
5
|
+
"version": "0.1.1",
|
|
6
|
+
"engines": {
|
|
7
|
+
"vscode": "^1.85.0"
|
|
8
|
+
},
|
|
9
|
+
"publisher": "eduardbar",
|
|
10
|
+
"license": "MIT",
|
|
11
|
+
"icon": "images/icon.png",
|
|
12
|
+
"repository": {
|
|
13
|
+
"type": "git",
|
|
14
|
+
"url": "https://github.com/eduardbar/drift"
|
|
15
|
+
},
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/eduardbar/drift/issues"
|
|
18
|
+
},
|
|
19
|
+
"homepage": "https://github.com/eduardbar/drift#readme",
|
|
20
|
+
"qna": "https://github.com/eduardbar/drift/issues",
|
|
21
|
+
"categories": ["Linters", "Programming Languages"],
|
|
22
|
+
"keywords": [
|
|
23
|
+
"technical debt",
|
|
24
|
+
"debt",
|
|
25
|
+
"code quality",
|
|
26
|
+
"linter",
|
|
27
|
+
"typescript",
|
|
28
|
+
"javascript",
|
|
29
|
+
"complexity",
|
|
30
|
+
"dead code",
|
|
31
|
+
"coupling",
|
|
32
|
+
"nesting",
|
|
33
|
+
"score",
|
|
34
|
+
"refactor",
|
|
35
|
+
"analyzer",
|
|
36
|
+
"analysis",
|
|
37
|
+
"anti-patterns",
|
|
38
|
+
"ast",
|
|
39
|
+
"cyclomatic",
|
|
40
|
+
"any",
|
|
41
|
+
"console.log",
|
|
42
|
+
"catch",
|
|
43
|
+
"multi-root ready"
|
|
44
|
+
],
|
|
45
|
+
"galleryBanner": {
|
|
46
|
+
"color": "#0a0a0f",
|
|
47
|
+
"theme": "dark"
|
|
48
|
+
},
|
|
49
|
+
"badges": [
|
|
50
|
+
{
|
|
51
|
+
"url": "https://img.shields.io/visual-studio-marketplace/v/eduardbar.vscode-drift?color=6366f1&label=version",
|
|
52
|
+
"href": "https://marketplace.visualstudio.com/items?itemName=eduardbar.vscode-drift",
|
|
53
|
+
"description": "Marketplace Version"
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
"url": "https://img.shields.io/visual-studio-marketplace/i/eduardbar.vscode-drift?color=8b5cf6&label=installs",
|
|
57
|
+
"href": "https://marketplace.visualstudio.com/items?itemName=eduardbar.vscode-drift",
|
|
58
|
+
"description": "Installs"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"activationEvents": [
|
|
62
|
+
"onLanguage:typescript",
|
|
63
|
+
"onLanguage:typescriptreact",
|
|
64
|
+
"onLanguage:javascript",
|
|
65
|
+
"onLanguage:javascriptreact"
|
|
66
|
+
],
|
|
67
|
+
"main": "./dist/extension.js",
|
|
68
|
+
"contributes": {
|
|
69
|
+
"views": {
|
|
70
|
+
"explorer": [
|
|
71
|
+
{
|
|
72
|
+
"id": "driftIssues",
|
|
73
|
+
"name": "Drift Issues",
|
|
74
|
+
"when": "workspaceFolderCount > 0"
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"commands": [
|
|
79
|
+
{
|
|
80
|
+
"command": "drift.scanWorkspace",
|
|
81
|
+
"title": "Drift: Scan Workspace"
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
"command": "drift.clearDiagnostics",
|
|
85
|
+
"title": "Drift: Clear Diagnostics"
|
|
86
|
+
}
|
|
87
|
+
],
|
|
88
|
+
"configuration": {
|
|
89
|
+
"title": "Drift",
|
|
90
|
+
"properties": {
|
|
91
|
+
"drift.enable": {
|
|
92
|
+
"type": "boolean",
|
|
93
|
+
"default": true,
|
|
94
|
+
"description": "Enable drift analysis on save"
|
|
95
|
+
},
|
|
96
|
+
"drift.minSeverity": {
|
|
97
|
+
"type": "string",
|
|
98
|
+
"enum": ["error", "warning", "info"],
|
|
99
|
+
"default": "info",
|
|
100
|
+
"description": "Minimum severity level to show"
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"scripts": {
|
|
106
|
+
"build": "tsc -p tsconfig.json",
|
|
107
|
+
"watch": "tsc -p tsconfig.json --watch",
|
|
108
|
+
"vscode:prepublish": "npm run build"
|
|
109
|
+
},
|
|
110
|
+
"devDependencies": {
|
|
111
|
+
"@types/vscode": "^1.85.0",
|
|
112
|
+
"@types/node": "^20.0.0",
|
|
113
|
+
"typescript": "^5.3.0"
|
|
114
|
+
},
|
|
115
|
+
"dependencies": {
|
|
116
|
+
"@eduardbar/drift": "*",
|
|
117
|
+
"ts-morph": "^23.0.0"
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Project } from 'ts-morph'
|
|
2
|
+
import type { FileReport } from '@eduardbar/drift'
|
|
3
|
+
|
|
4
|
+
// Import dinámico para compatibilidad CommonJS -> ESM
|
|
5
|
+
// @eduardbar/drift es "type": "module" pero desde CommonJS
|
|
6
|
+
// se debe usar import() dinámico.
|
|
7
|
+
// _analyzeFile se tipea como `Function` para evitar el conflicto de
|
|
8
|
+
// instancias duplicadas de ts-morph (una en este paquete, otra en
|
|
9
|
+
// @eduardbar/drift/node_modules). En runtime son compatibles.
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/ban-types
|
|
11
|
+
let _analyzeFile: Function | null = null
|
|
12
|
+
|
|
13
|
+
async function getAnalyzeFile(): Promise<Function> {
|
|
14
|
+
if (!_analyzeFile) {
|
|
15
|
+
// drift es ES module, desde CommonJS usamos import() dinámico
|
|
16
|
+
const drift = await import('@eduardbar/drift')
|
|
17
|
+
_analyzeFile = drift.analyzeFile
|
|
18
|
+
}
|
|
19
|
+
return _analyzeFile!
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export async function analyzeFilePath(filePath: string): Promise<FileReport | null> {
|
|
23
|
+
try {
|
|
24
|
+
const analyzeFile = await getAnalyzeFile()
|
|
25
|
+
const project = new Project({
|
|
26
|
+
compilerOptions: {
|
|
27
|
+
allowJs: true,
|
|
28
|
+
jsx: 1, // JsxEmit.Preserve
|
|
29
|
+
},
|
|
30
|
+
skipAddingFilesFromTsConfig: true,
|
|
31
|
+
})
|
|
32
|
+
const sourceFile = project.addSourceFileAtPath(filePath)
|
|
33
|
+
return analyzeFile(sourceFile) as FileReport
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error('[drift] analyzeFilePath error:', err)
|
|
36
|
+
return null
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import * as vscode from 'vscode'
|
|
2
|
+
import type { FileReport } from '@eduardbar/drift'
|
|
3
|
+
|
|
4
|
+
const SEVERITY_MAP: Record<string, vscode.DiagnosticSeverity> = {
|
|
5
|
+
error: vscode.DiagnosticSeverity.Error,
|
|
6
|
+
warning: vscode.DiagnosticSeverity.Warning,
|
|
7
|
+
info: vscode.DiagnosticSeverity.Information,
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export class DriftDiagnosticsProvider {
|
|
11
|
+
private collection: vscode.DiagnosticCollection
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.collection = vscode.languages.createDiagnosticCollection('drift')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
update(report: FileReport): void {
|
|
18
|
+
const uri = vscode.Uri.file(report.path)
|
|
19
|
+
const config = vscode.workspace.getConfiguration('drift')
|
|
20
|
+
const minSeverity = config.get<string>('minSeverity', 'info')
|
|
21
|
+
|
|
22
|
+
const severityOrder = ['error', 'warning', 'info']
|
|
23
|
+
const minIdx = severityOrder.indexOf(minSeverity)
|
|
24
|
+
|
|
25
|
+
const diagnostics: vscode.Diagnostic[] = report.issues
|
|
26
|
+
.filter(issue => severityOrder.indexOf(issue.severity) <= minIdx)
|
|
27
|
+
.map(issue => {
|
|
28
|
+
// line es 1-based en drift, VS Code usa 0-based
|
|
29
|
+
const line = Math.max(0, issue.line - 1)
|
|
30
|
+
const range = new vscode.Range(line, 0, line, Number.MAX_SAFE_INTEGER)
|
|
31
|
+
const diagnostic = new vscode.Diagnostic(
|
|
32
|
+
range,
|
|
33
|
+
`[drift/${issue.rule}] ${issue.message}`,
|
|
34
|
+
SEVERITY_MAP[issue.severity] ?? vscode.DiagnosticSeverity.Information
|
|
35
|
+
)
|
|
36
|
+
diagnostic.source = 'drift'
|
|
37
|
+
diagnostic.code = issue.rule
|
|
38
|
+
return diagnostic
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
this.collection.set(uri, diagnostics)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
clear(uri?: vscode.Uri): void {
|
|
45
|
+
if (uri) {
|
|
46
|
+
this.collection.delete(uri)
|
|
47
|
+
} else {
|
|
48
|
+
this.collection.clear()
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
dispose(): void {
|
|
53
|
+
this.collection.dispose()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as vscode from 'vscode'
|
|
2
|
+
import { analyzeFilePath } from './analyzer'
|
|
3
|
+
import { DriftDiagnosticsProvider } from './diagnostics'
|
|
4
|
+
import { DriftTreeProvider } from './treeview'
|
|
5
|
+
import { DriftStatusBarItem } from './statusbar'
|
|
6
|
+
import type { FileReport } from '@eduardbar/drift'
|
|
7
|
+
|
|
8
|
+
const SUPPORTED_LANGUAGES = ['typescript', 'typescriptreact', 'javascript', 'javascriptreact']
|
|
9
|
+
|
|
10
|
+
export function activate(context: vscode.ExtensionContext): void {
|
|
11
|
+
const diagnostics = new DriftDiagnosticsProvider()
|
|
12
|
+
const treeProvider = new DriftTreeProvider()
|
|
13
|
+
const statusBar = new DriftStatusBarItem()
|
|
14
|
+
|
|
15
|
+
// Registrar TreeView
|
|
16
|
+
const treeView = vscode.window.createTreeView('driftIssues', {
|
|
17
|
+
treeDataProvider: treeProvider,
|
|
18
|
+
showCollapseAll: true,
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
// Cache de reports para la status bar
|
|
22
|
+
const reportCache = new Map<string, FileReport>()
|
|
23
|
+
|
|
24
|
+
async function analyzeAndUpdate(document: vscode.TextDocument): Promise<void> {
|
|
25
|
+
const config = vscode.workspace.getConfiguration('drift')
|
|
26
|
+
if (!config.get<boolean>('enable', true)) return
|
|
27
|
+
|
|
28
|
+
if (!SUPPORTED_LANGUAGES.includes(document.languageId)) return
|
|
29
|
+
if (document.uri.scheme !== 'file') return
|
|
30
|
+
|
|
31
|
+
const filePath = document.uri.fsPath
|
|
32
|
+
|
|
33
|
+
const report = await analyzeFilePath(filePath)
|
|
34
|
+
if (!report) return
|
|
35
|
+
|
|
36
|
+
diagnostics.update(report)
|
|
37
|
+
treeProvider.updateFile(report)
|
|
38
|
+
reportCache.set(filePath, report)
|
|
39
|
+
statusBar.update(Array.from(reportCache.values()))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Trigger: al guardar
|
|
43
|
+
const onSave = vscode.workspace.onDidSaveTextDocument(analyzeAndUpdate)
|
|
44
|
+
|
|
45
|
+
// Comando: scan workspace
|
|
46
|
+
const scanCmd = vscode.commands.registerCommand('drift.scanWorkspace', async () => {
|
|
47
|
+
const files = await vscode.workspace.findFiles(
|
|
48
|
+
'**/*.{ts,tsx,js,jsx}',
|
|
49
|
+
'**/node_modules/**'
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
vscode.window.withProgress(
|
|
53
|
+
{
|
|
54
|
+
location: vscode.ProgressLocation.Notification,
|
|
55
|
+
title: 'drift: Scanning workspace...',
|
|
56
|
+
cancellable: false,
|
|
57
|
+
},
|
|
58
|
+
async (progress) => {
|
|
59
|
+
const total = files.length
|
|
60
|
+
let done = 0
|
|
61
|
+
|
|
62
|
+
for (const file of files) {
|
|
63
|
+
const report = await analyzeFilePath(file.fsPath)
|
|
64
|
+
if (report) {
|
|
65
|
+
diagnostics.update(report)
|
|
66
|
+
treeProvider.updateFile(report)
|
|
67
|
+
reportCache.set(file.fsPath, report)
|
|
68
|
+
}
|
|
69
|
+
done++
|
|
70
|
+
progress.report({ increment: (done / total) * 100 })
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
statusBar.update(Array.from(reportCache.values()))
|
|
74
|
+
vscode.window.showInformationMessage(`drift: ${total} files scanned.`)
|
|
75
|
+
}
|
|
76
|
+
)
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
// Comando: clear
|
|
80
|
+
const clearCmd = vscode.commands.registerCommand('drift.clearDiagnostics', () => {
|
|
81
|
+
diagnostics.clear()
|
|
82
|
+
treeProvider.clearAll()
|
|
83
|
+
reportCache.clear()
|
|
84
|
+
statusBar.update([])
|
|
85
|
+
})
|
|
86
|
+
|
|
87
|
+
// Comando: go to issue (desde TreeView click)
|
|
88
|
+
const goToCmd = vscode.commands.registerCommand(
|
|
89
|
+
'drift.goToIssue',
|
|
90
|
+
async (filePath: string, line: number) => {
|
|
91
|
+
const uri = vscode.Uri.file(filePath)
|
|
92
|
+
const doc = await vscode.workspace.openTextDocument(uri)
|
|
93
|
+
const editor = await vscode.window.showTextDocument(doc)
|
|
94
|
+
const pos = new vscode.Position(Math.max(0, line - 1), 0)
|
|
95
|
+
editor.selection = new vscode.Selection(pos, pos)
|
|
96
|
+
editor.revealRange(new vscode.Range(pos, pos), vscode.TextEditorRevealType.InCenter)
|
|
97
|
+
}
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
context.subscriptions.push(
|
|
101
|
+
{ dispose: () => diagnostics.dispose() },
|
|
102
|
+
{ dispose: () => statusBar.dispose() },
|
|
103
|
+
treeView,
|
|
104
|
+
onSave,
|
|
105
|
+
scanCmd,
|
|
106
|
+
clearCmd,
|
|
107
|
+
goToCmd,
|
|
108
|
+
)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export function deactivate(): void {}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import * as vscode from 'vscode'
|
|
2
|
+
import type { FileReport } from '@eduardbar/drift'
|
|
3
|
+
|
|
4
|
+
export class DriftStatusBarItem {
|
|
5
|
+
private item: vscode.StatusBarItem
|
|
6
|
+
|
|
7
|
+
constructor() {
|
|
8
|
+
this.item = vscode.window.createStatusBarItem(
|
|
9
|
+
vscode.StatusBarAlignment.Right,
|
|
10
|
+
100
|
|
11
|
+
)
|
|
12
|
+
this.item.command = 'drift.scanWorkspace'
|
|
13
|
+
this.item.tooltip = 'Click to scan workspace'
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
update(reports: FileReport[]): void {
|
|
17
|
+
if (reports.length === 0) {
|
|
18
|
+
this.item.text = '$(check) drift'
|
|
19
|
+
this.item.backgroundColor = undefined
|
|
20
|
+
this.item.show()
|
|
21
|
+
return
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const totalScore = Math.round(
|
|
25
|
+
reports.reduce((sum, r) => sum + r.score, 0) / reports.length
|
|
26
|
+
)
|
|
27
|
+
const totalIssues = reports.reduce((sum, r) => sum + r.issues.length, 0)
|
|
28
|
+
const hasErrors = reports.some(r => r.issues.some(i => i.severity === 'error'))
|
|
29
|
+
|
|
30
|
+
const icon = hasErrors ? '$(error)' : totalScore < 50 ? '$(warning)' : '$(check)'
|
|
31
|
+
this.item.text = `${icon} drift ${totalScore}/100 · ${totalIssues} issues`
|
|
32
|
+
|
|
33
|
+
if (hasErrors || totalScore < 30) {
|
|
34
|
+
this.item.backgroundColor = new vscode.ThemeColor('statusBarItem.errorBackground')
|
|
35
|
+
} else if (totalScore < 60) {
|
|
36
|
+
this.item.backgroundColor = new vscode.ThemeColor('statusBarItem.warningBackground')
|
|
37
|
+
} else {
|
|
38
|
+
this.item.backgroundColor = undefined
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
this.item.show()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
dispose(): void {
|
|
45
|
+
this.item.dispose()
|
|
46
|
+
}
|
|
47
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as vscode from 'vscode'
|
|
2
|
+
import * as path from 'path'
|
|
3
|
+
import type { FileReport } from '@eduardbar/drift'
|
|
4
|
+
|
|
5
|
+
type TreeItemType = 'file' | 'issue'
|
|
6
|
+
|
|
7
|
+
export class DriftTreeItem extends vscode.TreeItem {
|
|
8
|
+
constructor(
|
|
9
|
+
public readonly label: string,
|
|
10
|
+
public readonly collapsibleState: vscode.TreeItemCollapsibleState,
|
|
11
|
+
public readonly itemType: TreeItemType,
|
|
12
|
+
public readonly fileReport?: FileReport,
|
|
13
|
+
public readonly issueIndex?: number
|
|
14
|
+
) {
|
|
15
|
+
super(label, collapsibleState)
|
|
16
|
+
|
|
17
|
+
if (itemType === 'file' && fileReport) {
|
|
18
|
+
const score = fileReport.score
|
|
19
|
+
const issueCount = fileReport.issues.length
|
|
20
|
+
this.description = `score: ${score} • ${issueCount} issue${issueCount !== 1 ? 's' : ''}`
|
|
21
|
+
this.tooltip = fileReport.path
|
|
22
|
+
this.iconPath = score >= 70
|
|
23
|
+
? new vscode.ThemeIcon('check', new vscode.ThemeColor('testing.iconPassed'))
|
|
24
|
+
: score >= 40
|
|
25
|
+
? new vscode.ThemeIcon('warning', new vscode.ThemeColor('problemsWarningIcon.foreground'))
|
|
26
|
+
: new vscode.ThemeIcon('error', new vscode.ThemeColor('problemsErrorIcon.foreground'))
|
|
27
|
+
this.contextValue = 'driftFile'
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (itemType === 'issue' && fileReport && issueIndex !== undefined) {
|
|
31
|
+
const issue = fileReport.issues[issueIndex]
|
|
32
|
+
this.description = `line ${issue.line}`
|
|
33
|
+
this.tooltip = issue.message
|
|
34
|
+
this.iconPath = issue.severity === 'error'
|
|
35
|
+
? new vscode.ThemeIcon('circle-filled', new vscode.ThemeColor('problemsErrorIcon.foreground'))
|
|
36
|
+
: issue.severity === 'warning'
|
|
37
|
+
? new vscode.ThemeIcon('warning', new vscode.ThemeColor('problemsWarningIcon.foreground'))
|
|
38
|
+
: new vscode.ThemeIcon('info', new vscode.ThemeColor('problemsInfoIcon.foreground'))
|
|
39
|
+
|
|
40
|
+
// Click para ir a la línea
|
|
41
|
+
this.command = {
|
|
42
|
+
command: 'drift.goToIssue',
|
|
43
|
+
title: 'Go to Issue',
|
|
44
|
+
arguments: [fileReport.path, issue.line],
|
|
45
|
+
}
|
|
46
|
+
this.contextValue = 'driftIssue'
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export class DriftTreeProvider implements vscode.TreeDataProvider<DriftTreeItem> {
|
|
52
|
+
private _onDidChangeTreeData = new vscode.EventEmitter<DriftTreeItem | undefined | null | void>()
|
|
53
|
+
readonly onDidChangeTreeData = this._onDidChangeTreeData.event
|
|
54
|
+
|
|
55
|
+
private reports = new Map<string, FileReport>()
|
|
56
|
+
|
|
57
|
+
refresh(): void {
|
|
58
|
+
this._onDidChangeTreeData.fire()
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
updateFile(report: FileReport): void {
|
|
62
|
+
this.reports.set(report.path, report)
|
|
63
|
+
this.refresh()
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
clearFile(filePath: string): void {
|
|
67
|
+
this.reports.delete(filePath)
|
|
68
|
+
this.refresh()
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
clearAll(): void {
|
|
72
|
+
this.reports.clear()
|
|
73
|
+
this.refresh()
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
getTreeItem(element: DriftTreeItem): vscode.TreeItem {
|
|
77
|
+
return element
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
getChildren(element?: DriftTreeItem): DriftTreeItem[] {
|
|
81
|
+
if (!element) {
|
|
82
|
+
// Root: lista de archivos con issues, ordenados por score ascendente
|
|
83
|
+
return Array.from(this.reports.values())
|
|
84
|
+
.filter(r => r.issues.length > 0)
|
|
85
|
+
.sort((a, b) => a.score - b.score)
|
|
86
|
+
.map(r => new DriftTreeItem(
|
|
87
|
+
path.basename(r.path),
|
|
88
|
+
vscode.TreeItemCollapsibleState.Collapsed,
|
|
89
|
+
'file',
|
|
90
|
+
r
|
|
91
|
+
))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (element.itemType === 'file' && element.fileReport) {
|
|
95
|
+
return element.fileReport.issues.map((_, i) =>
|
|
96
|
+
new DriftTreeItem(
|
|
97
|
+
element.fileReport!.issues[i].rule,
|
|
98
|
+
vscode.TreeItemCollapsibleState.None,
|
|
99
|
+
'issue',
|
|
100
|
+
element.fileReport,
|
|
101
|
+
i
|
|
102
|
+
)
|
|
103
|
+
)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return []
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"moduleResolution": "node",
|
|
6
|
+
"lib": ["ES2022"],
|
|
7
|
+
"outDir": "./dist",
|
|
8
|
+
"rootDir": "./src",
|
|
9
|
+
"strict": true,
|
|
10
|
+
"esModuleInterop": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"allowSyntheticDefaultImports": true,
|
|
13
|
+
"declaration": true,
|
|
14
|
+
"sourceMap": true
|
|
15
|
+
},
|
|
16
|
+
"include": ["src/**/*"],
|
|
17
|
+
"exclude": ["node_modules", "dist"]
|
|
18
|
+
}
|
|
Binary file
|
|
Binary file
|