Skip to content

Commit 4ac25b2

Browse files
crisbetopkozlowski-opensource
authored andcommitted
perf(migrations): avoid re-traversing nodes when resolving bootstrap call dependencies (#49010)
Fixes that the migration was unnecessarily traversing top-level nodes. This was a large performance bottle-neck, because it involves a lot of language service lookups. PR Close #49010
1 parent 521ccfb commit 4ac25b2

File tree

2 files changed

+54
-2
lines changed

2 files changed

+54
-2
lines changed

packages/core/schematics/ng-generate/standalone-migration/standalone-bootstrap.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ function addNodesToCopy(
511511
function findAllSameFileReferences(
512512
rootNode: ts.Node, nodeLookup: NodeLookup, referenceResolver: ReferenceResolver): Set<ts.Node> {
513513
const results = new Set<ts.Node>();
514+
const traversedTopLevelNodes = new Set<ts.Node>();
514515
const excludeStart = rootNode.getStart();
515516
const excludeEnd = rootNode.getEnd();
516517

@@ -531,14 +532,21 @@ function findAllSameFileReferences(
531532
if (results.has(ref)) {
532533
continue;
533534
}
534-
const closestTopLevel = closestNode(ref, isTopLevelStatement);
535+
535536
results.add(ref);
536537

538+
const closestTopLevel = closestNode(ref, isTopLevelStatement);
539+
// Avoid re-traversing the same top-level nodes since we know what the result will be.
540+
if (!closestTopLevel || traversedTopLevelNodes.has(closestTopLevel)) {
541+
continue;
542+
}
543+
537544
// Keep searching, starting from the closest top-level node. We skip import declarations,
538545
// because we already know about them and they may put the search into an infinite loop.
539-
if (closestTopLevel && !ts.isImportDeclaration(closestTopLevel) &&
546+
if (!ts.isImportDeclaration(closestTopLevel) &&
540547
isOutsideRange(
541548
excludeStart, excludeEnd, closestTopLevel.getStart(), closestTopLevel.getEnd())) {
549+
traversedTopLevelNodes.add(closestTopLevel);
542550
walk(closestTopLevel);
543551
}
544552
}

packages/core/schematics/test/standalone_migration_spec.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3259,4 +3259,48 @@ describe('standalone migration', () => {
32593259
export class AppComponent {}
32603260
`));
32613261
});
3262+
3263+
it('should be able to migrate a bootstrapModule call where the root component does not belong to the bootstrapped component',
3264+
async () => {
3265+
writeFile('main.ts', `
3266+
import {AppModule} from './app/app.module';
3267+
import {platformBrowser} from '@angular/platform-browser';
3268+
3269+
platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e));
3270+
`);
3271+
3272+
writeFile('./app/root.module.ts', `
3273+
import {NgModule, Component} from '@angular/core';
3274+
3275+
@Component({selector: 'root-comp', template: '', standalone: true})
3276+
export class Root {}
3277+
3278+
@NgModule({imports: [Root], exports: [Root]})
3279+
export class RootModule {}
3280+
`);
3281+
3282+
writeFile('./app/app.module.ts', `
3283+
import {NgModule, Component} from '@angular/core';
3284+
import {RootModule, Root} from './root.module';
3285+
3286+
@NgModule({
3287+
imports: [RootModule],
3288+
bootstrap: [Root]
3289+
})
3290+
export class AppModule {}
3291+
`);
3292+
3293+
await runMigration('standalone-bootstrap');
3294+
3295+
expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(`
3296+
import {AppModule} from './app/app.module';
3297+
import {platformBrowser, bootstrapApplication} from '@angular/platform-browser';
3298+
import {importProvidersFrom} from '@angular/core';
3299+
import {RootModule, Root} from './app/root.module';
3300+
3301+
bootstrapApplication(Root, {
3302+
providers: [importProvidersFrom(RootModule)]
3303+
}).catch(e => console.error(e));
3304+
`));
3305+
});
32623306
});

0 commit comments

Comments
 (0)