@dr.pogodin/react-utils 1.42.0 → 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.
- package/babel.config.js +3 -0
- package/bin/build.js +5 -8
- package/bin/setup.js +2 -1
- package/build/development/client/getInj.js +7 -2
- package/build/development/client/getInj.js.map +1 -1
- package/build/development/client/index.js +1 -2
- package/build/development/client/index.js.map +1 -1
- package/build/development/client/init.js +16 -13
- package/build/development/client/init.js.map +1 -1
- package/build/development/index.js +4 -1
- package/build/development/index.js.map +1 -1
- package/build/development/server/Cache.js +5 -9
- package/build/development/server/Cache.js.map +1 -1
- package/build/development/server/index.js +14 -11
- package/build/development/server/index.js.map +1 -1
- package/build/development/server/renderer.js +36 -40
- package/build/development/server/renderer.js.map +1 -1
- package/build/development/server/server.js +19 -12
- package/build/development/server/server.js.map +1 -1
- package/build/development/server/utils/errors.js.map +1 -1
- package/build/development/shared/components/Button/index.js +2 -3
- package/build/development/shared/components/Button/index.js.map +1 -1
- package/build/development/shared/components/Checkbox/index.js +3 -1
- package/build/development/shared/components/Checkbox/index.js.map +1 -1
- package/build/development/shared/components/GenericLink/index.js +13 -6
- package/build/development/shared/components/GenericLink/index.js.map +1 -1
- package/build/development/shared/components/Input/index.js +5 -1
- package/build/development/shared/components/Input/index.js.map +1 -1
- package/build/development/shared/components/Link.js +5 -6
- package/build/development/shared/components/Link.js.map +1 -1
- package/build/development/shared/components/MetaTags.js +31 -24
- package/build/development/shared/components/MetaTags.js.map +1 -1
- package/build/development/shared/components/Modal/index.js +13 -6
- package/build/development/shared/components/Modal/index.js.map +1 -1
- package/build/development/shared/components/NavLink.js +6 -6
- package/build/development/shared/components/NavLink.js.map +1 -1
- package/build/development/shared/components/TextArea/index.js +6 -3
- package/build/development/shared/components/TextArea/index.js.map +1 -1
- package/build/development/shared/components/WithTooltip/Tooltip.js +35 -39
- package/build/development/shared/components/WithTooltip/Tooltip.js.map +1 -1
- package/build/development/shared/components/WithTooltip/index.js +27 -21
- package/build/development/shared/components/WithTooltip/index.js.map +1 -1
- package/build/development/shared/components/YouTubeVideo/index.js +4 -3
- package/build/development/shared/components/YouTubeVideo/index.js.map +1 -1
- package/build/development/shared/components/selectors/CustomDropdown/Options/index.js +4 -5
- package/build/development/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
- package/build/development/shared/components/selectors/CustomDropdown/index.js +7 -9
- package/build/development/shared/components/selectors/CustomDropdown/index.js.map +1 -1
- package/build/development/shared/components/selectors/NativeDropdown/index.js +3 -5
- package/build/development/shared/components/selectors/NativeDropdown/index.js.map +1 -1
- package/build/development/shared/components/selectors/Switch/index.js +21 -20
- package/build/development/shared/components/selectors/Switch/index.js.map +1 -1
- package/build/development/shared/utils/config.js +6 -3
- package/build/development/shared/utils/config.js.map +1 -1
- package/build/development/shared/utils/globalState.js +6 -0
- package/build/development/shared/utils/globalState.js.map +1 -1
- package/build/development/shared/utils/isomorphy/buildInfo.js.map +1 -1
- package/build/development/shared/utils/isomorphy/environment-check.js +6 -1
- package/build/development/shared/utils/isomorphy/environment-check.js.map +1 -1
- package/build/development/shared/utils/isomorphy/index.js +1 -3
- package/build/development/shared/utils/isomorphy/index.js.map +1 -1
- package/build/development/shared/utils/jest/E2eSsrEnv.js +26 -17
- package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
- package/build/development/shared/utils/jest/global.js +0 -7
- package/build/development/shared/utils/jest/global.js.map +1 -1
- package/build/development/shared/utils/jest/index.js +15 -4
- package/build/development/shared/utils/jest/index.js.map +1 -1
- package/build/development/shared/utils/splitComponent.js +37 -19
- package/build/development/shared/utils/splitComponent.js.map +1 -1
- package/build/development/shared/utils/time.js +3 -3
- package/build/development/shared/utils/time.js.map +1 -1
- package/build/development/shared/utils/webpack.js +11 -11
- package/build/development/shared/utils/webpack.js.map +1 -1
- package/build/development/web.bundle.js +30 -30
- package/build/production/client/getInj.js +4 -2
- package/build/production/client/getInj.js.map +1 -1
- package/build/production/client/index.js +2 -2
- package/build/production/client/index.js.map +1 -1
- package/build/production/client/init.js +4 -2
- package/build/production/client/init.js.map +1 -1
- package/build/production/index.js +2 -1
- package/build/production/index.js.map +1 -1
- package/build/production/server/Cache.js +1 -2
- package/build/production/server/Cache.js.map +1 -1
- package/build/production/server/index.js +9 -5
- package/build/production/server/index.js.map +1 -1
- package/build/production/server/renderer.js +24 -21
- package/build/production/server/renderer.js.map +1 -1
- package/build/production/server/server.js +13 -8
- package/build/production/server/server.js.map +1 -1
- package/build/production/server/utils/errors.js.map +1 -1
- package/build/production/shared/components/Button/index.js +1 -1
- package/build/production/shared/components/Button/index.js.map +1 -1
- package/build/production/shared/components/Checkbox/index.js +1 -1
- package/build/production/shared/components/Checkbox/index.js.map +1 -1
- package/build/production/shared/components/GenericLink/index.js +6 -4
- package/build/production/shared/components/GenericLink/index.js.map +1 -1
- package/build/production/shared/components/Input/index.js +3 -1
- package/build/production/shared/components/Input/index.js.map +1 -1
- package/build/production/shared/components/Link.js +3 -1
- package/build/production/shared/components/Link.js.map +1 -1
- package/build/production/shared/components/MetaTags.js +5 -2
- package/build/production/shared/components/MetaTags.js.map +1 -1
- package/build/production/shared/components/Modal/index.js +6 -2
- package/build/production/shared/components/Modal/index.js.map +1 -1
- package/build/production/shared/components/NavLink.js +4 -1
- package/build/production/shared/components/NavLink.js.map +1 -1
- package/build/production/shared/components/TextArea/index.js +4 -4
- package/build/production/shared/components/TextArea/index.js.map +1 -1
- package/build/production/shared/components/WithTooltip/Tooltip.js +37 -38
- package/build/production/shared/components/WithTooltip/Tooltip.js.map +1 -1
- package/build/production/shared/components/WithTooltip/index.js +4 -4
- package/build/production/shared/components/WithTooltip/index.js.map +1 -1
- package/build/production/shared/components/YouTubeVideo/index.js +3 -2
- package/build/production/shared/components/YouTubeVideo/index.js.map +1 -1
- package/build/production/shared/components/selectors/CustomDropdown/Options/index.js +2 -2
- package/build/production/shared/components/selectors/CustomDropdown/Options/index.js.map +1 -1
- package/build/production/shared/components/selectors/CustomDropdown/index.js +2 -2
- package/build/production/shared/components/selectors/CustomDropdown/index.js.map +1 -1
- package/build/production/shared/components/selectors/NativeDropdown/index.js +2 -2
- package/build/production/shared/components/selectors/NativeDropdown/index.js.map +1 -1
- package/build/production/shared/components/selectors/Switch/index.js +1 -1
- package/build/production/shared/components/selectors/Switch/index.js.map +1 -1
- package/build/production/shared/utils/config.js +6 -4
- package/build/production/shared/utils/config.js.map +1 -1
- package/build/production/shared/utils/globalState.js +4 -1
- package/build/production/shared/utils/globalState.js.map +1 -1
- package/build/production/shared/utils/isomorphy/buildInfo.js.map +1 -1
- package/build/production/shared/utils/isomorphy/environment-check.js +5 -1
- package/build/production/shared/utils/isomorphy/environment-check.js.map +1 -1
- package/build/production/shared/utils/isomorphy/index.js +1 -3
- package/build/production/shared/utils/isomorphy/index.js.map +1 -1
- package/build/production/shared/utils/jest/E2eSsrEnv.js +15 -8
- package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
- package/build/production/shared/utils/jest/global.js +1 -1
- package/build/production/shared/utils/jest/global.js.map +1 -1
- package/build/production/shared/utils/jest/index.js +13 -5
- package/build/production/shared/utils/jest/index.js.map +1 -1
- package/build/production/shared/utils/splitComponent.js +16 -8
- package/build/production/shared/utils/splitComponent.js.map +1 -1
- package/build/production/shared/utils/time.js +2 -2
- package/build/production/shared/utils/time.js.map +1 -1
- package/build/production/shared/utils/webpack.js +5 -2
- package/build/production/shared/utils/webpack.js.map +1 -1
- package/build/production/web.bundle.js +1 -1
- package/build/production/web.bundle.js.map +1 -1
- package/build/types-code/client/getInj.d.ts +1 -1
- package/build/types-code/client/index.d.ts +2 -2
- package/build/types-code/client/init.d.ts +1 -1
- package/build/types-code/index.d.ts +6 -5
- package/build/types-code/server/Cache.d.ts +1 -2
- package/build/types-code/server/index.d.ts +4 -5
- package/build/types-code/server/renderer.d.ts +8 -10
- package/build/types-code/server/server.d.ts +3 -5
- package/build/types-code/server/utils/errors.d.ts +1 -1
- package/build/types-code/shared/components/Button/index.d.ts +2 -2
- package/build/types-code/shared/components/TextArea/index.d.ts +3 -3
- package/build/types-code/shared/components/WithTooltip/Tooltip.d.ts +7 -1
- package/build/types-code/shared/components/index.d.ts +12 -12
- package/build/types-code/shared/utils/config.d.ts +1 -1
- package/build/types-code/shared/utils/globalState.d.ts +3 -6
- package/build/types-code/shared/utils/isomorphy/index.d.ts +1 -3
- package/build/types-code/shared/utils/jest/E2eSsrEnv.d.ts +1 -1
- package/build/types-code/shared/utils/jest/global.d.ts +4 -4
- package/build/types-code/shared/utils/jest/index.d.ts +2 -2
- package/build/types-code/shared/utils/webpack.d.ts +1 -1
- package/config/babel/node-ssr.d.ts +3 -2
- package/config/babel/node-ssr.js +5 -7
- package/config/babel/webpack.d.ts +3 -11
- package/config/babel/webpack.js +15 -15
- package/config/eslint/default.mjs +32 -0
- package/config/jest/default.js +10 -6
- package/config/jest/resolver.js +2 -0
- package/config/jest/setup.js +2 -2
- package/config/stylelint/default.js +3 -0
- package/config/webpack/app-base.d.ts +0 -6
- package/config/webpack/app-base.js +64 -70
- package/config/webpack/app-development.d.ts +2 -2
- package/config/webpack/app-development.js +8 -12
- package/config/webpack/app-production.js +1 -0
- package/config/webpack/lib-base.js +20 -18
- package/config/webpack/lib-development.js +1 -0
- package/config/webpack/lib-production.js +1 -0
- package/config/workbox/default.js +2 -5
- package/dev-styles.js +1 -0
- package/eslint.config.mjs +13 -0
- package/node-entry.js +7 -2
- package/null.js +1 -0
- package/package.json +17 -25
- package/prod-styles.js +1 -0
- package/src/client/getInj.ts +8 -3
- package/src/client/index.tsx +4 -4
- package/src/client/init.ts +18 -15
- package/src/index.ts +8 -3
- package/src/server/Cache.ts +7 -15
- package/src/server/index.ts +30 -21
- package/src/server/renderer.tsx +76 -66
- package/src/server/server.ts +38 -20
- package/src/server/utils/errors.ts +6 -3
- package/src/shared/components/Button/index.tsx +4 -5
- package/src/shared/components/Checkbox/index.tsx +10 -7
- package/src/shared/components/GenericLink/index.tsx +14 -7
- package/src/shared/components/Input/index.tsx +4 -1
- package/src/shared/components/Link.tsx +9 -5
- package/src/shared/components/MetaTags.tsx +21 -15
- package/src/shared/components/Modal/index.tsx +12 -11
- package/src/shared/components/NavLink.tsx +10 -5
- package/src/shared/components/TextArea/index.tsx +17 -9
- package/src/shared/components/WithTooltip/{Tooltip.tsx → Tooltip.ts} +35 -39
- package/src/shared/components/WithTooltip/index.tsx +36 -30
- package/src/shared/components/YouTubeVideo/index.tsx +10 -6
- package/src/shared/components/selectors/CustomDropdown/Options/index.tsx +5 -6
- package/src/shared/components/selectors/CustomDropdown/index.tsx +10 -14
- package/src/shared/components/selectors/NativeDropdown/index.tsx +5 -7
- package/src/shared/components/selectors/Switch/index.tsx +19 -17
- package/src/shared/utils/config.ts +12 -5
- package/src/shared/utils/globalState.ts +8 -5
- package/src/shared/utils/isomorphy/buildInfo.ts +1 -1
- package/src/shared/utils/isomorphy/environment-check.ts +5 -1
- package/src/shared/utils/isomorphy/index.ts +4 -6
- package/src/shared/utils/jest/E2eSsrEnv.ts +64 -39
- package/src/shared/utils/jest/global.ts +6 -8
- package/src/shared/utils/jest/{index.tsx → index.ts} +25 -12
- package/src/shared/utils/splitComponent.tsx +44 -25
- package/src/shared/utils/time.ts +16 -9
- package/src/shared/utils/webpack.ts +19 -14
- package/webpack.config.ts +36 -10
- package/config/eslint/default.json +0 -30
- package/config/eslint/jest.json +0 -19
- 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 {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
|
94
|
-
// for the Webpack regression:
|
|
95
|
-
|
|
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
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
};
|
|
130
|
-
}
|
|
134
|
+
options.logger ??= {
|
|
135
|
+
debug: noop,
|
|
136
|
+
info: noop,
|
|
137
|
+
log: noop,
|
|
138
|
+
warn: noop,
|
|
139
|
+
};
|
|
131
140
|
|
|
132
|
-
|
|
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
|
-
|
|
139
|
-
|
|
140
|
-
|
|
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
|
-
|
|
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
|
|
212
|
+
const request = requestString
|
|
213
|
+
? JSON.parse(requestString) as Record<string, unknown>
|
|
214
|
+
: {};
|
|
194
215
|
|
|
195
|
-
|
|
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
|
|
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':
|
|
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 {
|
|
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:
|
|
9
|
+
webpackConfig: Configuration | undefined;
|
|
12
10
|
webpackOutputFs: IFs;
|
|
13
|
-
webpackStats?:
|
|
11
|
+
webpackStats?: StatsCompilation;
|
|
14
12
|
}
|
|
15
13
|
}
|
|
16
14
|
|
|
17
15
|
export default function getGlobal(): Window {
|
|
18
|
-
return global as
|
|
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 (
|
|
64
|
-
|
|
65
|
-
|
|
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.
|
|
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(() =>
|
|
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
|
-
//
|
|
32
|
-
|
|
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:
|
|
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', () =>
|
|
71
|
-
|
|
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]
|
|
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 (
|
|
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 (
|
|
125
|
-
|
|
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 (
|
|
152
|
-
|
|
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 () =>
|
|
240
|
+
void bookStyleSheets(chunkName, clientChunkGroups, true);
|
|
241
|
+
return () => {
|
|
242
|
+
freeStyleSheets(chunkName, clientChunkGroups);
|
|
243
|
+
};
|
|
232
244
|
}, []);
|
|
233
245
|
|
|
234
246
|
return (
|
|
235
|
-
<Component
|
|
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
|
|
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>
|
package/src/shared/utils/time.ts
CHANGED
|
@@ -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>(
|
|
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>(
|
|
68
|
-
|
|
69
|
-
|
|
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<
|
|
12
|
+
export function requireWeak<T extends object>(
|
|
11
13
|
modulePath: string,
|
|
12
14
|
basePath?: string,
|
|
13
|
-
):
|
|
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
|
|
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
|
-
|
|
23
|
-
const
|
|
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 =
|
|
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
|
|
38
|
+
const res = def as T;
|
|
33
39
|
|
|
34
40
|
Object.entries(named).forEach(([name, value]) => {
|
|
35
|
-
const assigned = res[name as keyof
|
|
36
|
-
if (assigned
|
|
37
|
-
|
|
38
|
-
|
|
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 {
|