fcis 0.1.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/.plans/001-fcis-analyzer.md +832 -0
- package/.plans/002-fcis-analyzer-improvements.md +205 -0
- package/README.md +272 -0
- package/TECHNICAL.md +386 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +1836 -0
- package/dist/cli.js.map +1 -0
- package/dist/index.d.ts +709 -0
- package/dist/index.js +1845 -0
- package/dist/index.js.map +1 -0
- package/package.json +47 -0
- package/pnpm-workspace.yaml +0 -0
- package/src/analyzer.ts +266 -0
- package/src/classification/classifier.ts +156 -0
- package/src/classification/derive-status.ts +171 -0
- package/src/classification/quality-scorer.ts +481 -0
- package/src/cli.ts +286 -0
- package/src/detection/detect-markers.ts +480 -0
- package/src/detection/markers.ts +332 -0
- package/src/extraction/extract-functions.ts +570 -0
- package/src/extraction/extractor.ts +188 -0
- package/src/index.ts +111 -0
- package/src/reporting/report-console.ts +416 -0
- package/src/reporting/report-json.ts +232 -0
- package/src/scoring/scorer.ts +504 -0
- package/src/types.ts +248 -0
- package/tests/classifier.test.ts +480 -0
- package/tests/derive-status.test.ts +464 -0
- package/tests/detect-markers.test.ts +639 -0
- package/tests/extractor.test.ts +155 -0
- package/tests/integration.test.ts +706 -0
- package/tests/quality-scorer.test.ts +650 -0
- package/tests/scorer.test.ts +768 -0
- package/tsconfig.json +34 -0
- package/tsup.config.ts +17 -0
- package/vendor/ts-morph/.editorconfig +10 -0
- package/vendor/ts-morph/.gitattributes +11 -0
- package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
- package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
- package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
- package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
- package/vendor/ts-morph/.vscode/settings.json +10 -0
- package/vendor/ts-morph/CONTRIBUTING.md +23 -0
- package/vendor/ts-morph/DEVELOPMENT.md +32 -0
- package/vendor/ts-morph/LICENSE +21 -0
- package/vendor/ts-morph/deno.json +8 -0
- package/vendor/ts-morph/deno.lock +1233 -0
- package/vendor/ts-morph/docs/CNAME +1 -0
- package/vendor/ts-morph/docs/Gemfile +2 -0
- package/vendor/ts-morph/docs/_config.yml +5 -0
- package/vendor/ts-morph/docs/_layouts/default.html +159 -0
- package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
- package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
- package/vendor/ts-morph/docs/details/ambient.md +38 -0
- package/vendor/ts-morph/docs/details/async.md +31 -0
- package/vendor/ts-morph/docs/details/classes.md +314 -0
- package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
- package/vendor/ts-morph/docs/details/comments.md +122 -0
- package/vendor/ts-morph/docs/details/decorators.md +119 -0
- package/vendor/ts-morph/docs/details/documentation.md +73 -0
- package/vendor/ts-morph/docs/details/enums.md +117 -0
- package/vendor/ts-morph/docs/details/exports.md +308 -0
- package/vendor/ts-morph/docs/details/expressions.md +46 -0
- package/vendor/ts-morph/docs/details/functions.md +150 -0
- package/vendor/ts-morph/docs/details/generators.md +27 -0
- package/vendor/ts-morph/docs/details/identifiers.md +79 -0
- package/vendor/ts-morph/docs/details/imports.md +191 -0
- package/vendor/ts-morph/docs/details/index.md +52 -0
- package/vendor/ts-morph/docs/details/initializers.md +40 -0
- package/vendor/ts-morph/docs/details/interfaces.md +218 -0
- package/vendor/ts-morph/docs/details/literals.md +20 -0
- package/vendor/ts-morph/docs/details/modifiers.md +38 -0
- package/vendor/ts-morph/docs/details/modules.md +113 -0
- package/vendor/ts-morph/docs/details/namespaces.md +7 -0
- package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
- package/vendor/ts-morph/docs/details/parameters.md +64 -0
- package/vendor/ts-morph/docs/details/signatures.md +41 -0
- package/vendor/ts-morph/docs/details/source-files.md +292 -0
- package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
- package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
- package/vendor/ts-morph/docs/details/types.md +254 -0
- package/vendor/ts-morph/docs/details/variables.md +110 -0
- package/vendor/ts-morph/docs/emitting.md +151 -0
- package/vendor/ts-morph/docs/index.md +25 -0
- package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
- package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
- package/vendor/ts-morph/docs/manipulation/index.md +136 -0
- package/vendor/ts-morph/docs/manipulation/order.md +14 -0
- package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
- package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
- package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
- package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
- package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
- package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
- package/vendor/ts-morph/docs/metrics/performance.json +4 -0
- package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
- package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
- package/vendor/ts-morph/docs/navigation/directories.md +287 -0
- package/vendor/ts-morph/docs/navigation/example.md +50 -0
- package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
- package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
- package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
- package/vendor/ts-morph/docs/navigation/index.md +94 -0
- package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
- package/vendor/ts-morph/docs/navigation/program.md +25 -0
- package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
- package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
- package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
- package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
- package/vendor/ts-morph/docs/setup/file-system.md +106 -0
- package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
- package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
- package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
- package/vendor/ts-morph/docs/setup/index.md +94 -0
- package/vendor/ts-morph/docs/utilities.md +55 -0
- package/vendor/ts-morph/dprint.json +23 -0
- package/vendor/ts-morph/package.json +30 -0
- package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
- package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
- package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
- package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
- package/vendor/ts-morph/packages/common/LICENSE +21 -0
- package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
- package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
- package/vendor/ts-morph/packages/common/package.json +65 -0
- package/vendor/ts-morph/packages/common/readme.md +5 -0
- package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
- package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
- package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
- package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
- package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
- package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
- package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
- package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
- package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
- package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
- package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
- package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
- package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
- package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
- package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
- package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
- package/vendor/ts-morph/readme.md +14 -0
- package/vendor/ts-morph/rfcs/README.md +13 -0
- package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
- package/vendor/ts-morph/tsconfig.common.json +17 -0
- package/vitest.config.ts +16 -0
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
import { SourceFile, Project } from 'ts-morph';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Core type definitions for the FCIS Analyzer
|
|
5
|
+
*
|
|
6
|
+
* These types form the contract between the analyzer's layers:
|
|
7
|
+
* - Extraction layer produces ExtractedFunction and FileImports
|
|
8
|
+
* - Detection layer produces ImpurityMarker[]
|
|
9
|
+
* - Classification layer produces ClassifiedFunction
|
|
10
|
+
* - Scoring layer produces FileScore, DirectoryScore, ProjectScore
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* CallSite captures position and context for each call expression,
|
|
14
|
+
* enabling sequence analysis (GATHER→DECIDE→EXECUTE detection)
|
|
15
|
+
*/
|
|
16
|
+
type CallSite = {
|
|
17
|
+
expression: string;
|
|
18
|
+
line: number;
|
|
19
|
+
isAwaited: boolean;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Represents a function extracted from source code
|
|
23
|
+
*/
|
|
24
|
+
type ExtractedFunction = {
|
|
25
|
+
name: string | null;
|
|
26
|
+
filePath: string;
|
|
27
|
+
startLine: number;
|
|
28
|
+
endLine: number;
|
|
29
|
+
isAsync: boolean;
|
|
30
|
+
isExported: boolean;
|
|
31
|
+
bodyLineCount: number;
|
|
32
|
+
statementCount: number;
|
|
33
|
+
hasConditionals: boolean;
|
|
34
|
+
parentContext: string | null;
|
|
35
|
+
callSites: CallSite[];
|
|
36
|
+
hasAwait: boolean;
|
|
37
|
+
propertyAccessChains: string[];
|
|
38
|
+
kind: 'function' | 'method' | 'arrow' | 'function-expression' | 'getter' | 'setter';
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* Represents imports in a file
|
|
42
|
+
*/
|
|
43
|
+
type FileImports = {
|
|
44
|
+
filePath: string;
|
|
45
|
+
imports: {
|
|
46
|
+
moduleSpecifier: string;
|
|
47
|
+
namedImports: string[];
|
|
48
|
+
}[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* Strict marker type union — prevents typos, enables exhaustive matching
|
|
52
|
+
* Note: 'async-function' removed — async alone is not an impurity marker
|
|
53
|
+
* Note: React hook markers deferred to v2
|
|
54
|
+
*/
|
|
55
|
+
type MarkerType = 'await-expression' | 'database-call' | 'network-fetch' | 'network-http' | 'fs-import' | 'fs-call' | 'env-access' | 'console-log' | 'logging' | 'telemetry' | 'queue-enqueue' | 'event-emit';
|
|
56
|
+
/**
|
|
57
|
+
* Represents a detected impurity marker in a function
|
|
58
|
+
*/
|
|
59
|
+
type ImpurityMarker = {
|
|
60
|
+
type: MarkerType;
|
|
61
|
+
detail: string;
|
|
62
|
+
line?: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Binary classification — objective, no heuristics
|
|
66
|
+
*/
|
|
67
|
+
type FunctionClassification = 'pure' | 'impure';
|
|
68
|
+
/**
|
|
69
|
+
* Derived status for UX — actionable guidance
|
|
70
|
+
*/
|
|
71
|
+
type Status = 'ok' | 'review' | 'refactor';
|
|
72
|
+
/**
|
|
73
|
+
* A function that has been classified with markers, classification, quality score, and status
|
|
74
|
+
*/
|
|
75
|
+
type ClassifiedFunction = ExtractedFunction & {
|
|
76
|
+
markers: ImpurityMarker[];
|
|
77
|
+
classification: FunctionClassification;
|
|
78
|
+
qualityScore: number | null;
|
|
79
|
+
status: Status;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* Breakdown of function statuses
|
|
83
|
+
*/
|
|
84
|
+
type StatusBreakdown = {
|
|
85
|
+
ok: number;
|
|
86
|
+
review: number;
|
|
87
|
+
refactor: number;
|
|
88
|
+
};
|
|
89
|
+
/**
|
|
90
|
+
* Refactoring candidate summary for reporting
|
|
91
|
+
*/
|
|
92
|
+
type RefactoringCandidate = {
|
|
93
|
+
name: string | null;
|
|
94
|
+
filePath: string;
|
|
95
|
+
startLine: number;
|
|
96
|
+
bodyLineCount: number;
|
|
97
|
+
qualityScore: number;
|
|
98
|
+
markers: MarkerType[];
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
* Score for a single file
|
|
102
|
+
*/
|
|
103
|
+
type FileScore = {
|
|
104
|
+
filePath: string;
|
|
105
|
+
purity: number;
|
|
106
|
+
impurityQuality: number | null;
|
|
107
|
+
health: number;
|
|
108
|
+
pureCount: number;
|
|
109
|
+
impureCount: number;
|
|
110
|
+
excludedCount: number;
|
|
111
|
+
statusBreakdown: StatusBreakdown;
|
|
112
|
+
pureLineCount: number;
|
|
113
|
+
impureLineCount: number;
|
|
114
|
+
functions: ClassifiedFunction[];
|
|
115
|
+
refactoringCandidates: Omit<RefactoringCandidate, 'filePath'>[];
|
|
116
|
+
allExcluded?: boolean;
|
|
117
|
+
typeOnly?: boolean;
|
|
118
|
+
};
|
|
119
|
+
/**
|
|
120
|
+
* Score for a directory
|
|
121
|
+
*/
|
|
122
|
+
type DirectoryScore = {
|
|
123
|
+
dirPath: string;
|
|
124
|
+
purity: number;
|
|
125
|
+
impurityQuality: number | null;
|
|
126
|
+
health: number;
|
|
127
|
+
pureCount: number;
|
|
128
|
+
impureCount: number;
|
|
129
|
+
excludedCount: number;
|
|
130
|
+
statusBreakdown: StatusBreakdown;
|
|
131
|
+
pureLineCount: number;
|
|
132
|
+
impureLineCount: number;
|
|
133
|
+
fileScores: FileScore[];
|
|
134
|
+
};
|
|
135
|
+
/**
|
|
136
|
+
* Project-level score (top-level output)
|
|
137
|
+
*/
|
|
138
|
+
type ProjectScore = {
|
|
139
|
+
purity: number;
|
|
140
|
+
impurityQuality: number | null;
|
|
141
|
+
health: number;
|
|
142
|
+
pureCount: number;
|
|
143
|
+
impureCount: number;
|
|
144
|
+
excludedCount: number;
|
|
145
|
+
statusBreakdown: StatusBreakdown;
|
|
146
|
+
pureLineCount: number;
|
|
147
|
+
impureLineCount: number;
|
|
148
|
+
directoryScores: DirectoryScore[];
|
|
149
|
+
timestamp: string;
|
|
150
|
+
commitHash: string | null;
|
|
151
|
+
refactoringCandidates: RefactoringCandidate[];
|
|
152
|
+
allExcluded?: boolean;
|
|
153
|
+
subset?: boolean;
|
|
154
|
+
filesGlob?: string;
|
|
155
|
+
errors?: AnalysisError[];
|
|
156
|
+
};
|
|
157
|
+
/**
|
|
158
|
+
* Error encountered during analysis
|
|
159
|
+
*/
|
|
160
|
+
type AnalysisError = {
|
|
161
|
+
filePath: string;
|
|
162
|
+
error: string;
|
|
163
|
+
};
|
|
164
|
+
/**
|
|
165
|
+
* Configuration options for the analyzer
|
|
166
|
+
*/
|
|
167
|
+
type AnalyzerConfig = {
|
|
168
|
+
tsconfigPath: string;
|
|
169
|
+
filesGlob?: string;
|
|
170
|
+
minHealth?: number;
|
|
171
|
+
minPurity?: number;
|
|
172
|
+
minQuality?: number;
|
|
173
|
+
format?: 'console' | 'json' | 'summary';
|
|
174
|
+
outputPath?: string;
|
|
175
|
+
quiet?: boolean;
|
|
176
|
+
verbose?: boolean;
|
|
177
|
+
};
|
|
178
|
+
/**
|
|
179
|
+
* Quality scoring thresholds (configurable)
|
|
180
|
+
*/
|
|
181
|
+
type QualityThresholds = {
|
|
182
|
+
okThreshold: number;
|
|
183
|
+
reviewThreshold: number;
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* Default quality thresholds
|
|
187
|
+
*/
|
|
188
|
+
declare const DEFAULT_QUALITY_THRESHOLDS: QualityThresholds;
|
|
189
|
+
/**
|
|
190
|
+
* Result of extracting functions from a file
|
|
191
|
+
*/
|
|
192
|
+
type ExtractionResult = {
|
|
193
|
+
filePath: string;
|
|
194
|
+
functions: ExtractedFunction[];
|
|
195
|
+
imports: FileImports;
|
|
196
|
+
isTypeOnly: boolean;
|
|
197
|
+
error?: string;
|
|
198
|
+
};
|
|
199
|
+
/**
|
|
200
|
+
* Detection context passed to marker detection
|
|
201
|
+
*/
|
|
202
|
+
type DetectionContext = {
|
|
203
|
+
imports: FileImports;
|
|
204
|
+
pureFileImports: Set<string>;
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Analyzer - Main Orchestration Layer (Shell)
|
|
209
|
+
*
|
|
210
|
+
* This module orchestrates the full analysis pipeline:
|
|
211
|
+
* 1. Load project via ts-morph
|
|
212
|
+
* 2. Extract functions from source files
|
|
213
|
+
* 3. Detect impurity markers
|
|
214
|
+
* 4. Classify functions
|
|
215
|
+
* 5. Compute quality scores
|
|
216
|
+
* 6. Aggregate into file, directory, and project scores
|
|
217
|
+
*
|
|
218
|
+
* This is a SHELL module - it coordinates I/O and calls pure functions.
|
|
219
|
+
*/
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Run the full analysis pipeline
|
|
223
|
+
*
|
|
224
|
+
* @param config - Analyzer configuration
|
|
225
|
+
* @returns Project score with all metrics
|
|
226
|
+
*/
|
|
227
|
+
declare function analyze(config: AnalyzerConfig): Promise<ProjectScore>;
|
|
228
|
+
/**
|
|
229
|
+
* Analyze a single source file
|
|
230
|
+
*
|
|
231
|
+
* @param sourceFile - The ts-morph source file to analyze
|
|
232
|
+
* @returns FileScore with all metrics
|
|
233
|
+
*/
|
|
234
|
+
declare function analyzeFile(sourceFile: SourceFile): FileScore;
|
|
235
|
+
/**
|
|
236
|
+
* Analyze a single file from a file path (convenience function)
|
|
237
|
+
*
|
|
238
|
+
* @param tsconfigPath - Path to tsconfig.json
|
|
239
|
+
* @param filePath - Path to the file to analyze
|
|
240
|
+
* @returns FileScore with all metrics
|
|
241
|
+
*/
|
|
242
|
+
declare function analyzeFilePath(tsconfigPath: string, filePath: string): Promise<FileScore | null>;
|
|
243
|
+
/**
|
|
244
|
+
* Check if analysis passes thresholds
|
|
245
|
+
*
|
|
246
|
+
* @param score - Project score to check
|
|
247
|
+
* @param config - Configuration with threshold values
|
|
248
|
+
* @returns Object indicating pass/fail status and messages
|
|
249
|
+
*/
|
|
250
|
+
declare function checkThresholds(score: ProjectScore, config: Pick<AnalyzerConfig, 'minHealth' | 'minPurity' | 'minQuality'>): {
|
|
251
|
+
passed: boolean;
|
|
252
|
+
failures: string[];
|
|
253
|
+
};
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Detect all impurity markers in a function
|
|
257
|
+
*
|
|
258
|
+
* @param fn - The extracted function to analyze
|
|
259
|
+
* @param context - Detection context including imports
|
|
260
|
+
* @returns Array of detected impurity markers
|
|
261
|
+
*/
|
|
262
|
+
declare function detectMarkers(fn: ExtractedFunction, context: DetectionContext): ImpurityMarker[];
|
|
263
|
+
/**
|
|
264
|
+
* Create detection context from file imports
|
|
265
|
+
*/
|
|
266
|
+
declare function createDetectionContext(imports: FileImports): DetectionContext;
|
|
267
|
+
/**
|
|
268
|
+
* Get the names of functions imported from .pure files
|
|
269
|
+
*/
|
|
270
|
+
declare function getPureImportedFunctions(context: DetectionContext): Set<string>;
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Marker Definitions and Catalog
|
|
274
|
+
*
|
|
275
|
+
* This module defines the impurity marker types and provides a catalog
|
|
276
|
+
* of patterns for detecting each marker type. This is PURE code - all
|
|
277
|
+
* functions take data in and return data out with no I/O.
|
|
278
|
+
*
|
|
279
|
+
* The marker catalog is defined as a data structure to enable:
|
|
280
|
+
* - Extensibility (custom markers can be added)
|
|
281
|
+
* - Documentation (each marker has a description)
|
|
282
|
+
* - Testing (patterns can be validated in isolation)
|
|
283
|
+
*/
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Marker definition with detection patterns
|
|
287
|
+
*/
|
|
288
|
+
type MarkerDefinition = {
|
|
289
|
+
type: MarkerType;
|
|
290
|
+
description: string;
|
|
291
|
+
category: 'io' | 'state' | 'side-effect' | 'environment';
|
|
292
|
+
severity: 'high' | 'medium' | 'low';
|
|
293
|
+
};
|
|
294
|
+
/**
|
|
295
|
+
* Marker catalog - all recognized impurity markers
|
|
296
|
+
*/
|
|
297
|
+
declare const MARKER_CATALOG: Record<MarkerType, MarkerDefinition>;
|
|
298
|
+
/**
|
|
299
|
+
* Get marker definition by type
|
|
300
|
+
*/
|
|
301
|
+
declare function getMarkerDefinition(type: MarkerType): MarkerDefinition;
|
|
302
|
+
/**
|
|
303
|
+
* Get all marker types
|
|
304
|
+
*/
|
|
305
|
+
declare function getAllMarkerTypes(): MarkerType[];
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Function Classifier - Pure Core
|
|
309
|
+
*
|
|
310
|
+
* Classifies functions as pure or impure based on their markers.
|
|
311
|
+
* This is a PURE module - all functions take data in and return data out.
|
|
312
|
+
*
|
|
313
|
+
* Classification is binary and objective:
|
|
314
|
+
* - Pure: Zero I/O markers
|
|
315
|
+
* - Impure: Has one or more I/O markers
|
|
316
|
+
*
|
|
317
|
+
* Note: `async` without `await` (and without other I/O markers) is still pure.
|
|
318
|
+
*/
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Classify a function based on its markers
|
|
322
|
+
*
|
|
323
|
+
* @param fn - The extracted function
|
|
324
|
+
* @param markers - Detected impurity markers
|
|
325
|
+
* @returns The function classification ('pure' or 'impure')
|
|
326
|
+
*/
|
|
327
|
+
declare function classifyFunction(_fn: ExtractedFunction, markers: ImpurityMarker[]): FunctionClassification;
|
|
328
|
+
/**
|
|
329
|
+
* Check if a function should be excluded from scoring
|
|
330
|
+
*
|
|
331
|
+
* Excluded functions:
|
|
332
|
+
* - Trivial functions (< 3 statements and no conditionals)
|
|
333
|
+
* - Test files (should already be filtered at extraction)
|
|
334
|
+
* - Type-only files (should already be filtered at extraction)
|
|
335
|
+
*/
|
|
336
|
+
declare function shouldExcludeFunction(fn: ExtractedFunction): boolean;
|
|
337
|
+
/**
|
|
338
|
+
* Create a fully classified function with all computed properties
|
|
339
|
+
*
|
|
340
|
+
* @param fn - The extracted function
|
|
341
|
+
* @param markers - Detected impurity markers
|
|
342
|
+
* @param qualityScore - Quality score (null for pure functions)
|
|
343
|
+
* @param status - Derived status
|
|
344
|
+
* @returns A fully classified function
|
|
345
|
+
*/
|
|
346
|
+
declare function createClassifiedFunction(fn: ExtractedFunction, markers: ImpurityMarker[], qualityScore: number | null, status: Status): ClassifiedFunction;
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Quality Scorer - Pure Core
|
|
350
|
+
*
|
|
351
|
+
* Computes quality scores (0-100) for impure functions measuring how
|
|
352
|
+
* well-structured the impurity is. This is a PURE module - all functions
|
|
353
|
+
* take data in and return data out with no I/O.
|
|
354
|
+
*
|
|
355
|
+
* Quality signals measure structural quality, not whether impurity exists.
|
|
356
|
+
* A high score means the impure function is well-organized and maintainable.
|
|
357
|
+
*/
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Quality scoring weights and thresholds
|
|
361
|
+
*/
|
|
362
|
+
declare const QUALITY_WEIGHTS: {
|
|
363
|
+
readonly callsPureFile: 30;
|
|
364
|
+
readonly callsPureNamingConvention: 20;
|
|
365
|
+
readonly ioConcentratedAtStart: 15;
|
|
366
|
+
readonly ioConcentratedAtEnd: 15;
|
|
367
|
+
readonly lowComplexity: 10;
|
|
368
|
+
readonly shellNamingConvention: 5;
|
|
369
|
+
readonly callsPredicateFunctions: 5;
|
|
370
|
+
readonly ioInterleaved: -20;
|
|
371
|
+
readonly highComplexity: -15;
|
|
372
|
+
readonly multipleIoTypes: -10;
|
|
373
|
+
readonly noPureFunctionCalls: -10;
|
|
374
|
+
readonly veryLongFunction: -10;
|
|
375
|
+
};
|
|
376
|
+
/**
|
|
377
|
+
* Compute quality score for an impure function
|
|
378
|
+
*
|
|
379
|
+
* @param fn - The extracted function data
|
|
380
|
+
* @param markers - The detected impurity markers
|
|
381
|
+
* @param context - Detection context with import information
|
|
382
|
+
* @returns Quality score from 0-100
|
|
383
|
+
*/
|
|
384
|
+
declare function computeQualityScore(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): number;
|
|
385
|
+
/**
|
|
386
|
+
* Analysis result for a function
|
|
387
|
+
*/
|
|
388
|
+
type FunctionAnalysis = {
|
|
389
|
+
callsPureFile: boolean;
|
|
390
|
+
callsPureNamingConvention: boolean;
|
|
391
|
+
ioConcentratedAtStart: boolean;
|
|
392
|
+
ioConcentratedAtEnd: boolean;
|
|
393
|
+
lowComplexity: boolean;
|
|
394
|
+
shellNamingConvention: boolean;
|
|
395
|
+
callsPredicateFunctions: boolean;
|
|
396
|
+
ioInterleaved: boolean;
|
|
397
|
+
highComplexity: boolean;
|
|
398
|
+
multipleIoTypes: boolean;
|
|
399
|
+
noPureFunctionCalls: boolean;
|
|
400
|
+
veryLongFunction: boolean;
|
|
401
|
+
estimatedComplexity: number;
|
|
402
|
+
ioMarkerCount: number;
|
|
403
|
+
uniqueIoTypes: Set<MarkerType>;
|
|
404
|
+
pureCallCount: number;
|
|
405
|
+
predicateCallCount: number;
|
|
406
|
+
};
|
|
407
|
+
/**
|
|
408
|
+
* Analyze a function for quality signals
|
|
409
|
+
*/
|
|
410
|
+
declare function analyzeFunction(fn: ExtractedFunction, markers: ImpurityMarker[], context: DetectionContext): FunctionAnalysis;
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Status Derivation - Pure Core
|
|
414
|
+
*
|
|
415
|
+
* Derives actionable status from classification and quality score.
|
|
416
|
+
* This is a PURE module - all functions take data in and return data out with no I/O.
|
|
417
|
+
*
|
|
418
|
+
* Status provides developer guidance:
|
|
419
|
+
* - ok: No action needed (pure function, or well-structured impure function)
|
|
420
|
+
* - review: Consider improving if touching this code
|
|
421
|
+
* - refactor: Tangled code, prioritize cleanup
|
|
422
|
+
*/
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Derive status from classification and quality score
|
|
426
|
+
*
|
|
427
|
+
* Status rules:
|
|
428
|
+
* - Pure functions always get 'ok' (testable without mocks)
|
|
429
|
+
* - Impure functions with quality >= okThreshold (default 70) get 'ok'
|
|
430
|
+
* - Impure functions with quality >= reviewThreshold (default 40) get 'review'
|
|
431
|
+
* - Impure functions with quality < reviewThreshold get 'refactor'
|
|
432
|
+
*
|
|
433
|
+
* @param classification - 'pure' or 'impure'
|
|
434
|
+
* @param qualityScore - Quality score for impure functions (0-100), null for pure
|
|
435
|
+
* @param thresholds - Optional custom thresholds
|
|
436
|
+
* @returns Status: 'ok' | 'review' | 'refactor'
|
|
437
|
+
*/
|
|
438
|
+
declare function deriveStatus(classification: FunctionClassification, qualityScore: number | null, thresholds?: QualityThresholds): Status;
|
|
439
|
+
/**
|
|
440
|
+
* Get status description for display
|
|
441
|
+
*/
|
|
442
|
+
declare function getStatusDescription(status: Status): string;
|
|
443
|
+
/**
|
|
444
|
+
* Get status emoji for display
|
|
445
|
+
*/
|
|
446
|
+
declare function getStatusEmoji(status: Status): string;
|
|
447
|
+
/**
|
|
448
|
+
* Get status color for terminal display
|
|
449
|
+
*/
|
|
450
|
+
declare function getStatusColor(status: Status): 'green' | 'yellow' | 'red';
|
|
451
|
+
|
|
452
|
+
/**
|
|
453
|
+
* Scoring Engine - Pure Core
|
|
454
|
+
*
|
|
455
|
+
* Aggregates classification and quality scores into metrics at
|
|
456
|
+
* file, directory, and project levels. This is a PURE module -
|
|
457
|
+
* all functions take data in and return data out with no I/O.
|
|
458
|
+
*
|
|
459
|
+
* Metrics:
|
|
460
|
+
* - Purity: percentage of pure functions
|
|
461
|
+
* - Impurity Quality: average quality score of impure functions
|
|
462
|
+
* - Health: percentage of functions with status 'ok'
|
|
463
|
+
*/
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Score a single file based on its classified functions
|
|
467
|
+
*
|
|
468
|
+
* @param filePath - Path to the file
|
|
469
|
+
* @param functions - Classified functions from the file
|
|
470
|
+
* @param isTypeOnly - Whether the file contains only types
|
|
471
|
+
* @returns FileScore with all computed metrics
|
|
472
|
+
*/
|
|
473
|
+
declare function scoreFile(filePath: string, functions: ClassifiedFunction[], isTypeOnly?: boolean): FileScore;
|
|
474
|
+
/**
|
|
475
|
+
* Score a directory by aggregating its file scores
|
|
476
|
+
*
|
|
477
|
+
* @param dirPath - Path to the directory
|
|
478
|
+
* @param fileScores - Scores for all files in the directory
|
|
479
|
+
* @returns DirectoryScore with weighted averages
|
|
480
|
+
*/
|
|
481
|
+
declare function scoreDirectory(dirPath: string, fileScores: FileScore[]): DirectoryScore;
|
|
482
|
+
/**
|
|
483
|
+
* Score an entire project by aggregating directory scores
|
|
484
|
+
*
|
|
485
|
+
* @param directoryScores - Scores for all directories in the project
|
|
486
|
+
* @param options - Additional options for the project score
|
|
487
|
+
* @returns ProjectScore with all metrics
|
|
488
|
+
*/
|
|
489
|
+
declare function scoreProject(directoryScores: DirectoryScore[], options?: {
|
|
490
|
+
timestamp?: string;
|
|
491
|
+
commitHash?: string | null;
|
|
492
|
+
subset?: boolean;
|
|
493
|
+
filesGlob?: string;
|
|
494
|
+
errors?: {
|
|
495
|
+
filePath: string;
|
|
496
|
+
error: string;
|
|
497
|
+
}[];
|
|
498
|
+
}): ProjectScore;
|
|
499
|
+
/**
|
|
500
|
+
* Group file scores by directory
|
|
501
|
+
*
|
|
502
|
+
* @param fileScores - Array of file scores
|
|
503
|
+
* @returns Map of directory path to file scores
|
|
504
|
+
*/
|
|
505
|
+
declare function groupFilesByDirectory(fileScores: FileScore[]): Map<string, FileScore[]>;
|
|
506
|
+
/**
|
|
507
|
+
* Calculate delta between two project scores
|
|
508
|
+
*
|
|
509
|
+
* @param current - Current project score
|
|
510
|
+
* @param previous - Previous project score
|
|
511
|
+
* @returns Delta metrics
|
|
512
|
+
*/
|
|
513
|
+
declare function calculateDelta(current: ProjectScore, previous: ProjectScore): {
|
|
514
|
+
purityDelta: number;
|
|
515
|
+
impurityQualityDelta: number | null;
|
|
516
|
+
healthDelta: number;
|
|
517
|
+
pureCountDelta: number;
|
|
518
|
+
impureCountDelta: number;
|
|
519
|
+
};
|
|
520
|
+
/**
|
|
521
|
+
* Get diagnostic insights based on metrics
|
|
522
|
+
*
|
|
523
|
+
* @param score - Project or file score
|
|
524
|
+
* @returns Array of insight messages
|
|
525
|
+
*/
|
|
526
|
+
declare function getDiagnosticInsights(score: Pick<ProjectScore, 'purity' | 'impurityQuality' | 'health'>): string[];
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* JSON Report Generator - Shell Layer
|
|
530
|
+
*
|
|
531
|
+
* Serializes analysis results to JSON format for storage, diffing, and
|
|
532
|
+
* trend analysis. This is part of the SHELL layer as it produces output.
|
|
533
|
+
*
|
|
534
|
+
* The JSON output includes:
|
|
535
|
+
* - Timestamp and commit hash for versioning
|
|
536
|
+
* - Full project score with all metrics
|
|
537
|
+
* - Directory and file breakdowns
|
|
538
|
+
* - Refactoring candidates
|
|
539
|
+
* - Any errors encountered during analysis
|
|
540
|
+
*/
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* JSON report options
|
|
544
|
+
*/
|
|
545
|
+
type JsonReportOptions = {
|
|
546
|
+
pretty?: boolean;
|
|
547
|
+
includeFunction?: boolean;
|
|
548
|
+
};
|
|
549
|
+
/**
|
|
550
|
+
* Generate JSON report string from project score
|
|
551
|
+
*
|
|
552
|
+
* @param score - The project score to serialize
|
|
553
|
+
* @param options - Report options
|
|
554
|
+
* @returns JSON string representation
|
|
555
|
+
*/
|
|
556
|
+
declare function generateJsonReport(score: ProjectScore, options?: JsonReportOptions): string;
|
|
557
|
+
/**
|
|
558
|
+
* Write JSON report to file
|
|
559
|
+
*
|
|
560
|
+
* @param score - The project score to write
|
|
561
|
+
* @param outputPath - Path to write the JSON file
|
|
562
|
+
* @param options - Report options
|
|
563
|
+
*/
|
|
564
|
+
declare function writeJsonReport(score: ProjectScore, outputPath: string, options?: JsonReportOptions): void;
|
|
565
|
+
/**
|
|
566
|
+
* Read a previous JSON report from file
|
|
567
|
+
*
|
|
568
|
+
* @param inputPath - Path to the JSON file
|
|
569
|
+
* @returns Parsed ProjectScore or null if file doesn't exist
|
|
570
|
+
*/
|
|
571
|
+
declare function readJsonReport(inputPath: string): ProjectScore | null;
|
|
572
|
+
/**
|
|
573
|
+
* Create a summary JSON suitable for PR comments or CI output
|
|
574
|
+
*
|
|
575
|
+
* @param score - The project score
|
|
576
|
+
* @returns Minimal JSON object with key metrics
|
|
577
|
+
*/
|
|
578
|
+
declare function createSummaryJson(score: ProjectScore): {
|
|
579
|
+
health: number;
|
|
580
|
+
purity: number;
|
|
581
|
+
impurityQuality: number | null;
|
|
582
|
+
pureCount: number;
|
|
583
|
+
impureCount: number;
|
|
584
|
+
statusBreakdown: {
|
|
585
|
+
ok: number;
|
|
586
|
+
review: number;
|
|
587
|
+
refactor: number;
|
|
588
|
+
};
|
|
589
|
+
timestamp: string;
|
|
590
|
+
commitHash: string | null;
|
|
591
|
+
topRefactoringCandidates: Array<{
|
|
592
|
+
name: string | null;
|
|
593
|
+
filePath: string;
|
|
594
|
+
qualityScore: number;
|
|
595
|
+
}>;
|
|
596
|
+
};
|
|
597
|
+
/**
|
|
598
|
+
* Compare two project scores and generate a diff report
|
|
599
|
+
*
|
|
600
|
+
* @param current - Current project score
|
|
601
|
+
* @param previous - Previous project score
|
|
602
|
+
* @returns JSON object with comparison data
|
|
603
|
+
*/
|
|
604
|
+
declare function generateComparisonReport(current: ProjectScore, previous: ProjectScore): {
|
|
605
|
+
current: ReturnType<typeof createSummaryJson>;
|
|
606
|
+
previous: ReturnType<typeof createSummaryJson>;
|
|
607
|
+
delta: {
|
|
608
|
+
health: number;
|
|
609
|
+
purity: number;
|
|
610
|
+
impurityQuality: number | null;
|
|
611
|
+
pureCount: number;
|
|
612
|
+
impureCount: number;
|
|
613
|
+
};
|
|
614
|
+
improved: boolean;
|
|
615
|
+
newRefactoringCandidates: Array<{
|
|
616
|
+
name: string | null;
|
|
617
|
+
filePath: string;
|
|
618
|
+
}>;
|
|
619
|
+
resolvedRefactoringCandidates: Array<{
|
|
620
|
+
name: string | null;
|
|
621
|
+
filePath: string;
|
|
622
|
+
}>;
|
|
623
|
+
};
|
|
624
|
+
|
|
625
|
+
/**
|
|
626
|
+
* Console Report Generator - Shell Layer
|
|
627
|
+
*
|
|
628
|
+
* Produces human-readable console output with colors, visual bars,
|
|
629
|
+
* and formatted tables. This is part of the SHELL layer as it
|
|
630
|
+
* performs I/O (writing to console).
|
|
631
|
+
*/
|
|
632
|
+
|
|
633
|
+
/**
|
|
634
|
+
* Generate and print the full console report
|
|
635
|
+
*/
|
|
636
|
+
declare function printConsoleReport(score: ProjectScore, options?: {
|
|
637
|
+
verbose?: boolean;
|
|
638
|
+
}): void;
|
|
639
|
+
/**
|
|
640
|
+
* Generate a single-line summary suitable for CI output
|
|
641
|
+
*/
|
|
642
|
+
declare function generateSummaryLine(score: ProjectScore): string;
|
|
643
|
+
/**
|
|
644
|
+
* Print a file-level report (for verbose output)
|
|
645
|
+
*/
|
|
646
|
+
declare function printFileReport(fileScore: FileScore): void;
|
|
647
|
+
|
|
648
|
+
/**
|
|
649
|
+
* Extractor - Shell layer for loading TypeScript projects via ts-morph
|
|
650
|
+
*
|
|
651
|
+
* This module handles the I/O of loading a TypeScript project from disk.
|
|
652
|
+
* It's a thin shell that delegates to ts-morph for actual parsing.
|
|
653
|
+
*/
|
|
654
|
+
|
|
655
|
+
type ExtractorOptions = {
|
|
656
|
+
tsconfigPath: string;
|
|
657
|
+
filesGlob?: string;
|
|
658
|
+
};
|
|
659
|
+
type ExtractorResult = {
|
|
660
|
+
project: Project;
|
|
661
|
+
sourceFiles: SourceFile[];
|
|
662
|
+
errors: {
|
|
663
|
+
filePath: string;
|
|
664
|
+
error: string;
|
|
665
|
+
}[];
|
|
666
|
+
};
|
|
667
|
+
/**
|
|
668
|
+
* Loads a TypeScript project from a tsconfig.json path
|
|
669
|
+
*
|
|
670
|
+
* @param options - Configuration options for the extractor
|
|
671
|
+
* @returns The loaded project and source files
|
|
672
|
+
* @throws Error if tsconfig.json doesn't exist or is invalid
|
|
673
|
+
*/
|
|
674
|
+
declare function loadProject(options: ExtractorOptions): ExtractorResult;
|
|
675
|
+
/**
|
|
676
|
+
* Gets the commit hash from git if available
|
|
677
|
+
*/
|
|
678
|
+
declare function getCommitHash(): string | null;
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* AST Function Extraction
|
|
682
|
+
*
|
|
683
|
+
* Extracts all function-like nodes from a TypeScript source file into
|
|
684
|
+
* normalized ExtractedFunction structures. This is part of the SHELL layer
|
|
685
|
+
* as it interacts with ts-morph AST nodes.
|
|
686
|
+
*
|
|
687
|
+
* Handles all function forms:
|
|
688
|
+
* - FunctionDeclaration
|
|
689
|
+
* - MethodDeclaration
|
|
690
|
+
* - ArrowFunction (including those passed as arguments)
|
|
691
|
+
* - FunctionExpression (including those passed as arguments)
|
|
692
|
+
* - GetAccessorDeclaration
|
|
693
|
+
* - SetAccessorDeclaration
|
|
694
|
+
*/
|
|
695
|
+
|
|
696
|
+
/**
|
|
697
|
+
* Extract all functions from a source file
|
|
698
|
+
*/
|
|
699
|
+
declare function extractFunctions(sourceFile: SourceFile): ExtractedFunction[];
|
|
700
|
+
/**
|
|
701
|
+
* Extract imports from a source file
|
|
702
|
+
*/
|
|
703
|
+
declare function extractImports(sourceFile: SourceFile): FileImports;
|
|
704
|
+
/**
|
|
705
|
+
* Check if a file is type-only (contains only types, interfaces, enums, no function bodies)
|
|
706
|
+
*/
|
|
707
|
+
declare function isTypeOnlyFile(sourceFile: SourceFile): boolean;
|
|
708
|
+
|
|
709
|
+
export { type AnalysisError, type AnalyzerConfig, type CallSite, type ClassifiedFunction, DEFAULT_QUALITY_THRESHOLDS, type DetectionContext, type DirectoryScore, type ExtractedFunction, type ExtractionResult, type ExtractorOptions, type ExtractorResult, type FileImports, type FileScore, type FunctionAnalysis, type FunctionClassification, type ImpurityMarker, type JsonReportOptions, MARKER_CATALOG, type MarkerDefinition, type MarkerType, type ProjectScore, QUALITY_WEIGHTS, type QualityThresholds, type RefactoringCandidate, type Status, type StatusBreakdown, analyze, analyzeFile, analyzeFilePath, analyzeFunction as analyzeFunctionQuality, calculateDelta, checkThresholds, classifyFunction, computeQualityScore, createClassifiedFunction, createDetectionContext, createSummaryJson, deriveStatus, detectMarkers, extractFunctions, extractImports, generateComparisonReport, generateJsonReport, generateSummaryLine, getAllMarkerTypes, getCommitHash, getDiagnosticInsights, getMarkerDefinition, getPureImportedFunctions, getStatusColor, getStatusDescription, getStatusEmoji, groupFilesByDirectory, isTypeOnlyFile, loadProject, printConsoleReport, printFileReport, readJsonReport, scoreDirectory, scoreFile, scoreProject, shouldExcludeFunction, writeJsonReport };
|