diff --git a/extract.ts b/extract.ts index 549481a..64100bd 100644 --- a/extract.ts +++ b/extract.ts @@ -9,6 +9,7 @@ const allFunctions: string[] = []; const calledFunctions: Map = new Map(); let currentFunction = undefined; // to keep track of which function we are inside +let currentClass = undefined; // to keep track of which class we are inside // ================================================================================================= @@ -34,6 +35,25 @@ function extractFunctionCalls(node: ts.Node, sourceFile: ts.SourceFile, indentLe }); } + // e.g. `this.hello()` + if (ts.isMethodDeclaration(node)) { + if (node.name && ts.isIdentifier(node.name)) { + const methodName: string = node.name.getText(sourceFile); + const declaredFunction: string = currentClass ? `${currentClass}.${methodName}` : methodName; + if (!allFunctions.includes(declaredFunction)) { // This guard is necessary to prevent duplicates of class methods from appearing. + updateDeclaredFunctions(declaredFunction); + } + } + } + + // e.g. `constructor()` + if (ts.isConstructorDeclaration(node)) { + const declaredFunction = currentClass ? `${currentClass}.constructor` : 'constructor'; + if (!allFunctions.includes(declaredFunction)) { + updateDeclaredFunctions(declaredFunction); + } + } + // Arrow function if ( ts.isVariableDeclaration(node) && @@ -51,16 +71,43 @@ function extractFunctionCalls(node: ts.Node, sourceFile: ts.SourceFile, indentLe // First child must be `Identifier` // examples of what gets skipped: `fs.readFile('lol.json')` or `ipc.on('something', () => {})` if (ts.isCallExpression(node)) { - const child = node.getChildAt(0, sourceFile); - if (ts.isIdentifier(child)) { - const calledFunction: string = child.getText(sourceFile); + const expression = node.expression; + if (ts.isIdentifier(expression)) { + const calledFunction: string = expression.getText(sourceFile); updateCalledFunctions(calledFunction); } + + // method call: ClassName.foo() + if (ts.isPropertyAccessExpression(expression)) { + const methodName = expression.name.getText(sourceFile); + + if (expression.expression.kind === ts.SyntaxKind.ThisKeyword && currentClass) { + updateCalledFunctions(`${currentClass}.${methodName}`); + } else if (ts.isIdentifier(expression.expression)) { + const classOrObject = expression.expression.getText(sourceFile); + updateCalledFunctions(`${classOrObject}.${methodName}`); + } + } + } + + // e.g. `new ClassName()` + if (ts.isNewExpression(node) && ts.isIdentifier(node.expression)) { + updateCalledFunctions(node.expression.getText(sourceFile)); } + // e.g. `class ClassName` + if (ts.isClassDeclaration(node) && node.name) { + currentClass = node.name.getText(sourceFile); + } + // logNode(node, sourceFile, indentLevel); node.forEachChild(child => extractFunctionCalls(child, sourceFile, indentLevel + 1)); + + // Exit the current class + if (ts.isClassDeclaration(node)) { + currentClass = undefined; + } } /** @@ -161,7 +208,7 @@ export function processFiles(filenames: string[]) { } }); - console.log(calledFunctions); + // console.log(calledFunctions); const functions = { all: allFunctions,