Hone logo
Hone
Problems

Angular Find Component References

This challenge focuses on implementing a "find references" feature within an Angular application. This functionality is crucial for developers to understand where a particular component is being used across the codebase, aiding in refactoring, debugging, and understanding component dependencies.

Problem Description

You are tasked with building a service in Angular that can identify all the places where a specific Angular component is declared and used. This service will take the name of a component as input and return a list of all files and their corresponding line numbers where that component is either declared (in a module or routed) or used (in a template).

Key Requirements:

  1. Component Declaration Discovery: Identify where a component is declared within an Angular application. This includes:
    • Declarations within NgModules (declarations array).
    • Declarations for routing (component property in route configurations).
  2. Component Usage Discovery: Identify where a component is used in the templates of other components. This means looking for the component's selector within HTML templates.
  3. File and Line Number Reporting: The output should clearly indicate the file path and the specific line number where each reference is found.
  4. Case-Insensitive Matching (for selectors): Component selectors in templates can sometimes be written with different casing. The search should be case-insensitive.
  5. Handling Dynamic Usage: While a full AST (Abstract Syntax Tree) parsing of TypeScript code for dynamic component loading is outside the scope of this challenge, prioritize static template usage.

Expected Behavior:

The service should return an array of objects, where each object represents a reference and has the following structure:

interface ComponentReference {
  filePath: string;
  lineNumber: number;
  type: 'declaration' | 'usage'; // 'declaration' for module/routing, 'usage' for template
  description?: string; // Optional: e.g., "Declared in MyModule", "Used in MyComponentTemplate"
}

Edge Cases:

  • Components not declared anywhere.
  • Components not used anywhere.
  • Components declared but never used.
  • Components used but not properly declared (though this is a valid TypeScript error, the finder should still report usage).
  • Nested components (e.g., a component used within the template of another component).
  • Components used via routing.

Examples

Example 1:

Assume the following simplified file structure and content:

src/app/components/my-button/my-button.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-my-button', // This is what we'll be searching for
  templateUrl: './my-button.component.html',
  styleUrls: ['./my-button.component.css']
})
export class MyButtonComponent {}

src/app/app.module.ts

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { MyButtonComponent } from './components/my-button/my-button.component'; // Import
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
    MyButtonComponent // Declaration here
  ],
  imports: [
    BrowserModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

src/app/app.component.html

<h1>Welcome</h1>
<app-my-button></app-my-button> <!-- Usage here -->

Input: componentName = 'MyButtonComponent'

Output:

[
  {
    "filePath": "src/app/app.module.ts",
    "lineNumber": 5,
    "type": "declaration",
    "description": "Declared in AppModule"
  },
  {
    "filePath": "src/app/app.component.html",
    "lineNumber": 2,
    "type": "usage",
    "description": "Used in AppComponentTemplate"
  }
]

Explanation:

  • The MyButtonComponent is found in the declarations array of AppModule at line 5.
  • The app-my-button selector is found in AppComponent's template at line 2.

Example 2:

Assume the following simplified file structure and content:

src/app/components/user-profile/user-profile.component.ts

import { Component } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  templateUrl: './user-profile.component.html',
  styleUrls: ['./user-profile.component.css']
})
export class UserProfileComponent {}

src/app/app-routing.module.ts

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { UserProfileComponent } from './components/user-profile/user-profile.component'; // Import

const routes: Routes = [
  { path: 'profile', component: UserProfileComponent } // Route declaration here
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

Input: componentName = 'UserProfileComponent'

Output:

[
  {
    "filePath": "src/app/app-routing.module.ts",
    "lineNumber": 5,
    "type": "declaration",
    "description": "Declared in AppRoutingModule (route config)"
  }
]

Explanation:

  • The UserProfileComponent is found as the component property in the route configuration within AppRoutingModule at line 5. It is not declared in any other module or used in any template in this simplified example.

Constraints

  • The analysis should be performed on a simulated Angular project structure. You will be provided with file paths and their content.
  • The component name provided as input will be the TypeScript class name (e.g., MyButtonComponent).
  • The service should be written in TypeScript.
  • The search for declarations in modules should look for imports and declarations arrays.
  • The search for route declarations should look for the component property in route configurations.
  • The search for template usage should look for the component's selector property.
  • Performance is not a primary concern for this challenge, but the solution should be reasonably efficient for a moderate-sized project simulation.

Notes

  • Consider how to parse the TypeScript and HTML content. You might need to simulate basic parsing logic or use regular expressions for this challenge.
  • For declarations in modules, focus on static analysis of NgModule decorators.
  • For routing, focus on static analysis of route configurations.
  • When finding component usages in templates, remember that the selector is often defined in the @Component decorator. You'll need to correlate the component class name to its selector.
  • Think about how to associate imports with actual declarations and usages.
Loading editor...
typescript