fix(migrations): avoid internal modules when generating imports (#48958)

Adds some logic to prefer non-Angular-internal modules when generating imports. This allows us to generate better code for some cases like the `ɵInternalFormsSharedModule` in Forms.

Also adds some logic to prefer symbols that are already in the same file.

Fixes #48942.

PR Close #48958
This commit is contained in:
Kristiyan Kostadinov 2023-02-05 11:23:35 +01:00 committed by Andrew Scott
parent 49a7c9f94a
commit 32cf4e5cb9
2 changed files with 54 additions and 2 deletions

View file

@ -310,6 +310,7 @@ function findImportLocation(
target: Reference<NamedClassDeclaration>, inComponent: Reference<ts.ClassDeclaration>,
importMode: PotentialImportMode, typeChecker: TemplateTypeChecker): PotentialImport|null {
const importLocations = typeChecker.getPotentialImportsFor(target, inComponent.node, importMode);
let firstSameFileImport: PotentialImport|null = null;
let firstModuleImport: PotentialImport|null = null;
for (const location of importLocations) {
@ -318,12 +319,17 @@ function findImportLocation(
if (location.kind === PotentialImportKind.Standalone) {
return location;
}
if (location.kind === PotentialImportKind.NgModule && !firstModuleImport) {
if (!location.moduleSpecifier && !firstSameFileImport) {
firstSameFileImport = location;
}
if (location.kind === PotentialImportKind.NgModule && !firstModuleImport &&
// ɵ is used for some internal Angular modules that we want to skip over.
!location.symbolName.startsWith('ɵ')) {
firstModuleImport = location;
}
}
return firstModuleImport;
return firstSameFileImport || firstModuleImport || importLocations[0] || null;
}
/**

View file

@ -970,6 +970,52 @@ describe('standalone migration', () => {
expect(myCompContent).toContain('imports: [ButtonModule]');
});
it('should not reference internal modules', async () => {
writeFile('./should-migrate/module.ts', `
import {NgModule} from '@angular/core';
import {MyComp} from './comp';
import {ɵButtonModule} from '../do-not-migrate/button.module';
@NgModule({imports: [ɵButtonModule], declarations: [MyComp]})
export class Mod {}
`);
writeFile('./should-migrate/comp.ts', `
import {Component} from '@angular/core';
@Component({selector: 'my-comp', template: '<my-button>Hello</my-button>'})
export class MyComp {}
`);
writeFile('./do-not-migrate/button.module.ts', `
import {NgModule, forwardRef} from '@angular/core';
import {MyButton} from './button';
@NgModule({
imports: [forwardRef(() => ɵButtonModule)],
exports: [forwardRef(() => ɵButtonModule)]
})
export class ExporterModule {}
@NgModule({declarations: [MyButton], exports: [MyButton]})
export class ɵButtonModule {}
`);
writeFile('./do-not-migrate/button.ts', `
import {Component} from '@angular/core';
@Component({selector: 'my-button', template: '<ng-content></ng-content>'})
export class MyButton {}
`);
await runMigration('convert-to-standalone', './should-migrate');
const myCompContent = tree.readContent('./should-migrate/comp.ts');
expect(myCompContent)
.toContain(`import { ExporterModule } from '../do-not-migrate/button.module';`);
expect(myCompContent).toContain('imports: [ExporterModule]');
});
it('should migrate tests with a component declared through TestBed', async () => {
writeFile('app.spec.ts', `
import {NgModule, Component} from '@angular/core';