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});
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 {
interpolationConfig,
preserveWhitespaces,
@ -2084,23 +2086,11 @@ export function parseTemplate(
const {nodes, errors, styleUrls, styles, ngContentSelectors} =
htmlAstToRender3Ast(rootNodes, bindingParser);
if (errors && errors.length > 0) {
return {
interpolationConfig,
preserveWhitespaces,
template,
errors,
nodes: [],
styleUrls: [],
styles: [],
ngContentSelectors: []
};
}
return {
interpolationConfig,
preserveWhitespaces,
errors: null,
errors: errors.length > 0 ? errors : null,
template,
nodes,
styleUrls,
@ -2265,6 +2255,8 @@ export interface ParsedTemplate {
/**
* 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;

View File

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