Skip to content

Commit f601f3f

Browse files
committed
feat(platform-server): allow shimming the global env sooner
`@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/shims`), 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]: #39950 (comment) [5]: https://github.com/angular/angular-cli/blob/cc51432661eb4ab4b6a3/packages/schematics/angular/universal
1 parent 650d9fa commit f601f3f

File tree

20 files changed

+171
-5
lines changed

20 files changed

+171
-5
lines changed

aio/tools/transforms/angular-api-package/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ module.exports =
107107
'platform-browser-dynamic/index.ts',
108108
'platform-browser-dynamic/testing/index.ts',
109109
'platform-server/index.ts',
110+
'platform-server/shims/index.ts',
110111
'platform-server/testing/index.ts',
111112
'platform-webworker/index.ts',
112113
'platform-webworker-dynamic/index.ts',

aio/tools/transforms/authors-package/api-package.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ const packageMap = {
1818
forms: ['forms/index.ts'],
1919
'platform-browser': ['platform-browser/index.ts', 'platform-browser/animations/index.ts', 'platform-browser/testing/index.ts'],
2020
'platform-browser-dynamic': ['platform-browser-dynamic/index.ts', 'platform-browser-dynamic/testing/index.ts'],
21-
'platform-server': ['platform-server/index.ts', 'platform-server/testing/index.ts'],
21+
'platform-server': ['platform-server/index.ts', 'platform-server/shims/index.ts', 'platform-server/testing/index.ts'],
2222
router: ['router/index.ts', 'router/testing/index.ts', 'router/upgrade/index.ts'],
2323
'service-worker': ['service-worker/index.ts'],
2424
upgrade: ['upgrade/index.ts', 'upgrade/static/index.ts', 'upgrade/static/testing/index.ts']

goldens/public-api/platform-server/shims/shims.d.ts

Whitespace-only changes.

integration/cli-elements-universal/src/main.server.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import '@angular/platform-server/shims';
12
import { enableProdMode } from '@angular/core';
23

34
import { environment } from './environments/environment';

integration/typings_test_ts40/include-all.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynami
2727
import * as platformBrowserAnimations from '@angular/platform-browser/animations';
2828
import * as platformBrowserTesting from '@angular/platform-browser/testing';
2929
import * as platformServer from '@angular/platform-server';
30+
import * as platformServerShims from '@angular/platform-server/shims';
3031
import * as platformServerTesting from '@angular/platform-server/testing';
3132
import * as router from '@angular/router';
3233
import * as routerTesting from '@angular/router/testing';
@@ -56,6 +57,7 @@ export default {
5657
platformBrowserDynamicTesting,
5758
platformBrowserAnimations,
5859
platformServer,
60+
platformServerShims,
5961
platformServerTesting,
6062
router,
6163
routerTesting,

integration/typings_test_ts41/include-all.ts

+2
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import * as platformBrowserDynamicTesting from '@angular/platform-browser-dynami
2727
import * as platformBrowserAnimations from '@angular/platform-browser/animations';
2828
import * as platformBrowserTesting from '@angular/platform-browser/testing';
2929
import * as platformServer from '@angular/platform-server';
30+
import * as platformServerShims from '@angular/platform-server/shims';
3031
import * as platformServerTesting from '@angular/platform-server/testing';
3132
import * as router from '@angular/router';
3233
import * as routerTesting from '@angular/router/testing';
@@ -56,6 +57,7 @@ export default {
5657
platformBrowserDynamicTesting,
5758
platformBrowserAnimations,
5859
platformServer,
60+
platformServerShims,
5961
platformServerTesting,
6062
router,
6163
routerTesting,

packages/bazel/src/ng_package/ng_package.bzl

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ WELL_KNOWN_GLOBALS = {p: _global_name(p) for p in [
105105
"@angular/forms",
106106
"@angular/core/testing",
107107
"@angular/core",
108+
"@angular/platform-server/shims",
108109
"@angular/platform-server/testing",
109110
"@angular/platform-server",
110111
"@angular/common/testing",

packages/platform-server/BUILD.bazel

+2
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ ng_package(
3030
name = "npm_package",
3131
srcs = [
3232
"package.json",
33+
"//packages/platform-server/shims:package.json",
3334
"//packages/platform-server/testing:package.json",
3435
],
3536
entry_point = ":index.ts",
@@ -44,6 +45,7 @@ ng_package(
4445
],
4546
deps = [
4647
":platform-server",
48+
"//packages/platform-server/shims",
4749
"//packages/platform-server/testing",
4850
],
4951
)

packages/platform-server/package.json

+9-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,15 @@
2525
"ng-update": {
2626
"packageGroup": "NG_UPDATE_PACKAGE_GROUP"
2727
},
28-
"sideEffects": false,
28+
"sideEffects": [
29+
"./bundles/platform-server-shims.umd.js",
30+
"./bundles/platform-server-shims.umd.min.js",
31+
"./esm2015/shims/src/shims.js",
32+
"./fesm2015/shims.js",
33+
"./__ivy_ngcc__/bundles/platform-server-shims.umd.js",
34+
"./__ivy_ngcc__/esm2015/shims/src/shims.js",
35+
"./__ivy_ngcc__/fesm2015/shims.js"
36+
],
2937
"engines": {
3038
"node": ">=8.0"
3139
},
+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
load("//tools:defaults.bzl", "ng_module")
2+
3+
package(default_visibility = ["//visibility:public"])
4+
5+
exports_files(["package.json"])
6+
7+
ng_module(
8+
name = "shims",
9+
srcs = glob(
10+
[
11+
"*.ts",
12+
"src/**/*.ts",
13+
],
14+
),
15+
deps = [
16+
"//packages/platform-server",
17+
],
18+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Provides shims (such as DOM globals) for the server environment.
+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
// This file is not used to build this module. It is only used during editing
10+
// by the TypeScript language service and during build for verifcation. `ngc`
11+
// replaces this file with production index.ts when it rewrites private symbol
12+
// names.
13+
14+
export * from './public_api';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"name": "@angular/platform-server/shims",
3+
"sideEffects": true
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* @module
11+
* @description
12+
* Entry point for all public APIs of this package.
13+
*/
14+
export * from './src/shims';
+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
import {setDomTypes} from '@angular/platform-server/src/domino_adapter';
9+
10+
/**
11+
* Apply the necessary shims to make DOM globals (such as `Element`, `HTMLElement`, etc.) available
12+
* on the environment.
13+
*/
14+
export function init(): void {
15+
setDomTypes();
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
9+
/**
10+
* @module
11+
* @description
12+
* Entry point for all shimming APIs of the platform-server package.
13+
*/
14+
15+
import {init} from './init';
16+
17+
init();
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
load("//tools:defaults.bzl", "jasmine_node_test", "ts_library")
2+
load("//tools/circular_dependency_test:index.bzl", "circular_dependency_test")
3+
4+
circular_dependency_test(
5+
name = "circular_deps_test",
6+
entry_point = "angular/packages/platform-server/shims/index.js",
7+
deps = ["//packages/platform-server/shims"],
8+
)
9+
10+
ts_library(
11+
name = "test_lib",
12+
testonly = True,
13+
srcs = glob(["**/*.ts"]),
14+
deps = [
15+
"//packages/platform-server/shims",
16+
],
17+
)
18+
19+
jasmine_node_test(
20+
name = "test",
21+
bootstrap = ["//tools/testing:node_no_angular"],
22+
deps = [
23+
":test_lib",
24+
],
25+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/**
2+
* @license
3+
* Copyright Google LLC All Rights Reserved.
4+
*
5+
* Use of this source code is governed by an MIT-style license that can be
6+
* found in the LICENSE file at https://angular.io/license
7+
*/
8+
const domino = require('domino');
9+
import {init} from '../src/init';
10+
11+
describe('init()', () => {
12+
const globalClone = {...global};
13+
14+
afterEach(() => {
15+
// Un-patch `global`.
16+
const currentProps = Object.keys(global) as (keyof NodeJS.Global)[];
17+
for (const prop of currentProps) {
18+
if (globalClone.hasOwnProperty(prop)) {
19+
(global as any)[prop] = globalClone[prop];
20+
} else {
21+
delete (global as any)[prop];
22+
}
23+
}
24+
});
25+
26+
it('should load `domino.impl` onto `global`', () => {
27+
expect(global).not.toEqual(jasmine.objectContaining(domino.impl));
28+
29+
init();
30+
expect(global).toEqual(jasmine.objectContaining(domino.impl));
31+
});
32+
33+
it('should define `KeyboardEvent` on `global`', () => {
34+
expect((global as any).KeyboardEvent).not.toBe((domino.impl as any).Event);
35+
36+
init();
37+
expect((global as any).KeyboardEvent).toBe((domino.impl as any).Event);
38+
});
39+
});

packages/platform-server/src/domino_adapter.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ function _notImplemented(methodName: string) {
1414
return new Error('This method is not implemented in DominoAdapter: ' + methodName);
1515
}
1616

17-
function setDomTypes() {
18-
// Make all Domino types available as types in the global env.
17+
export function setDomTypes() {
18+
// Make all Domino types available in the global env.
1919
Object.assign(global, domino.impl);
2020
(global as any)['KeyboardEvent'] = domino.impl.Event;
2121
}

test-main.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ System.config({
2727
'domino': 'dist/all/@angular/empty.js',
2828
'url': 'dist/all/@angular/empty.js',
2929
'xhr2': 'dist/all/@angular/empty.js',
30-
'@angular/platform-server/src/domino_adapter': 'dist/all/empty.js',
30+
'@angular/platform-server/src/domino_adapter': 'dist/all/@angular/empty.js',
3131
'angular-in-memory-web-api': 'dist/all/@angular/misc/angular-in-memory-web-api',
3232
'rxjs': 'node_modules/rxjs',
3333
},
@@ -64,6 +64,7 @@ System.config({
6464
'@angular/platform-browser': {main: 'index.js', defaultExtension: 'js'},
6565
'@angular/platform-browser-dynamic/testing': {main: 'index.js', defaultExtension: 'js'},
6666
'@angular/platform-browser-dynamic': {main: 'index.js', defaultExtension: 'js'},
67+
'@angular/platform-server/shims': {main: 'index.js', defaultExtension: 'js'},
6768
'@angular/platform-server/testing': {main: 'index.js', defaultExtension: 'js'},
6869
'@angular/platform-server': {main: 'index.js', defaultExtension: 'js'},
6970
'@angular/private/testing': {main: 'index.js', defaultExtension: 'js'},

0 commit comments

Comments
 (0)