This commit consolidates the options that can modify the parsing of text (e.g. HTML, Angular templates, CSS, i18n) into an AST for further processing into a single `options` hash. This makes the code cleaner and more readable, but also enables us to support further options to parsing without triggering wide ranging changes to code that should not be affected by these new options. Specifically, it will let us pass information about the placement of a template that is being parsed in its containing file, which is essential for accurate SourceMap processing. PR Close #28055
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright Google Inc. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by an MIT-style license that can be
|
|
* found in the LICENSE file at https://angular.io/license
|
|
*/
|
|
|
|
import * as e from '../../../src/expression_parser/ast';
|
|
import {Lexer} from '../../../src/expression_parser/lexer';
|
|
import {Parser} from '../../../src/expression_parser/parser';
|
|
import * as html from '../../../src/ml_parser/ast';
|
|
import {HtmlParser} from '../../../src/ml_parser/html_parser';
|
|
import {WhitespaceVisitor} from '../../../src/ml_parser/html_whitespaces';
|
|
import {DEFAULT_INTERPOLATION_CONFIG} from '../../../src/ml_parser/interpolation_config';
|
|
import * as a from '../../../src/render3/r3_ast';
|
|
import {Render3ParseResult, htmlAstToRender3Ast} from '../../../src/render3/r3_template_transform';
|
|
import {processI18nMeta} from '../../../src/render3/view/i18n/meta';
|
|
import {BindingParser} from '../../../src/template_parser/binding_parser';
|
|
import {MockSchemaRegistry} from '../../../testing';
|
|
|
|
export function findExpression(tmpl: a.Node[], expr: string): e.AST|null {
|
|
const res = tmpl.reduce((found, node) => {
|
|
if (found !== null) {
|
|
return found;
|
|
} else {
|
|
return findExpressionInNode(node, expr);
|
|
}
|
|
}, null as e.AST | null);
|
|
if (res instanceof e.ASTWithSource) {
|
|
return res.ast;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
function findExpressionInNode(node: a.Node, expr: string): e.AST|null {
|
|
if (node instanceof a.Element || node instanceof a.Template) {
|
|
return findExpression(
|
|
[
|
|
...node.inputs,
|
|
...node.outputs,
|
|
...node.children,
|
|
],
|
|
expr);
|
|
} else if (node instanceof a.BoundAttribute || node instanceof a.BoundText) {
|
|
const ts = toStringExpression(node.value);
|
|
return toStringExpression(node.value) === expr ? node.value : null;
|
|
} else if (node instanceof a.BoundEvent) {
|
|
return toStringExpression(node.handler) === expr ? node.handler : null;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function toStringExpression(expr: e.AST): string {
|
|
while (expr instanceof e.ASTWithSource) {
|
|
expr = expr.ast;
|
|
}
|
|
if (expr instanceof e.PropertyRead) {
|
|
if (expr.receiver instanceof e.ImplicitReceiver) {
|
|
return expr.name;
|
|
} else {
|
|
return `${toStringExpression(expr.receiver)}.${expr.name}`;
|
|
}
|
|
} else if (expr instanceof e.ImplicitReceiver) {
|
|
return '';
|
|
} else if (expr instanceof e.Interpolation) {
|
|
let str = '{{';
|
|
for (let i = 0; i < expr.expressions.length; i++) {
|
|
str += expr.strings[i] + toStringExpression(expr.expressions[i]);
|
|
}
|
|
str += expr.strings[expr.strings.length - 1] + '}}';
|
|
return str;
|
|
} else {
|
|
throw new Error(`Unsupported type: ${(expr as any).constructor.name}`);
|
|
}
|
|
}
|
|
|
|
// Parse an html string to IVY specific info
|
|
export function parseR3(
|
|
input: string, options: {preserveWhitespaces?: boolean} = {}): Render3ParseResult {
|
|
const htmlParser = new HtmlParser();
|
|
|
|
const parseResult =
|
|
htmlParser.parse(input, 'path:://to/template', {tokenizeExpansionForms: true});
|
|
|
|
if (parseResult.errors.length > 0) {
|
|
const msg = parseResult.errors.map(e => e.toString()).join('\n');
|
|
throw new Error(msg);
|
|
}
|
|
|
|
let htmlNodes = processI18nMeta(parseResult).rootNodes;
|
|
|
|
if (!options.preserveWhitespaces) {
|
|
htmlNodes = html.visitAll(new WhitespaceVisitor(), htmlNodes);
|
|
}
|
|
|
|
const expressionParser = new Parser(new Lexer());
|
|
const schemaRegistry = new MockSchemaRegistry(
|
|
{'invalidProp': false}, {'mappedAttr': 'mappedProp'}, {'unknown': false, 'un-known': false},
|
|
['onEvent'], ['onEvent']);
|
|
const bindingParser =
|
|
new BindingParser(expressionParser, DEFAULT_INTERPOLATION_CONFIG, schemaRegistry, null, []);
|
|
return htmlAstToRender3Ast(htmlNodes, bindingParser);
|
|
} |