fix(migrations): account for CRLF characters in template migrations (#44013)

Previously, when parsing code for templates to migrate, CRLF characters were converted to just LF.
This meant that the source-spans being used to overwrite the template strings in the original source code were out of sync with the positions identified in the parsed templates.

This commit fixes this by parsing the raw text of the template taken from the source code instead of processed string contents.

Fixes #44005

PR Close #44013
This commit is contained in:
Pete Bacon Darwin 2021-11-02 10:23:59 +00:00 committed by Alex Rickabaugh
parent 0cab229e91
commit 3fada9ee7e
2 changed files with 36 additions and 7 deletions

View file

@ -76,7 +76,7 @@ describe('routerlink emptyExpr assignment migration', () => {
await runMigration();
expect(warnOutput.length).toBe(1);
expect(warnOutput[0]).toMatch(/^⮑ {3}index.ts@5:25/);
expect(warnOutput[0]).toMatch(/^⮑ {3}index\.ts@5:25/);
const content = tree.readContent('/index.ts');
expect(content).toContain(`<div [routerLink]="[]"></div>`);
@ -101,7 +101,7 @@ describe('routerlink emptyExpr assignment migration', () => {
await runMigration();
expect(warnOutput.length).toBe(1);
expect(warnOutput).toMatch(/^⮑ {3}tmpl.html@3:20/);
expect(warnOutput).toMatch(/^⮑ {3}tmpl\.html@3:20/);
const content = tree.readContent('/tmpl.html');
expect(content).toContain(`<some-comp [routerLink]="[]"></some-comp>`);
@ -215,9 +215,30 @@ describe('routerlink emptyExpr assignment migration', () => {
await runMigration();
expect(warnOutput.length).toBe(1);
expect(warnOutput[0]).toMatch(/^⮑ {3}index.ts@4:35/);
expect(warnOutput[0]).toMatch(/^⮑ {3}index\.ts@4:35/);
const content = tree.readContent('/index.ts');
expect(content).toContain(`<div [routerLink]='[]'>\r\n{{1}}\r\n</div>`);
});
it('should work for files that use CRLF line endings before routerLink bindings', async () => {
writeFile(
'/index.ts',
`
import {Component} from '@angular/core';
@Component({` +
'template: `' +
'\r\n\r\n\r\n<div [routerLink]>{{1}}</div>`' +
`})
export class MyComp {}
`);
await runMigration();
expect(warnOutput.length).toBe(1);
expect(warnOutput[0]).toMatch(/^⮑ {3}index\.ts@7:6/);
const content = tree.readContent('/index.ts');
expect(content).toContain(`\r\n\r\n\r\n<div [routerLink]="[]">{{1}}</div>`);
});
});

View file

@ -97,16 +97,24 @@ export class NgComponentTemplateVisitor {
if (propertyName === 'template' && ts.isStringLiteralLike(property.initializer)) {
// Need to add an offset of one to the start because the template quotes are
// not part of the template content.
const templateStartIdx = property.initializer.getStart() + 1;
// The `getText()` method gives us the original raw text.
// We could have used the `text` property, but if the template is defined as a backtick
// string then the `text` property contains a "cooked" version of the string. Such cooked
// strings will have converted CRLF characters to only LF. This messes up string
// replacements in template migrations.
// The raw text returned by `getText()` includes the enclosing quotes so we change the
// `content` and `start` values accordingly.
const content = property.initializer.getText().slice(1, -1);
const start = property.initializer.getStart() + 1;
const filePath = resolve(sourceFileName);
this.resolvedTemplates.push({
filePath: filePath,
container: node,
content: property.initializer.text,
content,
inline: true,
start: templateStartIdx,
start: start,
getCharacterAndLineOfPosition: pos =>
ts.getLineAndCharacterOfPosition(sourceFile, pos + templateStartIdx)
ts.getLineAndCharacterOfPosition(sourceFile, pos + start)
});
}
if (propertyName === 'templateUrl' && ts.isStringLiteralLike(property.initializer)) {