code-gauge 1.14.0 → 3.0.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.
Files changed (54) hide show
  1. package/README.md +97 -108
  2. package/dist/cli.cjs +3 -3
  3. package/dist/cli.cjs.map +1 -1
  4. package/dist/cli.js +3 -3
  5. package/dist/cli.js.map +1 -1
  6. package/dist/cliConfig.cjs +1 -1
  7. package/dist/cliConfig.cjs.map +1 -1
  8. package/dist/cliConfig.d.ts +16 -84
  9. package/dist/cliConfig.js +1 -1
  10. package/dist/cliConfig.js.map +1 -1
  11. package/dist/crossFileDuplication.cjs +1 -1
  12. package/dist/crossFileDuplication.cjs.map +1 -1
  13. package/dist/crossFileDuplication.d.ts +24 -10
  14. package/dist/crossFileDuplication.js +1 -1
  15. package/dist/crossFileDuplication.js.map +1 -1
  16. package/dist/duplication.cjs +1 -1
  17. package/dist/duplication.cjs.map +1 -1
  18. package/dist/duplication.d.ts +153 -4
  19. package/dist/duplication.js +1 -1
  20. package/dist/duplication.js.map +1 -1
  21. package/dist/index.cjs +1 -1
  22. package/dist/index.d.ts +3 -3
  23. package/dist/index.js +1 -1
  24. package/dist/languages.cjs +1 -1
  25. package/dist/languages.cjs.map +1 -1
  26. package/dist/languages.js +1 -1
  27. package/dist/languages.js.map +1 -1
  28. package/dist/metrics.cjs +1 -2
  29. package/dist/metrics.cjs.map +1 -1
  30. package/dist/metrics.d.ts +9 -4
  31. package/dist/metrics.js +1 -2
  32. package/dist/metrics.js.map +1 -1
  33. package/dist/nativeMetrics.cjs +1 -1
  34. package/dist/nativeMetrics.cjs.map +1 -1
  35. package/dist/nativeMetrics.d.ts +2 -2
  36. package/dist/nativeMetrics.js +1 -1
  37. package/dist/nativeMetrics.js.map +1 -1
  38. package/dist/ncss.cjs +1 -1
  39. package/dist/ncss.cjs.map +1 -1
  40. package/dist/ncss.d.ts +11 -5
  41. package/dist/ncss.js +1 -1
  42. package/dist/ncss.js.map +1 -1
  43. package/dist/types.d.ts +14 -79
  44. package/package.json +7 -5
  45. package/dist/architectureMetrics.cjs +0 -2
  46. package/dist/architectureMetrics.cjs.map +0 -1
  47. package/dist/architectureMetrics.d.ts +0 -41
  48. package/dist/architectureMetrics.js +0 -2
  49. package/dist/architectureMetrics.js.map +0 -1
  50. package/dist/typescriptProject.cjs +0 -3
  51. package/dist/typescriptProject.cjs.map +0 -1
  52. package/dist/typescriptProject.d.ts +0 -24
  53. package/dist/typescriptProject.js +0 -3
  54. package/dist/typescriptProject.js.map +0 -1
@@ -1,41 +0,0 @@
1
- import type { CodeMetrics } from './types.js';
2
- export interface ArchitectureSourceFile {
3
- file: string;
4
- metrics: CodeMetrics;
5
- }
6
- export interface ArchitectureFileMetrics {
7
- directLocalDependencyCount: number;
8
- duplicateSymbolGroupCount: number;
9
- file: string;
10
- structuralBreadthScore: number;
11
- structuralCoordination: StructuralCoordinationMetrics;
12
- structuralFeatureGroups: string[];
13
- transitiveLocalDependencyCount: number;
14
- }
15
- export interface ArchitectureMetrics {
16
- duplicateSymbolGroups: DuplicateSymbolGroup[];
17
- files: ArchitectureFileMetrics[];
18
- maxDirectLocalDependencyCount: number;
19
- maxDuplicateSymbolGroupCount: number;
20
- maxStructuralBreadthScore: number;
21
- maxStructuralCoordinationScore: number;
22
- maxTransitiveLocalDependencyCount: number;
23
- }
24
- export interface DuplicateSymbolGroup {
25
- declarations: DuplicateSymbolDeclaration[];
26
- files: string[];
27
- name: string;
28
- }
29
- export interface DuplicateSymbolDeclaration {
30
- file: string;
31
- line: number;
32
- }
33
- export interface StructuralCoordinationMetrics {
34
- asyncBoundaryCount: number;
35
- branchingScore: number;
36
- exceptionHandlingCount: number;
37
- moduleInteractionScore: number;
38
- score: number;
39
- stateMutationScore: number;
40
- }
41
- export declare function measureArchitecture(files: readonly ArchitectureSourceFile[], displayRoot: string): ArchitectureMetrics;
@@ -1,2 +0,0 @@
1
- import e from"node:path";function t(t,r){let i=t.map(t=>({...t,relativeFile:e.relative(r,t.file)||e.basename(t.file)})),a=n(i),o=y(i),s=C(o),c=i.map(e=>{let t=a.dependenciesByFile.get(e.relativeFile)??new Set,n=a.nonRelativeLocalImportCountByFile.get(e.relativeFile)??0,r=w(e.metrics),i=T(e.metrics,t.size,n);return{file:e.relativeFile,directLocalDependencyCount:t.size,duplicateSymbolGroupCount:s.get(e.relativeFile)??0,structuralBreadthScore:i.length,structuralCoordination:r,structuralFeatureGroups:i,transitiveLocalDependencyCount:v(e.relativeFile,a.dependenciesByFile)}});return{duplicateSymbolGroups:o,files:c,maxDirectLocalDependencyCount:O(c,`directLocalDependencyCount`),maxDuplicateSymbolGroupCount:O(c,`duplicateSymbolGroupCount`),maxStructuralBreadthScore:O(c,`structuralBreadthScore`),maxStructuralCoordinationScore:Math.max(0,...c.map(e=>e.structuralCoordination.score)),maxTransitiveLocalDependencyCount:O(c,`transitiveLocalDependencyCount`)}}function n(e){let t=new Set(e.map(e=>e.relativeFile)),n=p(e),r=new Map,i=new Map;for(let a of e){let e=new Set,s=0;for(let r of a.metrics.module.importSources){let i=o(a.relativeFile,r,t,n);i&&i!==a.relativeFile&&(e.add(i),h(a.relativeFile)===`.java`&&!r.startsWith(`.`)&&(s+=1))}r.set(a.relativeFile,e),i.set(a.relativeFile,s)}return{dependenciesByFile:r,nonRelativeLocalImportCountByFile:i}}const r=new Set([`.js`,`.jsx`,`.ts`,`.tsx`,`.mjs`,`.cjs`,`.mts`,`.cts`]),i=new Set([`.c`,`.c++`,`.cc`,`.cp`,`.cpp`,`.cxx`,`.h`,`.hh`,`.hpp`,`.hxx`,`.tcc`]),a=new Map([[`.js`,[`.ts`,`.tsx`]],[`.jsx`,[`.tsx`]],[`.mjs`,[`.mts`]],[`.cjs`,[`.cts`]]]);function o(t,n,i,o){let c=h(t);if(!n.startsWith(`.`))return c===`.rs`&&/^(?:crate|self|super)(?:::|$)/u.test(n)?l(t,n,i):c===`.java`?m(n,o):void 0;let u=g(t,n);if(r.has(c))for(let t of u){let n=a.get(e.extname(t));if(!n)continue;let r=t.slice(0,-e.extname(t).length);for(let e of n)if(i.has(`${r}${e}`))return`${r}${e}`}for(let e of u)if(i.has(e))return e;let{extensions:d,indexFiles:f}=s(t);for(let t of u){for(let e of d)if(i.has(`${t}${e}`))return`${t}${e}`;for(let n of f){let r=e.join(t,n);if(i.has(r))return r}}}function s(e){let t=h(e);return t===`.rb`?{extensions:[`.rb`],indexFiles:[]}:t===`.py`?{extensions:[`.py`],indexFiles:[`__init__.py`]}:i.has(t)?{extensions:[],indexFiles:[]}:r.has(t)?{extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.mts`,`.cts`,`.mjs`,`.cjs`],indexFiles:[`index.ts`,`index.tsx`,`index.js`]}:{extensions:[`.ts`,`.tsx`,`.js`,`.jsx`,`.py`,`.rb`,`.h`,`.hpp`,`.hh`,`.hxx`,`.c`,`.cpp`,`.cc`,`.cxx`],indexFiles:[`index.ts`,`index.tsx`,`index.js`,`__init__.py`]}}const c=new Set([`mod.rs`,`lib.rs`,`main.rs`]);function l(t,n,r){let i=n.split(`::`),a=i.shift(),o=0;for(;a===`super`&&i[0]===`super`;)i.shift(),o+=1;let s=i.join(`/`);if(!s)return;let l=f(e.dirname(t)),p=c.has(e.basename(t)),m=e.join(l,e.basename(t,`.rs`)),h;h=a===`self`?p?[l]:[m,l]:a===`super`?(p?[f(e.dirname(l)),l]:[l,f(e.dirname(l))]).map(e=>d(e,o)):u(l,r);let g=[s,i.slice(0,-1).join(`/`)].filter(Boolean);for(let n of h)for(let i of g)for(let a of[e.join(n,`${i}.rs`),e.join(n,i,`mod.rs`)])if(a!==t&&r.has(a))return a}function u(t,n){let r=[],i=t;for(;r.push(i),i!==``;)i=f(e.dirname(i));let a=r.filter(t=>n.has(e.join(t,`lib.rs`))||n.has(e.join(t,`main.rs`)));return a.length>0?a:r}function d(t,n){let r=t;for(let t=0;t<n&&r!==``;t+=1)r=f(e.dirname(r));return r}function f(e){return e===`.`?``:e}function p(t){let n=new Map;for(let{relativeFile:r,metrics:i}of t){if(h(r)!==`.java`)continue;let t=e.basename(r,e.extname(r)),a=(e,t)=>{let i=n.get(e)??[];i.some(e=>e.file===r&&e.qualifiedPath===t)||(i.push({file:r,qualifiedPath:t}),n.set(e,i))},o=!1;for(let e of i.module.declarations){let n=e.name.lastIndexOf(`::`),r=n===-1?e.name:e.name.slice(n+2),i=n===-1?void 0:e.name.slice(0,n).replaceAll(`.`,`/`);a(r,i?`${i}/${r}`:void 0),o||=r===t}o||a(t,void 0)}for(let e of n.values())e.sort((e,t)=>e.file.localeCompare(t.file));return n}function m(t,n){if(t.endsWith(`.*`))return;let r=t.split(`.`);for(let t=r.length;t>=1;--t){let i=r.slice(0,t),a=i.at(-1);if(!a)continue;let o=i.join(`/`),s=n.get(a)??[],c=s.find(e=>e.qualifiedPath===o);if(c)return c.file;let l=s.find(t=>{if(t.qualifiedPath)return!1;let n=t.file.replaceAll(`\\`,`/`),r=n.slice(0,n.length-e.extname(n).length);return r===o||r.endsWith(`/${o}`)});if(l)return l.file}}function h(t){return e.extname(t).toLowerCase()}function g(t,n){let r=e.dirname(t);if(_(n))return[e.normalize(e.join(r,n))];let i=/^(?<dots>\.+)(?<module>.*)$/u.exec(n),a=i?.groups?.dots,o=i?.groups?.module;if(!a||o===void 0)return[];let s=r;for(let t=1;t<a.length;t+=1)s=e.dirname(s);let c=o.replaceAll(`.`,e.sep);return[e.normalize(e.join(s,c))]}function _(e){return e.startsWith(`./`)||e.startsWith(`../`)||e.includes(`/`)}function v(e,t){let n=new Set([e]),r=[...t.get(e)??[]];for(;r.length>0;){let e=r.pop();!e||n.has(e)||(n.add(e),r.push(...t.get(e)??[]))}return n.size-1}function y(e){let t=new Map;for(let n of e)for(let e of n.metrics.module.declarations){if(!b(e))continue;let r=t.get(e.name)??[];r.push(x(n.relativeFile,e)),t.set(e.name,r)}return[...t.entries()].flatMap(([e,t])=>{let n=[...new Set(t.map(e=>e.file))].toSorted();return n.length>1?[{declarations:t.toSorted(S),files:n,name:e}]:[]}).toSorted((e,t)=>t.files.length-e.files.length||e.name.localeCompare(t.name))}function b(e){return(e.name.split(/::|\./u).at(-1)??e.name).length>=5}function x(e,t){return{file:e,line:t.startLine}}function S(e,t){return e.file.localeCompare(t.file)||e.line-t.line}function C(e){let t=new Map;for(let n of e)for(let e of n.files)t.set(e,(t.get(e)??0)+1);return t}function w(e){let t=e.syntaxFeatures.awaitExpressionCount,n=e.syntaxFeatures.tryStatementCount+e.syntaxFeatures.throwStatementCount,r=e.syntaxFeatures.mutableBindingCount+e.syntaxFeatures.assignmentCount,i=e.cyclomaticComplexity+e.syntaxFeatures.loopStatementCount+e.syntaxFeatures.returnStatementCount,a=e.callGraph.callCount+e.callGraph.internalEdgeCount*2+e.callGraph.maxCallDepth*3;return{asyncBoundaryCount:t,branchingScore:i,exceptionHandlingCount:n,moduleInteractionScore:a,score:t*2+n*3+r+i+a,stateMutationScore:r}}function T(e,t,n){let r=new Set;return E(r,`class-shapes`,e.classCount>0),E(r,`control-flow`,e.cognitiveComplexity>0),E(r,`external-dependencies`,e.coupling.externalImportCount-n>0),E(r,`functions`,e.functionCount>0),E(r,`local-dependencies`,e.coupling.relativeImportCount>0||t>0),E(r,`module-api`,e.coupling.exportCount>0||e.module.declarations.length>0),E(r,`state-mutation`,e.syntaxFeatures.mutableBindingCount+e.syntaxFeatures.assignmentCount>0),E(r,`type-shapes`,D(e)),[...r].toSorted()}function E(e,t,n){n&&e.add(t)}function D(e){return e.typeComplexity.typeAnnotationCount+e.typeComplexity.typeAliasCount+e.typeComplexity.interfaceCount+e.typeComplexity.genericParameterCount+e.typeComplexity.unionTypeCount+e.typeComplexity.intersectionTypeCount+e.typeComplexity.conditionalTypeCount>0}function O(e,t){return Math.max(0,...e.map(e=>e[t]).filter(e=>typeof e==`number`))}export{t as measureArchitecture};
2
- //# sourceMappingURL=architectureMetrics.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"architectureMetrics.js","names":[],"sources":["../src/architectureMetrics.ts"],"sourcesContent":["import path from 'node:path';\nimport type { CodeMetrics, DeclarationMetrics } from './types.js';\n\nexport interface ArchitectureSourceFile {\n file: string;\n metrics: CodeMetrics;\n}\n\nexport interface ArchitectureFileMetrics {\n directLocalDependencyCount: number;\n duplicateSymbolGroupCount: number;\n file: string;\n structuralBreadthScore: number;\n structuralCoordination: StructuralCoordinationMetrics;\n structuralFeatureGroups: string[];\n transitiveLocalDependencyCount: number;\n}\n\nexport interface ArchitectureMetrics {\n duplicateSymbolGroups: DuplicateSymbolGroup[];\n files: ArchitectureFileMetrics[];\n maxDirectLocalDependencyCount: number;\n maxDuplicateSymbolGroupCount: number;\n maxStructuralBreadthScore: number;\n maxStructuralCoordinationScore: number;\n maxTransitiveLocalDependencyCount: number;\n}\n\nexport interface DuplicateSymbolGroup {\n declarations: DuplicateSymbolDeclaration[];\n files: string[];\n name: string;\n}\n\nexport interface DuplicateSymbolDeclaration {\n file: string;\n line: number;\n}\n\nexport interface StructuralCoordinationMetrics {\n asyncBoundaryCount: number;\n branchingScore: number;\n exceptionHandlingCount: number;\n moduleInteractionScore: number;\n score: number;\n stateMutationScore: number;\n}\n\ninterface SourceFile {\n file: string;\n metrics: CodeMetrics;\n relativeFile: string;\n}\n\nconst duplicateSymbolNameLengthThreshold = 5;\n\nexport function measureArchitecture(\n files: readonly ArchitectureSourceFile[],\n displayRoot: string\n): ArchitectureMetrics {\n const sourceFiles = files.map((file) => ({\n ...file,\n relativeFile: path.relative(displayRoot, file.file) || path.basename(file.file),\n }));\n const dependencyGraph = measureDependencyGraph(sourceFiles);\n const duplicateSymbolGroups = measureDuplicateSymbolGroups(sourceFiles);\n const duplicateGroupCountByFile = measureDuplicateGroupCountByFile(duplicateSymbolGroups);\n const metrics = sourceFiles.map((sourceFile) => {\n const directDependencies = dependencyGraph.dependenciesByFile.get(sourceFile.relativeFile) ?? new Set<string>();\n const nonRelativeLocalImportCount =\n dependencyGraph.nonRelativeLocalImportCountByFile.get(sourceFile.relativeFile) ?? 0;\n const structuralCoordination = measureStructuralCoordination(sourceFile.metrics);\n const structuralFeatureGroups = measureStructuralFeatureGroups(\n sourceFile.metrics,\n directDependencies.size,\n nonRelativeLocalImportCount\n );\n return {\n file: sourceFile.relativeFile,\n directLocalDependencyCount: directDependencies.size,\n duplicateSymbolGroupCount: duplicateGroupCountByFile.get(sourceFile.relativeFile) ?? 0,\n structuralBreadthScore: structuralFeatureGroups.length,\n structuralCoordination,\n structuralFeatureGroups,\n transitiveLocalDependencyCount: measureTransitiveDependencyCount(\n sourceFile.relativeFile,\n dependencyGraph.dependenciesByFile\n ),\n };\n });\n\n return {\n duplicateSymbolGroups,\n files: metrics,\n maxDirectLocalDependencyCount: maxFileMetric(metrics, 'directLocalDependencyCount'),\n maxDuplicateSymbolGroupCount: maxFileMetric(metrics, 'duplicateSymbolGroupCount'),\n maxStructuralBreadthScore: maxFileMetric(metrics, 'structuralBreadthScore'),\n maxStructuralCoordinationScore: Math.max(0, ...metrics.map((file) => file.structuralCoordination.score)),\n maxTransitiveLocalDependencyCount: maxFileMetric(metrics, 'transitiveLocalDependencyCount'),\n };\n}\n\ninterface DependencyGraph {\n dependenciesByFile: Map<string, Set<string>>;\n /** Import sources without a relative prefix (Java dotted paths) that still resolved locally. */\n nonRelativeLocalImportCountByFile: Map<string, number>;\n}\n\nfunction measureDependencyGraph(files: SourceFile[]): DependencyGraph {\n const fileSet = new Set(files.map((file) => file.relativeFile));\n const javaFileIndex = buildJavaFileIndex(files);\n const dependenciesByFile = new Map<string, Set<string>>();\n const nonRelativeLocalImportCountByFile = new Map<string, number>();\n for (const file of files) {\n const dependencies = new Set<string>();\n let nonRelativeLocalImportCount = 0;\n for (const source of file.metrics.module.importSources) {\n const resolved = resolveLocalImport(file.relativeFile, source, fileSet, javaFileIndex);\n // A file importing itself (`import './a.js'` inside a.ts) is not a dependency.\n if (resolved && resolved !== file.relativeFile) {\n dependencies.add(resolved);\n // Only Java needs the external-count correction: its dotted imports classify as external\n // in coupling metrics. Rust crate::/self::/super:: sources already classify as relative,\n // so counting them would double-subtract from externalImportCount.\n if (fileExtension(file.relativeFile) === '.java' && !source.startsWith('.')) {\n nonRelativeLocalImportCount += 1;\n }\n }\n }\n dependenciesByFile.set(file.relativeFile, dependencies);\n nonRelativeLocalImportCountByFile.set(file.relativeFile, nonRelativeLocalImportCount);\n }\n return { dependenciesByFile, nonRelativeLocalImportCountByFile };\n}\n\nconst jsExtensions = new Set(['.js', '.jsx', '.ts', '.tsx', '.mjs', '.cjs', '.mts', '.cts']);\n// Must cover every extension the CLI's languageByExtension maps to C/C++ (uppercase `.C` arrives\n// here lowercased by fileExtension), or importProbes falls into the non-C catch-all branch.\nconst cExtensions = new Set(['.c', '.c++', '.cc', '.cp', '.cpp', '.cxx', '.h', '.hh', '.hpp', '.hxx', '.tcc']);\n\n/**\n * TypeScript's ESM extension substitutions: a JS runtime extension in an import may name the\n * TypeScript source that compiles to it. `.ts`-family extensions are intentionally absent (they\n * resolve exactly), and `.mjs`/`.cjs` never remap to plain `.ts`.\n */\nconst jsImportExtensionSubstitutions = new Map<string, string[]>([\n ['.js', ['.ts', '.tsx']],\n ['.jsx', ['.tsx']],\n ['.mjs', ['.mts']],\n ['.cjs', ['.cts']],\n]);\n\nfunction resolveLocalImport(\n fromFile: string,\n source: string,\n fileSet: Set<string>,\n javaFileIndex: Map<string, JavaClassLocation[]>\n): string | undefined {\n const fromExtension = fileExtension(fromFile);\n if (!source.startsWith('.')) {\n if (fromExtension === '.rs' && /^(?:crate|self|super)(?:::|$)/u.test(source)) {\n return resolveRustLocalImport(fromFile, source, fileSet);\n }\n // Java imports are dotted class paths without a leading dot; other absolute sources are external.\n return fromExtension === '.java' ? resolveJavaImport(source, javaFileIndex) : undefined;\n }\n const bases = localImportBases(fromFile, source);\n\n // The JS-family ESM remap follows TypeScript's substitution table, which tries the TypeScript\n // source BEFORE the named JS file: `./foo.js` names `foo.ts` even when `foo.js` also exists,\n // and `./foo.mjs` may only name `foo.mts` (never an unrelated `foo.ts`), vice versa for `.cjs`.\n // Non-JS importers never remap, so `#include \"config.inc\"` cannot rebind to same-stem files.\n if (jsExtensions.has(fromExtension)) {\n for (const base of bases) {\n const substitutions = jsImportExtensionSubstitutions.get(path.extname(base));\n if (!substitutions) {\n continue;\n }\n const stem = base.slice(0, -path.extname(base).length);\n for (const substitution of substitutions) {\n if (fileSet.has(`${stem}${substitution}`)) {\n return `${stem}${substitution}`;\n }\n }\n }\n }\n\n // An explicit-path source (`#include \"b.hpp\"`, `require_relative \"./b.rb\"`, `./foo.js` with no\n // TypeScript counterpart) resolves exactly before extension probing, so a sibling `b.ts` cannot\n // shadow the named file.\n for (const base of bases) {\n if (fileSet.has(base)) {\n return base;\n }\n }\n\n const { extensions, indexFiles } = importProbes(fromFile);\n\n // Extension probing keeps each base's full name, so `./user.service` prefers `user.service.ts`.\n for (const stem of bases) {\n for (const extension of extensions) {\n if (fileSet.has(`${stem}${extension}`)) {\n return `${stem}${extension}`;\n }\n }\n for (const indexFile of indexFiles) {\n const candidate = path.join(stem, indexFile);\n if (fileSet.has(candidate)) {\n return candidate;\n }\n }\n }\n return undefined;\n}\n\n/** Extension-less sources probe only extensions the importing language can actually load. */\nfunction importProbes(fromFile: string): { extensions: string[]; indexFiles: string[] } {\n const fromExtension = fileExtension(fromFile);\n if (fromExtension === '.rb') {\n return { extensions: ['.rb'], indexFiles: [] };\n }\n if (fromExtension === '.py') {\n return { extensions: ['.py'], indexFiles: ['__init__.py'] };\n }\n if (cExtensions.has(fromExtension)) {\n // The C preprocessor searches for the exact requested filename and never appends extensions,\n // so `#include \"config\"` must not resolve to a same-stem `config.h`.\n return { extensions: [], indexFiles: [] };\n }\n if (jsExtensions.has(fromExtension)) {\n return {\n extensions: ['.ts', '.tsx', '.js', '.jsx', '.mts', '.cts', '.mjs', '.cjs'],\n indexFiles: ['index.ts', 'index.tsx', 'index.js'],\n };\n }\n return {\n extensions: ['.ts', '.tsx', '.js', '.jsx', '.py', '.rb', '.h', '.hpp', '.hh', '.hxx', '.c', '.cpp', '.cc', '.cxx'],\n indexFiles: ['index.ts', 'index.tsx', 'index.js', '__init__.py'],\n };\n}\n\n/** Rust module owner files whose child modules live as siblings in the same directory. */\nconst rustOwnerFileNames = new Set(['mod.rs', 'lib.rs', 'main.rs']);\n\n/**\n * Resolves a Rust in-crate module path (`crate::a::b`, `self::x`, `super::x`) to a scanned file by\n * probing `<path>.rs` and `<path>/mod.rs` against the appropriate base directories. `crate::` roots\n * are inferred from ancestor directories containing `lib.rs`/`main.rs`, falling back to every\n * ancestor so single-crate scans without a crate root file still resolve.\n */\nfunction resolveRustLocalImport(fromFile: string, source: string, fileSet: Set<string>): string | undefined {\n const segments = source.split('::');\n const head = segments.shift();\n // Rust allows repeated `super` segments to ascend several modules; each extra one climbs a\n // directory before the remaining path is probed.\n let extraSuperCount = 0;\n while (head === 'super' && segments[0] === 'super') {\n segments.shift();\n extraSuperCount += 1;\n }\n const modulePath = segments.join('/');\n if (!modulePath) {\n return undefined;\n }\n const fromDirectory = normalizeDirectory(path.dirname(fromFile));\n const isOwner = rustOwnerFileNames.has(path.basename(fromFile));\n const stemDirectory = path.join(fromDirectory, path.basename(fromFile, '.rs'));\n\n let baseDirectories: string[];\n if (head === 'self') {\n // Children of a mod.rs/lib.rs/main.rs module are siblings; children of `a.rs` live in `a/`.\n baseDirectories = isOwner ? [fromDirectory] : [stemDirectory, fromDirectory];\n } else if (head === 'super') {\n baseDirectories = (\n isOwner\n ? [normalizeDirectory(path.dirname(fromDirectory)), fromDirectory]\n : [fromDirectory, normalizeDirectory(path.dirname(fromDirectory))]\n ).map((directory) => ascendDirectory(directory, extraSuperCount));\n } else {\n baseDirectories = rustCrateRootCandidates(fromDirectory, fileSet);\n }\n\n // The leaf may be an item rather than a module (`use crate::b::helper` keeps `helper`), so the\n // parent module path is probed as a fallback for each base directory.\n const modulePaths = [modulePath, segments.slice(0, -1).join('/')].filter(Boolean);\n for (const base of baseDirectories) {\n for (const candidateModulePath of modulePaths) {\n for (const candidate of [\n path.join(base, `${candidateModulePath}.rs`),\n path.join(base, candidateModulePath, 'mod.rs'),\n ]) {\n if (candidate !== fromFile && fileSet.has(candidate)) {\n return candidate;\n }\n }\n }\n }\n return undefined;\n}\n\n/** Ancestor directories of `fromDirectory` holding a crate root file, else all ancestors (nearest first). */\nfunction rustCrateRootCandidates(fromDirectory: string, fileSet: Set<string>): string[] {\n const ancestors: string[] = [];\n let current = fromDirectory;\n for (;;) {\n ancestors.push(current);\n if (current === '') {\n break;\n }\n current = normalizeDirectory(path.dirname(current));\n }\n const withRootFile = ancestors.filter(\n (directory) => fileSet.has(path.join(directory, 'lib.rs')) || fileSet.has(path.join(directory, 'main.rs'))\n );\n return withRootFile.length > 0 ? withRootFile : ancestors;\n}\n\nfunction ascendDirectory(directory: string, levels: number): string {\n let current = directory;\n for (let index = 0; index < levels && current !== ''; index += 1) {\n current = normalizeDirectory(path.dirname(current));\n }\n return current;\n}\n\nfunction normalizeDirectory(directory: string): string {\n return directory === '.' ? '' : directory;\n}\n\ninterface JavaClassLocation {\n file: string;\n /** `<packagePath>/<ClassName>` from the file's declared package, when one was parsed. */\n qualifiedPath?: string;\n}\n\n/** Indexes scanned `.java` files by class name so dotted imports resolve without scanning all files. */\nfunction buildJavaFileIndex(files: SourceFile[]): Map<string, JavaClassLocation[]> {\n const index = new Map<string, JavaClassLocation[]>();\n for (const { relativeFile, metrics } of files) {\n if (fileExtension(relativeFile) !== '.java') {\n continue;\n }\n const fileClassName = path.basename(relativeFile, path.extname(relativeFile));\n const addLocation = (className: string, qualifiedPath: string | undefined): void => {\n const locations = index.get(className) ?? [];\n if (!locations.some((location) => location.file === relativeFile && location.qualifiedPath === qualifiedPath)) {\n locations.push({ file: relativeFile, qualifiedPath });\n index.set(className, locations);\n }\n };\n // Declarations carry the package as `com.alpha::ServiceThing`, giving each top-level type's\n // true qualified path regardless of directory layout — including package-private types whose\n // names differ from the file's.\n let sawFileClass = false;\n for (const declaration of metrics.module.declarations) {\n const separatorIndex = declaration.name.lastIndexOf('::');\n const className = separatorIndex === -1 ? declaration.name : declaration.name.slice(separatorIndex + 2);\n const packagePath =\n separatorIndex === -1 ? undefined : declaration.name.slice(0, separatorIndex).replaceAll('.', '/');\n addLocation(className, packagePath ? `${packagePath}/${className}` : undefined);\n sawFileClass ||= className === fileClassName;\n }\n // Filename fallback for files whose public type produced no declaration.\n if (!sawFileClass) {\n addLocation(fileClassName, undefined);\n }\n }\n for (const locations of index.values()) {\n locations.sort((left, right) => left.file.localeCompare(right.file));\n }\n return index;\n}\n\n/**\n * Resolves a Java dotted import (`com.example.Helper`) to a scanned file whose path ends with the\n * package path, tolerating source-root prefixes (`src/main/java/...`). Static imports fall back to\n * the enclosing class once the member segment fails to match; wildcard imports name a package, not\n * a file, and stay unresolved. Ambiguity across source roots resolves to the lexicographically\n * first path for determinism.\n */\nfunction resolveJavaImport(source: string, javaFileIndex: Map<string, JavaClassLocation[]>): string | undefined {\n if (source.endsWith('.*')) {\n return undefined;\n }\n // Trailing segments are dropped one by one so nested types and static members resolve to their\n // enclosing top-level class file (`com.example.Outer.Inner.CONST` -> `Outer.java`).\n const segments = source.split('.');\n for (let end = segments.length; end >= 1; end -= 1) {\n const candidateSegments = segments.slice(0, end);\n const className = candidateSegments.at(-1);\n if (!className) {\n continue;\n }\n const relativeStem = candidateSegments.join('/');\n const locations = javaFileIndex.get(className) ?? [];\n // The declared package is authoritative: matching it exactly stops `import java.util.List`\n // from binding to a project class in package `util` just because the Maven layout contains a\n // `java/` path segment.\n const packageMatch = locations.find((location) => location.qualifiedPath === relativeStem);\n if (packageMatch) {\n return packageMatch.file;\n }\n // Files without a parsed package fall back to path-suffix matching, tolerating source-root\n // prefixes (`src/main/java/...`). Indexed paths use the platform separator, so normalize.\n const suffixMatch = locations.find((location) => {\n if (location.qualifiedPath) {\n return false;\n }\n // Class names stay case-sensitive; only the extension is normalized (`B.JAVA` matches `B`).\n const normalized = location.file.replaceAll('\\\\', '/');\n const stem = normalized.slice(0, normalized.length - path.extname(normalized).length);\n return stem === relativeStem || stem.endsWith(`/${relativeStem}`);\n });\n if (suffixMatch) {\n return suffixMatch.file;\n }\n }\n return undefined;\n}\n\n/** Extensions are matched lowercased so `MAIN.CPP` or `.C` files use their language's rules. */\nfunction fileExtension(file: string): string {\n return path.extname(file).toLowerCase();\n}\n\nfunction localImportBases(fromFile: string, source: string): string[] {\n const fromDirectory = path.dirname(fromFile);\n if (isPathRelativeImport(source)) {\n return [path.normalize(path.join(fromDirectory, source))];\n }\n\n const match = /^(?<dots>\\.+)(?<module>.*)$/u.exec(source);\n const dots = match?.groups?.dots;\n const moduleName = match?.groups?.module;\n if (!dots || moduleName === undefined) {\n return [];\n }\n\n let baseDirectory = fromDirectory;\n for (let index = 1; index < dots.length; index += 1) {\n baseDirectory = path.dirname(baseDirectory);\n }\n\n const modulePath = moduleName.replaceAll('.', path.sep);\n return [path.normalize(path.join(baseDirectory, modulePath))];\n}\n\nfunction isPathRelativeImport(source: string): boolean {\n return source.startsWith('./') || source.startsWith('../') || source.includes('/');\n}\n\nfunction measureTransitiveDependencyCount(file: string, graph: Map<string, Set<string>>): number {\n // Seed with the starting file so dependency cycles do not count it as its own dependency.\n const visited = new Set<string>([file]);\n const pending = [...(graph.get(file) ?? [])];\n while (pending.length > 0) {\n const current = pending.pop();\n if (!current || visited.has(current)) {\n continue;\n }\n visited.add(current);\n pending.push(...(graph.get(current) ?? []));\n }\n return visited.size - 1;\n}\n\nfunction measureDuplicateSymbolGroups(files: SourceFile[]): DuplicateSymbolGroup[] {\n const declarationsBySymbol = new Map<string, DuplicateSymbolDeclaration[]>();\n for (const file of files) {\n for (const declaration of file.metrics.module.declarations) {\n if (!isDuplicateSymbolCandidate(declaration)) {\n continue;\n }\n const declarations = declarationsBySymbol.get(declaration.name) ?? [];\n declarations.push(toDuplicateSymbolDeclaration(file.relativeFile, declaration));\n declarationsBySymbol.set(declaration.name, declarations);\n }\n }\n return [...declarationsBySymbol.entries()]\n .flatMap(([name, declarations]) => {\n const duplicateFiles = [...new Set(declarations.map((declaration) => declaration.file))].toSorted();\n return duplicateFiles.length > 1\n ? [{ declarations: declarations.toSorted(compareDuplicateDeclarations), files: duplicateFiles, name }]\n : [];\n })\n .toSorted((left, right) => right.files.length - left.files.length || left.name.localeCompare(right.name));\n}\n\nfunction isDuplicateSymbolCandidate(declaration: DeclarationMetrics): boolean {\n // The threshold applies to the local name so qualification cannot let short names sneak past\n // it; the codebase emits both `::` (packages/namespaces/modules, C++ out-of-line names) and `.`\n // (Go `Receiver.Method`) separators.\n const localName = declaration.name.split(/::|\\./u).at(-1) ?? declaration.name;\n return localName.length >= duplicateSymbolNameLengthThreshold;\n}\n\nfunction toDuplicateSymbolDeclaration(file: string, declaration: DeclarationMetrics): DuplicateSymbolDeclaration {\n return { file, line: declaration.startLine };\n}\n\nfunction compareDuplicateDeclarations(left: DuplicateSymbolDeclaration, right: DuplicateSymbolDeclaration): number {\n return left.file.localeCompare(right.file) || left.line - right.line;\n}\n\nfunction measureDuplicateGroupCountByFile(groups: DuplicateSymbolGroup[]): Map<string, number> {\n const counts = new Map<string, number>();\n for (const group of groups) {\n for (const file of group.files) {\n counts.set(file, (counts.get(file) ?? 0) + 1);\n }\n }\n return counts;\n}\n\nfunction measureStructuralCoordination(metrics: CodeMetrics): StructuralCoordinationMetrics {\n const asyncBoundaryCount = metrics.syntaxFeatures.awaitExpressionCount;\n const exceptionHandlingCount = metrics.syntaxFeatures.tryStatementCount + metrics.syntaxFeatures.throwStatementCount;\n const stateMutationScore = metrics.syntaxFeatures.mutableBindingCount + metrics.syntaxFeatures.assignmentCount;\n const branchingScore =\n metrics.cyclomaticComplexity +\n metrics.syntaxFeatures.loopStatementCount +\n metrics.syntaxFeatures.returnStatementCount;\n const moduleInteractionScore =\n metrics.callGraph.callCount + metrics.callGraph.internalEdgeCount * 2 + metrics.callGraph.maxCallDepth * 3;\n\n return {\n asyncBoundaryCount,\n branchingScore,\n exceptionHandlingCount,\n moduleInteractionScore,\n score:\n asyncBoundaryCount * 2 +\n exceptionHandlingCount * 3 +\n stateMutationScore +\n branchingScore +\n moduleInteractionScore,\n stateMutationScore,\n };\n}\n\n/**\n * Java imports are dotted package paths, so per-file coupling classifies them as external even\n * when they resolve inside the scanned project; the dependency graph corrects both feature tags.\n */\nfunction measureStructuralFeatureGroups(\n metrics: CodeMetrics,\n directLocalDependencyCount: number,\n nonRelativeLocalImportCount: number\n): string[] {\n const groups = new Set<string>();\n addGroup(groups, 'class-shapes', metrics.classCount > 0);\n addGroup(groups, 'control-flow', metrics.cognitiveComplexity > 0);\n addGroup(groups, 'external-dependencies', metrics.coupling.externalImportCount - nonRelativeLocalImportCount > 0);\n addGroup(groups, 'functions', metrics.functionCount > 0);\n addGroup(groups, 'local-dependencies', metrics.coupling.relativeImportCount > 0 || directLocalDependencyCount > 0);\n addGroup(groups, 'module-api', metrics.coupling.exportCount > 0 || metrics.module.declarations.length > 0);\n addGroup(\n groups,\n 'state-mutation',\n metrics.syntaxFeatures.mutableBindingCount + metrics.syntaxFeatures.assignmentCount > 0\n );\n addGroup(groups, 'type-shapes', hasTypeShapeMetrics(metrics));\n return [...groups].toSorted();\n}\n\nfunction addGroup(groups: Set<string>, group: string, condition: boolean): void {\n if (condition) {\n groups.add(group);\n }\n}\n\nfunction hasTypeShapeMetrics(metrics: CodeMetrics): boolean {\n return (\n metrics.typeComplexity.typeAnnotationCount +\n metrics.typeComplexity.typeAliasCount +\n metrics.typeComplexity.interfaceCount +\n metrics.typeComplexity.genericParameterCount +\n metrics.typeComplexity.unionTypeCount +\n metrics.typeComplexity.intersectionTypeCount +\n metrics.typeComplexity.conditionalTypeCount >\n 0\n );\n}\n\nfunction maxFileMetric(metrics: ArchitectureFileMetrics[], key: keyof ArchitectureFileMetrics): number {\n return Math.max(\n 0,\n ...metrics.map((metric) => metric[key]).filter((value): value is number => typeof value === 'number')\n );\n}\n"],"mappings":"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"}
@@ -1,3 +0,0 @@
1
- "use strict";const e=require("./_virtual/_rolldown/runtime.cjs");let t=require("node:fs/promises"),n=require("node:path");n=e.__toESM(n,1);let r=require("typescript/unstable/async"),i=require("typescript/unstable/ast");async function a(e,t=[]){let i=new r.API({cwd:process.cwd()});try{let r=(await i.updateSnapshot({openProject:e})).getProjects(),a=await c(t),s=new Set(a.keys()),d=new Set,f={callExpressionCount:0,configDiagnosticCount:0,declarationDiagnosticCount:0,measuredRootFileCount:0,resolvedCallExpressionCount:0,rootFileCount:0,semanticDiagnosticCount:0,suggestionDiagnosticCount:0,syntacticDiagnosticCount:0},p=[],m=new Set;for(let e of r){let t=await Promise.all(e.rootFiles.map(async e=>({canonicalFile:await l(e),file:e})));f.rootFileCount+=t.length,f.measuredRootFileCount+=t.filter(({canonicalFile:e})=>s.has(e)).length;let r=await e.program.getSyntacticDiagnostics(),i=await e.program.getSemanticDiagnostics(),c=await e.program.getSuggestionDiagnostics(),h=await e.program.getDeclarationDiagnostics(),g=await e.program.getConfigFileParsingDiagnostics();f.syntacticDiagnosticCount+=r.length,f.semanticDiagnosticCount+=i.length,f.suggestionDiagnosticCount+=c.length,f.declarationDiagnosticCount+=h.length,f.configDiagnosticCount+=g.length,F(d,[...r,...i,...c,...h,...g]);let _=s.size===0?t:[...a.entries()].map(([e,t])=>({canonicalFile:e,file:t})),v=n.default.dirname(e.configFileName),y=await l(v);for(let{canonicalFile:t,file:n}of _){if(m.has(t))continue;let r=await o(e,n,t,v,y);if(!r)continue;m.add(t);let i=N(r);f.callExpressionCount+=i.length;let s=await u(r,e.checker);p.push(...s.map(e=>({...e,file:a.get(t)??n})));for(let t of i)await e.checker.getResolvedSignature(t)&&(f.resolvedCallExpressionCount+=1)}}let h=f.callExpressionCount-f.resolvedCallExpressionCount;return{...f,configFile:e,diagnosticFileCount:d.size,projectCount:r.length,reactComponentFunctions:p,unresolvedCallExpressionCount:h,resolvedCallExpressionRatio:f.callExpressionCount===0?0:f.resolvedCallExpressionCount/f.callExpressionCount}}finally{await i.close()}}async function o(e,t,n,r,i){for(let a of s(t,n,r,i)){let t=await e.program.getSourceFile(a);if(t)return t}}function s(e,t,r,i){let a=new Set([e,t]);return t.startsWith(`${i}${n.default.sep}`)&&a.add(n.default.join(r,n.default.relative(i,t))),[...a]}async function c(e){let t=new Map;for(let n of e)t.set(await l(n),n);return t}async function l(e){try{return await(0,t.realpath)(e)}catch{return e}}async function u(e,t){let n=[],r=d(e),i=p(e.text);for(let a of r){if(!await f(a,t,i))continue;let r=D(a),o=y(a),s=C(e.text,o,r,e),c=M(e.text,s);n.push({implementationKey:`${o.pos}:${o.end}`,name:r?T(r):void 0,startColumn:c.column,startLine:c.line})}return w(n)}function d(e){let t=[];return P(e,e=>{(_(e)||v(e))&&t.push(e)}),t}async function f(e,t,n){let r=await t.getTypeAtLocation(e);if(!r)return!1;let i=D(e),a=i?T(i):void 0;if(n&&await h(r,e,t))return!0;let o=i?await t.getTypeAtLocation(i):void 0;return n&&o&&await h(o,e,t)?!0:E(a)?await m(r,e,t)||!!(o&&await m(o,e,t)):!1}function p(e){return/\bfrom\s+['"]react['"]|\bimport\s+['"]react['"]|\brequire\(\s*['"]react['"]\s*\)/u.test(e)}async function m(e,t,n){let i=await n.getSignaturesOfType(e,r.SignatureKind.Call);for(let e of i){let r=await n.getReturnTypeOfSignature(e);if(r&&await g(r,t,n))return!0}return!1}async function h(e,t,n){let r=await n.typeToString(e,t);return/\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\b/u.test(r)}async function g(e,t,n){let r=await n.typeToString(e,t);return/\b(?:JSX\.Element|React\.JSX\.Element|ReactElement|ReactNode)\b/u.test(r)}function _(e){return e.kind===i.SyntaxKind.FunctionDeclaration&&!A(e,`body`)?!1:e.kind===i.SyntaxKind.FunctionDeclaration||e.kind===i.SyntaxKind.FunctionExpression||e.kind===i.SyntaxKind.ArrowFunction}function v(e){if(e.kind!==i.SyntaxKind.VariableDeclaration)return!1;let t=A(e,`initializer`),n=t?O(t):void 0;return n?_(n)||n.kind===i.SyntaxKind.CallExpression:!1}function y(e){if(_(e))return e;let t=A(e,`initializer`),n=t?O(t):void 0;return n?_(n)?n:b(n)?S(n)??e:e:e}function b(e){if(e.kind!==i.SyntaxKind.CallExpression)return!1;let t=A(e,`expression`),n=t?x(t):void 0;return n===`memo`||n===`forwardRef`}function x(e){return T(A(e,`name`)||e)}function S(e){let t;return e.forEachChild(e=>{let n=O(e);return _(n)?t=n:b(n)&&(t=S(n)),t}),t}function C(e,t,n,r){let a=(0,i.getTokenPosOfNode)(t,r);if(t.kind!==i.SyntaxKind.FunctionDeclaration||!n)return a;let o=T(n),s=o?e.indexOf(o,a):-1,c=s>=0?e.lastIndexOf(`function`,s):-1;if(c<a)return a;let l=e.slice(a,c),u=/\basync\s*$/u.exec(l);return u?a+u.index:c}function w(e){let t=new Map;for(let n of e){let e=t.get(n.implementationKey);(!e||!e.name&&n.name)&&t.set(n.implementationKey,n)}return[...t.values()].map(e=>({name:e.name,startColumn:e.startColumn,startLine:e.startLine}))}function T(e){if(`escapedText`in e)return String(e.escapedText);if(`text`in e&&typeof e.text==`string`)return e.text}function E(e){return e!==void 0&&/^[A-Z]/u.test(e)}function D(e){let t=A(e,`name`);if(t)return t;let n=e;for(;n;){let e=A(n,`parent`);if(!e)return;if(e.kind===i.SyntaxKind.VariableDeclaration)return A(e,`name`);if(!k(e))return;n=e}}function O(e){let t=e;for(;k(t);){let e=A(t,`expression`);if(!e)return t;t=e}return t}function k(e){return e.kind===i.SyntaxKind.ParenthesizedExpression||e.kind===i.SyntaxKind.AsExpression||e.kind===i.SyntaxKind.TypeAssertionExpression||e.kind===i.SyntaxKind.SatisfiesExpression}function A(e,t){let n=e[t];return j(n)?n:void 0}function j(e){return typeof e==`object`&&!!e&&`kind`in e&&`pos`in e&&`end`in e}function M(e,t){let n=e.slice(0,t).split(`
2
- `);return{column:n.at(-1)?.length??0,line:n.length}}function N(e){let t=[];return P(e,e=>{e.kind===i.SyntaxKind.CallExpression&&t.push(e)}),t}function P(e,t){t(e),e.forEachChild(e=>{P(e,t)})}function F(e,t){for(let n of t)n.fileName&&e.add(n.fileName)}exports.measureTypeScriptProject=a;
3
- //# sourceMappingURL=typescriptProject.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typescriptProject.cjs","names":["API","path","realpath","SignatureKind","SyntaxKind","getTokenPosOfNode"],"sources":["../src/typescriptProject.ts"],"sourcesContent":["import { realpath } from 'node:fs/promises';\nimport path from 'node:path';\nimport { API, SignatureKind, type Checker, type Type } from 'typescript/unstable/async';\nimport { getTokenPosOfNode, SyntaxKind, type Node, type SourceFile } from 'typescript/unstable/ast';\n\nexport interface ReactComponentFunctionMetric {\n file: string;\n name?: string;\n startLine: number;\n startColumn: number;\n}\n\nexport interface TypeScriptProjectMetrics {\n callExpressionCount: number;\n configDiagnosticCount: number;\n configFile: string;\n declarationDiagnosticCount: number;\n diagnosticFileCount: number;\n measuredRootFileCount: number;\n projectCount: number;\n reactComponentFunctions: ReactComponentFunctionMetric[];\n resolvedCallExpressionCount: number;\n resolvedCallExpressionRatio: number;\n rootFileCount: number;\n semanticDiagnosticCount: number;\n suggestionDiagnosticCount: number;\n syntacticDiagnosticCount: number;\n unresolvedCallExpressionCount: number;\n}\n\ninterface DiagnosticLike {\n fileName?: string;\n}\n\ninterface ReactComponentCandidateMetric extends Omit<ReactComponentFunctionMetric, 'file'> {\n implementationKey: string;\n}\n\nexport async function measureTypeScriptProject(\n configFile: string,\n measuredFiles: readonly string[] = []\n): Promise<TypeScriptProjectMetrics> {\n const api = new API({ cwd: process.cwd() });\n try {\n const snapshot = await api.updateSnapshot({ openProject: configFile });\n const projects = snapshot.getProjects();\n const measuredFileByCanonicalFile = await mapMeasuredFilesByCanonicalFile(measuredFiles);\n const measuredFileSet = new Set(measuredFileByCanonicalFile.keys());\n const diagnosticFiles = new Set<string>();\n const totals = {\n callExpressionCount: 0,\n configDiagnosticCount: 0,\n declarationDiagnosticCount: 0,\n measuredRootFileCount: 0,\n resolvedCallExpressionCount: 0,\n rootFileCount: 0,\n semanticDiagnosticCount: 0,\n suggestionDiagnosticCount: 0,\n syntacticDiagnosticCount: 0,\n };\n const reactComponentFunctions: ReactComponentFunctionMetric[] = [];\n const analyzedFiles = new Set<string>();\n\n for (const project of projects) {\n const projectRootFiles = await Promise.all(\n project.rootFiles.map(async (file) => ({ canonicalFile: await canonicalizeFile(file), file }))\n );\n totals.rootFileCount += projectRootFiles.length;\n totals.measuredRootFileCount += projectRootFiles.filter(({ canonicalFile }) =>\n measuredFileSet.has(canonicalFile)\n ).length;\n\n const syntacticDiagnostics = await project.program.getSyntacticDiagnostics();\n const semanticDiagnostics = await project.program.getSemanticDiagnostics();\n const suggestionDiagnostics = await project.program.getSuggestionDiagnostics();\n const declarationDiagnostics = await project.program.getDeclarationDiagnostics();\n const configDiagnostics = await project.program.getConfigFileParsingDiagnostics();\n totals.syntacticDiagnosticCount += syntacticDiagnostics.length;\n totals.semanticDiagnosticCount += semanticDiagnostics.length;\n totals.suggestionDiagnosticCount += suggestionDiagnostics.length;\n totals.declarationDiagnosticCount += declarationDiagnostics.length;\n totals.configDiagnosticCount += configDiagnostics.length;\n addDiagnosticFiles(diagnosticFiles, [\n ...syntacticDiagnostics,\n ...semanticDiagnostics,\n ...suggestionDiagnostics,\n ...declarationDiagnostics,\n ...configDiagnostics,\n ]);\n\n const projectAnalysisFiles =\n measuredFileSet.size === 0\n ? projectRootFiles\n : [...measuredFileByCanonicalFile.entries()].map(([canonicalFile, file]) => ({ canonicalFile, file }));\n const configDirectory = path.dirname(project.configFileName);\n const canonicalConfigDirectory = await canonicalizeFile(configDirectory);\n for (const { canonicalFile, file } of projectAnalysisFiles) {\n if (analyzedFiles.has(canonicalFile)) {\n continue;\n }\n const sourceFile = await getProjectSourceFile(\n project,\n file,\n canonicalFile,\n configDirectory,\n canonicalConfigDirectory\n );\n if (!sourceFile) {\n continue;\n }\n analyzedFiles.add(canonicalFile);\n const callExpressions = collectCallExpressions(sourceFile);\n totals.callExpressionCount += callExpressions.length;\n const fileReactComponentFunctions = await collectReactComponentFunctions(sourceFile, project.checker);\n reactComponentFunctions.push(\n ...fileReactComponentFunctions.map((component) => ({\n ...component,\n file: measuredFileByCanonicalFile.get(canonicalFile) ?? file,\n }))\n );\n for (const callExpression of callExpressions) {\n if (await project.checker.getResolvedSignature(callExpression)) {\n totals.resolvedCallExpressionCount += 1;\n }\n }\n }\n }\n\n const unresolvedCallExpressionCount = totals.callExpressionCount - totals.resolvedCallExpressionCount;\n return {\n ...totals,\n configFile,\n diagnosticFileCount: diagnosticFiles.size,\n projectCount: projects.length,\n reactComponentFunctions,\n unresolvedCallExpressionCount,\n resolvedCallExpressionRatio:\n totals.callExpressionCount === 0 ? 0 : totals.resolvedCallExpressionCount / totals.callExpressionCount,\n };\n } finally {\n await api.close();\n }\n}\n\nasync function getProjectSourceFile(\n project: { configFileName: string; program: { getSourceFile: (file: string) => Promise<SourceFile | undefined> } },\n file: string,\n canonicalFile: string,\n configDirectory: string,\n canonicalConfigDirectory: string\n): Promise<SourceFile | undefined> {\n for (const candidate of getProjectFileCandidates(file, canonicalFile, configDirectory, canonicalConfigDirectory)) {\n const sourceFile = await project.program.getSourceFile(candidate);\n if (sourceFile) {\n return sourceFile;\n }\n }\n return undefined;\n}\n\nfunction getProjectFileCandidates(\n file: string,\n canonicalFile: string,\n configDirectory: string,\n canonicalConfigDirectory: string\n): string[] {\n const candidates = new Set([file, canonicalFile]);\n if (canonicalFile.startsWith(`${canonicalConfigDirectory}${path.sep}`)) {\n candidates.add(path.join(configDirectory, path.relative(canonicalConfigDirectory, canonicalFile)));\n }\n return [...candidates];\n}\n\nasync function mapMeasuredFilesByCanonicalFile(files: readonly string[]): Promise<Map<string, string>> {\n const measuredFileByCanonicalFile = new Map<string, string>();\n for (const file of files) {\n measuredFileByCanonicalFile.set(await canonicalizeFile(file), file);\n }\n return measuredFileByCanonicalFile;\n}\n\nasync function canonicalizeFile(file: string): Promise<string> {\n try {\n return await realpath(file);\n } catch {\n return file;\n }\n}\n\nasync function collectReactComponentFunctions(\n sourceFile: SourceFile,\n checker: Checker\n): Promise<Omit<ReactComponentFunctionMetric, 'file'>[]> {\n const components: ReactComponentCandidateMetric[] = [];\n const candidates = collectReactComponentCandidates(sourceFile);\n const hasReactReference = containsReactModuleReference(sourceFile.text);\n for (const candidate of candidates) {\n if (!(await isReactComponentCandidate(candidate, checker, hasReactReference))) {\n continue;\n }\n const name = findNameNode(candidate);\n const functionNode = findComponentFunctionNode(candidate);\n const startOffset = findFunctionStartPosition(sourceFile.text, functionNode, name, sourceFile);\n const startPosition = positionToLineColumn(sourceFile.text, startOffset);\n components.push({\n implementationKey: `${functionNode.pos}:${functionNode.end}`,\n name: name ? findCandidateName(name) : undefined,\n startColumn: startPosition.column,\n startLine: startPosition.line,\n });\n }\n return dedupeReactComponentFunctions(components);\n}\n\nfunction collectReactComponentCandidates(root: Node): Node[] {\n const candidates: Node[] = [];\n visitNode(root, (node) => {\n if (isFunctionLikeCandidate(node) || isVariableFunctionCandidate(node)) {\n candidates.push(node);\n }\n });\n return candidates;\n}\n\nasync function isReactComponentCandidate(node: Node, checker: Checker, hasReactReference: boolean): Promise<boolean> {\n const type = await checker.getTypeAtLocation(node);\n if (!type) {\n return false;\n }\n\n const name = findNameNode(node);\n const componentName = name ? findCandidateName(name) : undefined;\n if (hasReactReference && (await hasReactComponentTypeName(type, node, checker))) {\n return true;\n }\n\n const namedType = name ? await checker.getTypeAtLocation(name) : undefined;\n if (hasReactReference && namedType && (await hasReactComponentTypeName(namedType, node, checker))) {\n return true;\n }\n\n if (!isUppercaseComponentName(componentName)) {\n return false;\n }\n\n return (\n (await hasReactRenderableReturnType(type, node, checker)) ||\n Boolean(namedType && (await hasReactRenderableReturnType(namedType, node, checker)))\n );\n}\n\nfunction containsReactModuleReference(text: string): boolean {\n return /\\bfrom\\s+['\"]react['\"]|\\bimport\\s+['\"]react['\"]|\\brequire\\(\\s*['\"]react['\"]\\s*\\)/u.test(text);\n}\n\nasync function hasReactRenderableReturnType(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const signatures = await checker.getSignaturesOfType(type, SignatureKind.Call);\n for (const signature of signatures) {\n const returnType = await checker.getReturnTypeOfSignature(signature);\n if (returnType && (await isReactRenderableType(returnType, node, checker))) {\n return true;\n }\n }\n return false;\n}\n\nasync function hasReactComponentTypeName(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const typeName = await checker.typeToString(type, node);\n return /\\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\\b/u.test(\n typeName\n );\n}\n\nasync function isReactRenderableType(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const typeName = await checker.typeToString(type, node);\n return /\\b(?:JSX\\.Element|React\\.JSX\\.Element|ReactElement|ReactNode)\\b/u.test(typeName);\n}\n\nfunction isFunctionLikeCandidate(node: Node): boolean {\n if (node.kind === SyntaxKind.FunctionDeclaration && !getNodeProperty(node, 'body')) {\n return false;\n }\n\n return (\n node.kind === SyntaxKind.FunctionDeclaration ||\n node.kind === SyntaxKind.FunctionExpression ||\n node.kind === SyntaxKind.ArrowFunction\n );\n}\n\nfunction isVariableFunctionCandidate(node: Node): boolean {\n if (node.kind !== SyntaxKind.VariableDeclaration) {\n return false;\n }\n\n const initializer = getNodeProperty(node, 'initializer');\n const unwrappedInitializer = initializer ? unwrapExpression(initializer) : undefined;\n return unwrappedInitializer\n ? isFunctionLikeCandidate(unwrappedInitializer) || unwrappedInitializer.kind === SyntaxKind.CallExpression\n : false;\n}\n\nfunction findComponentFunctionNode(node: Node): Node {\n if (isFunctionLikeCandidate(node)) {\n return node;\n }\n\n const initializer = getNodeProperty(node, 'initializer');\n const unwrappedInitializer = initializer ? unwrapExpression(initializer) : undefined;\n if (!unwrappedInitializer) {\n return node;\n }\n if (isFunctionLikeCandidate(unwrappedInitializer)) {\n return unwrappedInitializer;\n }\n if (isComponentImplementationWrapperCall(unwrappedInitializer)) {\n return findComponentImplementationWrapperFunction(unwrappedInitializer) ?? node;\n }\n return node;\n}\n\nfunction isComponentImplementationWrapperCall(node: Node): boolean {\n if (node.kind !== SyntaxKind.CallExpression) {\n return false;\n }\n\n const expression = getNodeProperty(node, 'expression');\n const expressionName = expression ? findCallExpressionName(expression) : undefined;\n return expressionName === 'memo' || expressionName === 'forwardRef';\n}\n\nfunction findCallExpressionName(expression: Node): string | undefined {\n const name = getNodeProperty(expression, 'name');\n return name ? findCandidateName(name) : findCandidateName(expression);\n}\n\nfunction findComponentImplementationWrapperFunction(node: Node): Node | undefined {\n let functionNode: Node | undefined;\n node.forEachChild((child) => {\n const unwrappedChild = unwrapExpression(child);\n if (isFunctionLikeCandidate(unwrappedChild)) {\n functionNode = unwrappedChild;\n } else if (isComponentImplementationWrapperCall(unwrappedChild)) {\n functionNode = findComponentImplementationWrapperFunction(unwrappedChild);\n }\n return functionNode;\n });\n return functionNode;\n}\n\nfunction findFunctionStartPosition(\n text: string,\n functionNode: Node,\n name: Node | undefined,\n sourceFile: SourceFile\n): number {\n const tokenPosition = getTokenPosOfNode(functionNode, sourceFile);\n if (functionNode.kind !== SyntaxKind.FunctionDeclaration || !name) {\n return tokenPosition;\n }\n\n const functionName = findCandidateName(name);\n const namePosition = functionName ? text.indexOf(functionName, tokenPosition) : -1;\n const functionPosition = namePosition >= 0 ? text.lastIndexOf('function', namePosition) : -1;\n if (functionPosition < tokenPosition) {\n return tokenPosition;\n }\n\n const modifierText = text.slice(tokenPosition, functionPosition);\n const asyncMatch = /\\basync\\s*$/u.exec(modifierText);\n return asyncMatch ? tokenPosition + asyncMatch.index : functionPosition;\n}\n\nfunction dedupeReactComponentFunctions(\n components: readonly ReactComponentCandidateMetric[]\n): Omit<ReactComponentFunctionMetric, 'file'>[] {\n const componentByKey = new Map<string, ReactComponentCandidateMetric>();\n for (const component of components) {\n const existingComponent = componentByKey.get(component.implementationKey);\n if (!existingComponent || (!existingComponent.name && component.name)) {\n componentByKey.set(component.implementationKey, component);\n }\n }\n return [...componentByKey.values()].map((component) => ({\n name: component.name,\n startColumn: component.startColumn,\n startLine: component.startLine,\n }));\n}\n\nfunction findCandidateName(name: Node): string | undefined {\n if ('escapedText' in name) {\n return String(name.escapedText);\n }\n if ('text' in name && typeof name.text === 'string') {\n return name.text;\n }\n return undefined;\n}\n\nfunction isUppercaseComponentName(name: string | undefined): boolean {\n return name !== undefined && /^[A-Z]/u.test(name);\n}\n\nfunction findNameNode(node: Node): Node | undefined {\n const name = getNodeProperty(node, 'name');\n if (name) {\n return name;\n }\n\n let currentNode: Node | undefined = node;\n while (currentNode) {\n const parent = getNodeProperty(currentNode, 'parent');\n if (!parent) {\n return undefined;\n }\n if (parent.kind === SyntaxKind.VariableDeclaration) {\n return getNodeProperty(parent, 'name');\n }\n if (!isExpressionWrapperNode(parent)) {\n return undefined;\n }\n currentNode = parent;\n }\n return undefined;\n}\n\nfunction unwrapExpression(node: Node): Node {\n let currentNode = node;\n while (isExpressionWrapperNode(currentNode)) {\n const expression = getNodeProperty(currentNode, 'expression');\n if (!expression) {\n return currentNode;\n }\n currentNode = expression;\n }\n return currentNode;\n}\n\nfunction isExpressionWrapperNode(node: Node): boolean {\n return (\n node.kind === SyntaxKind.ParenthesizedExpression ||\n node.kind === SyntaxKind.AsExpression ||\n node.kind === SyntaxKind.TypeAssertionExpression ||\n node.kind === SyntaxKind.SatisfiesExpression\n );\n}\n\nfunction getNodeProperty(\n node: Node,\n property: 'body' | 'expression' | 'initializer' | 'name' | 'parent'\n): Node | undefined {\n const value = (node as Partial<Record<typeof property, Node>>)[property];\n return isNode(value) ? value : undefined;\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && 'kind' in value && 'pos' in value && 'end' in value;\n}\n\nfunction positionToLineColumn(text: string, position: number): { column: number; line: number } {\n const lines = text.slice(0, position).split('\\n');\n return { column: lines.at(-1)?.length ?? 0, line: lines.length };\n}\n\nfunction collectCallExpressions(root: Node): Node[] {\n const calls: Node[] = [];\n visitNode(root, (node) => {\n if (node.kind === SyntaxKind.CallExpression) {\n calls.push(node);\n }\n });\n return calls;\n}\n\nfunction visitNode(node: Node, visitor: (node: Node) => void): void {\n visitor(node);\n node.forEachChild((child) => {\n visitNode(child, visitor);\n return;\n });\n}\n\nfunction addDiagnosticFiles(files: Set<string>, diagnostics: readonly DiagnosticLike[]): void {\n for (const diagnostic of diagnostics) {\n if (diagnostic.fileName) {\n files.add(diagnostic.fileName);\n }\n }\n}\n"],"mappings":"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"}
@@ -1,24 +0,0 @@
1
- export interface ReactComponentFunctionMetric {
2
- file: string;
3
- name?: string;
4
- startLine: number;
5
- startColumn: number;
6
- }
7
- export interface TypeScriptProjectMetrics {
8
- callExpressionCount: number;
9
- configDiagnosticCount: number;
10
- configFile: string;
11
- declarationDiagnosticCount: number;
12
- diagnosticFileCount: number;
13
- measuredRootFileCount: number;
14
- projectCount: number;
15
- reactComponentFunctions: ReactComponentFunctionMetric[];
16
- resolvedCallExpressionCount: number;
17
- resolvedCallExpressionRatio: number;
18
- rootFileCount: number;
19
- semanticDiagnosticCount: number;
20
- suggestionDiagnosticCount: number;
21
- syntacticDiagnosticCount: number;
22
- unresolvedCallExpressionCount: number;
23
- }
24
- export declare function measureTypeScriptProject(configFile: string, measuredFiles?: readonly string[]): Promise<TypeScriptProjectMetrics>;
@@ -1,3 +0,0 @@
1
- import{realpath as e}from"node:fs/promises";import t from"node:path";import{API as n,SignatureKind as r}from"typescript/unstable/async";import{SyntaxKind as i,getTokenPosOfNode as a}from"typescript/unstable/ast";async function o(e,r=[]){let i=new n({cwd:process.cwd()});try{let n=(await i.updateSnapshot({openProject:e})).getProjects(),a=await l(r),o=new Set(a.keys()),c=new Set,f={callExpressionCount:0,configDiagnosticCount:0,declarationDiagnosticCount:0,measuredRootFileCount:0,resolvedCallExpressionCount:0,rootFileCount:0,semanticDiagnosticCount:0,suggestionDiagnosticCount:0,syntacticDiagnosticCount:0},p=[],m=new Set;for(let e of n){let n=await Promise.all(e.rootFiles.map(async e=>({canonicalFile:await u(e),file:e})));f.rootFileCount+=n.length,f.measuredRootFileCount+=n.filter(({canonicalFile:e})=>o.has(e)).length;let r=await e.program.getSyntacticDiagnostics(),i=await e.program.getSemanticDiagnostics(),l=await e.program.getSuggestionDiagnostics(),h=await e.program.getDeclarationDiagnostics(),g=await e.program.getConfigFileParsingDiagnostics();f.syntacticDiagnosticCount+=r.length,f.semanticDiagnosticCount+=i.length,f.suggestionDiagnosticCount+=l.length,f.declarationDiagnosticCount+=h.length,f.configDiagnosticCount+=g.length,I(c,[...r,...i,...l,...h,...g]);let _=o.size===0?n:[...a.entries()].map(([e,t])=>({canonicalFile:e,file:t})),v=t.dirname(e.configFileName),y=await u(v);for(let{canonicalFile:t,file:n}of _){if(m.has(t))continue;let r=await s(e,n,t,v,y);if(!r)continue;m.add(t);let i=P(r);f.callExpressionCount+=i.length;let o=await d(r,e.checker);p.push(...o.map(e=>({...e,file:a.get(t)??n})));for(let t of i)await e.checker.getResolvedSignature(t)&&(f.resolvedCallExpressionCount+=1)}}let h=f.callExpressionCount-f.resolvedCallExpressionCount;return{...f,configFile:e,diagnosticFileCount:c.size,projectCount:n.length,reactComponentFunctions:p,unresolvedCallExpressionCount:h,resolvedCallExpressionRatio:f.callExpressionCount===0?0:f.resolvedCallExpressionCount/f.callExpressionCount}}finally{await i.close()}}async function s(e,t,n,r,i){for(let a of c(t,n,r,i)){let t=await e.program.getSourceFile(a);if(t)return t}}function c(e,n,r,i){let a=new Set([e,n]);return n.startsWith(`${i}${t.sep}`)&&a.add(t.join(r,t.relative(i,n))),[...a]}async function l(e){let t=new Map;for(let n of e)t.set(await u(n),n);return t}async function u(t){try{return await e(t)}catch{return t}}async function d(e,t){let n=[],r=f(e),i=m(e.text);for(let a of r){if(!await p(a,t,i))continue;let r=O(a),o=b(a),s=w(e.text,o,r,e),c=N(e.text,s);n.push({implementationKey:`${o.pos}:${o.end}`,name:r?E(r):void 0,startColumn:c.column,startLine:c.line})}return T(n)}function f(e){let t=[];return F(e,e=>{(v(e)||y(e))&&t.push(e)}),t}async function p(e,t,n){let r=await t.getTypeAtLocation(e);if(!r)return!1;let i=O(e),a=i?E(i):void 0;if(n&&await g(r,e,t))return!0;let o=i?await t.getTypeAtLocation(i):void 0;return n&&o&&await g(o,e,t)?!0:D(a)?await h(r,e,t)||!!(o&&await h(o,e,t)):!1}function m(e){return/\bfrom\s+['"]react['"]|\bimport\s+['"]react['"]|\brequire\(\s*['"]react['"]\s*\)/u.test(e)}async function h(e,t,n){let i=await n.getSignaturesOfType(e,r.Call);for(let e of i){let r=await n.getReturnTypeOfSignature(e);if(r&&await _(r,t,n))return!0}return!1}async function g(e,t,n){let r=await n.typeToString(e,t);return/\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\b/u.test(r)}async function _(e,t,n){let r=await n.typeToString(e,t);return/\b(?:JSX\.Element|React\.JSX\.Element|ReactElement|ReactNode)\b/u.test(r)}function v(e){return e.kind===i.FunctionDeclaration&&!j(e,`body`)?!1:e.kind===i.FunctionDeclaration||e.kind===i.FunctionExpression||e.kind===i.ArrowFunction}function y(e){if(e.kind!==i.VariableDeclaration)return!1;let t=j(e,`initializer`),n=t?k(t):void 0;return n?v(n)||n.kind===i.CallExpression:!1}function b(e){if(v(e))return e;let t=j(e,`initializer`),n=t?k(t):void 0;return n?v(n)?n:x(n)?C(n)??e:e:e}function x(e){if(e.kind!==i.CallExpression)return!1;let t=j(e,`expression`),n=t?S(t):void 0;return n===`memo`||n===`forwardRef`}function S(e){return E(j(e,`name`)||e)}function C(e){let t;return e.forEachChild(e=>{let n=k(e);return v(n)?t=n:x(n)&&(t=C(n)),t}),t}function w(e,t,n,r){let o=a(t,r);if(t.kind!==i.FunctionDeclaration||!n)return o;let s=E(n),c=s?e.indexOf(s,o):-1,l=c>=0?e.lastIndexOf(`function`,c):-1;if(l<o)return o;let u=e.slice(o,l),d=/\basync\s*$/u.exec(u);return d?o+d.index:l}function T(e){let t=new Map;for(let n of e){let e=t.get(n.implementationKey);(!e||!e.name&&n.name)&&t.set(n.implementationKey,n)}return[...t.values()].map(e=>({name:e.name,startColumn:e.startColumn,startLine:e.startLine}))}function E(e){if(`escapedText`in e)return String(e.escapedText);if(`text`in e&&typeof e.text==`string`)return e.text}function D(e){return e!==void 0&&/^[A-Z]/u.test(e)}function O(e){let t=j(e,`name`);if(t)return t;let n=e;for(;n;){let e=j(n,`parent`);if(!e)return;if(e.kind===i.VariableDeclaration)return j(e,`name`);if(!A(e))return;n=e}}function k(e){let t=e;for(;A(t);){let e=j(t,`expression`);if(!e)return t;t=e}return t}function A(e){return e.kind===i.ParenthesizedExpression||e.kind===i.AsExpression||e.kind===i.TypeAssertionExpression||e.kind===i.SatisfiesExpression}function j(e,t){let n=e[t];return M(n)?n:void 0}function M(e){return typeof e==`object`&&!!e&&`kind`in e&&`pos`in e&&`end`in e}function N(e,t){let n=e.slice(0,t).split(`
2
- `);return{column:n.at(-1)?.length??0,line:n.length}}function P(e){let t=[];return F(e,e=>{e.kind===i.CallExpression&&t.push(e)}),t}function F(e,t){t(e),e.forEachChild(e=>{F(e,t)})}function I(e,t){for(let n of t)n.fileName&&e.add(n.fileName)}export{o as measureTypeScriptProject};
3
- //# sourceMappingURL=typescriptProject.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"typescriptProject.js","names":[],"sources":["../src/typescriptProject.ts"],"sourcesContent":["import { realpath } from 'node:fs/promises';\nimport path from 'node:path';\nimport { API, SignatureKind, type Checker, type Type } from 'typescript/unstable/async';\nimport { getTokenPosOfNode, SyntaxKind, type Node, type SourceFile } from 'typescript/unstable/ast';\n\nexport interface ReactComponentFunctionMetric {\n file: string;\n name?: string;\n startLine: number;\n startColumn: number;\n}\n\nexport interface TypeScriptProjectMetrics {\n callExpressionCount: number;\n configDiagnosticCount: number;\n configFile: string;\n declarationDiagnosticCount: number;\n diagnosticFileCount: number;\n measuredRootFileCount: number;\n projectCount: number;\n reactComponentFunctions: ReactComponentFunctionMetric[];\n resolvedCallExpressionCount: number;\n resolvedCallExpressionRatio: number;\n rootFileCount: number;\n semanticDiagnosticCount: number;\n suggestionDiagnosticCount: number;\n syntacticDiagnosticCount: number;\n unresolvedCallExpressionCount: number;\n}\n\ninterface DiagnosticLike {\n fileName?: string;\n}\n\ninterface ReactComponentCandidateMetric extends Omit<ReactComponentFunctionMetric, 'file'> {\n implementationKey: string;\n}\n\nexport async function measureTypeScriptProject(\n configFile: string,\n measuredFiles: readonly string[] = []\n): Promise<TypeScriptProjectMetrics> {\n const api = new API({ cwd: process.cwd() });\n try {\n const snapshot = await api.updateSnapshot({ openProject: configFile });\n const projects = snapshot.getProjects();\n const measuredFileByCanonicalFile = await mapMeasuredFilesByCanonicalFile(measuredFiles);\n const measuredFileSet = new Set(measuredFileByCanonicalFile.keys());\n const diagnosticFiles = new Set<string>();\n const totals = {\n callExpressionCount: 0,\n configDiagnosticCount: 0,\n declarationDiagnosticCount: 0,\n measuredRootFileCount: 0,\n resolvedCallExpressionCount: 0,\n rootFileCount: 0,\n semanticDiagnosticCount: 0,\n suggestionDiagnosticCount: 0,\n syntacticDiagnosticCount: 0,\n };\n const reactComponentFunctions: ReactComponentFunctionMetric[] = [];\n const analyzedFiles = new Set<string>();\n\n for (const project of projects) {\n const projectRootFiles = await Promise.all(\n project.rootFiles.map(async (file) => ({ canonicalFile: await canonicalizeFile(file), file }))\n );\n totals.rootFileCount += projectRootFiles.length;\n totals.measuredRootFileCount += projectRootFiles.filter(({ canonicalFile }) =>\n measuredFileSet.has(canonicalFile)\n ).length;\n\n const syntacticDiagnostics = await project.program.getSyntacticDiagnostics();\n const semanticDiagnostics = await project.program.getSemanticDiagnostics();\n const suggestionDiagnostics = await project.program.getSuggestionDiagnostics();\n const declarationDiagnostics = await project.program.getDeclarationDiagnostics();\n const configDiagnostics = await project.program.getConfigFileParsingDiagnostics();\n totals.syntacticDiagnosticCount += syntacticDiagnostics.length;\n totals.semanticDiagnosticCount += semanticDiagnostics.length;\n totals.suggestionDiagnosticCount += suggestionDiagnostics.length;\n totals.declarationDiagnosticCount += declarationDiagnostics.length;\n totals.configDiagnosticCount += configDiagnostics.length;\n addDiagnosticFiles(diagnosticFiles, [\n ...syntacticDiagnostics,\n ...semanticDiagnostics,\n ...suggestionDiagnostics,\n ...declarationDiagnostics,\n ...configDiagnostics,\n ]);\n\n const projectAnalysisFiles =\n measuredFileSet.size === 0\n ? projectRootFiles\n : [...measuredFileByCanonicalFile.entries()].map(([canonicalFile, file]) => ({ canonicalFile, file }));\n const configDirectory = path.dirname(project.configFileName);\n const canonicalConfigDirectory = await canonicalizeFile(configDirectory);\n for (const { canonicalFile, file } of projectAnalysisFiles) {\n if (analyzedFiles.has(canonicalFile)) {\n continue;\n }\n const sourceFile = await getProjectSourceFile(\n project,\n file,\n canonicalFile,\n configDirectory,\n canonicalConfigDirectory\n );\n if (!sourceFile) {\n continue;\n }\n analyzedFiles.add(canonicalFile);\n const callExpressions = collectCallExpressions(sourceFile);\n totals.callExpressionCount += callExpressions.length;\n const fileReactComponentFunctions = await collectReactComponentFunctions(sourceFile, project.checker);\n reactComponentFunctions.push(\n ...fileReactComponentFunctions.map((component) => ({\n ...component,\n file: measuredFileByCanonicalFile.get(canonicalFile) ?? file,\n }))\n );\n for (const callExpression of callExpressions) {\n if (await project.checker.getResolvedSignature(callExpression)) {\n totals.resolvedCallExpressionCount += 1;\n }\n }\n }\n }\n\n const unresolvedCallExpressionCount = totals.callExpressionCount - totals.resolvedCallExpressionCount;\n return {\n ...totals,\n configFile,\n diagnosticFileCount: diagnosticFiles.size,\n projectCount: projects.length,\n reactComponentFunctions,\n unresolvedCallExpressionCount,\n resolvedCallExpressionRatio:\n totals.callExpressionCount === 0 ? 0 : totals.resolvedCallExpressionCount / totals.callExpressionCount,\n };\n } finally {\n await api.close();\n }\n}\n\nasync function getProjectSourceFile(\n project: { configFileName: string; program: { getSourceFile: (file: string) => Promise<SourceFile | undefined> } },\n file: string,\n canonicalFile: string,\n configDirectory: string,\n canonicalConfigDirectory: string\n): Promise<SourceFile | undefined> {\n for (const candidate of getProjectFileCandidates(file, canonicalFile, configDirectory, canonicalConfigDirectory)) {\n const sourceFile = await project.program.getSourceFile(candidate);\n if (sourceFile) {\n return sourceFile;\n }\n }\n return undefined;\n}\n\nfunction getProjectFileCandidates(\n file: string,\n canonicalFile: string,\n configDirectory: string,\n canonicalConfigDirectory: string\n): string[] {\n const candidates = new Set([file, canonicalFile]);\n if (canonicalFile.startsWith(`${canonicalConfigDirectory}${path.sep}`)) {\n candidates.add(path.join(configDirectory, path.relative(canonicalConfigDirectory, canonicalFile)));\n }\n return [...candidates];\n}\n\nasync function mapMeasuredFilesByCanonicalFile(files: readonly string[]): Promise<Map<string, string>> {\n const measuredFileByCanonicalFile = new Map<string, string>();\n for (const file of files) {\n measuredFileByCanonicalFile.set(await canonicalizeFile(file), file);\n }\n return measuredFileByCanonicalFile;\n}\n\nasync function canonicalizeFile(file: string): Promise<string> {\n try {\n return await realpath(file);\n } catch {\n return file;\n }\n}\n\nasync function collectReactComponentFunctions(\n sourceFile: SourceFile,\n checker: Checker\n): Promise<Omit<ReactComponentFunctionMetric, 'file'>[]> {\n const components: ReactComponentCandidateMetric[] = [];\n const candidates = collectReactComponentCandidates(sourceFile);\n const hasReactReference = containsReactModuleReference(sourceFile.text);\n for (const candidate of candidates) {\n if (!(await isReactComponentCandidate(candidate, checker, hasReactReference))) {\n continue;\n }\n const name = findNameNode(candidate);\n const functionNode = findComponentFunctionNode(candidate);\n const startOffset = findFunctionStartPosition(sourceFile.text, functionNode, name, sourceFile);\n const startPosition = positionToLineColumn(sourceFile.text, startOffset);\n components.push({\n implementationKey: `${functionNode.pos}:${functionNode.end}`,\n name: name ? findCandidateName(name) : undefined,\n startColumn: startPosition.column,\n startLine: startPosition.line,\n });\n }\n return dedupeReactComponentFunctions(components);\n}\n\nfunction collectReactComponentCandidates(root: Node): Node[] {\n const candidates: Node[] = [];\n visitNode(root, (node) => {\n if (isFunctionLikeCandidate(node) || isVariableFunctionCandidate(node)) {\n candidates.push(node);\n }\n });\n return candidates;\n}\n\nasync function isReactComponentCandidate(node: Node, checker: Checker, hasReactReference: boolean): Promise<boolean> {\n const type = await checker.getTypeAtLocation(node);\n if (!type) {\n return false;\n }\n\n const name = findNameNode(node);\n const componentName = name ? findCandidateName(name) : undefined;\n if (hasReactReference && (await hasReactComponentTypeName(type, node, checker))) {\n return true;\n }\n\n const namedType = name ? await checker.getTypeAtLocation(name) : undefined;\n if (hasReactReference && namedType && (await hasReactComponentTypeName(namedType, node, checker))) {\n return true;\n }\n\n if (!isUppercaseComponentName(componentName)) {\n return false;\n }\n\n return (\n (await hasReactRenderableReturnType(type, node, checker)) ||\n Boolean(namedType && (await hasReactRenderableReturnType(namedType, node, checker)))\n );\n}\n\nfunction containsReactModuleReference(text: string): boolean {\n return /\\bfrom\\s+['\"]react['\"]|\\bimport\\s+['\"]react['\"]|\\brequire\\(\\s*['\"]react['\"]\\s*\\)/u.test(text);\n}\n\nasync function hasReactRenderableReturnType(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const signatures = await checker.getSignaturesOfType(type, SignatureKind.Call);\n for (const signature of signatures) {\n const returnType = await checker.getReturnTypeOfSignature(signature);\n if (returnType && (await isReactRenderableType(returnType, node, checker))) {\n return true;\n }\n }\n return false;\n}\n\nasync function hasReactComponentTypeName(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const typeName = await checker.typeToString(type, node);\n return /\\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\\b/u.test(\n typeName\n );\n}\n\nasync function isReactRenderableType(type: Type, node: Node, checker: Checker): Promise<boolean> {\n const typeName = await checker.typeToString(type, node);\n return /\\b(?:JSX\\.Element|React\\.JSX\\.Element|ReactElement|ReactNode)\\b/u.test(typeName);\n}\n\nfunction isFunctionLikeCandidate(node: Node): boolean {\n if (node.kind === SyntaxKind.FunctionDeclaration && !getNodeProperty(node, 'body')) {\n return false;\n }\n\n return (\n node.kind === SyntaxKind.FunctionDeclaration ||\n node.kind === SyntaxKind.FunctionExpression ||\n node.kind === SyntaxKind.ArrowFunction\n );\n}\n\nfunction isVariableFunctionCandidate(node: Node): boolean {\n if (node.kind !== SyntaxKind.VariableDeclaration) {\n return false;\n }\n\n const initializer = getNodeProperty(node, 'initializer');\n const unwrappedInitializer = initializer ? unwrapExpression(initializer) : undefined;\n return unwrappedInitializer\n ? isFunctionLikeCandidate(unwrappedInitializer) || unwrappedInitializer.kind === SyntaxKind.CallExpression\n : false;\n}\n\nfunction findComponentFunctionNode(node: Node): Node {\n if (isFunctionLikeCandidate(node)) {\n return node;\n }\n\n const initializer = getNodeProperty(node, 'initializer');\n const unwrappedInitializer = initializer ? unwrapExpression(initializer) : undefined;\n if (!unwrappedInitializer) {\n return node;\n }\n if (isFunctionLikeCandidate(unwrappedInitializer)) {\n return unwrappedInitializer;\n }\n if (isComponentImplementationWrapperCall(unwrappedInitializer)) {\n return findComponentImplementationWrapperFunction(unwrappedInitializer) ?? node;\n }\n return node;\n}\n\nfunction isComponentImplementationWrapperCall(node: Node): boolean {\n if (node.kind !== SyntaxKind.CallExpression) {\n return false;\n }\n\n const expression = getNodeProperty(node, 'expression');\n const expressionName = expression ? findCallExpressionName(expression) : undefined;\n return expressionName === 'memo' || expressionName === 'forwardRef';\n}\n\nfunction findCallExpressionName(expression: Node): string | undefined {\n const name = getNodeProperty(expression, 'name');\n return name ? findCandidateName(name) : findCandidateName(expression);\n}\n\nfunction findComponentImplementationWrapperFunction(node: Node): Node | undefined {\n let functionNode: Node | undefined;\n node.forEachChild((child) => {\n const unwrappedChild = unwrapExpression(child);\n if (isFunctionLikeCandidate(unwrappedChild)) {\n functionNode = unwrappedChild;\n } else if (isComponentImplementationWrapperCall(unwrappedChild)) {\n functionNode = findComponentImplementationWrapperFunction(unwrappedChild);\n }\n return functionNode;\n });\n return functionNode;\n}\n\nfunction findFunctionStartPosition(\n text: string,\n functionNode: Node,\n name: Node | undefined,\n sourceFile: SourceFile\n): number {\n const tokenPosition = getTokenPosOfNode(functionNode, sourceFile);\n if (functionNode.kind !== SyntaxKind.FunctionDeclaration || !name) {\n return tokenPosition;\n }\n\n const functionName = findCandidateName(name);\n const namePosition = functionName ? text.indexOf(functionName, tokenPosition) : -1;\n const functionPosition = namePosition >= 0 ? text.lastIndexOf('function', namePosition) : -1;\n if (functionPosition < tokenPosition) {\n return tokenPosition;\n }\n\n const modifierText = text.slice(tokenPosition, functionPosition);\n const asyncMatch = /\\basync\\s*$/u.exec(modifierText);\n return asyncMatch ? tokenPosition + asyncMatch.index : functionPosition;\n}\n\nfunction dedupeReactComponentFunctions(\n components: readonly ReactComponentCandidateMetric[]\n): Omit<ReactComponentFunctionMetric, 'file'>[] {\n const componentByKey = new Map<string, ReactComponentCandidateMetric>();\n for (const component of components) {\n const existingComponent = componentByKey.get(component.implementationKey);\n if (!existingComponent || (!existingComponent.name && component.name)) {\n componentByKey.set(component.implementationKey, component);\n }\n }\n return [...componentByKey.values()].map((component) => ({\n name: component.name,\n startColumn: component.startColumn,\n startLine: component.startLine,\n }));\n}\n\nfunction findCandidateName(name: Node): string | undefined {\n if ('escapedText' in name) {\n return String(name.escapedText);\n }\n if ('text' in name && typeof name.text === 'string') {\n return name.text;\n }\n return undefined;\n}\n\nfunction isUppercaseComponentName(name: string | undefined): boolean {\n return name !== undefined && /^[A-Z]/u.test(name);\n}\n\nfunction findNameNode(node: Node): Node | undefined {\n const name = getNodeProperty(node, 'name');\n if (name) {\n return name;\n }\n\n let currentNode: Node | undefined = node;\n while (currentNode) {\n const parent = getNodeProperty(currentNode, 'parent');\n if (!parent) {\n return undefined;\n }\n if (parent.kind === SyntaxKind.VariableDeclaration) {\n return getNodeProperty(parent, 'name');\n }\n if (!isExpressionWrapperNode(parent)) {\n return undefined;\n }\n currentNode = parent;\n }\n return undefined;\n}\n\nfunction unwrapExpression(node: Node): Node {\n let currentNode = node;\n while (isExpressionWrapperNode(currentNode)) {\n const expression = getNodeProperty(currentNode, 'expression');\n if (!expression) {\n return currentNode;\n }\n currentNode = expression;\n }\n return currentNode;\n}\n\nfunction isExpressionWrapperNode(node: Node): boolean {\n return (\n node.kind === SyntaxKind.ParenthesizedExpression ||\n node.kind === SyntaxKind.AsExpression ||\n node.kind === SyntaxKind.TypeAssertionExpression ||\n node.kind === SyntaxKind.SatisfiesExpression\n );\n}\n\nfunction getNodeProperty(\n node: Node,\n property: 'body' | 'expression' | 'initializer' | 'name' | 'parent'\n): Node | undefined {\n const value = (node as Partial<Record<typeof property, Node>>)[property];\n return isNode(value) ? value : undefined;\n}\n\nfunction isNode(value: unknown): value is Node {\n return typeof value === 'object' && value !== null && 'kind' in value && 'pos' in value && 'end' in value;\n}\n\nfunction positionToLineColumn(text: string, position: number): { column: number; line: number } {\n const lines = text.slice(0, position).split('\\n');\n return { column: lines.at(-1)?.length ?? 0, line: lines.length };\n}\n\nfunction collectCallExpressions(root: Node): Node[] {\n const calls: Node[] = [];\n visitNode(root, (node) => {\n if (node.kind === SyntaxKind.CallExpression) {\n calls.push(node);\n }\n });\n return calls;\n}\n\nfunction visitNode(node: Node, visitor: (node: Node) => void): void {\n visitor(node);\n node.forEachChild((child) => {\n visitNode(child, visitor);\n return;\n });\n}\n\nfunction addDiagnosticFiles(files: Set<string>, diagnostics: readonly DiagnosticLike[]): void {\n for (const diagnostic of diagnostics) {\n if (diagnostic.fileName) {\n files.add(diagnostic.fileName);\n }\n }\n}\n"],"mappings":"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"}