-
Notifications
You must be signed in to change notification settings - Fork 174
feat(parser): perform case sensitive parsing #68
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,3 @@ | ||
| export * from './template-parser'; | ||
| export * from './parse5-template-parser'; | ||
| export * from './parse5/parse5-template-parser'; | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| import { | ||
| beforeEach, | ||
| it, | ||
| describe, | ||
| expect, | ||
| inject | ||
| } from '@angular/core/testing'; | ||
| import { Parse5TemplateParser } from './parse5-template-parser'; | ||
|
|
||
| const caseSensitiveTemplate = | ||
| ` | ||
| <!DOCTYPE html> | ||
| <html> | ||
| <head></head> | ||
| <body> | ||
| <Div> | ||
| <Section Title="Bar"></Section> | ||
| </Div> | ||
| <sEctiON aTTrIbUtE=""> | ||
| Content | ||
| </sEctiON> | ||
| </body> | ||
| </html> | ||
| `; | ||
|
|
||
| const normalize = (template: string) => | ||
| template | ||
| .replace(/^\s+/gm, '') | ||
| .replace(/\s+$/gm, '') | ||
| .replace(/\n/gm, ''); | ||
|
|
||
| describe('Parse5TemplateParser', () => { | ||
|
|
||
| let parser = new Parse5TemplateParser(); | ||
|
|
||
| describe('parse', () => { | ||
|
|
||
| it('should handle capital letters', () => { | ||
| const ast = parser.parse(caseSensitiveTemplate); | ||
| const div = ast.childNodes[1].childNodes[2].childNodes[1]; | ||
| expect(div.nodeName).toBe('Div'); | ||
| expect(div.childNodes[1].attrs[0].name).toBe('Title'); | ||
| }); | ||
|
|
||
| it('should perform case sensitive parsing', () => { | ||
| const ast = parser.parse(caseSensitiveTemplate); | ||
| const section = ast.childNodes[1].childNodes[2].childNodes[3]; | ||
| expect(section.nodeName).toBe('sEctiON'); | ||
| expect(section.attrs[0].name).toBe('aTTrIbUtE'); | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| describe('serialize', () => { | ||
|
|
||
| it('should serialize the template keeping case sensitivity', () => { | ||
| const ast = parser.parse(caseSensitiveTemplate); | ||
| expect(normalize(parser.serialize(ast))) | ||
| .toBe(normalize(caseSensitiveTemplate)); | ||
| }); | ||
|
|
||
| }); | ||
|
|
||
| }); | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| import './tokenizer-patch'; | ||
|
|
||
| import { | ||
| beforeEach, | ||
| it, | ||
| describe, | ||
| expect, | ||
| inject | ||
| } from '@angular/core/testing'; | ||
|
|
||
| var Tokenizer = require('../../../../vendor/parse5/lib/tokenizer'); | ||
|
|
||
| describe('tokenizer\'s patch', () => { | ||
|
|
||
| let lexer: any; | ||
| beforeEach(() => { | ||
| lexer = new Tokenizer(); | ||
| }); | ||
|
|
||
| it('should keep case sensntivity of elements', () => { | ||
| lexer.write('<DiV></DiV>'); | ||
| const openDiv = lexer.getNextToken(); | ||
| expect(openDiv.type).toBe('START_TAG_TOKEN'); | ||
| expect(openDiv.tagName).toBe('DiV'); | ||
| expect(openDiv.selfClosing).toBe(false); | ||
|
|
||
| const closeDiv = lexer.getNextToken(); | ||
| expect(closeDiv.type).toBe('END_TAG_TOKEN'); | ||
| expect(closeDiv.tagName).toBe('DiV'); | ||
| }); | ||
|
|
||
| it('should preserve case sensitivity of complex elements', () => { | ||
| lexer.write('<mY-ApP></mY-ApP>'); | ||
| const open = lexer.getNextToken(); | ||
| expect(open.tagName).toBe('mY-ApP'); | ||
| const close = lexer.getNextToken(); | ||
| expect(close.tagName).toBe('mY-ApP'); | ||
| }); | ||
|
|
||
| it('should keep case sensitivity of attrs', () => { | ||
| lexer.write('<dIV StYlE="color: red;"></dIV>'); | ||
| const div = lexer.getNextToken(); | ||
| expect(div.tagName).toBe('dIV'); | ||
| expect(div.attrs[0].name).toBe('StYlE'); | ||
| expect(div.attrs[0].value).toBe('color: red;'); | ||
| }); | ||
|
|
||
| }); | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| var Tokenizer = require('../../../../vendor/parse5/lib/tokenizer'); | ||
|
|
||
| // Monkey patching the lexer in order to establish | ||
| // case sensitive parsing of the input templates. | ||
| // This way we'll be able to use case sensitive attribute | ||
| // and element selectors for stripping the content that | ||
| // is not required for the App Shell. | ||
| // | ||
| // Since we're patching module's internals we cannot | ||
| // use parse5 as dependency of the App Shell since we | ||
| // won't have access to the tokenizer in order to patch | ||
| // it runtime. Because of that we distribute the entire | ||
| // Runtime Parser as a single bundle which includes parse5. | ||
| Tokenizer.prototype.getNextToken = function () { | ||
| function replaceLastWithUppercase(token: any, prop: string, cp: number) { | ||
| if (token) { | ||
| let char = String.fromCharCode(cp); | ||
| let val = token[prop]; | ||
| let last = val[val.length - 1]; | ||
| if (last && last !== char) { | ||
| token[prop] = val.substring(0, val.length - 1) + last.toUpperCase(); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Tests get errors on this line if last is undefined (
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, it is safe. |
||
| } | ||
| } | ||
| } | ||
| while (!this.tokenQueue.length && this.active) { | ||
| this._hibernationSnapshot(); | ||
| const cp = this._consume(); | ||
| if (!this._ensureHibernation()) { | ||
| this[this.state](cp); | ||
| } | ||
| switch (this.state) { | ||
| case 'TAG_NAME_STATE': | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do we need case sensitivity for tag names?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I did it in order to be consistent with the Angular parser, since we allow tags as selectors. |
||
| replaceLastWithUppercase(this.currentToken, 'tagName', cp); | ||
| break; | ||
| case 'ATTRIBUTE_NAME_STATE': | ||
| replaceLastWithUppercase(this.currentAttr, 'name', cp); | ||
| break; | ||
| } | ||
| } | ||
| return this.tokenQueue.shift(); | ||
| }; | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs browserify dependency installed. I can take care of this.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.