UNPKG

@angular/platform-server

Version:

Angular - library for using Angular in Node.js

545 lines (534 loc) 14.9 kB
/** * @license Angular v21.2.7 * (c) 2010-2026 Google LLC. https://angular.dev/ * License: MIT */ import { ɵsetRootDomAdapter as _setRootDomAdapter, DOCUMENT, XhrFactory, PlatformLocation, ɵgetDOM as _getDOM, ɵPLATFORM_SERVER_ID as _PLATFORM_SERVER_ID, ɵNullViewportScroller as _NullViewportScroller, ViewportScroller } from '@angular/common'; import * as i0 from '@angular/core'; import { InjectionToken, inject, Injector, ɵstartMeasuring as _startMeasuring, ɵstopMeasuring as _stopMeasuring, Inject, Injectable, APP_ID, TransferState, PLATFORM_ID, PLATFORM_INITIALIZER, Testability, ɵTESTABILITY as _TESTABILITY, createPlatformFactory, platformCore, ɵsetDocument as _setDocument, NgModule } from '@angular/core'; import { ɵBrowserDomAdapter as _BrowserDomAdapter, EventManagerPlugin, EVENT_MANAGER_PLUGINS, BrowserModule } from '@angular/platform-browser'; import domino from '../third_party/domino/bundled-domino.mjs'; import { ɵHTTP_ROOT_INTERCEPTOR_FNS as _HTTP_ROOT_INTERCEPTOR_FNS } from '@angular/common/http'; import { Subject } from 'rxjs'; function setDomTypes() { Object.assign(globalThis, domino.impl); globalThis['KeyboardEvent'] = domino.impl.Event; } function parseDocument(html, url = '/') { let window = domino.createWindow(html, url); let doc = window.document; return doc; } function serializeDocument(doc) { return doc.serialize(); } class DominoAdapter extends _BrowserDomAdapter { static makeCurrent() { setDomTypes(); _setRootDomAdapter(new DominoAdapter()); } supportsDOMEvents = false; static defaultDoc; 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; } getGlobalEventTarget(doc, target) { if (target === 'window') { return doc.defaultView; } if (target === 'document') { return doc; } if (target === 'body') { return doc.body; } return null; } getBaseHref(doc) { const length = doc.head.children.length; for (let i = 0; i < length; i++) { const child = doc.head.children[i]; if (child.tagName === 'BASE') { return child.getAttribute('href') || ''; } } return ''; } dispatchEvent(el, evt) { el.dispatchEvent(evt); 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'); } } const INITIAL_CONFIG = new InjectionToken('Server.INITIAL_CONFIG'); const BEFORE_APP_SERIALIZED = new InjectionToken('Server.RENDER_MODULE_HOOK'); const ENABLE_DOM_EMULATION = new InjectionToken('ENABLE_DOM_EMULATION'); class PlatformState { _doc; _enableDomEmulation = enableDomEmulation(inject(Injector)); constructor(_doc) { this._doc = _doc; } renderToString() { if (ngDevMode && !this._enableDomEmulation && !window?.document) { throw new Error('Disabled DOM emulation should only run in browser environments'); } const measuringLabel = 'renderToString'; _startMeasuring(measuringLabel); const rendered = this._enableDomEmulation ? serializeDocument(this._doc) : this._doc.documentElement.outerHTML; _stopMeasuring(measuringLabel); return rendered; } getDocument() { return this._doc; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: PlatformState, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: PlatformState }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: PlatformState, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); function enableDomEmulation(injector) { return injector.get(ENABLE_DOM_EMULATION, true); } class ServerXhr { xhrImpl; async ɵloadImpl() { if (!this.xhrImpl) { const { default: xhr } = await import('xhr2'); this.xhrImpl = xhr; } } build() { const impl = this.xhrImpl; if (!impl) { throw new Error('Unexpected state in ServerXhr: XHR implementation is not loaded.'); } return new impl.XMLHttpRequest(); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerXhr, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerXhr }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerXhr, decorators: [{ type: Injectable }] }); function relativeUrlsTransformerInterceptorFn(request, next) { const platformLocation = inject(PlatformLocation); const { href, protocol, hostname, port } = platformLocation; if (!protocol.startsWith('http')) { return next(request); } let urlPrefix = `${protocol}//${hostname}`; if (port) { urlPrefix += `:${port}`; } const baseHref = platformLocation.getBaseHrefFromDOM() || href; const baseUrl = new URL(baseHref, urlPrefix); const newUrl = new URL(request.url, baseUrl).toString(); return next(request.clone({ url: newUrl })); } const SERVER_HTTP_PROVIDERS = [{ provide: XhrFactory, useClass: ServerXhr }, { provide: _HTTP_ROOT_INTERCEPTOR_FNS, useValue: relativeUrlsTransformerInterceptorFn, multi: true }]; function parseUrl(urlStr, origin) { const { hostname, protocol, port, pathname, search, hash, href } = new URL(urlStr, origin); return { hostname, href, protocol, port, pathname, search, hash }; } class ServerPlatformLocation { href = '/'; hostname = '/'; protocol = '/'; port = '/'; pathname = '/'; search = ''; hash = ''; _hashUpdate = new Subject(); _doc = inject(DOCUMENT); constructor() { const config = inject(INITIAL_CONFIG, { optional: true }); if (!config) { return; } if (config.url) { const url = parseUrl(config.url, this._doc.location.origin); 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 = url.href; } } getBaseHrefFromDOM() { return _getDOM().getBaseHref(this._doc); } onPopState(fn) { 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) { return; } this.hash = value; const newUrl = this.url; queueMicrotask(() => this._hashUpdate.next({ type: 'hashchange', state: null, oldUrl, newUrl })); } replaceState(state, title, newUrl) { const oldUrl = this.url; const parsedUrl = parseUrl(newUrl, this._doc.location.origin); this.pathname = parsedUrl.pathname; this.search = parsedUrl.search; this.href = parsedUrl.href; this.protocol = parsedUrl.protocol; 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'); } getState() { return undefined; } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerPlatformLocation, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerPlatformLocation }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerPlatformLocation, decorators: [{ type: Injectable }], ctorParameters: () => [] }); class ServerEventManagerPlugin extends EventManagerPlugin { doc; constructor(doc) { super(doc); this.doc = doc; } supports(eventName) { return true; } addEventListener(element, eventName, handler, options) { return _getDOM().onAndCancel(element, eventName, handler, options); } static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerEventManagerPlugin, deps: [{ token: DOCUMENT }], target: i0.ɵɵFactoryTarget.Injectable }); static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerEventManagerPlugin }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerEventManagerPlugin, decorators: [{ type: Injectable }], ctorParameters: () => [{ type: undefined, decorators: [{ type: Inject, args: [DOCUMENT] }] }] }); const TRANSFER_STATE_STATUS = new InjectionToken(typeof ngDevMode === 'undefined' || ngDevMode ? 'TRANSFER_STATE_STATUS' : '', { factory: () => ({ serialized: false }) }); const TRANSFER_STATE_SERIALIZATION_PROVIDERS = [{ provide: BEFORE_APP_SERIALIZED, useFactory: serializeTransferStateFactory, multi: true }]; function createScript(doc, textContent, nonce) { const script = doc.createElement('script'); script.textContent = textContent; if (nonce) { script.setAttribute('nonce', nonce); } return script; } function warnIfStateTransferHappened(injector) { const transferStateStatus = injector.get(TRANSFER_STATE_STATUS); if (transferStateStatus.serialized) { console.warn(`Angular detected an incompatible configuration, which causes duplicate serialization of the server-side application state.\n\n` + `This can happen if the server providers have been provided more than once using different mechanisms. For example:\n\n` + ` imports: [ServerModule], // Registers server providers\n` + ` providers: [provideServerRendering()] // Also registers server providers\n\n` + `To fix this, ensure that the \`provideServerRendering()\` function is the only provider used and remove the other(s).`); } transferStateStatus.serialized = true; } function serializeTransferStateFactory() { const doc = inject(DOCUMENT); const appId = inject(APP_ID); const transferStore = inject(TransferState); const injector = inject(Injector); return () => { const measuringLabel = 'serializeTransferStateFactory'; _startMeasuring(measuringLabel); const content = transferStore.toJson(); if (transferStore.isEmpty) { return; } if (typeof ngDevMode !== 'undefined' && ngDevMode) { warnIfStateTransferHappened(injector); } const script = createScript(doc, content, null); script.id = appId + '-state'; script.setAttribute('type', 'application/json'); doc.body.appendChild(script); _stopMeasuring(measuringLabel); }; } const INTERNAL_SERVER_PLATFORM_PROVIDERS = [{ provide: DOCUMENT, useFactory: _document }, { provide: PLATFORM_ID, useValue: _PLATFORM_SERVER_ID }, { provide: PLATFORM_INITIALIZER, useFactory: initDominoAdapter, multi: true }, { provide: PlatformLocation, useClass: ServerPlatformLocation, deps: [] }, { provide: PlatformState, deps: [DOCUMENT] }]; function initDominoAdapter() { const injector = inject(Injector); const _enableDomEmulation = enableDomEmulation(injector); return () => { if (_enableDomEmulation) { DominoAdapter.makeCurrent(); } else { _BrowserDomAdapter.makeCurrent(); } }; } const SERVER_RENDER_PROVIDERS = [{ provide: EVENT_MANAGER_PLUGINS, multi: true, useClass: ServerEventManagerPlugin }]; const PLATFORM_SERVER_PROVIDERS = [TRANSFER_STATE_SERIALIZATION_PROVIDERS, SERVER_RENDER_PROVIDERS, SERVER_HTTP_PROVIDERS, { provide: Testability, useValue: null }, { provide: _TESTABILITY, useValue: null }, { provide: ViewportScroller, useClass: _NullViewportScroller }]; class ServerModule { static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.7", ngImport: i0, type: ServerModule, exports: [BrowserModule] }); static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerModule, providers: PLATFORM_SERVER_PROVIDERS, imports: [BrowserModule] }); } i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.7", ngImport: i0, type: ServerModule, decorators: [{ type: NgModule, args: [{ exports: [BrowserModule], providers: PLATFORM_SERVER_PROVIDERS }] }] }); function _document() { const injector = inject(Injector); const config = injector.get(INITIAL_CONFIG, null); const _enableDomEmulation = enableDomEmulation(injector); let document; if (config && config.document) { document = typeof config.document === 'string' ? _enableDomEmulation ? parseDocument(config.document, config.url) : window.document : config.document; } else { document = _getDOM().createHtmlDocument(); } _setDocument(document); return document; } function platformServer(extraProviders) { const noServerModeSet = typeof ngServerMode === 'undefined'; if (noServerModeSet) { globalThis['ngServerMode'] = true; } const platform = createPlatformFactory(platformCore, 'server', INTERNAL_SERVER_PLATFORM_PROVIDERS)(extraProviders); if (noServerModeSet) { platform.onDestroy(() => { globalThis['ngServerMode'] = undefined; }); } return platform; } export { BEFORE_APP_SERIALIZED, DominoAdapter, ENABLE_DOM_EMULATION, INITIAL_CONFIG, INTERNAL_SERVER_PLATFORM_PROVIDERS, PLATFORM_SERVER_PROVIDERS, PlatformState, SERVER_RENDER_PROVIDERS, ServerModule, createScript, platformServer }; //# sourceMappingURL=_server-chunk.mjs.map