Skip to content

Commit d73865c

Browse files
author
helabenkhalfallah
committed
[EVOL]: update documentation
1 parent 9075f5b commit d73865c

File tree

4 files changed

+206
-227
lines changed

4 files changed

+206
-227
lines changed

README.md

Lines changed: 134 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ The system computes:
2424
## 📥 Installation
2525

2626
```bash
27+
pnpm install -D code-health-meter
28+
yarn install -D code-health-meter
2729
npm install -D code-health-meter
2830
```
2931

@@ -34,12 +36,23 @@ npm install -D code-health-meter
3436

3537
---
3638

39+
## ⚡ Quick Start
40+
41+
```bash
42+
npx code-health-meter --srcDir "./tests/mock-project" --format json
43+
```
44+
45+
✅ Generates `CodeComplexityReport.json`, `CodeModularityReport.json`, and `jscpd-report.json` under `tests/output/`.
46+
47+
---
48+
3749
## 🚦 CLI Usage
3850

3951
Run the tool with:
4052

4153
```bash
4254
npx code-health-meter --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html
55+
pnpm code-health-meter --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html
4356
```
4457

4558
Supported formats: `html`, `json`, or both.
@@ -53,22 +66,22 @@ To replicate the analysis presented in the paper:
5366
```bash
5467
git clone https://github.com/helabenkhalfallah/code-health-meter.git
5568
cd code-health-meter
56-
npm install
57-
npm run scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html,json
69+
pnpm install
70+
pnpm scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html
5871
```
5972

6073
### Output:
6174

62-
📂 `tests/mock-json-scan/`
63-
- `code-complexity-audit/CodeComplexityReport.json`
64-
- `code-modularity-audit/CodeModularityReport.json`
65-
- `code-modularity-audit/CodeModularityReport.svg`
75+
📂 `tests/mock-json-scan/`
76+
- `code-complexity-audit/CodeComplexityReport.json`
77+
- `code-modularity-audit/CodeModularityReport.json`
78+
- `code-modularity-audit/CodeModularityReport.svg`
6679
- `code-duplication-audit/jscpd-report.json`
6780

68-
📂 `tests/mock-html-scan/`
69-
- `code-complexity-audit/CodeComplexityReport.html`
70-
- `code-modularity-audit/CodeModularityReport.html`
71-
- `code-duplication-audit/html/index.html`
81+
📂 `tests/mock-html-scan/`
82+
- `code-complexity-audit/CodeComplexityReport.html`
83+
- `code-modularity-audit/CodeModularityReport.html`
84+
- `code-duplication-audit/html/index.html`
7285
- Additional styled UI in `styles/` and `js/`
7386

7487
> Note on Scale and Reproducibility: The included tests/mock-project is a simplified version intended for demonstration and functional validation of the Code Health Meter (CHM) framework. The original system evaluated in the TOSEM paper comprises approximately 14,000 lines of JavaScript/TypeScript code across 221 modules. Due to size and licensing constraints, that full system is not distributed as part of this artifact. However, the provided mock-project, along with the structured output reports, fully reproduces the CHM analysis pipeline, including complexity metrics, duplication detection, and graph-based modularity assessments.
@@ -85,6 +98,99 @@ npm run scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --form
8598

8699
---
87100

101+
## 🧩 Reusable APIs (Programmatic)
102+
103+
> **Analyzed entries vs raw files**: per-metric builders operate on **analyzed entries** produced by a single `inspectDirectory()` pass (not on raw file paths). The term _entries_ is used below to make this explicit.
104+
105+
### Complexity — per-metric builders
106+
107+
```js
108+
// src/kernel/complexity/CodeComplexityMetrics.js
109+
import {
110+
buildMaintainabilityReports, // MI
111+
buildSLOCReports, // SLOC
112+
buildCyclomaticReports, // CC
113+
buildHalsteadMetricReports // Halstead
114+
} from "./src/kernel/complexity/CodeComplexityMetrics.js";
115+
116+
// entries: array produced by a single inspectDirectory() pass
117+
const reports = [
118+
...buildMaintainabilityReports(entries),
119+
...buildSLOCReports(entries),
120+
...buildCyclomaticReports(entries),
121+
...buildHalsteadMetricReports(entries),
122+
];
123+
```
124+
125+
**Full complexity report (composer):**
126+
127+
```js
128+
// src/kernel/complexity/CodeComplexityBuilder.js
129+
import { buildFullComplexityReport } from "./src/kernel/complexity/CodeComplexityBuilder.js";
130+
131+
const { summary, auditReports } = buildFullComplexityReport({
132+
entries, // from inspectDirectory()
133+
metricIds: ["mi","sloc","cyclo","hal"],
134+
summaryBase, // aggregates you already computed
135+
buildAuditStats, // categorization helper
136+
});
137+
```
138+
139+
**Producing analyzed entries:**
140+
141+
```js
142+
// src/kernel/complexity/CodeComplexityUtils.js
143+
import { inspectDirectory } from "./src/kernel/complexity/CodeComplexityUtils.js";
144+
145+
const { summary, files: entries } = inspectDirectory({
146+
srcDir: "./tests/mock-project",
147+
options: {/* parser / analyzer options */}
148+
});
149+
```
150+
151+
### Modularity — graph metrics
152+
153+
```js
154+
// src/kernel/modularity/CodeModularityUtils.js, CodeModularityMetrics.js
155+
import {
156+
buildDirectoryTree, // Madge: obj() + svg()
157+
buildLouvainGraph, // Graphology graph (directed)
158+
} from "./src/kernel/modularity/CodeModularityUtils.js";
159+
import {
160+
detectCommunities, // Louvain communities + modularity
161+
readDensity,
162+
readDegreeCentralities
163+
} from "./src/kernel/modularity/CodeModularityMetrics.js";
164+
165+
const { tree, treeVisualization } = await buildDirectoryTree(".");
166+
const graph = await buildLouvainGraph(tree, treeVisualization);
167+
168+
const { modularity, communities } = detectCommunities(graph);
169+
const { density } = readDensity(graph);
170+
const { degreeCentrality, inDegreeCentrality, outDegreeCentrality } = readDegreeCentralities(graph);
171+
```
172+
173+
### Duplication — CLI + JSON output
174+
175+
The duplication auditor uses **jscpd** and writes JSON/HTML to `code-duplication-audit/`. Programmatic consumption can read and parse `jscpd-report.json`:
176+
177+
```js
178+
import fs from "fs";
179+
180+
const dup = JSON.parse(
181+
fs.readFileSync("./tests/output/code-duplication-audit/jscpd-report.json", "utf8")
182+
);
183+
184+
console.log("clones:", dup.total?.clones || dup.statistics?.total?.clones);
185+
```
186+
187+
#### Duplication — Fixed Reproducibility
188+
189+
The duplication module now correctly detects cloned code.
190+
✅ In the `tests/mock-project`, CHM identifies **1 clone of 6 lines**, matching the expected test results.
191+
192+
---
193+
88194
## 📚 Citation
89195
90196
```bibtex
@@ -99,22 +205,38 @@ npm run scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --form
99205
100206
---
101207
208+
## 🔍 Notes on Determinism & Reproducibility
209+
210+
- The dependency graph is **directed** by default; centralities are computed on this directed graph.
211+
- Louvain community detection is provided by Graphology; results are stable for a given codebase and toolchain.
212+
- CHM favors a **single-pass** pipeline for complexity (compute once, reuse entries across metrics).
213+
214+
---
215+
102216
## 🤝 Contributing
103217
104218
```bash
105219
git clone https://github.com/helabenkhalfallah/code-health-meter.git
106220
cd code-health-meter
107-
npm install
221+
pnpm install
108222
```
109223
110224
Run:
111225
112226
```bash
113-
npm run scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html,json
227+
pnpm scan --srcDir "./tests/mock-project" --outputDir "./tests/output" --format html,json
114228
```
115229
116230
---
117231
232+
## 📝 Response to Reviewers (TOSEM 2025 RCR)
233+
234+
- **Functional Badge**: Fixed duplication detection reproducibility (1 clone of 6 lines detected in `tests/mock-project`).
235+
- **Reusable Badge**: Exposed Cyclomatic Complexity and Halstead metrics as reusable functions. Documented rationale for metrics embedded in higher-level modules.
236+
- **Documentation**: Expanded test instructions, clarified programmatic APIs, and detailed reproducibility notes.
237+
238+
---
239+
118240
## 📜 License
119241
120242
Licensed under the MIT License. See the [LICENSE](./LICENSE) file.

src/kernel/complexity/CodeComplexityAuditor.js

Lines changed: 9 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -3,100 +3,23 @@ import { isAcceptedFileType, isExcludedFile } from '../../commons/AuditUtils.js'
33
import { buildAuditStats, buildFullComplexityReport } from './CodeComplexityBuilder.js';
44
import { inspectDirectory } from './CodeComplexityUtils.js';
55

6-
/**
7-
* Code Complexity Auditor — builds a full report while allowing per-metric reuse.
8-
*
9-
* Pipeline:
10-
* 1) inspectDirectory(): one-pass analysis (escomplex, SLOC, MI aggregates, per-file data).
11-
* 2) Filter auditable files (extensions/excludes).
12-
* 3) Compose per-metric reports using small, standalone metric builders.
13-
* 4) Merge stats into the summary (keeps original MI/SLOC totals/averages).
14-
*
15-
* Reviewers can import individual metrics (via CodeComplexityMetrics) without using this auditor.
16-
* This module preserves the original full-report behavior for CLI/HTML/JSON outputs.
17-
*
18-
* @module CodeComplexityAuditor
19-
* @see CodeComplexityBuilder#buildFullComplexityReport
20-
* @see CodeComplexityUtils#inspectDirectory
21-
*/
6+
/** @typedef {import('./CodeComplexityMetrics.js').AnalyzedFileEntry} AnalyzedFileEntry */
7+
/** @typedef {import('./CodeComplexityMetrics.js').MetricId} MetricId */
228

239
/**
24-
* String identifiers for per-file metrics available in the composer.
25-
* @typedef {'mi'|'sloc'|'cyclo'|'hal'} MetricId
26-
*/
27-
28-
/**
29-
* A single analyzed file entry produced upstream by inspectDirectory().
30-
* @typedef {Object} FileComplexityItem
31-
* @property {string} file
32-
* @property {number} fileMaintainability
33-
* @property {{ cyclomatic?: number, halstead?: Record<string, any> }} fileComplexity
34-
* @property {{ physical?: number, logical?: number }} fileSLOC
35-
*/
36-
37-
/**
38-
* A single formatted metric report entry (as produced by formatters in CodeComplexityConfig).
39-
* @typedef {Object} MetricReport
40-
* @property {string} title
41-
* @property {string} file
42-
* @property {number} [score]
43-
* @property {number} [scorePercent]
44-
* @property {string} [scoreUnit]
45-
*/
46-
47-
/**
48-
* Options for startComplexityAudit.
49-
* @typedef {Object} ComplexityAuditOptions
50-
* @property {{ ids?: MetricId[] }} [metrics] - Which per-metric builders to run.
51-
* @default { ids: ['mi','sloc','cyclo','hal'] }
52-
* @property {Object} [inspect] - Options forwarded to inspectDirectory (parser, etc.).
53-
*/
54-
55-
/**
56-
* Result of the complexity audit.
57-
* @typedef {Object} ComplexityAuditResult
58-
* @property {Object} summary - Combined summary (original aggregates + categorized stats).
59-
* @property {MetricReport[]} auditReports - Concatenated per-metric reports for all files.
60-
*/
61-
62-
/**
63-
* Start Code Complexity Audit (full report), while internally using per-metric builders.
64-
* Keeps the single-pass performance characteristics and exposes fine-grained metrics via
65-
* CodeComplexityMetrics.* for reusable consumption.
66-
*
67-
* Notes:
68-
* - Files are filtered by `isAcceptedFileType` / `isExcludedFile`, then sorted ascending
69-
* by `fileMaintainability`.
70-
* - On failure or when no files are found, this returns `{}` (not `null`) to preserve prior API.
10+
* Start Code Complexity Audit (full report), using per-metric builders internally.
11+
* Returns `{}` on failure to preserve prior API behavior.
7112
*
7213
* @async
7314
* @param {string} directory - Root directory to analyze.
74-
* @param {ComplexityAuditOptions|Object} options - Audit options. If you already used a flat
75-
* options bag, keep doing so; this function reads `options.metrics?.ids` when present and
76-
* forwards the rest to `inspectDirectory`.
77-
* @returns {Promise<ComplexityAuditResult|{}>} Full report `{ summary, auditReports }` or `{}` on failure.
78-
*
79-
* @example
80-
* // Full report with all metrics (default set)
81-
* const res = await startComplexityAudit('.', { inspect: { // escomplex opts } });
82-
*
83-
* @example
84-
* // Only MI + Cyclomatic
85-
* const res2 = await startComplexityAudit('.', { metrics: { ids: ['mi','cyclo'] } });
86-
*
87-
* @example
88-
* // Forward inspect options (e.g., TypeScript + JSX)
89-
* const res3 = await startComplexityAudit('.', {
90-
* inspect: { complexity: { // babel/typhon options } }
91-
* });
15+
* @param {Object} options - Options (forwarded to `inspectDirectory` if needed).
16+
* @returns {Promise<{summary:Object, auditReports:Object[]} | {}>}
9217
*/
9318
export const startComplexityAudit = async (directory, options) => {
9419
try {
9520
AppLogger.info(`[CodeComplexityAuditor - startAudit] directory: ${directory}`);
96-
9721
if (!directory?.length) return {};
9822

99-
// Support legacy: some callers pass all options directly; accept both {inspect} or flat
10023
const inspectOpts = options?.inspect ?? options;
10124

10225
const { summary, files } = inspectDirectory({
@@ -111,8 +34,8 @@ export const startComplexityAudit = async (directory, options) => {
11134

11235
if (!files?.length) return {};
11336

114-
/** @type {FileComplexityItem[]} */
115-
const auditableFiles = files
37+
/** @type {AnalyzedFileEntry[]} */
38+
const auditableEntries = files
11639
.filter((item) => {
11740
const fileName = item.file;
11841
return isAcceptedFileType(fileName) && !isExcludedFile(fileName);
@@ -123,7 +46,7 @@ export const startComplexityAudit = async (directory, options) => {
12346
const metricIds = options?.metrics?.ids ?? ['mi', 'sloc', 'cyclo', 'hal'];
12447

12548
const { summary: combinedSummary, auditReports } = buildFullComplexityReport({
126-
files: auditableFiles,
49+
entries: auditableEntries,
12750
metricIds,
12851
summaryBase: summary,
12952
buildAuditStats,

0 commit comments

Comments
 (0)