diff --git a/modules/@angular/core/src/di/reflective_provider.ts b/modules/@angular/core/src/di/reflective_provider.ts
index 2946b06588..58128b5329 100644
--- a/modules/@angular/core/src/di/reflective_provider.ts
+++ b/modules/@angular/core/src/di/reflective_provider.ts
@@ -223,7 +223,7 @@ function _extractToken(
if (!Array.isArray(metadata)) {
if (metadata instanceof Inject) {
- return _createDependency(metadata.token, optional, null);
+ return _createDependency(metadata['token'], optional, null);
} else {
return _createDependency(metadata, optional, null);
}
@@ -238,7 +238,7 @@ function _extractToken(
token = paramMetadata;
} else if (paramMetadata instanceof Inject) {
- token = paramMetadata.token;
+ token = paramMetadata['token'];
} else if (paramMetadata instanceof Optional) {
optional = true;
diff --git a/modules/@angular/core/test/dom/dom_adapter_spec.ts b/modules/@angular/core/test/dom/dom_adapter_spec.ts
index a11ca02e5d..b892a9b34f 100644
--- a/modules/@angular/core/test/dom/dom_adapter_spec.ts
+++ b/modules/@angular/core/test/dom/dom_adapter_spec.ts
@@ -12,6 +12,11 @@ import {el, stringifyElement} from '@angular/platform-browser/testing/browser_ut
export function main() {
describe('dom adapter', () => {
+ let defaultDoc: any;
+ beforeEach(() => {
+ defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
+ });
+
it('should not coalesque text nodes', () => {
const el1 = el('
a
');
const el2 = el('b
');
@@ -68,15 +73,15 @@ export function main() {
beforeEach(() => getDOM().resetBaseElement());
it('should return null if base element is absent',
- () => { expect(getDOM().getBaseHref()).toBeNull(); });
+ () => { expect(getDOM().getBaseHref(defaultDoc)).toBeNull(); });
it('should return the value of the base element', () => {
const baseEl = getDOM().createElement('base');
getDOM().setAttribute(baseEl, 'href', '/drop/bass/connon/');
- const headEl = getDOM().defaultDoc().head;
+ const headEl = defaultDoc.head;
getDOM().appendChild(headEl, baseEl);
- const baseHref = getDOM().getBaseHref();
+ const baseHref = getDOM().getBaseHref(defaultDoc);
getDOM().removeChild(headEl, baseEl);
getDOM().resetBaseElement();
@@ -86,10 +91,10 @@ export function main() {
it('should return a relative url', () => {
const baseEl = getDOM().createElement('base');
getDOM().setAttribute(baseEl, 'href', 'base');
- const headEl = getDOM().defaultDoc().head;
+ const headEl = defaultDoc.head;
getDOM().appendChild(headEl, baseEl);
- const baseHref = getDOM().getBaseHref();
+ const baseHref = getDOM().getBaseHref(defaultDoc);
getDOM().removeChild(headEl, baseEl);
getDOM().resetBaseElement();
diff --git a/modules/@angular/core/test/linker/integration_spec.ts b/modules/@angular/core/test/linker/integration_spec.ts
index 1963e4154a..916bfe6ba5 100644
--- a/modules/@angular/core/test/linker/integration_spec.ts
+++ b/modules/@angular/core/test/linker/integration_spec.ts
@@ -21,6 +21,7 @@ import {Attribute, Component, ContentChildren, Directive, HostBinding, HostListe
import {Renderer} from '@angular/core/src/render';
import {TestBed, async, fakeAsync, getTestBed, tick} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
+import {DOCUMENT} from '@angular/platform-browser/src/dom/dom_tokens';
import {dispatchEvent, el} from '@angular/platform-browser/testing/browser_util';
import {expect} from '@angular/platform-browser/testing/matchers';
@@ -838,19 +839,20 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
const template = '';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
+ const doc = TestBed.get(DOCUMENT);
const tc = fixture.debugElement.children[0];
const listener = tc.injector.get(DirectiveListeningDomEvent);
- dispatchEvent(getDOM().getGlobalEventTarget('window'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'window'), 'domEvent');
expect(listener.eventTypes).toEqual(['window_domEvent']);
listener.eventTypes = [];
- dispatchEvent(getDOM().getGlobalEventTarget('document'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'document'), 'domEvent');
expect(listener.eventTypes).toEqual(['document_domEvent', 'window_domEvent']);
fixture.destroy();
listener.eventTypes = [];
- dispatchEvent(getDOM().getGlobalEventTarget('body'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'body'), 'domEvent');
expect(listener.eventTypes).toEqual([]);
});
@@ -990,6 +992,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
const template = '';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
+ const doc = TestBed.get(DOCUMENT);
globalCounter = 0;
fixture.componentInstance.ctxBoolProp = true;
@@ -999,7 +1002,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
const listener = tc.injector.get(DirectiveListeningDomEvent);
const listenerother = tc.injector.get(DirectiveListeningDomEventOther);
- dispatchEvent(getDOM().getGlobalEventTarget('window'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'window'), 'domEvent');
expect(listener.eventTypes).toEqual(['window_domEvent']);
expect(listenerother.eventType).toEqual('other_domEvent');
expect(globalCounter).toEqual(1);
@@ -1007,12 +1010,12 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
fixture.componentInstance.ctxBoolProp = false;
fixture.detectChanges();
- dispatchEvent(getDOM().getGlobalEventTarget('window'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'window'), 'domEvent');
expect(globalCounter).toEqual(1);
fixture.componentInstance.ctxBoolProp = true;
fixture.detectChanges();
- dispatchEvent(getDOM().getGlobalEventTarget('window'), 'domEvent');
+ dispatchEvent(getDOM().getGlobalEventTarget(doc, 'window'), 'domEvent');
expect(globalCounter).toEqual(2);
// need to destroy to release all remaining global event listeners
diff --git a/modules/@angular/platform-browser/src/browser.ts b/modules/@angular/platform-browser/src/browser.ts
index 73a6e9ae75..24eb0b129c 100644
--- a/modules/@angular/platform-browser/src/browser.ts
+++ b/modules/@angular/platform-browser/src/browser.ts
@@ -30,7 +30,8 @@ import {DomSanitizer, DomSanitizerImpl} from './security/dom_sanitization_servic
export const INTERNAL_BROWSER_PLATFORM_PROVIDERS: Provider[] = [
{provide: PLATFORM_INITIALIZER, useValue: initDomAdapter, multi: true},
- {provide: PlatformLocation, useClass: BrowserPlatformLocation}
+ {provide: PlatformLocation, useClass: BrowserPlatformLocation},
+ {provide: DOCUMENT, useFactory: _document, deps: []},
];
/**
@@ -59,12 +60,8 @@ export function errorHandler(): ErrorHandler {
return new ErrorHandler();
}
-export function meta(): Meta {
- return new Meta(getDOM());
-}
-
export function _document(): any {
- return getDOM().defaultDoc();
+ return document;
}
export function _resolveDefaultAnimationDriver(): AnimationDriver {
@@ -83,7 +80,6 @@ export function _resolveDefaultAnimationDriver(): AnimationDriver {
providers: [
BROWSER_SANITIZATION_PROVIDERS,
{provide: ErrorHandler, useFactory: errorHandler, deps: []},
- {provide: DOCUMENT, useFactory: _document, deps: []},
{provide: EVENT_MANAGER_PLUGINS, useClass: DomEventsPlugin, multi: true},
{provide: EVENT_MANAGER_PLUGINS, useClass: KeyEventsPlugin, multi: true},
{provide: EVENT_MANAGER_PLUGINS, useClass: HammerGesturesPlugin, multi: true},
@@ -92,11 +88,11 @@ export function _resolveDefaultAnimationDriver(): AnimationDriver {
{provide: RootRenderer, useExisting: DomRootRenderer},
{provide: SharedStylesHost, useExisting: DomSharedStylesHost},
{provide: AnimationDriver, useFactory: _resolveDefaultAnimationDriver},
- {provide: Meta, useFactory: meta},
DomSharedStylesHost,
Testability,
EventManager,
ELEMENT_PROBE_PROVIDERS,
+ Meta,
Title,
],
exports: [CommonModule, ApplicationModule]
diff --git a/modules/@angular/platform-browser/src/browser/browser_adapter.ts b/modules/@angular/platform-browser/src/browser/browser_adapter.ts
index 4f0bfbbeeb..2beae22f63 100644
--- a/modules/@angular/platform-browser/src/browser/browser_adapter.ts
+++ b/modules/@angular/platform-browser/src/browser/browser_adapter.ts
@@ -107,10 +107,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
get attrToPropMap(): any { return _attrToPropMap; }
- query(selector: string): any { return document.querySelector(selector); }
- querySelector(el: Element, selector: string): HTMLElement {
- return el.querySelector(selector) as HTMLElement;
- }
+ querySelector(el: Element, selector: string): any { return el.querySelector(selector); }
querySelectorAll(el: any, selector: string): any[] { return el.querySelectorAll(selector); }
on(el: Node, evt: any, listener: any) { el.addEventListener(evt, listener, false); }
onAndCancel(el: Node, evt: any, listener: any): Function {
@@ -274,7 +271,6 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
createHtmlDocument(): HTMLDocument {
return document.implementation.createHTMLDocument('fakeTitle');
}
- defaultDoc(): HTMLDocument { return document; }
getBoundingClientRect(el: Element): any {
try {
return el.getBoundingClientRect();
@@ -282,8 +278,8 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
return {top: 0, bottom: 0, left: 0, right: 0, width: 0, height: 0};
}
}
- getTitle(): string { return document.title; }
- setTitle(newTitle: string) { document.title = newTitle || ''; }
+ getTitle(doc: Document): string { return document.title; }
+ setTitle(doc: Document, newTitle: string) { document.title = newTitle || ''; }
elementMatches(n: any, selector: string): boolean {
if (n instanceof HTMLElement) {
return n.matches && n.matches(selector) ||
@@ -330,7 +326,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
return _keyMap[key] || key;
}
- getGlobalEventTarget(target: string): EventTarget {
+ getGlobalEventTarget(doc: Document, target: string): EventTarget {
if (target === 'window') {
return window;
}
@@ -343,7 +339,7 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
}
getHistory(): History { return window.history; }
getLocation(): Location { return window.location; }
- getBaseHref(): string {
+ getBaseHref(doc: Document): string {
const href = getBaseElementHref();
return isBlank(href) ? null : relativePath(href);
}
diff --git a/modules/@angular/platform-browser/src/browser/generic_browser_adapter.ts b/modules/@angular/platform-browser/src/browser/generic_browser_adapter.ts
index 60b4230851..f2b5776e30 100644
--- a/modules/@angular/platform-browser/src/browser/generic_browser_adapter.ts
+++ b/modules/@angular/platform-browser/src/browser/generic_browser_adapter.ts
@@ -23,7 +23,7 @@ export abstract class GenericBrowserDomAdapter extends DomAdapter {
constructor() {
super();
try {
- const element = this.createElement('div', this.defaultDoc());
+ const element = this.createElement('div', document);
if (isPresent(this.getStyle(element, 'animationName'))) {
this._animationPrefix = '';
} else {
@@ -61,7 +61,7 @@ export abstract class GenericBrowserDomAdapter extends DomAdapter {
}
supportsDOMEvents(): boolean { return true; }
supportsNativeShadowDOM(): boolean {
- return typeof(this.defaultDoc().body).createShadowRoot === 'function';
+ return typeof(document.body).createShadowRoot === 'function';
}
getAnimationPrefix(): string { return this._animationPrefix ? this._animationPrefix : ''; }
getTransitionEnd(): string { return this._transitionEnd ? this._transitionEnd : ''; }
diff --git a/modules/@angular/platform-browser/src/browser/location/browser_platform_location.ts b/modules/@angular/platform-browser/src/browser/location/browser_platform_location.ts
index 33c395e722..9cf47389c2 100644
--- a/modules/@angular/platform-browser/src/browser/location/browser_platform_location.ts
+++ b/modules/@angular/platform-browser/src/browser/location/browser_platform_location.ts
@@ -7,9 +7,10 @@
*/
import {LocationChangeListener, PlatformLocation} from '@angular/common';
-import {Injectable} from '@angular/core';
+import {Inject, Injectable} from '@angular/core';
import {getDOM} from '../../dom/dom_adapter';
+import {DOCUMENT} from '../../dom/dom_tokens';
import {supportsState} from './history';
@@ -25,7 +26,7 @@ export class BrowserPlatformLocation extends PlatformLocation {
private _location: Location;
private _history: History;
- constructor() {
+ constructor(@Inject(DOCUMENT) private _doc: any) {
super();
this._init();
}
@@ -39,14 +40,14 @@ export class BrowserPlatformLocation extends PlatformLocation {
get location(): Location { return this._location; }
- getBaseHrefFromDOM(): string { return getDOM().getBaseHref(); }
+ getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc); }
onPopState(fn: LocationChangeListener): void {
- getDOM().getGlobalEventTarget('window').addEventListener('popstate', fn, false);
+ getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('popstate', fn, false);
}
onHashChange(fn: LocationChangeListener): void {
- getDOM().getGlobalEventTarget('window').addEventListener('hashchange', fn, false);
+ getDOM().getGlobalEventTarget(this._doc, 'window').addEventListener('hashchange', fn, false);
}
get pathname(): string { return this._location.pathname; }
diff --git a/modules/@angular/platform-browser/src/browser/meta.ts b/modules/@angular/platform-browser/src/browser/meta.ts
index b9ee0770f2..35717ff9dd 100644
--- a/modules/@angular/platform-browser/src/browser/meta.ts
+++ b/modules/@angular/platform-browser/src/browser/meta.ts
@@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Injectable} from '@angular/core';
-import {DomAdapter} from '../dom/dom_adapter';
+import {Inject, Injectable} from '@angular/core';
+
+import {DomAdapter, getDOM} from '../dom/dom_adapter';
+import {DOCUMENT} from '../dom/dom_tokens';
+
/**
* Represents a meta element.
@@ -33,7 +36,8 @@ export type MetaDefinition = {
*/
@Injectable()
export class Meta {
- constructor(private _dom: DomAdapter) {}
+ private _dom: DomAdapter;
+ constructor(@Inject(DOCUMENT) private _doc: any) { this._dom = getDOM(); }
addTag(tag: MetaDefinition, forceCreation: boolean = false): HTMLMetaElement {
if (!tag) return null;
@@ -52,13 +56,12 @@ export class Meta {
getTag(attrSelector: string): HTMLMetaElement {
if (!attrSelector) return null;
- return this._dom.query(`meta[${attrSelector}]`);
+ return this._dom.querySelector(this._doc, `meta[${attrSelector}]`);
}
getTags(attrSelector: string): HTMLMetaElement[] {
if (!attrSelector) return [];
- const list /*NodeList*/ =
- this._dom.querySelectorAll(this._dom.defaultDoc(), `meta[${attrSelector}]`);
+ const list /*NodeList*/ = this._dom.querySelectorAll(this._doc, `meta[${attrSelector}]`);
return list ? [].slice.call(list) : [];
}
@@ -92,7 +95,7 @@ export class Meta {
}
const element: HTMLMetaElement = this._dom.createElement('meta') as HTMLMetaElement;
this._setMetaElementAttributes(meta, element);
- const head = this._dom.getElementsByTagName(this._dom.defaultDoc(), 'head')[0];
+ const head = this._dom.getElementsByTagName(this._doc, 'head')[0];
this._dom.appendChild(head, element);
return element;
}
diff --git a/modules/@angular/platform-browser/src/browser/title.ts b/modules/@angular/platform-browser/src/browser/title.ts
index 4878f38665..588f323f45 100644
--- a/modules/@angular/platform-browser/src/browser/title.ts
+++ b/modules/@angular/platform-browser/src/browser/title.ts
@@ -6,7 +6,12 @@
* found in the LICENSE file at https://angular.io/license
*/
+import {Inject, Injectable} from '@angular/core';
+
import {getDOM} from '../dom/dom_adapter';
+import {DOCUMENT} from '../dom/dom_tokens';
+
+
/**
* A service that can be used to get and set the title of a current HTML document.
*
@@ -17,16 +22,18 @@ import {getDOM} from '../dom/dom_adapter';
*
* @experimental
*/
+@Injectable()
export class Title {
+ constructor(@Inject(DOCUMENT) private _doc: any) {}
/**
* Get the title of the current HTML document.
* @returns {string}
*/
- getTitle(): string { return getDOM().getTitle(); }
+ getTitle(): string { return getDOM().getTitle(this._doc); }
/**
* Set the title of the current HTML document.
* @param newTitle
*/
- setTitle(newTitle: string) { getDOM().setTitle(newTitle); }
+ setTitle(newTitle: string) { getDOM().setTitle(this._doc, newTitle); }
}
diff --git a/modules/@angular/platform-browser/src/dom/dom_adapter.ts b/modules/@angular/platform-browser/src/dom/dom_adapter.ts
index 9682584b28..84303f8cb1 100644
--- a/modules/@angular/platform-browser/src/dom/dom_adapter.ts
+++ b/modules/@angular/platform-browser/src/dom/dom_adapter.ts
@@ -53,8 +53,7 @@ export abstract class DomAdapter {
_attrToPropMap: {[key: string]: string};
abstract parse(templateHtml: string): any /** TODO #9100 */;
- abstract query(selector: string): any;
- abstract querySelector(el: any /** TODO #9100 */, selector: string): HTMLElement;
+ abstract querySelector(el: any /** TODO #9100 */, selector: string): any;
abstract querySelectorAll(el: any /** TODO #9100 */, selector: string): any[];
abstract on(
el: any /** TODO #9100 */, evt: any /** TODO #9100 */, listener: any /** TODO #9100 */): any
@@ -145,10 +144,9 @@ export abstract class DomAdapter {
/** TODO #9100 */;
abstract templateAwareRoot(el: any /** TODO #9100 */): any /** TODO #9100 */;
abstract createHtmlDocument(): HTMLDocument;
- abstract defaultDoc(): HTMLDocument;
abstract getBoundingClientRect(el: any /** TODO #9100 */): any /** TODO #9100 */;
- abstract getTitle(): string;
- abstract setTitle(newTitle: string): any /** TODO #9100 */;
+ abstract getTitle(doc: Document): string;
+ abstract setTitle(doc: Document, newTitle: string): any /** TODO #9100 */;
abstract elementMatches(n: any /** TODO #9100 */, selector: string): boolean;
abstract isTemplateElement(el: any): boolean;
abstract isTextNode(node: any /** TODO #9100 */): boolean;
@@ -164,10 +162,10 @@ export abstract class DomAdapter {
/** TODO #9100 */;
abstract supportsDOMEvents(): boolean;
abstract supportsNativeShadowDOM(): boolean;
- abstract getGlobalEventTarget(target: string): any;
+ abstract getGlobalEventTarget(doc: Document, target: string): any;
abstract getHistory(): History;
abstract getLocation(): Location;
- abstract getBaseHref(): string;
+ abstract getBaseHref(doc: Document): string;
abstract resetBaseElement(): void;
abstract getUserAgent(): string;
abstract setData(element: any /** TODO #9100 */, name: string, value: string): any
diff --git a/modules/@angular/platform-browser/src/dom/events/dom_events.ts b/modules/@angular/platform-browser/src/dom/events/dom_events.ts
index e1d48049ec..324603e035 100644
--- a/modules/@angular/platform-browser/src/dom/events/dom_events.ts
+++ b/modules/@angular/platform-browser/src/dom/events/dom_events.ts
@@ -6,11 +6,16 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Injectable} from '@angular/core';
+import {Inject, Injectable} from '@angular/core';
+
+import {DOCUMENT} from '../dom_tokens';
+
import {EventManagerPlugin} from './event_manager';
@Injectable()
export class DomEventsPlugin extends EventManagerPlugin {
+ constructor(@Inject(DOCUMENT) doc: any) { super(doc); }
+
// This plugin should come last in the list of plugins, because it accepts all
// events.
supports(eventName: string): boolean { return true; }
diff --git a/modules/@angular/platform-browser/src/dom/events/event_manager.ts b/modules/@angular/platform-browser/src/dom/events/event_manager.ts
index be89369801..6254e1d9d1 100644
--- a/modules/@angular/platform-browser/src/dom/events/event_manager.ts
+++ b/modules/@angular/platform-browser/src/dom/events/event_manager.ts
@@ -10,7 +10,6 @@ import {Inject, Injectable, InjectionToken, NgZone} from '@angular/core';
import {getDOM} from '../dom_adapter';
-
/**
* @stable
*/
@@ -62,6 +61,8 @@ export class EventManager {
}
export abstract class EventManagerPlugin {
+ constructor(private _doc: any) {}
+
manager: EventManager;
abstract supports(eventName: string): boolean;
@@ -69,7 +70,7 @@ export abstract class EventManagerPlugin {
abstract addEventListener(element: HTMLElement, eventName: string, handler: Function): Function;
addGlobalEventListener(element: string, eventName: string, handler: Function): Function {
- const target: HTMLElement = getDOM().getGlobalEventTarget(element);
+ const target: HTMLElement = getDOM().getGlobalEventTarget(this._doc, element);
if (!target) {
throw new Error(`Unsupported event target ${target} for event ${eventName}`);
}
diff --git a/modules/@angular/platform-browser/src/dom/events/hammer_gestures.ts b/modules/@angular/platform-browser/src/dom/events/hammer_gestures.ts
index e3bc720c3d..fc695c3f57 100644
--- a/modules/@angular/platform-browser/src/dom/events/hammer_gestures.ts
+++ b/modules/@angular/platform-browser/src/dom/events/hammer_gestures.ts
@@ -7,6 +7,9 @@
*/
import {Inject, Injectable, InjectionToken} from '@angular/core';
+
+import {DOCUMENT} from '../dom_tokens';
+
import {EventManagerPlugin} from './event_manager';
const EVENT_NAMES = {
@@ -85,7 +88,11 @@ export class HammerGestureConfig {
@Injectable()
export class HammerGesturesPlugin extends EventManagerPlugin {
- constructor(@Inject(HAMMER_GESTURE_CONFIG) private _config: HammerGestureConfig) { super(); }
+ constructor(
+ @Inject(DOCUMENT) doc: any,
+ @Inject(HAMMER_GESTURE_CONFIG) private _config: HammerGestureConfig) {
+ super(doc);
+ }
supports(eventName: string): boolean {
if (!EVENT_NAMES.hasOwnProperty(eventName.toLowerCase()) && !this.isCustomEvent(eventName)) {
diff --git a/modules/@angular/platform-browser/src/dom/events/key_events.ts b/modules/@angular/platform-browser/src/dom/events/key_events.ts
index e085fc96e7..0be06d9f17 100644
--- a/modules/@angular/platform-browser/src/dom/events/key_events.ts
+++ b/modules/@angular/platform-browser/src/dom/events/key_events.ts
@@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Injectable, NgZone} from '@angular/core';
+import {Inject, Injectable, NgZone} from '@angular/core';
+
import {getDOM} from '../dom_adapter';
+import {DOCUMENT} from '../dom_tokens';
+
import {EventManagerPlugin} from './event_manager';
const MODIFIER_KEYS = ['alt', 'control', 'meta', 'shift'];
@@ -23,7 +26,7 @@ const MODIFIER_KEY_GETTERS: {[key: string]: (event: KeyboardEvent) => boolean} =
*/
@Injectable()
export class KeyEventsPlugin extends EventManagerPlugin {
- constructor() { super(); }
+ constructor(@Inject(DOCUMENT) doc: any) { super(doc); }
supports(eventName: string): boolean { return KeyEventsPlugin.parseEventName(eventName) != null; }
diff --git a/modules/@angular/platform-browser/src/security/dom_sanitization_service.ts b/modules/@angular/platform-browser/src/security/dom_sanitization_service.ts
index 8fa0aa6d35..c7c5801b4f 100644
--- a/modules/@angular/platform-browser/src/security/dom_sanitization_service.ts
+++ b/modules/@angular/platform-browser/src/security/dom_sanitization_service.ts
@@ -6,7 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
-import {Injectable, Sanitizer, SecurityContext} from '@angular/core';
+import {Inject, Injectable, Sanitizer, SecurityContext} from '@angular/core';
+
+import {DOCUMENT} from '../dom/dom_tokens';
import {sanitizeHtml} from './html_sanitizer';
import {sanitizeStyle} from './style_sanitizer';
@@ -15,6 +17,7 @@ import {sanitizeUrl} from './url_sanitizer';
export {SecurityContext};
+
/**
* Marker interface for a value that's safe to use in a particular context.
*
@@ -147,6 +150,8 @@ export abstract class DomSanitizer implements Sanitizer {
@Injectable()
export class DomSanitizerImpl extends DomSanitizer {
+ constructor(@Inject(DOCUMENT) private _doc: any) { super(); }
+
sanitize(ctx: SecurityContext, value: any): string {
if (value == null) return null;
switch (ctx) {
@@ -155,7 +160,7 @@ export class DomSanitizerImpl extends DomSanitizer {
case SecurityContext.HTML:
if (value instanceof SafeHtmlImpl) return value.changingThisBreaksApplicationSecurity;
this.checkNotSafeValue(value, 'HTML');
- return sanitizeHtml(String(value));
+ return sanitizeHtml(this._doc, String(value));
case SecurityContext.STYLE:
if (value instanceof SafeStyleImpl) return value.changingThisBreaksApplicationSecurity;
this.checkNotSafeValue(value, 'Style');
diff --git a/modules/@angular/platform-browser/src/security/html_sanitizer.ts b/modules/@angular/platform-browser/src/security/html_sanitizer.ts
index b657f27124..1e4db7db28 100644
--- a/modules/@angular/platform-browser/src/security/html_sanitizer.ts
+++ b/modules/@angular/platform-browser/src/security/html_sanitizer.ts
@@ -9,6 +9,7 @@
import {isDevMode} from '@angular/core';
import {DomAdapter, getDOM} from '../dom/dom_adapter';
+import {DOCUMENT} from '../dom/dom_tokens';
import {sanitizeSrcset, sanitizeUrl} from './url_sanitizer';
@@ -243,7 +244,7 @@ function stripCustomNsAttrs(el: Element) {
* Sanitizes the given unsafe, untrusted HTML fragment, and returns HTML text that is safe to add to
* the DOM in a browser environment.
*/
-export function sanitizeHtml(unsafeHtmlInput: string): string {
+export function sanitizeHtml(defaultDoc: any, unsafeHtmlInput: string): string {
try {
const containerEl = getInertElement();
// Make sure unsafeHtml is actually a string (TypeScript types are not enforced at runtime).
@@ -262,7 +263,7 @@ export function sanitizeHtml(unsafeHtmlInput: string): string {
unsafeHtml = parsedHtml;
DOM.setInnerHTML(containerEl, unsafeHtml);
- if ((DOM.defaultDoc() as any).documentMode) {
+ if (defaultDoc.documentMode) {
// strip custom-namespaced attributes on IE<=11
stripCustomNsAttrs(containerEl);
}
diff --git a/modules/@angular/platform-browser/test/browser/meta_spec.ts b/modules/@angular/platform-browser/test/browser/meta_spec.ts
index 46858ccb01..ce2dcdfba2 100644
--- a/modules/@angular/platform-browser/test/browser/meta_spec.ts
+++ b/modules/@angular/platform-browser/test/browser/meta_spec.ts
@@ -14,9 +14,8 @@ import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('Meta service', () => {
-
- const metaService: Meta = new Meta(getDOM());
- const doc: HTMLDocument = getDOM().defaultDoc();
+ const doc: HTMLDocument = getDOM().createHtmlDocument();
+ const metaService: Meta = new Meta(doc);
let defaultMeta: HTMLMetaElement;
beforeEach(() => {
diff --git a/modules/@angular/platform-browser/test/browser/title_spec.ts b/modules/@angular/platform-browser/test/browser/title_spec.ts
index 5a843eecff..4bf89ff636 100644
--- a/modules/@angular/platform-browser/test/browser/title_spec.ts
+++ b/modules/@angular/platform-browser/test/browser/title_spec.ts
@@ -14,23 +14,24 @@ import {expect} from '@angular/platform-browser/testing/matchers';
export function main() {
describe('title service', () => {
- const initialTitle = getDOM().getTitle();
- const titleService = new Title();
+ const doc = getDOM().createHtmlDocument();
+ const initialTitle = getDOM().getTitle(doc);
+ const titleService = new Title(doc);
- afterEach(() => { getDOM().setTitle(initialTitle); });
+ afterEach(() => { getDOM().setTitle(doc, initialTitle); });
it('should allow reading initial title',
() => { expect(titleService.getTitle()).toEqual(initialTitle); });
it('should set a title on the injected document', () => {
titleService.setTitle('test title');
- expect(getDOM().getTitle()).toEqual('test title');
+ expect(getDOM().getTitle(doc)).toEqual('test title');
expect(titleService.getTitle()).toEqual('test title');
});
it('should reset title to empty string if title not provided', () => {
titleService.setTitle(null);
- expect(getDOM().getTitle()).toEqual('');
+ expect(getDOM().getTitle(doc)).toEqual('');
});
});
diff --git a/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts b/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts
index 4f6bf70649..8266961d6b 100644
--- a/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts
+++ b/modules/@angular/platform-browser/test/dom/events/event_manager_spec.ts
@@ -15,16 +15,20 @@ import {el} from '../../../testing/browser_util';
export function main() {
let domEventPlugin: DomEventsPlugin;
+ let doc: any;
describe('EventManager', () => {
- beforeEach(() => { domEventPlugin = new DomEventsPlugin(); });
+ beforeEach(() => {
+ doc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
+ domEventPlugin = new DomEventsPlugin(doc);
+ });
it('should delegate event bindings to plugins that are passed in from the most generic one to the most specific one',
() => {
const element = el('');
const handler = (e: any /** TODO #9100 */) => e;
- const plugin = new FakeEventManagerPlugin(['click']);
+ const plugin = new FakeEventManagerPlugin(doc, ['click']);
const manager = new EventManager([domEventPlugin, plugin], new FakeNgZone());
manager.addEventListener(element, 'click', handler);
expect(plugin.eventHandler['click']).toBe(handler);
@@ -34,8 +38,8 @@ export function main() {
const element = el('');
const clickHandler = (e: any /** TODO #9100 */) => e;
const dblClickHandler = (e: any /** TODO #9100 */) => e;
- const plugin1 = new FakeEventManagerPlugin(['dblclick']);
- const plugin2 = new FakeEventManagerPlugin(['click', 'dblclick']);
+ const plugin1 = new FakeEventManagerPlugin(doc, ['dblclick']);
+ const plugin2 = new FakeEventManagerPlugin(doc, ['click', 'dblclick']);
const manager = new EventManager([plugin2, plugin1], new FakeNgZone());
manager.addEventListener(element, 'click', clickHandler);
manager.addEventListener(element, 'dblclick', dblClickHandler);
@@ -45,7 +49,7 @@ export function main() {
it('should throw when no plugin can handle the event', () => {
const element = el('');
- const plugin = new FakeEventManagerPlugin(['dblclick']);
+ const plugin = new FakeEventManagerPlugin(doc, ['dblclick']);
const manager = new EventManager([plugin], new FakeNgZone());
expect(() => manager.addEventListener(element, 'click', null))
.toThrowError('No event manager plugin found for event click');
@@ -54,7 +58,7 @@ export function main() {
it('events are caught when fired from a child', () => {
const element = el('');
// Workaround for https://bugs.webkit.org/show_bug.cgi?id=122755
- getDOM().appendChild(getDOM().defaultDoc().body, element);
+ getDOM().appendChild(doc.body, element);
const child = getDOM().firstChild(element);
const dispatchedEvent = getDOM().createMouseEvent('click');
@@ -69,7 +73,7 @@ export function main() {
it('should add and remove global event listeners', () => {
const element = el('');
- getDOM().appendChild(getDOM().defaultDoc().body, element);
+ getDOM().appendChild(doc.body, element);
const dispatchedEvent = getDOM().createMouseEvent('click');
let receivedEvent: any /** TODO #9100 */ = null;
const handler = (e: any /** TODO #9100 */) => { receivedEvent = e; };
@@ -91,7 +95,7 @@ export function main() {
class FakeEventManagerPlugin extends EventManagerPlugin {
eventHandler: {[event: string]: Function} = {};
- constructor(public supportedEvents: string[]) { super(); }
+ constructor(doc: any, public supportedEvents: string[]) { super(doc); }
supports(eventName: string): boolean { return this.supportedEvents.indexOf(eventName) > -1; }
diff --git a/modules/@angular/platform-browser/test/dom/events/hammer_gestures_spec.ts b/modules/@angular/platform-browser/test/dom/events/hammer_gestures_spec.ts
index 84d2a1adfb..f1db6434b0 100644
--- a/modules/@angular/platform-browser/test/dom/events/hammer_gestures_spec.ts
+++ b/modules/@angular/platform-browser/test/dom/events/hammer_gestures_spec.ts
@@ -12,7 +12,7 @@ export function main() {
describe('HammerGesturesPlugin', () => {
it('should implement addGlobalEventListener', () => {
- const plugin = new HammerGesturesPlugin(new HammerGestureConfig());
+ const plugin = new HammerGesturesPlugin(document, new HammerGestureConfig());
spyOn(plugin, 'addEventListener').and.callFake(() => {});
diff --git a/modules/@angular/platform-browser/test/dom/events/key_events_spec.ts b/modules/@angular/platform-browser/test/dom/events/key_events_spec.ts
index d8b4de8888..3cf2b1d24d 100644
--- a/modules/@angular/platform-browser/test/dom/events/key_events_spec.ts
+++ b/modules/@angular/platform-browser/test/dom/events/key_events_spec.ts
@@ -59,7 +59,7 @@ export function main() {
});
it('should implement addGlobalEventListener', () => {
- const plugin = new KeyEventsPlugin();
+ const plugin = new KeyEventsPlugin(document);
spyOn(plugin, 'addEventListener').and.callFake(() => {});
diff --git a/modules/@angular/platform-browser/test/security/dom_sanitization_service_spec.ts b/modules/@angular/platform-browser/test/security/dom_sanitization_service_spec.ts
index c3b83d7ddb..e51d688e4a 100644
--- a/modules/@angular/platform-browser/test/security/dom_sanitization_service_spec.ts
+++ b/modules/@angular/platform-browser/test/security/dom_sanitization_service_spec.ts
@@ -14,7 +14,7 @@ import {DomSanitizerImpl} from '../../src/security/dom_sanitization_service';
export function main() {
t.describe('DOM Sanitization Service', () => {
t.it('accepts resource URL values for resource contexts', () => {
- const svc = new DomSanitizerImpl();
+ const svc = new DomSanitizerImpl(null);
const resourceUrl = svc.bypassSecurityTrustResourceUrl('http://hello/world');
t.expect(svc.sanitize(SecurityContext.URL, resourceUrl)).toBe('http://hello/world');
});
diff --git a/modules/@angular/platform-browser/test/security/html_sanitizer_spec.ts b/modules/@angular/platform-browser/test/security/html_sanitizer_spec.ts
index 309a213b65..04064a1c7b 100644
--- a/modules/@angular/platform-browser/test/security/html_sanitizer_spec.ts
+++ b/modules/@angular/platform-browser/test/security/html_sanitizer_spec.ts
@@ -14,10 +14,12 @@ import {sanitizeHtml} from '../../src/security/html_sanitizer';
export function main() {
t.describe('HTML sanitizer', () => {
+ let defaultDoc: any;
let originalLog: (msg: any) => any = null;
let logMsgs: string[];
t.beforeEach(() => {
+ defaultDoc = getDOM().supportsDOMEvents() ? document : getDOM().createHtmlDocument();
logMsgs = [];
originalLog = getDOM().log; // Monkey patch DOM.log.
getDOM().log = (msg) => logMsgs.push(msg);
@@ -25,52 +27,55 @@ export function main() {
t.afterEach(() => { getDOM().log = originalLog; });
t.it('serializes nested structures', () => {
- t.expect(sanitizeHtml(''))
+ t.expect(sanitizeHtml(defaultDoc, ''))
.toEqual('');
t.expect(logMsgs).toEqual([]);
});
t.it('serializes self closing elements', () => {
- t.expect(sanitizeHtml('Hello
World
')).toEqual('Hello
World
');
+ t.expect(sanitizeHtml(defaultDoc, 'Hello
World
'))
+ .toEqual('Hello
World
');
});
t.it('supports namespaced elements', () => {
- t.expect(sanitizeHtml('abc')).toEqual('abc');
+ t.expect(sanitizeHtml(defaultDoc, 'abc')).toEqual('abc');
});
t.it('supports namespaced attributes', () => {
- t.expect(sanitizeHtml('t'))
+ t.expect(sanitizeHtml(defaultDoc, 't'))
.toEqual('t');
- t.expect(sanitizeHtml('t')).toEqual('t');
- t.expect(sanitizeHtml('t'))
+ t.expect(sanitizeHtml(defaultDoc, 't')).toEqual('t');
+ t.expect(sanitizeHtml(defaultDoc, 't'))
.toEqual('t');
});
t.it('supports HTML5 elements', () => {
- t.expect(sanitizeHtml('Works'))
+ t.expect(sanitizeHtml(defaultDoc, 'Works'))
.toEqual('Works');
});
t.it('sanitizes srcset attributes', () => {
- t.expect(sanitizeHtml('
'))
+ t.expect(sanitizeHtml(defaultDoc, '
'))
.toEqual('
');
});
t.it('supports sanitizing plain text', () => {
- t.expect(sanitizeHtml('Hello, World')).toEqual('Hello, World');
+ t.expect(sanitizeHtml(defaultDoc, 'Hello, World')).toEqual('Hello, World');
});
t.it('ignores non-element, non-attribute nodes', () => {
- t.expect(sanitizeHtml('no.')).toEqual('no.');
- t.expect(sanitizeHtml('no.')).toEqual('no.');
+ t.expect(sanitizeHtml(defaultDoc, 'no.')).toEqual('no.');
+ t.expect(sanitizeHtml(defaultDoc, 'no.')).toEqual('no.');
t.expect(logMsgs.join('\n')).toMatch(/sanitizing HTML stripped some content/);
});
t.it('supports sanitizing escaped entities', () => {
- t.expect(sanitizeHtml('🚀')).toEqual('🚀');
+ t.expect(sanitizeHtml(defaultDoc, '🚀')).toEqual('🚀');
t.expect(logMsgs).toEqual([]);
});
t.it('does not warn when just re-encoding text', () => {
- t.expect(sanitizeHtml('Hellö Wörld
')).toEqual('Hellö Wörld
');
+ t.expect(sanitizeHtml(defaultDoc, 'Hellö Wörld
'))
+ .toEqual('Hellö Wörld
');
t.expect(logMsgs).toEqual([]);
});
t.it('escapes entities', () => {
- t.expect(sanitizeHtml('Hello < World
')).toEqual('Hello < World
');
- t.expect(sanitizeHtml('Hello < World
')).toEqual('Hello < World
');
- t.expect(sanitizeHtml('Hello
'))
+ t.expect(sanitizeHtml(defaultDoc, 'Hello < World
'))
+ .toEqual('Hello < World
');
+ t.expect(sanitizeHtml(defaultDoc, 'Hello < World
')).toEqual('Hello < World
');
+ t.expect(sanitizeHtml(defaultDoc, 'Hello
'))
.toEqual('Hello
'); // NB: quote encoded as ASCII ".
});
t.describe('should strip dangerous elements', () => {
@@ -80,11 +85,12 @@ export function main() {
];
for (const tag of dangerousTags) {
- t.it(
- `${tag}`, () => { t.expect(sanitizeHtml(`<${tag}>evil!${tag}>`)).toEqual('evil!'); });
+ t.it(`${tag}`, () => {
+ t.expect(sanitizeHtml(defaultDoc, `<${tag}>evil!${tag}>`)).toEqual('evil!');
+ });
}
t.it(`swallows frame entirely`, () => {
- t.expect(sanitizeHtml(`evil!`)).not.toContain('');
+ t.expect(sanitizeHtml(defaultDoc, `evil!`)).not.toContain('');
});
});
t.describe('should strip dangerous attributes', () => {
@@ -92,14 +98,14 @@ export function main() {
for (const attr of dangerousAttrs) {
t.it(`${attr}`, () => {
- t.expect(sanitizeHtml(`evil!`)).toEqual('evil!');
+ t.expect(sanitizeHtml(defaultDoc, `evil!`)).toEqual('evil!');
});
}
});
if (browserDetection.isWebkit) {
t.it('should prevent mXSS attacks', function() {
- t.expect(sanitizeHtml('CLICKME'))
+ t.expect(sanitizeHtml(defaultDoc, 'CLICKME'))
.toEqual('CLICKME');
});
}
diff --git a/modules/@angular/platform-server/src/location.ts b/modules/@angular/platform-server/src/location.ts
index 56dafb10a6..b91a0c3f8c 100644
--- a/modules/@angular/platform-server/src/location.ts
+++ b/modules/@angular/platform-server/src/location.ts
@@ -7,7 +7,8 @@
*/
import {LocationChangeEvent, LocationChangeListener, PlatformLocation} from '@angular/common';
-import {Injectable} from '@angular/core';
+import {Inject, Injectable} from '@angular/core';
+import {DOCUMENT} from '@angular/platform-browser';
import {Subject} from 'rxjs/Subject';
import * as url from 'url';
@@ -27,7 +28,9 @@ export class ServerPlatformLocation implements PlatformLocation {
private _hash: string = '';
private _hashUpdate = new Subject();
- getBaseHrefFromDOM(): string { return getDOM().getBaseHref(); }
+ constructor(@Inject(DOCUMENT) private _doc: any) {}
+
+ getBaseHrefFromDOM(): string { return getDOM().getBaseHref(this._doc); }
onPopState(fn: LocationChangeListener): void {
// No-op: a state stack is not implemented, so
diff --git a/modules/@angular/platform-server/src/parse5_adapter.ts b/modules/@angular/platform-server/src/parse5_adapter.ts
index 473a180c49..4542e6e9cc 100644
--- a/modules/@angular/platform-server/src/parse5_adapter.ts
+++ b/modules/@angular/platform-server/src/parse5_adapter.ts
@@ -22,14 +22,20 @@ const _attrToPropMap: {[key: string]: string} = {
'tabindex': 'tabIndex',
};
-let defDoc: any = null;
-
const mapProps = ['attribs', 'x-attribsNamespace', 'x-attribsPrefix'];
function _notImplemented(methodName: string) {
return new Error('This method is not implemented in Parse5DomAdapter: ' + methodName);
}
+/**
+ * Parses a document string to a Document object.
+ */
+export function parseDocument(html: string) {
+ return parse5.parse(html, {treeAdapter: parse5.treeAdapters.htmlparser2});
+}
+
+
/* tslint:disable:requireParameterType */
/**
* A `DomAdapter` powered by the `parse5` NodeJS module.
@@ -72,7 +78,6 @@ export class Parse5DomAdapter extends DomAdapter {
get attrToPropMap() { return _attrToPropMap; }
- query(selector: any) { throw _notImplemented('query'); }
querySelector(el: any, selector: string): any { return this.querySelectorAll(el, selector)[0]; }
querySelectorAll(el: any, selector: string): any[] {
const res: any[] = [];
@@ -468,7 +473,7 @@ export class Parse5DomAdapter extends DomAdapter {
}
createHtmlDocument(): Document {
const newDoc = treeAdapter.createDocument();
- newDoc.title = 'fake title';
+ newDoc.title = 'fakeTitle';
const head = treeAdapter.createElement('head', null, []);
const body = treeAdapter.createElement('body', 'http://www.w3.org/1999/xhtml', []);
this.appendChild(newDoc, head);
@@ -478,10 +483,9 @@ export class Parse5DomAdapter extends DomAdapter {
newDoc['_window'] = {};
return newDoc;
}
- defaultDoc(): Document { return defDoc = defDoc || this.createHtmlDocument(); }
getBoundingClientRect(el: any): any { return {left: 0, top: 0, width: 0, height: 0}; }
- getTitle(): string { return this.defaultDoc().title || ''; }
- setTitle(newTitle: string) { this.defaultDoc().title = newTitle; }
+ getTitle(doc: Document): string { return doc.title || ''; }
+ setTitle(doc: Document, newTitle: string) { doc.title = newTitle; }
isTemplateElement(el: any): boolean {
return this.isElementNode(el) && this.tagName(el) === 'template';
}
@@ -538,17 +542,17 @@ export class Parse5DomAdapter extends DomAdapter {
}
supportsDOMEvents(): boolean { return false; }
supportsNativeShadowDOM(): boolean { return false; }
- getGlobalEventTarget(target: string): any {
+ getGlobalEventTarget(doc: Document, target: string): any {
if (target == 'window') {
- return (this.defaultDoc())._window;
+ return (doc)._window;
} else if (target == 'document') {
- return this.defaultDoc();
+ return doc;
} else if (target == 'body') {
- return this.defaultDoc().body;
+ return doc.body;
}
}
- getBaseHref(): string {
- const base = this.querySelector(this.defaultDoc(), 'base');
+ getBaseHref(doc: Document): string {
+ const base = this.querySelector(doc, 'base');
let href = '';
if (base) {
href = this.getHref(base);
diff --git a/modules/@angular/platform-server/src/platform-server.ts b/modules/@angular/platform-server/src/platform-server.ts
index 31c1d80ec1..a054afa842 100644
--- a/modules/@angular/platform-server/src/platform-server.ts
+++ b/modules/@angular/platform-server/src/platform-server.ts
@@ -6,6 +6,9 @@
* found in the LICENSE file at https://angular.io/license
*/
-export {ServerModule, platformDynamicServer, platformServer} from './server';
+export {PlatformState} from './platform_state';
+export {INITIAL_CONFIG, ServerModule, platformDynamicServer, platformServer} from './server';
+export {renderModule, renderModuleFactory} from './utils';
+
export * from './private_export';
export {VERSION} from './version';
diff --git a/modules/@angular/platform-server/src/platform_state.ts b/modules/@angular/platform-server/src/platform_state.ts
new file mode 100644
index 0000000000..aaaaff5b30
--- /dev/null
+++ b/modules/@angular/platform-server/src/platform_state.ts
@@ -0,0 +1,34 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+const parse5 = require('parse5');
+
+import {Injectable, Inject} from '@angular/core';
+import {DOCUMENT} from '@angular/platform-browser';
+
+import {getDOM} from './private_import_platform-browser';
+
+/**
+ * Representation of the current platform state.
+ *
+ * @experimental
+ */
+@Injectable()
+export class PlatformState {
+ constructor(@Inject(DOCUMENT) private _doc: any) {}
+
+ /**
+ * Renders the current state of the platform to string.
+ */
+ renderToString(): string { return getDOM().getInnerHTML(this._doc); }
+
+ /**
+ * Returns the current DOM state.
+ */
+ getDocument(): any { return this._doc; }
+}
\ No newline at end of file
diff --git a/modules/@angular/platform-server/src/server.ts b/modules/@angular/platform-server/src/server.ts
index db302546b6..da9f68574f 100644
--- a/modules/@angular/platform-server/src/server.ts
+++ b/modules/@angular/platform-server/src/server.ts
@@ -8,13 +8,14 @@
import {PlatformLocation} from '@angular/common';
import {platformCoreDynamic} from '@angular/compiler';
-import {Injectable, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
-import {BrowserModule} from '@angular/platform-browser';
+import {Injectable, InjectionToken, Injector, NgModule, PLATFORM_INITIALIZER, PlatformRef, Provider, RootRenderer, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
+import {BrowserModule, DOCUMENT} from '@angular/platform-browser';
import {ServerPlatformLocation} from './location';
-import {Parse5DomAdapter} from './parse5_adapter';
+import {Parse5DomAdapter, parseDocument} from './parse5_adapter';
+import {PlatformState} from './platform_state';
import {DebugDomRootRenderer} from './private_import_core';
-import {DomAdapter, SharedStylesHost} from './private_import_platform-browser';
+import {SharedStylesHost, getDOM} from './private_import_platform-browser';
import {ServerRootRenderer} from './server_renderer';
@@ -23,15 +24,16 @@ function notSupported(feature: string): Error {
}
export const INTERNAL_SERVER_PLATFORM_PROVIDERS: Array = [
- {provide: PLATFORM_INITIALIZER, useValue: initParse5Adapter, multi: true},
+ {provide: DOCUMENT, useFactory: _document, deps: [Injector]},
+ {provide: PLATFORM_INITIALIZER, useFactory: initParse5Adapter, multi: true, deps: [Injector]},
{provide: PlatformLocation, useClass: ServerPlatformLocation},
+ PlatformState,
];
-function initParse5Adapter() {
- Parse5DomAdapter.makeCurrent();
+function initParse5Adapter(injector: Injector) {
+ return () => { Parse5DomAdapter.makeCurrent(); };
}
-
export function _createConditionalRootRenderer(rootRenderer: any) {
if (isDevMode()) {
return new DebugDomRootRenderer(rootRenderer);
@@ -46,15 +48,46 @@ export const SERVER_RENDER_PROVIDERS: Provider[] = [
SharedStylesHost
];
+/**
+ * Config object passed to initialize the platform.
+ *
+ * @experimental
+ */
+export interface PlatformConfig {
+ document?: string;
+ url?: string;
+}
+
+/**
+ * The DI token for setting the initial config for the platform.
+ *
+ * @experimental
+ */
+export const INITIAL_CONFIG = new InjectionToken('Server.INITIAL_CONFIG');
+
/**
* The ng module for the server.
*
* @experimental
*/
-@NgModule({exports: [BrowserModule], providers: SERVER_RENDER_PROVIDERS})
+@NgModule({
+ exports: [BrowserModule],
+ providers: [
+ SERVER_RENDER_PROVIDERS,
+ ]
+})
export class ServerModule {
}
+function _document(injector: Injector) {
+ let config: PlatformConfig|null = injector.get(INITIAL_CONFIG, null);
+ if (config && config.document) {
+ return parseDocument(config.document);
+ } else {
+ return getDOM().createHtmlDocument();
+ }
+}
+
/**
* @experimental
*/
diff --git a/modules/@angular/platform-server/src/server_renderer.ts b/modules/@angular/platform-server/src/server_renderer.ts
index 5a09ff65c4..eb26230be7 100644
--- a/modules/@angular/platform-server/src/server_renderer.ts
+++ b/modules/@angular/platform-server/src/server_renderer.ts
@@ -136,7 +136,7 @@ export class ServerRenderer implements Renderer {
}
listenGlobal(target: string, name: string, callback: Function): Function {
- const renderElement = getDOM().getGlobalEventTarget(target);
+ const renderElement = getDOM().getGlobalEventTarget(this._rootRenderer.document, target);
return this.listen(renderElement, name, callback);
}
diff --git a/modules/@angular/platform-server/src/utils.ts b/modules/@angular/platform-server/src/utils.ts
new file mode 100644
index 0000000000..c86ad3203b
--- /dev/null
+++ b/modules/@angular/platform-server/src/utils.ts
@@ -0,0 +1,71 @@
+/**
+ * @license
+ * Copyright Google Inc. All Rights Reserved.
+ *
+ * Use of this source code is governed by an MIT-style license that can be
+ * found in the LICENSE file at https://angular.io/license
+ */
+
+import {ApplicationRef, NgModuleFactory, NgModuleRef, PlatformRef, Provider, Type, destroyPlatform} from '@angular/core';
+import {filter} from 'rxjs/operator/filter';
+import {first} from 'rxjs/operator/first';
+import {toPromise} from 'rxjs/operator/toPromise';
+
+import {PlatformState} from './platform_state';
+import {INITIAL_CONFIG, platformDynamicServer, platformServer} from './server';
+
+const parse5 = require('parse5');
+
+export interface PlatformOptions {
+ document?: string;
+ url?: string;
+ extraProviders?: Provider[];
+}
+
+function _getPlatform(
+ platformFactory: (extraProviders: Provider[]) => PlatformRef,
+ options: PlatformOptions): PlatformRef {
+ const extraProviders = options.extraProviders ? options.extraProviders : [];
+ return platformFactory([
+ {provide: INITIAL_CONFIG, useValue: {document: options.document, url: options.url}},
+ extraProviders
+ ]);
+}
+
+function _render(
+ platform: PlatformRef, moduleRefPromise: Promise>): Promise {
+ return moduleRefPromise.then((moduleRef) => {
+ const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
+ return toPromise
+ .call(first.call(filter.call(applicationRef.isStable, (isStable: boolean) => isStable)))
+ .then(() => {
+ const output = platform.injector.get(PlatformState).renderToString();
+ destroyPlatform();
+ return output;
+ });
+ });
+}
+
+/**
+ * Renders a Module to string.
+ *
+ * Do not use this in a production server environment. Use pre-compiled {@link NgModuleFactory} with
+ * {link renderModuleFactory} instead.
+ *
+ * @experimental
+ */
+export function renderModule(module: Type, options: PlatformOptions): Promise {
+ const platform = _getPlatform(platformDynamicServer, options);
+ return _render(platform, platform.bootstrapModule(module));
+}
+
+/**
+ * Renders a {@link NgModuleFactory} to string.
+ *
+ * @experimental
+ */
+export function renderModuleFactory(
+ moduleFactory: NgModuleFactory, options: PlatformOptions): Promise {
+ const platform = _getPlatform(platformServer, options);
+ return _render(platform, platform.bootstrapModuleFactory(moduleFactory));
+}
diff --git a/modules/@angular/platform-server/test/integration_spec.ts b/modules/@angular/platform-server/test/integration_spec.ts
index e7938515b7..bc4b80427b 100644
--- a/modules/@angular/platform-server/test/integration_spec.ts
+++ b/modules/@angular/platform-server/test/integration_spec.ts
@@ -7,19 +7,15 @@
*/
import {PlatformLocation} from '@angular/common';
-import {Component, NgModule, destroyPlatform} from '@angular/core';
-import {async} from '@angular/core/testing';
+import {ApplicationRef, CompilerFactory, Component, NgModule, NgModuleRef, PlatformRef, destroyPlatform, getPlatform} from '@angular/core';
+import {async, inject} from '@angular/core/testing';
+import {DOCUMENT} from '@angular/platform-browser';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
-import {ServerModule, platformDynamicServer} from '@angular/platform-server';
-
-function writeBody(html: string): any {
- const dom = getDOM();
- const doc = dom.defaultDoc();
- const body = dom.querySelector(doc, 'body');
- dom.setInnerHTML(body, html);
- return body;
-}
-
+import {INITIAL_CONFIG, PlatformState, ServerModule, platformDynamicServer, renderModule, renderModuleFactory} from '@angular/platform-server';
+import {Subscription} from 'rxjs/Subscription';
+import {filter} from 'rxjs/operator/filter';
+import {first} from 'rxjs/operator/first';
+import {toPromise} from 'rxjs/operator/toPromise';
@Component({selector: 'app', template: `Works!`})
class MyServerApp {
@@ -38,43 +34,117 @@ export function main() {
afterEach(() => destroyPlatform());
it('should bootstrap', async(() => {
- const body = writeBody('');
- platformDynamicServer().bootstrapModule(ExampleModule).then(() => {
- expect(getDOM().getText(body)).toEqual('Works!');
- });
+ platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}])
+ .bootstrapModule(ExampleModule)
+ .then((moduleRef) => {
+ const doc = moduleRef.injector.get(DOCUMENT);
+ expect(getDOM().getText(doc)).toEqual('Works!');
+ });
}));
describe('PlatformLocation', () => {
it('is injectable', () => {
- const body = writeBody('');
- platformDynamicServer().bootstrapModule(ExampleModule).then(appRef => {
- const location: PlatformLocation = appRef.injector.get(PlatformLocation);
- expect(location.pathname).toBe('/');
- });
+ platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}])
+ .bootstrapModule(ExampleModule)
+ .then(appRef => {
+ const location: PlatformLocation = appRef.injector.get(PlatformLocation);
+ expect(location.pathname).toBe('/');
+ });
});
it('pushState causes the URL to update', () => {
- const body = writeBody('');
- platformDynamicServer().bootstrapModule(ExampleModule).then(appRef => {
- const location: PlatformLocation = appRef.injector.get(PlatformLocation);
- location.pushState(null, 'Test', '/foo#bar');
- expect(location.pathname).toBe('/foo');
- expect(location.hash).toBe('#bar');
- });
+ platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}])
+ .bootstrapModule(ExampleModule)
+ .then(appRef => {
+ const location: PlatformLocation = appRef.injector.get(PlatformLocation);
+ location.pushState(null, 'Test', '/foo#bar');
+ expect(location.pathname).toBe('/foo');
+ expect(location.hash).toBe('#bar');
+ });
});
it('allows subscription to the hash state', done => {
- const body = writeBody('');
- platformDynamicServer().bootstrapModule(ExampleModule).then(appRef => {
- const location: PlatformLocation = appRef.injector.get(PlatformLocation);
- expect(location.pathname).toBe('/');
- location.onHashChange((e: any) => {
- expect(e.type).toBe('hashchange');
- expect(e.oldUrl).toBe('/');
- expect(e.newUrl).toBe('/foo#bar');
- done();
- });
- location.pushState(null, 'Test', '/foo#bar');
- });
+ platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: ''}}])
+ .bootstrapModule(ExampleModule)
+ .then(appRef => {
+ const location: PlatformLocation = appRef.injector.get(PlatformLocation);
+ expect(location.pathname).toBe('/');
+ location.onHashChange((e: any) => {
+ expect(e.type).toBe('hashchange');
+ expect(e.oldUrl).toBe('/');
+ expect(e.newUrl).toBe('/foo#bar');
+ done();
+ });
+ location.pushState(null, 'Test', '/foo#bar');
+ });
});
});
});
+
+ describe('Platform Server', () => {
+ @Component({selector: 'app', template: '{{text}}'})
+ class MyAsyncServerApp {
+ text = '';
+
+ ngOnInit() {
+ Promise.resolve(null).then(() => setTimeout(() => { this.text = 'Works!'; }, 10));
+ }
+ }
+
+ @NgModule(
+ {declarations: [MyAsyncServerApp], imports: [ServerModule], bootstrap: [MyAsyncServerApp]})
+ class AsyncServerModule {
+ }
+
+ let doc: string;
+ let called: boolean;
+ let expectedOutput =
+ 'Works!';
+
+ beforeEach(() => {
+ destroyPlatform();
+ // PlatformConfig takes in a parsed document so that it can be cached across requests.
+ doc = '';
+ called = false;
+ });
+ afterEach(() => {
+ expect(called).toBe(true);
+ // Platform should have been destroyed at the end of rendering.
+ expect(getPlatform()).toBeNull();
+ });
+
+ it('PlatformState should render to string (Long form rendering)', async(() => {
+ const platform =
+ platformDynamicServer([{provide: INITIAL_CONFIG, useValue: {document: doc}}]);
+
+ platform.bootstrapModule(AsyncServerModule)
+ .then((moduleRef) => {
+ const applicationRef: ApplicationRef = moduleRef.injector.get(ApplicationRef);
+ return toPromise.call(first.call(
+ filter.call(applicationRef.isStable, (isStable: boolean) => isStable)));
+ })
+ .then((b) => {
+ expect(platform.injector.get(PlatformState).renderToString()).toBe(expectedOutput);
+ destroyPlatform();
+ called = true;
+ });
+ }));
+
+ it('renderModule should render to string (short form rendering)', async(() => {
+ renderModule(AsyncServerModule, {document: doc}).then(output => {
+ expect(output).toBe(expectedOutput);
+ called = true;
+ });
+ }));
+
+ it('renderModuleFactory should render to string (short form rendering)',
+ async(inject([PlatformRef], (defaultPlatform: PlatformRef) => {
+ const compilerFactory: CompilerFactory =
+ defaultPlatform.injector.get(CompilerFactory, null);
+ const moduleFactory =
+ compilerFactory.createCompiler().compileModuleSync(AsyncServerModule);
+ renderModuleFactory(moduleFactory, {document: doc}).then(output => {
+ expect(output).toBe(expectedOutput);
+ called = true;
+ });
+ })));
+ });
}
diff --git a/modules/@angular/platform-webworker/src/web_workers/worker/worker_adapter.ts b/modules/@angular/platform-webworker/src/web_workers/worker/worker_adapter.ts
index 3278c195e6..3834af9296 100644
--- a/modules/@angular/platform-webworker/src/web_workers/worker/worker_adapter.ts
+++ b/modules/@angular/platform-webworker/src/web_workers/worker/worker_adapter.ts
@@ -55,7 +55,6 @@ export class WorkerDomAdapter extends DomAdapter {
set attrToPropMap(value: {[key: string]: string}) { throw 'not implemented'; }
parse(templateHtml: string) { throw 'not implemented'; }
- query(selector: string): any { throw 'not implemented'; }
querySelector(el: any /** TODO #9100 */, selector: string): HTMLElement {
throw 'not implemented';
}
@@ -169,10 +168,9 @@ export class WorkerDomAdapter extends DomAdapter {
}
templateAwareRoot(el: any /** TODO #9100 */) { throw 'not implemented'; }
createHtmlDocument(): HTMLDocument { throw 'not implemented'; }
- defaultDoc(): HTMLDocument { throw 'not implemented'; }
getBoundingClientRect(el: any /** TODO #9100 */) { throw 'not implemented'; }
- getTitle(): string { throw 'not implemented'; }
- setTitle(newTitle: string) { throw 'not implemented'; }
+ getTitle(doc: Document): string { throw 'not implemented'; }
+ setTitle(doc: Document, newTitle: string) { throw 'not implemented'; }
elementMatches(n: any /** TODO #9100 */, selector: string): boolean { throw 'not implemented'; }
isTemplateElement(el: any): boolean { throw 'not implemented'; }
isTextNode(node: any /** TODO #9100 */): boolean { throw 'not implemented'; }
@@ -189,10 +187,10 @@ export class WorkerDomAdapter extends DomAdapter {
}
supportsDOMEvents(): boolean { throw 'not implemented'; }
supportsNativeShadowDOM(): boolean { throw 'not implemented'; }
- getGlobalEventTarget(target: string): any { throw 'not implemented'; }
+ getGlobalEventTarget(doc: Document, target: string): any { throw 'not implemented'; }
getHistory(): History { throw 'not implemented'; }
getLocation(): Location { throw 'not implemented'; }
- getBaseHref(): string { throw 'not implemented'; }
+ getBaseHref(doc: Document): string { throw 'not implemented'; }
resetBaseElement(): void { throw 'not implemented'; }
getUserAgent(): string { throw 'not implemented'; }
setData(element: any /** TODO #9100 */, name: string, value: string) { throw 'not implemented'; }
diff --git a/modules/@angular/platform-webworker/src/worker_app.ts b/modules/@angular/platform-webworker/src/worker_app.ts
index 72ad03736f..27e3e7405e 100644
--- a/modules/@angular/platform-webworker/src/worker_app.ts
+++ b/modules/@angular/platform-webworker/src/worker_app.ts
@@ -8,6 +8,7 @@
import {CommonModule} from '@angular/common';
import {APP_INITIALIZER, ApplicationModule, ErrorHandler, NgModule, NgZone, PlatformRef, Provider, RootRenderer, createPlatformFactory, platformCore} from '@angular/core';
+import {DOCUMENT} from '@angular/platform-browser';
import {BROWSER_SANITIZATION_PROVIDERS} from './private_import_platform-browser';
import {ON_WEB_WORKER} from './web_workers/shared/api';
@@ -60,7 +61,7 @@ export function setupWebWorker(): void {
*/
@NgModule({
providers: [
- BROWSER_SANITIZATION_PROVIDERS, Serializer,
+ BROWSER_SANITIZATION_PROVIDERS, Serializer, {provide: DOCUMENT, useValue: null},
{provide: ClientMessageBrokerFactory, useClass: ClientMessageBrokerFactory_},
{provide: ServiceMessageBrokerFactory, useClass: ServiceMessageBrokerFactory_},
WebWorkerRootRenderer, {provide: RootRenderer, useExisting: WebWorkerRootRenderer},
diff --git a/modules/@angular/platform-webworker/src/worker_render.ts b/modules/@angular/platform-webworker/src/worker_render.ts
index 3c8d103d70..c210a606df 100644
--- a/modules/@angular/platform-webworker/src/worker_render.ts
+++ b/modules/@angular/platform-webworker/src/worker_render.ts
@@ -135,7 +135,7 @@ function _exceptionHandler(): ErrorHandler {
}
function _document(): any {
- return getDOM().defaultDoc();
+ return document;
}
function createNgZone(): NgZone {
diff --git a/modules/benchmarks/src/tree/ng2_ftl/app.ngfactory.ts b/modules/benchmarks/src/tree/ng2_ftl/app.ngfactory.ts
index 4c36699c61..b079f02fa4 100644
--- a/modules/benchmarks/src/tree/ng2_ftl/app.ngfactory.ts
+++ b/modules/benchmarks/src/tree/ng2_ftl/app.ngfactory.ts
@@ -120,8 +120,8 @@ class AppModuleInjector extends import0.NgModuleInjector {
get _EVENT_MANAGER_PLUGINS_15(): any[] {
if ((this.__EVENT_MANAGER_PLUGINS_15 == (null as any))) {
(this.__EVENT_MANAGER_PLUGINS_15 = [
- new import20.DomEventsPlugin(), new import21.KeyEventsPlugin(),
- new import10.HammerGesturesPlugin(this._HAMMER_GESTURE_CONFIG_14)
+ new import20.DomEventsPlugin(document), new import21.KeyEventsPlugin(document),
+ new import10.HammerGesturesPlugin(document, this._HAMMER_GESTURE_CONFIG_14)
]);
}
return this.__EVENT_MANAGER_PLUGINS_15;
@@ -163,7 +163,7 @@ class AppModuleInjector extends import0.NgModuleInjector {
}
get _DomSanitizer_21(): import14.DomSanitizerImpl {
if ((this.__DomSanitizer_21 == (null as any))) {
- (this.__DomSanitizer_21 = new import14.DomSanitizerImpl());
+ (this.__DomSanitizer_21 = new import14.DomSanitizerImpl(document));
}
return this.__DomSanitizer_21;
}
@@ -206,7 +206,7 @@ class AppModuleInjector extends import0.NgModuleInjector {
}
get _Title_27(): import16.Title {
if ((this.__Title_27 == (null as any))) {
- (this.__Title_27 = new import16.Title());
+ (this.__Title_27 = new import16.Title(document));
}
return this.__Title_27;
}
diff --git a/modules/benchmarks/src/tree/ng2_next/tree.ts b/modules/benchmarks/src/tree/ng2_next/tree.ts
index cde503adb3..a68acea014 100644
--- a/modules/benchmarks/src/tree/ng2_next/tree.ts
+++ b/modules/benchmarks/src/tree/ng2_next/tree.ts
@@ -92,7 +92,7 @@ export class AppModule implements Injector {
constructor() {
initServicesIfNeeded();
- this.sanitizer = new DomSanitizerImpl();
+ this.sanitizer = new DomSanitizerImpl(document);
trustedEmptyColor = this.sanitizer.bypassSecurityTrustStyle('');
trustedGreyColor = this.sanitizer.bypassSecurityTrustStyle('grey');
this.componentFactory = createComponentFactory('#root', TreeComponent, TreeComponent_Host);
diff --git a/modules/benchmarks/src/tree/ng2_static_ftl/app.ngfactory.ts b/modules/benchmarks/src/tree/ng2_static_ftl/app.ngfactory.ts
index 9107df9387..356a8cd1f2 100644
--- a/modules/benchmarks/src/tree/ng2_static_ftl/app.ngfactory.ts
+++ b/modules/benchmarks/src/tree/ng2_static_ftl/app.ngfactory.ts
@@ -120,8 +120,8 @@ class AppModuleInjector extends import0.NgModuleInjector {
get _EVENT_MANAGER_PLUGINS_15(): any[] {
if ((this.__EVENT_MANAGER_PLUGINS_15 == (null as any))) {
(this.__EVENT_MANAGER_PLUGINS_15 = [
- new import20.DomEventsPlugin(), new import21.KeyEventsPlugin(),
- new import10.HammerGesturesPlugin(this._HAMMER_GESTURE_CONFIG_14)
+ new import20.DomEventsPlugin(document), new import21.KeyEventsPlugin(document),
+ new import10.HammerGesturesPlugin(document, this._HAMMER_GESTURE_CONFIG_14)
]);
}
return this.__EVENT_MANAGER_PLUGINS_15;
@@ -163,7 +163,7 @@ class AppModuleInjector extends import0.NgModuleInjector {
}
get _DomSanitizer_21(): import14.DomSanitizerImpl {
if ((this.__DomSanitizer_21 == (null as any))) {
- (this.__DomSanitizer_21 = new import14.DomSanitizerImpl());
+ (this.__DomSanitizer_21 = new import14.DomSanitizerImpl(document));
}
return this.__DomSanitizer_21;
}
@@ -206,7 +206,7 @@ class AppModuleInjector extends import0.NgModuleInjector {
}
get _Title_27(): import16.Title {
if ((this.__Title_27 == (null as any))) {
- (this.__Title_27 = new import16.Title());
+ (this.__Title_27 = new import16.Title(document));
}
return this.__Title_27;
}
diff --git a/tools/public_api_guard/platform-browser/index.d.ts b/tools/public_api_guard/platform-browser/index.d.ts
index 4fb87b358f..680cdcbd6b 100644
--- a/tools/public_api_guard/platform-browser/index.d.ts
+++ b/tools/public_api_guard/platform-browser/index.d.ts
@@ -60,7 +60,7 @@ export declare class HammerGestureConfig {
/** @experimental */
export declare class Meta {
- constructor(_dom: DomAdapter);
+ constructor(_doc: any);
addTag(tag: MetaDefinition, forceCreation?: boolean): HTMLMetaElement;
addTags(tags: MetaDefinition[], forceCreation?: boolean): HTMLMetaElement[];
getTag(attrSelector: string): HTMLMetaElement;
@@ -117,6 +117,7 @@ export interface SafeUrl extends SafeValue {
/** @experimental */
export declare class Title {
+ constructor(_doc: any);
getTitle(): string;
setTitle(newTitle: string): void;
}
diff --git a/tools/public_api_guard/platform-server/index.d.ts b/tools/public_api_guard/platform-server/index.d.ts
index 525f763bef..de8e4ac8a8 100644
--- a/tools/public_api_guard/platform-server/index.d.ts
+++ b/tools/public_api_guard/platform-server/index.d.ts
@@ -1,9 +1,25 @@
+/** @experimental */
+export declare const INITIAL_CONFIG: InjectionToken;
+
/** @experimental */
export declare const platformDynamicServer: (extraProviders?: Provider[]) => PlatformRef;
/** @experimental */
export declare const platformServer: (extraProviders?: Provider[]) => PlatformRef;
+/** @experimental */
+export declare class PlatformState {
+ constructor(_doc: any);
+ getDocument(): any;
+ renderToString(): string;
+}
+
+/** @experimental */
+export declare function renderModule(module: Type, options: PlatformOptions): Promise;
+
+/** @experimental */
+export declare function renderModuleFactory(moduleFactory: NgModuleFactory, options: PlatformOptions): Promise;
+
/** @experimental */
export declare class ServerModule {
}