diff --git a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts index 9f1805088c..5db7d33e45 100644 --- a/packages/compiler-cli/src/ngtsc/annotations/src/component.ts +++ b/packages/compiler-cli/src/ngtsc/annotations/src/component.ts @@ -869,12 +869,14 @@ export class ComponentDecoratorHandler implements // Unfortunately, the primary parse of the template above may not contain accurate source map // information. If used directly, it would result in incorrect code locations in template - // errors, etc. There are two main problems: + // errors, etc. There are three main problems: // // 1. `preserveWhitespaces: false` annihilates the correctness of template source mapping, as // the whitespace transformation changes the contents of HTML text nodes before they're // parsed into Angular expressions. - // 2. By default, the template parser strips leading trivia characters (like spaces, tabs, and + // 2. `preserveLineEndings: false` causes growing misalignments in templates that use '\r\n' + // line endings, by normalizing them to '\n'. + // 3. By default, the template parser strips leading trivia characters (like spaces, tabs, and // newlines). This also destroys source mapping information. // // In order to guarantee the correctness of diagnostics, templates are parsed a second time @@ -885,6 +887,7 @@ export class ComponentDecoratorHandler implements const {nodes: diagNodes} = parseTemplate(templateStr, template.sourceMapUrl, { preserveWhitespaces: true, + preserveLineEndings: true, interpolationConfig: template.interpolationConfig, range: templateRange ?? undefined, escapedString, diff --git a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts index f63a075015..fc060b6098 100644 --- a/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts +++ b/packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts @@ -110,6 +110,23 @@ export declare class AnimationEvent { env.driveMain(); }); + it('should have accurate diagnostics in a template using crlf line endings', () => { + env.write('test.ts', ` + import {Component} from '@angular/core'; + + @Component({ + selector: 'test', + templateUrl: './test.html', + }) + class TestCmp {} + `); + env.write('test.html', '\r\n{{does_not_exist}}\r\n'); + + const diags = env.driveDiagnostics(); + expect(diags.length).toBe(1); + expect(getSourceCodeForDiagnostic(diags[0])).toBe('does_not_exist'); + }); + it('should check regular attributes that are directive inputs', () => { env.tsconfig( {fullTemplateTypeCheck: true, strictInputTypes: true, strictAttributeTypes: true}); diff --git a/packages/compiler/src/render3/view/template.ts b/packages/compiler/src/render3/view/template.ts index a1e77ada50..e7d7618cc8 100644 --- a/packages/compiler/src/render3/view/template.ts +++ b/packages/compiler/src/render3/view/template.ts @@ -2003,6 +2003,10 @@ export interface ParseTemplateOptions { * Include whitespace nodes in the parsed output. */ preserveWhitespaces?: boolean; + /** + * Preserve original line endings instead of normalizing '\r\n' endings to '\n'. + */ + preserveLineEndings?: boolean; /** * How to parse interpolation markers. */