From 109555b33a4e148668afa9622bd8ad95c64ccfdc Mon Sep 17 00:00:00 2001 From: Pete Bacon Darwin Date: Mon, 31 Aug 2020 16:23:01 +0100 Subject: [PATCH] refactor(compiler): track the closing source-span of TagPlaceholders (#38645) The `TagPlaceholder` can contain children, in which case there are two source spans of interest: the opening tag and the closing tag. This commit now allows the closing tag source-span to be tracked, so that it can be used later in source-mapping. PR Close #38645 --- packages/compiler/src/i18n/i18n_ast.ts | 5 +++-- packages/compiler/src/i18n/i18n_parser.ts | 19 +++++++++++++++---- packages/compiler/src/i18n/message_bundle.ts | 3 ++- .../test/i18n/serializers/i18n_ast_spec.ts | 2 +- 4 files changed, 21 insertions(+), 8 deletions(-) diff --git a/packages/compiler/src/i18n/i18n_ast.ts b/packages/compiler/src/i18n/i18n_ast.ts index 0a7159bbc1..0f7c412205 100644 --- a/packages/compiler/src/i18n/i18n_ast.ts +++ b/packages/compiler/src/i18n/i18n_ast.ts @@ -87,7 +87,7 @@ export class TagPlaceholder implements Node { constructor( public tag: string, public attrs: {[k: string]: string}, public startName: string, public closeName: string, public children: Node[], public isVoid: boolean, - public sourceSpan: ParseSourceSpan) {} + public sourceSpan: ParseSourceSpan, public closeSourceSpan: ParseSourceSpan|null) {} visit(visitor: Visitor, context?: any): any { return visitor.visitTagPlaceholder(this, context); @@ -151,7 +151,8 @@ export class CloneVisitor implements Visitor { visitTagPlaceholder(ph: TagPlaceholder, context?: any): TagPlaceholder { const children = ph.children.map(n => n.visit(this, context)); return new TagPlaceholder( - ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan); + ph.tag, ph.attrs, ph.startName, ph.closeName, children, ph.isVoid, ph.sourceSpan, + ph.closeSourceSpan); } visitPlaceholder(ph: Placeholder, context?: any): Placeholder { diff --git a/packages/compiler/src/i18n/i18n_parser.ts b/packages/compiler/src/i18n/i18n_parser.ts index 1747e64463..855b9b3a23 100644 --- a/packages/compiler/src/i18n/i18n_parser.ts +++ b/packages/compiler/src/i18n/i18n_parser.ts @@ -93,7 +93,8 @@ class _I18nVisitor implements html.Visitor { } const node = new i18n.TagPlaceholder( - el.name, attrs, startPhName, closePhName, children, isVoid, el.sourceSpan); + el.name, attrs, startPhName, closePhName, children, isVoid, el.startSourceSpan, + el.endSourceSpan); return context.visitNodeFn(el, node); } @@ -167,22 +168,32 @@ class _I18nVisitor implements html.Visitor { if (splitInterpolation.strings[i].length) { // No need to add empty strings - nodes.push(new i18n.Text(splitInterpolation.strings[i], sourceSpan)); + const stringSpan = getOffsetSourceSpan(sourceSpan, splitInterpolation.stringSpans[i]); + nodes.push(new i18n.Text(splitInterpolation.strings[i], stringSpan)); } - nodes.push(new i18n.Placeholder(expression, phName, sourceSpan)); + const expressionSpan = + getOffsetSourceSpan(sourceSpan, splitInterpolation.expressionsSpans[i]); + nodes.push(new i18n.Placeholder(expression, phName, expressionSpan)); context.placeholderToContent[phName] = sDelimiter + expression + eDelimiter; } // The last index contains no expression const lastStringIdx = splitInterpolation.strings.length - 1; if (splitInterpolation.strings[lastStringIdx].length) { - nodes.push(new i18n.Text(splitInterpolation.strings[lastStringIdx], sourceSpan)); + const stringSpan = + getOffsetSourceSpan(sourceSpan, splitInterpolation.stringSpans[lastStringIdx]); + nodes.push(new i18n.Text(splitInterpolation.strings[lastStringIdx], stringSpan)); } return container; } } +function getOffsetSourceSpan( + sourceSpan: ParseSourceSpan, {start, end}: {start: number, end: number}): ParseSourceSpan { + return new ParseSourceSpan(sourceSpan.start.moveBy(start), sourceSpan.start.moveBy(end)); +} + const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g; diff --git a/packages/compiler/src/i18n/message_bundle.ts b/packages/compiler/src/i18n/message_bundle.ts index 37fb284604..036b6d45fb 100644 --- a/packages/compiler/src/i18n/message_bundle.ts +++ b/packages/compiler/src/i18n/message_bundle.ts @@ -94,7 +94,8 @@ class MapPlaceholderNames extends i18n.CloneVisitor { const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName; const children = ph.children.map(n => n.visit(this, mapper)); return new i18n.TagPlaceholder( - ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan); + ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan, + ph.closeSourceSpan); } visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder { diff --git a/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts b/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts index 3ee5872d41..910bb73533 100644 --- a/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts +++ b/packages/compiler/test/i18n/serializers/i18n_ast_spec.ts @@ -41,7 +41,7 @@ import {_extractMessages} from '../i18n_parser_spec'; new i18n.IcuPlaceholder(null!, '', null!), ], null!); - const tag = new i18n.TagPlaceholder('', {}, '', '', [container], false, null!); + const tag = new i18n.TagPlaceholder('', {}, '', '', [container], false, null!, null); const icu = new i18n.Icu('', '', {tag}, null!); icu.visit(visitor);