code-gauge 1.8.0 → 1.10.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/dist/types.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type SupportedLanguage = 'go' | 'javascript' | 'jsx' | 'python' | 'rust' | 'typescript' | 'tsx';
1
+ export type SupportedLanguage = 'c' | 'cpp' | 'go' | 'java' | 'javascript' | 'jsx' | 'python' | 'ruby' | 'rust' | 'typescript' | 'tsx';
2
2
  export type LanguageName = SupportedLanguage | (string & {});
3
3
  export type ParserLanguage = unknown;
4
4
  export interface LanguageDefinition {
@@ -41,6 +41,7 @@ export interface FunctionMetrics {
41
41
  returnsJsx: boolean;
42
42
  cyclomaticComplexity: number;
43
43
  cognitiveComplexity: number;
44
+ nestingDepth: number;
44
45
  callCount: number;
45
46
  uniqueCalleeCount: number;
46
47
  fanIn: number;
@@ -89,17 +90,32 @@ export interface SyntaxFeatureMetrics {
89
90
  tryStatementCount: number;
90
91
  }
91
92
  /**
92
- * Within-file structural duplication: copy-pasted code blocks whose syntax shape (node types,
93
- * ignoring identifiers and literals) repeats. Distinct from cross-file duplicate symbol names.
93
+ * Within-file structural duplication: copy-pasted regions whose normalized token sequence repeats.
94
+ * Identifiers are anonymized consistently by first-occurrence order and literals by kind, so
95
+ * consistently renamed copies match. Distinct from cross-file duplicate symbol names.
94
96
  */
95
97
  export interface DuplicationMetrics {
96
- /** Number of redundant (extra) copies of duplicated blocks, i.e. sum of (groupSize - 1). */
98
+ /** Number of redundant (extra) copies of duplicated regions, i.e. sum of (groupSize - 1). */
97
99
  duplicateBlockCount: number;
98
- /** Number of distinct shapes that appear more than once. */
100
+ /** Number of distinct normalized token sequences that appear more than once. */
99
101
  duplicateBlockGroupCount: number;
100
- /** Node count of the largest duplicated block, indicating how big the copied region is. */
102
+ /** 1-based line ranges of every counted copy, grouped by shared normalized token sequence. */
103
+ duplicateBlockGroups: DuplicateBlockOccurrence[][];
104
+ /** Number of distinct lines covered by any counted duplicate occurrence (originals included). */
105
+ duplicateLineCount: number;
106
+ /**
107
+ * duplicateLineCount / code lines (0 when the file has no code). Code lines are the denominator
108
+ * because duplicated lines are counted from matched tokens, which only ever land on code lines;
109
+ * dividing by total lines would let comment density deflate the ratio.
110
+ */
111
+ duplicationRatio: number;
112
+ /** Normalized token count of the largest duplicated region, indicating how big the copied region is. */
101
113
  maxDuplicateBlockSize: number;
102
114
  }
115
+ export interface DuplicateBlockOccurrence {
116
+ endLine: number;
117
+ startLine: number;
118
+ }
103
119
  export interface TypeComplexityMetrics {
104
120
  typeAnnotationCount: number;
105
121
  typeAliasCount: number;
@@ -1,2 +1,3 @@
1
- "use strict";var n=require("node:fs/promises"),t=require("node:path"),e=require("@typescript/native-preview/unstable/async"),i=require("@typescript/native-preview/unstable/ast");async function o(n,e,i,o,a){for(const r of function(n,e,i,o){const a=new Set([n,e]);e.startsWith(`${o}${t.sep}`)&&a.add(t.join(i,t.relative(o,e)));return[...a]}(e,i,o,a)){const t=await n.program.getSourceFile(r);if(t)return t}}async function a(t){try{return await n.realpath(t)}catch{return t}}async function r(n,t){const e=[],o=function(n){const t=[];return v(n,n=>{(f(n)||function(n){if(n.kind!==i.SyntaxKind.VariableDeclaration)return!1;const t=S(n,"initializer"),e=t?C(t):void 0;return!!e&&(f(e)||e.kind===i.SyntaxKind.CallExpression)}(n))&&t.push(n)}),t}(n),a=(r=n.text,/\bfrom\s+['"]react['"]|\bimport\s+['"]react['"]|\brequire\(\s*['"]react['"]\s*\)/u.test(r));var r;for(const i of o){if(!await s(i,t,a))continue;const o=y(i),r=p(i),c=m(n.text,r,o,n),u=h(n.text,c);e.push({implementationKey:`${r.pos}:${r.end}`,name:o?x(o):void 0,startColumn:u.column,startLine:u.line})}return function(n){const t=new Map;for(const e of n){const n=t.get(e.implementationKey);(!n||!n.name&&e.name)&&t.set(e.implementationKey,e)}return[...t.values()].map(n=>({name:n.name,startColumn:n.startColumn,startLine:n.startLine}))}(e)}async function s(n,t,e){const i=await t.getTypeAtLocation(n);if(!i)return!1;const o=y(n),a=o?x(o):void 0;if(e&&await u(i,n,t))return!0;const r=o?await t.getTypeAtLocation(o):void 0;return!!(e&&r&&await u(r,n,t))||!!function(n){return void 0!==n&&/^[A-Z]/u.test(n)}(a)&&(await c(i,n,t)||Boolean(r&&await c(r,n,t)))}async function c(n,t,i){const o=await i.getSignaturesOfType(n,e.SignatureKind.Call);for(const n of o){const e=await i.getReturnTypeOfSignature(n);if(e&&await l(e,t,i))return!0}return!1}async function u(n,t,e){const i=await e.typeToString(n,t);return/\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\b/u.test(i)}async function l(n,t,e){const i=await e.typeToString(n,t);return/\b(?:JSX\.Element|React\.JSX\.Element|ReactElement|ReactNode)\b/u.test(i)}function f(n){return!(n.kind===i.SyntaxKind.FunctionDeclaration&&!S(n,"body"))&&(n.kind===i.SyntaxKind.FunctionDeclaration||n.kind===i.SyntaxKind.FunctionExpression||n.kind===i.SyntaxKind.ArrowFunction)}function p(n){if(f(n))return n;const t=S(n,"initializer"),e=t?C(t):void 0;return e?f(e)?e:d(e)?g(e)??n:n:n}function d(n){if(n.kind!==i.SyntaxKind.CallExpression)return!1;const t=S(n,"expression"),e=t?function(n){const t=S(n,"name");return x(t||n)}(t):void 0;return"memo"===e||"forwardRef"===e}function g(n){let t;return n.forEachChild(n=>{const e=C(n);return f(e)?t=e:d(e)&&(t=g(e)),t}),t}function m(n,t,e,o){const a=i.getTokenPosOfNode(t,o);if(t.kind!==i.SyntaxKind.FunctionDeclaration||!e)return a;const r=x(e),s=r?n.indexOf(r,a):-1,c=s>=0?n.lastIndexOf("function",s):-1;if(c<a)return a;const u=n.slice(a,c),l=/\basync\s*$/u.exec(u);return l?a+l.index:c}function x(n){return"escapedText"in n?String(n.escapedText):"text"in n&&"string"==typeof n.text?n.text:void 0}function y(n){const t=S(n,"name");if(t)return t;let e=n;for(;e;){const n=S(e,"parent");if(!n)return;if(n.kind===i.SyntaxKind.VariableDeclaration)return S(n,"name");if(!w(n))return;e=n}}function C(n){let t=n;for(;w(t);){const n=S(t,"expression");if(!n)return t;t=n}return t}function w(n){return n.kind===i.SyntaxKind.ParenthesizedExpression||n.kind===i.SyntaxKind.AsExpression||n.kind===i.SyntaxKind.TypeAssertionExpression||n.kind===i.SyntaxKind.SatisfiesExpression}function S(n,t){const e=n[t];return function(n){return"object"==typeof n&&null!==n&&"kind"in n&&"pos"in n&&"end"in n}(e)?e:void 0}function h(n,t){const e=n.slice(0,t).split("\n");return{column:e.at(-1)?.length??0,line:e.length}}function E(n){const t=[];return v(n,n=>{n.kind===i.SyntaxKind.CallExpression&&t.push(n)}),t}function v(n,t){t(n),n.forEachChild(n=>{v(n,t)})}function F(n,t){for(const e of t)e.fileName&&n.add(e.fileName)}exports.measureTypeScriptProject=async function(n,i=[]){const s=new e.API({cwd:process.cwd()});try{const e=(await s.updateSnapshot({openProject:n})).getProjects(),c=await async function(n){const t=new Map;for(const e of n)t.set(await a(e),e);return t}(i),u=new Set(c.keys()),l=new Set,f={callExpressionCount:0,configDiagnosticCount:0,declarationDiagnosticCount:0,measuredRootFileCount:0,resolvedCallExpressionCount:0,rootFileCount:0,semanticDiagnosticCount:0,suggestionDiagnosticCount:0,syntacticDiagnosticCount:0},p=[],d=new Set;for(const n of e){const e=await Promise.all(n.rootFiles.map(async n=>({canonicalFile:await a(n),file:n})));f.rootFileCount+=e.length,f.measuredRootFileCount+=e.filter(({canonicalFile:n})=>u.has(n)).length;const i=await n.program.getSyntacticDiagnostics(),s=await n.program.getSemanticDiagnostics(),g=await n.program.getSuggestionDiagnostics(),m=await n.program.getDeclarationDiagnostics(),x=await n.program.getConfigFileParsingDiagnostics();f.syntacticDiagnosticCount+=i.length,f.semanticDiagnosticCount+=s.length,f.suggestionDiagnosticCount+=g.length,f.declarationDiagnosticCount+=m.length,f.configDiagnosticCount+=x.length,F(l,[...i,...s,...g,...m,...x]);const y=0===u.size?e:[...c.entries()].map(([n,t])=>({canonicalFile:n,file:t})),C=t.dirname(n.configFileName),w=await a(C);for(const{canonicalFile:t,file:e}of y){if(d.has(t))continue;const i=await o(n,e,t,C,w);if(!i)continue;d.add(t);const a=E(i);f.callExpressionCount+=a.length;const s=await r(i,n.checker);p.push(...s.map(n=>({...n,file:c.get(t)??e})));for(const t of a)await n.checker.getResolvedSignature(t)&&(f.resolvedCallExpressionCount+=1)}}const g=f.callExpressionCount-f.resolvedCallExpressionCount;return{...f,configFile:n,diagnosticFileCount:l.size,projectCount:e.length,reactComponentFunctions:p,unresolvedCallExpressionCount:g,resolvedCallExpressionRatio:0===f.callExpressionCount?0:f.resolvedCallExpressionCount/f.callExpressionCount}}finally{await s.close()}};
2
- //# sourceMappingURL=typescriptProject.cjs.map
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 +1 @@
1
- {"version":3,"file":"typescriptProject.cjs","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/native-preview/unstable/async';\nimport { getTokenPosOfNode, SyntaxKind, type Node, type SourceFile } from '@typescript/native-preview/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"],"names":["async","getProjectSourceFile","project","file","canonicalFile","configDirectory","canonicalConfigDirectory","candidate","candidates","Set","startsWith","path","sep","add","join","relative","getProjectFileCandidates","sourceFile","program","getSourceFile","canonicalizeFile","realpath","collectReactComponentFunctions","checker","components","root","visitNode","node","isFunctionLikeCandidate","kind","SyntaxKind","VariableDeclaration","initializer","getNodeProperty","unwrappedInitializer","unwrapExpression","undefined","CallExpression","isVariableFunctionCandidate","push","collectReactComponentCandidates","hasReactReference","text","test","isReactComponentCandidate","name","findNameNode","functionNode","findComponentFunctionNode","startOffset","findFunctionStartPosition","startPosition","positionToLineColumn","implementationKey","pos","end","findCandidateName","startColumn","column","startLine","line","componentByKey","Map","component","existingComponent","get","set","values","map","dedupeReactComponentFunctions","type","getTypeAtLocation","componentName","hasReactComponentTypeName","namedType","isUppercaseComponentName","hasReactRenderableReturnType","Boolean","signatures","getSignaturesOfType","SignatureKind","Call","signature","returnType","getReturnTypeOfSignature","isReactRenderableType","typeName","typeToString","FunctionDeclaration","FunctionExpression","ArrowFunction","isComponentImplementationWrapperCall","findComponentImplementationWrapperFunction","expression","expressionName","findCallExpressionName","forEachChild","child","unwrappedChild","tokenPosition","getTokenPosOfNode","functionName","namePosition","indexOf","functionPosition","lastIndexOf","modifierText","slice","asyncMatch","exec","index","String","escapedText","currentNode","parent","isExpressionWrapperNode","ParenthesizedExpression","AsExpression","TypeAssertionExpression","SatisfiesExpression","property","value","isNode","position","lines","split","at","length","collectCallExpressions","calls","visitor","addDiagnosticFiles","files","diagnostics","diagnostic","fileName","configFile","measuredFiles","api","API","cwd","process","projects","updateSnapshot","openProject","getProjects","measuredFileByCanonicalFile","mapMeasuredFilesByCanonicalFile","measuredFileSet","keys","diagnosticFiles","totals","callExpressionCount","configDiagnosticCount","declarationDiagnosticCount","measuredRootFileCount","resolvedCallExpressionCount","rootFileCount","semanticDiagnosticCount","suggestionDiagnosticCount","syntacticDiagnosticCount","reactComponentFunctions","analyzedFiles","projectRootFiles","Promise","all","rootFiles","filter","has","syntacticDiagnostics","getSyntacticDiagnostics","semanticDiagnostics","getSemanticDiagnostics","suggestionDiagnostics","getSuggestionDiagnostics","declarationDiagnostics","getDeclarationDiagnostics","configDiagnostics","getConfigFileParsingDiagnostics","projectAnalysisFiles","size","entries","dirname","configFileName","callExpressions","fileReactComponentFunctions","callExpression","getResolvedSignature","unresolvedCallExpressionCount","diagnosticFileCount","projectCount","resolvedCallExpressionRatio","close"],"mappings":"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"}
1
+ {"version":3,"file":"typescriptProject.cjs","names":["API","path","SignatureKind","SyntaxKind"],"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,2 +1,3 @@
1
- import{realpath as n}from"node:fs/promises";import t from"node:path";import{API as e,SignatureKind as o}from"@typescript/native-preview/unstable/async";import{SyntaxKind as i,getTokenPosOfNode as a}from"@typescript/native-preview/unstable/ast";async function r(n,o=[]){const i=new e({cwd:process.cwd()});try{const e=(await i.updateSnapshot({openProject:n})).getProjects(),a=await async function(n){const t=new Map;for(const e of n)t.set(await c(e),e);return t}(o),r=new Set(a.keys()),l=new Set,f={callExpressionCount:0,configDiagnosticCount:0,declarationDiagnosticCount:0,measuredRootFileCount:0,resolvedCallExpressionCount:0,rootFileCount:0,semanticDiagnosticCount:0,suggestionDiagnosticCount:0,syntacticDiagnosticCount:0},p=[],g=new Set;for(const n of e){const e=await Promise.all(n.rootFiles.map(async n=>({canonicalFile:await c(n),file:n})));f.rootFileCount+=e.length,f.measuredRootFileCount+=e.filter(({canonicalFile:n})=>r.has(n)).length;const o=await n.program.getSyntacticDiagnostics(),i=await n.program.getSemanticDiagnostics(),d=await n.program.getSuggestionDiagnostics(),m=await n.program.getDeclarationDiagnostics(),C=await n.program.getConfigFileParsingDiagnostics();f.syntacticDiagnosticCount+=o.length,f.semanticDiagnosticCount+=i.length,f.suggestionDiagnosticCount+=d.length,f.declarationDiagnosticCount+=m.length,f.configDiagnosticCount+=C.length,b(l,[...o,...i,...d,...m,...C]);const w=0===r.size?e:[...a.entries()].map(([n,t])=>({canonicalFile:n,file:t})),x=t.dirname(n.configFileName),y=await c(x);for(const{canonicalFile:t,file:e}of w){if(g.has(t))continue;const o=await s(n,e,t,x,y);if(!o)continue;g.add(t);const i=k(o);f.callExpressionCount+=i.length;const r=await u(o,n.checker);p.push(...r.map(n=>({...n,file:a.get(t)??e})));for(const t of i)await n.checker.getResolvedSignature(t)&&(f.resolvedCallExpressionCount+=1)}}const d=f.callExpressionCount-f.resolvedCallExpressionCount;return{...f,configFile:n,diagnosticFileCount:l.size,projectCount:e.length,reactComponentFunctions:p,unresolvedCallExpressionCount:d,resolvedCallExpressionRatio:0===f.callExpressionCount?0:f.resolvedCallExpressionCount/f.callExpressionCount}}finally{await i.close()}}async function s(n,e,o,i,a){for(const r of function(n,e,o,i){const a=new Set([n,e]);e.startsWith(`${i}${t.sep}`)&&a.add(t.join(o,t.relative(i,e)));return[...a]}(e,o,i,a)){const t=await n.program.getSourceFile(r);if(t)return t}}async function c(t){try{return await n(t)}catch{return t}}async function u(n,t){const e=[],o=function(n){const t=[];return S(n,n=>{(d(n)||function(n){if(n.kind!==i.VariableDeclaration)return!1;const t=F(n,"initializer"),e=t?E(t):void 0;return!!e&&(d(e)||e.kind===i.CallExpression)}(n))&&t.push(n)}),t}(n),a=(r=n.text,/\bfrom\s+['"]react['"]|\bimport\s+['"]react['"]|\brequire\(\s*['"]react['"]\s*\)/u.test(r));var r;for(const i of o){if(!await l(i,t,a))continue;const o=h(i),r=m(i),s=x(n.text,r,o,n),c=D(n.text,s);e.push({implementationKey:`${r.pos}:${r.end}`,name:o?y(o):void 0,startColumn:c.column,startLine:c.line})}return function(n){const t=new Map;for(const e of n){const n=t.get(e.implementationKey);(!n||!n.name&&e.name)&&t.set(e.implementationKey,e)}return[...t.values()].map(n=>({name:n.name,startColumn:n.startColumn,startLine:n.startLine}))}(e)}async function l(n,t,e){const o=await t.getTypeAtLocation(n);if(!o)return!1;const i=h(n),a=i?y(i):void 0;if(e&&await p(o,n,t))return!0;const r=i?await t.getTypeAtLocation(i):void 0;return!!(e&&r&&await p(r,n,t))||!!function(n){return void 0!==n&&/^[A-Z]/u.test(n)}(a)&&(await f(o,n,t)||Boolean(r&&await f(r,n,t)))}async function f(n,t,e){const i=await e.getSignaturesOfType(n,o.Call);for(const n of i){const o=await e.getReturnTypeOfSignature(n);if(o&&await g(o,t,e))return!0}return!1}async function p(n,t,e){const o=await e.typeToString(n,t);return/\b(?:FC|FunctionComponent|ComponentType|MemoExoticComponent|ForwardRefExoticComponent|LazyExoticComponent)\b/u.test(o)}async function g(n,t,e){const o=await e.typeToString(n,t);return/\b(?:JSX\.Element|React\.JSX\.Element|ReactElement|ReactNode)\b/u.test(o)}function d(n){return!(n.kind===i.FunctionDeclaration&&!F(n,"body"))&&(n.kind===i.FunctionDeclaration||n.kind===i.FunctionExpression||n.kind===i.ArrowFunction)}function m(n){if(d(n))return n;const t=F(n,"initializer"),e=t?E(t):void 0;return e?d(e)?e:C(e)?w(e)??n:n:n}function C(n){if(n.kind!==i.CallExpression)return!1;const t=F(n,"expression"),e=t?function(n){const t=F(n,"name");return y(t||n)}(t):void 0;return"memo"===e||"forwardRef"===e}function w(n){let t;return n.forEachChild(n=>{const e=E(n);return d(e)?t=e:C(e)&&(t=w(e)),t}),t}function x(n,t,e,o){const r=a(t,o);if(t.kind!==i.FunctionDeclaration||!e)return r;const s=y(e),c=s?n.indexOf(s,r):-1,u=c>=0?n.lastIndexOf("function",c):-1;if(u<r)return r;const l=n.slice(r,u),f=/\basync\s*$/u.exec(l);return f?r+f.index:u}function y(n){return"escapedText"in n?String(n.escapedText):"text"in n&&"string"==typeof n.text?n.text:void 0}function h(n){const t=F(n,"name");if(t)return t;let e=n;for(;e;){const n=F(e,"parent");if(!n)return;if(n.kind===i.VariableDeclaration)return F(n,"name");if(!v(n))return;e=n}}function E(n){let t=n;for(;v(t);){const n=F(t,"expression");if(!n)return t;t=n}return t}function v(n){return n.kind===i.ParenthesizedExpression||n.kind===i.AsExpression||n.kind===i.TypeAssertionExpression||n.kind===i.SatisfiesExpression}function F(n,t){const e=n[t];return function(n){return"object"==typeof n&&null!==n&&"kind"in n&&"pos"in n&&"end"in n}(e)?e:void 0}function D(n,t){const e=n.slice(0,t).split("\n");return{column:e.at(-1)?.length??0,line:e.length}}function k(n){const t=[];return S(n,n=>{n.kind===i.CallExpression&&t.push(n)}),t}function S(n,t){t(n),n.forEachChild(n=>{S(n,t)})}function b(n,t){for(const e of t)e.fileName&&n.add(e.fileName)}export{r as measureTypeScriptProject};
2
- //# sourceMappingURL=typescriptProject.js.map
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 +1 @@
1
- {"version":3,"file":"typescriptProject.js","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/native-preview/unstable/async';\nimport { getTokenPosOfNode, SyntaxKind, type Node, type SourceFile } from '@typescript/native-preview/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"],"names":["async","measureTypeScriptProject","configFile","measuredFiles","api","API","cwd","process","projects","updateSnapshot","openProject","getProjects","measuredFileByCanonicalFile","files","Map","file","set","canonicalizeFile","mapMeasuredFilesByCanonicalFile","measuredFileSet","Set","keys","diagnosticFiles","totals","callExpressionCount","configDiagnosticCount","declarationDiagnosticCount","measuredRootFileCount","resolvedCallExpressionCount","rootFileCount","semanticDiagnosticCount","suggestionDiagnosticCount","syntacticDiagnosticCount","reactComponentFunctions","analyzedFiles","project","projectRootFiles","Promise","all","rootFiles","map","canonicalFile","length","filter","has","syntacticDiagnostics","program","getSyntacticDiagnostics","semanticDiagnostics","getSemanticDiagnostics","suggestionDiagnostics","getSuggestionDiagnostics","declarationDiagnostics","getDeclarationDiagnostics","configDiagnostics","getConfigFileParsingDiagnostics","addDiagnosticFiles","projectAnalysisFiles","size","entries","configDirectory","path","dirname","configFileName","canonicalConfigDirectory","sourceFile","getProjectSourceFile","add","callExpressions","collectCallExpressions","fileReactComponentFunctions","collectReactComponentFunctions","checker","push","component","get","callExpression","getResolvedSignature","unresolvedCallExpressionCount","diagnosticFileCount","projectCount","resolvedCallExpressionRatio","close","candidate","candidates","startsWith","sep","join","relative","getProjectFileCandidates","getSourceFile","realpath","components","root","visitNode","node","isFunctionLikeCandidate","kind","SyntaxKind","VariableDeclaration","initializer","getNodeProperty","unwrappedInitializer","unwrapExpression","undefined","CallExpression","isVariableFunctionCandidate","collectReactComponentCandidates","hasReactReference","text","test","isReactComponentCandidate","name","findNameNode","functionNode","findComponentFunctionNode","startOffset","findFunctionStartPosition","startPosition","positionToLineColumn","implementationKey","pos","end","findCandidateName","startColumn","column","startLine","line","componentByKey","existingComponent","values","dedupeReactComponentFunctions","type","getTypeAtLocation","componentName","hasReactComponentTypeName","namedType","isUppercaseComponentName","hasReactRenderableReturnType","Boolean","signatures","getSignaturesOfType","SignatureKind","Call","signature","returnType","getReturnTypeOfSignature","isReactRenderableType","typeName","typeToString","FunctionDeclaration","FunctionExpression","ArrowFunction","isComponentImplementationWrapperCall","findComponentImplementationWrapperFunction","expression","expressionName","findCallExpressionName","forEachChild","child","unwrappedChild","tokenPosition","getTokenPosOfNode","functionName","namePosition","indexOf","functionPosition","lastIndexOf","modifierText","slice","asyncMatch","exec","index","String","escapedText","currentNode","parent","isExpressionWrapperNode","ParenthesizedExpression","AsExpression","TypeAssertionExpression","SatisfiesExpression","property","value","isNode","position","lines","split","at","calls","visitor","diagnostics","diagnostic","fileName"],"mappings":"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"}
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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "code-gauge",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
4
4
  "description": "Measure code metrics with tree-sitter.",
5
5
  "keywords": [
6
6
  "cli",
@@ -45,14 +45,18 @@
45
45
  "verify-full": "wb verify --full"
46
46
  },
47
47
  "dependencies": {
48
- "@typescript/native-preview": "7.0.0-dev.20260615.1",
49
48
  "commander": "15.0.0",
50
49
  "tree-sitter": "^0.21.1",
50
+ "tree-sitter-c": "^0.21.0",
51
+ "tree-sitter-cpp": "^0.22.0",
51
52
  "tree-sitter-go": "^0.21.2",
53
+ "tree-sitter-java": "^0.21.0",
52
54
  "tree-sitter-javascript": "^0.21.4",
53
55
  "tree-sitter-python": "^0.21.0",
56
+ "tree-sitter-ruby": "^0.21.0",
54
57
  "tree-sitter-rust": "^0.21.0",
55
- "tree-sitter-typescript": "^0.21.2"
58
+ "tree-sitter-typescript": "^0.21.2",
59
+ "typescript": "7.0.2"
56
60
  },
57
61
  "devDependencies": {
58
62
  "@tsconfig/node-lts": "24.0.0",
@@ -60,18 +64,18 @@
60
64
  "@types/node": "25.9.4",
61
65
  "@willbooster/oxfmt-config": "1.2.2",
62
66
  "@willbooster/oxlint-config": "1.4.8",
63
- "@willbooster/wb": "13.22.18",
64
- "build-ts": "17.1.27",
67
+ "@willbooster/wb": "13.25.0",
68
+ "build-ts": "17.1.31",
65
69
  "conventional-changelog-conventionalcommits": "9.3.1",
66
70
  "lefthook": "2.1.9",
67
- "oxfmt": "0.56.0",
68
- "oxlint": "1.71.0",
69
- "oxlint-tsgolint": "0.23.0",
71
+ "oxfmt": "0.58.0",
72
+ "oxlint": "1.73.0",
73
+ "oxlint-tsgolint": "0.24.0",
70
74
  "semantic-release": "25.0.5",
71
75
  "sort-package-json": "4.0.0",
72
76
  "vitest": "4.1.9"
73
77
  },
74
- "packageManager": "yarn@4.17.0",
78
+ "packageManager": "yarn@4.17.1",
75
79
  "engines": {
76
80
  "node": ">=24"
77
81
  },
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":""}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":""}