refactor(core): static-query schematic should handle binary expressions (#29663)
Currently we only check getters for property access expressions. This is wrong because property access expressions do not always cause the "getter" to be triggered. e.g. ```ts set a() {...} get a() {...} ngOnInit() { this.a = true; } ``` In that case the schematic currently incorrectly checks the "getter", while this is a binary expression and the property access is used as left-side of the binary expression. In that case we need to check the setter declaration of the property and not the "getter". In order to fix this, we need to also check `ts.BinaryExpression` nodes and check getters/setters based on the used operator token. There are three types of binary expressions: 1) Value assignment (using `=`). In that case only the setter is triggered. 2) Compound assignment (e.g. using `+=`). In that case `getter` and `setter` are triggered. 3) Comparison (e.g. using `===`). In that case only the getter is triggered. PR Close #29663
This commit is contained in:

committed by
Igor Minar

parent
82c77ce232
commit
00bf636afa
@ -831,6 +831,7 @@ describe('static-queries migration', () => {
|
||||
export class External {
|
||||
constructor(private comp: MyComp) {}
|
||||
|
||||
set query() { /** noop */ }
|
||||
get query() { return this.comp.query; }
|
||||
}
|
||||
`);
|
||||
@ -841,6 +842,109 @@ describe('static-queries migration', () => {
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should not mark queries as static if a value is assigned to accessor property', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
set myProp(value: any) { /* noop */}
|
||||
get myProp() {
|
||||
return this.query.myValue;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.myProp = true;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: false }) query: any;`);
|
||||
});
|
||||
|
||||
it('should mark queries as static if non-input setter uses query', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
get myProp() { return null; }
|
||||
set myProp(value: any) {
|
||||
this.query.doSomething();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.myProp = 'newValue';
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should check setter and getter when using compound assignment', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
private @${queryType}('test') query2: any;
|
||||
|
||||
get myProp() { return this.query2 }
|
||||
set myProp(value: any) {
|
||||
this.query.doSomething();
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.myProp *= 5;
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query2: any;`);
|
||||
});
|
||||
|
||||
it('should check getters when using comparison operator in binary expression', () => {
|
||||
writeFile('/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
||||
@Component({template: '<span #test></span>'})
|
||||
export class MyComp {
|
||||
private @${queryType}('test') query: any;
|
||||
|
||||
get myProp() { return this.query }
|
||||
set myProp(value: any) { /* noop */ }
|
||||
|
||||
ngOnInit() {
|
||||
if (this.myProp === 3) {
|
||||
// noop
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
runMigration();
|
||||
|
||||
expect(tree.readContent('/index.ts'))
|
||||
.toContain(`@${queryType}('test', { static: true }) query: any;`);
|
||||
});
|
||||
|
||||
it('should properly handle multiple tsconfig files', () => {
|
||||
writeFile('/src/index.ts', `
|
||||
import {Component, ${queryType}} from '@angular/core';
|
||||
|
Reference in New Issue
Block a user