2017-08-08 02:17:40 -07:00
|
|
|
/**
|
|
|
|
* @license
|
2020-05-19 12:08:49 -07:00
|
|
|
* Copyright Google LLC All Rights Reserved.
|
2017-08-08 02:17:40 -07:00
|
|
|
*
|
|
|
|
* 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 domino = require('domino');
|
|
|
|
|
2019-08-22 19:16:25 -07:00
|
|
|
import {ɵBrowserDomAdapter as BrowserDomAdapter} from '@angular/platform-browser';
|
|
|
|
import {ɵsetRootDomAdapter as setRootDomAdapter} from '@angular/common';
|
2017-08-08 02:17:40 -07:00
|
|
|
|
feat(platform-server): allow shimming the global env sooner (#40559)
`@angular/platform-server` provides the foundation for rendering an
Angular app on the server. In order to achieve that, it uses a
server-side DOM implementation (currently [domino][1]).
For rendering on the server to work as closely as possible to running
the app on the browser, we need to make DOM globals (such as `Element`,
`HTMLElement`, etc.), which are normally provided by the browser,
available as globals on the server as well.
Currently, `@angular/platform-server` achieves this by extending the
`global` object with the DOM implementation provided by `domino`. This
assignment happens in the [setDomTypes()][2] function, which is
[called in a `PLATFORM_INITIALIZER`][3]. While this works in most cases,
there are some scenarios where the DOM globals are needed sooner (i.e.
before initializing the platform). See, for example, #24551 and #39950
for more details on such issues.
This commit provides a way to solve this problem by exposing a
side-effect-ful entry-point (`@angular/platform-server/init`), that
shims the `global` object with DOM globals. People will be able to
import this entry-point in their server-rendered apps before
bootstrapping the app (for example, in their `main.server.ts` file).
(See also [#39950 (comment)][4].)
In a future update, the [`universal` schematics][5] will include such an
import by default in newly generated projects.
[1]: https://www.npmjs.com/package/domino
[2]: https://github.com/angular/angular/blob/0fc8466f1be392917e0c/packages/platform-server/src/domino_adapter.ts#L17-L21
[3]: https://github.com/angular/angular/blob/0fc8466f1be392917e0c/packages/platform-server/src/server.ts#L33
[4]: https://github.com/angular/angular/issues/39950#issuecomment-747598403
[5]: https://github.com/angular/angular-cli/blob/cc51432661eb4ab4b6a3/packages/schematics/angular/universal
PR Close #40559
2021-02-11 19:24:52 +02:00
|
|
|
export function setDomTypes() {
|
|
|
|
// Make all Domino types available in the global env.
|
2018-05-24 16:04:04 -07:00
|
|
|
Object.assign(global, domino.impl);
|
|
|
|
(global as any)['KeyboardEvent'] = domino.impl.Event;
|
|
|
|
}
|
|
|
|
|
2017-08-08 02:17:40 -07:00
|
|
|
/**
|
|
|
|
* Parses a document string to a Document object.
|
|
|
|
*/
|
|
|
|
export function parseDocument(html: string, url = '/') {
|
|
|
|
let window = domino.createWindow(html, url);
|
|
|
|
let doc = window.document;
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Serializes a document to string.
|
|
|
|
*/
|
|
|
|
export function serializeDocument(doc: Document): string {
|
|
|
|
return (doc as any).serialize();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* DOM Adapter for the server platform based on https://github.com/fgnass/domino.
|
|
|
|
*/
|
|
|
|
export class DominoAdapter extends BrowserDomAdapter {
|
2021-06-07 21:03:01 +02:00
|
|
|
static override makeCurrent() {
|
2018-05-24 16:04:04 -07:00
|
|
|
setDomTypes();
|
|
|
|
setRootDomAdapter(new DominoAdapter());
|
|
|
|
}
|
2017-08-08 02:17:40 -07:00
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override readonly supportsDOMEvents = false;
|
2017-08-08 02:17:40 -07:00
|
|
|
private static defaultDoc: Document;
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override createHtmlDocument(): HTMLDocument {
|
2017-08-08 02:17:40 -07:00
|
|
|
return parseDocument('<html><head><title>fakeTitle</title></head><body></body></html>');
|
|
|
|
}
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override getDefaultDocument(): Document {
|
2017-08-08 02:17:40 -07:00
|
|
|
if (!DominoAdapter.defaultDoc) {
|
|
|
|
DominoAdapter.defaultDoc = domino.createDocument();
|
|
|
|
}
|
|
|
|
return DominoAdapter.defaultDoc;
|
|
|
|
}
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override isElementNode(node: any): boolean {
|
2017-08-08 02:17:40 -07:00
|
|
|
return node ? node.nodeType === DominoAdapter.defaultDoc.ELEMENT_NODE : false;
|
|
|
|
}
|
2021-06-07 21:03:01 +02:00
|
|
|
override isShadowRoot(node: any): boolean {
|
2020-04-13 16:40:21 -07:00
|
|
|
return node.shadowRoot == node;
|
|
|
|
}
|
2017-08-08 02:17:40 -07:00
|
|
|
|
2021-04-29 22:03:51 +02:00
|
|
|
/** @deprecated No longer being used in Ivy code. To be removed in version 14. */
|
2021-06-07 21:03:01 +02:00
|
|
|
override getGlobalEventTarget(doc: Document, target: string): EventTarget|null {
|
2017-08-08 02:17:40 -07:00
|
|
|
if (target === 'window') {
|
|
|
|
return doc.defaultView;
|
|
|
|
}
|
|
|
|
if (target === 'document') {
|
|
|
|
return doc;
|
|
|
|
}
|
|
|
|
if (target === 'body') {
|
|
|
|
return doc.body;
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override getBaseHref(doc: Document): string {
|
2017-08-08 02:17:40 -07:00
|
|
|
// TODO(alxhub): Need relative path logic from BrowserDomAdapter here?
|
2021-03-10 08:20:40 +01:00
|
|
|
return doc.documentElement!.querySelector('base')?.getAttribute('href') || '';
|
2017-08-08 02:17:40 -07:00
|
|
|
}
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override dispatchEvent(el: Node, evt: any) {
|
2017-08-08 02:17:40 -07:00
|
|
|
el.dispatchEvent(evt);
|
|
|
|
|
|
|
|
// Dispatch the event to the window also.
|
|
|
|
const doc = el.ownerDocument || el;
|
|
|
|
const win = (doc as any).defaultView;
|
|
|
|
if (win) {
|
|
|
|
win.dispatchEvent(evt);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override getUserAgent(): string {
|
2020-04-13 16:40:21 -07:00
|
|
|
return 'Fake user agent';
|
|
|
|
}
|
2017-08-08 02:17:40 -07:00
|
|
|
|
2021-06-07 21:03:01 +02:00
|
|
|
override getCookie(name: string): string {
|
2021-03-10 08:20:40 +01:00
|
|
|
throw new Error('getCookie has not been implemented');
|
2020-04-13 16:40:21 -07:00
|
|
|
}
|
2018-02-23 14:33:19 -05:00
|
|
|
}
|