Skip to content

Commit

Permalink
fix(compiler): ensure localized strings are ES5 compatible for JIT mo…
Browse files Browse the repository at this point in the history
…de (#34265)

Previously the JIT evaluated code for ivy localized strings included
backtick tagged template strings, which are not compatible with ES5
in legacy browsers such as IE 11.

Now the generated code is ES5 compatible.

Fixes #34246

PR Close #34265
  • Loading branch information
petebacondarwin authored and AndrewKushnir committed Dec 6, 2019
1 parent 24bbcaf commit 26dba21
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 1 deletion.
38 changes: 37 additions & 1 deletion packages/compiler/src/output/abstract_js_emitter.ts
Expand Up @@ -7,7 +7,7 @@
*/


import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext} from './abstract_emitter';
import {AbstractEmitterVisitor, CATCH_ERROR_VAR, CATCH_STACK_VAR, EmitterVisitorContext, escapeIdentifier} from './abstract_emitter';
import * as o from './output_ast';

export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
Expand Down Expand Up @@ -150,6 +150,42 @@ export abstract class AbstractJsEmitterVisitor extends AbstractEmitterVisitor {
return null;
}

visitLocalizedString(ast: o.LocalizedString, ctx: EmitterVisitorContext): any {
// The following convoluted piece of code is effectively the downlevelled equivalent of
// ```
// $localize `...`
// ```
// which is effectively like:
// ```
// $localize(__makeTemplateObject(cooked, raw), expression1, expression2, ...);
// ```
//
// The `$localize` function expects a "template object", which is an array of "cooked" strings
// plus a `raw` property that contains an array of "raw" strings.
//
// In some environments a helper function called `__makeTemplateObject(cooked, raw)` might be
// available, in which case we use that. Otherwise we must create our own helper function
// inline.
//
// In the inline function, if `Object.defineProperty` is available we use that to attach the
// `raw` array.
ctx.print(
ast,
'$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})(');
const parts = [ast.serializeI18nHead()];
for (let i = 1; i < ast.messageParts.length; i++) {
parts.push(ast.serializeI18nTemplatePart(i));
}
ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.cooked, false)).join(', ')}], `);
ctx.print(ast, `[${parts.map(part => escapeIdentifier(part.raw, false)).join(', ')}])`);
ast.expressions.forEach(expression => {
ctx.print(ast, ', ');
expression.visitExpression(this, ctx);
});
ctx.print(ast, ')');
return null;
}

private _visitParams(params: o.FnParam[], ctx: EmitterVisitorContext): void {
this.visitAllObjects(param => ctx.print(null, param.name), params, ctx, ',');
}
Expand Down
9 changes: 9 additions & 0 deletions packages/compiler/test/output/js_emitter_spec.ts
Expand Up @@ -200,6 +200,15 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
].join('\n'));
});

it('should support ES5 localized strings', () => {
expect(emitStmt(new o.ExpressionStatement(o.localizedString(
{}, ['ab\\:c', 'd"e\'f'], ['ph1'],
[o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))]))))
.toEqual(
String.raw
`$localize((this&&this.__makeTemplateObject||function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e})(['ab\\:c', ':ph1:d"e\'f'], ['ab\\\\:c', ':ph1:d"e\'f']), (7 + 8));`);
});

it('should support try/catch', () => {
const bodyStmt = o.variable('body').callFn([]).toStmt();
const catchStmt =
Expand Down
7 changes: 7 additions & 0 deletions packages/compiler/test/output/ts_emitter_spec.ts
Expand Up @@ -249,6 +249,13 @@ const externalModuleIdentifier = new o.ExternalReference(anotherModuleUrl, 'some
].join('\n'));
});

it('should support localized strings', () => {
expect(emitStmt(new o.ExpressionStatement(o.localizedString(
{}, ['ab\\:c', 'd"e\'f'], ['ph1'],
[o.literal(7, o.NUMBER_TYPE).plus(o.literal(8, o.NUMBER_TYPE))]))))
.toEqual('$localize `ab\\\\:c${(7 + 8)}:ph1:d"e\'f`;');
});

it('should support try/catch', () => {
const bodyStmt = o.variable('body').callFn([]).toStmt();
const catchStmt =
Expand Down

0 comments on commit 26dba21

Please sign in to comment.