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
This commit is contained in:
Pete Bacon Darwin 2020-08-31 16:23:01 +01:00 committed by atscott
parent bf31ef29f6
commit 109555b33a
4 changed files with 21 additions and 8 deletions

View File

@ -87,7 +87,7 @@ export class TagPlaceholder implements Node {
constructor( constructor(
public tag: string, public attrs: {[k: string]: string}, public startName: string, public tag: string, public attrs: {[k: string]: string}, public startName: string,
public closeName: string, public children: Node[], public isVoid: boolean, 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 { visit(visitor: Visitor, context?: any): any {
return visitor.visitTagPlaceholder(this, context); return visitor.visitTagPlaceholder(this, context);
@ -151,7 +151,8 @@ export class CloneVisitor implements Visitor {
visitTagPlaceholder(ph: TagPlaceholder, context?: any): TagPlaceholder { visitTagPlaceholder(ph: TagPlaceholder, context?: any): TagPlaceholder {
const children = ph.children.map(n => n.visit(this, context)); const children = ph.children.map(n => n.visit(this, context));
return new TagPlaceholder( 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 { visitPlaceholder(ph: Placeholder, context?: any): Placeholder {

View File

@ -93,7 +93,8 @@ class _I18nVisitor implements html.Visitor {
} }
const node = new i18n.TagPlaceholder( 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); return context.visitNodeFn(el, node);
} }
@ -167,22 +168,32 @@ class _I18nVisitor implements html.Visitor {
if (splitInterpolation.strings[i].length) { if (splitInterpolation.strings[i].length) {
// No need to add empty strings // 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; context.placeholderToContent[phName] = sDelimiter + expression + eDelimiter;
} }
// The last index contains no expression // The last index contains no expression
const lastStringIdx = splitInterpolation.strings.length - 1; const lastStringIdx = splitInterpolation.strings.length - 1;
if (splitInterpolation.strings[lastStringIdx].length) { 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; 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 = const _CUSTOM_PH_EXP =
/\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g; /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g;

View File

@ -94,7 +94,8 @@ class MapPlaceholderNames extends i18n.CloneVisitor {
const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName; const closeName = ph.closeName ? mapper.toPublicName(ph.closeName)! : ph.closeName;
const children = ph.children.map(n => n.visit(this, mapper)); const children = ph.children.map(n => n.visit(this, mapper));
return new i18n.TagPlaceholder( 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 { visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder {

View File

@ -41,7 +41,7 @@ import {_extractMessages} from '../i18n_parser_spec';
new i18n.IcuPlaceholder(null!, '', null!), new i18n.IcuPlaceholder(null!, '', 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!); const icu = new i18n.Icu('', '', {tag}, null!);
icu.visit(visitor); icu.visit(visitor);