fix(compiler): incorrect spans for template literals (#60323) (#60331)

Fixes that we were producing zero-length spans for template literals and template literal elements.

Fixes #60320.
Fixes #60319.

PR Close #60323

PR Close #60331
This commit is contained in:
Kristiyan Kostadinov
2025-03-11 22:37:11 +01:00
committed by Andrew Kushnir
parent 705fb0d236
commit 56b551d273
11 changed files with 34 additions and 24 deletions
@@ -1040,7 +1040,7 @@ class _ParseAST {
this.advance();
return new LiteralPrimitive(this.span(start), this.sourceSpan(start), value);
} else if (this.next.isTemplateLiteralEnd()) {
return this.parseNoInterpolationTemplateLiteral(start);
return this.parseNoInterpolationTemplateLiteral();
} else if (this.next.isTemplateLiteralPart()) {
return this.parseTemplateLiteral();
} else if (this.next.isString() && this.next.kind === StringTokenKind.Plain) {
@@ -1406,8 +1406,9 @@ class _ParseAST {
return new VariableBinding(sourceSpan, key, value);
}
private parseNoInterpolationTemplateLiteral(start: number): AST {
private parseNoInterpolationTemplateLiteral(): AST {
const text = this.next.strValue;
const start = this.inputIndex;
this.advance();
const span = this.span(start);
const sourceSpan = this.sourceSpan(start);
@@ -1428,14 +1429,15 @@ class _ParseAST {
const token = this.next;
if (token.isTemplateLiteralPart() || token.isTemplateLiteralEnd()) {
const partStart = this.inputIndex;
this.advance();
elements.push(
new TemplateLiteralElement(
this.span(this.inputIndex),
this.sourceSpan(this.inputIndex),
this.span(partStart),
this.sourceSpan(partStart),
token.strValue,
),
);
this.advance();
if (token.isTemplateLiteralEnd()) {
break;
}
@@ -18,6 +18,7 @@ import {
PropertyRead,
TemplateBinding,
VariableBinding,
TemplateLiteral,
} from '@angular/compiler/src/expression_parser/ast';
import {Lexer} from '@angular/compiler/src/expression_parser/lexer';
import {Parser, SplitInterpolation} from '@angular/compiler/src/expression_parser/parser';
@@ -496,18 +497,34 @@ describe('parser', () => {
expect(unparseWithSpan(ast)).toContain(['a.b = c', '[nameSpan] b']);
});
it('should record template literal space', () => {
it('should record spans for untagged template literals with no interpolations', () => {
const ast = parseAction('`hello world`');
const unparsed = unparseWithSpan(ast);
expect(unparsed).toEqual([
['`hello world`', '`hello world`'],
['hello world', '`hello world`'],
]);
});
it('should record spans for untagged template literals with interpolations', () => {
const ast = parseAction('`before ${one} - ${two} - ${three} after`');
const unparsed = unparseWithSpan(ast);
expect(unparsed).toContain(['before ', '']);
expect(unparsed).toContain(['one', 'one']);
expect(unparsed).toContain(['one', '[nameSpan] one']);
expect(unparsed).toContain([' - ', '']);
expect(unparsed).toContain(['two', 'two']);
expect(unparsed).toContain(['two', '[nameSpan] two']);
expect(unparsed).toContain(['three', 'three']);
expect(unparsed).toContain(['three', '[nameSpan] three']);
expect(unparsed).toContain([' after', '']);
expect(unparsed).toEqual([
['`before ${one} - ${two} - ${three} after`', '`before ${one} - ${two} - ${three} after`'],
['before ', '`before '],
['one', 'one'],
['one', '[nameSpan] one'],
['', ''], // Implicit receiver
[' - ', ' - '],
['two', 'two'],
['two', '[nameSpan] two'],
['', ''], // Implicit receiver
[' - ', ' - '],
['three', 'three'],
['three', '[nameSpan] three'],
['', ''], // Implicit receiver
[' after', ' after`'],
]);
});
it('should include parenthesis in spans', () => {
@@ -306,7 +306,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFirstLContainer",
"getInitialLViewFlagsFromDef",
@@ -327,7 +327,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFirstLContainer",
"getInitialLViewFlagsFromDef",
@@ -255,7 +255,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFirstLContainer",
"getInitialLViewFlagsFromDef",
@@ -369,7 +369,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFactoryOf",
"getFirstLContainer",
@@ -356,7 +356,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFactoryOf",
"getFirstLContainer",
@@ -262,7 +262,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getDocument",
"getFactoryDef",
"getFilteredHeaders",
@@ -440,7 +440,6 @@
"getData",
"getDataKeys",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFactoryOf",
"getFirstLContainer",
@@ -229,7 +229,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFirstLContainer",
"getInitialLViewFlagsFromDef",
@@ -300,7 +300,6 @@
"getCurrentTNodePlaceholderOk",
"getDOM",
"getDeclarationTNode",
"getDirectiveDef",
"getFactoryDef",
"getFirstLContainer",
"getFirstNativeNode",