By ChatGPT & Benji Asperheim | 2025-07-09

Reverse Engineering Angular Components with Python

Reverse engineering and understanding complex Angular components can be a significant challenge, especially when dealing with legacy codebases or extensive projects. However, Python scripting can automate much of this effort, providing insights into component methods, their usage, and interactions.

In this article, we'll explore the search_angular_components.py script, which thoroughly scans Angular component TypeScript and HTML files to extract valuable information about methods, their calls, documentation, and usage contexts.

Check out the complete search_angular_components.py' Python code in the gist file for full details.

NOTE: Make sure to change directory = 'frontend/src/app' in the Python script to match the actual relative path of your Angular components.

Terminal screenshot of the Angular component results from Python search_angular_components.py script

Why Reverse Engineer Angular Components?

Here are some reasons to use a Python script like this to parse and evaluate method declarations and calls in an Angular component.

Identify Unused or Dead Methods

Unused methods can clutter codebases and complicate maintenance. Detecting them early simplifies refactoring and improves performance and readability.

Automatically Generate Accurate Documentation

Accurate, real-time documentation derived directly from the source code ensures consistency and greatly improves developer efficiency, especially in large teams.

Understand Usage Patterns

Gaining insight into how methods are called within TypeScript and HTML templates helps developers grasp the component's logic flow, enabling quicker and safer code modifications.

Facilitate Easier Refactoring and Onboarding

Detailed insights into component structure and behavior help new developers understand existing logic faster, significantly reducing onboarding time and simplifying refactoring efforts.

Provide Context for Automation Tools

Clear context and detailed component analysis serve as the foundation for developing further automation tools, such as linters, testing frameworks, or continuous integration checks.

Detailed Steps of the Script

Step 1: Component File Discovery

  • find_component_files(directory, match_string):

Recursively searches for .ts files containing Angular component decorators (@Component) matching a given selector.

Step 2: Method Extraction

  • extract_method_blocks(lines):

Parses the TypeScript file to identify methods, their visibility, method type (async, getter/setter), and signature.

Step 3: JSDoc Extraction

  • extract_jsdoc(lines, method_line_num):

Retrieves and cleans the JSDoc comments associated with method declarations for improved understanding and documentation.

Step 4: Method Call Detection

  • extract_methods_and_calls(file_path):

Identifies method calls within TypeScript and HTML templates, adding important usage context to each method.

Step 5: Contextual Analysis

  • find_parent_method_block(method_blocks, call_line):

Finds the enclosing method and block conditions (if, for, switch, etc.) to provide additional context on method interactions.

  • find_enclosing_condition(lines, call_line_num, parent_method_line):

Identifies the nearest enclosing conditional or loop construct for method calls.

Step 6: HTML Method Calls Extraction

  • extract_html_method_calls(html_path, methods):

Scans associated HTML templates to find method usage within event bindings or direct calls.

Step 7: Printing Results

  • print_results(file_path, methods):

Outputs comprehensive results, clearly highlighting methods, their contexts, usage, and associated documentation.

Running the Script

To analyze components with a Component TS file's selector: "" containing the word "example":

python3 search_angular_components.py --match example

Sample Output

Given the following Angular component (example.component.ts):

import { Component, OnInit } from "@angular/core";

@Component({
  selector: "app-example",
  templateUrl: "./example.component.html",
})
export class ExampleComponent implements OnInit {
  public counter = 0;
  private items: string[] = [];

  ngOnInit() {
    this.increment();
    this.logCurrentItem();
    if (this.counter > 0) {
      this.loadItems();
    }
  }

  /**
   * Increments the counter
   */
  public increment(): void {
    this.counter++;
    this.myPrivateLogger(this.counter);
  }

  /**
   * Loads items asynchronously from a pretend API
   */
  private async loadItems(): Promise<void> {
    this.items = await Promise.resolve(["apple", "banana", "cherry"]);
  }

  private myPrivateLogger(value: any) {
    console.log("log:", value);
  }

  /**
   * Returns the current item
   */
  public get currentItem(): string {
    return this.items[this.counter % this.items.length] || "none";
  }

  /**
   * Logs the current item to the console
   */
  public logCurrentItem(): void {
    console.log(this.currentItem);
  }

  /**
   * Removes an item
   * @param item The item to remove
   */
  public removeItem(item: string): void {
    const index = this.items.indexOf(item);
    if (index !== -1) {
      this.items.splice(index, 1);
    }
  }

  private unusedHelperMethod(): void {
    console.log("This method is never called");
  }

  onInputChange(event: Event): string {
    const input = event.target as HTMLInputElement;
    return input.value;
  }

  public set user(name: string) {
    console.log(`User set to: ${name}`);
  }

  public resetCounter(): void {
    this.counter = 0;
  }
}

And its associated template (example.component.html):

<h1>Example Component</h1>

<p>Current: {{ currentItem }}</p>

<button (click)="increment()">Increment</button>
<button (click)="removeItem('banana')">Remove Banana</button>
<button (click)="resetCounter()">Reset</button>
<input type="text" (input)="user = onInputChange($event)" placeholder="Set user name" />

Sample Output

You'll get a detailed output like the following:

--- Analyzing frontend/src/app/example.component.ts ---

Method: ngOnInit() [public] [L11]
  No calls found.
Method: increment() [public] [L22]
  JS Doc:
    Increments the counter
  TS Calls:
    L12: this.increment();
      └── Parent Method: public ngOnInit() [L11]
  HTML Calls (example.component.html):
    L5:
Method: loadItems() [private async] [L30]
  JS Doc:
    Loads items asynchronously from a pretend API
  TS Calls:
    L15: this.loadItems();
      └── Inside: if (this.counter > 0) { [L14]
      └── Parent Method: public ngOnInit() [L11]
Method: unusedHelperMethod() [private] [L63]
  No calls found.
...
--- Finished --
Component TS: frontend/src/app/example.component.ts
Component HTML: frontend/src/app/example.component.html

The script clearly outputs the following information for each method:

  • Method name and signature
  • Visibility and type annotations (public, private, async, etc.)
  • JSDoc documentation (if available)
  • TypeScript method calls (line number, context, and enclosing methods)
  • HTML template method calls (line number and code snippet)
  • Indications of unused methods

This detailed output assists developers in quickly understanding the interactions and documentation of component methods.

Conclusion

Utilizing this Python script significantly streamlines the process of auditing, understanding, and documenting Angular components. It provides immediate clarity on method usage, relationships, and documentation quality, facilitating improved development practices and more maintainable code.

Explore, refine, and integrate this approach into your workflow to keep your Angular projects transparent and well-documented.

Discover expert insights and tutorials on adaptive software development, Python, DevOps, creating website builders, and more at Learn Programming. Elevate your coding skills today!