@modern-js/runtime 1.3.5 → 1.4.2

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 (155) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/dist/js/modern/cli/index.js +4 -4
  3. package/dist/js/modern/common.js +1 -0
  4. package/dist/js/modern/exports/model.js +2 -1
  5. package/dist/js/modern/exports/router.js +2 -1
  6. package/dist/js/modern/exports/server.js +0 -1
  7. package/dist/js/modern/exports/ssr.js +2 -1
  8. package/dist/js/modern/router/cli/index.js +106 -0
  9. package/dist/js/modern/router/index.js +2 -0
  10. package/dist/js/modern/router/runtime/DefaultNotFound.js +13 -0
  11. package/dist/js/modern/router/runtime/index.js +4 -0
  12. package/dist/js/modern/router/runtime/plugin.js +88 -0
  13. package/dist/js/modern/router/runtime/utils.js +108 -0
  14. package/dist/js/modern/ssr/cli/index.js +120 -0
  15. package/dist/js/modern/ssr/index.js +109 -0
  16. package/dist/js/modern/ssr/index.node.js +78 -0
  17. package/dist/js/modern/ssr/prefetch.js +58 -0
  18. package/dist/js/modern/ssr/react/index.js +2 -0
  19. package/dist/js/modern/ssr/react/nossr/index.js +13 -0
  20. package/dist/js/modern/ssr/react/prerender/index.js +104 -0
  21. package/dist/js/modern/ssr/react/prerender/type.js +0 -0
  22. package/dist/js/modern/ssr/react/prerender/util.js +99 -0
  23. package/dist/js/modern/ssr/serverRender/entry.js +179 -0
  24. package/dist/js/modern/ssr/serverRender/helmet.js +46 -0
  25. package/dist/js/modern/ssr/serverRender/index.js +29 -0
  26. package/dist/js/modern/ssr/serverRender/loadable.js +48 -0
  27. package/dist/js/modern/ssr/serverRender/measure.js +11 -0
  28. package/dist/js/modern/ssr/serverRender/reduce.js +7 -0
  29. package/dist/js/modern/ssr/serverRender/styledComponent.js +8 -0
  30. package/dist/js/modern/ssr/serverRender/template.js +90 -0
  31. package/dist/js/modern/ssr/serverRender/type.js +8 -0
  32. package/dist/js/modern/ssr/utils.js +51 -0
  33. package/dist/js/modern/state/cli/index.js +110 -0
  34. package/dist/js/modern/state/index.js +2 -0
  35. package/dist/js/modern/state/plugins.js +7 -0
  36. package/dist/js/modern/state/runtime/index.js +4 -0
  37. package/dist/js/modern/state/runtime/plugin.js +71 -0
  38. package/dist/js/modern/state/types.js +1 -0
  39. package/dist/js/node/cli/index.js +5 -5
  40. package/dist/js/node/common.js +10 -0
  41. package/dist/js/node/exports/model.js +19 -5
  42. package/dist/js/node/exports/router.js +19 -5
  43. package/dist/js/node/exports/server.js +0 -17
  44. package/dist/js/node/exports/ssr.js +19 -5
  45. package/dist/js/node/router/cli/index.js +122 -0
  46. package/dist/js/node/router/index.js +30 -0
  47. package/dist/js/node/router/runtime/DefaultNotFound.js +26 -0
  48. package/dist/js/node/router/runtime/index.js +39 -0
  49. package/dist/js/node/router/runtime/plugin.js +111 -0
  50. package/dist/js/node/router/runtime/utils.js +128 -0
  51. package/dist/js/node/ssr/cli/index.js +136 -0
  52. package/dist/js/node/ssr/index.js +138 -0
  53. package/dist/js/node/ssr/index.node.js +105 -0
  54. package/dist/js/node/ssr/prefetch.js +75 -0
  55. package/dist/js/node/ssr/react/index.js +21 -0
  56. package/dist/js/node/ssr/react/nossr/index.js +28 -0
  57. package/dist/js/node/ssr/react/prerender/index.js +121 -0
  58. package/dist/js/node/ssr/react/prerender/type.js +0 -0
  59. package/dist/js/node/ssr/react/prerender/util.js +119 -0
  60. package/dist/js/node/ssr/serverRender/entry.js +208 -0
  61. package/dist/js/node/ssr/serverRender/helmet.js +52 -0
  62. package/dist/js/node/ssr/serverRender/index.js +46 -0
  63. package/dist/js/node/ssr/serverRender/loadable.js +60 -0
  64. package/dist/js/node/ssr/serverRender/measure.js +20 -0
  65. package/dist/js/node/ssr/serverRender/reduce.js +14 -0
  66. package/dist/js/node/ssr/serverRender/styledComponent.js +18 -0
  67. package/dist/js/node/ssr/serverRender/template.js +103 -0
  68. package/dist/js/node/ssr/serverRender/type.js +15 -0
  69. package/dist/js/node/ssr/utils.js +65 -0
  70. package/dist/js/node/state/cli/index.js +127 -0
  71. package/dist/js/node/state/index.js +30 -0
  72. package/dist/js/node/state/plugins.js +35 -0
  73. package/dist/js/node/state/runtime/index.js +61 -0
  74. package/dist/js/node/state/runtime/plugin.js +101 -0
  75. package/dist/js/node/state/types.js +5 -0
  76. package/dist/js/treeshaking/cli/index.js +4 -4
  77. package/dist/js/treeshaking/common.js +3 -0
  78. package/dist/js/treeshaking/exports/model.js +2 -1
  79. package/dist/js/treeshaking/exports/router.js +2 -1
  80. package/dist/js/treeshaking/exports/server.js +0 -1
  81. package/dist/js/treeshaking/exports/ssr.js +2 -1
  82. package/dist/js/treeshaking/router/cli/index.js +100 -0
  83. package/dist/js/treeshaking/router/index.js +2 -0
  84. package/dist/js/treeshaking/router/runtime/DefaultNotFound.js +15 -0
  85. package/dist/js/treeshaking/router/runtime/index.js +4 -0
  86. package/dist/js/treeshaking/router/runtime/plugin.js +89 -0
  87. package/dist/js/treeshaking/router/runtime/utils.js +106 -0
  88. package/dist/js/treeshaking/ssr/cli/index.js +113 -0
  89. package/dist/js/treeshaking/ssr/index.js +129 -0
  90. package/dist/js/treeshaking/ssr/index.node.js +100 -0
  91. package/dist/js/treeshaking/ssr/prefetch.js +97 -0
  92. package/dist/js/treeshaking/ssr/react/index.js +2 -0
  93. package/dist/js/treeshaking/ssr/react/nossr/index.js +16 -0
  94. package/dist/js/treeshaking/ssr/react/prerender/index.js +119 -0
  95. package/dist/js/treeshaking/ssr/react/prerender/type.js +0 -0
  96. package/dist/js/treeshaking/ssr/react/prerender/util.js +115 -0
  97. package/dist/js/treeshaking/ssr/serverRender/entry.js +267 -0
  98. package/dist/js/treeshaking/ssr/serverRender/helmet.js +37 -0
  99. package/dist/js/treeshaking/ssr/serverRender/index.js +69 -0
  100. package/dist/js/treeshaking/ssr/serverRender/loadable.js +59 -0
  101. package/dist/js/treeshaking/ssr/serverRender/measure.js +17 -0
  102. package/dist/js/treeshaking/ssr/serverRender/reduce.js +11 -0
  103. package/dist/js/treeshaking/ssr/serverRender/styledComponent.js +8 -0
  104. package/dist/js/treeshaking/ssr/serverRender/template.js +111 -0
  105. package/dist/js/treeshaking/ssr/serverRender/type.js +8 -0
  106. package/dist/js/treeshaking/ssr/utils.js +53 -0
  107. package/dist/js/treeshaking/state/cli/index.js +100 -0
  108. package/dist/js/treeshaking/state/index.js +2 -0
  109. package/dist/js/treeshaking/state/plugins.js +13 -0
  110. package/dist/js/treeshaking/state/runtime/index.js +4 -0
  111. package/dist/js/treeshaking/state/runtime/plugin.js +63 -0
  112. package/dist/js/treeshaking/state/types.js +1 -0
  113. package/dist/types/common.d.ts +1 -0
  114. package/dist/types/exports/model.d.ts +2 -1
  115. package/dist/types/exports/router.d.ts +2 -1
  116. package/dist/types/exports/server.d.ts +0 -1
  117. package/dist/types/exports/ssr.d.ts +2 -1
  118. package/dist/types/index.d.ts +2 -2
  119. package/dist/types/router/cli/index.d.ts +5 -0
  120. package/dist/types/router/index.d.ts +2 -0
  121. package/dist/types/router/runtime/DefaultNotFound.d.ts +2 -0
  122. package/dist/types/router/runtime/index.d.ts +6 -0
  123. package/dist/types/router/runtime/plugin.d.ts +51 -0
  124. package/dist/types/router/runtime/utils.d.ts +6 -0
  125. package/dist/types/ssr/cli/index.d.ts +5 -0
  126. package/dist/types/ssr/index.d.ts +18 -0
  127. package/dist/types/ssr/index.node.d.ts +4 -0
  128. package/dist/types/ssr/prefetch.d.ts +13 -0
  129. package/dist/types/ssr/react/index.d.ts +2 -0
  130. package/dist/types/ssr/react/nossr/index.d.ts +2 -0
  131. package/dist/types/ssr/react/prerender/index.d.ts +1 -0
  132. package/dist/types/ssr/react/prerender/type.d.ts +29 -0
  133. package/dist/types/ssr/react/prerender/util.d.ts +6 -0
  134. package/dist/types/ssr/serverRender/entry.d.ts +20 -0
  135. package/dist/types/ssr/serverRender/helmet.d.ts +2 -0
  136. package/dist/types/ssr/serverRender/index.d.ts +8 -0
  137. package/dist/types/ssr/serverRender/loadable.d.ts +2 -0
  138. package/dist/types/ssr/serverRender/measure.d.ts +1 -0
  139. package/dist/types/ssr/serverRender/reduce.d.ts +3 -0
  140. package/dist/types/ssr/serverRender/styledComponent.d.ts +2 -0
  141. package/dist/types/ssr/serverRender/template.d.ts +14 -0
  142. package/dist/types/ssr/serverRender/type.d.ts +32 -0
  143. package/dist/types/ssr/utils.d.ts +8 -0
  144. package/dist/types/state/cli/index.d.ts +5 -0
  145. package/dist/types/state/index.d.ts +2 -0
  146. package/dist/types/state/plugins.d.ts +4 -0
  147. package/dist/types/state/runtime/index.d.ts +4 -0
  148. package/dist/types/state/runtime/plugin.d.ts +17 -0
  149. package/dist/types/state/types.d.ts +17 -0
  150. package/package.json +36 -20
  151. package/types/model.d.ts +4 -1
  152. package/dist/js/modern/exports/request.js +0 -1
  153. package/dist/js/node/exports/request.js +0 -13
  154. package/dist/js/treeshaking/exports/request.js +0 -1
  155. package/dist/types/exports/request.d.ts +0 -1
@@ -0,0 +1,78 @@
1
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2
+
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
4
+
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
+
7
+ /* eslint-disable @typescript-eslint/no-var-requires */
8
+
9
+ /* eslint-disable @typescript-eslint/no-require-imports */
10
+ import path from 'path';
11
+ import { registerPrefetch } from '@modern-js/runtime-core';
12
+ import { isBrowser } from "../common";
13
+ import prefetch from "./prefetch";
14
+ import { formatServer } from "./utils";
15
+ const registeredApps = new WeakSet();
16
+
17
+ const plugin = () => ({
18
+ name: '@modern-js/plugin-ssr',
19
+ setup: () => {
20
+ return {
21
+ server: async ({
22
+ App,
23
+ context
24
+ }) => {
25
+ if (!registeredApps.has(App)) {
26
+ registerPrefetch(App, _context => prefetch(App, _context));
27
+ registeredApps.add(App);
28
+ }
29
+
30
+ if (!isBrowser()) {
31
+ const html = await require("./serverRender").render(context, (context === null || context === void 0 ? void 0 : context.ssrContext.distDir) || path.join(process.cwd(), 'dist'), App);
32
+ return html;
33
+ }
34
+
35
+ return null;
36
+ },
37
+
38
+ init({
39
+ context
40
+ }, next) {
41
+ const {
42
+ request
43
+ } = context.ssrContext;
44
+ context.ssrContext.request = formatServer(request);
45
+ return next({
46
+ context
47
+ });
48
+ },
49
+
50
+ pickContext: ({
51
+ context,
52
+ pickedContext
53
+ }, next) => {
54
+ const {
55
+ request,
56
+ response
57
+ } = context === null || context === void 0 ? void 0 : context.ssrContext;
58
+ const {
59
+ initialData
60
+ } = context;
61
+ return next({
62
+ context,
63
+ pickedContext: _objectSpread(_objectSpread({}, pickedContext), {}, {
64
+ initialData,
65
+ request,
66
+ response
67
+ })
68
+ });
69
+ }
70
+ };
71
+ }
72
+ });
73
+
74
+ export default plugin;
75
+ export * from "./react";
76
+ /* eslint-enable @typescript-eslint/no-require-imports */
77
+
78
+ /* eslint-enable @typescript-eslint/no-var-requires */
@@ -0,0 +1,58 @@
1
+ import path from 'path';
2
+ import fs from 'fs';
3
+ import { renderToStaticMarkup } from 'react-dom/server';
4
+ import { run } from '@modern-js/utils/ssr';
5
+ import { LOADABLE_STATS_FILE } from '@modern-js/utils/constants';
6
+ import { ChunkExtractor } from '@loadable/server'; // todo: SSRContext
7
+
8
+ import { jsx as _jsx } from "react/jsx-runtime";
9
+
10
+ const prefetch = async (App, context) => run(context.ssrContext.request.headers, async () => {
11
+ var _context$store;
12
+
13
+ const {
14
+ ssrContext
15
+ } = context;
16
+ const loadablefile = path.resolve(ssrContext.distDir, LOADABLE_STATS_FILE);
17
+
18
+ if (fs.existsSync(loadablefile)) {
19
+ const extractor = new ChunkExtractor({
20
+ statsFile: path.resolve(ssrContext.distDir, LOADABLE_STATS_FILE),
21
+ entrypoints: [ssrContext.entryName].filter(Boolean)
22
+ });
23
+ renderToStaticMarkup(extractor.collectChunks( /*#__PURE__*/_jsx(App, {
24
+ context: context
25
+ })));
26
+ } else {
27
+ renderToStaticMarkup( /*#__PURE__*/_jsx(App, {
28
+ context: context
29
+ }));
30
+ }
31
+
32
+ if (!context.loaderManager.hasPendingLoaders()) {
33
+ return {
34
+ initialData: context.initialData,
35
+ i18nData: context.__i18nData__
36
+ };
37
+ }
38
+
39
+ const loadersData = await context.loaderManager.awaitPendingLoaders();
40
+ Object.keys(loadersData).forEach(id => {
41
+ const data = loadersData[id];
42
+
43
+ if (data._error) {
44
+ ssrContext.logger.error('App Prefetch Loader', data._error);
45
+ ssrContext.metrics.emitCounter('app.prefetch.loader.error', 1);
46
+ delete data._error;
47
+ }
48
+ });
49
+ return {
50
+ loadersData,
51
+ initialData: context.initialData,
52
+ i18nData: context.__i18nData__,
53
+ // todo: move to plugin state
54
+ storeState: context === null || context === void 0 ? void 0 : (_context$store = context.store) === null || _context$store === void 0 ? void 0 : _context$store.getState()
55
+ };
56
+ });
57
+
58
+ export default prefetch;
@@ -0,0 +1,2 @@
1
+ export { PreRender } from "./prerender";
2
+ export { NoSSR } from "./nossr";
@@ -0,0 +1,13 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ let csr = false;
3
+ export const NoSSR = props => {
4
+ const [isMounted, setMounted] = useState(csr);
5
+ useEffect(() => {
6
+ csr = true;
7
+ setMounted(true);
8
+ });
9
+ const {
10
+ children
11
+ } = props;
12
+ return /*#__PURE__*/React.createElement(React.Fragment, null, isMounted ? children : null);
13
+ };
@@ -0,0 +1,104 @@
1
+ function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
2
+
3
+ function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
4
+
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
+
7
+ import withSideEffect from 'react-side-effect';
8
+ import React, { createElement } from 'react';
9
+ import { getOutermostProperty, aggKeysFromPropsList, exist, aggMatchesFromPropsList } from "./util";
10
+ const PROP_NAMES = {
11
+ INTERVAL: 'interval',
12
+ STALE_LIMIT: 'staleLimit',
13
+ LEVEL: 'level',
14
+ INCLUDES: 'includes',
15
+ EXCLUDES: 'excludes',
16
+ FALLBACK: 'fallback',
17
+ MATCHES: 'matches'
18
+ };
19
+
20
+ const handleClientStateChange = () => {// not used
21
+ };
22
+
23
+ const mapStateOnServer = reduceProps => {
24
+ const defaultProps = {
25
+ interval: 10,
26
+ staleLimit: false,
27
+ level: 0,
28
+ includes: null,
29
+ excludes: null,
30
+ fallback: false,
31
+ matches: null
32
+ };
33
+ return Object.keys(defaultProps).reduce((props, key) => {
34
+ const propKey = key;
35
+ const reduceProp = reduceProps[propKey];
36
+ let nextProps = props;
37
+
38
+ if (exist(reduceProp)) {
39
+ nextProps = _objectSpread(_objectSpread({}, props), {}, {
40
+ [propKey]: reduceProp
41
+ });
42
+ }
43
+
44
+ return nextProps;
45
+ }, defaultProps);
46
+ };
47
+
48
+ const reducePropsToState = propsList => {
49
+ const reduceProps = {
50
+ interval: getOutermostProperty(propsList, PROP_NAMES.INTERVAL),
51
+ staleLimit: getOutermostProperty(propsList, PROP_NAMES.STALE_LIMIT),
52
+ level: getOutermostProperty(propsList, PROP_NAMES.LEVEL),
53
+ includes: aggKeysFromPropsList(propsList, PROP_NAMES.INCLUDES),
54
+ excludes: aggKeysFromPropsList(propsList, PROP_NAMES.EXCLUDES),
55
+ fallback: getOutermostProperty(propsList, PROP_NAMES.FALLBACK),
56
+ matches: aggMatchesFromPropsList(propsList, PROP_NAMES.MATCHES)
57
+ };
58
+ return reduceProps;
59
+ };
60
+
61
+ function factory(Component) {
62
+ class Spr extends React.Component {
63
+ static set canUseDOM(canUseDOM) {
64
+ Component.canUseDOM = canUseDOM;
65
+ }
66
+
67
+ static get canUseDOM() {
68
+ return Component.canUseDOM;
69
+ }
70
+
71
+ verify() {
72
+ return true;
73
+ }
74
+
75
+ render() {
76
+ const newProps = _objectSpread({}, this.props);
77
+
78
+ const validate = this.verify();
79
+
80
+ if (!validate) {
81
+ throw new Error('invalid props, check usage');
82
+ }
83
+
84
+ return /*#__PURE__*/createElement(Component, _objectSpread({}, newProps));
85
+ }
86
+
87
+ }
88
+
89
+ _defineProperty(Spr, "peek", Component.peek);
90
+
91
+ _defineProperty(Spr, "rewind", Component.rewind);
92
+
93
+ _defineProperty(Spr, "config", () => {
94
+ const mappedState = Component.rewind();
95
+ return mappedState;
96
+ });
97
+
98
+ return Spr;
99
+ }
100
+
101
+ const NullComponent = () => null;
102
+
103
+ const SprSideEffects = withSideEffect(reducePropsToState, handleClientStateChange, mapStateOnServer)(NullComponent);
104
+ export const PreRender = factory(SprSideEffects);
File without changes
@@ -0,0 +1,99 @@
1
+ const REQUEST_META = ['header', 'query'];
2
+ export const getInnermostProperty = function getInnermostProperty(propsList, propName) {
3
+ for (let i = propsList.length - 1; i >= 0; i--) {
4
+ const props = propsList[i];
5
+
6
+ if (props.hasOwnProperty(propName)) {
7
+ return props[propName];
8
+ }
9
+ }
10
+
11
+ return null;
12
+ };
13
+ export const getOutermostProperty = function getOutermostProperty(propsList, propName) {
14
+ for (const props of propsList) {
15
+ if (props.hasOwnProperty(propName)) {
16
+ return props[propName];
17
+ }
18
+ }
19
+
20
+ return null;
21
+ };
22
+ export const aggKeysFromPropsList = function aggKeysFromPropsList(propsList, propName) {
23
+ const initResult = REQUEST_META.reduce((result, next) => {
24
+ const key = next;
25
+ result[key] = [];
26
+ return result;
27
+ }, {});
28
+ const res = propsList.filter(props => usefulObject(props[propName])).reduce((result, next) => {
29
+ REQUEST_META.forEach(key => {
30
+ const prop = next[propName];
31
+
32
+ if (prop !== null && prop !== void 0 && prop.hasOwnProperty(key) && usefulArray(prop[key])) {
33
+ result[key] = unique(result[key].concat(prop[key]));
34
+ }
35
+ });
36
+ return result;
37
+ }, initResult);
38
+ return REQUEST_META.reduce((result, next) => {
39
+ var _result$key;
40
+
41
+ const key = next;
42
+
43
+ if (result[key] && ((_result$key = result[key]) === null || _result$key === void 0 ? void 0 : _result$key.length) === 0) {
44
+ delete result[key];
45
+ }
46
+
47
+ return result;
48
+ }, res);
49
+ };
50
+ export const aggMatchesFromPropsList = function aggMatchesFromPropsList(propsList, propName) {
51
+ const initResult = REQUEST_META.reduce((result, next) => {
52
+ const key = next;
53
+ result[key] = {};
54
+ return result;
55
+ }, {});
56
+ const res = propsList.filter(props => usefulObject(props[propName])).reduce((result, next) => {
57
+ REQUEST_META.forEach(key => {
58
+ const prop = next[propName]; // 这边目前是浅拷贝,越后渲染优先级越高
59
+
60
+ if (prop !== null && prop !== void 0 && prop.hasOwnProperty(key) && usefulObject(prop[key])) {
61
+ result[key] = Object.assign(result[key], prop[key]);
62
+ }
63
+ });
64
+ return result;
65
+ }, initResult);
66
+ return REQUEST_META.reduce((result, next) => {
67
+ const key = next;
68
+
69
+ if (result[key] && Object.keys(result[key]).length === 0) {
70
+ delete result[key];
71
+ }
72
+
73
+ return result;
74
+ }, res);
75
+ };
76
+
77
+ function unique(arr) {
78
+ return Array.from(new Set(arr));
79
+ }
80
+
81
+ function usefulObject(target) {
82
+ if (!exist(target)) {
83
+ return false;
84
+ }
85
+
86
+ return target.constructor === Object && Object.keys(target).length > 0;
87
+ }
88
+
89
+ function usefulArray(target) {
90
+ if (!exist(target)) {
91
+ return false;
92
+ }
93
+
94
+ return Array.isArray(target) && target.length > 0;
95
+ }
96
+
97
+ export function exist(target) {
98
+ return target != null;
99
+ }
@@ -0,0 +1,179 @@
1
+ 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; }
2
+
3
+ import path from 'path';
4
+ import { LOADABLE_STATS_FILE } from '@modern-js/utils/constants';
5
+ import React from 'react';
6
+ import ReactDomServer from 'react-dom/server';
7
+ import serialize from 'serialize-javascript';
8
+ import ReactHelmet from 'react-helmet';
9
+ import { toFragments } from "./template";
10
+ import { RenderLevel } from "./type";
11
+ import helmetReplace from "./helmet";
12
+ import { reduce } from "./reduce";
13
+ import * as loadableRenderer from "./loadable";
14
+ import * as styledComponentRenderer from "./styledComponent";
15
+ import { time } from "./measure";
16
+
17
+ const buildTemplateData = (context, data, renderLevel) => {
18
+ const {
19
+ request
20
+ } = context;
21
+ return {
22
+ data,
23
+ context: {
24
+ request: {
25
+ params: request.params,
26
+ query: request.query,
27
+ pathname: request.pathname,
28
+ host: request.host,
29
+ url: request.url,
30
+ headers: request.headers,
31
+ cookieMap: request.cookieMap
32
+ }
33
+ },
34
+ renderLevel
35
+ };
36
+ };
37
+
38
+ export default class Entry {
39
+ constructor(options) {
40
+ _defineProperty(this, "entryName", void 0);
41
+
42
+ _defineProperty(this, "result", void 0);
43
+
44
+ _defineProperty(this, "metrics", void 0);
45
+
46
+ _defineProperty(this, "logger", void 0);
47
+
48
+ _defineProperty(this, "App", void 0);
49
+
50
+ _defineProperty(this, "fragments", void 0);
51
+
52
+ const {
53
+ ctx
54
+ } = options;
55
+ const {
56
+ entryName,
57
+ template: templateHTML
58
+ } = ctx;
59
+ this.fragments = toFragments(templateHTML);
60
+ this.entryName = entryName;
61
+ this.App = options.App;
62
+ this.metrics = ctx.metrics;
63
+ this.logger = ctx.logger;
64
+ this.result = {
65
+ renderLevel: RenderLevel.CLIENT_RENDER,
66
+ html: '',
67
+ chunksMap: {
68
+ js: '',
69
+ css: ''
70
+ }
71
+ };
72
+ }
73
+
74
+ async renderToHtml(context) {
75
+ const {
76
+ ssrContext
77
+ } = context;
78
+
79
+ if (ssrContext.redirection.url) {
80
+ return '';
81
+ }
82
+
83
+ const prefetchData = await this.prefetch(context);
84
+
85
+ if (ssrContext.redirection.url) {
86
+ return '';
87
+ }
88
+
89
+ if (this.result.renderLevel >= RenderLevel.SERVER_PREFETCH) {
90
+ this.result.html = this.renderToString(context);
91
+ }
92
+
93
+ if (ssrContext.redirection.url) {
94
+ return '';
95
+ }
96
+
97
+ let html = '';
98
+ const templateData = buildTemplateData(ssrContext, prefetchData, this.result.renderLevel);
99
+ const SSRData = this.getSSRDataScript(templateData);
100
+
101
+ for (const fragment of this.fragments) {
102
+ if (fragment.isVariable && fragment.content === 'SSRDataScript') {
103
+ html += fragment.getValue(SSRData);
104
+ } else {
105
+ html += fragment.getValue(this.result);
106
+ }
107
+ }
108
+
109
+ const helmetData = ReactHelmet.renderStatic();
110
+ return helmetData ? helmetReplace(html, helmetData) : html;
111
+ }
112
+
113
+ async prefetch(context) {
114
+ const {
115
+ App: {
116
+ prefetch
117
+ }
118
+ } = this;
119
+ let prefetchData;
120
+ const end = time();
121
+
122
+ try {
123
+ prefetchData = prefetch ? await prefetch(context) : null;
124
+ this.result.renderLevel = RenderLevel.SERVER_PREFETCH;
125
+ const prefetchCost = end();
126
+ this.logger.debug(`App Prefetch cost = %d ms`, prefetchCost);
127
+ this.metrics.emitTimer('app.prefetch.cost', prefetchCost);
128
+ } catch (e) {
129
+ this.result.renderLevel = RenderLevel.CLIENT_RENDER;
130
+ this.logger.error('App Prefetch Render', e);
131
+ this.metrics.emitCounter('app.prefetch.render.error', 1);
132
+ }
133
+
134
+ return prefetchData || {};
135
+ }
136
+
137
+ renderToString(context) {
138
+ let html = '';
139
+ const end = time();
140
+ const {
141
+ ssrContext
142
+ } = context;
143
+
144
+ try {
145
+ const App = /*#__PURE__*/React.createElement(this.App, {
146
+ context: Object.assign(context, {
147
+ ssr: true
148
+ })
149
+ }); // Todo render Hook
150
+
151
+ const renderContext = {
152
+ loadableManifest: path.resolve(ssrContext.distDir, LOADABLE_STATS_FILE),
153
+ result: this.result,
154
+ entryName: this.entryName
155
+ };
156
+ html = reduce(App, renderContext, [styledComponentRenderer.toHtml, loadableRenderer.toHtml, jsx => ReactDomServer.renderToString(jsx)]);
157
+ const cost = end();
158
+ this.logger.debug('App Render To HTML cost = %d ms', cost);
159
+ this.metrics.emitTimer('app.render.html.cost', cost);
160
+ this.result.renderLevel = RenderLevel.SERVER_RENDER;
161
+ } catch (e) {
162
+ this.logger.error('App Render To HTML', e);
163
+ this.metrics.emitCounter('app.render.html.error', 1);
164
+ }
165
+
166
+ return html;
167
+ }
168
+
169
+ getSSRDataScript(templateData) {
170
+ return {
171
+ SSRDataScript: `
172
+ <script>window._SSR_DATA = ${serialize(templateData, {
173
+ isJSON: true
174
+ })}</script>
175
+ `
176
+ };
177
+ }
178
+
179
+ }
@@ -0,0 +1,46 @@
1
+ // 用于 react-helmet 正则替换
2
+ const RE_HTML_ATTR = /<html[^>]*>/;
3
+ const RE_BODY_ATTR = /<body[^>]*>/;
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 修改模板
7
+
8
+ export default function helmet(content, helmetData) {
9
+ let result = content;
10
+ const bodyAttributes = helmetData.bodyAttributes.toString();
11
+
12
+ if (bodyAttributes) {
13
+ result = result.replace(RE_BODY_ATTR, `<body ${bodyAttributes}>`);
14
+ }
15
+
16
+ const htmlAttributes = helmetData.htmlAttributes.toString();
17
+
18
+ if (htmlAttributes) {
19
+ result = result.replace(RE_HTML_ATTR, `<html ${htmlAttributes}>`);
20
+ }
21
+
22
+ const base = helmetData.base.toString();
23
+ const link = helmetData.link.toString();
24
+ const meta = helmetData.meta.toString();
25
+ const noscript = helmetData.noscript.toString();
26
+ const script = helmetData.script.toString();
27
+ const style = helmetData.style.toString();
28
+ const title = helmetData.title.toString(); // 如果模板中存在 title,且 helmetData title 有内容则做替换
29
+
30
+ const existTitle = RE_TITLE.test(content);
31
+
32
+ if (TEST_TITLE_CONTENT.test(title.trim()) && existTitle) {
33
+ result = result.replace(RE_TITLE, title);
34
+ }
35
+
36
+ return result.replace(RE_LAST_IN_HEAD, `
37
+ ${base}
38
+ ${link}
39
+ ${meta}
40
+ ${noscript}
41
+ ${script}
42
+ ${style}
43
+ ${existTitle ? '' : title}
44
+ </head>
45
+ `);
46
+ }
@@ -0,0 +1,29 @@
1
+ import { RuntimeContext } from '@modern-js/runtime-core';
2
+ import { run } from '@modern-js/utils/ssr';
3
+ import { PreRender } from "../react/prerender";
4
+ import SSREntry from "./entry";
5
+ import { time } from "./measure";
6
+ export const render = async (ctx, _ = process.cwd(), App) => {
7
+ const {
8
+ ssrContext
9
+ } = ctx;
10
+ return run(ssrContext.request.headers, async () => {
11
+ const entry = new SSREntry({
12
+ ctx: ssrContext,
13
+ App
14
+ });
15
+ entry.metrics.emitCounter('app.visit.count', 1);
16
+ const end = time();
17
+ const html = await entry.renderToHtml(ctx);
18
+ const cost = end();
19
+ entry.logger.info('App Render Total cost = %d ms', cost);
20
+ entry.metrics.emitTimer('app.render.cost', cost);
21
+ const cacheConfig = PreRender.config();
22
+
23
+ if (cacheConfig) {
24
+ ctx.ssrContext.cacheConfig = cacheConfig;
25
+ }
26
+
27
+ return html;
28
+ });
29
+ };
@@ -0,0 +1,48 @@
1
+ import path from 'path';
2
+ import { ChunkExtractor } from '@loadable/server';
3
+
4
+ function getLoadableScripts(extractor) {
5
+ const check = scripts => (scripts || '').includes('__LOADABLE_REQUIRED_CHUNKS___ext');
6
+
7
+ const scripts = extractor.getScriptTags();
8
+
9
+ if (!check(scripts)) {
10
+ return '';
11
+ }
12
+
13
+ return scripts.split('</script>') // 前两个 script为 loadable 必须的 script
14
+ .slice(0, 2).map(i => `${i}</script>`).join('');
15
+ }
16
+
17
+ export const toHtml = (jsx, renderer, next) => {
18
+ const {
19
+ loadableManifest,
20
+ result: {
21
+ chunksMap
22
+ }
23
+ } = renderer;
24
+
25
+ if (!loadableManifest || chunksMap.js) {
26
+ return next(jsx);
27
+ }
28
+
29
+ const extractor = new ChunkExtractor({
30
+ statsFile: loadableManifest,
31
+ entrypoints: [renderer.entryName]
32
+ });
33
+ const html = next(extractor.collectChunks(jsx));
34
+ const chunks = extractor.getChunkAssets(extractor.chunks);
35
+ chunksMap.js = (chunksMap.js || '') + getLoadableScripts(extractor);
36
+
37
+ for (const v of chunks) {
38
+ const fileType = path.extname(v.url).slice(1);
39
+
40
+ if (fileType === 'js') {
41
+ chunksMap[fileType] += `<script src="${v.url}"></script>`;
42
+ } else if (fileType === 'css') {
43
+ chunksMap[fileType] += `<link href="${v.url}" rel="stylesheet" />`;
44
+ }
45
+ }
46
+
47
+ return html;
48
+ };
@@ -0,0 +1,11 @@
1
+ const getLatency = hrtime => {
2
+ const [s, ns] = process.hrtime(hrtime);
3
+ return s * 1e3 + ns / 1e6;
4
+ };
5
+
6
+ export const time = () => {
7
+ const hrtime = process.hrtime();
8
+ return () => {
9
+ return getLatency(hrtime);
10
+ };
11
+ };
@@ -0,0 +1,7 @@
1
+ export function reduce(jsx, renderer, middleware) {
2
+ let index = 0;
3
+
4
+ const createNext = () => App => middleware[index++](App, renderer, createNext());
5
+
6
+ return createNext()(jsx);
7
+ }
@@ -0,0 +1,8 @@
1
+ import { ServerStyleSheet } from '@modern-js/runtime-core/styled';
2
+ export const toHtml = (jsx, renderer, next) => {
3
+ const sheet = new ServerStyleSheet();
4
+ const html = next(sheet.collectStyles(jsx));
5
+ const css = sheet.getStyleTags();
6
+ renderer.result.chunksMap.css += css;
7
+ return html;
8
+ };