UNPKG

@angular/platform-server

Version:

Angular - library for using Angular in Node.js

1,153 lines (1,133 loc) 46.5 kB
/** * @license Angular v15.0.1 * (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, inject, EnvironmentInjector, Optional, ViewEncapsulation, RendererStyleFlags2, APP_ID, NgModule, Injector, PLATFORM_ID, PLATFORM_INITIALIZER, ɵALLOW_MULTIPLE_PLATFORMS, RendererFactory2, NgZone, Testability, ɵTESTABILITY, ɵsetDocument, createPlatformFactory, platformCore, Renderer2, ApplicationRef, ɵisPromise, importProvidersFrom, ɵinternalCreateApplication, Version } from '@angular/core'; import * as i1 from '@angular/platform-browser'; import { ɵBrowserDomAdapter, ɵflattenStyles, ɵNAMESPACE_URIS, ɵshimContentAttribute, ɵshimHostAttribute, ɵSharedStylesHost, ɵTRANSITION_ID, TransferState, ɵescapeHtml, EVENT_MANAGER_PLUGINS, BrowserModule } from '@angular/platform-browser'; import * as domino from 'domino'; import { ɵAnimationEngine } from '@angular/animations/browser'; import { ɵHttpInterceptorHandler, HttpBackend, HttpHandler, 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) { var _a; // TODO(alxhub): Need relative path logic from BrowserDomAdapter here? return ((_a = doc.documentElement.querySelector('base')) === null || _a === void 0 ? void 0 : _a.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: "15.0.1", ngImport: i0, type: PlatformState, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); PlatformState.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: PlatformState }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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 `renderApplication`, `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: "15.0.1", ngImport: i0, type: ServerXhr, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); ServerXhr.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerXhr }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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 zoneWrappedInterceptorHandler(platformLocation, config) { return new ZoneClientBackend(new ɵHttpInterceptorHandler(inject(HttpBackend), inject(EnvironmentInjector)), platformLocation, config); } const SERVER_HTTP_PROVIDERS = [ { provide: XhrFactory, useClass: ServerXhr }, { provide: HttpHandler, useFactory: zoneWrappedInterceptorHandler, deps: [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: "15.0.1", 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: "15.0.1", ngImport: i0, type: ServerPlatformLocation }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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: "15.0.1", 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: "15.0.1", ngImport: i0, type: ServerEventManagerPlugin /* extends EventManagerPlugin which is private */ }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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: "15.0.1", 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: "15.0.1", ngImport: i0, type: ServerRendererFactory2 }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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) { if (namespace) { const doc = this.document || ɵgetDOM().getDefaultDocument(); return doc.createElementNS(ɵNAMESPACE_URIS[namespace], name); } return ɵgetDOM().createElement(name, this.document); } createComment(value) { return ɵgetDOM().getDefaultDocument().createComment(value); } createText(value) { const doc = ɵgetDOM().getDefaultDocument(); return doc.createTextNode(value); } appendChild(parent, newChild) { const targetParent = isTemplateNode(parent) ? parent.content : parent; targetParent.appendChild(newChild); } insertBefore(parent, newChild, refChild) { if (parent) { const targetParent = isTemplateNode(parent) ? parent.content : parent; targetParent.insertBefore(newChild, refChild); } } removeChild(parent, oldChild) { if (parent) { parent.removeChild(oldChild); } } selectRootElement(selectorOrNode, preserveContent) { const el = typeof selectorOrNode === 'string' ? this.document.querySelector(selectorOrNode) : selectorOrNode; if (!el) { throw new Error(`The selector "${selectorOrNode}" did not match any elements`); } if (!preserveContent) { 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(); value = value == null ? '' : `${value}`.trim(); const styleMap = _readStyleAttribute(el); if (flags & RendererStyleFlags2.Important) { value += ' !important'; } styleMap[style] = 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 \`@Component\` decorator (see https://angular.io/api/core/Component#animations).`); } } function isTemplateNode(node) { return node.tagName === 'TEMPLATE' && node.content !== undefined; } 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); 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.slice(0, colonIndex).trim(); styleMap[name] = style.slice(colonIndex + 1).trim(); } } } return styleMap; } function _writeStyleAttribute(element, styleMap) { // We have to construct the `style` attribute ourselves, instead of going through // `element.style.setProperty` like the other renderers, because `setProperty` won't // write newer CSS properties that Domino doesn't know about like `clip-path`. let styleAttrValue = ''; for (const key in styleMap) { const newValue = styleMap[key]; if (newValue != null && newValue !== '') { styleAttrValue += key + ':' + newValue + ';'; } } if (styleAttrValue) { element.setAttribute('style', styleAttrValue); } else { element.removeAttribute('style'); } } /** * @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: "15.0.1", 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: "15.0.1", ngImport: i0, type: ServerStylesHost }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", 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 */ const TRANSFER_STATE_SERIALIZATION_PROVIDERS = [{ provide: BEFORE_APP_SERIALIZED, useFactory: serializeTransferStateFactory, deps: [DOCUMENT, APP_ID, TransferState], multi: true, }]; function serializeTransferStateFactory(doc, appId, transferStore) { return () => { // The `.toJSON` here causes the `onSerialize` callbacks to be called. // These callbacks can be used to provide the value for a given key. const content = transferStore.toJson(); if (transferStore.isEmpty) { // The state is empty, nothing to transfer, // avoid creating an extra `<script>` tag in this case. return; } const script = doc.createElement('script'); script.id = appId + '-state'; script.setAttribute('type', 'application/json'); script.textContent = ɵescapeHtml(content); doc.body.appendChild(script); }; } /** * NgModule to install on the server side while using the `TransferState` to transfer state from * server to client. * * Note: this module is not needed if the `renderApplication` function is used. * The `renderApplication` makes all providers from this module available in the application. * * @publicApi * @deprecated no longer needed, you can inject the `TransferState` in an app without providing * this module. */ class ServerTransferStateModule { } ServerTransferStateModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerTransferStateModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); ServerTransferStateModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0, type: ServerTransferStateModule }); ServerTransferStateModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerTransferStateModule }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerTransferStateModule, decorators: [{ type: NgModule, args: [{}] }] }); /** * @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 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: "15.0.1", ngImport: i0, type: ServerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); ServerModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "15.0.1", ngImport: i0, type: ServerModule, imports: [HttpClientModule, NoopAnimationsModule], exports: [BrowserModule] }); ServerModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerModule, providers: [ TRANSFER_STATE_SERIALIZATION_PROVIDERS, SERVER_RENDER_PROVIDERS, SERVER_HTTP_PROVIDERS, { provide: Testability, useValue: null }, { provide: ɵTESTABILITY, useValue: null }, { provide: ViewportScroller, useClass: ɵNullViewportScroller }, ], imports: [HttpClientModule, NoopAnimationsModule, BrowserModule] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "15.0.1", ngImport: i0, type: ServerModule, decorators: [{ type: NgModule, args: [{ exports: [BrowserModule], imports: [HttpClientModule, NoopAnimationsModule], providers: [ TRANSFER_STATE_SERIALIZATION_PROVIDERS, SERVER_RENDER_PROVIDERS, SERVER_HTTP_PROVIDERS, { provide: Testability, useValue: null }, { provide: ɵTESTABILITY, useValue: null }, { provide: ViewportScroller, useClass: ɵNullViewportScroller }, ], }] }] }); function _document(injector) { const config = injector.get(INITIAL_CONFIG, null); let document; if (config && config.document) { document = typeof config.document === 'string' ? parseDocument(config.document, config.url) : config.document; } else { document = ɵ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 _getPlatform(platformFactory, options) { var _a; const extraProviders = (_a = options.platformProviders) !== null && _a !== void 0 ? _a : []; return platformFactory([ { provide: INITIAL_CONFIG, useValue: { document: options.document, url: options.url } }, extraProviders ]); } /** * Adds the `ng-server-context` attribute to host elements of all bootstrapped components * within a given application. */ function appendServerContextInfo(serverContext, applicationRef) { applicationRef.components.forEach(componentRef => { const renderer = componentRef.injector.get(Renderer2); const element = componentRef.location.nativeElement; if (element) { renderer.setAttribute(element, 'ng-server-context', serverContext); } }); } function _render(platform, bootstrapPromise) { return bootstrapPromise.then((moduleOrApplicationRef) => { const environmentInjector = moduleOrApplicationRef.injector; const transitionId = environmentInjector.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 = moduleOrApplicationRef instanceof ApplicationRef ? moduleOrApplicationRef : environmentInjector.get(ApplicationRef); const serverContext = sanitizeServerContext(environmentInjector.get(SERVER_CONTEXT, DEFAULT_SERVER_CONTEXT)); return applicationRef.isStable.pipe((first((isStable) => isStable))) .toPromise() .then(() => { appendServerContextInfo(serverContext, applicationRef); const platformState = platform.injector.get(PlatformState); const asyncPromises = []; // Run any BEFORE_APP_SERIALIZED callbacks just before rendering to string. const callbacks = environmentInjector.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); }); }); } /** * Specifies the value that should be used if no server context value has been provided. */ const DEFAULT_SERVER_CONTEXT = 'other'; /** * An internal token that allows providing extra information about the server context * (e.g. whether SSR or SSG was used). The value is a string and characters other * than [a-zA-Z0-9\-] are removed. See the default value in `DEFAULT_SERVER_CONTEXT` const. */ const SERVER_CONTEXT = new InjectionToken('SERVER_CONTEXT'); /** * Sanitizes provided server context: * - removes all characters other than a-z, A-Z, 0-9 and `-` * - returns `other` if nothing is provided or the string is empty after sanitization */ function sanitizeServerContext(serverContext) { const context = serverContext.replace(/[^a-zA-Z0-9\-]/g, ''); return context.length > 0 ? context : DEFAULT_SERVER_CONTEXT; } /** * Bootstraps an application using provided NgModule and serializes the page content to string. * * @param moduleType A reference to an NgModule that should be used for bootstrap. * @param options Additional configuration for the render operation: * - `document` - the document of the page to render, either as an HTML string or * as a reference to the `document` instance. * - `url` - the URL for the current render request. * - `extraProviders` - set of platform level providers for the current render request. * * @publicApi */ function renderModule(moduleType, options) { const { document, url, extraProviders: platformProviders } = options; const platform = _getPlatform(platformDynamicServer, { document, url, platformProviders }); return _render(platform, platform.bootstrapModule(moduleType)); } /** * Bootstraps an instance of an Angular application and renders it to a string. * * Note: the root component passed into this function *must* be a standalone one (should have the * `standalone: true` flag in the `@Component` decorator config). * * ```typescript * @Component({ * standalone: true, * template: 'Hello world!' * }) * class RootComponent {} * * const output: string = await renderApplication(RootComponent, {appId: 'server-app'}); * ``` * * @param rootComponent A reference to a Standalone Component that should be rendered. * @param options Additional configuration for the render operation: * - `appId` - a string identifier of this application. The appId is used to prefix all * server-generated stylings and state keys of the application in TransferState * use-cases. * - `document` - the document of the page to render, either as an HTML string or * as a reference to the `document` instance. * - `url` - the URL for the current render request. * - `providers` - set of application level providers for the current render request. * - `platformProviders` - the platform level providers for the current render request. * * @returns A Promise, that returns serialized (to a string) rendered page, once resolved. * * @publicApi * @developerPreview */ function renderApplication(rootComponent, options) { var _a; const { document, url, platformProviders, appId } = options; const platform = _getPlatform(platformDynamicServer, { document, url, platformProviders }); const appProviders = [ importProvidersFrom(BrowserModule.withServerTransition({ appId })), importProvidersFrom(ServerModule), ...TRANSFER_STATE_SERIALIZATION_PROVIDERS, ...((_a = options.providers) !== null && _a !== void 0 ? _a : []), ]; return _render(platform, ɵinternalCreateApplication({ rootComponent, appProviders })); } /** * Bootstraps an application using provided {@link NgModuleFactory} and serializes the page content * to string. * * @param moduleFactory An instance of the {@link NgModuleFactory} that should be used for * bootstrap. * @param options Additional configuration for the render operation: * - `document` - the document of the page to render, either as an HTML string or * as a reference to the `document` instance. * - `url` - the URL for the current render request. * - `extraProviders` - set of 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 { document, url, extraProviders: platformProviders } = options; const platform = _getPlatform(platformServer, { document, url, platformProviders }); 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('15.0.1'); /** * @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, renderApplication, renderModule, renderModuleFactory, INTERNAL_SERVER_PLATFORM_PROVIDERS as ɵINTERNAL_SERVER_PLATFORM_PROVIDERS, SERVER_CONTEXT as ɵSERVER_CONTEXT, SERVER_RENDER_PROVIDERS as ɵSERVER_RENDER_PROVIDERS, ServerRendererFactory2 as ɵServerRendererFactory2, setDomTypes as ɵsetDomTypes }; //# sourceMappingURL=platform-server.mjs.map