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:
parent
aea265bfc5
commit
e7c3687936
@ -958,14 +958,6 @@ function sourceMapUrl(resourceUrl: string): string {
|
|||||||
* some of which might be useful for re-parsing the template with different options.
|
* some of which might be useful for re-parsing the template with different options.
|
||||||
*/
|
*/
|
||||||
export interface ParsedComponentTemplate extends ParsedTemplate {
|
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;
|
* True if the original template was stored inline;
|
||||||
* False if the template was in an external file.
|
* False if the template was in an external file.
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
import * as core from '../../core';
|
import * as core from '../../core';
|
||||||
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
import {DEFAULT_INTERPOLATION_CONFIG} from '../../ml_parser/interpolation_config';
|
||||||
import * as o from '../../output/output_ast';
|
import * as o from '../../output/output_ast';
|
||||||
|
import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../parse_util';
|
||||||
import {Identifiers as R3} from '../r3_identifiers';
|
import {Identifiers as R3} from '../r3_identifiers';
|
||||||
import {DeclarationListEmitMode, R3ComponentDef, R3ComponentMetadata, R3UsedDirectiveMetadata} from '../view/api';
|
import {DeclarationListEmitMode, R3ComponentDef, R3ComponentMetadata, R3UsedDirectiveMetadata} from '../view/api';
|
||||||
import {createComponentType} from '../view/compiler';
|
import {createComponentType} from '../view/compiler';
|
||||||
import {ParsedTemplate} from '../view/template';
|
import {ParsedTemplate} from '../view/template';
|
||||||
import {DefinitionMap} from '../view/util';
|
import {DefinitionMap} from '../view/util';
|
||||||
import {R3DeclareComponentMetadata} from './api';
|
|
||||||
|
|
||||||
|
import {R3DeclareComponentMetadata} from './api';
|
||||||
import {createDirectiveDefinitionMap} from './directive';
|
import {createDirectiveDefinitionMap} from './directive';
|
||||||
import {toOptionalLiteralArray} from './util';
|
import {toOptionalLiteralArray} from './util';
|
||||||
|
|
||||||
@ -79,13 +80,50 @@ export function createComponentDefinitionMap(meta: R3ComponentMetadata, template
|
|||||||
*/
|
*/
|
||||||
function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr {
|
function compileTemplateDefinition(template: ParsedTemplate): o.LiteralMapExpr {
|
||||||
const templateMap = new DefinitionMap<R3DeclareComponentMetadata['template']>();
|
const templateMap = new DefinitionMap<R3DeclareComponentMetadata['template']>();
|
||||||
const templateExpr =
|
const templateExpr = getTemplateExpression(template);
|
||||||
typeof template.template === 'string' ? o.literal(template.template) : template.template;
|
|
||||||
templateMap.set('source', templateExpr);
|
templateMap.set('source', templateExpr);
|
||||||
templateMap.set('isInline', o.literal(template.isInline));
|
templateMap.set('isInline', o.literal(template.isInline));
|
||||||
return templateMap.toLiteralMap();
|
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
|
* 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.
|
* individual directives. If the component does not use any directives, then null is returned.
|
||||||
|
@ -2092,6 +2092,7 @@ export function parseTemplate(
|
|||||||
interpolationConfig,
|
interpolationConfig,
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
template,
|
template,
|
||||||
|
templateUrl,
|
||||||
isInline,
|
isInline,
|
||||||
errors: parseResult.errors,
|
errors: parseResult.errors,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
@ -2117,6 +2118,7 @@ export function parseTemplate(
|
|||||||
interpolationConfig,
|
interpolationConfig,
|
||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
template,
|
template,
|
||||||
|
templateUrl,
|
||||||
isInline,
|
isInline,
|
||||||
errors: i18nMetaResult.errors,
|
errors: i18nMetaResult.errors,
|
||||||
nodes: [],
|
nodes: [],
|
||||||
@ -2149,6 +2151,7 @@ export function parseTemplate(
|
|||||||
preserveWhitespaces,
|
preserveWhitespaces,
|
||||||
errors: errors.length > 0 ? errors : null,
|
errors: errors.length > 0 ? errors : null,
|
||||||
template,
|
template,
|
||||||
|
templateUrl,
|
||||||
isInline,
|
isInline,
|
||||||
nodes,
|
nodes,
|
||||||
styleUrls,
|
styleUrls,
|
||||||
@ -2321,6 +2324,14 @@ export interface ParsedTemplate {
|
|||||||
*/
|
*/
|
||||||
template: string|o.Expression;
|
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`).
|
* Whether the template was inline (using `template`) or external (using `templateUrl`).
|
||||||
*/
|
*/
|
||||||
|
Loading…
x
Reference in New Issue
Block a user