oxlint-plugin-complexity
Advanced tools
| export interface DiffHunk { | ||
| newStart: number; | ||
| newCount: number; | ||
| } | ||
| export interface DiffFile { | ||
| /** Path in the old version, null for new files */ | ||
| oldPath: string | null; | ||
| /** Path in the new version, null for deleted files */ | ||
| newPath: string | null; | ||
| hunks: DiffHunk[]; | ||
| /** 1-based line numbers of added lines in the new file */ | ||
| addedLines: number[]; | ||
| /** 1-based line numbers in the new file where deletions occurred */ | ||
| deletedLines: number[]; | ||
| } | ||
| /** | ||
| * Parse unified diff output (e.g. from `git diff`) into structured file and line data. | ||
| */ | ||
| export declare function parseDiff(diff: string): DiffFile[]; | ||
| //# sourceMappingURL=diff-parser.d.ts.map |
| {"version":3,"file":"diff-parser.d.ts","sourceRoot":"","sources":["../src/diff-parser.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,QAAQ;IACvB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,QAAQ;IACvB,kDAAkD;IAClD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,sDAAsD;IACtD,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,KAAK,EAAE,QAAQ,EAAE,CAAC;IAClB,0DAA0D;IAC1D,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,oEAAoE;IACpE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAOD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,EAAE,CAgClD"} |
| import { parsePatch } from 'diff'; | ||
| function stripPrefix(path) { | ||
| if (!path || path === '/dev/null') | ||
| return null; | ||
| return path.replace(/^[ab]\//, ''); | ||
| } | ||
| /** | ||
| * Parse unified diff output (e.g. from `git diff`) into structured file and line data. | ||
| */ | ||
| export function parseDiff(diff) { | ||
| const patches = parsePatch(diff); | ||
| return patches.map((patch) => { | ||
| const addedLines = []; | ||
| const deletedLines = []; | ||
| const hunks = []; | ||
| for (const hunk of patch.hunks) { | ||
| hunks.push({ newStart: hunk.newStart, newCount: hunk.newLines }); | ||
| let newLine = hunk.newStart; | ||
| for (const line of hunk.lines) { | ||
| if (line.startsWith('+')) { | ||
| addedLines.push(newLine); | ||
| newLine++; | ||
| } | ||
| else if (line.startsWith('-')) { | ||
| deletedLines.push(newLine); | ||
| } | ||
| else { | ||
| newLine++; | ||
| } | ||
| } | ||
| } | ||
| return { | ||
| oldPath: stripPrefix(patch.oldFileName), | ||
| newPath: stripPrefix(patch.newFileName), | ||
| hunks, | ||
| addedLines, | ||
| deletedLines, | ||
| }; | ||
| }); | ||
| } | ||
| //# sourceMappingURL=diff-parser.js.map |
| {"version":3,"file":"diff-parser.js","sourceRoot":"","sources":["../src/diff-parser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAmBlC,SAAS,WAAW,CAAC,IAAwB;IAC3C,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,WAAW;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;AACrC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,SAAS,CAAC,IAAY;IACpC,MAAM,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;IAEjC,OAAO,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC3B,MAAM,UAAU,GAAa,EAAE,CAAC;QAChC,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,KAAK,GAAe,EAAE,CAAC;QAE7B,KAAK,MAAM,IAAI,IAAI,KAAK,CAAC,KAAK,EAAE,CAAC;YAC/B,KAAK,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,CAAC,CAAC;YAEjE,IAAI,OAAO,GAAG,IAAI,CAAC,QAAQ,CAAC;YAC5B,KAAK,MAAM,IAAI,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;gBAC9B,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBACzB,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;oBACzB,OAAO,EAAE,CAAC;gBACZ,CAAC;qBAAM,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;oBAChC,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC7B,CAAC;qBAAM,CAAC;oBACN,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO;YACL,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;YACvC,OAAO,EAAE,WAAW,CAAC,KAAK,CAAC,WAAW,CAAC;YACvC,KAAK;YACL,UAAU;YACV,YAAY;SACb,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"} |
| import { type FunctionComplexityResult } from './standalone.js'; | ||
| export type DiffIncludeMode = 'additions' | 'deletions' | 'both'; | ||
| export interface DiffAnalysisOptions { | ||
| /** Which changes to analyze: additions (default), deletions, or both */ | ||
| include?: DiffIncludeMode; | ||
| /** Custom file reader. Defaults to fs.readFileSync(path, 'utf-8') */ | ||
| readFile?: (path: string) => string; | ||
| } | ||
| export interface DiffFileResult { | ||
| path: string; | ||
| changedLines: number[]; | ||
| functions: FunctionComplexityResult[]; | ||
| } | ||
| export interface DiffAnalysisResult { | ||
| files: DiffFileResult[]; | ||
| } | ||
| /** | ||
| * Analyze complexity of functions touched by a unified diff. | ||
| * | ||
| * Parses the diff, reads each changed file, runs complexity analysis, | ||
| * and returns results filtered to only functions whose line range | ||
| * overlaps with the changed lines. | ||
| * | ||
| * @param diff - Unified diff string (e.g. output of `git diff`) | ||
| * @param options - Analysis options | ||
| */ | ||
| export declare function analyzeDiffComplexity(diff: string, options?: DiffAnalysisOptions): DiffAnalysisResult; | ||
| //# sourceMappingURL=diff.d.ts.map |
| {"version":3,"file":"diff.d.ts","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AACA,OAAO,EAAyB,KAAK,wBAAwB,EAAE,MAAM,iBAAiB,CAAC;AAGvF,MAAM,MAAM,eAAe,GAAG,WAAW,GAAG,WAAW,GAAG,MAAM,CAAC;AAEjE,MAAM,WAAW,mBAAmB;IAClC,wEAAwE;IACxE,OAAO,CAAC,EAAE,eAAe,CAAC;IAC1B,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAqDD;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,OAAO,CAAC,EAAE,mBAAmB,GAC5B,kBAAkB,CAapB"} |
+64
| import { readFileSync } from 'node:fs'; | ||
| import { analyzeFileComplexity } from './standalone.js'; | ||
| import { parseDiff } from './diff-parser.js'; | ||
| function getChangedLines(addedLines, deletedLines, include) { | ||
| switch (include) { | ||
| case 'additions': | ||
| return addedLines; | ||
| case 'deletions': | ||
| return deletedLines; | ||
| case 'both': | ||
| return [...addedLines, ...deletedLines]; | ||
| } | ||
| } | ||
| function hasOverlap(startLine, endLine, lines) { | ||
| for (const line of lines) { | ||
| if (line >= startLine && line <= endLine) | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
| function analyzeChangedFile(diffFile, include, readFile) { | ||
| if (!diffFile.newPath) | ||
| return null; | ||
| const path = diffFile.newPath; | ||
| const changedLines = getChangedLines(diffFile.addedLines, diffFile.deletedLines, include); | ||
| if (changedLines.length === 0) | ||
| return null; | ||
| const changedLineSet = new Set(changedLines); | ||
| let source; | ||
| try { | ||
| source = readFile(path); | ||
| } | ||
| catch { | ||
| return null; | ||
| } | ||
| const analysis = analyzeFileComplexity(source, path); | ||
| const touchedFunctions = analysis.functions.filter((fn) => hasOverlap(fn.startLine, fn.endLine, changedLineSet)); | ||
| if (touchedFunctions.length === 0) | ||
| return null; | ||
| return { path, changedLines, functions: touchedFunctions }; | ||
| } | ||
| /** | ||
| * Analyze complexity of functions touched by a unified diff. | ||
| * | ||
| * Parses the diff, reads each changed file, runs complexity analysis, | ||
| * and returns results filtered to only functions whose line range | ||
| * overlaps with the changed lines. | ||
| * | ||
| * @param diff - Unified diff string (e.g. output of `git diff`) | ||
| * @param options - Analysis options | ||
| */ | ||
| export function analyzeDiffComplexity(diff, options) { | ||
| const include = options?.include ?? 'additions'; | ||
| const readFile = options?.readFile ?? ((path) => readFileSync(path, 'utf-8')); | ||
| const diffFiles = parseDiff(diff); | ||
| const results = []; | ||
| for (const diffFile of diffFiles) { | ||
| const result = analyzeChangedFile(diffFile, include, readFile); | ||
| if (result) | ||
| results.push(result); | ||
| } | ||
| return { files: results }; | ||
| } | ||
| //# sourceMappingURL=diff.js.map |
| {"version":3,"file":"diff.js","sourceRoot":"","sources":["../src/diff.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,qBAAqB,EAAiC,MAAM,iBAAiB,CAAC;AACvF,OAAO,EAAE,SAAS,EAAiB,MAAM,kBAAkB,CAAC;AAqB5D,SAAS,eAAe,CACtB,UAAoB,EACpB,YAAsB,EACtB,OAAwB;IAExB,QAAQ,OAAO,EAAE,CAAC;QAChB,KAAK,WAAW;YACd,OAAO,UAAU,CAAC;QACpB,KAAK,WAAW;YACd,OAAO,YAAY,CAAC;QACtB,KAAK,MAAM;YACT,OAAO,CAAC,GAAG,UAAU,EAAE,GAAG,YAAY,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,UAAU,CAAC,SAAiB,EAAE,OAAe,EAAE,KAAkB;IACxE,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,IAAI,IAAI,SAAS,IAAI,IAAI,IAAI,OAAO;YAAE,OAAO,IAAI,CAAC;IACxD,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,SAAS,kBAAkB,CACzB,QAAkB,EAClB,OAAwB,EACxB,QAAkC;IAElC,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IACnC,MAAM,IAAI,GAAG,QAAQ,CAAC,OAAO,CAAC;IAE9B,MAAM,YAAY,GAAG,eAAe,CAAC,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;IAC1F,IAAI,YAAY,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3C,MAAM,cAAc,GAAG,IAAI,GAAG,CAAC,YAAY,CAAC,CAAC;IAE7C,IAAI,MAAc,CAAC;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC;IAC1B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,QAAQ,GAAG,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACrD,MAAM,gBAAgB,GAAG,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CACxD,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,EAAE,cAAc,CAAC,CACrD,CAAC;IAEF,IAAI,gBAAgB,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAC/C,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,gBAAgB,EAAE,CAAC;AAC7D,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,OAA6B;IAE7B,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,IAAI,WAAW,CAAC;IAChD,MAAM,QAAQ,GAAG,OAAO,EAAE,QAAQ,IAAI,CAAC,CAAC,IAAY,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,CAAC;IAEtF,MAAM,SAAS,GAAG,SAAS,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,OAAO,GAAqB,EAAE,CAAC;IAErC,KAAK,MAAM,QAAQ,IAAI,SAAS,EAAE,CAAC;QACjC,MAAM,MAAM,GAAG,kBAAkB,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;QAC/D,IAAI,MAAM;YAAE,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC;AAC5B,CAAC"} |
| import type { ESTreeNode, ComplexityPoint } from './types.js'; | ||
| export interface FunctionComplexityResult { | ||
| name: string; | ||
| startLine: number; | ||
| endLine: number; | ||
| cyclomatic: number; | ||
| cognitive: number; | ||
| cyclomaticPoints: ComplexityPoint[]; | ||
| cognitivePoints: ComplexityPoint[]; | ||
| } | ||
| export interface FileAnalysisResult { | ||
| filename: string; | ||
| functions: FunctionComplexityResult[]; | ||
| } | ||
| export declare function createLineOffsetTable(code: string): number[]; | ||
| export declare function offsetToLineCol(offset: number, lineOffsets: number[]): { | ||
| line: number; | ||
| column: number; | ||
| }; | ||
| export declare function walkAndDispatch(ast: ESTreeNode, code: string, visitor: Record<string, ((node: ESTreeNode) => void) | undefined>): void; | ||
| /** | ||
| * Analyze a source file for function complexity without the oxlint runtime. | ||
| * | ||
| * Parses the code with oxc-parser, runs the combined cyclomatic + cognitive | ||
| * complexity visitor, and returns results for every function in the file. | ||
| */ | ||
| export declare function analyzeFileComplexity(code: string, filename: string): FileAnalysisResult; | ||
| //# sourceMappingURL=standalone.d.ts.map |
| {"version":3,"file":"standalone.d.ts","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,UAAU,EAAgB,eAAe,EAAW,MAAM,YAAY,CAAC;AAErF,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,gBAAgB,EAAE,eAAe,EAAE,CAAC;IACpC,eAAe,EAAE,eAAe,EAAE,CAAC;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,wBAAwB,EAAE,CAAC;CACvC;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAQ5D;AAED,wBAAgB,eAAe,CAC7B,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,EAAE,GACpB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAOlC;AAqBD,wBAAgB,eAAe,CAC7B,GAAG,EAAE,UAAU,EACf,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC,GAChE,IAAI,CAmCN;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAmCxF"} |
| import { parseSync } from 'oxc-parser'; | ||
| import { walk } from 'estree-walker'; | ||
| import { createCombinedComplexityVisitor, } from './combined-visitor.js'; | ||
| import { getFunctionName } from './utils.js'; | ||
| export function createLineOffsetTable(code) { | ||
| const lineOffsets = [0]; | ||
| for (let i = 0; i < code.length; i++) { | ||
| if (code[i] === '\n') { | ||
| lineOffsets.push(i + 1); | ||
| } | ||
| } | ||
| return lineOffsets; | ||
| } | ||
| export function offsetToLineCol(offset, lineOffsets) { | ||
| for (let i = lineOffsets.length - 1; i >= 0; i--) { | ||
| if (offset >= lineOffsets[i]) { | ||
| return { line: i + 1, column: offset - lineOffsets[i] }; | ||
| } | ||
| } | ||
| return { line: 1, column: offset }; | ||
| } | ||
| function createStandaloneContext(code) { | ||
| return { | ||
| sourceCode: { | ||
| text: code, | ||
| getText: (node) => { | ||
| const n = node; | ||
| if (n && typeof n.start === 'number' && typeof n.end === 'number') { | ||
| return code.slice(n.start, n.end); | ||
| } | ||
| return ''; | ||
| }, | ||
| scopeManager: null, | ||
| getScope: () => null, | ||
| }, | ||
| options: [], | ||
| report: () => { }, | ||
| }; | ||
| } | ||
| export function walkAndDispatch(ast, code, visitor) { | ||
| const lineOffsets = createLineOffsetTable(code); | ||
| walk(ast, { | ||
| enter(node, parent) { | ||
| const esNode = node; | ||
| const nodeWithOffsets = node; | ||
| if (typeof nodeWithOffsets.start === 'number' && typeof nodeWithOffsets.end === 'number') { | ||
| const startLoc = offsetToLineCol(nodeWithOffsets.start, lineOffsets); | ||
| const endLoc = offsetToLineCol(nodeWithOffsets.end, lineOffsets); | ||
| Object.defineProperty(esNode, 'loc', { | ||
| value: { start: startLoc, end: endLoc }, | ||
| 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); | ||
| }, | ||
| }); | ||
| } | ||
| /** | ||
| * Analyze a source file for function complexity without the oxlint runtime. | ||
| * | ||
| * Parses the code with oxc-parser, runs the combined cyclomatic + cognitive | ||
| * complexity visitor, and returns results for every function in the file. | ||
| */ | ||
| export function analyzeFileComplexity(code, filename) { | ||
| const { program, errors } = parseSync(filename, code); | ||
| if (errors.length > 0) { | ||
| throw new Error(`Parse errors in "${filename}": ${errors.map((e) => e.message).join(', ')}`); | ||
| } | ||
| const functions = []; | ||
| let functionIndex = 0; | ||
| const context = createStandaloneContext(code); | ||
| const onComplexityCalculated = (result, node) => { | ||
| const funcNode = node; | ||
| const name = getFunctionName(funcNode, funcNode.parent); | ||
| const displayName = name === '<arrow>' || name === '<anonymous>' ? `anonymous_${functionIndex + 1}` : name; | ||
| functionIndex++; | ||
| const loc = node.loc; | ||
| functions.push({ | ||
| name: displayName, | ||
| startLine: loc?.start.line ?? 0, | ||
| endLine: loc?.end.line ?? 0, | ||
| cyclomatic: result.cyclomatic, | ||
| cognitive: result.cognitive, | ||
| cyclomaticPoints: result.cyclomaticPoints, | ||
| cognitivePoints: result.cognitivePoints, | ||
| }); | ||
| }; | ||
| const visitor = createCombinedComplexityVisitor(context, onComplexityCalculated); | ||
| walkAndDispatch(program, code, visitor); | ||
| return { filename, functions }; | ||
| } | ||
| //# sourceMappingURL=standalone.js.map |
| {"version":3,"file":"standalone.js","sourceRoot":"","sources":["../src/standalone.ts"],"names":[],"mappings":"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"} |
+27
-4
| { | ||
| "name": "oxlint-plugin-complexity", | ||
| "version": "2.0.3", | ||
| "version": "2.1.0", | ||
| "description": "Cyclomatic and cognitive complexity rules for oxlint", | ||
@@ -35,2 +35,10 @@ "keywords": [ | ||
| "types": "./dist/index.d.ts" | ||
| }, | ||
| "./standalone": { | ||
| "import": "./dist/standalone.js", | ||
| "types": "./dist/standalone.d.ts" | ||
| }, | ||
| "./diff": { | ||
| "import": "./dist/diff.js", | ||
| "types": "./dist/diff.d.ts" | ||
| } | ||
@@ -42,7 +50,8 @@ }, | ||
| "devDependencies": { | ||
| "@oxlint/plugins": "^1.48.0", | ||
| "@types/node": "^22.19.5", | ||
| "diff": "^8.0.4", | ||
| "estree-walker": "^3.0.3", | ||
| "husky": "^9.1.7", | ||
| "oxc-parser": "^0.120.0", | ||
| "@oxlint/plugins": "^1.48.0", | ||
| "oxc-parser": "^0.123.0", | ||
| "oxlint": "^1.48.0", | ||
@@ -55,4 +64,18 @@ "prettier": "3.8.1", | ||
| "peerDependencies": { | ||
| "@oxlint/plugins": ">=1.43.0" | ||
| "@oxlint/plugins": ">=1.43.0", | ||
| "diff": ">=5.0.0", | ||
| "estree-walker": ">=3.0.0", | ||
| "oxc-parser": ">=0.60.0" | ||
| }, | ||
| "peerDependenciesMeta": { | ||
| "diff": { | ||
| "optional": true | ||
| }, | ||
| "estree-walker": { | ||
| "optional": true | ||
| }, | ||
| "oxc-parser": { | ||
| "optional": true | ||
| } | ||
| }, | ||
| "engines": { | ||
@@ -59,0 +82,0 @@ "node": ">=20.0.0" |
+59
-3
@@ -160,4 +160,62 @@ # oxlint-plugin-complexity | ||
| --- | ||
| ## Standalone Library Usage | ||
| You can use this package as a standalone library to analyze complexity programmatically - no oxlint runtime needed. | ||
| ```bash | ||
| npm install oxlint-plugin-complexity oxc-parser estree-walker diff | ||
| ``` | ||
| ### Analyzing a file | ||
| ```typescript | ||
| import { analyzeFileComplexity } from 'oxlint-plugin-complexity/standalone'; | ||
| const code = ` | ||
| function processOrder(order, config) { | ||
| if (order.items.length === 0) { | ||
| return null; | ||
| } | ||
| for (const item of order.items) { | ||
| if (item.quantity > config.maxQuantity) { | ||
| if (config.strict) { | ||
| throw new Error('Over limit'); | ||
| } | ||
| } | ||
| } | ||
| return order; | ||
| } | ||
| `; | ||
| const result = analyzeFileComplexity(code, 'order.ts'); | ||
| for (const fn of result.functions) { | ||
| console.log(`${fn.name} (lines ${fn.startLine}-${fn.endLine})`); | ||
| console.log(` Cyclomatic: ${fn.cyclomatic}`); | ||
| console.log(` Cognitive: ${fn.cognitive}`); | ||
| } | ||
| ``` | ||
| Each function result includes `cyclomaticPoints` and `cognitivePoints` arrays with per-construct breakdowns (line, column, complexity contribution, and message). | ||
| ### Git Diff Analysis | ||
| Analyze only the functions touched by a diff - useful for CI gates, pre-commit hooks, and code review tools. | ||
| ```typescript | ||
| import { analyzeDiffComplexity } from 'oxlint-plugin-complexity/diff'; | ||
| import { execFileSync } from 'node:child_process'; | ||
| const diff = execFileSync('git', ['diff', 'HEAD~1']).toString(); | ||
| const result = analyzeDiffComplexity(diff); | ||
| for (const file of result.files) { | ||
| for (const fn of file.functions) { | ||
| if (fn.cognitive > 15) { | ||
| console.log(`${file.path}:${fn.startLine} ${fn.name} - cognitive: ${fn.cognitive}`); | ||
| } | ||
| } | ||
| } | ||
| ``` | ||
| ## Migration from v0.x | ||
@@ -182,4 +240,2 @@ | ||
| --- | ||
| ## Attribution | ||
@@ -186,0 +242,0 @@ |
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
265642
8.83%131
10.08%3557
8.58%246
29.47%4
300%11
10%2
100%