@modern-js/runtime 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # @modern-js/runtime
2
2
 
3
+ ## 1.16.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1100dd58c: chore: support react 18
8
+
9
+ chore: 支持 React 18
10
+
11
+ ### Patch Changes
12
+
13
+ - a480d6ad0: fix: remove helmet regexp's global tag
14
+ fix: 删除 helmet 中正则匹配的全局标记
15
+ - Updated dependencies [641592f52]
16
+ - Updated dependencies [3904b30a5]
17
+ - Updated dependencies [1100dd58c]
18
+ - Updated dependencies [e04e6e76a]
19
+ - Updated dependencies [81c66e4a4]
20
+ - Updated dependencies [2c305b6f5]
21
+ - @modern-js/utils@1.16.0
22
+ - @modern-js/webpack@1.16.0
23
+ - @modern-js/plugin@1.16.0
24
+
3
25
  ## 1.15.0
4
26
 
5
27
  ### Patch Changes
@@ -1,5 +1,5 @@
1
1
  import path from 'path';
2
- import { PLUGIN_SCHEMAS, createRuntimeExportsUtils, cleanRequireCache } from '@modern-js/utils';
2
+ import { PLUGIN_SCHEMAS, createRuntimeExportsUtils, cleanRequireCache, isReact18 } from '@modern-js/utils';
3
3
  import PluginState from "../state/cli";
4
4
  import PluginSSR from "../ssr/cli";
5
5
  import PluginRouter from "../router/cli";
@@ -11,8 +11,9 @@ export default (() => ({
11
11
  let runtimeExportsUtils = {};
12
12
  return {
13
13
  config() {
14
- const dir = api.useAppContext().internalDirectory;
14
+ const dir = api.useAppContext().internalDirectory || '';
15
15
  runtimeExportsUtils = createRuntimeExportsUtils(dir, 'index');
16
+ process.env.IS_REACT18 = isReact18(path.join(dir, '../../')).toString();
16
17
  return {
17
18
  runtime: {},
18
19
  runtimeByEntries: {},
@@ -26,7 +27,8 @@ export default (() => ({
26
27
  * So need to add alias
27
28
  */
28
29
  'styled-components': require.resolve('styled-components')
29
- }
30
+ },
31
+ envVars: ['IS_REACT18']
30
32
  }
31
33
  };
32
34
  },
@@ -11,12 +11,13 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
11
11
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
12
12
 
13
13
  import React, { useContext, useMemo } from 'react';
14
- import ReactDOM from 'react-dom';
14
+ import defaultReactDOM from 'react-dom';
15
15
  import hoistNonReactStatics from 'hoist-non-react-statics';
16
16
  import { runtime } from "./plugin";
17
17
  import { RuntimeReactContext } from "./runtime-context";
18
18
  import { createLoaderManager } from "./loader/loaderManager";
19
19
  import { jsx as _jsx } from "react/jsx-runtime";
20
+ const IS_REACT18 = process.env.IS_REACT18 === 'true';
20
21
 
21
22
  function isClientArgs(id) {
22
23
  return typeof id === 'string' || typeof HTMLElement !== 'undefined' && id instanceof HTMLElement;
@@ -105,7 +106,11 @@ export const bootstrap = async (BootApp,
105
106
  * When csr, id is root id.
106
107
  * When ssr, id is serverContext
107
108
  */
108
- id) => {
109
+ id,
110
+ /**
111
+ * root.render need use root to run function
112
+ */
113
+ root, render = defaultReactDOM.render, hydrate = defaultReactDOM.hydrate) => {
109
114
  let App = BootApp;
110
115
  let runner = runnerMap.get(App); // ensure Component used is created by `createApp`
111
116
 
@@ -169,18 +174,36 @@ id) => {
169
174
  context.initialData = initialData;
170
175
  }
171
176
 
177
+ const rootElement = typeof id !== 'string' ? id : document.getElementById(id || 'root'); // https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html
178
+
179
+ const ModernRender = App => {
180
+ if (IS_REACT18) {
181
+ root.render(App);
182
+ } else {
183
+ render(App, rootElement);
184
+ }
185
+ };
186
+
187
+ const ModernHydrate = (App, callback) => {
188
+ if (IS_REACT18) {
189
+ hydrate(rootElement, App);
190
+ } else {
191
+ hydrate(App, rootElement, callback);
192
+ }
193
+ };
194
+
172
195
  return runner.client({
173
196
  App,
174
197
  context,
175
- rootElement: typeof id !== 'string' ? id : document.getElementById(id || 'root')
198
+ ModernRender,
199
+ ModernHydrate
176
200
  }, {
177
201
  onLast: ({
178
- App,
179
- rootElement
202
+ App
180
203
  }) => {
181
- ReactDOM.render( /*#__PURE__*/React.createElement(App, {
204
+ ModernRender( /*#__PURE__*/React.createElement(App, {
182
205
  context
183
- }), rootElement);
206
+ }));
184
207
  }
185
208
  });
186
209
  } else {
@@ -4,11 +4,13 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
4
4
 
5
5
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
6
6
 
7
- import ReactDOM from 'react-dom';
8
7
  import { loadableReady } from '@loadable/component';
8
+ import hoistNonReactStatics from 'hoist-non-react-statics';
9
9
  import { RenderLevel } from "./serverRender/type";
10
+ import { WithCallback } from "./react/withCallback";
10
11
  import { formatClient, mockResponse } from "./utils";
11
12
  import { jsx as _jsx } from "react/jsx-runtime";
13
+ const IS_REACT18 = process.env.IS_REACT18 === 'true';
12
14
 
13
15
  const ssr = () => ({
14
16
  name: '@modern-js/plugin-ssr',
@@ -18,37 +20,52 @@ const ssr = () => ({
18
20
  client: async ({
19
21
  App,
20
22
  context,
21
- rootElement
23
+ ModernRender,
24
+ ModernHydrate
22
25
  }) => {
23
26
  var _window, _window$_SSR_DATA;
24
27
 
25
28
  const renderLevel = (_window = window) === null || _window === void 0 ? void 0 : (_window$_SSR_DATA = _window._SSR_DATA) === null || _window$_SSR_DATA === void 0 ? void 0 : _window$_SSR_DATA.renderLevel;
26
29
 
27
30
  if (renderLevel === RenderLevel.CLIENT_RENDER) {
28
- var _prefetch, _ref;
29
-
30
- await (App === null || App === void 0 ? void 0 : (_prefetch = (_ref = App).prefetch) === null || _prefetch === void 0 ? void 0 : _prefetch.call(_ref, context));
31
- ReactDOM.render( /*#__PURE__*/_jsx(App, {
31
+ // prefetch block render while csr
32
+ // await (App as any)?.prefetch?.(context);
33
+ ModernRender( /*#__PURE__*/_jsx(App, {
32
34
  context: context
33
- }), rootElement);
35
+ }));
34
36
  } else if (renderLevel === RenderLevel.SERVER_RENDER) {
35
37
  loadableReady(() => {
36
38
  const hydrateContext = _objectSpread(_objectSpread({}, context), {}, {
37
39
  _hydration: true
38
40
  });
39
41
 
40
- ReactDOM.hydrate( /*#__PURE__*/_jsx(App, {
41
- context: hydrateContext
42
- }), rootElement, () => {
42
+ const callback = () => {
43
43
  // won't cause component re-render because context's reference identity doesn't change
44
44
  delete hydrateContext._hydration;
45
- });
45
+ }; // callback: https://github.com/reactwg/react-18/discussions/5
46
+
47
+
48
+ if (IS_REACT18) {
49
+ let SSRApp = () => /*#__PURE__*/_jsx(WithCallback, {
50
+ callback: callback,
51
+ children: /*#__PURE__*/_jsx(App, {
52
+ context: hydrateContext
53
+ })
54
+ });
55
+
56
+ SSRApp = hoistNonReactStatics(SSRApp, App);
57
+ ModernHydrate( /*#__PURE__*/_jsx(SSRApp, {}));
58
+ } else {
59
+ ModernHydrate( /*#__PURE__*/_jsx(App, {
60
+ context: hydrateContext
61
+ }), callback);
62
+ }
46
63
  });
47
64
  } else {
48
65
  // unknown renderlevel or renderlevel is server prefetch.
49
- ReactDOM.render( /*#__PURE__*/_jsx(App, {
66
+ ModernHydrate( /*#__PURE__*/_jsx(App, {
50
67
  context: context
51
- }), rootElement);
68
+ }));
52
69
  }
53
70
  },
54
71
 
@@ -0,0 +1,17 @@
1
+ // See https://github.com/reactwg/react-18/discussions/5#discussioncomment-2276079
2
+ import { useRef, useLayoutEffect } from 'react';
3
+ export const WithCallback = ({
4
+ callback,
5
+ children
6
+ }) => {
7
+ const once = useRef(false);
8
+ useLayoutEffect(() => {
9
+ if (once.current) {
10
+ return;
11
+ }
12
+
13
+ once.current = true;
14
+ callback();
15
+ }, [callback]);
16
+ return children;
17
+ };
@@ -2,8 +2,8 @@
2
2
  const RE_HTML_ATTR = /<html[^>]*>/;
3
3
  const RE_BODY_ATTR = /<body[^>]*>/;
4
4
  const RE_LAST_IN_HEAD = /<\/head>/;
5
- const RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/g;
6
- const TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/g; // 通过 react-helmet 修改模板
5
+ const RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/;
6
+ const TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/; // 通过 react-helmet 修改模板
7
7
 
8
8
  export default function helmet(content, helmetData) {
9
9
  let result = content;
@@ -25,8 +25,9 @@ var _default = () => ({
25
25
  let runtimeExportsUtils = {};
26
26
  return {
27
27
  config() {
28
- const dir = api.useAppContext().internalDirectory;
28
+ const dir = api.useAppContext().internalDirectory || '';
29
29
  runtimeExportsUtils = (0, _utils.createRuntimeExportsUtils)(dir, 'index');
30
+ process.env.IS_REACT18 = (0, _utils.isReact18)(_path.default.join(dir, '../../')).toString();
30
31
  return {
31
32
  runtime: {},
32
33
  runtimeByEntries: {},
@@ -40,7 +41,8 @@ var _default = () => ({
40
41
  * So need to add alias
41
42
  */
42
43
  'styled-components': require.resolve('styled-components')
43
- }
44
+ },
45
+ envVars: ['IS_REACT18']
44
46
  }
45
47
  };
46
48
  },
@@ -37,6 +37,8 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
37
37
 
38
38
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
39
39
 
40
+ const IS_REACT18 = process.env.IS_REACT18 === 'true';
41
+
40
42
  function isClientArgs(id) {
41
43
  return typeof id === 'string' || typeof HTMLElement !== 'undefined' && id instanceof HTMLElement;
42
44
  }
@@ -129,7 +131,11 @@ const bootstrap = async (BootApp,
129
131
  * When csr, id is root id.
130
132
  * When ssr, id is serverContext
131
133
  */
132
- id) => {
134
+ id,
135
+ /**
136
+ * root.render need use root to run function
137
+ */
138
+ root, render = _reactDom.default.render, hydrate = _reactDom.default.hydrate) => {
133
139
  let App = BootApp;
134
140
  let runner = runnerMap.get(App); // ensure Component used is created by `createApp`
135
141
 
@@ -193,18 +199,36 @@ id) => {
193
199
  context.initialData = initialData;
194
200
  }
195
201
 
202
+ const rootElement = typeof id !== 'string' ? id : document.getElementById(id || 'root'); // https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html
203
+
204
+ const ModernRender = App => {
205
+ if (IS_REACT18) {
206
+ root.render(App);
207
+ } else {
208
+ render(App, rootElement);
209
+ }
210
+ };
211
+
212
+ const ModernHydrate = (App, callback) => {
213
+ if (IS_REACT18) {
214
+ hydrate(rootElement, App);
215
+ } else {
216
+ hydrate(App, rootElement, callback);
217
+ }
218
+ };
219
+
196
220
  return runner.client({
197
221
  App,
198
222
  context,
199
- rootElement: typeof id !== 'string' ? id : document.getElementById(id || 'root')
223
+ ModernRender,
224
+ ModernHydrate
200
225
  }, {
201
226
  onLast: ({
202
- App,
203
- rootElement
227
+ App
204
228
  }) => {
205
- _reactDom.default.render( /*#__PURE__*/_react.default.createElement(App, {
229
+ ModernRender( /*#__PURE__*/_react.default.createElement(App, {
206
230
  context
207
- }), rootElement);
231
+ }));
208
232
  }
209
233
  });
210
234
  } else {
@@ -6,12 +6,14 @@ Object.defineProperty(exports, "__esModule", {
6
6
  var _exportNames = {};
7
7
  exports.default = void 0;
8
8
 
9
- var _reactDom = _interopRequireDefault(require("react-dom"));
10
-
11
9
  var _component = require("@loadable/component");
12
10
 
11
+ var _hoistNonReactStatics = _interopRequireDefault(require("hoist-non-react-statics"));
12
+
13
13
  var _type = require("./serverRender/type");
14
14
 
15
+ var _withCallback = require("./react/withCallback");
16
+
15
17
  var _utils = require("./utils");
16
18
 
17
19
  var _jsxRuntime = require("react/jsx-runtime");
@@ -38,6 +40,8 @@ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { va
38
40
 
39
41
  function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
40
42
 
43
+ const IS_REACT18 = process.env.IS_REACT18 === 'true';
44
+
41
45
  const ssr = () => ({
42
46
  name: '@modern-js/plugin-ssr',
43
47
  setup: () => {
@@ -46,38 +50,52 @@ const ssr = () => ({
46
50
  client: async ({
47
51
  App,
48
52
  context,
49
- rootElement
53
+ ModernRender,
54
+ ModernHydrate
50
55
  }) => {
51
56
  var _window, _window$_SSR_DATA;
52
57
 
53
58
  const renderLevel = (_window = window) === null || _window === void 0 ? void 0 : (_window$_SSR_DATA = _window._SSR_DATA) === null || _window$_SSR_DATA === void 0 ? void 0 : _window$_SSR_DATA.renderLevel;
54
59
 
55
60
  if (renderLevel === _type.RenderLevel.CLIENT_RENDER) {
56
- var _prefetch, _ref;
57
-
58
- await (App === null || App === void 0 ? void 0 : (_prefetch = (_ref = App).prefetch) === null || _prefetch === void 0 ? void 0 : _prefetch.call(_ref, context));
59
-
60
- _reactDom.default.render( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
61
+ // prefetch block render while csr
62
+ // await (App as any)?.prefetch?.(context);
63
+ ModernRender( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
61
64
  context: context
62
- }), rootElement);
65
+ }));
63
66
  } else if (renderLevel === _type.RenderLevel.SERVER_RENDER) {
64
67
  (0, _component.loadableReady)(() => {
65
68
  const hydrateContext = _objectSpread(_objectSpread({}, context), {}, {
66
69
  _hydration: true
67
70
  });
68
71
 
69
- _reactDom.default.hydrate( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
70
- context: hydrateContext
71
- }), rootElement, () => {
72
+ const callback = () => {
72
73
  // won't cause component re-render because context's reference identity doesn't change
73
74
  delete hydrateContext._hydration;
74
- });
75
+ }; // callback: https://github.com/reactwg/react-18/discussions/5
76
+
77
+
78
+ if (IS_REACT18) {
79
+ let SSRApp = () => /*#__PURE__*/(0, _jsxRuntime.jsx)(_withCallback.WithCallback, {
80
+ callback: callback,
81
+ children: /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
82
+ context: hydrateContext
83
+ })
84
+ });
85
+
86
+ SSRApp = (0, _hoistNonReactStatics.default)(SSRApp, App);
87
+ ModernHydrate( /*#__PURE__*/(0, _jsxRuntime.jsx)(SSRApp, {}));
88
+ } else {
89
+ ModernHydrate( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
90
+ context: hydrateContext
91
+ }), callback);
92
+ }
75
93
  });
76
94
  } else {
77
95
  // unknown renderlevel or renderlevel is server prefetch.
78
- _reactDom.default.render( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
96
+ ModernHydrate( /*#__PURE__*/(0, _jsxRuntime.jsx)(App, {
79
97
  context: context
80
- }), rootElement);
98
+ }));
81
99
  }
82
100
  },
83
101
 
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.WithCallback = void 0;
7
+
8
+ var _react = require("react");
9
+
10
+ // See https://github.com/reactwg/react-18/discussions/5#discussioncomment-2276079
11
+ const WithCallback = ({
12
+ callback,
13
+ children
14
+ }) => {
15
+ const once = (0, _react.useRef)(false);
16
+ (0, _react.useLayoutEffect)(() => {
17
+ if (once.current) {
18
+ return;
19
+ }
20
+
21
+ once.current = true;
22
+ callback();
23
+ }, [callback]);
24
+ return children;
25
+ };
26
+
27
+ exports.WithCallback = WithCallback;
@@ -8,8 +8,8 @@ exports.default = helmet;
8
8
  const RE_HTML_ATTR = /<html[^>]*>/;
9
9
  const RE_BODY_ATTR = /<body[^>]*>/;
10
10
  const RE_LAST_IN_HEAD = /<\/head>/;
11
- const RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/g;
12
- const TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/g; // 通过 react-helmet 修改模板
11
+ const RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/;
12
+ const TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/; // 通过 react-helmet 修改模板
13
13
 
14
14
  function helmet(content, helmetData) {
15
15
  let result = content;
@@ -1,7 +1,7 @@
1
1
  import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
2
2
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
3
3
  import path from 'path';
4
- import { PLUGIN_SCHEMAS, createRuntimeExportsUtils, cleanRequireCache } from '@modern-js/utils';
4
+ import { PLUGIN_SCHEMAS, createRuntimeExportsUtils, cleanRequireCache, isReact18 } from '@modern-js/utils';
5
5
  import PluginState from "../state/cli";
6
6
  import PluginSSR from "../ssr/cli";
7
7
  import PluginRouter from "../router/cli";
@@ -14,8 +14,9 @@ export default (function () {
14
14
  var runtimeExportsUtils = {};
15
15
  return {
16
16
  config: function config() {
17
- var dir = api.useAppContext().internalDirectory;
17
+ var dir = api.useAppContext().internalDirectory || '';
18
18
  runtimeExportsUtils = createRuntimeExportsUtils(dir, 'index');
19
+ process.env.IS_REACT18 = isReact18(path.join(dir, '../../')).toString();
19
20
  return {
20
21
  runtime: {},
21
22
  runtimeByEntries: {},
@@ -29,7 +30,8 @@ export default (function () {
29
30
  * So need to add alias
30
31
  */
31
32
  'styled-components': require.resolve('styled-components')
32
- }
33
+ },
34
+ envVars: ['IS_REACT18']
33
35
  }
34
36
  };
35
37
  },
@@ -5,12 +5,13 @@ import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
5
5
  import _toConsumableArray from "@babel/runtime/helpers/esm/toConsumableArray";
6
6
  var _excluded = ["context"];
7
7
  import React, { useContext, useMemo } from 'react';
8
- import ReactDOM from 'react-dom';
8
+ import defaultReactDOM from 'react-dom';
9
9
  import hoistNonReactStatics from 'hoist-non-react-statics';
10
10
  import { runtime } from "./plugin";
11
11
  import { RuntimeReactContext } from "./runtime-context";
12
12
  import { createLoaderManager } from "./loader/loaderManager";
13
13
  import { jsx as _jsx } from "react/jsx-runtime";
14
+ var IS_REACT18 = process.env.IS_REACT18 === 'true';
14
15
 
15
16
  function isClientArgs(id) {
16
17
  return typeof id === 'string' || typeof HTMLElement !== 'undefined' && id instanceof HTMLElement;
@@ -99,13 +100,36 @@ export var bootstrap = /*#__PURE__*/function () {
99
100
  * When csr, id is root id.
100
101
  * When ssr, id is serverContext
101
102
  */
102
- id) {
103
- var App, runner, context, runInit, isBrowser, _ssrData$data, _ssrData$data2, ssrData, loadersData, initialLoadersState, initialData, _initialData;
103
+ id,
104
+ /**
105
+ * root.render need use root to run function
106
+ */
107
+ root) {
108
+ var render,
109
+ hydrate,
110
+ App,
111
+ runner,
112
+ context,
113
+ runInit,
114
+ isBrowser,
115
+ _ssrData$data,
116
+ _ssrData$data2,
117
+ ssrData,
118
+ loadersData,
119
+ initialLoadersState,
120
+ initialData,
121
+ _rootElement,
122
+ ModernRender,
123
+ ModernHydrate,
124
+ _initialData,
125
+ _args = arguments;
104
126
 
105
127
  return _regeneratorRuntime().wrap(function _callee$(_context2) {
106
128
  while (1) {
107
129
  switch (_context2.prev = _context2.next) {
108
130
  case 0:
131
+ render = _args.length > 3 && _args[3] !== undefined ? _args[3] : defaultReactDOM.render;
132
+ hydrate = _args.length > 4 && _args[4] !== undefined ? _args[4] : defaultReactDOM.hydrate;
109
133
  App = BootApp;
110
134
  runner = runnerMap.get(App); // ensure Component used is created by `createApp`
111
135
 
@@ -133,7 +157,7 @@ export var bootstrap = /*#__PURE__*/function () {
133
157
 
134
158
 
135
159
  if (id) {
136
- _context2.next = 7;
160
+ _context2.next = 9;
137
161
  break;
138
162
  }
139
163
 
@@ -141,16 +165,16 @@ export var bootstrap = /*#__PURE__*/function () {
141
165
  context: context
142
166
  }));
143
167
 
144
- case 7:
168
+ case 9:
145
169
  isBrowser = typeof window !== 'undefined' && window.name !== 'nodejs';
146
170
 
147
171
  if (!isBrowser) {
148
- _context2.next = 25;
172
+ _context2.next = 30;
149
173
  break;
150
174
  }
151
175
 
152
176
  if (!isClientArgs(id)) {
153
- _context2.next = 22;
177
+ _context2.next = 27;
154
178
  break;
155
179
  }
156
180
 
@@ -174,38 +198,56 @@ export var bootstrap = /*#__PURE__*/function () {
174
198
  ssrContext: ssrData === null || ssrData === void 0 ? void 0 : ssrData.context
175
199
  } : {}));
176
200
  context.initialData = ssrData === null || ssrData === void 0 ? void 0 : (_ssrData$data2 = ssrData.data) === null || _ssrData$data2 === void 0 ? void 0 : _ssrData$data2.initialData;
177
- _context2.next = 17;
201
+ _context2.next = 19;
178
202
  return runInit(context);
179
203
 
180
- case 17:
204
+ case 19:
181
205
  initialData = _context2.sent;
182
206
 
183
207
  if (initialData) {
184
208
  context.initialData = initialData;
185
209
  }
186
210
 
211
+ _rootElement = typeof id !== 'string' ? id : document.getElementById(id || 'root'); // https://reactjs.org/blog/2022/03/08/react-18-upgrade-guide.html
212
+
213
+ ModernRender = function ModernRender(App) {
214
+ if (IS_REACT18) {
215
+ root.render(App);
216
+ } else {
217
+ render(App, _rootElement);
218
+ }
219
+ };
220
+
221
+ ModernHydrate = function ModernHydrate(App, callback) {
222
+ if (IS_REACT18) {
223
+ hydrate(_rootElement, App);
224
+ } else {
225
+ hydrate(App, _rootElement, callback);
226
+ }
227
+ };
228
+
187
229
  return _context2.abrupt("return", runner.client({
188
230
  App: App,
189
231
  context: context,
190
- rootElement: typeof id !== 'string' ? id : document.getElementById(id || 'root')
232
+ ModernRender: ModernRender,
233
+ ModernHydrate: ModernHydrate
191
234
  }, {
192
235
  onLast: function onLast(_ref8) {
193
- var App = _ref8.App,
194
- rootElement = _ref8.rootElement;
195
- ReactDOM.render( /*#__PURE__*/React.createElement(App, {
236
+ var App = _ref8.App;
237
+ ModernRender( /*#__PURE__*/React.createElement(App, {
196
238
  context: context
197
- }), rootElement);
239
+ }));
198
240
  }
199
241
  }));
200
242
 
201
- case 22:
243
+ case 27:
202
244
  throw Error('`bootstrap` needs id in browser environment, it needs to be string or element');
203
245
 
204
- case 23:
205
- _context2.next = 31;
246
+ case 28:
247
+ _context2.next = 36;
206
248
  break;
207
249
 
208
- case 25:
250
+ case 30:
209
251
  Object.assign(context, {
210
252
  ssrContext: id,
211
253
  isBrowser: false,
@@ -215,10 +257,10 @@ export var bootstrap = /*#__PURE__*/function () {
215
257
  skipStatic: process.env.NODE_ENV === 'production' && !id.staticGenerate
216
258
  })
217
259
  });
218
- _context2.next = 28;
260
+ _context2.next = 33;
219
261
  return runInit(context);
220
262
 
221
- case 28:
263
+ case 33:
222
264
  _initialData = _context2.sent;
223
265
  context.initialData = _initialData;
224
266
  return _context2.abrupt("return", runner.server({
@@ -226,7 +268,7 @@ export var bootstrap = /*#__PURE__*/function () {
226
268
  context: context
227
269
  }));
228
270
 
229
- case 31:
271
+ case 36:
230
272
  case "end":
231
273
  return _context2.stop();
232
274
  }
@@ -234,7 +276,7 @@ export var bootstrap = /*#__PURE__*/function () {
234
276
  }, _callee);
235
277
  }));
236
278
 
237
- return function bootstrap(_x, _x2) {
279
+ return function bootstrap(_x, _x2, _x3) {
238
280
  return _ref6.apply(this, arguments);
239
281
  };
240
282
  }();
@@ -1,11 +1,13 @@
1
1
  import _regeneratorRuntime from "@babel/runtime/helpers/esm/regeneratorRuntime";
2
2
  import _objectSpread from "@babel/runtime/helpers/esm/objectSpread2";
3
3
  import _asyncToGenerator from "@babel/runtime/helpers/esm/asyncToGenerator";
4
- import ReactDOM from 'react-dom';
5
4
  import { loadableReady } from '@loadable/component';
5
+ import hoistNonReactStatics from 'hoist-non-react-statics';
6
6
  import { RenderLevel } from "./serverRender/type";
7
+ import { WithCallback } from "./react/withCallback";
7
8
  import { formatClient, mockResponse } from "./utils";
8
9
  import { jsx as _jsx } from "react/jsx-runtime";
10
+ var IS_REACT18 = process.env.IS_REACT18 === 'true';
9
11
 
10
12
  var ssr = function ssr() {
11
13
  return {
@@ -17,52 +19,58 @@ var ssr = function ssr() {
17
19
  var _client = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime().mark(function _callee(_ref) {
18
20
  var _window, _window$_SSR_DATA;
19
21
 
20
- var App, context, rootElement, renderLevel, _prefetch, _ref2;
21
-
22
+ var App, context, ModernRender, ModernHydrate, renderLevel;
22
23
  return _regeneratorRuntime().wrap(function _callee$(_context) {
23
24
  while (1) {
24
25
  switch (_context.prev = _context.next) {
25
26
  case 0:
26
- App = _ref.App, context = _ref.context, rootElement = _ref.rootElement;
27
+ App = _ref.App, context = _ref.context, ModernRender = _ref.ModernRender, ModernHydrate = _ref.ModernHydrate;
27
28
  renderLevel = (_window = window) === null || _window === void 0 ? void 0 : (_window$_SSR_DATA = _window._SSR_DATA) === null || _window$_SSR_DATA === void 0 ? void 0 : _window$_SSR_DATA.renderLevel;
28
29
 
29
- if (!(renderLevel === RenderLevel.CLIENT_RENDER)) {
30
- _context.next = 8;
31
- break;
32
- }
33
-
34
- _context.next = 5;
35
- return App === null || App === void 0 ? void 0 : (_prefetch = (_ref2 = App).prefetch) === null || _prefetch === void 0 ? void 0 : _prefetch.call(_ref2, context);
36
-
37
- case 5:
38
- ReactDOM.render( /*#__PURE__*/_jsx(App, {
39
- context: context
40
- }), rootElement);
41
- _context.next = 9;
42
- break;
43
-
44
- case 8:
45
- if (renderLevel === RenderLevel.SERVER_RENDER) {
30
+ if (renderLevel === RenderLevel.CLIENT_RENDER) {
31
+ // prefetch block render while csr
32
+ // await (App as any)?.prefetch?.(context);
33
+ ModernRender( /*#__PURE__*/_jsx(App, {
34
+ context: context
35
+ }));
36
+ } else if (renderLevel === RenderLevel.SERVER_RENDER) {
46
37
  loadableReady(function () {
47
38
  var hydrateContext = _objectSpread(_objectSpread({}, context), {}, {
48
39
  _hydration: true
49
40
  });
50
41
 
51
- ReactDOM.hydrate( /*#__PURE__*/_jsx(App, {
52
- context: hydrateContext
53
- }), rootElement, function () {
42
+ var callback = function callback() {
54
43
  // won't cause component re-render because context's reference identity doesn't change
55
44
  delete hydrateContext._hydration;
56
- });
45
+ }; // callback: https://github.com/reactwg/react-18/discussions/5
46
+
47
+
48
+ if (IS_REACT18) {
49
+ var SSRApp = function SSRApp() {
50
+ return /*#__PURE__*/_jsx(WithCallback, {
51
+ callback: callback,
52
+ children: /*#__PURE__*/_jsx(App, {
53
+ context: hydrateContext
54
+ })
55
+ });
56
+ };
57
+
58
+ SSRApp = hoistNonReactStatics(SSRApp, App);
59
+ ModernHydrate( /*#__PURE__*/_jsx(SSRApp, {}));
60
+ } else {
61
+ ModernHydrate( /*#__PURE__*/_jsx(App, {
62
+ context: hydrateContext
63
+ }), callback);
64
+ }
57
65
  });
58
66
  } else {
59
67
  // unknown renderlevel or renderlevel is server prefetch.
60
- ReactDOM.render( /*#__PURE__*/_jsx(App, {
68
+ ModernHydrate( /*#__PURE__*/_jsx(App, {
61
69
  context: context
62
- }), rootElement);
70
+ }));
63
71
  }
64
72
 
65
- case 9:
73
+ case 3:
66
74
  case "end":
67
75
  return _context.stop();
68
76
  }
@@ -76,10 +84,10 @@ var ssr = function ssr() {
76
84
 
77
85
  return client;
78
86
  }(),
79
- init: function init(_ref3, next) {
87
+ init: function init(_ref2, next) {
80
88
  var _window2, _window2$_SSR_DATA, _window2$_SSR_DATA$co;
81
89
 
82
- var context = _ref3.context;
90
+ var context = _ref2.context;
83
91
  var request = (_window2 = window) === null || _window2 === void 0 ? void 0 : (_window2$_SSR_DATA = _window2._SSR_DATA) === null || _window2$_SSR_DATA === void 0 ? void 0 : (_window2$_SSR_DATA$co = _window2$_SSR_DATA.context) === null || _window2$_SSR_DATA$co === void 0 ? void 0 : _window2$_SSR_DATA$co.request;
84
92
 
85
93
  if (!request) {
@@ -98,11 +106,11 @@ var ssr = function ssr() {
98
106
  context: context
99
107
  });
100
108
  },
101
- pickContext: function pickContext(_ref4, next) {
109
+ pickContext: function pickContext(_ref3, next) {
102
110
  var _window3, _window3$_SSR_DATA, _window3$_SSR_DATA$co;
103
111
 
104
- var context = _ref4.context,
105
- pickedContext = _ref4.pickedContext;
112
+ var context = _ref3.context,
113
+ pickedContext = _ref3.pickedContext;
106
114
  var request = (_window3 = window) === null || _window3 === void 0 ? void 0 : (_window3$_SSR_DATA = _window3._SSR_DATA) === null || _window3$_SSR_DATA === void 0 ? void 0 : (_window3$_SSR_DATA$co = _window3$_SSR_DATA.context) === null || _window3$_SSR_DATA$co === void 0 ? void 0 : _window3$_SSR_DATA$co.request;
107
115
  var initialData = context.initialData;
108
116
 
@@ -0,0 +1,16 @@
1
+ // See https://github.com/reactwg/react-18/discussions/5#discussioncomment-2276079
2
+ import { useRef, useLayoutEffect } from 'react';
3
+ export var WithCallback = function WithCallback(_ref) {
4
+ var callback = _ref.callback,
5
+ children = _ref.children;
6
+ var once = useRef(false);
7
+ useLayoutEffect(function () {
8
+ if (once.current) {
9
+ return;
10
+ }
11
+
12
+ once.current = true;
13
+ callback();
14
+ }, [callback]);
15
+ return children;
16
+ };
@@ -2,8 +2,8 @@
2
2
  var RE_HTML_ATTR = /<html[^>]*>/;
3
3
  var RE_BODY_ATTR = /<body[^>]*>/;
4
4
  var RE_LAST_IN_HEAD = /<\/head>/;
5
- var RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/g;
6
- var TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/g; // 通过 react-helmet 修改模板
5
+ var RE_TITLE = /<title[^>]*>([\s\S\n\r]*?)<\/title>/;
6
+ var TEST_TITLE_CONTENT = /(?<=<title[^>]*>)([\s\S\n\r]*?)([.|\S])([\s\S\n\r]*?)(?=<\/title>)/; // 通过 react-helmet 修改模板
7
7
 
8
8
  export default function helmet(content, helmetData) {
9
9
  var result = content;
@@ -7,7 +7,11 @@ export declare type CreateAppOptions = {
7
7
  export declare const createApp: ({
8
8
  plugins
9
9
  }: CreateAppOptions) => (App?: React.ComponentType<any>) => React.ComponentType<any>;
10
- declare type BootStrap<T = unknown> = (App: React.ComponentType, id?: string | Record<string, any> | HTMLElement) => Promise<T>;
10
+ interface HydrateFunc {
11
+ (container: Element | Document, initialChildren: React.ReactNode): void;
12
+ (initialChildren: React.ReactNode, container: Element | Document, callback?: () => void): void;
13
+ }
14
+ declare type BootStrap<T = unknown> = (App: React.ComponentType, id?: string | Record<string, any> | HTMLElement, root?: any, render?: (children: React.ReactNode, rootElement?: HTMLElement) => void, hydrate?: HydrateFunc) => Promise<T>;
11
15
  export declare const bootstrap: BootStrap;
12
16
  export declare const useRuntimeContext: () => TRuntimeContext;
13
17
  export {};
@@ -14,7 +14,8 @@ declare const runtimeHooks: {
14
14
  client: import("@modern-js/plugin").AsyncPipeline<{
15
15
  App: React.ComponentType<any>;
16
16
  readonly context?: RuntimeContext | undefined;
17
- rootElement: HTMLElement;
17
+ ModernRender: (App: React.ReactNode) => void;
18
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
18
19
  }, void>;
19
20
  server: import("@modern-js/plugin").AsyncPipeline<{
20
21
  App: React.ComponentType<any>;
@@ -52,7 +53,8 @@ export declare const createRuntime: () => import("@modern-js/plugin").Manager<{
52
53
  client: import("@modern-js/plugin").AsyncPipeline<{
53
54
  App: React.ComponentType<any>;
54
55
  readonly context?: RuntimeContext | undefined;
55
- rootElement: HTMLElement;
56
+ ModernRender: (App: React.ReactNode) => void;
57
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
56
58
  }, void>;
57
59
  server: import("@modern-js/plugin").AsyncPipeline<{
58
60
  App: React.ComponentType<any>;
@@ -90,7 +92,8 @@ export declare const runtime: import("@modern-js/plugin").Manager<{
90
92
  client: import("@modern-js/plugin").AsyncPipeline<{
91
93
  App: React.ComponentType<any>;
92
94
  readonly context?: RuntimeContext | undefined;
93
- rootElement: HTMLElement;
95
+ ModernRender: (App: React.ReactNode) => void;
96
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
94
97
  }, void>;
95
98
  server: import("@modern-js/plugin").AsyncPipeline<{
96
99
  App: React.ComponentType<any>;
@@ -116,7 +119,8 @@ export declare const createPlugin: (setup?: Setup<{
116
119
  client: import("@modern-js/plugin").AsyncPipeline<{
117
120
  App: React.ComponentType<any>;
118
121
  readonly context?: RuntimeContext | undefined;
119
- rootElement: HTMLElement;
122
+ ModernRender: (App: React.ReactNode) => void;
123
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
120
124
  }, void>;
121
125
  server: import("@modern-js/plugin").AsyncPipeline<{
122
126
  App: React.ComponentType<any>;
@@ -141,7 +145,8 @@ export declare const createPlugin: (setup?: Setup<{
141
145
  client: import("@modern-js/plugin").AsyncPipeline<{
142
146
  App: React.ComponentType<any>;
143
147
  readonly context?: RuntimeContext | undefined;
144
- rootElement: HTMLElement;
148
+ ModernRender: (App: React.ReactNode) => void;
149
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
145
150
  }, void>;
146
151
  server: import("@modern-js/plugin").AsyncPipeline<{
147
152
  App: React.ComponentType<any>;
@@ -166,7 +171,8 @@ export declare const createPlugin: (setup?: Setup<{
166
171
  client: import("@modern-js/plugin").AsyncPipeline<{
167
172
  App: React.ComponentType<any>;
168
173
  readonly context?: RuntimeContext | undefined;
169
- rootElement: HTMLElement;
174
+ ModernRender: (App: React.ReactNode) => void;
175
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
170
176
  }, void>;
171
177
  server: import("@modern-js/plugin").AsyncPipeline<{
172
178
  App: React.ComponentType<any>;
@@ -191,7 +197,8 @@ export declare const createPlugin: (setup?: Setup<{
191
197
  client: import("@modern-js/plugin").AsyncPipeline<{
192
198
  App: React.ComponentType<any>;
193
199
  readonly context?: RuntimeContext | undefined;
194
- rootElement: HTMLElement;
200
+ ModernRender: (App: React.ReactNode) => void;
201
+ ModernHydrate: (App: React.ReactNode, callback?: () => void) => void;
195
202
  }, void>;
196
203
  server: import("@modern-js/plugin").AsyncPipeline<{
197
204
  App: React.ComponentType<any>;
@@ -1,2 +1,2 @@
1
1
  import React from 'react';
2
- export declare const NoSSR: (props: React.PropsWithChildren<React.ReactNode>) => React.FunctionComponentElement<{}>;
2
+ export declare const NoSSR: (props: React.PropsWithChildren<unknown>) => React.FunctionComponentElement<{}>;
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export declare const WithCallback: React.FC<{
3
+ callback: () => void;
4
+ children: React.ReactElement;
5
+ }>;
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  "modern",
12
12
  "modern.js"
13
13
  ],
14
- "version": "1.15.0",
14
+ "version": "1.16.0",
15
15
  "jsnext:source": "./src/index.ts",
16
16
  "types": "./type.d.ts",
17
17
  "main": "./dist/js/node/index.js",
@@ -140,9 +140,9 @@
140
140
  "@loadable/babel-plugin": "^5.13.2",
141
141
  "@loadable/server": "^5.15.1",
142
142
  "@loadable/component": "^5.15.0",
143
- "@modern-js/plugin": "1.15.0",
144
- "@modern-js/webpack": "1.15.0",
145
- "@modern-js/utils": "1.15.0",
143
+ "@modern-js/plugin": "1.16.0",
144
+ "@modern-js/webpack": "1.16.0",
145
+ "@modern-js/utils": "1.16.0",
146
146
  "@modern-js-reduck/plugin-auto-actions": "^1.0.2",
147
147
  "@modern-js-reduck/plugin-devtools": "^1.0.3",
148
148
  "@modern-js-reduck/plugin-effects": "^1.0.2",
@@ -156,19 +156,21 @@
156
156
  "@types/loadable__component": "^5.13.4",
157
157
  "@types/styled-components": "^5.1.14"
158
158
  },
159
+ "peerDependencies": {
160
+ "react": ">=17",
161
+ "react-dom": ">=17"
162
+ },
159
163
  "devDependencies": {
160
164
  "react": "^17",
161
165
  "react-dom": "^17",
162
166
  "ts-jest": "^27.0.4",
163
167
  "typescript": "^4",
164
168
  "jest": "^27",
165
- "@modern-js/core": "1.15.0",
166
- "@modern-js/types": "1.15.0",
167
- "@modern-js/utils": "1.15.0",
169
+ "@modern-js/core": "1.16.0",
170
+ "@modern-js/types": "1.16.0",
171
+ "@modern-js/utils": "1.16.0",
168
172
  "@types/jest": "^27",
169
173
  "@types/node": "^14",
170
- "@types/react": "^17",
171
- "@types/react-dom": "^17",
172
174
  "@types/react-side-effect": "^1.1.1",
173
175
  "@types/loadable__webpack-plugin": "^5.7.3",
174
176
  "@types/serialize-javascript": "^5.0.1",