Skip to content
This repository was archived by the owner on Oct 12, 2021. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions app-shell/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
"format": "clang-format -i -style=file --glob=src/**/*.ts",
"pree2e": "webdriver-manager update",
"e2e": "protractor",
"build_publish": "rm -rf dist && tsc -p src/tsconfig.publish.es5.json && tsc -p src/tsconfig.publish.es6.json && cp src/package.json dist/package.json"
"clean": "rm -rf dist",
"build": "ng build && tsc -p src/tsconfig.publish.es5.json && tsc -p src/tsconfig.publish.es6.json && cp src/package.json dist/app/package.json && browserify dist/app/shell-parser/index.js -s shellParserFactory > dist/app/shell-parser.js && rm -rf dist/app/shell-parser && rm -rf dist/app/vendor",
Copy link
Contributor

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.

Copy link
Member Author

@mgechev mgechev Jun 30, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

"build_publish": "npm run clean && npm run build"
},
"private": true,
"dependencies": {
Expand All @@ -21,14 +23,14 @@
"@angular/platform-browser-dynamic": "2.0.0-rc.0",
"@angular/router": "2.0.0-rc.0",
"es6-shim": "^0.35.0",
"parse5": "^2.1.5",
"reflect-metadata": "0.1.3",
"rxjs": "5.0.0-beta.6",
"systemjs": "0.19.26",
"zone.js": "^0.6.12"
},
"devDependencies": {
"angular-cli": "0.0.*",
"browserify": "^13.0.1",
"clang-format": "^1.0.35",
"codelyzer": "0.0.14",
"ember-cli-inject-live-reload": "^1.4.0",
Expand All @@ -37,6 +39,7 @@
"karma": "^0.13.15",
"karma-chrome-launcher": "^0.2.3",
"karma-jasmine": "^0.3.8",
"parse5": "2.1.5",
"protractor": "^3.3.0",
"ts-node": "^0.5.5",
"tslint": "^3.6.0",
Expand Down
2 changes: 1 addition & 1 deletion app-shell/src/app/shell-parser/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ export type RouteDefinition = string;

const SHELL_PARSER_CACHE_NAME = 'mobile-toolkit:app-shell';
const APP_SHELL_URL = './app_shell.html';
const NO_RENDER_CSS_SELECTOR = '.shell-no-render';
const NO_RENDER_CSS_SELECTOR = '[shellNoRender]';
const ROUTE_DEFINITIONS: RouteDefinition[] = [];

// TODO(mgechev): use if we decide to include @angular/core
Expand Down
14 changes: 7 additions & 7 deletions app-shell/src/app/shell-parser/shell-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,10 @@ const prerenderedTemplate = `
<header>
<h1>Hey I'm appshell!</h1>
</header>
<content class="shell-no-render">
<content shellNoRender>
<p>Hello world</p>
</content>
<div class="shell-no-render bar baz">
<div shellNoRender class="bar baz">
</div>
</body>
</html>
Expand Down Expand Up @@ -58,7 +58,7 @@ const strippedContent = `
<header>
<h1>Hey I'm appshell!</h1>
</header>
<div class="shell-no-render bar baz">
<div shellNoRender="" class="bar baz">
</div>
</body>
</html>
Expand All @@ -73,7 +73,7 @@ const strippedWithComposedSelector = `
<header>
<h1>Hey I'm appshell!</h1>
</header>
<content class="shell-no-render">
<content shellNoRender="">
<p>Hello world</p>
</content>
</body>
Expand Down Expand Up @@ -144,7 +144,7 @@ describe('ShellParserImpl', () => {
it('should strip with nested selector', (done: any) => {
const mockScope = new MockWorkerScope();
const parser = createMockedWorker(mockScope, {
NO_RENDER_CSS_SELECTOR: 'content.shell-no-render'
NO_RENDER_CSS_SELECTOR: 'content[shellNoRender]'
});
const response = new MockResponse(prerenderedTemplate);
parser.parseDoc(response)
Expand All @@ -158,7 +158,7 @@ describe('ShellParserImpl', () => {
it('should strip with nested selector', (done: any) => {
const mockScope = new MockWorkerScope();
const parser = createMockedWorker(mockScope, {
NO_RENDER_CSS_SELECTOR: '.shell-no-render.bar'
NO_RENDER_CSS_SELECTOR: '[shellNoRender].bar'
});
const response = new MockResponse(prerenderedTemplate);
parser.parseDoc(response)
Expand All @@ -172,7 +172,7 @@ describe('ShellParserImpl', () => {
it('should return content type "text/html" with status 200', (done: any) => {
const mockScope = new MockWorkerScope();
const parser = createMockedWorker(mockScope, {
NO_RENDER_CSS_SELECTOR: '.shell-no-render.bar'
NO_RENDER_CSS_SELECTOR: '[shellNoRender].bar'
});
const response = new MockResponse(prerenderedTemplate);
parser.parseDoc(response)
Expand Down
2 changes: 1 addition & 1 deletion app-shell/src/app/shell-parser/template-parser/index.ts
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
@@ -1,8 +1,10 @@
import {ASTNode} from '../ast';
import {TemplateParser} from './template-parser';
import {ASTNode} from '../../ast';
import {TemplateParser} from '../template-parser';

var Parser = require('../../../vendor/parse5/lib/parser');
var Serializer = require('../../../vendor/parse5/lib/serializer');
import './tokenizer-patch';

var Parser = require('../../../../vendor/parse5/lib/parser');
var Serializer = require('../../../../vendor/parse5/lib/serializer');

export class Parse5TemplateParser extends TemplateParser {
parse(template: string): ASTNode {
Expand Down
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();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests get errors on this line if last is undefined (TypeError: Cannot read property 'toUpperCase' of undefined). If I change to if (last && last !== char) then tests pass. Safe change to make?

Copy link
Member Author

Choose a reason for hiding this comment

The 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':
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need case sensitivity for tag names?

Copy link
Member Author

@mgechev mgechev Jun 30, 2016

Choose a reason for hiding this comment

The 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();
};

2 changes: 1 addition & 1 deletion app-shell/src/tsconfig.publish.es5.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": true,
"outDir": "../dist/",
"outDir": "../dist/app",
"rootDir": "./app",
"sourceMap": true,
"target": "es5",
Expand Down
2 changes: 1 addition & 1 deletion app-shell/src/tsconfig.publish.es6.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"moduleResolution": "node",
"noEmitOnError": true,
"noImplicitAny": true,
"outDir": "../dist/esm",
"outDir": "../dist/app/esm",
"rootDir": "./app",
"sourceMap": true,
"target": "es6",
Expand Down