@angular/platform-server
Version:
Angular - library for using Angular in Node.js
1,030 lines (1,010 loc) • 40.5 kB
JavaScript
/**
* @license Angular v13.3.8
* (c) 2010-2022 Google LLC. https://angular.io/
* License: MIT
*/
import { ɵsetRootDomAdapter, DOCUMENT, XhrFactory, PlatformLocation, ɵgetDOM, ɵPLATFORM_SERVER_ID, ViewportScroller, ɵNullViewportScroller } from '@angular/common';
import * as i0 from '@angular/core';
import { Injectable, Inject, InjectionToken, Injector, Optional, ViewEncapsulation, RendererStyleFlags2, PLATFORM_ID, PLATFORM_INITIALIZER, ɵALLOW_MULTIPLE_PLATFORMS, RendererFactory2, NgZone, Testability, NgModule, ɵsetDocument, createPlatformFactory, platformCore, APP_ID, ApplicationRef, ɵisPromise, Version } from '@angular/core';
import * as i1 from '@angular/platform-browser';
import { ɵBrowserDomAdapter, ɵflattenStyles, ɵNAMESPACE_URIS, ɵshimContentAttribute, ɵshimHostAttribute, ɵSharedStylesHost, ɵTRANSITION_ID, EVENT_MANAGER_PLUGINS, BrowserModule, ɵescapeHtml, TransferState } from '@angular/platform-browser';
import * as domino from 'domino';
import { ɵAnimationEngine } from '@angular/animations/browser';
import { ɵHttpInterceptingHandler, HttpHandler, HttpBackend, HttpClientModule } from '@angular/common/http';
import { ɵplatformCoreDynamic } from '@angular/platform-browser-dynamic';
import { ɵAnimationRendererFactory, NoopAnimationsModule } from '@angular/platform-browser/animations';
import { Observable, Subject } from 'rxjs';
import * as xhr2 from 'xhr2';
import * as url from 'url';
import { DomElementSchemaRegistry } from '@angular/compiler';
import { first } from 'rxjs/operators';
/**
* @license
* Copyright Google LLC 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
*/
function setDomTypes() {
// Make all Domino types available in the global env.
Object.assign(global, domino.impl);
global['KeyboardEvent'] = domino.impl.Event;
}
/**
* Parses a document string to a Document object.
*/
function parseDocument(html, url = '/') {
let window = domino.createWindow(html, url);
let doc = window.document;
return doc;
}
/**
* Serializes a document to string.
*/
function serializeDocument(doc) {
return doc.serialize();
}
/**
* DOM Adapter for the server platform based on https://github.com/fgnass/domino.
*/
class DominoAdapter extends ɵBrowserDomAdapter {
constructor() {
super(...arguments);
this.supportsDOMEvents = false;
}
static makeCurrent() {
setDomTypes();
ɵsetRootDomAdapter(new DominoAdapter());
}
createHtmlDocument() {
return parseDocument('<html><head><title>fakeTitle</title></head><body></body></html>');
}
getDefaultDocument() {
if (!DominoAdapter.defaultDoc) {
DominoAdapter.defaultDoc = domino.createDocument();
}
return DominoAdapter.defaultDoc;
}
isElementNode(node) {
return node ? node.nodeType === DominoAdapter.defaultDoc.ELEMENT_NODE : false;
}
isShadowRoot(node) {
return node.shadowRoot == node;
}
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
getGlobalEventTarget(doc, target) {
if (target === 'window') {
return doc.defaultView;
}
if (target === 'document') {
return doc;
}
if (target === 'body') {
return doc.body;
}
return null;
}
getBaseHref(doc) {
// TODO(alxhub): Need relative path logic from BrowserDomAdapter here?
return doc.documentElement.querySelector('base')?.getAttribute('href') || '';
}
dispatchEvent(el, evt) {
el.dispatchEvent(evt);
// Dispatch the event to the window also.
const doc = el.ownerDocument || el;
const win = doc.defaultView;
if (win) {
win.dispatchEvent(evt);
}
}
getUserAgent() {
return 'Fake user agent';
}
getCookie(name) {
throw new Error('getCookie has not been implemented');
}
}
/**
* @license
* Copyright Google LLC 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
*/
/**
* Representation of the current platform state.
*
* @publicApi
*/
class PlatformState {
constructor(_doc) {
this._doc = _doc;
}
/**
* Renders the current state of the platform to string.
*/
renderToString() {
return serializeDocument(this._doc);
}
/**
* Returns the current DOM state.
*/
getDocument() {
return this._doc;
}
}
PlatformState.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: PlatformState, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
PlatformState.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: PlatformState });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: PlatformState, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }]; } });
/**
* @license
* Copyright Google LLC 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
*/
/**
* The DI token for setting the initial config for the platform.
*
* @publicApi
*/
const INITIAL_CONFIG = new InjectionToken('Server.INITIAL_CONFIG');
/**
* A function that will be executed when calling `renderModuleFactory` or `renderModule` just
* before current platform state is rendered to string.
*
* @publicApi
*/
const BEFORE_APP_SERIALIZED = new InjectionToken('Server.RENDER_MODULE_HOOK');
/**
* @license
* Copyright Google LLC 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
*/
// @see https://www.w3.org/Protocols/HTTP/1.1/draft-ietf-http-v11-spec-01#URI-syntax
const isAbsoluteUrl = /^[a-zA-Z\-\+.]+:\/\//;
class ServerXhr {
build() {
return new xhr2.XMLHttpRequest();
}
}
ServerXhr.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerXhr, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
ServerXhr.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerXhr });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerXhr, decorators: [{
type: Injectable
}] });
class ZoneMacroTaskWrapper {
wrap(request) {
return new Observable((observer) => {
let task = null;
let scheduled = false;
let sub = null;
let savedResult = null;
let savedError = null;
const scheduleTask = (_task) => {
task = _task;
scheduled = true;
const delegate = this.delegate(request);
sub = delegate.subscribe(res => savedResult = res, err => {
if (!scheduled) {
throw new Error('An http observable was completed twice. This shouldn\'t happen, please file a bug.');
}
savedError = err;
scheduled = false;
task.invoke();
}, () => {
if (!scheduled) {
throw new Error('An http observable was completed twice. This shouldn\'t happen, please file a bug.');
}
scheduled = false;
task.invoke();
});
};
const cancelTask = (_task) => {
if (!scheduled) {
return;
}
scheduled = false;
if (sub) {
sub.unsubscribe();
sub = null;
}
};
const onComplete = () => {
if (savedError !== null) {
observer.error(savedError);
}
else {
observer.next(savedResult);
observer.complete();
}
};
// MockBackend for Http is synchronous, which means that if scheduleTask is by
// scheduleMacroTask, the request will hit MockBackend and the response will be
// sent, causing task.invoke() to be called.
const _task = Zone.current.scheduleMacroTask('ZoneMacroTaskWrapper.subscribe', onComplete, {}, () => null, cancelTask);
scheduleTask(_task);
return () => {
if (scheduled && task) {
task.zone.cancelTask(task);
scheduled = false;
}
if (sub) {
sub.unsubscribe();
sub = null;
}
};
});
}
}
class ZoneClientBackend extends ZoneMacroTaskWrapper {
constructor(backend, platformLocation, config) {
super();
this.backend = backend;
this.platformLocation = platformLocation;
this.config = config;
}
handle(request) {
const { href, protocol, hostname, port } = this.platformLocation;
if (this.config.useAbsoluteUrl && !isAbsoluteUrl.test(request.url) &&
isAbsoluteUrl.test(href)) {
const baseHref = this.platformLocation.getBaseHrefFromDOM() || href;
const urlPrefix = `${protocol}//${hostname}` + (port ? `:${port}` : '');
const baseUrl = new URL(baseHref, urlPrefix);
const url = new URL(request.url, baseUrl);
return this.wrap(request.clone({ url: url.toString() }));
}
return this.wrap(request);
}
delegate(request) {
return this.backend.handle(request);
}
}
function zoneWrappedInterceptingHandler(backend, injector, platformLocation, config) {
const realBackend = new ɵHttpInterceptingHandler(backend, injector);
return new ZoneClientBackend(realBackend, platformLocation, config);
}
const SERVER_HTTP_PROVIDERS = [
{ provide: XhrFactory, useClass: ServerXhr }, {
provide: HttpHandler,
useFactory: zoneWrappedInterceptingHandler,
deps: [HttpBackend, Injector, PlatformLocation, INITIAL_CONFIG]
}
];
/**
* @license
* Copyright Google LLC 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
*/
function parseUrl(urlStr) {
const parsedUrl = url.parse(urlStr);
return {
hostname: parsedUrl.hostname || '',
protocol: parsedUrl.protocol || '',
port: parsedUrl.port || '',
pathname: parsedUrl.pathname || '',
search: parsedUrl.search || '',
hash: parsedUrl.hash || '',
};
}
/**
* Server-side implementation of URL state. Implements `pathname`, `search`, and `hash`
* but not the state stack.
*/
class ServerPlatformLocation {
constructor(_doc, _config) {
this._doc = _doc;
this.href = '/';
this.hostname = '/';
this.protocol = '/';
this.port = '/';
this.pathname = '/';
this.search = '';
this.hash = '';
this._hashUpdate = new Subject();
const config = _config;
if (!config) {
return;
}
if (config.url) {
const url = parseUrl(config.url);
this.protocol = url.protocol;
this.hostname = url.hostname;
this.port = url.port;
this.pathname = url.pathname;
this.search = url.search;
this.hash = url.hash;
this.href = _doc.location.href;
}
if (config.useAbsoluteUrl) {
if (!config.baseUrl) {
throw new Error(`"PlatformConfig.baseUrl" must be set if "useAbsoluteUrl" is true`);
}
const url = parseUrl(config.baseUrl);
this.protocol = url.protocol;
this.hostname = url.hostname;
this.port = url.port;
}
}
getBaseHrefFromDOM() {
return ɵgetDOM().getBaseHref(this._doc);
}
onPopState(fn) {
// No-op: a state stack is not implemented, so
// no events will ever come.
return () => { };
}
onHashChange(fn) {
const subscription = this._hashUpdate.subscribe(fn);
return () => subscription.unsubscribe();
}
get url() {
return `${this.pathname}${this.search}${this.hash}`;
}
setHash(value, oldUrl) {
if (this.hash === value) {
// Don't fire events if the hash has not changed.
return;
}
this.hash = value;
const newUrl = this.url;
scheduleMicroTask(() => this._hashUpdate.next({ type: 'hashchange', state: null, oldUrl, newUrl }));
}
replaceState(state, title, newUrl) {
const oldUrl = this.url;
const parsedUrl = parseUrl(newUrl);
this.pathname = parsedUrl.pathname;
this.search = parsedUrl.search;
this.setHash(parsedUrl.hash, oldUrl);
}
pushState(state, title, newUrl) {
this.replaceState(state, title, newUrl);
}
forward() {
throw new Error('Not implemented');
}
back() {
throw new Error('Not implemented');
}
// History API isn't available on server, therefore return undefined
getState() {
return undefined;
}
}
ServerPlatformLocation.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerPlatformLocation, deps: [{ token: DOCUMENT }, { token: INITIAL_CONFIG, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
ServerPlatformLocation.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerPlatformLocation });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerPlatformLocation, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [INITIAL_CONFIG]
}] }]; } });
function scheduleMicroTask(fn) {
Zone.current.scheduleMicroTask('scheduleMicrotask', fn);
}
/**
* @license
* Copyright Google LLC 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
*/
class ServerEventManagerPlugin /* extends EventManagerPlugin which is private */ {
constructor(doc) {
this.doc = doc;
}
// Handle all events on the server.
supports(eventName) {
return true;
}
addEventListener(element, eventName, handler) {
return ɵgetDOM().onAndCancel(element, eventName, handler);
}
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
addGlobalEventListener(element, eventName, handler) {
const target = ɵgetDOM().getGlobalEventTarget(this.doc, element);
if (!target) {
throw new Error(`Unsupported event target ${target} for event ${eventName}`);
}
return this.addEventListener(target, eventName, handler);
}
}
ServerEventManagerPlugin.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerEventManagerPlugin /* extends EventManagerPlugin which is private */, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable });
ServerEventManagerPlugin.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerEventManagerPlugin /* extends EventManagerPlugin which is private */ });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerEventManagerPlugin /* extends EventManagerPlugin which is private */, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }]; } });
/**
* @license
* Copyright Google LLC 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 EMPTY_ARRAY = [];
const DEFAULT_SCHEMA = new DomElementSchemaRegistry();
class ServerRendererFactory2 {
constructor(eventManager, ngZone, document, sharedStylesHost) {
this.eventManager = eventManager;
this.ngZone = ngZone;
this.document = document;
this.sharedStylesHost = sharedStylesHost;
this.rendererByCompId = new Map();
this.schema = DEFAULT_SCHEMA;
this.defaultRenderer = new DefaultServerRenderer2(eventManager, document, ngZone, this.schema);
}
createRenderer(element, type) {
if (!element || !type) {
return this.defaultRenderer;
}
switch (type.encapsulation) {
case ViewEncapsulation.Emulated: {
let renderer = this.rendererByCompId.get(type.id);
if (!renderer) {
renderer = new EmulatedEncapsulationServerRenderer2(this.eventManager, this.document, this.ngZone, this.sharedStylesHost, this.schema, type);
this.rendererByCompId.set(type.id, renderer);
}
renderer.applyToHost(element);
return renderer;
}
default: {
if (!this.rendererByCompId.has(type.id)) {
const styles = ɵflattenStyles(type.id, type.styles, []);
this.sharedStylesHost.addStyles(styles);
this.rendererByCompId.set(type.id, this.defaultRenderer);
}
return this.defaultRenderer;
}
}
}
begin() { }
end() { }
}
ServerRendererFactory2.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerRendererFactory2, deps: [{ token: i1.EventManager }, { token: i0.NgZone }, { token: DOCUMENT }, { token: i1.ɵSharedStylesHost }], target: i0.ɵɵFactoryTarget.Injectable });
ServerRendererFactory2.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerRendererFactory2 });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerRendererFactory2, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: i1.EventManager }, { type: i0.NgZone }, { type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: i1.ɵSharedStylesHost }]; } });
class DefaultServerRenderer2 {
constructor(eventManager, document, ngZone, schema) {
this.eventManager = eventManager;
this.document = document;
this.ngZone = ngZone;
this.schema = schema;
this.data = Object.create(null);
this.destroyNode = null;
}
destroy() { }
createElement(name, namespace, debugInfo) {
if (namespace) {
const doc = this.document || ɵgetDOM().getDefaultDocument();
return doc.createElementNS(ɵNAMESPACE_URIS[namespace], name);
}
return ɵgetDOM().createElement(name, this.document);
}
createComment(value, debugInfo) {
return ɵgetDOM().getDefaultDocument().createComment(value);
}
createText(value, debugInfo) {
const doc = ɵgetDOM().getDefaultDocument();
return doc.createTextNode(value);
}
appendChild(parent, newChild) {
parent.appendChild(newChild);
}
insertBefore(parent, newChild, refChild) {
if (parent) {
parent.insertBefore(newChild, refChild);
}
}
removeChild(parent, oldChild) {
if (parent) {
parent.removeChild(oldChild);
}
}
selectRootElement(selectorOrNode, debugInfo) {
let el;
if (typeof selectorOrNode === 'string') {
el = this.document.querySelector(selectorOrNode);
if (!el) {
throw new Error(`The selector "${selectorOrNode}" did not match any elements`);
}
}
else {
el = selectorOrNode;
}
while (el.firstChild) {
el.removeChild(el.firstChild);
}
return el;
}
parentNode(node) {
return node.parentNode;
}
nextSibling(node) {
return node.nextSibling;
}
setAttribute(el, name, value, namespace) {
if (namespace) {
el.setAttributeNS(ɵNAMESPACE_URIS[namespace], namespace + ':' + name, value);
}
else {
el.setAttribute(name, value);
}
}
removeAttribute(el, name, namespace) {
if (namespace) {
el.removeAttributeNS(ɵNAMESPACE_URIS[namespace], name);
}
else {
el.removeAttribute(name);
}
}
addClass(el, name) {
el.classList.add(name);
}
removeClass(el, name) {
el.classList.remove(name);
}
setStyle(el, style, value, flags) {
style = style.replace(/([a-z])([A-Z])/g, '$1-$2').toLowerCase();
const styleMap = _readStyleAttribute(el);
if (flags & RendererStyleFlags2.Important) {
value += ' !important';
}
styleMap[style] = value == null ? '' : value;
_writeStyleAttribute(el, styleMap);
}
removeStyle(el, style, flags) {
// IE requires '' instead of null
// see https://github.com/angular/angular/issues/7916
this.setStyle(el, style, '', flags);
}
// The value was validated already as a property binding, against the property name.
// To know this value is safe to use as an attribute, the security context of the
// attribute with the given name is checked against that security context of the
// property.
_isSafeToReflectProperty(tagName, propertyName) {
return this.schema.securityContext(tagName, propertyName, true) ===
this.schema.securityContext(tagName, propertyName, false);
}
setProperty(el, name, value) {
checkNoSyntheticProp(name, 'property');
if (name === 'innerText') {
// Domino does not support innerText. Just map it to textContent.
el.textContent = value;
}
el[name] = value;
// Mirror property values for known HTML element properties in the attributes.
// Skip `innerhtml` which is conservatively marked as an attribute for security
// purposes but is not actually an attribute.
const tagName = el.tagName.toLowerCase();
if (value != null && (typeof value === 'number' || typeof value == 'string') &&
name.toLowerCase() !== 'innerhtml' && this.schema.hasElement(tagName, EMPTY_ARRAY) &&
this.schema.hasProperty(tagName, name, EMPTY_ARRAY) &&
this._isSafeToReflectProperty(tagName, name)) {
this.setAttribute(el, name, value.toString());
}
}
setValue(node, value) {
node.textContent = value;
}
listen(target, eventName, callback) {
checkNoSyntheticProp(eventName, 'listener');
if (typeof target === 'string') {
return this.eventManager.addGlobalEventListener(target, eventName, this.decoratePreventDefault(callback));
}
return this.eventManager.addEventListener(target, eventName, this.decoratePreventDefault(callback));
}
decoratePreventDefault(eventHandler) {
return (event) => {
// Ivy uses `Function` as a special token that allows us to unwrap the function
// so that it can be invoked programmatically by `DebugNode.triggerEventHandler`.
if (event === Function) {
return eventHandler;
}
// Run the event handler inside the ngZone because event handlers are not patched
// by Zone on the server. This is required only for tests.
const allowDefaultBehavior = this.ngZone.runGuarded(() => eventHandler(event));
if (allowDefaultBehavior === false) {
event.preventDefault();
event.returnValue = false;
}
return undefined;
};
}
}
const AT_CHARCODE = '@'.charCodeAt(0);
function checkNoSyntheticProp(name, nameKind) {
if (name.charCodeAt(0) === AT_CHARCODE) {
throw new Error(`Unexpected synthetic ${nameKind} ${name} found. Please make sure that:
- Either \`BrowserAnimationsModule\` or \`NoopAnimationsModule\` are imported in your application.
- There is corresponding configuration for the animation named \`${name}\` defined in the \`animations\` field of the \` \` decorator (see https://angular.io/api/core/Component#animations).`);
}
}
class EmulatedEncapsulationServerRenderer2 extends DefaultServerRenderer2 {
constructor(eventManager, document, ngZone, sharedStylesHost, schema, component) {
super(eventManager, document, ngZone, schema);
this.component = component;
// Add a 's' prefix to style attributes to indicate server.
const componentId = 's' + component.id;
const styles = ɵflattenStyles(componentId, component.styles, []);
sharedStylesHost.addStyles(styles);
this.contentAttr = ɵshimContentAttribute(componentId);
this.hostAttr = ɵshimHostAttribute(componentId);
}
applyToHost(element) {
super.setAttribute(element, this.hostAttr, '');
}
createElement(parent, name) {
const el = super.createElement(parent, name, this.document);
super.setAttribute(el, this.contentAttr, '');
return el;
}
}
function _readStyleAttribute(element) {
const styleMap = {};
const styleAttribute = element.getAttribute('style');
if (styleAttribute) {
const styleList = styleAttribute.split(/;+/g);
for (let i = 0; i < styleList.length; i++) {
const style = styleList[i].trim();
if (style.length > 0) {
const colonIndex = style.indexOf(':');
if (colonIndex === -1) {
throw new Error(`Invalid CSS style: ${style}`);
}
const name = style.substr(0, colonIndex).trim();
styleMap[name] = style.substr(colonIndex + 1).trim();
}
}
}
return styleMap;
}
function _writeStyleAttribute(element, styleMap) {
let styleAttrValue = '';
for (const key in styleMap) {
const newValue = styleMap[key];
if (newValue != null) {
styleAttrValue += key + ':' + styleMap[key] + ';';
}
}
element.setAttribute('style', styleAttrValue);
}
/**
* @license
* Copyright Google LLC 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
*/
class ServerStylesHost extends ɵSharedStylesHost {
constructor(doc, transitionId) {
super();
this.doc = doc;
this.transitionId = transitionId;
this.head = null;
this._styleNodes = new Set();
this.head = doc.getElementsByTagName('head')[0];
}
_addStyle(style) {
let adapter = ɵgetDOM();
const el = adapter.createElement('style');
el.textContent = style;
if (!!this.transitionId) {
el.setAttribute('ng-transition', this.transitionId);
}
this.head.appendChild(el);
this._styleNodes.add(el);
}
onStylesAdded(additions) {
additions.forEach(style => this._addStyle(style));
}
ngOnDestroy() {
this._styleNodes.forEach(styleNode => styleNode.remove());
}
}
ServerStylesHost.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerStylesHost, deps: [{ token: DOCUMENT }, { token: ɵTRANSITION_ID, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
ServerStylesHost.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerStylesHost });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerStylesHost, decorators: [{
type: Injectable
}], ctorParameters: function () { return [{ type: undefined, decorators: [{
type: Inject,
args: [DOCUMENT]
}] }, { type: undefined, decorators: [{
type: Optional
}, {
type: Inject,
args: [ɵTRANSITION_ID]
}] }]; } });
/**
* @license
* Copyright Google LLC 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
*/
function notSupported(feature) {
throw new Error(`platform-server does not support '${feature}'.`);
}
const INTERNAL_SERVER_PLATFORM_PROVIDERS = [
{ provide: DOCUMENT, useFactory: _document, deps: [Injector] },
{ provide: PLATFORM_ID, useValue: ɵPLATFORM_SERVER_ID },
{ provide: PLATFORM_INITIALIZER, useFactory: initDominoAdapter, multi: true, deps: [Injector] }, {
provide: PlatformLocation,
useClass: ServerPlatformLocation,
deps: [DOCUMENT, [Optional, INITIAL_CONFIG]]
},
{ provide: PlatformState, deps: [DOCUMENT] },
// Add special provider that allows multiple instances of platformServer* to be created.
{ provide: ɵALLOW_MULTIPLE_PLATFORMS, useValue: true }
];
function initDominoAdapter(injector) {
return () => {
DominoAdapter.makeCurrent();
};
}
function instantiateServerRendererFactory(renderer, engine, zone) {
return new ɵAnimationRendererFactory(renderer, engine, zone);
}
const SERVER_RENDER_PROVIDERS = [
ServerRendererFactory2,
{
provide: RendererFactory2,
useFactory: instantiateServerRendererFactory,
deps: [ServerRendererFactory2, ɵAnimationEngine, NgZone]
},
ServerStylesHost,
{ provide: ɵSharedStylesHost, useExisting: ServerStylesHost },
{ provide: EVENT_MANAGER_PLUGINS, multi: true, useClass: ServerEventManagerPlugin },
];
/**
* The ng module for the server.
*
* @publicApi
*/
class ServerModule {
}
ServerModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ServerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerModule, imports: [HttpClientModule, NoopAnimationsModule], exports: [BrowserModule] });
ServerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerModule, providers: [
SERVER_RENDER_PROVIDERS,
SERVER_HTTP_PROVIDERS,
{ provide: Testability, useValue: null },
{ provide: ViewportScroller, useClass: ɵNullViewportScroller },
], imports: [[HttpClientModule, NoopAnimationsModule], BrowserModule] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerModule, decorators: [{
type: NgModule,
args: [{
exports: [BrowserModule],
imports: [HttpClientModule, NoopAnimationsModule],
providers: [
SERVER_RENDER_PROVIDERS,
SERVER_HTTP_PROVIDERS,
{ provide: Testability, useValue: null },
{ provide: ViewportScroller, useClass: ɵNullViewportScroller },
],
}]
}] });
function _document(injector) {
let config = injector.get(INITIAL_CONFIG, null);
const document = config && config.document ? parseDocument(config.document, config.url) :
ɵgetDOM().createHtmlDocument();
// Tell ivy about the global document
ɵsetDocument(document);
return document;
}
/**
* @publicApi
*/
const platformServer = createPlatformFactory(platformCore, 'server', INTERNAL_SERVER_PLATFORM_PROVIDERS);
/**
* The server platform that supports the runtime compiler.
*
* @publicApi
*/
const platformDynamicServer = createPlatformFactory(ɵplatformCoreDynamic, 'serverDynamic', INTERNAL_SERVER_PLATFORM_PROVIDERS);
/**
* @license
* Copyright Google LLC 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
*/
function serializeTransferStateFactory(doc, appId, transferStore) {
return () => {
const script = doc.createElement('script');
script.id = appId + '-state';
script.setAttribute('type', 'application/json');
script.textContent = ɵescapeHtml(transferStore.toJson());
doc.body.appendChild(script);
};
}
/**
* NgModule to install on the server side while using the `TransferState` to transfer state from
* server to client.
*
* @publicApi
*/
class ServerTransferStateModule {
}
ServerTransferStateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerTransferStateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
ServerTransferStateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerTransferStateModule });
ServerTransferStateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerTransferStateModule, providers: [
TransferState, {
provide: BEFORE_APP_SERIALIZED,
useFactory: serializeTransferStateFactory,
deps: [DOCUMENT, APP_ID, TransferState],
multi: true,
}
] });
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.3.8", ngImport: i0, type: ServerTransferStateModule, decorators: [{
type: NgModule,
args: [{
providers: [
TransferState, {
provide: BEFORE_APP_SERIALIZED,
useFactory: serializeTransferStateFactory,
deps: [DOCUMENT, APP_ID, TransferState],
multi: true,
}
]
}]
}] });
/**
* @license
* Copyright Google LLC 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
*/
function _getPlatform(platformFactory, options) {
const extraProviders = options.extraProviders ? options.extraProviders : [];
return platformFactory([
{ provide: INITIAL_CONFIG, useValue: { document: options.document, url: options.url } },
extraProviders
]);
}
function _render(platform, moduleRefPromise) {
return moduleRefPromise.then((moduleRef) => {
const transitionId = moduleRef.injector.get(ɵTRANSITION_ID, null);
if (!transitionId) {
throw new Error(`renderModule[Factory]() requires the use of BrowserModule.withServerTransition() to ensure
the server-rendered app can be properly bootstrapped into a client app.`);
}
const applicationRef = moduleRef.injector.get(ApplicationRef);
return applicationRef.isStable.pipe((first((isStable) => isStable)))
.toPromise()
.then(() => {
const platformState = platform.injector.get(PlatformState);
const asyncPromises = [];
// Run any BEFORE_APP_SERIALIZED callbacks just before rendering to string.
const callbacks = moduleRef.injector.get(BEFORE_APP_SERIALIZED, null);
if (callbacks) {
for (const callback of callbacks) {
try {
const callbackResult = callback();
if (ɵisPromise(callbackResult)) {
// TODO: in TS3.7, callbackResult is void.
asyncPromises.push(callbackResult);
}
}
catch (e) {
// Ignore exceptions.
console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', e);
}
}
}
const complete = () => {
const output = platformState.renderToString();
platform.destroy();
return output;
};
if (asyncPromises.length === 0) {
return complete();
}
return Promise
.all(asyncPromises.map(asyncPromise => {
return asyncPromise.catch(e => {
console.warn('Ignoring BEFORE_APP_SERIALIZED Exception: ', e);
});
}))
.then(complete);
});
});
}
/**
* Renders a Module to string.
*
* `document` is the full document HTML of the page to render, as a string.
* `url` is the URL for the current render request.
* `extraProviders` are the platform level providers for the current render request.
*
* @publicApi
*/
function renderModule(module, options) {
const platform = _getPlatform(platformDynamicServer, options);
return _render(platform, platform.bootstrapModule(module));
}
/**
* Renders a {@link NgModuleFactory} to string.
*
* `document` is the full document HTML of the page to render, as a string.
* `url` is the URL for the current render request.
* `extraProviders` are the platform level providers for the current render request.
*
* @publicApi
*
* @deprecated
* This symbol is no longer necessary as of Angular v13.
* Use {@link renderModule} API instead.
*/
function renderModuleFactory(moduleFactory, options) {
const platform = _getPlatform(platformServer, options);
return _render(platform, platform.bootstrapModuleFactory(moduleFactory));
}
/**
* @license
* Copyright Google LLC 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
*/
/**
* @license
* Copyright Google LLC 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
*/
/**
* @publicApi
*/
const VERSION = new Version('13.3.8');
/**
* @license
* Copyright Google LLC 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
*/
/**
* @license
* Copyright Google LLC 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
*/
// This file only reexports content of the `src` folder. Keep it that way.
/**
* @license
* Copyright Google LLC 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
*/
/**
* Generated bundle index. Do not edit.
*/
export { BEFORE_APP_SERIALIZED, INITIAL_CONFIG, PlatformState, ServerModule, ServerTransferStateModule, VERSION, platformDynamicServer, platformServer, renderModule, renderModuleFactory, INTERNAL_SERVER_PLATFORM_PROVIDERS as ɵINTERNAL_SERVER_PLATFORM_PROVIDERS, SERVER_RENDER_PROVIDERS as ɵSERVER_RENDER_PROVIDERS, ServerRendererFactory2 as ɵServerRendererFactory2 };
//# sourceMappingURL=platform-server.mjs.map