Skip to content

Commit 2bd708f

Browse files
crisbetomattrbeck
authored andcommitted
fix(compiler-cli): escape template literal in TCB
Fixes a regression introduced by #67381 where we weren't escaping backticks in template literals. Fixes #67675.
1 parent dc04465 commit 2bd708f

File tree

3 files changed

+43
-4
lines changed

3 files changed

+43
-4
lines changed

packages/compiler-cli/src/ngtsc/typecheck/src/expression.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -351,17 +351,19 @@ class TcbExprTranslator implements AstVisitor {
351351
let result: string;
352352

353353
if (length === 1) {
354-
result = `\`${head.text}\``;
354+
result = `\`${this.escapeTemplateLiteral(head.text)}\``;
355355
} else {
356-
let parts = [`\`${head.text}`];
356+
let parts = [`\`${this.escapeTemplateLiteral(head.text)}`];
357357
const tailIndex = length - 1;
358358

359359
for (let i = 1; i < tailIndex; i++) {
360360
const expr = this.translate(ast.expressions[i - 1]);
361-
parts.push(`\${${expr.print()}}${ast.elements[i].text}`);
361+
parts.push(`\${${expr.print()}}${this.escapeTemplateLiteral(ast.elements[i].text)}`);
362362
}
363363
const resolvedExpression = this.translate(ast.expressions[tailIndex - 1]);
364-
parts.push(`\${${resolvedExpression.print()}}${ast.elements[tailIndex].text}\``);
364+
parts.push(
365+
`\${${resolvedExpression.print()}}${this.escapeTemplateLiteral(ast.elements[tailIndex].text)}\``,
366+
);
365367
result = parts.join('');
366368
}
367369
return new TcbExpr(result);
@@ -446,6 +448,10 @@ class TcbExprTranslator implements AstVisitor {
446448
// (a!.method(...) as any)
447449
return new TcbExpr(`(${expr}!(${args}) as any)`);
448450
}
451+
452+
private escapeTemplateLiteral(value: string) {
453+
return value.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '$\\{');
454+
}
449455
}
450456

451457
/**

packages/compiler-cli/src/ngtsc/typecheck/test/type_check_block_spec.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,7 @@ describe('type check blocks', () => {
220220
expect(tcb('{{ `${a} - ${b} - ${c}` }}')).toContain(
221221
'"" + (`${((this).a)} - ${((this).b)} - ${((this).c)}`);',
222222
);
223+
expect(tcb('{{ `a${`hello ${name}`}b` }}')).toContain('(`a${`hello ${((this).name)}`}b`)');
223224
});
224225

225226
it('should handle tagged template literals', () => {
@@ -232,6 +233,13 @@ describe('type check blocks', () => {
232233
);
233234
});
234235

236+
it('should escape characters in template literals', () => {
237+
expect(tcb('{{ `a\\`b` }}')).toContain('(`a\\`b`)');
238+
expect(tcb('{{ `a\\${b}` }}')).toContain('(`a$\\{b}`)');
239+
expect(tcb('{{ `a\\\\b` }}')).toContain('(`a\\\\b`)');
240+
expect(tcb('{{ `a\\\`${middle}\\\`b` }}')).toContain('(`a\\\`${((this).middle)}\\\`b`)');
241+
});
242+
235243
it('should generate regular expressions', () => {
236244
const TEMPLATE = '{{ /\\d+/g.test("123") }}';
237245
expect(tcb(TEMPLATE)).toContain('(((/\\d+/g)).test("123"))');

packages/compiler-cli/test/ngtsc/template_typecheck_spec.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3484,6 +3484,31 @@ runInEachFileSystem(() => {
34843484
`Argument of type 'number' is not assignable to parameter of type 'string'.`,
34853485
);
34863486
});
3487+
3488+
it('should check template literals with escaped characters', () => {
3489+
env.write(
3490+
'test.ts',
3491+
`
3492+
import {Component} from '@angular/core';
3493+
3494+
@Component({
3495+
template: '{{\\\`Hello \\\\\`\${check(name)}\\\\\`\\\`}}',
3496+
})
3497+
export class Main {
3498+
name = 'test';
3499+
check(input: number): string {
3500+
return String(input);
3501+
}
3502+
}
3503+
`,
3504+
);
3505+
3506+
const diags = env.driveDiagnostics();
3507+
expect(diags.length).toBe(1);
3508+
expect(diags[0].messageText).toBe(
3509+
`Argument of type 'string' is not assignable to parameter of type 'number'.`,
3510+
);
3511+
});
34873512
});
34883513

34893514
describe('tagged template literals', () => {

0 commit comments

Comments
 (0)