fix(ivy): taking "interpolation" config option into account (FW-723) (#27363)
PR Close #27363
This commit is contained in:

committed by
Igor Minar

parent
159788685a
commit
8e644d99fc
@ -230,8 +230,11 @@ class HtmlAstToIvyAst implements html.Visitor {
|
||||
Object.keys(meta.placeholders).forEach(key => {
|
||||
const value = meta.placeholders[key];
|
||||
if (key.startsWith(I18N_ICU_VAR_PREFIX)) {
|
||||
vars[key] =
|
||||
this._visitTextWithInterpolation(`{{${value}}}`, expansion.sourceSpan) as t.BoundText;
|
||||
const config = this.bindingParser.interpolationConfig;
|
||||
// ICU expression is a plain string, not wrapped into start
|
||||
// and end tags, so we wrap it before passing to binding parser
|
||||
const wrapped = `${config.start}${value}${config.end}`;
|
||||
vars[key] = this._visitTextWithInterpolation(wrapped, expansion.sourceSpan) as t.BoundText;
|
||||
} else {
|
||||
placeholders[key] = this._visitTextWithInterpolation(value, expansion.sourceSpan);
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
import {ViewEncapsulation} from '../../core';
|
||||
import {InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {ParseSourceSpan} from '../../parse_util';
|
||||
import * as t from '../r3_ast';
|
||||
@ -184,7 +185,6 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||
*/
|
||||
viewProviders: o.Expression|null;
|
||||
|
||||
|
||||
/**
|
||||
* Path to the .ts file in which this template's generated code will be included, relative to
|
||||
* the compilation root. This will be used to generate identifiers that need to be globally
|
||||
@ -197,6 +197,11 @@ export interface R3ComponentMetadata extends R3DirectiveMetadata {
|
||||
* (used by Closure Compiler's output of `goog.getMsg` for transition period)
|
||||
*/
|
||||
i18nUseExternalIds: boolean;
|
||||
|
||||
/**
|
||||
* Overrides the default interpolation start and end delimiters ({{ and }})
|
||||
*/
|
||||
interpolation: InterpolationConfig;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -14,6 +14,7 @@ import {ConstantPool, DefinitionKind} from '../../constant_pool';
|
||||
import * as core from '../../core';
|
||||
import {AST, ParsedEvent} from '../../expression_parser/ast';
|
||||
import {LifecycleHooks} from '../../lifecycle_reflector';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import * as o from '../../output/output_ast';
|
||||
import {typeSourceSpan} from '../../parse_util';
|
||||
import {CssSelector, SelectorMatcher} from '../../selector';
|
||||
@ -382,6 +383,7 @@ export function compileComponentFromRender2(
|
||||
styles: (summary.template && summary.template.styles) || EMPTY_ARRAY,
|
||||
encapsulation:
|
||||
(summary.template && summary.template.encapsulation) || core.ViewEncapsulation.Emulated,
|
||||
interpolation: DEFAULT_INTERPOLATION_CONFIG,
|
||||
animations: null,
|
||||
viewProviders:
|
||||
component.viewProviders.length > 0 ? new o.WrappedNodeExpr(component.viewProviders) : null,
|
||||
|
@ -10,7 +10,7 @@ import {decimalDigest} from '../../../i18n/digest';
|
||||
import * as i18n from '../../../i18n/i18n_ast';
|
||||
import {createI18nMessageFactory} from '../../../i18n/i18n_parser';
|
||||
import * as html from '../../../ml_parser/ast';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../ml_parser/interpolation_config';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../../ml_parser/interpolation_config';
|
||||
import {ParseTreeResult} from '../../../ml_parser/parser';
|
||||
|
||||
import {I18N_ATTR, I18N_ATTR_PREFIX, I18nMeta, hasI18nAttrs, icuFromI18nMessage, metaFromI18nMessage, parseI18nMeta} from './util';
|
||||
@ -25,10 +25,14 @@ function setI18nRefs(html: html.Node & {i18n: i18n.AST}, i18n: i18n.Node) {
|
||||
* stored with other element's and attribute's information.
|
||||
*/
|
||||
export class I18nMetaVisitor implements html.Visitor {
|
||||
// i18n message generation factory
|
||||
private _createI18nMessage = createI18nMessageFactory(DEFAULT_INTERPOLATION_CONFIG);
|
||||
private _createI18nMessage: any;
|
||||
|
||||
constructor(private config: {keepI18nAttrs: boolean}) {}
|
||||
constructor(
|
||||
private interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG,
|
||||
private keepI18nAttrs: boolean = false) {
|
||||
// i18n message generation factory
|
||||
this._createI18nMessage = createI18nMessageFactory(interpolationConfig);
|
||||
}
|
||||
|
||||
private _generateI18nMessage(
|
||||
nodes: html.Node[], meta: string|i18n.AST = '',
|
||||
@ -81,7 +85,7 @@ export class I18nMetaVisitor implements html.Visitor {
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.config.keepI18nAttrs) {
|
||||
if (!this.keepI18nAttrs) {
|
||||
// update element's attributes,
|
||||
// keeping only non-i18n related ones
|
||||
element.attrs = attrs;
|
||||
@ -116,8 +120,12 @@ export class I18nMetaVisitor implements html.Visitor {
|
||||
visitExpansionCase(expansionCase: html.ExpansionCase, context: any): any { return expansionCase; }
|
||||
}
|
||||
|
||||
export function processI18nMeta(htmlAstWithErrors: ParseTreeResult): ParseTreeResult {
|
||||
export function processI18nMeta(
|
||||
htmlAstWithErrors: ParseTreeResult,
|
||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): ParseTreeResult {
|
||||
return new ParseTreeResult(
|
||||
html.visitAll(new I18nMetaVisitor({keepI18nAttrs: false}), htmlAstWithErrors.rootNodes),
|
||||
html.visitAll(
|
||||
new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false),
|
||||
htmlAstWithErrors.rootNodes),
|
||||
htmlAstWithErrors.errors);
|
||||
}
|
@ -17,7 +17,7 @@ import * as i18n from '../../i18n/i18n_ast';
|
||||
import * as html from '../../ml_parser/ast';
|
||||
import {HtmlParser} from '../../ml_parser/html_parser';
|
||||
import {WhitespaceVisitor} from '../../ml_parser/html_whitespaces';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||
import {DEFAULT_INTERPOLATION_CONFIG, InterpolationConfig} from '../../ml_parser/interpolation_config';
|
||||
import {isNgContainer as checkIsNgContainer, splitNsName} from '../../ml_parser/tags';
|
||||
import {mapLiteral} from '../../output/map_util';
|
||||
import * as o from '../../output/output_ast';
|
||||
@ -1396,11 +1396,13 @@ function interpolate(args: o.Expression[]): o.Expression {
|
||||
* @param templateUrl URL to use for source mapping of the parsed template
|
||||
*/
|
||||
export function parseTemplate(
|
||||
template: string, templateUrl: string, options: {preserveWhitespaces?: boolean}):
|
||||
template: string, templateUrl: string,
|
||||
options: {preserveWhitespaces?: boolean, interpolationConfig?: InterpolationConfig} = {}):
|
||||
{errors?: ParseError[], nodes: t.Node[], hasNgContent: boolean, ngContentSelectors: string[]} {
|
||||
const bindingParser = makeBindingParser();
|
||||
const {interpolationConfig, preserveWhitespaces} = options;
|
||||
const bindingParser = makeBindingParser(interpolationConfig);
|
||||
const htmlParser = new HtmlParser();
|
||||
const parseResult = htmlParser.parse(template, templateUrl, true);
|
||||
const parseResult = htmlParser.parse(template, templateUrl, true, interpolationConfig);
|
||||
|
||||
if (parseResult.errors && parseResult.errors.length > 0) {
|
||||
return {errors: parseResult.errors, nodes: [], hasNgContent: false, ngContentSelectors: []};
|
||||
@ -1412,17 +1414,18 @@ export function parseTemplate(
|
||||
// before we run whitespace removal process, because existing i18n
|
||||
// extraction process (ng xi18n) relies on a raw content to generate
|
||||
// message ids
|
||||
const i18nConfig = {keepI18nAttrs: !options.preserveWhitespaces};
|
||||
rootNodes = html.visitAll(new I18nMetaVisitor(i18nConfig), rootNodes);
|
||||
rootNodes =
|
||||
html.visitAll(new I18nMetaVisitor(interpolationConfig, !preserveWhitespaces), rootNodes);
|
||||
|
||||
if (!options.preserveWhitespaces) {
|
||||
if (!preserveWhitespaces) {
|
||||
rootNodes = html.visitAll(new WhitespaceVisitor(), rootNodes);
|
||||
|
||||
// run i18n meta visitor again in case we remove whitespaces, because
|
||||
// that might affect generated i18n message content. During this pass
|
||||
// i18n IDs generated at the first pass will be preserved, so we can mimic
|
||||
// existing extraction process (ng xi18n)
|
||||
rootNodes = html.visitAll(new I18nMetaVisitor({keepI18nAttrs: false}), rootNodes);
|
||||
rootNodes = html.visitAll(
|
||||
new I18nMetaVisitor(interpolationConfig, /* keepI18nAttrs */ false), rootNodes);
|
||||
}
|
||||
|
||||
const {nodes, hasNgContent, ngContentSelectors, errors} =
|
||||
@ -1437,10 +1440,10 @@ export function parseTemplate(
|
||||
/**
|
||||
* Construct a `BindingParser` with a default configuration.
|
||||
*/
|
||||
export function makeBindingParser(): BindingParser {
|
||||
export function makeBindingParser(
|
||||
interpolationConfig: InterpolationConfig = DEFAULT_INTERPOLATION_CONFIG): BindingParser {
|
||||
return new BindingParser(
|
||||
new Parser(new Lexer()), DEFAULT_INTERPOLATION_CONFIG, new DomElementSchemaRegistry(), null,
|
||||
[]);
|
||||
new Parser(new Lexer()), interpolationConfig, new DomElementSchemaRegistry(), null, []);
|
||||
}
|
||||
|
||||
function resolveSanitizationFn(input: t.BoundAttribute, context: core.SecurityContext) {
|
||||
|
Reference in New Issue
Block a user