mirror of
https://github.com/angular/angular
synced 2026-05-24 09:28:37 +00:00
fix(migrations): fix provide-initializer migration when using useFactory (#58518)
Priori to this commit, initializer functions with dependencies were not migrated correctly. With this commit, the function is executed in a injection context to allow the usage of `inject`. PR Close #58518
This commit is contained in:
parent
a6d2d2dc10
commit
5ce10264a4
2 changed files with 91 additions and 30 deletions
|
|
@ -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();
|
||||
}`,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
})];`,
|
||||
);
|
||||
});
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue