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
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON Report Generator - Shell Layer
|
|
3
|
+
*
|
|
4
|
+
* Serializes analysis results to JSON format for storage, diffing, and
|
|
5
|
+
* trend analysis. This is part of the SHELL layer as it produces output.
|
|
6
|
+
*
|
|
7
|
+
* The JSON output includes:
|
|
8
|
+
* - Timestamp and commit hash for versioning
|
|
9
|
+
* - Full project score with all metrics
|
|
10
|
+
* - Directory and file breakdowns
|
|
11
|
+
* - Refactoring candidates
|
|
12
|
+
* - Any errors encountered during analysis
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import * as fs from 'node:fs'
|
|
16
|
+
import * as path from 'node:path'
|
|
17
|
+
|
|
18
|
+
import type { ProjectScore } from '../types.js'
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* JSON report options
|
|
22
|
+
*/
|
|
23
|
+
export type JsonReportOptions = {
|
|
24
|
+
pretty?: boolean // Pretty print with indentation (default: true)
|
|
25
|
+
includeFunction?: boolean // Include function details (default: false for smaller output)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Generate JSON report string from project score
|
|
30
|
+
*
|
|
31
|
+
* @param score - The project score to serialize
|
|
32
|
+
* @param options - Report options
|
|
33
|
+
* @returns JSON string representation
|
|
34
|
+
*/
|
|
35
|
+
export function generateJsonReport(
|
|
36
|
+
score: ProjectScore,
|
|
37
|
+
options: JsonReportOptions = {},
|
|
38
|
+
): string {
|
|
39
|
+
const { pretty = true, includeFunction = false } = options
|
|
40
|
+
|
|
41
|
+
// Create a copy to potentially filter out function details
|
|
42
|
+
const reportData = prepareReportData(score, includeFunction)
|
|
43
|
+
|
|
44
|
+
if (pretty) {
|
|
45
|
+
return JSON.stringify(reportData, null, 2)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return JSON.stringify(reportData)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Prepare report data, optionally excluding function details for smaller output
|
|
53
|
+
*/
|
|
54
|
+
function prepareReportData(
|
|
55
|
+
score: ProjectScore,
|
|
56
|
+
includeFunctions: boolean,
|
|
57
|
+
): ProjectScore {
|
|
58
|
+
if (includeFunctions) {
|
|
59
|
+
return score
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Create a shallow copy with stripped function details
|
|
63
|
+
return {
|
|
64
|
+
...score,
|
|
65
|
+
directoryScores: score.directoryScores.map(dir => ({
|
|
66
|
+
...dir,
|
|
67
|
+
fileScores: dir.fileScores.map(file => ({
|
|
68
|
+
...file,
|
|
69
|
+
functions: [], // Strip function details to reduce size
|
|
70
|
+
})),
|
|
71
|
+
})),
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Write JSON report to file
|
|
77
|
+
*
|
|
78
|
+
* @param score - The project score to write
|
|
79
|
+
* @param outputPath - Path to write the JSON file
|
|
80
|
+
* @param options - Report options
|
|
81
|
+
*/
|
|
82
|
+
export function writeJsonReport(
|
|
83
|
+
score: ProjectScore,
|
|
84
|
+
outputPath: string,
|
|
85
|
+
options: JsonReportOptions = {},
|
|
86
|
+
): void {
|
|
87
|
+
const json = generateJsonReport(score, options)
|
|
88
|
+
|
|
89
|
+
// Ensure directory exists
|
|
90
|
+
const dir = path.dirname(outputPath)
|
|
91
|
+
if (!fs.existsSync(dir)) {
|
|
92
|
+
fs.mkdirSync(dir, { recursive: true })
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
fs.writeFileSync(outputPath, json, 'utf-8')
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Read a previous JSON report from file
|
|
100
|
+
*
|
|
101
|
+
* @param inputPath - Path to the JSON file
|
|
102
|
+
* @returns Parsed ProjectScore or null if file doesn't exist
|
|
103
|
+
*/
|
|
104
|
+
export function readJsonReport(inputPath: string): ProjectScore | null {
|
|
105
|
+
if (!fs.existsSync(inputPath)) {
|
|
106
|
+
return null
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const content = fs.readFileSync(inputPath, 'utf-8')
|
|
110
|
+
return JSON.parse(content) as ProjectScore
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Create a summary JSON suitable for PR comments or CI output
|
|
115
|
+
*
|
|
116
|
+
* @param score - The project score
|
|
117
|
+
* @returns Minimal JSON object with key metrics
|
|
118
|
+
*/
|
|
119
|
+
export function createSummaryJson(score: ProjectScore): {
|
|
120
|
+
health: number
|
|
121
|
+
purity: number
|
|
122
|
+
impurityQuality: number | null
|
|
123
|
+
pureCount: number
|
|
124
|
+
impureCount: number
|
|
125
|
+
statusBreakdown: { ok: number; review: number; refactor: number }
|
|
126
|
+
timestamp: string
|
|
127
|
+
commitHash: string | null
|
|
128
|
+
topRefactoringCandidates: Array<{
|
|
129
|
+
name: string | null
|
|
130
|
+
filePath: string
|
|
131
|
+
qualityScore: number
|
|
132
|
+
}>
|
|
133
|
+
} {
|
|
134
|
+
return {
|
|
135
|
+
health: Math.round(score.health * 10) / 10,
|
|
136
|
+
purity: Math.round(score.purity * 10) / 10,
|
|
137
|
+
impurityQuality:
|
|
138
|
+
score.impurityQuality !== null
|
|
139
|
+
? Math.round(score.impurityQuality * 10) / 10
|
|
140
|
+
: null,
|
|
141
|
+
pureCount: score.pureCount,
|
|
142
|
+
impureCount: score.impureCount,
|
|
143
|
+
statusBreakdown: score.statusBreakdown,
|
|
144
|
+
timestamp: score.timestamp,
|
|
145
|
+
commitHash: score.commitHash,
|
|
146
|
+
topRefactoringCandidates: score.refactoringCandidates
|
|
147
|
+
.slice(0, 5)
|
|
148
|
+
.map(c => ({
|
|
149
|
+
name: c.name,
|
|
150
|
+
filePath: c.filePath,
|
|
151
|
+
qualityScore: c.qualityScore,
|
|
152
|
+
})),
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Compare two project scores and generate a diff report
|
|
158
|
+
*
|
|
159
|
+
* @param current - Current project score
|
|
160
|
+
* @param previous - Previous project score
|
|
161
|
+
* @returns JSON object with comparison data
|
|
162
|
+
*/
|
|
163
|
+
export function generateComparisonReport(
|
|
164
|
+
current: ProjectScore,
|
|
165
|
+
previous: ProjectScore,
|
|
166
|
+
): {
|
|
167
|
+
current: ReturnType<typeof createSummaryJson>
|
|
168
|
+
previous: ReturnType<typeof createSummaryJson>
|
|
169
|
+
delta: {
|
|
170
|
+
health: number
|
|
171
|
+
purity: number
|
|
172
|
+
impurityQuality: number | null
|
|
173
|
+
pureCount: number
|
|
174
|
+
impureCount: number
|
|
175
|
+
}
|
|
176
|
+
improved: boolean
|
|
177
|
+
newRefactoringCandidates: Array<{ name: string | null; filePath: string }>
|
|
178
|
+
resolvedRefactoringCandidates: Array<{
|
|
179
|
+
name: string | null
|
|
180
|
+
filePath: string
|
|
181
|
+
}>
|
|
182
|
+
} {
|
|
183
|
+
const currentSummary = createSummaryJson(current)
|
|
184
|
+
const previousSummary = createSummaryJson(previous)
|
|
185
|
+
|
|
186
|
+
// Calculate deltas
|
|
187
|
+
const delta = {
|
|
188
|
+
health: Math.round((current.health - previous.health) * 10) / 10,
|
|
189
|
+
purity: Math.round((current.purity - previous.purity) * 10) / 10,
|
|
190
|
+
impurityQuality:
|
|
191
|
+
current.impurityQuality !== null && previous.impurityQuality !== null
|
|
192
|
+
? Math.round(
|
|
193
|
+
(current.impurityQuality - previous.impurityQuality) * 10,
|
|
194
|
+
) / 10
|
|
195
|
+
: null,
|
|
196
|
+
pureCount: current.pureCount - previous.pureCount,
|
|
197
|
+
impureCount: current.impureCount - previous.impureCount,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Find new and resolved refactoring candidates
|
|
201
|
+
const currentCandidateKeys = new Set(
|
|
202
|
+
current.refactoringCandidates.map(c => `${c.filePath}:${c.name}`),
|
|
203
|
+
)
|
|
204
|
+
const previousCandidateKeys = new Set(
|
|
205
|
+
previous.refactoringCandidates.map(c => `${c.filePath}:${c.name}`),
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
const newRefactoringCandidates = current.refactoringCandidates
|
|
209
|
+
.filter(c => !previousCandidateKeys.has(`${c.filePath}:${c.name}`))
|
|
210
|
+
.map(c => ({ name: c.name, filePath: c.filePath }))
|
|
211
|
+
|
|
212
|
+
const resolvedRefactoringCandidates = previous.refactoringCandidates
|
|
213
|
+
.filter(c => !currentCandidateKeys.has(`${c.filePath}:${c.name}`))
|
|
214
|
+
.map(c => ({ name: c.name, filePath: c.filePath }))
|
|
215
|
+
|
|
216
|
+
// Overall improvement check (health went up or stayed same with other improvements)
|
|
217
|
+
const improved =
|
|
218
|
+
delta.health > 0 ||
|
|
219
|
+
(delta.health >= 0 && delta.purity > 0) ||
|
|
220
|
+
(delta.health >= 0 &&
|
|
221
|
+
delta.impurityQuality !== null &&
|
|
222
|
+
delta.impurityQuality > 0)
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
current: currentSummary,
|
|
226
|
+
previous: previousSummary,
|
|
227
|
+
delta,
|
|
228
|
+
improved,
|
|
229
|
+
newRefactoringCandidates,
|
|
230
|
+
resolvedRefactoringCandidates,
|
|
231
|
+
}
|
|
232
|
+
}
|