@dr.pogodin/react-utils 1.41.4 → 1.41.6

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 (44) hide show
  1. package/build/development/client/index.js +7 -8
  2. package/build/development/client/index.js.map +1 -1
  3. package/build/development/server/renderer.js +34 -29
  4. package/build/development/server/renderer.js.map +1 -1
  5. package/build/development/shared/components/GenericLink/index.js +2 -1
  6. package/build/development/shared/components/GenericLink/index.js.map +1 -1
  7. package/build/development/shared/components/Link.js +2 -2
  8. package/build/development/shared/components/Link.js.map +1 -1
  9. package/build/development/shared/components/MetaTags.js +2 -2
  10. package/build/development/shared/components/MetaTags.js.map +1 -1
  11. package/build/development/shared/components/NavLink.js +2 -2
  12. package/build/development/shared/components/NavLink.js.map +1 -1
  13. package/build/development/shared/utils/jest/E2eSsrEnv.js +16 -0
  14. package/build/development/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  15. package/build/development/web.bundle.js +140 -23
  16. package/build/production/client/index.js +2 -2
  17. package/build/production/client/index.js.map +1 -1
  18. package/build/production/server/renderer.js +4 -5
  19. package/build/production/server/renderer.js.map +1 -1
  20. package/build/production/shared/components/GenericLink/index.js +1 -1
  21. package/build/production/shared/components/GenericLink/index.js.map +1 -1
  22. package/build/production/shared/components/Link.js +2 -2
  23. package/build/production/shared/components/Link.js.map +1 -1
  24. package/build/production/shared/components/MetaTags.js +2 -2
  25. package/build/production/shared/components/MetaTags.js.map +1 -1
  26. package/build/production/shared/components/NavLink.js +1 -1
  27. package/build/production/shared/components/NavLink.js.map +1 -1
  28. package/build/production/shared/utils/jest/E2eSsrEnv.js +14 -2
  29. package/build/production/shared/utils/jest/E2eSsrEnv.js.map +1 -1
  30. package/build/production/web.bundle.js +1 -1
  31. package/build/production/web.bundle.js.map +1 -1
  32. package/build/types-code/shared/components/GenericLink/index.d.ts +1 -1
  33. package/build/types-code/shared/components/Link.d.ts +1 -1
  34. package/build/types-code/shared/components/NavLink.d.ts +1 -1
  35. package/config/jest/resolver.js +5 -3
  36. package/config/jest/setup.js +11 -1
  37. package/package.json +19 -20
  38. package/src/client/index.tsx +7 -10
  39. package/src/server/renderer.tsx +33 -38
  40. package/src/shared/components/GenericLink/index.tsx +2 -1
  41. package/src/shared/components/Link.tsx +1 -1
  42. package/src/shared/components/MetaTags.tsx +1 -1
  43. package/src/shared/components/NavLink.tsx +1 -1
  44. package/src/shared/utils/jest/E2eSsrEnv.ts +18 -0
@@ -1,5 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
- import type { Link, LinkProps, NavLink, NavLinkProps } from 'react-router-dom';
2
+ import type { Link, LinkProps, NavLink, NavLinkProps } from 'react-router';
3
3
  import './style.scss';
4
4
  type LinkT = typeof Link;
5
5
  type NavLinkT = typeof NavLink;
@@ -5,7 +5,7 @@
5
5
  * - User opts to open the reference in a new tab;
6
6
  * - User explicitely opts to use <a>.
7
7
  */
8
- import { type LinkProps } from 'react-router-dom';
8
+ import { type LinkProps } from 'react-router';
9
9
  import { type PropsT as GenericLinkPropsT } from './GenericLink';
10
10
  type PropsT = Omit<GenericLinkPropsT, 'routerLinkType'> & LinkProps;
11
11
  declare const Link: React.FunctionComponent<PropsT>;
@@ -1,4 +1,4 @@
1
- import { type NavLinkProps } from 'react-router-dom';
1
+ import { type NavLinkProps } from 'react-router';
2
2
  import { type PropsT as GenericLinkPropsT } from './GenericLink';
3
3
  type PropsT = Omit<GenericLinkPropsT, 'routerLinkType'> & NavLinkProps;
4
4
  declare const NavLink: React.FunctionComponent<PropsT>;
@@ -1,10 +1,12 @@
1
1
  module.exports = (path, options) => {
2
+ const ops = { ...options };
3
+
2
4
  // Appending "development" option we ensure that Jest tests use development
3
5
  // version of the library's build for browsers, which for example contains
4
6
  // "data-testid" attributes (they are optimized-out of production builds).
5
- if (options.conditions && !options.conditions.includes('development')) {
6
- options.conditions.push('development');
7
+ if (ops.conditions && !ops.conditions.includes('development')) {
8
+ ops.conditions = [...ops.conditions, 'development'];
7
9
  }
8
10
 
9
- return options.defaultResolver(path, { ...options });
11
+ return options.defaultResolver(path, ops);
10
12
  };
@@ -1,3 +1,13 @@
1
- /* eslint-disable import/no-extraneous-dependencies */
1
+ /* eslint-disable global-require, import/no-extraneous-dependencies */
2
+ /* global globalThis */
2
3
 
3
4
  import 'raf/polyfill';
5
+
6
+ // TODO: This is a temporary polyfill necessary for react-router,
7
+ // as JSDom does not provide TextEncoder, see:
8
+ // https://github.com/remix-run/react-router/issues/12363
9
+ if (!globalThis.TextEncoder || !globalThis.TextDecoder) {
10
+ const { TextDecoder, TextEncoder } = require('node:util');
11
+ globalThis.TextEncoder = TextEncoder;
12
+ globalThis.TextDecoder = TextDecoder;
13
+ }
package/package.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.41.4",
2
+ "version": "1.41.6",
3
3
  "bin": {
4
4
  "react-utils-build": "bin/build.js",
5
5
  "react-utils-setup": "bin/setup.js"
@@ -30,25 +30,25 @@
30
30
  "lodash": "^4.17.21",
31
31
  "morgan": "^1.10.0",
32
32
  "node-forge": "^1.3.1",
33
- "qs": "^6.13.1",
33
+ "qs": "^6.14.0",
34
34
  "raf": "^3.4.1",
35
35
  "react": "^19.0.0",
36
36
  "react-dom": "^19.0.0",
37
- "react-helmet": "^6.1.0",
38
- "react-router-dom": "^6.28.1",
37
+ "react-helmet-async": "^2.0.5",
38
+ "react-router": "^7.1.2",
39
39
  "request-ip": "^3.3.0",
40
40
  "rimraf": "^6.0.0",
41
41
  "serialize-javascript": "^6.0.2",
42
42
  "serve-favicon": "^2.5.0",
43
43
  "source-map-support": "^0.5.21",
44
- "uuid": "^11.0.4",
44
+ "uuid": "^11.0.5",
45
45
  "winston": "^3.17.0"
46
46
  },
47
47
  "description": "Collection of generic ReactJS components and utils",
48
48
  "devDependencies": {
49
49
  "@babel/cli": "^7.26.4",
50
50
  "@babel/core": "^7.26.0",
51
- "@babel/eslint-parser": "^7.25.9",
51
+ "@babel/eslint-parser": "^7.26.5",
52
52
  "@babel/eslint-plugin": "^7.25.9",
53
53
  "@babel/node": "^7.26.0",
54
54
  "@babel/plugin-transform-runtime": "^7.25.9",
@@ -60,8 +60,8 @@
60
60
  "@dr.pogodin/babel-preset-svgr": "^1.9.0",
61
61
  "@pmmmwh/react-refresh-webpack-plugin": "^0.5.15",
62
62
  "@testing-library/dom": "^10.4.0",
63
- "@testing-library/react": "^16.1.0",
64
- "@testing-library/user-event": "^14.5.2",
63
+ "@testing-library/react": "^16.2.0",
64
+ "@testing-library/user-event": "^14.6.0",
65
65
  "@tsconfig/recommended": "^1.0.8",
66
66
  "@types/compression": "^1.7.5",
67
67
  "@types/config": "^3.3.5",
@@ -74,9 +74,8 @@
74
74
  "@types/morgan": "^1.9.9",
75
75
  "@types/node-forge": "^1.3.11",
76
76
  "@types/pretty": "^2.0.3",
77
- "@types/react": "^19.0.4",
78
- "@types/react-dom": "^19.0.2",
79
- "@types/react-helmet": "^6.1.11",
77
+ "@types/react": "^19.0.7",
78
+ "@types/react-dom": "^19.0.3",
80
79
  "@types/request-ip": "^0.0.41",
81
80
  "@types/serialize-javascript": "^5.0.4",
82
81
  "@types/serve-favicon": "^2.5.7",
@@ -94,36 +93,36 @@
94
93
  "eslint-config-airbnb-typescript": "^18.0.0",
95
94
  "eslint-import-resolver-babel-module": "^5.3.2",
96
95
  "eslint-plugin-import": "^2.31.0",
97
- "eslint-plugin-jest": "^28.10.0",
96
+ "eslint-plugin-jest": "^28.11.0",
98
97
  "eslint-plugin-jsx-a11y": "^6.10.2",
99
- "eslint-plugin-react": "^7.37.3",
98
+ "eslint-plugin-react": "^7.37.4",
100
99
  "eslint-plugin-react-hooks": "^4.6.2",
101
100
  "identity-obj-proxy": "^3.0.0",
102
101
  "jest": "^29.7.0",
103
102
  "jest-environment-jsdom": "^29.7.0",
104
- "memfs": "^4.15.3",
103
+ "memfs": "^4.17.0",
105
104
  "mini-css-extract-plugin": "^2.9.2",
106
105
  "mockdate": "^3.0.5",
107
106
  "nodelist-foreach-polyfill": "^1.2.0",
108
- "postcss": "^8.4.49",
107
+ "postcss": "^8.5.1",
109
108
  "postcss-loader": "^8.1.1",
110
109
  "postcss-scss": "^4.0.9",
111
110
  "pretty": "^2.0.0",
112
111
  "react-refresh": "^0.16.0",
113
112
  "regenerator-runtime": "^0.14.1",
114
113
  "resolve-url-loader": "^5.0.0",
115
- "sass": "^1.83.1",
114
+ "sass": "^1.83.4",
116
115
  "sass-loader": "^16.0.4",
117
116
  "sitemap": "^8.0.0",
118
117
  "source-map-loader": "^5.0.0",
119
- "stylelint": "^16.12.0",
118
+ "stylelint": "^16.13.2",
120
119
  "stylelint-config-standard-scss": "^14.0.0",
121
120
  "supertest": "^7.0.0",
122
121
  "tsc-alias": "^1.8.10",
123
- "tstyche": "^3.3.1",
124
- "typed-scss-modules": "^8.1.0",
122
+ "tstyche": "^3.5.0",
123
+ "typed-scss-modules": "^8.1.1",
125
124
  "typescript": "^5.7.3",
126
- "typescript-eslint": "^8.19.1",
125
+ "typescript-eslint": "^8.20.0",
127
126
  "webpack": "^5.97.1",
128
127
  "webpack-dev-middleware": "^7.4.2",
129
128
  "webpack-hot-middleware": "^2.26.1",
@@ -2,12 +2,12 @@
2
2
  /* global document */
3
3
 
4
4
  import { type ComponentType } from 'react';
5
+ import { createRoot, hydrateRoot } from 'react-dom/client';
6
+ import { HelmetProvider } from 'react-helmet-async';
7
+ import { BrowserRouter } from 'react-router';
5
8
 
6
9
  import { GlobalStateProvider } from '@dr.pogodin/react-global-state';
7
10
 
8
- import { createRoot, hydrateRoot } from 'react-dom/client';
9
- import { BrowserRouter } from 'react-router-dom';
10
-
11
11
  import getInj from './getInj';
12
12
 
13
13
  type OptionsT = {
@@ -28,13 +28,10 @@ export default function Launch(
28
28
  if (!container) throw Error('Failed to find container for React app');
29
29
  const scene = (
30
30
  <GlobalStateProvider initialState={getInj().ISTATE || options.initialState}>
31
- <BrowserRouter
32
- future={{
33
- v7_relativeSplatPath: true,
34
- v7_startTransition: true,
35
- }}
36
- >
37
- <Application />
31
+ <BrowserRouter>
32
+ <HelmetProvider>
33
+ <Application />
34
+ </HelmetProvider>
38
35
  </BrowserRouter>
39
36
  </GlobalStateProvider>
40
37
  );
@@ -27,9 +27,9 @@ import {
27
27
  import config from 'config';
28
28
  import forge from 'node-forge';
29
29
 
30
- import { type PipeableStream, renderToPipeableStream } from 'react-dom/server';
31
- import { Helmet } from 'react-helmet';
32
- import { StaticRouter } from 'react-router-dom/server';
30
+ import { prerenderToNodeStream } from 'react-dom/static';
31
+ import { HelmetProvider } from 'react-helmet-async';
32
+ import { StaticRouter } from 'react-router';
33
33
  import serializeJs from 'serialize-javascript';
34
34
  import { type BuildInfoT, setBuildInfo } from 'utils/isomorphy/buildInfo';
35
35
 
@@ -403,7 +403,7 @@ export default function factory(
403
403
  prepareCipher(buildInfo.key) as Promise<any>,
404
404
  ]);
405
405
 
406
- let helmet;
406
+ let helmet: any;
407
407
 
408
408
  // Gets the mapping between code chunk names and their asset files.
409
409
  // These data come from the Webpack compilation, either from the stats
@@ -426,7 +426,7 @@ export default function factory(
426
426
  const App = ops.Application;
427
427
  let appHtmlMarkup: string = '';
428
428
  const ssrContext = new ServerSsrContext(req, chunkGroups, initialState);
429
- let stream: PipeableStream;
429
+ let stream: NodeJS.ReadableStream;
430
430
  if (App) {
431
431
  const ssrStart = Date.now();
432
432
 
@@ -436,30 +436,26 @@ export default function factory(
436
436
 
437
437
  const renderPass = async () => {
438
438
  ssrContext.chunks = [];
439
- return new Promise<PipeableStream>((resolve, reject) => {
440
- // TODO: pipeableStream has .abort() method,
441
- // and we should wire it up to the SSR timeout below.
442
- const pipeableStream = renderToPipeableStream(
443
- <GlobalStateProvider
444
- initialState={ssrContext.state}
445
- ssrContext={ssrContext}
446
- >
447
- <StaticRouter
448
- future={{
449
- v7_relativeSplatPath: true,
450
- v7_startTransition: true,
451
- }}
452
- location={req.url}
453
- >
439
+
440
+ // TODO: prerenderToNodeStream has (abort) "signal" option,
441
+ // and we should wire it up to the SSR timeout below.
442
+ const helmetContext = {};
443
+ const { prelude } = await prerenderToNodeStream(
444
+ <GlobalStateProvider
445
+ initialState={ssrContext.state}
446
+ ssrContext={ssrContext}
447
+ >
448
+ <StaticRouter location={req.url}>
449
+ <HelmetProvider context={helmetContext}>
454
450
  <App2 />
455
- </StaticRouter>
456
- </GlobalStateProvider>,
457
- {
458
- onAllReady: () => resolve(pipeableStream),
459
- onError: reject,
460
- },
461
- );
462
- });
451
+ </HelmetProvider>
452
+ </StaticRouter>
453
+ </GlobalStateProvider>,
454
+ { onError: (error) => { throw error; } },
455
+ );
456
+ ({ helmet } = helmetContext as any);
457
+
458
+ return prelude;
463
459
  };
464
460
 
465
461
  let ssrRound = 0;
@@ -492,16 +488,15 @@ export default function factory(
492
488
 
493
489
  ops.logger!.log(ssrContext.dirty ? 'warn' : 'info', logMsg);
494
490
 
495
- stream!.pipe(new Writable({
496
- write: (chunk, _, done) => {
497
- appHtmlMarkup += chunk.toString();
498
- done();
499
- },
500
- }));
501
-
502
- /* This takes care about server-side rendering of page title and meta tags
503
- * (still demands injection into HTML template, which happens below). */
504
- helmet = Helmet.renderStatic();
491
+ await new Promise((ready) => {
492
+ stream!.pipe(new Writable({
493
+ destroy: ready,
494
+ write: (chunk, _, done) => {
495
+ appHtmlMarkup += chunk.toString();
496
+ done();
497
+ },
498
+ }));
499
+ });
505
500
  }
506
501
 
507
502
  /* Encrypts data to be injected into HTML.
@@ -5,7 +5,7 @@ import type {
5
5
  LinkProps,
6
6
  NavLink,
7
7
  NavLinkProps,
8
- } from 'react-router-dom';
8
+ } from 'react-router';
9
9
 
10
10
  import './style.scss';
11
11
 
@@ -112,6 +112,7 @@ const GenericLink = ({
112
112
  return (
113
113
  <L
114
114
  className={className}
115
+ discover="none"
115
116
  // disabled
116
117
  onMouseDown={onMouseDown}
117
118
  replace={replace}
@@ -6,7 +6,7 @@
6
6
  * - User explicitely opts to use <a>.
7
7
  */
8
8
 
9
- import { type LinkProps, Link as RrLink } from 'react-router-dom';
9
+ import { type LinkProps, Link as RrLink } from 'react-router';
10
10
 
11
11
  import GenericLink, { type PropsT as GenericLinkPropsT } from './GenericLink';
12
12
 
@@ -6,7 +6,7 @@ import {
6
6
  useMemo,
7
7
  } from 'react';
8
8
 
9
- import { Helmet } from 'react-helmet';
9
+ import { Helmet } from 'react-helmet-async';
10
10
 
11
11
  type PropsT = {
12
12
  children?: ReactNode;
@@ -1,4 +1,4 @@
1
- import { type NavLinkProps, NavLink as RrNavLink } from 'react-router-dom';
1
+ import { type NavLinkProps, NavLink as RrNavLink } from 'react-router';
2
2
 
3
3
  import GenericLink, { type PropsT as GenericLinkPropsT } from './GenericLink';
4
4
 
@@ -37,6 +37,8 @@ import type {
37
37
  JestEnvironmentConfig,
38
38
  } from '@jest/environment';
39
39
 
40
+ import { setBuildInfo } from '../isomorphy/buildInfo';
41
+
40
42
  export default class E2eSsrEnv extends JsdomEnv {
41
43
  pragmas: Record<string, string | string[]>;
42
44
 
@@ -232,6 +234,22 @@ export default class E2eSsrEnv extends JsdomEnv {
232
234
  async setup() {
233
235
  await super.setup();
234
236
  await this.runWebpack();
237
+
238
+ // NOTE: It is possible that the Webpack run above, and the SSR run below
239
+ // load different versions of the same module (CommonJS, and ES), and it may
240
+ // cause very confusing problems (e.g. see:
241
+ // https://github.com/birdofpreyru/react-utils/issues/413).
242
+ // It seems we can't reset the cache of ES modules, and Jest's module reset
243
+ // does not reset modules loaded in this enviroment module, and also only
244
+ // replacing entire cache object by and empty {} seems to help (in contrast
245
+ // to deleting all entries by their keys, as it is done within .teardown()
246
+ // method below). Thus, for now we do this as a hotfix, and we also reset
247
+ // build info to undefined, because ES module version not beeing reset
248
+ // triggers an error on the subsequent test using the environment.
249
+ // TODO: Look for a cleaner solution.
250
+ require.cache = {};
251
+ setBuildInfo(undefined, true);
252
+
235
253
  if (this.withSsr) await this.runSsr();
236
254
  this.global.REACT_UTILS_FORCE_CLIENT_SIDE = true;
237
255
  }