🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

oxlint-plugin-complexity

Package Overview
Dependencies
Maintainers
1
Versions
22
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

oxlint-plugin-complexity - npm Package Compare versions

Comparing version
2.0.1
to
2.0.2
+4
dist/analyze.d.ts
import { type ModuleAnalysisResult, type ModuleComplexityOptions } from './module/visitor.js';
/** Standalone module complexity analysis (no linting context required). */
export declare function analyzeModule(code: string, filename?: string, options?: ModuleComplexityOptions): ModuleAnalysisResult;
//# sourceMappingURL=analyze.d.ts.map
{"version":3,"file":"analyze.d.ts","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAIA,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,uBAAuB,EAC7B,MAAM,qBAAqB,CAAC;AA6E7B,2EAA2E;AAC3E,wBAAgB,aAAa,CAC3B,IAAI,EAAE,MAAM,EACZ,QAAQ,GAAE,MAAoB,EAC9B,OAAO,CAAC,EAAE,uBAAuB,GAChC,oBAAoB,CA4BtB"}
import { parseSync } from 'oxc-parser';
import { walk } from 'estree-walker';
import { createModuleAnalysisVisitor, } from './module/visitor.js';
function createLineOffsetTable(code) {
const lineOffsets = [0];
for (let i = 0; i < code.length; i++) {
if (code[i] === '\n') {
lineOffsets.push(i + 1);
}
}
return lineOffsets;
}
function offsetToLineCol(offset, lineOffsets) {
let lo = 0;
let hi = lineOffsets.length - 1;
while (lo < hi) {
const mid = (lo + hi + 1) >>> 1;
if (lineOffsets[mid] <= offset)
lo = mid;
else
hi = mid - 1;
}
return { line: lo + 1, column: offset - lineOffsets[lo] };
}
/** Single-pass AST walk: adds parent/loc references and dispatches visitor handlers. */
function walkAndDispatch(ast, code, visitor) {
const lineOffsets = createLineOffsetTable(code);
walk(ast, {
enter(node, parent) {
const esNode = node;
const raw = node;
if (typeof raw.start === 'number' && typeof raw.end === 'number') {
Object.defineProperty(esNode, 'loc', {
value: {
start: offsetToLineCol(raw.start, lineOffsets),
end: offsetToLineCol(raw.end, lineOffsets),
},
writable: true,
enumerable: false,
configurable: true,
});
}
Object.defineProperty(esNode, 'parent', {
value: parent,
writable: true,
enumerable: false,
configurable: true,
});
visitor[esNode.type]?.(esNode);
visitor['*']?.(esNode);
},
leave(node) {
const esNode = node;
visitor[`${esNode.type}:exit`]?.(esNode);
visitor['*:exit']?.(esNode);
},
});
}
function createLibraryContext(code) {
return {
sourceCode: {
text: code,
getText: () => code,
scopeManager: null,
getScope: () => null,
},
options: [],
report: () => { },
};
}
/** Standalone module complexity analysis (no linting context required). */
export function analyzeModule(code, filename = 'module.js', options) {
const { program, errors } = parseSync(filename, code);
if (errors.length > 0) {
throw new Error(`Parse errors in "${filename}": ${errors.map((e) => e.message).join(', ')}`);
}
const ast = program;
let result;
const visitor = createModuleAnalysisVisitor(createLibraryContext(code), (r) => {
result = r;
}, undefined, options);
walkAndDispatch(ast, code, visitor);
if (!result) {
throw new Error('Module analysis did not produce a result');
}
return result;
}
//# sourceMappingURL=analyze.js.map
{"version":3,"file":"analyze.js","sourceRoot":"","sources":["../src/analyze.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAGrC,OAAO,EACL,2BAA2B,GAG5B,MAAM,qBAAqB,CAAC;AAE7B,SAAS,qBAAqB,CAAC,IAAY;IACzC,MAAM,WAAW,GAAa,CAAC,CAAC,CAAC,CAAC;IAClC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACrC,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;YACrB,WAAW,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC;AAED,SAAS,eAAe,CAAC,MAAc,EAAE,WAAqB;IAC5D,IAAI,EAAE,GAAG,CAAC,CAAC;IACX,IAAI,EAAE,GAAG,WAAW,CAAC,MAAM,GAAG,CAAC,CAAC;IAChC,OAAO,EAAE,GAAG,EAAE,EAAE,CAAC;QACf,MAAM,GAAG,GAAG,CAAC,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC;QAChC,IAAI,WAAW,CAAC,GAAG,CAAC,IAAI,MAAM;YAAE,EAAE,GAAG,GAAG,CAAC;;YACpC,EAAE,GAAG,GAAG,GAAG,CAAC,CAAC;IACpB,CAAC;IACD,OAAO,EAAE,IAAI,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,WAAW,CAAC,EAAE,CAAC,EAAE,CAAC;AAC5D,CAAC;AAID,wFAAwF;AACxF,SAAS,eAAe,CAAC,GAAe,EAAE,IAAY,EAAE,OAA0B;IAChF,MAAM,WAAW,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAEhD,IAAI,CAAC,GAAuB,EAAE;QAC5B,KAAK,CAAC,IAAI,EAAE,MAAM;YAChB,MAAM,MAAM,GAAG,IAA6B,CAAC;YAC7C,MAAM,GAAG,GAAG,IAAmD,CAAC;YAEhE,IAAI,OAAO,GAAG,CAAC,KAAK,KAAK,QAAQ,IAAI,OAAO,GAAG,CAAC,GAAG,KAAK,QAAQ,EAAE,CAAC;gBACjE,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,KAAK,EAAE;oBACnC,KAAK,EAAE;wBACL,KAAK,EAAE,eAAe,CAAC,GAAG,CAAC,KAAK,EAAE,WAAW,CAAC;wBAC9C,GAAG,EAAE,eAAe,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC;qBAC3C;oBACD,QAAQ,EAAE,IAAI;oBACd,UAAU,EAAE,KAAK;oBACjB,YAAY,EAAE,IAAI;iBACnB,CAAC,CAAC;YACL,CAAC;YAED,MAAM,CAAC,cAAc,CAAC,MAAM,EAAE,QAAQ,EAAE;gBACtC,KAAK,EAAE,MAA+B;gBACtC,QAAQ,EAAE,IAAI;gBACd,UAAU,EAAE,KAAK;gBACjB,YAAY,EAAE,IAAI;aACnB,CAAC,CAAC;YAEH,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YAC/B,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QACzB,CAAC;QACD,KAAK,CAAC,IAAI;YACR,MAAM,MAAM,GAAG,IAA6B,CAAC;YAC7C,OAAO,CAAC,GAAG,MAAM,CAAC,IAAI,OAAO,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;YACzC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,MAAM,CAAC,CAAC;QAC9B,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,SAAS,oBAAoB,CAAC,IAAY;IACxC,OAAO;QACL,UAAU,EAAE;YACV,IAAI,EAAE,IAAI;YACV,OAAO,EAAE,GAAG,EAAE,CAAC,IAAI;YACnB,YAAY,EAAE,IAAI;YAClB,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;SACrB;QACD,OAAO,EAAE,EAAE;QACX,MAAM,EAAE,GAAG,EAAE,GAAE,CAAC;KACK,CAAC;AAC1B,CAAC;AAED,2EAA2E;AAC3E,MAAM,UAAU,aAAa,CAC3B,IAAY,EACZ,WAAmB,WAAW,EAC9B,OAAiC;IAEjC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,SAAS,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAEtD,IAAI,MAAM,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtB,MAAM,IAAI,KAAK,CACb,oBAAoB,QAAQ,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,CAAsB,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjG,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,OAAgC,CAAC;IAC7C,IAAI,MAAwC,CAAC;IAE7C,MAAM,OAAO,GAAG,2BAA2B,CACzC,oBAAoB,CAAC,IAAI,CAAC,EAC1B,CAAC,CAAC,EAAE,EAAE;QACJ,MAAM,GAAG,CAAC,CAAC;IACb,CAAC,EACD,SAAS,EACT,OAAO,CACR,CAAC;IAEF,eAAe,CAAC,GAAG,EAAE,IAAI,EAAE,OAA4B,CAAC,CAAC;IAEzD,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
/**
* Raw operator/operand frequency maps collected during AST traversal.
*/
export interface HalsteadCounts {
/** Operator identifier -> occurrence count */
operators: Map<string, number>;
/** Operand identifier -> occurrence count */
operands: Map<string, number>;
}
/**
* Computed Halstead complexity metrics derived from operator/operand counts.
*
* Formulas (Halstead, 1977):
* length = N1 + N2
* vocabulary = n1 + n2
* volume = length * log2(vocabulary)
* difficulty = (n1 / 2) * (N2 / n2)
* effort = difficulty * volume
* bugs = volume / 3000
* time = effort / 18 (seconds)
*/
export interface HalsteadMetrics {
/** Unique operator count */
n1: number;
/** Unique operand count */
n2: number;
/** Total operator occurrences */
N1: number;
/** Total operand occurrences */
N2: number;
/** Program length: N1 + N2 */
length: number;
/** Program vocabulary: n1 + n2 */
vocabulary: number;
/** Program volume: length * log2(vocabulary) */
volume: number;
/** Difficulty: (n1/2) * (N2/n2) */
difficulty: number;
/** Effort: difficulty * volume */
effort: number;
/** Estimated bugs: volume / 3000 */
bugs: number;
/** Estimated time to understand (seconds): effort / 18 */
time: number;
}
/** Increment a count in a Map by a given amount. */
export declare function incrementCount(map: Map<string, number>, key: string, amount?: number): void;
/**
* Calculate Halstead metrics from raw operator/operand counts.
* Pure math -- no AST dependency.
*/
export declare function calculateHalsteadMetrics(counts: HalsteadCounts): HalsteadMetrics;
export declare function createHalsteadCounts(): HalsteadCounts;
/** Merge source counts into target (mutates target). */
export declare function mergeHalsteadCounts(target: HalsteadCounts, source: HalsteadCounts): void;
//# sourceMappingURL=calculate.d.ts.map
{"version":3,"file":"calculate.d.ts","sourceRoot":"","sources":["../../src/halstead/calculate.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd;AAED,oDAAoD;AACpD,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,IAAI,CAE9F;AAED;;;GAGG;AACH,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAehF;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAED,wDAAwD;AACxD,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAOxF"}
/** Increment a count in a Map by a given amount. */
export function incrementCount(map, key, amount = 1) {
map.set(key, (map.get(key) ?? 0) + amount);
}
/**
* Calculate Halstead metrics from raw operator/operand counts.
* Pure math -- no AST dependency.
*/
export function calculateHalsteadMetrics(counts) {
const n1 = counts.operators.size;
const n2 = counts.operands.size;
const N1 = sumValues(counts.operators);
const N2 = sumValues(counts.operands);
const length = N1 + N2;
const vocabulary = n1 + n2;
const volume = vocabulary > 0 ? length * Math.log2(vocabulary) : 0;
const difficulty = n2 > 0 ? (n1 / 2) * (N2 / n2) : 0;
const effort = difficulty * volume;
const bugs = volume / 3000;
const time = effort / 18;
return { n1, n2, N1, N2, length, vocabulary, volume, difficulty, effort, bugs, time };
}
export function createHalsteadCounts() {
return { operators: new Map(), operands: new Map() };
}
/** Merge source counts into target (mutates target). */
export function mergeHalsteadCounts(target, source) {
for (const [key, count] of source.operators) {
incrementCount(target.operators, key, count);
}
for (const [key, count] of source.operands) {
incrementCount(target.operands, key, count);
}
}
function sumValues(map) {
let total = 0;
for (const count of map.values()) {
total += count;
}
return total;
}
//# sourceMappingURL=calculate.js.map
{"version":3,"file":"calculate.js","sourceRoot":"","sources":["../../src/halstead/calculate.ts"],"names":[],"mappings":"AA+CA,oDAAoD;AACpD,MAAM,UAAU,cAAc,CAAC,GAAwB,EAAE,GAAW,EAAE,SAAiB,CAAC;IACtF,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC7D,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;IACjC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAChC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC;IAEzB,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AACvD,CAAC;AAED,wDAAwD;AACxD,MAAM,UAAU,mBAAmB,CAAC,MAAsB,EAAE,MAAsB;IAChF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5C,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC3C,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAwB;IACzC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QACjC,KAAK,IAAI,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
export type { HalsteadCounts, HalsteadMetrics } from './types.js';
export { calculateHalsteadMetrics, createHalsteadCounts, incrementCount, mergeHalsteadCounts, } from './calculate.js';
export { createHalsteadVisitorHandlers } from './visitor.js';
//# sourceMappingURL=index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/halstead/index.ts"],"names":[],"mappings":"AAAA,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAClE,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC"}
export { calculateHalsteadMetrics, createHalsteadCounts, incrementCount, mergeHalsteadCounts, } from './calculate.js';
export { createHalsteadVisitorHandlers } from './visitor.js';
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/halstead/index.ts"],"names":[],"mappings":"AACA,OAAO,EACL,wBAAwB,EACxB,oBAAoB,EACpB,cAAc,EACd,mBAAmB,GACpB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,6BAA6B,EAAE,MAAM,cAAc,CAAC"}
/**
* Raw operator/operand frequency maps collected during AST traversal.
*/
export interface HalsteadCounts {
/** Operator identifier -> occurrence count */
operators: Map<string, number>;
/** Operand identifier -> occurrence count */
operands: Map<string, number>;
}
/**
* Computed Halstead complexity metrics derived from operator/operand counts.
*
* Formulas (Halstead, 1977):
* length = N1 + N2
* vocabulary = n1 + n2
* volume = length * log2(vocabulary)
* difficulty = (n1 / 2) * (N2 / n2)
* effort = difficulty * volume
* bugs = volume / 3000
* time = effort / 18 (seconds)
*/
export interface HalsteadMetrics {
/** Unique operator count */
n1: number;
/** Unique operand count */
n2: number;
/** Total operator occurrences */
N1: number;
/** Total operand occurrences */
N2: number;
/** Program length: N1 + N2 */
length: number;
/** Program vocabulary: n1 + n2 */
vocabulary: number;
/** Program volume: length * log2(vocabulary) */
volume: number;
/** Difficulty: (n1/2) * (N2/n2) */
difficulty: number;
/** Effort: difficulty * volume */
effort: number;
/** Estimated bugs: volume / 3000 */
bugs: number;
/** Estimated time to understand (seconds): effort / 18 */
time: number;
}
//# sourceMappingURL=types.d.ts.map
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/halstead/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,6CAA6C;IAC7C,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,4BAA4B;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,2BAA2B;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,iCAAiC;IACjC,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,gDAAgD;IAChD,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,kCAAkC;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAC;CACd"}
export {};
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/halstead/types.ts"],"names":[],"mappings":""}
/**
* Halstead operator/operand classification for JavaScript/TypeScript AST nodes.
*
* Classification based on escomplex's es5.js (MIT), extended for modern JS/TS.
*/
import type { ESTreeNode } from '../types.js';
import type { HalsteadCounts } from './calculate.js';
export interface HalsteadVisitorCallbacks {
onFunctionEnter?: () => void;
onFunctionExit?: (counts: HalsteadCounts) => void;
}
/**
* Create visitor handlers that classify AST nodes into Halstead operators and operands.
*
* Maintains a scope stack mirroring function boundaries, plus a module-level accumulator
* that always receives every operator/operand.
*/
export declare function createHalsteadVisitorHandlers(callbacks?: HalsteadVisitorCallbacks): {
handlers: Record<string, (node: ESTreeNode) => void>;
moduleCounts: HalsteadCounts;
};
//# sourceMappingURL=visitor.d.ts.map
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/halstead/visitor.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAGrD,MAAM,WAAW,wBAAwB;IACvC,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAgED;;;;;GAKG;AAEH,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,wBAAwB,GAAG;IACnF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC;IACrD,YAAY,EAAE,cAAc,CAAC;CAC9B,CAuKA"}
import { createHalsteadCounts, incrementCount } from './calculate.js';
/** Extract the operator string from a node with an `operator` property. */
function getNodeOperator(node) {
return node.operator;
}
/** Check if a node is a non-computed key/property of its parent. */
function isNonComputedKeyOf(node, parentType) {
const parent = node.parent;
if (parent?.type !== parentType)
return false;
const keyed = parent;
if (keyed.computed)
return false;
return keyed.key === node || keyed.property === node;
}
/** Declaration-site parent types mapped to their identifier-holding property. */
const DECLARATION_PARENTS = [
{ type: 'VariableDeclarator', prop: 'id' },
{ type: 'ImportSpecifier', prop: 'local' },
{ type: 'ImportDefaultSpecifier', prop: 'local' },
{ type: 'ImportNamespaceSpecifier', prop: 'local' },
{ type: 'CatchClause', prop: 'param' },
{ type: 'LabeledStatement', prop: 'label' },
];
/**
* Check if an Identifier is at a declaration site (should be skipped as operand).
*/
// eslint-disable-next-line complexity/complexity -- Type-dispatch across many AST parent types
function isDeclarationIdentifier(node) {
const parent = node.parent;
if (!parent)
return false;
const n = node;
// Function declaration/expression: check id and params
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
const fn = parent;
return fn.id?.name === n.name || (fn.params?.includes(node) ?? false);
}
// Arrow function: check params
if (parent.type === 'ArrowFunctionExpression') {
const arrow = parent;
return arrow.params?.includes(node) ?? false;
}
// Class declaration/expression: check id
if (parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression') {
const cls = parent;
return cls.id?.name === n.name;
}
// Table-driven check for simple "is node === parent.prop" patterns
for (const { type, prop } of DECLARATION_PARENTS) {
if (parent.type === type) {
return parent[prop] === node;
}
}
return false;
}
/**
* Create visitor handlers that classify AST nodes into Halstead operators and operands.
*
* Maintains a scope stack mirroring function boundaries, plus a module-level accumulator
* that always receives every operator/operand.
*/
// eslint-disable-next-line complexity/complexity -- Visitor factory pattern requires many nested handlers
export function createHalsteadVisitorHandlers(callbacks) {
const moduleCounts = createHalsteadCounts();
const scopeStack = [];
function currentScope() {
return scopeStack[scopeStack.length - 1];
}
function addOperator(name) {
incrementCount(moduleCounts.operators, name);
const scope = currentScope();
if (scope) {
incrementCount(scope.operators, name);
}
}
function addOperand(name) {
incrementCount(moduleCounts.operands, name);
const scope = currentScope();
if (scope) {
incrementCount(scope.operands, name);
}
}
function enterFunction() {
scopeStack.push(createHalsteadCounts());
callbacks?.onFunctionEnter?.();
}
function exitFunction() {
const scope = scopeStack.pop();
if (scope) {
callbacks?.onFunctionExit?.(scope);
}
}
/** Helper to create a simple handler that adds a fixed operator. */
function operatorHandler(name) {
return () => addOperator(name);
}
/** Helper to create a handler that reads the operator from the node. */
function nodeOperatorHandler() {
return (node) => addOperator(getNodeOperator(node));
}
const handlers = {
// --- Function boundaries ---
FunctionDeclaration(node) {
enterFunction();
addOperator('function');
const fn = node;
if (fn.id?.name)
addOperand(fn.id.name);
},
'FunctionDeclaration:exit': () => exitFunction(),
FunctionExpression(node) {
enterFunction();
addOperator('function');
const fn = node;
if (fn.id?.name)
addOperand(fn.id.name);
},
'FunctionExpression:exit': () => exitFunction(),
ArrowFunctionExpression() {
enterFunction();
addOperator('=>');
},
'ArrowFunctionExpression:exit': () => exitFunction(),
// --- Control flow operators ---
IfStatement: operatorHandler('if'),
'IfStatement:exit'(node) {
const ifNode = node;
if (ifNode.alternate)
addOperator('else');
},
ForStatement: operatorHandler('for'),
ForInStatement: operatorHandler('forin'),
ForOfStatement: operatorHandler('forof'),
WhileStatement: operatorHandler('while'),
DoWhileStatement: operatorHandler('dowhile'),
SwitchStatement: operatorHandler('switch'),
SwitchCase(node) {
const sc = node;
addOperator(sc.test !== null ? 'case' : 'default');
},
CatchClause: operatorHandler('catch'),
BreakStatement: operatorHandler('break'),
ContinueStatement: operatorHandler('continue'),
ReturnStatement: operatorHandler('return'),
ThrowStatement: operatorHandler('throw'),
// --- Expressions ---
NewExpression: operatorHandler('new'),
ClassDeclaration: operatorHandler('class'),
ClassExpression: operatorHandler('class'),
ImportDeclaration: operatorHandler('import'),
ExportNamedDeclaration: operatorHandler('export'),
ExportDefaultDeclaration: operatorHandler('export'),
AwaitExpression: operatorHandler('await'),
YieldExpression: operatorHandler('yield'),
// Binary, logical, assignment, unary, update -- use the node's operator string
BinaryExpression: nodeOperatorHandler(),
LogicalExpression: nodeOperatorHandler(),
AssignmentExpression: nodeOperatorHandler(),
UnaryExpression: nodeOperatorHandler(),
UpdateExpression: nodeOperatorHandler(),
CallExpression: operatorHandler('()'),
ArrayExpression: operatorHandler('[]'),
ObjectExpression: operatorHandler('{}'),
MemberExpression(node) {
const member = node;
addOperator(member.computed ? '[]' : member.optional ? '?.' : '.');
},
Property: operatorHandler(':'),
ConditionalExpression: operatorHandler('?:'),
VariableDeclaration(node) {
const decl = node;
addOperator(decl.kind);
},
VariableDeclarator(node) {
const declarator = node;
if (declarator.init !== null && declarator.init !== undefined) {
addOperator('=');
}
const id = declarator.id;
if (id.type === 'Identifier' && id.name) {
addOperand(id.name);
}
},
TemplateLiteral: operatorHandler('`'),
SpreadElement: operatorHandler('...'),
RestElement: operatorHandler('...'),
// --- Operands ---
Identifier(node) {
if (isDeclarationIdentifier(node))
return;
if (isNonComputedKeyOf(node, 'MemberExpression'))
return;
if (isNonComputedKeyOf(node, 'Property'))
return;
if (isNonComputedKeyOf(node, 'MethodDefinition'))
return;
addOperand(node.name);
},
Literal(node) {
const lit = node;
addOperand(lit.raw ?? String(lit.value));
},
ThisExpression() {
addOperand('this');
},
TemplateElement(node) {
const elem = node;
if (elem.value.raw) {
addOperand(elem.value.raw);
}
},
};
return { handlers, moduleCounts };
}
//# sourceMappingURL=visitor.js.map
{"version":3,"file":"visitor.js","sourceRoot":"","sources":["../../src/halstead/visitor.ts"],"names":[],"mappings":"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"}
/**
* Halstead operator/operand classification for JavaScript/TypeScript AST nodes.
* Based on escomplex's es5.js (MIT), extended for modern JS/TS.
*/
import type { ESTreeNode } from '../types.js';
import type { HalsteadCounts } from './halstead.js';
export interface HalsteadVisitorCallbacks {
onFunctionEnter?: () => void;
onFunctionExit?: (counts: HalsteadCounts) => void;
}
export declare function createHalsteadVisitorHandlers(callbacks?: HalsteadVisitorCallbacks): {
handlers: Record<string, (node: ESTreeNode) => void>;
moduleCounts: HalsteadCounts;
};
//# sourceMappingURL=halstead-visitor.d.ts.map
{"version":3,"file":"halstead-visitor.d.ts","sourceRoot":"","sources":["../../src/module/halstead-visitor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAC9C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAGpD,MAAM,WAAW,wBAAwB;IACvC,eAAe,CAAC,EAAE,MAAM,IAAI,CAAC;IAC7B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAwDD,wBAAgB,6BAA6B,CAAC,SAAS,CAAC,EAAE,wBAAwB,GAAG;IACnF,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC,CAAC;IACrD,YAAY,EAAE,cAAc,CAAC;CAC9B,CA4JA"}
import { createHalsteadCounts, incrementCount } from './halstead.js';
function getNodeOperator(node) {
return node.operator;
}
/** Caller must pre-check that parent type is MemberExpression, Property, or MethodDefinition. */
function isNonComputedKeyOf(node) {
const parent = node.parent;
if (!parent)
return false;
const keyed = parent;
if (keyed.computed)
return false;
return keyed.key === node || keyed.property === node;
}
const DECLARATION_PARENTS = new Map([
['VariableDeclarator', 'id'],
['ImportSpecifier', 'local'],
['ImportDefaultSpecifier', 'local'],
['ImportNamespaceSpecifier', 'local'],
['CatchClause', 'param'],
['LabeledStatement', 'label'],
]);
/** Skip identifiers at declaration sites (they are not operands). */
// eslint-disable-next-line complexity/complexity -- Type-dispatch across many AST parent types
function isDeclarationIdentifier(node) {
const parent = node.parent;
if (!parent)
return false;
const n = node;
if (parent.type === 'FunctionDeclaration' || parent.type === 'FunctionExpression') {
const fn = parent;
return fn.id?.name === n.name || (fn.params?.includes(node) ?? false);
}
if (parent.type === 'ArrowFunctionExpression') {
const arrow = parent;
return arrow.params?.includes(node) ?? false;
}
if (parent.type === 'ClassDeclaration' || parent.type === 'ClassExpression') {
const cls = parent;
return cls.id?.name === n.name;
}
const prop = DECLARATION_PARENTS.get(parent.type);
if (prop) {
return parent[prop] === node;
}
return false;
}
// eslint-disable-next-line complexity/complexity -- Visitor factory pattern requires many nested handlers
export function createHalsteadVisitorHandlers(callbacks) {
const moduleCounts = createHalsteadCounts();
const scopeStack = [];
function currentScope() {
return scopeStack[scopeStack.length - 1];
}
function addCount(kind, name) {
incrementCount(moduleCounts[kind], name);
const scope = currentScope();
if (scope) {
incrementCount(scope[kind], name);
}
}
function addOperator(name) {
addCount('operators', name);
}
function addOperand(name) {
addCount('operands', name);
}
function enterFunction() {
scopeStack.push(createHalsteadCounts());
callbacks?.onFunctionEnter?.();
}
function exitFunction() {
const scope = scopeStack.pop();
if (scope) {
callbacks?.onFunctionExit?.(scope);
}
}
function operatorHandler(name) {
return () => addOperator(name);
}
function nodeOperatorHandler() {
return (node) => addOperator(getNodeOperator(node));
}
function handleNamedFunction(node) {
enterFunction();
addOperator('function');
const fn = node;
if (fn.id?.name)
addOperand(fn.id.name);
}
const handlers = {
FunctionDeclaration: handleNamedFunction,
'FunctionDeclaration:exit': () => exitFunction(),
FunctionExpression: handleNamedFunction,
'FunctionExpression:exit': () => exitFunction(),
ArrowFunctionExpression() {
enterFunction();
addOperator('=>');
},
'ArrowFunctionExpression:exit': () => exitFunction(),
IfStatement: operatorHandler('if'),
'IfStatement:exit'(node) {
const ifNode = node;
if (ifNode.alternate)
addOperator('else');
},
ForStatement: operatorHandler('for'),
ForInStatement: operatorHandler('forin'),
ForOfStatement: operatorHandler('forof'),
WhileStatement: operatorHandler('while'),
DoWhileStatement: operatorHandler('dowhile'),
SwitchStatement: operatorHandler('switch'),
SwitchCase(node) {
const sc = node;
addOperator(sc.test !== null ? 'case' : 'default');
},
CatchClause: operatorHandler('catch'),
BreakStatement: operatorHandler('break'),
ContinueStatement: operatorHandler('continue'),
ReturnStatement: operatorHandler('return'),
ThrowStatement: operatorHandler('throw'),
NewExpression: operatorHandler('new'),
ClassDeclaration: operatorHandler('class'),
ClassExpression: operatorHandler('class'),
ImportDeclaration: operatorHandler('import'),
ExportNamedDeclaration: operatorHandler('export'),
ExportDefaultDeclaration: operatorHandler('export'),
AwaitExpression: operatorHandler('await'),
YieldExpression: operatorHandler('yield'),
BinaryExpression: nodeOperatorHandler(),
LogicalExpression: nodeOperatorHandler(),
AssignmentExpression: nodeOperatorHandler(),
UnaryExpression: nodeOperatorHandler(),
UpdateExpression: nodeOperatorHandler(),
CallExpression: operatorHandler('()'),
ArrayExpression: operatorHandler('[]'),
ObjectExpression: operatorHandler('{}'),
MemberExpression(node) {
const member = node;
addOperator(member.computed ? '[]' : member.optional ? '?.' : '.');
},
Property: operatorHandler(':'),
ConditionalExpression: operatorHandler('?:'),
VariableDeclaration(node) {
const decl = node;
addOperator(decl.kind);
},
VariableDeclarator(node) {
const declarator = node;
if (declarator.init !== null && declarator.init !== undefined) {
addOperator('=');
}
const id = declarator.id;
if (id.type === 'Identifier' && id.name) {
addOperand(id.name);
}
},
TemplateLiteral: operatorHandler('`'),
SpreadElement: operatorHandler('...'),
RestElement: operatorHandler('...'),
Identifier(node) {
if (isDeclarationIdentifier(node))
return;
const pt = node.parent?.type;
if (pt === 'MemberExpression' || pt === 'Property' || pt === 'MethodDefinition') {
if (isNonComputedKeyOf(node))
return;
}
addOperand(node.name);
},
Literal(node) {
const lit = node;
addOperand(lit.raw ?? String(lit.value));
},
ThisExpression() {
addOperand('this');
},
TemplateElement(node) {
const elem = node;
if (elem.value.raw) {
addOperand(elem.value.raw);
}
},
};
return { handlers, moduleCounts };
}
//# sourceMappingURL=halstead-visitor.js.map
{"version":3,"file":"halstead-visitor.js","sourceRoot":"","sources":["../../src/module/halstead-visitor.ts"],"names":[],"mappings":"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"}
export interface HalsteadCounts {
operators: Map<string, number>;
operands: Map<string, number>;
}
/**
* Halstead metrics derived from operator/operand counts.
*
* Formulas (Halstead, 1977):
* length = N1 + N2
* vocabulary = n1 + n2
* volume = length * log2(vocabulary)
* difficulty = (n1 / 2) * (N2 / n2)
* effort = difficulty * volume
* bugs = volume / 3000
* time = effort / 18 (seconds)
*/
export interface HalsteadMetrics {
n1: number;
n2: number;
N1: number;
N2: number;
length: number;
vocabulary: number;
volume: number;
difficulty: number;
effort: number;
bugs: number;
time: number;
}
export declare function incrementCount(map: Map<string, number>, key: string, amount?: number): void;
export declare function calculateHalsteadMetrics(counts: HalsteadCounts): HalsteadMetrics;
export declare function createHalsteadCounts(): HalsteadCounts;
export declare function mergeHalsteadCounts(target: HalsteadCounts, source: HalsteadCounts): void;
//# sourceMappingURL=halstead.d.ts.map
{"version":3,"file":"halstead.d.ts","sourceRoot":"","sources":["../../src/module/halstead.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC/B,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC/B;AAED;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,MAAU,GAAG,IAAI,CAE9F;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAehF;AAED,wBAAgB,oBAAoB,IAAI,cAAc,CAErD;AAED,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,cAAc,GAAG,IAAI,CAOxF"}
export function incrementCount(map, key, amount = 1) {
map.set(key, (map.get(key) ?? 0) + amount);
}
export function calculateHalsteadMetrics(counts) {
const n1 = counts.operators.size;
const n2 = counts.operands.size;
const N1 = sumValues(counts.operators);
const N2 = sumValues(counts.operands);
const length = N1 + N2;
const vocabulary = n1 + n2;
const volume = vocabulary > 0 ? length * Math.log2(vocabulary) : 0;
const difficulty = n2 > 0 ? (n1 / 2) * (N2 / n2) : 0;
const effort = difficulty * volume;
const bugs = volume / 3000;
const time = effort / 18;
return { n1, n2, N1, N2, length, vocabulary, volume, difficulty, effort, bugs, time };
}
export function createHalsteadCounts() {
return { operators: new Map(), operands: new Map() };
}
export function mergeHalsteadCounts(target, source) {
for (const [key, count] of source.operators) {
incrementCount(target.operators, key, count);
}
for (const [key, count] of source.operands) {
incrementCount(target.operands, key, count);
}
}
function sumValues(map) {
let total = 0;
for (const count of map.values()) {
total += count;
}
return total;
}
//# sourceMappingURL=halstead.js.map
{"version":3,"file":"halstead.js","sourceRoot":"","sources":["../../src/module/halstead.ts"],"names":[],"mappings":"AA+BA,MAAM,UAAU,cAAc,CAAC,GAAwB,EAAE,GAAW,EAAE,SAAiB,CAAC;IACtF,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,MAAsB;IAC7D,MAAM,EAAE,GAAG,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC;IACjC,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC;IAChC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,MAAM,EAAE,GAAG,SAAS,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;IAEtC,MAAM,MAAM,GAAG,EAAE,GAAG,EAAE,CAAC;IACvB,MAAM,UAAU,GAAG,EAAE,GAAG,EAAE,CAAC;IAC3B,MAAM,MAAM,GAAG,UAAU,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACnE,MAAM,UAAU,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrD,MAAM,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACnC,MAAM,IAAI,GAAG,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,IAAI,GAAG,MAAM,GAAG,EAAE,CAAC;IAEzB,OAAO,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;AACxF,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,EAAE,SAAS,EAAE,IAAI,GAAG,EAAE,EAAE,QAAQ,EAAE,IAAI,GAAG,EAAE,EAAE,CAAC;AACvD,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,MAAsB,EAAE,MAAsB;IAChF,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,SAAS,EAAE,CAAC;QAC5C,cAAc,CAAC,MAAM,CAAC,SAAS,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/C,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAC3C,cAAc,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;IAC9C,CAAC;AACH,CAAC;AAED,SAAS,SAAS,CAAC,GAAwB;IACzC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,GAAG,CAAC,MAAM,EAAE,EAAE,CAAC;QACjC,KAAK,IAAI,KAAK,CAAC;IACjB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
export type { FunctionMetrics, AggregateComplexity, ModuleAnalysisResult, ModuleComplexityOptions, } from './types.js';
export { calculateMaintainabilityIndex, computeMIInputs } from './maintainability.js';
export { createModuleAnalysisVisitor } from './visitor.js';
//# sourceMappingURL=index.d.ts.map
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/module/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,eAAe,EACf,mBAAmB,EACnB,oBAAoB,EACpB,uBAAuB,GACxB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
export { calculateMaintainabilityIndex, computeMIInputs } from './maintainability.js';
export { createModuleAnalysisVisitor } from './visitor.js';
//# sourceMappingURL=index.js.map
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/module/index.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,6BAA6B,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACtF,OAAO,EAAE,2BAA2B,EAAE,MAAM,cAAc,CAAC"}
import type { HalsteadMetrics } from '../halstead/types.js';
/**
* Calculate the Maintainability Index using the formula from escomplex (SIG model):
*
* MI = 171 - 3.42 * ln(avgEffort) - 0.23 * ln(avgCyclomatic) - 16.2 * ln(avgLOC)
* MI_scaled = max(0, MI * 100 / 171)
*
* If there are zero functions, the module is treated as one "function" using
* module-level Halstead metrics and total LOC.
*/
export declare function calculateMaintainabilityIndex(avgEffort: number, avgCyclomatic: number, avgLOC: number): {
raw: number;
scaled: number;
};
/**
* Compute average function metrics for MI calculation.
* If no functions, treats the whole module as one function.
*/
export declare function computeMIInputs(functionCount: number, totalCyclomatic: number, totalLOC: number, moduleHalstead: HalsteadMetrics, functionEffortSum: number): {
avgEffort: number;
avgCyclomatic: number;
avgLOC: number;
};
//# sourceMappingURL=maintainability.d.ts.map
{"version":3,"file":"maintainability.d.ts","sourceRoot":"","sources":["../../src/module/maintainability.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAE5D;;;;;;;;GAQG;AACH,wBAAgB,6BAA6B,CAC3C,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAUjC;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,eAAe,EAC/B,iBAAiB,EAAE,MAAM,GACxB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAc9D"}
/**
* Calculate the Maintainability Index using the formula from escomplex (SIG model):
*
* MI = 171 - 3.42 * ln(avgEffort) - 0.23 * ln(avgCyclomatic) - 16.2 * ln(avgLOC)
* MI_scaled = max(0, MI * 100 / 171)
*
* If there are zero functions, the module is treated as one "function" using
* module-level Halstead metrics and total LOC.
*/
export function calculateMaintainabilityIndex(avgEffort, avgCyclomatic, avgLOC) {
// Guard against log(0) — use 1 as minimum for ln arguments
const lnEffort = Math.log(Math.max(avgEffort, 1));
const lnCyclomatic = Math.log(Math.max(avgCyclomatic, 1));
const lnLOC = Math.log(Math.max(avgLOC, 1));
const raw = 171 - 3.42 * lnEffort - 0.23 * lnCyclomatic - 16.2 * lnLOC;
const scaled = Math.max(0, (raw * 100) / 171);
return { raw, scaled };
}
/**
* Compute average function metrics for MI calculation.
* If no functions, treats the whole module as one function.
*/
export function computeMIInputs(functionCount, totalCyclomatic, totalLOC, moduleHalstead, functionEffortSum) {
if (functionCount === 0) {
return {
avgEffort: moduleHalstead.effort,
avgCyclomatic: 1,
avgLOC: Math.max(totalLOC, 1),
};
}
return {
avgEffort: functionEffortSum / functionCount,
avgCyclomatic: totalCyclomatic / functionCount,
avgLOC: totalLOC / functionCount,
};
}
//# sourceMappingURL=maintainability.js.map
{"version":3,"file":"maintainability.js","sourceRoot":"","sources":["../../src/module/maintainability.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AACH,MAAM,UAAU,6BAA6B,CAC3C,SAAiB,EACjB,aAAqB,EACrB,MAAc;IAEd,2DAA2D;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC;IAClD,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,CAAC;IAC1D,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC;IAE5C,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,GAAG,QAAQ,GAAG,IAAI,GAAG,YAAY,GAAG,IAAI,GAAG,KAAK,CAAC;IACvE,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC;IAE9C,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,CAAC;AACzB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,aAAqB,EACrB,eAAuB,EACvB,QAAgB,EAChB,cAA+B,EAC/B,iBAAyB;IAEzB,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;QACxB,OAAO;YACL,SAAS,EAAE,cAAc,CAAC,MAAM;YAChC,aAAa,EAAE,CAAC;YAChB,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC;SAC9B,CAAC;IACJ,CAAC;IAED,OAAO;QACL,SAAS,EAAE,iBAAiB,GAAG,aAAa;QAC5C,aAAa,EAAE,eAAe,GAAG,aAAa;QAC9C,MAAM,EAAE,QAAQ,GAAG,aAAa;KACjC,CAAC;AACJ,CAAC"}
import type { HalsteadMetrics } from '../halstead/types.js';
import type { ComplexityPoint } from '../types.js';
export interface FunctionMetrics {
name: string;
lineStart: number;
lineEnd: number;
loc: number;
cyclomatic: number;
cognitive: number;
halstead: HalsteadMetrics;
cyclomaticPoints: ComplexityPoint[];
cognitivePoints: ComplexityPoint[];
}
export interface AggregateComplexity {
/** WMC-style total (sum of all function complexities) */
sum: number;
/** Worst hotspot */
max: number;
/** Average across functions */
average: number;
/** Number of functions above the threshold */
countAboveThreshold: number;
}
export interface ModuleAnalysisResult {
functions: FunctionMetrics[];
/** Module-wide Halstead metrics (all operators/operands across the file) */
halstead: HalsteadMetrics;
cyclomatic: AggregateComplexity;
cognitive: AggregateComplexity;
/** Raw Maintainability Index (0-171 scale) */
maintainabilityIndex: number;
/** Scaled Maintainability Index (0-100 scale) */
maintainabilityIndexScaled: number;
totalLOC: number;
functionCount: number;
}
export interface ModuleComplexityOptions {
/** Minimum MI (scaled 0-100). Default: 20 */
maintainabilityIndex?: number;
/** Maximum aggregate cyclomatic. Default: 0 (disabled) */
maxCyclomaticSum?: number;
/** Maximum aggregate cognitive. Default: 0 (disabled) */
maxCognitiveSum?: number;
/** Threshold for countAboveThreshold (cyclomatic). Default: 10 */
cyclomaticThreshold?: number;
/** Threshold for countAboveThreshold (cognitive). Default: 10 */
cognitiveThreshold?: number;
/** How many hotspot functions to show in diagnostics. Default: 3 */
topHotspots?: number;
}
//# sourceMappingURL=types.d.ts.map
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/module/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAC5D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,eAAe,EAAE,eAAe,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,yDAAyD;IACzD,GAAG,EAAE,MAAM,CAAC;IACZ,oBAAoB;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,+BAA+B;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,4EAA4E;IAC5E,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,mBAAmB,CAAC;IAChC,SAAS,EAAE,mBAAmB,CAAC;IAC/B,8CAA8C;IAC9C,oBAAoB,EAAE,MAAM,CAAC;IAC7B,iDAAiD;IACjD,0BAA0B,EAAE,MAAM,CAAC;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,6CAA6C;IAC7C,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,0DAA0D;IAC1D,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iEAAiE;IACjE,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oEAAoE;IACpE,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB"}
export {};
//# sourceMappingURL=types.js.map
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/module/types.ts"],"names":[],"mappings":""}
import type { Context, ESTreeNode, Visitor, ComplexityPoint } from '../types.js';
import { type CombinedComplexityResult } from '../combined-visitor.js';
import { type HalsteadMetrics } from './halstead.js';
export interface FunctionMetrics {
name: string;
lineStart: number;
lineEnd: number;
loc: number;
cyclomatic: number;
cognitive: number;
halstead: HalsteadMetrics;
cyclomaticPoints: ComplexityPoint[];
cognitivePoints: ComplexityPoint[];
}
export interface AggregateComplexity {
sum: number;
max: number;
average: number;
countAboveThreshold: number;
}
export interface MIDecomposition {
/** 3.42 * ln(avgEffort) — how much expression complexity lowers MI */
effortTerm: number;
/** 0.23 * ln(avgCyclomatic) — how much branching lowers MI */
cyclomaticTerm: number;
/** 16.2 * ln(avgLOC) — how much code length lowers MI */
locTerm: number;
/** Which factor contributes the most to lowering MI */
mainContributor: 'effort' | 'cyclomatic' | 'loc';
}
export interface ModuleAnalysisResult {
functions: FunctionMetrics[];
halstead: HalsteadMetrics;
cyclomatic: AggregateComplexity;
cognitive: AggregateComplexity;
/** Module complexity score (0-100, higher = more complex). Inverted Maintainability Index. */
moduleComplexity: number;
complexityDecomposition: MIDecomposition;
totalLOC: number;
functionCount: number;
}
export interface ModuleComplexityOptions {
moduleComplexity?: number;
maxCyclomaticSum?: number;
maxCognitiveSum?: number;
cyclomaticThreshold?: number;
cognitiveThreshold?: number;
}
/**
* Module complexity = 100 - scaled Maintainability Index.
*
* MI = 171 - 3.42 * ln(avgEffort) - 0.23 * ln(avgCyclomatic) - 16.2 * ln(avgLOC)
* Scaled MI = max(0, MI * 100 / 171)
* Module complexity = 100 - Scaled MI (0 = trivial, 100 = maximally complex)
*/
export declare function calculateModuleComplexity(avgEffort: number, avgCyclomatic: number, avgLOC: number): {
score: number;
decomposition: MIDecomposition;
};
/** If no functions, treats the whole module as one function. */
export declare function computeMIInputs(functionCount: number, totalCyclomatic: number, totalLOC: number, moduleHalstead: HalsteadMetrics, functionEffortSum: number): {
avgEffort: number;
avgCyclomatic: number;
avgLOC: number;
};
/**
* Creates a visitor that computes cyclomatic + cognitive + Halstead per function,
* then aggregates into module-level metrics with Maintainability Index on Program:exit.
*/
export declare function createModuleAnalysisVisitor(context: Context, onModuleAnalyzed: (result: ModuleAnalysisResult) => void, onFunctionAnalyzed?: (result: CombinedComplexityResult, node: ESTreeNode) => void, options?: ModuleComplexityOptions): Visitor;
//# sourceMappingURL=visitor.d.ts.map
{"version":3,"file":"visitor.d.ts","sourceRoot":"","sources":["../../src/module/visitor.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAgB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE/F,OAAO,EAEL,KAAK,wBAAwB,EAC9B,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,eAAe,CAAC;AAEvB,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,eAAe,CAAC;IAC1B,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,eAAe,EAAE,eAAe,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,mBAAmB;IAClC,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,cAAc,EAAE,MAAM,CAAC;IACvB,yDAAyD;IACzD,OAAO,EAAE,MAAM,CAAC;IAChB,uDAAuD;IACvD,eAAe,EAAE,QAAQ,GAAG,YAAY,GAAG,KAAK,CAAC;CAClD;AAED,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,QAAQ,EAAE,eAAe,CAAC;IAC1B,UAAU,EAAE,mBAAmB,CAAC;IAChC,SAAS,EAAE,mBAAmB,CAAC;IAC/B,8FAA8F;IAC9F,gBAAgB,EAAE,MAAM,CAAC;IACzB,uBAAuB,EAAE,eAAe,CAAC;IACzC,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,GACb;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,eAAe,CAAA;CAAE,CAwBnD;AAED,gEAAgE;AAChE,wBAAgB,eAAe,CAC7B,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,eAAe,EAC/B,iBAAiB,EAAE,MAAM,GACxB;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAc9D;AASD;;;GAGG;AACH,wBAAgB,2BAA2B,CACzC,OAAO,EAAE,OAAO,EAChB,gBAAgB,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,EACxD,kBAAkB,CAAC,EAAE,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,EAAE,UAAU,KAAK,IAAI,EACjF,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAsHT"}
import { getFunctionName } from '../utils.js';
import { createCombinedComplexityVisitor, } from '../combined-visitor.js';
import { createHalsteadVisitorHandlers } from './halstead-visitor.js';
import { calculateHalsteadMetrics, createHalsteadCounts, } from './halstead.js';
/**
* Module complexity = 100 - scaled Maintainability Index.
*
* MI = 171 - 3.42 * ln(avgEffort) - 0.23 * ln(avgCyclomatic) - 16.2 * ln(avgLOC)
* Scaled MI = max(0, MI * 100 / 171)
* Module complexity = 100 - Scaled MI (0 = trivial, 100 = maximally complex)
*/
export function calculateModuleComplexity(avgEffort, avgCyclomatic, avgLOC) {
const lnEffort = Math.log(Math.max(avgEffort, 1));
const lnCyclomatic = Math.log(Math.max(avgCyclomatic, 1));
const lnLOC = Math.log(Math.max(avgLOC, 1));
const effortTerm = 3.42 * lnEffort;
const cyclomaticTerm = 0.23 * lnCyclomatic;
const locTerm = 16.2 * lnLOC;
const rawMI = 171 - effortTerm - cyclomaticTerm - locTerm;
const scaledMI = Math.max(0, (rawMI * 100) / 171);
const score = Math.min(100, 100 - scaledMI);
let mainContributor = 'effort';
if (locTerm >= effortTerm && locTerm >= cyclomaticTerm) {
mainContributor = 'loc';
}
else if (cyclomaticTerm >= effortTerm) {
mainContributor = 'cyclomatic';
}
return {
score,
decomposition: { effortTerm, cyclomaticTerm, locTerm, mainContributor },
};
}
/** If no functions, treats the whole module as one function. */
export function computeMIInputs(functionCount, totalCyclomatic, totalLOC, moduleHalstead, functionEffortSum) {
if (functionCount === 0) {
return {
avgEffort: moduleHalstead.effort,
avgCyclomatic: 1,
avgLOC: Math.max(totalLOC, 1),
};
}
return {
avgEffort: functionEffortSum / functionCount,
avgCyclomatic: totalCyclomatic / functionCount,
avgLOC: totalLOC / functionCount,
};
}
/**
* Creates a visitor that computes cyclomatic + cognitive + Halstead per function,
* then aggregates into module-level metrics with Maintainability Index on Program:exit.
*/
export function createModuleAnalysisVisitor(context, onModuleAnalyzed, onFunctionAnalyzed, options) {
const opts = options ?? {};
const cyclomaticThreshold = opts.cyclomaticThreshold ?? 10;
const cognitiveThreshold = opts.cognitiveThreshold ?? 10;
const pendingFunctions = [];
const combinedVisitor = createCombinedComplexityVisitor(context, (result, node) => {
const funcNode = node;
const name = getFunctionName(funcNode, funcNode.parent);
pendingFunctions.push({ node, name, result, halsteadCounts: createHalsteadCounts() });
onFunctionAnalyzed?.(result, node);
});
const { handlers: halsteadHandlers, moduleCounts } = createHalsteadVisitorHandlers({
onFunctionExit(counts) {
const last = pendingFunctions[pendingFunctions.length - 1];
if (last)
last.halsteadCounts = counts;
},
});
// Merge both visitors: for overlapping keys, call both handlers
const combinedHandlerMap = combinedVisitor;
const allKeys = new Set([...Object.keys(combinedVisitor), ...Object.keys(halsteadHandlers)]);
const mergedVisitor = {};
for (const key of allKeys) {
const a = combinedHandlerMap[key];
const b = halsteadHandlers[key];
const handler = a && b
? (node) => {
a(node);
b(node);
}
: (a ?? b);
if (handler)
mergedVisitor[key] = handler;
}
// Reset per-file state when entering a new Program (prevents accumulation across files)
const originalProgramEnter = mergedVisitor['Program'];
mergedVisitor['Program'] = (node) => {
pendingFunctions.length = 0;
moduleCounts.operators.clear();
moduleCounts.operands.clear();
originalProgramEnter?.(node);
};
const originalProgramExit = mergedVisitor['Program:exit'];
mergedVisitor['Program:exit'] = (node) => {
originalProgramExit?.(node);
const totalLOC = node.loc ? node.loc.end.line : 0;
const functions = pendingFunctions.map((pf) => {
const halstead = calculateHalsteadMetrics(pf.halsteadCounts);
const lineStart = pf.node.loc?.start.line ?? 0;
const lineEnd = pf.node.loc?.end.line ?? 0;
const loc = Math.max(lineEnd - lineStart + 1, 1);
return {
name: pf.name,
lineStart,
lineEnd,
loc,
cyclomatic: pf.result.cyclomatic,
cognitive: pf.result.cognitive,
halstead,
cyclomaticPoints: pf.result.cyclomaticPoints,
cognitivePoints: pf.result.cognitivePoints,
};
});
const moduleHalstead = calculateHalsteadMetrics(moduleCounts);
const cyclomatic = aggregateComplexity(functions.map((f) => f.cyclomatic), cyclomaticThreshold);
const cognitive = aggregateComplexity(functions.map((f) => f.cognitive), cognitiveThreshold);
const functionEffortSum = functions.reduce((sum, f) => sum + f.halstead.effort, 0);
const miInputs = computeMIInputs(functions.length, cyclomatic.sum, totalLOC, moduleHalstead, functionEffortSum);
const mc = calculateModuleComplexity(miInputs.avgEffort, miInputs.avgCyclomatic, miInputs.avgLOC);
onModuleAnalyzed({
functions,
halstead: moduleHalstead,
cyclomatic,
cognitive,
moduleComplexity: mc.score,
complexityDecomposition: mc.decomposition,
totalLOC,
functionCount: functions.length,
});
};
return mergedVisitor;
}
function aggregateComplexity(values, threshold) {
if (values.length === 0) {
return { sum: 0, max: 0, average: 0, countAboveThreshold: 0 };
}
let sum = 0;
let max = 0;
let countAboveThreshold = 0;
for (const v of values) {
sum += v;
if (v > max)
max = v;
if (v > threshold)
countAboveThreshold++;
}
return { sum, max, average: sum / values.length, countAboveThreshold };
}
//# sourceMappingURL=visitor.js.map
{"version":3,"file":"visitor.js","sourceRoot":"","sources":["../../src/module/visitor.ts"],"names":[],"mappings":"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"}
+2
-2
{
"name": "oxlint-plugin-complexity",
"version": "2.0.1",
"version": "2.0.2",
"description": "Cyclomatic and cognitive complexity rules for oxlint",

@@ -44,3 +44,3 @@ "keywords": [

"husky": "^9.1.7",
"oxc-parser": "^0.115.0",
"oxc-parser": "^0.116.0",
"@oxlint/plugins": "^1.48.0",

@@ -47,0 +47,0 @@ "oxlint": "^1.48.0",