diff --git a/app-shell/src/app/app-shell-providers.ts b/app-shell/src/app/app-shell-providers.ts new file mode 100644 index 0000000..745d175 --- /dev/null +++ b/app-shell/src/app/app-shell-providers.ts @@ -0,0 +1,27 @@ +import { Provider} from '@angular/core'; + +import { IS_PRERENDER } from './is-prerender.service'; +import { TemplateVisibilityStrategy } from './template-visibility-strategy'; +import { TemplateCommentStrategy } from './template-comment-strategy'; + +export const APP_SHELL_RUNTIME_PROVIDERS: Provider[] = [ + { + provide: IS_PRERENDER, + useValue: false + }, + { + provide: TemplateVisibilityStrategy, + useClass: TemplateCommentStrategy + } +]; + +export const APP_SHELL_BUILD_PROVIDERS: Provider[] = [ + { + provide: IS_PRERENDER, + useValue: true + }, + { + provide: TemplateVisibilityStrategy, + useClass: TemplateCommentStrategy + } +]; diff --git a/app-shell/src/app/index.ts b/app-shell/src/app/index.ts index d9c2768..394070d 100644 --- a/app-shell/src/app/index.ts +++ b/app-shell/src/app/index.ts @@ -1,3 +1,4 @@ export * from './module'; export * from './prerender'; export * from './shell'; + diff --git a/app-shell/src/app/shell.spec.ts b/app-shell/src/app/shell.spec.ts index 8a33d56..3ea3cfe 100644 --- a/app-shell/src/app/shell.spec.ts +++ b/app-shell/src/app/shell.spec.ts @@ -22,6 +22,7 @@ export default function () { expect(fixture.debugElement.childNodes.length).toBe(1); expect(fixture.debugElement.childNodes[0].nativeNode.data).toBe('template bindings={}'); }); + it('should render the element at runtime', () => { const fixture = TestBed .configureTestingModule({ @@ -55,6 +56,7 @@ export default function () { expect(fixture.debugElement.childNodes[0].nativeNode.data).toBe('template bindings={}'); expect(fixture.debugElement.childNodes[1].nativeNode.name).toBe('div'); }); + it('should NOT render the element at runtime', () => { const fixture = TestBed .configureTestingModule({ diff --git a/app-shell/src/app/template-comment-strategy.ts b/app-shell/src/app/template-comment-strategy.ts new file mode 100644 index 0000000..fd84c5c --- /dev/null +++ b/app-shell/src/app/template-comment-strategy.ts @@ -0,0 +1,31 @@ +import { Injectable } from '@angular/core'; +import { __platform_browser_private__ as _ } from '@angular/platform-browser'; + +import { TemplateVisibilityStrategy } from './template-visibility-strategy'; + +@Injectable() +export class TemplateCommentStrategy extends TemplateVisibilityStrategy { + private _hidden: any[] = []; + private _shown: any[] = []; + + show(marker: string) { + (this._rootNodes[marker] || []) + .filter((node: any) => this._shown.indexOf(node) < 0) + .forEach((node: any) => { + _.getDOM().setAttribute(node, marker, ''); + this._shown.push(node); + }); + } + + hide(marker: string) { + const DOM = _.getDOM(); + (this._rootNodes[marker] || []) + .filter((node: any) => this._hidden.indexOf(node) < 0) + .forEach((node: any) => { + const comment = DOM.createComment(`${marker}(${DOM.getOuterHTML(node)})`); + const parentNode = DOM.parentElement(node); + DOM.replaceChild(parentNode, comment, node); + this._hidden.push(node); + }); + } +} diff --git a/app-shell/src/app/template-visibility-strategy.ts b/app-shell/src/app/template-visibility-strategy.ts new file mode 100644 index 0000000..36a6f43 --- /dev/null +++ b/app-shell/src/app/template-visibility-strategy.ts @@ -0,0 +1,15 @@ +export interface DirectiveNodes { + [marker: string]: any[]; +}; + +export abstract class TemplateVisibilityStrategy { + protected _rootNodes: DirectiveNodes = {}; + + abstract show(marker: string): void; + abstract hide(marker: string): void; + + setRootNodes(marker: string, ...rootNodes: any[]) { + const nodes = this._rootNodes[marker] || []; + this._rootNodes[marker] = nodes.concat(rootNodes); + } +} diff --git a/app-shell/src/experimental/shell-parser/ast/ast-node.ts b/app-shell/src/experimental/shell-parser/ast/ast-node.ts index 663a35c..6035ead 100644 --- a/app-shell/src/experimental/shell-parser/ast/ast-node.ts +++ b/app-shell/src/experimental/shell-parser/ast/ast-node.ts @@ -9,5 +9,5 @@ export interface ASTNode { parentNode?: ASTNode; nodeName: string; value?: string; + data?: string; } - diff --git a/app-shell/src/experimental/shell-parser/ast/index.ts b/app-shell/src/experimental/shell-parser/ast/index.ts index ddebadd..1f72a8d 100644 --- a/app-shell/src/experimental/shell-parser/ast/index.ts +++ b/app-shell/src/experimental/shell-parser/ast/index.ts @@ -1,2 +1 @@ export * from './ast-node'; - diff --git a/app-shell/src/experimental/shell-parser/config.ts b/app-shell/src/experimental/shell-parser/config.ts index 96a186b..be13ab3 100644 --- a/app-shell/src/experimental/shell-parser/config.ts +++ b/app-shell/src/experimental/shell-parser/config.ts @@ -3,6 +3,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 = '[shellNoRender]'; +const RENDER_MARKER = 'shellRender'; const ROUTE_DEFINITIONS: RouteDefinition[] = []; const INLINE_IMAGES: string[] = ['png', 'svg', 'jpg']; @@ -13,6 +14,7 @@ export interface ShellParserConfig { APP_SHELL_URL?: string; SHELL_PARSER_CACHE_NAME?: string; NO_RENDER_CSS_SELECTOR?: string; + RENDER_MARKER?: string; ROUTE_DEFINITIONS?: RouteDefinition[]; INLINE_IMAGES?: string[]; } @@ -21,6 +23,7 @@ export const SHELL_PARSER_DEFAULT_CONFIG: ShellParserConfig = { SHELL_PARSER_CACHE_NAME, APP_SHELL_URL, NO_RENDER_CSS_SELECTOR, + RENDER_MARKER, ROUTE_DEFINITIONS, INLINE_IMAGES }; diff --git a/app-shell/src/experimental/shell-parser/node-matcher/css-node-matcher.spec.ts b/app-shell/src/experimental/shell-parser/node-matcher/css-node-matcher.spec.ts index 2180578..5edf27f 100644 --- a/app-shell/src/experimental/shell-parser/node-matcher/css-node-matcher.spec.ts +++ b/app-shell/src/experimental/shell-parser/node-matcher/css-node-matcher.spec.ts @@ -1,6 +1,3 @@ -import { - inject -} from '@angular/core/testing'; import { ASTNode } from '../ast'; import { CssSelector } from './css-selector'; import { CssNodeMatcher } from './css-node-matcher'; diff --git a/app-shell/src/experimental/shell-parser/node-matcher/css-selector/css-selector.spec.ts b/app-shell/src/experimental/shell-parser/node-matcher/css-selector/css-selector.spec.ts index 0f5dae5..91a5074 100644 --- a/app-shell/src/experimental/shell-parser/node-matcher/css-selector/css-selector.spec.ts +++ b/app-shell/src/experimental/shell-parser/node-matcher/css-selector/css-selector.spec.ts @@ -1,6 +1,3 @@ -import { - inject -} from '@angular/core/testing'; import { CssSelector } from './css-selector'; describe('CssSelector', () => { @@ -52,7 +49,5 @@ describe('CssSelector', () => { expect(result.elementId).toBe('baz'); expect(result.classNames).toEqual(['foo', 'qux']); }); - }); }); - diff --git a/app-shell/src/experimental/shell-parser/node-matcher/css-selector/index.ts b/app-shell/src/experimental/shell-parser/node-matcher/css-selector/index.ts index c33a033..724cfb0 100644 --- a/app-shell/src/experimental/shell-parser/node-matcher/css-selector/index.ts +++ b/app-shell/src/experimental/shell-parser/node-matcher/css-selector/index.ts @@ -1,2 +1 @@ export * from './css-selector'; - diff --git a/app-shell/src/experimental/shell-parser/node-matcher/node-matcher.ts b/app-shell/src/experimental/shell-parser/node-matcher/node-matcher.ts index ba6bd29..1dbcb2a 100644 --- a/app-shell/src/experimental/shell-parser/node-matcher/node-matcher.ts +++ b/app-shell/src/experimental/shell-parser/node-matcher/node-matcher.ts @@ -3,4 +3,3 @@ import {ASTNode} from '../ast'; export abstract class NodeMatcher { abstract match(node: ASTNode): boolean; } - diff --git a/app-shell/src/experimental/shell-parser/node-visitor/index.ts b/app-shell/src/experimental/shell-parser/node-visitor/index.ts index edbc12d..33da9dd 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/index.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/index.ts @@ -1,4 +1,5 @@ export * from './node-visitor'; export * from './resource-inline'; export * from './template-strip-visitor'; +export * from './template-recover-visitor'; diff --git a/app-shell/src/experimental/shell-parser/node-visitor/node-visitor.ts b/app-shell/src/experimental/shell-parser/node-visitor/node-visitor.ts index 74a0d3a..d483452 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/node-visitor.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/node-visitor.ts @@ -16,8 +16,6 @@ export abstract class NodeVisitor { } else { return null; } - }) + }); } - } - diff --git a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.spec.ts b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.spec.ts index 9203dda..fea66a7 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.spec.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.spec.ts @@ -1,7 +1,3 @@ -import { - inject -} from '@angular/core/testing'; - import {ASTNode} from '../../ast'; import {MockWorkerScope, MockResponse} from '../../testing'; import {InlineStyleResourceInlineVisitor} from './'; diff --git a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.ts b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.ts index 99ebba7..5de6320 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/inline-style-resource-inline-visitor.ts @@ -1,8 +1,5 @@ import {ASTNode, ASTAttribute} from '../../ast'; import {ResourceInlineVisitor} from './resource-inline-visitor'; -import {WorkerScope} from '../../context'; - -const URL_REGEXP = /:\s+url\(['"]?(.*?)['"]?\)/gmi; export class InlineStyleResourceInlineVisitor extends ResourceInlineVisitor { diff --git a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/resource-inline-visitor.ts b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/resource-inline-visitor.ts index d5f3f41..433869f 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/resource-inline-visitor.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/resource-inline-visitor.ts @@ -1,4 +1,3 @@ -import {ASTNode} from '../../ast'; import {NodeVisitor} from '../node-visitor'; import {WorkerScope} from '../../context'; @@ -13,8 +12,7 @@ export abstract class ResourceInlineVisitor extends NodeVisitor { inlineAssets(style: string) { let urls = this.getImagesUrls(style); urls = urls.filter((url: string, idx: number) => urls.indexOf(url) === idx); - return this.processInline(urls, style) - .then((content: string) => content); + return this.processInline(urls, style); } protected getImagesUrls(styles: string): string[] { @@ -55,6 +53,4 @@ export abstract class ResourceInlineVisitor extends NodeVisitor { img ? content.replace(new RegExp(urls[idx], 'g'), img) : content, styles); }); } - } - diff --git a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.spec.ts b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.spec.ts index 753e4f1..7b99eb3 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.spec.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.spec.ts @@ -1,7 +1,3 @@ -import { - inject -} from '@angular/core/testing'; - import {ASTNode} from '../../ast'; import {MockWorkerScope, MockResponse} from '../../testing'; import {StylesheetResourceInlineVisitor} from './'; @@ -193,4 +189,3 @@ describe('ResourceInlineVisitor', () => { }); }); - diff --git a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.ts b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.ts index cdeca53..ef32cfc 100644 --- a/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.ts +++ b/app-shell/src/experimental/shell-parser/node-visitor/resource-inline/stylesheet-resource-inline-visitor.ts @@ -17,6 +17,4 @@ export class StylesheetResourceInlineVisitor extends ResourceInlineVisitor { } return Promise.resolve(node); } - } - diff --git a/app-shell/src/experimental/shell-parser/node-visitor/template-recover-visitor.spec.ts b/app-shell/src/experimental/shell-parser/node-visitor/template-recover-visitor.spec.ts new file mode 100644 index 0000000..85ddd37 --- /dev/null +++ b/app-shell/src/experimental/shell-parser/node-visitor/template-recover-visitor.spec.ts @@ -0,0 +1,111 @@ +import {ASTNode} from '../ast'; +import {TemplateRecoverVisitor} from './'; +import {Parse5TemplateParser} from '../template-parser'; + +describe('TemplateRecoverVisitor', () => { + + let astRoot: ASTNode; + let nestedNode: ASTNode; + let differentComments: ASTNode; + + beforeEach(() => { + astRoot = { + nodeName: '#comment', + attrs: null, + data: 'shellRender(