@modern-js/runtime 2.6.0 → 2.7.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.
Files changed (70) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/dist/cjs/common.js +6 -0
  3. package/dist/cjs/core/{app-config.js → appConfig.js} +3 -3
  4. package/dist/cjs/core/compatible.js +4 -4
  5. package/dist/cjs/core/index.js +6 -6
  6. package/dist/cjs/core/loader/useLoader.js +2 -2
  7. package/dist/cjs/core/types.js +15 -0
  8. package/dist/cjs/router/runtime/DeferredDataScripts.js +165 -0
  9. package/dist/cjs/router/runtime/index.js +116 -3
  10. package/dist/cjs/router/runtime/plugin.js +14 -1
  11. package/dist/cjs/router/runtime/plugin.node.js +12 -52
  12. package/dist/cjs/router/runtime/utils.js +68 -69
  13. package/dist/cjs/{runtime-context.js → runtimeContext.js} +3 -3
  14. package/dist/cjs/ssr/cli/index.js +2 -0
  15. package/dist/cjs/ssr/index.node.js +1 -0
  16. package/dist/cjs/ssr/serverRender/renderToStream/buildTemplate.after.js +2 -14
  17. package/dist/cjs/ssr/serverRender/renderToStream/renderToPipe.js +22 -7
  18. package/dist/cjs/ssr/serverRender/renderToStream/template.js +2 -1
  19. package/dist/cjs/ssr/serverRender/renderToString/entry.js +19 -8
  20. package/dist/esm/common.js +3 -1
  21. package/dist/esm/core/compatible.js +1 -1
  22. package/dist/esm/core/index.js +2 -2
  23. package/dist/esm/core/loader/useLoader.js +1 -1
  24. package/dist/esm/core/types.js +1 -0
  25. package/dist/esm/router/runtime/DeferredDataScripts.js +166 -0
  26. package/dist/esm/router/runtime/index.js +3 -2
  27. package/dist/esm/router/runtime/plugin.js +11 -3
  28. package/dist/esm/router/runtime/plugin.node.js +11 -68
  29. package/dist/esm/router/runtime/utils.js +138 -81
  30. package/dist/esm/ssr/cli/index.js +2 -0
  31. package/dist/esm/ssr/index.node.js +1 -0
  32. package/dist/esm/ssr/serverRender/renderToStream/buildTemplate.after.js +2 -4
  33. package/dist/esm/ssr/serverRender/renderToStream/renderToPipe.js +19 -8
  34. package/dist/esm/ssr/serverRender/renderToStream/template.js +2 -1
  35. package/dist/esm/ssr/serverRender/renderToString/entry.js +15 -7
  36. package/dist/esm-node/common.js +4 -0
  37. package/dist/esm-node/core/compatible.js +1 -1
  38. package/dist/esm-node/core/index.js +2 -2
  39. package/dist/esm-node/core/loader/useLoader.js +1 -1
  40. package/dist/esm-node/core/types.js +0 -0
  41. package/dist/esm-node/router/runtime/DeferredDataScripts.js +148 -0
  42. package/dist/esm-node/router/runtime/index.js +113 -2
  43. package/dist/esm-node/router/runtime/plugin.js +15 -2
  44. package/dist/esm-node/router/runtime/plugin.node.js +12 -55
  45. package/dist/esm-node/router/runtime/utils.js +59 -69
  46. package/dist/esm-node/ssr/cli/index.js +2 -0
  47. package/dist/esm-node/ssr/index.node.js +1 -0
  48. package/dist/esm-node/ssr/serverRender/renderToStream/buildTemplate.after.js +2 -4
  49. package/dist/esm-node/ssr/serverRender/renderToStream/renderToPipe.js +22 -7
  50. package/dist/esm-node/ssr/serverRender/renderToStream/template.js +2 -1
  51. package/dist/esm-node/ssr/serverRender/renderToString/entry.js +19 -8
  52. package/dist/types/common.d.ts +3 -1
  53. package/dist/types/core/compatible.d.ts +1 -1
  54. package/dist/types/core/index.d.ts +4 -3
  55. package/dist/types/core/loader/index.d.ts +1 -2
  56. package/dist/types/core/loader/useLoader.d.ts +1 -15
  57. package/dist/types/core/plugin.d.ts +15 -15
  58. package/dist/types/core/types.d.ts +22 -0
  59. package/dist/types/index.d.ts +1 -1
  60. package/dist/types/router/runtime/DeferredDataScripts.d.ts +8 -0
  61. package/dist/types/router/runtime/index.d.ts +4 -2
  62. package/dist/types/router/runtime/utils.d.ts +23 -4
  63. package/dist/types/{runtime-context.d.ts → runtimeContext.d.ts} +2 -0
  64. package/dist/types/ssr/serverRender/renderToStream/buildTemplate.after.d.ts +2 -0
  65. package/package.json +12 -15
  66. /package/dist/esm/core/{app-config.js → appConfig.js} +0 -0
  67. /package/dist/esm/{runtime-context.js → runtimeContext.js} +0 -0
  68. /package/dist/esm-node/core/{app-config.js → appConfig.js} +0 -0
  69. /package/dist/esm-node/{runtime-context.js → runtimeContext.js} +0 -0
  70. /package/dist/types/core/{app-config.d.ts → appConfig.d.ts} +0 -0
@@ -0,0 +1,148 @@
1
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
+ import { Suspense, useEffect, useRef, useMemo, useContext } from "react";
3
+ import {
4
+ Await,
5
+ UNSAFE_DataRouterContext as DataRouterContext,
6
+ useAsyncError
7
+ } from "react-router-dom";
8
+ import { serializeJson } from "@modern-js/utils/serialize";
9
+ import { JSX_SHELL_STREAM_END_MARK } from "../../common";
10
+ import { serializeErrors } from "./utils";
11
+ const setupFnStr = `function s(r,e){_ROUTER_DATA.r=_ROUTER_DATA.r||{},_ROUTER_DATA.r[r]=_ROUTER_DATA.r[r]||{};return new Promise((function(A,R){_ROUTER_DATA.r[r][e]={resolve:A,reject:R}}))};`;
12
+ const resolveFnStr = `function r(e,r,o,A){A?_ROUTER_DATA.r[e][r].reject(A):_ROUTER_DATA.r[e][r].resolve(o)};`;
13
+ const preResolvedFnStr = `function p(e,r){return void 0!==r?Promise.reject(new Error(r.message)):Promise.resovle(e)};`;
14
+ const DeferredDataScripts = () => {
15
+ const context = useContext(DataRouterContext);
16
+ const { staticContext } = context || {};
17
+ const hydratedRef = useRef(false);
18
+ useEffect(() => {
19
+ hydratedRef.current = true;
20
+ }, []);
21
+ const deferredScripts = useMemo(() => {
22
+ if (!staticContext) {
23
+ return null;
24
+ }
25
+ const activeDeferreds = staticContext.activeDeferreds || [];
26
+ const _ROUTER_DATA = {
27
+ loaderData: staticContext.loaderData,
28
+ errors: serializeErrors(staticContext.errors)
29
+ };
30
+ let initialScripts = [
31
+ `_ROUTER_DATA = ${serializeJson(_ROUTER_DATA)};`,
32
+ `_ROUTER_DATA.s = ${setupFnStr}`,
33
+ `_ROUTER_DATA.r = ${resolveFnStr}`,
34
+ `_ROUTER_DATA.p = ${preResolvedFnStr}`
35
+ ].join("\n");
36
+ const deferredDataScripts = [];
37
+ initialScripts += Object.entries(activeDeferreds).map(([routeId, deferredData]) => {
38
+ const pendingKeys = new Set(deferredData.pendingKeys);
39
+ const { deferredKeys } = deferredData;
40
+ const deferredKeyPromiseStr = deferredKeys.map((key) => {
41
+ if (pendingKeys.has(key)) {
42
+ deferredDataScripts.push(
43
+ /* @__PURE__ */ jsx(
44
+ DeferredDataScript,
45
+ {
46
+ data: deferredData.data[key],
47
+ dataKey: key,
48
+ routeId
49
+ },
50
+ `${routeId} | ${key}`
51
+ )
52
+ );
53
+ return `${JSON.stringify(key)}: _ROUTER_DATA.s(${JSON.stringify(
54
+ routeId
55
+ )},${JSON.stringify(key)}) `;
56
+ } else {
57
+ const trackedPromise = deferredData.data[key];
58
+ if (typeof trackedPromise._error !== "undefined") {
59
+ const error = {
60
+ message: trackedPromise._error.message,
61
+ stack: process.env.NODE_ENV !== "production" ? trackedPromise._error.stack : void 0
62
+ };
63
+ return `${JSON.stringify(
64
+ key
65
+ )}: _ROUTER_DATA.p(${void 0}, ${serializeJson(error)})`;
66
+ } else {
67
+ if (typeof trackedPromise._data === "undefined") {
68
+ throw new Error(
69
+ `The deferred data for ${key} was not resolved, did you forget to return data from a deferred promise`
70
+ );
71
+ }
72
+ return `${JSON.stringify(key)}: _ROUTER_DATA.p(${serializeJson(
73
+ trackedPromise._data
74
+ )})`;
75
+ }
76
+ }
77
+ }).join(",\n");
78
+ return `Object.assign(_ROUTER_DATA.loaderData[${JSON.stringify(
79
+ routeId
80
+ )}], {${deferredKeyPromiseStr}});`;
81
+ }).join("\n");
82
+ return [initialScripts, deferredDataScripts];
83
+ }, []);
84
+ if (!deferredScripts) {
85
+ return null;
86
+ }
87
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
88
+ !hydratedRef.current && /* @__PURE__ */ jsx(
89
+ "script",
90
+ {
91
+ async: true,
92
+ suppressHydrationWarning: true,
93
+ dangerouslySetInnerHTML: { __html: deferredScripts[0] }
94
+ }
95
+ ),
96
+ !hydratedRef.current && deferredScripts[1],
97
+ JSX_SHELL_STREAM_END_MARK
98
+ ] });
99
+ };
100
+ const DeferredDataScript = ({
101
+ data,
102
+ routeId,
103
+ dataKey
104
+ }) => {
105
+ return /* @__PURE__ */ jsx(Suspense, { children: typeof document === "undefined" && data && dataKey && routeId ? /* @__PURE__ */ jsx(
106
+ Await,
107
+ {
108
+ resolve: data,
109
+ errorElement: /* @__PURE__ */ jsx(ErrorDeferredDataScript, { routeId, dataKey }),
110
+ children: (data2) => /* @__PURE__ */ jsx(
111
+ "script",
112
+ {
113
+ async: true,
114
+ suppressHydrationWarning: true,
115
+ dangerouslySetInnerHTML: {
116
+ __html: `_ROUTER_DATA.r(${JSON.stringify(
117
+ routeId
118
+ )}, ${JSON.stringify(dataKey)}, ${serializeJson(data2)});`
119
+ }
120
+ }
121
+ )
122
+ }
123
+ ) : null });
124
+ };
125
+ const ErrorDeferredDataScript = ({
126
+ routeId,
127
+ dataKey
128
+ }) => {
129
+ const error = useAsyncError();
130
+ return /* @__PURE__ */ jsx(
131
+ "script",
132
+ {
133
+ suppressHydrationWarning: true,
134
+ dangerouslySetInnerHTML: {
135
+ __html: `_ROUTER_DATA.r(${JSON.stringify(routeId)}, ${JSON.stringify(
136
+ dataKey
137
+ )}, ${void 0}, ${serializeJson({
138
+ message: error.message,
139
+ stack: error.stack
140
+ })});`
141
+ }
142
+ }
143
+ );
144
+ };
145
+ var DeferredDataScripts_default = DeferredDataScripts;
146
+ export {
147
+ DeferredDataScripts_default as default
148
+ };
@@ -1,9 +1,120 @@
1
1
  import { routerPlugin } from "./plugin";
2
2
  var runtime_default = routerPlugin;
3
3
  import { modifyRoutes } from "./plugin";
4
- export * from "react-router-dom";
5
4
  export * from "./withRouter";
5
+ import {
6
+ createBrowserRouter,
7
+ createHashRouter,
8
+ createMemoryRouter,
9
+ RouterProvider,
10
+ BrowserRouter,
11
+ HashRouter,
12
+ MemoryRouter,
13
+ Router,
14
+ Await,
15
+ Form,
16
+ Link,
17
+ NavLink,
18
+ Navigate,
19
+ Outlet,
20
+ Route,
21
+ Routes,
22
+ ScrollRestoration,
23
+ useActionData,
24
+ useAsyncError,
25
+ useAsyncValue,
26
+ useBeforeUnload,
27
+ useFetcher,
28
+ useFetchers,
29
+ useFormAction,
30
+ useHref,
31
+ useInRouterContext,
32
+ useLinkClickHandler,
33
+ useLoaderData,
34
+ useLocation,
35
+ useMatch,
36
+ useMatches,
37
+ useNavigate,
38
+ useNavigation,
39
+ useNavigationType,
40
+ useOutlet,
41
+ useOutletContext,
42
+ useParams,
43
+ useResolvedPath,
44
+ useRevalidator,
45
+ useRouteError,
46
+ useRouteLoaderData,
47
+ useRoutes,
48
+ useSearchParams,
49
+ useSubmit,
50
+ createRoutesFromChildren,
51
+ createRoutesFromElements,
52
+ createSearchParams,
53
+ generatePath,
54
+ isRouteErrorResponse,
55
+ matchPath,
56
+ matchRoutes,
57
+ renderMatches,
58
+ resolvePath
59
+ } from "react-router-dom";
60
+ import { defer, json, redirect } from "@modern-js/utils/remix-router";
6
61
  export {
62
+ Await,
63
+ BrowserRouter,
64
+ Form,
65
+ HashRouter,
66
+ Link,
67
+ MemoryRouter,
68
+ NavLink,
69
+ Navigate,
70
+ Outlet,
71
+ Route,
72
+ Router,
73
+ RouterProvider,
74
+ Routes,
75
+ ScrollRestoration,
76
+ createBrowserRouter,
77
+ createHashRouter,
78
+ createMemoryRouter,
79
+ createRoutesFromChildren,
80
+ createRoutesFromElements,
81
+ createSearchParams,
7
82
  runtime_default as default,
8
- modifyRoutes
83
+ defer,
84
+ generatePath,
85
+ isRouteErrorResponse,
86
+ json,
87
+ matchPath,
88
+ matchRoutes,
89
+ modifyRoutes,
90
+ redirect,
91
+ renderMatches,
92
+ resolvePath,
93
+ useActionData,
94
+ useAsyncError,
95
+ useAsyncValue,
96
+ useBeforeUnload,
97
+ useFetcher,
98
+ useFetchers,
99
+ useFormAction,
100
+ useHref,
101
+ useInRouterContext,
102
+ useLinkClickHandler,
103
+ useLoaderData,
104
+ useLocation,
105
+ useMatch,
106
+ useMatches,
107
+ useNavigate,
108
+ useNavigation,
109
+ useNavigationType,
110
+ useOutlet,
111
+ useOutletContext,
112
+ useParams,
113
+ useResolvedPath,
114
+ useRevalidator,
115
+ useRouteError,
116
+ useRouteLoaderData,
117
+ useRoutes,
118
+ useSearchParams,
119
+ useSubmit
9
120
  };
@@ -8,7 +8,7 @@ import {
8
8
  useLocation
9
9
  } from "react-router-dom";
10
10
  import hoistNonReactStatics from "hoist-non-react-statics";
11
- import { renderRoutes, urlJoin } from "./utils";
11
+ import { deserializeErrors, renderRoutes, urlJoin } from "./utils";
12
12
  let finalRouteConfig = {
13
13
  routes: []
14
14
  };
@@ -55,7 +55,20 @@ const routerPlugin = ({
55
55
  const routes = createRoutes ? createRoutes() : createRoutesFromElements(renderRoutes(finalRouteConfig));
56
56
  const baseUrl = ((_a = window._SERVER_DATA) == null ? void 0 : _a.router.baseUrl) || select(location.pathname);
57
57
  const _basename = baseUrl === "/" ? urlJoin(baseUrl, basename) : baseUrl;
58
- const router = supportHtml5History ? createBrowserRouter(routes, { basename: _basename }) : createHashRouter(routes, { basename: _basename });
58
+ let hydrationData = window._ROUTER_DATA;
59
+ if (hydrationData == null ? void 0 : hydrationData.errors) {
60
+ hydrationData = {
61
+ ...hydrationData,
62
+ errors: deserializeErrors(hydrationData.errors)
63
+ };
64
+ }
65
+ const router = supportHtml5History ? createBrowserRouter(routes, {
66
+ basename: _basename,
67
+ hydrationData
68
+ }) : createHashRouter(routes, {
69
+ basename: _basename,
70
+ hydrationData
71
+ });
59
72
  return /* @__PURE__ */ jsx(App, { ...props, children: /* @__PURE__ */ jsx(RouterProvider, { router }) });
60
73
  };
61
74
  };
@@ -1,10 +1,6 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx } from "react/jsx-runtime";
2
2
  import { useContext } from "react";
3
- import serialize from "serialize-javascript";
4
- import {
5
- createStaticHandler,
6
- isRouteErrorResponse
7
- } from "@remix-run/router";
3
+ import { createStaticHandler } from "@modern-js/utils/remix-router";
8
4
  import {
9
5
  createStaticRouter,
10
6
  StaticRouterProvider
@@ -41,26 +37,6 @@ function createFetchHeaders(requestHeaders) {
41
37
  }
42
38
  return headers;
43
39
  }
44
- function serializeErrors(errors) {
45
- if (!errors) {
46
- return null;
47
- }
48
- const entries = Object.entries(errors);
49
- const serialized = {};
50
- for (const [key, val] of entries) {
51
- if (isRouteErrorResponse(val)) {
52
- serialized[key] = { ...val, __type: "RouteErrorResponse" };
53
- } else if (val instanceof Error) {
54
- serialized[key] = {
55
- message: val.message,
56
- __type: "Error"
57
- };
58
- } else {
59
- serialized[key] = val;
60
- }
61
- }
62
- return serialized;
63
- }
64
40
  const routerPlugin = ({
65
41
  basename = "",
66
42
  routesConfig,
@@ -74,10 +50,10 @@ const routerPlugin = ({
74
50
  if (!routesConfig && !createRoutes) {
75
51
  return next({ context });
76
52
  }
77
- const { request } = context.ssrContext;
53
+ const { request, mode: ssrMode } = context.ssrContext;
78
54
  const baseUrl = request.baseUrl;
79
55
  const _basename = baseUrl === "/" ? urlJoin(baseUrl, basename) : baseUrl;
80
- const routes = createRoutes ? createRoutes() : createRoutesFromElements(renderRoutes(routesConfig));
56
+ const routes = createRoutes ? createRoutes() : createRoutesFromElements(renderRoutes(routesConfig, ssrMode));
81
57
  const { query } = createStaticHandler(routes, {
82
58
  basename: _basename
83
59
  });
@@ -100,33 +76,14 @@ const routerPlugin = ({
100
76
  const getRouteApp = () => {
101
77
  return (props) => {
102
78
  const { router, routerContext } = useContext(RuntimeReactContext);
103
- const data = {
104
- loaderData: routerContext.loaderData,
105
- actionData: routerContext.actionData,
106
- errors: serializeErrors(routerContext.errors)
107
- };
108
- const hydrateScript = `window.__staticRouterHydrationData = ${serialize(
109
- data,
110
- { isJSON: true }
111
- )};`;
112
- return /* @__PURE__ */ jsxs(App, { ...props, children: [
113
- /* @__PURE__ */ jsx(
114
- StaticRouterProvider,
115
- {
116
- router,
117
- context: routerContext,
118
- hydrate: false
119
- }
120
- ),
121
- /* @__PURE__ */ jsx(
122
- "script",
123
- {
124
- suppressHydrationWarning: true,
125
- id: "the-nonce",
126
- dangerouslySetInnerHTML: { __html: hydrateScript }
127
- }
128
- )
129
- ] });
79
+ return /* @__PURE__ */ jsx(App, { ...props, children: /* @__PURE__ */ jsx(
80
+ StaticRouterProvider,
81
+ {
82
+ router,
83
+ context: routerContext,
84
+ hydrate: false
85
+ }
86
+ ) });
130
87
  };
131
88
  };
132
89
  const RouteApp = getRouteApp();
@@ -1,54 +1,16 @@
1
1
  import { jsx } from "react/jsx-runtime";
2
- import { Suspense } from "react";
3
2
  import { Route } from "react-router-dom";
3
+ import { renderNestedRoute } from "@modern-js/utils/nestedRoutes";
4
+ import {
5
+ ErrorResponse,
6
+ isRouteErrorResponse
7
+ } from "@modern-js/utils/remix-router";
4
8
  import { DefaultNotFound } from "./DefaultNotFound";
5
- const renderNestedRoute = (nestedRoute, parent) => {
6
- const { children, index, id, component, isRoot } = nestedRoute;
7
- const Component = component;
8
- const routeProps = {
9
- caseSensitive: nestedRoute.caseSensitive,
10
- path: nestedRoute.path,
11
- id: nestedRoute.id,
12
- loader: createLoader(nestedRoute),
13
- action: nestedRoute.action,
14
- hasErrorBoundary: nestedRoute.hasErrorBoundary,
15
- shouldRevalidate: nestedRoute.shouldRevalidate,
16
- handle: nestedRoute.handle,
17
- index: nestedRoute.index,
18
- element: nestedRoute.element,
19
- errorElement: nestedRoute.errorElement
20
- };
21
- if (nestedRoute.error) {
22
- const errorElement = /* @__PURE__ */ jsx(nestedRoute.error, {});
23
- routeProps.errorElement = errorElement;
24
- }
25
- let element;
26
- if (Component) {
27
- if (parent == null ? void 0 : parent.loading) {
28
- const Loading = parent.loading;
29
- if (isLoadableComponent(Component)) {
30
- element = /* @__PURE__ */ jsx(Component, { fallback: /* @__PURE__ */ jsx(Loading, {}) });
31
- } else {
32
- element = /* @__PURE__ */ jsx(Suspense, { fallback: /* @__PURE__ */ jsx(Loading, {}), children: /* @__PURE__ */ jsx(Component, {}) });
33
- }
34
- } else if (isLoadableComponent(Component) || isRoot) {
35
- element = /* @__PURE__ */ jsx(Component, {});
36
- } else {
37
- element = /* @__PURE__ */ jsx(Suspense, { fallback: null, children: /* @__PURE__ */ jsx(Component, {}) });
38
- }
39
- } else {
40
- nestedRoute.loading = parent == null ? void 0 : parent.loading;
41
- }
42
- if (element) {
43
- routeProps.element = element;
44
- }
45
- const childElements = children == null ? void 0 : children.map((childRoute) => {
46
- return renderNestedRoute(childRoute, nestedRoute);
47
- });
48
- const routeElement = index ? /* @__PURE__ */ jsx(Route, { ...routeProps, index: true }, id) : /* @__PURE__ */ jsx(Route, { ...routeProps, index: false, children: childElements }, id);
49
- return routeElement;
50
- };
51
- function getRouteComponents(routes, globalApp) {
9
+ import DeferredDataScripts from "./DeferredDataScripts";
10
+ function getRouteComponents(routes, {
11
+ globalApp,
12
+ ssrMode
13
+ }) {
52
14
  const Layout = ({ Component, ...props }) => {
53
15
  const GlobalLayout = globalApp;
54
16
  if (!GlobalLayout) {
@@ -59,7 +21,9 @@ function getRouteComponents(routes, globalApp) {
59
21
  const routeElements = [];
60
22
  for (const route of routes) {
61
23
  if (route.type === "nested") {
62
- const routeElement = renderNestedRoute(route);
24
+ const routeElement = renderNestedRoute(route, {
25
+ DeferredDataComponent: ssrMode === "stream" ? DeferredDataScripts : void 0
26
+ });
63
27
  routeElements.push(routeElement);
64
28
  } else {
65
29
  const routeElement = /* @__PURE__ */ jsx(
@@ -76,7 +40,7 @@ function getRouteComponents(routes, globalApp) {
76
40
  routeElements.push(/* @__PURE__ */ jsx(Route, { path: "*", element: /* @__PURE__ */ jsx(DefaultNotFound, {}) }, "*"));
77
41
  return routeElements;
78
42
  }
79
- function renderRoutes(routesConfig) {
43
+ function renderRoutes(routesConfig, ssrMode) {
80
44
  if (!routesConfig) {
81
45
  return null;
82
46
  }
@@ -84,7 +48,7 @@ function renderRoutes(routesConfig) {
84
48
  if (!routes) {
85
49
  return null;
86
50
  }
87
- const routeElements = getRouteComponents(routes, globalApp);
51
+ const routeElements = getRouteComponents(routes, { globalApp, ssrMode });
88
52
  return routeElements;
89
53
  }
90
54
  function getLocation(serverContext) {
@@ -118,31 +82,57 @@ function standardSlash(str) {
118
82
  }
119
83
  return addr;
120
84
  }
121
- function createLoader(route) {
122
- const { loader } = route;
123
- if (loader) {
124
- return (args) => {
125
- if (typeof route.lazyImport === "function") {
126
- route.lazyImport();
127
- }
128
- return loader(args);
129
- };
130
- } else {
131
- return () => {
132
- if (typeof route.lazyImport === "function") {
133
- route.lazyImport();
134
- }
135
- return null;
136
- };
85
+ function serializeErrors(errors) {
86
+ if (!errors) {
87
+ return null;
137
88
  }
89
+ const entries = Object.entries(errors);
90
+ const serialized = {};
91
+ for (const [key, val] of entries) {
92
+ if (isRouteErrorResponse(val)) {
93
+ serialized[key] = { ...val, __type: "RouteErrorResponse" };
94
+ } else if (val instanceof Error) {
95
+ serialized[key] = {
96
+ message: val.message,
97
+ stack: val.stack,
98
+ __type: "Error"
99
+ };
100
+ } else {
101
+ serialized[key] = val;
102
+ }
103
+ }
104
+ return serialized;
138
105
  }
139
- function isLoadableComponent(component) {
140
- return component && component.displayName === "Loadable" && component.preload && typeof component.preload === "function";
106
+ function deserializeErrors(errors) {
107
+ if (!errors) {
108
+ return null;
109
+ }
110
+ const entries = Object.entries(errors);
111
+ const serialized = {};
112
+ for (const [key, val] of entries) {
113
+ if (val && val.__type === "RouteErrorResponse") {
114
+ serialized[key] = new ErrorResponse(
115
+ val.status,
116
+ val.statusText,
117
+ val.data,
118
+ val.internal === true
119
+ );
120
+ } else if (val && val.__type === "Error") {
121
+ const error = new Error(val.message);
122
+ error.stack = val.stack;
123
+ serialized[key] = error;
124
+ } else {
125
+ serialized[key] = val;
126
+ }
127
+ }
128
+ return serialized;
141
129
  }
142
130
  export {
131
+ deserializeErrors,
143
132
  getLocation,
144
133
  getRouteComponents,
145
134
  renderRoutes,
135
+ serializeErrors,
146
136
  standardSlash,
147
137
  urlJoin
148
138
  };
@@ -39,6 +39,8 @@ var cli_default = () => ({
39
39
  return {
40
40
  source: {
41
41
  alias: {
42
+ // ensure that all packages use the same storage in @modern-js/utils/ssr
43
+ "@modern-js/utils/ssr": require.resolve("@modern-js/utils/ssr"),
42
44
  "@modern-js/runtime/plugins": pluginsExportsUtils.getPath()
43
45
  }
44
46
  },
@@ -15,6 +15,7 @@ const ssr = (config = {}) => ({
15
15
  init({ context }, next) {
16
16
  const { request } = context.ssrContext;
17
17
  context.ssrContext.request = formatServer(request);
18
+ context.ssrContext.mode = config.mode;
18
19
  return next({ context });
19
20
  },
20
21
  pickContext: ({ context, pickedContext }, next) => {
@@ -1,4 +1,4 @@
1
- import serialize from "serialize-javascript";
1
+ import { serializeJson } from "@modern-js/utils/serialize";
2
2
  import { buildTemplate } from "./buildTemplate.share";
3
3
  function buildShellAfterTemplate(afterAppTemplate, options) {
4
4
  const callbacks = [injectSSRDataScript];
@@ -26,9 +26,7 @@ function buildShellAfterTemplate(afterAppTemplate, options) {
26
26
  renderLevel
27
27
  };
28
28
  return `
29
- <script>window._SSR_DATA = ${serialize(SSRData, {
30
- isJSON: true
31
- })}</script>
29
+ <script>window._SSR_DATA = ${serializeJson(SSRData)}</script>
32
30
  `;
33
31
  }
34
32
  }
@@ -1,8 +1,15 @@
1
1
  import { Transform } from "stream";
2
2
  import { RenderLevel } from "../types";
3
+ import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common";
3
4
  import { getTemplates } from "./template";
5
+ var ShellChunkStatus = /* @__PURE__ */ ((ShellChunkStatus2) => {
6
+ ShellChunkStatus2[ShellChunkStatus2["IDLE"] = 0] = "IDLE";
7
+ ShellChunkStatus2[ShellChunkStatus2["START"] = 1] = "START";
8
+ ShellChunkStatus2[ShellChunkStatus2["FINIESH"] = 2] = "FINIESH";
9
+ return ShellChunkStatus2;
10
+ })(ShellChunkStatus || {});
4
11
  function renderToPipe(rootElement, context, options) {
5
- let isShellStream = true;
12
+ let shellChunkStatus = 0 /* IDLE */;
6
13
  const { ssrContext } = context;
7
14
  const forUserPipe = (stream) => {
8
15
  return new Promise((resolve) => {
@@ -23,9 +30,20 @@ function renderToPipe(rootElement, context, options) {
23
30
  const injectableTransform = new Transform({
24
31
  transform(chunk, _encoding, callback) {
25
32
  try {
26
- if (isShellStream) {
27
- this.push(joinChunk(shellBefore, chunk, shellAfter));
28
- isShellStream = false;
33
+ if (shellChunkStatus !== 2 /* FINIESH */) {
34
+ let concatedChunk = chunk.toString();
35
+ if (shellChunkStatus === 0 /* IDLE */) {
36
+ concatedChunk = `${shellBefore}${concatedChunk}`;
37
+ shellChunkStatus = 1 /* START */;
38
+ }
39
+ if (shellChunkStatus === 1 /* START */ && concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)) {
40
+ concatedChunk = concatedChunk.replace(
41
+ ESCAPED_SHELL_STREAM_END_MARK,
42
+ shellAfter
43
+ );
44
+ shellChunkStatus = 2 /* FINIESH */;
45
+ }
46
+ this.push(concatedChunk);
29
47
  } else {
30
48
  this.push(chunk);
31
49
  }
@@ -68,9 +86,6 @@ function renderToPipe(rootElement, context, options) {
68
86
  });
69
87
  };
70
88
  return forUserPipe;
71
- function joinChunk(before = "", chunk, after = "") {
72
- return `${before}${chunk.toString()}${after}`;
73
- }
74
89
  }
75
90
  var renderToPipe_default = renderToPipe;
76
91
  export {
@@ -2,7 +2,7 @@ import { buildShellAfterTemplate } from "./buildTemplate.after";
2
2
  import { buildShellBeforeTemplate } from "./bulidTemplate.before";
3
3
  const HTML_SEPARATOR = "<!--<?- html ?>-->";
4
4
  const getTemplates = (context, renderLevel) => {
5
- const { ssrContext } = context;
5
+ const { ssrContext, routerContext } = context;
6
6
  const [beforeAppTemplate = "", afterAppHtmlTemplate = ""] = ssrContext.template.split(HTML_SEPARATOR) || [];
7
7
  const builtBeforeTemplate = buildShellBeforeTemplate(
8
8
  beforeAppTemplate,
@@ -10,6 +10,7 @@ const getTemplates = (context, renderLevel) => {
10
10
  );
11
11
  const builtAfterTemplate = buildShellAfterTemplate(afterAppHtmlTemplate, {
12
12
  ssrContext,
13
+ routerContext,
13
14
  renderLevel
14
15
  });
15
16
  return {