feat(compiler): change html parser to preserve comments

This commit is contained in:
vsavkin
2016-03-06 20:21:20 -08:00
committed by Victor Savkin
parent f1796d67f4
commit 70d18b5b53
6 changed files with 36 additions and 6 deletions

View File

@ -23,10 +23,16 @@ export class HtmlElementAst implements HtmlAst {
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); } visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitElement(this, context); }
} }
export class HtmlCommentAst implements HtmlAst {
constructor(public value: string, public sourceSpan: ParseSourceSpan) {}
visit(visitor: HtmlAstVisitor, context: any): any { return visitor.visitComment(this, context); }
}
export interface HtmlAstVisitor { export interface HtmlAstVisitor {
visitElement(ast: HtmlElementAst, context: any): any; visitElement(ast: HtmlElementAst, context: any): any;
visitAttr(ast: HtmlAttrAst, context: any): any; visitAttr(ast: HtmlAttrAst, context: any): any;
visitText(ast: HtmlTextAst, context: any): any; visitText(ast: HtmlTextAst, context: any): any;
visitComment(ast: HtmlCommentAst, context: any): any;
} }
export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[], context: any = null): any[] { export function htmlVisitAll(visitor: HtmlAstVisitor, asts: HtmlAst[], context: any = null): any[] {

View File

@ -11,7 +11,7 @@ import {
import {ListWrapper} from 'angular2/src/facade/collection'; import {ListWrapper} from 'angular2/src/facade/collection';
import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlElementAst} from './html_ast'; import {HtmlAst, HtmlAttrAst, HtmlTextAst, HtmlCommentAst, HtmlElementAst} from './html_ast';
import {Injectable} from 'angular2/src/core/di'; import {Injectable} from 'angular2/src/core/di';
import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer'; import {HtmlToken, HtmlTokenType, tokenizeHtml} from './html_lexer';
@ -98,9 +98,11 @@ class TreeBuilder {
this._advanceIf(HtmlTokenType.CDATA_END); this._advanceIf(HtmlTokenType.CDATA_END);
} }
private _consumeComment(startToken: HtmlToken) { private _consumeComment(token: HtmlToken) {
this._advanceIf(HtmlTokenType.RAW_TEXT); var text = this._advanceIf(HtmlTokenType.RAW_TEXT);
this._advanceIf(HtmlTokenType.COMMENT_END); this._advanceIf(HtmlTokenType.COMMENT_END);
var value = isPresent(text) ? text.parts[0].trim() : null;
this._addToParent(new HtmlCommentAst(value, token.sourceSpan))
} }
private _consumeText(token: HtmlToken) { private _consumeText(token: HtmlToken) {

View File

@ -8,7 +8,14 @@ import {
isPresent isPresent
} from 'angular2/src/facade/lang'; } from 'angular2/src/facade/lang';
import {HtmlAstVisitor, HtmlAttrAst, HtmlElementAst, HtmlTextAst, HtmlAst} from './html_ast'; import {
HtmlAstVisitor,
HtmlAttrAst,
HtmlElementAst,
HtmlTextAst,
HtmlCommentAst,
HtmlAst
} from './html_ast';
import {HtmlParser, HtmlParseTreeResult} from './html_parser'; import {HtmlParser, HtmlParseTreeResult} from './html_parser';
import {dashCaseToCamelCase, camelCaseToDashCase} from './util'; import {dashCaseToCamelCase, camelCaseToDashCase} from './util';
@ -37,6 +44,8 @@ export class LegacyHtmlAstTransformer implements HtmlAstVisitor {
constructor(private dashCaseSelectors?: string[]) {} constructor(private dashCaseSelectors?: string[]) {}
visitComment(ast: HtmlCommentAst, context: any): any { return ast; }
visitElement(ast: HtmlElementAst, context: any): HtmlElementAst { visitElement(ast: HtmlElementAst, context: any): HtmlElementAst {
this.visitingTemplateEl = ast.name.toLowerCase() == 'template'; this.visitingTemplateEl = ast.name.toLowerCase() == 'template';
let attrs = ast.attrs.map(attr => attr.visit(this, null)); let attrs = ast.attrs.map(attr => attr.visit(this, null));

View File

@ -20,6 +20,7 @@ import {
HtmlTextAst, HtmlTextAst,
HtmlAttrAst, HtmlAttrAst,
HtmlAst, HtmlAst,
HtmlCommentAst,
htmlVisitAll htmlVisitAll
} from './html_ast'; } from './html_ast';
import {HtmlParser} from './html_parser'; import {HtmlParser} from './html_parser';
@ -126,6 +127,7 @@ class TemplatePreparseVisitor implements HtmlAstVisitor {
} }
return null; return null;
} }
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitAttr(ast: HtmlAttrAst, context: any): any { return null; } visitAttr(ast: HtmlAttrAst, context: any): any { return null; }
visitText(ast: HtmlTextAst, context: any): any { return null; } visitText(ast: HtmlTextAst, context: any): any { return null; }
} }

View File

@ -41,6 +41,7 @@ import {
HtmlElementAst, HtmlElementAst,
HtmlAttrAst, HtmlAttrAst,
HtmlTextAst, HtmlTextAst,
HtmlCommentAst,
htmlVisitAll htmlVisitAll
} from './html_ast'; } from './html_ast';
@ -209,6 +210,8 @@ class TemplateParseVisitor implements HtmlAstVisitor {
return new AttrAst(ast.name, ast.value, ast.sourceSpan); return new AttrAst(ast.name, ast.value, ast.sourceSpan);
} }
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitElement(element: HtmlElementAst, component: Component): any { visitElement(element: HtmlElementAst, component: Component): any {
var nodeName = element.name; var nodeName = element.name;
var preparsedElement = preparseElement(element); var preparsedElement = preparseElement(element);
@ -676,6 +679,7 @@ class NonBindableVisitor implements HtmlAstVisitor {
return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children, return new ElementAst(ast.name, htmlVisitAll(this, ast.attrs), [], [], [], [], children,
ngContentIndex, ast.sourceSpan); ngContentIndex, ast.sourceSpan);
} }
visitComment(ast: HtmlCommentAst, context: any): any { return null; }
visitAttr(ast: HtmlAttrAst, context: any): AttrAst { visitAttr(ast: HtmlAttrAst, context: any): AttrAst {
return new AttrAst(ast.name, ast.value, ast.sourceSpan); return new AttrAst(ast.name, ast.value, ast.sourceSpan);
} }

View File

@ -17,6 +17,7 @@ import {
HtmlElementAst, HtmlElementAst,
HtmlAttrAst, HtmlAttrAst,
HtmlTextAst, HtmlTextAst,
HtmlCommentAst,
htmlVisitAll htmlVisitAll
} from 'angular2/src/compiler/html_ast'; } from 'angular2/src/compiler/html_ast';
import {ParseError, ParseLocation, ParseSourceSpan} from 'angular2/src/compiler/parse_util'; import {ParseError, ParseLocation, ParseSourceSpan} from 'angular2/src/compiler/parse_util';
@ -233,9 +234,9 @@ export function main() {
}); });
describe('comments', () => { describe('comments', () => {
it('should ignore comments', () => { it('should preserve comments', () => {
expect(humanizeDom(parser.parse('<!-- comment --><div></div>', 'TestComp'))) expect(humanizeDom(parser.parse('<!-- comment --><div></div>', 'TestComp')))
.toEqual([[HtmlElementAst, 'div', 0]]); .toEqual([[HtmlCommentAst, 'comment', 0], [HtmlElementAst, 'div', 0]]);
}); });
}); });
@ -362,6 +363,12 @@ class Humanizer implements HtmlAstVisitor {
return null; return null;
} }
visitComment(ast: HtmlCommentAst, context: any): any {
var res = this._appendContext(ast, [HtmlCommentAst, ast.value, this.elDepth]);
this.result.push(res);
return null;
}
private _appendContext(ast: HtmlAst, input: any[]): any[] { private _appendContext(ast: HtmlAst, input: any[]): any[] {
if (!this.includeSourceSpan) return input; if (!this.includeSourceSpan) return input;
input.push(ast.sourceSpan.toString()); input.push(ast.sourceSpan.toString());