feat(HtmlParser): enforce only void & foreign elts can be self closed
BREAKING CHANGE: `<whatever />` used to be expanded to `<whatever></whatever>`. The parser now follows the HTML5 spec more closely. Only void and foreign elements can be self closed. Closes #5591
This commit is contained in:
parent
56604468e0
commit
d388c0ae62
|
@ -134,6 +134,11 @@ class TreeBuilder {
|
|||
if (this.peek.type === HtmlTokenType.TAG_OPEN_END_VOID) {
|
||||
this._advance();
|
||||
selfClosing = true;
|
||||
if (namespacePrefix(fullName) == null && !getHtmlTagDefinition(fullName).isVoid) {
|
||||
this.errors.push(HtmlTreeError.create(
|
||||
fullName, startTagToken.sourceSpan.start,
|
||||
`Only void and foreign elements can be self closed "${startTagToken.parts[1]}"`));
|
||||
}
|
||||
} else if (this.peek.type === HtmlTokenType.TAG_OPEN_END) {
|
||||
this._advance();
|
||||
selfClosing = false;
|
||||
|
|
|
@ -147,6 +147,16 @@ export function main() {
|
|||
expect(humanizeDom(parser.parse('<DiV><P></p></dIv>', 'TestComp')))
|
||||
.toEqual([[HtmlElementAst, 'DiV', 0], [HtmlElementAst, 'P', 1]]);
|
||||
});
|
||||
|
||||
it('should support self closing void elements', () => {
|
||||
expect(humanizeDom(parser.parse('<input />', 'TestComp')))
|
||||
.toEqual([[HtmlElementAst, 'input', 0]]);
|
||||
});
|
||||
|
||||
it('should support self closing foreign elements', () => {
|
||||
expect(humanizeDom(parser.parse('<math />', 'TestComp')))
|
||||
.toEqual([[HtmlElementAst, '@math:math', 0]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('attributes', () => {
|
||||
|
@ -175,8 +185,8 @@ export function main() {
|
|||
});
|
||||
|
||||
it('should support mamespace', () => {
|
||||
expect(humanizeDom(parser.parse('<use xlink:href="Port" />', 'TestComp')))
|
||||
.toEqual([[HtmlElementAst, 'use', 0], [HtmlAttrAst, '@xlink:href', 'Port']]);
|
||||
expect(humanizeDom(parser.parse('<svg:use xlink:href="Port" />', 'TestComp')))
|
||||
.toEqual([[HtmlElementAst, '@svg:use', 0], [HtmlAttrAst, '@xlink:href', 'Port']]);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -216,6 +226,22 @@ export function main() {
|
|||
.toEqual([['input', 'Void elements do not have end tags "input"', '0:7']]);
|
||||
});
|
||||
|
||||
it('should report self closing html element', () => {
|
||||
let errors = parser.parse('<p />', 'TestComp').errors;
|
||||
expect(errors.length).toEqual(1);
|
||||
expect(humanizeErrors(errors))
|
||||
.toEqual([['p', 'Only void and foreign elements can be self closed "p"', '0:0']]);
|
||||
});
|
||||
|
||||
it('should report self closing custom element', () => {
|
||||
let errors = parser.parse('<my-cmp />', 'TestComp').errors;
|
||||
expect(errors.length).toEqual(1);
|
||||
expect(humanizeErrors(errors))
|
||||
.toEqual([
|
||||
['my-cmp', 'Only void and foreign elements can be self closed "my-cmp"', '0:0']
|
||||
]);
|
||||
});
|
||||
|
||||
it('should also report lexer errors', () => {
|
||||
let errors = parser.parse('<!-err--><div></p></div>', 'TestComp').errors;
|
||||
expect(errors.length).toEqual(2);
|
||||
|
|
|
@ -728,8 +728,8 @@ Parser Error: Unexpected token 'b' at column 3 in [a b] in TestComp@0:5 ("<div [
|
|||
type: new CompileTypeMetadata({name: 'DirB'}),
|
||||
template: new CompileTemplateMetadata({ngContentSelectors: []})
|
||||
});
|
||||
expect(() => parse('<div/>', [dirB, dirA])).toThrowError(`Template parse errors:
|
||||
More than one component: DirB,DirA ("[ERROR ->]<div/>"): TestComp@0:0`);
|
||||
expect(() => parse('<div>', [dirB, dirA])).toThrowError(`Template parse errors:
|
||||
More than one component: DirB,DirA ("[ERROR ->]<div>"): TestComp@0:0`);
|
||||
});
|
||||
|
||||
it('should not allow components or element bindings nor dom events on explicit embedded templates',
|
||||
|
|
|
@ -578,10 +578,12 @@ export function main() {
|
|||
inject(
|
||||
[TestComponentBuilder, AsyncTestCompleter],
|
||||
(tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(MyComp, new ViewMetadata({
|
||||
template: '<p><child-cmp var-alice/><child-cmp var-bob/></p>',
|
||||
directives: [ChildComp]
|
||||
}))
|
||||
tcb.overrideView(
|
||||
MyComp, new ViewMetadata({
|
||||
template:
|
||||
'<p><child-cmp var-alice></child-cmp><child-cmp var-bob></child-cmp></p>',
|
||||
directives: [ChildComp]
|
||||
}))
|
||||
|
||||
.createAsync(MyComp)
|
||||
.then((fixture) => {
|
||||
|
|
|
@ -352,7 +352,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include platform directives.', () async {
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(
|
||||
template: '<bar></bar>');
|
||||
final viewAnnotation = new AnnotationModel()
|
||||
..name = 'View'
|
||||
..isView = true;
|
||||
|
@ -370,7 +371,8 @@ void allTests() {
|
|||
});
|
||||
|
||||
it('should include platform directives when it it a list.', () async {
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(template: '<bar/>');
|
||||
fooComponentMeta.template = new CompileTemplateMetadata(
|
||||
template: '<bar></bar>');
|
||||
final viewAnnotation = new AnnotationModel()
|
||||
..name = 'View'
|
||||
..isView = true;
|
||||
|
|
Loading…
Reference in New Issue