@umijs/renderer-react 4.0.0-rc.8 → 4.0.1

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.
@@ -1,3 +1,22 @@
1
1
  import React from 'react';
2
- export declare const AppContext: React.Context<any>;
3
- export declare function useAppData(): any;
2
+ import { IClientRoute, ILoaderData, IRouteComponents, IRoutesById } from './types';
3
+ interface IAppContextType {
4
+ routes: IRoutesById;
5
+ routeComponents: IRouteComponents;
6
+ clientRoutes: IClientRoute[];
7
+ pluginManager: any;
8
+ rootElement?: HTMLElement;
9
+ basename?: string;
10
+ clientLoaderData: ILoaderData;
11
+ preloadRoute?: (to: string) => void;
12
+ serverLoaderData: ILoaderData;
13
+ }
14
+ export declare const AppContext: React.Context<IAppContextType>;
15
+ export declare function useAppData(): IAppContextType;
16
+ export declare function useServerLoaderData(): {
17
+ data: any;
18
+ };
19
+ export declare function useClientLoaderData(): {
20
+ data: any;
21
+ };
22
+ export {};
@@ -1,5 +1,16 @@
1
1
  import React from 'react';
2
- export const AppContext = React.createContext(null);
2
+ import { useRouteData } from './routeContext';
3
+ export const AppContext = React.createContext({});
3
4
  export function useAppData() {
4
5
  return React.useContext(AppContext);
5
6
  }
7
+ export function useServerLoaderData() {
8
+ const route = useRouteData();
9
+ const appData = useAppData();
10
+ return { data: appData.serverLoaderData[route.route.id] };
11
+ }
12
+ export function useClientLoaderData() {
13
+ const route = useRouteData();
14
+ const appData = useAppData();
15
+ return { data: appData.clientLoaderData[route.route.id] };
16
+ }
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { History } from 'history';
2
2
  import React from 'react';
3
3
  import { IRouteComponents, IRoutesById } from './types';
4
+ export declare function Routes(): React.ReactElement<any, string | React.JSXElementConstructor<any>> | null;
4
5
  export declare function renderClient(opts: {
6
+ publicPath?: string;
7
+ runtimePublicPath?: boolean;
5
8
  rootElement?: HTMLElement;
6
9
  routes: IRoutesById;
7
10
  routeComponents: IRouteComponents;
@@ -9,4 +12,5 @@ export declare function renderClient(opts: {
9
12
  basename?: string;
10
13
  loadingComponent?: React.ReactNode;
11
14
  history: History;
15
+ hydrate?: boolean;
12
16
  }): void;
package/dist/browser.js CHANGED
@@ -1,6 +1,7 @@
1
- import React from 'react';
2
- import ReactDOM from 'react-dom';
3
- import { Router, useRoutes } from 'react-router-dom';
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ // compatible with < react@18 in @umijs/preset-umi/src/features/react
3
+ import ReactDOM from 'react-dom/client';
4
+ import { matchRoutes, Router, useRoutes } from 'react-router-dom';
4
5
  import { AppContext, useAppData } from './appContext';
5
6
  import { createClientRoutes } from './routes';
6
7
  function BrowserRoutes(props) {
@@ -28,7 +29,7 @@ function BrowserRoutes(props) {
28
29
  }, [history, props.routes, props.clientRoutes]);
29
30
  return (React.createElement(Router, { navigator: history, location: state.location, basename: props.basename }, props.children));
30
31
  }
31
- function Routes() {
32
+ export function Routes() {
32
33
  const { clientRoutes } = useAppData();
33
34
  return useRoutes(clientRoutes);
34
35
  }
@@ -40,6 +41,13 @@ export function renderClient(opts) {
40
41
  routeComponents: opts.routeComponents,
41
42
  loadingComponent: opts.loadingComponent,
42
43
  });
44
+ opts.pluginManager.applyPlugins({
45
+ key: 'patchClientRoutes',
46
+ type: 'event',
47
+ args: {
48
+ routes: clientRoutes,
49
+ },
50
+ });
43
51
  let rootContainer = (React.createElement(BrowserRoutes, { basename: basename, pluginManager: opts.pluginManager, routes: opts.routes, clientRoutes: clientRoutes, history: opts.history },
44
52
  React.createElement(Routes, null)));
45
53
  for (const key of [
@@ -58,20 +66,96 @@ export function renderClient(opts) {
58
66
  args: {},
59
67
  });
60
68
  }
61
- const browser = (React.createElement(AppContext.Provider, { value: {
62
- routes: opts.routes,
63
- routeComponents: opts.routeComponents,
64
- clientRoutes,
65
- pluginManager: opts.pluginManager,
66
- rootElement: opts.rootElement,
67
- basename,
68
- } }, rootContainer));
69
- // @ts-ignore
70
- if (ReactDOM.createRoot) {
69
+ const Browser = () => {
70
+ const [clientLoaderData, setClientLoaderData] = useState({});
71
+ const [serverLoaderData, setServerLoaderData] = useState(
71
72
  // @ts-ignore
72
- ReactDOM.createRoot(rootElement).render(browser);
73
+ window.__UMI_LOADER_DATA__ || {});
74
+ const handleRouteChange = useCallback((id, isFirst) => {
75
+ var _a;
76
+ // Patched routes has to id
77
+ const matchedRouteIds = (((_a = matchRoutes(clientRoutes, id)) === null || _a === void 0 ? void 0 : _a.map(
78
+ // @ts-ignore
79
+ (route) => route.route.id)) || []).filter(Boolean);
80
+ matchedRouteIds.forEach((id) => {
81
+ var _a;
82
+ // preload
83
+ // @ts-ignore
84
+ const manifest = window.__umi_manifest__;
85
+ if (manifest) {
86
+ const routeIdReplaced = id.replace(/[\/\-]/g, '_');
87
+ const preloadId = 'preload-' + routeIdReplaced;
88
+ if (!document.getElementById(preloadId)) {
89
+ const key = Object.keys(manifest).find((k) => k.startsWith(routeIdReplaced + '.'));
90
+ if (!key)
91
+ return;
92
+ let file = manifest[key];
93
+ const link = document.createElement('link');
94
+ link.id = preloadId;
95
+ link.rel = 'preload';
96
+ link.as = 'script';
97
+ // publicPath already in the manifest,
98
+ // but if runtimePublicPath is true, we need to replace it
99
+ if (opts.runtimePublicPath) {
100
+ file = file.replace(new RegExp(`^${opts.publicPath}`),
101
+ // @ts-ignore
102
+ window.publicPath);
103
+ }
104
+ link.href = file;
105
+ document.head.appendChild(link);
106
+ }
107
+ }
108
+ // server loader
109
+ if (!isFirst && opts.routes[id].hasServerLoader) {
110
+ fetch('/__serverLoader?route=' + id)
111
+ .then((d) => d.json())
112
+ .then((data) => {
113
+ // setServerLoaderData when startTransition because if ssr is enabled,
114
+ // the component may being hydrated and setLoaderData will break the hydration
115
+ React.startTransition(() => {
116
+ setServerLoaderData((d) => ({ ...d, [id]: data }));
117
+ });
118
+ })
119
+ .catch(console.error);
120
+ }
121
+ // client loader
122
+ // onPatchClientRoutes 添加的 route 在 opts.routes 里是不存在的
123
+ const clientLoader = (_a = opts.routes[id]) === null || _a === void 0 ? void 0 : _a.clientLoader;
124
+ if (clientLoader && !clientLoaderData[id]) {
125
+ clientLoader().then((data) => {
126
+ setClientLoaderData((d) => ({ ...d, [id]: data }));
127
+ });
128
+ }
129
+ });
130
+ }, [clientLoaderData]);
131
+ useEffect(() => {
132
+ handleRouteChange(window.location.pathname, true);
133
+ return opts.history.listen((e) => {
134
+ handleRouteChange(e.location.pathname);
135
+ });
136
+ }, []);
137
+ return (React.createElement(AppContext.Provider, { value: {
138
+ routes: opts.routes,
139
+ routeComponents: opts.routeComponents,
140
+ clientRoutes,
141
+ pluginManager: opts.pluginManager,
142
+ rootElement: opts.rootElement,
143
+ basename,
144
+ clientLoaderData,
145
+ serverLoaderData,
146
+ preloadRoute: handleRouteChange,
147
+ } }, rootContainer));
148
+ };
149
+ if (opts.hydrate) {
150
+ ReactDOM.hydrateRoot(rootElement, React.createElement(Browser, null));
73
151
  }
74
152
  else {
75
- ReactDOM.render(browser, rootElement);
153
+ if (ReactDOM.createRoot) {
154
+ ReactDOM.createRoot(rootElement).render(React.createElement(Browser, null));
155
+ }
156
+ else {
157
+ // @ts-ignore
158
+ ReactDOM.render(React.createElement(Browser, null), rootElement);
159
+ }
76
160
  }
77
161
  }
package/dist/index.d.ts CHANGED
@@ -1,5 +1,7 @@
1
1
  export { createBrowserHistory, createHashHistory, createMemoryHistory, History, } from 'history';
2
- export { createSearchParams, Link, matchPath, matchRoutes, NavLink, Outlet, useLocation, useMatch, useNavigate, useOutlet, useParams, useResolvedPath, useRoutes, useSearchParams, } from 'react-router-dom';
3
- export { useAppData } from './appContext';
2
+ export { createSearchParams, matchPath, matchRoutes, Navigate, NavLink, Outlet, resolvePath, useLocation, useMatch, useNavigate, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useSearchParams, } from 'react-router-dom';
3
+ export { useAppData, useClientLoaderData, useServerLoaderData, } from './appContext';
4
4
  export { renderClient } from './browser';
5
+ export { LinkWithPrefetch as Link } from './link';
5
6
  export { useRouteData } from './routeContext';
7
+ export { __useFetcher } from './useFetcher';
package/dist/index.js CHANGED
@@ -1,5 +1,7 @@
1
1
  export { createBrowserHistory, createHashHistory, createMemoryHistory, } from 'history';
2
- export { createSearchParams, Link, matchPath, matchRoutes, NavLink, Outlet, useLocation, useMatch, useNavigate, useOutlet, useParams, useResolvedPath, useRoutes, useSearchParams, } from 'react-router-dom';
3
- export { useAppData } from './appContext';
2
+ export { createSearchParams, matchPath, matchRoutes, Navigate, NavLink, Outlet, resolvePath, useLocation, useMatch, useNavigate, useOutlet, useOutletContext, useParams, useResolvedPath, useRoutes, useSearchParams, } from 'react-router-dom';
3
+ export { useAppData, useClientLoaderData, useServerLoaderData, } from './appContext';
4
4
  export { renderClient } from './browser';
5
+ export { LinkWithPrefetch as Link } from './link';
5
6
  export { useRouteData } from './routeContext';
7
+ export { __useFetcher } from './useFetcher';
package/dist/link.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ import React, { PropsWithChildren } from 'react';
2
+ import { LinkProps } from 'react-router-dom';
3
+ export declare function LinkWithPrefetch(props: PropsWithChildren<{
4
+ prefetch?: boolean;
5
+ } & LinkProps & React.RefAttributes<HTMLAnchorElement>>): JSX.Element;
package/dist/link.js ADDED
@@ -0,0 +1,8 @@
1
+ import React from 'react';
2
+ import { Link } from 'react-router-dom';
3
+ import { useAppData } from './appContext';
4
+ export function LinkWithPrefetch(props) {
5
+ const appData = useAppData();
6
+ const to = typeof props.to === 'string' ? props.to : props.to.pathname;
7
+ return (React.createElement(Link, { onMouseEnter: () => { var _a; return props.prefetch && to && ((_a = appData.preloadRoute) === null || _a === void 0 ? void 0 : _a.call(appData, to)); }, ...props }, props.children));
8
+ }
@@ -1,6 +1,7 @@
1
1
  import React from 'react';
2
+ import { IRoute } from './types';
2
3
  export interface IRouteContextType {
3
- route: any;
4
+ route: IRoute;
4
5
  }
5
6
  export declare const RouteContext: React.Context<IRouteContextType | undefined>;
6
7
  export declare function useRouteData(): IRouteContextType;
package/dist/routes.d.ts CHANGED
@@ -1,25 +1,8 @@
1
1
  import React from 'react';
2
- import { IRoute, IRoutesById } from './types';
2
+ import { IClientRoute, IRoutesById } from './types';
3
3
  export declare function createClientRoutes(opts: {
4
4
  routesById: IRoutesById;
5
5
  routeComponents: Record<string, any>;
6
6
  parentId?: string;
7
7
  loadingComponent?: React.ReactNode;
8
- }): {
9
- parentId?: string | undefined;
10
- id: string;
11
- path: string | undefined;
12
- index: boolean | undefined;
13
- element: JSX.Element;
14
- }[];
15
- export declare function createClientRoute(opts: {
16
- route: IRoute;
17
- routeComponent: any;
18
- loadingComponent?: React.ReactNode;
19
- }): {
20
- parentId?: string | undefined;
21
- id: string;
22
- path: string | undefined;
23
- index: boolean | undefined;
24
- element: JSX.Element;
25
- };
8
+ }): IClientRoute[];
package/dist/routes.js CHANGED
@@ -1,16 +1,4 @@
1
- var __rest = (this && this.__rest) || function (s, e) {
2
- var t = {};
3
- for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
- t[p] = s[p];
5
- if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
- for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
- if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
- t[p[i]] = s[p[i]];
9
- }
10
- return t;
11
- };
12
1
  // @ts-ignore
13
- import loadable from '@loadable/component';
14
2
  import React from 'react';
15
3
  import { Navigate } from 'react-router-dom';
16
4
  import { RouteContext } from './routeContext';
@@ -31,30 +19,42 @@ export function createClientRoutes(opts) {
31
19
  loadingComponent: opts.loadingComponent,
32
20
  });
33
21
  if (children.length > 0) {
34
- // @ts-ignore
35
22
  route.children = children;
36
23
  // TODO: remove me
37
24
  // compatible with @ant-design/pro-layout
38
- // @ts-ignore
39
25
  route.routes = children;
40
26
  }
41
27
  return route;
42
28
  });
43
29
  }
44
- export function createClientRoute(opts) {
30
+ function createClientRoute(opts) {
45
31
  const { route } = opts;
46
- const { id, path, index, redirect } = route, props = __rest(route, ["id", "path", "index", "redirect"]);
47
- return Object.assign({ id: id, path: path, index: index, element: redirect ? (React.createElement(Navigate, { to: redirect })) : (React.createElement(RouteContext.Provider, { value: {
32
+ const { redirect, ...props } = route;
33
+ return {
34
+ element: redirect ? (React.createElement(Navigate, { to: redirect })) : (React.createElement(RouteContext.Provider, { value: {
48
35
  route: opts.route,
49
36
  } },
50
- React.createElement(RemoteComponent, { loader: opts.routeComponent, loadingComponent: opts.loadingComponent || DefaultLoading }))) }, props);
37
+ React.createElement(RemoteComponent, { loader: opts.routeComponent, loadingComponent: opts.loadingComponent || DefaultLoading }))),
38
+ ...props,
39
+ };
51
40
  }
52
41
  function DefaultLoading() {
53
42
  return React.createElement("div", null);
54
43
  }
55
44
  function RemoteComponent(props) {
56
- const Component = loadable(props.loader, {
57
- fallback: React.createElement(props.loadingComponent, null),
58
- });
59
- return React.createElement(Component, null);
45
+ const useSuspense = true; // !!React.startTransition;
46
+ if (useSuspense) {
47
+ const Component = props.loader;
48
+ return (React.createElement(React.Suspense, { fallback: React.createElement(props.loadingComponent, null) },
49
+ React.createElement(Component, null)));
50
+ }
51
+ else {
52
+ return null;
53
+ // // @ts-ignore
54
+ // import loadable from '@loadable/component';
55
+ // const Component = loadable(props.loader, {
56
+ // fallback: <props.loadingComponent />,
57
+ // });
58
+ // return <Component />;
59
+ }
60
60
  }
@@ -0,0 +1,12 @@
1
+ /// <reference types="react" />
2
+ import { IRouteComponents, IRoutesById } from './types';
3
+ export declare function getClientRootComponent(opts: {
4
+ routes: IRoutesById;
5
+ routeComponents: IRouteComponents;
6
+ pluginManager: any;
7
+ location: string;
8
+ loaderData: {
9
+ [routeKey: string]: any;
10
+ };
11
+ manifest: any;
12
+ }): Promise<JSX.Element>;
package/dist/server.js ADDED
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import { StaticRouter } from 'react-router-dom/server';
3
+ import { AppContext } from './appContext';
4
+ import { Routes } from './browser';
5
+ import { createClientRoutes } from './routes';
6
+ // Get the root React component for ReactDOMServer.renderToString
7
+ export async function getClientRootComponent(opts) {
8
+ const basename = '/';
9
+ const components = { ...opts.routeComponents };
10
+ const clientRoutes = createClientRoutes({
11
+ routesById: opts.routes,
12
+ routeComponents: components,
13
+ });
14
+ let rootContainer = (React.createElement(StaticRouter, { basename: basename, location: opts.location },
15
+ React.createElement(Routes, null)));
16
+ for (const key of [
17
+ // Lowest to the highest priority
18
+ 'innerProvider',
19
+ 'i18nProvider',
20
+ 'accessProvider',
21
+ 'dataflowProvider',
22
+ 'outerProvider',
23
+ 'rootContainer',
24
+ ]) {
25
+ rootContainer = opts.pluginManager.applyPlugins({
26
+ type: 'modify',
27
+ key: key,
28
+ initialValue: rootContainer,
29
+ args: {},
30
+ });
31
+ }
32
+ return (React.createElement(Html, { loaderData: opts.loaderData, manifest: opts.manifest },
33
+ React.createElement(AppContext.Provider, { value: {
34
+ routes: opts.routes,
35
+ routeComponents: opts.routeComponents,
36
+ clientRoutes,
37
+ pluginManager: opts.pluginManager,
38
+ basename,
39
+ clientLoaderData: {},
40
+ serverLoaderData: opts.loaderData,
41
+ } }, rootContainer)));
42
+ }
43
+ function Html({ children, loaderData, manifest }) {
44
+ // TODO: 处理 head 标签,比如 favicon.ico 的一致性
45
+ // TODO: root 支持配置
46
+ return (React.createElement("html", { lang: "en" },
47
+ React.createElement("head", null,
48
+ React.createElement("meta", { charSet: "utf-8" }),
49
+ React.createElement("meta", { name: "viewport", content: "width=device-width, initial-scale=1" }),
50
+ manifest.assets['umi.css'] && (React.createElement("link", { rel: "stylesheet", href: manifest.assets['umi.css'] }))),
51
+ React.createElement("body", null,
52
+ React.createElement("noscript", { dangerouslySetInnerHTML: {
53
+ __html: `<b>Enable JavaScript to run this app.</b>`,
54
+ } }),
55
+ React.createElement("div", { id: "root" }, children),
56
+ React.createElement("script", { dangerouslySetInnerHTML: {
57
+ __html: `window.__UMI_LOADER_DATA__ = ${JSON.stringify(loaderData)}`,
58
+ } }))));
59
+ }
package/dist/types.d.ts CHANGED
@@ -1,9 +1,22 @@
1
+ /// <reference types="react" />
1
2
  export interface IRoute {
2
3
  id: string;
3
4
  path?: string;
4
5
  index?: boolean;
5
6
  parentId?: string;
6
7
  redirect?: string;
8
+ clientLoader?: () => Promise<any>;
9
+ hasServerLoader?: boolean;
10
+ }
11
+ export interface IClientRoute {
12
+ id: string;
13
+ element: React.ReactNode;
14
+ children?: IClientRoute[];
15
+ routes?: IClientRoute[];
16
+ path?: string;
17
+ index?: boolean;
18
+ parentId?: string;
19
+ clientLoader?: () => Promise<any>;
7
20
  }
8
21
  export interface IRoutesById {
9
22
  [id: string]: IRoute;
@@ -11,3 +24,6 @@ export interface IRoutesById {
11
24
  export interface IRouteComponents {
12
25
  [id: string]: any;
13
26
  }
27
+ export interface ILoaderData {
28
+ [routeKey: string]: any;
29
+ }
@@ -0,0 +1,3 @@
1
+ export declare function __useFetcher(): {
2
+ load(path?: string): void;
3
+ };
@@ -0,0 +1,11 @@
1
+ import { useLocation } from 'react-router-dom';
2
+ import { useAppData } from './appContext';
3
+ export function __useFetcher() {
4
+ const { preloadRoute } = useAppData();
5
+ const location = useLocation();
6
+ return {
7
+ load(path) {
8
+ preloadRoute(path || location.pathname);
9
+ },
10
+ };
11
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umijs/renderer-react",
3
- "version": "4.0.0-rc.8",
3
+ "version": "4.0.1",
4
4
  "description": "@umijs/renderer-react",
5
5
  "homepage": "https://github.com/umijs/umi-next/tree/master/packages/renderer-react#readme",
6
6
  "bugs": "https://github.com/umijs/umi-next/issues",
@@ -17,17 +17,18 @@
17
17
  ],
18
18
  "scripts": {
19
19
  "build": "pnpm tsc",
20
- "build:deps": "pnpm esno ../../scripts/bundleDeps.ts",
21
- "dev": "pnpm build -- --watch"
20
+ "build:deps": "umi-scripts bundleDeps",
21
+ "dev": "pnpm build -- --watch",
22
+ "test": "umi-scripts jest-turbo"
22
23
  },
23
24
  "dependencies": {
24
25
  "@loadable/component": "5.15.2",
25
26
  "history": "5.3.0",
26
- "react-router-dom": "6.2.2"
27
+ "react-router-dom": "6.3.0"
27
28
  },
28
29
  "devDependencies": {
29
- "react": "17.0.2",
30
- "react-dom": "17.0.2"
30
+ "react": "18.1.0",
31
+ "react-dom": "18.1.0"
31
32
  },
32
33
  "peerDependencies": {
33
34
  "react": ">=16.8",