Angular Component Property Renaming via AST Transformation
This challenge focuses on implementing an AST (Abstract Syntax Tree) transformation in an Angular project to automatically rename component properties. This is a common refactoring task, and automating it using AST manipulation can significantly improve developer productivity and reduce errors when dealing with large codebases. You will be provided with a simplified Angular component's TypeScript code and tasked with transforming it to rename a specified property.
Problem Description
You are given a string containing the TypeScript code of a simple Angular component. Your task is to write a TypeScript function that parses this code into an AST, identifies all occurrences of a target property name within the component's class definition (including its usage in the constructor, property declarations, and getter/setter methods), and renames them to a new, specified property name. The function should return the modified TypeScript code as a string, reflecting the property renaming.
Key Requirements:
- AST Parsing: Utilize the
typescriptlibrary to parse the input TypeScript code into an AST. - Target Identification: Accurately identify all instances of the target property name within the component's class body. This includes:
- Property declarations (e.g.,
public oldPropertyName: string;) - Constructor parameter declarations (e.g.,
constructor(private oldPropertyName: string)) - Getter/Setter methods (e.g.,
get oldPropertyName() { return this._oldPropertyName; })
- Property declarations (e.g.,
- Renaming: Replace all identified occurrences of the target property name with the new property name.
- Code Generation: Use the
typescriptlibrary to generate TypeScript code from the modified AST. - Preservation: Ensure that the overall structure and syntax of the component code remain valid after the transformation. Comments and other code elements should be preserved as much as possible.
Expected Behavior:
The function should take the component's TypeScript code, the old property name, and the new property name as input. It should return the modified TypeScript code with the property renamed. If the old property name is not found, the function should return the original code unchanged.
Edge Cases to Consider:
- Property Name Conflicts: Handle cases where the new property name already exists in the component. (For simplicity, assume this doesn't happen in the provided input, but consider it for robustness).
- Complex Property Types: The property type can be simple (e.g.,
string,number) or complex (e.g.,MyInterface,Array<string>). - Decorators: The component may have decorators (e.g.,
@Component). These should be preserved. - Template Literals: The property might be used within template literals.
- Comments: Comments should be preserved.
- Multiple Occurrences: The property name might appear multiple times within the component.
Examples
Example 1:
Input: `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{oldPropertyName}}</p>' \n})\nexport class MyComponent {\n public oldPropertyName: string = 'Hello';\n constructor(private oldPropertyName: string) {}\n get oldPropertyName() { return this.oldPropertyName; }\n set oldPropertyName(value: string) { this.oldPropertyName = value; }\n}`
Output: `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{newPropertyName}}</p>' \n})\nexport class MyComponent {\n public newPropertyName: string = 'Hello';\n constructor(private newPropertyName: string) {}\n get newPropertyName() { return this.newPropertyName; }\n set newPropertyName(value: string) { this.newPropertyName = value; }\n}`
Explanation: All instances of `oldPropertyName` have been replaced with `newPropertyName`.
**Example 2:**
Input: import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{oldPropertyName}}</p>' \n})\nexport class MyComponent {\n private oldPropertyName: number = 123;\n}
Output: import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{newPropertyName}}</p>' \n})\nexport class MyComponent {\n private newPropertyName: number = 123;\n}
Explanation: The property name has been updated.
Example 3:
Input: `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{oldPropertyName}}</p>' \n})\nexport class MyComponent { \n}`
Output: `import { Component } from '@angular/core';\n\n@Component({\n selector: 'app-my-component',\n template: '<p>{{oldPropertyName}}</p>' \n})\nexport class MyComponent { \n}`
Explanation: The old property name was not found, so the original code is returned.
Constraints
- Input String Length: The input TypeScript code string will be no longer than 1000 characters.
- Property Name Length: The old and new property names will be strings with a maximum length of 30 characters.
- Performance: The function should complete within 1 second for the given input size.
- Dependencies: You are allowed to use the
typescriptlibrary. No other external libraries are permitted.
Notes
- Focus on accurately identifying and replacing the property name within the component's class definition.
- The provided input will be a valid Angular component's TypeScript code.
- Consider using the
typescriptlibrary's AST traversal capabilities to efficiently locate and modify the target property name. - Error handling is not required for this challenge. Assume the input is well-formed.
- The goal is to demonstrate your understanding of AST transformation and its application in Angular development.
- You do not need to handle all possible Angular constructs, focus on the core property renaming task.