fix(compiler): do not throw away render3 AST on errors (#39413)

Currently render3's `parseTemplate` throws away the parsed AST and
returns an empty list of HTML nodes if HTML->R3 translation failed. This
is not preferrable in some contexts like that of a language service,
where we would like a well-formed AST even if it is has errors.

PR Close #39413
This commit is contained in:
ayazhafiz 2020-10-24 12:50:00 -05:00 committed by Alex Rickabaugh
parent 0a63eeaff1
commit 19b88cef71
2 changed files with 16 additions and 23 deletions

View File

@ -2046,6 +2046,8 @@ export function parseTemplate(
{leadingTriviaChars: LEADING_TRIVIA_CHARS, ...options, tokenizeExpansionForms: true}); {leadingTriviaChars: LEADING_TRIVIA_CHARS, ...options, tokenizeExpansionForms: true});
if (parseResult.errors && parseResult.errors.length > 0) { if (parseResult.errors && parseResult.errors.length > 0) {
// TODO(ayazhafiz): we may not always want to bail out at this point (e.g. in
// the context of a language service).
return { return {
interpolationConfig, interpolationConfig,
preserveWhitespaces, preserveWhitespaces,
@ -2084,23 +2086,11 @@ export function parseTemplate(
const {nodes, errors, styleUrls, styles, ngContentSelectors} = const {nodes, errors, styleUrls, styles, ngContentSelectors} =
htmlAstToRender3Ast(rootNodes, bindingParser); htmlAstToRender3Ast(rootNodes, bindingParser);
if (errors && errors.length > 0) {
return {
interpolationConfig,
preserveWhitespaces,
template,
errors,
nodes: [],
styleUrls: [],
styles: [],
ngContentSelectors: []
};
}
return { return {
interpolationConfig, interpolationConfig,
preserveWhitespaces, preserveWhitespaces,
errors: null, errors: errors.length > 0 ? errors : null,
template, template,
nodes, nodes,
styleUrls, styleUrls,
@ -2265,6 +2255,8 @@ export interface ParsedTemplate {
/** /**
* Any errors from parsing the template the first time. * Any errors from parsing the template the first time.
*
* `null` if there are no errors. Otherwise, the array of errors is guaranteed to be non-empty.
*/ */
errors: ParseError[]|null; errors: ParseError[]|null;

View File

@ -433,15 +433,16 @@ describe('findNodeAtPosition for expression AST', () => {
expect(node).toBeInstanceOf(e.BindingPipe); expect(node).toBeInstanceOf(e.BindingPipe);
}); });
it('should locate binding pipe without identifier', it('should locate binding pipe without identifier', () => {
() => { const {errors, nodes, position} = parse(`{{ title | ¦ }}`);
// TODO: We are not able to locate pipe if identifier is missing because the expect(errors?.length).toBe(1);
// parser throws an error. This case is important for autocomplete. expect(errors![0].toString())
// const {errors, nodes, position} = parse(`{{ title | ¦ }}`); .toContain(
// expect(errors).toBe(null); 'Unexpected end of input, expected identifier or keyword at the end of the expression');
// const node = findNodeAtPosition(nodes, position); const node = findNodeAtPosition(nodes, position);
// expect(isExpressionNode(node!)).toBe(true); expect(isExpressionNode(node!)).toBe(true);
// expect(node).toBeInstanceOf(e.BindingPipe); // TODO: We want this to be a BindingPipe.
expect(node).toBeInstanceOf(e.Interpolation);
}); });
it('should locate method call', () => { it('should locate method call', () => {