mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
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.
This commit is contained in:
committed by
Matthew Beck (Berry)
parent
dc0446552a
commit
2bd708fb6b
@@ -351,17 +351,19 @@ class TcbExprTranslator implements AstVisitor {
|
||||
let result: string;
|
||||
|
||||
if (length === 1) {
|
||||
result = `\`${head.text}\``;
|
||||
result = `\`${this.escapeTemplateLiteral(head.text)}\``;
|
||||
} else {
|
||||
let parts = [`\`${head.text}`];
|
||||
let parts = [`\`${this.escapeTemplateLiteral(head.text)}`];
|
||||
const tailIndex = length - 1;
|
||||
|
||||
for (let i = 1; i < tailIndex; i++) {
|
||||
const expr = this.translate(ast.expressions[i - 1]);
|
||||
parts.push(`\${${expr.print()}}${ast.elements[i].text}`);
|
||||
parts.push(`\${${expr.print()}}${this.escapeTemplateLiteral(ast.elements[i].text)}`);
|
||||
}
|
||||
const resolvedExpression = this.translate(ast.expressions[tailIndex - 1]);
|
||||
parts.push(`\${${resolvedExpression.print()}}${ast.elements[tailIndex].text}\``);
|
||||
parts.push(
|
||||
`\${${resolvedExpression.print()}}${this.escapeTemplateLiteral(ast.elements[tailIndex].text)}\``,
|
||||
);
|
||||
result = parts.join('');
|
||||
}
|
||||
return new TcbExpr(result);
|
||||
@@ -446,6 +448,10 @@ class TcbExprTranslator implements AstVisitor {
|
||||
// (a!.method(...) as any)
|
||||
return new TcbExpr(`(${expr}!(${args}) as any)`);
|
||||
}
|
||||
|
||||
private escapeTemplateLiteral(value: string) {
|
||||
return value.replace(/\\/g, '\\\\').replace(/`/g, '\\`').replace(/\${/g, '$\\{');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -220,6 +220,7 @@ describe('type check blocks', () => {
|
||||
expect(tcb('{{ `${a} - ${b} - ${c}` }}')).toContain(
|
||||
'"" + (`${((this).a)} - ${((this).b)} - ${((this).c)}`);',
|
||||
);
|
||||
expect(tcb('{{ `a${`hello ${name}`}b` }}')).toContain('(`a${`hello ${((this).name)}`}b`)');
|
||||
});
|
||||
|
||||
it('should handle tagged template literals', () => {
|
||||
@@ -232,6 +233,13 @@ describe('type check blocks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should escape characters in template literals', () => {
|
||||
expect(tcb('{{ `a\\`b` }}')).toContain('(`a\\`b`)');
|
||||
expect(tcb('{{ `a\\${b}` }}')).toContain('(`a$\\{b}`)');
|
||||
expect(tcb('{{ `a\\\\b` }}')).toContain('(`a\\\\b`)');
|
||||
expect(tcb('{{ `a\\\`${middle}\\\`b` }}')).toContain('(`a\\\`${((this).middle)}\\\`b`)');
|
||||
});
|
||||
|
||||
it('should generate regular expressions', () => {
|
||||
const TEMPLATE = '{{ /\\d+/g.test("123") }}';
|
||||
expect(tcb(TEMPLATE)).toContain('(((/\\d+/g)).test("123"))');
|
||||
|
||||
@@ -3484,6 +3484,31 @@ runInEachFileSystem(() => {
|
||||
`Argument of type 'number' is not assignable to parameter of type 'string'.`,
|
||||
);
|
||||
});
|
||||
|
||||
it('should check template literals with escaped characters', () => {
|
||||
env.write(
|
||||
'test.ts',
|
||||
`
|
||||
import {Component} from '@angular/core';
|
||||
|
||||
@Component({
|
||||
template: '{{\\\`Hello \\\\\`\${check(name)}\\\\\`\\\`}}',
|
||||
})
|
||||
export class Main {
|
||||
name = 'test';
|
||||
check(input: number): string {
|
||||
return String(input);
|
||||
}
|
||||
}
|
||||
`,
|
||||
);
|
||||
|
||||
const diags = env.driveDiagnostics();
|
||||
expect(diags.length).toBe(1);
|
||||
expect(diags[0].messageText).toBe(
|
||||
`Argument of type 'string' is not assignable to parameter of type 'number'.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('tagged template literals', () => {
|
||||
|
||||
Reference in New Issue
Block a user