@dr.pogodin/react-utils 1.25.6 → 1.26.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 (290) hide show
  1. package/babel.config.js +3 -1
  2. package/bin/build.js +18 -4
  3. package/build/development/client/getInj.js +9 -8
  4. package/build/development/client/getInj.js.map +1 -1
  5. package/build/development/client/index.js +6 -3
  6. package/build/development/client/index.js.map +1 -1
  7. package/build/development/client/init.js +4 -0
  8. package/build/development/client/init.js.map +1 -1
  9. package/build/development/index.js +166 -38
  10. package/build/development/index.js.map +1 -1
  11. package/build/development/server/Cache.js +13 -13
  12. package/build/development/server/Cache.js.map +1 -1
  13. package/build/development/server/index.js +16 -23
  14. package/build/development/server/index.js.map +1 -1
  15. package/build/development/server/renderer.js +56 -43
  16. package/build/development/server/renderer.js.map +1 -1
  17. package/build/development/server/server.js +9 -5
  18. package/build/development/server/server.js.map +1 -1
  19. package/build/development/server/utils/errors.js +12 -9
  20. package/build/development/server/utils/errors.js.map +1 -1
  21. package/build/development/server/utils/index.js +2 -2
  22. package/build/development/server/utils/index.js.map +1 -1
  23. package/build/development/shared/components/Button/index.js +7 -5
  24. package/build/development/shared/components/Button/index.js.map +1 -1
  25. package/build/development/shared/components/Checkbox/index.js +24 -26
  26. package/build/development/shared/components/Checkbox/index.js.map +1 -1
  27. package/build/development/shared/components/Dropdown/index.js +24 -17
  28. package/build/development/shared/components/Dropdown/index.js.map +1 -1
  29. package/build/development/shared/components/GenericLink/index.js +44 -37
  30. package/build/development/shared/components/GenericLink/index.js.map +1 -1
  31. package/build/development/shared/components/Input/index.js +7 -7
  32. package/build/development/shared/components/Input/index.js.map +1 -1
  33. package/build/development/shared/components/Link.js +10 -9
  34. package/build/development/shared/components/Link.js.map +1 -1
  35. package/build/development/shared/components/MetaTags.js +22 -18
  36. package/build/development/shared/components/MetaTags.js.map +1 -1
  37. package/build/development/shared/components/Modal/index.js +16 -16
  38. package/build/development/shared/components/Modal/index.js.map +1 -1
  39. package/build/development/shared/components/NavLink.js +10 -9
  40. package/build/development/shared/components/NavLink.js.map +1 -1
  41. package/build/development/shared/components/PageLayout/index.js +16 -18
  42. package/build/development/shared/components/PageLayout/index.js.map +1 -1
  43. package/build/development/shared/components/ScalableRect/index.js +27 -7
  44. package/build/development/shared/components/ScalableRect/index.js.map +1 -1
  45. package/build/development/shared/components/Throbber/index.js +14 -22
  46. package/build/development/shared/components/Throbber/index.js.map +1 -1
  47. package/build/development/shared/components/WithTooltip/Tooltip.js +19 -21
  48. package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
  49. package/build/development/shared/components/WithTooltip/index.js +11 -10
  50. package/build/development/shared/components/WithTooltip/index.js.map +1 -1
  51. package/build/development/shared/components/YouTubeVideo/index.js +15 -13
  52. package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
  53. package/build/development/shared/components/index.js +2 -2
  54. package/build/development/shared/components/index.js.map +1 -1
  55. package/build/development/shared/utils/config.js.map +1 -1
  56. package/build/development/shared/utils/globalState.js +15 -0
  57. package/build/development/shared/utils/globalState.js.map +1 -0
  58. package/build/development/shared/utils/index.js +13 -10
  59. package/build/development/shared/utils/index.js.map +1 -1
  60. package/build/development/shared/utils/isomorphy/buildInfo.js +7 -3
  61. package/build/development/shared/utils/isomorphy/buildInfo.js.map +1 -1
  62. package/build/development/shared/utils/isomorphy/environment-check.js.map +1 -1
  63. package/build/development/shared/utils/isomorphy/index.js.map +1 -1
  64. package/build/development/shared/utils/jest/E2eSsrEnv.js +35 -28
  65. package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  66. package/build/development/shared/utils/jest/global.js +17 -0
  67. package/build/development/shared/utils/jest/global.js.map +1 -0
  68. package/build/development/shared/utils/jest/index.js +17 -9
  69. package/build/development/shared/utils/jest/index.js.map +1 -1
  70. package/build/development/shared/utils/splitComponent.js +25 -34
  71. package/build/development/shared/utils/splitComponent.js.map +1 -1
  72. package/build/development/shared/utils/time.js +16 -12
  73. package/build/development/shared/utils/time.js.map +1 -1
  74. package/build/development/shared/utils/webpack.js +3 -3
  75. package/build/development/shared/utils/webpack.js.map +1 -1
  76. package/build/development/web.bundle.js +92 -82
  77. package/build/production/client/getInj.js +1 -1
  78. package/build/production/client/getInj.js.map +1 -1
  79. package/build/production/client/index.js +4 -4
  80. package/build/production/client/index.js.map +1 -1
  81. package/build/production/client/init.js +3 -1
  82. package/build/production/client/init.js.map +1 -1
  83. package/build/production/index.js +1 -1
  84. package/build/production/index.js.map +1 -1
  85. package/build/production/server/Cache.js +7 -8
  86. package/build/production/server/Cache.js.map +1 -1
  87. package/build/production/server/index.js +5 -4
  88. package/build/production/server/index.js.map +1 -1
  89. package/build/production/server/renderer.js +32 -30
  90. package/build/production/server/renderer.js.map +1 -1
  91. package/build/production/server/server.js +7 -5
  92. package/build/production/server/server.js.map +1 -1
  93. package/build/production/server/utils/errors.js +9 -10
  94. package/build/production/server/utils/errors.js.map +1 -1
  95. package/build/production/server/utils/index.js +1 -1
  96. package/build/production/server/utils/index.js.map +1 -1
  97. package/build/production/shared/components/Button/index.js +3 -3
  98. package/build/production/shared/components/Button/index.js.map +1 -1
  99. package/build/production/shared/components/Checkbox/index.js +11 -11
  100. package/build/production/shared/components/Checkbox/index.js.map +1 -1
  101. package/build/production/shared/components/Dropdown/index.js +11 -11
  102. package/build/production/shared/components/Dropdown/index.js.map +1 -1
  103. package/build/production/shared/components/GenericLink/index.js +25 -20
  104. package/build/production/shared/components/GenericLink/index.js.map +1 -1
  105. package/build/production/shared/components/Input/index.js +7 -7
  106. package/build/production/shared/components/Input/index.js.map +1 -1
  107. package/build/production/shared/components/Link.js +2 -2
  108. package/build/production/shared/components/Link.js.map +1 -1
  109. package/build/production/shared/components/MetaTags.js +10 -10
  110. package/build/production/shared/components/MetaTags.js.map +1 -1
  111. package/build/production/shared/components/Modal/index.js +2 -2
  112. package/build/production/shared/components/Modal/index.js.map +1 -1
  113. package/build/production/shared/components/NavLink.js +1 -1
  114. package/build/production/shared/components/NavLink.js.map +1 -1
  115. package/build/production/shared/components/PageLayout/index.js +2 -2
  116. package/build/production/shared/components/PageLayout/index.js.map +1 -1
  117. package/build/production/shared/components/ScalableRect/index.js +7 -3
  118. package/build/production/shared/components/ScalableRect/index.js.map +1 -1
  119. package/build/production/shared/components/Throbber/index.js +2 -2
  120. package/build/production/shared/components/Throbber/index.js.map +1 -1
  121. package/build/production/shared/components/WithTooltip/Tooltip.js +13 -13
  122. package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
  123. package/build/production/shared/components/WithTooltip/index.js +3 -3
  124. package/build/production/shared/components/WithTooltip/index.js.map +1 -1
  125. package/build/production/shared/components/YouTubeVideo/index.js +7 -7
  126. package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
  127. package/build/production/shared/components/index.js +1 -1
  128. package/build/production/shared/components/index.js.map +1 -1
  129. package/build/production/shared/utils/config.js.map +1 -1
  130. package/build/production/shared/utils/globalState.js +3 -0
  131. package/build/production/shared/utils/globalState.js.map +1 -0
  132. package/build/production/shared/utils/index.js +2 -2
  133. package/build/production/shared/utils/index.js.map +1 -1
  134. package/build/production/shared/utils/isomorphy/buildInfo.js +7 -4
  135. package/build/production/shared/utils/isomorphy/buildInfo.js.map +1 -1
  136. package/build/production/shared/utils/isomorphy/environment-check.js.map +1 -1
  137. package/build/production/shared/utils/isomorphy/index.js.map +1 -1
  138. package/build/production/shared/utils/jest/E2eSsrEnv.js +9 -9
  139. package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  140. package/build/production/shared/utils/jest/global.js +2 -0
  141. package/build/production/shared/utils/jest/global.js.map +1 -0
  142. package/build/production/shared/utils/jest/index.js +5 -5
  143. package/build/production/shared/utils/jest/index.js.map +1 -1
  144. package/build/production/shared/utils/splitComponent.js +16 -16
  145. package/build/production/shared/utils/splitComponent.js.map +1 -1
  146. package/build/production/shared/utils/time.js +12 -8
  147. package/build/production/shared/utils/time.js.map +1 -1
  148. package/build/production/shared/utils/webpack.js +3 -3
  149. package/build/production/shared/utils/webpack.js.map +1 -1
  150. package/build/production/web.bundle.js +1 -1
  151. package/build/production/web.bundle.js.map +1 -1
  152. package/build/types-code/client/getInj.d.ts +3 -0
  153. package/build/types-code/client/index.d.ts +11 -0
  154. package/build/types-code/client/init.d.ts +9 -0
  155. package/build/types-code/index.d.ts +10 -0
  156. package/build/types-code/server/Cache.d.ts +37 -0
  157. package/build/types-code/server/index.d.ts +145 -0
  158. package/build/types-code/server/renderer.d.ts +106 -0
  159. package/build/types-code/server/server.d.ts +41 -0
  160. package/build/types-code/server/utils/errors.d.ts +104 -0
  161. package/build/types-code/server/utils/index.d.ts +1 -0
  162. package/build/types-code/shared/components/Button/index.d.ts +27 -0
  163. package/build/types-code/shared/components/Checkbox/index.d.ts +21 -0
  164. package/build/types-code/shared/components/Dropdown/index.d.ts +23 -0
  165. package/build/types-code/shared/components/GenericLink/index.d.ts +61 -0
  166. package/build/types-code/shared/components/Input/index.d.ts +11 -0
  167. package/build/types-code/shared/components/Link.d.ts +12 -0
  168. package/build/types-code/shared/components/MetaTags.d.ts +68 -0
  169. package/build/types-code/shared/components/Modal/index.d.ts +26 -0
  170. package/build/types-code/shared/components/NavLink.d.ts +5 -0
  171. package/build/types-code/shared/components/PageLayout/index.d.ts +16 -0
  172. package/build/types-code/shared/components/ScalableRect/index.d.ts +19 -0
  173. package/build/types-code/shared/components/Throbber/index.d.ts +9 -0
  174. package/build/types-code/shared/components/WithTooltip/Tooltip.d.ts +23 -0
  175. package/build/types-code/shared/components/WithTooltip/index.d.ts +17 -0
  176. package/build/types-code/shared/components/YouTubeVideo/index.d.ts +13 -0
  177. package/build/types-code/shared/components/index.d.ts +16 -0
  178. package/build/types-code/shared/utils/config.d.ts +2 -0
  179. package/build/types-code/shared/utils/globalState.d.ts +20 -0
  180. package/build/types-code/shared/utils/index.d.ts +52 -0
  181. package/build/types-code/shared/utils/isomorphy/buildInfo.d.ts +23 -0
  182. package/build/types-code/shared/utils/isomorphy/environment-check.d.ts +11 -0
  183. package/build/types-code/shared/utils/isomorphy/index.d.ts +20 -0
  184. package/build/types-code/shared/utils/jest/E2eSsrEnv.d.ts +31 -0
  185. package/build/types-code/shared/utils/jest/global.d.ts +12 -0
  186. package/build/types-code/shared/utils/jest/index.d.ts +85 -0
  187. package/build/types-code/shared/utils/splitComponent.d.ts +41 -0
  188. package/build/types-code/shared/utils/time.d.ts +62 -0
  189. package/build/types-code/shared/utils/webpack.d.ts +18 -0
  190. package/build/types-scss/__tests__/js/config/publicPath support/__assets__/style.scss.d.ts +1 -0
  191. package/build/types-scss/__tests__/js/config/stylename-generation/__assets__/MockPackageA/TestComponent/style.scss.d.ts +1 -0
  192. package/build/types-scss/__tests__/js/config/stylename-generation/__assets__/MockPackageB/TestComponent/style.scss.d.ts +1 -0
  193. package/build/types-scss/__tests__/js/config/stylename-generation/__assets__/style.scss.d.ts +1 -0
  194. package/build/types-scss/__tests__/js/shared/components/NavLink/styles.scss.d.ts +1 -0
  195. package/build/types-scss/__tests__/js/shared/utils/splitComponent/__assets__/SampleScene/ComponentA/style.scss.d.ts +1 -0
  196. package/build/types-scss/__tests__/js/shared/utils/splitComponent/__assets__/SampleScene/ComponentB/style.scss.d.ts +1 -0
  197. package/build/types-scss/__tests__/js/shared/utils/splitComponent/__assets__/SampleScene/ComponentC/style.scss.d.ts +1 -0
  198. package/build/types-scss/__tests__/js/shared/utils/splitComponent/__assets__/SampleScene/style.scss.d.ts +1 -0
  199. package/build/types-scss/__tests__/ts/config/publicPath support/__assets__/style.scss.d.ts +1 -0
  200. package/build/types-scss/__tests__/ts/config/stylename-generation/__assets__/MockPackageA/TestComponent/style.scss.d.ts +1 -0
  201. package/build/types-scss/__tests__/ts/config/stylename-generation/__assets__/MockPackageB/TestComponent/style.scss.d.ts +1 -0
  202. package/build/types-scss/__tests__/ts/config/stylename-generation/__assets__/style.scss.d.ts +1 -0
  203. package/build/types-scss/__tests__/ts/shared/components/NavLink/styles.scss.d.ts +1 -0
  204. package/build/types-scss/__tests__/ts/shared/utils/splitComponent/__assets__/SampleScene/ComponentA/style.scss.d.ts +1 -0
  205. package/build/types-scss/__tests__/ts/shared/utils/splitComponent/__assets__/SampleScene/ComponentB/style.scss.d.ts +1 -0
  206. package/build/types-scss/__tests__/ts/shared/utils/splitComponent/__assets__/SampleScene/ComponentC/style.scss.d.ts +1 -0
  207. package/build/types-scss/__tests__/ts/shared/utils/splitComponent/__assets__/SampleScene/style.scss.d.ts +1 -0
  208. package/build/types-scss/src/shared/components/Button/style.scss.d.ts +6 -0
  209. package/build/types-scss/src/shared/components/Checkbox/theme.scss.d.ts +6 -0
  210. package/build/types-scss/src/shared/components/Dropdown/theme.scss.d.ts +9 -0
  211. package/build/types-scss/src/shared/components/GenericLink/style.scss.d.ts +1 -0
  212. package/build/types-scss/src/shared/components/Input/theme.scss.d.ts +6 -0
  213. package/build/types-scss/src/shared/components/Modal/base-theme.scss.d.ts +5 -0
  214. package/build/types-scss/src/shared/components/PageLayout/base-theme.scss.d.ts +6 -0
  215. package/build/types-scss/src/shared/components/ScalableRect/style.scss.d.ts +2 -0
  216. package/build/types-scss/src/shared/components/Throbber/theme.scss.d.ts +6 -0
  217. package/build/types-scss/src/shared/components/WithTooltip/default-theme.scss.d.ts +7 -0
  218. package/build/types-scss/src/shared/components/YouTubeVideo/base.scss.d.ts +5 -0
  219. package/build/types-scss/src/shared/components/YouTubeVideo/throbber.scss.d.ts +4 -0
  220. package/config/eslint/jest.json +3 -2
  221. package/config/eslint/typescript.js +34 -0
  222. package/config/jest/default.js +3 -3
  223. package/package.json +74 -32
  224. package/src/client/getInj.ts +43 -0
  225. package/src/client/index.tsx +40 -0
  226. package/src/client/init.ts +47 -0
  227. package/src/index.ts +58 -0
  228. package/src/server/Cache.ts +68 -0
  229. package/src/server/index.ts +230 -0
  230. package/src/server/renderer.tsx +604 -0
  231. package/src/server/server.ts +309 -0
  232. package/src/server/utils/errors.ts +135 -0
  233. package/src/server/utils/index.ts +3 -0
  234. package/src/shared/components/Button/index.tsx +146 -0
  235. package/src/shared/components/Button/style.scss +53 -0
  236. package/src/shared/components/Checkbox/index.tsx +71 -0
  237. package/src/shared/components/Checkbox/theme.scss +43 -0
  238. package/src/shared/components/Dropdown/index.tsx +144 -0
  239. package/src/shared/components/Dropdown/theme.scss +63 -0
  240. package/src/shared/components/GenericLink/index.tsx +157 -0
  241. package/src/shared/components/GenericLink/style.scss +3 -0
  242. package/src/shared/components/Input/index.tsx +59 -0
  243. package/src/shared/components/Input/theme.scss +27 -0
  244. package/src/shared/components/Link.tsx +21 -0
  245. package/src/shared/components/MetaTags.tsx +170 -0
  246. package/src/shared/components/Modal/base-theme.scss +38 -0
  247. package/src/shared/components/Modal/index.tsx +144 -0
  248. package/src/shared/components/Modal/styles.scss +5 -0
  249. package/src/shared/components/NavLink.tsx +13 -0
  250. package/src/shared/components/PageLayout/base-theme.scss +30 -0
  251. package/src/shared/components/PageLayout/index.tsx +76 -0
  252. package/src/shared/components/ScalableRect/index.tsx +84 -0
  253. package/src/shared/components/ScalableRect/style.scss +10 -0
  254. package/src/shared/components/Throbber/index.tsx +43 -0
  255. package/src/shared/components/Throbber/theme.scss +26 -0
  256. package/src/shared/components/WithTooltip/Tooltip.tsx +353 -0
  257. package/src/shared/components/WithTooltip/default-theme.scss +36 -0
  258. package/src/shared/components/WithTooltip/index.tsx +204 -0
  259. package/src/shared/components/YouTubeVideo/base.scss +13 -0
  260. package/src/shared/components/YouTubeVideo/index.tsx +96 -0
  261. package/src/shared/components/YouTubeVideo/throbber.scss +11 -0
  262. package/src/shared/components/index.ts +17 -0
  263. package/src/shared/utils/config.ts +21 -0
  264. package/src/shared/utils/globalState.ts +29 -0
  265. package/src/shared/utils/index.ts +105 -0
  266. package/src/shared/utils/isomorphy/buildInfo.ts +54 -0
  267. package/src/shared/utils/isomorphy/environment-check.ts +18 -0
  268. package/src/shared/utils/isomorphy/index.ts +38 -0
  269. package/src/shared/utils/jest/E2eSsrEnv.ts +250 -0
  270. package/src/shared/utils/jest/global.ts +19 -0
  271. package/src/shared/utils/jest/index.tsx +157 -0
  272. package/src/shared/utils/splitComponent.tsx +255 -0
  273. package/src/shared/utils/time.ts +118 -0
  274. package/src/shared/utils/webpack.ts +45 -0
  275. package/src/styles/_global/reset.css +52 -0
  276. package/src/styles/global.scss +11 -0
  277. package/tsconfig.configs.json +18 -0
  278. package/tsconfig.json +27 -0
  279. package/tsconfig.types.json +53 -0
  280. package/typed-scss-modules.config.ts +9 -0
  281. package/types.d.ts +37 -0
  282. package/{webpack.config.js → webpack.config.ts} +7 -3
  283. package/config/babel/node-ssr.js +0 -85
  284. package/config/babel/webpack.js +0 -123
  285. package/config/webpack/app-base.js +0 -330
  286. package/config/webpack/app-development.js +0 -80
  287. package/config/webpack/app-production.js +0 -60
  288. package/config/webpack/lib-base.js +0 -155
  289. package/config/webpack/lib-development.js +0 -45
  290. package/config/webpack/lib-production.js +0 -44
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Just an aggregation of all exported components into a single module.
3
+ */
4
+
5
+ export { default as Button } from 'components/Button';
6
+ export { default as Checkbox } from 'components/Checkbox';
7
+ export { default as Dropdown } from 'components/Dropdown';
8
+ export { default as Input } from 'components/Input';
9
+ export { default as Link } from 'components/Link';
10
+ export { default as PageLayout } from 'components/PageLayout';
11
+ export { default as MetaTags } from 'components/MetaTags';
12
+ export { default as Modal, BaseModal } from 'components/Modal';
13
+ export { default as NavLink } from 'components/NavLink';
14
+ export { default as ScalableRect } from 'components/ScalableRect';
15
+ export { default as Throbber } from 'components/Throbber';
16
+ export { default as WithTooltip } from 'components/WithTooltip';
17
+ export { default as YouTubeVideo } from 'components/YouTubeVideo';
@@ -0,0 +1,21 @@
1
+ /* global document */
2
+
3
+ import { IS_CLIENT_SIDE } from './isomorphy/environment-check';
4
+ import { requireWeak } from './webpack';
5
+
6
+ const config = (
7
+ IS_CLIENT_SIDE
8
+ // eslint-disable-next-line global-require
9
+ ? require('client/getInj').default().CONFIG
10
+ : requireWeak('config')
11
+ ) || {};
12
+
13
+ // The safeguard for "document" is necessary because in non-Node environments,
14
+ // like React Native, IS_CLIENT_SIDE is "true", however "document" and a bunch
15
+ // of other browser-world features are not available.
16
+ if (IS_CLIENT_SIDE && typeof document !== 'undefined') {
17
+ const cookie = require('cookie'); // eslint-disable-line global-require
18
+ config.CSRF = cookie.parse(document.cookie).csrfToken;
19
+ }
20
+
21
+ export default config;
@@ -0,0 +1,29 @@
1
+ import type { Request } from 'express';
2
+
3
+ import RGS, { type API, type SsrContext } from '@dr.pogodin/react-global-state';
4
+
5
+ export type ChunkGroupsT = {
6
+ [chunkName: string]: string[];
7
+ };
8
+
9
+ // The type of data object injected by server into generated markup.
10
+ export type InjT = {
11
+ CHUNK_GROUPS?: ChunkGroupsT;
12
+ CONFIG?: { [key: string]: any },
13
+ ISTATE?: unknown;
14
+ };
15
+
16
+ export interface SsrContextT<StateT> extends SsrContext<StateT> {
17
+ chunkGroups: ChunkGroupsT;
18
+ chunks: string[];
19
+ req: Request,
20
+ status: number;
21
+ }
22
+
23
+ const {
24
+ getSsrContext,
25
+ } = (RGS as unknown) as API<unknown, SsrContextT<unknown>>;
26
+
27
+ export {
28
+ getSsrContext,
29
+ };
@@ -0,0 +1,105 @@
1
+ import themedImpl, {
2
+ type Theme,
3
+ COMPOSE,
4
+ PRIORITY,
5
+ ThemeProvider,
6
+ } from '@dr.pogodin/react-themes';
7
+
8
+ import config from './config';
9
+ import * as isomorphy from './isomorphy';
10
+ import time from './time';
11
+ import * as webpack from './webpack';
12
+
13
+ import type * as JuT from './jest';
14
+
15
+ export { Barrier, Emitter, Semaphore } from '@dr.pogodin/js-utils';
16
+
17
+ export { getSsrContext } from './globalState';
18
+ export { default as splitComponent } from './splitComponent';
19
+
20
+ type ThemedT = typeof themedImpl & {
21
+ COMPOSE: typeof COMPOSE;
22
+ PRIORITY: typeof PRIORITY;
23
+ };
24
+
25
+ const themed: ThemedT = themedImpl as ThemedT;
26
+
27
+ themed.COMPOSE = COMPOSE;
28
+ themed.PRIORITY = PRIORITY;
29
+
30
+ // Note: it should be done this way, as in some environments
31
+ // "process" might not exist, and process.env.NODE_CONFIG_ENV
32
+ // not injected by Webpack.
33
+ let NODE_CONFIG_ENV;
34
+ try {
35
+ NODE_CONFIG_ENV = process.env.NODE_CONFIG_ENV;
36
+ } catch { /* noop */ }
37
+
38
+ const env = NODE_CONFIG_ENV || process.env.NODE_ENV;
39
+
40
+ const JU: typeof JuT | null = env !== 'production'
41
+ ? webpack.requireWeak('./jest', __dirname) as typeof JuT | null
42
+ : null;
43
+
44
+ /**
45
+ * @category Utilities
46
+ * @global
47
+ * @func withRetries
48
+ * @desc
49
+ * ```js
50
+ * import { withRetries } from '@dr.pogodin/react-utils';
51
+ * ```
52
+ * Attempts to perform given asynchronous `action` up to `maxRetries` times,
53
+ * with the given `interval` between attempts. If any attempt is successful,
54
+ * the result is returned immediately, with no further attempts done;
55
+ * otherwise, if all attempts fail, the result Promise rejects after the last
56
+ * attempt.
57
+ * @param {function} action
58
+ * @param {number} [maxRetries=5] Optional. Maximum number of retries. Defaults
59
+ * to 5 attempts.
60
+ * @param {number} [interval=1000] Optional. Interval between retries [ms].
61
+ * Defaults to 1 second.
62
+ * @return {Promise} Resolves to the result of successful operation, or
63
+ * rejects with the error from the latst failed attempt.
64
+ * @example
65
+ * import { withRetries } from '@dr.pogodin/react-utils';
66
+ *
67
+ * let firstCall = true;
68
+ *
69
+ * function sampleAction() {
70
+ * if (!firstCall) return 'success';
71
+ * firstCall = false;
72
+ * throw Error('The first call to this method fails');
73
+ * }
74
+ *
75
+ * withRetries(sampleAction).then(console.log);
76
+ * // It will print 'success' after one second, once the second attempt
77
+ * // is performed.
78
+ */
79
+ export async function withRetries(
80
+ action: () => Promise<unknown>,
81
+ maxRetries = 5,
82
+ interval = 1000,
83
+ ) {
84
+ /* eslint-disable no-await-in-loop */
85
+ for (let n = 1; ; ++n) {
86
+ try {
87
+ return await action();
88
+ } catch (error) {
89
+ if (n < maxRetries) await time.timer(interval);
90
+ else throw error;
91
+ }
92
+ }
93
+ /* eslint-enable no-await-in-loop */
94
+ }
95
+
96
+ export {
97
+ type Theme,
98
+ config,
99
+ isomorphy,
100
+ JU,
101
+ themed,
102
+ ThemeProvider,
103
+ time,
104
+ webpack,
105
+ };
@@ -0,0 +1,54 @@
1
+ // Encapsulates access to "Build Info" data.
2
+
3
+ /* global BUILD_INFO */
4
+
5
+ // BEWARE: This should match the type of build info object generated by
6
+ // Webpack build (see "/config/webpack/app-base.js"), and currently this
7
+ // match is not checked automatically.
8
+ export type BuildInfoT = {
9
+ key: string;
10
+ publicPath: string;
11
+ timestamp: string;
12
+ useServiceWorker: boolean;
13
+ };
14
+
15
+ declare global {
16
+ // Depending on the build mode & environment, BUILD_INFO is either a global
17
+ // variable defined at the app launch, or it is replaced by the actual value
18
+ // by the Webpack build.
19
+ const BUILD_INFO: BuildInfoT | undefined;
20
+ }
21
+
22
+ let buildInfo: BuildInfoT | undefined;
23
+
24
+ // On the client side "BUILD_INFO" should be injected by Webpack. Note, however,
25
+ // that in test environment we may need situations were environment is mocked as
26
+ // client-side, although no proper Webpack compilation is executed, thus no info
27
+ // injected; because of this we don't do a hard environment check here.
28
+ if (typeof BUILD_INFO !== 'undefined') buildInfo = BUILD_INFO;
29
+
30
+ /**
31
+ * In scenarious where "BUILD_INFO" is not injected by Webpack (server-side,
32
+ * tests, etc.) we expect the host codebase to explicitly set it before it is
33
+ * ever requested. As a precaution, this function throws if build info has been
34
+ * set already, unless `force` flag is explicitly set.
35
+ * @param info
36
+ * @param force
37
+ */
38
+ export function setBuildInfo(info?: BuildInfoT, force = false) {
39
+ if (buildInfo !== undefined && !force) {
40
+ throw Error('"Build Info" is already initialized');
41
+ }
42
+ buildInfo = info;
43
+ }
44
+
45
+ /**
46
+ * Returns "Build Info" object; throws if it has not been initialized yet.
47
+ * @returns
48
+ */
49
+ export function getBuildInfo(): BuildInfoT {
50
+ if (buildInfo === undefined) {
51
+ throw Error('"Build Info" has not been initialized yet');
52
+ }
53
+ return buildInfo;
54
+ }
@@ -0,0 +1,18 @@
1
+ // Checks for client- vs. server-side environment detection.
2
+
3
+ declare global {
4
+ // eslint-disable-next-line no-var, vars-on-top
5
+ var REACT_UTILS_FORCE_CLIENT_SIDE: boolean | undefined;
6
+ }
7
+
8
+ /**
9
+ * `true` within client-side environment (browser), `false` at server-side.
10
+ */
11
+ export const IS_CLIENT_SIDE: boolean = typeof process !== 'object'
12
+ || !process.versions || !process.versions.node
13
+ || !!global.REACT_UTILS_FORCE_CLIENT_SIDE;
14
+
15
+ /**
16
+ * `true` within the server-side environment (node), `false` at client-side.
17
+ */
18
+ export const IS_SERVER_SIDE: boolean = !IS_CLIENT_SIDE;
@@ -0,0 +1,38 @@
1
+ import { getBuildInfo } from './buildInfo';
2
+ import { IS_CLIENT_SIDE, IS_SERVER_SIDE } from './environment-check';
3
+
4
+ /**
5
+ * @ignore
6
+ * @return {string} Code mode: "development" or "production".
7
+ */
8
+ function getMode() {
9
+ return process.env.NODE_ENV;
10
+ }
11
+
12
+ /**
13
+ * Returns `true` if development version of the code is running;
14
+ * `false` otherwise.
15
+ * @return {boolean}
16
+ */
17
+ export function isDevBuild() {
18
+ return getMode() === 'development';
19
+ }
20
+
21
+ /**
22
+ * Returns `true` if production build of the code is running;
23
+ * `false` otherwise.
24
+ * @return {boolean}
25
+ */
26
+ export function isProdBuild() {
27
+ return getMode() === 'production';
28
+ }
29
+
30
+ /**
31
+ * Returns build timestamp of the front-end JS bundle.
32
+ * @return {string} ISO date/time string.
33
+ */
34
+ export function buildTimestamp() {
35
+ return getBuildInfo().timestamp;
36
+ }
37
+
38
+ export { IS_CLIENT_SIDE, IS_SERVER_SIDE, getBuildInfo };
@@ -0,0 +1,250 @@
1
+ /**
2
+ * Jest environment for end-to-end SSR and client-side testing. It relies on
3
+ * the standard react-utils mechanics to execute SSR of given scene, and also
4
+ * Webpack build of the code for client-side execution, it further exposes
5
+ * Jsdom environment for the client-side testing of the outcomes.
6
+ */
7
+ /* eslint-disable global-require, import/no-dynamic-require */
8
+
9
+ // BEWARE: The module is not imported into the JU module / the main assembly of
10
+ // the library, because doing so easily breaks stuff:
11
+ // 1) This module depends on Node-specific modules, which would make JU
12
+ // incompatible with JsDom if included into JU.
13
+ // 2) If this module is weakly imported from somewhere else in the lib,
14
+ // it seems to randomly break tests using it for a different reason,
15
+ // probably some sort of a require-loop, or some issues with weak
16
+ // require in that scenario.
17
+
18
+ import path from 'path';
19
+
20
+ import type { Request, Response } from 'express';
21
+ import { defaults, noop, set } from 'lodash';
22
+
23
+ // As this environment is a part of the Jest testing utils,
24
+ // we assume development dependencies are available when it is used.
25
+ /* eslint-disable import/no-extraneous-dependencies */
26
+ import register from '@babel/register/experimental-worker';
27
+
28
+ import JsdomEnv from 'jest-environment-jsdom';
29
+ import { type IFs, createFsFromVolume, Volume } from 'memfs';
30
+ import webpack from 'webpack';
31
+ /* eslint-enable import/no-extraneous-dependencies */
32
+
33
+ import ssrFactory from 'server/renderer';
34
+
35
+ import type {
36
+ EnvironmentContext,
37
+ JestEnvironmentConfig,
38
+ } from '@jest/environment';
39
+
40
+ export default class E2eSsrEnv extends JsdomEnv {
41
+ pragmas: Record<string, string | string[]>;
42
+
43
+ ssrRequest: object;
44
+
45
+ rootDir: string;
46
+
47
+ testFolder: string;
48
+
49
+ withSsr: boolean;
50
+
51
+ webpackStats?: webpack.StatsCompilation;
52
+
53
+ /**
54
+ * Loads Webpack config, and exposes it to the environment via global
55
+ * webpackConfig object.
56
+ */
57
+ loadWebpackConfig() {
58
+ const optionsString = this.pragmas['webpack-config-options'] as string;
59
+
60
+ const options = (optionsString
61
+ ? JSON.parse(optionsString) : {}) as webpack.Configuration;
62
+
63
+ defaults(options, {
64
+ context: this.testFolder,
65
+ fs: this.global.webpackOutputFs,
66
+ });
67
+
68
+ const factoryPath = this.pragmas['webpack-config-factory'] as string;
69
+ let factory = require(path.resolve(this.rootDir, factoryPath));
70
+ factory = 'default' in factory ? factory.default : factory;
71
+
72
+ this.global.webpackConfig = factory(options);
73
+
74
+ const fs = this.global.webpackOutputFs as IFs;
75
+ let buildInfo = `${options.context}/.build-info`;
76
+ if (fs.existsSync(buildInfo)) {
77
+ buildInfo = fs.readFileSync(buildInfo, 'utf8') as string;
78
+ this.global.buildInfo = JSON.parse(buildInfo);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Executes Webpack build.
84
+ * @return {Promise}
85
+ */
86
+ async runWebpack() {
87
+ this.loadWebpackConfig();
88
+
89
+ const compiler = webpack(this.global.webpackConfig as webpack.Configuration);
90
+ compiler.outputFileSystem = this.global.webpackOutputFs;
91
+ return new Promise<void>((done, fail) => {
92
+ compiler.run((err, stats) => {
93
+ if (err) fail(err);
94
+ if (stats?.hasErrors()) {
95
+ // eslint-disable-next-line no-console
96
+ console.error(stats.toJson().errors);
97
+ fail(Error('Webpack compilation failed'));
98
+ }
99
+
100
+ this.global.webpackStats = stats?.toJson();
101
+
102
+ // Keeps reference to the raw Webpack stats object, which should be
103
+ // explicitly passed to the server-side renderer alongside the request,
104
+ // so that it can to pick up asset paths for different named chunks.
105
+ this.webpackStats = stats;
106
+
107
+ done();
108
+ });
109
+ });
110
+ }
111
+
112
+ async runSsr() {
113
+ const optionsString = this.pragmas['ssr-options'] as string;
114
+ const options = optionsString ? JSON.parse(optionsString) : {};
115
+
116
+ // TODO: This is temporary to shortcut the logging added to SSR.
117
+ if (options.logger === undefined) {
118
+ options.logger = {
119
+ debug: noop,
120
+ info: noop,
121
+ log: noop,
122
+ warn: noop,
123
+ };
124
+ }
125
+
126
+ if (!options.buildInfo) options.buildInfo = this.global.buildInfo;
127
+
128
+ let cleanup: (() => void) | undefined;
129
+
130
+ if (options.entry) {
131
+ const p = path.resolve(this.testFolder, options.entry);
132
+ const module = require(p);
133
+ cleanup = module.cleanup;
134
+ options.Application = module[options.entryExportName || 'default'];
135
+ }
136
+
137
+ const renderer = ssrFactory(this.global.webpackConfig!, options);
138
+ let status = 200; // OK
139
+ const markup = await new Promise<string>((done, fail) => {
140
+ renderer(
141
+ this.ssrRequest as Request,
142
+
143
+ // TODO: This will do for now, with the current implementation of
144
+ // the renderer, but it will require a rework once the renderer is
145
+ // updated to do streaming.
146
+ ({
147
+ cookie: noop,
148
+ send: done,
149
+ set: noop,
150
+ status: (value: number) => {
151
+ status = value;
152
+ },
153
+
154
+ // This is how up-to-date Webpack stats are passed to the server in
155
+ // development mode, and we use this here always, instead of having
156
+ // to pass some information via filesystem.
157
+ locals: {
158
+ webpack: {
159
+ devMiddleware: {
160
+ stats: this.webpackStats,
161
+ },
162
+ },
163
+ },
164
+ } as unknown) as Response,
165
+
166
+ (error) => {
167
+ if (error) fail(error);
168
+ else done('');
169
+ },
170
+ );
171
+ });
172
+
173
+ this.global.ssrMarkup = markup;
174
+ this.global.ssrOptions = options;
175
+ this.global.ssrStatus = status;
176
+
177
+ if (cleanup) cleanup();
178
+ }
179
+
180
+ constructor(
181
+ config: JestEnvironmentConfig,
182
+ context: EnvironmentContext,
183
+ ) {
184
+ const pragmas = context.docblockPragmas;
185
+
186
+ const requestString = pragmas['ssr-request'] as string;
187
+ const request = requestString ? JSON.parse(requestString) : {};
188
+
189
+ if (!request.url) request.url = '/';
190
+ request.csrfToken = noop;
191
+
192
+ // This ensures the initial JsDom URL matches the value we use for SSR.
193
+ set(
194
+ config.projectConfig,
195
+ 'testEnvironmentOptions.url',
196
+ `http://localhost${request.url}`,
197
+ );
198
+
199
+ super(config, context);
200
+
201
+ this.global.dom = this.dom;
202
+ this.global.webpackOutputFs = createFsFromVolume(new Volume());
203
+
204
+ // Extracts necessary settings from config and context.
205
+ const { projectConfig } = config;
206
+ this.rootDir = projectConfig.rootDir;
207
+ this.testFolder = path.dirname(context.testPath);
208
+ this.withSsr = !pragmas['no-ssr'];
209
+ this.ssrRequest = request;
210
+ this.pragmas = pragmas;
211
+
212
+ // The usual "babel-jest" transformation setup does not apply to
213
+ // the environment code and imports from it, this workaround enables it.
214
+ const optionsString = this.pragmas['ssr-options'] as string;
215
+ const options = optionsString ? JSON.parse(optionsString) : {};
216
+ let root;
217
+ switch (options.root) {
218
+ case 'TEST': root = this.testFolder; break;
219
+ default: root = process.cwd();
220
+ }
221
+ register({
222
+ envName: options.babelEnv,
223
+ extensions: ['.js', '.jsx', '.ts', '.tsx', '.svg'],
224
+ root,
225
+ });
226
+ }
227
+
228
+ async setup() {
229
+ await super.setup();
230
+ await this.runWebpack();
231
+ if (this.withSsr) await this.runSsr();
232
+ this.global.REACT_UTILS_FORCE_CLIENT_SIDE = true;
233
+ }
234
+
235
+ async teardown() {
236
+ delete this.global.REACT_UTILS_FORCE_CLIENT_SIDE;
237
+
238
+ // Resets module cache and @babel/register. Effectively this ensures that
239
+ // the next time an instance of this environment is set up, all modules are
240
+ // transformed by Babel from scratch, thus taking into account the latest
241
+ // Babel config (which may change between different environment instances,
242
+ // which does not seem to be taken into account by Babel / Node caches
243
+ // automatically).
244
+ Object.keys(require.cache).forEach((key) => {
245
+ delete require.cache[key];
246
+ });
247
+ register.revert();
248
+ super.teardown();
249
+ }
250
+ }
@@ -0,0 +1,19 @@
1
+ import { type IFs } from 'memfs';
2
+
3
+ /* eslint-disable import/no-extraneous-dependencies */
4
+ import webpack from 'webpack';
5
+ /* eslint-enable import/no-extraneous-dependencies */
6
+
7
+ declare global {
8
+ interface Window {
9
+ ssrMarkup: string | undefined;
10
+ ssrStatus: number | undefined;
11
+ webpackConfig: webpack.Configuration | undefined;
12
+ webpackOutputFs: IFs;
13
+ webpackStats?: webpack.StatsCompilation;
14
+ }
15
+ }
16
+
17
+ export default function getGlobal(): Window {
18
+ return global as any;
19
+ }