@dr.pogodin/react-utils 1.41.17 → 1.42.1

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 (230) hide show
  1. package/babel.config.js +3 -0
  2. package/bin/build.js +5 -8
  3. package/bin/setup.js +2 -1
  4. package/build/development/client/getInj.js +7 -2
  5. package/build/development/client/getInj.js.map +1 -1
  6. package/build/development/client/index.js +1 -2
  7. package/build/development/client/index.js.map +1 -1
  8. package/build/development/client/init.js +16 -13
  9. package/build/development/client/init.js.map +1 -1
  10. package/build/development/index.js +4 -1
  11. package/build/development/index.js.map +1 -1
  12. package/build/development/server/Cache.js +5 -9
  13. package/build/development/server/Cache.js.map +1 -1
  14. package/build/development/server/index.js +14 -11
  15. package/build/development/server/index.js.map +1 -1
  16. package/build/development/server/renderer.js +36 -41
  17. package/build/development/server/renderer.js.map +1 -1
  18. package/build/development/server/server.js +22 -13
  19. package/build/development/server/server.js.map +1 -1
  20. package/build/development/server/utils/errors.js.map +1 -1
  21. package/build/development/shared/components/Button/index.js +2 -3
  22. package/build/development/shared/components/Button/index.js.map +1 -1
  23. package/build/development/shared/components/Checkbox/index.js +3 -1
  24. package/build/development/shared/components/Checkbox/index.js.map +1 -1
  25. package/build/development/shared/components/GenericLink/index.js +13 -6
  26. package/build/development/shared/components/GenericLink/index.js.map +1 -1
  27. package/build/development/shared/components/Input/index.js +5 -1
  28. package/build/development/shared/components/Input/index.js.map +1 -1
  29. package/build/development/shared/components/Link.js +5 -6
  30. package/build/development/shared/components/Link.js.map +1 -1
  31. package/build/development/shared/components/MetaTags.js +31 -24
  32. package/build/development/shared/components/MetaTags.js.map +1 -1
  33. package/build/development/shared/components/Modal/index.js +13 -6
  34. package/build/development/shared/components/Modal/index.js.map +1 -1
  35. package/build/development/shared/components/NavLink.js +6 -6
  36. package/build/development/shared/components/NavLink.js.map +1 -1
  37. package/build/development/shared/components/TextArea/index.js +6 -3
  38. package/build/development/shared/components/TextArea/index.js.map +1 -1
  39. package/build/development/shared/components/WithTooltip/Tooltip.js +35 -39
  40. package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
  41. package/build/development/shared/components/WithTooltip/index.js +27 -21
  42. package/build/development/shared/components/WithTooltip/index.js.map +1 -1
  43. package/build/development/shared/components/YouTubeVideo/index.js +4 -3
  44. package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
  45. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js +4 -5
  46. package/build/development/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  47. package/build/development/shared/components/selectors/CustomDropdown/index.js +7 -9
  48. package/build/development/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  49. package/build/development/shared/components/selectors/NativeDropdown/index.js +3 -5
  50. package/build/development/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  51. package/build/development/shared/components/selectors/Switch/index.js +21 -20
  52. package/build/development/shared/components/selectors/Switch/index.js.map +1 -1
  53. package/build/development/shared/utils/config.js +6 -3
  54. package/build/development/shared/utils/config.js.map +1 -1
  55. package/build/development/shared/utils/globalState.js +6 -0
  56. package/build/development/shared/utils/globalState.js.map +1 -1
  57. package/build/development/shared/utils/isomorphy/buildInfo.js.map +1 -1
  58. package/build/development/shared/utils/isomorphy/environment-check.js +6 -1
  59. package/build/development/shared/utils/isomorphy/environment-check.js.map +1 -1
  60. package/build/development/shared/utils/isomorphy/index.js +1 -3
  61. package/build/development/shared/utils/isomorphy/index.js.map +1 -1
  62. package/build/development/shared/utils/jest/E2eSsrEnv.js +26 -17
  63. package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  64. package/build/development/shared/utils/jest/global.js +0 -7
  65. package/build/development/shared/utils/jest/global.js.map +1 -1
  66. package/build/development/shared/utils/jest/index.js +15 -4
  67. package/build/development/shared/utils/jest/index.js.map +1 -1
  68. package/build/development/shared/utils/splitComponent.js +37 -19
  69. package/build/development/shared/utils/splitComponent.js.map +1 -1
  70. package/build/development/shared/utils/time.js +3 -3
  71. package/build/development/shared/utils/time.js.map +1 -1
  72. package/build/development/shared/utils/webpack.js +11 -11
  73. package/build/development/shared/utils/webpack.js.map +1 -1
  74. package/build/development/web.bundle.js +31 -31
  75. package/build/production/client/getInj.js +4 -2
  76. package/build/production/client/getInj.js.map +1 -1
  77. package/build/production/client/index.js +2 -2
  78. package/build/production/client/index.js.map +1 -1
  79. package/build/production/client/init.js +4 -2
  80. package/build/production/client/init.js.map +1 -1
  81. package/build/production/index.js +2 -1
  82. package/build/production/index.js.map +1 -1
  83. package/build/production/server/Cache.js +1 -2
  84. package/build/production/server/Cache.js.map +1 -1
  85. package/build/production/server/index.js +9 -5
  86. package/build/production/server/index.js.map +1 -1
  87. package/build/production/server/renderer.js +24 -21
  88. package/build/production/server/renderer.js.map +1 -1
  89. package/build/production/server/server.js +14 -9
  90. package/build/production/server/server.js.map +1 -1
  91. package/build/production/server/utils/errors.js.map +1 -1
  92. package/build/production/shared/components/Button/index.js +1 -1
  93. package/build/production/shared/components/Button/index.js.map +1 -1
  94. package/build/production/shared/components/Checkbox/index.js +1 -1
  95. package/build/production/shared/components/Checkbox/index.js.map +1 -1
  96. package/build/production/shared/components/GenericLink/index.js +6 -4
  97. package/build/production/shared/components/GenericLink/index.js.map +1 -1
  98. package/build/production/shared/components/Input/index.js +3 -1
  99. package/build/production/shared/components/Input/index.js.map +1 -1
  100. package/build/production/shared/components/Link.js +3 -1
  101. package/build/production/shared/components/Link.js.map +1 -1
  102. package/build/production/shared/components/MetaTags.js +5 -2
  103. package/build/production/shared/components/MetaTags.js.map +1 -1
  104. package/build/production/shared/components/Modal/index.js +6 -2
  105. package/build/production/shared/components/Modal/index.js.map +1 -1
  106. package/build/production/shared/components/NavLink.js +4 -1
  107. package/build/production/shared/components/NavLink.js.map +1 -1
  108. package/build/production/shared/components/TextArea/index.js +4 -4
  109. package/build/production/shared/components/TextArea/index.js.map +1 -1
  110. package/build/production/shared/components/WithTooltip/Tooltip.js +37 -38
  111. package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
  112. package/build/production/shared/components/WithTooltip/index.js +4 -4
  113. package/build/production/shared/components/WithTooltip/index.js.map +1 -1
  114. package/build/production/shared/components/YouTubeVideo/index.js +3 -2
  115. package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
  116. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js +2 -2
  117. package/build/production/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
  118. package/build/production/shared/components/selectors/CustomDropdown/index.js +2 -2
  119. package/build/production/shared/components/selectors/CustomDropdown/index.js.map +1 -1
  120. package/build/production/shared/components/selectors/NativeDropdown/index.js +2 -2
  121. package/build/production/shared/components/selectors/NativeDropdown/index.js.map +1 -1
  122. package/build/production/shared/components/selectors/Switch/index.js +1 -1
  123. package/build/production/shared/components/selectors/Switch/index.js.map +1 -1
  124. package/build/production/shared/utils/config.js +6 -4
  125. package/build/production/shared/utils/config.js.map +1 -1
  126. package/build/production/shared/utils/globalState.js +4 -1
  127. package/build/production/shared/utils/globalState.js.map +1 -1
  128. package/build/production/shared/utils/isomorphy/buildInfo.js.map +1 -1
  129. package/build/production/shared/utils/isomorphy/environment-check.js +5 -1
  130. package/build/production/shared/utils/isomorphy/environment-check.js.map +1 -1
  131. package/build/production/shared/utils/isomorphy/index.js +1 -3
  132. package/build/production/shared/utils/isomorphy/index.js.map +1 -1
  133. package/build/production/shared/utils/jest/E2eSsrEnv.js +15 -8
  134. package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  135. package/build/production/shared/utils/jest/global.js +1 -1
  136. package/build/production/shared/utils/jest/global.js.map +1 -1
  137. package/build/production/shared/utils/jest/index.js +13 -5
  138. package/build/production/shared/utils/jest/index.js.map +1 -1
  139. package/build/production/shared/utils/splitComponent.js +16 -8
  140. package/build/production/shared/utils/splitComponent.js.map +1 -1
  141. package/build/production/shared/utils/time.js +2 -2
  142. package/build/production/shared/utils/time.js.map +1 -1
  143. package/build/production/shared/utils/webpack.js +5 -2
  144. package/build/production/shared/utils/webpack.js.map +1 -1
  145. package/build/production/web.bundle.js +1 -1
  146. package/build/production/web.bundle.js.map +1 -1
  147. package/build/types-code/client/getInj.d.ts +1 -1
  148. package/build/types-code/client/index.d.ts +2 -2
  149. package/build/types-code/client/init.d.ts +1 -1
  150. package/build/types-code/index.d.ts +6 -5
  151. package/build/types-code/server/Cache.d.ts +1 -2
  152. package/build/types-code/server/index.d.ts +4 -5
  153. package/build/types-code/server/renderer.d.ts +9 -11
  154. package/build/types-code/server/server.d.ts +3 -5
  155. package/build/types-code/server/utils/errors.d.ts +1 -1
  156. package/build/types-code/shared/components/Button/index.d.ts +2 -2
  157. package/build/types-code/shared/components/TextArea/index.d.ts +3 -3
  158. package/build/types-code/shared/components/WithTooltip/Tooltip.d.ts +7 -1
  159. package/build/types-code/shared/components/index.d.ts +12 -12
  160. package/build/types-code/shared/utils/config.d.ts +1 -1
  161. package/build/types-code/shared/utils/globalState.d.ts +3 -6
  162. package/build/types-code/shared/utils/isomorphy/index.d.ts +1 -3
  163. package/build/types-code/shared/utils/jest/E2eSsrEnv.d.ts +1 -1
  164. package/build/types-code/shared/utils/jest/global.d.ts +4 -4
  165. package/build/types-code/shared/utils/jest/index.d.ts +2 -2
  166. package/build/types-code/shared/utils/webpack.d.ts +1 -1
  167. package/config/babel/node-ssr.d.ts +3 -2
  168. package/config/babel/node-ssr.js +5 -7
  169. package/config/babel/webpack.d.ts +3 -11
  170. package/config/babel/webpack.js +15 -15
  171. package/config/eslint/default.mjs +32 -0
  172. package/config/jest/default.js +10 -6
  173. package/config/jest/resolver.js +2 -0
  174. package/config/jest/setup.js +2 -2
  175. package/config/stylelint/default.js +3 -0
  176. package/config/webpack/app-base.d.ts +0 -6
  177. package/config/webpack/app-base.js +64 -70
  178. package/config/webpack/app-development.d.ts +2 -2
  179. package/config/webpack/app-development.js +8 -12
  180. package/config/webpack/app-production.js +1 -0
  181. package/config/webpack/lib-base.js +20 -18
  182. package/config/webpack/lib-development.js +1 -0
  183. package/config/webpack/lib-production.js +1 -0
  184. package/config/workbox/default.js +2 -5
  185. package/dev-styles.js +1 -0
  186. package/eslint.config.mjs +13 -0
  187. package/node-entry.js +7 -2
  188. package/null.js +1 -0
  189. package/package.json +27 -35
  190. package/prod-styles.js +1 -0
  191. package/src/client/getInj.ts +8 -3
  192. package/src/client/index.tsx +4 -4
  193. package/src/client/init.ts +18 -15
  194. package/src/index.ts +8 -3
  195. package/src/server/Cache.ts +7 -15
  196. package/src/server/index.ts +30 -21
  197. package/src/server/renderer.tsx +77 -67
  198. package/src/server/server.ts +41 -21
  199. package/src/server/utils/errors.ts +6 -3
  200. package/src/shared/components/Button/index.tsx +4 -5
  201. package/src/shared/components/Checkbox/index.tsx +10 -7
  202. package/src/shared/components/GenericLink/index.tsx +14 -7
  203. package/src/shared/components/Input/index.tsx +4 -1
  204. package/src/shared/components/Link.tsx +9 -5
  205. package/src/shared/components/MetaTags.tsx +21 -15
  206. package/src/shared/components/Modal/index.tsx +12 -11
  207. package/src/shared/components/NavLink.tsx +10 -5
  208. package/src/shared/components/TextArea/index.tsx +17 -9
  209. package/src/shared/components/WithTooltip/{Tooltip.tsx → Tooltip.ts} +35 -39
  210. package/src/shared/components/WithTooltip/index.tsx +36 -30
  211. package/src/shared/components/YouTubeVideo/index.tsx +10 -6
  212. package/src/shared/components/selectors/CustomDropdown/Options/index.tsx +5 -6
  213. package/src/shared/components/selectors/CustomDropdown/index.tsx +10 -14
  214. package/src/shared/components/selectors/NativeDropdown/index.tsx +5 -7
  215. package/src/shared/components/selectors/Switch/index.tsx +19 -17
  216. package/src/shared/utils/config.ts +12 -5
  217. package/src/shared/utils/globalState.ts +8 -5
  218. package/src/shared/utils/isomorphy/buildInfo.ts +1 -1
  219. package/src/shared/utils/isomorphy/environment-check.ts +5 -1
  220. package/src/shared/utils/isomorphy/index.ts +4 -6
  221. package/src/shared/utils/jest/E2eSsrEnv.ts +64 -39
  222. package/src/shared/utils/jest/global.ts +6 -8
  223. package/src/shared/utils/jest/{index.tsx → index.ts} +25 -12
  224. package/src/shared/utils/splitComponent.tsx +44 -25
  225. package/src/shared/utils/time.ts +16 -9
  226. package/src/shared/utils/webpack.ts +19 -14
  227. package/webpack.config.ts +36 -10
  228. package/config/eslint/default.json +0 -30
  229. package/config/eslint/jest.json +0 -19
  230. package/config/eslint/typescript.js +0 -46
@@ -4,7 +4,6 @@
4
4
  * Webpack build of the code for client-side execution, it further exposes
5
5
  * Jsdom environment for the client-side testing of the outcomes.
6
6
  */
7
- /* eslint-disable global-require, import/no-dynamic-require */
8
7
 
9
8
  // BEWARE: The module is not imported into the JU module / the main assembly of
10
9
  // the library, because doing so easily breaks stuff:
@@ -15,10 +14,13 @@
15
14
  // probably some sort of a require-loop, or some issues with weak
16
15
  // require in that scenario.
17
16
 
17
+ // TODO: We need to add correct typing for environment options.
18
+
18
19
  import path from 'path';
19
20
 
20
21
  import type { Request, Response } from 'express';
21
22
  import { defaults, noop, set } from 'lodash';
23
+ import type { ReactNode } from 'react';
22
24
 
23
25
  // As this environment is a part of the Jest testing utils,
24
26
  // we assume development dependencies are available when it is used.
@@ -26,8 +28,8 @@ import { defaults, noop, set } from 'lodash';
26
28
  import register from '@babel/register/experimental-worker';
27
29
 
28
30
  import JsdomEnv from 'jest-environment-jsdom';
29
- import { type IFs, createFsFromVolume, Volume } from 'memfs';
30
- import webpack from 'webpack';
31
+ import { createFsFromVolume, Volume } from 'memfs';
32
+ import webpack, { type Configuration } from 'webpack';
31
33
  /* eslint-enable import/no-extraneous-dependencies */
32
34
 
33
35
  import ssrFactory from 'server/renderer';
@@ -56,11 +58,11 @@ export default class E2eSsrEnv extends JsdomEnv {
56
58
  * Loads Webpack config, and exposes it to the environment via global
57
59
  * webpackConfig object.
58
60
  */
59
- loadWebpackConfig() {
61
+ private loadWebpackConfig() {
60
62
  const optionsString = this.pragmas['webpack-config-options'] as string;
61
63
 
62
- const options = (optionsString
63
- ? JSON.parse(optionsString) : {}) as webpack.Configuration;
64
+ const options = (optionsString ? JSON.parse(optionsString) : {}) as
65
+ webpack.Configuration;
64
66
 
65
67
  defaults(options, {
66
68
  context: this.testFolder,
@@ -68,12 +70,16 @@ export default class E2eSsrEnv extends JsdomEnv {
68
70
  });
69
71
 
70
72
  const factoryPath = this.pragmas['webpack-config-factory'] as string;
71
- let factory = require(path.resolve(this.rootDir, factoryPath));
73
+ // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-require-imports
74
+ let factory = require(path.resolve(this.rootDir, factoryPath)) as
75
+ (((ops: Configuration) => Configuration) | {
76
+ default: (ops: Configuration) => Configuration;
77
+ });
72
78
  factory = 'default' in factory ? factory.default : factory;
73
79
 
74
80
  this.global.webpackConfig = factory(options);
75
81
 
76
- const fs = this.global.webpackOutputFs as IFs;
82
+ const fs = this.global.webpackOutputFs;
77
83
  let buildInfo = `${options.context}/.build-info`;
78
84
  if (fs.existsSync(buildInfo)) {
79
85
  buildInfo = fs.readFileSync(buildInfo, 'utf8') as string;
@@ -85,14 +91,17 @@ export default class E2eSsrEnv extends JsdomEnv {
85
91
  * Executes Webpack build.
86
92
  * @return {Promise}
87
93
  */
88
- async runWebpack() {
94
+ async runWebpack(): Promise<void> {
89
95
  this.loadWebpackConfig();
90
96
 
91
- const compiler = webpack(this.global.webpackConfig as webpack.Configuration);
97
+ if (!this.global.webpackConfig) throw Error('Failed to load Webpack config');
98
+ const compiler = webpack(this.global.webpackConfig);
92
99
 
93
- // TODO: The "as typeof compiler.outputFileSystem" piece below is a workaround
94
- // for the Webpack regression: https://github.com/webpack/webpack/issues/18242
95
- compiler.outputFileSystem = this.global.webpackOutputFs as typeof compiler.outputFileSystem;
100
+ // TODO: The "as typeof compiler.outputFileSystem" piece below is
101
+ // a workaround for the Webpack regression:
102
+ // https://github.com/webpack/webpack/issues/18242
103
+ compiler.outputFileSystem = this.global.webpackOutputFs as
104
+ typeof compiler.outputFileSystem;
96
105
 
97
106
  return new Promise<void>((done, fail) => {
98
107
  compiler.run((err, stats) => {
@@ -115,35 +124,43 @@ export default class E2eSsrEnv extends JsdomEnv {
115
124
  });
116
125
  }
117
126
 
118
- async runSsr() {
127
+ async runSsr(): Promise<void> {
119
128
  const optionsString = this.pragmas['ssr-options'] as string;
120
- const options = optionsString ? JSON.parse(optionsString) : {};
129
+ const options = optionsString
130
+ ? JSON.parse(optionsString) as Record<string, unknown>
131
+ : {};
121
132
 
122
133
  // TODO: This is temporary to shortcut the logging added to SSR.
123
- if (options.logger === undefined) {
124
- options.logger = {
125
- debug: noop,
126
- info: noop,
127
- log: noop,
128
- warn: noop,
129
- };
130
- }
134
+ options.logger ??= {
135
+ debug: noop,
136
+ info: noop,
137
+ log: noop,
138
+ warn: noop,
139
+ };
131
140
 
132
- if (!options.buildInfo) options.buildInfo = this.global.buildInfo;
141
+ options.buildInfo ??= this.global.buildInfo;
133
142
 
134
143
  let cleanup: (() => void) | undefined;
135
144
 
136
145
  if (options.entry) {
137
- const p = path.resolve(this.testFolder, options.entry);
138
- const module = require(p);
139
- cleanup = module.cleanup;
140
- options.Application = module[options.entryExportName || 'default'];
146
+ const p = path.resolve(this.testFolder, options.entry as string);
147
+ // TODO: This sure can be replaced by a dynamic import().
148
+ // eslint-disable-next-line import/no-dynamic-require, @typescript-eslint/no-require-imports
149
+ const module = require(p) as NodeJS.Module;
150
+ if ('cleanup' in module) cleanup = module.cleanup as () => void;
151
+
152
+ const exportName = (options.entryExportName as string) || 'default';
153
+ if (exportName in module) {
154
+ options.Application = (
155
+ module as unknown as Record<string, unknown>
156
+ )[exportName] as ReactNode;
157
+ }
141
158
  }
142
159
 
143
160
  const renderer = ssrFactory(this.global.webpackConfig!, options);
144
161
  let status = 200; // OK
145
162
  const markup = await new Promise<string>((done, fail) => {
146
- renderer(
163
+ void renderer(
147
164
  this.ssrRequest as Request,
148
165
 
149
166
  // TODO: This will do for now, with the current implementation of
@@ -170,7 +187,9 @@ export default class E2eSsrEnv extends JsdomEnv {
170
187
  } as unknown) as Response,
171
188
 
172
189
  (error) => {
173
- if (error) fail(error);
190
+ // TODO: Strictly speaking, that error as Error casting is not all
191
+ // correct, but it works, so no need to spend time on it right now.
192
+ if (error) fail(error as Error);
174
193
  else done('');
175
194
  },
176
195
  );
@@ -190,16 +209,18 @@ export default class E2eSsrEnv extends JsdomEnv {
190
209
  const pragmas = context.docblockPragmas;
191
210
 
192
211
  const requestString = pragmas['ssr-request'] as string;
193
- const request = requestString ? JSON.parse(requestString) : {};
212
+ const request = requestString
213
+ ? JSON.parse(requestString) as Record<string, unknown>
214
+ : {};
194
215
 
195
- if (!request.url) request.url = '/';
216
+ request.url ??= '/';
196
217
  request.csrfToken = noop;
197
218
 
198
219
  // This ensures the initial JsDom URL matches the value we use for SSR.
199
220
  set(
200
221
  config.projectConfig,
201
222
  'testEnvironmentOptions.url',
202
- `http://localhost${request.url}`,
223
+ `http://localhost${request.url as string}`,
203
224
  );
204
225
 
205
226
  super(config, context);
@@ -218,20 +239,24 @@ export default class E2eSsrEnv extends JsdomEnv {
218
239
  // The usual "babel-jest" transformation setup does not apply to
219
240
  // the environment code and imports from it, this workaround enables it.
220
241
  const optionsString = this.pragmas['ssr-options'] as string;
221
- const options = optionsString ? JSON.parse(optionsString) : {};
242
+ const options = optionsString
243
+ ? JSON.parse(optionsString) as Record<string, unknown>
244
+ : {};
222
245
  let root;
223
246
  switch (options.root) {
224
- case 'TEST': root = this.testFolder; break;
247
+ case 'TEST':
248
+ root = this.testFolder;
249
+ break;
225
250
  default: root = process.cwd();
226
251
  }
227
252
  register({
228
- envName: options.babelEnv,
253
+ envName: options.babelEnv as string,
229
254
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.svg'],
230
255
  root,
231
256
  });
232
257
  }
233
258
 
234
- async setup() {
259
+ async setup(): Promise<void> {
235
260
  await super.setup();
236
261
  await this.runWebpack();
237
262
 
@@ -254,7 +279,7 @@ export default class E2eSsrEnv extends JsdomEnv {
254
279
  this.global.REACT_UTILS_FORCE_CLIENT_SIDE = true;
255
280
  }
256
281
 
257
- async teardown() {
282
+ async teardown(): Promise<void> {
258
283
  delete this.global.REACT_UTILS_FORCE_CLIENT_SIDE;
259
284
 
260
285
  // Resets module cache and @babel/register. Effectively this ensures that
@@ -267,6 +292,6 @@ export default class E2eSsrEnv extends JsdomEnv {
267
292
  delete require.cache[key];
268
293
  });
269
294
  register.revert();
270
- super.teardown();
295
+ await super.teardown();
271
296
  }
272
297
  }
@@ -1,19 +1,17 @@
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 */
1
+ import type { IFs } from 'memfs';
2
+ import type { Configuration, StatsCompilation } from 'webpack';
6
3
 
7
4
  declare global {
5
+ // eslint-disable-next-line @typescript-eslint/consistent-type-definitions
8
6
  interface Window {
9
7
  ssrMarkup: string | undefined;
10
8
  ssrStatus: number | undefined;
11
- webpackConfig: webpack.Configuration | undefined;
9
+ webpackConfig: Configuration | undefined;
12
10
  webpackOutputFs: IFs;
13
- webpackStats?: webpack.StatsCompilation;
11
+ webpackStats?: StatsCompilation;
14
12
  }
15
13
  }
16
14
 
17
15
  export default function getGlobal(): Window {
18
- return global as any;
16
+ return global as unknown as Window;
19
17
  }
@@ -4,6 +4,7 @@
4
4
  import type {
5
5
  AxiosRequestConfig,
6
6
  AxiosResponse,
7
+ AxiosStatic,
7
8
  InternalAxiosRequestConfig,
8
9
  } from 'axios';
9
10
 
@@ -29,14 +30,14 @@ const originalProcessVersions = process.versions;
29
30
  * Tricks **react-utils** into thinking the test is running within client-side
30
31
  * (browser) environment.
31
32
  */
32
- export function mockClientSide() {
33
+ export function mockClientSide(): void {
33
34
  Object.defineProperty(process, 'versions', { value: undefined });
34
35
  }
35
36
 
36
37
  /**
37
38
  * Reverts the effect of {@link module:JU.mockClientSide mockClientSide(..)}.
38
39
  */
39
- export function unmockClientSide() {
40
+ export function unmockClientSide(): void {
40
41
  Object.defineProperty(process, 'versions', {
41
42
  value: originalProcessVersions,
42
43
  writable: false,
@@ -49,7 +50,7 @@ export function unmockClientSide() {
49
50
  * @param {number} seed
50
51
  * @return {string}
51
52
  */
52
- export function getMockUuid(seed = 0) {
53
+ export function getMockUuid(seed = 0): string {
53
54
  const x = seed.toString(16).padStart(32, '0');
54
55
  return `${x.slice(0, 8)}-${x.slice(8, 12)}-${x.slice(12, 16)}-${x.slice(16, 20)}-${x.slice(20)}`;
55
56
  }
@@ -57,12 +58,14 @@ export function getMockUuid(seed = 0) {
57
58
  export type AxiosRequestHandlerT =
58
59
  (config: AxiosRequestConfig) => Partial<AxiosResponse> | null | undefined;
59
60
 
60
- export function mockAxios(handlers: AxiosRequestHandlerT[]) {
61
- const axios = jest.requireActual('axios');
61
+ export function mockAxios(handlers: AxiosRequestHandlerT[]): AxiosStatic {
62
+ const axios: AxiosStatic = jest.requireActual('axios');
62
63
 
63
- axios.defaults.adapter = async (config: AxiosRequestConfig): Promise<AxiosResponse> => {
64
- for (let i = 0; i < handlers.length; ++i) {
65
- const res = handlers[i]?.(config);
64
+ axios.defaults.adapter = async (
65
+ config: AxiosRequestConfig,
66
+ ): Promise<AxiosResponse> => {
67
+ for (const handler of handlers) {
68
+ const res = handler(config);
66
69
  if (res) {
67
70
  return {
68
71
  config: config as InternalAxiosRequestConfig,
@@ -79,6 +82,7 @@ export function mockAxios(handlers: AxiosRequestHandlerT[]) {
79
82
  let res: AxiosResponse;
80
83
  try {
81
84
  res = await axios({ ...config, adapter: ['xhr', 'http', 'fetch'] });
85
+ // eslint-disable-next-line no-console
82
86
  console.warn(
83
87
  'Network request has not been mocked for a test.\n\nConfig:\n',
84
88
  config,
@@ -86,6 +90,7 @@ export function mockAxios(handlers: AxiosRequestHandlerT[]) {
86
90
  JSON.stringify(res, null, 2),
87
91
  );
88
92
  } catch (e) {
93
+ // eslint-disable-next-line no-console
89
94
  console.warn(
90
95
  'Network request has not been mocked for a test, and failed.\n\nConfig:\n',
91
96
  config,
@@ -107,9 +112,9 @@ export function mockAxios(handlers: AxiosRequestHandlerT[]) {
107
112
  * @returns {Promise} Wait for this to "jump after" any async code which should
108
113
  * be executed because of the mock time movement.
109
114
  */
110
- export async function mockTimer(time: number) {
115
+ export async function mockTimer(time: number): Promise<void> {
111
116
  mockdate.set(time + Date.now());
112
- jest.advanceTimersByTime(time);
117
+ await jest.advanceTimersByTimeAsync(time);
113
118
  }
114
119
 
115
120
  export type MountedSceneT = HTMLElement & {
@@ -134,7 +139,9 @@ export function mount(scene: ReactNode): MountedSceneT {
134
139
  // when it is simulating user events.
135
140
  global.IS_REACT_ACT_ENVIRONMENT = true;
136
141
 
137
- act(() => root.unmount());
142
+ act(() => {
143
+ root.unmount();
144
+ });
138
145
  res.remove();
139
146
  };
140
147
 
@@ -160,9 +167,15 @@ type SnapshotOptionsT = {
160
167
  export async function snapshot(
161
168
  element: React.ReactElement,
162
169
  options?: SnapshotOptionsT,
163
- ) {
170
+ ): Promise<RenderResult> {
164
171
  let res: RenderResult | undefined;
165
172
 
173
+ // TODO: Just adding async to the actor function breaks stuff, as it makes
174
+ // act() asynchronous no matter the `options.await` value, thus breaking all
175
+ // calls that do not await for snapshot() result... thus... perhaps we need
176
+ // to have a more complex typing to ensure it all works as intended in all
177
+ // cases, and while being correctly enforced by TypeScript.
178
+ // eslint-disable-next-line @typescript-eslint/promise-function-async
166
179
  const promise = act(() => {
167
180
  res = render(element);
168
181
  return options?.await;
@@ -1,4 +1,3 @@
1
- /* eslint-disable react/jsx-props-no-spreading */
2
1
  /* global document */
3
2
 
4
3
  import {
@@ -28,11 +27,19 @@ import {
28
27
  let clientChunkGroups: ChunkGroupsT;
29
28
 
30
29
  if (IS_CLIENT_SIDE) {
31
- // eslint-disable-next-line global-require
32
- clientChunkGroups = require('client/getInj').default().CHUNK_GROUPS || {};
30
+ // TODO: Rewrite to avoid these overrides of ESLint rules.
31
+ /* eslint-disable @typescript-eslint/no-unsafe-assignment,
32
+ @typescript-eslint/no-require-imports,
33
+ @typescript-eslint/no-unsafe-call,
34
+ @typescript-eslint/no-unsafe-member-access */
35
+ clientChunkGroups = require('client/getInj').default().CHUNK_GROUPS ?? {};
36
+ /* eslint-enable @typescript-eslint/no-unsafe-assignment,
37
+ @typescript-eslint/no-require-imports,
38
+ @typescript-eslint/no-unsafe-call,
39
+ @typescript-eslint/no-unsafe-member-access */
33
40
  }
34
41
 
35
- const refCounts: { [path: string]: number } = {};
42
+ const refCounts: Record<string, number> = {};
36
43
 
37
44
  function getPublicPath() {
38
45
  return getBuildInfo().publicPath;
@@ -67,12 +74,18 @@ function bookStyleSheet(
67
74
  }
68
75
 
69
76
  res = new Barrier<void>();
70
- link.addEventListener('load', () => res!.resolve());
71
- link.addEventListener('error', () => res!.resolve());
77
+ link.addEventListener('load', () => {
78
+ if (!res) throw Error('Internal error');
79
+ void res.resolve();
80
+ });
81
+ link.addEventListener('error', () => {
82
+ if (!res) throw Error('Internal error');
83
+ void res.resolve();
84
+ });
72
85
  }
73
86
 
74
87
  if (refCount) {
75
- const current = refCounts[path] || 0;
88
+ const current = refCounts[path] ?? 0;
76
89
  refCounts[path] = 1 + current;
77
90
  }
78
91
 
@@ -86,8 +99,7 @@ function bookStyleSheet(
86
99
  function getLoadedStyleSheets(): Set<string> {
87
100
  const res = new Set<string>();
88
101
  const { styleSheets } = document;
89
- for (let i = 0; i < styleSheets.length; ++i) {
90
- const href = styleSheets[i]?.href;
102
+ for (const { href } of styleSheets) {
91
103
  if (href) res.add(href);
92
104
  }
93
105
  return res;
@@ -110,7 +122,7 @@ function assertChunkName(
110
122
  * @return Resolves once all pending stylesheets, necessary for
111
123
  * the chunk, are either loaded, or failed to load.
112
124
  */
113
- export function bookStyleSheets(
125
+ export async function bookStyleSheets(
114
126
  chunkName: string,
115
127
  chunkGroups: ChunkGroupsT,
116
128
  refCount: boolean,
@@ -121,9 +133,8 @@ export function bookStyleSheets(
121
133
 
122
134
  const loadedSheets = getLoadedStyleSheets();
123
135
 
124
- for (let i = 0; i < assets.length; ++i) {
125
- const asset = assets[i];
126
- if (asset?.endsWith('.css')) {
136
+ for (const asset of assets) {
137
+ if (asset.endsWith('.css')) {
127
138
  const promise = bookStyleSheet(asset, loadedSheets, refCount);
128
139
  if (promise) promises.push(promise);
129
140
  }
@@ -144,13 +155,12 @@ export function bookStyleSheets(
144
155
  export function freeStyleSheets(
145
156
  chunkName: string,
146
157
  chunkGroups: ChunkGroupsT,
147
- ) {
158
+ ): void {
148
159
  const assets = chunkGroups[chunkName];
149
160
  if (!assets) return;
150
161
 
151
- for (let i = 0; i < assets.length; ++i) {
152
- const asset = assets[i];
153
- if (asset?.endsWith('.css')) {
162
+ for (const asset of assets) {
163
+ if (asset.endsWith('.css')) {
154
164
  const path = `${getPublicPath()}/${asset}`;
155
165
 
156
166
  const pathRefCount = refCounts[path];
@@ -168,7 +178,7 @@ export function freeStyleSheets(
168
178
  const usedChunkNames = new Set();
169
179
 
170
180
  type ComponentOrModule<PropsT> = ComponentType<PropsT> | {
171
- default: ComponentType<PropsT>,
181
+ default: ComponentType<PropsT>;
172
182
  };
173
183
 
174
184
  /**
@@ -189,9 +199,9 @@ export default function splitComponent<
189
199
  placeholder,
190
200
  }: {
191
201
  chunkName: string;
192
- getComponent: () => Promise<ComponentOrModule<ComponentPropsT>>,
193
- placeholder?: ReactNode,
194
- }) {
202
+ getComponent: () => Promise<ComponentOrModule<ComponentPropsT>>;
203
+ placeholder?: ReactNode;
204
+ }): FunctionComponent<ComponentPropsT> {
195
205
  // On the client side we can check right away if the chunk name is known.
196
206
  if (IS_CLIENT_SIDE) assertChunkName(chunkName, clientChunkGroups);
197
207
 
@@ -227,12 +237,18 @@ export default function splitComponent<
227
237
  // This takes care about stylesheets management every time an instance of
228
238
  // this component is mounted / unmounted.
229
239
  useInsertionEffect(() => {
230
- bookStyleSheets(chunkName, clientChunkGroups, true);
231
- return () => freeStyleSheets(chunkName, clientChunkGroups);
240
+ void bookStyleSheets(chunkName, clientChunkGroups, true);
241
+ return () => {
242
+ freeStyleSheets(chunkName, clientChunkGroups);
243
+ };
232
244
  }, []);
233
245
 
234
246
  return (
235
- <Component {...(rest as unknown as ComponentPropsT)} ref={ref}>
247
+ <Component
248
+ // eslint-disable-next-line react/jsx-props-no-spreading
249
+ {...(rest as unknown as ComponentPropsT)}
250
+ ref={ref}
251
+ >
236
252
  {children}
237
253
  </Component>
238
254
  );
@@ -246,7 +262,10 @@ export default function splitComponent<
246
262
  ...rest
247
263
  }: ComponentPropsT) => (
248
264
  <Suspense fallback={placeholder}>
249
- <LazyComponent {...rest as Parameters<typeof LazyComponent>[0]}>
265
+ <LazyComponent
266
+ // eslint-disable-next-line react/jsx-props-no-spreading
267
+ {...rest as Parameters<typeof LazyComponent>[0]}
268
+ >
250
269
  {children}
251
270
  </LazyComponent>
252
271
  </Suspense>
@@ -1,6 +1,6 @@
1
1
  import { serialize } from 'cookie';
2
- import dayjs from 'dayjs';
3
2
  import { useEffect } from 'react';
3
+ import dayjs from 'dayjs';
4
4
 
5
5
  import {
6
6
  DAY_MS,
@@ -30,10 +30,13 @@ export function useCurrent({
30
30
  globalStatePath = 'currentTime',
31
31
  precision = 5 * SEC_MS,
32
32
  } = {}): number {
33
- const [now, setter] = useGlobalState<ForceT, number>(globalStatePath, Date.now);
33
+ const [now, setter] = useGlobalState<ForceT, number>(
34
+ globalStatePath,
35
+ Date.now,
36
+ );
34
37
  useEffect(() => {
35
- let timerId: NodeJS.Timeout;
36
- const update = () => {
38
+ let timerId: NodeJS.Timeout | undefined;
39
+ const update = (): void => {
37
40
  setter((old) => {
38
41
  const neu = Date.now();
39
42
  return Math.abs(neu - old) > precision ? neu : old;
@@ -41,7 +44,7 @@ export function useCurrent({
41
44
  if (autorefresh) timerId = setTimeout(update, precision);
42
45
  };
43
46
  update();
44
- return () => {
47
+ return (): void => {
45
48
  if (timerId) clearTimeout(timerId);
46
49
  };
47
50
  }, [autorefresh, precision, setter]);
@@ -64,10 +67,14 @@ export function useTimezoneOffset({
64
67
  globalStatePath = 'timezoneOffset',
65
68
  } = {}): number {
66
69
  const ssrContext = getSsrContext(false);
67
- const [offset, setOffset] = useGlobalState<ForceT, number>(globalStatePath, () => {
68
- const value = cookieName && ssrContext?.req?.cookies?.[cookieName];
69
- return value ? parseInt(value, 10) : 0;
70
- });
70
+ const [offset, setOffset] = useGlobalState<ForceT, number>(
71
+ globalStatePath,
72
+ () => {
73
+ const value = cookieName
74
+ && ssrContext?.req.cookies[cookieName] as string;
75
+ return value ? parseInt(value) : 0;
76
+ },
77
+ );
71
78
  useEffect(() => {
72
79
  const date = new Date();
73
80
  const value = date.getTimezoneOffset();
@@ -1,3 +1,5 @@
1
+ import type PathT from 'path';
2
+
1
3
  import { IS_CLIENT_SIDE } from './isomorphy';
2
4
 
3
5
  /**
@@ -7,37 +9,40 @@ import { IS_CLIENT_SIDE } from './isomorphy';
7
9
  * @param [basePath]
8
10
  * @return Required module.
9
11
  */
10
- export function requireWeak<Module extends NodeJS.Module>(
12
+ export function requireWeak<T extends object>(
11
13
  modulePath: string,
12
14
  basePath?: string,
13
- ): Module | null {
15
+ ): T | null {
14
16
  if (IS_CLIENT_SIDE) return null;
15
17
 
16
18
  // TODO: On one hand, this try/catch wrap silencing errors is bad, as it may
17
19
  // hide legit errors, in a way difficult to notice and understand; but on the
18
- // other hand it fails for some (unclear, but legit?) reasons in some environments,
20
+ // other hand it fails for some (unclear, but legit?) reasons in some
21
+ // environments,
19
22
  // like during the static code generation for docs. Perhaps, something should
20
23
  // be implemented differently here.
21
24
  try {
22
- /* eslint-disable no-eval */
23
- const { resolve } = eval('require')('path');
25
+ // eslint-disable-next-line no-eval
26
+ const req = eval('require') as (path: string) => unknown;
27
+
28
+ // eslint-disable-next-line @typescript-eslint/unbound-method
29
+ const { resolve } = req('path') as typeof PathT;
30
+
24
31
  const path = basePath ? resolve(basePath, modulePath) : modulePath;
25
- const module = eval('require')(path) as Module;
26
- /* eslint-enable no-eval */
32
+ const module = req(path) as T;
27
33
 
28
34
  if (!('default' in module) || !module.default) return module;
29
35
 
30
36
  const { default: def, ...named } = module;
31
37
 
32
- const res = def as Module;
38
+ const res = def as T;
33
39
 
34
40
  Object.entries(named).forEach(([name, value]) => {
35
- const assigned = res[name as keyof Module];
36
- if (assigned !== undefined) {
37
- if (assigned !== value) {
38
- throw Error('Conflict between default and named exports');
39
- }
40
- } else res[name as keyof Module] = value;
41
+ const assigned = res[name as keyof T];
42
+ if (assigned) (res[name as keyof T] as unknown) = value;
43
+ else if (assigned !== value) {
44
+ throw Error('Conflict between default and named exports');
45
+ }
41
46
  });
42
47
  return res;
43
48
  } catch {