refactor(core): migrations do not properly handle multiple templates in source file (#29841)
Currently if there are multiple source files within a given TypeScript source file, only the last template in the source file is checked as we store templates in a `Map` with the source file paths as keys. This is problematic as multiple templates can live within the same source file and we therefore accidentally overwrite existing entries in the resolved templates map. PR Close #29841
This commit is contained in:
parent
446e3573e3
commit
b0c1282fbe
|
@ -24,9 +24,9 @@ export interface TemplateVariableAssignment {
|
||||||
* Analyzes a given resolved template by looking for property assignments to local
|
* Analyzes a given resolved template by looking for property assignments to local
|
||||||
* template variables within bound events.
|
* template variables within bound events.
|
||||||
*/
|
*/
|
||||||
export function analyzeResolvedTemplate(
|
export function analyzeResolvedTemplate(template: ResolvedTemplate): TemplateVariableAssignment[]|
|
||||||
filePath: string, template: ResolvedTemplate): TemplateVariableAssignment[]|null {
|
null {
|
||||||
const templateNodes = parseHtmlGracefully(template.content, filePath);
|
const templateNodes = parseHtmlGracefully(template.content, template.filePath);
|
||||||
|
|
||||||
if (!templateNodes) {
|
if (!templateNodes) {
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -33,8 +33,9 @@ export class Rule extends Rules.TypedRule {
|
||||||
|
|
||||||
// Analyze each resolved template and print a warning for property writes to
|
// Analyze each resolved template and print a warning for property writes to
|
||||||
// template variables.
|
// template variables.
|
||||||
resolvedTemplates.forEach((template, filePath) => {
|
resolvedTemplates.forEach(template => {
|
||||||
const nodes = analyzeResolvedTemplate(filePath, template);
|
const filePath = template.filePath;
|
||||||
|
const nodes = analyzeResolvedTemplate(template);
|
||||||
const templateFile =
|
const templateFile =
|
||||||
template.inline ? sourceFile : createHtmlSourceFile(filePath, template.content);
|
template.inline ? sourceFile : createHtmlSourceFile(filePath, template.content);
|
||||||
|
|
||||||
|
|
|
@ -71,8 +71,9 @@ function runTemplateVariableAssignmentCheck(
|
||||||
|
|
||||||
// Analyze each resolved template and print a warning for property writes to
|
// Analyze each resolved template and print a warning for property writes to
|
||||||
// template variables.
|
// template variables.
|
||||||
resolvedTemplates.forEach((template, filePath) => {
|
resolvedTemplates.forEach(template => {
|
||||||
const nodes = analyzeResolvedTemplate(filePath, template);
|
const filePath = template.filePath;
|
||||||
|
const nodes = analyzeResolvedTemplate(template);
|
||||||
|
|
||||||
if (!nodes) {
|
if (!nodes) {
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -197,4 +197,26 @@ describe('template variable assignment migration', () => {
|
||||||
|
|
||||||
expect(warnOutput.length).toBe(0);
|
expect(warnOutput.length).toBe(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should be able to report multiple templates within the same source file', () => {
|
||||||
|
writeFile('/index.ts', `
|
||||||
|
import {Component} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<ng-template let-one><a (sayHello)="one=true"></a></ng-template>',
|
||||||
|
})
|
||||||
|
export class MyComp {}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
template: '<ng-template let-two><b (greet)="two=true"></b></ng-template>',
|
||||||
|
})
|
||||||
|
export class MyComp2 {}
|
||||||
|
`);
|
||||||
|
|
||||||
|
runMigration();
|
||||||
|
|
||||||
|
expect(warnOutput.length).toBe(2);
|
||||||
|
expect(warnOutput[0]).toMatch(/^⮑ {3}index.ts@5:56: Found assignment/);
|
||||||
|
expect(warnOutput[1]).toMatch(/^⮑ {3}index.ts@10:53: Found assignment/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -39,7 +39,7 @@ export interface ResolvedTemplate {
|
||||||
* TypeScript source files (inline templates or external referenced templates)
|
* TypeScript source files (inline templates or external referenced templates)
|
||||||
*/
|
*/
|
||||||
export class NgComponentTemplateVisitor {
|
export class NgComponentTemplateVisitor {
|
||||||
resolvedTemplates = new Map<string, ResolvedTemplate>();
|
resolvedTemplates: ResolvedTemplate[] = [];
|
||||||
|
|
||||||
constructor(public typeChecker: ts.TypeChecker) {}
|
constructor(public typeChecker: ts.TypeChecker) {}
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ export class NgComponentTemplateVisitor {
|
||||||
// not part of the template content.
|
// not part of the template content.
|
||||||
const templateStartIdx = property.initializer.getStart() + 1;
|
const templateStartIdx = property.initializer.getStart() + 1;
|
||||||
const filePath = resolve(sourceFileName);
|
const filePath = resolve(sourceFileName);
|
||||||
this.resolvedTemplates.set(filePath, {
|
this.resolvedTemplates.push({
|
||||||
filePath: filePath,
|
filePath: filePath,
|
||||||
container: node,
|
container: node,
|
||||||
content: property.initializer.text,
|
content: property.initializer.text,
|
||||||
|
@ -117,7 +117,7 @@ export class NgComponentTemplateVisitor {
|
||||||
const fileContent = readFileSync(templatePath, 'utf8');
|
const fileContent = readFileSync(templatePath, 'utf8');
|
||||||
const lineStartsMap = computeLineStartsMap(fileContent);
|
const lineStartsMap = computeLineStartsMap(fileContent);
|
||||||
|
|
||||||
this.resolvedTemplates.set(templatePath, {
|
this.resolvedTemplates.push({
|
||||||
filePath: templatePath,
|
filePath: templatePath,
|
||||||
container: node,
|
container: node,
|
||||||
content: fileContent,
|
content: fileContent,
|
||||||
|
|
Loading…
Reference in New Issue