@modern-js/runtime 2.7.0 → 2.9.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 (37) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +2 -2
  3. package/dist/cjs/cli/index.js +4 -3
  4. package/dist/cjs/router/runtime/DeferredDataScripts.js +2 -140
  5. package/dist/cjs/router/runtime/DeferredDataScripts.node.js +165 -0
  6. package/dist/cjs/ssr/cli/index.js +1 -2
  7. package/dist/cjs/ssr/serverRender/index.js +8 -0
  8. package/dist/cjs/ssr/serverRender/renderToStream/bulidTemplate.before.js +7 -3
  9. package/dist/cjs/ssr/serverRender/renderToStream/renderToPipe.worker.js +25 -9
  10. package/dist/cjs/ssr/serverRender/utils.js +3 -0
  11. package/dist/cjs/state/runtime/index.js +2 -8
  12. package/dist/esm/cli/index.js +4 -3
  13. package/dist/esm/router/runtime/DeferredDataScripts.js +2 -164
  14. package/dist/esm/router/runtime/DeferredDataScripts.node.js +166 -0
  15. package/dist/esm/ssr/cli/index.js +2 -3
  16. package/dist/esm/ssr/serverRender/index.js +6 -1
  17. package/dist/esm/ssr/serverRender/renderToStream/bulidTemplate.before.js +2 -2
  18. package/dist/esm/ssr/serverRender/renderToStream/renderToPipe.worker.js +24 -11
  19. package/dist/esm/ssr/serverRender/utils.js +2 -1
  20. package/dist/esm/state/runtime/index.js +1 -2
  21. package/dist/esm-node/cli/index.js +4 -3
  22. package/dist/esm-node/router/runtime/DeferredDataScripts.js +2 -144
  23. package/dist/esm-node/router/runtime/DeferredDataScripts.node.js +148 -0
  24. package/dist/esm-node/ssr/cli/index.js +2 -3
  25. package/dist/esm-node/ssr/serverRender/index.js +8 -0
  26. package/dist/esm-node/ssr/serverRender/renderToStream/bulidTemplate.before.js +6 -2
  27. package/dist/esm-node/ssr/serverRender/renderToStream/renderToPipe.worker.js +25 -9
  28. package/dist/esm-node/ssr/serverRender/utils.js +2 -0
  29. package/dist/esm-node/state/runtime/index.js +1 -4
  30. package/dist/types/router/runtime/DeferredDataScripts.d.ts +2 -7
  31. package/dist/types/router/runtime/DeferredDataScripts.node.d.ts +8 -0
  32. package/dist/types/router/runtime/index.d.ts +1 -1
  33. package/dist/types/ssr/serverRender/renderToStream/renderToPipe.worker.d.ts +1 -1
  34. package/dist/types/ssr/serverRender/utils.d.ts +1 -0
  35. package/dist/types/state/runtime/index.d.ts +0 -1
  36. package/package.json +12 -12
  37. package/types/router.d.ts +1 -1
@@ -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_node_default = DeferredDataScripts;
146
+ export {
147
+ DeferredDataScripts_node_default as default
148
+ };
@@ -5,7 +5,7 @@ import {
5
5
  LOADABLE_STATS_FILE,
6
6
  isUseSSRBundle,
7
7
  createRuntimeExportsUtils,
8
- isSingleEntry
8
+ isSSGEntry
9
9
  } from "@modern-js/utils";
10
10
  const PLUGIN_IDENTIFIER = "ssr";
11
11
  const hasStringSSREntry = (userConfig) => {
@@ -113,8 +113,7 @@ var cli_default = () => ({
113
113
  );
114
114
  }
115
115
  }
116
- const ssgConfig = userConfig.output.ssg;
117
- const useSSG = isSingleEntry(entrypoints) ? Boolean(ssgConfig) : ssgConfig === true || typeof (ssgConfig == null ? void 0 : ssgConfig[0]) === "function" || Boolean(ssgConfig == null ? void 0 : ssgConfig[entryName]);
116
+ const useSSG = isSSGEntry(userConfig, entryName, entrypoints);
118
117
  ssrConfigMap.set(entryName, ssrConfig || useSSG);
119
118
  if (ssrConfig || useSSG) {
120
119
  imports.push({
@@ -1,5 +1,13 @@
1
1
  import { isReact18 } from "../utils";
2
+ import { CSS_CHUNKS_PLACEHOLDER } from "./utils";
2
3
  async function serverRender(options) {
4
+ var _a, _b;
5
+ if ((_a = options.context.ssrContext) == null ? void 0 : _a.template) {
6
+ options.context.ssrContext.template = (_b = options.context.ssrContext) == null ? void 0 : _b.template.replace(
7
+ "</head>",
8
+ `${CSS_CHUNKS_PLACEHOLDER}</head>`
9
+ );
10
+ }
3
11
  if (isReact18() && options.config.mode === "stream") {
4
12
  const pipe = await require("./renderToStream").render(options);
5
13
  return pipe;
@@ -1,11 +1,11 @@
1
1
  import ReactHelmet from "react-helmet";
2
2
  import { matchRoutes } from "react-router-dom";
3
3
  import helmetReplace from "../helmet";
4
+ import { CSS_CHUNKS_PLACEHOLDER } from "../utils";
4
5
  import {
5
6
  HEAD_REG_EXP,
6
7
  buildTemplate
7
8
  } from "./buildTemplate.share";
8
- const CSS_CHUNKS_PLACEHOLDER = "<!--<?- chunksMap.css ?>-->";
9
9
  function getHeadTemplate(beforeEntryTemplate, context) {
10
10
  const callbacks = [
11
11
  (headTemplate2) => {
@@ -29,7 +29,11 @@ function getHeadTemplate(beforeEntryTemplate, context) {
29
29
  }
30
30
  const { routeAssets } = routeManifest;
31
31
  const cssChunks = [];
32
- const matches = matchRoutes(routes, routerContext.location);
32
+ const matches = matchRoutes(
33
+ routes,
34
+ routerContext.location,
35
+ routerContext.basename
36
+ );
33
37
  matches == null ? void 0 : matches.forEach((match, index) => {
34
38
  if (!index) {
35
39
  return;
@@ -1,9 +1,16 @@
1
1
  import { RenderLevel } from "../types";
2
+ import { ESCAPED_SHELL_STREAM_END_MARK } from "../../../common";
2
3
  import { getTemplates } from "./template";
4
+ var ShellChunkStatus = /* @__PURE__ */ ((ShellChunkStatus2) => {
5
+ ShellChunkStatus2[ShellChunkStatus2["IDLE"] = 0] = "IDLE";
6
+ ShellChunkStatus2[ShellChunkStatus2["START"] = 1] = "START";
7
+ ShellChunkStatus2[ShellChunkStatus2["FINIESH"] = 2] = "FINIESH";
8
+ return ShellChunkStatus2;
9
+ })(ShellChunkStatus || {});
3
10
  function renderToPipe(rootElement, context, options) {
4
- let isShellStream = true;
11
+ let shellChunkStatus = 0 /* IDLE */;
5
12
  const { ssrContext } = context;
6
- const forUserPipe = async (stream) => {
13
+ const forUserPipe = async () => {
7
14
  let renderToReadableStream;
8
15
  try {
9
16
  ({ renderToReadableStream } = require("react-dom/server"));
@@ -35,11 +42,20 @@ function renderToPipe(rootElement, context, options) {
35
42
  controller.close();
36
43
  return;
37
44
  }
38
- if (isShellStream) {
39
- controller.enqueue(encodeForWebStream(shellBefore));
40
- controller.enqueue(value);
41
- controller.enqueue(encodeForWebStream(shellAfter));
42
- isShellStream = false;
45
+ if (shellChunkStatus !== 2 /* FINIESH */) {
46
+ let concatedChunk = new TextDecoder().decode(value);
47
+ if (shellChunkStatus === 0 /* IDLE */) {
48
+ concatedChunk = `${shellBefore}${concatedChunk}`;
49
+ shellChunkStatus = 1 /* START */;
50
+ }
51
+ if (shellChunkStatus === 1 /* START */ && concatedChunk.endsWith(ESCAPED_SHELL_STREAM_END_MARK)) {
52
+ concatedChunk = concatedChunk.replace(
53
+ ESCAPED_SHELL_STREAM_END_MARK,
54
+ shellAfter
55
+ );
56
+ shellChunkStatus = 2 /* FINIESH */;
57
+ }
58
+ controller.enqueue(encodeForWebStream(concatedChunk));
43
59
  } else {
44
60
  controller.enqueue(value);
45
61
  }
@@ -48,7 +64,7 @@ function renderToPipe(rootElement, context, options) {
48
64
  push();
49
65
  }
50
66
  });
51
- return readableOriginal(injectableStream).readableOriginal(stream);
67
+ return injectableStream;
52
68
  } catch (err) {
53
69
  ssrContext.metrics.emitCounter("app.render.streaming.shell.error", 1);
54
70
  const { shellAfter: shellAfter2, shellBefore: shellBefore2 } = getTemplates(
@@ -59,7 +75,7 @@ function renderToPipe(rootElement, context, options) {
59
75
  return fallbackHtml;
60
76
  }
61
77
  };
62
- return forUserPipe;
78
+ return forUserPipe();
63
79
  }
64
80
  let encoder;
65
81
  function encodeForWebStream(thing) {
@@ -1,3 +1,4 @@
1
+ const CSS_CHUNKS_PLACEHOLDER = "<!--<?- chunksMap.css ?>-->";
1
2
  function getLoadableScripts(extractor) {
2
3
  const check = (scripts2) => (scripts2 || "").includes("__LOADABLE_REQUIRED_CHUNKS___ext");
3
4
  const scripts = extractor.getScriptTags();
@@ -7,5 +8,6 @@ function getLoadableScripts(extractor) {
7
8
  return scripts.split("</script>").slice(0, 2).map((i) => `${i}</script>`).join("");
8
9
  }
9
10
  export {
11
+ CSS_CHUNKS_PLACEHOLDER,
10
12
  getLoadableScripts
11
13
  };
@@ -1,9 +1,6 @@
1
1
  export * from "@modern-js-reduck/react";
2
- import { model, createStore } from "@modern-js-reduck/store";
3
2
  import { default as default2 } from "./plugin";
4
3
  export * from "./plugin";
5
4
  export {
6
- createStore,
7
- default2 as default,
8
- model
5
+ default2 as default
9
6
  };
@@ -1,8 +1,3 @@
1
- /// <reference types="react" />
1
+ declare const _default: () => null;
2
2
 
3
- /**
4
- * DeferredDataScripts only renders in server side,
5
- * it doesn't need to be hydrated in client side.
6
- */
7
- declare const DeferredDataScripts: () => JSX.Element | null;
8
- export default DeferredDataScripts;
3
+ export default _default;
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+
3
+ /**
4
+ * DeferredDataScripts only renders in server side,
5
+ * it doesn't need to be hydrated in client side.
6
+ */
7
+ declare const DeferredDataScripts: () => JSX.Element | null;
8
+ export default DeferredDataScripts;
@@ -4,6 +4,6 @@ export type { SingleRouteConfig, RouterConfig };
4
4
  export default routerPlugin;
5
5
  export { modifyRoutes } from './plugin';
6
6
  export * from './withRouter';
7
- export type { FormEncType, FormMethod, GetScrollRestorationKeyFunction, ParamKeyValuePair, SubmitOptions, URLSearchParamsInit, FetcherWithComponents, ActionFunction, ActionFunctionArgs, AwaitProps, unstable_Blocker, unstable_BlockerFunction, DataRouteMatch, DataRouteObject, Fetcher, Hash, IndexRouteObject, IndexRouteProps, JsonFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, Location, MemoryRouterProps, NavigateFunction, NavigateOptions, NavigateProps, Navigation, Navigator, NonIndexRouteObject, OutletProps, Params, ParamParseKey, Path, PathMatch, Pathname, PathPattern, PathRouteProps, RedirectFunction, RelativeRoutingType, RouteMatch, RouteObject, RouteProps, RouterProps, RouterProviderProps, RoutesProps, Search, ShouldRevalidateFunction, To } from 'react-router-dom';
7
+ export type { FormEncType, FormMethod, GetScrollRestorationKeyFunction, ParamKeyValuePair, SubmitOptions, URLSearchParamsInit, FetcherWithComponents, BrowserRouterProps, HashRouterProps, HistoryRouterProps, LinkProps, NavLinkProps, FormProps, ScrollRestorationProps, SubmitFunction, ActionFunction, ActionFunctionArgs, AwaitProps, unstable_Blocker, unstable_BlockerFunction, DataRouteMatch, DataRouteObject, Fetcher, Hash, IndexRouteObject, IndexRouteProps, JsonFunction, LayoutRouteProps, LoaderFunction, LoaderFunctionArgs, Location, MemoryRouterProps, NavigateFunction, NavigateOptions, NavigateProps, Navigation, Navigator, NonIndexRouteObject, OutletProps, Params, ParamParseKey, Path, PathMatch, Pathname, PathPattern, PathRouteProps, RedirectFunction, RelativeRoutingType, RouteMatch, RouteObject, RouteProps, RouterProps, RouterProviderProps, RoutesProps, Search, ShouldRevalidateFunction, To } from 'react-router-dom';
8
8
  export { createBrowserRouter, createHashRouter, createMemoryRouter, RouterProvider, BrowserRouter, HashRouter, MemoryRouter, Router, Await, Form, Link, NavLink, Navigate, Outlet, Route, Routes, ScrollRestoration, useActionData, useAsyncError, useAsyncValue, useBeforeUnload, useFetcher, useFetchers, useFormAction, useHref, useInRouterContext, useLinkClickHandler, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes, useSearchParams, useSubmit, createRoutesFromChildren, createRoutesFromElements, createSearchParams, generatePath, isRouteErrorResponse, matchPath, matchRoutes, renderMatches, resolvePath } from 'react-router-dom';
9
9
  export { defer, json, redirect } from '@modern-js/utils/remix-router';
@@ -4,5 +4,5 @@ import type { Writable } from 'stream';
4
4
  import type { RenderToReadableStreamOptions } from 'react-dom/server';
5
5
  import { RuntimeContext } from '../types';
6
6
  export type Pipe<T extends Writable> = (output: T) => Promise<T | string>;
7
- declare function renderToPipe(rootElement: React.ReactElement, context: RuntimeContext, options?: RenderToReadableStreamOptions): Pipe<Writable>;
7
+ declare function renderToPipe(rootElement: React.ReactElement, context: RuntimeContext, options?: RenderToReadableStreamOptions): Promise<string | ReadableStream<any>>;
8
8
  export default renderToPipe;
@@ -1,2 +1,3 @@
1
1
  import type { ChunkExtractor } from '@loadable/server';
2
+ export declare const CSS_CHUNKS_PLACEHOLDER = "<!--<?- chunksMap.css ?>-->";
2
3
  export declare function getLoadableScripts(extractor: ChunkExtractor): string;
@@ -1,4 +1,3 @@
1
1
  export * from '@modern-js-reduck/react';
2
- export { model, createStore } from '@modern-js-reduck/store';
3
2
  export { default } from './plugin';
4
3
  export * from './plugin';
package/package.json CHANGED
@@ -2,8 +2,8 @@
2
2
  "name": "@modern-js/runtime",
3
3
  "description": "A Progressive React Framework for modern web development.",
4
4
  "homepage": "https://modernjs.dev",
5
- "bugs": "https://github.com/modern-js-dev/modern.js/issues",
6
- "repository": "modern-js-dev/modern.js",
5
+ "bugs": "https://github.com/web-infra-dev/modern.js/issues",
6
+ "repository": "web-infra-dev/modern.js",
7
7
  "license": "MIT",
8
8
  "keywords": [
9
9
  "react",
@@ -11,7 +11,7 @@
11
11
  "modern",
12
12
  "modern.js"
13
13
  ],
14
- "version": "2.7.0",
14
+ "version": "2.9.0",
15
15
  "engines": {
16
16
  "node": ">=14.17.6"
17
17
  },
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "./model": {
69
69
  "jsnext:source": "./src/state/index.ts",
70
- "types": "./dist/types/model.d.ts",
70
+ "types": "./types/model.d.ts",
71
71
  "default": "./dist/esm/state/index.js"
72
72
  },
73
73
  "./cli": {
@@ -158,9 +158,9 @@
158
158
  "react-side-effect": "^2.1.1",
159
159
  "redux-logger": "^3.0.6",
160
160
  "styled-components": "^5.3.1",
161
- "@modern-js/plugin": "2.7.0",
162
- "@modern-js/types": "2.7.0",
163
- "@modern-js/utils": "2.7.0"
161
+ "@modern-js/plugin": "2.9.0",
162
+ "@modern-js/utils": "2.9.0",
163
+ "@modern-js/types": "2.9.0"
164
164
  },
165
165
  "peerDependencies": {
166
166
  "react": ">=17",
@@ -181,11 +181,11 @@
181
181
  "react-dom": "^18",
182
182
  "ts-jest": "^27.0.4",
183
183
  "typescript": "^4",
184
- "@modern-js/core": "2.7.0",
185
- "@modern-js/server-core": "2.7.0",
186
- "@modern-js/app-tools": "2.7.0",
187
- "@scripts/build": "2.7.0",
188
- "@scripts/jest-config": "2.7.0"
184
+ "@modern-js/app-tools": "2.9.0",
185
+ "@modern-js/core": "2.9.0",
186
+ "@modern-js/server-core": "2.9.0",
187
+ "@scripts/jest-config": "2.9.0",
188
+ "@scripts/build": "2.9.0"
189
189
  },
190
190
  "sideEffects": false,
191
191
  "modernConfig": {},
package/types/router.d.ts CHANGED
@@ -3,7 +3,7 @@ import '../dist/types/router';
3
3
 
4
4
  declare module '@modern-js/app-tools' {
5
5
  interface RuntimeUserConfig {
6
- router?: RouterConfig | boolean;
6
+ router?: Partial<RouterConfig> | boolean;
7
7
  }
8
8
  }
9
9