diff --git a/packages/core/schematics/migrations/provide-initializer/utils.ts b/packages/core/schematics/migrations/provide-initializer/utils.ts index 14a1f8669be..9dff9d6573d 100644 --- a/packages/core/schematics/migrations/provide-initializer/utils.ts +++ b/packages/core/schematics/migrations/provide-initializer/utils.ts @@ -90,36 +90,40 @@ function tryParseProviderExpression(node: ts.Node): ProviderInfo | undefined { let deps: string[] = []; let initializerToken: string | undefined; let useExisting: ts.Expression | undefined; - let useFactory: ts.Expression | undefined; + let useFactoryCode: string | undefined; let useValue: ts.Expression | undefined; let multi = false; for (const property of node.properties) { - if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) { - continue; + if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name)) { + switch (property.name.text) { + case 'deps': + if (ts.isArrayLiteralExpression(property.initializer)) { + deps = property.initializer.elements.map((el) => el.getText()); + } + break; + case 'provide': + initializerToken = property.initializer.getText(); + break; + case 'useExisting': + useExisting = property.initializer; + break; + case 'useFactory': + useFactoryCode = property.initializer.getText(); + break; + case 'useValue': + useValue = property.initializer; + break; + case 'multi': + multi = property.initializer.kind === ts.SyntaxKind.TrueKeyword; + break; + } } - switch (property.name.text) { - case 'deps': - if (ts.isArrayLiteralExpression(property.initializer)) { - deps = property.initializer.elements.map((el) => el.getText()); - } - break; - case 'provide': - initializerToken = property.initializer.getText(); - break; - case 'useExisting': - useExisting = property.initializer; - break; - case 'useFactory': - useFactory = property.initializer; - break; - case 'useValue': - useValue = property.initializer; - break; - case 'multi': - multi = property.initializer.kind === ts.SyntaxKind.TrueKeyword; - break; + // Handle the `useFactory() {}` shorthand case. + if (ts.isMethodDeclaration(property) && property.name.getText() === 'useFactory') { + const params = property.parameters.map((param) => param.getText()).join(', '); + useFactoryCode = `(${params}) => ${property.body?.getText()}`; } } @@ -146,12 +150,15 @@ function tryParseProviderExpression(node: ts.Node): ProviderInfo | undefined { }; } - if (useFactory) { + if (useFactoryCode) { const args = deps.map((dep) => `inject(${dep})`); return { ...info, importInject: deps.length > 0, - initializerCode: `(${useFactory.getText()})(${args.join(', ')})`, + initializerCode: `() => { + const initializerFn = (${useFactoryCode})(${args.join(', ')}); + return initializerFn(); + }`, }; } diff --git a/packages/core/schematics/test/provide_initializer_spec.ts b/packages/core/schematics/test/provide_initializer_spec.ts index c2a53f73c90..8598105a953 100644 --- a/packages/core/schematics/test/provide_initializer_spec.ts +++ b/packages/core/schematics/test/provide_initializer_spec.ts @@ -110,6 +110,29 @@ describe('Provide initializer migration', () => { expect(content).toContain(`const providers = [provideAppInitializer(initializerFn)];`); }); + it('should transform APP_INITIALIZER + useFactory shorthand into provideAppInitializer', async () => { + const content = await migrateCode(` + import { APP_INITIALIZER } from '@angular/core'; + + const providers = [{ + provide: APP_INITIALIZER, + useFactory() { + const service = inject(Service); + return () => service.init(); + }, + multi: true, + }]; + `); + + expect(content).toContain(`const providers = [provideAppInitializer(() => { + const initializerFn = (() => { + const service = inject(Service); + return () => service.init(); + })(); + return initializerFn(); + })];`); + }); + it('should transform APP_INITIALIZER + useFactory into provideAppInitializer', async () => { const content = await migrateCode(` import { APP_INITIALIZER } from '@angular/core'; @@ -124,10 +147,13 @@ describe('Provide initializer migration', () => { }]; `); - expect(content).toContain(`const providers = [provideAppInitializer((() => { + expect(content).toContain(`const providers = [provideAppInitializer(() => { + const initializerFn = (() => { const service = inject(Service); return () => service.init(); - })())];`); + })(); + return initializerFn(); + })];`); }); it('should transform APP_INITIALIZER + useExisting into provideAppInitializer', async () => { @@ -147,6 +173,31 @@ describe('Provide initializer migration', () => { ); }); + it('should transform APP_INITIALIZER + deps + useFactory shorthand into provideAppInitializer', async () => { + const content = await migrateCode(` + import { APP_INITIALIZER } from '@angular/core'; + + const providers = [{ + provide: APP_INITIALIZER, + useFactory(a: ServiceA, b: ServiceB) { + return () => a.init(); + }, + deps: [ServiceA, ServiceB], + multi: true, + }]; + `); + + expect(content).toContain(`import { inject, provideAppInitializer } from '@angular/core';`); + expect(content).toContain( + `const providers = [provideAppInitializer(() => { + const initializerFn = ((a: ServiceA, b: ServiceB) => { + return () => a.init(); + })(inject(ServiceA), inject(ServiceB)); + return initializerFn(); + })];`, + ); + }); + it('should transform APP_INITIALIZER + deps into provideAppInitializer', async () => { const content = await migrateCode(` import { APP_INITIALIZER } from '@angular/core'; @@ -163,9 +214,12 @@ describe('Provide initializer migration', () => { expect(content).toContain(`import { inject, provideAppInitializer } from '@angular/core';`); expect(content).toContain( - `const providers = [provideAppInitializer(((a: ServiceA, b: ServiceB) => { + `const providers = [provideAppInitializer(() => { + const initializerFn = ((a: ServiceA, b: ServiceB) => { return () => a.init(); - })(inject(ServiceA), inject(ServiceB)))];`, + })(inject(ServiceA), inject(ServiceB)); + return initializerFn(); + })];`, ); });