@pythonidaer/complexity-report 1.0.2

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 (57) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/LICENSE +21 -0
  3. package/README.md +103 -0
  4. package/assets/prettify.css +1 -0
  5. package/assets/prettify.js +2 -0
  6. package/assets/sort-arrow-sprite.png +0 -0
  7. package/complexity-breakdown.js +53 -0
  8. package/decision-points/ast-utils.js +127 -0
  9. package/decision-points/decision-type.js +92 -0
  10. package/decision-points/function-matching.js +185 -0
  11. package/decision-points/in-params.js +262 -0
  12. package/decision-points/index.js +6 -0
  13. package/decision-points/node-helpers.js +89 -0
  14. package/decision-points/parent-map.js +62 -0
  15. package/decision-points/parse-main.js +101 -0
  16. package/decision-points/ternary-multiline.js +86 -0
  17. package/export-generators/helpers.js +309 -0
  18. package/export-generators/index.js +143 -0
  19. package/export-generators/md-exports.js +160 -0
  20. package/export-generators/txt-exports.js +262 -0
  21. package/function-boundaries/arrow-brace-body.js +302 -0
  22. package/function-boundaries/arrow-helpers.js +93 -0
  23. package/function-boundaries/arrow-jsx.js +73 -0
  24. package/function-boundaries/arrow-object-literal.js +65 -0
  25. package/function-boundaries/arrow-single-expr.js +72 -0
  26. package/function-boundaries/brace-scanning.js +151 -0
  27. package/function-boundaries/index.js +67 -0
  28. package/function-boundaries/named-helpers.js +227 -0
  29. package/function-boundaries/parse-utils.js +456 -0
  30. package/function-extraction/ast-utils.js +112 -0
  31. package/function-extraction/extract-callback.js +65 -0
  32. package/function-extraction/extract-from-eslint.js +91 -0
  33. package/function-extraction/extract-name-ast.js +133 -0
  34. package/function-extraction/extract-name-regex.js +267 -0
  35. package/function-extraction/index.js +6 -0
  36. package/function-extraction/utils.js +29 -0
  37. package/function-hierarchy.js +427 -0
  38. package/html-generators/about.js +75 -0
  39. package/html-generators/file-boundary-builders.js +36 -0
  40. package/html-generators/file-breakdown.js +412 -0
  41. package/html-generators/file-data.js +50 -0
  42. package/html-generators/file-helpers.js +100 -0
  43. package/html-generators/file-javascript.js +430 -0
  44. package/html-generators/file-line-render.js +160 -0
  45. package/html-generators/file.css +370 -0
  46. package/html-generators/file.js +207 -0
  47. package/html-generators/folder.js +424 -0
  48. package/html-generators/index.js +6 -0
  49. package/html-generators/main-index.js +346 -0
  50. package/html-generators/shared.css +471 -0
  51. package/html-generators/utils.js +15 -0
  52. package/index.js +36 -0
  53. package/integration/eslint/index.js +94 -0
  54. package/integration/threshold/index.js +45 -0
  55. package/package.json +64 -0
  56. package/report/cli.js +58 -0
  57. package/report/index.js +559 -0
@@ -0,0 +1,471 @@
1
+ /* Shared CSS for Complexity Report HTML Pages */
2
+
3
+ /* Base Styles */
4
+ body, html {
5
+ margin: 0;
6
+ padding: 0;
7
+ height: 100%;
8
+ }
9
+
10
+ body {
11
+ font-family: Helvetica Neue, Helvetica, Arial;
12
+ font-size: 14px;
13
+ color: #333;
14
+ }
15
+
16
+ *, *:after, *:before {
17
+ -webkit-box-sizing: border-box;
18
+ -moz-box-sizing: border-box;
19
+ box-sizing: border-box;
20
+ }
21
+
22
+ /* Typography */
23
+ h1 {
24
+ font-size: 20px;
25
+ margin: 0;
26
+ }
27
+
28
+ h2 {
29
+ font-size: 14px;
30
+ }
31
+
32
+ h3 {
33
+ font-size: 14px;
34
+ margin: 12px 0 4px 0;
35
+ font-weight: bold;
36
+ }
37
+
38
+ p {
39
+ margin: 4px 0;
40
+ }
41
+
42
+ ul {
43
+ margin: 4px 0;
44
+ padding-left: 20px;
45
+ }
46
+
47
+ li {
48
+ margin: 2px 0;
49
+ }
50
+
51
+ .small {
52
+ font-size: 12px;
53
+ }
54
+
55
+ .strong {
56
+ font-weight: bold;
57
+ }
58
+
59
+ /* Links */
60
+ a {
61
+ color: #0074D9;
62
+ text-decoration: none;
63
+ }
64
+
65
+ a:hover {
66
+ text-decoration: underline;
67
+ }
68
+
69
+ .about-link {
70
+ color: #0074D9;
71
+ text-decoration: none;
72
+ font-size: 14px;
73
+ }
74
+
75
+ .about-link:hover {
76
+ text-decoration: underline;
77
+ }
78
+
79
+ .quiet {
80
+ color: #7f7f7f;
81
+ color: rgba(0,0,0,0.5);
82
+ }
83
+
84
+ .quiet a {
85
+ opacity: 0.7;
86
+ }
87
+
88
+ /* Code */
89
+ code {
90
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
91
+ font-size: 13px;
92
+ background-color: #f5f5f5;
93
+ padding: 2px 4px;
94
+ border-radius: 3px;
95
+ }
96
+
97
+ pre {
98
+ font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace;
99
+ margin: 0;
100
+ padding: 0;
101
+ -moz-tab-size: 2;
102
+ -o-tab-size: 2;
103
+ tab-size: 2;
104
+ }
105
+
106
+ /* Spacing Utilities */
107
+ .pad1 {
108
+ padding: 10px;
109
+ }
110
+
111
+ .pad1y {
112
+ padding: 10px 0;
113
+ }
114
+
115
+ .pad2 {
116
+ padding: 10px;
117
+ }
118
+
119
+ .pad2x {
120
+ padding: 0 20px;
121
+ }
122
+
123
+ .pad2y {
124
+ padding: 20px 0;
125
+ }
126
+
127
+ .space-top1 {
128
+ padding: 10px 0 0 0;
129
+ }
130
+
131
+ .space-left2 {
132
+ padding-left: 55px;
133
+ }
134
+
135
+ .space-right2 {
136
+ padding-right: 20px;
137
+ }
138
+
139
+ /* Layout */
140
+ .clearfix {
141
+ display: block;
142
+ }
143
+
144
+ .clearfix:after {
145
+ content: '';
146
+ display: block;
147
+ height: 0;
148
+ clear: both;
149
+ visibility: hidden;
150
+ }
151
+
152
+ .fl {
153
+ float: left;
154
+ }
155
+
156
+ .center {
157
+ text-align: center;
158
+ }
159
+
160
+ .header-row {
161
+ display: flex;
162
+ justify-content: space-between;
163
+ align-items: center;
164
+ margin-bottom: 0;
165
+ }
166
+
167
+ .header-row h1 {
168
+ margin: 0;
169
+ }
170
+
171
+ /* Coverage Summary Table */
172
+ .coverage-summary {
173
+ border-collapse: collapse;
174
+ width: 100%;
175
+ }
176
+
177
+ .coverage-summary tr {
178
+ border-bottom: 1px solid #bbb;
179
+ }
180
+
181
+ .keyline-all {
182
+ border: 1px solid #ddd;
183
+ }
184
+
185
+ .coverage-summary td,
186
+ .coverage-summary th {
187
+ padding: 10px;
188
+ }
189
+
190
+ .coverage-summary tbody tr {
191
+ background-color: rgb(230, 245, 208);
192
+ }
193
+
194
+ /* Row background colors based on complexity level (only for function rows, not folder rows) */
195
+ .coverage-summary tbody tr[data-complexity].high {
196
+ background-color: #fff4c2;
197
+ }
198
+
199
+ .coverage-summary tbody tr[data-complexity].medium,
200
+ .coverage-summary tbody tr[data-complexity].low {
201
+ background-color: #FCE1E5;
202
+ }
203
+
204
+ .coverage-summary tbody {
205
+ border: 1px solid #bbb;
206
+ }
207
+
208
+ .coverage-summary td {
209
+ border-right: 1px solid #bbb;
210
+ }
211
+
212
+ .coverage-summary td:last-child {
213
+ border-right: none;
214
+ }
215
+
216
+ .coverage-summary th {
217
+ text-align: left;
218
+ font-weight: normal;
219
+ white-space: nowrap;
220
+ cursor: pointer;
221
+ user-select: none;
222
+ border: none !important;
223
+ }
224
+
225
+ .coverage-summary th:hover {
226
+ background-color: rgba(0, 0, 0, 0.05);
227
+ }
228
+
229
+ /* Sort arrow sprite (matching coverage report) */
230
+ .coverage-summary .sorter {
231
+ height: 10px;
232
+ width: 7px;
233
+ display: inline-block;
234
+ margin-left: 0.5em;
235
+ background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent;
236
+ }
237
+
238
+ .coverage-summary .sorted .sorter {
239
+ background-position: 0 -20px;
240
+ }
241
+
242
+ .coverage-summary .sorted-desc .sorter {
243
+ background-position: 0 -10px;
244
+ }
245
+
246
+ /* Footer */
247
+ .footer {
248
+ height: 48px;
249
+ padding: 20px;
250
+ }
251
+
252
+ .coverage-summary th.file {
253
+ border-right: none !important;
254
+ }
255
+
256
+ .coverage-summary th.pct,
257
+ .coverage-summary th.abs,
258
+ .coverage-summary th.pic {
259
+ text-align: right;
260
+ }
261
+
262
+ .coverage-summary td.pct,
263
+ .coverage-summary td.abs {
264
+ text-align: right;
265
+ }
266
+
267
+ .coverage-summary td.file {
268
+ white-space: nowrap;
269
+ }
270
+
271
+ .coverage-summary td.pic {
272
+ min-width: 120px !important;
273
+ text-align: right;
274
+ }
275
+
276
+ .coverage-summary td.bar {
277
+ padding: 10px;
278
+ min-width: 120px !important;
279
+ }
280
+
281
+ /* Chart / Progress Bar */
282
+ .chart {
283
+ line-height: 0;
284
+ font-size: 0;
285
+ white-space: nowrap;
286
+ }
287
+
288
+ .cover-fill,
289
+ .cover-empty {
290
+ display: inline-block;
291
+ height: 12px;
292
+ vertical-align: top;
293
+ }
294
+
295
+ .cover-empty {
296
+ background: white;
297
+ }
298
+
299
+ .cover-full {
300
+ border-right: none !important;
301
+ }
302
+
303
+ /* Complexity Level Colors - Default (for homepage folder rows) */
304
+ .low .cover-fill {
305
+ background: #C21F39;
306
+ }
307
+
308
+ .low .chart {
309
+ border: 1px solid #C21F39;
310
+ }
311
+
312
+ .high .cover-fill {
313
+ background: rgb(77,146,33);
314
+ }
315
+
316
+ .high .chart {
317
+ border: 1px solid rgb(77,146,33);
318
+ }
319
+
320
+ .medium .cover-fill {
321
+ background: #f9cd0b;
322
+ }
323
+
324
+ .medium .chart {
325
+ border: 1px solid #f9cd0b;
326
+ }
327
+
328
+ /* Complexity Level Colors - Function rows only (for folder pages) */
329
+ .function-complexity-table .high .cover-fill {
330
+ background: #f9cd0b;
331
+ }
332
+
333
+ .function-complexity-table .high .chart {
334
+ border: 1px solid #f9cd0b;
335
+ }
336
+
337
+ .function-complexity-table .medium .cover-fill {
338
+ background: #C21F39;
339
+ }
340
+
341
+ .function-complexity-table .medium .chart {
342
+ border: 1px solid #C21F39;
343
+ }
344
+
345
+ .acceptable .cover-fill {
346
+ background: rgb(100,150,50);
347
+ }
348
+
349
+ .acceptable .chart {
350
+ border: 1px solid rgb(100,150,50);
351
+ }
352
+
353
+ .good .cover-fill {
354
+ background: rgb(120,160,70);
355
+ }
356
+
357
+ .good .chart {
358
+ border: 1px solid rgb(120,160,70);
359
+ }
360
+
361
+ .status-line {
362
+ height: 10px;
363
+ }
364
+
365
+ .status-line.low {
366
+ background: #C21F39;
367
+ }
368
+
369
+ .status-line.high {
370
+ background: rgb(77,146,33);
371
+ }
372
+
373
+ .status-line.medium {
374
+ background: #f9cd0b;
375
+ }
376
+
377
+ /* Complexity Values */
378
+ .complexity-value {
379
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
380
+ }
381
+
382
+ .complexity-value.low {
383
+ color: #C21F39;
384
+ }
385
+
386
+ .complexity-value.medium {
387
+ color: #f9cd0b;
388
+ }
389
+
390
+ .complexity-value.high {
391
+ color: rgb(77,146,33);
392
+ }
393
+
394
+ .complexity-value.acceptable {
395
+ color: rgb(100,150,50);
396
+ }
397
+
398
+ .complexity-value.good {
399
+ color: rgb(120,160,70);
400
+ }
401
+
402
+ .fraction {
403
+ font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace;
404
+ font-size: 10px;
405
+ color: #555;
406
+ background: #E8E8E8;
407
+ padding: 4px 5px;
408
+ border-radius: 3px;
409
+ vertical-align: middle;
410
+ }
411
+
412
+ /* Sort Icons */
413
+ .sort-icon {
414
+ display: inline-block;
415
+ width: 12px;
416
+ height: 12px;
417
+ margin-left: 5px;
418
+ color: #999;
419
+ opacity: 0.5;
420
+ }
421
+
422
+ .sort-icon.active {
423
+ color: #000;
424
+ opacity: 1;
425
+ }
426
+
427
+ /* Controls */
428
+ .controls {
429
+ margin: 20px 0;
430
+ padding: 15px;
431
+ background: #f5f5f5;
432
+ border-radius: 4px;
433
+ }
434
+
435
+ .controls label {
436
+ margin-right: 15px;
437
+ font-weight: normal;
438
+ }
439
+
440
+ .controls input,
441
+ .controls select {
442
+ margin-left: 5px;
443
+ padding: 5px;
444
+ border: 1px solid #ddd;
445
+ border-radius: 3px;
446
+ }
447
+
448
+ /* Additional Color Classes */
449
+ .acceptable {
450
+ background: rgb(240,250,230);
451
+ }
452
+
453
+ .good {
454
+ background: rgb(245,252,240);
455
+ }
456
+
457
+ .acceptable .cover-fill {
458
+ background: rgb(100,150,50);
459
+ }
460
+
461
+ .good .cover-fill {
462
+ background: rgb(120,160,70);
463
+ }
464
+
465
+ .acceptable .chart {
466
+ border: 1px solid rgb(100,150,50);
467
+ }
468
+
469
+ .good .chart {
470
+ border: 1px solid rgb(120,160,70);
471
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Escapes HTML special characters
3
+ * @param {string} text - Text to escape
4
+ * @returns {string} Escaped HTML
5
+ */
6
+ export function escapeHtml(text) {
7
+ const map = {
8
+ '&': '&',
9
+ '<': '&lt;',
10
+ '>': '&gt;',
11
+ '"': '&quot;',
12
+ "'": '&#039;'
13
+ };
14
+ return text.replace(/[&<>"']/g, m => map[m]);
15
+ }
package/index.js ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * complexity-report - AST-based cyclomatic complexity analyzer
3
+ *
4
+ * Public API for programmatic usage
5
+ */
6
+
7
+ export { generateComplexityReport } from './report/index.js';
8
+
9
+ // Export utility functions that might be useful for consumers
10
+ export { findESLintConfig, getComplexityVariant } from './integration/eslint/index.js';
11
+ export { getComplexityThreshold } from './integration/threshold/index.js';
12
+
13
+ /**
14
+ * Type definitions for API consumers (JSDoc)
15
+ *
16
+ * @typedef {Object} ComplexityReportOptions
17
+ * @property {string} [cwd] - Project root directory (defaults to process.cwd())
18
+ * @property {string} [outputDir] - Output directory for reports (defaults to 'complexity' under cwd)
19
+ * @property {boolean} [showAllInitially] - Show all functions initially in HTML report
20
+ * @property {boolean} [showAllColumnsInitially] - Show all breakdown columns initially
21
+ * @property {boolean} [hideTableInitially] - Hide breakdown table initially
22
+ * @property {boolean} [hideLinesInitially] - Hide line numbers initially
23
+ * @property {boolean} [hideHighlightsInitially] - Hide code highlights initially
24
+ * @property {boolean} [shouldExport] - Generate TXT/MD export files
25
+ *
26
+ * @typedef {Object} ComplexityReportResult
27
+ * @property {Object} stats - Statistics about analyzed functions
28
+ * @property {number} stats.allFunctionsCount - Total number of functions analyzed
29
+ * @property {number} stats.maxComplexity - Highest complexity found
30
+ * @property {number} stats.avgComplexity - Average complexity
31
+ * @property {number} stats.withinThreshold - Number of functions within threshold
32
+ * @property {number} stats.withinThresholdPercentage - Percentage within threshold
33
+ * @property {Array} stats.overThreshold - Functions exceeding complexity threshold
34
+ * @property {Array} folders - Folder-level complexity data
35
+ * @property {string} complexityDir - Path to generated report directory
36
+ */
@@ -0,0 +1,94 @@
1
+ import { ESLint } from 'eslint';
2
+ import { mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
3
+ import { resolve } from 'path';
4
+
5
+ const CONFIG_NAMES = ['eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs'];
6
+ const COMPLEXITY_DIR = 'complexity';
7
+ const REPORT_FILENAME = 'complexity-report.json';
8
+
9
+ /** @type {'classic' | 'modified'} ESLint complexity variant; see https://eslint.org/docs/latest/rules/complexity */
10
+ const DEFAULT_VARIANT = 'classic';
11
+
12
+ /**
13
+ * Finds the project's ESLint config file (flat config).
14
+ * ESLint 9+ looks for eslint.config.js, .mjs, .cjs in that order.
15
+ * @param {string} projectRoot - Root directory of the project
16
+ * @returns {string | null} Absolute path to config file, or null if not found
17
+ */
18
+ export function findESLintConfig(projectRoot) {
19
+ for (const name of CONFIG_NAMES) {
20
+ const path = resolve(projectRoot, name);
21
+ if (existsSync(path)) return path;
22
+ }
23
+ return null;
24
+ }
25
+
26
+ /**
27
+ * Reads the complexity rule variant from the config file.
28
+ * Classic: each switch case +1; modified: whole switch +1.
29
+ * Defaults to "classic" if not found.
30
+ * See https://eslint.org/docs/latest/rules/complexity (option "variant").
31
+ * @param {string} configPath - Absolute path to eslint.config.js
32
+ * @returns {'classic' | 'modified'}
33
+ */
34
+ export function getComplexityVariant(configPath) {
35
+ try {
36
+ const content = readFileSync(configPath, 'utf-8');
37
+ if (/variant:\s*["']modified["']/.test(content)) return 'modified';
38
+ } catch {
39
+ // ignore
40
+ }
41
+ return DEFAULT_VARIANT;
42
+ }
43
+
44
+ /**
45
+ * Runs ESLint using the project's config with complexity overridden to max: 0
46
+ * so every function gets a complexity diagnostic. Writes the JSON report to
47
+ * complexity/complexity-report.json (same mental model as Vitest coverage/).
48
+ * @param {string} projectRoot - Root directory of the project
49
+ * @returns {Promise<Array>} ESLint results as JSON array (same shape as --format=json)
50
+ */
51
+ export async function runESLintComplexityCheck(projectRoot) {
52
+ const configPath = findESLintConfig(projectRoot);
53
+ if (!configPath) {
54
+ const tried = CONFIG_NAMES.join(', ');
55
+ console.error(
56
+ `No ESLint flat config found. Tried: ${tried}. Add one at project root to run the complexity report.`
57
+ );
58
+ process.exit(1);
59
+ }
60
+
61
+ const complexityDir = resolve(projectRoot, COMPLEXITY_DIR);
62
+ mkdirSync(complexityDir, { recursive: true });
63
+
64
+ const reportPath = resolve(complexityDir, REPORT_FILENAME);
65
+ const variant = getComplexityVariant(configPath);
66
+
67
+ console.log('Running ESLint to collect complexity for all functions...');
68
+ try {
69
+ const eslint = new ESLint({
70
+ cwd: projectRoot,
71
+ overrideConfigFile: configPath,
72
+ ignorePatterns: [
73
+ '**/__tests__/**',
74
+ '**/*.test.{js,ts,tsx}',
75
+ '**/*.spec.{js,ts,tsx}',
76
+ ],
77
+ overrideConfig: {
78
+ rules: {
79
+ complexity: ['warn', { max: 0, variant }],
80
+ },
81
+ },
82
+ });
83
+
84
+ const results = await eslint.lintFiles(['.']);
85
+
86
+ const json = JSON.stringify(results, null, 0);
87
+ writeFileSync(reportPath, json, 'utf-8');
88
+
89
+ return results;
90
+ } catch (error) {
91
+ console.error('Error running ESLint:', error.message);
92
+ process.exit(1);
93
+ }
94
+ }
@@ -0,0 +1,45 @@
1
+ import { readFileSync } from 'fs';
2
+ import { resolve } from 'path';
3
+
4
+ /**
5
+ * Reads the complexity threshold from eslint.config.js
6
+ * Returns the maximum threshold value found (different file types
7
+ * can have different thresholds)
8
+ * @param {string} projectRoot - Root directory of the project
9
+ * @returns {number} Maximum complexity threshold value
10
+ */
11
+ export function getComplexityThreshold(projectRoot) {
12
+ const configPath = resolve(projectRoot, 'eslint.config.js');
13
+
14
+ try {
15
+ const configContent = readFileSync(configPath, 'utf-8');
16
+
17
+ // Extract all complexity max values using regex
18
+ // Pattern: complexity: ["warn", { max: <number>, variant: "classic" }]
19
+ const complexityMatches = configContent.match(/complexity:\s*\["warn",\s*\{\s*max:\s*(\d+)/g);
20
+
21
+ if (!complexityMatches || complexityMatches.length === 0) {
22
+ // Default to 10 if not found
23
+ console.warn('Could not find complexity threshold in eslint.config.js, defaulting to 10');
24
+ return 10;
25
+ }
26
+
27
+ // Extract all max values
28
+ const maxValues = complexityMatches.map((match) => {
29
+ const valueMatch = match.match(/max:\s*(\d+)/);
30
+ return valueMatch ? parseInt(valueMatch[1], 10) : null;
31
+ }).filter((val) => val !== null);
32
+
33
+ if (maxValues.length === 0) {
34
+ console.warn('Could not parse complexity threshold values, defaulting to 10');
35
+ return 10;
36
+ }
37
+
38
+ // Return the maximum threshold value (to be safe, use the highest)
39
+ const maxThreshold = Math.max(...maxValues);
40
+ return maxThreshold;
41
+ } catch (error) {
42
+ console.warn(`Error reading eslint.config.js: ${error.message}, defaulting to 10`);
43
+ return 10;
44
+ }
45
+ }
package/package.json ADDED
@@ -0,0 +1,64 @@
1
+ {
2
+ "name": "@pythonidaer/complexity-report",
3
+ "version": "1.0.2",
4
+ "description": "AST-based cyclomatic complexity analyzer with interactive HTML reports and detailed function breakdowns",
5
+ "type": "module",
6
+ "main": "index.js",
7
+ "bin": {
8
+ "complexity-report": "report/cli.js"
9
+ },
10
+ "engines": {
11
+ "node": ">=18"
12
+ },
13
+ "scripts": {
14
+ "test": "vitest",
15
+ "test:coverage": "vitest --coverage",
16
+ "report": "node report/cli.js"
17
+ },
18
+ "files": [
19
+ "index.js",
20
+ "report/",
21
+ "integration/",
22
+ "function-boundaries/",
23
+ "function-extraction/",
24
+ "decision-points/",
25
+ "html-generators/",
26
+ "export-generators/",
27
+ "assets/",
28
+ "complexity-breakdown.js",
29
+ "function-hierarchy.js",
30
+ "README.md",
31
+ "CHANGELOG.md",
32
+ "LICENSE"
33
+ ],
34
+ "keywords": [
35
+ "complexity",
36
+ "cyclomatic-complexity",
37
+ "eslint",
38
+ "code-quality",
39
+ "static-analysis",
40
+ "ast",
41
+ "javascript",
42
+ "typescript",
43
+ "code-metrics",
44
+ "complexity-report"
45
+ ],
46
+ "author": "Johnny Hammond",
47
+ "license": "MIT",
48
+ "repository": {
49
+ "type": "git",
50
+ "url": "git+https://github.com/Pythonidaer/complexity-report.git"
51
+ },
52
+ "homepage": "https://github.com/Pythonidaer/complexity-report#readme",
53
+ "dependencies": {
54
+ "eslint": "^9.0.0",
55
+ "@typescript-eslint/typescript-estree": "^8.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "vitest": "^2.0.0",
59
+ "@vitest/coverage-v8": "^2.0.0"
60
+ },
61
+ "peerDependencies": {
62
+ "eslint": ">=9.0.0"
63
+ }
64
+ }