This repository was archived by the owner on Jun 3, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 239
Expand file tree
/
Copy pathServerHTML.js
More file actions
149 lines (131 loc) · 4.42 KB
/
ServerHTML.js
File metadata and controls
149 lines (131 loc) · 4.42 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
/**
* This module is responsible for generating the HTML page response for
* the react application middleware.
*/
/* eslint-disable react/no-danger */
/* eslint-disable react/no-array-index-key */
import React, { Children } from 'react';
import PropTypes from 'prop-types';
import serialize from 'serialize-javascript';
import config from '../../../config';
import ifElse from '../../../shared/utils/logic/ifElse';
import removeNil from '../../../shared/utils/arrays/removeNil';
import getClientBundleEntryAssets from './getClientBundleEntryAssets';
import ClientConfig from '../../../config/components/ClientConfig';
import HTML from '../../../shared/components/HTML';
// PRIVATES
function KeyedComponent({ children }) {
return Children.only(children);
}
// Resolve the assets (js/css) for the client bundle's entry chunk.
const clientEntryAssets = getClientBundleEntryAssets();
function stylesheetTag(stylesheetFilePath) {
return (
<link
href={stylesheetFilePath}
media="screen, projection"
rel="stylesheet"
type="text/css"
/>
);
}
function scriptTag(jsFilePath) {
return <script type="text/javascript" src={jsFilePath} />;
}
// COMPONENT
function ServerHTML(props) {
const { asyncComponentsState, helmet, nonce, children } = props;
// Creates an inline script definition that is protected by the nonce.
const inlineScript = body => (
<script
nonce={nonce}
type="text/javascript"
dangerouslySetInnerHTML={{ __html: body }}
/>
);
const headerElements = removeNil([
...ifElse(helmet)(() => helmet.meta.toComponent(), []),
...ifElse(helmet)(() => helmet.title.toComponent(), []),
...ifElse(helmet)(() => helmet.base.toComponent(), []),
...ifElse(helmet)(() => helmet.link.toComponent(), []),
ifElse(clientEntryAssets && clientEntryAssets.css)(() =>
stylesheetTag(clientEntryAssets.css),
),
...ifElse(helmet)(() => helmet.style.toComponent(), []),
]);
const bodyElements = removeNil([
// Binds the client configuration object to the window object so
// that we can safely expose some configuration values to the
// client bundle that gets executed in the browser.
<ClientConfig nonce={nonce} />,
// Bind our async components state so the client knows which ones
// to initialise so that the checksum matches the server response.
// @see https://github.com/ctrlplusb/react-async-component
ifElse(asyncComponentsState)(() =>
inlineScript(
`window.__ASYNC_COMPONENTS_REHYDRATE_STATE__=${serialize(
asyncComponentsState,
)};`,
),
),
// Enable the polyfill io script?
// This can't be configured within a react-helmet component as we
// may need the polyfill's before our client JS gets parsed.
ifElse(config('polyfillIO.enabled'))(() =>
scriptTag(
`${config('polyfillIO.url')}?features=${config(
'polyfillIO.features',
).join(',')}`,
),
),
// When we are in development mode our development server will
// generate a vendor DLL in order to dramatically reduce our
// compilation times. Therefore we need to inject the path to the
// vendor dll bundle below.
ifElse(
process.env.BUILD_FLAG_IS_DEV === 'true' &&
config('bundles.client.devVendorDLL.enabled'),
)(() =>
scriptTag(
`${config('bundles.client.webPath')}${config(
'bundles.client.devVendorDLL.name',
)}.js?t=${Date.now()}`,
),
),
ifElse(clientEntryAssets && clientEntryAssets.js)(() =>
scriptTag(clientEntryAssets.js),
),
...ifElse(helmet)(() => helmet.script.toComponent(), []),
]);
return (
<HTML
htmlAttributes={ifElse(helmet)(
() => helmet.htmlAttributes.toComponent(),
null,
)}
headerElements={headerElements.map((x, idx) => (
<KeyedComponent key={idx}>{x}</KeyedComponent>
))}
bodyElements={bodyElements.map((x, idx) => (
<KeyedComponent key={idx}>{x}</KeyedComponent>
))}
>
{children}
</HTML>
);
}
export default ServerHTML;
ServerHTML.propTypes = {
// eslint-disable-next-line react/forbid-prop-types
asyncComponentsState: PropTypes.object,
// eslint-disable-next-line react/forbid-prop-types
helmet: PropTypes.object,
nonce: PropTypes.string,
children: PropTypes.node,
};
ServerHTML.defaultProps = {
asyncComponentsState: undefined,
helmet: undefined,
nonce: undefined,
children: null,
};