refactor(core): static-query schematic should handle function callbacks (#29663)
Currently the static-query schematic is not able to properly handle call expressions that pass function declarations that access a given query. e.g. ```ts ngOnInit() { this._callFunction(() => this.myQuery.doSomething()); } _callFunction(cb: any) { cb(); } ``` In that case the passed function is executed synchronously in the "ngOnInit" lifecycle and therefore the query needs to be detected as "static". We can fix this by keeping track of the current function context and using it to resolve identifiers to the passed arguments. PR Close #29663
This commit is contained in:

committed by
Igor Minar

parent
00bf636afa
commit
1102b02406
@ -639,6 +639,93 @@ describe('static-queries migration', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should handle function callbacks which statically access queries', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
ngOnInit() {
|
||||
this.callSync(() => this.query.doSomething());
|
||||
}
|
||||
|
||||
callSync(cb: Function) {
|
||||
this.callSync2(cb);
|
||||
}
|
||||
|
||||
callSync2(cb: Function) {
|
||||
cb();
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should handle class instantiations with specified callbacks that access queries', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
import {External} from './external';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
ngOnInit() {
|
||||
new External(() => this.query.doSomething());
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
writeFile('/external.ts', `
|
||||
export class External {
|
||||
constructor(cb: () => void) {
|
||||
// Add extra parentheses to ensure that expression is unwrapped.
|
||||
((cb))();
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should handle nested functions with arguments from parent closure', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
ngOnInit() {
|
||||
this.callSync(() => this.query.doSomething());
|
||||
}
|
||||
|
||||
callSync(cb: Function) {
|
||||
function callSyncNested() {
|
||||
// The "cb" identifier comes from the "callSync" function.
|
||||
cb();
|
||||
}
|
||||
|
||||
callSyncNested();
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries used in setTimeout as static', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
Reference in New Issue
Block a user