measure-code 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/cli.cjs +1 -1
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +1 -1
- package/dist/cli.js.map +1 -1
- package/dist/languages.cjs +1 -1
- package/dist/languages.cjs.map +1 -1
- package/dist/languages.js +1 -1
- package/dist/languages.js.map +1 -1
- package/dist/metrics.cjs +1 -1
- package/dist/metrics.cjs.map +1 -1
- package/dist/metrics.js +1 -1
- package/dist/metrics.js.map +1 -1
- package/dist/types.d.ts +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,4 +50,4 @@ console.log(metrics.cyclomaticComplexity);
|
|
|
50
50
|
measure-code ~/ghq/github.com/WillBoosterLab/exercode
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
The CLI scans JavaScript, JSX, TypeScript, TSX, Python, and Go files, skips generated/vendor/test directories by default, and reports functions
|
|
53
|
+
The CLI scans JavaScript, JSX, TypeScript, TSX, Python, and Go files, skips generated/vendor/test/fixture/tool directories by default, and reports high-risk files, functions, and React components. The default thresholds are file code LOC >= 300, function physical LOC span >= 80, component physical LOC span >= 250, cognitive complexity >= 15, cyclomatic complexity >= 20, function calls >= 50, import sources >= 20, and intra-file fan-out >= 8. Use `--include-tests` to include test files, `--json` for machine-readable output, `--max-findings` to control report length, `--fail-on-risk` or `--fail-on-error` for CI, or tune the defaults with the threshold options.
|
package/dist/cli.cjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
"use strict";var t=require("node:fs/promises"),e=require("node:os"),
|
|
2
|
+
"use strict";var t=require("node:fs/promises"),e=require("node:os"),n=require("node:path"),i=require("commander"),o=require("./metrics.cjs");const r=new Map([[".cjs","javascript"],[".cts","typescript"],[".go","go"],[".js","javascript"],[".jsx","jsx"],[".mjs","javascript"],[".mts","typescript"],[".py","python"],[".ts","typescript"],[".tsx","tsx"]]),s=new Set([".agents",".claude",".cursor",".git",".next",".playwright-cli",".tox",".tmp",".turbo",".venv",".yarn","__fixtures__","__generated__","__pycache__","coverage","dist","fixtures","generated","node_modules","test-fixtures","vendor","venv"]),c=new Set(["__tests__","test","tests"]),a=/(?:^test(?:[_-].*)?|\.(?:spec|test)|[_-]test)\.[^.]+$/iu;async function l(e,i,o,r,s,c,a){let p,f;try{p=await t.realpath(e)}catch(t){return void r.push(`${b(e,a)}: ${k(t)}`)}if(w(p,a)&&!s.has(p)){s.add(p);try{f=await t.readdir(e,{withFileTypes:!0})}catch(t){return void r.push(`${b(e,a)}: ${k(t)}`)}for(const t of f){const p=n.join(e,t.name);if(t.isSymbolicLink())await u(t.name,p,i,o,r,s,c,a);else if(t.isDirectory()){if(L(t.name,i))continue;await l(p,i,o,r,s,c,a)}else t.isFile()&&await m(p,i,o,r,c,a)}}}async function u(e,i,o,r,s,c,a,u){let p,f;try{p=await t.realpath(i)}catch(t){return void s.push(`${b(i,u)}: ${k(t)}`)}if(w(p,u)){try{f=await t.stat(i)}catch(t){return void s.push(`${b(i,u)}: ${k(t)}`)}if(f.isDirectory()){if(L(e,o)||L(n.basename(p),o))return;await l(i,o,r,s,c,a,u)}else f.isFile()&&await m(i,o,r,s,a,u,p,p)}}async function m(t,e,n,i,o,r,s=t,c){const a=T(s,e);a&&await p(t,a,n,i,o,r,c)}async function p(e,n,i,r,s,c,a){try{const r=a??await t.realpath(e);if(s.has(r))return;s.add(r);const c=await t.readFile(e,"utf8");i.push({file:e,metrics:o.measureCode(c,{language:n})})}catch(t){r.push(`${b(e,c)}: ${k(t)}`)}}function f(t,e,n,i){const o=[],r=b(t,i);return h(o,"file LOC",e.lines.code,n.fileLocThreshold),h(o,"import sources",e.coupling.importSourceCount,n.importThreshold),0===o.length?[]:[{file:r,language:e.language,kind:"file",cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,triggers:o,score:d(o)}]}function h(t,e,n,i){n<i||t.push({metric:e,value:n,threshold:i,score:n/i})}function d(t){return Math.max(...t.map(t=>t.score))}function g(t,e){return e.score-t.score||t.file.localeCompare(e.file)||(t.startLine??0)-(e.startLine??0)||(t.endLine??0)-(e.endLine??0)||t.kind.localeCompare(e.kind)}function y(t){return void 0===t.startLine||void 0===t.endLine?t.file:`${t.file}:${t.startLine}-${t.endLine}`}function C(t){return t.name?`${t.kind} ${t.name}`:t.kind}function x(t){return`(${t.triggers.map(t=>`${t.metric} ${$(t.value)} >= ${$(t.threshold)}`).join(", ")}; cyclomatic ${t.cyclomaticComplexity}, cognitive ${t.cognitiveComplexity})`}function $(t){return Number.isInteger(t)?String(t):t.toFixed(2)}function v(t){let e=0,n=0,i=0,o=0,r=0,s=0,c=0,a=0,l=0,u=0,m=0,p=0,f=0,h=0,d=0,g=0;for(const y of t)e+=y.metrics.functionCount,n+=y.metrics.lines.code,i=Math.max(i,y.metrics.maxCyclomaticComplexity),o=Math.max(o,y.metrics.maxCognitiveComplexity),r+=y.metrics.callGraph.callCount,s+=y.metrics.callGraph.internalCallCount,c=Math.max(c,y.metrics.callGraph.maxCallDepth),a+=y.metrics.coupling.importSourceCount,l+=y.metrics.coupling.relativeImportCount,u+=y.metrics.coupling.externalImportCount,m+=y.metrics.coupling.exportCount,p+=y.metrics.cohesion.averageFunctionIdentifierOverlap,f+=y.metrics.typeComplexity.typeAnnotationCount,h+=y.metrics.typeComplexity.typeAliasCount,d+=y.metrics.typeComplexity.interfaceCount,g+=y.metrics.typeComplexity.genericParameterCount;return{fileCount:t.length,functionCount:e,linesOfCode:n,maxCyclomaticComplexity:i,maxCognitiveComplexity:o,callCount:r,internalCallCount:s,maxCallDepth:c,importSourceCount:a,relativeImportCount:l,externalImportCount:u,exportCount:m,averageFunctionIdentifierOverlap:0===t.length?0:p/t.length,typeAnnotationCount:f,typeAliasCount:h,interfaceCount:d,genericParameterCount:g}}function L(t,e){return!!s.has(t)||!e.includeTests&&c.has(t)}function w(t,e){const i=n.relative(e,t);return""===i||".."!==i&&!i.startsWith(`..${n.sep}`)&&!n.isAbsolute(i)}function T(t,e,i=!1){const o=t.toLowerCase();if((i||!(o.endsWith(".d.ts")||o.endsWith(".d.mts")||o.endsWith(".d.cts")||o.endsWith(".min.js")||o.endsWith(".pnp.cjs")))&&(i||e.includeTests||!a.test(n.basename(t))))return r.get(n.extname(o))}function O(t){if(!/^[1-9]\d*$/u.test(t))throw new i.InvalidArgumentError("Expected a positive integer.");const e=Number(t);if(!Number.isSafeInteger(e)||e<1)throw new i.InvalidArgumentError("Expected a positive integer.");return e}function b(t,e){return n.relative(e,t)||n.basename(t)}function _(t){process.stdout.write(t)}function j(t){process.stderr.write(t)}function k(t){return t instanceof Error?t.message:String(t)}(async function(){const o=(new i.Command).name("measure-code").description("Measure code metrics and list high-risk findings.").argument("[target]","file or directory to measure",".").option("--cognitive-threshold <number>","minimum cognitive complexity to report",O,15).option("--cyclomatic-threshold <number>","minimum cyclomatic complexity to report",O,20).option("--function-loc-threshold <number>","minimum function physical LOC span to report",O,80).option("--component-loc-threshold <number>","minimum React component physical LOC span to report",O,250).option("--file-loc-threshold <number>","minimum file code LOC to report",O,300).option("--import-threshold <number>","minimum unique import sources per file to report",O,20).option("--call-threshold <number>","minimum function call count to report",O,50).option("--fan-out-threshold <number>","minimum intra-file fan-out per function to report",O,8).option("--max-findings <number>","maximum number of risk findings to print",O,20).option("--include-tests","include test files and test directories").option("--json","print JSON output").option("--fail-on-error","exit with code 1 when files or directories cannot be scanned").option("--fail-on-risk","exit with code 1 when high-risk findings are found");o.action(async(i,o)=>{const r=function(t){if("~"===t)return e.homedir();if(t.startsWith("~/"))return n.join(e.homedir(),t.slice(2));return n.resolve(t)}(i),s=await async function(e,i){const o=[],r=[],s=new Set;let c=e;try{c=await t.realpath(e)}catch{}const a=n.dirname(c);let u;try{u=await t.stat(c)}catch(t){const e=`${b(c,a)}: ${k(t)}`;return{displayRoot:a,files:o,errors:[e],fatalError:e}}if(u.isFile()){const t=n.dirname(c),e=T(c,i,!0);if(!e){const e=`${b(c,t)}: unsupported file type`;return{displayRoot:t,files:o,errors:[e],fatalError:e}}return await p(c,e,o,r,s,t,c),{displayRoot:t,files:o,errors:r}}return await l(c,i,o,r,new Set,s,c),{displayRoot:c,files:o,errors:r}}(r,o),c=function(t,e,n){const i=t.flatMap(({file:t,metrics:i})=>[...f(t,i,e,n),...i.functions.flatMap(o=>function(t,e,n,i,o){const r=n.endLine-n.startLine+1,s=function(t,e){return function(t){return"javascript"===t||"jsx"===t||"typescript"===t||"tsx"===t}(t)&&e.returnsJsx&&void 0!==e.name&&/^[A-Z]/u.test(e.name)}(e,n),c=s?"component":"function",a=[];if(h(a,"cognitive complexity",n.cognitiveComplexity,i.cognitiveThreshold),h(a,"cyclomatic complexity",n.cyclomaticComplexity,i.cyclomaticThreshold),h(a,s?"component LOC":"function LOC",r,function(t,e){return t?e.componentLocThreshold:e.functionLocThreshold}(s,i)),h(a,"function calls",n.callCount,i.callThreshold),h(a,"fan-out",n.fanOut,i.fanOutThreshold),0===a.length)return[];return[{file:b(t,o),language:e,kind:c,name:n.name??"<anonymous>",startLine:n.startLine,endLine:n.endLine,cyclomaticComplexity:n.cyclomaticComplexity,cognitiveComplexity:n.cognitiveComplexity,triggers:a,score:d(a)}]}(t,i.language,o,e,n))]);return i.sort(g),i}(s.files,o,s.displayRoot);o.json?function(t,e,n){const i=v(t.files),o=e.slice(0,n.maxFindings);_(JSON.stringify({summary:i,thresholds:{cyclomaticComplexity:n.cyclomaticThreshold,cognitiveComplexity:n.cognitiveThreshold,callCount:n.callThreshold,componentLoc:n.componentLocThreshold,fanOut:n.fanOutThreshold,fileLoc:n.fileLocThreshold,functionLoc:n.functionLocThreshold,importSources:n.importThreshold},totalRisks:e.length,truncated:o.length<e.length,risks:o,errors:t.errors},void 0,2)+"\n")}(s,c,o):function(t,e,n,i){if(e.fatalError)return void j(`Error: ${e.fatalError}\n`);const o=v(e.files);if(_(`Measured ${o.fileCount} files under ${t}\n`),_(`LOC ${o.linesOfCode}, functions ${o.functionCount}, max cyclomatic ${o.maxCyclomaticComplexity}, max cognitive ${o.maxCognitiveComplexity}\n`),_(`Calls ${o.callCount}, internal edges ${o.internalCallCount}, max call depth ${o.maxCallDepth}, imports ${o.importSourceCount}, exports ${o.exportCount}\n`),_(`Type annotations ${o.typeAnnotationCount}, type aliases ${o.typeAliasCount}, interfaces ${o.interfaceCount}, avg cohesion ${o.averageFunctionIdentifierOverlap.toFixed(2)}\n`),_(`Risk thresholds: file LOC >= ${i.fileLocThreshold}, function LOC >= ${i.functionLocThreshold}, component LOC >= ${i.componentLocThreshold}, cognitive >= ${i.cognitiveThreshold}, cyclomatic >= ${i.cyclomaticThreshold}, calls >= ${i.callThreshold}, imports >= ${i.importThreshold}, fan-out >= ${i.fanOutThreshold}\n`),0===n.length)_("No high-risk findings found.\n");else{const t=n.slice(0,i.maxFindings),e=n.length>t.length?` of ${n.length}`:"";_(`\nHigh-risk findings (top ${t.length}${e}):\n`);for(const e of t)_(`${y(e)} ${C(e)} ${x(e)}\n`)}if(e.errors.length>0){j(`\nSkipped ${e.errors.length} files or directories:\n`);for(const t of e.errors.slice(0,10))j(`- ${t}\n`);e.errors.length>10&&j(`- ... ${e.errors.length-10} more\n`)}}(r,s,c,o),(s.fatalError||o.failOnError&&s.errors.length>0||o.failOnRisk&&c.length>0)&&(process.exitCode=1)}),await o.parseAsync()})().catch(t=>{j(`Error: ${k(t)}\n`),process.exitCode=1});
|
|
3
3
|
//# sourceMappingURL=cli.cjs.map
|
package/dist/cli.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.cjs","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readdir, readFile, realpath, stat } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { measureCode } from './metrics.js';\nimport type { CodeMetrics, FunctionMetrics, LanguageName } from './types.js';\n\ninterface CliOptions {\n cognitiveThreshold: number;\n cyclomaticThreshold: number;\n failOnError?: boolean;\n failOnRisk?: boolean;\n includeTests?: boolean;\n json?: boolean;\n maxFindings: number;\n}\n\ninterface FileMetrics {\n file: string;\n metrics: CodeMetrics;\n}\n\ninterface RiskFinding {\n cognitiveComplexity: number;\n cyclomaticComplexity: number;\n endLine: number;\n file: string;\n language: LanguageName;\n name: string;\n score: number;\n startLine: number;\n}\n\ninterface ScanResult {\n displayRoot: string;\n errors: string[];\n fatalError?: string;\n files: FileMetrics[];\n}\n\nconst languageByExtension = new Map<string, LanguageName>([\n ['.cjs', 'javascript'],\n ['.cts', 'typescript'],\n ['.go', 'go'],\n ['.js', 'javascript'],\n ['.jsx', 'jsx'],\n ['.mjs', 'javascript'],\n ['.mts', 'typescript'],\n ['.py', 'python'],\n ['.ts', 'typescript'],\n ['.tsx', 'tsx'],\n]);\n\nconst ignoredDirectoryNames = new Set([\n '.git',\n '.next',\n '.tox',\n '.tmp',\n '.turbo',\n '.venv',\n '.yarn',\n '__generated__',\n '__pycache__',\n 'coverage',\n 'dist',\n 'generated',\n 'node_modules',\n 'vendor',\n 'venv',\n]);\n\nconst testDirectoryNames = new Set(['__tests__', 'test', 'tests']);\nconst testFilePattern = /(?:^test(?:[_-].*)?|\\.(?:spec|test)|[_-]test)\\.[^.]+$/iu;\n\n// oxlint-disable-next-line unicorn/prefer-top-level-await -- CommonJS build output cannot preserve top-level await.\nvoid main().catch((error: unknown) => {\n writeStderr(`Error: ${formatError(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const program = new Command()\n .name('measure-code')\n .description('Measure code metrics and list high-risk functions.')\n .argument('[target]', 'file or directory to measure', '.')\n .option('--cyclomatic-threshold <number>', 'minimum cyclomatic complexity to report', parsePositiveInteger, 10)\n .option('--cognitive-threshold <number>', 'minimum cognitive complexity to report', parsePositiveInteger, 15)\n .option('--max-findings <number>', 'maximum number of risk findings to print', parsePositiveInteger, 20)\n .option('--include-tests', 'include test files and test directories')\n .option('--json', 'print JSON output')\n .option('--fail-on-error', 'exit with code 1 when files or directories cannot be scanned')\n .option('--fail-on-risk', 'exit with code 1 when high-risk functions are found');\n\n program.action(async (target: string, options: CliOptions) => {\n const resolvedTarget = resolveTarget(target);\n const result = await scanTarget(resolvedTarget, options);\n const risks = findRiskyFunctions(result.files, options, result.displayRoot);\n\n if (options.json) {\n printJson(result, risks, options);\n } else {\n printTextReport(resolvedTarget, result, risks, options);\n }\n\n if (\n result.fatalError ||\n (options.failOnError && result.errors.length > 0) ||\n (options.failOnRisk && risks.length > 0)\n ) {\n process.exitCode = 1;\n }\n });\n\n await program.parseAsync();\n}\n\nfunction resolveTarget(target: string): string {\n if (target === '~') {\n return os.homedir();\n }\n\n if (target.startsWith('~/')) {\n return path.join(os.homedir(), target.slice(2));\n }\n\n return path.resolve(target);\n}\n\nasync function scanTarget(target: string, options: CliOptions): Promise<ScanResult> {\n const files: FileMetrics[] = [];\n const errors: string[] = [];\n const visitedFiles = new Set<string>();\n let canonicalTarget = target;\n try {\n canonicalTarget = await realpath(target);\n } catch {\n // stat below reports missing targets with the original path.\n }\n\n const fallbackDisplayRoot = path.dirname(canonicalTarget);\n let targetStat;\n\n try {\n targetStat = await stat(canonicalTarget);\n } catch (error) {\n const fatalError = `${formatPath(canonicalTarget, fallbackDisplayRoot)}: ${formatError(error)}`;\n return { displayRoot: fallbackDisplayRoot, files, errors: [fatalError], fatalError };\n }\n\n if (targetStat.isFile()) {\n const displayRoot = path.dirname(canonicalTarget);\n const language = getLanguage(canonicalTarget, options, true);\n if (!language) {\n const fatalError = `${formatPath(canonicalTarget, displayRoot)}: unsupported file type`;\n return { displayRoot, files, errors: [fatalError], fatalError };\n }\n\n await measureFile(canonicalTarget, language, files, errors, visitedFiles, displayRoot, canonicalTarget);\n return { displayRoot, files, errors };\n }\n\n await scanDirectory(canonicalTarget, options, files, errors, new Set(), visitedFiles, canonicalTarget);\n return { displayRoot: canonicalTarget, files, errors };\n}\n\nasync function scanDirectory(\n directory: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedDirectory;\n try {\n resolvedDirectory = await realpath(directory);\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedDirectory, rootDirectory)) {\n return;\n }\n\n if (visitedDirectories.has(resolvedDirectory)) {\n return;\n }\n visitedDirectories.add(resolvedDirectory);\n\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(directory, entry.name);\n if (entry.isSymbolicLink()) {\n await scanSymbolicLink(\n entry.name,\n entryPath,\n options,\n files,\n errors,\n visitedDirectories,\n visitedFiles,\n rootDirectory\n );\n continue;\n }\n\n if (entry.isDirectory()) {\n if (shouldSkipDirectory(entry.name, options)) {\n continue;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n continue;\n }\n\n if (entry.isFile()) {\n await measureScannableFile(entryPath, options, files, errors, visitedFiles, rootDirectory);\n }\n }\n}\n\nasync function scanSymbolicLink(\n name: string,\n entryPath: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedPath;\n try {\n resolvedPath = await realpath(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedPath, rootDirectory)) {\n return;\n }\n\n let entryStat;\n try {\n entryStat = await stat(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (entryStat.isDirectory()) {\n if (shouldSkipDirectory(name, options) || shouldSkipDirectory(path.basename(resolvedPath), options)) {\n return;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n return;\n }\n\n if (entryStat.isFile()) {\n await measureScannableFile(\n entryPath,\n options,\n files,\n errors,\n visitedFiles,\n rootDirectory,\n resolvedPath,\n resolvedPath\n );\n }\n}\n\nasync function measureScannableFile(\n file: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n languageFile = file,\n realFile?: string\n): Promise<void> {\n const language = getLanguage(languageFile, options);\n if (language) {\n await measureFile(file, language, files, errors, visitedFiles, displayRoot, realFile);\n }\n}\n\nasync function measureFile(\n file: string,\n language: LanguageName,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n realFile?: string\n): Promise<void> {\n try {\n const resolvedFile = realFile ?? (await realpath(file));\n if (visitedFiles.has(resolvedFile)) {\n return;\n }\n visitedFiles.add(resolvedFile);\n\n const code = await readFile(file, 'utf8');\n files.push({\n file,\n metrics: measureCode(code, { language }),\n });\n } catch (error) {\n errors.push(`${formatPath(file, displayRoot)}: ${formatError(error)}`);\n }\n}\n\nfunction findRiskyFunctions(files: FileMetrics[], options: CliOptions, displayRoot: string): RiskFinding[] {\n const findings = files.flatMap(({ file, metrics }) =>\n metrics.functions\n .filter((fn) => isRiskyFunction(fn, options))\n .map((fn) => createRiskFinding(file, metrics.language, fn, options, displayRoot))\n );\n\n findings.sort((left, right) => right.score - left.score || right.cyclomaticComplexity - left.cyclomaticComplexity);\n return findings;\n}\n\nfunction isRiskyFunction(fn: FunctionMetrics, options: CliOptions): boolean {\n return fn.cyclomaticComplexity >= options.cyclomaticThreshold || fn.cognitiveComplexity >= options.cognitiveThreshold;\n}\n\nfunction createRiskFinding(\n file: string,\n language: LanguageName,\n fn: FunctionMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding {\n return {\n file: formatPath(file, displayRoot),\n language,\n name: fn.name ?? '<anonymous>',\n startLine: fn.startLine,\n endLine: fn.endLine,\n cyclomaticComplexity: fn.cyclomaticComplexity,\n cognitiveComplexity: fn.cognitiveComplexity,\n score: Math.max(\n fn.cyclomaticComplexity / options.cyclomaticThreshold,\n fn.cognitiveComplexity / options.cognitiveThreshold\n ),\n };\n}\n\nfunction printJson(result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n const summary = summarize(result.files);\n const reportedRisks = risks.slice(0, options.maxFindings);\n writeStdout(\n JSON.stringify(\n {\n summary,\n thresholds: {\n cyclomaticComplexity: options.cyclomaticThreshold,\n cognitiveComplexity: options.cognitiveThreshold,\n },\n totalRisks: risks.length,\n truncated: reportedRisks.length < risks.length,\n risks: reportedRisks,\n errors: result.errors,\n },\n undefined,\n 2\n ) + '\\n'\n );\n}\n\nfunction printTextReport(target: string, result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n if (result.fatalError) {\n writeStderr(`Error: ${result.fatalError}\\n`);\n return;\n }\n\n const summary = summarize(result.files);\n writeStdout(`Measured ${summary.fileCount} files under ${target}\\n`);\n writeStdout(\n `LOC ${summary.linesOfCode}, functions ${summary.functionCount}, max cyclomatic ${summary.maxCyclomaticComplexity}, max cognitive ${summary.maxCognitiveComplexity}\\n`\n );\n writeStdout(\n `Calls ${summary.callCount}, internal edges ${summary.internalCallCount}, max call depth ${summary.maxCallDepth}, imports ${summary.importSourceCount}, exports ${summary.exportCount}\\n`\n );\n writeStdout(\n `Type annotations ${summary.typeAnnotationCount}, type aliases ${summary.typeAliasCount}, interfaces ${summary.interfaceCount}, avg cohesion ${summary.averageFunctionIdentifierOverlap.toFixed(2)}\\n`\n );\n writeStdout(\n `Risk thresholds: cyclomatic >= ${options.cyclomaticThreshold}, cognitive >= ${options.cognitiveThreshold}\\n`\n );\n\n if (risks.length === 0) {\n writeStdout('No high-risk functions found.\\n');\n } else {\n const reportedRisks = risks.slice(0, options.maxFindings);\n const totalSuffix = risks.length > reportedRisks.length ? ` of ${risks.length}` : '';\n writeStdout(`\\nHigh-risk functions (top ${reportedRisks.length}${totalSuffix}):\\n`);\n for (const risk of reportedRisks) {\n writeStdout(\n `${risk.file}:${risk.startLine}-${risk.endLine} ${risk.name} ` +\n `(cyclomatic ${risk.cyclomaticComplexity}, cognitive ${risk.cognitiveComplexity})\\n`\n );\n }\n }\n\n if (result.errors.length > 0) {\n writeStderr(`\\nSkipped ${result.errors.length} files or directories:\\n`);\n for (const error of result.errors.slice(0, 10)) {\n writeStderr(`- ${error}\\n`);\n }\n if (result.errors.length > 10) {\n writeStderr(`- ... ${result.errors.length - 10} more\\n`);\n }\n }\n}\n\nfunction summarize(files: FileMetrics[]): {\n fileCount: number;\n functionCount: number;\n linesOfCode: number;\n maxCognitiveComplexity: number;\n maxCyclomaticComplexity: number;\n callCount: number;\n internalCallCount: number;\n maxCallDepth: number;\n importSourceCount: number;\n relativeImportCount: number;\n externalImportCount: number;\n exportCount: number;\n averageFunctionIdentifierOverlap: number;\n typeAnnotationCount: number;\n typeAliasCount: number;\n interfaceCount: number;\n genericParameterCount: number;\n} {\n let functionCount = 0;\n let linesOfCode = 0;\n let maxCyclomaticComplexity = 0;\n let maxCognitiveComplexity = 0;\n let callCount = 0;\n let internalCallCount = 0;\n let maxCallDepth = 0;\n let importSourceCount = 0;\n let relativeImportCount = 0;\n let externalImportCount = 0;\n let exportCount = 0;\n let cohesionTotal = 0;\n let typeAnnotationCount = 0;\n let typeAliasCount = 0;\n let interfaceCount = 0;\n let genericParameterCount = 0;\n\n for (const file of files) {\n functionCount += file.metrics.functionCount;\n linesOfCode += file.metrics.lines.code;\n maxCyclomaticComplexity = Math.max(maxCyclomaticComplexity, file.metrics.maxCyclomaticComplexity);\n maxCognitiveComplexity = Math.max(maxCognitiveComplexity, file.metrics.maxCognitiveComplexity);\n callCount += file.metrics.callGraph.callCount;\n internalCallCount += file.metrics.callGraph.internalCallCount;\n maxCallDepth = Math.max(maxCallDepth, file.metrics.callGraph.maxCallDepth);\n importSourceCount += file.metrics.coupling.importSourceCount;\n relativeImportCount += file.metrics.coupling.relativeImportCount;\n externalImportCount += file.metrics.coupling.externalImportCount;\n exportCount += file.metrics.coupling.exportCount;\n cohesionTotal += file.metrics.cohesion.averageFunctionIdentifierOverlap;\n typeAnnotationCount += file.metrics.typeComplexity.typeAnnotationCount;\n typeAliasCount += file.metrics.typeComplexity.typeAliasCount;\n interfaceCount += file.metrics.typeComplexity.interfaceCount;\n genericParameterCount += file.metrics.typeComplexity.genericParameterCount;\n }\n\n return {\n fileCount: files.length,\n functionCount,\n linesOfCode,\n maxCyclomaticComplexity,\n maxCognitiveComplexity,\n callCount,\n internalCallCount,\n maxCallDepth,\n importSourceCount,\n relativeImportCount,\n externalImportCount,\n exportCount,\n averageFunctionIdentifierOverlap: files.length === 0 ? 0 : cohesionTotal / files.length,\n typeAnnotationCount,\n typeAliasCount,\n interfaceCount,\n genericParameterCount,\n };\n}\n\nfunction shouldSkipDirectory(name: string, options: CliOptions): boolean {\n if (ignoredDirectoryNames.has(name)) {\n return true;\n }\n\n if (options.includeTests) {\n return false;\n }\n\n return testDirectoryNames.has(name);\n}\n\nfunction isWithinDirectory(candidate: string, directory: string): boolean {\n const relative = path.relative(directory, candidate);\n return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));\n}\n\nfunction getLanguage(file: string, options: CliOptions, explicitTarget = false): LanguageName | undefined {\n const lowerFile = file.toLowerCase();\n if (\n !explicitTarget &&\n (lowerFile.endsWith('.d.ts') ||\n lowerFile.endsWith('.d.mts') ||\n lowerFile.endsWith('.d.cts') ||\n lowerFile.endsWith('.min.js'))\n ) {\n return undefined;\n }\n\n if (!explicitTarget && !options.includeTests && testFilePattern.test(path.basename(file))) {\n return undefined;\n }\n\n return languageByExtension.get(path.extname(lowerFile));\n}\n\nfunction parsePositiveInteger(value: string): number {\n if (!/^[1-9]\\d*$/u.test(value)) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n return parsed;\n}\n\nfunction formatPath(file: string, base: string): string {\n return path.relative(base, file) || path.basename(file);\n}\n\nfunction writeStdout(message: string): void {\n process.stdout.write(message);\n}\n\nfunction writeStderr(message: string): void {\n process.stderr.write(message);\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"names":["languageByExtension","Map","ignoredDirectoryNames","Set","testDirectoryNames","testFilePattern","async","scanDirectory","directory","options","files","errors","visitedDirectories","visitedFiles","rootDirectory","resolvedDirectory","entries","realpath","error","push","formatPath","formatError","isWithinDirectory","has","add","readdir","withFileTypes","entry","entryPath","path","join","name","isSymbolicLink","scanSymbolicLink","isDirectory","shouldSkipDirectory","isFile","measureScannableFile","resolvedPath","entryStat","stat","basename","file","displayRoot","languageFile","realFile","language","getLanguage","measureFile","resolvedFile","code","readFile","metrics","measureCode","summarize","functionCount","linesOfCode","maxCyclomaticComplexity","maxCognitiveComplexity","callCount","internalCallCount","maxCallDepth","importSourceCount","relativeImportCount","externalImportCount","exportCount","cohesionTotal","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","lines","Math","max","callGraph","coupling","cohesion","averageFunctionIdentifierOverlap","typeComplexity","fileCount","length","includeTests","candidate","relative","startsWith","sep","isAbsolute","explicitTarget","lowerFile","toLowerCase","endsWith","test","get","extname","parsePositiveInteger","value","InvalidArgumentError","parsed","Number","isSafeInteger","base","writeStdout","message","process","stdout","write","writeStderr","stderr","Error","String","program","Command","description","argument","option","action","target","resolvedTarget","os","homedir","slice","resolve","resolveTarget","result","canonicalTarget","fallbackDisplayRoot","dirname","targetStat","fatalError","scanTarget","risks","findings","flatMap","functions","filter","fn","cyclomaticComplexity","cyclomaticThreshold","cognitiveComplexity","cognitiveThreshold","isRiskyFunction","map","startLine","endLine","score","createRiskFinding","sort","left","right","findRiskyFunctions","json","summary","reportedRisks","maxFindings","JSON","stringify","thresholds","totalRisks","truncated","undefined","printJson","toFixed","totalSuffix","risk","printTextReport","failOnError","failOnRisk","exitCode","parseAsync","main","catch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.cjs","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readdir, readFile, realpath, stat } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { measureCode } from './metrics.js';\nimport type { CodeMetrics, FunctionMetrics, LanguageName } from './types.js';\n\ninterface CliOptions {\n callThreshold: number;\n cognitiveThreshold: number;\n componentLocThreshold: number;\n cyclomaticThreshold: number;\n fanOutThreshold: number;\n failOnError?: boolean;\n failOnRisk?: boolean;\n fileLocThreshold: number;\n includeTests?: boolean;\n importThreshold: number;\n functionLocThreshold: number;\n json?: boolean;\n maxFindings: number;\n}\n\ninterface FileMetrics {\n file: string;\n metrics: CodeMetrics;\n}\n\ninterface RiskTrigger {\n metric: string;\n score: number;\n threshold: number;\n value: number;\n}\n\ninterface RiskFinding {\n cognitiveComplexity: number;\n cyclomaticComplexity: number;\n endLine?: number;\n file: string;\n kind: 'component' | 'file' | 'function';\n language: LanguageName;\n name?: string;\n score: number;\n startLine?: number;\n triggers: RiskTrigger[];\n}\n\ninterface ScanResult {\n displayRoot: string;\n errors: string[];\n fatalError?: string;\n files: FileMetrics[];\n}\n\nconst languageByExtension = new Map<string, LanguageName>([\n ['.cjs', 'javascript'],\n ['.cts', 'typescript'],\n ['.go', 'go'],\n ['.js', 'javascript'],\n ['.jsx', 'jsx'],\n ['.mjs', 'javascript'],\n ['.mts', 'typescript'],\n ['.py', 'python'],\n ['.ts', 'typescript'],\n ['.tsx', 'tsx'],\n]);\n\nconst ignoredDirectoryNames = new Set([\n '.agents',\n '.claude',\n '.cursor',\n '.git',\n '.next',\n '.playwright-cli',\n '.tox',\n '.tmp',\n '.turbo',\n '.venv',\n '.yarn',\n '__fixtures__',\n '__generated__',\n '__pycache__',\n 'coverage',\n 'dist',\n 'fixtures',\n 'generated',\n 'node_modules',\n 'test-fixtures',\n 'vendor',\n 'venv',\n]);\n\nconst testDirectoryNames = new Set(['__tests__', 'test', 'tests']);\nconst testFilePattern = /(?:^test(?:[_-].*)?|\\.(?:spec|test)|[_-]test)\\.[^.]+$/iu;\n\n// oxlint-disable-next-line unicorn/prefer-top-level-await -- CommonJS build output cannot preserve top-level await.\nvoid main().catch((error: unknown) => {\n writeStderr(`Error: ${formatError(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const program = new Command()\n .name('measure-code')\n .description('Measure code metrics and list high-risk findings.')\n .argument('[target]', 'file or directory to measure', '.')\n .option('--cognitive-threshold <number>', 'minimum cognitive complexity to report', parsePositiveInteger, 15)\n .option('--cyclomatic-threshold <number>', 'minimum cyclomatic complexity to report', parsePositiveInteger, 20)\n .option(\n '--function-loc-threshold <number>',\n 'minimum function physical LOC span to report',\n parsePositiveInteger,\n 80\n )\n .option(\n '--component-loc-threshold <number>',\n 'minimum React component physical LOC span to report',\n parsePositiveInteger,\n 250\n )\n .option('--file-loc-threshold <number>', 'minimum file code LOC to report', parsePositiveInteger, 300)\n .option('--import-threshold <number>', 'minimum unique import sources per file to report', parsePositiveInteger, 20)\n .option('--call-threshold <number>', 'minimum function call count to report', parsePositiveInteger, 50)\n .option(\n '--fan-out-threshold <number>',\n 'minimum intra-file fan-out per function to report',\n parsePositiveInteger,\n 8\n )\n .option('--max-findings <number>', 'maximum number of risk findings to print', parsePositiveInteger, 20)\n .option('--include-tests', 'include test files and test directories')\n .option('--json', 'print JSON output')\n .option('--fail-on-error', 'exit with code 1 when files or directories cannot be scanned')\n .option('--fail-on-risk', 'exit with code 1 when high-risk findings are found');\n\n program.action(async (target: string, options: CliOptions) => {\n const resolvedTarget = resolveTarget(target);\n const result = await scanTarget(resolvedTarget, options);\n const risks = findRiskyFunctions(result.files, options, result.displayRoot);\n\n if (options.json) {\n printJson(result, risks, options);\n } else {\n printTextReport(resolvedTarget, result, risks, options);\n }\n\n if (\n result.fatalError ||\n (options.failOnError && result.errors.length > 0) ||\n (options.failOnRisk && risks.length > 0)\n ) {\n process.exitCode = 1;\n }\n });\n\n await program.parseAsync();\n}\n\nfunction resolveTarget(target: string): string {\n if (target === '~') {\n return os.homedir();\n }\n\n if (target.startsWith('~/')) {\n return path.join(os.homedir(), target.slice(2));\n }\n\n return path.resolve(target);\n}\n\nasync function scanTarget(target: string, options: CliOptions): Promise<ScanResult> {\n const files: FileMetrics[] = [];\n const errors: string[] = [];\n const visitedFiles = new Set<string>();\n let canonicalTarget = target;\n try {\n canonicalTarget = await realpath(target);\n } catch {\n // stat below reports missing targets with the original path.\n }\n\n const fallbackDisplayRoot = path.dirname(canonicalTarget);\n let targetStat;\n\n try {\n targetStat = await stat(canonicalTarget);\n } catch (error) {\n const fatalError = `${formatPath(canonicalTarget, fallbackDisplayRoot)}: ${formatError(error)}`;\n return { displayRoot: fallbackDisplayRoot, files, errors: [fatalError], fatalError };\n }\n\n if (targetStat.isFile()) {\n const displayRoot = path.dirname(canonicalTarget);\n const language = getLanguage(canonicalTarget, options, true);\n if (!language) {\n const fatalError = `${formatPath(canonicalTarget, displayRoot)}: unsupported file type`;\n return { displayRoot, files, errors: [fatalError], fatalError };\n }\n\n await measureFile(canonicalTarget, language, files, errors, visitedFiles, displayRoot, canonicalTarget);\n return { displayRoot, files, errors };\n }\n\n await scanDirectory(canonicalTarget, options, files, errors, new Set(), visitedFiles, canonicalTarget);\n return { displayRoot: canonicalTarget, files, errors };\n}\n\nasync function scanDirectory(\n directory: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedDirectory;\n try {\n resolvedDirectory = await realpath(directory);\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedDirectory, rootDirectory)) {\n return;\n }\n\n if (visitedDirectories.has(resolvedDirectory)) {\n return;\n }\n visitedDirectories.add(resolvedDirectory);\n\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(directory, entry.name);\n if (entry.isSymbolicLink()) {\n await scanSymbolicLink(\n entry.name,\n entryPath,\n options,\n files,\n errors,\n visitedDirectories,\n visitedFiles,\n rootDirectory\n );\n continue;\n }\n\n if (entry.isDirectory()) {\n if (shouldSkipDirectory(entry.name, options)) {\n continue;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n continue;\n }\n\n if (entry.isFile()) {\n await measureScannableFile(entryPath, options, files, errors, visitedFiles, rootDirectory);\n }\n }\n}\n\nasync function scanSymbolicLink(\n name: string,\n entryPath: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedPath;\n try {\n resolvedPath = await realpath(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedPath, rootDirectory)) {\n return;\n }\n\n let entryStat;\n try {\n entryStat = await stat(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (entryStat.isDirectory()) {\n if (shouldSkipDirectory(name, options) || shouldSkipDirectory(path.basename(resolvedPath), options)) {\n return;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n return;\n }\n\n if (entryStat.isFile()) {\n await measureScannableFile(\n entryPath,\n options,\n files,\n errors,\n visitedFiles,\n rootDirectory,\n resolvedPath,\n resolvedPath\n );\n }\n}\n\nasync function measureScannableFile(\n file: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n languageFile = file,\n realFile?: string\n): Promise<void> {\n const language = getLanguage(languageFile, options);\n if (language) {\n await measureFile(file, language, files, errors, visitedFiles, displayRoot, realFile);\n }\n}\n\nasync function measureFile(\n file: string,\n language: LanguageName,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n realFile?: string\n): Promise<void> {\n try {\n const resolvedFile = realFile ?? (await realpath(file));\n if (visitedFiles.has(resolvedFile)) {\n return;\n }\n visitedFiles.add(resolvedFile);\n\n const code = await readFile(file, 'utf8');\n files.push({\n file,\n metrics: measureCode(code, { language }),\n });\n } catch (error) {\n errors.push(`${formatPath(file, displayRoot)}: ${formatError(error)}`);\n }\n}\n\nfunction findRiskyFunctions(files: FileMetrics[], options: CliOptions, displayRoot: string): RiskFinding[] {\n const findings = files.flatMap(({ file, metrics }) => [\n ...findRiskyFileMetrics(file, metrics, options, displayRoot),\n ...metrics.functions.flatMap((fn) => findRiskyFunctionMetrics(file, metrics.language, fn, options, displayRoot)),\n ]);\n\n findings.sort(compareRiskFindings);\n return findings;\n}\n\nfunction findRiskyFileMetrics(\n file: string,\n metrics: CodeMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding[] {\n const triggers: RiskTrigger[] = [];\n const formattedFile = formatPath(file, displayRoot);\n addTrigger(triggers, 'file LOC', metrics.lines.code, options.fileLocThreshold);\n addTrigger(triggers, 'import sources', metrics.coupling.importSourceCount, options.importThreshold);\n if (triggers.length === 0) {\n return [];\n }\n\n return [\n {\n file: formattedFile,\n language: metrics.language,\n kind: 'file',\n cyclomaticComplexity: metrics.cyclomaticComplexity,\n cognitiveComplexity: metrics.cognitiveComplexity,\n triggers,\n score: maxTriggerScore(triggers),\n },\n ];\n}\n\nfunction findRiskyFunctionMetrics(\n file: string,\n language: LanguageName,\n fn: FunctionMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding[] {\n const loc = fn.endLine - fn.startLine + 1;\n const isComponent = isReactComponent(language, fn);\n const kind = isComponent ? 'component' : 'function';\n const triggers: RiskTrigger[] = [];\n addTrigger(triggers, 'cognitive complexity', fn.cognitiveComplexity, options.cognitiveThreshold);\n addTrigger(triggers, 'cyclomatic complexity', fn.cyclomaticComplexity, options.cyclomaticThreshold);\n addTrigger(triggers, isComponent ? 'component LOC' : 'function LOC', loc, getLocThreshold(isComponent, options));\n addTrigger(triggers, 'function calls', fn.callCount, options.callThreshold);\n addTrigger(triggers, 'fan-out', fn.fanOut, options.fanOutThreshold);\n if (triggers.length === 0) {\n return [];\n }\n\n return [\n {\n file: formatPath(file, displayRoot),\n language,\n kind,\n name: fn.name ?? '<anonymous>',\n startLine: fn.startLine,\n endLine: fn.endLine,\n cyclomaticComplexity: fn.cyclomaticComplexity,\n cognitiveComplexity: fn.cognitiveComplexity,\n triggers,\n score: maxTriggerScore(triggers),\n },\n ];\n}\n\nfunction addTrigger(triggers: RiskTrigger[], metric: string, value: number, threshold: number): void {\n if (value < threshold) {\n return;\n }\n\n triggers.push({ metric, value, threshold, score: value / threshold });\n}\n\nfunction isReactComponent(language: LanguageName, fn: FunctionMetrics): boolean {\n return isJavaScriptLikeLanguage(language) && fn.returnsJsx && fn.name !== undefined && /^[A-Z]/u.test(fn.name);\n}\n\nfunction isJavaScriptLikeLanguage(language: LanguageName): boolean {\n return language === 'javascript' || language === 'jsx' || language === 'typescript' || language === 'tsx';\n}\n\nfunction getLocThreshold(isComponent: boolean, options: CliOptions): number {\n return isComponent ? options.componentLocThreshold : options.functionLocThreshold;\n}\n\nfunction maxTriggerScore(triggers: RiskTrigger[]): number {\n return Math.max(...triggers.map((trigger) => trigger.score));\n}\n\nfunction compareRiskFindings(left: RiskFinding, right: RiskFinding): number {\n return (\n right.score - left.score ||\n left.file.localeCompare(right.file) ||\n (left.startLine ?? 0) - (right.startLine ?? 0) ||\n (left.endLine ?? 0) - (right.endLine ?? 0) ||\n left.kind.localeCompare(right.kind)\n );\n}\n\nfunction printJson(result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n const summary = summarize(result.files);\n const reportedRisks = risks.slice(0, options.maxFindings);\n writeStdout(\n JSON.stringify(\n {\n summary,\n thresholds: {\n cyclomaticComplexity: options.cyclomaticThreshold,\n cognitiveComplexity: options.cognitiveThreshold,\n callCount: options.callThreshold,\n componentLoc: options.componentLocThreshold,\n fanOut: options.fanOutThreshold,\n fileLoc: options.fileLocThreshold,\n functionLoc: options.functionLocThreshold,\n importSources: options.importThreshold,\n },\n totalRisks: risks.length,\n truncated: reportedRisks.length < risks.length,\n risks: reportedRisks,\n errors: result.errors,\n },\n undefined,\n 2\n ) + '\\n'\n );\n}\n\nfunction printTextReport(target: string, result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n if (result.fatalError) {\n writeStderr(`Error: ${result.fatalError}\\n`);\n return;\n }\n\n const summary = summarize(result.files);\n writeStdout(`Measured ${summary.fileCount} files under ${target}\\n`);\n writeStdout(\n `LOC ${summary.linesOfCode}, functions ${summary.functionCount}, max cyclomatic ${summary.maxCyclomaticComplexity}, max cognitive ${summary.maxCognitiveComplexity}\\n`\n );\n writeStdout(\n `Calls ${summary.callCount}, internal edges ${summary.internalCallCount}, max call depth ${summary.maxCallDepth}, imports ${summary.importSourceCount}, exports ${summary.exportCount}\\n`\n );\n writeStdout(\n `Type annotations ${summary.typeAnnotationCount}, type aliases ${summary.typeAliasCount}, interfaces ${summary.interfaceCount}, avg cohesion ${summary.averageFunctionIdentifierOverlap.toFixed(2)}\\n`\n );\n writeStdout(\n `Risk thresholds: file LOC >= ${options.fileLocThreshold}, function LOC >= ${options.functionLocThreshold}, component LOC >= ${options.componentLocThreshold}, cognitive >= ${options.cognitiveThreshold}, cyclomatic >= ${options.cyclomaticThreshold}, calls >= ${options.callThreshold}, imports >= ${options.importThreshold}, fan-out >= ${options.fanOutThreshold}\\n`\n );\n\n if (risks.length === 0) {\n writeStdout('No high-risk findings found.\\n');\n } else {\n const reportedRisks = risks.slice(0, options.maxFindings);\n const totalSuffix = risks.length > reportedRisks.length ? ` of ${risks.length}` : '';\n writeStdout(`\\nHigh-risk findings (top ${reportedRisks.length}${totalSuffix}):\\n`);\n for (const risk of reportedRisks) {\n writeStdout(`${formatRiskLocation(risk)} ${formatRiskName(risk)} ${formatRiskMetrics(risk)}\\n`);\n }\n }\n\n if (result.errors.length > 0) {\n writeStderr(`\\nSkipped ${result.errors.length} files or directories:\\n`);\n for (const error of result.errors.slice(0, 10)) {\n writeStderr(`- ${error}\\n`);\n }\n if (result.errors.length > 10) {\n writeStderr(`- ... ${result.errors.length - 10} more\\n`);\n }\n }\n}\n\nfunction formatRiskLocation(risk: RiskFinding): string {\n return risk.startLine === undefined || risk.endLine === undefined\n ? risk.file\n : `${risk.file}:${risk.startLine}-${risk.endLine}`;\n}\n\nfunction formatRiskName(risk: RiskFinding): string {\n return risk.name ? `${risk.kind} ${risk.name}` : risk.kind;\n}\n\nfunction formatRiskMetrics(risk: RiskFinding): string {\n const triggerText = risk.triggers\n .map(\n (trigger) => `${trigger.metric} ${formatMetricValue(trigger.value)} >= ${formatMetricValue(trigger.threshold)}`\n )\n .join(', ');\n return `(${triggerText}; cyclomatic ${risk.cyclomaticComplexity}, cognitive ${risk.cognitiveComplexity})`;\n}\n\nfunction formatMetricValue(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\nfunction summarize(files: FileMetrics[]): {\n fileCount: number;\n functionCount: number;\n linesOfCode: number;\n maxCognitiveComplexity: number;\n maxCyclomaticComplexity: number;\n callCount: number;\n internalCallCount: number;\n maxCallDepth: number;\n importSourceCount: number;\n relativeImportCount: number;\n externalImportCount: number;\n exportCount: number;\n averageFunctionIdentifierOverlap: number;\n typeAnnotationCount: number;\n typeAliasCount: number;\n interfaceCount: number;\n genericParameterCount: number;\n} {\n let functionCount = 0;\n let linesOfCode = 0;\n let maxCyclomaticComplexity = 0;\n let maxCognitiveComplexity = 0;\n let callCount = 0;\n let internalCallCount = 0;\n let maxCallDepth = 0;\n let importSourceCount = 0;\n let relativeImportCount = 0;\n let externalImportCount = 0;\n let exportCount = 0;\n let cohesionTotal = 0;\n let typeAnnotationCount = 0;\n let typeAliasCount = 0;\n let interfaceCount = 0;\n let genericParameterCount = 0;\n\n for (const file of files) {\n functionCount += file.metrics.functionCount;\n linesOfCode += file.metrics.lines.code;\n maxCyclomaticComplexity = Math.max(maxCyclomaticComplexity, file.metrics.maxCyclomaticComplexity);\n maxCognitiveComplexity = Math.max(maxCognitiveComplexity, file.metrics.maxCognitiveComplexity);\n callCount += file.metrics.callGraph.callCount;\n internalCallCount += file.metrics.callGraph.internalCallCount;\n maxCallDepth = Math.max(maxCallDepth, file.metrics.callGraph.maxCallDepth);\n importSourceCount += file.metrics.coupling.importSourceCount;\n relativeImportCount += file.metrics.coupling.relativeImportCount;\n externalImportCount += file.metrics.coupling.externalImportCount;\n exportCount += file.metrics.coupling.exportCount;\n cohesionTotal += file.metrics.cohesion.averageFunctionIdentifierOverlap;\n typeAnnotationCount += file.metrics.typeComplexity.typeAnnotationCount;\n typeAliasCount += file.metrics.typeComplexity.typeAliasCount;\n interfaceCount += file.metrics.typeComplexity.interfaceCount;\n genericParameterCount += file.metrics.typeComplexity.genericParameterCount;\n }\n\n return {\n fileCount: files.length,\n functionCount,\n linesOfCode,\n maxCyclomaticComplexity,\n maxCognitiveComplexity,\n callCount,\n internalCallCount,\n maxCallDepth,\n importSourceCount,\n relativeImportCount,\n externalImportCount,\n exportCount,\n averageFunctionIdentifierOverlap: files.length === 0 ? 0 : cohesionTotal / files.length,\n typeAnnotationCount,\n typeAliasCount,\n interfaceCount,\n genericParameterCount,\n };\n}\n\nfunction shouldSkipDirectory(name: string, options: CliOptions): boolean {\n if (ignoredDirectoryNames.has(name)) {\n return true;\n }\n\n if (options.includeTests) {\n return false;\n }\n\n return testDirectoryNames.has(name);\n}\n\nfunction isWithinDirectory(candidate: string, directory: string): boolean {\n const relative = path.relative(directory, candidate);\n return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));\n}\n\nfunction getLanguage(file: string, options: CliOptions, explicitTarget = false): LanguageName | undefined {\n const lowerFile = file.toLowerCase();\n if (\n !explicitTarget &&\n (lowerFile.endsWith('.d.ts') ||\n lowerFile.endsWith('.d.mts') ||\n lowerFile.endsWith('.d.cts') ||\n lowerFile.endsWith('.min.js') ||\n lowerFile.endsWith('.pnp.cjs'))\n ) {\n return undefined;\n }\n\n if (!explicitTarget && !options.includeTests && testFilePattern.test(path.basename(file))) {\n return undefined;\n }\n\n return languageByExtension.get(path.extname(lowerFile));\n}\n\nfunction parsePositiveInteger(value: string): number {\n if (!/^[1-9]\\d*$/u.test(value)) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n return parsed;\n}\n\nfunction formatPath(file: string, base: string): string {\n return path.relative(base, file) || path.basename(file);\n}\n\nfunction writeStdout(message: string): void {\n process.stdout.write(message);\n}\n\nfunction writeStderr(message: string): void {\n process.stderr.write(message);\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"names":["languageByExtension","Map","ignoredDirectoryNames","Set","testDirectoryNames","testFilePattern","async","scanDirectory","directory","options","files","errors","visitedDirectories","visitedFiles","rootDirectory","resolvedDirectory","entries","realpath","error","push","formatPath","formatError","isWithinDirectory","has","add","readdir","withFileTypes","entry","entryPath","path","join","name","isSymbolicLink","scanSymbolicLink","isDirectory","shouldSkipDirectory","isFile","measureScannableFile","resolvedPath","entryStat","stat","basename","file","displayRoot","languageFile","realFile","language","getLanguage","measureFile","resolvedFile","code","readFile","metrics","measureCode","findRiskyFileMetrics","triggers","formattedFile","addTrigger","lines","fileLocThreshold","coupling","importSourceCount","importThreshold","length","kind","cyclomaticComplexity","cognitiveComplexity","score","maxTriggerScore","metric","value","threshold","Math","max","map","trigger","compareRiskFindings","left","right","localeCompare","startLine","endLine","formatRiskLocation","risk","undefined","formatRiskName","formatRiskMetrics","formatMetricValue","Number","isInteger","String","toFixed","summarize","functionCount","linesOfCode","maxCyclomaticComplexity","maxCognitiveComplexity","callCount","internalCallCount","maxCallDepth","relativeImportCount","externalImportCount","exportCount","cohesionTotal","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","callGraph","cohesion","averageFunctionIdentifierOverlap","typeComplexity","fileCount","includeTests","candidate","relative","startsWith","sep","isAbsolute","explicitTarget","lowerFile","toLowerCase","endsWith","test","get","extname","parsePositiveInteger","InvalidArgumentError","parsed","isSafeInteger","base","writeStdout","message","process","stdout","write","writeStderr","stderr","Error","program","Command","description","argument","option","action","target","resolvedTarget","os","homedir","slice","resolve","resolveTarget","result","canonicalTarget","fallbackDisplayRoot","dirname","targetStat","fatalError","scanTarget","risks","findings","flatMap","functions","fn","loc","isComponent","isJavaScriptLikeLanguage","returnsJsx","isReactComponent","cognitiveThreshold","cyclomaticThreshold","componentLocThreshold","functionLocThreshold","getLocThreshold","callThreshold","fanOut","fanOutThreshold","findRiskyFunctionMetrics","sort","findRiskyFunctions","json","summary","reportedRisks","maxFindings","JSON","stringify","thresholds","componentLoc","fileLoc","functionLoc","importSources","totalRisks","truncated","printJson","totalSuffix","printTextReport","failOnError","failOnRisk","exitCode","parseAsync","main","catch"],"mappings":"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"}
|
package/dist/cli.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{realpath as t,stat as e,readdir as
|
|
2
|
+
import{realpath as t,stat as e,readdir as n,readFile as o}from"node:fs/promises";import i from"node:os";import r from"node:path";import{Command as s,InvalidArgumentError as c}from"commander";import{measureCode as a}from"./metrics.js";const l=new Map([[".cjs","javascript"],[".cts","typescript"],[".go","go"],[".js","javascript"],[".jsx","jsx"],[".mjs","javascript"],[".mts","typescript"],[".py","python"],[".ts","typescript"],[".tsx","tsx"]]),u=new Set([".agents",".claude",".cursor",".git",".next",".playwright-cli",".tox",".tmp",".turbo",".venv",".yarn","__fixtures__","__generated__","__pycache__","coverage","dist","fixtures","generated","node_modules","test-fixtures","vendor","venv"]),m=new Set(["__tests__","test","tests"]),p=/(?:^test(?:[_-].*)?|\.(?:spec|test)|[_-]test)\.[^.]+$/iu;async function f(e,o,i,s,c,a,l){let u,m;try{u=await t(e)}catch(t){return void s.push(`${S(e,l)}: ${A(t)}`)}if(_(u,l)&&!c.has(u)){c.add(u);try{m=await n(e,{withFileTypes:!0})}catch(t){return void s.push(`${S(e,l)}: ${A(t)}`)}for(const t of m){const n=r.join(e,t.name);if(t.isSymbolicLink())await h(t.name,n,o,i,s,c,a,l);else if(t.isDirectory()){if(b(t.name,o))continue;await f(n,o,i,s,c,a,l)}else t.isFile()&&await d(n,o,i,s,a,l)}}}async function h(n,o,i,s,c,a,l,u){let m,p;try{m=await t(o)}catch(t){return void c.push(`${S(o,u)}: ${A(t)}`)}if(_(m,u)){try{p=await e(o)}catch(t){return void c.push(`${S(o,u)}: ${A(t)}`)}if(p.isDirectory()){if(b(n,i)||b(r.basename(m),i))return;await f(o,i,s,c,a,l,u)}else p.isFile()&&await d(o,i,s,c,l,u,m,m)}}async function d(t,e,n,o,i,r,s=t,c){const a=j(s,e);a&&await y(t,a,n,o,i,r,c)}async function y(e,n,i,r,s,c,l){try{const r=l??await t(e);if(s.has(r))return;s.add(r);const c=await o(e,"utf8");i.push({file:e,metrics:a(c,{language:n})})}catch(t){r.push(`${S(e,c)}: ${A(t)}`)}}function g(t,e,n,o){const i=[],r=S(t,o);return C(i,"file LOC",e.lines.code,n.fileLocThreshold),C(i,"import sources",e.coupling.importSourceCount,n.importThreshold),0===i.length?[]:[{file:r,language:e.language,kind:"file",cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,triggers:i,score:x(i)}]}function C(t,e,n,o){n<o||t.push({metric:e,value:n,threshold:o,score:n/o})}function x(t){return Math.max(...t.map(t=>t.score))}function $(t,e){return e.score-t.score||t.file.localeCompare(e.file)||(t.startLine??0)-(e.startLine??0)||(t.endLine??0)-(e.endLine??0)||t.kind.localeCompare(e.kind)}function v(t){return void 0===t.startLine||void 0===t.endLine?t.file:`${t.file}:${t.startLine}-${t.endLine}`}function L(t){return t.name?`${t.kind} ${t.name}`:t.kind}function w(t){return`(${t.triggers.map(t=>`${t.metric} ${T(t.value)} >= ${T(t.threshold)}`).join(", ")}; cyclomatic ${t.cyclomaticComplexity}, cognitive ${t.cognitiveComplexity})`}function T(t){return Number.isInteger(t)?String(t):t.toFixed(2)}function O(t){let e=0,n=0,o=0,i=0,r=0,s=0,c=0,a=0,l=0,u=0,m=0,p=0,f=0,h=0,d=0,y=0;for(const g of t)e+=g.metrics.functionCount,n+=g.metrics.lines.code,o=Math.max(o,g.metrics.maxCyclomaticComplexity),i=Math.max(i,g.metrics.maxCognitiveComplexity),r+=g.metrics.callGraph.callCount,s+=g.metrics.callGraph.internalCallCount,c=Math.max(c,g.metrics.callGraph.maxCallDepth),a+=g.metrics.coupling.importSourceCount,l+=g.metrics.coupling.relativeImportCount,u+=g.metrics.coupling.externalImportCount,m+=g.metrics.coupling.exportCount,p+=g.metrics.cohesion.averageFunctionIdentifierOverlap,f+=g.metrics.typeComplexity.typeAnnotationCount,h+=g.metrics.typeComplexity.typeAliasCount,d+=g.metrics.typeComplexity.interfaceCount,y+=g.metrics.typeComplexity.genericParameterCount;return{fileCount:t.length,functionCount:e,linesOfCode:n,maxCyclomaticComplexity:o,maxCognitiveComplexity:i,callCount:r,internalCallCount:s,maxCallDepth:c,importSourceCount:a,relativeImportCount:l,externalImportCount:u,exportCount:m,averageFunctionIdentifierOverlap:0===t.length?0:p/t.length,typeAnnotationCount:f,typeAliasCount:h,interfaceCount:d,genericParameterCount:y}}function b(t,e){return!!u.has(t)||!e.includeTests&&m.has(t)}function _(t,e){const n=r.relative(e,t);return""===n||".."!==n&&!n.startsWith(`..${r.sep}`)&&!r.isAbsolute(n)}function j(t,e,n=!1){const o=t.toLowerCase();if((n||!(o.endsWith(".d.ts")||o.endsWith(".d.mts")||o.endsWith(".d.cts")||o.endsWith(".min.js")||o.endsWith(".pnp.cjs")))&&(n||e.includeTests||!p.test(r.basename(t))))return l.get(r.extname(o))}function k(t){if(!/^[1-9]\d*$/u.test(t))throw new c("Expected a positive integer.");const e=Number(t);if(!Number.isSafeInteger(e)||e<1)throw new c("Expected a positive integer.");return e}function S(t,e){return r.relative(e,t)||r.basename(t)}function E(t){process.stdout.write(t)}function F(t){process.stderr.write(t)}function A(t){return t instanceof Error?t.message:String(t)}(async function(){const n=(new s).name("measure-code").description("Measure code metrics and list high-risk findings.").argument("[target]","file or directory to measure",".").option("--cognitive-threshold <number>","minimum cognitive complexity to report",k,15).option("--cyclomatic-threshold <number>","minimum cyclomatic complexity to report",k,20).option("--function-loc-threshold <number>","minimum function physical LOC span to report",k,80).option("--component-loc-threshold <number>","minimum React component physical LOC span to report",k,250).option("--file-loc-threshold <number>","minimum file code LOC to report",k,300).option("--import-threshold <number>","minimum unique import sources per file to report",k,20).option("--call-threshold <number>","minimum function call count to report",k,50).option("--fan-out-threshold <number>","minimum intra-file fan-out per function to report",k,8).option("--max-findings <number>","maximum number of risk findings to print",k,20).option("--include-tests","include test files and test directories").option("--json","print JSON output").option("--fail-on-error","exit with code 1 when files or directories cannot be scanned").option("--fail-on-risk","exit with code 1 when high-risk findings are found");n.action(async(n,o)=>{const s=function(t){if("~"===t)return i.homedir();if(t.startsWith("~/"))return r.join(i.homedir(),t.slice(2));return r.resolve(t)}(n),c=await async function(n,o){const i=[],s=[],c=new Set;let a=n;try{a=await t(n)}catch{}const l=r.dirname(a);let u;try{u=await e(a)}catch(t){const e=`${S(a,l)}: ${A(t)}`;return{displayRoot:l,files:i,errors:[e],fatalError:e}}if(u.isFile()){const t=r.dirname(a),e=j(a,o,!0);if(!e){const e=`${S(a,t)}: unsupported file type`;return{displayRoot:t,files:i,errors:[e],fatalError:e}}return await y(a,e,i,s,c,t,a),{displayRoot:t,files:i,errors:s}}return await f(a,o,i,s,new Set,c,a),{displayRoot:a,files:i,errors:s}}(s,o),a=function(t,e,n){const o=t.flatMap(({file:t,metrics:o})=>[...g(t,o,e,n),...o.functions.flatMap(i=>function(t,e,n,o,i){const r=n.endLine-n.startLine+1,s=function(t,e){return function(t){return"javascript"===t||"jsx"===t||"typescript"===t||"tsx"===t}(t)&&e.returnsJsx&&void 0!==e.name&&/^[A-Z]/u.test(e.name)}(e,n),c=s?"component":"function",a=[];if(C(a,"cognitive complexity",n.cognitiveComplexity,o.cognitiveThreshold),C(a,"cyclomatic complexity",n.cyclomaticComplexity,o.cyclomaticThreshold),C(a,s?"component LOC":"function LOC",r,function(t,e){return t?e.componentLocThreshold:e.functionLocThreshold}(s,o)),C(a,"function calls",n.callCount,o.callThreshold),C(a,"fan-out",n.fanOut,o.fanOutThreshold),0===a.length)return[];return[{file:S(t,i),language:e,kind:c,name:n.name??"<anonymous>",startLine:n.startLine,endLine:n.endLine,cyclomaticComplexity:n.cyclomaticComplexity,cognitiveComplexity:n.cognitiveComplexity,triggers:a,score:x(a)}]}(t,o.language,i,e,n))]);return o.sort($),o}(c.files,o,c.displayRoot);o.json?function(t,e,n){const o=O(t.files),i=e.slice(0,n.maxFindings);E(JSON.stringify({summary:o,thresholds:{cyclomaticComplexity:n.cyclomaticThreshold,cognitiveComplexity:n.cognitiveThreshold,callCount:n.callThreshold,componentLoc:n.componentLocThreshold,fanOut:n.fanOutThreshold,fileLoc:n.fileLocThreshold,functionLoc:n.functionLocThreshold,importSources:n.importThreshold},totalRisks:e.length,truncated:i.length<e.length,risks:i,errors:t.errors},void 0,2)+"\n")}(c,a,o):function(t,e,n,o){if(e.fatalError)return void F(`Error: ${e.fatalError}\n`);const i=O(e.files);if(E(`Measured ${i.fileCount} files under ${t}\n`),E(`LOC ${i.linesOfCode}, functions ${i.functionCount}, max cyclomatic ${i.maxCyclomaticComplexity}, max cognitive ${i.maxCognitiveComplexity}\n`),E(`Calls ${i.callCount}, internal edges ${i.internalCallCount}, max call depth ${i.maxCallDepth}, imports ${i.importSourceCount}, exports ${i.exportCount}\n`),E(`Type annotations ${i.typeAnnotationCount}, type aliases ${i.typeAliasCount}, interfaces ${i.interfaceCount}, avg cohesion ${i.averageFunctionIdentifierOverlap.toFixed(2)}\n`),E(`Risk thresholds: file LOC >= ${o.fileLocThreshold}, function LOC >= ${o.functionLocThreshold}, component LOC >= ${o.componentLocThreshold}, cognitive >= ${o.cognitiveThreshold}, cyclomatic >= ${o.cyclomaticThreshold}, calls >= ${o.callThreshold}, imports >= ${o.importThreshold}, fan-out >= ${o.fanOutThreshold}\n`),0===n.length)E("No high-risk findings found.\n");else{const t=n.slice(0,o.maxFindings),e=n.length>t.length?` of ${n.length}`:"";E(`\nHigh-risk findings (top ${t.length}${e}):\n`);for(const e of t)E(`${v(e)} ${L(e)} ${w(e)}\n`)}if(e.errors.length>0){F(`\nSkipped ${e.errors.length} files or directories:\n`);for(const t of e.errors.slice(0,10))F(`- ${t}\n`);e.errors.length>10&&F(`- ... ${e.errors.length-10} more\n`)}}(s,c,a,o),(c.fatalError||o.failOnError&&c.errors.length>0||o.failOnRisk&&a.length>0)&&(process.exitCode=1)}),await n.parseAsync()})().catch(t=>{F(`Error: ${A(t)}\n`),process.exitCode=1});
|
|
3
3
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readdir, readFile, realpath, stat } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { measureCode } from './metrics.js';\nimport type { CodeMetrics, FunctionMetrics, LanguageName } from './types.js';\n\ninterface CliOptions {\n cognitiveThreshold: number;\n cyclomaticThreshold: number;\n failOnError?: boolean;\n failOnRisk?: boolean;\n includeTests?: boolean;\n json?: boolean;\n maxFindings: number;\n}\n\ninterface FileMetrics {\n file: string;\n metrics: CodeMetrics;\n}\n\ninterface RiskFinding {\n cognitiveComplexity: number;\n cyclomaticComplexity: number;\n endLine: number;\n file: string;\n language: LanguageName;\n name: string;\n score: number;\n startLine: number;\n}\n\ninterface ScanResult {\n displayRoot: string;\n errors: string[];\n fatalError?: string;\n files: FileMetrics[];\n}\n\nconst languageByExtension = new Map<string, LanguageName>([\n ['.cjs', 'javascript'],\n ['.cts', 'typescript'],\n ['.go', 'go'],\n ['.js', 'javascript'],\n ['.jsx', 'jsx'],\n ['.mjs', 'javascript'],\n ['.mts', 'typescript'],\n ['.py', 'python'],\n ['.ts', 'typescript'],\n ['.tsx', 'tsx'],\n]);\n\nconst ignoredDirectoryNames = new Set([\n '.git',\n '.next',\n '.tox',\n '.tmp',\n '.turbo',\n '.venv',\n '.yarn',\n '__generated__',\n '__pycache__',\n 'coverage',\n 'dist',\n 'generated',\n 'node_modules',\n 'vendor',\n 'venv',\n]);\n\nconst testDirectoryNames = new Set(['__tests__', 'test', 'tests']);\nconst testFilePattern = /(?:^test(?:[_-].*)?|\\.(?:spec|test)|[_-]test)\\.[^.]+$/iu;\n\n// oxlint-disable-next-line unicorn/prefer-top-level-await -- CommonJS build output cannot preserve top-level await.\nvoid main().catch((error: unknown) => {\n writeStderr(`Error: ${formatError(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const program = new Command()\n .name('measure-code')\n .description('Measure code metrics and list high-risk functions.')\n .argument('[target]', 'file or directory to measure', '.')\n .option('--cyclomatic-threshold <number>', 'minimum cyclomatic complexity to report', parsePositiveInteger, 10)\n .option('--cognitive-threshold <number>', 'minimum cognitive complexity to report', parsePositiveInteger, 15)\n .option('--max-findings <number>', 'maximum number of risk findings to print', parsePositiveInteger, 20)\n .option('--include-tests', 'include test files and test directories')\n .option('--json', 'print JSON output')\n .option('--fail-on-error', 'exit with code 1 when files or directories cannot be scanned')\n .option('--fail-on-risk', 'exit with code 1 when high-risk functions are found');\n\n program.action(async (target: string, options: CliOptions) => {\n const resolvedTarget = resolveTarget(target);\n const result = await scanTarget(resolvedTarget, options);\n const risks = findRiskyFunctions(result.files, options, result.displayRoot);\n\n if (options.json) {\n printJson(result, risks, options);\n } else {\n printTextReport(resolvedTarget, result, risks, options);\n }\n\n if (\n result.fatalError ||\n (options.failOnError && result.errors.length > 0) ||\n (options.failOnRisk && risks.length > 0)\n ) {\n process.exitCode = 1;\n }\n });\n\n await program.parseAsync();\n}\n\nfunction resolveTarget(target: string): string {\n if (target === '~') {\n return os.homedir();\n }\n\n if (target.startsWith('~/')) {\n return path.join(os.homedir(), target.slice(2));\n }\n\n return path.resolve(target);\n}\n\nasync function scanTarget(target: string, options: CliOptions): Promise<ScanResult> {\n const files: FileMetrics[] = [];\n const errors: string[] = [];\n const visitedFiles = new Set<string>();\n let canonicalTarget = target;\n try {\n canonicalTarget = await realpath(target);\n } catch {\n // stat below reports missing targets with the original path.\n }\n\n const fallbackDisplayRoot = path.dirname(canonicalTarget);\n let targetStat;\n\n try {\n targetStat = await stat(canonicalTarget);\n } catch (error) {\n const fatalError = `${formatPath(canonicalTarget, fallbackDisplayRoot)}: ${formatError(error)}`;\n return { displayRoot: fallbackDisplayRoot, files, errors: [fatalError], fatalError };\n }\n\n if (targetStat.isFile()) {\n const displayRoot = path.dirname(canonicalTarget);\n const language = getLanguage(canonicalTarget, options, true);\n if (!language) {\n const fatalError = `${formatPath(canonicalTarget, displayRoot)}: unsupported file type`;\n return { displayRoot, files, errors: [fatalError], fatalError };\n }\n\n await measureFile(canonicalTarget, language, files, errors, visitedFiles, displayRoot, canonicalTarget);\n return { displayRoot, files, errors };\n }\n\n await scanDirectory(canonicalTarget, options, files, errors, new Set(), visitedFiles, canonicalTarget);\n return { displayRoot: canonicalTarget, files, errors };\n}\n\nasync function scanDirectory(\n directory: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedDirectory;\n try {\n resolvedDirectory = await realpath(directory);\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedDirectory, rootDirectory)) {\n return;\n }\n\n if (visitedDirectories.has(resolvedDirectory)) {\n return;\n }\n visitedDirectories.add(resolvedDirectory);\n\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(directory, entry.name);\n if (entry.isSymbolicLink()) {\n await scanSymbolicLink(\n entry.name,\n entryPath,\n options,\n files,\n errors,\n visitedDirectories,\n visitedFiles,\n rootDirectory\n );\n continue;\n }\n\n if (entry.isDirectory()) {\n if (shouldSkipDirectory(entry.name, options)) {\n continue;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n continue;\n }\n\n if (entry.isFile()) {\n await measureScannableFile(entryPath, options, files, errors, visitedFiles, rootDirectory);\n }\n }\n}\n\nasync function scanSymbolicLink(\n name: string,\n entryPath: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedPath;\n try {\n resolvedPath = await realpath(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedPath, rootDirectory)) {\n return;\n }\n\n let entryStat;\n try {\n entryStat = await stat(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (entryStat.isDirectory()) {\n if (shouldSkipDirectory(name, options) || shouldSkipDirectory(path.basename(resolvedPath), options)) {\n return;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n return;\n }\n\n if (entryStat.isFile()) {\n await measureScannableFile(\n entryPath,\n options,\n files,\n errors,\n visitedFiles,\n rootDirectory,\n resolvedPath,\n resolvedPath\n );\n }\n}\n\nasync function measureScannableFile(\n file: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n languageFile = file,\n realFile?: string\n): Promise<void> {\n const language = getLanguage(languageFile, options);\n if (language) {\n await measureFile(file, language, files, errors, visitedFiles, displayRoot, realFile);\n }\n}\n\nasync function measureFile(\n file: string,\n language: LanguageName,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n realFile?: string\n): Promise<void> {\n try {\n const resolvedFile = realFile ?? (await realpath(file));\n if (visitedFiles.has(resolvedFile)) {\n return;\n }\n visitedFiles.add(resolvedFile);\n\n const code = await readFile(file, 'utf8');\n files.push({\n file,\n metrics: measureCode(code, { language }),\n });\n } catch (error) {\n errors.push(`${formatPath(file, displayRoot)}: ${formatError(error)}`);\n }\n}\n\nfunction findRiskyFunctions(files: FileMetrics[], options: CliOptions, displayRoot: string): RiskFinding[] {\n const findings = files.flatMap(({ file, metrics }) =>\n metrics.functions\n .filter((fn) => isRiskyFunction(fn, options))\n .map((fn) => createRiskFinding(file, metrics.language, fn, options, displayRoot))\n );\n\n findings.sort((left, right) => right.score - left.score || right.cyclomaticComplexity - left.cyclomaticComplexity);\n return findings;\n}\n\nfunction isRiskyFunction(fn: FunctionMetrics, options: CliOptions): boolean {\n return fn.cyclomaticComplexity >= options.cyclomaticThreshold || fn.cognitiveComplexity >= options.cognitiveThreshold;\n}\n\nfunction createRiskFinding(\n file: string,\n language: LanguageName,\n fn: FunctionMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding {\n return {\n file: formatPath(file, displayRoot),\n language,\n name: fn.name ?? '<anonymous>',\n startLine: fn.startLine,\n endLine: fn.endLine,\n cyclomaticComplexity: fn.cyclomaticComplexity,\n cognitiveComplexity: fn.cognitiveComplexity,\n score: Math.max(\n fn.cyclomaticComplexity / options.cyclomaticThreshold,\n fn.cognitiveComplexity / options.cognitiveThreshold\n ),\n };\n}\n\nfunction printJson(result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n const summary = summarize(result.files);\n const reportedRisks = risks.slice(0, options.maxFindings);\n writeStdout(\n JSON.stringify(\n {\n summary,\n thresholds: {\n cyclomaticComplexity: options.cyclomaticThreshold,\n cognitiveComplexity: options.cognitiveThreshold,\n },\n totalRisks: risks.length,\n truncated: reportedRisks.length < risks.length,\n risks: reportedRisks,\n errors: result.errors,\n },\n undefined,\n 2\n ) + '\\n'\n );\n}\n\nfunction printTextReport(target: string, result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n if (result.fatalError) {\n writeStderr(`Error: ${result.fatalError}\\n`);\n return;\n }\n\n const summary = summarize(result.files);\n writeStdout(`Measured ${summary.fileCount} files under ${target}\\n`);\n writeStdout(\n `LOC ${summary.linesOfCode}, functions ${summary.functionCount}, max cyclomatic ${summary.maxCyclomaticComplexity}, max cognitive ${summary.maxCognitiveComplexity}\\n`\n );\n writeStdout(\n `Calls ${summary.callCount}, internal edges ${summary.internalCallCount}, max call depth ${summary.maxCallDepth}, imports ${summary.importSourceCount}, exports ${summary.exportCount}\\n`\n );\n writeStdout(\n `Type annotations ${summary.typeAnnotationCount}, type aliases ${summary.typeAliasCount}, interfaces ${summary.interfaceCount}, avg cohesion ${summary.averageFunctionIdentifierOverlap.toFixed(2)}\\n`\n );\n writeStdout(\n `Risk thresholds: cyclomatic >= ${options.cyclomaticThreshold}, cognitive >= ${options.cognitiveThreshold}\\n`\n );\n\n if (risks.length === 0) {\n writeStdout('No high-risk functions found.\\n');\n } else {\n const reportedRisks = risks.slice(0, options.maxFindings);\n const totalSuffix = risks.length > reportedRisks.length ? ` of ${risks.length}` : '';\n writeStdout(`\\nHigh-risk functions (top ${reportedRisks.length}${totalSuffix}):\\n`);\n for (const risk of reportedRisks) {\n writeStdout(\n `${risk.file}:${risk.startLine}-${risk.endLine} ${risk.name} ` +\n `(cyclomatic ${risk.cyclomaticComplexity}, cognitive ${risk.cognitiveComplexity})\\n`\n );\n }\n }\n\n if (result.errors.length > 0) {\n writeStderr(`\\nSkipped ${result.errors.length} files or directories:\\n`);\n for (const error of result.errors.slice(0, 10)) {\n writeStderr(`- ${error}\\n`);\n }\n if (result.errors.length > 10) {\n writeStderr(`- ... ${result.errors.length - 10} more\\n`);\n }\n }\n}\n\nfunction summarize(files: FileMetrics[]): {\n fileCount: number;\n functionCount: number;\n linesOfCode: number;\n maxCognitiveComplexity: number;\n maxCyclomaticComplexity: number;\n callCount: number;\n internalCallCount: number;\n maxCallDepth: number;\n importSourceCount: number;\n relativeImportCount: number;\n externalImportCount: number;\n exportCount: number;\n averageFunctionIdentifierOverlap: number;\n typeAnnotationCount: number;\n typeAliasCount: number;\n interfaceCount: number;\n genericParameterCount: number;\n} {\n let functionCount = 0;\n let linesOfCode = 0;\n let maxCyclomaticComplexity = 0;\n let maxCognitiveComplexity = 0;\n let callCount = 0;\n let internalCallCount = 0;\n let maxCallDepth = 0;\n let importSourceCount = 0;\n let relativeImportCount = 0;\n let externalImportCount = 0;\n let exportCount = 0;\n let cohesionTotal = 0;\n let typeAnnotationCount = 0;\n let typeAliasCount = 0;\n let interfaceCount = 0;\n let genericParameterCount = 0;\n\n for (const file of files) {\n functionCount += file.metrics.functionCount;\n linesOfCode += file.metrics.lines.code;\n maxCyclomaticComplexity = Math.max(maxCyclomaticComplexity, file.metrics.maxCyclomaticComplexity);\n maxCognitiveComplexity = Math.max(maxCognitiveComplexity, file.metrics.maxCognitiveComplexity);\n callCount += file.metrics.callGraph.callCount;\n internalCallCount += file.metrics.callGraph.internalCallCount;\n maxCallDepth = Math.max(maxCallDepth, file.metrics.callGraph.maxCallDepth);\n importSourceCount += file.metrics.coupling.importSourceCount;\n relativeImportCount += file.metrics.coupling.relativeImportCount;\n externalImportCount += file.metrics.coupling.externalImportCount;\n exportCount += file.metrics.coupling.exportCount;\n cohesionTotal += file.metrics.cohesion.averageFunctionIdentifierOverlap;\n typeAnnotationCount += file.metrics.typeComplexity.typeAnnotationCount;\n typeAliasCount += file.metrics.typeComplexity.typeAliasCount;\n interfaceCount += file.metrics.typeComplexity.interfaceCount;\n genericParameterCount += file.metrics.typeComplexity.genericParameterCount;\n }\n\n return {\n fileCount: files.length,\n functionCount,\n linesOfCode,\n maxCyclomaticComplexity,\n maxCognitiveComplexity,\n callCount,\n internalCallCount,\n maxCallDepth,\n importSourceCount,\n relativeImportCount,\n externalImportCount,\n exportCount,\n averageFunctionIdentifierOverlap: files.length === 0 ? 0 : cohesionTotal / files.length,\n typeAnnotationCount,\n typeAliasCount,\n interfaceCount,\n genericParameterCount,\n };\n}\n\nfunction shouldSkipDirectory(name: string, options: CliOptions): boolean {\n if (ignoredDirectoryNames.has(name)) {\n return true;\n }\n\n if (options.includeTests) {\n return false;\n }\n\n return testDirectoryNames.has(name);\n}\n\nfunction isWithinDirectory(candidate: string, directory: string): boolean {\n const relative = path.relative(directory, candidate);\n return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));\n}\n\nfunction getLanguage(file: string, options: CliOptions, explicitTarget = false): LanguageName | undefined {\n const lowerFile = file.toLowerCase();\n if (\n !explicitTarget &&\n (lowerFile.endsWith('.d.ts') ||\n lowerFile.endsWith('.d.mts') ||\n lowerFile.endsWith('.d.cts') ||\n lowerFile.endsWith('.min.js'))\n ) {\n return undefined;\n }\n\n if (!explicitTarget && !options.includeTests && testFilePattern.test(path.basename(file))) {\n return undefined;\n }\n\n return languageByExtension.get(path.extname(lowerFile));\n}\n\nfunction parsePositiveInteger(value: string): number {\n if (!/^[1-9]\\d*$/u.test(value)) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n return parsed;\n}\n\nfunction formatPath(file: string, base: string): string {\n return path.relative(base, file) || path.basename(file);\n}\n\nfunction writeStdout(message: string): void {\n process.stdout.write(message);\n}\n\nfunction writeStderr(message: string): void {\n process.stderr.write(message);\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"names":["languageByExtension","Map","ignoredDirectoryNames","Set","testDirectoryNames","testFilePattern","async","scanDirectory","directory","options","files","errors","visitedDirectories","visitedFiles","rootDirectory","resolvedDirectory","entries","realpath","error","push","formatPath","formatError","isWithinDirectory","has","add","readdir","withFileTypes","entry","entryPath","path","join","name","isSymbolicLink","scanSymbolicLink","isDirectory","shouldSkipDirectory","isFile","measureScannableFile","resolvedPath","entryStat","stat","basename","file","displayRoot","languageFile","realFile","language","getLanguage","measureFile","resolvedFile","code","readFile","metrics","measureCode","summarize","functionCount","linesOfCode","maxCyclomaticComplexity","maxCognitiveComplexity","callCount","internalCallCount","maxCallDepth","importSourceCount","relativeImportCount","externalImportCount","exportCount","cohesionTotal","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","lines","Math","max","callGraph","coupling","cohesion","averageFunctionIdentifierOverlap","typeComplexity","fileCount","length","includeTests","candidate","relative","startsWith","sep","isAbsolute","explicitTarget","lowerFile","toLowerCase","endsWith","test","get","extname","parsePositiveInteger","value","InvalidArgumentError","parsed","Number","isSafeInteger","base","writeStdout","message","process","stdout","write","writeStderr","stderr","Error","String","program","Command","description","argument","option","action","target","resolvedTarget","os","homedir","slice","resolve","resolveTarget","result","canonicalTarget","fallbackDisplayRoot","dirname","targetStat","fatalError","scanTarget","risks","findings","flatMap","functions","filter","fn","cyclomaticComplexity","cyclomaticThreshold","cognitiveComplexity","cognitiveThreshold","isRiskyFunction","map","startLine","endLine","score","createRiskFinding","sort","left","right","findRiskyFunctions","json","summary","reportedRisks","maxFindings","JSON","stringify","thresholds","totalRisks","truncated","undefined","printJson","toFixed","totalSuffix","risk","printTextReport","failOnError","failOnRisk","exitCode","parseAsync","main","catch"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"cli.js","sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { readdir, readFile, realpath, stat } from 'node:fs/promises';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { Command, InvalidArgumentError } from 'commander';\nimport { measureCode } from './metrics.js';\nimport type { CodeMetrics, FunctionMetrics, LanguageName } from './types.js';\n\ninterface CliOptions {\n callThreshold: number;\n cognitiveThreshold: number;\n componentLocThreshold: number;\n cyclomaticThreshold: number;\n fanOutThreshold: number;\n failOnError?: boolean;\n failOnRisk?: boolean;\n fileLocThreshold: number;\n includeTests?: boolean;\n importThreshold: number;\n functionLocThreshold: number;\n json?: boolean;\n maxFindings: number;\n}\n\ninterface FileMetrics {\n file: string;\n metrics: CodeMetrics;\n}\n\ninterface RiskTrigger {\n metric: string;\n score: number;\n threshold: number;\n value: number;\n}\n\ninterface RiskFinding {\n cognitiveComplexity: number;\n cyclomaticComplexity: number;\n endLine?: number;\n file: string;\n kind: 'component' | 'file' | 'function';\n language: LanguageName;\n name?: string;\n score: number;\n startLine?: number;\n triggers: RiskTrigger[];\n}\n\ninterface ScanResult {\n displayRoot: string;\n errors: string[];\n fatalError?: string;\n files: FileMetrics[];\n}\n\nconst languageByExtension = new Map<string, LanguageName>([\n ['.cjs', 'javascript'],\n ['.cts', 'typescript'],\n ['.go', 'go'],\n ['.js', 'javascript'],\n ['.jsx', 'jsx'],\n ['.mjs', 'javascript'],\n ['.mts', 'typescript'],\n ['.py', 'python'],\n ['.ts', 'typescript'],\n ['.tsx', 'tsx'],\n]);\n\nconst ignoredDirectoryNames = new Set([\n '.agents',\n '.claude',\n '.cursor',\n '.git',\n '.next',\n '.playwright-cli',\n '.tox',\n '.tmp',\n '.turbo',\n '.venv',\n '.yarn',\n '__fixtures__',\n '__generated__',\n '__pycache__',\n 'coverage',\n 'dist',\n 'fixtures',\n 'generated',\n 'node_modules',\n 'test-fixtures',\n 'vendor',\n 'venv',\n]);\n\nconst testDirectoryNames = new Set(['__tests__', 'test', 'tests']);\nconst testFilePattern = /(?:^test(?:[_-].*)?|\\.(?:spec|test)|[_-]test)\\.[^.]+$/iu;\n\n// oxlint-disable-next-line unicorn/prefer-top-level-await -- CommonJS build output cannot preserve top-level await.\nvoid main().catch((error: unknown) => {\n writeStderr(`Error: ${formatError(error)}\\n`);\n process.exitCode = 1;\n});\n\nasync function main(): Promise<void> {\n const program = new Command()\n .name('measure-code')\n .description('Measure code metrics and list high-risk findings.')\n .argument('[target]', 'file or directory to measure', '.')\n .option('--cognitive-threshold <number>', 'minimum cognitive complexity to report', parsePositiveInteger, 15)\n .option('--cyclomatic-threshold <number>', 'minimum cyclomatic complexity to report', parsePositiveInteger, 20)\n .option(\n '--function-loc-threshold <number>',\n 'minimum function physical LOC span to report',\n parsePositiveInteger,\n 80\n )\n .option(\n '--component-loc-threshold <number>',\n 'minimum React component physical LOC span to report',\n parsePositiveInteger,\n 250\n )\n .option('--file-loc-threshold <number>', 'minimum file code LOC to report', parsePositiveInteger, 300)\n .option('--import-threshold <number>', 'minimum unique import sources per file to report', parsePositiveInteger, 20)\n .option('--call-threshold <number>', 'minimum function call count to report', parsePositiveInteger, 50)\n .option(\n '--fan-out-threshold <number>',\n 'minimum intra-file fan-out per function to report',\n parsePositiveInteger,\n 8\n )\n .option('--max-findings <number>', 'maximum number of risk findings to print', parsePositiveInteger, 20)\n .option('--include-tests', 'include test files and test directories')\n .option('--json', 'print JSON output')\n .option('--fail-on-error', 'exit with code 1 when files or directories cannot be scanned')\n .option('--fail-on-risk', 'exit with code 1 when high-risk findings are found');\n\n program.action(async (target: string, options: CliOptions) => {\n const resolvedTarget = resolveTarget(target);\n const result = await scanTarget(resolvedTarget, options);\n const risks = findRiskyFunctions(result.files, options, result.displayRoot);\n\n if (options.json) {\n printJson(result, risks, options);\n } else {\n printTextReport(resolvedTarget, result, risks, options);\n }\n\n if (\n result.fatalError ||\n (options.failOnError && result.errors.length > 0) ||\n (options.failOnRisk && risks.length > 0)\n ) {\n process.exitCode = 1;\n }\n });\n\n await program.parseAsync();\n}\n\nfunction resolveTarget(target: string): string {\n if (target === '~') {\n return os.homedir();\n }\n\n if (target.startsWith('~/')) {\n return path.join(os.homedir(), target.slice(2));\n }\n\n return path.resolve(target);\n}\n\nasync function scanTarget(target: string, options: CliOptions): Promise<ScanResult> {\n const files: FileMetrics[] = [];\n const errors: string[] = [];\n const visitedFiles = new Set<string>();\n let canonicalTarget = target;\n try {\n canonicalTarget = await realpath(target);\n } catch {\n // stat below reports missing targets with the original path.\n }\n\n const fallbackDisplayRoot = path.dirname(canonicalTarget);\n let targetStat;\n\n try {\n targetStat = await stat(canonicalTarget);\n } catch (error) {\n const fatalError = `${formatPath(canonicalTarget, fallbackDisplayRoot)}: ${formatError(error)}`;\n return { displayRoot: fallbackDisplayRoot, files, errors: [fatalError], fatalError };\n }\n\n if (targetStat.isFile()) {\n const displayRoot = path.dirname(canonicalTarget);\n const language = getLanguage(canonicalTarget, options, true);\n if (!language) {\n const fatalError = `${formatPath(canonicalTarget, displayRoot)}: unsupported file type`;\n return { displayRoot, files, errors: [fatalError], fatalError };\n }\n\n await measureFile(canonicalTarget, language, files, errors, visitedFiles, displayRoot, canonicalTarget);\n return { displayRoot, files, errors };\n }\n\n await scanDirectory(canonicalTarget, options, files, errors, new Set(), visitedFiles, canonicalTarget);\n return { displayRoot: canonicalTarget, files, errors };\n}\n\nasync function scanDirectory(\n directory: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedDirectory;\n try {\n resolvedDirectory = await realpath(directory);\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedDirectory, rootDirectory)) {\n return;\n }\n\n if (visitedDirectories.has(resolvedDirectory)) {\n return;\n }\n visitedDirectories.add(resolvedDirectory);\n\n let entries;\n try {\n entries = await readdir(directory, { withFileTypes: true });\n } catch (error) {\n errors.push(`${formatPath(directory, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n for (const entry of entries) {\n const entryPath = path.join(directory, entry.name);\n if (entry.isSymbolicLink()) {\n await scanSymbolicLink(\n entry.name,\n entryPath,\n options,\n files,\n errors,\n visitedDirectories,\n visitedFiles,\n rootDirectory\n );\n continue;\n }\n\n if (entry.isDirectory()) {\n if (shouldSkipDirectory(entry.name, options)) {\n continue;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n continue;\n }\n\n if (entry.isFile()) {\n await measureScannableFile(entryPath, options, files, errors, visitedFiles, rootDirectory);\n }\n }\n}\n\nasync function scanSymbolicLink(\n name: string,\n entryPath: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedDirectories: Set<string>,\n visitedFiles: Set<string>,\n rootDirectory: string\n): Promise<void> {\n let resolvedPath;\n try {\n resolvedPath = await realpath(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (!isWithinDirectory(resolvedPath, rootDirectory)) {\n return;\n }\n\n let entryStat;\n try {\n entryStat = await stat(entryPath);\n } catch (error) {\n errors.push(`${formatPath(entryPath, rootDirectory)}: ${formatError(error)}`);\n return;\n }\n\n if (entryStat.isDirectory()) {\n if (shouldSkipDirectory(name, options) || shouldSkipDirectory(path.basename(resolvedPath), options)) {\n return;\n }\n await scanDirectory(entryPath, options, files, errors, visitedDirectories, visitedFiles, rootDirectory);\n return;\n }\n\n if (entryStat.isFile()) {\n await measureScannableFile(\n entryPath,\n options,\n files,\n errors,\n visitedFiles,\n rootDirectory,\n resolvedPath,\n resolvedPath\n );\n }\n}\n\nasync function measureScannableFile(\n file: string,\n options: CliOptions,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n languageFile = file,\n realFile?: string\n): Promise<void> {\n const language = getLanguage(languageFile, options);\n if (language) {\n await measureFile(file, language, files, errors, visitedFiles, displayRoot, realFile);\n }\n}\n\nasync function measureFile(\n file: string,\n language: LanguageName,\n files: FileMetrics[],\n errors: string[],\n visitedFiles: Set<string>,\n displayRoot: string,\n realFile?: string\n): Promise<void> {\n try {\n const resolvedFile = realFile ?? (await realpath(file));\n if (visitedFiles.has(resolvedFile)) {\n return;\n }\n visitedFiles.add(resolvedFile);\n\n const code = await readFile(file, 'utf8');\n files.push({\n file,\n metrics: measureCode(code, { language }),\n });\n } catch (error) {\n errors.push(`${formatPath(file, displayRoot)}: ${formatError(error)}`);\n }\n}\n\nfunction findRiskyFunctions(files: FileMetrics[], options: CliOptions, displayRoot: string): RiskFinding[] {\n const findings = files.flatMap(({ file, metrics }) => [\n ...findRiskyFileMetrics(file, metrics, options, displayRoot),\n ...metrics.functions.flatMap((fn) => findRiskyFunctionMetrics(file, metrics.language, fn, options, displayRoot)),\n ]);\n\n findings.sort(compareRiskFindings);\n return findings;\n}\n\nfunction findRiskyFileMetrics(\n file: string,\n metrics: CodeMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding[] {\n const triggers: RiskTrigger[] = [];\n const formattedFile = formatPath(file, displayRoot);\n addTrigger(triggers, 'file LOC', metrics.lines.code, options.fileLocThreshold);\n addTrigger(triggers, 'import sources', metrics.coupling.importSourceCount, options.importThreshold);\n if (triggers.length === 0) {\n return [];\n }\n\n return [\n {\n file: formattedFile,\n language: metrics.language,\n kind: 'file',\n cyclomaticComplexity: metrics.cyclomaticComplexity,\n cognitiveComplexity: metrics.cognitiveComplexity,\n triggers,\n score: maxTriggerScore(triggers),\n },\n ];\n}\n\nfunction findRiskyFunctionMetrics(\n file: string,\n language: LanguageName,\n fn: FunctionMetrics,\n options: CliOptions,\n displayRoot: string\n): RiskFinding[] {\n const loc = fn.endLine - fn.startLine + 1;\n const isComponent = isReactComponent(language, fn);\n const kind = isComponent ? 'component' : 'function';\n const triggers: RiskTrigger[] = [];\n addTrigger(triggers, 'cognitive complexity', fn.cognitiveComplexity, options.cognitiveThreshold);\n addTrigger(triggers, 'cyclomatic complexity', fn.cyclomaticComplexity, options.cyclomaticThreshold);\n addTrigger(triggers, isComponent ? 'component LOC' : 'function LOC', loc, getLocThreshold(isComponent, options));\n addTrigger(triggers, 'function calls', fn.callCount, options.callThreshold);\n addTrigger(triggers, 'fan-out', fn.fanOut, options.fanOutThreshold);\n if (triggers.length === 0) {\n return [];\n }\n\n return [\n {\n file: formatPath(file, displayRoot),\n language,\n kind,\n name: fn.name ?? '<anonymous>',\n startLine: fn.startLine,\n endLine: fn.endLine,\n cyclomaticComplexity: fn.cyclomaticComplexity,\n cognitiveComplexity: fn.cognitiveComplexity,\n triggers,\n score: maxTriggerScore(triggers),\n },\n ];\n}\n\nfunction addTrigger(triggers: RiskTrigger[], metric: string, value: number, threshold: number): void {\n if (value < threshold) {\n return;\n }\n\n triggers.push({ metric, value, threshold, score: value / threshold });\n}\n\nfunction isReactComponent(language: LanguageName, fn: FunctionMetrics): boolean {\n return isJavaScriptLikeLanguage(language) && fn.returnsJsx && fn.name !== undefined && /^[A-Z]/u.test(fn.name);\n}\n\nfunction isJavaScriptLikeLanguage(language: LanguageName): boolean {\n return language === 'javascript' || language === 'jsx' || language === 'typescript' || language === 'tsx';\n}\n\nfunction getLocThreshold(isComponent: boolean, options: CliOptions): number {\n return isComponent ? options.componentLocThreshold : options.functionLocThreshold;\n}\n\nfunction maxTriggerScore(triggers: RiskTrigger[]): number {\n return Math.max(...triggers.map((trigger) => trigger.score));\n}\n\nfunction compareRiskFindings(left: RiskFinding, right: RiskFinding): number {\n return (\n right.score - left.score ||\n left.file.localeCompare(right.file) ||\n (left.startLine ?? 0) - (right.startLine ?? 0) ||\n (left.endLine ?? 0) - (right.endLine ?? 0) ||\n left.kind.localeCompare(right.kind)\n );\n}\n\nfunction printJson(result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n const summary = summarize(result.files);\n const reportedRisks = risks.slice(0, options.maxFindings);\n writeStdout(\n JSON.stringify(\n {\n summary,\n thresholds: {\n cyclomaticComplexity: options.cyclomaticThreshold,\n cognitiveComplexity: options.cognitiveThreshold,\n callCount: options.callThreshold,\n componentLoc: options.componentLocThreshold,\n fanOut: options.fanOutThreshold,\n fileLoc: options.fileLocThreshold,\n functionLoc: options.functionLocThreshold,\n importSources: options.importThreshold,\n },\n totalRisks: risks.length,\n truncated: reportedRisks.length < risks.length,\n risks: reportedRisks,\n errors: result.errors,\n },\n undefined,\n 2\n ) + '\\n'\n );\n}\n\nfunction printTextReport(target: string, result: ScanResult, risks: RiskFinding[], options: CliOptions): void {\n if (result.fatalError) {\n writeStderr(`Error: ${result.fatalError}\\n`);\n return;\n }\n\n const summary = summarize(result.files);\n writeStdout(`Measured ${summary.fileCount} files under ${target}\\n`);\n writeStdout(\n `LOC ${summary.linesOfCode}, functions ${summary.functionCount}, max cyclomatic ${summary.maxCyclomaticComplexity}, max cognitive ${summary.maxCognitiveComplexity}\\n`\n );\n writeStdout(\n `Calls ${summary.callCount}, internal edges ${summary.internalCallCount}, max call depth ${summary.maxCallDepth}, imports ${summary.importSourceCount}, exports ${summary.exportCount}\\n`\n );\n writeStdout(\n `Type annotations ${summary.typeAnnotationCount}, type aliases ${summary.typeAliasCount}, interfaces ${summary.interfaceCount}, avg cohesion ${summary.averageFunctionIdentifierOverlap.toFixed(2)}\\n`\n );\n writeStdout(\n `Risk thresholds: file LOC >= ${options.fileLocThreshold}, function LOC >= ${options.functionLocThreshold}, component LOC >= ${options.componentLocThreshold}, cognitive >= ${options.cognitiveThreshold}, cyclomatic >= ${options.cyclomaticThreshold}, calls >= ${options.callThreshold}, imports >= ${options.importThreshold}, fan-out >= ${options.fanOutThreshold}\\n`\n );\n\n if (risks.length === 0) {\n writeStdout('No high-risk findings found.\\n');\n } else {\n const reportedRisks = risks.slice(0, options.maxFindings);\n const totalSuffix = risks.length > reportedRisks.length ? ` of ${risks.length}` : '';\n writeStdout(`\\nHigh-risk findings (top ${reportedRisks.length}${totalSuffix}):\\n`);\n for (const risk of reportedRisks) {\n writeStdout(`${formatRiskLocation(risk)} ${formatRiskName(risk)} ${formatRiskMetrics(risk)}\\n`);\n }\n }\n\n if (result.errors.length > 0) {\n writeStderr(`\\nSkipped ${result.errors.length} files or directories:\\n`);\n for (const error of result.errors.slice(0, 10)) {\n writeStderr(`- ${error}\\n`);\n }\n if (result.errors.length > 10) {\n writeStderr(`- ... ${result.errors.length - 10} more\\n`);\n }\n }\n}\n\nfunction formatRiskLocation(risk: RiskFinding): string {\n return risk.startLine === undefined || risk.endLine === undefined\n ? risk.file\n : `${risk.file}:${risk.startLine}-${risk.endLine}`;\n}\n\nfunction formatRiskName(risk: RiskFinding): string {\n return risk.name ? `${risk.kind} ${risk.name}` : risk.kind;\n}\n\nfunction formatRiskMetrics(risk: RiskFinding): string {\n const triggerText = risk.triggers\n .map(\n (trigger) => `${trigger.metric} ${formatMetricValue(trigger.value)} >= ${formatMetricValue(trigger.threshold)}`\n )\n .join(', ');\n return `(${triggerText}; cyclomatic ${risk.cyclomaticComplexity}, cognitive ${risk.cognitiveComplexity})`;\n}\n\nfunction formatMetricValue(value: number): string {\n return Number.isInteger(value) ? String(value) : value.toFixed(2);\n}\n\nfunction summarize(files: FileMetrics[]): {\n fileCount: number;\n functionCount: number;\n linesOfCode: number;\n maxCognitiveComplexity: number;\n maxCyclomaticComplexity: number;\n callCount: number;\n internalCallCount: number;\n maxCallDepth: number;\n importSourceCount: number;\n relativeImportCount: number;\n externalImportCount: number;\n exportCount: number;\n averageFunctionIdentifierOverlap: number;\n typeAnnotationCount: number;\n typeAliasCount: number;\n interfaceCount: number;\n genericParameterCount: number;\n} {\n let functionCount = 0;\n let linesOfCode = 0;\n let maxCyclomaticComplexity = 0;\n let maxCognitiveComplexity = 0;\n let callCount = 0;\n let internalCallCount = 0;\n let maxCallDepth = 0;\n let importSourceCount = 0;\n let relativeImportCount = 0;\n let externalImportCount = 0;\n let exportCount = 0;\n let cohesionTotal = 0;\n let typeAnnotationCount = 0;\n let typeAliasCount = 0;\n let interfaceCount = 0;\n let genericParameterCount = 0;\n\n for (const file of files) {\n functionCount += file.metrics.functionCount;\n linesOfCode += file.metrics.lines.code;\n maxCyclomaticComplexity = Math.max(maxCyclomaticComplexity, file.metrics.maxCyclomaticComplexity);\n maxCognitiveComplexity = Math.max(maxCognitiveComplexity, file.metrics.maxCognitiveComplexity);\n callCount += file.metrics.callGraph.callCount;\n internalCallCount += file.metrics.callGraph.internalCallCount;\n maxCallDepth = Math.max(maxCallDepth, file.metrics.callGraph.maxCallDepth);\n importSourceCount += file.metrics.coupling.importSourceCount;\n relativeImportCount += file.metrics.coupling.relativeImportCount;\n externalImportCount += file.metrics.coupling.externalImportCount;\n exportCount += file.metrics.coupling.exportCount;\n cohesionTotal += file.metrics.cohesion.averageFunctionIdentifierOverlap;\n typeAnnotationCount += file.metrics.typeComplexity.typeAnnotationCount;\n typeAliasCount += file.metrics.typeComplexity.typeAliasCount;\n interfaceCount += file.metrics.typeComplexity.interfaceCount;\n genericParameterCount += file.metrics.typeComplexity.genericParameterCount;\n }\n\n return {\n fileCount: files.length,\n functionCount,\n linesOfCode,\n maxCyclomaticComplexity,\n maxCognitiveComplexity,\n callCount,\n internalCallCount,\n maxCallDepth,\n importSourceCount,\n relativeImportCount,\n externalImportCount,\n exportCount,\n averageFunctionIdentifierOverlap: files.length === 0 ? 0 : cohesionTotal / files.length,\n typeAnnotationCount,\n typeAliasCount,\n interfaceCount,\n genericParameterCount,\n };\n}\n\nfunction shouldSkipDirectory(name: string, options: CliOptions): boolean {\n if (ignoredDirectoryNames.has(name)) {\n return true;\n }\n\n if (options.includeTests) {\n return false;\n }\n\n return testDirectoryNames.has(name);\n}\n\nfunction isWithinDirectory(candidate: string, directory: string): boolean {\n const relative = path.relative(directory, candidate);\n return relative === '' || (relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative));\n}\n\nfunction getLanguage(file: string, options: CliOptions, explicitTarget = false): LanguageName | undefined {\n const lowerFile = file.toLowerCase();\n if (\n !explicitTarget &&\n (lowerFile.endsWith('.d.ts') ||\n lowerFile.endsWith('.d.mts') ||\n lowerFile.endsWith('.d.cts') ||\n lowerFile.endsWith('.min.js') ||\n lowerFile.endsWith('.pnp.cjs'))\n ) {\n return undefined;\n }\n\n if (!explicitTarget && !options.includeTests && testFilePattern.test(path.basename(file))) {\n return undefined;\n }\n\n return languageByExtension.get(path.extname(lowerFile));\n}\n\nfunction parsePositiveInteger(value: string): number {\n if (!/^[1-9]\\d*$/u.test(value)) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n\n const parsed = Number(value);\n if (!Number.isSafeInteger(parsed) || parsed < 1) {\n throw new InvalidArgumentError('Expected a positive integer.');\n }\n return parsed;\n}\n\nfunction formatPath(file: string, base: string): string {\n return path.relative(base, file) || path.basename(file);\n}\n\nfunction writeStdout(message: string): void {\n process.stdout.write(message);\n}\n\nfunction writeStderr(message: string): void {\n process.stderr.write(message);\n}\n\nfunction formatError(error: unknown): string {\n return error instanceof Error ? error.message : String(error);\n}\n"],"names":["languageByExtension","Map","ignoredDirectoryNames","Set","testDirectoryNames","testFilePattern","async","scanDirectory","directory","options","files","errors","visitedDirectories","visitedFiles","rootDirectory","resolvedDirectory","entries","realpath","error","push","formatPath","formatError","isWithinDirectory","has","add","readdir","withFileTypes","entry","entryPath","path","join","name","isSymbolicLink","scanSymbolicLink","isDirectory","shouldSkipDirectory","isFile","measureScannableFile","resolvedPath","entryStat","stat","basename","file","displayRoot","languageFile","realFile","language","getLanguage","measureFile","resolvedFile","code","readFile","metrics","measureCode","findRiskyFileMetrics","triggers","formattedFile","addTrigger","lines","fileLocThreshold","coupling","importSourceCount","importThreshold","length","kind","cyclomaticComplexity","cognitiveComplexity","score","maxTriggerScore","metric","value","threshold","Math","max","map","trigger","compareRiskFindings","left","right","localeCompare","startLine","endLine","formatRiskLocation","risk","undefined","formatRiskName","formatRiskMetrics","formatMetricValue","Number","isInteger","String","toFixed","summarize","functionCount","linesOfCode","maxCyclomaticComplexity","maxCognitiveComplexity","callCount","internalCallCount","maxCallDepth","relativeImportCount","externalImportCount","exportCount","cohesionTotal","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","callGraph","cohesion","averageFunctionIdentifierOverlap","typeComplexity","fileCount","includeTests","candidate","relative","startsWith","sep","isAbsolute","explicitTarget","lowerFile","toLowerCase","endsWith","test","get","extname","parsePositiveInteger","InvalidArgumentError","parsed","isSafeInteger","base","writeStdout","message","process","stdout","write","writeStderr","stderr","Error","program","Command","description","argument","option","action","target","resolvedTarget","os","homedir","slice","resolve","resolveTarget","result","canonicalTarget","fallbackDisplayRoot","dirname","targetStat","fatalError","scanTarget","risks","findings","flatMap","functions","fn","loc","isComponent","isJavaScriptLikeLanguage","returnsJsx","isReactComponent","cognitiveThreshold","cyclomaticThreshold","componentLocThreshold","functionLocThreshold","getLocThreshold","callThreshold","fanOut","fanOutThreshold","findRiskyFunctionMetrics","sort","findRiskyFunctions","json","summary","reportedRisks","maxFindings","JSON","stringify","thresholds","componentLoc","fileLoc","functionLoc","importSources","totalRisks","truncated","printJson","totalSuffix","printTextReport","failOnError","failOnRisk","exitCode","parseAsync","main","catch"],"mappings":"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"}
|
package/dist/languages.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("tree-sitter-go"),t=require("tree-sitter-javascript"),a=require("tree-sitter-python"),n=require("tree-sitter-typescript");const s=["function","function_declaration","function_definition","function_item","function_declarator","method_declaration","method_definition","method_spec","arrow_function","generator_function","generator_function_declaration","lambda","lambda_expression"],r=["class","class_declaration","class_definition","interface_declaration","trait_item","impl_item","struct_item","enum_item"],i=["if_statement","elif_clause","else_if_clause","for_statement","for_in_statement","while_statement","do_statement","catch_clause","except_clause","case_clause","switch_case","match_arm","conditional_expression","ternary_expression"];function o(e){return function(e,t){if("object"!=typeof e||null===e||!(t in e))return!1;return Boolean(e[t])}(e,"default")?e.default:e}function c(e){return n[e]}const u=[{name:"javascript",aliases:["js","mjs","cjs"],parserLanguage:o(t)},{name:"jsx",parserLanguage:o(t)},{name:"typescript",aliases:["ts"],parserLanguage:c("typescript")},{name:"tsx",parserLanguage:c("tsx")},{name:"python",aliases:["py"],parserLanguage:o(a)},{name:"go",parserLanguage:o(e)}].map(e=>({functionNodeTypes:s,classNodeTypes:r,decisionNodeTypes:i,nestingNodeTypes:i,...e}));const p=u.map(e=>e.name);exports.createLanguageRegistry=function(e=u){const t=new Map;for(const a of e){t.set(a.name,a);for(const e of a.aliases??[])t.set(e,a)}return t},exports.defaultLanguages=u,exports.supportedLanguages=p;
|
|
1
|
+
"use strict";var e=require("tree-sitter-go"),t=require("tree-sitter-javascript"),a=require("tree-sitter-python"),n=require("tree-sitter-typescript");const s=["function","function_declaration","function_definition","function_expression","function_item","function_declarator","method_declaration","method_definition","method_spec","arrow_function","generator_function","generator_function_declaration","lambda","lambda_expression"],r=["class","class_declaration","class_definition","interface_declaration","trait_item","impl_item","struct_item","enum_item"],i=["if_statement","elif_clause","else_if_clause","for_statement","for_in_statement","while_statement","do_statement","catch_clause","except_clause","case_clause","switch_case","match_arm","conditional_expression","ternary_expression"];function o(e){return function(e,t){if("object"!=typeof e||null===e||!(t in e))return!1;return Boolean(e[t])}(e,"default")?e.default:e}function c(e){return n[e]}const u=[{name:"javascript",aliases:["js","mjs","cjs"],parserLanguage:o(t)},{name:"jsx",parserLanguage:o(t)},{name:"typescript",aliases:["ts"],parserLanguage:c("typescript")},{name:"tsx",parserLanguage:c("tsx")},{name:"python",aliases:["py"],parserLanguage:o(a)},{name:"go",parserLanguage:o(e)}].map(e=>({functionNodeTypes:s,classNodeTypes:r,decisionNodeTypes:i,nestingNodeTypes:i,...e}));const p=u.map(e=>e.name);exports.createLanguageRegistry=function(e=u){const t=new Map;for(const a of e){t.set(a.name,a);for(const e of a.aliases??[])t.set(e,a)}return t},exports.defaultLanguages=u,exports.supportedLanguages=p;
|
|
2
2
|
//# sourceMappingURL=languages.cjs.map
|
package/dist/languages.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"languages.cjs","sources":["../src/languages.ts"],"sourcesContent":["import Go from 'tree-sitter-go';\nimport JavaScript from 'tree-sitter-javascript';\nimport Python from 'tree-sitter-python';\nimport TypeScript from 'tree-sitter-typescript';\nimport type { LanguageDefinition, LanguageName, ParserLanguage } from './types.js';\n\ntype GrammarModule = unknown;\n\nconst commonFunctionNodes = [\n 'function',\n 'function_declaration',\n 'function_definition',\n 'function_item',\n 'function_declarator',\n 'method_declaration',\n 'method_definition',\n 'method_spec',\n 'arrow_function',\n 'generator_function',\n 'generator_function_declaration',\n 'lambda',\n 'lambda_expression',\n] as const;\n\nconst commonClassNodes = [\n 'class',\n 'class_declaration',\n 'class_definition',\n 'interface_declaration',\n 'trait_item',\n 'impl_item',\n 'struct_item',\n 'enum_item',\n] as const;\n\nconst commonDecisionNodes = [\n 'if_statement',\n 'elif_clause',\n 'else_if_clause',\n 'for_statement',\n 'for_in_statement',\n 'while_statement',\n 'do_statement',\n 'catch_clause',\n 'except_clause',\n 'case_clause',\n 'switch_case',\n 'match_arm',\n 'conditional_expression',\n 'ternary_expression',\n] as const;\n\nfunction normalizeGrammar(module: GrammarModule): ParserLanguage {\n if (isGrammarWrapper(module, 'default')) {\n return module.default;\n }\n\n return module;\n}\n\nfunction getTypeScriptGrammar(name: 'typescript' | 'tsx'): ParserLanguage {\n const grammars = TypeScript as unknown as Record<string, GrammarModule>;\n return grammars[name];\n}\n\nfunction isGrammarWrapper(value: GrammarModule, key: 'default'): value is Record<typeof key, ParserLanguage> {\n if (typeof value !== 'object' || value === null || !(key in value)) {\n return false;\n }\n\n return Boolean((value as Record<string, ParserLanguage>)[key]);\n}\n\nexport const defaultLanguages: readonly LanguageDefinition[] = [\n {\n name: 'javascript',\n aliases: ['js', 'mjs', 'cjs'],\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'jsx',\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'typescript',\n aliases: ['ts'],\n parserLanguage: getTypeScriptGrammar('typescript'),\n },\n {\n name: 'tsx',\n parserLanguage: getTypeScriptGrammar('tsx'),\n },\n {\n name: 'python',\n aliases: ['py'],\n parserLanguage: normalizeGrammar(Python as unknown as GrammarModule),\n },\n {\n name: 'go',\n parserLanguage: normalizeGrammar(Go as unknown as GrammarModule),\n },\n].map((language) => ({\n functionNodeTypes: commonFunctionNodes,\n classNodeTypes: commonClassNodes,\n decisionNodeTypes: commonDecisionNodes,\n nestingNodeTypes: commonDecisionNodes,\n ...language,\n}));\n\nexport function createLanguageRegistry(\n languages: readonly LanguageDefinition[] = defaultLanguages\n): Map<LanguageName, LanguageDefinition> {\n const registry = new Map<LanguageName, LanguageDefinition>();\n\n for (const language of languages) {\n registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n registry.set(alias, language);\n }\n }\n\n return registry;\n}\n\nexport const supportedLanguages = defaultLanguages.map((language) => language.name);\n"],"names":["commonFunctionNodes","commonClassNodes","commonDecisionNodes","normalizeGrammar","module","value","key","Boolean","isGrammarWrapper","default","getTypeScriptGrammar","name","TypeScript","defaultLanguages","aliases","parserLanguage","JavaScript","Python","Go","map","language","functionNodeTypes","classNodeTypes","decisionNodeTypes","nestingNodeTypes","supportedLanguages","languages","registry","Map","set","alias"],"mappings":"qJAQA,MAAMA,EAAsB,CAC1B,WACA,uBACA,sBACA,gBACA,sBACA,qBACA,oBACA,cACA,iBACA,qBACA,iCACA,SACA,qBAGIC,EAAmB,CACvB,QACA,oBACA,mBACA,wBACA,aACA,YACA,cACA,aAGIC,EAAsB,CAC1B,eACA,cACA,iBACA,gBACA,mBACA,kBACA,eACA,eACA,gBACA,cACA,cACA,YACA,yBACA,sBAGF,SAASC,EAAiBC,GACxB,OAYF,SAA0BC,EAAsBC,GAC9C,GAAqB,iBAAVD,GAAgC,OAAVA,KAAoBC,KAAOD,GAC1D,OAAO,EAGT,OAAOE,QAASF,EAAyCC,GAC3D,CAlBME,CAAiBJ,EAAQ,WACpBA,EAAOK,QAGTL,CACT,CAEA,SAASM,EAAqBC,GAE5B,OADiBC,EACDD,EAClB,CAUO,MAAME,EAAkD,CAC7D,CACEF,KAAM,aACNG,QAAS,CAAC,KAAM,MAAO,OACvBC,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,MACNI,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,aACNG,QAAS,CAAC,MACVC,eAAgBL,EAAqB,eAEvC,CACEC,KAAM,MACNI,eAAgBL,EAAqB,QAEvC,CACEC,KAAM,SACNG,QAAS,CAAC,MACVC,eAAgBZ,EAAiBc,IAEnC,CACEN,KAAM,KACNI,eAAgBZ,EAAiBe,KAEnCC,IAAKC,IAAQ,CACbC,kBAAmBrB,EACnBsB,eAAgBrB,EAChBsB,kBAAmBrB,EACnBsB,iBAAkBtB,KACfkB,KAkBE,MAAMK,EAAqBZ,EAAiBM,IAAKC,GAAaA,EAAST,qCAfvE,SACLe,EAA2Cb,GAE3C,MAAMc,EAAW,IAAIC,IAErB,IAAK,MAAMR,KAAYM,EAAW,CAChCC,EAASE,IAAIT,EAAST,KAAMS,GAC5B,IAAK,MAAMU,KAASV,EAASN,SAAW,GACtCa,EAASE,IAAIC,EAAOV,EAExB,CAEA,OAAOO,CACT"}
|
|
1
|
+
{"version":3,"file":"languages.cjs","sources":["../src/languages.ts"],"sourcesContent":["import Go from 'tree-sitter-go';\nimport JavaScript from 'tree-sitter-javascript';\nimport Python from 'tree-sitter-python';\nimport TypeScript from 'tree-sitter-typescript';\nimport type { LanguageDefinition, LanguageName, ParserLanguage } from './types.js';\n\ntype GrammarModule = unknown;\n\nconst commonFunctionNodes = [\n 'function',\n 'function_declaration',\n 'function_definition',\n 'function_expression',\n 'function_item',\n 'function_declarator',\n 'method_declaration',\n 'method_definition',\n 'method_spec',\n 'arrow_function',\n 'generator_function',\n 'generator_function_declaration',\n 'lambda',\n 'lambda_expression',\n] as const;\n\nconst commonClassNodes = [\n 'class',\n 'class_declaration',\n 'class_definition',\n 'interface_declaration',\n 'trait_item',\n 'impl_item',\n 'struct_item',\n 'enum_item',\n] as const;\n\nconst commonDecisionNodes = [\n 'if_statement',\n 'elif_clause',\n 'else_if_clause',\n 'for_statement',\n 'for_in_statement',\n 'while_statement',\n 'do_statement',\n 'catch_clause',\n 'except_clause',\n 'case_clause',\n 'switch_case',\n 'match_arm',\n 'conditional_expression',\n 'ternary_expression',\n] as const;\n\nfunction normalizeGrammar(module: GrammarModule): ParserLanguage {\n if (isGrammarWrapper(module, 'default')) {\n return module.default;\n }\n\n return module;\n}\n\nfunction getTypeScriptGrammar(name: 'typescript' | 'tsx'): ParserLanguage {\n const grammars = TypeScript as unknown as Record<string, GrammarModule>;\n return grammars[name];\n}\n\nfunction isGrammarWrapper(value: GrammarModule, key: 'default'): value is Record<typeof key, ParserLanguage> {\n if (typeof value !== 'object' || value === null || !(key in value)) {\n return false;\n }\n\n return Boolean((value as Record<string, ParserLanguage>)[key]);\n}\n\nexport const defaultLanguages: readonly LanguageDefinition[] = [\n {\n name: 'javascript',\n aliases: ['js', 'mjs', 'cjs'],\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'jsx',\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'typescript',\n aliases: ['ts'],\n parserLanguage: getTypeScriptGrammar('typescript'),\n },\n {\n name: 'tsx',\n parserLanguage: getTypeScriptGrammar('tsx'),\n },\n {\n name: 'python',\n aliases: ['py'],\n parserLanguage: normalizeGrammar(Python as unknown as GrammarModule),\n },\n {\n name: 'go',\n parserLanguage: normalizeGrammar(Go as unknown as GrammarModule),\n },\n].map((language) => ({\n functionNodeTypes: commonFunctionNodes,\n classNodeTypes: commonClassNodes,\n decisionNodeTypes: commonDecisionNodes,\n nestingNodeTypes: commonDecisionNodes,\n ...language,\n}));\n\nexport function createLanguageRegistry(\n languages: readonly LanguageDefinition[] = defaultLanguages\n): Map<LanguageName, LanguageDefinition> {\n const registry = new Map<LanguageName, LanguageDefinition>();\n\n for (const language of languages) {\n registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n registry.set(alias, language);\n }\n }\n\n return registry;\n}\n\nexport const supportedLanguages = defaultLanguages.map((language) => language.name);\n"],"names":["commonFunctionNodes","commonClassNodes","commonDecisionNodes","normalizeGrammar","module","value","key","Boolean","isGrammarWrapper","default","getTypeScriptGrammar","name","TypeScript","defaultLanguages","aliases","parserLanguage","JavaScript","Python","Go","map","language","functionNodeTypes","classNodeTypes","decisionNodeTypes","nestingNodeTypes","supportedLanguages","languages","registry","Map","set","alias"],"mappings":"qJAQA,MAAMA,EAAsB,CAC1B,WACA,uBACA,sBACA,sBACA,gBACA,sBACA,qBACA,oBACA,cACA,iBACA,qBACA,iCACA,SACA,qBAGIC,EAAmB,CACvB,QACA,oBACA,mBACA,wBACA,aACA,YACA,cACA,aAGIC,EAAsB,CAC1B,eACA,cACA,iBACA,gBACA,mBACA,kBACA,eACA,eACA,gBACA,cACA,cACA,YACA,yBACA,sBAGF,SAASC,EAAiBC,GACxB,OAYF,SAA0BC,EAAsBC,GAC9C,GAAqB,iBAAVD,GAAgC,OAAVA,KAAoBC,KAAOD,GAC1D,OAAO,EAGT,OAAOE,QAASF,EAAyCC,GAC3D,CAlBME,CAAiBJ,EAAQ,WACpBA,EAAOK,QAGTL,CACT,CAEA,SAASM,EAAqBC,GAE5B,OADiBC,EACDD,EAClB,CAUO,MAAME,EAAkD,CAC7D,CACEF,KAAM,aACNG,QAAS,CAAC,KAAM,MAAO,OACvBC,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,MACNI,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,aACNG,QAAS,CAAC,MACVC,eAAgBL,EAAqB,eAEvC,CACEC,KAAM,MACNI,eAAgBL,EAAqB,QAEvC,CACEC,KAAM,SACNG,QAAS,CAAC,MACVC,eAAgBZ,EAAiBc,IAEnC,CACEN,KAAM,KACNI,eAAgBZ,EAAiBe,KAEnCC,IAAKC,IAAQ,CACbC,kBAAmBrB,EACnBsB,eAAgBrB,EAChBsB,kBAAmBrB,EACnBsB,iBAAkBtB,KACfkB,KAkBE,MAAMK,EAAqBZ,EAAiBM,IAAKC,GAAaA,EAAST,qCAfvE,SACLe,EAA2Cb,GAE3C,MAAMc,EAAW,IAAIC,IAErB,IAAK,MAAMR,KAAYM,EAAW,CAChCC,EAASE,IAAIT,EAAST,KAAMS,GAC5B,IAAK,MAAMU,KAASV,EAASN,SAAW,GACtCa,EAASE,IAAIC,EAAOV,EAExB,CAEA,OAAOO,CACT"}
|
package/dist/languages.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"tree-sitter-go";import t from"tree-sitter-javascript";import n from"tree-sitter-python";import a from"tree-sitter-typescript";const s=["function","function_declaration","function_definition","function_item","function_declarator","method_declaration","method_definition","method_spec","arrow_function","generator_function","generator_function_declaration","lambda","lambda_expression"],r=["class","class_declaration","class_definition","interface_declaration","trait_item","impl_item","struct_item","enum_item"],i=["if_statement","elif_clause","else_if_clause","for_statement","for_in_statement","while_statement","do_statement","catch_clause","except_clause","case_clause","switch_case","match_arm","conditional_expression","ternary_expression"];function o(e){return function(e,t){if("object"!=typeof e||null===e||!(t in e))return!1;return Boolean(e[t])}(e,"default")?e.default:e}function c(e){return a[e]}const m=[{name:"javascript",aliases:["js","mjs","cjs"],parserLanguage:o(t)},{name:"jsx",parserLanguage:o(t)},{name:"typescript",aliases:["ts"],parserLanguage:c("typescript")},{name:"tsx",parserLanguage:c("tsx")},{name:"python",aliases:["py"],parserLanguage:o(n)},{name:"go",parserLanguage:o(e)}].map(e=>({functionNodeTypes:s,classNodeTypes:r,decisionNodeTypes:i,nestingNodeTypes:i,...e}));function p(e=m){const t=new Map;for(const n of e){t.set(n.name,n);for(const e of n.aliases??[])t.set(e,n)}return t}const _=m.map(e=>e.name);export{p as createLanguageRegistry,m as defaultLanguages,_ as supportedLanguages};
|
|
1
|
+
import e from"tree-sitter-go";import t from"tree-sitter-javascript";import n from"tree-sitter-python";import a from"tree-sitter-typescript";const s=["function","function_declaration","function_definition","function_expression","function_item","function_declarator","method_declaration","method_definition","method_spec","arrow_function","generator_function","generator_function_declaration","lambda","lambda_expression"],r=["class","class_declaration","class_definition","interface_declaration","trait_item","impl_item","struct_item","enum_item"],i=["if_statement","elif_clause","else_if_clause","for_statement","for_in_statement","while_statement","do_statement","catch_clause","except_clause","case_clause","switch_case","match_arm","conditional_expression","ternary_expression"];function o(e){return function(e,t){if("object"!=typeof e||null===e||!(t in e))return!1;return Boolean(e[t])}(e,"default")?e.default:e}function c(e){return a[e]}const m=[{name:"javascript",aliases:["js","mjs","cjs"],parserLanguage:o(t)},{name:"jsx",parserLanguage:o(t)},{name:"typescript",aliases:["ts"],parserLanguage:c("typescript")},{name:"tsx",parserLanguage:c("tsx")},{name:"python",aliases:["py"],parserLanguage:o(n)},{name:"go",parserLanguage:o(e)}].map(e=>({functionNodeTypes:s,classNodeTypes:r,decisionNodeTypes:i,nestingNodeTypes:i,...e}));function p(e=m){const t=new Map;for(const n of e){t.set(n.name,n);for(const e of n.aliases??[])t.set(e,n)}return t}const _=m.map(e=>e.name);export{p as createLanguageRegistry,m as defaultLanguages,_ as supportedLanguages};
|
|
2
2
|
//# sourceMappingURL=languages.js.map
|
package/dist/languages.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"languages.js","sources":["../src/languages.ts"],"sourcesContent":["import Go from 'tree-sitter-go';\nimport JavaScript from 'tree-sitter-javascript';\nimport Python from 'tree-sitter-python';\nimport TypeScript from 'tree-sitter-typescript';\nimport type { LanguageDefinition, LanguageName, ParserLanguage } from './types.js';\n\ntype GrammarModule = unknown;\n\nconst commonFunctionNodes = [\n 'function',\n 'function_declaration',\n 'function_definition',\n 'function_item',\n 'function_declarator',\n 'method_declaration',\n 'method_definition',\n 'method_spec',\n 'arrow_function',\n 'generator_function',\n 'generator_function_declaration',\n 'lambda',\n 'lambda_expression',\n] as const;\n\nconst commonClassNodes = [\n 'class',\n 'class_declaration',\n 'class_definition',\n 'interface_declaration',\n 'trait_item',\n 'impl_item',\n 'struct_item',\n 'enum_item',\n] as const;\n\nconst commonDecisionNodes = [\n 'if_statement',\n 'elif_clause',\n 'else_if_clause',\n 'for_statement',\n 'for_in_statement',\n 'while_statement',\n 'do_statement',\n 'catch_clause',\n 'except_clause',\n 'case_clause',\n 'switch_case',\n 'match_arm',\n 'conditional_expression',\n 'ternary_expression',\n] as const;\n\nfunction normalizeGrammar(module: GrammarModule): ParserLanguage {\n if (isGrammarWrapper(module, 'default')) {\n return module.default;\n }\n\n return module;\n}\n\nfunction getTypeScriptGrammar(name: 'typescript' | 'tsx'): ParserLanguage {\n const grammars = TypeScript as unknown as Record<string, GrammarModule>;\n return grammars[name];\n}\n\nfunction isGrammarWrapper(value: GrammarModule, key: 'default'): value is Record<typeof key, ParserLanguage> {\n if (typeof value !== 'object' || value === null || !(key in value)) {\n return false;\n }\n\n return Boolean((value as Record<string, ParserLanguage>)[key]);\n}\n\nexport const defaultLanguages: readonly LanguageDefinition[] = [\n {\n name: 'javascript',\n aliases: ['js', 'mjs', 'cjs'],\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'jsx',\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'typescript',\n aliases: ['ts'],\n parserLanguage: getTypeScriptGrammar('typescript'),\n },\n {\n name: 'tsx',\n parserLanguage: getTypeScriptGrammar('tsx'),\n },\n {\n name: 'python',\n aliases: ['py'],\n parserLanguage: normalizeGrammar(Python as unknown as GrammarModule),\n },\n {\n name: 'go',\n parserLanguage: normalizeGrammar(Go as unknown as GrammarModule),\n },\n].map((language) => ({\n functionNodeTypes: commonFunctionNodes,\n classNodeTypes: commonClassNodes,\n decisionNodeTypes: commonDecisionNodes,\n nestingNodeTypes: commonDecisionNodes,\n ...language,\n}));\n\nexport function createLanguageRegistry(\n languages: readonly LanguageDefinition[] = defaultLanguages\n): Map<LanguageName, LanguageDefinition> {\n const registry = new Map<LanguageName, LanguageDefinition>();\n\n for (const language of languages) {\n registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n registry.set(alias, language);\n }\n }\n\n return registry;\n}\n\nexport const supportedLanguages = defaultLanguages.map((language) => language.name);\n"],"names":["commonFunctionNodes","commonClassNodes","commonDecisionNodes","normalizeGrammar","module","value","key","Boolean","isGrammarWrapper","default","getTypeScriptGrammar","name","TypeScript","defaultLanguages","aliases","parserLanguage","JavaScript","Python","Go","map","language","functionNodeTypes","classNodeTypes","decisionNodeTypes","nestingNodeTypes","createLanguageRegistry","languages","registry","Map","set","alias","supportedLanguages"],"mappings":"4IAQA,MAAMA,EAAsB,CAC1B,WACA,uBACA,sBACA,gBACA,sBACA,qBACA,oBACA,cACA,iBACA,qBACA,iCACA,SACA,qBAGIC,EAAmB,CACvB,QACA,oBACA,mBACA,wBACA,aACA,YACA,cACA,aAGIC,EAAsB,CAC1B,eACA,cACA,iBACA,gBACA,mBACA,kBACA,eACA,eACA,gBACA,cACA,cACA,YACA,yBACA,sBAGF,SAASC,EAAiBC,GACxB,OAYF,SAA0BC,EAAsBC,GAC9C,GAAqB,iBAAVD,GAAgC,OAAVA,KAAoBC,KAAOD,GAC1D,OAAO,EAGT,OAAOE,QAASF,EAAyCC,GAC3D,CAlBME,CAAiBJ,EAAQ,WACpBA,EAAOK,QAGTL,CACT,CAEA,SAASM,EAAqBC,GAE5B,OADiBC,EACDD,EAClB,CAUO,MAAME,EAAkD,CAC7D,CACEF,KAAM,aACNG,QAAS,CAAC,KAAM,MAAO,OACvBC,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,MACNI,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,aACNG,QAAS,CAAC,MACVC,eAAgBL,EAAqB,eAEvC,CACEC,KAAM,MACNI,eAAgBL,EAAqB,QAEvC,CACEC,KAAM,SACNG,QAAS,CAAC,MACVC,eAAgBZ,EAAiBc,IAEnC,CACEN,KAAM,KACNI,eAAgBZ,EAAiBe,KAEnCC,IAAKC,IAAQ,CACbC,kBAAmBrB,EACnBsB,eAAgBrB,EAChBsB,kBAAmBrB,EACnBsB,iBAAkBtB,KACfkB,KAGE,SAASK,EACdC,EAA2Cb,GAE3C,MAAMc,EAAW,IAAIC,IAErB,IAAK,MAAMR,KAAYM,EAAW,CAChCC,EAASE,IAAIT,EAAST,KAAMS,GAC5B,IAAK,MAAMU,KAASV,EAASN,SAAW,GACtCa,EAASE,IAAIC,EAAOV,EAExB,CAEA,OAAOO,CACT,CAEO,MAAMI,EAAqBlB,EAAiBM,IAAKC,GAAaA,EAAST"}
|
|
1
|
+
{"version":3,"file":"languages.js","sources":["../src/languages.ts"],"sourcesContent":["import Go from 'tree-sitter-go';\nimport JavaScript from 'tree-sitter-javascript';\nimport Python from 'tree-sitter-python';\nimport TypeScript from 'tree-sitter-typescript';\nimport type { LanguageDefinition, LanguageName, ParserLanguage } from './types.js';\n\ntype GrammarModule = unknown;\n\nconst commonFunctionNodes = [\n 'function',\n 'function_declaration',\n 'function_definition',\n 'function_expression',\n 'function_item',\n 'function_declarator',\n 'method_declaration',\n 'method_definition',\n 'method_spec',\n 'arrow_function',\n 'generator_function',\n 'generator_function_declaration',\n 'lambda',\n 'lambda_expression',\n] as const;\n\nconst commonClassNodes = [\n 'class',\n 'class_declaration',\n 'class_definition',\n 'interface_declaration',\n 'trait_item',\n 'impl_item',\n 'struct_item',\n 'enum_item',\n] as const;\n\nconst commonDecisionNodes = [\n 'if_statement',\n 'elif_clause',\n 'else_if_clause',\n 'for_statement',\n 'for_in_statement',\n 'while_statement',\n 'do_statement',\n 'catch_clause',\n 'except_clause',\n 'case_clause',\n 'switch_case',\n 'match_arm',\n 'conditional_expression',\n 'ternary_expression',\n] as const;\n\nfunction normalizeGrammar(module: GrammarModule): ParserLanguage {\n if (isGrammarWrapper(module, 'default')) {\n return module.default;\n }\n\n return module;\n}\n\nfunction getTypeScriptGrammar(name: 'typescript' | 'tsx'): ParserLanguage {\n const grammars = TypeScript as unknown as Record<string, GrammarModule>;\n return grammars[name];\n}\n\nfunction isGrammarWrapper(value: GrammarModule, key: 'default'): value is Record<typeof key, ParserLanguage> {\n if (typeof value !== 'object' || value === null || !(key in value)) {\n return false;\n }\n\n return Boolean((value as Record<string, ParserLanguage>)[key]);\n}\n\nexport const defaultLanguages: readonly LanguageDefinition[] = [\n {\n name: 'javascript',\n aliases: ['js', 'mjs', 'cjs'],\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'jsx',\n parserLanguage: normalizeGrammar(JavaScript as unknown as GrammarModule),\n },\n {\n name: 'typescript',\n aliases: ['ts'],\n parserLanguage: getTypeScriptGrammar('typescript'),\n },\n {\n name: 'tsx',\n parserLanguage: getTypeScriptGrammar('tsx'),\n },\n {\n name: 'python',\n aliases: ['py'],\n parserLanguage: normalizeGrammar(Python as unknown as GrammarModule),\n },\n {\n name: 'go',\n parserLanguage: normalizeGrammar(Go as unknown as GrammarModule),\n },\n].map((language) => ({\n functionNodeTypes: commonFunctionNodes,\n classNodeTypes: commonClassNodes,\n decisionNodeTypes: commonDecisionNodes,\n nestingNodeTypes: commonDecisionNodes,\n ...language,\n}));\n\nexport function createLanguageRegistry(\n languages: readonly LanguageDefinition[] = defaultLanguages\n): Map<LanguageName, LanguageDefinition> {\n const registry = new Map<LanguageName, LanguageDefinition>();\n\n for (const language of languages) {\n registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n registry.set(alias, language);\n }\n }\n\n return registry;\n}\n\nexport const supportedLanguages = defaultLanguages.map((language) => language.name);\n"],"names":["commonFunctionNodes","commonClassNodes","commonDecisionNodes","normalizeGrammar","module","value","key","Boolean","isGrammarWrapper","default","getTypeScriptGrammar","name","TypeScript","defaultLanguages","aliases","parserLanguage","JavaScript","Python","Go","map","language","functionNodeTypes","classNodeTypes","decisionNodeTypes","nestingNodeTypes","createLanguageRegistry","languages","registry","Map","set","alias","supportedLanguages"],"mappings":"4IAQA,MAAMA,EAAsB,CAC1B,WACA,uBACA,sBACA,sBACA,gBACA,sBACA,qBACA,oBACA,cACA,iBACA,qBACA,iCACA,SACA,qBAGIC,EAAmB,CACvB,QACA,oBACA,mBACA,wBACA,aACA,YACA,cACA,aAGIC,EAAsB,CAC1B,eACA,cACA,iBACA,gBACA,mBACA,kBACA,eACA,eACA,gBACA,cACA,cACA,YACA,yBACA,sBAGF,SAASC,EAAiBC,GACxB,OAYF,SAA0BC,EAAsBC,GAC9C,GAAqB,iBAAVD,GAAgC,OAAVA,KAAoBC,KAAOD,GAC1D,OAAO,EAGT,OAAOE,QAASF,EAAyCC,GAC3D,CAlBME,CAAiBJ,EAAQ,WACpBA,EAAOK,QAGTL,CACT,CAEA,SAASM,EAAqBC,GAE5B,OADiBC,EACDD,EAClB,CAUO,MAAME,EAAkD,CAC7D,CACEF,KAAM,aACNG,QAAS,CAAC,KAAM,MAAO,OACvBC,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,MACNI,eAAgBZ,EAAiBa,IAEnC,CACEL,KAAM,aACNG,QAAS,CAAC,MACVC,eAAgBL,EAAqB,eAEvC,CACEC,KAAM,MACNI,eAAgBL,EAAqB,QAEvC,CACEC,KAAM,SACNG,QAAS,CAAC,MACVC,eAAgBZ,EAAiBc,IAEnC,CACEN,KAAM,KACNI,eAAgBZ,EAAiBe,KAEnCC,IAAKC,IAAQ,CACbC,kBAAmBrB,EACnBsB,eAAgBrB,EAChBsB,kBAAmBrB,EACnBsB,iBAAkBtB,KACfkB,KAGE,SAASK,EACdC,EAA2Cb,GAE3C,MAAMc,EAAW,IAAIC,IAErB,IAAK,MAAMR,KAAYM,EAAW,CAChCC,EAASE,IAAIT,EAAST,KAAMS,GAC5B,IAAK,MAAMU,KAASV,EAASN,SAAW,GACtCa,EAASE,IAAIC,EAAOV,EAExB,CAEA,OAAOO,CACT,CAEO,MAAMI,EAAqBlB,EAAiBM,IAAKC,GAAaA,EAAST"}
|
package/dist/metrics.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("tree-sitter"),t=require("./languages.cjs");const n=new Set(["&&","||","and","or"]),o=new Set(["+","-","*","/","%","**","=","+=","-=","*=","/=","%=","==","!=","===","!==","<","<=",">",">=","!","~","&","|","^","<<",">>","=>","return","throw","yield","await","break","continue"]),i=new Set(["identifier","property_identifier","field_identifier","type_identifier","number","integer","float","string","string_literal","template_string","character_literal","true","false","null","undefined","nil"]);class r{registry=t.createLanguageRegistry();registerLanguage(e){this.registry.set(e.name,e);for(const t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(t,n){const r=this.registry.get(n.language);if(!r)throw new Error(`Unsupported language: ${n.language}`);const a=new e;a.setLanguage(r.parserLanguage);const C=a.parse(t,void 0,{bufferSize:t.length+1}).rootNode,x=function(e,t,n){const o=t.map(e=>function(e,t){const n=s(e,t,0),o=function(e){const t=new Set;let n=0;function o(e){if(function(e){return"call_expression"===e.type||"call"===e.type}(e)){n+=1;const o=function(e){const t=e.childForFieldName("function")??e.namedChild(0);if(!t)return;return y(t)}(e);o&&t.add(o)}for(const t of e.namedChildren)o(t)}return o(e),{callCount:n,callees:t}}(e);return{name:d(e),startLine:e.startPosition.row+1,endLine:e.endPosition.row+1,cyclomaticComplexity:n.cyclomaticComplexity,cognitiveComplexity:n.cognitiveComplexity,callCount:o.callCount,callees:o.callees,identifiers:c(e)}}(e,n)),i=function(e){const t=new Set(e.map(e=>e.name).filter(e=>void 0!==e)),n=new Map,o=new Map,i=new Map;let r=0,a=0;const s=new Set;for(const c of e){r+=c.callCount;for(const e of c.callees)s.add(e);if(!c.name)continue;const e=new Set([...c.callees].filter(e=>t.has(e)));i.set(c.name,e),o.set(c.name,e.size);for(const t of e)n.set(t,(n.get(t)??0)+1),a+=1}const c=function(e){const t=new Set;for(const n of e.keys())g(n,n,e,new Set)&&t.add(n);return t}(i);return{fanInByName:n,fanOutByName:o,recursiveNames:c,metrics:{callCount:r,uniqueCalleeCount:s.size,internalCallCount:a,internalEdgeCount:N([...i.values()].map(e=>e.size)),recursiveFunctionCount:c.size,maxFanIn:b(n),maxFanOut:b(o),maxCallDepth:h(i)}}}(o);return{functions:o.map(e=>({name:e.name,startLine:e.startLine,endLine:e.endLine,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:i.fanInByName.get(e.name??"")??0,fanOut:i.fanOutByName.get(e.name??"")??0,recursive:i.recursiveNames.has(e.name??"")})),callGraph:i.metrics,coupling:l(e),cohesion:f(o),typeComplexity:p(e)}}(C,u(C,new Set(r.functionNodeTypes)),r),_=x.functions,M=s(C,r,0),I=function(e,t){const n=0===e.length?[]:e.split(/\r\n|\n|\r/),o=function(e){const t=[];function n(e){if("comment"===e.type||"line_comment"===e.type||"block_comment"===e.type)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:Number.POSITIVE_INFINITY});for(const t of e.namedChildren)n(t)}return n(e),t}(t);let i=0,r=0;for(const[e,t]of n.entries())""!==t.trim()?m(t,e,o)&&(r+=1):i+=1;return{total:n.length,code:n.length-i-r,comment:r,blank:i}}(t,C),z=function(e,t){const n=new Map,r=new Map;function a(e){if("comment"!==e.type){if(0===e.childCount){const a=t.slice(e.startIndex,e.endIndex);return void(o.has(a)||o.has(e.type)?v(n,a||e.type):i.has(e.type)&&v(r,a))}o.has(e.type)&&v(n,e.type);for(const t of e.children)a(t)}}a(e);const s=n.size,c=r.size,u=N(n.values()),l=N(r.values()),f=s+c,p=u+l,m=0===f?0:p*Math.log2(f),d=0===c?0:s/2*(l/c),y=d*m;return{distinctOperators:s,distinctOperands:c,totalOperators:u,totalOperands:l,vocabulary:f,length:p,volume:m,difficulty:d,effort:y,time:y/18,bugs:m/3e3}}(C,t);return{language:r.name,bytes:Buffer.byteLength(t),lines:I,functions:_,classCount:u(C,new Set(r.classNodeTypes)).length,functionCount:_.length,cyclomaticComplexity:M.cyclomaticComplexity,maxCyclomaticComplexity:S(_,"cyclomaticComplexity"),cognitiveComplexity:M.cognitiveComplexity,maxCognitiveComplexity:S(_,"cognitiveComplexity"),nestingDepth:M.nestingDepth,callGraph:x.callGraph,coupling:x.coupling,cohesion:x.cohesion,typeComplexity:x.typeComplexity,halstead:z,maintainabilityIndex:w(z.volume,M.cyclomaticComplexity,I.code),syntaxTree:n.includeSyntaxTree?C.toString():void 0}}}const a=new r;function s(e,t,o){let i=1,r=0,a=o;const s=new Set(t.decisionNodeTypes),c=new Set(t.nestingNodeTypes);function u(e,t){const o=s.has(e.type),l=c.has(e.type);o&&(i+=1,r+=1+t),function(e){if(e.isNamed)return!1;return n.has(e.text)}(e)&&(i+=1,r+=1);const f=l?t+1:t;a=Math.max(a,f);for(const t of e.children)u(t,f)}for(const t of e.children)u(t,o);return{cyclomaticComplexity:i,cognitiveComplexity:r,nestingDepth:a}}function c(e){const t=new Set;return function e(n){"identifier"!==n.type&&"property_identifier"!==n.type&&"field_identifier"!==n.type||t.add(n.text);for(const t of n.namedChildren)e(t)}(e),t}function u(e,t){const n=[];return function e(o){t.has(o.type)&&n.push(o);for(const t of o.namedChildren)e(t)}(e),n}function l(e){const t=new Set;let n=0,o=0,i=0;return function e(r){if(function(e){return"import_statement"===e.type||"import_declaration"===e.type||"import_from_statement"===e.type||"import_spec"===e.type||"import_spec_list"===e.type}(r)){n+=1;const e=function(e){const t=C(e);return t?(n=t.text,n.replaceAll(/^['"`]|['"`]$/gu,"")):void 0;var n}(r);e&&(t.add(e),(e.startsWith(".")||e.startsWith("/"))&&(i+=1))}(function(e){return e.type.startsWith("export")||"public_field_definition"===e.type})(r)&&(o+=1);for(const t of r.namedChildren)e(t)}(e),{importCount:n,importSourceCount:t.size,relativeImportCount:i,externalImportCount:t.size-i,exportCount:o}}function f(e){const t=new Set,n=new Set;let o=0,i=0;for(const n of e)for(const e of n.identifiers)t.add(e);for(let t=0;t<e.length;t+=1)for(let r=t+1;r<e.length;r+=1){const a=e[t],s=e[r];if(!a||!s)continue;const c=_(a.identifiers,s.identifiers),u=new Set([...a.identifiers,...s.identifiers]).size;for(const e of c)n.add(e);o+=0===u?0:c.size/u,i+=1}return{averageFunctionIdentifierOverlap:0===i?1:o/i,sharedIdentifierCount:n.size,uniqueIdentifierCount:t.size}}function p(e){const t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};return function e(n){switch(n.type){case"type_annotation":t.typeAnnotationCount+=1;break;case"type_alias_declaration":t.typeAliasCount+=1;break;case"interface_declaration":t.interfaceCount+=1;break;case"type_parameters":case"type_parameter":t.genericParameterCount+="type_parameter"===n.type?1:0;break;case"union_type":t.unionTypeCount+=1;break;case"intersection_type":t.intersectionTypeCount+=1;break;case"conditional_type":t.conditionalTypeCount+=1;break;case"as_expression":case"type_assertion":t.typeAssertionCount+=1;break;case"non_null_expression":t.nonNullAssertionCount+=1;break;case"satisfies_expression":t.satisfiesExpressionCount+=1}for(const t of n.namedChildren)e(t)}(e),t}function m(e,t,n){const o=n.filter(e=>e.line===t);if(0===o.length)return!1;const i=e.search(/\S/),r=e.trimEnd().length;return o.some(e=>e.startColumn<=i&&e.endColumn>=r)}function d(e){const t=e.childForFieldName("name");if(t)return t.text;const n=e.parent;if(!n)return;const o=n.childForFieldName("name");return o?.text}function y(e){if("identifier"===e.type||"property_identifier"===e.type||"field_identifier"===e.type||"attribute"===e.type)return e.text;for(let t=e.namedChildCount-1;t>=0;t-=1){const n=e.namedChild(t);if(!n)continue;const o=y(n);if(o)return o}}function C(e){if("string"===e.type||"string_literal"===e.type||"interpreted_string_literal"===e.type)return e;for(const t of e.namedChildren){const e=C(t);if(e)return e}}function g(e,t,n,o){const i=n.get(e);if(!i)return!1;for(const e of i){if(e===t)return!0;if(!o.has(e)&&(o.add(e),g(e,t,n,o)))return!0}return!1}function h(e){let t=0;for(const n of e.keys())t=Math.max(t,x(n,e,new Set));return t}function x(e,t,n){const o=t.get(e);if(!o||0===o.size||n.has(e))return 0;n.add(e);let i=0;for(const e of o)i=Math.max(i,1+x(e,t,new Set(n)));return i}function _(e,t){const n=new Set;for(const o of e)t.has(o)&&n.add(o);return n}function w(e,t,n){if(0===n)return 100;const o=171-5.2*Math.log(Math.max(e,1))-.23*t-16.2*Math.log(n);return Math.max(0,Math.min(100,100*o/171))}function v(e,t){e.set(t,(e.get(t)??0)+1)}function S(e,t){return 0===e.length?0:Math.max(...e.map(e=>e[t]))}function b(e){let t=0;for(const n of e.values())t=Math.max(t,n);return t}function N(e){let t=0;for(const n of e)t+=n;return t}exports.TreeMeasurer=r,exports.defaultMeasurer=a,exports.measureCode=function(e,t){return a.measure(e,t)};
|
|
1
|
+
"use strict";var e=require("tree-sitter"),t=require("./languages.cjs");const n=new Set(["&&","||","and","or"]),o=new Set(["+","-","*","/","%","**","=","+=","-=","*=","/=","%=","==","!=","===","!==","<","<=",">",">=","!","~","&","|","^","<<",">>","=>","return","throw","yield","await","break","continue"]),r=new Set(["identifier","property_identifier","field_identifier","type_identifier","number","integer","float","string","string_literal","template_string","character_literal","true","false","null","undefined","nil"]);class i{registry=t.createLanguageRegistry();registerLanguage(e){this.registry.set(e.name,e);for(const t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(t,n){const i=this.registry.get(n.language);if(!i)throw new Error(`Unsupported language: ${n.language}`);const s=new e;s.setLanguage(i.parserLanguage);const f=s.parse(t,void 0,{bufferSize:t.length+1}).rootNode,d=function(e,t,n){const o=t.map((e,t)=>function(e,t,n){const o=a(e,t,0,!0),r=function(e,t){const n=new Set,o=new Set(t.functionNodeTypes);let r=0;function i(e,t){if(t||!o.has(e.type)){if(v(e)){r+=1;const t=function(e){const t=e.childForFieldName("function")??e.namedChild(0);if(!t)return;return S(t)}(e);t&&n.add(t)}for(const t of e.namedChildren)i(t,!1)}}return i(e,!0),{callCount:r,callees:n}}(e,t);return{index:n,name:_(e),startLine:e.startPosition.row+1,endLine:e.endPosition.row+1,returnsJsx:l(e,t),cyclomaticComplexity:o.cyclomaticComplexity,cognitiveComplexity:o.cognitiveComplexity,callCount:r.callCount,callees:r.callees,identifiers:c(e)}}(e,n,t)),r=function(e){const t=function(e){const t=new Map;for(const n of e)n.name&&t.set(n.name,t.has(n.name)?void 0:n.index);return new Map([...t.entries()].filter(e=>void 0!==e[1]))}(e),n=new Set(t.keys()),o=new Map,r=new Map,i=new Map;let s=0,a=0;const c=new Set;for(const u of e){s+=u.callCount;for(const e of u.callees)c.add(e);const e=new Set([...u.callees].filter(e=>n.has(e))),l=new Set;for(const n of e){const e=t.get(n);void 0!==e&&l.add(e)}i.set(u.index,l),r.set(u.index,e.size),a+=e.size;for(const e of l)o.set(e,(o.get(e)??0)+1)}const u=function(e){const t=new Set;for(const n of e.keys())k(n,n,e,new Set)&&t.add(n);return t}(i);return{fanInByIndex:o,fanOutByIndex:r,recursiveIndexes:u,metrics:{callCount:s,uniqueCalleeCount:c.size,internalCallCount:a,internalEdgeCount:q([...i.values()].map(e=>e.size)),recursiveFunctionCount:u.size,maxFanIn:R(o),maxFanOut:R(r),maxCallDepth:z(i)}}}(o);return{functions:o.map(e=>({name:e.name,startLine:e.startLine,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:r.fanInByIndex.get(e.index)??0,fanOut:r.fanOutByIndex.get(e.index)??0,recursive:r.recursiveIndexes.has(e.index)})),callGraph:r.metrics,coupling:h(e,n),cohesion:C(o),typeComplexity:g(e)}}(f,u(f,new Set(i.functionNodeTypes)),i),p=d.functions,m=a(f,i,0,!1),y=function(e,t){const n=0===e.length?[]:e.split(/\r\n|\n|\r/),o=function(e){const t=[];function n(e){if("comment"===e.type||"line_comment"===e.type||"block_comment"===e.type)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:Number.POSITIVE_INFINITY});for(const t of e.namedChildren)n(t)}return n(e),t}(t);let r=0,i=0;for(const[e,t]of n.entries())""!==t.trim()?x(t,e,o)&&(i+=1):r+=1;return{total:n.length,code:n.length-r-i,comment:i,blank:r}}(t,f),w=function(e,t){const n=new Map,i=new Map;function s(e){if("comment"!==e.type){if(0===e.childCount){const s=t.slice(e.startIndex,e.endIndex);return void(o.has(s)||o.has(e.type)?O(n,s||e.type):r.has(e.type)&&O(i,s))}o.has(e.type)&&O(n,e.type);for(const t of e.children)s(t)}}s(e);const a=n.size,c=i.size,u=q(n.values()),l=q(i.values()),f=a+c,d=u+l,p=0===f?0:d*Math.log2(f),m=0===c?0:a/2*(l/c),y=m*p;return{distinctOperators:a,distinctOperands:c,totalOperators:u,totalOperands:l,vocabulary:f,length:d,volume:p,difficulty:m,effort:y,time:y/18,bugs:p/3e3}}(f,t);return{language:i.name,bytes:Buffer.byteLength(t),lines:y,functions:p,classCount:u(f,new Set(i.classNodeTypes)).length,functionCount:p.length,cyclomaticComplexity:m.cyclomaticComplexity,maxCyclomaticComplexity:E(p,"cyclomaticComplexity"),cognitiveComplexity:m.cognitiveComplexity,maxCognitiveComplexity:E(p,"cognitiveComplexity"),nestingDepth:m.nestingDepth,callGraph:d.callGraph,coupling:d.coupling,cohesion:d.cohesion,typeComplexity:d.typeComplexity,halstead:w,maintainabilityIndex:A(w.volume,m.cyclomaticComplexity,y.code),syntaxTree:n.includeSyntaxTree?f.toString():void 0}}}const s=new i;function a(e,t,o,r){let i=1,s=0,a=o;const c=new Set(t.functionNodeTypes),u=new Set(t.decisionNodeTypes),l=new Set(t.nestingNodeTypes);function f(e,t,o){if(r&&c.has(e.type))return;const d=u.has(e.type),p=l.has(e.type);d&&(i+=1,s+=1+t),function(e){if(e.isNamed)return!1;return n.has(e.text)}(e)&&(i+=1,s+=1);const m=p?t+1:t;a=Math.max(a,m);for(const t of e.children)f(t,m)}for(const t of e.children)f(t,o);return{cyclomaticComplexity:i,cognitiveComplexity:s,nestingDepth:a}}function c(e){const t=new Set;return function e(n){"identifier"!==n.type&&"property_identifier"!==n.type&&"field_identifier"!==n.type||t.add(n.text);for(const t of n.namedChildren)e(t)}(e),t}function u(e,t){const n=[];return function e(o){t.has(o.type)&&n.push(o);for(const t of o.namedChildren)e(t)}(e),n}function l(e,t){const n=new Set(t.functionNodeTypes);return function t(o,r){if(!r&&n.has(o.type))return!1;if("return_statement"===o.type)return d(o,n)||p(o,n);if("arrow_function"===e.type&&o===f(e)&&"statement_block"!==o.type&&!n.has(o.type))return d(o,n)||p(o,n);for(const e of o.namedChildren)if(t(e,!1))return!0;return!1}(e,!0)}function f(e){return e.childForFieldName("body")??e.namedChild(e.namedChildCount-1)??void 0}function d(e,t){return m(e,t,e=>e.type.startsWith("jsx_")||function(e,t){if(!v(e)||!function(e){if(!e)return!1;const t=S(e);return"map"===t||"flatMap"===t}(e.childForFieldName("function")??e.namedChild(0)))return!1;return e.namedChildren.some(e=>y(e,t))}(e,t))}function p(e,t){return m(e,t,F)}function m(e,t,n){return function e(o,r){if(!r&&t.has(o.type))return!1;if(n(o))return!0;for(const t of o.namedChildren)if(e(t,!1))return!0;return!1}(e,!0)}function y(e,t){return t.has(e.type)?function(e,t){const n="arrow_function"===e.type?f(e):void 0;if(n&&"statement_block"!==n.type&&!t.has(n.type))return d(n,t)||p(n,t);return function(e,t,n){function o(e,r){if(!r&&t.has(e.type))return!1;if("return_statement"===e.type&&n(e))return!0;for(const t of e.namedChildren)if(o(t,!1))return!0;return!1}return o(e,!0)}(e,t,e=>d(e,t)||p(e,t))}(e,t):e.namedChildren.some(e=>y(e,t))}function h(e,t){const n=new Set;let o=0,r=0;!function e(i){if(function(e){return"import_statement"===e.type||"import_declaration"===e.type||"import_from_statement"===e.type||"import_spec"===e.type||"import_spec_list"===e.type}(i)){o+=1;for(const e of function(e,t){if("python"===t.name){const t=function(e){if("import_from_statement"===e.type){const t=e.childForFieldName("module_name");return t?[I(t.text)]:[]}if("import_statement"!==e.type)return[];return e.namedChildren.map(e=>N(e)).filter(e=>void 0!==e)}(e);if(t.length>0)return t}const n=e.childForFieldName("source")??M(e);return n?[T(n.text)]:[]}(i,t))n.add(e)}(function(e){return e.type.startsWith("export")||"public_field_definition"===e.type})(i)&&(r+=1);for(const t of i.namedChildren)e(t)}(e);const i=[...n].filter(b).length;return{importCount:o,importSourceCount:n.size,relativeImportCount:i,externalImportCount:n.size-i,exportCount:r}}function C(e){const t=new Set,n=new Set;let o=0,r=0;for(const n of e)for(const e of n.identifiers)t.add(e);for(let t=0;t<e.length;t+=1)for(let i=t+1;i<e.length;i+=1){const s=e[t],a=e[i];if(!s||!a)continue;const c=P(s.identifiers,a.identifiers),u=new Set([...s.identifiers,...a.identifiers]).size;for(const e of c)n.add(e);o+=0===u?0:c.size/u,r+=1}return{averageFunctionIdentifierOverlap:0===r?1:o/r,sharedIdentifierCount:n.size,uniqueIdentifierCount:t.size}}function g(e){const t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};return function e(n){switch(n.type){case"type_annotation":t.typeAnnotationCount+=1;break;case"type_alias_declaration":t.typeAliasCount+=1;break;case"interface_declaration":t.interfaceCount+=1;break;case"type_parameters":case"type_parameter":t.genericParameterCount+="type_parameter"===n.type?1:0;break;case"union_type":t.unionTypeCount+=1;break;case"intersection_type":t.intersectionTypeCount+=1;break;case"conditional_type":t.conditionalTypeCount+=1;break;case"as_expression":case"type_assertion":t.typeAssertionCount+=1;break;case"non_null_expression":t.nonNullAssertionCount+=1;break;case"satisfies_expression":t.satisfiesExpressionCount+=1}for(const t of n.namedChildren)e(t)}(e),t}function x(e,t,n){const o=n.filter(e=>e.line===t);if(0===o.length)return!1;const r=e.search(/\S/),i=e.trimEnd().length;return o.some(e=>e.startColumn<=r&&e.endColumn>=i)}function _(e){const t=function(e){let t=e;for(;t;){const e=t.parent,n=e?.parent;if("arguments"!==e?.type||"call_expression"!==n?.type)return;if(!w(n))return;const o=n.parent;if("variable_declarator"===o?.type)return o.childForFieldName("name")?.text;t=n}return}(e);if(t)return t;const n=e.childForFieldName("name");if(n)return n.text;const o=e.parent;if(!o)return;const r=o.childForFieldName("name");return r?.text}function w(e){const t=e.childForFieldName("function")??e.namedChild(0);return"memo"===t?.text||"React.memo"===t?.text||"forwardRef"===t?.text||"React.forwardRef"===t?.text}function v(e){return"call_expression"===e.type||"call"===e.type}function S(e){if("identifier"===e.type||"property_identifier"===e.type||"field_identifier"===e.type||"attribute"===e.type)return e.text;for(let t=e.namedChildCount-1;t>=0;t-=1){const n=e.namedChild(t);if(!n)continue;const o=S(n);if(o)return o}}function F(e){if(!v(e))return!1;const t=e.childForFieldName("function")??e.namedChild(0);return"React.createElement"===t?.text||"createElement"===t?.text}function b(e){return e.startsWith(".")||e.startsWith("/")}function N(e){if("dotted_name"===e.type||"relative_import"===e.type)return I(e.text);const t=e.childForFieldName("name");if(t)return I(t.text);for(const t of e.namedChildren){const e=N(t);if(e)return e}}function I(e){return e.replaceAll(/\s+/gu,"")}function M(e){if("string"===e.type||"string_literal"===e.type||"interpreted_string_literal"===e.type)return e;for(const t of e.namedChildren){const e=M(t);if(e)return e}}function T(e){return e.replaceAll(/^['"`]|['"`]$/gu,"")}function k(e,t,n,o){const r=n.get(e);if(!r)return!1;for(const e of r){if(e===t)return!0;if(!o.has(e)&&(o.add(e),k(e,t,n,o)))return!0}return!1}function z(e){let t=0;for(const n of e.keys())t=Math.max(t,L(n,e,new Set));return t}function L(e,t,n){const o=t.get(e);if(!o||0===o.size||n.has(e))return 0;n.add(e);let r=0;for(const e of o)r=Math.max(r,1+L(e,t,new Set(n)));return r}function P(e,t){const n=new Set;for(const o of e)t.has(o)&&n.add(o);return n}function A(e,t,n){if(0===n)return 100;const o=171-5.2*Math.log(Math.max(e,1))-.23*t-16.2*Math.log(n);return Math.max(0,Math.min(100,100*o/171))}function O(e,t){e.set(t,(e.get(t)??0)+1)}function E(e,t){return 0===e.length?0:Math.max(...e.map(e=>e[t]))}function R(e){let t=0;for(const n of e.values())t=Math.max(t,n);return t}function q(e){let t=0;for(const n of e)t+=n;return t}exports.TreeMeasurer=i,exports.defaultMeasurer=s,exports.measureCode=function(e,t){return s.measure(e,t)};
|
|
2
2
|
//# sourceMappingURL=metrics.cjs.map
|
package/dist/metrics.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.cjs","sources":["../src/metrics.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport { createLanguageRegistry } from './languages.js';\nimport type {\n CallGraphMetrics,\n CodeMetrics,\n CohesionMetrics,\n CouplingMetrics,\n FunctionMetrics,\n HalsteadMetrics,\n LanguageDefinition,\n LanguageName,\n MeasureOptions,\n TypeComplexityMetrics,\n} from './types.js';\n\nconst booleanOperators = new Set(['&&', '||', 'and', 'or']);\nconst operatorTexts = new Set([\n '+',\n '-',\n '*',\n '/',\n '%',\n '**',\n '=',\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '==',\n '!=',\n '===',\n '!==',\n '<',\n '<=',\n '>',\n '>=',\n '!',\n '~',\n '&',\n '|',\n '^',\n '<<',\n '>>',\n '=>',\n 'return',\n 'throw',\n 'yield',\n 'await',\n 'break',\n 'continue',\n]);\n\nconst operandNodeTypes = new Set([\n 'identifier',\n 'property_identifier',\n 'field_identifier',\n 'type_identifier',\n 'number',\n 'integer',\n 'float',\n 'string',\n 'string_literal',\n 'template_string',\n 'character_literal',\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'nil',\n]);\n\ninterface ComplexityResult {\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n nestingDepth: number;\n}\n\ninterface CommentSpan {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\ninterface FunctionAnalysis {\n name?: string;\n startLine: number;\n endLine: number;\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n callCount: number;\n callees: Set<string>;\n identifiers: Set<string>;\n}\n\ninterface StructuralMetrics {\n callGraph: CallGraphMetrics;\n cohesion: CohesionMetrics;\n coupling: CouplingMetrics;\n functions: FunctionMetrics[];\n typeComplexity: TypeComplexityMetrics;\n}\n\nexport class TreeMeasurer {\n private readonly registry = createLanguageRegistry();\n\n registerLanguage(language: LanguageDefinition): void {\n this.registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n this.registry.set(alias, language);\n }\n }\n\n getSupportedLanguages(): LanguageName[] {\n return [...new Set([...this.registry.values()].map((language) => language.name))];\n }\n\n measure(code: string, options: MeasureOptions): CodeMetrics {\n const language = this.registry.get(options.language);\n if (!language) {\n throw new Error(`Unsupported language: ${options.language}`);\n }\n\n const parser = new Parser();\n parser.setLanguage(language.parserLanguage);\n const tree = parser.parse(code, undefined, {\n bufferSize: code.length + 1,\n });\n const root = tree.rootNode;\n const functions = collectNodes(root, new Set(language.functionNodeTypes));\n const structuralMetrics = measureStructuralMetrics(root, functions, language);\n const functionMetrics = structuralMetrics.functions;\n const globalComplexity = measureComplexity(root, language, 0);\n const lines = measureLines(code, root);\n const halstead = measureHalstead(root, code);\n\n return {\n language: language.name,\n bytes: Buffer.byteLength(code),\n lines,\n functions: functionMetrics,\n classCount: collectNodes(root, new Set(language.classNodeTypes)).length,\n functionCount: functionMetrics.length,\n cyclomaticComplexity: globalComplexity.cyclomaticComplexity,\n maxCyclomaticComplexity: maxMetric(functionMetrics, 'cyclomaticComplexity'),\n cognitiveComplexity: globalComplexity.cognitiveComplexity,\n maxCognitiveComplexity: maxMetric(functionMetrics, 'cognitiveComplexity'),\n nestingDepth: globalComplexity.nestingDepth,\n callGraph: structuralMetrics.callGraph,\n coupling: structuralMetrics.coupling,\n cohesion: structuralMetrics.cohesion,\n typeComplexity: structuralMetrics.typeComplexity,\n halstead,\n maintainabilityIndex: calculateMaintainabilityIndex(\n halstead.volume,\n globalComplexity.cyclomaticComplexity,\n lines.code\n ),\n syntaxTree: options.includeSyntaxTree ? root.toString() : undefined,\n };\n }\n}\n\nexport const defaultMeasurer = new TreeMeasurer();\n\nexport function measureCode(code: string, options: MeasureOptions): CodeMetrics {\n return defaultMeasurer.measure(code, options);\n}\n\nfunction measureStructuralMetrics(\n root: Parser.SyntaxNode,\n functions: Parser.SyntaxNode[],\n language: LanguageDefinition\n): StructuralMetrics {\n const analyses = functions.map((node) => analyzeFunction(node, language));\n const callGraph = measureCallGraph(analyses);\n const functionsWithGraph = analyses.map((analysis) => ({\n name: analysis.name,\n startLine: analysis.startLine,\n endLine: analysis.endLine,\n cyclomaticComplexity: analysis.cyclomaticComplexity,\n cognitiveComplexity: analysis.cognitiveComplexity,\n callCount: analysis.callCount,\n uniqueCalleeCount: analysis.callees.size,\n fanIn: callGraph.fanInByName.get(analysis.name ?? '') ?? 0,\n fanOut: callGraph.fanOutByName.get(analysis.name ?? '') ?? 0,\n recursive: callGraph.recursiveNames.has(analysis.name ?? ''),\n }));\n\n return {\n functions: functionsWithGraph,\n callGraph: callGraph.metrics,\n coupling: measureCoupling(root),\n cohesion: measureCohesion(analyses),\n typeComplexity: measureTypeComplexity(root),\n };\n}\n\nfunction analyzeFunction(node: Parser.SyntaxNode, language: LanguageDefinition): FunctionAnalysis {\n const complexity = measureComplexity(node, language, 0);\n const calls = collectCalls(node);\n return {\n name: findFunctionName(node),\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n cyclomaticComplexity: complexity.cyclomaticComplexity,\n cognitiveComplexity: complexity.cognitiveComplexity,\n callCount: calls.callCount,\n callees: calls.callees,\n identifiers: collectIdentifiers(node),\n };\n}\n\nfunction measureCallGraph(analyses: FunctionAnalysis[]): {\n fanInByName: Map<string, number>;\n fanOutByName: Map<string, number>;\n metrics: CallGraphMetrics;\n recursiveNames: Set<string>;\n} {\n const functionNames = new Set(analyses.map((analysis) => analysis.name).filter((name) => name !== undefined));\n const fanInByName = new Map<string, number>();\n const fanOutByName = new Map<string, number>();\n const graph = new Map<string, Set<string>>();\n let callCount = 0;\n let internalCallCount = 0;\n const allCallees = new Set<string>();\n\n for (const analysis of analyses) {\n callCount += analysis.callCount;\n for (const callee of analysis.callees) {\n allCallees.add(callee);\n }\n\n if (!analysis.name) {\n continue;\n }\n\n const internalCallees = new Set([...analysis.callees].filter((callee) => functionNames.has(callee)));\n graph.set(analysis.name, internalCallees);\n fanOutByName.set(analysis.name, internalCallees.size);\n for (const callee of internalCallees) {\n fanInByName.set(callee, (fanInByName.get(callee) ?? 0) + 1);\n internalCallCount += 1;\n }\n }\n\n const recursiveNames = findRecursiveNames(graph);\n\n return {\n fanInByName,\n fanOutByName,\n recursiveNames,\n metrics: {\n callCount,\n uniqueCalleeCount: allCallees.size,\n internalCallCount,\n internalEdgeCount: sum([...graph.values()].map((callees) => callees.size)),\n recursiveFunctionCount: recursiveNames.size,\n maxFanIn: maxMapValue(fanInByName),\n maxFanOut: maxMapValue(fanOutByName),\n maxCallDepth: measureMaxCallDepth(graph),\n },\n };\n}\n\nfunction measureComplexity(node: Parser.SyntaxNode, language: LanguageDefinition, nesting: number): ComplexityResult {\n let cyclomaticComplexity = 1;\n let cognitiveComplexity = 0;\n let nestingDepth = nesting;\n const decisionNodes = new Set(language.decisionNodeTypes);\n const nestingNodes = new Set(language.nestingNodeTypes);\n\n function visit(current: Parser.SyntaxNode, currentNesting: number): void {\n const isDecision = decisionNodes.has(current.type);\n const isNesting = nestingNodes.has(current.type);\n\n if (isDecision) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1 + currentNesting;\n }\n\n if (isBooleanOperator(current)) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1;\n }\n\n const childNesting = isNesting ? currentNesting + 1 : currentNesting;\n nestingDepth = Math.max(nestingDepth, childNesting);\n\n for (const child of current.children) {\n visit(child, childNesting);\n }\n }\n\n for (const child of node.children) {\n visit(child, nesting);\n }\n\n return { cyclomaticComplexity, cognitiveComplexity, nestingDepth };\n}\n\nfunction isBooleanOperator(node: Parser.SyntaxNode): boolean {\n if (node.isNamed) {\n return false;\n }\n\n return booleanOperators.has(node.text);\n}\n\nfunction collectCalls(root: Parser.SyntaxNode): { callCount: number; callees: Set<string> } {\n const callees = new Set<string>();\n let callCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isCallNode(node)) {\n callCount += 1;\n const callee = findCalleeName(node);\n if (callee) {\n callees.add(callee);\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return { callCount, callees };\n}\n\nfunction collectIdentifiers(root: Parser.SyntaxNode): Set<string> {\n const identifiers = new Set<string>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'identifier' || node.type === 'property_identifier' || node.type === 'field_identifier') {\n identifiers.add(node.text);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return identifiers;\n}\n\nfunction collectNodes(root: Parser.SyntaxNode, nodeTypes: Set<string>): Parser.SyntaxNode[] {\n const nodes: Parser.SyntaxNode[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (nodeTypes.has(node.type)) {\n nodes.push(node);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return nodes;\n}\n\nfunction measureCoupling(root: Parser.SyntaxNode): CouplingMetrics {\n const importSources = new Set<string>();\n let importCount = 0;\n let exportCount = 0;\n let relativeImportCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isImportNode(node)) {\n importCount += 1;\n const source = findImportSource(node);\n if (source) {\n importSources.add(source);\n if (source.startsWith('.') || source.startsWith('/')) {\n relativeImportCount += 1;\n }\n }\n }\n\n if (isExportNode(node)) {\n exportCount += 1;\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n\n return {\n importCount,\n importSourceCount: importSources.size,\n relativeImportCount,\n externalImportCount: importSources.size - relativeImportCount,\n exportCount,\n };\n}\n\nfunction measureCohesion(analyses: FunctionAnalysis[]): CohesionMetrics {\n const allIdentifiers = new Set<string>();\n const sharedIdentifiers = new Set<string>();\n let overlapTotal = 0;\n let pairCount = 0;\n\n for (const analysis of analyses) {\n for (const identifier of analysis.identifiers) {\n allIdentifiers.add(identifier);\n }\n }\n\n for (let leftIndex = 0; leftIndex < analyses.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < analyses.length; rightIndex += 1) {\n const left = analyses[leftIndex];\n const right = analyses[rightIndex];\n if (!left || !right) {\n continue;\n }\n\n const intersection = intersectSets(left.identifiers, right.identifiers);\n const unionSize = new Set([...left.identifiers, ...right.identifiers]).size;\n for (const identifier of intersection) {\n sharedIdentifiers.add(identifier);\n }\n overlapTotal += unionSize === 0 ? 0 : intersection.size / unionSize;\n pairCount += 1;\n }\n }\n\n return {\n averageFunctionIdentifierOverlap: pairCount === 0 ? 1 : overlapTotal / pairCount,\n sharedIdentifierCount: sharedIdentifiers.size,\n uniqueIdentifierCount: allIdentifiers.size,\n };\n}\n\nfunction measureTypeComplexity(root: Parser.SyntaxNode): TypeComplexityMetrics {\n const metrics: TypeComplexityMetrics = {\n typeAnnotationCount: 0,\n typeAliasCount: 0,\n interfaceCount: 0,\n genericParameterCount: 0,\n unionTypeCount: 0,\n intersectionTypeCount: 0,\n conditionalTypeCount: 0,\n typeAssertionCount: 0,\n nonNullAssertionCount: 0,\n satisfiesExpressionCount: 0,\n };\n\n function visit(node: Parser.SyntaxNode): void {\n switch (node.type) {\n case 'type_annotation': {\n metrics.typeAnnotationCount += 1;\n break;\n }\n case 'type_alias_declaration': {\n metrics.typeAliasCount += 1;\n break;\n }\n case 'interface_declaration': {\n metrics.interfaceCount += 1;\n break;\n }\n case 'type_parameters':\n case 'type_parameter': {\n metrics.genericParameterCount += node.type === 'type_parameter' ? 1 : 0;\n break;\n }\n case 'union_type': {\n metrics.unionTypeCount += 1;\n break;\n }\n case 'intersection_type': {\n metrics.intersectionTypeCount += 1;\n break;\n }\n case 'conditional_type': {\n metrics.conditionalTypeCount += 1;\n break;\n }\n case 'as_expression':\n case 'type_assertion': {\n metrics.typeAssertionCount += 1;\n break;\n }\n case 'non_null_expression': {\n metrics.nonNullAssertionCount += 1;\n break;\n }\n case 'satisfies_expression': {\n metrics.satisfiesExpressionCount += 1;\n break;\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return metrics;\n}\n\nfunction measureLines(code: string, root: Parser.SyntaxNode): CodeMetrics['lines'] {\n const sourceLines = code.length === 0 ? [] : code.split(/\\r\\n|\\n|\\r/);\n const commentSpans = collectCommentSpans(root);\n let blank = 0;\n let comment = 0;\n\n for (const [index, line] of sourceLines.entries()) {\n if (line.trim() === '') {\n blank += 1;\n continue;\n }\n\n if (isCommentOnlyLine(line, index, commentSpans)) {\n comment += 1;\n }\n }\n\n return {\n total: sourceLines.length,\n code: sourceLines.length - blank - comment,\n comment,\n blank,\n };\n}\n\nfunction collectCommentSpans(root: Parser.SyntaxNode): CommentSpan[] {\n const spans: CommentSpan[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n for (let row = node.startPosition.row; row <= node.endPosition.row; row += 1) {\n spans.push({\n line: row,\n startColumn: row === node.startPosition.row ? node.startPosition.column : 0,\n endColumn: row === node.endPosition.row ? node.endPosition.column : Number.POSITIVE_INFINITY,\n });\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return spans;\n}\n\nfunction isCommentOnlyLine(line: string, lineIndex: number, spans: CommentSpan[]): boolean {\n const relevantSpans = spans.filter((span) => span.line === lineIndex);\n if (relevantSpans.length === 0) {\n return false;\n }\n\n const firstContentColumn = line.search(/\\S/);\n const lastContentColumn = line.trimEnd().length;\n\n return relevantSpans.some((span) => span.startColumn <= firstContentColumn && span.endColumn >= lastContentColumn);\n}\n\nfunction measureHalstead(root: Parser.SyntaxNode, code: string): HalsteadMetrics {\n const operators = new Map<string, number>();\n const operands = new Map<string, number>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment') {\n return;\n }\n\n if (node.childCount === 0) {\n const text = code.slice(node.startIndex, node.endIndex);\n if (operatorTexts.has(text) || operatorTexts.has(node.type)) {\n incrementCount(operators, text || node.type);\n } else if (operandNodeTypes.has(node.type)) {\n incrementCount(operands, text);\n }\n return;\n }\n\n if (operatorTexts.has(node.type)) {\n incrementCount(operators, node.type);\n }\n\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(root);\n\n const distinctOperators = operators.size;\n const distinctOperands = operands.size;\n const totalOperators = sum(operators.values());\n const totalOperands = sum(operands.values());\n const vocabulary = distinctOperators + distinctOperands;\n const length = totalOperators + totalOperands;\n const volume = vocabulary === 0 ? 0 : length * Math.log2(vocabulary);\n const difficulty = distinctOperands === 0 ? 0 : (distinctOperators / 2) * (totalOperands / distinctOperands);\n const effort = difficulty * volume;\n\n return {\n distinctOperators,\n distinctOperands,\n totalOperators,\n totalOperands,\n vocabulary,\n length,\n volume,\n difficulty,\n effort,\n time: effort / 18,\n bugs: volume / 3000,\n };\n}\n\nfunction findFunctionName(node: Parser.SyntaxNode): string | undefined {\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return nameNode.text;\n }\n\n const parent = node.parent;\n if (!parent) {\n return undefined;\n }\n\n const parentName = parent.childForFieldName('name');\n return parentName?.text;\n}\n\nfunction isCallNode(node: Parser.SyntaxNode): boolean {\n return node.type === 'call_expression' || node.type === 'call';\n}\n\nfunction findCalleeName(node: Parser.SyntaxNode): string | undefined {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n if (!calleeNode) {\n return undefined;\n }\n\n return findRightmostIdentifier(calleeNode);\n}\n\nfunction findRightmostIdentifier(node: Parser.SyntaxNode): string | undefined {\n if (\n node.type === 'identifier' ||\n node.type === 'property_identifier' ||\n node.type === 'field_identifier' ||\n node.type === 'attribute'\n ) {\n return node.text;\n }\n\n for (let index = node.namedChildCount - 1; index >= 0; index -= 1) {\n const child = node.namedChild(index);\n if (!child) {\n continue;\n }\n\n const identifier = findRightmostIdentifier(child);\n if (identifier) {\n return identifier;\n }\n }\n\n return undefined;\n}\n\nfunction isImportNode(node: Parser.SyntaxNode): boolean {\n return (\n node.type === 'import_statement' ||\n node.type === 'import_declaration' ||\n node.type === 'import_from_statement' ||\n node.type === 'import_spec' ||\n node.type === 'import_spec_list'\n );\n}\n\nfunction findImportSource(node: Parser.SyntaxNode): string | undefined {\n const sourceNode = findFirstStringNode(node);\n return sourceNode ? unquote(sourceNode.text) : undefined;\n}\n\nfunction findFirstStringNode(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n if (node.type === 'string' || node.type === 'string_literal' || node.type === 'interpreted_string_literal') {\n return node;\n }\n\n for (const child of node.namedChildren) {\n const stringNode = findFirstStringNode(child);\n if (stringNode) {\n return stringNode;\n }\n }\n\n return undefined;\n}\n\nfunction unquote(value: string): string {\n return value.replaceAll(/^['\"`]|['\"`]$/gu, '');\n}\n\nfunction isExportNode(node: Parser.SyntaxNode): boolean {\n return node.type.startsWith('export') || node.type === 'public_field_definition';\n}\n\nfunction findRecursiveNames(graph: Map<string, Set<string>>): Set<string> {\n const recursiveNames = new Set<string>();\n\n for (const name of graph.keys()) {\n if (canReach(name, name, graph, new Set())) {\n recursiveNames.add(name);\n }\n }\n\n return recursiveNames;\n}\n\nfunction canReach(start: string, target: string, graph: Map<string, Set<string>>, visited: Set<string>): boolean {\n const callees = graph.get(start);\n if (!callees) {\n return false;\n }\n\n for (const callee of callees) {\n if (callee === target) {\n return true;\n }\n\n if (!visited.has(callee)) {\n visited.add(callee);\n if (canReach(callee, target, graph, visited)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction measureMaxCallDepth(graph: Map<string, Set<string>>): number {\n let maxDepth = 0;\n for (const name of graph.keys()) {\n maxDepth = Math.max(maxDepth, measureCallDepth(name, graph, new Set()));\n }\n return maxDepth;\n}\n\nfunction measureCallDepth(name: string, graph: Map<string, Set<string>>, pathNames: Set<string>): number {\n const callees = graph.get(name);\n if (!callees || callees.size === 0 || pathNames.has(name)) {\n return 0;\n }\n\n pathNames.add(name);\n let maxDepth = 0;\n for (const callee of callees) {\n maxDepth = Math.max(maxDepth, 1 + measureCallDepth(callee, graph, new Set(pathNames)));\n }\n return maxDepth;\n}\n\nfunction intersectSets(left: Set<string>, right: Set<string>): Set<string> {\n const intersection = new Set<string>();\n for (const value of left) {\n if (right.has(value)) {\n intersection.add(value);\n }\n }\n return intersection;\n}\n\nfunction calculateMaintainabilityIndex(volume: number, complexity: number, loc: number): number {\n if (loc === 0) {\n return 100;\n }\n\n const raw = 171 - 5.2 * Math.log(Math.max(volume, 1)) - 0.23 * complexity - 16.2 * Math.log(loc);\n return Math.max(0, Math.min(100, (raw * 100) / 171));\n}\n\nfunction incrementCount(map: Map<string, number>, value: string): void {\n map.set(value, (map.get(value) ?? 0) + 1);\n}\n\nfunction maxMetric(functions: FunctionMetrics[], key: 'cyclomaticComplexity' | 'cognitiveComplexity'): number {\n return functions.length === 0 ? 0 : Math.max(...functions.map((fn) => fn[key]));\n}\n\nfunction maxMapValue(map: Map<string, number>): number {\n let maximum = 0;\n for (const value of map.values()) {\n maximum = Math.max(maximum, value);\n }\n return maximum;\n}\n\nfunction sum(values: Iterable<number>): number {\n let total = 0;\n for (const value of values) {\n total += value;\n }\n return total;\n}\n"],"names":["booleanOperators","Set","operatorTexts","operandNodeTypes","TreeMeasurer","registry","createLanguageRegistry","registerLanguage","language","this","set","name","alias","aliases","getSupportedLanguages","values","map","measure","code","options","get","Error","parser","Parser","setLanguage","parserLanguage","root","parse","undefined","bufferSize","length","rootNode","structuralMetrics","functions","analyses","node","complexity","measureComplexity","calls","callees","callCount","visit","type","isCallNode","callee","calleeNode","childForFieldName","namedChild","findRightmostIdentifier","findCalleeName","add","child","namedChildren","collectCalls","findFunctionName","startLine","startPosition","row","endLine","endPosition","cyclomaticComplexity","cognitiveComplexity","identifiers","collectIdentifiers","analyzeFunction","callGraph","functionNames","analysis","filter","fanInByName","Map","fanOutByName","graph","internalCallCount","allCallees","internalCallees","has","size","recursiveNames","keys","canReach","findRecursiveNames","metrics","uniqueCalleeCount","internalEdgeCount","sum","recursiveFunctionCount","maxFanIn","maxMapValue","maxFanOut","maxCallDepth","measureMaxCallDepth","measureCallGraph","fanIn","fanOut","recursive","coupling","measureCoupling","cohesion","measureCohesion","typeComplexity","measureTypeComplexity","measureStructuralMetrics","collectNodes","functionNodeTypes","functionMetrics","globalComplexity","lines","sourceLines","split","commentSpans","spans","push","line","startColumn","column","endColumn","Number","POSITIVE_INFINITY","collectCommentSpans","blank","comment","index","entries","trim","isCommentOnlyLine","total","measureLines","halstead","operators","operands","childCount","text","slice","startIndex","endIndex","incrementCount","children","distinctOperators","distinctOperands","totalOperators","totalOperands","vocabulary","volume","Math","log2","difficulty","effort","time","bugs","measureHalstead","bytes","Buffer","byteLength","classCount","classNodeTypes","functionCount","maxCyclomaticComplexity","maxMetric","maxCognitiveComplexity","nestingDepth","maintainabilityIndex","calculateMaintainabilityIndex","syntaxTree","includeSyntaxTree","toString","defaultMeasurer","nesting","decisionNodes","decisionNodeTypes","nestingNodes","nestingNodeTypes","current","currentNesting","isDecision","isNesting","isNamed","isBooleanOperator","childNesting","max","nodeTypes","nodes","importSources","importCount","exportCount","relativeImportCount","isImportNode","source","sourceNode","findFirstStringNode","value","replaceAll","findImportSource","startsWith","isExportNode","importSourceCount","externalImportCount","allIdentifiers","sharedIdentifiers","overlapTotal","pairCount","identifier","leftIndex","rightIndex","left","right","intersection","intersectSets","unionSize","averageFunctionIdentifierOverlap","sharedIdentifierCount","uniqueIdentifierCount","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","unionTypeCount","intersectionTypeCount","conditionalTypeCount","typeAssertionCount","nonNullAssertionCount","satisfiesExpressionCount","lineIndex","relevantSpans","span","firstContentColumn","search","lastContentColumn","trimEnd","some","nameNode","parent","parentName","namedChildCount","stringNode","start","target","visited","maxDepth","measureCallDepth","pathNames","loc","raw","log","min","key","fn","maximum"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"metrics.cjs","sources":["../src/metrics.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport { createLanguageRegistry } from './languages.js';\nimport type {\n CallGraphMetrics,\n CodeMetrics,\n CohesionMetrics,\n CouplingMetrics,\n FunctionMetrics,\n HalsteadMetrics,\n LanguageDefinition,\n LanguageName,\n MeasureOptions,\n TypeComplexityMetrics,\n} from './types.js';\n\nconst booleanOperators = new Set(['&&', '||', 'and', 'or']);\nconst operatorTexts = new Set([\n '+',\n '-',\n '*',\n '/',\n '%',\n '**',\n '=',\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '==',\n '!=',\n '===',\n '!==',\n '<',\n '<=',\n '>',\n '>=',\n '!',\n '~',\n '&',\n '|',\n '^',\n '<<',\n '>>',\n '=>',\n 'return',\n 'throw',\n 'yield',\n 'await',\n 'break',\n 'continue',\n]);\n\nconst operandNodeTypes = new Set([\n 'identifier',\n 'property_identifier',\n 'field_identifier',\n 'type_identifier',\n 'number',\n 'integer',\n 'float',\n 'string',\n 'string_literal',\n 'template_string',\n 'character_literal',\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'nil',\n]);\n\ninterface ComplexityResult {\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n nestingDepth: number;\n}\n\ninterface CommentSpan {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\ninterface FunctionAnalysis {\n index: number;\n name?: string;\n startLine: number;\n endLine: number;\n returnsJsx: boolean;\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n callCount: number;\n callees: Set<string>;\n identifiers: Set<string>;\n}\n\ninterface StructuralMetrics {\n callGraph: CallGraphMetrics;\n cohesion: CohesionMetrics;\n coupling: CouplingMetrics;\n functions: FunctionMetrics[];\n typeComplexity: TypeComplexityMetrics;\n}\n\nexport class TreeMeasurer {\n private readonly registry = createLanguageRegistry();\n\n registerLanguage(language: LanguageDefinition): void {\n this.registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n this.registry.set(alias, language);\n }\n }\n\n getSupportedLanguages(): LanguageName[] {\n return [...new Set([...this.registry.values()].map((language) => language.name))];\n }\n\n measure(code: string, options: MeasureOptions): CodeMetrics {\n const language = this.registry.get(options.language);\n if (!language) {\n throw new Error(`Unsupported language: ${options.language}`);\n }\n\n const parser = new Parser();\n parser.setLanguage(language.parserLanguage);\n const tree = parser.parse(code, undefined, {\n bufferSize: code.length + 1,\n });\n const root = tree.rootNode;\n const functions = collectNodes(root, new Set(language.functionNodeTypes));\n const structuralMetrics = measureStructuralMetrics(root, functions, language);\n const functionMetrics = structuralMetrics.functions;\n const globalComplexity = measureComplexity(root, language, 0, false);\n const lines = measureLines(code, root);\n const halstead = measureHalstead(root, code);\n\n return {\n language: language.name,\n bytes: Buffer.byteLength(code),\n lines,\n functions: functionMetrics,\n classCount: collectNodes(root, new Set(language.classNodeTypes)).length,\n functionCount: functionMetrics.length,\n cyclomaticComplexity: globalComplexity.cyclomaticComplexity,\n maxCyclomaticComplexity: maxMetric(functionMetrics, 'cyclomaticComplexity'),\n cognitiveComplexity: globalComplexity.cognitiveComplexity,\n maxCognitiveComplexity: maxMetric(functionMetrics, 'cognitiveComplexity'),\n nestingDepth: globalComplexity.nestingDepth,\n callGraph: structuralMetrics.callGraph,\n coupling: structuralMetrics.coupling,\n cohesion: structuralMetrics.cohesion,\n typeComplexity: structuralMetrics.typeComplexity,\n halstead,\n maintainabilityIndex: calculateMaintainabilityIndex(\n halstead.volume,\n globalComplexity.cyclomaticComplexity,\n lines.code\n ),\n syntaxTree: options.includeSyntaxTree ? root.toString() : undefined,\n };\n }\n}\n\nexport const defaultMeasurer = new TreeMeasurer();\n\nexport function measureCode(code: string, options: MeasureOptions): CodeMetrics {\n return defaultMeasurer.measure(code, options);\n}\n\nfunction measureStructuralMetrics(\n root: Parser.SyntaxNode,\n functions: Parser.SyntaxNode[],\n language: LanguageDefinition\n): StructuralMetrics {\n const analyses = functions.map((node, index) => analyzeFunction(node, language, index));\n const callGraph = measureCallGraph(analyses);\n const functionsWithGraph = analyses.map((analysis) => ({\n name: analysis.name,\n startLine: analysis.startLine,\n endLine: analysis.endLine,\n returnsJsx: analysis.returnsJsx,\n cyclomaticComplexity: analysis.cyclomaticComplexity,\n cognitiveComplexity: analysis.cognitiveComplexity,\n callCount: analysis.callCount,\n uniqueCalleeCount: analysis.callees.size,\n fanIn: callGraph.fanInByIndex.get(analysis.index) ?? 0,\n fanOut: callGraph.fanOutByIndex.get(analysis.index) ?? 0,\n recursive: callGraph.recursiveIndexes.has(analysis.index),\n }));\n\n return {\n functions: functionsWithGraph,\n callGraph: callGraph.metrics,\n coupling: measureCoupling(root, language),\n cohesion: measureCohesion(analyses),\n typeComplexity: measureTypeComplexity(root),\n };\n}\n\nfunction analyzeFunction(node: Parser.SyntaxNode, language: LanguageDefinition, index: number): FunctionAnalysis {\n const complexity = measureComplexity(node, language, 0, true);\n const calls = collectCalls(node, language);\n return {\n index,\n name: findFunctionName(node),\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n returnsJsx: returnsJsx(node, language),\n cyclomaticComplexity: complexity.cyclomaticComplexity,\n cognitiveComplexity: complexity.cognitiveComplexity,\n callCount: calls.callCount,\n callees: calls.callees,\n identifiers: collectIdentifiers(node),\n };\n}\n\nfunction measureCallGraph(analyses: FunctionAnalysis[]): {\n fanInByIndex: Map<number, number>;\n fanOutByIndex: Map<number, number>;\n metrics: CallGraphMetrics;\n recursiveIndexes: Set<number>;\n} {\n const indexesByName = mapUniqueFunctionIndexesByName(analyses);\n const functionNames = new Set(indexesByName.keys());\n const fanInByIndex = new Map<number, number>();\n const fanOutByIndex = new Map<number, number>();\n const graph = new Map<number, Set<number>>();\n let callCount = 0;\n let internalCallCount = 0;\n const allCallees = new Set<string>();\n\n for (const analysis of analyses) {\n callCount += analysis.callCount;\n for (const callee of analysis.callees) {\n allCallees.add(callee);\n }\n\n const internalCalleeNames = new Set([...analysis.callees].filter((callee) => functionNames.has(callee)));\n const internalCalleeIndexes = new Set<number>();\n for (const callee of internalCalleeNames) {\n const calleeIndex = indexesByName.get(callee);\n if (calleeIndex !== undefined) {\n internalCalleeIndexes.add(calleeIndex);\n }\n }\n\n graph.set(analysis.index, internalCalleeIndexes);\n fanOutByIndex.set(analysis.index, internalCalleeNames.size);\n internalCallCount += internalCalleeNames.size;\n for (const calleeIndex of internalCalleeIndexes) {\n fanInByIndex.set(calleeIndex, (fanInByIndex.get(calleeIndex) ?? 0) + 1);\n }\n }\n\n const recursiveIndexes = findRecursiveIndexes(graph);\n\n return {\n fanInByIndex,\n fanOutByIndex,\n recursiveIndexes,\n metrics: {\n callCount,\n uniqueCalleeCount: allCallees.size,\n internalCallCount,\n internalEdgeCount: sum([...graph.values()].map((callees) => callees.size)),\n recursiveFunctionCount: recursiveIndexes.size,\n maxFanIn: maxMapValue(fanInByIndex),\n maxFanOut: maxMapValue(fanOutByIndex),\n maxCallDepth: measureMaxCallDepth(graph),\n },\n };\n}\n\nfunction mapUniqueFunctionIndexesByName(analyses: FunctionAnalysis[]): Map<string, number> {\n const indexesByName = new Map<string, number | undefined>();\n for (const analysis of analyses) {\n if (!analysis.name) {\n continue;\n }\n\n indexesByName.set(analysis.name, indexesByName.has(analysis.name) ? undefined : analysis.index);\n }\n return new Map([...indexesByName.entries()].filter((entry): entry is [string, number] => entry[1] !== undefined));\n}\n\nfunction measureComplexity(\n node: Parser.SyntaxNode,\n language: LanguageDefinition,\n nesting: number,\n stopAtNestedFunctions: boolean\n): ComplexityResult {\n let cyclomaticComplexity = 1;\n let cognitiveComplexity = 0;\n let nestingDepth = nesting;\n const functionNodes = new Set(language.functionNodeTypes);\n const decisionNodes = new Set(language.decisionNodeTypes);\n const nestingNodes = new Set(language.nestingNodeTypes);\n\n function visit(current: Parser.SyntaxNode, currentNesting: number, insideRoot: boolean): void {\n if (stopAtNestedFunctions && !insideRoot && functionNodes.has(current.type)) {\n return;\n }\n\n const isDecision = decisionNodes.has(current.type);\n const isNesting = nestingNodes.has(current.type);\n\n if (isDecision) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1 + currentNesting;\n }\n\n if (isBooleanOperator(current)) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1;\n }\n\n const childNesting = isNesting ? currentNesting + 1 : currentNesting;\n nestingDepth = Math.max(nestingDepth, childNesting);\n\n for (const child of current.children) {\n visit(child, childNesting, false);\n }\n }\n\n for (const child of node.children) {\n visit(child, nesting, false);\n }\n\n return { cyclomaticComplexity, cognitiveComplexity, nestingDepth };\n}\n\nfunction isBooleanOperator(node: Parser.SyntaxNode): boolean {\n if (node.isNamed) {\n return false;\n }\n\n return booleanOperators.has(node.text);\n}\n\nfunction collectCalls(\n root: Parser.SyntaxNode,\n language: LanguageDefinition\n): { callCount: number; callees: Set<string> } {\n const callees = new Set<string>();\n const functionNodeTypes = new Set(language.functionNodeTypes);\n let callCount = 0;\n\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): void {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return;\n }\n\n if (isCallNode(node)) {\n callCount += 1;\n const callee = findCalleeName(node);\n if (callee) {\n callees.add(callee);\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child, false);\n }\n }\n\n visit(root, true);\n return { callCount, callees };\n}\n\nfunction collectIdentifiers(root: Parser.SyntaxNode): Set<string> {\n const identifiers = new Set<string>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'identifier' || node.type === 'property_identifier' || node.type === 'field_identifier') {\n identifiers.add(node.text);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return identifiers;\n}\n\nfunction collectNodes(root: Parser.SyntaxNode, nodeTypes: Set<string>): Parser.SyntaxNode[] {\n const nodes: Parser.SyntaxNode[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (nodeTypes.has(node.type)) {\n nodes.push(node);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return nodes;\n}\n\nfunction returnsJsx(root: Parser.SyntaxNode, language: LanguageDefinition): boolean {\n const functionNodeTypes = new Set(language.functionNodeTypes);\n\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (node.type === 'return_statement') {\n return containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes);\n }\n\n if (\n root.type === 'arrow_function' &&\n node === getArrowFunctionBody(root) &&\n node.type !== 'statement_block' &&\n !functionNodeTypes.has(node.type)\n ) {\n return containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes);\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction getArrowFunctionBody(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n return node.childForFieldName('body') ?? node.namedChild(node.namedChildCount - 1) ?? undefined;\n}\n\nfunction containsJsxExpression(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n return containsNode(\n root,\n functionNodeTypes,\n (node) => node.type.startsWith('jsx_') || isJsxMappingCall(node, functionNodeTypes)\n );\n}\n\nfunction containsReactCreateElementCall(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n return containsNode(root, functionNodeTypes, isReactCreateElementCall);\n}\n\nfunction containsNode(\n root: Parser.SyntaxNode,\n functionNodeTypes: Set<string>,\n predicate: (node: Parser.SyntaxNode) => boolean\n): boolean {\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (predicate(node)) {\n return true;\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction isJsxMappingCall(node: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n if (!isCallNode(node) || !isArrayMappingCallee(node.childForFieldName('function') ?? node.namedChild(0))) {\n return false;\n }\n\n return node.namedChildren.some((child) => containsReturnedJsxFunction(child, functionNodeTypes));\n}\n\nfunction isArrayMappingCallee(node: Parser.SyntaxNode | null): boolean {\n if (!node) {\n return false;\n }\n\n const calleeName = findRightmostIdentifier(node);\n return calleeName === 'map' || calleeName === 'flatMap';\n}\n\nfunction containsReturnedJsxFunction(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n if (functionNodeTypes.has(root.type)) {\n return returnsJsxFromFunctionNode(root, functionNodeTypes);\n }\n\n return root.namedChildren.some((child) => containsReturnedJsxFunction(child, functionNodeTypes));\n}\n\nfunction returnsJsxFromFunctionNode(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n const body = root.type === 'arrow_function' ? getArrowFunctionBody(root) : undefined;\n if (body && body.type !== 'statement_block' && !functionNodeTypes.has(body.type)) {\n return containsJsxExpression(body, functionNodeTypes) || containsReactCreateElementCall(body, functionNodeTypes);\n }\n\n return containsOwnReturnNode(\n root,\n functionNodeTypes,\n (node) => containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes)\n );\n}\n\nfunction containsOwnReturnNode(\n root: Parser.SyntaxNode,\n functionNodeTypes: Set<string>,\n predicate: (node: Parser.SyntaxNode) => boolean\n): boolean {\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (node.type === 'return_statement' && predicate(node)) {\n return true;\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction measureCoupling(root: Parser.SyntaxNode, language: LanguageDefinition): CouplingMetrics {\n const importSources = new Set<string>();\n let importCount = 0;\n let exportCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isImportNode(node)) {\n importCount += 1;\n for (const source of findImportSources(node, language)) {\n importSources.add(source);\n }\n }\n\n if (isExportNode(node)) {\n exportCount += 1;\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n\n const relativeImportCount = [...importSources].filter(isRelativeImportSource).length;\n\n return {\n importCount,\n importSourceCount: importSources.size,\n relativeImportCount,\n externalImportCount: importSources.size - relativeImportCount,\n exportCount,\n };\n}\n\nfunction measureCohesion(analyses: FunctionAnalysis[]): CohesionMetrics {\n const allIdentifiers = new Set<string>();\n const sharedIdentifiers = new Set<string>();\n let overlapTotal = 0;\n let pairCount = 0;\n\n for (const analysis of analyses) {\n for (const identifier of analysis.identifiers) {\n allIdentifiers.add(identifier);\n }\n }\n\n for (let leftIndex = 0; leftIndex < analyses.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < analyses.length; rightIndex += 1) {\n const left = analyses[leftIndex];\n const right = analyses[rightIndex];\n if (!left || !right) {\n continue;\n }\n\n const intersection = intersectSets(left.identifiers, right.identifiers);\n const unionSize = new Set([...left.identifiers, ...right.identifiers]).size;\n for (const identifier of intersection) {\n sharedIdentifiers.add(identifier);\n }\n overlapTotal += unionSize === 0 ? 0 : intersection.size / unionSize;\n pairCount += 1;\n }\n }\n\n return {\n averageFunctionIdentifierOverlap: pairCount === 0 ? 1 : overlapTotal / pairCount,\n sharedIdentifierCount: sharedIdentifiers.size,\n uniqueIdentifierCount: allIdentifiers.size,\n };\n}\n\nfunction measureTypeComplexity(root: Parser.SyntaxNode): TypeComplexityMetrics {\n const metrics: TypeComplexityMetrics = {\n typeAnnotationCount: 0,\n typeAliasCount: 0,\n interfaceCount: 0,\n genericParameterCount: 0,\n unionTypeCount: 0,\n intersectionTypeCount: 0,\n conditionalTypeCount: 0,\n typeAssertionCount: 0,\n nonNullAssertionCount: 0,\n satisfiesExpressionCount: 0,\n };\n\n function visit(node: Parser.SyntaxNode): void {\n switch (node.type) {\n case 'type_annotation': {\n metrics.typeAnnotationCount += 1;\n break;\n }\n case 'type_alias_declaration': {\n metrics.typeAliasCount += 1;\n break;\n }\n case 'interface_declaration': {\n metrics.interfaceCount += 1;\n break;\n }\n case 'type_parameters':\n case 'type_parameter': {\n metrics.genericParameterCount += node.type === 'type_parameter' ? 1 : 0;\n break;\n }\n case 'union_type': {\n metrics.unionTypeCount += 1;\n break;\n }\n case 'intersection_type': {\n metrics.intersectionTypeCount += 1;\n break;\n }\n case 'conditional_type': {\n metrics.conditionalTypeCount += 1;\n break;\n }\n case 'as_expression':\n case 'type_assertion': {\n metrics.typeAssertionCount += 1;\n break;\n }\n case 'non_null_expression': {\n metrics.nonNullAssertionCount += 1;\n break;\n }\n case 'satisfies_expression': {\n metrics.satisfiesExpressionCount += 1;\n break;\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return metrics;\n}\n\nfunction measureLines(code: string, root: Parser.SyntaxNode): CodeMetrics['lines'] {\n const sourceLines = code.length === 0 ? [] : code.split(/\\r\\n|\\n|\\r/);\n const commentSpans = collectCommentSpans(root);\n let blank = 0;\n let comment = 0;\n\n for (const [index, line] of sourceLines.entries()) {\n if (line.trim() === '') {\n blank += 1;\n continue;\n }\n\n if (isCommentOnlyLine(line, index, commentSpans)) {\n comment += 1;\n }\n }\n\n return {\n total: sourceLines.length,\n code: sourceLines.length - blank - comment,\n comment,\n blank,\n };\n}\n\nfunction collectCommentSpans(root: Parser.SyntaxNode): CommentSpan[] {\n const spans: CommentSpan[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n for (let row = node.startPosition.row; row <= node.endPosition.row; row += 1) {\n spans.push({\n line: row,\n startColumn: row === node.startPosition.row ? node.startPosition.column : 0,\n endColumn: row === node.endPosition.row ? node.endPosition.column : Number.POSITIVE_INFINITY,\n });\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return spans;\n}\n\nfunction isCommentOnlyLine(line: string, lineIndex: number, spans: CommentSpan[]): boolean {\n const relevantSpans = spans.filter((span) => span.line === lineIndex);\n if (relevantSpans.length === 0) {\n return false;\n }\n\n const firstContentColumn = line.search(/\\S/);\n const lastContentColumn = line.trimEnd().length;\n\n return relevantSpans.some((span) => span.startColumn <= firstContentColumn && span.endColumn >= lastContentColumn);\n}\n\nfunction measureHalstead(root: Parser.SyntaxNode, code: string): HalsteadMetrics {\n const operators = new Map<string, number>();\n const operands = new Map<string, number>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment') {\n return;\n }\n\n if (node.childCount === 0) {\n const text = code.slice(node.startIndex, node.endIndex);\n if (operatorTexts.has(text) || operatorTexts.has(node.type)) {\n incrementCount(operators, text || node.type);\n } else if (operandNodeTypes.has(node.type)) {\n incrementCount(operands, text);\n }\n return;\n }\n\n if (operatorTexts.has(node.type)) {\n incrementCount(operators, node.type);\n }\n\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(root);\n\n const distinctOperators = operators.size;\n const distinctOperands = operands.size;\n const totalOperators = sum(operators.values());\n const totalOperands = sum(operands.values());\n const vocabulary = distinctOperators + distinctOperands;\n const length = totalOperators + totalOperands;\n const volume = vocabulary === 0 ? 0 : length * Math.log2(vocabulary);\n const difficulty = distinctOperands === 0 ? 0 : (distinctOperators / 2) * (totalOperands / distinctOperands);\n const effort = difficulty * volume;\n\n return {\n distinctOperators,\n distinctOperands,\n totalOperators,\n totalOperands,\n vocabulary,\n length,\n volume,\n difficulty,\n effort,\n time: effort / 18,\n bugs: volume / 3000,\n };\n}\n\nfunction findFunctionName(node: Parser.SyntaxNode): string | undefined {\n const wrappedName = findWrappedComponentName(node);\n if (wrappedName) {\n return wrappedName;\n }\n\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return nameNode.text;\n }\n\n const parent = node.parent;\n if (!parent) {\n return undefined;\n }\n\n const parentName = parent.childForFieldName('name');\n return parentName?.text;\n}\n\nfunction findWrappedComponentName(node: Parser.SyntaxNode): string | undefined {\n let current: Parser.SyntaxNode | undefined = node;\n while (current) {\n const argumentsNode: Parser.SyntaxNode | null = current.parent;\n const callNode: Parser.SyntaxNode | null | undefined = argumentsNode?.parent;\n if (argumentsNode?.type !== 'arguments' || callNode?.type !== 'call_expression') {\n return undefined;\n }\n\n if (!isReactComponentWrapperCall(callNode)) {\n return undefined;\n }\n\n const declaratorNode = callNode.parent;\n if (declaratorNode?.type === 'variable_declarator') {\n return declaratorNode.childForFieldName('name')?.text;\n }\n\n current = callNode;\n }\n\n return undefined;\n}\n\nfunction isReactComponentWrapperCall(node: Parser.SyntaxNode): boolean {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n return (\n calleeNode?.text === 'memo' ||\n calleeNode?.text === 'React.memo' ||\n calleeNode?.text === 'forwardRef' ||\n calleeNode?.text === 'React.forwardRef'\n );\n}\n\nfunction isCallNode(node: Parser.SyntaxNode): boolean {\n return node.type === 'call_expression' || node.type === 'call';\n}\n\nfunction findCalleeName(node: Parser.SyntaxNode): string | undefined {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n if (!calleeNode) {\n return undefined;\n }\n\n return findRightmostIdentifier(calleeNode);\n}\n\nfunction findRightmostIdentifier(node: Parser.SyntaxNode): string | undefined {\n if (\n node.type === 'identifier' ||\n node.type === 'property_identifier' ||\n node.type === 'field_identifier' ||\n node.type === 'attribute'\n ) {\n return node.text;\n }\n\n for (let index = node.namedChildCount - 1; index >= 0; index -= 1) {\n const child = node.namedChild(index);\n if (!child) {\n continue;\n }\n\n const identifier = findRightmostIdentifier(child);\n if (identifier) {\n return identifier;\n }\n }\n\n return undefined;\n}\n\nfunction isReactCreateElementCall(node: Parser.SyntaxNode): boolean {\n if (!isCallNode(node)) {\n return false;\n }\n\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n return calleeNode?.text === 'React.createElement' || calleeNode?.text === 'createElement';\n}\n\nfunction isImportNode(node: Parser.SyntaxNode): boolean {\n return (\n node.type === 'import_statement' ||\n node.type === 'import_declaration' ||\n node.type === 'import_from_statement' ||\n node.type === 'import_spec' ||\n node.type === 'import_spec_list'\n );\n}\n\nfunction findImportSources(node: Parser.SyntaxNode, language: LanguageDefinition): string[] {\n if (language.name === 'python') {\n const pythonSources = findPythonImportSources(node);\n if (pythonSources.length > 0) {\n return pythonSources;\n }\n }\n\n const sourceNode = node.childForFieldName('source') ?? findFirstStringNode(node);\n return sourceNode ? [unquote(sourceNode.text)] : [];\n}\n\nfunction isRelativeImportSource(source: string): boolean {\n return source.startsWith('.') || source.startsWith('/');\n}\n\nfunction findPythonImportSources(node: Parser.SyntaxNode): string[] {\n if (node.type === 'import_from_statement') {\n const moduleNode = node.childForFieldName('module_name');\n return moduleNode ? [normalizeImportSource(moduleNode.text)] : [];\n }\n\n if (node.type !== 'import_statement') {\n return [];\n }\n\n return node.namedChildren\n .map((child) => findPythonImportedModuleName(child))\n .filter((source) => source !== undefined);\n}\n\nfunction findPythonImportedModuleName(node: Parser.SyntaxNode): string | undefined {\n if (node.type === 'dotted_name' || node.type === 'relative_import') {\n return normalizeImportSource(node.text);\n }\n\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return normalizeImportSource(nameNode.text);\n }\n\n for (const child of node.namedChildren) {\n const source = findPythonImportedModuleName(child);\n if (source) {\n return source;\n }\n }\n\n return undefined;\n}\n\nfunction normalizeImportSource(source: string): string {\n return source.replaceAll(/\\s+/gu, '');\n}\n\nfunction findFirstStringNode(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n if (node.type === 'string' || node.type === 'string_literal' || node.type === 'interpreted_string_literal') {\n return node;\n }\n\n for (const child of node.namedChildren) {\n const stringNode = findFirstStringNode(child);\n if (stringNode) {\n return stringNode;\n }\n }\n\n return undefined;\n}\n\nfunction unquote(value: string): string {\n return value.replaceAll(/^['\"`]|['\"`]$/gu, '');\n}\n\nfunction isExportNode(node: Parser.SyntaxNode): boolean {\n return node.type.startsWith('export') || node.type === 'public_field_definition';\n}\n\nfunction findRecursiveIndexes(graph: Map<number, Set<number>>): Set<number> {\n const recursiveIndexes = new Set<number>();\n\n for (const index of graph.keys()) {\n if (canReach(index, index, graph, new Set())) {\n recursiveIndexes.add(index);\n }\n }\n\n return recursiveIndexes;\n}\n\nfunction canReach(start: number, target: number, graph: Map<number, Set<number>>, visited: Set<number>): boolean {\n const callees = graph.get(start);\n if (!callees) {\n return false;\n }\n\n for (const callee of callees) {\n if (callee === target) {\n return true;\n }\n\n if (!visited.has(callee)) {\n visited.add(callee);\n if (canReach(callee, target, graph, visited)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction measureMaxCallDepth(graph: Map<number, Set<number>>): number {\n let maxDepth = 0;\n for (const index of graph.keys()) {\n maxDepth = Math.max(maxDepth, measureCallDepth(index, graph, new Set()));\n }\n return maxDepth;\n}\n\nfunction measureCallDepth(index: number, graph: Map<number, Set<number>>, pathIndexes: Set<number>): number {\n const callees = graph.get(index);\n if (!callees || callees.size === 0 || pathIndexes.has(index)) {\n return 0;\n }\n\n pathIndexes.add(index);\n let maxDepth = 0;\n for (const callee of callees) {\n maxDepth = Math.max(maxDepth, 1 + measureCallDepth(callee, graph, new Set(pathIndexes)));\n }\n return maxDepth;\n}\n\nfunction intersectSets(left: Set<string>, right: Set<string>): Set<string> {\n const intersection = new Set<string>();\n for (const value of left) {\n if (right.has(value)) {\n intersection.add(value);\n }\n }\n return intersection;\n}\n\nfunction calculateMaintainabilityIndex(volume: number, complexity: number, loc: number): number {\n if (loc === 0) {\n return 100;\n }\n\n const raw = 171 - 5.2 * Math.log(Math.max(volume, 1)) - 0.23 * complexity - 16.2 * Math.log(loc);\n return Math.max(0, Math.min(100, (raw * 100) / 171));\n}\n\nfunction incrementCount(map: Map<string, number>, value: string): void {\n map.set(value, (map.get(value) ?? 0) + 1);\n}\n\nfunction maxMetric(functions: FunctionMetrics[], key: 'cyclomaticComplexity' | 'cognitiveComplexity'): number {\n return functions.length === 0 ? 0 : Math.max(...functions.map((fn) => fn[key]));\n}\n\nfunction maxMapValue(map: Map<unknown, number>): number {\n let maximum = 0;\n for (const value of map.values()) {\n maximum = Math.max(maximum, value);\n }\n return maximum;\n}\n\nfunction sum(values: Iterable<number>): number {\n let total = 0;\n for (const value of values) {\n total += value;\n }\n return total;\n}\n"],"names":["booleanOperators","Set","operatorTexts","operandNodeTypes","TreeMeasurer","registry","createLanguageRegistry","registerLanguage","language","this","set","name","alias","aliases","getSupportedLanguages","values","map","measure","code","options","get","Error","parser","Parser","setLanguage","parserLanguage","root","parse","undefined","bufferSize","length","rootNode","structuralMetrics","functions","analyses","node","index","complexity","measureComplexity","calls","callees","functionNodeTypes","callCount","visit","insideRoot","has","type","isCallNode","callee","calleeNode","childForFieldName","namedChild","findRightmostIdentifier","findCalleeName","add","child","namedChildren","collectCalls","findFunctionName","startLine","startPosition","row","endLine","endPosition","returnsJsx","cyclomaticComplexity","cognitiveComplexity","identifiers","collectIdentifiers","analyzeFunction","callGraph","indexesByName","Map","analysis","entries","filter","entry","mapUniqueFunctionIndexesByName","functionNames","keys","fanInByIndex","fanOutByIndex","graph","internalCallCount","allCallees","internalCalleeNames","internalCalleeIndexes","calleeIndex","size","recursiveIndexes","canReach","findRecursiveIndexes","metrics","uniqueCalleeCount","internalEdgeCount","sum","recursiveFunctionCount","maxFanIn","maxMapValue","maxFanOut","maxCallDepth","measureMaxCallDepth","measureCallGraph","fanIn","fanOut","recursive","coupling","measureCoupling","cohesion","measureCohesion","typeComplexity","measureTypeComplexity","measureStructuralMetrics","collectNodes","functionMetrics","globalComplexity","lines","sourceLines","split","commentSpans","spans","push","line","startColumn","column","endColumn","Number","POSITIVE_INFINITY","collectCommentSpans","blank","comment","trim","isCommentOnlyLine","total","measureLines","halstead","operators","operands","childCount","text","slice","startIndex","endIndex","incrementCount","children","distinctOperators","distinctOperands","totalOperators","totalOperands","vocabulary","volume","Math","log2","difficulty","effort","time","bugs","measureHalstead","bytes","Buffer","byteLength","classCount","classNodeTypes","functionCount","maxCyclomaticComplexity","maxMetric","maxCognitiveComplexity","nestingDepth","maintainabilityIndex","calculateMaintainabilityIndex","syntaxTree","includeSyntaxTree","toString","defaultMeasurer","nesting","stopAtNestedFunctions","functionNodes","decisionNodes","decisionNodeTypes","nestingNodes","nestingNodeTypes","current","currentNesting","isDecision","isNesting","isNamed","isBooleanOperator","childNesting","max","nodeTypes","nodes","containsJsxExpression","containsReactCreateElementCall","getArrowFunctionBody","namedChildCount","containsNode","startsWith","calleeName","isArrayMappingCallee","some","containsReturnedJsxFunction","isJsxMappingCall","isReactCreateElementCall","predicate","body","containsOwnReturnNode","returnsJsxFromFunctionNode","importSources","importCount","exportCount","isImportNode","source","pythonSources","moduleNode","normalizeImportSource","findPythonImportedModuleName","findPythonImportSources","sourceNode","findFirstStringNode","unquote","findImportSources","isExportNode","relativeImportCount","isRelativeImportSource","importSourceCount","externalImportCount","allIdentifiers","sharedIdentifiers","overlapTotal","pairCount","identifier","leftIndex","rightIndex","left","right","intersection","intersectSets","unionSize","averageFunctionIdentifierOverlap","sharedIdentifierCount","uniqueIdentifierCount","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","unionTypeCount","intersectionTypeCount","conditionalTypeCount","typeAssertionCount","nonNullAssertionCount","satisfiesExpressionCount","lineIndex","relevantSpans","span","firstContentColumn","search","lastContentColumn","trimEnd","wrappedName","argumentsNode","parent","callNode","isReactComponentWrapperCall","declaratorNode","findWrappedComponentName","nameNode","parentName","replaceAll","stringNode","value","start","target","visited","maxDepth","measureCallDepth","pathIndexes","loc","raw","log","min","key","fn","maximum"],"mappings":"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"}
|
package/dist/metrics.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e from"tree-sitter";import{createLanguageRegistry as t}from"./languages.js";const n=new Set(["&&","||","and","or"]),o=new Set(["+","-","*","/","%","**","=","+=","-=","*=","/=","%=","==","!=","===","!==","<","<=",">",">=","!","~","&","|","^","<<",">>","=>","return","throw","yield","await","break","continue"]),i=new Set(["identifier","property_identifier","field_identifier","type_identifier","number","integer","float","string","string_literal","template_string","character_literal","true","false","null","undefined","nil"]);class r{registry=t();registerLanguage(e){this.registry.set(e.name,e);for(const t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(t,n){const r=this.registry.get(n.language);if(!r)throw new Error(`Unsupported language: ${n.language}`);const a=new e;a.setLanguage(r.parserLanguage);const s=a.parse(t,void 0,{bufferSize:t.length+1}).rootNode,h=function(e,t,n){const o=t.map(e=>function(e,t){const n=c(e,t,0),o=function(e){const t=new Set;let n=0;function o(e){if(function(e){return"call_expression"===e.type||"call"===e.type}(e)){n+=1;const o=function(e){const t=e.childForFieldName("function")??e.namedChild(0);if(!t)return;return C(t)}(e);o&&t.add(o)}for(const t of e.namedChildren)o(t)}return o(e),{callCount:n,callees:t}}(e);return{name:y(e),startLine:e.startPosition.row+1,endLine:e.endPosition.row+1,cyclomaticComplexity:n.cyclomaticComplexity,cognitiveComplexity:n.cognitiveComplexity,callCount:o.callCount,callees:o.callees,identifiers:l(e)}}(e,n)),i=function(e){const t=new Set(e.map(e=>e.name).filter(e=>void 0!==e)),n=new Map,o=new Map,i=new Map;let r=0,a=0;const s=new Set;for(const c of e){r+=c.callCount;for(const e of c.callees)s.add(e);if(!c.name)continue;const e=new Set([...c.callees].filter(e=>t.has(e)));i.set(c.name,e),o.set(c.name,e.size);for(const t of e)n.set(t,(n.get(t)??0)+1),a+=1}const c=function(e){const t=new Set;for(const n of e.keys())g(n,n,e,new Set)&&t.add(n);return t}(i);return{fanInByName:n,fanOutByName:o,recursiveNames:c,metrics:{callCount:r,uniqueCalleeCount:s.size,internalCallCount:a,internalEdgeCount:I([...i.values()].map(e=>e.size)),recursiveFunctionCount:c.size,maxFanIn:N(n),maxFanOut:N(o),maxCallDepth:x(i)}}}(o);return{functions:o.map(e=>({name:e.name,startLine:e.startLine,endLine:e.endLine,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:i.fanInByName.get(e.name??"")??0,fanOut:i.fanOutByName.get(e.name??"")??0,recursive:i.recursiveNames.has(e.name??"")})),callGraph:i.metrics,coupling:f(e),cohesion:p(o),typeComplexity:m(e)}}(s,u(s,new Set(r.functionNodeTypes)),r),_=h.functions,w=c(s,r,0),M=function(e,t){const n=0===e.length?[]:e.split(/\r\n|\n|\r/),o=function(e){const t=[];function n(e){if("comment"===e.type||"line_comment"===e.type||"block_comment"===e.type)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:Number.POSITIVE_INFINITY});for(const t of e.namedChildren)n(t)}return n(e),t}(t);let i=0,r=0;for(const[e,t]of n.entries())""!==t.trim()?d(t,e,o)&&(r+=1):i+=1;return{total:n.length,code:n.length-i-r,comment:r,blank:i}}(t,s),z=function(e,t){const n=new Map,r=new Map;function a(e){if("comment"!==e.type){if(0===e.childCount){const a=t.slice(e.startIndex,e.endIndex);return void(o.has(a)||o.has(e.type)?S(n,a||e.type):i.has(e.type)&&S(r,a))}o.has(e.type)&&S(n,e.type);for(const t of e.children)a(t)}}a(e);const s=n.size,c=r.size,l=I(n.values()),u=I(r.values()),f=s+c,p=l+u,m=0===f?0:p*Math.log2(f),d=0===c?0:s/2*(u/c),y=d*m;return{distinctOperators:s,distinctOperands:c,totalOperators:l,totalOperands:u,vocabulary:f,length:p,volume:m,difficulty:d,effort:y,time:y/18,bugs:m/3e3}}(s,t);return{language:r.name,bytes:Buffer.byteLength(t),lines:M,functions:_,classCount:u(s,new Set(r.classNodeTypes)).length,functionCount:_.length,cyclomaticComplexity:w.cyclomaticComplexity,maxCyclomaticComplexity:b(_,"cyclomaticComplexity"),cognitiveComplexity:w.cognitiveComplexity,maxCognitiveComplexity:b(_,"cognitiveComplexity"),nestingDepth:w.nestingDepth,callGraph:h.callGraph,coupling:h.coupling,cohesion:h.cohesion,typeComplexity:h.typeComplexity,halstead:z,maintainabilityIndex:v(z.volume,w.cyclomaticComplexity,M.code),syntaxTree:n.includeSyntaxTree?s.toString():void 0}}}const a=new r;function s(e,t){return a.measure(e,t)}function c(e,t,o){let i=1,r=0,a=o;const s=new Set(t.decisionNodeTypes),c=new Set(t.nestingNodeTypes);function l(e,t){const o=s.has(e.type),u=c.has(e.type);o&&(i+=1,r+=1+t),function(e){if(e.isNamed)return!1;return n.has(e.text)}(e)&&(i+=1,r+=1);const f=u?t+1:t;a=Math.max(a,f);for(const t of e.children)l(t,f)}for(const t of e.children)l(t,o);return{cyclomaticComplexity:i,cognitiveComplexity:r,nestingDepth:a}}function l(e){const t=new Set;return function e(n){"identifier"!==n.type&&"property_identifier"!==n.type&&"field_identifier"!==n.type||t.add(n.text);for(const t of n.namedChildren)e(t)}(e),t}function u(e,t){const n=[];return function e(o){t.has(o.type)&&n.push(o);for(const t of o.namedChildren)e(t)}(e),n}function f(e){const t=new Set;let n=0,o=0,i=0;return function e(r){if(function(e){return"import_statement"===e.type||"import_declaration"===e.type||"import_from_statement"===e.type||"import_spec"===e.type||"import_spec_list"===e.type}(r)){n+=1;const e=function(e){const t=h(e);return t?(n=t.text,n.replaceAll(/^['"`]|['"`]$/gu,"")):void 0;var n}(r);e&&(t.add(e),(e.startsWith(".")||e.startsWith("/"))&&(i+=1))}(function(e){return e.type.startsWith("export")||"public_field_definition"===e.type})(r)&&(o+=1);for(const t of r.namedChildren)e(t)}(e),{importCount:n,importSourceCount:t.size,relativeImportCount:i,externalImportCount:t.size-i,exportCount:o}}function p(e){const t=new Set,n=new Set;let o=0,i=0;for(const n of e)for(const e of n.identifiers)t.add(e);for(let t=0;t<e.length;t+=1)for(let r=t+1;r<e.length;r+=1){const a=e[t],s=e[r];if(!a||!s)continue;const c=w(a.identifiers,s.identifiers),l=new Set([...a.identifiers,...s.identifiers]).size;for(const e of c)n.add(e);o+=0===l?0:c.size/l,i+=1}return{averageFunctionIdentifierOverlap:0===i?1:o/i,sharedIdentifierCount:n.size,uniqueIdentifierCount:t.size}}function m(e){const t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};return function e(n){switch(n.type){case"type_annotation":t.typeAnnotationCount+=1;break;case"type_alias_declaration":t.typeAliasCount+=1;break;case"interface_declaration":t.interfaceCount+=1;break;case"type_parameters":case"type_parameter":t.genericParameterCount+="type_parameter"===n.type?1:0;break;case"union_type":t.unionTypeCount+=1;break;case"intersection_type":t.intersectionTypeCount+=1;break;case"conditional_type":t.conditionalTypeCount+=1;break;case"as_expression":case"type_assertion":t.typeAssertionCount+=1;break;case"non_null_expression":t.nonNullAssertionCount+=1;break;case"satisfies_expression":t.satisfiesExpressionCount+=1}for(const t of n.namedChildren)e(t)}(e),t}function d(e,t,n){const o=n.filter(e=>e.line===t);if(0===o.length)return!1;const i=e.search(/\S/),r=e.trimEnd().length;return o.some(e=>e.startColumn<=i&&e.endColumn>=r)}function y(e){const t=e.childForFieldName("name");if(t)return t.text;const n=e.parent;if(!n)return;const o=n.childForFieldName("name");return o?.text}function C(e){if("identifier"===e.type||"property_identifier"===e.type||"field_identifier"===e.type||"attribute"===e.type)return e.text;for(let t=e.namedChildCount-1;t>=0;t-=1){const n=e.namedChild(t);if(!n)continue;const o=C(n);if(o)return o}}function h(e){if("string"===e.type||"string_literal"===e.type||"interpreted_string_literal"===e.type)return e;for(const t of e.namedChildren){const e=h(t);if(e)return e}}function g(e,t,n,o){const i=n.get(e);if(!i)return!1;for(const e of i){if(e===t)return!0;if(!o.has(e)&&(o.add(e),g(e,t,n,o)))return!0}return!1}function x(e){let t=0;for(const n of e.keys())t=Math.max(t,_(n,e,new Set));return t}function _(e,t,n){const o=t.get(e);if(!o||0===o.size||n.has(e))return 0;n.add(e);let i=0;for(const e of o)i=Math.max(i,1+_(e,t,new Set(n)));return i}function w(e,t){const n=new Set;for(const o of e)t.has(o)&&n.add(o);return n}function v(e,t,n){if(0===n)return 100;const o=171-5.2*Math.log(Math.max(e,1))-.23*t-16.2*Math.log(n);return Math.max(0,Math.min(100,100*o/171))}function S(e,t){e.set(t,(e.get(t)??0)+1)}function b(e,t){return 0===e.length?0:Math.max(...e.map(e=>e[t]))}function N(e){let t=0;for(const n of e.values())t=Math.max(t,n);return t}function I(e){let t=0;for(const n of e)t+=n;return t}export{r as TreeMeasurer,a as defaultMeasurer,s as measureCode};
|
|
1
|
+
import e from"tree-sitter";import{createLanguageRegistry as t}from"./languages.js";const n=new Set(["&&","||","and","or"]),o=new Set(["+","-","*","/","%","**","=","+=","-=","*=","/=","%=","==","!=","===","!==","<","<=",">",">=","!","~","&","|","^","<<",">>","=>","return","throw","yield","await","break","continue"]),i=new Set(["identifier","property_identifier","field_identifier","type_identifier","number","integer","float","string","string_literal","template_string","character_literal","true","false","null","undefined","nil"]);class r{registry=t();registerLanguage(e){this.registry.set(e.name,e);for(const t of e.aliases??[])this.registry.set(t,e)}getSupportedLanguages(){return[...new Set([...this.registry.values()].map(e=>e.name))]}measure(t,n){const r=this.registry.get(n.language);if(!r)throw new Error(`Unsupported language: ${n.language}`);const s=new e;s.setLanguage(r.parserLanguage);const a=s.parse(t,void 0,{bufferSize:t.length+1}).rootNode,d=function(e,t,n){const o=t.map((e,t)=>function(e,t,n){const o=c(e,t,0,!0),i=function(e,t){const n=new Set,o=new Set(t.functionNodeTypes);let i=0;function r(e,t){if(t||!o.has(e.type)){if(S(e)){i+=1;const t=function(e){const t=e.childForFieldName("function")??e.namedChild(0);if(!t)return;return F(t)}(e);t&&n.add(t)}for(const t of e.namedChildren)r(t,!1)}}return r(e,!0),{callCount:i,callees:n}}(e,t);return{index:n,name:w(e),startLine:e.startPosition.row+1,endLine:e.endPosition.row+1,returnsJsx:f(e,t),cyclomaticComplexity:o.cyclomaticComplexity,cognitiveComplexity:o.cognitiveComplexity,callCount:i.callCount,callees:i.callees,identifiers:u(e)}}(e,n,t)),i=function(e){const t=function(e){const t=new Map;for(const n of e)n.name&&t.set(n.name,t.has(n.name)?void 0:n.index);return new Map([...t.entries()].filter(e=>void 0!==e[1]))}(e),n=new Set(t.keys()),o=new Map,i=new Map,r=new Map;let s=0,a=0;const c=new Set;for(const u of e){s+=u.callCount;for(const e of u.callees)c.add(e);const e=new Set([...u.callees].filter(e=>n.has(e))),l=new Set;for(const n of e){const e=t.get(n);void 0!==e&&l.add(e)}r.set(u.index,l),i.set(u.index,e.size),a+=e.size;for(const e of l)o.set(e,(o.get(e)??0)+1)}const u=function(e){const t=new Set;for(const n of e.keys())z(n,n,e,new Set)&&t.add(n);return t}(r);return{fanInByIndex:o,fanOutByIndex:i,recursiveIndexes:u,metrics:{callCount:s,uniqueCalleeCount:c.size,internalCallCount:a,internalEdgeCount:D([...r.values()].map(e=>e.size)),recursiveFunctionCount:u.size,maxFanIn:R(o),maxFanOut:R(i),maxCallDepth:L(r)}}}(o);return{functions:o.map(e=>({name:e.name,startLine:e.startLine,endLine:e.endLine,returnsJsx:e.returnsJsx,cyclomaticComplexity:e.cyclomaticComplexity,cognitiveComplexity:e.cognitiveComplexity,callCount:e.callCount,uniqueCalleeCount:e.callees.size,fanIn:i.fanInByIndex.get(e.index)??0,fanOut:i.fanOutByIndex.get(e.index)??0,recursive:i.recursiveIndexes.has(e.index)})),callGraph:i.metrics,coupling:C(e,n),cohesion:g(o),typeComplexity:x(e)}}(a,l(a,new Set(r.functionNodeTypes)),r),p=d.functions,m=c(a,r,0,!1),y=function(e,t){const n=0===e.length?[]:e.split(/\r\n|\n|\r/),o=function(e){const t=[];function n(e){if("comment"===e.type||"line_comment"===e.type||"block_comment"===e.type)for(let n=e.startPosition.row;n<=e.endPosition.row;n+=1)t.push({line:n,startColumn:n===e.startPosition.row?e.startPosition.column:0,endColumn:n===e.endPosition.row?e.endPosition.column:Number.POSITIVE_INFINITY});for(const t of e.namedChildren)n(t)}return n(e),t}(t);let i=0,r=0;for(const[e,t]of n.entries())""!==t.trim()?_(t,e,o)&&(r+=1):i+=1;return{total:n.length,code:n.length-i-r,comment:r,blank:i}}(t,a),h=function(e,t){const n=new Map,r=new Map;function s(e){if("comment"!==e.type){if(0===e.childCount){const s=t.slice(e.startIndex,e.endIndex);return void(o.has(s)||o.has(e.type)?E(n,s||e.type):i.has(e.type)&&E(r,s))}o.has(e.type)&&E(n,e.type);for(const t of e.children)s(t)}}s(e);const a=n.size,c=r.size,u=D(n.values()),l=D(r.values()),f=a+c,d=u+l,p=0===f?0:d*Math.log2(f),m=0===c?0:a/2*(l/c),y=m*p;return{distinctOperators:a,distinctOperands:c,totalOperators:u,totalOperands:l,vocabulary:f,length:d,volume:p,difficulty:m,effort:y,time:y/18,bugs:p/3e3}}(a,t);return{language:r.name,bytes:Buffer.byteLength(t),lines:y,functions:p,classCount:l(a,new Set(r.classNodeTypes)).length,functionCount:p.length,cyclomaticComplexity:m.cyclomaticComplexity,maxCyclomaticComplexity:B(p,"cyclomaticComplexity"),cognitiveComplexity:m.cognitiveComplexity,maxCognitiveComplexity:B(p,"cognitiveComplexity"),nestingDepth:m.nestingDepth,callGraph:d.callGraph,coupling:d.coupling,cohesion:d.cohesion,typeComplexity:d.typeComplexity,halstead:h,maintainabilityIndex:O(h.volume,m.cyclomaticComplexity,y.code),syntaxTree:n.includeSyntaxTree?a.toString():void 0}}}const s=new r;function a(e,t){return s.measure(e,t)}function c(e,t,o,i){let r=1,s=0,a=o;const c=new Set(t.functionNodeTypes),u=new Set(t.decisionNodeTypes),l=new Set(t.nestingNodeTypes);function f(e,t,o){if(i&&c.has(e.type))return;const d=u.has(e.type),p=l.has(e.type);d&&(r+=1,s+=1+t),function(e){if(e.isNamed)return!1;return n.has(e.text)}(e)&&(r+=1,s+=1);const m=p?t+1:t;a=Math.max(a,m);for(const t of e.children)f(t,m)}for(const t of e.children)f(t,o);return{cyclomaticComplexity:r,cognitiveComplexity:s,nestingDepth:a}}function u(e){const t=new Set;return function e(n){"identifier"!==n.type&&"property_identifier"!==n.type&&"field_identifier"!==n.type||t.add(n.text);for(const t of n.namedChildren)e(t)}(e),t}function l(e,t){const n=[];return function e(o){t.has(o.type)&&n.push(o);for(const t of o.namedChildren)e(t)}(e),n}function f(e,t){const n=new Set(t.functionNodeTypes);return function t(o,i){if(!i&&n.has(o.type))return!1;if("return_statement"===o.type)return p(o,n)||m(o,n);if("arrow_function"===e.type&&o===d(e)&&"statement_block"!==o.type&&!n.has(o.type))return p(o,n)||m(o,n);for(const e of o.namedChildren)if(t(e,!1))return!0;return!1}(e,!0)}function d(e){return e.childForFieldName("body")??e.namedChild(e.namedChildCount-1)??void 0}function p(e,t){return y(e,t,e=>e.type.startsWith("jsx_")||function(e,t){if(!S(e)||!function(e){if(!e)return!1;const t=F(e);return"map"===t||"flatMap"===t}(e.childForFieldName("function")??e.namedChild(0)))return!1;return e.namedChildren.some(e=>h(e,t))}(e,t))}function m(e,t){return y(e,t,b)}function y(e,t,n){return function e(o,i){if(!i&&t.has(o.type))return!1;if(n(o))return!0;for(const t of o.namedChildren)if(e(t,!1))return!0;return!1}(e,!0)}function h(e,t){return t.has(e.type)?function(e,t){const n="arrow_function"===e.type?d(e):void 0;if(n&&"statement_block"!==n.type&&!t.has(n.type))return p(n,t)||m(n,t);return function(e,t,n){function o(e,i){if(!i&&t.has(e.type))return!1;if("return_statement"===e.type&&n(e))return!0;for(const t of e.namedChildren)if(o(t,!1))return!0;return!1}return o(e,!0)}(e,t,e=>p(e,t)||m(e,t))}(e,t):e.namedChildren.some(e=>h(e,t))}function C(e,t){const n=new Set;let o=0,i=0;!function e(r){if(function(e){return"import_statement"===e.type||"import_declaration"===e.type||"import_from_statement"===e.type||"import_spec"===e.type||"import_spec_list"===e.type}(r)){o+=1;for(const e of function(e,t){if("python"===t.name){const t=function(e){if("import_from_statement"===e.type){const t=e.childForFieldName("module_name");return t?[M(t.text)]:[]}if("import_statement"!==e.type)return[];return e.namedChildren.map(e=>I(e)).filter(e=>void 0!==e)}(e);if(t.length>0)return t}const n=e.childForFieldName("source")??k(e);return n?[T(n.text)]:[]}(r,t))n.add(e)}(function(e){return e.type.startsWith("export")||"public_field_definition"===e.type})(r)&&(i+=1);for(const t of r.namedChildren)e(t)}(e);const r=[...n].filter(N).length;return{importCount:o,importSourceCount:n.size,relativeImportCount:r,externalImportCount:n.size-r,exportCount:i}}function g(e){const t=new Set,n=new Set;let o=0,i=0;for(const n of e)for(const e of n.identifiers)t.add(e);for(let t=0;t<e.length;t+=1)for(let r=t+1;r<e.length;r+=1){const s=e[t],a=e[r];if(!s||!a)continue;const c=A(s.identifiers,a.identifiers),u=new Set([...s.identifiers,...a.identifiers]).size;for(const e of c)n.add(e);o+=0===u?0:c.size/u,i+=1}return{averageFunctionIdentifierOverlap:0===i?1:o/i,sharedIdentifierCount:n.size,uniqueIdentifierCount:t.size}}function x(e){const t={typeAnnotationCount:0,typeAliasCount:0,interfaceCount:0,genericParameterCount:0,unionTypeCount:0,intersectionTypeCount:0,conditionalTypeCount:0,typeAssertionCount:0,nonNullAssertionCount:0,satisfiesExpressionCount:0};return function e(n){switch(n.type){case"type_annotation":t.typeAnnotationCount+=1;break;case"type_alias_declaration":t.typeAliasCount+=1;break;case"interface_declaration":t.interfaceCount+=1;break;case"type_parameters":case"type_parameter":t.genericParameterCount+="type_parameter"===n.type?1:0;break;case"union_type":t.unionTypeCount+=1;break;case"intersection_type":t.intersectionTypeCount+=1;break;case"conditional_type":t.conditionalTypeCount+=1;break;case"as_expression":case"type_assertion":t.typeAssertionCount+=1;break;case"non_null_expression":t.nonNullAssertionCount+=1;break;case"satisfies_expression":t.satisfiesExpressionCount+=1}for(const t of n.namedChildren)e(t)}(e),t}function _(e,t,n){const o=n.filter(e=>e.line===t);if(0===o.length)return!1;const i=e.search(/\S/),r=e.trimEnd().length;return o.some(e=>e.startColumn<=i&&e.endColumn>=r)}function w(e){const t=function(e){let t=e;for(;t;){const e=t.parent,n=e?.parent;if("arguments"!==e?.type||"call_expression"!==n?.type)return;if(!v(n))return;const o=n.parent;if("variable_declarator"===o?.type)return o.childForFieldName("name")?.text;t=n}return}(e);if(t)return t;const n=e.childForFieldName("name");if(n)return n.text;const o=e.parent;if(!o)return;const i=o.childForFieldName("name");return i?.text}function v(e){const t=e.childForFieldName("function")??e.namedChild(0);return"memo"===t?.text||"React.memo"===t?.text||"forwardRef"===t?.text||"React.forwardRef"===t?.text}function S(e){return"call_expression"===e.type||"call"===e.type}function F(e){if("identifier"===e.type||"property_identifier"===e.type||"field_identifier"===e.type||"attribute"===e.type)return e.text;for(let t=e.namedChildCount-1;t>=0;t-=1){const n=e.namedChild(t);if(!n)continue;const o=F(n);if(o)return o}}function b(e){if(!S(e))return!1;const t=e.childForFieldName("function")??e.namedChild(0);return"React.createElement"===t?.text||"createElement"===t?.text}function N(e){return e.startsWith(".")||e.startsWith("/")}function I(e){if("dotted_name"===e.type||"relative_import"===e.type)return M(e.text);const t=e.childForFieldName("name");if(t)return M(t.text);for(const t of e.namedChildren){const e=I(t);if(e)return e}}function M(e){return e.replaceAll(/\s+/gu,"")}function k(e){if("string"===e.type||"string_literal"===e.type||"interpreted_string_literal"===e.type)return e;for(const t of e.namedChildren){const e=k(t);if(e)return e}}function T(e){return e.replaceAll(/^['"`]|['"`]$/gu,"")}function z(e,t,n,o){const i=n.get(e);if(!i)return!1;for(const e of i){if(e===t)return!0;if(!o.has(e)&&(o.add(e),z(e,t,n,o)))return!0}return!1}function L(e){let t=0;for(const n of e.keys())t=Math.max(t,P(n,e,new Set));return t}function P(e,t,n){const o=t.get(e);if(!o||0===o.size||n.has(e))return 0;n.add(e);let i=0;for(const e of o)i=Math.max(i,1+P(e,t,new Set(n)));return i}function A(e,t){const n=new Set;for(const o of e)t.has(o)&&n.add(o);return n}function O(e,t,n){if(0===n)return 100;const o=171-5.2*Math.log(Math.max(e,1))-.23*t-16.2*Math.log(n);return Math.max(0,Math.min(100,100*o/171))}function E(e,t){e.set(t,(e.get(t)??0)+1)}function B(e,t){return 0===e.length?0:Math.max(...e.map(e=>e[t]))}function R(e){let t=0;for(const n of e.values())t=Math.max(t,n);return t}function D(e){let t=0;for(const n of e)t+=n;return t}export{r as TreeMeasurer,s as defaultMeasurer,a as measureCode};
|
|
2
2
|
//# sourceMappingURL=metrics.js.map
|
package/dist/metrics.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metrics.js","sources":["../src/metrics.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport { createLanguageRegistry } from './languages.js';\nimport type {\n CallGraphMetrics,\n CodeMetrics,\n CohesionMetrics,\n CouplingMetrics,\n FunctionMetrics,\n HalsteadMetrics,\n LanguageDefinition,\n LanguageName,\n MeasureOptions,\n TypeComplexityMetrics,\n} from './types.js';\n\nconst booleanOperators = new Set(['&&', '||', 'and', 'or']);\nconst operatorTexts = new Set([\n '+',\n '-',\n '*',\n '/',\n '%',\n '**',\n '=',\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '==',\n '!=',\n '===',\n '!==',\n '<',\n '<=',\n '>',\n '>=',\n '!',\n '~',\n '&',\n '|',\n '^',\n '<<',\n '>>',\n '=>',\n 'return',\n 'throw',\n 'yield',\n 'await',\n 'break',\n 'continue',\n]);\n\nconst operandNodeTypes = new Set([\n 'identifier',\n 'property_identifier',\n 'field_identifier',\n 'type_identifier',\n 'number',\n 'integer',\n 'float',\n 'string',\n 'string_literal',\n 'template_string',\n 'character_literal',\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'nil',\n]);\n\ninterface ComplexityResult {\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n nestingDepth: number;\n}\n\ninterface CommentSpan {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\ninterface FunctionAnalysis {\n name?: string;\n startLine: number;\n endLine: number;\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n callCount: number;\n callees: Set<string>;\n identifiers: Set<string>;\n}\n\ninterface StructuralMetrics {\n callGraph: CallGraphMetrics;\n cohesion: CohesionMetrics;\n coupling: CouplingMetrics;\n functions: FunctionMetrics[];\n typeComplexity: TypeComplexityMetrics;\n}\n\nexport class TreeMeasurer {\n private readonly registry = createLanguageRegistry();\n\n registerLanguage(language: LanguageDefinition): void {\n this.registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n this.registry.set(alias, language);\n }\n }\n\n getSupportedLanguages(): LanguageName[] {\n return [...new Set([...this.registry.values()].map((language) => language.name))];\n }\n\n measure(code: string, options: MeasureOptions): CodeMetrics {\n const language = this.registry.get(options.language);\n if (!language) {\n throw new Error(`Unsupported language: ${options.language}`);\n }\n\n const parser = new Parser();\n parser.setLanguage(language.parserLanguage);\n const tree = parser.parse(code, undefined, {\n bufferSize: code.length + 1,\n });\n const root = tree.rootNode;\n const functions = collectNodes(root, new Set(language.functionNodeTypes));\n const structuralMetrics = measureStructuralMetrics(root, functions, language);\n const functionMetrics = structuralMetrics.functions;\n const globalComplexity = measureComplexity(root, language, 0);\n const lines = measureLines(code, root);\n const halstead = measureHalstead(root, code);\n\n return {\n language: language.name,\n bytes: Buffer.byteLength(code),\n lines,\n functions: functionMetrics,\n classCount: collectNodes(root, new Set(language.classNodeTypes)).length,\n functionCount: functionMetrics.length,\n cyclomaticComplexity: globalComplexity.cyclomaticComplexity,\n maxCyclomaticComplexity: maxMetric(functionMetrics, 'cyclomaticComplexity'),\n cognitiveComplexity: globalComplexity.cognitiveComplexity,\n maxCognitiveComplexity: maxMetric(functionMetrics, 'cognitiveComplexity'),\n nestingDepth: globalComplexity.nestingDepth,\n callGraph: structuralMetrics.callGraph,\n coupling: structuralMetrics.coupling,\n cohesion: structuralMetrics.cohesion,\n typeComplexity: structuralMetrics.typeComplexity,\n halstead,\n maintainabilityIndex: calculateMaintainabilityIndex(\n halstead.volume,\n globalComplexity.cyclomaticComplexity,\n lines.code\n ),\n syntaxTree: options.includeSyntaxTree ? root.toString() : undefined,\n };\n }\n}\n\nexport const defaultMeasurer = new TreeMeasurer();\n\nexport function measureCode(code: string, options: MeasureOptions): CodeMetrics {\n return defaultMeasurer.measure(code, options);\n}\n\nfunction measureStructuralMetrics(\n root: Parser.SyntaxNode,\n functions: Parser.SyntaxNode[],\n language: LanguageDefinition\n): StructuralMetrics {\n const analyses = functions.map((node) => analyzeFunction(node, language));\n const callGraph = measureCallGraph(analyses);\n const functionsWithGraph = analyses.map((analysis) => ({\n name: analysis.name,\n startLine: analysis.startLine,\n endLine: analysis.endLine,\n cyclomaticComplexity: analysis.cyclomaticComplexity,\n cognitiveComplexity: analysis.cognitiveComplexity,\n callCount: analysis.callCount,\n uniqueCalleeCount: analysis.callees.size,\n fanIn: callGraph.fanInByName.get(analysis.name ?? '') ?? 0,\n fanOut: callGraph.fanOutByName.get(analysis.name ?? '') ?? 0,\n recursive: callGraph.recursiveNames.has(analysis.name ?? ''),\n }));\n\n return {\n functions: functionsWithGraph,\n callGraph: callGraph.metrics,\n coupling: measureCoupling(root),\n cohesion: measureCohesion(analyses),\n typeComplexity: measureTypeComplexity(root),\n };\n}\n\nfunction analyzeFunction(node: Parser.SyntaxNode, language: LanguageDefinition): FunctionAnalysis {\n const complexity = measureComplexity(node, language, 0);\n const calls = collectCalls(node);\n return {\n name: findFunctionName(node),\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n cyclomaticComplexity: complexity.cyclomaticComplexity,\n cognitiveComplexity: complexity.cognitiveComplexity,\n callCount: calls.callCount,\n callees: calls.callees,\n identifiers: collectIdentifiers(node),\n };\n}\n\nfunction measureCallGraph(analyses: FunctionAnalysis[]): {\n fanInByName: Map<string, number>;\n fanOutByName: Map<string, number>;\n metrics: CallGraphMetrics;\n recursiveNames: Set<string>;\n} {\n const functionNames = new Set(analyses.map((analysis) => analysis.name).filter((name) => name !== undefined));\n const fanInByName = new Map<string, number>();\n const fanOutByName = new Map<string, number>();\n const graph = new Map<string, Set<string>>();\n let callCount = 0;\n let internalCallCount = 0;\n const allCallees = new Set<string>();\n\n for (const analysis of analyses) {\n callCount += analysis.callCount;\n for (const callee of analysis.callees) {\n allCallees.add(callee);\n }\n\n if (!analysis.name) {\n continue;\n }\n\n const internalCallees = new Set([...analysis.callees].filter((callee) => functionNames.has(callee)));\n graph.set(analysis.name, internalCallees);\n fanOutByName.set(analysis.name, internalCallees.size);\n for (const callee of internalCallees) {\n fanInByName.set(callee, (fanInByName.get(callee) ?? 0) + 1);\n internalCallCount += 1;\n }\n }\n\n const recursiveNames = findRecursiveNames(graph);\n\n return {\n fanInByName,\n fanOutByName,\n recursiveNames,\n metrics: {\n callCount,\n uniqueCalleeCount: allCallees.size,\n internalCallCount,\n internalEdgeCount: sum([...graph.values()].map((callees) => callees.size)),\n recursiveFunctionCount: recursiveNames.size,\n maxFanIn: maxMapValue(fanInByName),\n maxFanOut: maxMapValue(fanOutByName),\n maxCallDepth: measureMaxCallDepth(graph),\n },\n };\n}\n\nfunction measureComplexity(node: Parser.SyntaxNode, language: LanguageDefinition, nesting: number): ComplexityResult {\n let cyclomaticComplexity = 1;\n let cognitiveComplexity = 0;\n let nestingDepth = nesting;\n const decisionNodes = new Set(language.decisionNodeTypes);\n const nestingNodes = new Set(language.nestingNodeTypes);\n\n function visit(current: Parser.SyntaxNode, currentNesting: number): void {\n const isDecision = decisionNodes.has(current.type);\n const isNesting = nestingNodes.has(current.type);\n\n if (isDecision) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1 + currentNesting;\n }\n\n if (isBooleanOperator(current)) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1;\n }\n\n const childNesting = isNesting ? currentNesting + 1 : currentNesting;\n nestingDepth = Math.max(nestingDepth, childNesting);\n\n for (const child of current.children) {\n visit(child, childNesting);\n }\n }\n\n for (const child of node.children) {\n visit(child, nesting);\n }\n\n return { cyclomaticComplexity, cognitiveComplexity, nestingDepth };\n}\n\nfunction isBooleanOperator(node: Parser.SyntaxNode): boolean {\n if (node.isNamed) {\n return false;\n }\n\n return booleanOperators.has(node.text);\n}\n\nfunction collectCalls(root: Parser.SyntaxNode): { callCount: number; callees: Set<string> } {\n const callees = new Set<string>();\n let callCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isCallNode(node)) {\n callCount += 1;\n const callee = findCalleeName(node);\n if (callee) {\n callees.add(callee);\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return { callCount, callees };\n}\n\nfunction collectIdentifiers(root: Parser.SyntaxNode): Set<string> {\n const identifiers = new Set<string>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'identifier' || node.type === 'property_identifier' || node.type === 'field_identifier') {\n identifiers.add(node.text);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return identifiers;\n}\n\nfunction collectNodes(root: Parser.SyntaxNode, nodeTypes: Set<string>): Parser.SyntaxNode[] {\n const nodes: Parser.SyntaxNode[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (nodeTypes.has(node.type)) {\n nodes.push(node);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return nodes;\n}\n\nfunction measureCoupling(root: Parser.SyntaxNode): CouplingMetrics {\n const importSources = new Set<string>();\n let importCount = 0;\n let exportCount = 0;\n let relativeImportCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isImportNode(node)) {\n importCount += 1;\n const source = findImportSource(node);\n if (source) {\n importSources.add(source);\n if (source.startsWith('.') || source.startsWith('/')) {\n relativeImportCount += 1;\n }\n }\n }\n\n if (isExportNode(node)) {\n exportCount += 1;\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n\n return {\n importCount,\n importSourceCount: importSources.size,\n relativeImportCount,\n externalImportCount: importSources.size - relativeImportCount,\n exportCount,\n };\n}\n\nfunction measureCohesion(analyses: FunctionAnalysis[]): CohesionMetrics {\n const allIdentifiers = new Set<string>();\n const sharedIdentifiers = new Set<string>();\n let overlapTotal = 0;\n let pairCount = 0;\n\n for (const analysis of analyses) {\n for (const identifier of analysis.identifiers) {\n allIdentifiers.add(identifier);\n }\n }\n\n for (let leftIndex = 0; leftIndex < analyses.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < analyses.length; rightIndex += 1) {\n const left = analyses[leftIndex];\n const right = analyses[rightIndex];\n if (!left || !right) {\n continue;\n }\n\n const intersection = intersectSets(left.identifiers, right.identifiers);\n const unionSize = new Set([...left.identifiers, ...right.identifiers]).size;\n for (const identifier of intersection) {\n sharedIdentifiers.add(identifier);\n }\n overlapTotal += unionSize === 0 ? 0 : intersection.size / unionSize;\n pairCount += 1;\n }\n }\n\n return {\n averageFunctionIdentifierOverlap: pairCount === 0 ? 1 : overlapTotal / pairCount,\n sharedIdentifierCount: sharedIdentifiers.size,\n uniqueIdentifierCount: allIdentifiers.size,\n };\n}\n\nfunction measureTypeComplexity(root: Parser.SyntaxNode): TypeComplexityMetrics {\n const metrics: TypeComplexityMetrics = {\n typeAnnotationCount: 0,\n typeAliasCount: 0,\n interfaceCount: 0,\n genericParameterCount: 0,\n unionTypeCount: 0,\n intersectionTypeCount: 0,\n conditionalTypeCount: 0,\n typeAssertionCount: 0,\n nonNullAssertionCount: 0,\n satisfiesExpressionCount: 0,\n };\n\n function visit(node: Parser.SyntaxNode): void {\n switch (node.type) {\n case 'type_annotation': {\n metrics.typeAnnotationCount += 1;\n break;\n }\n case 'type_alias_declaration': {\n metrics.typeAliasCount += 1;\n break;\n }\n case 'interface_declaration': {\n metrics.interfaceCount += 1;\n break;\n }\n case 'type_parameters':\n case 'type_parameter': {\n metrics.genericParameterCount += node.type === 'type_parameter' ? 1 : 0;\n break;\n }\n case 'union_type': {\n metrics.unionTypeCount += 1;\n break;\n }\n case 'intersection_type': {\n metrics.intersectionTypeCount += 1;\n break;\n }\n case 'conditional_type': {\n metrics.conditionalTypeCount += 1;\n break;\n }\n case 'as_expression':\n case 'type_assertion': {\n metrics.typeAssertionCount += 1;\n break;\n }\n case 'non_null_expression': {\n metrics.nonNullAssertionCount += 1;\n break;\n }\n case 'satisfies_expression': {\n metrics.satisfiesExpressionCount += 1;\n break;\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return metrics;\n}\n\nfunction measureLines(code: string, root: Parser.SyntaxNode): CodeMetrics['lines'] {\n const sourceLines = code.length === 0 ? [] : code.split(/\\r\\n|\\n|\\r/);\n const commentSpans = collectCommentSpans(root);\n let blank = 0;\n let comment = 0;\n\n for (const [index, line] of sourceLines.entries()) {\n if (line.trim() === '') {\n blank += 1;\n continue;\n }\n\n if (isCommentOnlyLine(line, index, commentSpans)) {\n comment += 1;\n }\n }\n\n return {\n total: sourceLines.length,\n code: sourceLines.length - blank - comment,\n comment,\n blank,\n };\n}\n\nfunction collectCommentSpans(root: Parser.SyntaxNode): CommentSpan[] {\n const spans: CommentSpan[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n for (let row = node.startPosition.row; row <= node.endPosition.row; row += 1) {\n spans.push({\n line: row,\n startColumn: row === node.startPosition.row ? node.startPosition.column : 0,\n endColumn: row === node.endPosition.row ? node.endPosition.column : Number.POSITIVE_INFINITY,\n });\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return spans;\n}\n\nfunction isCommentOnlyLine(line: string, lineIndex: number, spans: CommentSpan[]): boolean {\n const relevantSpans = spans.filter((span) => span.line === lineIndex);\n if (relevantSpans.length === 0) {\n return false;\n }\n\n const firstContentColumn = line.search(/\\S/);\n const lastContentColumn = line.trimEnd().length;\n\n return relevantSpans.some((span) => span.startColumn <= firstContentColumn && span.endColumn >= lastContentColumn);\n}\n\nfunction measureHalstead(root: Parser.SyntaxNode, code: string): HalsteadMetrics {\n const operators = new Map<string, number>();\n const operands = new Map<string, number>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment') {\n return;\n }\n\n if (node.childCount === 0) {\n const text = code.slice(node.startIndex, node.endIndex);\n if (operatorTexts.has(text) || operatorTexts.has(node.type)) {\n incrementCount(operators, text || node.type);\n } else if (operandNodeTypes.has(node.type)) {\n incrementCount(operands, text);\n }\n return;\n }\n\n if (operatorTexts.has(node.type)) {\n incrementCount(operators, node.type);\n }\n\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(root);\n\n const distinctOperators = operators.size;\n const distinctOperands = operands.size;\n const totalOperators = sum(operators.values());\n const totalOperands = sum(operands.values());\n const vocabulary = distinctOperators + distinctOperands;\n const length = totalOperators + totalOperands;\n const volume = vocabulary === 0 ? 0 : length * Math.log2(vocabulary);\n const difficulty = distinctOperands === 0 ? 0 : (distinctOperators / 2) * (totalOperands / distinctOperands);\n const effort = difficulty * volume;\n\n return {\n distinctOperators,\n distinctOperands,\n totalOperators,\n totalOperands,\n vocabulary,\n length,\n volume,\n difficulty,\n effort,\n time: effort / 18,\n bugs: volume / 3000,\n };\n}\n\nfunction findFunctionName(node: Parser.SyntaxNode): string | undefined {\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return nameNode.text;\n }\n\n const parent = node.parent;\n if (!parent) {\n return undefined;\n }\n\n const parentName = parent.childForFieldName('name');\n return parentName?.text;\n}\n\nfunction isCallNode(node: Parser.SyntaxNode): boolean {\n return node.type === 'call_expression' || node.type === 'call';\n}\n\nfunction findCalleeName(node: Parser.SyntaxNode): string | undefined {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n if (!calleeNode) {\n return undefined;\n }\n\n return findRightmostIdentifier(calleeNode);\n}\n\nfunction findRightmostIdentifier(node: Parser.SyntaxNode): string | undefined {\n if (\n node.type === 'identifier' ||\n node.type === 'property_identifier' ||\n node.type === 'field_identifier' ||\n node.type === 'attribute'\n ) {\n return node.text;\n }\n\n for (let index = node.namedChildCount - 1; index >= 0; index -= 1) {\n const child = node.namedChild(index);\n if (!child) {\n continue;\n }\n\n const identifier = findRightmostIdentifier(child);\n if (identifier) {\n return identifier;\n }\n }\n\n return undefined;\n}\n\nfunction isImportNode(node: Parser.SyntaxNode): boolean {\n return (\n node.type === 'import_statement' ||\n node.type === 'import_declaration' ||\n node.type === 'import_from_statement' ||\n node.type === 'import_spec' ||\n node.type === 'import_spec_list'\n );\n}\n\nfunction findImportSource(node: Parser.SyntaxNode): string | undefined {\n const sourceNode = findFirstStringNode(node);\n return sourceNode ? unquote(sourceNode.text) : undefined;\n}\n\nfunction findFirstStringNode(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n if (node.type === 'string' || node.type === 'string_literal' || node.type === 'interpreted_string_literal') {\n return node;\n }\n\n for (const child of node.namedChildren) {\n const stringNode = findFirstStringNode(child);\n if (stringNode) {\n return stringNode;\n }\n }\n\n return undefined;\n}\n\nfunction unquote(value: string): string {\n return value.replaceAll(/^['\"`]|['\"`]$/gu, '');\n}\n\nfunction isExportNode(node: Parser.SyntaxNode): boolean {\n return node.type.startsWith('export') || node.type === 'public_field_definition';\n}\n\nfunction findRecursiveNames(graph: Map<string, Set<string>>): Set<string> {\n const recursiveNames = new Set<string>();\n\n for (const name of graph.keys()) {\n if (canReach(name, name, graph, new Set())) {\n recursiveNames.add(name);\n }\n }\n\n return recursiveNames;\n}\n\nfunction canReach(start: string, target: string, graph: Map<string, Set<string>>, visited: Set<string>): boolean {\n const callees = graph.get(start);\n if (!callees) {\n return false;\n }\n\n for (const callee of callees) {\n if (callee === target) {\n return true;\n }\n\n if (!visited.has(callee)) {\n visited.add(callee);\n if (canReach(callee, target, graph, visited)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction measureMaxCallDepth(graph: Map<string, Set<string>>): number {\n let maxDepth = 0;\n for (const name of graph.keys()) {\n maxDepth = Math.max(maxDepth, measureCallDepth(name, graph, new Set()));\n }\n return maxDepth;\n}\n\nfunction measureCallDepth(name: string, graph: Map<string, Set<string>>, pathNames: Set<string>): number {\n const callees = graph.get(name);\n if (!callees || callees.size === 0 || pathNames.has(name)) {\n return 0;\n }\n\n pathNames.add(name);\n let maxDepth = 0;\n for (const callee of callees) {\n maxDepth = Math.max(maxDepth, 1 + measureCallDepth(callee, graph, new Set(pathNames)));\n }\n return maxDepth;\n}\n\nfunction intersectSets(left: Set<string>, right: Set<string>): Set<string> {\n const intersection = new Set<string>();\n for (const value of left) {\n if (right.has(value)) {\n intersection.add(value);\n }\n }\n return intersection;\n}\n\nfunction calculateMaintainabilityIndex(volume: number, complexity: number, loc: number): number {\n if (loc === 0) {\n return 100;\n }\n\n const raw = 171 - 5.2 * Math.log(Math.max(volume, 1)) - 0.23 * complexity - 16.2 * Math.log(loc);\n return Math.max(0, Math.min(100, (raw * 100) / 171));\n}\n\nfunction incrementCount(map: Map<string, number>, value: string): void {\n map.set(value, (map.get(value) ?? 0) + 1);\n}\n\nfunction maxMetric(functions: FunctionMetrics[], key: 'cyclomaticComplexity' | 'cognitiveComplexity'): number {\n return functions.length === 0 ? 0 : Math.max(...functions.map((fn) => fn[key]));\n}\n\nfunction maxMapValue(map: Map<string, number>): number {\n let maximum = 0;\n for (const value of map.values()) {\n maximum = Math.max(maximum, value);\n }\n return maximum;\n}\n\nfunction sum(values: Iterable<number>): number {\n let total = 0;\n for (const value of values) {\n total += value;\n }\n return total;\n}\n"],"names":["booleanOperators","Set","operatorTexts","operandNodeTypes","TreeMeasurer","registry","createLanguageRegistry","registerLanguage","language","this","set","name","alias","aliases","getSupportedLanguages","values","map","measure","code","options","get","Error","parser","Parser","setLanguage","parserLanguage","root","parse","undefined","bufferSize","length","rootNode","structuralMetrics","functions","analyses","node","complexity","measureComplexity","calls","callees","callCount","visit","type","isCallNode","callee","calleeNode","childForFieldName","namedChild","findRightmostIdentifier","findCalleeName","add","child","namedChildren","collectCalls","findFunctionName","startLine","startPosition","row","endLine","endPosition","cyclomaticComplexity","cognitiveComplexity","identifiers","collectIdentifiers","analyzeFunction","callGraph","functionNames","analysis","filter","fanInByName","Map","fanOutByName","graph","internalCallCount","allCallees","internalCallees","has","size","recursiveNames","keys","canReach","findRecursiveNames","metrics","uniqueCalleeCount","internalEdgeCount","sum","recursiveFunctionCount","maxFanIn","maxMapValue","maxFanOut","maxCallDepth","measureMaxCallDepth","measureCallGraph","fanIn","fanOut","recursive","coupling","measureCoupling","cohesion","measureCohesion","typeComplexity","measureTypeComplexity","measureStructuralMetrics","collectNodes","functionNodeTypes","functionMetrics","globalComplexity","lines","sourceLines","split","commentSpans","spans","push","line","startColumn","column","endColumn","Number","POSITIVE_INFINITY","collectCommentSpans","blank","comment","index","entries","trim","isCommentOnlyLine","total","measureLines","halstead","operators","operands","childCount","text","slice","startIndex","endIndex","incrementCount","children","distinctOperators","distinctOperands","totalOperators","totalOperands","vocabulary","volume","Math","log2","difficulty","effort","time","bugs","measureHalstead","bytes","Buffer","byteLength","classCount","classNodeTypes","functionCount","maxCyclomaticComplexity","maxMetric","maxCognitiveComplexity","nestingDepth","maintainabilityIndex","calculateMaintainabilityIndex","syntaxTree","includeSyntaxTree","toString","defaultMeasurer","measureCode","nesting","decisionNodes","decisionNodeTypes","nestingNodes","nestingNodeTypes","current","currentNesting","isDecision","isNesting","isNamed","isBooleanOperator","childNesting","max","nodeTypes","nodes","importSources","importCount","exportCount","relativeImportCount","isImportNode","source","sourceNode","findFirstStringNode","value","replaceAll","findImportSource","startsWith","isExportNode","importSourceCount","externalImportCount","allIdentifiers","sharedIdentifiers","overlapTotal","pairCount","identifier","leftIndex","rightIndex","left","right","intersection","intersectSets","unionSize","averageFunctionIdentifierOverlap","sharedIdentifierCount","uniqueIdentifierCount","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","unionTypeCount","intersectionTypeCount","conditionalTypeCount","typeAssertionCount","nonNullAssertionCount","satisfiesExpressionCount","lineIndex","relevantSpans","span","firstContentColumn","search","lastContentColumn","trimEnd","some","nameNode","parent","parentName","namedChildCount","stringNode","start","target","visited","maxDepth","measureCallDepth","pathNames","loc","raw","log","min","key","fn","maximum"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"metrics.js","sources":["../src/metrics.ts"],"sourcesContent":["import Parser from 'tree-sitter';\nimport { createLanguageRegistry } from './languages.js';\nimport type {\n CallGraphMetrics,\n CodeMetrics,\n CohesionMetrics,\n CouplingMetrics,\n FunctionMetrics,\n HalsteadMetrics,\n LanguageDefinition,\n LanguageName,\n MeasureOptions,\n TypeComplexityMetrics,\n} from './types.js';\n\nconst booleanOperators = new Set(['&&', '||', 'and', 'or']);\nconst operatorTexts = new Set([\n '+',\n '-',\n '*',\n '/',\n '%',\n '**',\n '=',\n '+=',\n '-=',\n '*=',\n '/=',\n '%=',\n '==',\n '!=',\n '===',\n '!==',\n '<',\n '<=',\n '>',\n '>=',\n '!',\n '~',\n '&',\n '|',\n '^',\n '<<',\n '>>',\n '=>',\n 'return',\n 'throw',\n 'yield',\n 'await',\n 'break',\n 'continue',\n]);\n\nconst operandNodeTypes = new Set([\n 'identifier',\n 'property_identifier',\n 'field_identifier',\n 'type_identifier',\n 'number',\n 'integer',\n 'float',\n 'string',\n 'string_literal',\n 'template_string',\n 'character_literal',\n 'true',\n 'false',\n 'null',\n 'undefined',\n 'nil',\n]);\n\ninterface ComplexityResult {\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n nestingDepth: number;\n}\n\ninterface CommentSpan {\n line: number;\n startColumn: number;\n endColumn: number;\n}\n\ninterface FunctionAnalysis {\n index: number;\n name?: string;\n startLine: number;\n endLine: number;\n returnsJsx: boolean;\n cyclomaticComplexity: number;\n cognitiveComplexity: number;\n callCount: number;\n callees: Set<string>;\n identifiers: Set<string>;\n}\n\ninterface StructuralMetrics {\n callGraph: CallGraphMetrics;\n cohesion: CohesionMetrics;\n coupling: CouplingMetrics;\n functions: FunctionMetrics[];\n typeComplexity: TypeComplexityMetrics;\n}\n\nexport class TreeMeasurer {\n private readonly registry = createLanguageRegistry();\n\n registerLanguage(language: LanguageDefinition): void {\n this.registry.set(language.name, language);\n for (const alias of language.aliases ?? []) {\n this.registry.set(alias, language);\n }\n }\n\n getSupportedLanguages(): LanguageName[] {\n return [...new Set([...this.registry.values()].map((language) => language.name))];\n }\n\n measure(code: string, options: MeasureOptions): CodeMetrics {\n const language = this.registry.get(options.language);\n if (!language) {\n throw new Error(`Unsupported language: ${options.language}`);\n }\n\n const parser = new Parser();\n parser.setLanguage(language.parserLanguage);\n const tree = parser.parse(code, undefined, {\n bufferSize: code.length + 1,\n });\n const root = tree.rootNode;\n const functions = collectNodes(root, new Set(language.functionNodeTypes));\n const structuralMetrics = measureStructuralMetrics(root, functions, language);\n const functionMetrics = structuralMetrics.functions;\n const globalComplexity = measureComplexity(root, language, 0, false);\n const lines = measureLines(code, root);\n const halstead = measureHalstead(root, code);\n\n return {\n language: language.name,\n bytes: Buffer.byteLength(code),\n lines,\n functions: functionMetrics,\n classCount: collectNodes(root, new Set(language.classNodeTypes)).length,\n functionCount: functionMetrics.length,\n cyclomaticComplexity: globalComplexity.cyclomaticComplexity,\n maxCyclomaticComplexity: maxMetric(functionMetrics, 'cyclomaticComplexity'),\n cognitiveComplexity: globalComplexity.cognitiveComplexity,\n maxCognitiveComplexity: maxMetric(functionMetrics, 'cognitiveComplexity'),\n nestingDepth: globalComplexity.nestingDepth,\n callGraph: structuralMetrics.callGraph,\n coupling: structuralMetrics.coupling,\n cohesion: structuralMetrics.cohesion,\n typeComplexity: structuralMetrics.typeComplexity,\n halstead,\n maintainabilityIndex: calculateMaintainabilityIndex(\n halstead.volume,\n globalComplexity.cyclomaticComplexity,\n lines.code\n ),\n syntaxTree: options.includeSyntaxTree ? root.toString() : undefined,\n };\n }\n}\n\nexport const defaultMeasurer = new TreeMeasurer();\n\nexport function measureCode(code: string, options: MeasureOptions): CodeMetrics {\n return defaultMeasurer.measure(code, options);\n}\n\nfunction measureStructuralMetrics(\n root: Parser.SyntaxNode,\n functions: Parser.SyntaxNode[],\n language: LanguageDefinition\n): StructuralMetrics {\n const analyses = functions.map((node, index) => analyzeFunction(node, language, index));\n const callGraph = measureCallGraph(analyses);\n const functionsWithGraph = analyses.map((analysis) => ({\n name: analysis.name,\n startLine: analysis.startLine,\n endLine: analysis.endLine,\n returnsJsx: analysis.returnsJsx,\n cyclomaticComplexity: analysis.cyclomaticComplexity,\n cognitiveComplexity: analysis.cognitiveComplexity,\n callCount: analysis.callCount,\n uniqueCalleeCount: analysis.callees.size,\n fanIn: callGraph.fanInByIndex.get(analysis.index) ?? 0,\n fanOut: callGraph.fanOutByIndex.get(analysis.index) ?? 0,\n recursive: callGraph.recursiveIndexes.has(analysis.index),\n }));\n\n return {\n functions: functionsWithGraph,\n callGraph: callGraph.metrics,\n coupling: measureCoupling(root, language),\n cohesion: measureCohesion(analyses),\n typeComplexity: measureTypeComplexity(root),\n };\n}\n\nfunction analyzeFunction(node: Parser.SyntaxNode, language: LanguageDefinition, index: number): FunctionAnalysis {\n const complexity = measureComplexity(node, language, 0, true);\n const calls = collectCalls(node, language);\n return {\n index,\n name: findFunctionName(node),\n startLine: node.startPosition.row + 1,\n endLine: node.endPosition.row + 1,\n returnsJsx: returnsJsx(node, language),\n cyclomaticComplexity: complexity.cyclomaticComplexity,\n cognitiveComplexity: complexity.cognitiveComplexity,\n callCount: calls.callCount,\n callees: calls.callees,\n identifiers: collectIdentifiers(node),\n };\n}\n\nfunction measureCallGraph(analyses: FunctionAnalysis[]): {\n fanInByIndex: Map<number, number>;\n fanOutByIndex: Map<number, number>;\n metrics: CallGraphMetrics;\n recursiveIndexes: Set<number>;\n} {\n const indexesByName = mapUniqueFunctionIndexesByName(analyses);\n const functionNames = new Set(indexesByName.keys());\n const fanInByIndex = new Map<number, number>();\n const fanOutByIndex = new Map<number, number>();\n const graph = new Map<number, Set<number>>();\n let callCount = 0;\n let internalCallCount = 0;\n const allCallees = new Set<string>();\n\n for (const analysis of analyses) {\n callCount += analysis.callCount;\n for (const callee of analysis.callees) {\n allCallees.add(callee);\n }\n\n const internalCalleeNames = new Set([...analysis.callees].filter((callee) => functionNames.has(callee)));\n const internalCalleeIndexes = new Set<number>();\n for (const callee of internalCalleeNames) {\n const calleeIndex = indexesByName.get(callee);\n if (calleeIndex !== undefined) {\n internalCalleeIndexes.add(calleeIndex);\n }\n }\n\n graph.set(analysis.index, internalCalleeIndexes);\n fanOutByIndex.set(analysis.index, internalCalleeNames.size);\n internalCallCount += internalCalleeNames.size;\n for (const calleeIndex of internalCalleeIndexes) {\n fanInByIndex.set(calleeIndex, (fanInByIndex.get(calleeIndex) ?? 0) + 1);\n }\n }\n\n const recursiveIndexes = findRecursiveIndexes(graph);\n\n return {\n fanInByIndex,\n fanOutByIndex,\n recursiveIndexes,\n metrics: {\n callCount,\n uniqueCalleeCount: allCallees.size,\n internalCallCount,\n internalEdgeCount: sum([...graph.values()].map((callees) => callees.size)),\n recursiveFunctionCount: recursiveIndexes.size,\n maxFanIn: maxMapValue(fanInByIndex),\n maxFanOut: maxMapValue(fanOutByIndex),\n maxCallDepth: measureMaxCallDepth(graph),\n },\n };\n}\n\nfunction mapUniqueFunctionIndexesByName(analyses: FunctionAnalysis[]): Map<string, number> {\n const indexesByName = new Map<string, number | undefined>();\n for (const analysis of analyses) {\n if (!analysis.name) {\n continue;\n }\n\n indexesByName.set(analysis.name, indexesByName.has(analysis.name) ? undefined : analysis.index);\n }\n return new Map([...indexesByName.entries()].filter((entry): entry is [string, number] => entry[1] !== undefined));\n}\n\nfunction measureComplexity(\n node: Parser.SyntaxNode,\n language: LanguageDefinition,\n nesting: number,\n stopAtNestedFunctions: boolean\n): ComplexityResult {\n let cyclomaticComplexity = 1;\n let cognitiveComplexity = 0;\n let nestingDepth = nesting;\n const functionNodes = new Set(language.functionNodeTypes);\n const decisionNodes = new Set(language.decisionNodeTypes);\n const nestingNodes = new Set(language.nestingNodeTypes);\n\n function visit(current: Parser.SyntaxNode, currentNesting: number, insideRoot: boolean): void {\n if (stopAtNestedFunctions && !insideRoot && functionNodes.has(current.type)) {\n return;\n }\n\n const isDecision = decisionNodes.has(current.type);\n const isNesting = nestingNodes.has(current.type);\n\n if (isDecision) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1 + currentNesting;\n }\n\n if (isBooleanOperator(current)) {\n cyclomaticComplexity += 1;\n cognitiveComplexity += 1;\n }\n\n const childNesting = isNesting ? currentNesting + 1 : currentNesting;\n nestingDepth = Math.max(nestingDepth, childNesting);\n\n for (const child of current.children) {\n visit(child, childNesting, false);\n }\n }\n\n for (const child of node.children) {\n visit(child, nesting, false);\n }\n\n return { cyclomaticComplexity, cognitiveComplexity, nestingDepth };\n}\n\nfunction isBooleanOperator(node: Parser.SyntaxNode): boolean {\n if (node.isNamed) {\n return false;\n }\n\n return booleanOperators.has(node.text);\n}\n\nfunction collectCalls(\n root: Parser.SyntaxNode,\n language: LanguageDefinition\n): { callCount: number; callees: Set<string> } {\n const callees = new Set<string>();\n const functionNodeTypes = new Set(language.functionNodeTypes);\n let callCount = 0;\n\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): void {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return;\n }\n\n if (isCallNode(node)) {\n callCount += 1;\n const callee = findCalleeName(node);\n if (callee) {\n callees.add(callee);\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child, false);\n }\n }\n\n visit(root, true);\n return { callCount, callees };\n}\n\nfunction collectIdentifiers(root: Parser.SyntaxNode): Set<string> {\n const identifiers = new Set<string>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'identifier' || node.type === 'property_identifier' || node.type === 'field_identifier') {\n identifiers.add(node.text);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return identifiers;\n}\n\nfunction collectNodes(root: Parser.SyntaxNode, nodeTypes: Set<string>): Parser.SyntaxNode[] {\n const nodes: Parser.SyntaxNode[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (nodeTypes.has(node.type)) {\n nodes.push(node);\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return nodes;\n}\n\nfunction returnsJsx(root: Parser.SyntaxNode, language: LanguageDefinition): boolean {\n const functionNodeTypes = new Set(language.functionNodeTypes);\n\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (node.type === 'return_statement') {\n return containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes);\n }\n\n if (\n root.type === 'arrow_function' &&\n node === getArrowFunctionBody(root) &&\n node.type !== 'statement_block' &&\n !functionNodeTypes.has(node.type)\n ) {\n return containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes);\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction getArrowFunctionBody(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n return node.childForFieldName('body') ?? node.namedChild(node.namedChildCount - 1) ?? undefined;\n}\n\nfunction containsJsxExpression(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n return containsNode(\n root,\n functionNodeTypes,\n (node) => node.type.startsWith('jsx_') || isJsxMappingCall(node, functionNodeTypes)\n );\n}\n\nfunction containsReactCreateElementCall(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n return containsNode(root, functionNodeTypes, isReactCreateElementCall);\n}\n\nfunction containsNode(\n root: Parser.SyntaxNode,\n functionNodeTypes: Set<string>,\n predicate: (node: Parser.SyntaxNode) => boolean\n): boolean {\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (predicate(node)) {\n return true;\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction isJsxMappingCall(node: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n if (!isCallNode(node) || !isArrayMappingCallee(node.childForFieldName('function') ?? node.namedChild(0))) {\n return false;\n }\n\n return node.namedChildren.some((child) => containsReturnedJsxFunction(child, functionNodeTypes));\n}\n\nfunction isArrayMappingCallee(node: Parser.SyntaxNode | null): boolean {\n if (!node) {\n return false;\n }\n\n const calleeName = findRightmostIdentifier(node);\n return calleeName === 'map' || calleeName === 'flatMap';\n}\n\nfunction containsReturnedJsxFunction(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n if (functionNodeTypes.has(root.type)) {\n return returnsJsxFromFunctionNode(root, functionNodeTypes);\n }\n\n return root.namedChildren.some((child) => containsReturnedJsxFunction(child, functionNodeTypes));\n}\n\nfunction returnsJsxFromFunctionNode(root: Parser.SyntaxNode, functionNodeTypes: Set<string>): boolean {\n const body = root.type === 'arrow_function' ? getArrowFunctionBody(root) : undefined;\n if (body && body.type !== 'statement_block' && !functionNodeTypes.has(body.type)) {\n return containsJsxExpression(body, functionNodeTypes) || containsReactCreateElementCall(body, functionNodeTypes);\n }\n\n return containsOwnReturnNode(\n root,\n functionNodeTypes,\n (node) => containsJsxExpression(node, functionNodeTypes) || containsReactCreateElementCall(node, functionNodeTypes)\n );\n}\n\nfunction containsOwnReturnNode(\n root: Parser.SyntaxNode,\n functionNodeTypes: Set<string>,\n predicate: (node: Parser.SyntaxNode) => boolean\n): boolean {\n function visit(node: Parser.SyntaxNode, insideRoot: boolean): boolean {\n if (!insideRoot && functionNodeTypes.has(node.type)) {\n return false;\n }\n\n if (node.type === 'return_statement' && predicate(node)) {\n return true;\n }\n\n for (const child of node.namedChildren) {\n if (visit(child, false)) {\n return true;\n }\n }\n return false;\n }\n\n return visit(root, true);\n}\n\nfunction measureCoupling(root: Parser.SyntaxNode, language: LanguageDefinition): CouplingMetrics {\n const importSources = new Set<string>();\n let importCount = 0;\n let exportCount = 0;\n\n function visit(node: Parser.SyntaxNode): void {\n if (isImportNode(node)) {\n importCount += 1;\n for (const source of findImportSources(node, language)) {\n importSources.add(source);\n }\n }\n\n if (isExportNode(node)) {\n exportCount += 1;\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n\n const relativeImportCount = [...importSources].filter(isRelativeImportSource).length;\n\n return {\n importCount,\n importSourceCount: importSources.size,\n relativeImportCount,\n externalImportCount: importSources.size - relativeImportCount,\n exportCount,\n };\n}\n\nfunction measureCohesion(analyses: FunctionAnalysis[]): CohesionMetrics {\n const allIdentifiers = new Set<string>();\n const sharedIdentifiers = new Set<string>();\n let overlapTotal = 0;\n let pairCount = 0;\n\n for (const analysis of analyses) {\n for (const identifier of analysis.identifiers) {\n allIdentifiers.add(identifier);\n }\n }\n\n for (let leftIndex = 0; leftIndex < analyses.length; leftIndex += 1) {\n for (let rightIndex = leftIndex + 1; rightIndex < analyses.length; rightIndex += 1) {\n const left = analyses[leftIndex];\n const right = analyses[rightIndex];\n if (!left || !right) {\n continue;\n }\n\n const intersection = intersectSets(left.identifiers, right.identifiers);\n const unionSize = new Set([...left.identifiers, ...right.identifiers]).size;\n for (const identifier of intersection) {\n sharedIdentifiers.add(identifier);\n }\n overlapTotal += unionSize === 0 ? 0 : intersection.size / unionSize;\n pairCount += 1;\n }\n }\n\n return {\n averageFunctionIdentifierOverlap: pairCount === 0 ? 1 : overlapTotal / pairCount,\n sharedIdentifierCount: sharedIdentifiers.size,\n uniqueIdentifierCount: allIdentifiers.size,\n };\n}\n\nfunction measureTypeComplexity(root: Parser.SyntaxNode): TypeComplexityMetrics {\n const metrics: TypeComplexityMetrics = {\n typeAnnotationCount: 0,\n typeAliasCount: 0,\n interfaceCount: 0,\n genericParameterCount: 0,\n unionTypeCount: 0,\n intersectionTypeCount: 0,\n conditionalTypeCount: 0,\n typeAssertionCount: 0,\n nonNullAssertionCount: 0,\n satisfiesExpressionCount: 0,\n };\n\n function visit(node: Parser.SyntaxNode): void {\n switch (node.type) {\n case 'type_annotation': {\n metrics.typeAnnotationCount += 1;\n break;\n }\n case 'type_alias_declaration': {\n metrics.typeAliasCount += 1;\n break;\n }\n case 'interface_declaration': {\n metrics.interfaceCount += 1;\n break;\n }\n case 'type_parameters':\n case 'type_parameter': {\n metrics.genericParameterCount += node.type === 'type_parameter' ? 1 : 0;\n break;\n }\n case 'union_type': {\n metrics.unionTypeCount += 1;\n break;\n }\n case 'intersection_type': {\n metrics.intersectionTypeCount += 1;\n break;\n }\n case 'conditional_type': {\n metrics.conditionalTypeCount += 1;\n break;\n }\n case 'as_expression':\n case 'type_assertion': {\n metrics.typeAssertionCount += 1;\n break;\n }\n case 'non_null_expression': {\n metrics.nonNullAssertionCount += 1;\n break;\n }\n case 'satisfies_expression': {\n metrics.satisfiesExpressionCount += 1;\n break;\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return metrics;\n}\n\nfunction measureLines(code: string, root: Parser.SyntaxNode): CodeMetrics['lines'] {\n const sourceLines = code.length === 0 ? [] : code.split(/\\r\\n|\\n|\\r/);\n const commentSpans = collectCommentSpans(root);\n let blank = 0;\n let comment = 0;\n\n for (const [index, line] of sourceLines.entries()) {\n if (line.trim() === '') {\n blank += 1;\n continue;\n }\n\n if (isCommentOnlyLine(line, index, commentSpans)) {\n comment += 1;\n }\n }\n\n return {\n total: sourceLines.length,\n code: sourceLines.length - blank - comment,\n comment,\n blank,\n };\n}\n\nfunction collectCommentSpans(root: Parser.SyntaxNode): CommentSpan[] {\n const spans: CommentSpan[] = [];\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment' || node.type === 'line_comment' || node.type === 'block_comment') {\n for (let row = node.startPosition.row; row <= node.endPosition.row; row += 1) {\n spans.push({\n line: row,\n startColumn: row === node.startPosition.row ? node.startPosition.column : 0,\n endColumn: row === node.endPosition.row ? node.endPosition.column : Number.POSITIVE_INFINITY,\n });\n }\n }\n\n for (const child of node.namedChildren) {\n visit(child);\n }\n }\n\n visit(root);\n return spans;\n}\n\nfunction isCommentOnlyLine(line: string, lineIndex: number, spans: CommentSpan[]): boolean {\n const relevantSpans = spans.filter((span) => span.line === lineIndex);\n if (relevantSpans.length === 0) {\n return false;\n }\n\n const firstContentColumn = line.search(/\\S/);\n const lastContentColumn = line.trimEnd().length;\n\n return relevantSpans.some((span) => span.startColumn <= firstContentColumn && span.endColumn >= lastContentColumn);\n}\n\nfunction measureHalstead(root: Parser.SyntaxNode, code: string): HalsteadMetrics {\n const operators = new Map<string, number>();\n const operands = new Map<string, number>();\n\n function visit(node: Parser.SyntaxNode): void {\n if (node.type === 'comment') {\n return;\n }\n\n if (node.childCount === 0) {\n const text = code.slice(node.startIndex, node.endIndex);\n if (operatorTexts.has(text) || operatorTexts.has(node.type)) {\n incrementCount(operators, text || node.type);\n } else if (operandNodeTypes.has(node.type)) {\n incrementCount(operands, text);\n }\n return;\n }\n\n if (operatorTexts.has(node.type)) {\n incrementCount(operators, node.type);\n }\n\n for (const child of node.children) {\n visit(child);\n }\n }\n\n visit(root);\n\n const distinctOperators = operators.size;\n const distinctOperands = operands.size;\n const totalOperators = sum(operators.values());\n const totalOperands = sum(operands.values());\n const vocabulary = distinctOperators + distinctOperands;\n const length = totalOperators + totalOperands;\n const volume = vocabulary === 0 ? 0 : length * Math.log2(vocabulary);\n const difficulty = distinctOperands === 0 ? 0 : (distinctOperators / 2) * (totalOperands / distinctOperands);\n const effort = difficulty * volume;\n\n return {\n distinctOperators,\n distinctOperands,\n totalOperators,\n totalOperands,\n vocabulary,\n length,\n volume,\n difficulty,\n effort,\n time: effort / 18,\n bugs: volume / 3000,\n };\n}\n\nfunction findFunctionName(node: Parser.SyntaxNode): string | undefined {\n const wrappedName = findWrappedComponentName(node);\n if (wrappedName) {\n return wrappedName;\n }\n\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return nameNode.text;\n }\n\n const parent = node.parent;\n if (!parent) {\n return undefined;\n }\n\n const parentName = parent.childForFieldName('name');\n return parentName?.text;\n}\n\nfunction findWrappedComponentName(node: Parser.SyntaxNode): string | undefined {\n let current: Parser.SyntaxNode | undefined = node;\n while (current) {\n const argumentsNode: Parser.SyntaxNode | null = current.parent;\n const callNode: Parser.SyntaxNode | null | undefined = argumentsNode?.parent;\n if (argumentsNode?.type !== 'arguments' || callNode?.type !== 'call_expression') {\n return undefined;\n }\n\n if (!isReactComponentWrapperCall(callNode)) {\n return undefined;\n }\n\n const declaratorNode = callNode.parent;\n if (declaratorNode?.type === 'variable_declarator') {\n return declaratorNode.childForFieldName('name')?.text;\n }\n\n current = callNode;\n }\n\n return undefined;\n}\n\nfunction isReactComponentWrapperCall(node: Parser.SyntaxNode): boolean {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n return (\n calleeNode?.text === 'memo' ||\n calleeNode?.text === 'React.memo' ||\n calleeNode?.text === 'forwardRef' ||\n calleeNode?.text === 'React.forwardRef'\n );\n}\n\nfunction isCallNode(node: Parser.SyntaxNode): boolean {\n return node.type === 'call_expression' || node.type === 'call';\n}\n\nfunction findCalleeName(node: Parser.SyntaxNode): string | undefined {\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n if (!calleeNode) {\n return undefined;\n }\n\n return findRightmostIdentifier(calleeNode);\n}\n\nfunction findRightmostIdentifier(node: Parser.SyntaxNode): string | undefined {\n if (\n node.type === 'identifier' ||\n node.type === 'property_identifier' ||\n node.type === 'field_identifier' ||\n node.type === 'attribute'\n ) {\n return node.text;\n }\n\n for (let index = node.namedChildCount - 1; index >= 0; index -= 1) {\n const child = node.namedChild(index);\n if (!child) {\n continue;\n }\n\n const identifier = findRightmostIdentifier(child);\n if (identifier) {\n return identifier;\n }\n }\n\n return undefined;\n}\n\nfunction isReactCreateElementCall(node: Parser.SyntaxNode): boolean {\n if (!isCallNode(node)) {\n return false;\n }\n\n const calleeNode = node.childForFieldName('function') ?? node.namedChild(0);\n return calleeNode?.text === 'React.createElement' || calleeNode?.text === 'createElement';\n}\n\nfunction isImportNode(node: Parser.SyntaxNode): boolean {\n return (\n node.type === 'import_statement' ||\n node.type === 'import_declaration' ||\n node.type === 'import_from_statement' ||\n node.type === 'import_spec' ||\n node.type === 'import_spec_list'\n );\n}\n\nfunction findImportSources(node: Parser.SyntaxNode, language: LanguageDefinition): string[] {\n if (language.name === 'python') {\n const pythonSources = findPythonImportSources(node);\n if (pythonSources.length > 0) {\n return pythonSources;\n }\n }\n\n const sourceNode = node.childForFieldName('source') ?? findFirstStringNode(node);\n return sourceNode ? [unquote(sourceNode.text)] : [];\n}\n\nfunction isRelativeImportSource(source: string): boolean {\n return source.startsWith('.') || source.startsWith('/');\n}\n\nfunction findPythonImportSources(node: Parser.SyntaxNode): string[] {\n if (node.type === 'import_from_statement') {\n const moduleNode = node.childForFieldName('module_name');\n return moduleNode ? [normalizeImportSource(moduleNode.text)] : [];\n }\n\n if (node.type !== 'import_statement') {\n return [];\n }\n\n return node.namedChildren\n .map((child) => findPythonImportedModuleName(child))\n .filter((source) => source !== undefined);\n}\n\nfunction findPythonImportedModuleName(node: Parser.SyntaxNode): string | undefined {\n if (node.type === 'dotted_name' || node.type === 'relative_import') {\n return normalizeImportSource(node.text);\n }\n\n const nameNode = node.childForFieldName('name');\n if (nameNode) {\n return normalizeImportSource(nameNode.text);\n }\n\n for (const child of node.namedChildren) {\n const source = findPythonImportedModuleName(child);\n if (source) {\n return source;\n }\n }\n\n return undefined;\n}\n\nfunction normalizeImportSource(source: string): string {\n return source.replaceAll(/\\s+/gu, '');\n}\n\nfunction findFirstStringNode(node: Parser.SyntaxNode): Parser.SyntaxNode | undefined {\n if (node.type === 'string' || node.type === 'string_literal' || node.type === 'interpreted_string_literal') {\n return node;\n }\n\n for (const child of node.namedChildren) {\n const stringNode = findFirstStringNode(child);\n if (stringNode) {\n return stringNode;\n }\n }\n\n return undefined;\n}\n\nfunction unquote(value: string): string {\n return value.replaceAll(/^['\"`]|['\"`]$/gu, '');\n}\n\nfunction isExportNode(node: Parser.SyntaxNode): boolean {\n return node.type.startsWith('export') || node.type === 'public_field_definition';\n}\n\nfunction findRecursiveIndexes(graph: Map<number, Set<number>>): Set<number> {\n const recursiveIndexes = new Set<number>();\n\n for (const index of graph.keys()) {\n if (canReach(index, index, graph, new Set())) {\n recursiveIndexes.add(index);\n }\n }\n\n return recursiveIndexes;\n}\n\nfunction canReach(start: number, target: number, graph: Map<number, Set<number>>, visited: Set<number>): boolean {\n const callees = graph.get(start);\n if (!callees) {\n return false;\n }\n\n for (const callee of callees) {\n if (callee === target) {\n return true;\n }\n\n if (!visited.has(callee)) {\n visited.add(callee);\n if (canReach(callee, target, graph, visited)) {\n return true;\n }\n }\n }\n\n return false;\n}\n\nfunction measureMaxCallDepth(graph: Map<number, Set<number>>): number {\n let maxDepth = 0;\n for (const index of graph.keys()) {\n maxDepth = Math.max(maxDepth, measureCallDepth(index, graph, new Set()));\n }\n return maxDepth;\n}\n\nfunction measureCallDepth(index: number, graph: Map<number, Set<number>>, pathIndexes: Set<number>): number {\n const callees = graph.get(index);\n if (!callees || callees.size === 0 || pathIndexes.has(index)) {\n return 0;\n }\n\n pathIndexes.add(index);\n let maxDepth = 0;\n for (const callee of callees) {\n maxDepth = Math.max(maxDepth, 1 + measureCallDepth(callee, graph, new Set(pathIndexes)));\n }\n return maxDepth;\n}\n\nfunction intersectSets(left: Set<string>, right: Set<string>): Set<string> {\n const intersection = new Set<string>();\n for (const value of left) {\n if (right.has(value)) {\n intersection.add(value);\n }\n }\n return intersection;\n}\n\nfunction calculateMaintainabilityIndex(volume: number, complexity: number, loc: number): number {\n if (loc === 0) {\n return 100;\n }\n\n const raw = 171 - 5.2 * Math.log(Math.max(volume, 1)) - 0.23 * complexity - 16.2 * Math.log(loc);\n return Math.max(0, Math.min(100, (raw * 100) / 171));\n}\n\nfunction incrementCount(map: Map<string, number>, value: string): void {\n map.set(value, (map.get(value) ?? 0) + 1);\n}\n\nfunction maxMetric(functions: FunctionMetrics[], key: 'cyclomaticComplexity' | 'cognitiveComplexity'): number {\n return functions.length === 0 ? 0 : Math.max(...functions.map((fn) => fn[key]));\n}\n\nfunction maxMapValue(map: Map<unknown, number>): number {\n let maximum = 0;\n for (const value of map.values()) {\n maximum = Math.max(maximum, value);\n }\n return maximum;\n}\n\nfunction sum(values: Iterable<number>): number {\n let total = 0;\n for (const value of values) {\n total += value;\n }\n return total;\n}\n"],"names":["booleanOperators","Set","operatorTexts","operandNodeTypes","TreeMeasurer","registry","createLanguageRegistry","registerLanguage","language","this","set","name","alias","aliases","getSupportedLanguages","values","map","measure","code","options","get","Error","parser","Parser","setLanguage","parserLanguage","root","parse","undefined","bufferSize","length","rootNode","structuralMetrics","functions","analyses","node","index","complexity","measureComplexity","calls","callees","functionNodeTypes","callCount","visit","insideRoot","has","type","isCallNode","callee","calleeNode","childForFieldName","namedChild","findRightmostIdentifier","findCalleeName","add","child","namedChildren","collectCalls","findFunctionName","startLine","startPosition","row","endLine","endPosition","returnsJsx","cyclomaticComplexity","cognitiveComplexity","identifiers","collectIdentifiers","analyzeFunction","callGraph","indexesByName","Map","analysis","entries","filter","entry","mapUniqueFunctionIndexesByName","functionNames","keys","fanInByIndex","fanOutByIndex","graph","internalCallCount","allCallees","internalCalleeNames","internalCalleeIndexes","calleeIndex","size","recursiveIndexes","canReach","findRecursiveIndexes","metrics","uniqueCalleeCount","internalEdgeCount","sum","recursiveFunctionCount","maxFanIn","maxMapValue","maxFanOut","maxCallDepth","measureMaxCallDepth","measureCallGraph","fanIn","fanOut","recursive","coupling","measureCoupling","cohesion","measureCohesion","typeComplexity","measureTypeComplexity","measureStructuralMetrics","collectNodes","functionMetrics","globalComplexity","lines","sourceLines","split","commentSpans","spans","push","line","startColumn","column","endColumn","Number","POSITIVE_INFINITY","collectCommentSpans","blank","comment","trim","isCommentOnlyLine","total","measureLines","halstead","operators","operands","childCount","text","slice","startIndex","endIndex","incrementCount","children","distinctOperators","distinctOperands","totalOperators","totalOperands","vocabulary","volume","Math","log2","difficulty","effort","time","bugs","measureHalstead","bytes","Buffer","byteLength","classCount","classNodeTypes","functionCount","maxCyclomaticComplexity","maxMetric","maxCognitiveComplexity","nestingDepth","maintainabilityIndex","calculateMaintainabilityIndex","syntaxTree","includeSyntaxTree","toString","defaultMeasurer","measureCode","nesting","stopAtNestedFunctions","functionNodes","decisionNodes","decisionNodeTypes","nestingNodes","nestingNodeTypes","current","currentNesting","isDecision","isNesting","isNamed","isBooleanOperator","childNesting","max","nodeTypes","nodes","containsJsxExpression","containsReactCreateElementCall","getArrowFunctionBody","namedChildCount","containsNode","startsWith","calleeName","isArrayMappingCallee","some","containsReturnedJsxFunction","isJsxMappingCall","isReactCreateElementCall","predicate","body","containsOwnReturnNode","returnsJsxFromFunctionNode","importSources","importCount","exportCount","isImportNode","source","pythonSources","moduleNode","normalizeImportSource","findPythonImportedModuleName","findPythonImportSources","sourceNode","findFirstStringNode","unquote","findImportSources","isExportNode","relativeImportCount","isRelativeImportSource","importSourceCount","externalImportCount","allIdentifiers","sharedIdentifiers","overlapTotal","pairCount","identifier","leftIndex","rightIndex","left","right","intersection","intersectSets","unionSize","averageFunctionIdentifierOverlap","sharedIdentifierCount","uniqueIdentifierCount","typeAnnotationCount","typeAliasCount","interfaceCount","genericParameterCount","unionTypeCount","intersectionTypeCount","conditionalTypeCount","typeAssertionCount","nonNullAssertionCount","satisfiesExpressionCount","lineIndex","relevantSpans","span","firstContentColumn","search","lastContentColumn","trimEnd","wrappedName","argumentsNode","parent","callNode","isReactComponentWrapperCall","declaratorNode","findWrappedComponentName","nameNode","parentName","replaceAll","stringNode","value","start","target","visited","maxDepth","measureCallDepth","pathIndexes","loc","raw","log","min","key","fn","maximum"],"mappings":"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"}
|
package/dist/types.d.ts
CHANGED