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:
- Component Declaration Discovery: Identify where a component is declared within an Angular application. This includes:
- Declarations within
NgModules (declarationsarray). - Declarations for routing (
componentproperty in route configurations).
- Declarations within
- 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.
- File and Line Number Reporting: The output should clearly indicate the file path and the specific line number where each reference is found.
- Case-Insensitive Matching (for selectors): Component selectors in templates can sometimes be written with different casing. The search should be case-insensitive.
- 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
MyButtonComponentis found in thedeclarationsarray ofAppModuleat line 5. - The
app-my-buttonselector is found inAppComponent'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
UserProfileComponentis found as thecomponentproperty in the route configuration withinAppRoutingModuleat 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
declarationsarrays. - The search for route declarations should look for the
componentproperty in route configurations. - The search for template usage should look for the component's
selectorproperty. - 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
NgModuledecorators. - For routing, focus on static analysis of route configurations.
- When finding component usages in templates, remember that the selector is often defined in the
@Componentdecorator. You'll need to correlate the component class name to its selector. - Think about how to associate imports with actual declarations and usages.