@barissozen/csns 0.6.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/.env.example +8 -0
- package/LICENSE +21 -0
- package/README.md +364 -0
- package/dist/agent/agent-runner.d.ts +48 -0
- package/dist/agent/agent-runner.d.ts.map +1 -0
- package/dist/agent/agent-runner.js +180 -0
- package/dist/agent/agent-runner.js.map +1 -0
- package/dist/agent/architect.d.ts +34 -0
- package/dist/agent/architect.d.ts.map +1 -0
- package/dist/agent/architect.js +156 -0
- package/dist/agent/architect.js.map +1 -0
- package/dist/agent/codebase-reader.d.ts +35 -0
- package/dist/agent/codebase-reader.d.ts.map +1 -0
- package/dist/agent/codebase-reader.js +305 -0
- package/dist/agent/codebase-reader.js.map +1 -0
- package/dist/agent/context-builder.d.ts +39 -0
- package/dist/agent/context-builder.d.ts.map +1 -0
- package/dist/agent/context-builder.js +163 -0
- package/dist/agent/context-builder.js.map +1 -0
- package/dist/agent/index.d.ts +14 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +12 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/output-parser.d.ts +27 -0
- package/dist/agent/output-parser.d.ts.map +1 -0
- package/dist/agent/output-parser.js +154 -0
- package/dist/agent/output-parser.js.map +1 -0
- package/dist/agent/process-spawner.d.ts +56 -0
- package/dist/agent/process-spawner.d.ts.map +1 -0
- package/dist/agent/process-spawner.js +153 -0
- package/dist/agent/process-spawner.js.map +1 -0
- package/dist/agent/tracer/index.d.ts +8 -0
- package/dist/agent/tracer/index.d.ts.map +1 -0
- package/dist/agent/tracer/index.js +6 -0
- package/dist/agent/tracer/index.js.map +1 -0
- package/dist/agent/tracer/reverse-engineer.d.ts +113 -0
- package/dist/agent/tracer/reverse-engineer.d.ts.map +1 -0
- package/dist/agent/tracer/reverse-engineer.js +695 -0
- package/dist/agent/tracer/reverse-engineer.js.map +1 -0
- package/dist/agent/tracer/runtime-tracer.d.ts +61 -0
- package/dist/agent/tracer/runtime-tracer.d.ts.map +1 -0
- package/dist/agent/tracer/runtime-tracer.js +484 -0
- package/dist/agent/tracer/runtime-tracer.js.map +1 -0
- package/dist/agent/tracer/semantic-analyzer.d.ts +24 -0
- package/dist/agent/tracer/semantic-analyzer.d.ts.map +1 -0
- package/dist/agent/tracer/semantic-analyzer.js +132 -0
- package/dist/agent/tracer/semantic-analyzer.js.map +1 -0
- package/dist/agent/tracer/static-analyzer.d.ts +44 -0
- package/dist/agent/tracer/static-analyzer.d.ts.map +1 -0
- package/dist/agent/tracer/static-analyzer.js +453 -0
- package/dist/agent/tracer/static-analyzer.js.map +1 -0
- package/dist/agent/tracer/tracer-agent.d.ts +61 -0
- package/dist/agent/tracer/tracer-agent.d.ts.map +1 -0
- package/dist/agent/tracer/tracer-agent.js +252 -0
- package/dist/agent/tracer/tracer-agent.js.map +1 -0
- package/dist/bin/csns.d.ts +24 -0
- package/dist/bin/csns.d.ts.map +1 -0
- package/dist/bin/csns.js +389 -0
- package/dist/bin/csns.js.map +1 -0
- package/dist/bin/pc.d.ts +13 -0
- package/dist/bin/pc.d.ts.map +1 -0
- package/dist/bin/pc.js +212 -0
- package/dist/bin/pc.js.map +1 -0
- package/dist/brief/brief-collector.d.ts +42 -0
- package/dist/brief/brief-collector.d.ts.map +1 -0
- package/dist/brief/brief-collector.js +228 -0
- package/dist/brief/brief-collector.js.map +1 -0
- package/dist/brief/index.d.ts +3 -0
- package/dist/brief/index.d.ts.map +1 -0
- package/dist/brief/index.js +3 -0
- package/dist/brief/index.js.map +1 -0
- package/dist/brief/smart-brief.d.ts +52 -0
- package/dist/brief/smart-brief.d.ts.map +1 -0
- package/dist/brief/smart-brief.js +440 -0
- package/dist/brief/smart-brief.js.map +1 -0
- package/dist/calculator/calculator.d.ts +7 -0
- package/dist/calculator/calculator.d.ts.map +1 -0
- package/dist/calculator/calculator.js +18 -0
- package/dist/calculator/calculator.js.map +1 -0
- package/dist/calculator/index.d.ts +2 -0
- package/dist/calculator/index.d.ts.map +1 -0
- package/dist/calculator/index.js +2 -0
- package/dist/calculator/index.js.map +1 -0
- package/dist/i18n/en.d.ts +6 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +136 -0
- package/dist/i18n/en.js.map +1 -0
- package/dist/i18n/index.d.ts +31 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +44 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/i18n/tr.d.ts +6 -0
- package/dist/i18n/tr.d.ts.map +1 -0
- package/dist/i18n/tr.js +136 -0
- package/dist/i18n/tr.js.map +1 -0
- package/dist/i18n/types.d.ts +86 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +9 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +72 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic-provider.d.ts +18 -0
- package/dist/llm/anthropic-provider.d.ts.map +1 -0
- package/dist/llm/anthropic-provider.js +55 -0
- package/dist/llm/anthropic-provider.js.map +1 -0
- package/dist/llm/factory.d.ts +21 -0
- package/dist/llm/factory.d.ts.map +1 -0
- package/dist/llm/factory.js +59 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/index.d.ts +7 -0
- package/dist/llm/index.d.ts.map +1 -0
- package/dist/llm/index.js +5 -0
- package/dist/llm/index.js.map +1 -0
- package/dist/llm/ollama-provider.d.ts +20 -0
- package/dist/llm/ollama-provider.d.ts.map +1 -0
- package/dist/llm/ollama-provider.js +62 -0
- package/dist/llm/ollama-provider.js.map +1 -0
- package/dist/llm/openai-provider.d.ts +20 -0
- package/dist/llm/openai-provider.d.ts.map +1 -0
- package/dist/llm/openai-provider.js +65 -0
- package/dist/llm/openai-provider.js.map +1 -0
- package/dist/llm/resolve.d.ts +10 -0
- package/dist/llm/resolve.d.ts.map +1 -0
- package/dist/llm/resolve.js +21 -0
- package/dist/llm/resolve.js.map +1 -0
- package/dist/llm/types.d.ts +45 -0
- package/dist/llm/types.d.ts.map +1 -0
- package/dist/llm/types.js +11 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.d.ts.map +1 -0
- package/dist/memory/index.js +2 -0
- package/dist/memory/index.js.map +1 -0
- package/dist/memory/memory-layer.d.ts +54 -0
- package/dist/memory/memory-layer.d.ts.map +1 -0
- package/dist/memory/memory-layer.js +297 -0
- package/dist/memory/memory-layer.js.map +1 -0
- package/dist/orchestrator/dependency-graph.d.ts +39 -0
- package/dist/orchestrator/dependency-graph.d.ts.map +1 -0
- package/dist/orchestrator/dependency-graph.js +134 -0
- package/dist/orchestrator/dependency-graph.js.map +1 -0
- package/dist/orchestrator/escalator.d.ts +42 -0
- package/dist/orchestrator/escalator.d.ts.map +1 -0
- package/dist/orchestrator/escalator.js +163 -0
- package/dist/orchestrator/escalator.js.map +1 -0
- package/dist/orchestrator/evaluator.d.ts +34 -0
- package/dist/orchestrator/evaluator.d.ts.map +1 -0
- package/dist/orchestrator/evaluator.js +335 -0
- package/dist/orchestrator/evaluator.js.map +1 -0
- package/dist/orchestrator/index.d.ts +9 -0
- package/dist/orchestrator/index.d.ts.map +1 -0
- package/dist/orchestrator/index.js +9 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/integration-evaluator.d.ts +59 -0
- package/dist/orchestrator/integration-evaluator.d.ts.map +1 -0
- package/dist/orchestrator/integration-evaluator.js +405 -0
- package/dist/orchestrator/integration-evaluator.js.map +1 -0
- package/dist/orchestrator/milestone-manager.d.ts +28 -0
- package/dist/orchestrator/milestone-manager.d.ts.map +1 -0
- package/dist/orchestrator/milestone-manager.js +208 -0
- package/dist/orchestrator/milestone-manager.js.map +1 -0
- package/dist/orchestrator/orchestrator.d.ts +35 -0
- package/dist/orchestrator/orchestrator.d.ts.map +1 -0
- package/dist/orchestrator/orchestrator.js +338 -0
- package/dist/orchestrator/orchestrator.js.map +1 -0
- package/dist/orchestrator/planner.d.ts +15 -0
- package/dist/orchestrator/planner.d.ts.map +1 -0
- package/dist/orchestrator/planner.js +87 -0
- package/dist/orchestrator/planner.js.map +1 -0
- package/dist/orchestrator/recovery.d.ts +45 -0
- package/dist/orchestrator/recovery.d.ts.map +1 -0
- package/dist/orchestrator/recovery.js +98 -0
- package/dist/orchestrator/recovery.js.map +1 -0
- package/dist/run-calculator-test.d.ts +11 -0
- package/dist/run-calculator-test.d.ts.map +1 -0
- package/dist/run-calculator-test.js +212 -0
- package/dist/run-calculator-test.js.map +1 -0
- package/dist/run-real-task.d.ts +14 -0
- package/dist/run-real-task.d.ts.map +1 -0
- package/dist/run-real-task.js +185 -0
- package/dist/run-real-task.js.map +1 -0
- package/dist/run-todo-test.d.ts +6 -0
- package/dist/run-todo-test.d.ts.map +1 -0
- package/dist/run-todo-test.js +149 -0
- package/dist/run-todo-test.js.map +1 -0
- package/dist/todo/index.d.ts +2 -0
- package/dist/todo/index.d.ts.map +1 -0
- package/dist/todo/index.js +2 -0
- package/dist/todo/index.js.map +1 -0
- package/dist/todo/server.d.ts +2 -0
- package/dist/todo/server.d.ts.map +1 -0
- package/dist/todo/server.js +32 -0
- package/dist/todo/server.js.map +1 -0
- package/dist/types/index.d.ts +412 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +5 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Analyzer — LLM ile Wiring Mantık Kontrolü
|
|
3
|
+
*
|
|
4
|
+
* LLMProvider abstraction ile herhangi bir model kullanabilir.
|
|
5
|
+
*/
|
|
6
|
+
import type { LLMProvider } from '../../llm/types.js';
|
|
7
|
+
import type { DependencyEdge, ExportNode, WiringIssue, SemanticInsight } from '../../types/index.js';
|
|
8
|
+
export declare class SemanticAnalyzer {
|
|
9
|
+
private provider;
|
|
10
|
+
constructor(provider?: LLMProvider | null);
|
|
11
|
+
/**
|
|
12
|
+
* Import graph + dosya özetlerini LLM'e gönder, semantic sorunları tespit et
|
|
13
|
+
*/
|
|
14
|
+
analyze(edges: DependencyEdge[], exports: ExportNode[], fileSummaries: Map<string, string>, architecture?: string): Promise<SemanticInsight[]>;
|
|
15
|
+
/**
|
|
16
|
+
* Semantic insight'ları WiringIssue'lara dönüştür
|
|
17
|
+
*/
|
|
18
|
+
toWiringIssues(insights: SemanticInsight[]): WiringIssue[];
|
|
19
|
+
private buildPrompt;
|
|
20
|
+
private parseResponse;
|
|
21
|
+
private mapCategory;
|
|
22
|
+
private generateSuggestion;
|
|
23
|
+
}
|
|
24
|
+
//# sourceMappingURL=semantic-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-analyzer.d.ts","sourceRoot":"","sources":["../../../src/agent/tracer/semantic-analyzer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EACV,cAAc,EACd,UAAU,EACV,WAAW,EACX,eAAe,EAChB,MAAM,sBAAsB,CAAC;AA0B9B,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAqB;gBAEzB,QAAQ,CAAC,EAAE,WAAW,GAAG,IAAI;IAIzC;;OAEG;IACG,OAAO,CACX,KAAK,EAAE,cAAc,EAAE,EACvB,OAAO,EAAE,UAAU,EAAE,EACrB,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAClC,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC;IAqB7B;;OAEG;IACH,cAAc,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,WAAW,EAAE;IAc1D,OAAO,CAAC,WAAW;IAyCnB,OAAO,CAAC,aAAa;IAgBrB,OAAO,CAAC,WAAW;IAWnB,OAAO,CAAC,kBAAkB;CAU3B"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Analyzer — LLM ile Wiring Mantık Kontrolü
|
|
3
|
+
*
|
|
4
|
+
* LLMProvider abstraction ile herhangi bir model kullanabilir.
|
|
5
|
+
*/
|
|
6
|
+
const SEMANTIC_SYSTEM_PROMPT = `You are an experienced software architect. Your task is to identify wiring issues in a TypeScript project.
|
|
7
|
+
|
|
8
|
+
You will receive:
|
|
9
|
+
1. Import/export graph — which file imports what
|
|
10
|
+
2. File summaries — what each file does
|
|
11
|
+
3. Architecture decisions — how the project is structured
|
|
12
|
+
|
|
13
|
+
Detect:
|
|
14
|
+
- **injection-missing**: A service/handler needs another service but it's not injected
|
|
15
|
+
- **config-mismatch**: Env variable or config value used but undefined or type mismatch
|
|
16
|
+
- **interface-drift**: Contract mismatch between two modules (parameter types, return types)
|
|
17
|
+
- **handler-gap**: Route defined but handler not connected, or missing link in middleware chain
|
|
18
|
+
- **data-flow-break**: Data should flow A→B→C but B→C connection is broken
|
|
19
|
+
|
|
20
|
+
OUTPUT: JSON array only, each element:
|
|
21
|
+
{
|
|
22
|
+
"category": "injection-missing|config-mismatch|interface-drift|handler-gap|data-flow-break",
|
|
23
|
+
"description": "Human-readable description",
|
|
24
|
+
"files": ["file1.ts", "file2.ts"],
|
|
25
|
+
"confidence": 0.0-1.0
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
Mark uncertain findings with low confidence. Stay silent rather than produce false positives.`;
|
|
29
|
+
export class SemanticAnalyzer {
|
|
30
|
+
provider;
|
|
31
|
+
constructor(provider) {
|
|
32
|
+
this.provider = provider ?? null;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Import graph + dosya özetlerini LLM'e gönder, semantic sorunları tespit et
|
|
36
|
+
*/
|
|
37
|
+
async analyze(edges, exports, fileSummaries, architecture) {
|
|
38
|
+
if (!this.provider) {
|
|
39
|
+
return []; // No LLM provider, skip
|
|
40
|
+
}
|
|
41
|
+
const prompt = this.buildPrompt(edges, exports, fileSummaries, architecture);
|
|
42
|
+
try {
|
|
43
|
+
const response = await this.provider.chat([{ role: 'user', content: prompt }], { system: SEMANTIC_SYSTEM_PROMPT, maxTokens: 4096 });
|
|
44
|
+
return this.parseResponse(response.text);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
const msg = error instanceof Error ? error.message : 'Unknown error';
|
|
48
|
+
console.warn(`⚠️ Semantic analysis failed: ${msg}`);
|
|
49
|
+
return [];
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Semantic insight'ları WiringIssue'lara dönüştür
|
|
54
|
+
*/
|
|
55
|
+
toWiringIssues(insights) {
|
|
56
|
+
return insights
|
|
57
|
+
.filter(i => i.confidence >= 0.5) // düşük güvenli olanları filtrele
|
|
58
|
+
.map(insight => ({
|
|
59
|
+
type: this.mapCategory(insight.category),
|
|
60
|
+
severity: insight.confidence >= 0.8 ? 'warning' : 'info',
|
|
61
|
+
file: insight.files[0] ?? 'unknown',
|
|
62
|
+
detail: `[LLM %.0f%%] ${insight.description}`.replace('%.0f', String(Math.round(insight.confidence * 100))),
|
|
63
|
+
suggestion: this.generateSuggestion(insight),
|
|
64
|
+
}));
|
|
65
|
+
}
|
|
66
|
+
// ── Private ────────────────────────────────────────────────
|
|
67
|
+
buildPrompt(edges, exports, fileSummaries, architecture) {
|
|
68
|
+
const parts = [];
|
|
69
|
+
// Graph özeti
|
|
70
|
+
parts.push('## Import/Export Grafiği\n');
|
|
71
|
+
for (const edge of edges.slice(0, 100)) { // max 100 edge
|
|
72
|
+
parts.push(`${edge.source} → ${edge.target} [${edge.symbols.join(', ')}]`);
|
|
73
|
+
}
|
|
74
|
+
// Export listesi
|
|
75
|
+
parts.push('\n## Dosya Export\'ları\n');
|
|
76
|
+
const byFile = new Map();
|
|
77
|
+
for (const exp of exports) {
|
|
78
|
+
if (!byFile.has(exp.file))
|
|
79
|
+
byFile.set(exp.file, []);
|
|
80
|
+
byFile.get(exp.file).push(exp);
|
|
81
|
+
}
|
|
82
|
+
for (const [file, exps] of byFile) {
|
|
83
|
+
parts.push(`**${file}**: ${exps.map(e => `${e.symbol}(${e.kind})`).join(', ')}`);
|
|
84
|
+
}
|
|
85
|
+
// Dosya özetleri
|
|
86
|
+
parts.push('\n## Dosya İçerik Özetleri\n');
|
|
87
|
+
for (const [file, summary] of fileSummaries) {
|
|
88
|
+
parts.push(`### ${file}\n${summary.slice(0, 500)}\n`);
|
|
89
|
+
}
|
|
90
|
+
// Mimari
|
|
91
|
+
if (architecture) {
|
|
92
|
+
parts.push(`\n## Mimari Kararlar\n${architecture.slice(0, 1500)}\n`);
|
|
93
|
+
}
|
|
94
|
+
parts.push('\n---\nBu projedeki wiring sorunlarını JSON array olarak raporla.');
|
|
95
|
+
return parts.join('\n');
|
|
96
|
+
}
|
|
97
|
+
parseResponse(text) {
|
|
98
|
+
// JSON array çıkar
|
|
99
|
+
const jsonMatch = text.match(/\[[\s\S]*\]/);
|
|
100
|
+
if (!jsonMatch)
|
|
101
|
+
return [];
|
|
102
|
+
try {
|
|
103
|
+
const parsed = JSON.parse(jsonMatch[0]);
|
|
104
|
+
return parsed.filter(item => item.category && item.description && Array.isArray(item.files) &&
|
|
105
|
+
typeof item.confidence === 'number');
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
return [];
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
mapCategory(category) {
|
|
112
|
+
const map = {
|
|
113
|
+
'injection-missing': 'missing-import',
|
|
114
|
+
'config-mismatch': 'type-mismatch',
|
|
115
|
+
'interface-drift': 'type-mismatch',
|
|
116
|
+
'handler-gap': 'missing-import',
|
|
117
|
+
'data-flow-break': 'runtime-gap',
|
|
118
|
+
};
|
|
119
|
+
return map[category];
|
|
120
|
+
}
|
|
121
|
+
generateSuggestion(insight) {
|
|
122
|
+
const map = {
|
|
123
|
+
'injection-missing': 'Inject the missing service via constructor or factory',
|
|
124
|
+
'config-mismatch': 'Check config/env values, compare with .env.example',
|
|
125
|
+
'interface-drift': 'Define a shared interface and ensure both sides implement it',
|
|
126
|
+
'handler-gap': 'Check route definition and handler binding, verify middleware chain',
|
|
127
|
+
'data-flow-break': 'Find the broken point in data flow, add missing intermediate layer',
|
|
128
|
+
};
|
|
129
|
+
return map[insight.category];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
//# sourceMappingURL=semantic-analyzer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"semantic-analyzer.js","sourceRoot":"","sources":["../../../src/agent/tracer/semantic-analyzer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Analyzer — Import/Export Graph + Dead Code + Circular Dep Tespiti
|
|
3
|
+
*
|
|
4
|
+
* AST-free, regex tabanlı hızlı analiz.
|
|
5
|
+
* Tüm .ts/.tsx dosyalarını tarar, import/export grafiği çıkarır.
|
|
6
|
+
* Kullanılmayan export'ları, kırık import'ları, döngüsel bağımlılıkları tespit eder.
|
|
7
|
+
*/
|
|
8
|
+
import type { ImportEdge, ExportNode, DependencyEdge, WiringIssue } from '../../types/index.js';
|
|
9
|
+
export declare class StaticAnalyzer {
|
|
10
|
+
private projectRoot;
|
|
11
|
+
constructor(projectRoot: string);
|
|
12
|
+
/**
|
|
13
|
+
* Tüm kaynak dosyaları tara, import/export grafiğini çıkar
|
|
14
|
+
*/
|
|
15
|
+
buildGraph(): Promise<{
|
|
16
|
+
imports: ImportEdge[];
|
|
17
|
+
exports: ExportNode[];
|
|
18
|
+
edges: DependencyEdge[];
|
|
19
|
+
files: string[];
|
|
20
|
+
}>;
|
|
21
|
+
/**
|
|
22
|
+
* Static wiring sorunlarını tespit et
|
|
23
|
+
*/
|
|
24
|
+
findIssues(): Promise<WiringIssue[]>;
|
|
25
|
+
private extractImports;
|
|
26
|
+
private extractExports;
|
|
27
|
+
private buildEdges;
|
|
28
|
+
private findDeadExports;
|
|
29
|
+
private findMissingImports;
|
|
30
|
+
private findCircularDeps;
|
|
31
|
+
private findPhantomDeps;
|
|
32
|
+
private collectSourceFiles;
|
|
33
|
+
private walk;
|
|
34
|
+
/**
|
|
35
|
+
* Relative import'u dosya yoluna çözümle.
|
|
36
|
+
* './foo.js' → 'src/foo.ts' (TypeScript .js → .ts mapping dahil)
|
|
37
|
+
*/
|
|
38
|
+
private resolveImportPath;
|
|
39
|
+
private isEntryPoint;
|
|
40
|
+
private isNodeBuiltin;
|
|
41
|
+
private findLineNumber;
|
|
42
|
+
private inferExportKind;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=static-analyzer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-analyzer.d.ts","sourceRoot":"","sources":["../../../src/agent/tracer/static-analyzer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,cAAc,EACd,WAAW,EACZ,MAAM,sBAAsB,CAAC;AA+B9B,qBAAa,cAAc;IACzB,OAAO,CAAC,WAAW,CAAS;gBAEhB,WAAW,EAAE,MAAM;IAM/B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC;QAC1B,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,OAAO,EAAE,UAAU,EAAE,CAAC;QACtB,KAAK,EAAE,cAAc,EAAE,CAAC;QACxB,KAAK,EAAE,MAAM,EAAE,CAAC;KACjB,CAAC;IAkBF;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IAqB1C,OAAO,CAAC,cAAc;IAyDtB,OAAO,CAAC,cAAc;IAgDtB,OAAO,CAAC,UAAU;IA8BlB,OAAO,CAAC,eAAe;IA2DvB,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,gBAAgB;YAkDV,eAAe;YA+Df,kBAAkB;YAMlB,IAAI;IAmBlB;;;OAGG;IACH,OAAO,CAAC,iBAAiB;IA0BzB,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,aAAa;IAWrB,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,eAAe;CAQxB"}
|
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Static Analyzer — Import/Export Graph + Dead Code + Circular Dep Tespiti
|
|
3
|
+
*
|
|
4
|
+
* AST-free, regex tabanlı hızlı analiz.
|
|
5
|
+
* Tüm .ts/.tsx dosyalarını tarar, import/export grafiği çıkarır.
|
|
6
|
+
* Kullanılmayan export'ları, kırık import'ları, döngüsel bağımlılıkları tespit eder.
|
|
7
|
+
*/
|
|
8
|
+
import { readFile, readdir } from 'node:fs/promises';
|
|
9
|
+
import { join, relative, dirname, extname } from 'node:path';
|
|
10
|
+
const SOURCE_EXTS = new Set(['.ts', '.tsx', '.js', '.jsx', '.mjs']);
|
|
11
|
+
const SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build', 'coverage', '.next']);
|
|
12
|
+
// ── Regex Patterns ───────────────────────────────────────────
|
|
13
|
+
/** import { X, Y } from './path' */
|
|
14
|
+
const NAMED_IMPORT = /import\s+(?:type\s+)?{([^}]+)}\s+from\s+['"]([^'"]+)['"]/g;
|
|
15
|
+
/** import X from './path' */
|
|
16
|
+
const DEFAULT_IMPORT = /import\s+(?:type\s+)?(\w+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
17
|
+
/** import * as X from './path' */
|
|
18
|
+
const STAR_IMPORT = /import\s+\*\s+as\s+(\w+)\s+from\s+['"]([^'"]+)['"]/g;
|
|
19
|
+
/** export { X, Y } from './path' (re-export) */
|
|
20
|
+
const RE_EXPORT = /export\s+(?:type\s+)?{([^}]+)}\s+from\s+['"]([^'"]+)['"]/g;
|
|
21
|
+
/** export default class/function/const X */
|
|
22
|
+
const EXPORT_DEFAULT = /export\s+default\s+(?:class|function|const|abstract\s+class)\s+(\w+)/g;
|
|
23
|
+
/** export class/function/const/type/interface/enum X */
|
|
24
|
+
const EXPORT_NAMED = /export\s+(?:declare\s+)?(?:abstract\s+)?(?:class|function|const|let|var|type|interface|enum)\s+(\w+)/g;
|
|
25
|
+
/** export default (anonymous) */
|
|
26
|
+
const EXPORT_DEFAULT_ANON = /export\s+default\s+(?!class|function|const|abstract)/g;
|
|
27
|
+
/** import type { X } from ... (TypeScript) */
|
|
28
|
+
const TYPE_IMPORT = /import\s+type\s+/;
|
|
29
|
+
export class StaticAnalyzer {
|
|
30
|
+
projectRoot;
|
|
31
|
+
constructor(projectRoot) {
|
|
32
|
+
this.projectRoot = projectRoot;
|
|
33
|
+
}
|
|
34
|
+
// ── Public API ─────────────────────────────────────────────
|
|
35
|
+
/**
|
|
36
|
+
* Tüm kaynak dosyaları tara, import/export grafiğini çıkar
|
|
37
|
+
*/
|
|
38
|
+
async buildGraph() {
|
|
39
|
+
const files = await this.collectSourceFiles();
|
|
40
|
+
const allImports = [];
|
|
41
|
+
const allExports = [];
|
|
42
|
+
for (const file of files) {
|
|
43
|
+
const content = await readFile(join(this.projectRoot, file), 'utf-8');
|
|
44
|
+
const imports = this.extractImports(file, content);
|
|
45
|
+
const exports = this.extractExports(file, content);
|
|
46
|
+
allImports.push(...imports);
|
|
47
|
+
allExports.push(...exports);
|
|
48
|
+
}
|
|
49
|
+
const edges = this.buildEdges(allImports, files);
|
|
50
|
+
return { imports: allImports, exports: allExports, edges, files };
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Static wiring sorunlarını tespit et
|
|
54
|
+
*/
|
|
55
|
+
async findIssues() {
|
|
56
|
+
const { imports, exports, edges, files } = await this.buildGraph();
|
|
57
|
+
const issues = [];
|
|
58
|
+
// 1. Dead exports — hiçbir yerde import edilmeyen export'lar
|
|
59
|
+
issues.push(...this.findDeadExports(exports, imports, files));
|
|
60
|
+
// 2. Missing imports — resolve edilemeyen import'lar
|
|
61
|
+
issues.push(...this.findMissingImports(imports, files));
|
|
62
|
+
// 3. Circular dependencies
|
|
63
|
+
issues.push(...this.findCircularDeps(edges));
|
|
64
|
+
// 4. Phantom dependencies — package.json'da olmayan import'lar
|
|
65
|
+
issues.push(...await this.findPhantomDeps(imports));
|
|
66
|
+
return issues;
|
|
67
|
+
}
|
|
68
|
+
// ── Import Extraction ──────────────────────────────────────
|
|
69
|
+
extractImports(file, content) {
|
|
70
|
+
const imports = [];
|
|
71
|
+
const lines = content.split('\n');
|
|
72
|
+
// Named imports: import { X, Y } from './path'
|
|
73
|
+
for (const match of content.matchAll(NAMED_IMPORT)) {
|
|
74
|
+
const symbols = match[1].split(',').map(s => {
|
|
75
|
+
const parts = s.trim().split(/\s+as\s+/);
|
|
76
|
+
return parts[0].trim();
|
|
77
|
+
}).filter(s => s.length > 0);
|
|
78
|
+
const line = this.findLineNumber(lines, match.index);
|
|
79
|
+
const isTypeOnly = TYPE_IMPORT.test(match[0]);
|
|
80
|
+
imports.push({
|
|
81
|
+
from: file,
|
|
82
|
+
to: match[2],
|
|
83
|
+
symbols,
|
|
84
|
+
isTypeOnly,
|
|
85
|
+
line,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
// Default imports: import X from './path'
|
|
89
|
+
for (const match of content.matchAll(DEFAULT_IMPORT)) {
|
|
90
|
+
// Named import regex ile overlap kontrolü
|
|
91
|
+
if (match[0].includes('{'))
|
|
92
|
+
continue;
|
|
93
|
+
const line = this.findLineNumber(lines, match.index);
|
|
94
|
+
const isTypeOnly = TYPE_IMPORT.test(match[0]);
|
|
95
|
+
imports.push({
|
|
96
|
+
from: file,
|
|
97
|
+
to: match[2],
|
|
98
|
+
symbols: ['default'],
|
|
99
|
+
isTypeOnly,
|
|
100
|
+
line,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
// Star imports: import * as X from './path'
|
|
104
|
+
for (const match of content.matchAll(STAR_IMPORT)) {
|
|
105
|
+
const line = this.findLineNumber(lines, match.index);
|
|
106
|
+
imports.push({
|
|
107
|
+
from: file,
|
|
108
|
+
to: match[2],
|
|
109
|
+
symbols: ['*'],
|
|
110
|
+
isTypeOnly: false,
|
|
111
|
+
line,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
return imports;
|
|
115
|
+
}
|
|
116
|
+
// ── Export Extraction ──────────────────────────────────────
|
|
117
|
+
extractExports(file, content) {
|
|
118
|
+
const exports = [];
|
|
119
|
+
const lines = content.split('\n');
|
|
120
|
+
const seen = new Set();
|
|
121
|
+
// Re-exports: export { X } from './path'
|
|
122
|
+
for (const match of content.matchAll(RE_EXPORT)) {
|
|
123
|
+
const symbols = match[1].split(',').map(s => s.trim().split(/\s+as\s+/).pop().trim());
|
|
124
|
+
for (const sym of symbols) {
|
|
125
|
+
if (sym && !seen.has(sym)) {
|
|
126
|
+
seen.add(sym);
|
|
127
|
+
exports.push({ file, symbol: sym, kind: 're-export', line: this.findLineNumber(lines, match.index) });
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
// Named exports: export class/function/const/type/interface/enum X
|
|
132
|
+
for (const match of content.matchAll(EXPORT_NAMED)) {
|
|
133
|
+
const sym = match[1];
|
|
134
|
+
if (!seen.has(sym)) {
|
|
135
|
+
seen.add(sym);
|
|
136
|
+
const kind = this.inferExportKind(match[0]);
|
|
137
|
+
exports.push({ file, symbol: sym, kind, line: this.findLineNumber(lines, match.index) });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Default exports: export default class X
|
|
141
|
+
for (const match of content.matchAll(EXPORT_DEFAULT)) {
|
|
142
|
+
const sym = match[1];
|
|
143
|
+
if (!seen.has(sym)) {
|
|
144
|
+
seen.add(sym);
|
|
145
|
+
exports.push({ file, symbol: sym, kind: 'default', line: this.findLineNumber(lines, match.index) });
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Anonymous default exports
|
|
149
|
+
for (const match of content.matchAll(EXPORT_DEFAULT_ANON)) {
|
|
150
|
+
if (!seen.has('default')) {
|
|
151
|
+
seen.add('default');
|
|
152
|
+
exports.push({ file, symbol: 'default', kind: 'default', line: this.findLineNumber(lines, match.index) });
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return exports;
|
|
156
|
+
}
|
|
157
|
+
// ── Edge Building ──────────────────────────────────────────
|
|
158
|
+
buildEdges(imports, files) {
|
|
159
|
+
const edgeMap = new Map();
|
|
160
|
+
const fileSet = new Set(files);
|
|
161
|
+
for (const imp of imports) {
|
|
162
|
+
const resolved = this.resolveImportPath(imp.from, imp.to, fileSet);
|
|
163
|
+
if (!resolved)
|
|
164
|
+
continue; // external package
|
|
165
|
+
const key = `${imp.from}→${resolved}`;
|
|
166
|
+
const existing = edgeMap.get(key);
|
|
167
|
+
if (existing) {
|
|
168
|
+
existing.weight++;
|
|
169
|
+
for (const s of imp.symbols) {
|
|
170
|
+
if (!existing.symbols.includes(s))
|
|
171
|
+
existing.symbols.push(s);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
edgeMap.set(key, {
|
|
176
|
+
source: imp.from,
|
|
177
|
+
target: resolved,
|
|
178
|
+
symbols: [...imp.symbols],
|
|
179
|
+
weight: 1,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return [...edgeMap.values()];
|
|
184
|
+
}
|
|
185
|
+
// ── Issue Detection ────────────────────────────────────────
|
|
186
|
+
findDeadExports(exports, imports, files) {
|
|
187
|
+
const issues = [];
|
|
188
|
+
const fileSet = new Set(files);
|
|
189
|
+
// Tüm import edilen symbol'leri topla (dosya bazlı)
|
|
190
|
+
const importedSymbols = new Map(); // file → symbols
|
|
191
|
+
for (const imp of imports) {
|
|
192
|
+
const resolved = this.resolveImportPath(imp.from, imp.to, fileSet);
|
|
193
|
+
if (!resolved)
|
|
194
|
+
continue;
|
|
195
|
+
if (!importedSymbols.has(resolved)) {
|
|
196
|
+
importedSymbols.set(resolved, new Set());
|
|
197
|
+
}
|
|
198
|
+
for (const sym of imp.symbols) {
|
|
199
|
+
importedSymbols.get(resolved).add(sym);
|
|
200
|
+
// Star import → tüm export'ları kullanılmış say
|
|
201
|
+
if (sym === '*') {
|
|
202
|
+
importedSymbols.get(resolved).add('*');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
for (const exp of exports) {
|
|
207
|
+
// Entry point dosyalarını atla (index.ts, bin/*, main)
|
|
208
|
+
if (this.isEntryPoint(exp.file))
|
|
209
|
+
continue;
|
|
210
|
+
// Type-only export'ları atla (type/interface)
|
|
211
|
+
if (exp.kind === 'type' || exp.kind === 'interface')
|
|
212
|
+
continue;
|
|
213
|
+
const usedSymbols = importedSymbols.get(exp.file);
|
|
214
|
+
if (!usedSymbols) {
|
|
215
|
+
// Hiçbir dosya bu dosyayı import etmiyor
|
|
216
|
+
issues.push({
|
|
217
|
+
type: 'dead-export',
|
|
218
|
+
severity: 'warning',
|
|
219
|
+
file: exp.file,
|
|
220
|
+
symbol: exp.symbol,
|
|
221
|
+
detail: `Export '${exp.symbol}' hiçbir yerde kullanılmıyor (${exp.file}:${exp.line})`,
|
|
222
|
+
suggestion: `Kullanılmıyorsa kaldır veya export'u internal yap`,
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
else if (!usedSymbols.has(exp.symbol) && !usedSymbols.has('default') && !usedSymbols.has('*')) {
|
|
226
|
+
issues.push({
|
|
227
|
+
type: 'dead-export',
|
|
228
|
+
severity: 'info',
|
|
229
|
+
file: exp.file,
|
|
230
|
+
symbol: exp.symbol,
|
|
231
|
+
detail: `Export '${exp.symbol}' import ediliyor ama bu symbol kullanılmıyor (${exp.file}:${exp.line})`,
|
|
232
|
+
suggestion: `Import listesinden ve export'tan kaldırılabilir`,
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return issues;
|
|
237
|
+
}
|
|
238
|
+
findMissingImports(imports, files) {
|
|
239
|
+
const issues = [];
|
|
240
|
+
const fileSet = new Set(files);
|
|
241
|
+
for (const imp of imports) {
|
|
242
|
+
// Sadece relative import'ları kontrol et
|
|
243
|
+
if (!imp.to.startsWith('.'))
|
|
244
|
+
continue;
|
|
245
|
+
const resolved = this.resolveImportPath(imp.from, imp.to, fileSet);
|
|
246
|
+
if (!resolved) {
|
|
247
|
+
issues.push({
|
|
248
|
+
type: 'missing-import',
|
|
249
|
+
severity: 'critical',
|
|
250
|
+
file: imp.from,
|
|
251
|
+
symbol: imp.symbols.join(', '),
|
|
252
|
+
detail: `Import çözümlenemedi: '${imp.to}' (${imp.from}:${imp.line})`,
|
|
253
|
+
suggestion: `Dosya mevcut mu? Yol doğru mu? Extension .js ekli mi?`,
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
return issues;
|
|
258
|
+
}
|
|
259
|
+
findCircularDeps(edges) {
|
|
260
|
+
const issues = [];
|
|
261
|
+
const adjList = new Map();
|
|
262
|
+
for (const edge of edges) {
|
|
263
|
+
if (!adjList.has(edge.source))
|
|
264
|
+
adjList.set(edge.source, []);
|
|
265
|
+
adjList.get(edge.source).push(edge.target);
|
|
266
|
+
}
|
|
267
|
+
const visited = new Set();
|
|
268
|
+
const inStack = new Set();
|
|
269
|
+
const path = [];
|
|
270
|
+
const dfs = (node) => {
|
|
271
|
+
if (inStack.has(node)) {
|
|
272
|
+
// Döngü bulundu — path'ten cycle'ı çıkar
|
|
273
|
+
const cycleStart = path.indexOf(node);
|
|
274
|
+
const cycle = path.slice(cycleStart);
|
|
275
|
+
cycle.push(node);
|
|
276
|
+
issues.push({
|
|
277
|
+
type: 'circular-dep',
|
|
278
|
+
severity: 'warning',
|
|
279
|
+
file: node,
|
|
280
|
+
detail: `Döngüsel bağımlılık: ${cycle.join(' → ')}`,
|
|
281
|
+
suggestion: `Bağımlılık yönünü değiştir veya ortak bir interface modülüne çıkar`,
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (visited.has(node))
|
|
286
|
+
return;
|
|
287
|
+
visited.add(node);
|
|
288
|
+
inStack.add(node);
|
|
289
|
+
path.push(node);
|
|
290
|
+
for (const neighbor of adjList.get(node) ?? []) {
|
|
291
|
+
dfs(neighbor);
|
|
292
|
+
}
|
|
293
|
+
path.pop();
|
|
294
|
+
inStack.delete(node);
|
|
295
|
+
};
|
|
296
|
+
for (const node of adjList.keys()) {
|
|
297
|
+
dfs(node);
|
|
298
|
+
}
|
|
299
|
+
return issues;
|
|
300
|
+
}
|
|
301
|
+
async findPhantomDeps(imports) {
|
|
302
|
+
const issues = [];
|
|
303
|
+
// package.json oku
|
|
304
|
+
let pkgDeps;
|
|
305
|
+
try {
|
|
306
|
+
const pkg = JSON.parse(await readFile(join(this.projectRoot, 'package.json'), 'utf-8'));
|
|
307
|
+
pkgDeps = new Set([
|
|
308
|
+
...Object.keys(pkg.dependencies ?? {}),
|
|
309
|
+
...Object.keys(pkg.devDependencies ?? {}),
|
|
310
|
+
...Object.keys(pkg.peerDependencies ?? {}),
|
|
311
|
+
]);
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
return issues; // package.json yoksa kontrol etme
|
|
315
|
+
}
|
|
316
|
+
// External import'ları topla
|
|
317
|
+
const externalImports = new Set();
|
|
318
|
+
for (const imp of imports) {
|
|
319
|
+
if (imp.to.startsWith('.') || imp.to.startsWith('node:'))
|
|
320
|
+
continue;
|
|
321
|
+
// Scoped package: @scope/name → @scope/name
|
|
322
|
+
const pkgName = imp.to.startsWith('@')
|
|
323
|
+
? imp.to.split('/').slice(0, 2).join('/')
|
|
324
|
+
: imp.to.split('/')[0];
|
|
325
|
+
externalImports.add(pkgName);
|
|
326
|
+
}
|
|
327
|
+
for (const pkg of externalImports) {
|
|
328
|
+
// Node built-in modüllerini atla
|
|
329
|
+
if (this.isNodeBuiltin(pkg))
|
|
330
|
+
continue;
|
|
331
|
+
if (!pkgDeps.has(pkg)) {
|
|
332
|
+
issues.push({
|
|
333
|
+
type: 'phantom-dep',
|
|
334
|
+
severity: 'critical',
|
|
335
|
+
file: 'package.json',
|
|
336
|
+
symbol: pkg,
|
|
337
|
+
detail: `'${pkg}' import ediliyor ama package.json'da dependency olarak tanımlı değil`,
|
|
338
|
+
suggestion: `npm install ${pkg} veya import'u kaldır`,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
// Kullanılmayan dependency'ler (ters yön)
|
|
343
|
+
for (const dep of pkgDeps) {
|
|
344
|
+
if (dep.startsWith('@types/'))
|
|
345
|
+
continue; // type packages
|
|
346
|
+
if (!externalImports.has(dep)) {
|
|
347
|
+
issues.push({
|
|
348
|
+
type: 'unused-dep',
|
|
349
|
+
severity: 'info',
|
|
350
|
+
file: 'package.json',
|
|
351
|
+
symbol: dep,
|
|
352
|
+
detail: `'${dep}' package.json'da var ama hiçbir dosyada import edilmiyor`,
|
|
353
|
+
suggestion: `npm uninstall ${dep} (veya dolaylı kullanılıyorsa ignore et)`,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return issues;
|
|
358
|
+
}
|
|
359
|
+
// ── Helpers ────────────────────────────────────────────────
|
|
360
|
+
async collectSourceFiles() {
|
|
361
|
+
const files = [];
|
|
362
|
+
await this.walk(this.projectRoot, files);
|
|
363
|
+
return files;
|
|
364
|
+
}
|
|
365
|
+
async walk(dir, files) {
|
|
366
|
+
let entries;
|
|
367
|
+
try {
|
|
368
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
369
|
+
}
|
|
370
|
+
catch {
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
for (const entry of entries) {
|
|
374
|
+
if (entry.isDirectory()) {
|
|
375
|
+
if (SKIP_DIRS.has(entry.name) || entry.name.startsWith('.'))
|
|
376
|
+
continue;
|
|
377
|
+
await this.walk(join(dir, entry.name), files);
|
|
378
|
+
}
|
|
379
|
+
else if (entry.isFile()) {
|
|
380
|
+
const ext = extname(entry.name).toLowerCase();
|
|
381
|
+
if (SOURCE_EXTS.has(ext)) {
|
|
382
|
+
files.push(relative(this.projectRoot, join(dir, entry.name)).replace(/\\/g, '/'));
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
/**
|
|
388
|
+
* Relative import'u dosya yoluna çözümle.
|
|
389
|
+
* './foo.js' → 'src/foo.ts' (TypeScript .js → .ts mapping dahil)
|
|
390
|
+
*/
|
|
391
|
+
resolveImportPath(fromFile, importPath, fileSet) {
|
|
392
|
+
if (!importPath.startsWith('.'))
|
|
393
|
+
return null; // external
|
|
394
|
+
const fromDir = dirname(fromFile);
|
|
395
|
+
const rawResolved = join(fromDir, importPath).replace(/\\/g, '/');
|
|
396
|
+
// Aday uzantılar: olduğu gibi, .ts, .tsx, .js→.ts, /index.ts
|
|
397
|
+
const candidates = [
|
|
398
|
+
rawResolved,
|
|
399
|
+
rawResolved.replace(/\.js$/, '.ts'),
|
|
400
|
+
rawResolved.replace(/\.js$/, '.tsx'),
|
|
401
|
+
rawResolved.replace(/\.jsx$/, '.tsx'),
|
|
402
|
+
`${rawResolved}.ts`,
|
|
403
|
+
`${rawResolved}.tsx`,
|
|
404
|
+
`${rawResolved}/index.ts`,
|
|
405
|
+
`${rawResolved}/index.tsx`,
|
|
406
|
+
`${rawResolved}/index.js`,
|
|
407
|
+
];
|
|
408
|
+
for (const candidate of candidates) {
|
|
409
|
+
if (fileSet.has(candidate))
|
|
410
|
+
return candidate;
|
|
411
|
+
}
|
|
412
|
+
return null;
|
|
413
|
+
}
|
|
414
|
+
isEntryPoint(file) {
|
|
415
|
+
return /^(src\/)?index\.ts$/.test(file) ||
|
|
416
|
+
file.includes('bin/') ||
|
|
417
|
+
file.endsWith('.test.ts') ||
|
|
418
|
+
file.endsWith('.spec.ts');
|
|
419
|
+
}
|
|
420
|
+
isNodeBuiltin(name) {
|
|
421
|
+
const builtins = new Set([
|
|
422
|
+
'assert', 'buffer', 'child_process', 'cluster', 'console', 'constants',
|
|
423
|
+
'crypto', 'dgram', 'dns', 'domain', 'events', 'fs', 'http', 'https',
|
|
424
|
+
'module', 'net', 'os', 'path', 'perf_hooks', 'process', 'punycode',
|
|
425
|
+
'querystring', 'readline', 'repl', 'stream', 'string_decoder', 'sys',
|
|
426
|
+
'timers', 'tls', 'tty', 'url', 'util', 'v8', 'vm', 'worker_threads', 'zlib',
|
|
427
|
+
]);
|
|
428
|
+
return builtins.has(name);
|
|
429
|
+
}
|
|
430
|
+
findLineNumber(lines, charIndex) {
|
|
431
|
+
let count = 0;
|
|
432
|
+
for (let i = 0; i < lines.length; i++) {
|
|
433
|
+
count += lines[i].length + 1; // +1 for \n
|
|
434
|
+
if (count > charIndex)
|
|
435
|
+
return i + 1;
|
|
436
|
+
}
|
|
437
|
+
return lines.length;
|
|
438
|
+
}
|
|
439
|
+
inferExportKind(match) {
|
|
440
|
+
if (match.includes('function'))
|
|
441
|
+
return 'function';
|
|
442
|
+
if (match.includes('class'))
|
|
443
|
+
return 'class';
|
|
444
|
+
if (match.includes('type '))
|
|
445
|
+
return 'type';
|
|
446
|
+
if (match.includes('interface'))
|
|
447
|
+
return 'interface';
|
|
448
|
+
if (match.includes('enum'))
|
|
449
|
+
return 'enum';
|
|
450
|
+
return 'const';
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
//# sourceMappingURL=static-analyzer.js.map
|