-
Notifications
You must be signed in to change notification settings - Fork 13
refactor(schematics): extract migration rules into separate files #1502
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
mistrykaran91
commented
Feb 11, 2026
- Split monolithic element-migration.ts into individual rule files
- Move template/symbol manipulation utilities to respective rule files
- I confirm that this MR follows the contribution guidelines.
- Split monolithic element-migration.ts into individual rule files - Move template/symbol manipulation utilities to respective rule files
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code Review
This pull request refactors the monolithic element migration schematic into smaller, more manageable rule files. This is a great step towards improving the maintainability and readability of the schematics code.
However, I've identified a few issues with the current implementation:
- There's a significant performance regression due to the way the new rules are chained, causing project files to be parsed multiple times.
- Some utility interfaces and functions have been moved to locations that create unnecessary coupling between different migrations.
- A minor issue with a duplicated copyright header.
Please see my detailed comments for suggestions on how to address these points.
| return chain([ | ||
| componentPropertyNameMigrationRule(path, componentPropertyNameChanges), | ||
| symbolRenamingMigrationRule(path, symbolRenamingChanges), | ||
| attributeSelectorMigrationRule(path, attributeSelectorChanges), | ||
| elementSelectorMigrationRule(path, elementSelectorChanges), | ||
| symbolRemovalMigrationRule(path, symbolRemovalChanges), | ||
| classMemberReplacementMigrationRule(path, classMemberReplacementChanges) | ||
| ])(tree, context); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This refactoring is great for organizing the migration logic into separate files. However, using chain this way introduces a significant performance issue. Each rule in the chain calls discoverSourceFiles independently, which is an expensive operation as it involves creating a TypeScript program. This means the project's files will be parsed multiple times (once for each rule in the chain).
A more performant approach would be to loop through the source files once and apply all migration rules for each file. This would be similar to the original implementation but with the logic for each migration type extracted into its own function.
For example, you could have a structure like this:
export const elementMigrationRule = (
options: { path: string },
migrationData: ElementMigrationData
): Rule => {
return async (tree: Tree, context: SchematicContext) => {
for await (const { path: filePath, sourceFile, typeChecker } of discoverSourceFiles(
tree,
context,
options.path
)) {
// Apply all migrations for the current file
applyComponentPropertyNameMigration(tree, filePath, sourceFile, migrationData.componentPropertyNameChanges);
applySymbolRenamingMigration(tree, filePath, sourceFile, typeChecker, migrationData.symbolRenamingChanges);
// ... and so on for other migrations
}
return tree;
};
};This would retain the code organization benefits while avoiding the performance overhead of repeatedly parsing the project files.
| /** | ||
| * Copyright (c) Siemens 2016 - 2025 | ||
| * SPDX-License-Identifier: MIT | ||
| */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| export interface Replacement { | ||
| start: number; | ||
| end: number; | ||
| text: string; | ||
| } | ||
|
|
||
| export interface ChangeInstruction { | ||
| start: number; | ||
| width: number; | ||
| newNode: ts.Node; | ||
| } | ||
|
|
||
| export interface VisitFunctionCallOptions { | ||
| sourceFile: ts.SourceFile; | ||
| moduleSpecifier: string | RegExp; | ||
| className?: string; | ||
| functionName: string; | ||
| visitor: (node: ts.CallExpression) => void; | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The VisitFunctionCallOptions interface and its related visitStaticFunctionCalls function (which has been moved to ngx-translate/index.ts) are general-purpose utilities. Placing the interface here within the element-migration rules creates an unnecessary coupling, as seen with the ngx-translate migration now depending on this file.
To improve modularity and reusability, consider moving VisitFunctionCallOptions and other shared interfaces like ChangeInstruction and Replacement to a more central location, like a new interfaces.ts file under schematics/utils. Similarly, the visitStaticFunctionCalls function should probably be moved back to ts-utils.ts.
|
Documentation. Coverage Reports: |