From 660fbf5d2755739b010bfaa23a73406046df69bf Mon Sep 17 00:00:00 2001 From: cexbrayat Date: Fri, 3 Feb 2023 18:21:10 +0100 Subject: [PATCH] fix(migrations): migrate HttpClientModule to provideHttpClient() (#48949) The `standalone-bootstrap` migration now migrates `HttpClientModule` imports to `provideHttpClient(withInterceptorsFromDi())` instead of `importProvidersFrom(HttpClientModule)`. The `withInterceptorsFromDi()` feature is added to make sure class-based interceptors still works if there are any in the application. Fixes #48948 PR Close #48949 --- .../standalone-bootstrap.ts | 16 +++++++ .../test/standalone_migration_spec.ts | 47 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/packages/core/schematics/ng-generate/standalone-migration/standalone-bootstrap.ts b/packages/core/schematics/ng-generate/standalone-migration/standalone-bootstrap.ts index c9d01e4af32..d629903c8d5 100644 --- a/packages/core/schematics/ng-generate/standalone-migration/standalone-bootstrap.ts +++ b/packages/core/schematics/ng-generate/standalone-migration/standalone-bootstrap.ts @@ -308,6 +308,22 @@ function migrateImportsForBootstrapCall( tracker.addImport(sourceFile, 'provideNoopAnimations', animationsImport), [], [])); continue; } + + // `HttpClientModule` can be replaced with `provideHttpClient()`. + const httpClientModule = 'common/http'; + const httpClientImport = `@angular/${httpClientModule}`; + if (isClassReferenceInAngularModule( + element, 'HttpClientModule', httpClientModule, typeChecker)) { + const callArgs = [ + // we add `withInterceptorsFromDi()` to the call to ensure that class-based interceptors + // still work + ts.factory.createCallExpression( + tracker.addImport(sourceFile, 'withInterceptorsFromDi', httpClientImport), [], []) + ]; + providersInNewCall.push(ts.factory.createCallExpression( + tracker.addImport(sourceFile, 'provideHttpClient', httpClientImport), [], callArgs)); + continue; + } } const target = diff --git a/packages/core/schematics/test/standalone_migration_spec.ts b/packages/core/schematics/test/standalone_migration_spec.ts index de5eec13601..4e434b62544 100644 --- a/packages/core/schematics/test/standalone_migration_spec.ts +++ b/packages/core/schematics/test/standalone_migration_spec.ts @@ -124,6 +124,16 @@ describe('standalone migration', () => { } `); + writeFile('/node_modules/@angular/common/http/index.d.ts', ` + import {ModuleWithProviders} from '@angular/core'; + + export declare class HttpClientModule { + static ɵfac: i0.ɵɵFactoryDeclaration; + static ɵmod: i0.ɵɵNgModuleDeclaration; + static ɵinj: i0.ɵɵInjectorDeclaration; + } + `); + writeFile('/node_modules/@angular/core/testing/index.d.ts', ` export declare class TestBed { static configureTestingModule(config: any): any; @@ -3393,6 +3403,43 @@ describe('standalone migration', () => { `)); }); + it('should convert HttpClientModule references to provideHttpClient(withInterceptorsFromDi())', + async () => { + writeFile('main.ts', ` + import {AppModule} from './app/app.module'; + import {platformBrowser} from '@angular/platform-browser'; + + platformBrowser().bootstrapModule(AppModule).catch(e => console.error(e)); + `); + + writeFile('./app/app.module.ts', ` + import {NgModule, Component} from '@angular/core'; + import {HttpClientModule} from '@angular/common/http'; + + @Component({template: 'hello'}) + export class AppComponent {} + + @NgModule({ + declarations: [AppComponent], + bootstrap: [AppComponent], + imports: [HttpClientModule] + }) + export class AppModule {} + `); + + await runMigration('standalone-bootstrap'); + + expect(stripWhitespace(tree.readContent('main.ts'))).toBe(stripWhitespace(` + import {AppComponent} from './app/app.module'; + import {platformBrowser, bootstrapApplication} from '@angular/platform-browser'; + import {withInterceptorsFromDi, provideHttpClient} from '@angular/common/http'; + + bootstrapApplication(AppComponent, { + providers: [provideHttpClient(withInterceptorsFromDi())] + }).catch(e => console.error(e)); + `)); + }); + it('should omit standalone directives from the imports array from the importProvidersFrom call', async () => { writeFile('main.ts', `