feat(ivy): add error reporting to the html to ivy transformer (#23546)

PR Close #23546
This commit is contained in:
Victor Berchet
2018-04-27 14:39:07 -07:00
committed by Igor Minar
parent 46674d5fac
commit 08e7efc69e
7 changed files with 121 additions and 64 deletions

View File

@ -6,7 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ParsedEvent, ParsedProperty, ParsedVariable} from '../expression_parser/ast';
import {ParsedEvent, ParsedProperty, ParsedVariable, ParserError} from '../expression_parser/ast';
import * as html from '../ml_parser/ast';
import {replaceNgsp} from '../ml_parser/html_whitespaces';
import {isNgTemplate} from '../ml_parser/tags';
@ -14,9 +14,10 @@ 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-)|(@))(.+))|\[\(([^\)]+)\)\]|\[([^\]]+)\]|\(([^\)]+)\))$/;
@ -46,9 +47,39 @@ const CLASS_ATTR = 'class';
// Default selector used by `<ng-content>` if none specified
const DEFAULT_CONTENT_SELECTOR = '*';
export class HtmlToTemplateTransform implements html.Visitor {
errors: ParseError[];
// 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 `<ng-content>`
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 `<ng-content>` in the template ?

View File

@ -12,7 +12,7 @@ import {CompileReflector} from '../../compile_reflector';
import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, convertPropertyBinding} from '../../compiler_util/expression_converter';
import {ConstantPool, DefinitionKind} from '../../constant_pool';
import * as core from '../../core';
import {AST, AstMemoryEfficientTransformer, BindingPipe, BoundElementBindingType, FunctionCall, ImplicitReceiver, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../../expression_parser/ast';
import {AST, AstMemoryEfficientTransformer, BindingPipe, BindingType, FunctionCall, ImplicitReceiver, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../../expression_parser/ast';
import {Identifiers} from '../../identifiers';
import {LifecycleHooks} from '../../lifecycle_reflector';
import * as o from '../../output/output_ast';
@ -21,9 +21,10 @@ import {CssSelector, SelectorMatcher} from '../../selector';
import {BindingParser} from '../../template_parser/binding_parser';
import {OutputContext, error} from '../../util';
import * as t from './../r3_ast';
import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, dependenciesFromGlobalMetadata} from './../r3_factory';
import {Identifiers as R3} from './../r3_identifiers';
import * as t from '../r3_ast';
import {R3DependencyMetadata, R3ResolvedDependencyType, compileFactoryFunction, dependenciesFromGlobalMetadata} from '../r3_factory';
import {Identifiers as R3} from '../r3_identifiers';
import {Render3ParseResult} from '../r3_template_transform';
import {R3ComponentDef, R3ComponentMetadata, R3DirectiveDef, R3DirectiveMetadata, R3QueryMetadata} from './api';
import {BindingScope, TemplateDefinitionBuilder} from './template';
import {CONTEXT_NAME, DefinitionMap, ID_SEPARATOR, MEANING_SEPARATOR, TEMPORARY_NAME, asLiteral, conditionallyCreateMapObjectLiteral, getQueryPredicate, temporaryAllocator, unsupported} from './util';
@ -191,9 +192,8 @@ export function compileDirectiveFromRender2(
* information.
*/
export function compileComponentFromRender2(
outputCtx: OutputContext, component: CompileDirectiveMetadata, nodes: t.Node[],
hasNgContent: boolean, ngContentSelectors: string[], reflector: CompileReflector,
bindingParser: BindingParser, directiveTypeBySel: Map<string, any>,
outputCtx: OutputContext, component: CompileDirectiveMetadata, render3Ast: Render3ParseResult,
reflector: CompileReflector, bindingParser: BindingParser, directiveTypeBySel: Map<string, any>,
pipeTypeByName: Map<string, any>) {
const name = identifierName(component.type) !;
name || error(`Cannot resolver the name of ${component.type}`);
@ -207,7 +207,9 @@ export function compileComponentFromRender2(
...directiveMetadataFromGlobalMetadata(component, outputCtx, reflector),
selector: component.selector,
template: {
nodes, hasNgContent, ngContentSelectors,
nodes: render3Ast.nodes,
hasNgContent: render3Ast.hasNgContent,
ngContentSelectors: render3Ast.ngContentSelectors,
},
lifecycle: {
usesOnChanges: