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.
Files changed (151) hide show
  1. package/.plans/001-fcis-analyzer.md +832 -0
  2. package/.plans/002-fcis-analyzer-improvements.md +205 -0
  3. package/README.md +272 -0
  4. package/TECHNICAL.md +386 -0
  5. package/dist/cli.d.ts +1 -0
  6. package/dist/cli.js +1836 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/index.d.ts +709 -0
  9. package/dist/index.js +1845 -0
  10. package/dist/index.js.map +1 -0
  11. package/package.json +47 -0
  12. package/pnpm-workspace.yaml +0 -0
  13. package/src/analyzer.ts +266 -0
  14. package/src/classification/classifier.ts +156 -0
  15. package/src/classification/derive-status.ts +171 -0
  16. package/src/classification/quality-scorer.ts +481 -0
  17. package/src/cli.ts +286 -0
  18. package/src/detection/detect-markers.ts +480 -0
  19. package/src/detection/markers.ts +332 -0
  20. package/src/extraction/extract-functions.ts +570 -0
  21. package/src/extraction/extractor.ts +188 -0
  22. package/src/index.ts +111 -0
  23. package/src/reporting/report-console.ts +416 -0
  24. package/src/reporting/report-json.ts +232 -0
  25. package/src/scoring/scorer.ts +504 -0
  26. package/src/types.ts +248 -0
  27. package/tests/classifier.test.ts +480 -0
  28. package/tests/derive-status.test.ts +464 -0
  29. package/tests/detect-markers.test.ts +639 -0
  30. package/tests/extractor.test.ts +155 -0
  31. package/tests/integration.test.ts +706 -0
  32. package/tests/quality-scorer.test.ts +650 -0
  33. package/tests/scorer.test.ts +768 -0
  34. package/tsconfig.json +34 -0
  35. package/tsup.config.ts +17 -0
  36. package/vendor/ts-morph/.editorconfig +10 -0
  37. package/vendor/ts-morph/.gitattributes +11 -0
  38. package/vendor/ts-morph/.github/CODE_OF_CONDUCT.md +77 -0
  39. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  40. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/custom.md +4 -0
  41. package/vendor/ts-morph/.github/ISSUE_TEMPLATE/feature_request.md +18 -0
  42. package/vendor/ts-morph/.github/workflows/ci.yml +50 -0
  43. package/vendor/ts-morph/.github/workflows/publish.yml +53 -0
  44. package/vendor/ts-morph/.vscode/settings.json +10 -0
  45. package/vendor/ts-morph/CONTRIBUTING.md +23 -0
  46. package/vendor/ts-morph/DEVELOPMENT.md +32 -0
  47. package/vendor/ts-morph/LICENSE +21 -0
  48. package/vendor/ts-morph/deno.json +8 -0
  49. package/vendor/ts-morph/deno.lock +1233 -0
  50. package/vendor/ts-morph/docs/CNAME +1 -0
  51. package/vendor/ts-morph/docs/Gemfile +2 -0
  52. package/vendor/ts-morph/docs/_config.yml +5 -0
  53. package/vendor/ts-morph/docs/_layouts/default.html +159 -0
  54. package/vendor/ts-morph/docs/_script-templates/main.ts +116 -0
  55. package/vendor/ts-morph/docs/assets/css/style.scss +212 -0
  56. package/vendor/ts-morph/docs/details/ambient.md +38 -0
  57. package/vendor/ts-morph/docs/details/async.md +31 -0
  58. package/vendor/ts-morph/docs/details/classes.md +314 -0
  59. package/vendor/ts-morph/docs/details/comment-ranges.md +7 -0
  60. package/vendor/ts-morph/docs/details/comments.md +122 -0
  61. package/vendor/ts-morph/docs/details/decorators.md +119 -0
  62. package/vendor/ts-morph/docs/details/documentation.md +73 -0
  63. package/vendor/ts-morph/docs/details/enums.md +117 -0
  64. package/vendor/ts-morph/docs/details/exports.md +308 -0
  65. package/vendor/ts-morph/docs/details/expressions.md +46 -0
  66. package/vendor/ts-morph/docs/details/functions.md +150 -0
  67. package/vendor/ts-morph/docs/details/generators.md +27 -0
  68. package/vendor/ts-morph/docs/details/identifiers.md +79 -0
  69. package/vendor/ts-morph/docs/details/imports.md +191 -0
  70. package/vendor/ts-morph/docs/details/index.md +52 -0
  71. package/vendor/ts-morph/docs/details/initializers.md +40 -0
  72. package/vendor/ts-morph/docs/details/interfaces.md +218 -0
  73. package/vendor/ts-morph/docs/details/literals.md +20 -0
  74. package/vendor/ts-morph/docs/details/modifiers.md +38 -0
  75. package/vendor/ts-morph/docs/details/modules.md +113 -0
  76. package/vendor/ts-morph/docs/details/namespaces.md +7 -0
  77. package/vendor/ts-morph/docs/details/object-literal-expressions.md +106 -0
  78. package/vendor/ts-morph/docs/details/parameters.md +64 -0
  79. package/vendor/ts-morph/docs/details/signatures.md +41 -0
  80. package/vendor/ts-morph/docs/details/source-files.md +292 -0
  81. package/vendor/ts-morph/docs/details/type-aliases.md +34 -0
  82. package/vendor/ts-morph/docs/details/type-parameters.md +72 -0
  83. package/vendor/ts-morph/docs/details/types.md +254 -0
  84. package/vendor/ts-morph/docs/details/variables.md +110 -0
  85. package/vendor/ts-morph/docs/emitting.md +151 -0
  86. package/vendor/ts-morph/docs/index.md +25 -0
  87. package/vendor/ts-morph/docs/manipulation/code-writer.md +20 -0
  88. package/vendor/ts-morph/docs/manipulation/formatting.md +76 -0
  89. package/vendor/ts-morph/docs/manipulation/index.md +136 -0
  90. package/vendor/ts-morph/docs/manipulation/order.md +14 -0
  91. package/vendor/ts-morph/docs/manipulation/performance.md +222 -0
  92. package/vendor/ts-morph/docs/manipulation/removing.md +31 -0
  93. package/vendor/ts-morph/docs/manipulation/renaming.md +106 -0
  94. package/vendor/ts-morph/docs/manipulation/settings.md +76 -0
  95. package/vendor/ts-morph/docs/manipulation/structures.md +117 -0
  96. package/vendor/ts-morph/docs/manipulation/transforms.md +84 -0
  97. package/vendor/ts-morph/docs/metrics/performance.json +4 -0
  98. package/vendor/ts-morph/docs/navigation/ambient-modules.md +22 -0
  99. package/vendor/ts-morph/docs/navigation/compiler-nodes.md +82 -0
  100. package/vendor/ts-morph/docs/navigation/directories.md +287 -0
  101. package/vendor/ts-morph/docs/navigation/example.md +50 -0
  102. package/vendor/ts-morph/docs/navigation/finding-references.md +53 -0
  103. package/vendor/ts-morph/docs/navigation/getting-source-files.md +59 -0
  104. package/vendor/ts-morph/docs/navigation/images/getChildrenVsForEachChild.gif +0 -0
  105. package/vendor/ts-morph/docs/navigation/index.md +94 -0
  106. package/vendor/ts-morph/docs/navigation/language-service.md +23 -0
  107. package/vendor/ts-morph/docs/navigation/program.md +25 -0
  108. package/vendor/ts-morph/docs/navigation/type-checker.md +33 -0
  109. package/vendor/ts-morph/docs/setup/adding-source-files.md +145 -0
  110. package/vendor/ts-morph/docs/setup/ast-viewers.md +46 -0
  111. package/vendor/ts-morph/docs/setup/diagnostics.md +109 -0
  112. package/vendor/ts-morph/docs/setup/file-system.md +106 -0
  113. package/vendor/ts-morph/docs/setup/images/atom-ast.png +0 -0
  114. package/vendor/ts-morph/docs/setup/images/atom-ast_small.png +0 -0
  115. package/vendor/ts-morph/docs/setup/images/atom-command-palette.png +0 -0
  116. package/vendor/ts-morph/docs/setup/images/atom-file.png +0 -0
  117. package/vendor/ts-morph/docs/setup/images/ts-ast-viewer.png +0 -0
  118. package/vendor/ts-morph/docs/setup/index.md +94 -0
  119. package/vendor/ts-morph/docs/utilities.md +55 -0
  120. package/vendor/ts-morph/dprint.json +23 -0
  121. package/vendor/ts-morph/package.json +30 -0
  122. package/vendor/ts-morph/packages/bootstrap/LICENSE +21 -0
  123. package/vendor/ts-morph/packages/bootstrap/lib/ts-morph-bootstrap.d.ts +397 -0
  124. package/vendor/ts-morph/packages/bootstrap/package.json +46 -0
  125. package/vendor/ts-morph/packages/bootstrap/readme.md +200 -0
  126. package/vendor/ts-morph/packages/common/LICENSE +21 -0
  127. package/vendor/ts-morph/packages/common/lib/ts-morph-common.d.ts +1082 -0
  128. package/vendor/ts-morph/packages/common/lib/typescript.d.ts +11439 -0
  129. package/vendor/ts-morph/packages/common/package.json +65 -0
  130. package/vendor/ts-morph/packages/common/readme.md +5 -0
  131. package/vendor/ts-morph/packages/scripts/changeTypeScriptVersion.ts +28 -0
  132. package/vendor/ts-morph/packages/scripts/createDeclarationProject.ts +47 -0
  133. package/vendor/ts-morph/packages/scripts/deps.ts +2 -0
  134. package/vendor/ts-morph/packages/scripts/execScript.ts +31 -0
  135. package/vendor/ts-morph/packages/scripts/folders.ts +11 -0
  136. package/vendor/ts-morph/packages/scripts/getDevCompilerVersions.ts +19 -0
  137. package/vendor/ts-morph/packages/scripts/mod.ts +7 -0
  138. package/vendor/ts-morph/packages/scripts/utils/Memoize.ts +36 -0
  139. package/vendor/ts-morph/packages/scripts/utils/forEachTypeText.ts +23 -0
  140. package/vendor/ts-morph/packages/scripts/utils/makeConstructorsPrivate.ts +26 -0
  141. package/vendor/ts-morph/packages/scripts/utils/mod.ts +4 -0
  142. package/vendor/ts-morph/packages/scripts/utils/printDiagnostics.ts +10 -0
  143. package/vendor/ts-morph/packages/ts-morph/LICENSE +21 -0
  144. package/vendor/ts-morph/packages/ts-morph/lib/ts-morph.d.ts +11198 -0
  145. package/vendor/ts-morph/packages/ts-morph/package.json +78 -0
  146. package/vendor/ts-morph/packages/ts-morph/readme.md +111 -0
  147. package/vendor/ts-morph/readme.md +14 -0
  148. package/vendor/ts-morph/rfcs/README.md +13 -0
  149. package/vendor/ts-morph/rfcs/RFC-0001 - Inserting Into Statements Handling Comments.md +181 -0
  150. package/vendor/ts-morph/tsconfig.common.json +17 -0
  151. 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
+ }