fix(compiler): lexer support for template literals in object literals (#61601)

This commit fixes a shortcoming of the lexer with template literals

fixes #61572

PR Close #61601
This commit is contained in:
Matthieu Riegler 2025-05-22 05:50:56 +02:00 committed by Jessica Janiuk
parent 3e70d64b20
commit 3e102f0b84
3 changed files with 26 additions and 7 deletions

View file

@ -212,8 +212,7 @@ class _Scanner {
private readonly length: number;
private peek = 0;
private index = -1;
private literalInterpolationDepth = 0;
private braceDepth = 0;
private braceStack: ('interpolation' | 'expression')[] = [];
constructor(private readonly input: string) {
this.length = input.length;
@ -341,7 +340,7 @@ class _Scanner {
}
private scanOpenBrace(start: number, code: number): Token {
this.braceDepth++;
this.braceStack.push('expression');
this.advance();
return newCharacterToken(start, this.index, code);
}
@ -349,13 +348,12 @@ class _Scanner {
private scanCloseBrace(start: number, code: number): Token {
this.advance();
if (this.braceDepth === 0 && this.literalInterpolationDepth > 0) {
this.literalInterpolationDepth--;
const currentBrace = this.braceStack.pop();
if (currentBrace === 'interpolation') {
this.tokens.push(newOperatorToken(start, this.index, '}'));
return this.scanTemplateLiteralPart(this.index);
}
this.braceDepth--;
return newCharacterToken(start, this.index, code);
}
@ -512,7 +510,7 @@ class _Scanner {
// @ts-expect-error
if (this.peek === chars.$LBRACE) {
this.literalInterpolationDepth++;
this.braceStack.push('interpolation');
this.tokens.push(
new StringToken(
start,

View file

@ -588,6 +588,20 @@ describe('lexer', () => {
expectStringToken(tokens[8], 29, 33, '!!!', StringTokenKind.TemplateLiteralEnd);
});
it('should tokenize a template literal in an literal object value', () => {
const tokens: Token[] = lex('{foo: `${name}`}');
expect(tokens.length).toBe(9);
expectCharacterToken(tokens[0], 0, 1, '{');
expectIdentifierToken(tokens[1], 1, 4, 'foo');
expectCharacterToken(tokens[2], 4, 5, ':');
expectStringToken(tokens[3], 6, 7, '', StringTokenKind.TemplateLiteralPart);
expectOperatorToken(tokens[4], 7, 9, '${');
expectIdentifierToken(tokens[5], 9, 13, 'name');
expectOperatorToken(tokens[6], 13, 14, '}');
expectStringToken(tokens[7], 14, 15, '', StringTokenKind.TemplateLiteralEnd);
expectCharacterToken(tokens[8], 15, 16, '}');
});
it('should produce an error if a template literal is not terminated', () => {
expectErrorToken(
lex('`hello')[0],

View file

@ -453,6 +453,13 @@ describe('parser', () => {
checkBinding('`hello ${(name | capitalize)}!!!`', '`hello ${((name | capitalize))}!!!`');
});
it('should parse template literals in objects literals', () => {
checkBinding('{"a": `${name}`}');
checkBinding('{"a": `hello ${name}!`}');
checkBinding('{"a": `hello ${`hello ${`hello`}`}!`}');
checkBinding('{"a": `hello ${{"b": `hello`}}`}');
});
it('should report error if interpolation is empty', () => {
expectBindingError(
'`hello ${}`',