refactor(compiler): synthesize external template node for partial compilation (#40237)

When partially compiling a component with an external template, we must
synthesize a new AST node for the string literal that holds the contents of
the external template, since we want to source-map this expression directly
back to the original external template file.

PR Close #40237
This commit is contained in:
Pete Bacon Darwin 2020-12-29 14:36:56 +00:00 committed by atscott
parent aea265bfc5
commit e7c3687936
3 changed files with 52 additions and 11 deletions

View File

@ -958,14 +958,6 @@ function sourceMapUrl(resourceUrl: string): string {
* some of which might be useful for re-parsing the template with different options.
*/
export interface ParsedComponentTemplate extends ParsedTemplate {
/**
* A full path to the file which contains the template.
*
* This can be either the original .ts file if the template is inline, or the .html file if an
* external file was used.
*/
templateUrl: string;
/**
* True if the original template was stored inline;
* False if the template was in an external file.

View File

@ -8,13 +8,14 @@
import * as core from '../../core';
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
import * as o from '../../output/output_ast';
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../parse_util';
import {Identifiers as R3} from '../r3_identifiers';
import {DeclarationListEmitMode, R3ComponentDef, R3ComponentMetadata, R3UsedDirectiveMetadata} from '../view/api';
import {createComponentType} from '../view/compiler';
import {ParsedTemplate} from '../view/template';
import {DefinitionMap} from '../view/util';
import {R3DeclareComponentMetadata} from './api';
import {R3DeclareComponentMetadata} from './api';
import {createDirectiveDefinitionMap} from './directive';
import {toOptionalLiteralArray} from './util';
@ -79,13 +80,50 @@ export function createComponentDefinitionMap(meta: R3ComponentMetadata, template
*/
function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr {
const templateMap = new DefinitionMap<R3DeclareComponentMetadata['template']>();
const templateExpr =
typeof template.template === 'string' ? o.literal(template.template) : template.template;
const templateExpr = getTemplateExpression(template);
templateMap.set('source', templateExpr);
templateMap.set('isInline', o.literal(template.isInline));
return templateMap.toLiteralMap();
}
function getTemplateExpression(template: ParsedTemplate): o.Expression {
if (typeof template.template === 'string') {
if (template.isInline) {
// The template is inline but not a simple literal string, so give up with trying to
// source-map it and just return a simple literal here.
return o.literal(template.template);
} else {
// The template is external so we must synthesize an expression node with the appropriate
// source-span.
const contents = template.template;
const file = new ParseSourceFile(contents, template.templateUrl);
const start = new ParseLocation(file, 0, 0, 0);
const end = computeEndLocation(file, contents);
const span = new ParseSourceSpan(start, end);
return o.literal(contents, null, span);
}
} else {
// The template is inline so we can just reuse the current expression node.
return template.template;
}
}
function computeEndLocation(file: ParseSourceFile, contents: string): ParseLocation {
const length = contents.length;
let lineStart = 0;
let lastLineStart = 0;
let line = 0;
do {
lineStart = contents.indexOf('\n', lastLineStart);
if (lineStart !== -1) {
lastLineStart = lineStart + 1;
line++;
}
} while (lineStart !== -1);
return new ParseLocation(file, length, line, length - lastLineStart);
}
/**
* Compiles the directives as registered in the component metadata into an array literal of the
* individual directives. If the component does not use any directives, then null is returned.

View File

@ -2092,6 +2092,7 @@ export function parseTemplate(
interpolationConfig,
preserveWhitespaces,
template,
templateUrl,
isInline,
errors: parseResult.errors,
nodes: [],
@ -2117,6 +2118,7 @@ export function parseTemplate(
interpolationConfig,
preserveWhitespaces,
template,
templateUrl,
isInline,
errors: i18nMetaResult.errors,
nodes: [],
@ -2149,6 +2151,7 @@ export function parseTemplate(
preserveWhitespaces,
errors: errors.length > 0 ? errors : null,
template,
templateUrl,
isInline,
nodes,
styleUrls,
@ -2321,6 +2324,14 @@ export interface ParsedTemplate {
*/
template: string|o.Expression;
/**
* A full path to the file which contains the template.
*
* This can be either the original .ts file if the template is inline, or the .html file if an
* external file was used.
*/
templateUrl: string;
/**
* Whether the template was inline (using `template`) or external (using `templateUrl`).
*/