@dr.pogodin/react-utils 1.15.7 → 1.16.0

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.
Files changed (37) hide show
  1. package/build/development/client/index.js +3 -5
  2. package/build/development/client/index.js.map +1 -1
  3. package/build/development/server/renderer.js +78 -36
  4. package/build/development/server/renderer.js.map +1 -1
  5. package/build/development/shared/components/CodeSplit/index.js +108 -9
  6. package/build/development/shared/components/CodeSplit/index.js.map +1 -1
  7. package/build/development/shared/components/Modal/index.js +2 -3
  8. package/build/development/shared/components/Modal/index.js.map +1 -1
  9. package/build/development/shared/utils/jest.js +14 -77
  10. package/build/development/shared/utils/jest.js.map +1 -1
  11. package/build/development/shared/utils/splitComponent.js +12 -13
  12. package/build/development/shared/utils/splitComponent.js.map +1 -1
  13. package/build/development/web.bundle.js +20 -40
  14. package/build/production/client/index.js +2 -2
  15. package/build/production/client/index.js.map +1 -1
  16. package/build/production/server/renderer.js +26 -13
  17. package/build/production/server/renderer.js.map +1 -1
  18. package/build/production/shared/components/CodeSplit/index.js +8 -5
  19. package/build/production/shared/components/CodeSplit/index.js.map +1 -1
  20. package/build/production/shared/components/Modal/index.js +2 -3
  21. package/build/production/shared/components/Modal/index.js.map +1 -1
  22. package/build/production/shared/utils/jest.js +6 -28
  23. package/build/production/shared/utils/jest.js.map +1 -1
  24. package/build/production/shared/utils/splitComponent.js +6 -7
  25. package/build/production/shared/utils/splitComponent.js.map +1 -1
  26. package/build/production/web.bundle.js +1 -1
  27. package/build/production/web.bundle.js.LICENSE.txt +2 -7
  28. package/build/production/web.bundle.js.map +1 -1
  29. package/package.json +25 -25
  30. package/build/development/shared/components/CodeSplit/ClientSide.js +0 -155
  31. package/build/development/shared/components/CodeSplit/ClientSide.js.map +0 -1
  32. package/build/development/shared/components/CodeSplit/ServerSide.js +0 -111
  33. package/build/development/shared/components/CodeSplit/ServerSide.js.map +0 -1
  34. package/build/production/shared/components/CodeSplit/ClientSide.js +0 -21
  35. package/build/production/shared/components/CodeSplit/ClientSide.js.map +0 -1
  36. package/build/production/shared/components/CodeSplit/ServerSide.js +0 -18
  37. package/build/production/shared/components/CodeSplit/ServerSide.js.map +0 -1
@@ -1,7 +1,5 @@
1
1
  "use strict";
2
2
 
3
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4
-
5
3
  Object.defineProperty(exports, "__esModule", {
6
4
  value: true
7
5
  });
@@ -9,7 +7,7 @@ exports.default = Launch;
9
7
 
10
8
  var _reactGlobalState = require("@dr.pogodin/react-global-state");
11
9
 
12
- var _reactDom = _interopRequireDefault(require("react-dom"));
10
+ var _client = require("react-dom/client");
13
11
 
14
12
  var _reactRouterDom = require("react-router-dom");
15
13
 
@@ -24,11 +22,11 @@ var _jsxRuntime = require("react/jsx-runtime");
24
22
  * @param {object} Application Root application component
25
23
  */
26
24
  function Launch(Application) {
27
- _reactDom.default.hydrate( /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactGlobalState.GlobalStateProvider, {
25
+ (0, _client.hydrateRoot)(document.getElementById('react-view'), /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactGlobalState.GlobalStateProvider, {
28
26
  initialState: window.ISTATE,
29
27
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactRouterDom.BrowserRouter, {
30
28
  children: /*#__PURE__*/(0, _jsxRuntime.jsx)(Application, {})
31
29
  })
32
- }), document.getElementById('react-view'));
30
+ }));
33
31
  }
34
32
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/client/index.jsx"],"names":["Launch","Application","ReactDom","hydrate","window","ISTATE","document","getElementById"],"mappings":";;;;;;;;;AAGA;;AAEA;;AACA;;;;AANA;;AACA;;AAOA;AACA;AACA;AACA;AACe,SAASA,MAAT,CAAgBC,WAAhB,EAA6B;AAC1CC,oBAASC,OAAT,eAEI,qBAAC,qCAAD;AAAqB,IAAA,YAAY,EAAEC,MAAM,CAACC,MAA1C;AAAA,2BACE,qBAAC,6BAAD;AAAA,6BACE,qBAAC,WAAD;AADF;AADF,IAFJ,EAOKC,QAAQ,CAACC,cAAT,CAAwB,YAAxB,CAPL;AASD","sourcesContent":["// Initialization of client-side code.\n/* global document, window */\n\nimport { GlobalStateProvider } from '@dr.pogodin/react-global-state';\n\nimport ReactDom from 'react-dom';\nimport { BrowserRouter } from 'react-router-dom';\n\n/**\n * Prepares and launches the app at client side.\n * @param {object} Application Root application component\n */\nexport default function Launch(Application) {\n ReactDom.hydrate(\n (\n <GlobalStateProvider initialState={window.ISTATE}>\n <BrowserRouter>\n <Application />\n </BrowserRouter>\n </GlobalStateProvider>\n ), document.getElementById('react-view'),\n );\n}\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../../../src/client/index.jsx"],"names":["Launch","Application","document","getElementById","window","ISTATE"],"mappings":";;;;;;;AAGA;;AAEA;;AACA;;;;AANA;;AACA;;AAOA;AACA;AACA;AACA;AACe,SAASA,MAAT,CAAgBC,WAAhB,EAA6B;AAC1C,2BACEC,QAAQ,CAACC,cAAT,CAAwB,YAAxB,CADF,eAEE,qBAAC,qCAAD;AAAqB,IAAA,YAAY,EAAEC,MAAM,CAACC,MAA1C;AAAA,2BACE,qBAAC,6BAAD;AAAA,6BACE,qBAAC,WAAD;AADF;AADF,IAFF;AAQD","sourcesContent":["// Initialization of client-side code.\n/* global document, window */\n\nimport { GlobalStateProvider } from '@dr.pogodin/react-global-state';\n\nimport { hydrateRoot } from 'react-dom/client';\nimport { BrowserRouter } from 'react-router-dom';\n\n/**\n * Prepares and launches the app at client side.\n * @param {object} Application Root application component\n */\nexport default function Launch(Application) {\n hydrateRoot(\n document.getElementById('react-view'),\n <GlobalStateProvider initialState={window.ISTATE}>\n <BrowserRouter>\n <Application />\n </BrowserRouter>\n </GlobalStateProvider>,\n );\n}\n"],"file":"index.js"}
@@ -9,6 +9,8 @@ exports.SCRIPT_LOCATIONS = void 0;
9
9
  exports.default = factory;
10
10
  exports.isBrotliAcceptable = isBrotliAcceptable;
11
11
 
12
+ var _stream = require("stream");
13
+
12
14
  var _reactGlobalState = require("@dr.pogodin/react-global-state");
13
15
 
14
16
  var _lodash = require("lodash");
@@ -23,7 +25,7 @@ var _path = _interopRequireDefault(require("path"));
23
25
 
24
26
  var _zlib = require("zlib");
25
27
 
26
- var _server = _interopRequireDefault(require("react-dom/server"));
28
+ var _server = require("react-dom/server");
27
29
 
28
30
  var _reactHelmet = require("react-helmet");
29
31
 
@@ -142,6 +144,43 @@ function isBrotliAcceptable(req) {
142
144
 
143
145
  return false;
144
146
  }
147
+ /**
148
+ * Given an array of extra script strings / objects, it returns an object with
149
+ * arrays of scripts to inject in different HTML template locations. During
150
+ * the script groupping it also filters out any empty scripts.
151
+ * @param {({
152
+ * code: string;
153
+ * location: string;
154
+ * }|string)[]} [scripts=[]]
155
+ * @return {{
156
+ * BODY_OPEN: string[];
157
+ * DEFAULT: string[];
158
+ * HEAD_OPEN: string[];
159
+ * }}
160
+ */
161
+
162
+
163
+ function groupExtraScripts(scripts = []) {
164
+ const res = {
165
+ [SCRIPT_LOCATIONS.BODY_OPEN]: '',
166
+ [SCRIPT_LOCATIONS.DEFAULT]: '',
167
+ [SCRIPT_LOCATIONS.HEAD_OPEN]: ''
168
+ };
169
+
170
+ for (let i = 0; i < scripts.length; ++i) {
171
+ const script = scripts[i];
172
+
173
+ if ((0, _lodash.isString)(script)) {
174
+ if (script) res[SCRIPT_LOCATIONS.DEFAULT] += script;
175
+ } else if (script.code) {
176
+ if (res[script.location] !== undefined) {
177
+ res[script.location] += script.code;
178
+ } else throw Error(`Invalid location "${script.location}"`);
179
+ }
180
+ }
181
+
182
+ return res;
183
+ }
145
184
  /**
146
185
  * Creates the middleware.
147
186
  * @param {object} webpackConfig
@@ -153,6 +192,7 @@ function isBrotliAcceptable(req) {
153
192
  * may cut a few corners.
154
193
  * @param {number} options.maxSsrRounds
155
194
  * @param {number} options.ssrTimeout
195
+ * @param {function} [options.staticCacheController]
156
196
  * @return {function} Created middleware.
157
197
  */
158
198
 
@@ -165,12 +205,7 @@ function factory(webpackConfig, options) {
165
205
  publicPath,
166
206
  path: outputPath
167
207
  } = webpackConfig.output;
168
-
169
- let manifestLink = _fs.default.existsSync(`${outputPath}/manifest.json`);
170
-
171
- manifestLink = manifestLink ? `<link rel="manifest" href="${publicPath}manifest.json"></link>` : ''; // TODO: Update the caching mechanics to stored cached data gzipped,
172
- // and serve them without a need to unpacking server-side.
173
-
208
+ const manifestLink = _fs.default.existsSync(`${outputPath}/manifest.json`) ? `<link rel="manifest" href="${publicPath}manifest.json">` : '';
174
209
  const cache = options.staticCacheController ? new _Cache.default(options.staticCacheSize) : null;
175
210
  const CHUNK_GROUPS = readChunkGroupsJson(outputPath);
176
211
  const ops = (0, _lodash.defaults)((0, _lodash.clone)(options), {
@@ -234,21 +269,31 @@ function factory(webpackConfig, options) {
234
269
  // Array of chunk names encountered during the rendering.
235
270
  chunks: []
236
271
  };
272
+ let stream;
237
273
 
238
274
  if (App) {
239
- let markup;
240
275
  const ssrStart = Date.now();
241
276
 
242
- for (let round = 0; round < options.maxSsrRounds; ++round) {
277
+ const renderPass = async () => {
243
278
  ssrContext.chunks = [];
244
- markup = _server.default.renderToString( /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactGlobalState.GlobalStateProvider, {
245
- initialState: ssrContext.state,
246
- ssrContext: ssrContext,
247
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_server2.StaticRouter, {
248
- location: req.url,
249
- children: /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {})
250
- })
251
- }));
279
+ return new Promise((resolve, reject) => {
280
+ const pipeableStream = (0, _server.renderToPipeableStream)( /*#__PURE__*/(0, _jsxRuntime.jsx)(_reactGlobalState.GlobalStateProvider, {
281
+ initialState: ssrContext.state,
282
+ ssrContext: ssrContext,
283
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(_server2.StaticRouter, {
284
+ location: req.url,
285
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {})
286
+ })
287
+ }), {
288
+ onAllReady: () => resolve(pipeableStream),
289
+ onError: reject
290
+ });
291
+ });
292
+ };
293
+
294
+ for (let round = 0; round < options.maxSsrRounds; ++round) {
295
+ stream = await renderPass(); // eslint-disable-line no-await-in-loop
296
+
252
297
  if (!ssrContext.dirty) break;
253
298
  /* eslint-disable no-await-in-loop */
254
299
 
@@ -258,7 +303,13 @@ function factory(webpackConfig, options) {
258
303
  /* eslint-enable no-await-in-loop */
259
304
  }
260
305
 
261
- App = markup;
306
+ App = '';
307
+ stream.pipe(new _stream.Writable({
308
+ write: (chunk, _, done) => {
309
+ App += chunk.toString();
310
+ done();
311
+ }
312
+ }));
262
313
  /* This takes care about server-side rendering of page title and meta tags
263
314
  * (still demands injection into HTML template, which happens below). */
264
315
 
@@ -315,42 +366,33 @@ function factory(webpackConfig, options) {
315
366
  let scriptChunkString = '';
316
367
  chunkSet.forEach(chunk => {
317
368
  if (chunk.endsWith('.css')) {
318
- styleChunkString += `<link href="${publicPath}${chunk}" rel="stylesheet" />`;
369
+ styleChunkString += `<link href="${publicPath}${chunk}" rel="stylesheet">`;
319
370
  } else if (chunk.endsWith('.js') // In dev mode HMR adds JS updates into asset arrays,
320
371
  // and they (updates) should be ignored.
321
372
  && !chunk.endsWith('.hot-update.js')) {
322
373
  scriptChunkString += `<script src="${publicPath}${chunk}" type="application/javascript"></script>`;
323
374
  }
324
375
  });
325
- let bodyOpenExtraScripts;
326
- let defaultExtraScripts;
327
- let headOpenExtraScripts;
328
-
329
- if (extraScripts) {
330
- bodyOpenExtraScripts = extraScripts.filter(script => (0, _lodash.isObject)(script) && script.location === SCRIPT_LOCATIONS.BODY_OPEN).map(script => script.code).join('');
331
- defaultExtraScripts = extraScripts.filter(script => (0, _lodash.isString)(script) || script.location === SCRIPT_LOCATIONS.DEFAULT).map(script => (0, _lodash.isString)(script) ? script : script.code).join('');
332
- headOpenExtraScripts = extraScripts.filter(script => (0, _lodash.isObject)(script) && script.location === SCRIPT_LOCATIONS.HEAD_OPEN).map(script => script.code).join('');
333
- }
334
-
335
- const faviconLink = ops.favicon ? '<link rel="shortcut icon" href="/favicon.ico" />' : '';
376
+ const grouppedExtraScripts = groupExtraScripts(extraScripts);
377
+ const faviconLink = ops.favicon ? '<link rel="shortcut icon" href="/favicon.ico">' : '';
336
378
  const html = `<!DOCTYPE html>
337
379
  <html lang="en">
338
380
  <head>
339
- ${headOpenExtraScripts || ''}
381
+ ${grouppedExtraScripts[SCRIPT_LOCATIONS.HEAD_OPEN]}
340
382
  ${helmet ? helmet.title.toString() : ''}
341
383
  ${helmet ? helmet.meta.toString() : ''}
342
- <meta name="theme-color" content="#FFFFFF"/>
384
+ <meta name="theme-color" content="#FFFFFF">
343
385
  ${manifestLink}
344
386
  ${styleChunkString}
345
387
  ${faviconLink}
346
- <meta charset="utf-8" />
388
+ <meta charset="utf-8">
347
389
  <meta
348
390
  content="width=device-width,initial-scale=1.0"
349
391
  name="viewport"
350
- />
392
+ >
351
393
  </head>
352
394
  <body>
353
- ${bodyOpenExtraScripts || ''}
395
+ ${grouppedExtraScripts[SCRIPT_LOCATIONS.BODY_OPEN]}
354
396
  <div id="react-view">${App || ''}</div>
355
397
  <script
356
398
  id="inj"
@@ -360,7 +402,7 @@ function factory(webpackConfig, options) {
360
402
  window.INJ="${INJ}"
361
403
  </script>
362
404
  ${scriptChunkString}
363
- ${defaultExtraScripts || ''}
405
+ ${grouppedExtraScripts[SCRIPT_LOCATIONS.DEFAULT]}
364
406
  </body>
365
407
  </html>`;
366
408
  res.send(html);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/server/renderer.jsx"],"names":["sanitizedConfig","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","factory","webpackConfig","options","buildInfo","global","TRU_BUILD_INFO","publicPath","outputPath","output","manifestLink","existsSync","cache","staticCacheController","Cache","staticCacheSize","CHUNK_GROUPS","beforeRender","next","set","cacheRef","data","noCsp","send","error","html","h","toString","regex","RegExp","nonce","replace","configToInject","extraScripts","initialState","all","helmet","App","Application","ssrContext","state","chunks","markup","ssrStart","Date","now","round","maxSsrRounds","ReactDOM","renderToString","dirty","timeout","ssrTimeout","ok","race","allSettled","pending","time","timer","then","Helmet","renderStatic","chunkGroups","webpackStats","locals","toJson","namedChunkGroups","item","assets","map","name","dr_pogodin_react_utils___split_components","payload","CONFIG","ISTATE","ignoreFunction","unsafe","update","util","createBuffer","finish","INJ","encode64","status","chunkSet","Set","forEach","chunk","asset","add","styleChunkString","scriptChunkString","endsWith","bodyOpenExtraScripts","defaultExtraScripts","headOpenExtraScripts","filter","script","location","code","join","faviconLink","favicon","title","meta","buffer"],"mappings":"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","sourcesContent":["/**\n * ExpressJS middleware for server-side rendering of a ReactJS app.\n */\n\nimport { GlobalStateProvider } from '@dr.pogodin/react-global-state';\n\nimport {\n clone,\n cloneDeep,\n defaults,\n isObject,\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 ReactDOM from 'react-dom/server';\nimport { Helmet } from 'react-helmet';\nimport { StaticRouter } from 'react-router-dom/server';\nimport serializeJs from 'serialize-javascript';\n\nimport time from 'utils/time';\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 * Creates the middleware.\n * @param {object} webpackConfig\n * @param {object} options Additional options:\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\n * @param {number} options.ssrTimeout\n * @return {function} Created middleware.\n */\nexport default function factory(webpackConfig, options) {\n const buildInfo = getBuildInfo(webpackConfig.context);\n\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 let manifestLink = fs.existsSync(`${outputPath}/manifest.json`);\n manifestLink = manifestLink ? (\n `<link rel=\"manifest\" href=\"${publicPath}manifest.json\"></link>`\n ) : '';\n\n // TODO: Update the caching mechanics to stored cached data gzipped,\n // and serve them without a need to unpacking server-side.\n const cache = options.staticCacheController\n ? new Cache(options.staticCacheSize) : null;\n\n const CHUNK_GROUPS = readChunkGroupsJson(outputPath);\n\n const ops = defaults(clone(options), {\n beforeRender: () => Promise.resolve({}),\n });\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 let cacheRef;\n if (cache) {\n cacheRef = options.staticCacheController(req);\n if (cacheRef) {\n const data = cache.get(cacheRef);\n if (data !== null) {\n if (options.noCsp && isBrotliAcceptable(req)) {\n res.set('Content-Type', 'text/html');\n res.set('Content-Encoding', 'br');\n res.send(data);\n } else {\n brotliDecompress(data, (error, html) => {\n if (error) next(error);\n else {\n let h = html.toString();\n if (!options.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(data.nonce, 'g');\n h = h.replace(regex, req.nonce);\n }\n res.send(h);\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 = options.Application;\n const ssrContext = {\n req,\n state: cloneDeep(initialState || {}),\n\n // Array of chunk names encountered during the rendering.\n chunks: [],\n };\n if (App) {\n let markup;\n const ssrStart = Date.now();\n for (let round = 0; round < options.maxSsrRounds; ++round) {\n ssrContext.chunks = [];\n markup = ReactDOM.renderToString((\n <GlobalStateProvider\n initialState={ssrContext.state}\n ssrContext={ssrContext}\n >\n <StaticRouter location={req.url}>\n <App />\n </StaticRouter>\n </GlobalStateProvider>\n ));\n\n if (!ssrContext.dirty) break;\n\n /* eslint-disable no-await-in-loop */\n const timeout = options.ssrTimeout + ssrStart - Date.now();\n const ok = timeout > 0 && await Promise.race([\n Promise.allSettled(ssrContext.pending),\n time.timer(timeout).then(() => false),\n ]);\n if (!ok) break;\n /* eslint-enable no-await-in-loop */\n }\n App = markup;\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 if (ssrContext.status) res.status(ssrContext.status);\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 let bodyOpenExtraScripts;\n let defaultExtraScripts;\n let headOpenExtraScripts;\n if (extraScripts) {\n bodyOpenExtraScripts = extraScripts\n .filter((script) => isObject(script)\n && script.location === SCRIPT_LOCATIONS.BODY_OPEN)\n .map((script) => script.code)\n .join('');\n defaultExtraScripts = extraScripts\n .filter((script) => isString(script)\n || script.location === SCRIPT_LOCATIONS.DEFAULT)\n .map((script) => (isString(script) ? script : script.code))\n .join('');\n headOpenExtraScripts = extraScripts\n .filter((script) => isObject(script)\n && script.location === SCRIPT_LOCATIONS.HEAD_OPEN)\n .map((script) => script.code)\n .join('');\n }\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 ${headOpenExtraScripts || ''}\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 ${bodyOpenExtraScripts || ''}\n <div id=\"react-view\">${App || ''}</div>\n <script\n id=\"inj\"\n type=\"application/javascript\"\n ${options.noCsp ? '' : `nonce=\"${req.nonce}\"`}\n >\n window.INJ=\"${INJ}\"\n </script>\n ${scriptChunkString}\n ${defaultExtraScripts || ''}\n </body>\n </html>`;\n\n res.send(html);\n\n if (cacheRef) {\n brotliCompress(html, (error, buffer) => {\n if (error) throw error;\n buffer.nonce = req.nonce; // eslint-disable-line no-param-reassign\n cache.add(buffer, cacheRef.key);\n });\n }\n } catch (error) {\n next(error);\n }\n };\n}\n"],"file":"renderer.js"}
1
+ {"version":3,"sources":["../../../src/server/renderer.jsx"],"names":["sanitizedConfig","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","code","location","undefined","Error","factory","webpackConfig","options","buildInfo","global","TRU_BUILD_INFO","publicPath","outputPath","output","manifestLink","existsSync","cache","staticCacheController","Cache","staticCacheSize","CHUNK_GROUPS","beforeRender","next","set","cacheRef","data","noCsp","send","error","html","h","toString","regex","RegExp","nonce","replace","configToInject","extraScripts","initialState","all","helmet","App","Application","ssrContext","state","chunks","stream","ssrStart","Date","now","renderPass","pipeableStream","onAllReady","onError","round","maxSsrRounds","dirty","timeout","ssrTimeout","ok","race","allSettled","pending","time","timer","then","pipe","Writable","write","chunk","_","done","Helmet","renderStatic","chunkGroups","webpackStats","locals","toJson","namedChunkGroups","item","assets","map","name","dr_pogodin_react_utils___split_components","payload","CONFIG","ISTATE","ignoreFunction","unsafe","update","util","createBuffer","finish","INJ","encode64","status","chunkSet","Set","forEach","asset","add","styleChunkString","scriptChunkString","endsWith","grouppedExtraScripts","faviconLink","favicon","title","meta","buffer"],"mappings":"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","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';\n\nimport time from 'utils/time';\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 the middleware.\n * @param {object} webpackConfig\n * @param {object} options Additional options:\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\n * @param {number} options.ssrTimeout\n * @param {function} [options.staticCacheController]\n * @return {function} Created middleware.\n */\nexport default function factory(webpackConfig, options) {\n const buildInfo = getBuildInfo(webpackConfig.context);\n\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 = options.staticCacheController\n ? new Cache(options.staticCacheSize) : null;\n\n const CHUNK_GROUPS = readChunkGroupsJson(outputPath);\n\n const ops = defaults(clone(options), {\n beforeRender: () => Promise.resolve({}),\n });\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 let cacheRef;\n if (cache) {\n cacheRef = options.staticCacheController(req);\n if (cacheRef) {\n const data = cache.get(cacheRef);\n if (data !== null) {\n if (options.noCsp && isBrotliAcceptable(req)) {\n res.set('Content-Type', 'text/html');\n res.set('Content-Encoding', 'br');\n res.send(data);\n } else {\n brotliDecompress(data, (error, html) => {\n if (error) next(error);\n else {\n let h = html.toString();\n if (!options.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(data.nonce, 'g');\n h = h.replace(regex, req.nonce);\n }\n res.send(h);\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 = options.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 for (let round = 0; round < options.maxSsrRounds; ++round) {\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 = options.ssrTimeout + ssrStart - Date.now();\n const ok = timeout > 0 && await Promise.race([\n Promise.allSettled(ssrContext.pending),\n time.timer(timeout).then(() => false),\n ]);\n if (!ok) break;\n /* eslint-enable no-await-in-loop */\n }\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 if (ssrContext.status) res.status(ssrContext.status);\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 ${options.noCsp ? '' : `nonce=\"${req.nonce}\"`}\n >\n window.INJ=\"${INJ}\"\n </script>\n ${scriptChunkString}\n ${grouppedExtraScripts[SCRIPT_LOCATIONS.DEFAULT]}\n </body>\n </html>`;\n\n res.send(html);\n\n if (cacheRef) {\n brotliCompress(html, (error, buffer) => {\n if (error) throw error;\n buffer.nonce = req.nonce; // eslint-disable-line no-param-reassign\n cache.add(buffer, cacheRef.key);\n });\n }\n } catch (error) {\n next(error);\n }\n };\n}\n"],"file":"renderer.js"}
@@ -5,17 +5,116 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
5
5
  Object.defineProperty(exports, "__esModule", {
6
6
  value: true
7
7
  });
8
- exports.default = void 0;
8
+ exports.default = CodeSplit;
9
9
 
10
- var _webpack = require("../../utils/webpack");
10
+ var _propTypes = _interopRequireDefault(require("prop-types"));
11
11
 
12
- var _ClientSide = _interopRequireDefault(require("./ClientSide"));
12
+ var _react = require("react");
13
13
 
14
- // This module exposes client- or server-side version of the <CodeSplit>
15
- // component, depending on the environment. That component is used internally
16
- // to implement the code splitting. Library users should use splitComponent()
17
- // function to use the code splitting (see "utils/splitComponent.js").
18
- var _default = (0, _webpack.requireWeak)("./ServerSide", __dirname) || _ClientSide.default;
14
+ var _reactGlobalState = require("@dr.pogodin/react-global-state");
19
15
 
20
- exports.default = _default;
16
+ var _Barrier = require("../../utils/Barrier");
17
+
18
+ var _isomorphy = require("../../utils/isomorphy");
19
+
20
+ var _jsxRuntime = require("react/jsx-runtime");
21
+
22
+ /* eslint-disable react/jsx-props-no-spreading */
23
+
24
+ /* global document, window */
25
+ function CodeSplit({
26
+ children,
27
+ chunkName,
28
+ getComponent,
29
+ placeholder,
30
+ ...props
31
+ }) {
32
+ const {
33
+ current: heap
34
+ } = (0, _react.useRef)({
35
+ mounted: false,
36
+ pendingStyles: []
37
+ });
38
+ const {
39
+ publicPath
40
+ } = (0, _isomorphy.getBuildInfo)(); // TODO: Not sure whether it is fine for the inner React.lazy() mechanics
41
+ // if we dynamically create the lazy component inside a render of another
42
+ // component, or does it expect we only create it once on outside of any
43
+ // component.
44
+
45
+ const LazyComponent = /*#__PURE__*/(0, _react.lazy)(async () => {
46
+ const res = await getComponent();
47
+ if (heap.pendingStyles.length) await Promise.all(heap.pendingStyles);
48
+ return res.default ? res : {
49
+ default: res
50
+ };
51
+ });
52
+
53
+ if (_isomorphy.IS_SERVER_SIDE) {
54
+ const {
55
+ chunks
56
+ } = (0, _reactGlobalState.getGlobalState)().ssrContext;
57
+
58
+ if (chunks.includes(chunkName)) {
59
+ throw Error(`Chunk name clash for "${chunkName}"`);
60
+ } else chunks.push(chunkName);
61
+ } else if (!heap.mounted) {
62
+ heap.mounted = true;
63
+ window.CHUNK_GROUPS[chunkName].forEach(asset => {
64
+ if (!asset.endsWith('.css')) return;
65
+ const path = `${publicPath}/${asset}`;
66
+ let link = document.querySelector(`link[href="${path}"]`);
67
+
68
+ if (!link) {
69
+ link = document.createElement('link');
70
+ link.setAttribute('href', path);
71
+ link.setAttribute('rel', 'stylesheet');
72
+ const barrier = (0, _Barrier.newBarrier)();
73
+ link.onload = barrier.resolve; // Even if the style load failed, still allow to mount the component,
74
+ // abeit with broken styling.
75
+
76
+ link.onerror = barrier.resolve;
77
+ heap.pendingStyles.push(barrier);
78
+ const head = document.querySelector('head');
79
+ head.appendChild(link);
80
+ }
81
+
82
+ window.STYLESHEET_USAGE_COUNTERS ||= {};
83
+ window.STYLESHEET_USAGE_COUNTERS[path] ||= 0;
84
+ ++window.STYLESHEET_USAGE_COUNTERS[path];
85
+ });
86
+ } // This effectively fires only once, just before the component unmounts.
87
+
88
+
89
+ (0, _react.useEffect)(() => () => {
90
+ heap.mounted = false;
91
+ window.CHUNK_GROUPS[chunkName].forEach(item => {
92
+ if (!item.endsWith('.css')) return;
93
+ const path = `${publicPath}/${item}`;
94
+
95
+ if (--window.STYLESHEET_USAGE_COUNTERS[path] <= 0) {
96
+ const link = document.querySelector(`link[href="${path}"]`);
97
+ const head = document.querySelector('head');
98
+ head.removeChild(link);
99
+ }
100
+ });
101
+ }, [chunkName, heap, publicPath]);
102
+ return /*#__PURE__*/(0, _jsxRuntime.jsx)(_react.Suspense, {
103
+ fallback: placeholder,
104
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(LazyComponent, { ...props,
105
+ children: children
106
+ })
107
+ });
108
+ }
109
+
110
+ CodeSplit.propTypes = {
111
+ children: _propTypes.default.node,
112
+ chunkName: _propTypes.default.string.isRequired,
113
+ getComponent: _propTypes.default.func.isRequired,
114
+ placeholder: _propTypes.default.node
115
+ };
116
+ CodeSplit.defaultProps = {
117
+ children: undefined,
118
+ placeholder: undefined
119
+ };
21
120
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/shared/components/CodeSplit/index.jsx"],"names":["__dirname","ClientSide"],"mappings":";;;;;;;;;AAKA;;AACA;;AANA;AACA;AACA;AACA;eAKe,0CAA4BA,SAA5B,KAA0CC,mB","sourcesContent":["// This module exposes client- or server-side version of the <CodeSplit>\n// component, depending on the environment. That component is used internally\n// to implement the code splitting. Library users should use splitComponent()\n// function to use the code splitting (see \"utils/splitComponent.js\").\n\nimport { requireWeak } from 'utils/webpack';\nimport ClientSide from './ClientSide';\n\nexport default requireWeak('./ServerSide', __dirname) || ClientSide;\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../../../../../src/shared/components/CodeSplit/index.jsx"],"names":["CodeSplit","children","chunkName","getComponent","placeholder","props","current","heap","mounted","pendingStyles","publicPath","LazyComponent","res","length","Promise","all","default","IS_SERVER_SIDE","chunks","ssrContext","includes","Error","push","window","CHUNK_GROUPS","forEach","asset","endsWith","path","link","document","querySelector","createElement","setAttribute","barrier","onload","resolve","onerror","head","appendChild","STYLESHEET_USAGE_COUNTERS","item","removeChild","propTypes","PT","node","string","isRequired","func","defaultProps","undefined"],"mappings":"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","sourcesContent":["/* eslint-disable react/jsx-props-no-spreading */\n/* global document, window */\n\nimport PT from 'prop-types';\n\nimport {\n lazy,\n Suspense,\n useEffect,\n useRef,\n} from 'react';\n\nimport { getGlobalState } from '@dr.pogodin/react-global-state';\nimport { newBarrier } from 'utils/Barrier';\nimport { getBuildInfo, IS_SERVER_SIDE } from 'utils/isomorphy';\n\nexport default function CodeSplit({\n children,\n chunkName,\n getComponent,\n placeholder,\n ...props\n}) {\n const { current: heap } = useRef({\n mounted: false,\n pendingStyles: [],\n });\n const { publicPath } = getBuildInfo();\n\n // TODO: Not sure whether it is fine for the inner React.lazy() mechanics\n // if we dynamically create the lazy component inside a render of another\n // component, or does it expect we only create it once on outside of any\n // component.\n const LazyComponent = lazy(async () => {\n const res = await getComponent();\n if (heap.pendingStyles.length) await Promise.all(heap.pendingStyles);\n return res.default ? res : { default: res };\n });\n\n if (IS_SERVER_SIDE) {\n const { chunks } = getGlobalState().ssrContext;\n if (chunks.includes(chunkName)) {\n throw Error(`Chunk name clash for \"${chunkName}\"`);\n } else chunks.push(chunkName);\n } else if (!heap.mounted) {\n heap.mounted = true;\n\n window.CHUNK_GROUPS[chunkName].forEach((asset) => {\n if (!asset.endsWith('.css')) return;\n const path = `${publicPath}/${asset}`;\n let link = document.querySelector(`link[href=\"${path}\"]`);\n if (!link) {\n link = document.createElement('link');\n link.setAttribute('href', path);\n link.setAttribute('rel', 'stylesheet');\n\n const barrier = newBarrier();\n link.onload = barrier.resolve;\n\n // Even if the style load failed, still allow to mount the component,\n // abeit with broken styling.\n link.onerror = barrier.resolve;\n\n heap.pendingStyles.push(barrier);\n\n const head = document.querySelector('head');\n head.appendChild(link);\n }\n window.STYLESHEET_USAGE_COUNTERS ||= {};\n window.STYLESHEET_USAGE_COUNTERS[path] ||= 0;\n ++window.STYLESHEET_USAGE_COUNTERS[path];\n });\n }\n\n // This effectively fires only once, just before the component unmounts.\n useEffect(() => () => {\n heap.mounted = false;\n window.CHUNK_GROUPS[chunkName].forEach((item) => {\n if (!item.endsWith('.css')) return;\n const path = `${publicPath}/${item}`;\n if (--window.STYLESHEET_USAGE_COUNTERS[path] <= 0) {\n const link = document.querySelector(`link[href=\"${path}\"]`);\n const head = document.querySelector('head');\n head.removeChild(link);\n }\n });\n }, [chunkName, heap, publicPath]);\n\n return (\n <Suspense fallback={placeholder}>\n <LazyComponent {...props}>\n {children}\n </LazyComponent>\n </Suspense>\n );\n}\n\nCodeSplit.propTypes = {\n children: PT.node,\n chunkName: PT.string.isRequired,\n getComponent: PT.func.isRequired,\n placeholder: PT.node,\n};\n\nCodeSplit.defaultProps = {\n children: undefined,\n placeholder: undefined,\n};\n"],"file":"index.js"}
@@ -33,13 +33,12 @@ const baseTheme = {
33
33
  * The `<Modal>` component implements a simple themeable modal window, wrapped
34
34
  * into the default theme. `<BaseModal>` exposes the base non-themed component.
35
35
  * **Children:** Component children are rendered as the modal content.
36
- * @param {object} props Component properties.
36
+ * @param {object} props Component properties. Beside props documented below,
37
+ * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties) are supported as well.
37
38
  * @param {function} [props.onCancel] The callback to trigger when user
38
39
  * clicks outside the modal, or presses Escape. It is expected to hide the
39
40
  * modal.
40
41
  * @param {ModalTheme} [props.theme] _Ad hoc_ theme.
41
- * @param {...any} [props....]
42
- * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties)
43
42
  */
44
43
  function BaseModal({
45
44
  children,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../../src/shared/components/Modal/index.jsx"],"names":["BaseModal","children","onCancel","theme","containerRef","overlayRef","portal","setPortal","p","document","createElement","body","classList","add","appendChild","remove","removeChild","focusLast","elems","current","querySelectorAll","i","length","focus","activeElement","ReactDom","createPortal","overlay","e","key","node","container","event","stopPropagation","ThemedModal","baseTheme","propTypes","PT","func","themeType","isRequired","defaultProps","noop"],"mappings":"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","sourcesContent":["/* global document */\n\nimport { noop } from 'lodash';\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport ReactDom from 'react-dom';\nimport PT from 'prop-types';\nimport themed from '@dr.pogodin/react-themes';\n\nimport baseTheme from './base-theme.scss';\nimport './styles.scss';\n\n/**\n * The `<Modal>` component implements a simple themeable modal window, wrapped\n * into the default theme. `<BaseModal>` exposes the base non-themed component.\n * **Children:** Component children are rendered as the modal content.\n * @param {object} props Component properties.\n * @param {function} [props.onCancel] The callback to trigger when user\n * clicks outside the modal, or presses Escape. It is expected to hide the\n * modal.\n * @param {ModalTheme} [props.theme] _Ad hoc_ theme.\n * @param {...any} [props....]\n * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties)\n */\nfunction BaseModal({\n children,\n onCancel,\n theme,\n}) {\n const containerRef = useRef();\n const overlayRef = useRef();\n const [portal, setPortal] = useState();\n\n useEffect(() => {\n const p = document.createElement('div');\n document.body.classList.add('scrolling-disabled-by-modal');\n document.body.appendChild(p);\n setPortal(p);\n return () => {\n document.body.classList.remove('scrolling-disabled-by-modal');\n document.body.removeChild(p);\n };\n }, []);\n\n const focusLast = useMemo(() => (\n <div\n onFocus={() => {\n const elems = containerRef.current.querySelectorAll('*');\n for (let i = elems.length - 1; i >= 0; --i) {\n elems[i].focus();\n if (document.activeElement === elems[i]) return;\n }\n overlayRef.current.focus();\n }}\n /* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n tabIndex=\"0\"\n /* eslint-enable jsx-a11y/no-noninteractive-tabindex */\n />\n ), []);\n\n return portal ? ReactDom.createPortal(\n (\n <>\n {focusLast}\n <div\n aria-label=\"Cancel\"\n className={theme.overlay}\n onClick={() => onCancel()}\n onKeyDown={(e) => {\n if (e.key === 'Escape') onCancel();\n }}\n ref={(node) => {\n if (node && node !== overlayRef.current) {\n overlayRef.current = node;\n node.focus();\n }\n }}\n role=\"button\"\n tabIndex=\"0\"\n />\n <div\n aria-modal=\"true\"\n className={theme.container}\n onWheel={(event) => event.stopPropagation()}\n ref={containerRef}\n role=\"dialog\"\n >\n {children}\n </div>\n <div\n onFocus={() => {\n overlayRef.current.focus();\n }}\n /* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n tabIndex=\"0\"\n /* eslint-enable jsx-a11y/no-noninteractive-tabindex */\n />\n {focusLast}\n </>\n ),\n portal,\n ) : null;\n}\n\nconst ThemedModal = themed(\n 'Modal',\n [\n 'container',\n 'overlay',\n ],\n baseTheme,\n)(BaseModal);\n\nBaseModal.propTypes = {\n onCancel: PT.func,\n children: PT.node,\n theme: ThemedModal.themeType.isRequired,\n};\n\nBaseModal.defaultProps = {\n onCancel: noop,\n children: null,\n};\n\nexport default ThemedModal;\n\n/* Non-themed version of the Modal. */\nexport { BaseModal };\n"],"file":"index.js"}
1
+ {"version":3,"sources":["../../../../../src/shared/components/Modal/index.jsx"],"names":["BaseModal","children","onCancel","theme","containerRef","overlayRef","portal","setPortal","p","document","createElement","body","classList","add","appendChild","remove","removeChild","focusLast","elems","current","querySelectorAll","i","length","focus","activeElement","ReactDom","createPortal","overlay","e","key","node","container","event","stopPropagation","ThemedModal","baseTheme","propTypes","PT","func","themeType","isRequired","defaultProps","noop"],"mappings":"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","sourcesContent":["/* global document */\n\nimport { noop } from 'lodash';\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n} from 'react';\nimport ReactDom from 'react-dom';\nimport PT from 'prop-types';\nimport themed from '@dr.pogodin/react-themes';\n\nimport baseTheme from './base-theme.scss';\nimport './styles.scss';\n\n/**\n * The `<Modal>` component implements a simple themeable modal window, wrapped\n * into the default theme. `<BaseModal>` exposes the base non-themed component.\n * **Children:** Component children are rendered as the modal content.\n * @param {object} props Component properties. Beside props documented below,\n * [Other theming properties](https://www.npmjs.com/package/@dr.pogodin/react-themes#themed-component-properties) are supported as well.\n * @param {function} [props.onCancel] The callback to trigger when user\n * clicks outside the modal, or presses Escape. It is expected to hide the\n * modal.\n * @param {ModalTheme} [props.theme] _Ad hoc_ theme.\n */\nfunction BaseModal({\n children,\n onCancel,\n theme,\n}) {\n const containerRef = useRef();\n const overlayRef = useRef();\n const [portal, setPortal] = useState();\n\n useEffect(() => {\n const p = document.createElement('div');\n document.body.classList.add('scrolling-disabled-by-modal');\n document.body.appendChild(p);\n setPortal(p);\n return () => {\n document.body.classList.remove('scrolling-disabled-by-modal');\n document.body.removeChild(p);\n };\n }, []);\n\n const focusLast = useMemo(() => (\n <div\n onFocus={() => {\n const elems = containerRef.current.querySelectorAll('*');\n for (let i = elems.length - 1; i >= 0; --i) {\n elems[i].focus();\n if (document.activeElement === elems[i]) return;\n }\n overlayRef.current.focus();\n }}\n /* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n tabIndex=\"0\"\n /* eslint-enable jsx-a11y/no-noninteractive-tabindex */\n />\n ), []);\n\n return portal ? ReactDom.createPortal(\n (\n <>\n {focusLast}\n <div\n aria-label=\"Cancel\"\n className={theme.overlay}\n onClick={() => onCancel()}\n onKeyDown={(e) => {\n if (e.key === 'Escape') onCancel();\n }}\n ref={(node) => {\n if (node && node !== overlayRef.current) {\n overlayRef.current = node;\n node.focus();\n }\n }}\n role=\"button\"\n tabIndex=\"0\"\n />\n <div\n aria-modal=\"true\"\n className={theme.container}\n onWheel={(event) => event.stopPropagation()}\n ref={containerRef}\n role=\"dialog\"\n >\n {children}\n </div>\n <div\n onFocus={() => {\n overlayRef.current.focus();\n }}\n /* eslint-disable jsx-a11y/no-noninteractive-tabindex */\n tabIndex=\"0\"\n /* eslint-enable jsx-a11y/no-noninteractive-tabindex */\n />\n {focusLast}\n </>\n ),\n portal,\n ) : null;\n}\n\nconst ThemedModal = themed(\n 'Modal',\n [\n 'container',\n 'overlay',\n ],\n baseTheme,\n)(BaseModal);\n\nBaseModal.propTypes = {\n onCancel: PT.func,\n children: PT.node,\n theme: ThemedModal.themeType.isRequired,\n};\n\nBaseModal.defaultProps = {\n onCancel: noop,\n children: null,\n};\n\nexport default ThemedModal;\n\n/* Non-themed version of the Modal. */\nexport { BaseModal };\n"],"file":"index.js"}