/** * @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 {ParsedEvent, ParsedProperty, ParsedVariable} from '../expression_parser/ast'; import * as html from '../ml_parser/ast'; import {replaceNgsp} from '../ml_parser/html_whitespaces'; import {isNgTemplate} from '../ml_parser/tags'; import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util'; import {isStyleUrlResolvable} from '../style_url_resolver'; import {BindingParser} from '../template_parser/binding_parser'; import {PreparsedElementType, preparseElement} from '../template_parser/template_preparser'; import {syntaxError} from '../util'; import * as t from './r3_ast'; const BIND_NAME_REGEXP = /^(?:(?:(?:(bind-)|(let-)|(ref-|#)|(on-)|(bindon-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/; // Group 1 = "bind-" const KW_BIND_IDX = 1; // Group 2 = "let-" const KW_LET_IDX = 2; // Group 3 = "ref-/#" const KW_REF_IDX = 3; // Group 4 = "on-" const KW_ON_IDX = 4; // Group 5 = "bindon-" const KW_BINDON_IDX = 5; // Group 6 = "@" const KW_AT_IDX = 6; // Group 7 = the identifier after "bind-", "let-", "ref-/#", "on-", "bindon-" or "@" const IDENT_KW_IDX = 7; // Group 8 = identifier inside [()] const IDENT_BANANA_BOX_IDX = 8; // Group 9 = identifier inside [] const IDENT_PROPERTY_IDX = 9; // Group 10 = identifier inside () const IDENT_EVENT_IDX = 10; const TEMPLATE_ATTR_PREFIX = '*'; // Default selector used by `` if none specified const DEFAULT_CONTENT_SELECTOR = '*'; // Result of the html AST to Ivy AST transformation export type Render3ParseResult = { nodes: t.Node[]; errors: ParseError[]; // Any non default (empty or '*') selector found in the template ngContentSelectors: string[]; // Wether the template contains any `` hasNgContent: boolean; }; export function htmlAstToRender3Ast( htmlNodes: html.Node[], bindingParser: BindingParser): Render3ParseResult { const transformer = new HtmlAstToIvyAst(bindingParser); const ivyNodes = html.visitAll(transformer, htmlNodes); // Errors might originate in either the binding parser or the html to ivy transformer const allErrors = bindingParser.errors.concat(transformer.errors); const errors: ParseError[] = allErrors.filter(e => e.level === ParseErrorLevel.ERROR); if (errors.length > 0) { const errorString = errors.join('\n'); throw syntaxError(`Template parse errors:\n${errorString}`, errors); } return { nodes: ivyNodes, errors: allErrors, ngContentSelectors: transformer.ngContentSelectors, hasNgContent: transformer.hasNgContent, }; } class HtmlAstToIvyAst implements html.Visitor { errors: ParseError[] = []; // Selectors for the `ng-content` tags. Only non `*` selectors are recorded here ngContentSelectors: string[] = []; // Any `` in the template ? hasNgContent = false; constructor(private bindingParser: BindingParser) {} // HTML visitor visitElement(element: html.Element): t.Node|null { const preparsedElement = preparseElement(element); if (preparsedElement.type === PreparsedElementType.SCRIPT || preparsedElement.type === PreparsedElementType.STYLE) { // Skipping