Last active
February 20, 2025 07:44
-
-
Save tusharxoxoxo/ca8812602ac91596523b8d70485f336b to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//1st implementation, without using treesitter | |
import * as fs from 'fs'; | |
import * as path from 'path'; | |
import { fileURLToPath } from 'url'; | |
const __filename = fileURLToPath(import.meta.url); | |
const __dirname = path.dirname(__filename); | |
function isJSDocComment(comment: string): boolean { | |
return comment.startsWith('/**') && comment.endsWith('*/'); | |
} | |
function extractJSDocAndFunctions(code: string): { functions: Array<{ name: string; hasJSDoc: boolean }> } { | |
const lines = code.split('\n'); | |
const functions: Array<{ name: string; hasJSDoc: boolean }> = []; | |
let currentJSDoc = ''; | |
for (let i = 0; i < lines.length; i++) { | |
const line = lines[i].trim(); | |
if (line.startsWith('/**')) { | |
currentJSDoc = line; | |
while (i + 1 < lines.length && !lines[i + 1].includes('*/')) { | |
i++; | |
currentJSDoc += '\n' + lines[i]; | |
} | |
if (i + 1 < lines.length && lines[i + 1].includes('*/')) { | |
i++; | |
currentJSDoc += '\n' + lines[i].trim(); | |
} | |
} | |
else if (line.startsWith('function')) { | |
const functionMatch = line.match(/function\s+(\w+)/); | |
if (functionMatch) { | |
functions.push({ | |
name: functionMatch[1], | |
hasJSDoc: isJSDocComment(currentJSDoc) | |
}); | |
} | |
currentJSDoc = ''; | |
} | |
} | |
return { functions }; | |
} | |
function analyzeJSDocCoverage(filePath: string): void { | |
const code = fs.readFileSync(filePath, 'utf8'); | |
const { functions } = extractJSDocAndFunctions(code); | |
const totalFunctions = functions.length; | |
const functionsWithJSDoc = functions.filter(f => f.hasJSDoc).length; | |
const coverage = (functionsWithJSDoc / totalFunctions) * 100; | |
console.log('JSDoc Coverage Analysis:'); | |
console.log('------------------------'); | |
console.log(`Total functions: ${totalFunctions}`); | |
console.log(`Functions with JSDoc: ${functionsWithJSDoc}`); | |
console.log(`Coverage: ${coverage.toFixed(2)}%`); | |
console.log('\nDetailed Analysis:'); | |
console.log('------------------------'); | |
functions.forEach(func => { | |
console.log(`${func.name}: ${func.hasJSDoc ? '✓ Has JSDoc' : '✗ No JSDoc'}`); | |
}); | |
} | |
const filePath = path.join(__dirname, 'jj.js'); | |
analyzeJSDocCoverage(filePath); | |
//another implementation using treesitter | |
import * as fs from 'fs'; | |
import * as path from 'path'; | |
import { fileURLToPath } from 'url'; | |
import Parser from 'tree-sitter'; | |
import JavaScript from 'tree-sitter-javascript'; | |
const __filename = fileURLToPath(import.meta.url); | |
const __dirname = path.dirname(__filename); | |
interface FunctionInfo { | |
name: string; | |
hasJSDoc: boolean; | |
startLine: number; | |
} | |
function isJSDocComment(comment: string): boolean { | |
return comment.trim().startsWith('/**') && comment.trim().endsWith('*/'); | |
} | |
function findPrecedingComment(code: string, startLine: number): string { | |
const lines = code.split('\n'); | |
let comment = ''; | |
let currentLine = startLine - 1; | |
while (currentLine >= 0) { | |
const line = lines[currentLine].trim(); | |
if (line === '') { | |
currentLine--; | |
continue; | |
} | |
if (line.startsWith('/*')) { | |
comment = line; | |
while (currentLine + 1 < lines.length && !lines[currentLine].includes('*/')) { | |
currentLine++; | |
comment += '\n' + lines[currentLine]; | |
} | |
break; | |
} | |
break; | |
} | |
return comment; | |
} | |
function extractJSDocAndFunctions(code: string): { functions: FunctionInfo[] } { | |
const parser = new Parser(); | |
parser.setLanguage(JavaScript); | |
const tree = parser.parse(code); | |
const functions: FunctionInfo[] = []; | |
// Query to find all function declarations and function expressions | |
const query = JavaScript.query(` | |
(function_declaration name: (identifier) @func_name) @function | |
(function name: (identifier) @func_name) @function | |
(method_definition name: (property_identifier) @func_name) @function | |
(arrow_function) @function | |
`); | |
const matches = query.matches(tree.rootNode); | |
for (const match of matches) { | |
const funcNode = match.captures.find(capture => capture.name === 'function')?.node; | |
const nameNode = match.captures.find(capture => capture.name === 'func_name')?.node; | |
if (funcNode) { | |
const startLine = funcNode.startPosition.row; | |
const comment = findPrecedingComment(code, startLine); | |
functions.push({ | |
name: nameNode ? nameNode.text : '(anonymous)', | |
hasJSDoc: isJSDocComment(comment), | |
startLine | |
}); | |
} | |
} | |
return { functions }; | |
} | |
function analyzeJSDocCoverage(filePath: string): void { | |
const code = fs.readFileSync(filePath, 'utf8'); | |
const { functions } = extractJSDocAndFunctions(code); | |
// Sort functions by line number for clearer output | |
functions.sort((a, b) => a.startLine - b.startLine); | |
const totalFunctions = functions.length; | |
const functionsWithJSDoc = functions.filter(f => f.hasJSDoc).length; | |
const coverage = totalFunctions > 0 ? (functionsWithJSDoc / totalFunctions) * 100 : 0; | |
console.log('JSDoc Coverage Analysis:'); | |
console.log('------------------------'); | |
console.log(`Total functions: ${totalFunctions}`); | |
console.log(`Functions with JSDoc: ${functionsWithJSDoc}`); | |
console.log(`Coverage: ${coverage.toFixed(2)}%`); | |
console.log('\nDetailed Analysis:'); | |
console.log('------------------------'); | |
functions.forEach(func => { | |
console.log(`${func.name} (line ${func.startLine + 1}): ${func.hasJSDoc ? '✓ Has JSDoc' : '✗ No JSDoc'}`); | |
}); | |
} | |
const filePath = path.join(__dirname, 'test.js'); | |
analyzeJSDocCoverage(filePath); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment