@dr.pogodin/react-utils 1.21.0 → 1.21.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/development/client/index.js +0 -7
- package/build/development/client/index.js.map +1 -1
- package/build/development/client/init.js +3 -17
- package/build/development/client/init.js.map +1 -1
- package/build/development/index.js +0 -13
- package/build/development/index.js.map +1 -1
- package/build/development/server/Cache.js +2 -9
- package/build/development/server/Cache.js.map +1 -1
- package/build/development/server/index.js +7 -32
- package/build/development/server/index.js.map +1 -1
- package/build/development/server/renderer.js +24 -80
- package/build/development/server/renderer.js.map +1 -1
- package/build/development/server/server.js +21 -61
- package/build/development/server/server.js.map +1 -1
- package/build/development/server/utils/errors.js +2 -9
- package/build/development/server/utils/errors.js.map +1 -1
- package/build/development/server/utils/index.js +0 -4
- package/build/development/shared/components/Button/index.js +2 -13
- package/build/development/shared/components/Button/index.js.map +1 -1
- package/build/development/shared/components/Checkbox/index.js +2 -9
- package/build/development/shared/components/Checkbox/index.js.map +1 -1
- package/build/development/shared/components/CodeSplit/index.js +11 -22
- package/build/development/shared/components/CodeSplit/index.js.map +1 -1
- package/build/development/shared/components/Dropdown/index.js +0 -11
- package/build/development/shared/components/Dropdown/index.js.map +1 -1
- package/build/development/shared/components/GenericLink/index.js +3 -9
- package/build/development/shared/components/GenericLink/index.js.map +1 -1
- package/build/development/shared/components/Input/index.js +0 -7
- package/build/development/shared/components/Input/index.js.map +1 -1
- package/build/development/shared/components/Link.js +3 -6
- package/build/development/shared/components/Link.js.map +1 -1
- package/build/development/shared/components/MetaTags.js +1 -8
- package/build/development/shared/components/MetaTags.js.map +1 -1
- package/build/development/shared/components/Modal/index.js +2 -19
- package/build/development/shared/components/Modal/index.js.map +1 -1
- package/build/development/shared/components/NavLink.js +2 -6
- package/build/development/shared/components/NavLink.js.map +1 -1
- package/build/development/shared/components/PageLayout/index.js +0 -7
- package/build/development/shared/components/PageLayout/index.js.map +1 -1
- package/build/development/shared/components/ScalableRect/index.js +1 -6
- package/build/development/shared/components/ScalableRect/index.js.map +1 -1
- package/build/development/shared/components/Throbber/index.js +0 -9
- package/build/development/shared/components/Throbber/index.js.map +1 -1
- package/build/development/shared/components/WithTooltip/Tooltip.js +9 -28
- package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
- package/build/development/shared/components/WithTooltip/index.js +0 -16
- package/build/development/shared/components/WithTooltip/index.js.map +1 -1
- package/build/development/shared/components/YouTubeVideo/index.js +3 -11
- package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
- package/build/development/shared/components/index.js +0 -17
- package/build/development/shared/components/index.js.map +1 -1
- package/build/development/shared/utils/Barrier.js +1 -13
- package/build/development/shared/utils/Barrier.js.map +1 -1
- package/build/development/shared/utils/Emitter.js +4 -13
- package/build/development/shared/utils/Emitter.js.map +1 -1
- package/build/development/shared/utils/Semaphore.js +13 -19
- package/build/development/shared/utils/Semaphore.js.map +1 -1
- package/build/development/shared/utils/config.js +0 -5
- package/build/development/shared/utils/config.js.map +1 -1
- package/build/development/shared/utils/index.js +5 -26
- package/build/development/shared/utils/index.js.map +1 -1
- package/build/development/shared/utils/isomorphy.js +6 -12
- package/build/development/shared/utils/isomorphy.js.map +1 -1
- package/build/development/shared/utils/jest/E2eSsrEnv.js +20 -41
- package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
- package/build/development/shared/utils/jest/index.js +10 -31
- package/build/development/shared/utils/jest/index.js.map +1 -1
- package/build/development/shared/utils/splitComponent.js +2 -5
- package/build/development/shared/utils/splitComponent.js.map +1 -1
- package/build/development/shared/utils/time.js +6 -15
- package/build/development/shared/utils/time.js.map +1 -1
- package/build/development/shared/utils/webpack.js +1 -5
- package/build/development/shared/utils/webpack.js.map +1 -1
- package/build/development/style.css +5 -24
- package/build/development/web.bundle.js +27 -27
- package/build/production/client/index.js.map +1 -1
- package/build/production/client/init.js.map +1 -1
- package/build/production/index.js.map +1 -1
- package/build/production/server/Cache.js.map +1 -1
- package/build/production/server/index.js.map +1 -1
- package/build/production/server/renderer.js.map +1 -1
- package/build/production/server/server.js.map +1 -1
- package/build/production/server/utils/errors.js.map +1 -1
- package/build/production/shared/components/Button/index.js.map +1 -1
- package/build/production/shared/components/Checkbox/index.js.map +1 -1
- package/build/production/shared/components/CodeSplit/index.js.map +1 -1
- package/build/production/shared/components/Dropdown/index.js.map +1 -1
- package/build/production/shared/components/GenericLink/index.js.map +1 -1
- package/build/production/shared/components/Input/index.js.map +1 -1
- package/build/production/shared/components/Link.js.map +1 -1
- package/build/production/shared/components/MetaTags.js.map +1 -1
- package/build/production/shared/components/Modal/index.js.map +1 -1
- package/build/production/shared/components/NavLink.js.map +1 -1
- package/build/production/shared/components/PageLayout/index.js.map +1 -1
- package/build/production/shared/components/ScalableRect/index.js.map +1 -1
- package/build/production/shared/components/Throbber/index.js.map +1 -1
- package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
- package/build/production/shared/components/WithTooltip/index.js.map +1 -1
- package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
- package/build/production/shared/components/index.js.map +1 -1
- package/build/production/shared/utils/Barrier.js.map +1 -1
- package/build/production/shared/utils/Emitter.js.map +1 -1
- package/build/production/shared/utils/Semaphore.js.map +1 -1
- package/build/production/shared/utils/config.js.map +1 -1
- package/build/production/shared/utils/index.js.map +1 -1
- package/build/production/shared/utils/isomorphy.js.map +1 -1
- package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
- package/build/production/shared/utils/jest/index.js.map +1 -1
- package/build/production/shared/utils/splitComponent.js.map +1 -1
- package/build/production/shared/utils/time.js.map +1 -1
- package/build/production/shared/utils/webpack.js.map +1 -1
- package/build/production/style.css +1 -1
- package/build/production/style.css.map +1 -1
- package/build/production/web.bundle.js.map +1 -1
- package/config/babel/node-ssr.js +1 -1
- package/package.json +26 -26
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
|
|
5
4
|
Object.defineProperty(exports, "__esModule", {
|
|
6
5
|
value: true
|
|
7
6
|
});
|
|
@@ -9,48 +8,33 @@ exports.SCRIPT_LOCATIONS = void 0;
|
|
|
9
8
|
exports.default = factory;
|
|
10
9
|
exports.isBrotliAcceptable = isBrotliAcceptable;
|
|
11
10
|
exports.newDefaultLogger = newDefaultLogger;
|
|
12
|
-
|
|
13
11
|
var _stream = require("stream");
|
|
14
|
-
|
|
15
12
|
var _reactGlobalState = require("@dr.pogodin/react-global-state");
|
|
16
|
-
|
|
17
13
|
var _lodash = require("lodash");
|
|
18
|
-
|
|
19
14
|
var _config = _interopRequireDefault(require("config"));
|
|
20
|
-
|
|
21
15
|
var _nodeForge = _interopRequireDefault(require("node-forge"));
|
|
22
|
-
|
|
23
16
|
var _fs = _interopRequireDefault(require("fs"));
|
|
24
|
-
|
|
25
17
|
var _path = _interopRequireDefault(require("path"));
|
|
26
|
-
|
|
27
18
|
var _zlib = require("zlib");
|
|
28
|
-
|
|
29
19
|
var _server = require("react-dom/server");
|
|
30
|
-
|
|
31
20
|
var _reactHelmet = require("react-helmet");
|
|
32
|
-
|
|
33
21
|
var _server2 = require("react-router-dom/server");
|
|
34
|
-
|
|
35
22
|
var _serializeJavascript = _interopRequireDefault(require("serialize-javascript"));
|
|
36
|
-
|
|
37
23
|
var _time = require("../shared/utils/time");
|
|
38
|
-
|
|
39
24
|
var _winston = _interopRequireDefault(require("winston"));
|
|
40
|
-
|
|
41
25
|
var _Cache = _interopRequireDefault(require("./Cache"));
|
|
42
|
-
|
|
43
26
|
var _jsxRuntime = require("react/jsx-runtime");
|
|
44
|
-
|
|
45
27
|
/**
|
|
46
28
|
* ExpressJS middleware for server-side rendering of a ReactJS app.
|
|
47
29
|
*/
|
|
30
|
+
|
|
48
31
|
const sanitizedConfig = (0, _lodash.omit)(_config.default, 'SECRET');
|
|
49
32
|
const SCRIPT_LOCATIONS = {
|
|
50
33
|
BODY_OPEN: 'BODY_OPEN',
|
|
51
34
|
DEFAULT: 'DEFAULT',
|
|
52
35
|
HEAD_OPEN: 'HEAD_OPEN'
|
|
53
36
|
};
|
|
37
|
+
|
|
54
38
|
/**
|
|
55
39
|
* Reads build-time information about the app. This information is generated
|
|
56
40
|
* by our standard Webpack config for apps, and it is written into
|
|
@@ -61,14 +45,12 @@ const SCRIPT_LOCATIONS = {
|
|
|
61
45
|
* @param {String} context Webpack context path used during the build.
|
|
62
46
|
* @return {Object} Resolves to the build-time information.
|
|
63
47
|
*/
|
|
64
|
-
|
|
65
48
|
exports.SCRIPT_LOCATIONS = SCRIPT_LOCATIONS;
|
|
66
|
-
|
|
67
49
|
function getBuildInfo(context) {
|
|
68
50
|
const url = _path.default.resolve(context, '.build-info');
|
|
69
|
-
|
|
70
51
|
return JSON.parse(_fs.default.readFileSync(url));
|
|
71
52
|
}
|
|
53
|
+
|
|
72
54
|
/**
|
|
73
55
|
* Attempts to read from disk the named chunk groups mapping generated
|
|
74
56
|
* by Webpack during the compilation.
|
|
@@ -78,21 +60,17 @@ function getBuildInfo(context) {
|
|
|
78
60
|
* @param {string} buildDir
|
|
79
61
|
* @return {object}
|
|
80
62
|
*/
|
|
81
|
-
|
|
82
|
-
|
|
83
63
|
function readChunkGroupsJson(buildDir) {
|
|
84
64
|
const url = _path.default.resolve(buildDir, '__chunk_groups__.json');
|
|
85
|
-
|
|
86
65
|
let res;
|
|
87
|
-
|
|
88
66
|
try {
|
|
89
67
|
res = JSON.parse(_fs.default.readFileSync(url));
|
|
90
68
|
} catch (err) {
|
|
91
69
|
res = null;
|
|
92
70
|
}
|
|
93
|
-
|
|
94
71
|
return res;
|
|
95
72
|
}
|
|
73
|
+
|
|
96
74
|
/**
|
|
97
75
|
* Prepares a new Cipher for data encryption.
|
|
98
76
|
* @ignore
|
|
@@ -102,14 +80,11 @@ function readChunkGroupsJson(buildDir) {
|
|
|
102
80
|
* 1. cipher - a new Cipher, ready for encryption;
|
|
103
81
|
* 2. iv - initial vector used by the cipher.
|
|
104
82
|
*/
|
|
105
|
-
|
|
106
|
-
|
|
107
83
|
function prepareCipher(key) {
|
|
108
84
|
return new Promise((resolve, reject) => {
|
|
109
85
|
_nodeForge.default.random.getBytes(32, (err, iv) => {
|
|
110
86
|
if (err) reject(err);else {
|
|
111
87
|
const cipher = _nodeForge.default.cipher.createCipher('AES-CBC', key);
|
|
112
|
-
|
|
113
88
|
cipher.start({
|
|
114
89
|
iv
|
|
115
90
|
});
|
|
@@ -121,6 +96,7 @@ function prepareCipher(key) {
|
|
|
121
96
|
});
|
|
122
97
|
});
|
|
123
98
|
}
|
|
99
|
+
|
|
124
100
|
/**
|
|
125
101
|
* Given an incoming HTTP requests, it deduces whether Brotli-encoded responses
|
|
126
102
|
* are acceptable to the caller.
|
|
@@ -128,25 +104,20 @@ function prepareCipher(key) {
|
|
|
128
104
|
* @return {boolean}
|
|
129
105
|
* @ignore
|
|
130
106
|
*/
|
|
131
|
-
|
|
132
|
-
|
|
133
107
|
function isBrotliAcceptable(req) {
|
|
134
108
|
const acceptable = req.get('accept-encoding');
|
|
135
|
-
|
|
136
109
|
if (acceptable) {
|
|
137
110
|
const ops = acceptable.split(',');
|
|
138
|
-
|
|
139
111
|
for (let i = 0; i < ops.length; ++i) {
|
|
140
112
|
const [type, priority] = ops[i].trim().split(';q=');
|
|
141
|
-
|
|
142
113
|
if ((type === '*' || type === 'br') && (!priority || parseFloat(priority) > 0)) {
|
|
143
114
|
return true;
|
|
144
115
|
}
|
|
145
116
|
}
|
|
146
117
|
}
|
|
147
|
-
|
|
148
118
|
return false;
|
|
149
119
|
}
|
|
120
|
+
|
|
150
121
|
/**
|
|
151
122
|
* Given an array of extra script strings / objects, it returns an object with
|
|
152
123
|
* arrays of scripts to inject in different HTML template locations. During
|
|
@@ -161,18 +132,14 @@ function isBrotliAcceptable(req) {
|
|
|
161
132
|
* HEAD_OPEN: string[];
|
|
162
133
|
* }}
|
|
163
134
|
*/
|
|
164
|
-
|
|
165
|
-
|
|
166
135
|
function groupExtraScripts(scripts = []) {
|
|
167
136
|
const res = {
|
|
168
137
|
[SCRIPT_LOCATIONS.BODY_OPEN]: '',
|
|
169
138
|
[SCRIPT_LOCATIONS.DEFAULT]: '',
|
|
170
139
|
[SCRIPT_LOCATIONS.HEAD_OPEN]: ''
|
|
171
140
|
};
|
|
172
|
-
|
|
173
141
|
for (let i = 0; i < scripts.length; ++i) {
|
|
174
142
|
const script = scripts[i];
|
|
175
|
-
|
|
176
143
|
if ((0, _lodash.isString)(script)) {
|
|
177
144
|
if (script) res[SCRIPT_LOCATIONS.DEFAULT] += script;
|
|
178
145
|
} else if (script.code) {
|
|
@@ -181,17 +148,15 @@ function groupExtraScripts(scripts = []) {
|
|
|
181
148
|
} else throw Error(`Invalid location "${script.location}"`);
|
|
182
149
|
}
|
|
183
150
|
}
|
|
184
|
-
|
|
185
151
|
return res;
|
|
186
152
|
}
|
|
153
|
+
|
|
187
154
|
/**
|
|
188
155
|
* Creates a new default (Winston) logger.
|
|
189
156
|
* @param {object} [options={}]
|
|
190
157
|
* @param {string} [options.defaultLogLevel='info']
|
|
191
158
|
* @return {object}
|
|
192
159
|
*/
|
|
193
|
-
|
|
194
|
-
|
|
195
160
|
function newDefaultLogger({
|
|
196
161
|
defaultLogLevel = 'info'
|
|
197
162
|
} = {}) {
|
|
@@ -209,17 +174,16 @@ function newDefaultLogger({
|
|
|
209
174
|
...rest
|
|
210
175
|
}) => {
|
|
211
176
|
let res = `${level}\t(at ${timestamp}):\t${message}`;
|
|
212
|
-
|
|
213
177
|
if (Object.keys(rest).length) {
|
|
214
178
|
res += `\n${JSON.stringify(rest, null, 2)}`;
|
|
215
179
|
}
|
|
216
|
-
|
|
217
180
|
if (stack) res += `\n${stack}`;
|
|
218
181
|
return res;
|
|
219
182
|
})),
|
|
220
183
|
transports: [new transports.Console()]
|
|
221
184
|
});
|
|
222
185
|
}
|
|
186
|
+
|
|
223
187
|
/**
|
|
224
188
|
* Creates the middleware.
|
|
225
189
|
* @param {object} webpackConfig
|
|
@@ -251,27 +215,26 @@ function newDefaultLogger({
|
|
|
251
215
|
* If undefined - infinite age is assumed.
|
|
252
216
|
* @return {function} Created middleware.
|
|
253
217
|
*/
|
|
254
|
-
|
|
255
|
-
|
|
256
218
|
function factory(webpackConfig, options) {
|
|
257
219
|
const ops = (0, _lodash.defaults)((0, _lodash.clone)(options), {
|
|
258
220
|
beforeRender: () => Promise.resolve({}),
|
|
259
221
|
maxSsrRounds: 10,
|
|
260
222
|
ssrTimeout: 1000,
|
|
261
223
|
staticCacheSize: 1.e7
|
|
262
|
-
});
|
|
224
|
+
});
|
|
225
|
+
|
|
226
|
+
// Note: in normal use the default logger is created and set in the root
|
|
263
227
|
// server function, and this initialization is for testing uses, where
|
|
264
228
|
// renderer is imported directly.
|
|
265
|
-
|
|
266
229
|
if (ops.logger === undefined) {
|
|
267
230
|
ops.logger = newDefaultLogger({
|
|
268
231
|
defaultLogLevel: ops.defaultLoggerLogLevel
|
|
269
232
|
});
|
|
270
233
|
}
|
|
271
|
-
|
|
272
234
|
const buildInfo = ops.buildInfo || getBuildInfo(webpackConfig.context);
|
|
273
|
-
global.TRU_BUILD_INFO = buildInfo;
|
|
235
|
+
global.TRU_BUILD_INFO = buildInfo;
|
|
274
236
|
|
|
237
|
+
// publicPath from webpack.output has a trailing slash at the end.
|
|
275
238
|
const {
|
|
276
239
|
publicPath,
|
|
277
240
|
path: outputPath
|
|
@@ -285,19 +248,15 @@ function factory(webpackConfig, options) {
|
|
|
285
248
|
res.set('Cache-Control', 'no-cache');
|
|
286
249
|
res.cookie('csrfToken', req.csrfToken());
|
|
287
250
|
let cacheRef;
|
|
288
|
-
|
|
289
251
|
if (cache) {
|
|
290
252
|
cacheRef = ops.staticCacheController(req);
|
|
291
|
-
|
|
292
253
|
if (cacheRef) {
|
|
293
254
|
const data = cache.get(cacheRef);
|
|
294
|
-
|
|
295
255
|
if (data !== null) {
|
|
296
256
|
const {
|
|
297
257
|
buffer,
|
|
298
258
|
status
|
|
299
259
|
} = data;
|
|
300
|
-
|
|
301
260
|
if (ops.noCsp && isBrotliAcceptable(req)) {
|
|
302
261
|
res.set('Content-Type', 'text/html');
|
|
303
262
|
res.set('Content-Encoding', 'br');
|
|
@@ -308,7 +267,6 @@ function factory(webpackConfig, options) {
|
|
|
308
267
|
(0, _zlib.brotliDecompress)(buffer, (error, html) => {
|
|
309
268
|
if (error) failed(error);else {
|
|
310
269
|
let h = html.toString();
|
|
311
|
-
|
|
312
270
|
if (!ops.noCsp) {
|
|
313
271
|
// TODO: Starting from Node v15 we'll be able to use string's
|
|
314
272
|
// .replaceAll() method instead relying on reg. expression for
|
|
@@ -316,7 +274,6 @@ function factory(webpackConfig, options) {
|
|
|
316
274
|
const regex = new RegExp(buffer.nonce, 'g');
|
|
317
275
|
h = h.replace(regex, req.nonce);
|
|
318
276
|
}
|
|
319
|
-
|
|
320
277
|
if (status !== 200) res.status(status);
|
|
321
278
|
res.send(h);
|
|
322
279
|
done();
|
|
@@ -324,12 +281,10 @@ function factory(webpackConfig, options) {
|
|
|
324
281
|
});
|
|
325
282
|
});
|
|
326
283
|
}
|
|
327
|
-
|
|
328
284
|
return;
|
|
329
285
|
}
|
|
330
286
|
}
|
|
331
287
|
}
|
|
332
|
-
|
|
333
288
|
const [{
|
|
334
289
|
configToInject,
|
|
335
290
|
extraScripts,
|
|
@@ -339,8 +294,8 @@ function factory(webpackConfig, options) {
|
|
|
339
294
|
iv
|
|
340
295
|
}] = await Promise.all([ops.beforeRender(req, sanitizedConfig), prepareCipher(buildInfo.key)]);
|
|
341
296
|
let helmet;
|
|
342
|
-
/* Optional server-side rendering. */
|
|
343
297
|
|
|
298
|
+
/* Optional server-side rendering. */
|
|
344
299
|
let App = ops.Application;
|
|
345
300
|
const ssrContext = {
|
|
346
301
|
req,
|
|
@@ -349,10 +304,8 @@ function factory(webpackConfig, options) {
|
|
|
349
304
|
chunks: []
|
|
350
305
|
};
|
|
351
306
|
let stream;
|
|
352
|
-
|
|
353
307
|
if (App) {
|
|
354
308
|
const ssrStart = Date.now();
|
|
355
|
-
|
|
356
309
|
const renderPass = async () => {
|
|
357
310
|
ssrContext.chunks = [];
|
|
358
311
|
return new Promise((resolve, reject) => {
|
|
@@ -369,16 +322,14 @@ function factory(webpackConfig, options) {
|
|
|
369
322
|
});
|
|
370
323
|
});
|
|
371
324
|
};
|
|
372
|
-
|
|
373
325
|
let ssrRound = 0;
|
|
374
326
|
let bailed = false;
|
|
375
|
-
|
|
376
327
|
for (; ssrRound < ops.maxSsrRounds; ++ssrRound) {
|
|
377
328
|
stream = await renderPass(); // eslint-disable-line no-await-in-loop
|
|
378
329
|
|
|
379
330
|
if (!ssrContext.dirty) break;
|
|
380
|
-
/* eslint-disable no-await-in-loop */
|
|
381
331
|
|
|
332
|
+
/* eslint-disable no-await-in-loop */
|
|
382
333
|
const timeout = ops.ssrTimeout + ssrStart - Date.now();
|
|
383
334
|
bailed = timeout <= 0 || !(await Promise.race([Promise.allSettled(ssrContext.pending), (0, _time.timer)(timeout).then(() => false)]));
|
|
384
335
|
if (bailed) break;
|
|
@@ -386,7 +337,6 @@ function factory(webpackConfig, options) {
|
|
|
386
337
|
}
|
|
387
338
|
|
|
388
339
|
let logMsg;
|
|
389
|
-
|
|
390
340
|
if (ssrContext.dirty) {
|
|
391
341
|
// NOTE: In the case of incomplete SSR one more round is necessary
|
|
392
342
|
// to ensure the correct hydration when some pending promises have
|
|
@@ -394,7 +344,6 @@ function factory(webpackConfig, options) {
|
|
|
394
344
|
stream = await renderPass();
|
|
395
345
|
logMsg = bailed ? `SSR timed out after ${ops.ssrTimeout} second(s)` : `SSR bailed out after ${ops.maxSsrRounds} round(s)`;
|
|
396
346
|
} else logMsg = `SSR completed in ${ssrRound + 1} round(s)`;
|
|
397
|
-
|
|
398
347
|
ops.logger.log(ssrContext.dirty ? 'warn' : 'info', logMsg);
|
|
399
348
|
App = '';
|
|
400
349
|
stream.pipe(new _stream.Writable({
|
|
@@ -403,15 +352,13 @@ function factory(webpackConfig, options) {
|
|
|
403
352
|
done();
|
|
404
353
|
}
|
|
405
354
|
}));
|
|
355
|
+
|
|
406
356
|
/* This takes care about server-side rendering of page title and meta tags
|
|
407
357
|
* (still demands injection into HTML template, which happens below). */
|
|
408
|
-
|
|
409
358
|
helmet = _reactHelmet.Helmet.renderStatic();
|
|
410
359
|
}
|
|
411
|
-
|
|
412
360
|
let chunkGroups;
|
|
413
361
|
const webpackStats = (0, _lodash.get)(res.locals, 'webpack.devMiddleware.stats');
|
|
414
|
-
|
|
415
362
|
if (webpackStats) {
|
|
416
363
|
chunkGroups = (0, _lodash.mapValues)(webpackStats.toJson({
|
|
417
364
|
all: false,
|
|
@@ -420,13 +367,12 @@ function factory(webpackConfig, options) {
|
|
|
420
367
|
name
|
|
421
368
|
}) => name));
|
|
422
369
|
} else if (CHUNK_GROUPS) chunkGroups = CHUNK_GROUPS;else chunkGroups = {};
|
|
370
|
+
|
|
423
371
|
/* Encrypts data to be injected into HTML.
|
|
424
372
|
* Keep in mind, that this encryption is no way secure: as the JS bundle
|
|
425
373
|
* contains decryption key and is able to decode it at the client side.
|
|
426
374
|
* Hovewer, for a number of reasons, encryption of injected data is still
|
|
427
375
|
* better than injection of a plain text. */
|
|
428
|
-
|
|
429
|
-
|
|
430
376
|
delete ssrContext.state.dr_pogodin_react_utils___split_components;
|
|
431
377
|
const payload = (0, _serializeJavascript.default)({
|
|
432
378
|
CHUNK_GROUPS: chunkGroups,
|
|
@@ -438,10 +384,10 @@ function factory(webpackConfig, options) {
|
|
|
438
384
|
});
|
|
439
385
|
cipher.update(_nodeForge.default.util.createBuffer(payload, 'utf8'));
|
|
440
386
|
cipher.finish();
|
|
441
|
-
|
|
442
387
|
const INJ = _nodeForge.default.util.encode64(`${iv}${cipher.output.data}`);
|
|
388
|
+
const chunkSet = new Set();
|
|
443
389
|
|
|
444
|
-
|
|
390
|
+
// TODO: "main" chunk has to be added explicitly,
|
|
445
391
|
// because unlike all other chunks they are not managed by <CodeSplit>
|
|
446
392
|
// component, thus they are not added to the ssrContext.chunks
|
|
447
393
|
// automatically. Actually, names of these entry chunks should be
|
|
@@ -449,7 +395,6 @@ function factory(webpackConfig, options) {
|
|
|
449
395
|
// remove or add other entry points, but it requires additional
|
|
450
396
|
// efforts to figure out how to automatically order them right,
|
|
451
397
|
// thus for now this handles the default config.
|
|
452
|
-
|
|
453
398
|
['main', ...ssrContext.chunks].forEach(chunk => {
|
|
454
399
|
const assets = chunkGroups[chunk];
|
|
455
400
|
if (assets) assets.forEach(asset => chunkSet.add(asset));
|
|
@@ -459,7 +404,8 @@ function factory(webpackConfig, options) {
|
|
|
459
404
|
chunkSet.forEach(chunk => {
|
|
460
405
|
if (chunk.endsWith('.css')) {
|
|
461
406
|
styleChunkString += `<link href="${publicPath}${chunk}" rel="stylesheet">`;
|
|
462
|
-
} else if (chunk.endsWith('.js')
|
|
407
|
+
} else if (chunk.endsWith('.js')
|
|
408
|
+
// In dev mode HMR adds JS updates into asset arrays,
|
|
463
409
|
// and they (updates) should be ignored.
|
|
464
410
|
&& !chunk.endsWith('.hot-update.js')) {
|
|
465
411
|
scriptChunkString += `<script src="${publicPath}${chunk}" type="application/javascript"></script>`;
|
|
@@ -499,7 +445,6 @@ function factory(webpackConfig, options) {
|
|
|
499
445
|
</html>`;
|
|
500
446
|
const status = ssrContext.status || 200;
|
|
501
447
|
if (status !== 200) res.status(status);
|
|
502
|
-
|
|
503
448
|
if (cacheRef && status < 500) {
|
|
504
449
|
// Note: waiting for the caching to complete is not strictly necessary,
|
|
505
450
|
// but it greately simplifies testing, and error reporting.
|
|
@@ -507,7 +452,6 @@ function factory(webpackConfig, options) {
|
|
|
507
452
|
(0, _zlib.brotliCompress)(html, (error, buffer) => {
|
|
508
453
|
if (error) failed(error);else {
|
|
509
454
|
buffer.nonce = req.nonce; // eslint-disable-line no-param-reassign
|
|
510
|
-
|
|
511
455
|
cache.add({
|
|
512
456
|
buffer,
|
|
513
457
|
status
|
|
@@ -516,11 +460,11 @@ function factory(webpackConfig, options) {
|
|
|
516
460
|
}
|
|
517
461
|
});
|
|
518
462
|
});
|
|
519
|
-
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// Note: as caching code above may throw in some cases, sending response
|
|
520
466
|
// before it completes will likely hide the error, making it difficult
|
|
521
467
|
// to debug. Thus, at least for now, lets send response after it.
|
|
522
|
-
|
|
523
|
-
|
|
524
468
|
res.send(html);
|
|
525
469
|
} catch (error) {
|
|
526
470
|
next(error);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"renderer.js","names":["sanitizedConfig","omit","config","SCRIPT_LOCATIONS","BODY_OPEN","DEFAULT","HEAD_OPEN","getBuildInfo","context","url","path","resolve","JSON","parse","fs","readFileSync","readChunkGroupsJson","buildDir","res","err","prepareCipher","key","Promise","reject","forge","random","getBytes","iv","cipher","createCipher","start","isBrotliAcceptable","req","acceptable","get","ops","split","i","length","type","priority","trim","parseFloat","groupExtraScripts","scripts","script","isString","code","location","undefined","Error","newDefaultLogger","defaultLogLevel","format","transports","winston","createLogger","level","combine","splat","timestamp","colorize","printf","message","stack","rest","Object","keys","stringify","Console","factory","webpackConfig","options","defaults","clone","beforeRender","maxSsrRounds","ssrTimeout","staticCacheSize","logger","defaultLoggerLogLevel","buildInfo","global","TRU_BUILD_INFO","publicPath","outputPath","output","manifestLink","existsSync","cache","staticCacheController","Cache","CHUNK_GROUPS","next","set","cookie","csrfToken","cacheRef","data","buffer","status","noCsp","send","done","failed","brotliDecompress","error","html","h","toString","regex","RegExp","nonce","replace","configToInject","extraScripts","initialState","all","helmet","App","Application","ssrContext","state","cloneDeep","chunks","stream","ssrStart","Date","now","renderPass","pipeableStream","renderToPipeableStream","onAllReady","onError","ssrRound","bailed","dirty","timeout","race","allSettled","pending","timer","then","logMsg","log","pipe","Writable","write","chunk","_","Helmet","renderStatic","chunkGroups","webpackStats","locals","mapValues","toJson","namedChunkGroups","item","assets","map","name","dr_pogodin_react_utils___split_components","payload","serializeJs","CONFIG","ISTATE","ignoreFunction","unsafe","update","util","createBuffer","finish","INJ","encode64","chunkSet","Set","forEach","asset","add","styleChunkString","scriptChunkString","endsWith","grouppedExtraScripts","faviconLink","favicon","title","meta","brotliCompress"],"sources":["../../../src/server/renderer.jsx"],"sourcesContent":["/**\n * ExpressJS middleware for server-side rendering of a ReactJS app.\n */\n\nimport { Writable } from 'stream';\n\nimport { GlobalStateProvider } from '@dr.pogodin/react-global-state';\n\nimport {\n clone,\n cloneDeep,\n defaults,\n isString,\n get,\n mapValues,\n omit,\n} from 'lodash';\n\nimport config from 'config';\nimport forge from 'node-forge';\nimport fs from 'fs';\nimport path from 'path';\nimport { brotliCompress, brotliDecompress } from 'zlib';\n\nimport { renderToPipeableStream } from 'react-dom/server';\nimport { Helmet } from 'react-helmet';\nimport { StaticRouter } from 'react-router-dom/server';\nimport serializeJs from 'serialize-javascript';\nimport { timer } from 'utils/time';\nimport winston from 'winston';\n\nimport Cache from './Cache';\n\nconst sanitizedConfig = omit(config, 'SECRET');\n\nexport const SCRIPT_LOCATIONS = {\n BODY_OPEN: 'BODY_OPEN',\n DEFAULT: 'DEFAULT',\n HEAD_OPEN: 'HEAD_OPEN',\n};\n\n/**\n * Reads build-time information about the app. This information is generated\n * by our standard Webpack config for apps, and it is written into\n * \".build-info\" file in the context folder specified in Webpack config.\n * At the moment, that file contains build timestamp and a random 32-bit key,\n * suitable for cryptographical use.\n * @ignore\n * @param {String} context Webpack context path used during the build.\n * @return {Object} Resolves to the build-time information.\n */\nfunction getBuildInfo(context) {\n const url = path.resolve(context, '.build-info');\n return JSON.parse(fs.readFileSync(url));\n}\n\n/**\n * Attempts to read from disk the named chunk groups mapping generated\n * by Webpack during the compilation.\n * It will not work for development builds, where these stats should be captured\n * via compilator callback.\n * @ignore\n * @param {string} buildDir\n * @return {object}\n */\nfunction readChunkGroupsJson(buildDir) {\n const url = path.resolve(buildDir, '__chunk_groups__.json');\n let res;\n try {\n res = JSON.parse(fs.readFileSync(url));\n } catch (err) {\n res = null;\n }\n return res;\n}\n\n/**\n * Prepares a new Cipher for data encryption.\n * @ignore\n * @param {String} key Encryption key (32-bit random key is expected, see\n * node-forge documentation, in case of doubts).\n * @return {Promise} Resolves to the object with two fields:\n * 1. cipher - a new Cipher, ready for encryption;\n * 2. iv - initial vector used by the cipher.\n */\nfunction prepareCipher(key) {\n return new Promise((resolve, reject) => {\n forge.random.getBytes(32, (err, iv) => {\n if (err) reject(err);\n else {\n const cipher = forge.cipher.createCipher('AES-CBC', key);\n cipher.start({ iv });\n resolve({ cipher, iv });\n }\n });\n });\n}\n\n/**\n * Given an incoming HTTP requests, it deduces whether Brotli-encoded responses\n * are acceptable to the caller.\n * @param {object} req\n * @return {boolean}\n * @ignore\n */\nexport function isBrotliAcceptable(req) {\n const acceptable = req.get('accept-encoding');\n if (acceptable) {\n const ops = acceptable.split(',');\n for (let i = 0; i < ops.length; ++i) {\n const [type, priority] = ops[i].trim().split(';q=');\n if ((type === '*' || type === 'br')\n && (!priority || parseFloat(priority) > 0)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Given an array of extra script strings / objects, it returns an object with\n * arrays of scripts to inject in different HTML template locations. During\n * the script groupping it also filters out any empty scripts.\n * @param {({\n * code: string;\n * location: string;\n * }|string)[]} [scripts=[]]\n * @return {{\n * BODY_OPEN: string[];\n * DEFAULT: string[];\n * HEAD_OPEN: string[];\n * }}\n */\nfunction groupExtraScripts(scripts = []) {\n const res = {\n [SCRIPT_LOCATIONS.BODY_OPEN]: '',\n [SCRIPT_LOCATIONS.DEFAULT]: '',\n [SCRIPT_LOCATIONS.HEAD_OPEN]: '',\n };\n for (let i = 0; i < scripts.length; ++i) {\n const script = scripts[i];\n if (isString(script)) {\n if (script) res[SCRIPT_LOCATIONS.DEFAULT] += script;\n } else if (script.code) {\n if (res[script.location] !== undefined) {\n res[script.location] += script.code;\n } else throw Error(`Invalid location \"${script.location}\"`);\n }\n }\n return res;\n}\n\n/**\n * Creates a new default (Winston) logger.\n * @param {object} [options={}]\n * @param {string} [options.defaultLogLevel='info']\n * @return {object}\n */\nexport function newDefaultLogger({\n defaultLogLevel = 'info',\n} = {}) {\n const { format, transports } = winston;\n return winston.createLogger({\n level: defaultLogLevel,\n format: format.combine(\n format.splat(),\n format.timestamp(),\n format.colorize(),\n format.printf(\n ({\n level,\n message,\n timestamp,\n stack,\n ...rest\n }) => {\n let res = `${level}\\t(at ${timestamp}):\\t${message}`;\n if (Object.keys(rest).length) {\n res += `\\n${JSON.stringify(rest, null, 2)}`;\n }\n if (stack) res += `\\n${stack}`;\n return res;\n },\n ),\n ),\n transports: [new transports.Console()],\n });\n}\n\n/**\n * Creates the middleware.\n * @param {object} webpackConfig\n * @param {object} options Additional options:\n * @param {Component} [options.Application] The root ReactJS component of\n * the app to use for the server-side rendering. When not provided\n * the server-side rendering is disabled.\n * @param {object} [options.buildInfo] \"Build info\" object to use. If provided,\n * it will be used, instead of trying to load from the filesystem the one\n * generated by the Webpack build. It is intended for test environments,\n * where passing this stuff via file system is no bueno.\n * @param {boolean} [options.favicon] `true` will include favicon\n * link into the rendered HTML templates.\n * @param {boolean} [options.noCsp] `true` means that no\n * Content-Security-Policy (CSP) is used by server, thus the renderer\n * may cut a few corners.\n * @param {number} [options.maxSsrRounds=10] Maximum number of SSR rounds.\n * @param {number} [options.ssrTimeout=1000] SSR timeout in milliseconds,\n * defaults to 1 second.\n * @param {number} [options.staticCacheSize=1.e7] The maximum\n * static cache size in bytes. Defaults to ~10 MB.\n * @param {function} [options.staticCacheController] When given, it activates,\n * and controls the static caching of generated HTML markup. When this function\n * is provided, on each incoming request it is triggered with the request\n * passed in as the argument. To attempt to serve the response from the cache\n * it should return the object with the following fields:\n * - `key: string` – the cache key for the response;\n * - `maxage?: number` – the maximum age of cached result in ms.\n * If undefined - infinite age is assumed.\n * @return {function} Created middleware.\n */\nexport default function factory(webpackConfig, options) {\n const ops = defaults(clone(options), {\n beforeRender: () => Promise.resolve({}),\n maxSsrRounds: 10,\n ssrTimeout: 1000,\n staticCacheSize: 1.e7,\n });\n\n // Note: in normal use the default logger is created and set in the root\n // server function, and this initialization is for testing uses, where\n // renderer is imported directly.\n if (ops.logger === undefined) {\n ops.logger = newDefaultLogger({\n defaultLogLevel: ops.defaultLoggerLogLevel,\n });\n }\n\n const buildInfo = ops.buildInfo || getBuildInfo(webpackConfig.context);\n global.TRU_BUILD_INFO = buildInfo;\n\n // publicPath from webpack.output has a trailing slash at the end.\n const { publicPath, path: outputPath } = webpackConfig.output;\n\n const manifestLink = fs.existsSync(`${outputPath}/manifest.json`)\n ? `<link rel=\"manifest\" href=\"${publicPath}manifest.json\">` : '';\n\n const cache = ops.staticCacheController\n ? new Cache(ops.staticCacheSize) : null;\n\n const CHUNK_GROUPS = readChunkGroupsJson(outputPath);\n\n return async (req, res, next) => {\n try {\n // Ensures any caches always revalidate HTML markup before reuse.\n res.set('Cache-Control', 'no-cache');\n\n res.cookie('csrfToken', req.csrfToken());\n\n let cacheRef;\n if (cache) {\n cacheRef = ops.staticCacheController(req);\n if (cacheRef) {\n const data = cache.get(cacheRef);\n if (data !== null) {\n const { buffer, status } = data;\n if (ops.noCsp && isBrotliAcceptable(req)) {\n res.set('Content-Type', 'text/html');\n res.set('Content-Encoding', 'br');\n if (status !== 200) res.status(status);\n res.send(buffer);\n } else {\n await new Promise((done, failed) => {\n brotliDecompress(buffer, (error, html) => {\n if (error) failed(error);\n else {\n let h = html.toString();\n if (!ops.noCsp) {\n // TODO: Starting from Node v15 we'll be able to use string's\n // .replaceAll() method instead relying on reg. expression for\n // global matching.\n const regex = new RegExp(buffer.nonce, 'g');\n h = h.replace(regex, req.nonce);\n }\n if (status !== 200) res.status(status);\n res.send(h);\n done();\n }\n });\n });\n }\n return;\n }\n }\n }\n\n const [{\n configToInject,\n extraScripts,\n initialState,\n }, {\n cipher,\n iv,\n }] = await Promise.all([\n ops.beforeRender(req, sanitizedConfig),\n prepareCipher(buildInfo.key),\n ]);\n\n let helmet;\n\n /* Optional server-side rendering. */\n let App = ops.Application;\n const ssrContext = {\n req,\n state: cloneDeep(initialState || {}),\n\n // Array of chunk names encountered during the rendering.\n chunks: [],\n };\n let stream;\n if (App) {\n const ssrStart = Date.now();\n\n const renderPass = async () => {\n ssrContext.chunks = [];\n return new Promise((resolve, reject) => {\n const pipeableStream = renderToPipeableStream(\n <GlobalStateProvider\n initialState={ssrContext.state}\n ssrContext={ssrContext}\n >\n <StaticRouter location={req.url}>\n <App />\n </StaticRouter>\n </GlobalStateProvider>,\n {\n onAllReady: () => resolve(pipeableStream),\n onError: reject,\n },\n );\n });\n };\n\n let ssrRound = 0;\n let bailed = false;\n for (; ssrRound < ops.maxSsrRounds; ++ssrRound) {\n stream = await renderPass(); // eslint-disable-line no-await-in-loop\n\n if (!ssrContext.dirty) break;\n\n /* eslint-disable no-await-in-loop */\n const timeout = ops.ssrTimeout + ssrStart - Date.now();\n bailed = timeout <= 0 || !await Promise.race([\n Promise.allSettled(ssrContext.pending),\n timer(timeout).then(() => false),\n ]);\n if (bailed) break;\n /* eslint-enable no-await-in-loop */\n }\n\n let logMsg;\n if (ssrContext.dirty) {\n // NOTE: In the case of incomplete SSR one more round is necessary\n // to ensure the correct hydration when some pending promises have\n // resolved and placed their data into the initial global state.\n stream = await renderPass();\n\n logMsg = bailed ? `SSR timed out after ${ops.ssrTimeout} second(s)`\n : `SSR bailed out after ${ops.maxSsrRounds} round(s)`;\n } else logMsg = `SSR completed in ${ssrRound + 1} round(s)`;\n\n ops.logger.log(ssrContext.dirty ? 'warn' : 'info', logMsg);\n\n App = '';\n stream.pipe(new Writable({\n write: (chunk, _, done) => {\n App += chunk.toString();\n done();\n },\n }));\n\n /* This takes care about server-side rendering of page title and meta tags\n * (still demands injection into HTML template, which happens below). */\n helmet = Helmet.renderStatic();\n }\n\n let chunkGroups;\n const webpackStats = get(res.locals, 'webpack.devMiddleware.stats');\n if (webpackStats) {\n chunkGroups = mapValues(\n webpackStats.toJson({\n all: false,\n chunkGroups: true,\n }).namedChunkGroups,\n (item) => item.assets.map(({ name }) => name),\n );\n } else if (CHUNK_GROUPS) chunkGroups = CHUNK_GROUPS;\n else chunkGroups = {};\n\n /* Encrypts data to be injected into HTML.\n * Keep in mind, that this encryption is no way secure: as the JS bundle\n * contains decryption key and is able to decode it at the client side.\n * Hovewer, for a number of reasons, encryption of injected data is still\n * better than injection of a plain text. */\n delete ssrContext.state.dr_pogodin_react_utils___split_components;\n\n const payload = serializeJs({\n CHUNK_GROUPS: chunkGroups,\n CONFIG: configToInject || sanitizedConfig,\n ISTATE: ssrContext.state,\n }, {\n ignoreFunction: true,\n unsafe: true,\n });\n cipher.update(forge.util.createBuffer(payload, 'utf8'));\n cipher.finish();\n const INJ = forge.util.encode64(`${iv}${cipher.output.data}`);\n\n const chunkSet = new Set();\n\n // TODO: \"main\" chunk has to be added explicitly,\n // because unlike all other chunks they are not managed by <CodeSplit>\n // component, thus they are not added to the ssrContext.chunks\n // automatically. Actually, names of these entry chunks should be\n // read from Wepback config, as the end user may customize them,\n // remove or add other entry points, but it requires additional\n // efforts to figure out how to automatically order them right,\n // thus for now this handles the default config.\n [\n 'main',\n ...ssrContext.chunks,\n ].forEach((chunk) => {\n const assets = chunkGroups[chunk];\n if (assets) assets.forEach((asset) => chunkSet.add(asset));\n });\n\n let styleChunkString = '';\n let scriptChunkString = '';\n chunkSet.forEach((chunk) => {\n if (chunk.endsWith('.css')) {\n styleChunkString += `<link href=\"${publicPath}${chunk}\" rel=\"stylesheet\">`;\n } else if (\n chunk.endsWith('.js')\n // In dev mode HMR adds JS updates into asset arrays,\n // and they (updates) should be ignored.\n && !chunk.endsWith('.hot-update.js')\n ) {\n scriptChunkString += `<script src=\"${publicPath}${chunk}\" type=\"application/javascript\"></script>`;\n }\n });\n\n const grouppedExtraScripts = groupExtraScripts(extraScripts);\n\n const faviconLink = ops.favicon ? (\n '<link rel=\"shortcut icon\" href=\"/favicon.ico\">'\n ) : '';\n\n const html = `<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.HEAD_OPEN]}\n ${helmet ? helmet.title.toString() : ''}\n ${helmet ? helmet.meta.toString() : ''}\n <meta name=\"theme-color\" content=\"#FFFFFF\">\n ${manifestLink}\n ${styleChunkString}\n ${faviconLink}\n <meta charset=\"utf-8\">\n <meta\n content=\"width=device-width,initial-scale=1.0\"\n name=\"viewport\"\n >\n </head>\n <body>\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.BODY_OPEN]}\n <div id=\"react-view\">${App || ''}</div>\n <script\n id=\"inj\"\n type=\"application/javascript\"\n ${ops.noCsp ? '' : `nonce=\"${req.nonce}\"`}\n >\n window.INJ=\"${INJ}\"\n </script>\n ${scriptChunkString}\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.DEFAULT]}\n </body>\n </html>`;\n\n const status = ssrContext.status || 200;\n if (status !== 200) res.status(status);\n\n if (cacheRef && status < 500) {\n // Note: waiting for the caching to complete is not strictly necessary,\n // but it greately simplifies testing, and error reporting.\n await new Promise((done, failed) => {\n brotliCompress(html, (error, buffer) => {\n if (error) failed(error);\n else {\n buffer.nonce = req.nonce; // eslint-disable-line no-param-reassign\n cache.add({ buffer, status }, cacheRef.key);\n done();\n }\n });\n });\n }\n\n // Note: as caching code above may throw in some cases, sending response\n // before it completes will likely hide the error, making it difficult\n // to debug. Thus, at least for now, lets send response after it.\n res.send(html);\n } catch (error) {\n next(error);\n }\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"renderer.js","names":["sanitizedConfig","omit","config","SCRIPT_LOCATIONS","BODY_OPEN","DEFAULT","HEAD_OPEN","getBuildInfo","context","url","path","resolve","JSON","parse","fs","readFileSync","readChunkGroupsJson","buildDir","res","err","prepareCipher","key","Promise","reject","forge","random","getBytes","iv","cipher","createCipher","start","isBrotliAcceptable","req","acceptable","get","ops","split","i","length","type","priority","trim","parseFloat","groupExtraScripts","scripts","script","isString","code","location","undefined","Error","newDefaultLogger","defaultLogLevel","format","transports","winston","createLogger","level","combine","splat","timestamp","colorize","printf","message","stack","rest","Object","keys","stringify","Console","factory","webpackConfig","options","defaults","clone","beforeRender","maxSsrRounds","ssrTimeout","staticCacheSize","logger","defaultLoggerLogLevel","buildInfo","global","TRU_BUILD_INFO","publicPath","outputPath","output","manifestLink","existsSync","cache","staticCacheController","Cache","CHUNK_GROUPS","next","set","cookie","csrfToken","cacheRef","data","buffer","status","noCsp","send","done","failed","brotliDecompress","error","html","h","toString","regex","RegExp","nonce","replace","configToInject","extraScripts","initialState","all","helmet","App","Application","ssrContext","state","cloneDeep","chunks","stream","ssrStart","Date","now","renderPass","pipeableStream","renderToPipeableStream","onAllReady","onError","ssrRound","bailed","dirty","timeout","race","allSettled","pending","timer","then","logMsg","log","pipe","Writable","write","chunk","_","Helmet","renderStatic","chunkGroups","webpackStats","locals","mapValues","toJson","namedChunkGroups","item","assets","map","name","dr_pogodin_react_utils___split_components","payload","serializeJs","CONFIG","ISTATE","ignoreFunction","unsafe","update","util","createBuffer","finish","INJ","encode64","chunkSet","Set","forEach","asset","add","styleChunkString","scriptChunkString","endsWith","grouppedExtraScripts","faviconLink","favicon","title","meta","brotliCompress"],"sources":["../../../src/server/renderer.jsx"],"sourcesContent":["/**\n * ExpressJS middleware for server-side rendering of a ReactJS app.\n */\n\nimport { Writable } from 'stream';\n\nimport { GlobalStateProvider } from '@dr.pogodin/react-global-state';\n\nimport {\n clone,\n cloneDeep,\n defaults,\n isString,\n get,\n mapValues,\n omit,\n} from 'lodash';\n\nimport config from 'config';\nimport forge from 'node-forge';\nimport fs from 'fs';\nimport path from 'path';\nimport { brotliCompress, brotliDecompress } from 'zlib';\n\nimport { renderToPipeableStream } from 'react-dom/server';\nimport { Helmet } from 'react-helmet';\nimport { StaticRouter } from 'react-router-dom/server';\nimport serializeJs from 'serialize-javascript';\nimport { timer } from 'utils/time';\nimport winston from 'winston';\n\nimport Cache from './Cache';\n\nconst sanitizedConfig = omit(config, 'SECRET');\n\nexport const SCRIPT_LOCATIONS = {\n BODY_OPEN: 'BODY_OPEN',\n DEFAULT: 'DEFAULT',\n HEAD_OPEN: 'HEAD_OPEN',\n};\n\n/**\n * Reads build-time information about the app. This information is generated\n * by our standard Webpack config for apps, and it is written into\n * \".build-info\" file in the context folder specified in Webpack config.\n * At the moment, that file contains build timestamp and a random 32-bit key,\n * suitable for cryptographical use.\n * @ignore\n * @param {String} context Webpack context path used during the build.\n * @return {Object} Resolves to the build-time information.\n */\nfunction getBuildInfo(context) {\n const url = path.resolve(context, '.build-info');\n return JSON.parse(fs.readFileSync(url));\n}\n\n/**\n * Attempts to read from disk the named chunk groups mapping generated\n * by Webpack during the compilation.\n * It will not work for development builds, where these stats should be captured\n * via compilator callback.\n * @ignore\n * @param {string} buildDir\n * @return {object}\n */\nfunction readChunkGroupsJson(buildDir) {\n const url = path.resolve(buildDir, '__chunk_groups__.json');\n let res;\n try {\n res = JSON.parse(fs.readFileSync(url));\n } catch (err) {\n res = null;\n }\n return res;\n}\n\n/**\n * Prepares a new Cipher for data encryption.\n * @ignore\n * @param {String} key Encryption key (32-bit random key is expected, see\n * node-forge documentation, in case of doubts).\n * @return {Promise} Resolves to the object with two fields:\n * 1. cipher - a new Cipher, ready for encryption;\n * 2. iv - initial vector used by the cipher.\n */\nfunction prepareCipher(key) {\n return new Promise((resolve, reject) => {\n forge.random.getBytes(32, (err, iv) => {\n if (err) reject(err);\n else {\n const cipher = forge.cipher.createCipher('AES-CBC', key);\n cipher.start({ iv });\n resolve({ cipher, iv });\n }\n });\n });\n}\n\n/**\n * Given an incoming HTTP requests, it deduces whether Brotli-encoded responses\n * are acceptable to the caller.\n * @param {object} req\n * @return {boolean}\n * @ignore\n */\nexport function isBrotliAcceptable(req) {\n const acceptable = req.get('accept-encoding');\n if (acceptable) {\n const ops = acceptable.split(',');\n for (let i = 0; i < ops.length; ++i) {\n const [type, priority] = ops[i].trim().split(';q=');\n if ((type === '*' || type === 'br')\n && (!priority || parseFloat(priority) > 0)) {\n return true;\n }\n }\n }\n return false;\n}\n\n/**\n * Given an array of extra script strings / objects, it returns an object with\n * arrays of scripts to inject in different HTML template locations. During\n * the script groupping it also filters out any empty scripts.\n * @param {({\n * code: string;\n * location: string;\n * }|string)[]} [scripts=[]]\n * @return {{\n * BODY_OPEN: string[];\n * DEFAULT: string[];\n * HEAD_OPEN: string[];\n * }}\n */\nfunction groupExtraScripts(scripts = []) {\n const res = {\n [SCRIPT_LOCATIONS.BODY_OPEN]: '',\n [SCRIPT_LOCATIONS.DEFAULT]: '',\n [SCRIPT_LOCATIONS.HEAD_OPEN]: '',\n };\n for (let i = 0; i < scripts.length; ++i) {\n const script = scripts[i];\n if (isString(script)) {\n if (script) res[SCRIPT_LOCATIONS.DEFAULT] += script;\n } else if (script.code) {\n if (res[script.location] !== undefined) {\n res[script.location] += script.code;\n } else throw Error(`Invalid location \"${script.location}\"`);\n }\n }\n return res;\n}\n\n/**\n * Creates a new default (Winston) logger.\n * @param {object} [options={}]\n * @param {string} [options.defaultLogLevel='info']\n * @return {object}\n */\nexport function newDefaultLogger({\n defaultLogLevel = 'info',\n} = {}) {\n const { format, transports } = winston;\n return winston.createLogger({\n level: defaultLogLevel,\n format: format.combine(\n format.splat(),\n format.timestamp(),\n format.colorize(),\n format.printf(\n ({\n level,\n message,\n timestamp,\n stack,\n ...rest\n }) => {\n let res = `${level}\\t(at ${timestamp}):\\t${message}`;\n if (Object.keys(rest).length) {\n res += `\\n${JSON.stringify(rest, null, 2)}`;\n }\n if (stack) res += `\\n${stack}`;\n return res;\n },\n ),\n ),\n transports: [new transports.Console()],\n });\n}\n\n/**\n * Creates the middleware.\n * @param {object} webpackConfig\n * @param {object} options Additional options:\n * @param {Component} [options.Application] The root ReactJS component of\n * the app to use for the server-side rendering. When not provided\n * the server-side rendering is disabled.\n * @param {object} [options.buildInfo] \"Build info\" object to use. If provided,\n * it will be used, instead of trying to load from the filesystem the one\n * generated by the Webpack build. It is intended for test environments,\n * where passing this stuff via file system is no bueno.\n * @param {boolean} [options.favicon] `true` will include favicon\n * link into the rendered HTML templates.\n * @param {boolean} [options.noCsp] `true` means that no\n * Content-Security-Policy (CSP) is used by server, thus the renderer\n * may cut a few corners.\n * @param {number} [options.maxSsrRounds=10] Maximum number of SSR rounds.\n * @param {number} [options.ssrTimeout=1000] SSR timeout in milliseconds,\n * defaults to 1 second.\n * @param {number} [options.staticCacheSize=1.e7] The maximum\n * static cache size in bytes. Defaults to ~10 MB.\n * @param {function} [options.staticCacheController] When given, it activates,\n * and controls the static caching of generated HTML markup. When this function\n * is provided, on each incoming request it is triggered with the request\n * passed in as the argument. To attempt to serve the response from the cache\n * it should return the object with the following fields:\n * - `key: string` – the cache key for the response;\n * - `maxage?: number` – the maximum age of cached result in ms.\n * If undefined - infinite age is assumed.\n * @return {function} Created middleware.\n */\nexport default function factory(webpackConfig, options) {\n const ops = defaults(clone(options), {\n beforeRender: () => Promise.resolve({}),\n maxSsrRounds: 10,\n ssrTimeout: 1000,\n staticCacheSize: 1.e7,\n });\n\n // Note: in normal use the default logger is created and set in the root\n // server function, and this initialization is for testing uses, where\n // renderer is imported directly.\n if (ops.logger === undefined) {\n ops.logger = newDefaultLogger({\n defaultLogLevel: ops.defaultLoggerLogLevel,\n });\n }\n\n const buildInfo = ops.buildInfo || getBuildInfo(webpackConfig.context);\n global.TRU_BUILD_INFO = buildInfo;\n\n // publicPath from webpack.output has a trailing slash at the end.\n const { publicPath, path: outputPath } = webpackConfig.output;\n\n const manifestLink = fs.existsSync(`${outputPath}/manifest.json`)\n ? `<link rel=\"manifest\" href=\"${publicPath}manifest.json\">` : '';\n\n const cache = ops.staticCacheController\n ? new Cache(ops.staticCacheSize) : null;\n\n const CHUNK_GROUPS = readChunkGroupsJson(outputPath);\n\n return async (req, res, next) => {\n try {\n // Ensures any caches always revalidate HTML markup before reuse.\n res.set('Cache-Control', 'no-cache');\n\n res.cookie('csrfToken', req.csrfToken());\n\n let cacheRef;\n if (cache) {\n cacheRef = ops.staticCacheController(req);\n if (cacheRef) {\n const data = cache.get(cacheRef);\n if (data !== null) {\n const { buffer, status } = data;\n if (ops.noCsp && isBrotliAcceptable(req)) {\n res.set('Content-Type', 'text/html');\n res.set('Content-Encoding', 'br');\n if (status !== 200) res.status(status);\n res.send(buffer);\n } else {\n await new Promise((done, failed) => {\n brotliDecompress(buffer, (error, html) => {\n if (error) failed(error);\n else {\n let h = html.toString();\n if (!ops.noCsp) {\n // TODO: Starting from Node v15 we'll be able to use string's\n // .replaceAll() method instead relying on reg. expression for\n // global matching.\n const regex = new RegExp(buffer.nonce, 'g');\n h = h.replace(regex, req.nonce);\n }\n if (status !== 200) res.status(status);\n res.send(h);\n done();\n }\n });\n });\n }\n return;\n }\n }\n }\n\n const [{\n configToInject,\n extraScripts,\n initialState,\n }, {\n cipher,\n iv,\n }] = await Promise.all([\n ops.beforeRender(req, sanitizedConfig),\n prepareCipher(buildInfo.key),\n ]);\n\n let helmet;\n\n /* Optional server-side rendering. */\n let App = ops.Application;\n const ssrContext = {\n req,\n state: cloneDeep(initialState || {}),\n\n // Array of chunk names encountered during the rendering.\n chunks: [],\n };\n let stream;\n if (App) {\n const ssrStart = Date.now();\n\n const renderPass = async () => {\n ssrContext.chunks = [];\n return new Promise((resolve, reject) => {\n const pipeableStream = renderToPipeableStream(\n <GlobalStateProvider\n initialState={ssrContext.state}\n ssrContext={ssrContext}\n >\n <StaticRouter location={req.url}>\n <App />\n </StaticRouter>\n </GlobalStateProvider>,\n {\n onAllReady: () => resolve(pipeableStream),\n onError: reject,\n },\n );\n });\n };\n\n let ssrRound = 0;\n let bailed = false;\n for (; ssrRound < ops.maxSsrRounds; ++ssrRound) {\n stream = await renderPass(); // eslint-disable-line no-await-in-loop\n\n if (!ssrContext.dirty) break;\n\n /* eslint-disable no-await-in-loop */\n const timeout = ops.ssrTimeout + ssrStart - Date.now();\n bailed = timeout <= 0 || !await Promise.race([\n Promise.allSettled(ssrContext.pending),\n timer(timeout).then(() => false),\n ]);\n if (bailed) break;\n /* eslint-enable no-await-in-loop */\n }\n\n let logMsg;\n if (ssrContext.dirty) {\n // NOTE: In the case of incomplete SSR one more round is necessary\n // to ensure the correct hydration when some pending promises have\n // resolved and placed their data into the initial global state.\n stream = await renderPass();\n\n logMsg = bailed ? `SSR timed out after ${ops.ssrTimeout} second(s)`\n : `SSR bailed out after ${ops.maxSsrRounds} round(s)`;\n } else logMsg = `SSR completed in ${ssrRound + 1} round(s)`;\n\n ops.logger.log(ssrContext.dirty ? 'warn' : 'info', logMsg);\n\n App = '';\n stream.pipe(new Writable({\n write: (chunk, _, done) => {\n App += chunk.toString();\n done();\n },\n }));\n\n /* This takes care about server-side rendering of page title and meta tags\n * (still demands injection into HTML template, which happens below). */\n helmet = Helmet.renderStatic();\n }\n\n let chunkGroups;\n const webpackStats = get(res.locals, 'webpack.devMiddleware.stats');\n if (webpackStats) {\n chunkGroups = mapValues(\n webpackStats.toJson({\n all: false,\n chunkGroups: true,\n }).namedChunkGroups,\n (item) => item.assets.map(({ name }) => name),\n );\n } else if (CHUNK_GROUPS) chunkGroups = CHUNK_GROUPS;\n else chunkGroups = {};\n\n /* Encrypts data to be injected into HTML.\n * Keep in mind, that this encryption is no way secure: as the JS bundle\n * contains decryption key and is able to decode it at the client side.\n * Hovewer, for a number of reasons, encryption of injected data is still\n * better than injection of a plain text. */\n delete ssrContext.state.dr_pogodin_react_utils___split_components;\n\n const payload = serializeJs({\n CHUNK_GROUPS: chunkGroups,\n CONFIG: configToInject || sanitizedConfig,\n ISTATE: ssrContext.state,\n }, {\n ignoreFunction: true,\n unsafe: true,\n });\n cipher.update(forge.util.createBuffer(payload, 'utf8'));\n cipher.finish();\n const INJ = forge.util.encode64(`${iv}${cipher.output.data}`);\n\n const chunkSet = new Set();\n\n // TODO: \"main\" chunk has to be added explicitly,\n // because unlike all other chunks they are not managed by <CodeSplit>\n // component, thus they are not added to the ssrContext.chunks\n // automatically. Actually, names of these entry chunks should be\n // read from Wepback config, as the end user may customize them,\n // remove or add other entry points, but it requires additional\n // efforts to figure out how to automatically order them right,\n // thus for now this handles the default config.\n [\n 'main',\n ...ssrContext.chunks,\n ].forEach((chunk) => {\n const assets = chunkGroups[chunk];\n if (assets) assets.forEach((asset) => chunkSet.add(asset));\n });\n\n let styleChunkString = '';\n let scriptChunkString = '';\n chunkSet.forEach((chunk) => {\n if (chunk.endsWith('.css')) {\n styleChunkString += `<link href=\"${publicPath}${chunk}\" rel=\"stylesheet\">`;\n } else if (\n chunk.endsWith('.js')\n // In dev mode HMR adds JS updates into asset arrays,\n // and they (updates) should be ignored.\n && !chunk.endsWith('.hot-update.js')\n ) {\n scriptChunkString += `<script src=\"${publicPath}${chunk}\" type=\"application/javascript\"></script>`;\n }\n });\n\n const grouppedExtraScripts = groupExtraScripts(extraScripts);\n\n const faviconLink = ops.favicon ? (\n '<link rel=\"shortcut icon\" href=\"/favicon.ico\">'\n ) : '';\n\n const html = `<!DOCTYPE html>\n <html lang=\"en\">\n <head>\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.HEAD_OPEN]}\n ${helmet ? helmet.title.toString() : ''}\n ${helmet ? helmet.meta.toString() : ''}\n <meta name=\"theme-color\" content=\"#FFFFFF\">\n ${manifestLink}\n ${styleChunkString}\n ${faviconLink}\n <meta charset=\"utf-8\">\n <meta\n content=\"width=device-width,initial-scale=1.0\"\n name=\"viewport\"\n >\n </head>\n <body>\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.BODY_OPEN]}\n <div id=\"react-view\">${App || ''}</div>\n <script\n id=\"inj\"\n type=\"application/javascript\"\n ${ops.noCsp ? '' : `nonce=\"${req.nonce}\"`}\n >\n window.INJ=\"${INJ}\"\n </script>\n ${scriptChunkString}\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.DEFAULT]}\n </body>\n </html>`;\n\n const status = ssrContext.status || 200;\n if (status !== 200) res.status(status);\n\n if (cacheRef && status < 500) {\n // Note: waiting for the caching to complete is not strictly necessary,\n // but it greately simplifies testing, and error reporting.\n await new Promise((done, failed) => {\n brotliCompress(html, (error, buffer) => {\n if (error) failed(error);\n else {\n buffer.nonce = req.nonce; // eslint-disable-line no-param-reassign\n cache.add({ buffer, status }, cacheRef.key);\n done();\n }\n });\n });\n }\n\n // Note: as caching code above may throw in some cases, sending response\n // before it completes will likely hide the error, making it difficult\n // to debug. Thus, at least for now, lets send response after it.\n res.send(html);\n } catch (error) {\n next(error);\n }\n };\n}\n"],"mappings":"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"}
|