;
layout?: boolean;
-}
+ children?: RouteItem[];
+};
export type ComponentWithChildren = ComponentType>;
@@ -157,7 +159,7 @@ export type SetRender = (render: Renderer) => void;
export type AddWrapper = (wrapper: RouteWrapper, forLayout?: boolean) => void;
export interface RouteModules {
- [routeId: string]: RouteComponent;
+ [routeId: string]: ComponentModule;
}
export interface AssetsManifest {
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9353a2d7fc..1cae7ab5f5 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1021,7 +1021,7 @@ importers:
open: ^8.4.0
path-to-regexp: ^6.2.0
react: ^18.2.0
- react-router: ^6.9.0
+ react-router: 6.9.0
regenerator-runtime: ^0.13.0
resolve.exports: ^1.1.0
sass: ^1.50.0
@@ -1410,6 +1410,7 @@ importers:
packages/runtime:
specifiers:
'@ice/jsx-runtime': ^0.2.0
+ '@remix-run/router': 1.4.0
'@types/react': ^18.0.8
'@types/react-dom': ^18.0.3
ejs: ^3.1.6
@@ -1418,10 +1419,11 @@ importers:
htmlparser2: ^8.0.1
react: ^18.0.0
react-dom: ^18.0.0
- react-router-dom: ^6.9.0
+ react-router-dom: 6.9.0
regenerator-runtime: ^0.13.9
dependencies:
'@ice/jsx-runtime': link:../jsx-runtime
+ '@remix-run/router': 1.4.0
ejs: 3.1.8
fs-extra: 10.1.0
history: 5.3.0
From 89e9e74fd3a4ff0102741199dcd5492395eaf8f8 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Wed, 29 Mar 2023 14:52:00 +0800
Subject: [PATCH 03/38] refactor: render mode
---
.../ice/src/middlewares/renderMiddleware.ts | 1 -
packages/ice/src/routes.ts | 26 ++++---
packages/ice/src/service/ServerRunner.ts | 4 -
packages/ice/templates/core/routes.ts.ejs | 2 +-
.../miniapp-runtime/src/app/runClientApp.tsx | 22 +-----
packages/runtime/src/App.tsx | 20 +++++
packages/runtime/src/AppContext.tsx | 6 ++
packages/runtime/src/AppRouter.tsx | 34 --------
packages/runtime/src/AppWrapper.ts | 0
packages/runtime/src/ClientRouter.tsx | 30 +++++++
packages/runtime/src/Document.tsx | 6 +-
packages/runtime/src/RouteContext.ts | 2 -
packages/runtime/src/RouteWrapper.tsx | 3 -
packages/runtime/src/ServerRouter.tsx | 51 ++++++++++++
.../runtime/src/{AppData.tsx => appData.ts} | 14 ----
packages/runtime/src/index.ts | 10 +--
packages/runtime/src/routes.tsx | 9 +--
packages/runtime/src/runClientApp.tsx | 36 ++++-----
packages/runtime/src/runServerApp.tsx | 78 +++++--------------
packages/runtime/src/runtime.tsx | 7 +-
packages/runtime/src/types.ts | 11 ++-
21 files changed, 180 insertions(+), 192 deletions(-)
create mode 100644 packages/runtime/src/App.tsx
delete mode 100644 packages/runtime/src/AppRouter.tsx
create mode 100644 packages/runtime/src/AppWrapper.ts
create mode 100644 packages/runtime/src/ClientRouter.tsx
create mode 100644 packages/runtime/src/ServerRouter.tsx
rename packages/runtime/src/{AppData.tsx => appData.ts} (70%)
diff --git a/packages/ice/src/middlewares/renderMiddleware.ts b/packages/ice/src/middlewares/renderMiddleware.ts
index d9c46cc02c..9aa4d77244 100644
--- a/packages/ice/src/middlewares/renderMiddleware.ts
+++ b/packages/ice/src/middlewares/renderMiddleware.ts
@@ -65,7 +65,6 @@ export default function createRenderMiddleware(options: Options): Middleware {
req,
res,
};
-
serverModule.renderToResponse(requestContext, {
renderMode,
documentOnly,
diff --git a/packages/ice/src/routes.ts b/packages/ice/src/routes.ts
index 1a4c9f3730..bcf89f88c0 100644
--- a/packages/ice/src/routes.ts
+++ b/packages/ice/src/routes.ts
@@ -69,18 +69,22 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
const routeProperties: string[] = [
`path: '${formatPath(routePath || '')}',`,
`async lazy() {
- const componentModule = await ${loadStatement};
- const loader = createRouteLoader({
+ const componentModule = await ${loadStatement};
+ const loader = createRouteLoader({
+ routeId: '${id}',
+ requestContext,
+ renderMode,
+ module: componentModule,
+ });
+ return {
+ Component: () => wrapRouteComponent({
routeId: '${id}',
- requestContext,
- renderMode,
- module: componentModule,
- });
- return {
- Component: componentModule.default,
- loader,
- };
- },`,
+ isLayout: ${layout},
+ RouteComponent: componentModule.default,
+ }),
+ loader,
+ };
+ },`,
`componentName: '${componentName}',`,
`index: ${index},`,
`id: '${id}',`,
diff --git a/packages/ice/src/service/ServerRunner.ts b/packages/ice/src/service/ServerRunner.ts
index b809aa60a0..7ec68bf824 100644
--- a/packages/ice/src/service/ServerRunner.ts
+++ b/packages/ice/src/service/ServerRunner.ts
@@ -269,10 +269,6 @@ class ServerRunner extends Runner {
return { externalize: bundlePath };
}
- if (id.includes('runtimeModule')) {
- console.log('code ===>', await transformJsxRuntime(code));
- }
-
return {
code: await transformJsxRuntime(code),
};
diff --git a/packages/ice/templates/core/routes.ts.ejs b/packages/ice/templates/core/routes.ts.ejs
index cbba09c485..5286a65fdd 100644
--- a/packages/ice/templates/core/routes.ts.ejs
+++ b/packages/ice/templates/core/routes.ts.ejs
@@ -1,4 +1,4 @@
-import { createRouteLoader } from '@ice/runtime';
+import { createRouteLoader, wrapRouteComponent } from '@ice/runtime';
<%- routeImports.length ? routeImports.join('\n') + '\n\n' : ''; -%>
export default ({
requestContext,
diff --git a/packages/miniapp-runtime/src/app/runClientApp.tsx b/packages/miniapp-runtime/src/app/runClientApp.tsx
index 4248e4eee2..3954dc3ec0 100644
--- a/packages/miniapp-runtime/src/app/runClientApp.tsx
+++ b/packages/miniapp-runtime/src/app/runClientApp.tsx
@@ -2,7 +2,7 @@ import React from 'react';
import type {
AppContext, RunClientAppOptions,
} from '@ice/runtime';
-import { AppContextProvider, AppDataProvider, getAppData, getAppConfig, Runtime } from '@ice/runtime';
+import { AppContextProvider, getAppData, getAppConfig, Runtime } from '@ice/runtime';
import { eventCenter } from '../emitter/emitter.js';
import { APP_READY } from '../constants/index.js';
import App from './App.js';
@@ -48,24 +48,10 @@ async function render(
render(
document.getElementById('ice-container'),
-
+
-
+
- ,
- );
-}
-
-interface BrowserEntryProps {
- appContext: AppContext;
-}
-
-function BrowserEntry({
- appContext,
-}: BrowserEntryProps) {
- return (
-
-
-
+ ,
);
}
diff --git a/packages/runtime/src/App.tsx b/packages/runtime/src/App.tsx
new file mode 100644
index 0000000000..823c27031d
--- /dev/null
+++ b/packages/runtime/src/App.tsx
@@ -0,0 +1,20 @@
+
+import React from 'react';
+import AppErrorBoundary from './AppErrorBoundary.js';
+import { useAppContext } from './AppContext.js';
+
+export default function App({ children }) {
+ const { appConfig } = useAppContext();
+ const { strict, errorBoundary } = appConfig.app;
+ const StrictMode = strict ? React.StrictMode : React.Fragment;
+
+ const ErrorBoundary = errorBoundary ? AppErrorBoundary : React.Fragment;
+
+ return (
+
+
+ {children}
+
+
+ );
+}
diff --git a/packages/runtime/src/AppContext.tsx b/packages/runtime/src/AppContext.tsx
index aa09b2537b..ff05e7fdbf 100644
--- a/packages/runtime/src/AppContext.tsx
+++ b/packages/runtime/src/AppContext.tsx
@@ -10,9 +10,15 @@ function useAppContext() {
return value;
}
+function useAppData() {
+ const value = React.useContext(Context);
+ return value.appData;
+}
+
const AppContextProvider = Context.Provider;
export {
useAppContext,
+ useAppData,
AppContextProvider,
};
diff --git a/packages/runtime/src/AppRouter.tsx b/packages/runtime/src/AppRouter.tsx
deleted file mode 100644
index 070009e457..0000000000
--- a/packages/runtime/src/AppRouter.tsx
+++ /dev/null
@@ -1,34 +0,0 @@
-import * as React from 'react';
-import type { RouteObject } from 'react-router-dom';
-import { Router, useRoutes } from 'react-router-dom';
-import type { AppRouterProps } from './types.js';
-import { Router as RouterSingle, useRoutes as useRoutesSingle } from './single-router.js';
-
-const AppRouter: React.ComponentType = (props) => {
- const { action, location, navigator, static: staticProps, routes, basename } = props;
- const IceRouter = process.env.ICE_CORE_ROUTER === 'true' ? Router : RouterSingle;
-
- return (
-
-
-
- );
-};
-
-interface RoutesProps {
- routes: RouteObject[];
-}
-
-function Routes({ routes }: RoutesProps) {
- const useIceRoutes = process.env.ICE_CORE_ROUTER === 'true' ? useRoutes : useRoutesSingle;
- const element = useIceRoutes(routes);
- return element;
-}
-
-export default AppRouter;
diff --git a/packages/runtime/src/AppWrapper.ts b/packages/runtime/src/AppWrapper.ts
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
new file mode 100644
index 0000000000..cc13d3c344
--- /dev/null
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -0,0 +1,30 @@
+import React from 'react';
+import { RouterProvider } from 'react-router-dom';
+import { createRouter } from '@remix-run/router';
+import type { AppRouterProps } from './types.js';
+import App from './App.js';
+import { Router } from './single-router.js';
+
+function ClientRouter(props: AppRouterProps) {
+ const { routes, history, loaderData } = props;
+ let element: React.ReactNode;
+ if (process.env.ICE_CORE_ROUTER === 'true') {
+ const routerOptions = {
+ routes,
+ history,
+ hydrationData: loaderData ? { loaderData } : undefined,
+ };
+ const router = createRouter(routerOptions).initialize();
+ element = ;
+ } else {
+ element = ;
+ }
+
+ return (
+
+ {element}
+
+ );
+}
+
+export default ClientRouter;
diff --git a/packages/runtime/src/Document.tsx b/packages/runtime/src/Document.tsx
index b4736c0c36..0d32fa969a 100644
--- a/packages/runtime/src/Document.tsx
+++ b/packages/runtime/src/Document.tsx
@@ -1,7 +1,6 @@
import * as React from 'react';
import type { WindowContext, RouteMatch, AssetsManifest } from './types.js';
-import { useAppContext } from './AppContext.js';
-import { useAppData } from './AppData.js';
+import { useAppContext, useAppData } from './AppContext.js';
import { getMeta, getTitle, getLinks, getScripts } from './routesConfig.js';
import getCurrentRoutePath from './utils/getCurrentRoutePath.js';
@@ -105,13 +104,10 @@ export const Scripts: ScriptsType = (props: ScriptsProps) => {
} = props;
const routeScripts = getScripts(matches, loaderData);
- console.log('document matches', matches);
- console.log('document loaderData', loaderData);
const pageAssets = getPageAssets(matches, assetsManifest);
const entryAssets = getEntryAssets(assetsManifest);
// Page assets need to be load before entry assets, so when call dynamic import won't cause duplicate js chunk loaded.
let scripts = pageAssets.concat(entryAssets).filter(path => path.indexOf('.js') > -1);
- console.log('scripts ==>', assetsManifest, scripts);
if (assetsManifest.dataLoader) {
scripts.unshift(`${assetsManifest.publicPath}${assetsManifest.dataLoader}`);
}
diff --git a/packages/runtime/src/RouteContext.ts b/packages/runtime/src/RouteContext.ts
index c082f4922d..6f0c9f3e48 100644
--- a/packages/runtime/src/RouteContext.ts
+++ b/packages/runtime/src/RouteContext.ts
@@ -7,7 +7,6 @@ DataContext.displayName = 'Data';
function useData(): T {
const data = useLoaderData();
- console.log('data ==>', data);
return (data as any).data;
}
const DataProvider = DataContext.Provider;
@@ -17,7 +16,6 @@ ConfigContext.displayName = 'Config';
function useConfig(): RouteConfig {
const data = useLoaderData();
- console.log('pageConfig ==>', data);
return (data as any).pageConfig;
}
const ConfigProvider = ConfigContext.Provider;
diff --git a/packages/runtime/src/RouteWrapper.tsx b/packages/runtime/src/RouteWrapper.tsx
index 07cfaf2755..1020c7bf1d 100644
--- a/packages/runtime/src/RouteWrapper.tsx
+++ b/packages/runtime/src/RouteWrapper.tsx
@@ -1,7 +1,5 @@
import * as React from 'react';
import type { RouteWrapperConfig } from './types.js';
-import { useAppContext } from './AppContext.js';
-import { DataProvider, ConfigProvider } from './RouteContext.js';
interface Props {
id: string;
@@ -12,7 +10,6 @@ interface Props {
export default function RouteWrapper(props: Props) {
const { wrappers = [], id, isLayout } = props;
- const { loaderData } = useAppContext();
// layout should only be wrapped by Wrapper with `layout: true`
const filtered = isLayout ? wrappers.filter(wrapper => wrapper.layout === true) : wrappers;
const RouteWrappers = filtered.map(item => item.Wrapper);
diff --git a/packages/runtime/src/ServerRouter.tsx b/packages/runtime/src/ServerRouter.tsx
new file mode 100644
index 0000000000..fc4abeaf0c
--- /dev/null
+++ b/packages/runtime/src/ServerRouter.tsx
@@ -0,0 +1,51 @@
+import React from 'react';
+import { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';
+import type { AppRouterProps } from './types.js';
+import { RouteItem } from './types.js';
+import { useAppContext } from './AppContext.js';
+import App from './App.js';
+import { RouteComponent } from './routes.js';
+
+
+function createServerRoutes(routes: AppRouterProps['routes']) {
+ return routes.map((route) => {
+ let dataRoute = {
+ // Static Router need element or Component when matched.
+ element: ,
+ id: route.id,
+ index: route.index,
+ path: route.path,
+ children: null,
+ };
+
+ if (route?.children?.length > 0) {
+ let children = createServerRoutes(
+ route.children,
+ );
+ dataRoute.children = children;
+ }
+ return dataRoute;
+ });
+}
+
+function ServerRouter(props: AppRouterProps) {
+ const { routes, loaderData, location } = props;
+ const { matches, basename } = useAppContext();
+
+ const routerContext = {
+ matches, basename, loaderData, location,
+ };
+
+ const router = createStaticRouter(createServerRoutes(routes), routerContext);
+
+ return (
+
+
+
+ );
+}
+
+export default ServerRouter;
diff --git a/packages/runtime/src/AppData.tsx b/packages/runtime/src/appData.ts
similarity index 70%
rename from packages/runtime/src/AppData.tsx
rename to packages/runtime/src/appData.ts
index b90fd84d68..351b3a6ade 100644
--- a/packages/runtime/src/AppData.tsx
+++ b/packages/runtime/src/appData.ts
@@ -1,18 +1,6 @@
-import * as React from 'react';
import type { AppExport, AppData, RequestContext } from './types.js';
import { callDataLoader } from './dataLoader.js';
-const Context = React.createContext(undefined);
-
-Context.displayName = 'AppDataContext';
-
-function useAppData (): T {
- const value = React.useContext(Context);
- return value;
-}
-
-const AppDataProvider = Context.Provider;
-
/**
* Call the getData of app config.
*/
@@ -37,6 +25,4 @@ async function getAppData(appExport: AppExport, requestContext?: RequestContext)
export {
getAppData,
- useAppData,
- AppDataProvider,
};
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index a5ddf4a139..fd19e53765 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -22,8 +22,8 @@ import type {
import Runtime from './runtime.js';
import runClientApp from './runClientApp.js';
import type { RunClientAppOptions } from './runClientApp.js';
-import { useAppContext, AppContextProvider } from './AppContext.js';
-import { useAppData, AppDataProvider, getAppData } from './AppData.js';
+import { useAppContext, useAppData, AppContextProvider } from './AppContext.js';
+import { getAppData } from './appData.js';
import { useData, useConfig, DataProvider, ConfigProvider } from './RouteContext.js';
import {
Meta,
@@ -43,7 +43,6 @@ import type {
} from './Document.js';
import dataLoader, { defineDataLoader, defineServerDataLoader, defineStaticDataLoader, callDataLoader } from './dataLoader.js';
import getRequestContext from './requestContext.js';
-import AppRouter from './AppRouter.js';
import AppErrorBoundary from './AppErrorBoundary.js';
import getAppConfig, { defineAppConfig } from './appConfig.js';
import { routerHistory as history } from './history.js';
@@ -51,7 +50,7 @@ import KeepAliveOutlet from './KeepAliveOutlet.js';
import ClientOnly from './ClientOnly.js';
import useMounted from './useMounted.js';
import { withSuspense, useSuspenseData } from './Suspense.js';
-import { createRouteLoader } from './routes.js';
+import { createRouteLoader, wrapRouteComponent } from './routes.js';
export {
getAppConfig,
@@ -60,7 +59,6 @@ export {
runClientApp,
AppContextProvider,
useAppContext,
- AppDataProvider,
useAppData,
useData,
getAppData,
@@ -89,7 +87,6 @@ export {
history,
KeepAliveOutlet,
- AppRouter,
AppErrorBoundary,
ClientOnly,
useMounted,
@@ -98,6 +95,7 @@ export {
useSuspenseData,
createRouteLoader,
+ wrapRouteComponent,
};
export type {
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 555a3d9971..2b82e323de 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -56,9 +56,9 @@ export function wrapRouteComponent(options: {
routeId: string;
isLayout?: boolean;
RouteComponent: React.ComponentType;
- RouteWrappers?: RouteWrapperConfig[];
}) {
- const { routeId, isLayout, RouteComponent, RouteWrappers } = options;
+ const { routeId, isLayout, RouteComponent } = options;
+ const { RouteWrappers } = useAppContext();
return (
@@ -69,7 +69,6 @@ export function wrapRouteComponent(options: {
export function RouteComponent({ id }: { id: string }) {
// get current route component from latest routeModules
const { routeModules } = useAppContext();
- console.log('routeModules[id]', routeModules[id]);
const { Component } = routeModules[id] || {};
if (process.env.NODE_ENV === 'development') {
if (!Component) {
@@ -101,7 +100,6 @@ export function createRouteLoader(options: RouteLoaderOptions): () => Promise {
const { dataLoader, pageConfig, staticDataLoader, serverDataLoader } = options.module;
const { requestContext, renderMode, routeId } = options;
-
const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__;
const globalLoader = hasGlobalLoader ? (window as any).__ICE_DATA_LOADER__ : null;
let routeData: any;
@@ -120,9 +118,10 @@ export function createRouteLoader(options: RouteLoaderOptions): () => Promise', loaderData);
// CSR and load next route data.
if (typeof window !== 'undefined') {
- // await updateRoutesConfig(loaderData);
+ await updateRoutesConfig(loaderData);
}
return loaderData;
};
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index 6d802eaf4c..72896e43dc 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -1,7 +1,6 @@
import React from 'react';
import * as ReactDOM from 'react-dom/client';
-import { RouterProvider } from 'react-router-dom';
-import { createRouter, createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
+import { createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
import type { History } from '@remix-run/router';
import type {
AppContext, WindowContext, AppExport, RouteItem, RuntimeModules, AppConfig, AssetsManifest,
@@ -9,14 +8,15 @@ import type {
import { createHistory as createHistorySingle } from './single-router.js';
import { setHistory } from './history.js';
import Runtime from './runtime.js';
-import { AppDataProvider, getAppData } from './AppData.js';
+import { getAppData } from './appData.js';
import { getRoutesPath } from './routes.js';
import type { RouteLoaderOptions } from './routes.js';
import getRequestContext from './requestContext.js';
import getAppConfig from './appConfig.js';
-import DefaultAppRouter from './AppRouter.js';
+import ClientRouter from './ClientRouter.js';
import { setFetcher } from './dataLoader.js';
import addLeadingSlash from './utils/addLeadingSlash.js';
+import { AppContextProvider } from './AppContext.js';
export interface RunClientAppOptions {
app: AppExport;
@@ -83,7 +83,7 @@ export default async function runClientApp(options: RunClientAppOptions) {
};
const runtime = new Runtime(appContext, runtimeOptions);
- runtime.setAppRouter(DefaultAppRouter);
+ runtime.setAppRouter(ClientRouter);
// Load static module before getAppData,
// so we can call request in in getAppData which provide by `plugin-request`.
if (runtimeModules.statics) {
@@ -96,7 +96,8 @@ export default async function runClientApp(options: RunClientAppOptions) {
appData = await getAppData(app, requestContext);
}
- if (hydrate && !downgrade && !documentOnly) {
+ const needHydrate = hydrate && !downgrade && !documentOnly;
+ if (needHydrate) {
runtime.setRender((container, element) => {
return ReactDOM.hydrateRoot(container, element);
});
@@ -107,21 +108,21 @@ export default async function runClientApp(options: RunClientAppOptions) {
await Promise.all(runtimeModules.commons.map(m => runtime.loadModule(m)).filter(Boolean));
}
- return render({ runtime, history });
+ return render({ runtime, history, needHydrate });
}
interface RenderOptions {
history: History;
runtime: Runtime;
+ needHydrate: boolean;
}
-async function render({ history, runtime }: RenderOptions) {
+async function render({ history, runtime, needHydrate }: RenderOptions) {
const appContext = runtime.getAppContext();
- const { appConfig, appData, routes, loaderData } = appContext;
+ const { appConfig, loaderData, routes } = appContext;
const appRender = runtime.getRender();
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
- // const RouteWrappers = runtime.getWrappers();
- // const AppRouter = runtime.getAppRouter();
+ const AppRouter = runtime.getAppRouter();
const rootId = appConfig.app.rootId || 'app';
let root = document.getElementById(rootId);
@@ -131,20 +132,13 @@ async function render({ history, runtime }: RenderOptions) {
document.body.appendChild(root);
console.warn(`Root node #${rootId} is not found, current root is automatically created by the framework.`);
}
-
- const router = createRouter({
- routes,
- history,
- hydrationData: { loaderData },
- }).initialize();
-
return appRender(
root,
-
+
- Loading...
} />
+
- ,
+ ,
);
}
diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx
index 4bf874dc1f..b87a44cd55 100644
--- a/packages/runtime/src/runServerApp.tsx
+++ b/packages/runtime/src/runServerApp.tsx
@@ -3,7 +3,6 @@ import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { parsePath } from 'react-router-dom';
import type { Location } from 'history';
-import { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';
import type {
AppContext, RouteItem, ServerContext,
AppExport, AssetsManifest,
@@ -16,18 +15,17 @@ import type {
} from './types.js';
import Runtime from './runtime.js';
import { AppContextProvider } from './AppContext.js';
-import { AppDataProvider, getAppData } from './AppData.js';
+import { getAppData } from './appData.js';
import getAppConfig from './appConfig.js';
import { DocumentContextProvider } from './Document.js';
-import { loadRouteModules, RouteComponent } from './routes.js';
+import { loadRouteModules } from './routes.js';
import type { RouteLoaderOptions } from './routes.js';
import { pipeToString, renderToNodeStream } from './server/streamRender.js';
-import { createStaticNavigator } from './server/navigator.js';
import type { NodeWritablePiper } from './server/streamRender.js';
import getRequestContext from './requestContext.js';
import matchRoutes from './matchRoutes.js';
import getCurrentRoutePath from './utils/getCurrentRoutePath.js';
-import DefaultAppRouter from './AppRouter.js';
+import ServerRouter from './ServerRouter.js';
import { renderHTMLToJS } from './renderHTMLToJS.js';
import addLeadingSlash from './utils/addLeadingSlash.js';
@@ -69,6 +67,7 @@ export async function renderToEntry(
requestContext: ServerContext,
renderOptions: RenderOptions,
) {
+ console.log('renderToEntry.tsx');
const result = await renderToHTML(requestContext, renderOptions);
const { value } = result;
@@ -98,6 +97,7 @@ export async function renderToHTML(
requestContext: ServerContext,
renderOptions: RenderOptions,
): Promise {
+ console.log('renderHTMLToJS.tsx');
const result = await doRender(requestContext, renderOptions);
const { value } = result;
@@ -220,7 +220,7 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
serverData,
};
const runtime = new Runtime(appContext, runtimeOptions);
- runtime.setAppRouter(DefaultAppRouter);
+ runtime.setAppRouter(ServerRouter);
// Load static module before getAppData.
if (runtimeModules.statics) {
await Promise.all(runtimeModules.statics.map(m => runtime.loadModule(m)).filter(Boolean));
@@ -294,28 +294,6 @@ interface RenderServerEntry {
location: Location;
renderOptions: RenderOptions;
}
-
-function createServerRoutes(routes, routeModules) {
- return routes.map((route) => {
- let dataRoute = {
- element: ,
- id: route.id,
- index: route.index,
- path: route.path,
- };
-
- if (route?.children?.length > 0) {
- let children = createServerRoutes(
- routes.children,
- routeModules,
- );
- // @ts-ignore
- dataRoute.children = children;
- }
- return dataRoute;
- });
-}
-
/**
* Render App by SSR.
*/
@@ -329,43 +307,22 @@ async function renderServerEntry(
): Promise {
const { Document } = renderOptions;
const appContext = runtime.getAppContext();
- // @ts-ignore
- const { appData, routePath, routeModules, loaderData } = appContext;
- const staticNavigator = createStaticNavigator();
+ const { routes, routePath, loaderData } = appContext;
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
- const RouteWrappers = runtime.getWrappers();
const AppRouter = runtime.getAppRouter();
- let routes = createServerRoutes(
- appContext.routes,
- routeModules,
- );
- const context = {
- matches,
- basemane: appContext.basename,
- location,
- loaderData,
- };
- const router = createStaticRouter(routes, context);
const documentContext = {
main: (
-
+
),
};
-
const element = (
-
+
-
-
-
-
-
+
+
+
-
+
);
const pipe = renderToNodeStream(element);
@@ -411,23 +368,24 @@ function renderDocument(options: RenderDocumentOptions): RenderResult {
serverData,
} = renderOptions;
- const routesData = null;
const appData = null;
const appConfig = getAppConfig(app);
- const matchedRoutesConfig = {};
+ const loaderData = {};
matches.forEach(async (match) => {
const { id } = match.route;
const pageConfig = routesConfig[id];
- matchedRoutesConfig[id] = pageConfig ? pageConfig({}) : {};
+ loaderData[id] = {
+ pageConfig: pageConfig ? pageConfig({}) : {},
+ };
});
const appContext: AppContext = {
assetsManifest,
appConfig,
appData,
- loaderData: {},
+ loaderData,
matches,
routes,
documentOnly: true,
diff --git a/packages/runtime/src/runtime.tsx b/packages/runtime/src/runtime.tsx
index 95e1809691..a5b24aaeab 100644
--- a/packages/runtime/src/runtime.tsx
+++ b/packages/runtime/src/runtime.tsx
@@ -44,7 +44,12 @@ class Runtime {
this.runtimeOptions = runtimeOptions;
}
- public getAppContext = () => this.appContext;
+ public getAppContext = () => {
+ return {
+ ...this.appContext,
+ RouteWrappers: this.RouteWrappers,
+ };
+ };
public setAppContext = (appContext: AppContext) => {
this.appContext = appContext;
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index 76bd710495..ae0403573a 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -1,5 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'http';
-import type { Action, InitialEntry, Location, AgnosticRouteObject } from '@remix-run/router';
+import type { InitialEntry, History, Location, AgnosticRouteObject } from '@remix-run/router';
import type { ComponentType, PropsWithChildren } from 'react';
import type { HydrationOptions, Root } from 'react-dom/client';
import type { Navigator, Params, RouteObject } from 'react-router-dom';
@@ -88,6 +88,7 @@ export interface AppContext {
assetsManifest?: AssetsManifest;
loaderData?: LoaderDatas;
routeModules?: RouteModules;
+ RouteWrappers?: RouteWrapperConfig[];
routePath?: string;
matches?: RouteMatch[];
routes?: RouteItem[];
@@ -218,12 +219,10 @@ export interface RuntimeModules {
}
export interface AppRouterProps {
- action: Action;
- location: Location;
- navigator: Navigator;
+ history?: History;
routes: RouteObject[];
- static?: boolean;
- basename?: string;
+ loaderData?: LoaderDatas;
+ location?: Location;
}
export interface AppRouteProps {
From 3787d3453a140872425b6bd41eae50cf43f34e50 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Wed, 29 Mar 2023 19:34:25 +0800
Subject: [PATCH 04/38] fix: code splitting false
---
packages/ice/src/routes.ts | 29 ++++++------
packages/ice/templates/core/routes.ts.ejs | 2 +-
.../miniapp-runtime/src/app/runClientApp.tsx | 1 -
packages/runtime/src/App.tsx | 1 -
packages/runtime/src/ClientRouter.tsx | 14 ++----
packages/runtime/src/Document.tsx | 3 +-
.../src/{AppWrapper.ts => RenderWrapper.ts} | 0
packages/runtime/src/ServerRouter.tsx | 35 +--------------
packages/runtime/src/dataLoader.ts | 3 +-
packages/runtime/src/index.ts | 4 +-
packages/runtime/src/routes.tsx | 13 +++---
packages/runtime/src/runClientApp.tsx | 42 ++++++++++++++---
packages/runtime/src/runServerApp.tsx | 45 ++++++++++++++++---
packages/runtime/src/types.ts | 16 +++----
14 files changed, 115 insertions(+), 93 deletions(-)
rename packages/runtime/src/{AppWrapper.ts => RenderWrapper.ts} (100%)
diff --git a/packages/ice/src/routes.ts b/packages/ice/src/routes.ts
index bcf89f88c0..a4a5ff16aa 100644
--- a/packages/ice/src/routes.ts
+++ b/packages/ice/src/routes.ts
@@ -66,23 +66,24 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
routeImports.push(`import * as ${routeSpecifier} from '${formatPath(componentPath)}';`);
loadStatement = routeSpecifier;
}
+ const component = `Component: () => WrapRouteComponent({
+ routeId: '${id}',
+ isLayout: ${layout},
+ RouteComponent: ${lazy ? 'componentModule' : loadStatement}.default,
+ })`;
+ const loader = `loader: createRouteLoader({
+ routeId: '${id}',
+ requestContext,
+ renderMode,
+ module: ${lazy ? 'componentModule' : loadStatement},
+ })`;
const routeProperties: string[] = [
`path: '${formatPath(routePath || '')}',`,
`async lazy() {
- const componentModule = await ${loadStatement};
- const loader = createRouteLoader({
- routeId: '${id}',
- requestContext,
- renderMode,
- module: componentModule,
- });
+ ${lazy ? `const componentModule = await ${loadStatement}` : ''};
return {
- Component: () => wrapRouteComponent({
- routeId: '${id}',
- isLayout: ${layout},
- RouteComponent: componentModule.default,
- }),
- loader,
+ ${component},
+ ${loader},
};
},`,
`componentName: '${componentName}',`,
@@ -90,7 +91,7 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
`id: '${id}',`,
'exact: true,',
`exports: ${JSON.stringify(exports)},`,
- ];
+ ].filter(Boolean);
if (layout) {
routeProperties.push('layout: true,');
diff --git a/packages/ice/templates/core/routes.ts.ejs b/packages/ice/templates/core/routes.ts.ejs
index 5286a65fdd..3e1f7cec9e 100644
--- a/packages/ice/templates/core/routes.ts.ejs
+++ b/packages/ice/templates/core/routes.ts.ejs
@@ -1,4 +1,4 @@
-import { createRouteLoader, wrapRouteComponent } from '@ice/runtime';
+import { createRouteLoader, WrapRouteComponent } from '@ice/runtime';
<%- routeImports.length ? routeImports.join('\n') + '\n\n' : ''; -%>
export default ({
requestContext,
diff --git a/packages/miniapp-runtime/src/app/runClientApp.tsx b/packages/miniapp-runtime/src/app/runClientApp.tsx
index 3954dc3ec0..9a0884039b 100644
--- a/packages/miniapp-runtime/src/app/runClientApp.tsx
+++ b/packages/miniapp-runtime/src/app/runClientApp.tsx
@@ -42,7 +42,6 @@ async function render(
runtime: Runtime,
) {
const appContext = runtime.getAppContext();
- const { appData } = appContext;
const render = runtime.getRender();
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
diff --git a/packages/runtime/src/App.tsx b/packages/runtime/src/App.tsx
index 823c27031d..effd7ef575 100644
--- a/packages/runtime/src/App.tsx
+++ b/packages/runtime/src/App.tsx
@@ -7,7 +7,6 @@ export default function App({ children }) {
const { appConfig } = useAppContext();
const { strict, errorBoundary } = appConfig.app;
const StrictMode = strict ? React.StrictMode : React.Fragment;
-
const ErrorBoundary = errorBoundary ? AppErrorBoundary : React.Fragment;
return (
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index cc13d3c344..04e8bee92f 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -1,25 +1,19 @@
import React from 'react';
import { RouterProvider } from 'react-router-dom';
-import { createRouter } from '@remix-run/router';
+
import type { AppRouterProps } from './types.js';
import App from './App.js';
import { Router } from './single-router.js';
function ClientRouter(props: AppRouterProps) {
- const { routes, history, loaderData } = props;
+ const { router, routes } = props;
+
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
- const routerOptions = {
- routes,
- history,
- hydrationData: loaderData ? { loaderData } : undefined,
- };
- const router = createRouter(routerOptions).initialize();
- element = ;
+ element = loading>} />;
} else {
element = ;
}
-
return (
{element}
diff --git a/packages/runtime/src/Document.tsx b/packages/runtime/src/Document.tsx
index 0d32fa969a..299d219729 100644
--- a/packages/runtime/src/Document.tsx
+++ b/packages/runtime/src/Document.tsx
@@ -145,7 +145,7 @@ export type DataType = (props: DataProps) => JSX.Element;
// use app context separately
export const Data: DataType = (props: DataProps) => {
- const { documentOnly, matches, downgrade, renderMode, serverData, loaderData } = useAppContext();
+ const { documentOnly, matches, downgrade, renderMode, serverData, loaderData, revalidate } = useAppContext();
const appData = useAppData();
const {
ScriptElement = 'script',
@@ -162,6 +162,7 @@ export const Data: DataType = (props: DataProps) => {
documentOnly,
renderMode,
serverData,
+ revalidate,
};
return (
diff --git a/packages/runtime/src/AppWrapper.ts b/packages/runtime/src/RenderWrapper.ts
similarity index 100%
rename from packages/runtime/src/AppWrapper.ts
rename to packages/runtime/src/RenderWrapper.ts
diff --git a/packages/runtime/src/ServerRouter.tsx b/packages/runtime/src/ServerRouter.tsx
index fc4abeaf0c..dad0dea0fa 100644
--- a/packages/runtime/src/ServerRouter.tsx
+++ b/packages/runtime/src/ServerRouter.tsx
@@ -1,42 +1,11 @@
import React from 'react';
-import { createStaticRouter, StaticRouterProvider } from 'react-router-dom/server.mjs';
+import { StaticRouterProvider } from 'react-router-dom/server.mjs';
import type { AppRouterProps } from './types.js';
-import { RouteItem } from './types.js';
-import { useAppContext } from './AppContext.js';
import App from './App.js';
-import { RouteComponent } from './routes.js';
-function createServerRoutes(routes: AppRouterProps['routes']) {
- return routes.map((route) => {
- let dataRoute = {
- // Static Router need element or Component when matched.
- element: ,
- id: route.id,
- index: route.index,
- path: route.path,
- children: null,
- };
-
- if (route?.children?.length > 0) {
- let children = createServerRoutes(
- route.children,
- );
- dataRoute.children = children;
- }
- return dataRoute;
- });
-}
-
function ServerRouter(props: AppRouterProps) {
- const { routes, loaderData, location } = props;
- const { matches, basename } = useAppContext();
-
- const routerContext = {
- matches, basename, loaderData, location,
- };
-
- const router = createStaticRouter(createServerRoutes(routes), routerContext);
+ const { router, routerContext } = props;
return (
diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts
index c2632f6dfe..dcb80936be 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -1,6 +1,5 @@
-import type { DataLoaderConfig, DataLoaderResult, RuntimeModules, AppExport, StaticRuntimePlugin, CommonJsRuntime, StaticDataLoader } from './types.js';
+import type { RequestContext, RenderMode, DataLoaderConfig, DataLoaderResult, RuntimeModules, AppExport, StaticRuntimePlugin, CommonJsRuntime, StaticDataLoader } from './types.js';
import getRequestContext from './requestContext.js';
-import type { RequestContext, RenderMode } from './types.js';
interface Loaders {
[routeId: string]: DataLoaderConfig;
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index fd19e53765..48a5812db2 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -50,7 +50,7 @@ import KeepAliveOutlet from './KeepAliveOutlet.js';
import ClientOnly from './ClientOnly.js';
import useMounted from './useMounted.js';
import { withSuspense, useSuspenseData } from './Suspense.js';
-import { createRouteLoader, wrapRouteComponent } from './routes.js';
+import { createRouteLoader, WrapRouteComponent } from './routes.js';
export {
getAppConfig,
@@ -95,7 +95,7 @@ export {
useSuspenseData,
createRouteLoader,
- wrapRouteComponent,
+ WrapRouteComponent,
};
export type {
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 2b82e323de..3dd8f3f85f 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,12 +1,11 @@
import React from 'react';
-import type { RouteItem, RouteModules, RouteWrapperConfig, RenderMode, DataLoaderConfig } from './types.js';
+import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
import { callDataLoader } from './dataLoader.js';
-import type { RequestContext, ComponentModule } from './types.js';
import { updateRoutesConfig } from './routesConfig.js';
-type RouteModule = Pick;
+type RouteModule = Pick;
export function getRoutesPath(routes: RouteItem[], parentPath = ''): string[] {
let paths = [];
@@ -23,7 +22,7 @@ export function getRoutesPath(routes: RouteItem[], parentPath = ''): string[] {
}
export async function loadRouteModule(route: RouteModule, routeModulesCache = {}) {
- const { id, load, lazy } = route;
+ const { id, lazy } = route;
if (
typeof window !== 'undefined' && // Don't use module cache and should load again in ssr. Ref: https://github.com/ice-lab/ice-next/issues/82
id in routeModulesCache
@@ -32,8 +31,7 @@ export async function loadRouteModule(route: RouteModule, routeModulesCache = {}
}
try {
- // Function load will return route module when lazy loaded is disabled.
- const routeModule = lazy ? await lazy() : await load();
+ const routeModule = await lazy();
routeModulesCache[id] = routeModule;
return routeModule;
} catch (error) {
@@ -52,7 +50,7 @@ export async function loadRouteModules(routes: RouteModule[], originRouteModules
}
// Wrap route component with runtime wrappers.
-export function wrapRouteComponent(options: {
+export function WrapRouteComponent(options: {
routeId: string;
isLayout?: boolean;
RouteComponent: React.ComponentType;
@@ -118,7 +116,6 @@ export function createRouteLoader(options: RouteLoaderOptions): () => Promise', loaderData);
// CSR and load next route data.
if (typeof window !== 'undefined') {
await updateRoutesConfig(loaderData);
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index 72896e43dc..c015ce7956 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import * as ReactDOM from 'react-dom/client';
-import { createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
+import { createRouter, createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
import type { History } from '@remix-run/router';
import type {
AppContext, WindowContext, AppExport, RouteItem, RuntimeModules, AppConfig, AssetsManifest,
@@ -51,13 +51,14 @@ export default async function runClientApp(options: RunClientAppOptions) {
documentOnly,
renderMode,
serverData,
+ revalidate,
} = windowContext;
const formattedBasename = addLeadingSlash(basename);
const requestContext = getRequestContext(window.location);
const appConfig = getAppConfig(app);
const routes = createRoutes ? createRoutes({
requestContext,
- renderMode,
+ renderMode: 'CSR',
}) : [];
const historyOptions = {
memoryRouter,
@@ -80,6 +81,7 @@ export default async function runClientApp(options: RunClientAppOptions) {
renderMode,
requestContext,
serverData,
+ revalidate,
};
const runtime = new Runtime(appContext, runtimeOptions);
@@ -98,8 +100,21 @@ export default async function runClientApp(options: RunClientAppOptions) {
const needHydrate = hydrate && !downgrade && !documentOnly;
if (needHydrate) {
+ const defaultOnRecoverableError = typeof reportError === 'function' ? reportError
+ : function (error: unknown) {
+ console['error'](error);
+ };
runtime.setRender((container, element) => {
- return ReactDOM.hydrateRoot(container, element);
+ const hydrateOptions = revalidate
+ ? {
+ onRecoverableError(error: unknown) {
+ // Ignore this error caused by router.revalidate
+ if ((error as Error)?.message?.indexOf('This Suspense boundary received an update before it finished hydrating.') == -1) {
+ defaultOnRecoverableError(error);
+ }
+ },
+ } : {};
+ return ReactDOM.hydrateRoot(container, element, hydrateOptions);
});
}
// Reset app context after app context is updated.
@@ -119,7 +134,7 @@ interface RenderOptions {
async function render({ history, runtime, needHydrate }: RenderOptions) {
const appContext = runtime.getAppContext();
- const { appConfig, loaderData, routes } = appContext;
+ const { appConfig, loaderData, routes, revalidate } = appContext;
const appRender = runtime.getRender();
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
const AppRouter = runtime.getAppRouter();
@@ -132,14 +147,29 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
document.body.appendChild(root);
console.warn(`Root node #${rootId} is not found, current root is automatically created by the framework.`);
}
- return appRender(
+ const hydrationData = needHydrate ? { loaderData } : undefined;
+ const routerOptions = {
+ routes,
+ history,
+ hydrationData,
+ };
+ // Create router before render.
+ const router = process.env.ICE_CORE_ROUTER === 'true' ? createRouter(routerOptions).initialize() : null;
+
+ const renderRoot = appRender(
root,
-
+
,
);
+ if (revalidate) {
+ setTimeout(() => {
+ router?.revalidate();
+ });
+ }
+ return renderRoot;
}
interface HistoryOptions {
diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx
index b87a44cd55..981c5042b6 100644
--- a/packages/runtime/src/runServerApp.tsx
+++ b/packages/runtime/src/runServerApp.tsx
@@ -2,7 +2,9 @@ import type { ServerResponse } from 'http';
import * as React from 'react';
import * as ReactDOMServer from 'react-dom/server';
import { parsePath } from 'react-router-dom';
+import { createStaticRouter } from 'react-router-dom/server.mjs';
import type { Location } from 'history';
+import type { RouteObject } from 'react-router-dom';
import type {
AppContext, RouteItem, ServerContext,
AppExport, AssetsManifest,
@@ -18,7 +20,7 @@ import { AppContextProvider } from './AppContext.js';
import { getAppData } from './appData.js';
import getAppConfig from './appConfig.js';
import { DocumentContextProvider } from './Document.js';
-import { loadRouteModules } from './routes.js';
+import { loadRouteModules, RouteComponent } from './routes.js';
import type { RouteLoaderOptions } from './routes.js';
import { pipeToString, renderToNodeStream } from './server/streamRender.js';
import type { NodeWritablePiper } from './server/streamRender.js';
@@ -181,6 +183,10 @@ async function sendResult(res: ServerResponse, result: RenderResult) {
res.end(result.value);
}
+function needRevalidate(matchedRoutes: RouteMatch[]) {
+ return matchedRoutes.some(({ route }) => route.exports.includes('dataLoader') && route.exports.includes('staticDataLoader'));
+}
+
async function doRender(serverContext: ServerContext, renderOptions: RenderOptions): Promise {
const { req } = serverContext;
const {
@@ -248,19 +254,20 @@ async function doRender(serverContext: ServerContext, renderOptions: RenderOptio
}
try {
- const routeModules = await loadRouteModules(matches.map(({ route: { id, load, lazy } }) => ({ id, load, lazy })));
+ const routeModules = await loadRouteModules(matches.map(({ route: { id, lazy } }) => ({ id, lazy })));
const loaderData = {};
for (const routeId in routeModules) {
const { loader } = routeModules[routeId];
if (loader) {
- const { data, pageConfig } = await loader(location, requestContext);
+ const { data, pageConfig } = await loader();
loaderData[routeId] = {
data,
pageConfig,
};
}
}
- runtime.setAppContext({ ...appContext, routeModules, loaderData, routePath, matches, appData });
+ const revalidate = renderMode === 'SSG' && needRevalidate(matches);
+ runtime.setAppContext({ ...appContext, revalidate, routeModules, loaderData, routePath, matches, appData });
if (runtimeModules.commons) {
await Promise.all(runtimeModules.commons.map(m => runtime.loadModule(m)).filter(Boolean));
}
@@ -288,6 +295,28 @@ function render404(): RenderResult {
};
}
+
+function createServerRoutes(routes: RouteObject[]) {
+ return routes.map((route) => {
+ let dataRoute = {
+ // Static Router need element or Component when matched.
+ element: ,
+ id: route.id,
+ index: route.index,
+ path: route.path,
+ children: null,
+ };
+
+ if (route?.children?.length > 0) {
+ let children = createServerRoutes(
+ route.children,
+ );
+ dataRoute.children = children;
+ }
+ return dataRoute;
+ });
+}
+
interface RenderServerEntry {
runtime: Runtime;
matches: RouteMatch[];
@@ -307,12 +336,16 @@ async function renderServerEntry(
): Promise {
const { Document } = renderOptions;
const appContext = runtime.getAppContext();
- const { routes, routePath, loaderData } = appContext;
+ const { routes, routePath, loaderData, basename } = appContext;
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
const AppRouter = runtime.getAppRouter();
+ const routerContext = {
+ matches, basename, loaderData, location,
+ };
+ const router = createStaticRouter(createServerRoutes(routes), routerContext);
const documentContext = {
main: (
-
+
),
};
const element = (
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index ae0403573a..c01510cfc4 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -1,8 +1,8 @@
import type { IncomingMessage, ServerResponse } from 'http';
-import type { InitialEntry, History, Location, AgnosticRouteObject } from '@remix-run/router';
+import type { InitialEntry, AgnosticRouteObject, Router } from '@remix-run/router';
import type { ComponentType, PropsWithChildren } from 'react';
import type { HydrationOptions, Root } from 'react-dom/client';
-import type { Navigator, Params, RouteObject } from 'react-router-dom';
+import type { Params, RouteObject } from 'react-router-dom';
type UseConfig = () => RouteConfig>;
type UseData = () => RouteData;
@@ -99,11 +99,12 @@ export interface AppContext {
downgrade?: boolean;
renderMode?: RenderMode;
requestContext?: RequestContext;
+ revalidate?: boolean;
}
export type WindowContext = Pick<
AppContext,
- 'appData' | 'loaderData' | 'routePath' | 'downgrade' | 'matchedIds' | 'documentOnly' | 'renderMode' | 'serverData'
+ 'appData' | 'loaderData' | 'routePath' | 'downgrade' | 'matchedIds' | 'documentOnly' | 'renderMode' | 'serverData' | 'revalidate'
>;
export type Renderer = (
@@ -134,7 +135,7 @@ export type ComponentModule = {
export type RouteItem = AgnosticRouteObject & {
componentName: string;
- load?: () => Promise;
+ exports: string[];
layout?: boolean;
children?: RouteItem[];
};
@@ -219,10 +220,9 @@ export interface RuntimeModules {
}
export interface AppRouterProps {
- history?: History;
- routes: RouteObject[];
- loaderData?: LoaderDatas;
- location?: Location;
+ routes?: RouteObject[];
+ router?: Router;
+ routerContext?: any;
}
export interface AppRouteProps {
From ff2f114a728c7244aca14cbbf5e32eec2fdb9356 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 30 Mar 2023 10:09:07 +0800
Subject: [PATCH 05/38] feat: add location for icestark
---
packages/runtime/src/runClientApp.tsx | 2 +-
packages/runtime/src/types.ts | 3 ++-
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index c015ce7956..8e9a86981c 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -160,7 +160,7 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
root,
-
+
,
);
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index c01510cfc4..9d2cccff90 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -1,5 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'http';
-import type { InitialEntry, AgnosticRouteObject, Router } from '@remix-run/router';
+import type { InitialEntry, AgnosticRouteObject, Router, Location } from '@remix-run/router';
import type { ComponentType, PropsWithChildren } from 'react';
import type { HydrationOptions, Root } from 'react-dom/client';
import type { Params, RouteObject } from 'react-router-dom';
@@ -223,6 +223,7 @@ export interface AppRouterProps {
routes?: RouteObject[];
router?: Router;
routerContext?: any;
+ location?: Location;
}
export interface AppRouteProps {
From 62a5376a2322ae8e74195d5b406c712dbf851baf Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 30 Mar 2023 11:01:29 +0800
Subject: [PATCH 06/38] chore: remove console
---
packages/runtime/src/runServerApp.tsx | 1 -
1 file changed, 1 deletion(-)
diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx
index 981c5042b6..f441d5d62e 100644
--- a/packages/runtime/src/runServerApp.tsx
+++ b/packages/runtime/src/runServerApp.tsx
@@ -99,7 +99,6 @@ export async function renderToHTML(
requestContext: ServerContext,
renderOptions: RenderOptions,
): Promise {
- console.log('renderHTMLToJS.tsx');
const result = await doRender(requestContext, renderOptions);
const { value } = result;
From bb0f5f42942457e0675b7013d401a07cd76261c6 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 30 Mar 2023 11:30:26 +0800
Subject: [PATCH 07/38] test: examples
---
examples/basic-project/ice.config.mts | 1 -
examples/basic-project/src/pages/layout.tsx | 38 +++++++++++++++++++++
2 files changed, 38 insertions(+), 1 deletion(-)
create mode 100644 examples/basic-project/src/pages/layout.tsx
diff --git a/examples/basic-project/ice.config.mts b/examples/basic-project/ice.config.mts
index 4a61a033dc..8baac7c3c8 100644
--- a/examples/basic-project/ice.config.mts
+++ b/examples/basic-project/ice.config.mts
@@ -4,7 +4,6 @@ import customPlugin from './plugin';
export default defineConfig(() => ({
ssr: true,
- ssg: false,
publicPath: '/',
polyfill: 'entry',
syntaxFeatures: {
diff --git a/examples/basic-project/src/pages/layout.tsx b/examples/basic-project/src/pages/layout.tsx
new file mode 100644
index 0000000000..618740508c
--- /dev/null
+++ b/examples/basic-project/src/pages/layout.tsx
@@ -0,0 +1,38 @@
+import { Outlet, useData, useConfig, definePageConfig, defineDataLoader } from 'ice';
+
+export default function Layout() {
+ const data = useData();
+ const config = useConfig();
+
+ console.log('render Layout', 'data', data, 'config', config);
+
+ return (
+
+
ICE 3.0 Layout
+
+
+ );
+}
+
+
+export const pageConfig = definePageConfig(() => {
+ return {
+ title: 'Layout',
+ meta: [
+ {
+ name: 'layout-color',
+ content: '#f00',
+ },
+ ],
+ };
+});
+
+export const dataLoader = defineDataLoader(() => {
+ return new Promise((resolve) => {
+ setTimeout(() => {
+ resolve({
+ layout: true,
+ });
+ }, 1 * 100);
+ });
+});
From c5b1a0517323c36d54c52141f20687a38c05e232 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 30 Mar 2023 15:43:07 +0800
Subject: [PATCH 08/38] fix: dataloader is undefined
---
packages/runtime/src/routes.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 3dd8f3f85f..18ffa47793 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -112,7 +112,7 @@ export function createRouteLoader(options: RouteLoaderOptions): () => Promise
Date: Fri, 31 Mar 2023 14:09:38 +0800
Subject: [PATCH 09/38] fix: test
---
tests/integration/with-antd-mobile.test.ts | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/tests/integration/with-antd-mobile.test.ts b/tests/integration/with-antd-mobile.test.ts
index 548f01c356..8c4d70e409 100644
--- a/tests/integration/with-antd-mobile.test.ts
+++ b/tests/integration/with-antd-mobile.test.ts
@@ -11,10 +11,9 @@ describe(`build ${example}`, () => {
test('open /', async () => {
await buildFixture(example);
- const res = await setupBrowser({ example, disableJS: false });
+ const res = await setupBrowser({ example });
page = res.page;
browser = res.browser;
- await page.waitForFunction('document.getElementsByTagName(\'h2\').length > 0');
expect(await page.$$text('h2')).toStrictEqual(['Counter']);
});
From bb2ce263430f7955f635bb3b9fe5659c4ff2d297 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 31 Mar 2023 15:34:06 +0800
Subject: [PATCH 10/38] fix: test case
---
examples/with-store/ice.config.mts | 1 +
examples/with-store/src/app.tsx | 6 +++++-
packages/ice/src/routes.ts | 5 ++++-
.../core/{routes.ts.ejs => routes.tsx.ejs} | 2 +-
packages/plugin-store/src/runtime.tsx | 10 ++++------
packages/runtime/src/RouteWrapper.tsx | 7 ++++---
packages/runtime/src/index.ts | 3 ++-
packages/runtime/src/routes.tsx | 20 ++++++++++++++-----
packages/runtime/src/runClientApp.tsx | 3 ++-
packages/runtime/src/types.ts | 5 +++++
10 files changed, 43 insertions(+), 19 deletions(-)
rename packages/ice/templates/core/{routes.ts.ejs => routes.tsx.ejs} (63%)
diff --git a/examples/with-store/ice.config.mts b/examples/with-store/ice.config.mts
index 8f105b7264..d9944d8bb5 100644
--- a/examples/with-store/ice.config.mts
+++ b/examples/with-store/ice.config.mts
@@ -2,6 +2,7 @@ import { defineConfig } from '@ice/app';
import store from '@ice/plugin-store';
export default defineConfig(() => ({
+ ssg: false,
plugins: [
store({
resetPageState: true,
diff --git a/examples/with-store/src/app.tsx b/examples/with-store/src/app.tsx
index 87805d7ca9..a41605439a 100644
--- a/examples/with-store/src/app.tsx
+++ b/examples/with-store/src/app.tsx
@@ -19,4 +19,8 @@ export const dataLoader = defineDataLoader(() => {
});
});
-export default defineAppConfig(() => ({}));
+export default defineAppConfig(() => ({
+ router: {
+ type: 'hash',
+ },
+}));
diff --git a/packages/ice/src/routes.ts b/packages/ice/src/routes.ts
index a4a5ff16aa..1f45ff1de6 100644
--- a/packages/ice/src/routes.ts
+++ b/packages/ice/src/routes.ts
@@ -69,7 +69,7 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
const component = `Component: () => WrapRouteComponent({
routeId: '${id}',
isLayout: ${layout},
- RouteComponent: ${lazy ? 'componentModule' : loadStatement}.default,
+ routeExports: ${lazy ? 'componentModule' : loadStatement},
})`;
const loader = `loader: createRouteLoader({
routeId: '${id}',
@@ -82,10 +82,13 @@ export function getRoutesDefination(nestRouteManifest: NestedRouteManifest[], la
`async lazy() {
${lazy ? `const componentModule = await ${loadStatement}` : ''};
return {
+ ${lazy ? '...componentModule' : `...${loadStatement}`},
${component},
${loader},
};
},`,
+ // Empty errorElement to avoid default ui provided by react-router.
+ 'ErrorBoundary: RouteErrorComponent,',
`componentName: '${componentName}',`,
`index: ${index},`,
`id: '${id}',`,
diff --git a/packages/ice/templates/core/routes.ts.ejs b/packages/ice/templates/core/routes.tsx.ejs
similarity index 63%
rename from packages/ice/templates/core/routes.ts.ejs
rename to packages/ice/templates/core/routes.tsx.ejs
index 3e1f7cec9e..16b78a77da 100644
--- a/packages/ice/templates/core/routes.ts.ejs
+++ b/packages/ice/templates/core/routes.tsx.ejs
@@ -1,4 +1,4 @@
-import { createRouteLoader, WrapRouteComponent } from '@ice/runtime';
+import { createRouteLoader, WrapRouteComponent, RouteErrorComponent } from '@ice/runtime';
<%- routeImports.length ? routeImports.join('\n') + '\n\n' : ''; -%>
export default ({
requestContext,
diff --git a/packages/plugin-store/src/runtime.tsx b/packages/plugin-store/src/runtime.tsx
index e375712827..74cd1d169c 100644
--- a/packages/plugin-store/src/runtime.tsx
+++ b/packages/plugin-store/src/runtime.tsx
@@ -27,12 +27,10 @@ const runtime: RuntimePlugin = async ({ appContext, addWrapper, addProvider, use
addProvider(StoreProvider);
// Add page store .
- const StoreProviderWrapper: RouteWrapper = ({ children, routeId }) => {
- const { routeModules } = useAppContext();
- const routeModule = routeModules[routeId];
- if (routeModule?.[PAGE_STORE_PROVIDER]) {
- const Provider = routeModule[PAGE_STORE_PROVIDER];
- const initialStates = routeModule[PAGE_STORE_INITIAL_STATES];
+ const StoreProviderWrapper: RouteWrapper = ({ routeExports, children }) => {
+ if (routeExports?.[PAGE_STORE_PROVIDER]) {
+ const Provider = routeExports[PAGE_STORE_PROVIDER];
+ const initialStates = routeExports[PAGE_STORE_INITIAL_STATES];
if (initialStates) {
return {children};
}
diff --git a/packages/runtime/src/RouteWrapper.tsx b/packages/runtime/src/RouteWrapper.tsx
index 1020c7bf1d..c9629bdfad 100644
--- a/packages/runtime/src/RouteWrapper.tsx
+++ b/packages/runtime/src/RouteWrapper.tsx
@@ -1,15 +1,16 @@
import * as React from 'react';
-import type { RouteWrapperConfig } from './types.js';
+import type { RouteWrapperConfig, RouteExports } from './types.js';
interface Props {
id: string;
isLayout?: boolean;
wrappers?: RouteWrapperConfig[];
children?: React.ReactNode;
+ routeExports: RouteExports;
}
export default function RouteWrapper(props: Props) {
- const { wrappers = [], id, isLayout } = props;
+ const { wrappers = [], id, isLayout, routeExports } = props;
// layout should only be wrapped by Wrapper with `layout: true`
const filtered = isLayout ? wrappers.filter(wrapper => wrapper.layout === true) : wrappers;
const RouteWrappers = filtered.map(item => item.Wrapper);
@@ -18,7 +19,7 @@ export default function RouteWrapper(props: Props) {
if (RouteWrappers.length) {
element = RouteWrappers.reduce((preElement, CurrentWrapper) => (
-
+
{preElement}
), props.children);
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index 48a5812db2..30b05bc54a 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -50,7 +50,7 @@ import KeepAliveOutlet from './KeepAliveOutlet.js';
import ClientOnly from './ClientOnly.js';
import useMounted from './useMounted.js';
import { withSuspense, useSuspenseData } from './Suspense.js';
-import { createRouteLoader, WrapRouteComponent } from './routes.js';
+import { createRouteLoader, WrapRouteComponent, RouteErrorComponent } from './routes.js';
export {
getAppConfig,
@@ -96,6 +96,7 @@ export {
createRouteLoader,
WrapRouteComponent,
+ RouteErrorComponent,
};
export type {
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 18ffa47793..1deb81caf8 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,5 +1,6 @@
import React from 'react';
-import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
+import { useRouteError } from 'react-router-dom';
+import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule, RouteExports } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
import { callDataLoader } from './dataLoader.js';
@@ -53,13 +54,13 @@ export async function loadRouteModules(routes: RouteModule[], originRouteModules
export function WrapRouteComponent(options: {
routeId: string;
isLayout?: boolean;
- RouteComponent: React.ComponentType;
+ routeExports: RouteExports;
}) {
- const { routeId, isLayout, RouteComponent } = options;
+ const { routeId, isLayout, routeExports } = options;
const { RouteWrappers } = useAppContext();
return (
-
-
+
+
);
}
@@ -79,6 +80,15 @@ export function RouteComponent({ id }: { id: string }) {
return ;
}
+export function RouteErrorComponent() {
+ const error = useRouteError();
+ if (error) {
+ // Re-throws the error so it can be caught by App Error Boundary.
+ throw error;
+ }
+ return <>>;
+}
+
/**
* Create loader function for route module.
*/
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index 8e9a86981c..aa3431b0c1 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -134,7 +134,7 @@ interface RenderOptions {
async function render({ history, runtime, needHydrate }: RenderOptions) {
const appContext = runtime.getAppContext();
- const { appConfig, loaderData, routes, revalidate } = appContext;
+ const { appConfig, loaderData, routes, revalidate, basename } = appContext;
const appRender = runtime.getRender();
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
const AppRouter = runtime.getAppRouter();
@@ -149,6 +149,7 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
}
const hydrationData = needHydrate ? { loaderData } : undefined;
const routerOptions = {
+ basename,
routes,
history,
hydrationData,
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index 9d2cccff90..ac1b83b106 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -151,6 +151,11 @@ export interface RouteWrapperConfig {
layout?: boolean;
}
+export interface RouteExports {
+ [key: string]: any;
+ default: React.ComponentType;
+}
+
export type AppProvider = ComponentWithChildren;
export type RouteWrapper = ComponentType;
From e77a94c0dc86511a0ddaada7b8300e1dc65521c9 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 31 Mar 2023 17:52:52 +0800
Subject: [PATCH 11/38] fix: test case
---
packages/ice/src/constant.ts | 15 +-
packages/runtime/src/ClientRouter.tsx | 13 +-
packages/runtime/src/RouteContext.ts | 13 +-
packages/runtime/src/RouteWrapper.tsx | 4 +-
packages/runtime/src/index.ts | 4 +-
packages/runtime/src/router.ts | 4 +-
packages/runtime/src/routes.tsx | 4 +-
packages/runtime/src/runClientApp.tsx | 21 +-
packages/runtime/src/single-router.tsx | 18 ++
packages/runtime/src/types.ts | 8 +-
packages/runtime/tests/routes.test.tsx | 307 ++++++-------------
packages/runtime/tests/runClientApp.test.tsx | 43 +--
12 files changed, 185 insertions(+), 269 deletions(-)
diff --git a/packages/ice/src/constant.ts b/packages/ice/src/constant.ts
index cf339bd8dc..caeb9b6b15 100644
--- a/packages/ice/src/constant.ts
+++ b/packages/ice/src/constant.ts
@@ -39,15 +39,22 @@ export const TARGETS = [
export const RUNTIME_EXPORTS = [
{
- specifier: ['Link', 'Outlet', 'useParams', 'useSearchParams', 'useLocation', 'useNavigate'],
+ specifier: [
+ 'Link',
+ 'Outlet',
+ 'useParams',
+ 'useSearchParams',
+ 'useLocation',
+ 'useData',
+ 'useConfig',
+ 'useNavigate',
+ ],
source: '@ice/runtime/router',
},
{
specifier: [
'defineAppConfig',
'useAppData',
- 'useData',
- 'useConfig',
'history',
'KeepAliveOutlet',
'useMounted',
@@ -60,4 +67,4 @@ export const RUNTIME_EXPORTS = [
],
source: '@ice/runtime',
},
-];
\ No newline at end of file
+];
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index 04e8bee92f..8dc1106783 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -3,16 +3,21 @@ import { RouterProvider } from 'react-router-dom';
import type { AppRouterProps } from './types.js';
import App from './App.js';
-import { Router } from './single-router.js';
+import { DataContextProvider } from './single-router.js';
function ClientRouter(props: AppRouterProps) {
- const { router, routes } = props;
+ const { router, routes, Component, loaderData } = props;
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
- element = loading>} />;
+ element = >} />;
} else {
- element = ;
+ const routeItem = routes[0];
+ element = (
+
+
+
+ );
}
return (
diff --git a/packages/runtime/src/RouteContext.ts b/packages/runtime/src/RouteContext.ts
index 6f0c9f3e48..9ddd8948f3 100644
--- a/packages/runtime/src/RouteContext.ts
+++ b/packages/runtime/src/RouteContext.ts
@@ -1,28 +1,17 @@
-import * as React from 'react';
import { useLoaderData } from 'react-router-dom';
-import type { RouteData, RouteConfig } from './types.js';
-
-const DataContext = React.createContext(undefined);
-DataContext.displayName = 'Data';
+import type { RouteConfig } from './types.js';
function useData(): T {
const data = useLoaderData();
return (data as any).data;
}
-const DataProvider = DataContext.Provider;
-
-const ConfigContext = React.createContext | undefined>(undefined);
-ConfigContext.displayName = 'Config';
function useConfig(): RouteConfig {
const data = useLoaderData();
return (data as any).pageConfig;
}
-const ConfigProvider = ConfigContext.Provider;
export {
useData,
- DataProvider,
useConfig,
- ConfigProvider,
};
diff --git a/packages/runtime/src/RouteWrapper.tsx b/packages/runtime/src/RouteWrapper.tsx
index c9629bdfad..adacd13289 100644
--- a/packages/runtime/src/RouteWrapper.tsx
+++ b/packages/runtime/src/RouteWrapper.tsx
@@ -1,12 +1,12 @@
import * as React from 'react';
-import type { RouteWrapperConfig, RouteExports } from './types.js';
+import type { RouteWrapperConfig, ComponentModule } from './types.js';
interface Props {
id: string;
isLayout?: boolean;
wrappers?: RouteWrapperConfig[];
children?: React.ReactNode;
- routeExports: RouteExports;
+ routeExports: ComponentModule;
}
export default function RouteWrapper(props: Props) {
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index 30b05bc54a..879bdc5f72 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -24,7 +24,7 @@ import runClientApp from './runClientApp.js';
import type { RunClientAppOptions } from './runClientApp.js';
import { useAppContext, useAppData, AppContextProvider } from './AppContext.js';
import { getAppData } from './appData.js';
-import { useData, useConfig, DataProvider, ConfigProvider } from './RouteContext.js';
+import { useData, useConfig } from './RouteContext.js';
import {
Meta,
Title,
@@ -65,8 +65,6 @@ export {
defineDataLoader,
defineServerDataLoader,
defineStaticDataLoader,
- DataProvider,
- ConfigProvider,
useConfig,
Meta,
Title,
diff --git a/packages/runtime/src/router.ts b/packages/runtime/src/router.ts
index 5dc65e77c2..c86bfa1140 100644
--- a/packages/runtime/src/router.ts
+++ b/packages/runtime/src/router.ts
@@ -5,4 +5,6 @@ export {
useSearchParams,
useLocation,
useNavigate,
-} from 'react-router-dom';
\ No newline at end of file
+} from 'react-router-dom';
+
+export { useData, useConfig } from './RouteContext.js';
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 1deb81caf8..56a7ed5b7a 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -54,7 +54,7 @@ export async function loadRouteModules(routes: RouteModule[], originRouteModules
export function WrapRouteComponent(options: {
routeId: string;
isLayout?: boolean;
- routeExports: RouteExports;
+ routeExports: ComponentModule;
}) {
const { routeId, isLayout, routeExports } = options;
const { RouteWrappers } = useAppContext();
@@ -99,7 +99,7 @@ interface LoaderData {
export interface RouteLoaderOptions {
routeId: string;
- requestContext: RequestContext;
+ requestContext?: RequestContext;
module: ComponentModule;
renderMode: RenderMode;
}
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index aa3431b0c1..f889190130 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -11,6 +11,7 @@ import Runtime from './runtime.js';
import { getAppData } from './appData.js';
import { getRoutesPath } from './routes.js';
import type { RouteLoaderOptions } from './routes.js';
+import { loadRouteModule } from './routes.js';
import getRequestContext from './requestContext.js';
import getAppConfig from './appConfig.js';
import ClientRouter from './ClientRouter.js';
@@ -154,14 +155,28 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
history,
hydrationData,
};
+ let router = null;
+ let singleComponent = null;
+ let routeData = null;
// Create router before render.
- const router = process.env.ICE_CORE_ROUTER === 'true' ? createRouter(routerOptions).initialize() : null;
-
+ if (process.env.ICE_CORE_ROUTER === 'true') {
+ router = createRouter(routerOptions).initialize();
+ } else {
+ const { Component, loader } = await loadRouteModule(routes[0]);
+ singleComponent = Component || routes[0].Component;
+ routeData = loader && await loader();
+ }
const renderRoot = appRender(
root,
-
+
,
);
diff --git a/packages/runtime/src/single-router.tsx b/packages/runtime/src/single-router.tsx
index bb58d8059f..ce20e796d1 100644
--- a/packages/runtime/src/single-router.tsx
+++ b/packages/runtime/src/single-router.tsx
@@ -4,6 +4,24 @@
*/
import * as React from 'react';
import type { History } from '@remix-run/router';
+import type { LoaderData } from './types.js';
+
+const Context = React.createContext(undefined);
+
+Context.displayName = 'DataContext';
+
+export function useData(): T {
+ const value = React.useContext(Context);
+ return value.data;
+}
+
+export function useConfig() {
+ const value = React.useContext(Context);
+ return value.pageConfig;
+}
+
+export const DataContextProvider = Context.Provider;
+
export const useRoutes = (routes) => {
return <>{routes[0].element}>;
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index ac1b83b106..16f6a6abbc 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -135,6 +135,7 @@ export type ComponentModule = {
export type RouteItem = AgnosticRouteObject & {
componentName: string;
+ Component?: ComponentType;
exports: string[];
layout?: boolean;
children?: RouteItem[];
@@ -151,11 +152,6 @@ export interface RouteWrapperConfig {
layout?: boolean;
}
-export interface RouteExports {
- [key: string]: any;
- default: React.ComponentType;
-}
-
export type AppProvider = ComponentWithChildren;
export type RouteWrapper = ComponentType;
@@ -229,6 +225,8 @@ export interface AppRouterProps {
router?: Router;
routerContext?: any;
location?: Location;
+ Component?: ComponentType;
+ loaderData?: LoaderData;
}
export interface AppRouteProps {
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index ffd72c94a1..beeb2e8c91 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -5,17 +5,13 @@
import React from 'react';
import { renderToString } from 'react-dom/server';
import { expect, it, describe, beforeEach, afterEach, vi } from 'vitest';
-import type { RouteComponent as IRouteComponent } from '../src/types';
-import RouteWrapper from '../src/RouteWrapper';
import { AppContextProvider } from '../src/AppContext';
import {
- filterMatchesToLoad,
- createRouteElements,
RouteComponent,
loadRouteModules,
- loadRoutesData,
- getRoutesConfig,
+ createRouteLoader,
getRoutesPath,
+ WrapRouteComponent,
} from '../src/routes.js';
describe('routes', () => {
@@ -40,17 +36,33 @@ describe('routes', () => {
default: () => <>>,
pageConfig: () => ({ title: 'about' }),
};
+ const homeLazyItem = {
+ Component: homeItem.default,
+ loader: createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'CSR',
+ }),
+ };
+ const aboutLazyItem = {
+ Component: aboutItem.default,
+ loader: createRouteLoader({
+ routeId: 'about',
+ module: aboutItem,
+ renderMode: 'CSR',
+ }),
+ };
const routeModules = [
{
id: 'home',
- load: async () => {
- return homeItem as IRouteComponent;
+ lazy: async () => {
+ return homeLazyItem;
},
},
{
id: 'about',
- load: async () => {
- return aboutItem as IRouteComponent;
+ lazy: async () => {
+ return aboutLazyItem;
},
},
];
@@ -61,7 +73,7 @@ describe('routes', () => {
home
,
+ Component: () => home
,
},
},
}}
@@ -84,12 +96,32 @@ describe('routes', () => {
process.env.NODE_ENV = currentEnv;
});
+ it('route WrapRouteComponent', () => {
+ const domstring = renderToString(
+ // @ts-ignore
+ wrapper{children}
, layout: false }] }}>
+ home
}} />
+ ,
+ );
+ expect(domstring).toBe('');
+ });
+
+ it('route WrapRouteComponent match layout', () => {
+ const domstring = renderToString(
+ // @ts-ignore
+ wrapper{children}
, layout: false }] }}>
+ home
}} />
+ ,
+ );
+ expect(domstring).toBe('home
');
+ });
+
it('load route modules', async () => {
windowSpy.mockImplementation(() => ({}));
- const routeModule = await loadRouteModules(routeModules, { home: homeItem });
+ const routeModule = await loadRouteModules(routeModules, {});
expect(routeModule).toStrictEqual({
- home: homeItem,
- about: aboutItem,
+ home: homeLazyItem,
+ about: aboutLazyItem,
});
});
@@ -97,7 +129,7 @@ describe('routes', () => {
const routeModule = await loadRouteModules([{
id: 'error',
// @ts-ignore
- load: async () => {
+ lazy: async () => {
throw new Error('err');
return {};
},
@@ -108,59 +140,53 @@ describe('routes', () => {
});
it('load route data for SSG', async () => {
- const routeModule = await loadRouteModules(routeModules);
- const routesDataSSG = await loadRoutesData(
- // @ts-ignore
- [{ route: routeModules[0] }],
- {},
- routeModule,
- {
- renderMode: 'SSG',
- },
- );
+ const routesDataSSG = await createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'SSG',
+ })();
expect(routesDataSSG).toStrictEqual({
- home: {
+ data: {
type: 'getStaticData',
},
+ pageConfig: {
+ title: 'home',
+ },
});
});
it('load route data for SSR', async () => {
- const routeModule = await loadRouteModules(routeModules);
- const routesDataSSR = await loadRoutesData(
- // @ts-ignore
- [{ route: routeModules[0] }],
- {},
- routeModule,
- {
- renderMode: 'SSR',
- },
- );
+ const routesDataSSR = await createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'SSR',
+ })();
expect(routesDataSSR).toStrictEqual({
- home: {
+ data: {
type: 'getServerData',
},
+ pageConfig: {
+ title: 'home',
+ },
});
});
it('load route data for CSR', async () => {
- const routeModule = await loadRouteModules(routeModules);
- const routesDataCSR = await loadRoutesData(
- // @ts-ignore
- [{ route: routeModules[0] }],
- {},
- routeModule,
- {
- renderMode: 'CSR',
- },
- );
+ const routesDataCSR = await createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'CSR',
+ })();
expect(routesDataCSR).toStrictEqual({
- home: {
+ data: {
type: 'getData',
},
+ pageConfig: {
+ title: 'home',
+ },
});
});
@@ -170,180 +196,35 @@ describe('routes', () => {
getData: async (id) => ({ id: `${id}_data` }),
},
}));
- const routesData = await loadRoutesData(
- // @ts-ignore
- [{ route: routeModules[0] }],
- {},
- {},
- { renderMode: 'SSG' },
- );
- expect(routesData).toStrictEqual({
- home: {
+ const routesDataCSR = await createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'CSR',
+ })();
+
+ expect(routesDataCSR).toStrictEqual({
+ data: {
id: 'home_data',
},
- });
- });
-
- it('get routes config', async () => {
- const routeModule = await loadRouteModules(routeModules);
- const routesConfig = getRoutesConfig(
- // @ts-ignore
- [{ route: routeModules[0] }],
- { home: {} },
- routeModule,
- );
- expect(routesConfig).toStrictEqual({
- home: {
+ pageConfig: {
title: 'home',
},
});
});
- it('get routes config when failed get route module', async () => {
- const routesConfig = getRoutesConfig(
- // @ts-ignore
- [{ route: routeModules[0] }],
- { home: {} },
- {},
- );
- expect(routesConfig).toStrictEqual({
- home: {},
- });
- });
+ it('get routes config without data', async () => {
+ const routesDataCSR = await createRouteLoader({
+ routeId: 'about',
+ module: aboutItem,
+ renderMode: 'CSR',
+ })();
- it('create route element', () => {
- const routeElement = createRouteElements([{
- path: '/',
- id: 'home',
- componentName: 'home',
- }]);
- expect(routeElement).toEqual([{
- componentName: 'home',
- element: (
-
-
-
- ),
- id: 'home',
- path: '/',
- }]);
- });
-
- it('create route with children', () => {
- const routeElement = createRouteElements([{
- path: '/',
- id: 'home',
- componentName: 'home',
- children: [{
- path: '/about',
- id: 'about',
- componentName: 'about',
- }],
- }]);
- expect(routeElement).toEqual([{
- componentName: 'home',
- element: (
-
-
-
- ),
- children: [{
- componentName: 'about',
- element: (
-
-
-
- ),
- id: 'about',
- path: '/about',
- }],
- id: 'home',
- path: '/',
- }]);
- });
-
- it('filter new matches', () => {
- const oldMatches = [
- {
- pathname: '/',
- route: {
- id: '/page/layout',
- },
- },
- {
- pathname: '/',
- route: {
- id: '/page/home',
- },
- },
- ];
-
- const newMatches = [
- {
- pathname: '/',
- route: {
- id: '/page/layout',
- },
- },
- {
- pathname: '/about',
- route: {
- id: '/page/about',
- },
- },
- ];
-
- // @ts-ignore
- const matches = filterMatchesToLoad(oldMatches, newMatches);
-
- expect(
- matches,
- ).toEqual([{
- pathname: '/about',
- route: {
- id: '/page/about',
- },
- }]);
- });
-
- it('filter matches with path changed', () => {
- const oldMatches = [
- {
- pathname: '/users/123',
- route: {
- id: '/users/123',
- },
- },
- ];
-
- const newMatches = [
- {
- pathname: '/users/456',
- route: {
- id: '/users/456',
- },
- },
- ];
-
- // @ts-ignore
- const matches = filterMatchesToLoad(oldMatches, newMatches);
-
- expect(
- matches,
- ).toEqual([
- {
- pathname: '/users/456',
- route: {
- id: '/users/456',
- },
+ expect(routesDataCSR).toStrictEqual({
+ data: undefined,
+ pageConfig: {
+ title: 'about',
},
- ]);
+ });
});
it('get routes flatten path', () => {
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index 04b68f002e..88ef3e91f4 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -2,11 +2,12 @@
* @vitest-environment jsdom
*/
-import React from 'react';
+import React, { useEffect } from 'react';
import { renderToString } from 'react-dom/server';
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
-import runClientApp, { loadNextPage } from '../src/runClientApp';
-import { useAppData } from '../src/AppData';
+import runClientApp from '../src/runClientApp';
+import { useAppData } from '../src/AppContext';
+import { createRouteLoader } from '../src/routes';
import { useConfig, useData } from '../src/RouteContext';
describe('run client app', () => {
@@ -79,22 +80,23 @@ describe('run client app', () => {
addProvider(Provider);
};
+ const homeItem = {
+ default: () => {
+ // eslint-disable-next-line react-hooks/rules-of-hooks
+ const appData = useAppData();
+ return (
+ home{appData?.msg || ''}
+ );
+ },
+ pageConfig: () => ({ title: 'home' }),
+ dataLoader: async () => ({ data: 'test' }),
+ };
const basicRoutes = [
{
id: 'home',
path: '/',
componentName: 'Home',
- load: async () => ({
- default: () => {
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const appData = useAppData();
- return (
- home{appData?.msg || ''}
- );
- },
- pageConfig: () => ({ title: 'home' }),
- dataLoader: async () => ({ data: 'test' }),
- }),
+ Component: homeItem.default,
},
];
@@ -105,9 +107,10 @@ describe('run client app', () => {
return { msg: staticMsg };
},
},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime], statics: [staticRuntime] },
- hydrate: false,
+ hydrate: true,
});
expect(domstring).toBe('homestatic
');
});
@@ -117,10 +120,10 @@ describe('run client app', () => {
...mockData,
location: new URL('http://localhost:4000/?test=1&runtime=true&baisc'),
}));
-
await runClientApp({
app: {},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
@@ -131,14 +134,14 @@ describe('run client app', () => {
process.env.ICE_CORE_ROUTER = '';
await runClientApp({
app: {},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
process.env.ICE_CORE_ROUTER = 'true';
expect(domstring).toBe('home
');
});
-
it('run client with wrapper', async () => {
await runClientApp({
app: {},
From 7530846a24196f09ab53ee79fa11aa25492c04eb Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 31 Mar 2023 17:54:17 +0800
Subject: [PATCH 12/38] fix: types
---
packages/runtime/src/routes.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 56a7ed5b7a..46db33fa50 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import { useRouteError } from 'react-router-dom';
-import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule, RouteExports } from './types.js';
+import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
import { callDataLoader } from './dataLoader.js';
From 6e2e859786b8147af110a8499c8cbe9352370fe9 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 11:31:48 +0800
Subject: [PATCH 13/38] fix: test case
---
packages/ice/package.json | 2 +-
packages/miniapp-runtime/src/app/App.tsx | 2 +-
packages/runtime/package.json | 7 +-
packages/runtime/src/AppErrorBoundary.tsx | 2 +-
packages/runtime/src/ClientRouter.tsx | 3 +-
packages/runtime/src/runClientApp.tsx | 1 +
packages/runtime/tests/routesConfig.test.ts | 7 +-
packages/runtime/tests/runClientApp.test.tsx | 165 ++----
packages/runtime/tests/runServerApp.test.tsx | 71 ++-
pnpm-lock.yaml | 517 +++++++++++++++++--
10 files changed, 563 insertions(+), 214 deletions(-)
diff --git a/packages/ice/package.json b/packages/ice/package.json
index 72fa6e179c..07290a19d2 100644
--- a/packages/ice/package.json
+++ b/packages/ice/package.json
@@ -83,7 +83,7 @@
"esbuild": "^0.16.5",
"jest": "^29.0.2",
"react": "^18.2.0",
- "react-router": "6.9.0",
+ "react-router": "6.10.0",
"unplugin": "^0.9.0",
"webpack": "^5.76.2",
"webpack-dev-server": "^4.7.4"
diff --git a/packages/miniapp-runtime/src/app/App.tsx b/packages/miniapp-runtime/src/app/App.tsx
index 02cd818647..1e359cc69e 100644
--- a/packages/miniapp-runtime/src/app/App.tsx
+++ b/packages/miniapp-runtime/src/app/App.tsx
@@ -7,7 +7,7 @@ export default function App() {
const { strict, errorBoundary } = appConfig.app;
const StrictMode = strict ? React.StrictMode : React.Fragment;
const ErrorBoundary = errorBoundary ? AppErrorBoundary : React.Fragment;
-
+ console.log('ErrorBoundary', ErrorBoundary);
return (
diff --git a/packages/runtime/package.json b/packages/runtime/package.json
index 9cf6ac535b..a022d0d5d8 100644
--- a/packages/runtime/package.json
+++ b/packages/runtime/package.json
@@ -37,17 +37,18 @@
"@types/react-dom": "^18.0.3",
"react": "^18.0.0",
"react-dom": "^18.0.0",
- "regenerator-runtime": "^0.13.9"
+ "regenerator-runtime": "^0.13.9",
+ "@remix-run/web-fetch": "^4.3.3"
},
"sideEffects": false,
"dependencies": {
"@ice/jsx-runtime": "^0.2.0",
- "@remix-run/router": "1.4.0",
+ "@remix-run/router": "1.5.0",
"ejs": "^3.1.6",
"fs-extra": "^10.0.0",
"history": "^5.3.0",
"htmlparser2": "^8.0.1",
- "react-router-dom": "6.9.0"
+ "react-router-dom": "6.10.0"
},
"peerDependencies": {
"react": "^18.1.0",
diff --git a/packages/runtime/src/AppErrorBoundary.tsx b/packages/runtime/src/AppErrorBoundary.tsx
index 886a1df2b2..08793e41b2 100644
--- a/packages/runtime/src/AppErrorBoundary.tsx
+++ b/packages/runtime/src/AppErrorBoundary.tsx
@@ -29,4 +29,4 @@ export default class AppErrorBoundary extends React.Component {
return this.props.children;
}
-}
\ No newline at end of file
+}
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index 8dc1106783..a8fb607f66 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -6,13 +6,12 @@ import App from './App.js';
import { DataContextProvider } from './single-router.js';
function ClientRouter(props: AppRouterProps) {
- const { router, routes, Component, loaderData } = props;
+ const { router, Component, loaderData } = props;
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
element = >} />;
} else {
- const routeItem = routes[0];
element = (
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index f889190130..f9023978eb 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -181,6 +181,7 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
,
);
if (revalidate) {
+ // Revalidate after render for SSG while staticDataLoader and dataLoader both defined.
setTimeout(() => {
router?.revalidate();
});
diff --git a/packages/runtime/tests/routesConfig.test.ts b/packages/runtime/tests/routesConfig.test.ts
index 4344e1ac18..49c234e369 100644
--- a/packages/runtime/tests/routesConfig.test.ts
+++ b/packages/runtime/tests/routesConfig.test.ts
@@ -44,7 +44,7 @@ describe('routes config', () => {
it('update routes config', async () => {
const routesConfig = {
- home: {
+ pageConfig: {
title: 'home',
meta: [
{
@@ -61,12 +61,11 @@ describe('routes config', () => {
}],
},
};
- // @ts-ignore
- await updateRoutesConfig([{ route: { id: 'home' } }], routesConfig);
+ await updateRoutesConfig(routesConfig);
expect(insertTags.length).toBe(1);
expect(insertTags[0]?.type).toBe('meta');
expect(appendTags.length).toBe(2);
expect(appendTags[0]?.type).toBe('link');
expect(appendTags[1]?.type).toBe('script');
});
-});
\ No newline at end of file
+});
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index 88ef3e91f4..90f497f312 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -2,13 +2,11 @@
* @vitest-environment jsdom
*/
-import React, { useEffect } from 'react';
+import React from 'react';
import { renderToString } from 'react-dom/server';
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
import runClientApp from '../src/runClientApp';
import { useAppData } from '../src/AppContext';
-import { createRouteLoader } from '../src/routes';
-import { useConfig, useData } from '../src/RouteContext';
describe('run client app', () => {
let windowSpy;
@@ -54,7 +52,9 @@ describe('run client app', () => {
setRender((container, element) => {
try {
domstring = renderToString(element as any);
- } catch (err) { }
+ } catch (err) {
+ domstring = '';
+ }
});
};
@@ -64,13 +64,6 @@ describe('run client app', () => {
staticMsg = 'static';
};
- const wrapperRuntime = async ({ addWrapper }) => {
- const RouteWrapper = ({ children }) => {
- return {children}
;
- };
- addWrapper(RouteWrapper, true);
- };
-
const providerRuntmie = async ({ addProvider }) => {
const Provider = ({ children }) => {
return {children}
;
@@ -96,6 +89,7 @@ describe('run client app', () => {
id: 'home',
path: '/',
componentName: 'Home',
+ // Make sure the component is loaded before render.
Component: homeItem.default,
},
];
@@ -131,59 +125,51 @@ describe('run client app', () => {
});
it('run client single-router', async () => {
+ const sigleRoutes = [
+ {
+ id: 'home',
+ path: '/',
+ lazy: () => {
+ return {
+ Component: homeItem.default,
+ loader: () => {},
+ };
+ },
+ },
+ ];
process.env.ICE_CORE_ROUTER = '';
await runClientApp({
app: {},
// @ts-ignore don't need to pass params in test case.
- createRoutes: () => basicRoutes,
+ createRoutes: () => sigleRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
process.env.ICE_CORE_ROUTER = 'true';
expect(domstring).toBe('home
');
});
- it('run client with wrapper', async () => {
- await runClientApp({
- app: {},
- routes: basicRoutes,
- runtimeModules: { commons: [serverRuntime, wrapperRuntime] },
- hydrate: true,
- });
- expect(domstring).toBe('');
- });
it('run client with app provider', async () => {
await runClientApp({
app: {},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime, providerRuntmie] },
hydrate: true,
});
expect(domstring).toBe('');
});
- it('run client with empty route', async () => {
- await runClientApp({
- app: {},
- routes: [],
- runtimeModules: { commons: [serverRuntime] },
- hydrate: false,
- });
- });
-
it('run client with memory router', async () => {
const routes = [...basicRoutes, {
id: 'about',
path: '/about',
componentName: 'About',
- load: async () => ({
- default: () => {
- return (
- about
- );
- },
- }),
- }];
+ Component: () => {
+ return (
+ about
+ );
+ } }];
await runClientApp({
app: {
default: {
@@ -193,7 +179,8 @@ describe('run client app', () => {
},
},
},
- routes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => routes,
runtimeModules: { commons: [serverRuntime] },
hydrate: true,
});
@@ -204,7 +191,8 @@ describe('run client app', () => {
default: {
},
},
- routes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: true,
});
@@ -221,18 +209,16 @@ describe('run client app', () => {
id: 'about',
path: '/about',
componentName: 'About',
- load: async () => ({
- default: () => {
- return (
- about
- );
- },
- }),
- }];
+ Component: () => {
+ return (
+ about
+ );
+ } }];
await runClientApp({
app: {
},
- routes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => routes,
runtimeModules: { commons: [serverRuntime] },
hydrate: true,
memoryRouter: true,
@@ -250,7 +236,8 @@ describe('run client app', () => {
},
},
},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: true,
});
@@ -266,7 +253,8 @@ describe('run client app', () => {
return { msg: '-getAppData' };
},
},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
@@ -294,7 +282,8 @@ describe('run client app', () => {
return { msg: 'app' };
},
},
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
@@ -312,75 +301,11 @@ describe('run client app', () => {
},
},
},
- routes: [{
- id: 'home',
- path: '/',
- componentName: 'Home',
- load: async () => ({
- default: () => {
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const config = useConfig();
- // eslint-disable-next-line react-hooks/rules-of-hooks
- const data = useData();
- return (
- home{data?.data}{config.title}
- );
- },
- pageConfig: () => ({ title: 'home' }),
- dataLoader: async () => ({ data: 'test' }),
- }),
- }],
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
runtimeModules: { commons: [serverRuntime] },
hydrate: false,
});
- expect(domstring).toBe('hometesthome
');
- });
-
- it('load next page', async () => {
- const indexPage = {
- default: () => <>>,
- pageConfig: () => ({ title: 'index' }),
- dataLoader: async () => ({ type: 'getDataIndex' }),
- };
- const aboutPage = {
- default: () => <>>,
- pageConfig: () => ({ title: 'about' }),
- dataLoader: async () => ({ type: 'getDataAbout' }),
- };
- const mockedModules = [
- {
- id: 'index',
- load: async () => {
- return indexPage;
- },
- },
- {
- id: 'about',
- load: async () => {
- return aboutPage;
- },
- },
- ];
- const { routesData, routesConfig, routeModules } = await loadNextPage(
- // @ts-ignore
- [{ route: mockedModules[0] }],
- {
- // @ts-ignore
- matches: [{ route: mockedModules[1] }],
- routesData: {},
- routeModules: {},
- },
- );
- expect(routesData).toStrictEqual({
- index: { type: 'getDataIndex' },
- });
- expect(routesConfig).toStrictEqual({
- index: {
- title: 'index',
- },
- });
- expect(routeModules).toStrictEqual({
- index: indexPage,
- });
+ expect(domstring).toBe('home
');
});
});
diff --git a/packages/runtime/tests/runServerApp.test.tsx b/packages/runtime/tests/runServerApp.test.tsx
index b5342d8f68..bfad032c4c 100644
--- a/packages/runtime/tests/runServerApp.test.tsx
+++ b/packages/runtime/tests/runServerApp.test.tsx
@@ -5,23 +5,32 @@ import React from 'react';
import { expect, it, describe } from 'vitest';
import { renderToHTML, renderToResponse } from '../src/runServerApp';
import { Meta, Title, Links, Main, Scripts } from '../src/Document';
+import {
+ createRouteLoader,
+} from '../src/routes.js';
describe('run server app', () => {
process.env.ICE_CORE_ROUTER = 'true';
+ const homeItem = {
+ default: () => home
,
+ pageConfig: () => ({ title: 'home' }),
+ dataLoader: async () => ({ data: 'test' }),
+ };
const basicRoutes = [
{
id: 'home',
path: 'home',
componentName: 'home',
- load: async () => ({
- default: () => {
- return (
- home
- );
- },
- pageConfig: () => ({ title: 'home' }),
- getData: async () => ({ data: 'test' }),
- }),
+ lazy: () => {
+ return {
+ Component: homeItem.default,
+ loader: createRouteLoader({
+ routeId: 'home',
+ module: homeItem,
+ renderMode: 'SSR',
+ }),
+ };
+ },
},
];
@@ -61,7 +70,8 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
renderMode: 'SSR',
});
@@ -81,7 +91,8 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
renderMode: 'SSR',
basename: '/ice',
@@ -100,7 +111,8 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
renderMode: 'SSR',
serverOnlyBasename: '/',
@@ -120,7 +132,8 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
});
// @ts-ignore
@@ -143,7 +156,8 @@ describe('run server app', () => {
},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
});
// @ts-ignore
@@ -162,18 +176,24 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: [{
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => [{
id: 'home',
path: 'home',
componentName: 'Home',
- load: async () => ({
- default: () => {
- throw new Error('err');
- return (
- home
- );
- },
- }),
+ lazy: () => {
+ return {
+ Component: () => {
+ throw new Error('err');
+ return (
+ home
+ );
+ },
+ loader: () => {
+ return { data: {}, pageConfig: {} };
+ },
+ };
+ },
}],
Document,
});
@@ -201,7 +221,8 @@ describe('run server app', () => {
app: {},
assetsManifest,
runtimeModules: { commons: [] },
- routes: basicRoutes,
+ // @ts-ignore don't need to pass params in test case.
+ createRoutes: () => basicRoutes,
Document,
renderMode: 'SSR',
routePath: '/',
@@ -210,4 +231,4 @@ describe('run server app', () => {
expect(!!htmlContent).toBe(true);
expect(htmlContent.includes('home
=14.19.0', npm: '>=3.0.0'}
+ hasBin: true
+ peerDependencies:
+ react: '>=18.0.0'
+ react-dom: '>=18.0.0'
+ dependencies:
+ '@babel/generator': 7.18.10
+ '@babel/parser': 7.18.10
+ '@babel/traverse': 7.18.10
+ '@babel/types': 7.18.10
+ '@ice/bundles': 0.1.8
+ '@ice/route-manifest': 1.1.1
+ '@ice/runtime': 1.1.5
+ '@ice/webpack-config': 1.0.12
+ '@swc/helpers': 0.4.14
+ '@types/express': 4.17.17
+ address: 1.2.2
+ build-scripts: 2.1.0
+ chalk: 4.1.2
+ commander: 9.5.0
+ consola: 2.15.3
+ cross-spawn: 7.0.3
+ detect-port: 1.5.1
+ dotenv: 16.0.3
+ dotenv-expand: 8.0.3
+ ejs: 3.1.8
+ estree-walker: 3.0.3
+ fast-glob: 3.2.12
+ find-up: 5.0.0
+ fs-extra: 10.1.0
+ micromatch: 4.0.5
+ mlly: 1.1.1
+ mrmime: 1.0.1
+ open: 8.4.2
+ path-to-regexp: 6.2.1
+ regenerator-runtime: 0.13.11
+ resolve.exports: 1.1.1
+ semver: 7.3.8
+ temp: 0.9.4
+ yargs-parser: 21.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@ice/app/3.1.6_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-ch0UwX9VJikgg+cd9Yh9KUtkdbZVzTmm1vSZU/HP3fzsLugTWkxQz1LoULIS0hAfUCvrruzvCaXqGKoLeqfHpw==}
+ engines: {node: '>=14.19.0', npm: '>=3.0.0'}
+ hasBin: true
+ peerDependencies:
+ react: '>=18.0.0'
+ react-dom: '>=18.0.0'
+ dependencies:
+ '@babel/generator': 7.18.10
+ '@babel/parser': 7.18.10
+ '@babel/traverse': 7.18.10
+ '@babel/types': 7.18.10
+ '@ice/bundles': 0.1.8
+ '@ice/route-manifest': 1.1.1
+ '@ice/runtime': 1.1.5_biqbaboplfbrettd7655fr4n2y
+ '@ice/webpack-config': 1.0.12
+ '@swc/helpers': 0.4.14
+ '@types/express': 4.17.17
+ address: 1.2.2
+ build-scripts: 2.1.0
+ chalk: 4.1.2
+ commander: 9.5.0
+ consola: 2.15.3
+ cross-spawn: 7.0.3
+ detect-port: 1.5.1
+ dotenv: 16.0.3
+ dotenv-expand: 8.0.3
+ ejs: 3.1.8
+ estree-walker: 3.0.3
+ fast-glob: 3.2.12
+ find-up: 5.0.0
+ fs-extra: 10.1.0
+ micromatch: 4.0.5
+ mlly: 1.1.1
+ mrmime: 1.0.1
+ open: 8.4.2
+ path-to-regexp: 6.2.1
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ regenerator-runtime: 0.13.11
+ resolve.exports: 1.1.1
+ semver: 7.3.8
+ temp: 0.9.4
+ yargs-parser: 21.1.1
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@ice/bundles/0.1.8:
+ resolution: {integrity: sha512-cjaUkwcVkVO5EOdZqyFyDFYAPs5nnNXdOVRAY3CegBMOxy8K5MgoQO5xJkOMmphUSm+X0pbb0xUza1b9wR2fYA==}
+ dependencies:
+ '@ice/swc-plugin-keep-export': 0.1.4
+ '@ice/swc-plugin-node-transform': 0.1.0-5
+ '@ice/swc-plugin-remove-export': 0.1.2
+ '@swc/core': 1.3.19
+ ansi-html-community: 0.0.8
+ caniuse-lite: 1.0.30001462
+ chokidar: 3.5.3
+ core-js: 3.29.1
+ core-js-pure: 3.29.0
+ error-stack-parser: 2.1.4
+ esbuild: 0.16.17
+ events: 3.3.0
+ html-entities: 2.3.3
+ jest-worker: 27.5.1
+ less: 4.1.2
+ postcss: 8.4.12
+ react-refresh: 0.14.0
+ sass: 1.50.0
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
+ /@ice/jsx-runtime/0.2.0:
+ resolution: {integrity: sha512-yUfoWloQq2mdyCTsTTWwxtx0dmiwC4Xe2QEaQCg/yFqQKz7hOFBOrju6RodMl7K0fdpj6k1UBfjLd9gPyUBAbw==}
+ peerDependencies:
+ react: ^16 || ^17 || ^18
+ dependencies:
+ style-unit: 3.0.5
+ dev: true
+
+ /@ice/jsx-runtime/0.2.0_react@18.2.0:
+ resolution: {integrity: sha512-yUfoWloQq2mdyCTsTTWwxtx0dmiwC4Xe2QEaQCg/yFqQKz7hOFBOrju6RodMl7K0fdpj6k1UBfjLd9gPyUBAbw==}
+ peerDependencies:
+ react: ^16 || ^17 || ^18
+ dependencies:
+ react: 18.2.0
+ style-unit: 3.0.5
+
/@ice/pkg-plugin-component/1.0.0:
resolution: {integrity: sha512-Mff6Em1RwY2NOZgciQyy2NXL65u9ebHV2Jqb5746vUVEh0l7Xpokb+3djV1pKcgED4wU8pSuSQ6jj+hXd9Ht1Q==}
dependencies:
@@ -5147,6 +5284,56 @@ packages:
- ts-node
dev: true
+ /@ice/route-manifest/1.1.1:
+ resolution: {integrity: sha512-dmbSO13Dobzm7o56qTqavVG0yJQNN6+u1cG3jt3tZK9XjZgpEffdPDGkYPhRx732eR970W3J5qpYrOZvH+bTaw==}
+ dependencies:
+ minimatch: 5.1.6
+ dev: true
+
+ /@ice/runtime/1.1.5:
+ resolution: {integrity: sha512-QodI80xuhsPk8+q4Jprc9YXJ6xrsrtxLwBMa7zozvuo7lz0eycSahGukOnUJu7BB+ip6iBqID6q+yAxkQbMzmw==}
+ peerDependencies:
+ react: ^18.1.0
+ react-dom: ^18.1.0
+ dependencies:
+ '@ice/jsx-runtime': 0.2.0
+ ejs: 3.1.8
+ fs-extra: 10.1.0
+ history: 5.3.0
+ htmlparser2: 8.0.1
+ react-router-dom: 6.10.0
+ dev: true
+
+ /@ice/runtime/1.1.5_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-QodI80xuhsPk8+q4Jprc9YXJ6xrsrtxLwBMa7zozvuo7lz0eycSahGukOnUJu7BB+ip6iBqID6q+yAxkQbMzmw==}
+ peerDependencies:
+ react: ^18.1.0
+ react-dom: ^18.1.0
+ dependencies:
+ '@ice/jsx-runtime': 0.2.0_react@18.2.0
+ ejs: 3.1.8
+ fs-extra: 10.1.0
+ history: 5.3.0
+ htmlparser2: 8.0.1
+ react: 18.2.0
+ react-dom: 18.2.0_react@18.2.0
+ react-router-dom: 6.10.0_biqbaboplfbrettd7655fr4n2y
+
+ /@ice/runtime/1.1.5_react@18.2.0:
+ resolution: {integrity: sha512-QodI80xuhsPk8+q4Jprc9YXJ6xrsrtxLwBMa7zozvuo7lz0eycSahGukOnUJu7BB+ip6iBqID6q+yAxkQbMzmw==}
+ peerDependencies:
+ react: ^18.1.0
+ react-dom: ^18.1.0
+ dependencies:
+ '@ice/jsx-runtime': 0.2.0_react@18.2.0
+ ejs: 3.1.8
+ fs-extra: 10.1.0
+ history: 5.3.0
+ htmlparser2: 8.0.1
+ react: 18.2.0
+ react-router-dom: 6.10.0_react@18.2.0
+ dev: false
+
/@ice/sandbox/1.1.4:
resolution: {integrity: sha512-MEVF0Ze3McKDutnFiUAhUoc+WwOFxITVBgSSHmbGpKtWbXJX9kUVlx3VsEVJvdqU3O1kiBNx6zE1sFMjKPRTIQ==}
dev: false
@@ -5183,18 +5370,43 @@ packages:
- react-native
dev: false
+ /@ice/swc-plugin-keep-export/0.1.4:
+ resolution: {integrity: sha512-fOc09KALmL2zJK1xNGTEt/C27mXL7NVn/v1eRjjuM4uer+qmWIxYXIa9dpfTX5ZUn8zXhrKH8lGdczoKHCzyQQ==}
+ dev: true
+
/@ice/swc-plugin-keep-export/0.1.5-0:
resolution: {integrity: sha512-huUXmRHLL5nz6x9gxSFwsHn8EAKveGFaFqW0o3rce+aSFBxAWFM/f6NyFbQzYCF+NNMn5YDgQUOz5wtiYcHlwg==}
dev: false
+ /@ice/swc-plugin-node-transform/0.1.0-5:
+ resolution: {integrity: sha512-YCZUQwS4r9kjF2RDaPsChP+SEfWHecq2uqwCkZ4+akjL0hZBxbxkN0kXCRMV2O0rNDSBfMpUUdBeAcrCuFuxHw==}
+ dev: true
+
/@ice/swc-plugin-node-transform/0.1.1-1:
resolution: {integrity: sha512-eqgqor4w1uqysDskVL82LEoQPk00Q5fZIk3YWVQGHgCwOmu/kDH4Gx0XLT3hUo+u6la8rF9AtPHMmVQ8w79tRQ==}
dev: false
+ /@ice/swc-plugin-remove-export/0.1.2:
+ resolution: {integrity: sha512-HPeYj+z1ylaD5fJkSqyJ+eXbrHiCdy/t/t56uyf20aqsAyx12EiHVnfV4blW31DSWhFt/veAUXYzbaJ8b9KLOQ==}
+ dev: true
+
/@ice/swc-plugin-remove-export/0.1.3-0:
resolution: {integrity: sha512-fLa+qSmK3iGqk8FdUHMkjv5oTvwiQIkLTVLs3o4uQbphfsRJE1eWam0IHA/vFs7WzoE3m3pwMcDv3LUOAhAysA==}
dev: false
+ /@ice/webpack-config/1.0.12:
+ resolution: {integrity: sha512-8o7E1w1virHm82bhdxsftzi+epB/avwOIzySvt5pmYc0eGdQ1hjYHYIBtnOs3Jvw6JE/DXIOf4KRqsufqqTddA==}
+ dependencies:
+ '@ice/bundles': 0.1.8
+ '@rollup/pluginutils': 4.2.1
+ browserslist: 4.21.5
+ consola: 2.15.3
+ fast-glob: 3.2.12
+ process: 0.11.10
+ transitivePeerDependencies:
+ - supports-color
+ dev: true
+
/@iceworks/generate-project/2.0.2:
resolution: {integrity: sha512-t7/uHl5kM71o+xyR+FnaPsgyFqhFQm89TdqPahM4Kv/ubdKDknFVUYLio1khMDGY8Ops0ahn/+KM+gFnHEKSQw==}
engines: {node: '>=12.20.0'}
@@ -6045,10 +6257,42 @@ packages:
engines: {node: '>=14'}
dev: false
- /@remix-run/router/1.4.0:
- resolution: {integrity: sha512-BJ9SxXux8zAg991UmT8slpwpsd31K1dHHbD3Ba4VzD+liLQ4WAMSxQp2d2ZPRPfN0jN2NPRowcSSoM7lCaF08Q==}
+ /@remix-run/router/1.5.0:
+ resolution: {integrity: sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==}
engines: {node: '>=14'}
+ /@remix-run/web-blob/3.0.4:
+ resolution: {integrity: sha512-AfegzZvSSDc+LwnXV+SwROTrDtoLiPxeFW+jxgvtDAnkuCX1rrzmVJ6CzqZ1Ai0bVfmJadkG5GxtAfYclpPmgw==}
+ dependencies:
+ '@remix-run/web-stream': 1.0.3
+ web-encoding: 1.1.5
+ dev: true
+
+ /@remix-run/web-fetch/4.3.3:
+ resolution: {integrity: sha512-DK9vA2tgsadcFPpxW4fvN198tiWpyPhwR0EYOuM4QjpDCz0G619c9RDMdyMy6a7Qb/jwiyx9SOPHWc65QAl+1g==}
+ engines: {node: ^10.17 || >=12.3}
+ dependencies:
+ '@remix-run/web-blob': 3.0.4
+ '@remix-run/web-form-data': 3.0.4
+ '@remix-run/web-stream': 1.0.3
+ '@web3-storage/multipart-parser': 1.0.0
+ abort-controller: 3.0.0
+ data-uri-to-buffer: 3.0.1
+ mrmime: 1.0.1
+ dev: true
+
+ /@remix-run/web-form-data/3.0.4:
+ resolution: {integrity: sha512-UMF1jg9Vu9CLOf8iHBdY74Mm3PUvMW8G/XZRJE56SxKaOFWGSWlfxfG+/a3boAgHFLTkP7K4H1PxlRugy1iQtw==}
+ dependencies:
+ web-encoding: 1.1.5
+ dev: true
+
+ /@remix-run/web-stream/1.0.3:
+ resolution: {integrity: sha512-wlezlJaA5NF6SsNMiwQnnAW6tnPzQ5I8qk0Y0pSohm0eHKa2FQ1QhEKLVVcDDu02TmkfHgnux0igNfeYhDOXiA==}
+ dependencies:
+ web-streams-polyfill: 3.2.1
+ dev: true
+
/@rollup/plugin-alias/3.1.9_rollup@2.79.1:
resolution: {integrity: sha512-QI5fsEvm9bDzt32k39wpOwZhVzRcL5ydcffUHMyLVaVaLeC70I8TJZ17F1z1eMoLu4E/UOcH9BWVkKpIKdrfiw==}
engines: {node: '>=8.0.0'}
@@ -6411,6 +6655,15 @@ packages:
transitivePeerDependencies:
- supports-color
+ /@swc/core-darwin-arm64/1.3.19:
+ resolution: {integrity: sha512-6xLtmXzS4nNWGQkajbiAjGXspUJfxS2IWoGQ16J9nfOFdttKyoIG5o5+mxUfKeg5bXw9cI+r675kN/irx3z7MQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-darwin-arm64/1.3.41:
resolution: {integrity: sha512-D4fybODToO/BvuP35bionDUrSuTVVr8eW+mApr1unOqb3mfiqOrVv0VP2fpWNRYiA+xMq+oBCB6KcGpL60HKWQ==}
engines: {node: '>=10'}
@@ -6419,6 +6672,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-darwin-x64/1.3.19:
+ resolution: {integrity: sha512-qCDQcngYBeWrsNS1kcBslRD0dahKcYKaUUWRC9yHpRcs3SRvnSpJyWQR4y9RCdO9YNmixJ9+5+zPD9qcgL7jBw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-darwin-x64/1.3.41:
resolution: {integrity: sha512-0RoVyiPCnylf3TG77C3S86PRSmaq+SaYB4VDLJFz3qcEHz1pfP0LhyskhgX4wjQV1mveDzFEn1BVAuo0eOMwZA==}
engines: {node: '>=10'}
@@ -6427,6 +6689,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm-gnueabihf/1.3.19:
+ resolution: {integrity: sha512-ufbKW6Lhii1+kVCXnsHgqYIpRvXhPjdhMudfP4KKVgJtT6TsdEIr+KRAQIBHLjRUsTKA2DLsGEpu9jfjwFiNEg==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm-gnueabihf/1.3.41:
resolution: {integrity: sha512-mZW7GeY7Uw1nkKoWpx898ou20oCSt8MR+jAVuAhMjX+G4Zr0WWXYSigWNiRymhR6Q9KhyvoFpMckguSvYWmXsw==}
engines: {node: '>=10'}
@@ -6435,6 +6706,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm64-gnu/1.3.19:
+ resolution: {integrity: sha512-HHhqLRZv9Ss8orJrlEP4XRcLuqLDwFtGgbtHU8kyWBmQEtK42uT18Pf5RJBo5sPJHY8m5EO8C8y3hIbGmKtLyg==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm64-gnu/1.3.41:
resolution: {integrity: sha512-e91LGn+6KuLFw3sWk5swwGc/dP4tXs0mg3HrhjImRoofU02Bb9aHcj5zgrSO8ZByvDtm/Knn16h1ojxIMOFaxg==}
engines: {node: '>=10'}
@@ -6443,6 +6723,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-arm64-musl/1.3.19:
+ resolution: {integrity: sha512-vipnF3C6T1368uHQqz8RpdszWxxGh0X8VBK3TdTOSWvI/duNZtZXEOZlB2Nh9w+u09umVw0MsJhvg86Aon39mA==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-arm64-musl/1.3.41:
resolution: {integrity: sha512-Q7hmrniLWsQ7zjtImGcjx1tl5/Qxpel+fC+OXTnGvAyyoGssSftIBlXMnqVLteL78zhxIPAzi+gizWAe5RGqrA==}
engines: {node: '>=10'}
@@ -6451,6 +6740,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-x64-gnu/1.3.19:
+ resolution: {integrity: sha512-dUbq8mnIqBhU7OppfY3ncOvl26691WFGxd97QtnnlfMZrKnaofKFMIxE9sTHOLSbBo16AylnEMiwa45w2UWDEg==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-x64-gnu/1.3.41:
resolution: {integrity: sha512-h4sv1sCfZQgRIwmykz8WPqVpbvHb13Qm3SsrbOudhAp2MuzpWzsgMP5hAEpdCP/nWreiCz3aoM6L8JeakRDq0g==}
engines: {node: '>=10'}
@@ -6459,6 +6757,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-linux-x64-musl/1.3.19:
+ resolution: {integrity: sha512-RiVZrlkNGcj9jZyjF7YFOW3fj9fWPC25AYkknLpWxAmLQcp1piAWj+aSixmMWUC4QJau78VZzcm+kRgIOECALw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-linux-x64-musl/1.3.41:
resolution: {integrity: sha512-Z7c26i38378d0NT/dcz8qPSAXm41lqhNzykdhKhI+95mA9m4pskP18T/0I45rmyx1ywifypu+Ip+SXmKeVSPgQ==}
engines: {node: '>=10'}
@@ -6467,6 +6774,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-arm64-msvc/1.3.19:
+ resolution: {integrity: sha512-r2U6GC+go2iiLx5JBZIJswYFiMv0yOsm+pgE1srVvAc8dP02320t9yh0Uj4Sr2hDipTWJ33Y5PMZwEsZSfBVbQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-arm64-msvc/1.3.41:
resolution: {integrity: sha512-I0CYnPc+ZGc912YeN0TykIOf/Q7yJQHRwDuhewwD6RkbiSEaVfSux5pAmmdoKw2aGMSq+cwLmgPe9HYLRNz+4w==}
engines: {node: '>=10'}
@@ -6475,6 +6791,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-ia32-msvc/1.3.19:
+ resolution: {integrity: sha512-SPpESDa4vr0PRvUiqXSi8oZSTmkDOGrZ/pSiLD7ISgjsQ5RQMbPkuEK0ztWljim87q2fO0bGVVhyaVYxdOVS1A==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-ia32-msvc/1.3.41:
resolution: {integrity: sha512-EygN4CVDWF29/U2T5fXGfWyLvRbMd2hiUgkciAl7zHuyJ6nKl+kpodqV2A0Wd4sFtSNedU0gQEBEXEe7cqvmsA==}
engines: {node: '>=10'}
@@ -6483,6 +6808,15 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core-win32-x64-msvc/1.3.19:
+ resolution: {integrity: sha512-0X5HqFC1wQlheOQDZeF6KNOSURZKkGISNK3aTSmTq9g7dDJ/kTcVjsdKbu2rK4ibCnlC9IS0cLK9FpROnsVPwA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+ requiresBuild: true
+ dev: true
+ optional: true
+
/@swc/core-win32-x64-msvc/1.3.41:
resolution: {integrity: sha512-Mfp8qD1hNwWWRy0ISdwQJu1g0UYoVTtuQlO0z3aGbXqL51ew9e56+8j3M1U9i95lXFyWkARgjDCcKkQi+WezyA==}
engines: {node: '>=10'}
@@ -6491,6 +6825,24 @@ packages:
requiresBuild: true
optional: true
+ /@swc/core/1.3.19:
+ resolution: {integrity: sha512-KiXUv2vpmOaGhoLCN9Rw7Crsfq1YmOR2ZbajiqNAh/iu0d3CKn5JZhLRs6S7nCk78cwFFac2obQfTWPePLUe/g==}
+ engines: {node: '>=10'}
+ hasBin: true
+ requiresBuild: true
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.3.19
+ '@swc/core-darwin-x64': 1.3.19
+ '@swc/core-linux-arm-gnueabihf': 1.3.19
+ '@swc/core-linux-arm64-gnu': 1.3.19
+ '@swc/core-linux-arm64-musl': 1.3.19
+ '@swc/core-linux-x64-gnu': 1.3.19
+ '@swc/core-linux-x64-musl': 1.3.19
+ '@swc/core-win32-arm64-msvc': 1.3.19
+ '@swc/core-win32-ia32-msvc': 1.3.19
+ '@swc/core-win32-x64-msvc': 1.3.19
+ dev: true
+
/@swc/core/1.3.41:
resolution: {integrity: sha512-v6P2dfqJDpZ/7RXPvWge9oI6YgolDM0jtNhQZ2qdXrLBzaWQdDoBGBTJ8KN/nTgGhX3IkNvSB1fafXQ+nVnqAQ==}
engines: {node: '>=10'}
@@ -6511,7 +6863,6 @@ packages:
resolution: {integrity: sha512-4C7nX/dvpzB7za4Ql9K81xK3HPxCpHMgwTZVyf+9JQ6VUbn9jjZVN7/Nkdz/Ugzs2CSjqnL/UPXroiVBVHUWUw==}
dependencies:
tslib: 2.5.0
- dev: false
/@szmarczak/http-timer/1.1.2:
resolution: {integrity: sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==}
@@ -7205,7 +7556,6 @@ packages:
/@uni/env/1.1.0:
resolution: {integrity: sha512-2GVgUzxIaO2vGElXEuc45+I7L6Jbw8inLDDFuC0K4htjKtPmYywKSE6oDhvmdAXb4GCOH8hmxECYtAh1rjsgoQ==}
- dev: false
/@use-gesture/core/10.2.20:
resolution: {integrity: sha512-4lFhHc8so4yIHkBEs641DnEsBxPyhJ5GEjB4PURFDH4p/FcZriH6w99knZgI63zN/MBFfylMyb8+PDuj6RIXKQ==}
@@ -7260,6 +7610,10 @@ packages:
- terser
dev: true
+ /@web3-storage/multipart-parser/1.0.0:
+ resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==}
+ dev: true
+
/@webassemblyjs/ast/1.11.1:
resolution: {integrity: sha512-ukBh14qFLjxTQNTXocdyksN5QdM28S1CxHt2rdskFyL+xFV7VremuBLVbmCePj+URalXBENx/9Lm7lnhihtCSw==}
dependencies:
@@ -7357,6 +7711,12 @@ packages:
/@xtuc/long/4.2.2:
resolution: {integrity: sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==}
+ /@zxing/text-encoding/0.9.0:
+ resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==}
+ requiresBuild: true
+ dev: true
+ optional: true
+
/JSONStream/1.3.5:
resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==}
hasBin: true
@@ -7369,6 +7729,13 @@ packages:
resolution: {integrity: sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==}
dev: true
+ /abort-controller/3.0.0:
+ resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==}
+ engines: {node: '>=6.5'}
+ dependencies:
+ event-target-shim: 5.0.1
+ dev: true
+
/accepts/1.3.8:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
@@ -7864,7 +8231,6 @@ packages:
/async/3.2.4:
resolution: {integrity: sha512-iAB+JbDEGXhyIUavoDl9WP/Jj106Kz9DEn1DPgYw5ruDn0e3Wgi3sKFm55sASdGBNOQB8F59d9qQ7deqrHA8wQ==}
- dev: false
/asynckit/0.4.0:
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
@@ -9603,6 +9969,11 @@ packages:
assert-plus: 1.0.0
dev: false
+ /data-uri-to-buffer/3.0.1:
+ resolution: {integrity: sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==}
+ engines: {node: '>= 6'}
+ dev: true
+
/data-urls/3.0.2:
resolution: {integrity: sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==}
engines: {node: '>=12'}
@@ -10018,12 +10389,10 @@ packages:
/dotenv-expand/8.0.3:
resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==}
engines: {node: '>=12'}
- dev: false
/dotenv/16.0.3:
resolution: {integrity: sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==}
engines: {node: '>=12'}
- dev: false
/dts-bundle/0.7.3:
resolution: {integrity: sha512-EEAEuPRk8QyKhoN90NHTh+spSQujkkvOnKWUfuzpmC/fgryiWopL1SegSktx0UsoPfNidIGVDN7/AXpBDBv0WQ==}
@@ -10065,7 +10434,6 @@ packages:
hasBin: true
dependencies:
jake: 10.8.5
- dev: false
/electron-to-chromium/1.4.322:
resolution: {integrity: sha512-KovjizNC9XB7dno/2GjxX8VS0SlfPpCjtyoKft+bCO+UfD8bFy16hY4Sh9s0h9BDxbRH2U0zX5VBjpM1LTcNlg==}
@@ -11080,7 +11448,6 @@ packages:
resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==}
dependencies:
'@types/estree': 1.0.0
- dev: false
/esutils/2.0.3:
resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
@@ -11101,6 +11468,11 @@ packages:
'@types/node': 17.0.45
require-like: 0.1.2
+ /event-target-shim/5.0.1:
+ resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==}
+ engines: {node: '>=6'}
+ dev: true
+
/eventemitter3/4.0.7:
resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==}
@@ -11379,7 +11751,6 @@ packages:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
minimatch: 5.1.6
- dev: false
/filesize/8.0.7:
resolution: {integrity: sha512-pjmC+bkIF8XI7fWaH8KxHcZL3DPybs1roSKP4rKDvy20tAWwIObE4+JIseG2byfGKhud5ZnM4YSGKBz7Sh0ndQ==}
@@ -13050,7 +13421,6 @@ packages:
chalk: 4.1.2
filelist: 1.0.4
minimatch: 3.1.2
- dev: false
/jest-changed-files/28.1.3:
resolution: {integrity: sha512-esaOfUWJXk2nfZt9SPyC8gA1kNfdKLkQWyzsMlqq8msYSlNKfmZxfRgZn4Cd4MGVUF+7v6dBs0d5TOAKa7iIiA==}
@@ -14105,7 +14475,6 @@ packages:
/jsonc-parser/3.2.0:
resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==}
- dev: false
/jsonfile/4.0.0:
resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
@@ -14850,7 +15219,6 @@ packages:
pathe: 1.1.0
pkg-types: 1.0.2
ufo: 1.1.1
- dev: false
/moment/2.29.4:
resolution: {integrity: sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==}
@@ -15422,7 +15790,6 @@ packages:
/path-to-regexp/6.2.1:
resolution: {integrity: sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw==}
- dev: false
/path-type/4.0.0:
resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
@@ -15430,7 +15797,6 @@ packages:
/pathe/1.1.0:
resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==}
- dev: false
/pathval/1.1.1:
resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==}
@@ -15492,7 +15858,6 @@ packages:
jsonc-parser: 3.2.0
mlly: 1.1.1
pathe: 1.1.0
- dev: false
/pkg-up/3.1.0:
resolution: {integrity: sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==}
@@ -16773,7 +17138,6 @@ packages:
/process/0.11.10:
resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==}
engines: {node: '>= 0.6.0'}
- dev: false
/progress/2.0.3:
resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==}
@@ -17781,17 +18145,39 @@ packages:
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
- /react-router-dom/6.9.0_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-/seUAPY01VAuwkGyVBPCn1OXfVbaWGGu4QN9uj0kCPcTyNYgL1ldZpxZUpRU7BLheKQI4Twtl/OW2nHRF1u26Q==}
+ /react-router-dom/6.10.0:
+ resolution: {integrity: sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==}
engines: {node: '>=14'}
peerDependencies:
react: '>=16.8'
react-dom: '>=16.8'
dependencies:
- '@remix-run/router': 1.4.0
+ '@remix-run/router': 1.5.0
+ react-router: 6.10.0
+ dev: true
+
+ /react-router-dom/6.10.0_biqbaboplfbrettd7655fr4n2y:
+ resolution: {integrity: sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+ dependencies:
+ '@remix-run/router': 1.5.0
react: 18.2.0
react-dom: 18.2.0_react@18.2.0
- react-router: 6.9.0_react@18.2.0
+ react-router: 6.10.0_react@18.2.0
+
+ /react-router-dom/6.10.0_react@18.2.0:
+ resolution: {integrity: sha512-E5dfxRPuXKJqzwSe/qGcqdwa18QiWC6f3H3cWXM24qj4N0/beCIf/CWTipop2xm7mR0RCS99NnaqPNjHtrAzCg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: '>=16.8'
+ react-dom: '>=16.8'
+ dependencies:
+ '@remix-run/router': 1.5.0
+ react: 18.2.0
+ react-router: 6.10.0_react@18.2.0
dev: false
/react-router/5.3.4_react@17.0.2:
@@ -17810,13 +18196,22 @@ packages:
tiny-invariant: 1.3.1
tiny-warning: 1.0.3
- /react-router/6.9.0_react@18.2.0:
- resolution: {integrity: sha512-51lKevGNUHrt6kLuX3e/ihrXoXCa9ixY/nVWRLlob4r/l0f45x3SzBvYJe3ctleLUQQ5fVa4RGgJOTH7D9Umhw==}
+ /react-router/6.10.0:
+ resolution: {integrity: sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ react: '>=16.8'
+ dependencies:
+ '@remix-run/router': 1.5.0
+ dev: true
+
+ /react-router/6.10.0_react@18.2.0:
+ resolution: {integrity: sha512-Nrg0BWpQqrC3ZFFkyewrflCud9dio9ME3ojHCF/WLsprJVzkq3q3UeEhMCAW1dobjeGbWgjNn/PVF6m46ANxXQ==}
engines: {node: '>=14'}
peerDependencies:
react: '>=16.8'
dependencies:
- '@remix-run/router': 1.4.0
+ '@remix-run/router': 1.5.0
react: 18.2.0
/react-textarea-autosize/8.4.0_h7fc2el62uaa77gho3xhys6ola:
@@ -18278,7 +18673,6 @@ packages:
hasBin: true
dependencies:
glob: 7.2.3
- dev: false
/rimraf/3.0.2:
resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==}
@@ -19153,7 +19547,6 @@ packages:
dependencies:
'@babel/runtime': 7.21.0
universal-env: 3.3.3
- dev: false
/stylehacks/5.1.1_postcss@8.4.12:
resolution: {integrity: sha512-sBpcd5Hx7G6seo7b1LkpttvTz7ikD0LlH5RmdcBNb6fFR0Fl7LQwHDFr300q4cwUqi+IYrFGmsIHieMBfnN/Bw==}
@@ -19413,7 +19806,6 @@ packages:
dependencies:
mkdirp: 0.5.6
rimraf: 2.6.3
- dev: false
/term-size/2.2.1:
resolution: {integrity: sha512-wK0Ri4fOGjv/XPy8SBHZChl8CM7uMc5VML7SqiQ0zG7+J5Vr+RMQDoHa2CNT6KHUnTGIXH34UDMkPzAUyapBZg==}
@@ -19452,6 +19844,7 @@ packages:
serialize-javascript: 6.0.1
terser: 5.14.2
webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
+ dev: true
/terser-webpack-plugin/5.3.5_ru5mwuzjydtus6p6kkdltzgpiq:
resolution: {integrity: sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==}
@@ -19476,7 +19869,6 @@ packages:
serialize-javascript: 6.0.1
terser: 5.14.2
webpack: 5.76.2_esbuild@0.16.17
- dev: true
/terser-webpack-plugin/5.3.5_webpack@5.76.2:
resolution: {integrity: sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==}
@@ -19919,7 +20311,6 @@ packages:
/ufo/1.1.1:
resolution: {integrity: sha512-MvlCc4GHrmZdAllBc0iUDowff36Q9Ndw/UzqmEKyrfSzokTd9ZCy1i+IIk5hrYKkjoYVQyNbrw7/F8XJ2rEwTg==}
- dev: false
/uglify-js/3.17.4:
resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==}
@@ -20059,7 +20450,6 @@ packages:
engines: {npm: '>=3.0.0'}
dependencies:
'@uni/env': 1.1.0
- dev: false
/universalify/0.1.2:
resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
@@ -20589,9 +20979,22 @@ packages:
dependencies:
defaults: 1.0.4
+ /web-encoding/1.1.5:
+ resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==}
+ dependencies:
+ util: 0.12.5
+ optionalDependencies:
+ '@zxing/text-encoding': 0.9.0
+ dev: true
+
/web-namespaces/1.1.4:
resolution: {integrity: sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw==}
+ /web-streams-polyfill/3.2.1:
+ resolution: {integrity: sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==}
+ engines: {node: '>= 8'}
+ dev: true
+
/webidl-conversions/3.0.1:
resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
@@ -20653,7 +21056,7 @@ packages:
mime-types: 2.1.35
range-parser: 1.2.1
schema-utils: 4.0.0
- webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
+ webpack: 5.76.2_esbuild@0.16.17
/webpack-dev-server/4.11.1_debug@4.3.4+webpack@5.76.2:
resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==}
@@ -20741,7 +21144,7 @@ packages:
serve-index: 1.9.1
sockjs: 0.3.24
spdy: 4.0.2
- webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
+ webpack: 5.76.2_esbuild@0.16.17
webpack-dev-middleware: 5.3.3_webpack@5.76.2
ws: 8.12.1
transitivePeerDependencies:
@@ -20850,7 +21253,6 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
- dev: true
/webpack/5.76.2_x5vjxkescd7wbsfiatu6dm4yia:
resolution: {integrity: sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==}
@@ -20890,6 +21292,7 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
+ dev: true
/webpackbar/5.0.2_webpack@5.76.2:
resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==}
From d7509af115915fb696c0087f3a755d1f91a38f79 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 11:35:08 +0800
Subject: [PATCH 14/38] fix: lock
---
pnpm-lock.yaml | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 8ec16e40bf..673a4cf261 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1288,7 +1288,7 @@ importers:
webpack: ^5.76.2
webpack-dev-server: ^4.9.2
dependencies:
- '@remix-run/router': 1.3.3
+ '@remix-run/router': 1.5.0
chalk: 4.1.2
consola: 2.15.3
htmlparser2: 8.0.1
@@ -6252,11 +6252,6 @@ packages:
react-dom: 18.2.0_react@18.2.0
dev: false
- /@remix-run/router/1.3.3:
- resolution: {integrity: sha512-YRHie1yQEj0kqqCTCJEfHqYSSNlZQ696QJG+MMiW4mxSl9I0ojz/eRhJS4fs88Z5i6D1SmoF9d3K99/QOhI8/w==}
- engines: {node: '>=14'}
- dev: false
-
/@remix-run/router/1.5.0:
resolution: {integrity: sha512-bkUDCp8o1MvFO+qxkODcbhSqRa6P2GXgrGZVpt0dCXNW2HCSCqYI0ZoAqEOSAjRWmmlKcYgFvN4B4S+zo/f8kg==}
engines: {node: '>=14'}
@@ -19844,7 +19839,6 @@ packages:
serialize-javascript: 6.0.1
terser: 5.14.2
webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
- dev: true
/terser-webpack-plugin/5.3.5_ru5mwuzjydtus6p6kkdltzgpiq:
resolution: {integrity: sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==}
@@ -19869,6 +19863,7 @@ packages:
serialize-javascript: 6.0.1
terser: 5.14.2
webpack: 5.76.2_esbuild@0.16.17
+ dev: true
/terser-webpack-plugin/5.3.5_webpack@5.76.2:
resolution: {integrity: sha512-AOEDLDxD2zylUGf/wxHxklEkOe2/r+seuyOWujejFrIxHf11brA1/dWQNIgXa1c6/Wkxgu7zvv0JhOWfc2ELEA==}
@@ -21056,7 +21051,7 @@ packages:
mime-types: 2.1.35
range-parser: 1.2.1
schema-utils: 4.0.0
- webpack: 5.76.2_esbuild@0.16.17
+ webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
/webpack-dev-server/4.11.1_debug@4.3.4+webpack@5.76.2:
resolution: {integrity: sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw==}
@@ -21144,7 +21139,7 @@ packages:
serve-index: 1.9.1
sockjs: 0.3.24
spdy: 4.0.2
- webpack: 5.76.2_esbuild@0.16.17
+ webpack: 5.76.2_x5vjxkescd7wbsfiatu6dm4yia
webpack-dev-middleware: 5.3.3_webpack@5.76.2
ws: 8.12.1
transitivePeerDependencies:
@@ -21253,6 +21248,7 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
+ dev: true
/webpack/5.76.2_x5vjxkescd7wbsfiatu6dm4yia:
resolution: {integrity: sha512-Th05ggRm23rVzEOlX8y67NkYCHa9nTNcwHPBhdg+lKG+mtiW7XgggjAeeLnADAe7mLjJ6LUNfgHAuRRh+Z6J7w==}
@@ -21292,7 +21288,6 @@ packages:
- '@swc/core'
- esbuild
- uglify-js
- dev: true
/webpackbar/5.0.2_webpack@5.76.2:
resolution: {integrity: sha512-BmFJo7veBDgQzfWXl/wwYXr/VFus0614qZ8i9znqcl9fnEdiVkdbi0TedLQ6xAK92HZHDJ0QmyQ0fmuZPAgCYQ==}
From a6bbef1ee41b8facd077fc563f51df824ca845a7 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Thu, 6 Apr 2023 11:44:54 +0800
Subject: [PATCH 15/38] feat: async data loader
---
examples/with-data-loader/package.json | 3 +-
.../src/pages/with-defer-loader.tsx | 50 ++++++++++++
.../src/pages/with-defer-loaders.tsx | 80 +++++++++++++++++++
.../with-data-loader/src/pages/with-ssr.tsx | 60 ++++++++++++++
packages/runtime/src/dataLoader.ts | 61 ++++++--------
packages/runtime/src/routes.tsx | 62 +++++++++++---
6 files changed, 265 insertions(+), 51 deletions(-)
create mode 100644 examples/with-data-loader/src/pages/with-defer-loader.tsx
create mode 100644 examples/with-data-loader/src/pages/with-defer-loaders.tsx
create mode 100644 examples/with-data-loader/src/pages/with-ssr.tsx
diff --git a/examples/with-data-loader/package.json b/examples/with-data-loader/package.json
index 4cdfb8420e..8ca8b75ad2 100644
--- a/examples/with-data-loader/package.json
+++ b/examples/with-data-loader/package.json
@@ -13,7 +13,8 @@
"@ice/app": "workspace:*",
"@ice/runtime": "workspace:*",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-router-dom": "^6.10.0"
},
"devDependencies": {
"@types/react": "^18.0.17",
diff --git a/examples/with-data-loader/src/pages/with-defer-loader.tsx b/examples/with-data-loader/src/pages/with-defer-loader.tsx
new file mode 100644
index 0000000000..2be2ca0838
--- /dev/null
+++ b/examples/with-data-loader/src/pages/with-defer-loader.tsx
@@ -0,0 +1,50 @@
+import { useData, defineDataLoader } from 'ice';
+import * as React from 'react';
+import { Await } from 'react-router-dom';
+import styles from './index.module.css';
+
+export default function Home() {
+ const data = useData();
+
+ return (
+ <>
+ With dataLoader
+ Loading item info...}
+ >
+ Error loading!
+ }
+ >
+ {(itemInfo) => {
+ return (
+
+ Item id is {itemInfo.id}
+
+ );
+}}
+
+
+ >
+ );
+}
+
+export function pageConfig() {
+ return {
+ title: 'Home',
+ };
+}
+
+export const dataLoader = defineDataLoader(async () => {
+ const promise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ id: 1233,
+ });
+ }, 100);
+ });
+ return await promise;
+}, { defer: true });
+
diff --git a/examples/with-data-loader/src/pages/with-defer-loaders.tsx b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
new file mode 100644
index 0000000000..ef49c93ebd
--- /dev/null
+++ b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
@@ -0,0 +1,80 @@
+import { useData, defineDataLoader } from 'ice';
+import * as React from 'react';
+import { Await } from 'react-router-dom';
+import styles from './index.module.css';
+
+export default function Home() {
+ const data = useData();
+
+ return (
+ <>
+ With dataLoader
+ Loading item info...}
+ >
+ Error loading!
+ }
+ >
+ {(itemInfo) => {
+ return (
+
+ Item id is {itemInfo.id}
+
+ );
+}}
+
+
+ Loading item info...}
+ >
+ Error loading!
+ }
+ >
+ {(itemInfo) => {
+ return (
+
+ Item price is {itemInfo.price}
+
+ );
+}}
+
+
+ >
+ );
+}
+
+export function pageConfig() {
+ return {
+ title: 'Home',
+ };
+}
+
+export const dataLoader = defineDataLoader([
+ async () => {
+ const promise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ id: 1233,
+ });
+ }, 100);
+ });
+ return await promise;
+ },
+ async () => {
+ const promise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ price: 9.99,
+ });
+ }, 2000);
+ });
+ return await promise;
+ },
+], { defer: true });
+
diff --git a/examples/with-data-loader/src/pages/with-ssr.tsx b/examples/with-data-loader/src/pages/with-ssr.tsx
new file mode 100644
index 0000000000..fe75f77874
--- /dev/null
+++ b/examples/with-data-loader/src/pages/with-ssr.tsx
@@ -0,0 +1,60 @@
+import { useData, defineDataLoader, defineServerDataLoader } from 'ice';
+import * as React from 'react';
+import { Await } from 'react-router-dom';
+import styles from './index.module.css';
+
+export default function Home() {
+ const data = useData();
+
+ return (
+ <>
+ With dataLoader
+ Loading item info...}
+ >
+ Error loading!
+ }
+ >
+ {(itemInfo) => {
+ return (
+
+ Item id is {itemInfo.id}
+
+ );
+}}
+
+
+ >
+ );
+}
+
+export function pageConfig() {
+ return {
+ title: 'Home',
+ };
+}
+
+export const dataLoader = defineDataLoader(async () => {
+ const promise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ id: 1233,
+ });
+ }, 100);
+ });
+ return await promise;
+}, { defer: true });
+
+export const serverDataLoader = defineServerDataLoader(async () => {
+ const promise = new Promise((resolve, reject) => {
+ setTimeout(() => {
+ resolve({
+ id: 1233,
+ });
+ }, 100);
+ });
+ return await promise;
+});
\ No newline at end of file
diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts
index dcb80936be..dfea439acd 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -7,7 +7,6 @@ interface Loaders {
interface CachedResult {
value: any;
- status: string;
}
interface LoaderOptions {
@@ -16,20 +15,26 @@ interface LoaderOptions {
appExport: AppExport;
}
+interface DefineOptions {
+ defer?: boolean;
+}
+
+type DefineResult = [DataLoaderConfig, DefineOptions?];
+
export interface LoadRoutesDataOptions {
renderMode: RenderMode;
}
-export function defineDataLoader(dataLoaderConfig: DataLoaderConfig): DataLoaderConfig {
- return dataLoaderConfig;
+export function defineDataLoader(dataLoaderConfig: DataLoaderConfig, options?: DefineOptions): DefineResult {
+ return [dataLoaderConfig, options];
}
-export function defineServerDataLoader(dataLoaderConfig: DataLoaderConfig): DataLoaderConfig {
- return dataLoaderConfig;
+export function defineServerDataLoader(dataLoaderConfig: DataLoaderConfig, options?: DefineOptions): DefineResult {
+ return [dataLoaderConfig, options];
}
-export function defineStaticDataLoader(dataLoaderConfig: DataLoaderConfig): DataLoaderConfig {
- return dataLoaderConfig;
+export function defineStaticDataLoader(dataLoaderConfig: DataLoaderConfig): DefineResult {
+ return [dataLoaderConfig];
}
/**
@@ -129,7 +134,8 @@ export function callDataLoader(dataLoader: DataLoaderConfig, requestContext: Req
const loaders = dataLoader.map(loader => {
return typeof loader === 'object' ? loadDataByCustomFetcher(loader) : loader(requestContext);
});
- return Promise.all(loaders);
+
+ return loaders;
}
if (typeof dataLoader === 'object') {
@@ -156,7 +162,6 @@ function loadInitialDataInClient(loaders: Loaders) {
if (dataFromSSR) {
cache.set(renderMode === 'SSG' ? `${id}_ssg` : id, {
value: dataFromSSR,
- status: 'RESOLVED',
});
if (renderMode === 'SSR') {
@@ -164,15 +169,14 @@ function loadInitialDataInClient(loaders: Loaders) {
}
}
- const dataLoader = loaders[id];
+ const dataLoaderConfig = loaders[id];
- if (dataLoader) {
+ if (dataLoaderConfig) {
const requestContext = getRequestContext(window.location);
- const loader = callDataLoader(dataLoader, requestContext);
+ const loader = callDataLoader(dataLoaderConfig[0], requestContext);
cache.set(id, {
value: loader,
- status: 'LOADING',
});
}
});
@@ -214,38 +218,20 @@ async function init(dataloaderConfig: Loaders, options: LoaderOptions) {
}
(window as any).__ICE_DATA_LOADER__ = {
- getData: async (id, options: LoadRoutesDataOptions) => {
+ getData: (id, options: LoadRoutesDataOptions) => {
let result;
- // first render for ssg use data from build time.
- // second render for ssg will use data from data loader.
+ // First render for ssg use data from build time, second render for ssg will use data from data loader.
const cacheKey = `${id}${options?.renderMode === 'SSG' ? '_ssg' : ''}`;
+
+ // In CSR, all dataLoader is called by global data loader to avoid bundle dataLoader in page bundle duplicate.
result = cache.get(cacheKey);
// Always fetch new data after cache is been used.
cache.delete(cacheKey);
// Already send data request.
if (result) {
- const { status, value } = result;
-
- if (status === 'RESOLVED') {
- return result;
- }
-
- try {
- if (Array.isArray(value)) {
- return await Promise.all(value);
- }
-
- return await value;
- } catch (error) {
- console.error('DataLoader: getData error.\n', error);
-
- return {
- message: 'DataLoader: getData error.',
- error,
- };
- }
+ return result.value;
}
const dataLoader = dataloaderConfig[id];
@@ -256,9 +242,8 @@ async function init(dataloaderConfig: Loaders, options: LoaderOptions) {
}
// Call dataLoader.
- // In CSR, all dataLoader is called by global data loader to avoid bundle dataLoader in page bundle duplicate.
const requestContext = getRequestContext(window.location);
- return await callDataLoader(dataLoader, requestContext);
+ return callDataLoader(dataLoader[0], requestContext);
},
};
}
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 18ffa47793..fe9327b59e 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,4 +1,5 @@
import React from 'react';
+import { defer } from 'react-router-dom';
import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
@@ -94,26 +95,63 @@ export interface RouteLoaderOptions {
renderMode: RenderMode;
}
-export function createRouteLoader(options: RouteLoaderOptions): () => Promise {
- return async () => {
- const { dataLoader, pageConfig, staticDataLoader, serverDataLoader } = options.module;
- const { requestContext, renderMode, routeId } = options;
+export function createRouteLoader(options: RouteLoaderOptions): () => any {
+ const { dataLoader, pageConfig, staticDataLoader, serverDataLoader } = options.module;
+ const { requestContext, renderMode, routeId } = options;
+
+ let loaderConfig: any;
+ if (renderMode === 'SSG') {
+ loaderConfig = staticDataLoader;
+ } else if (renderMode === 'SSR') {
+ loaderConfig = serverDataLoader || dataLoader;
+ } else {
+ loaderConfig = dataLoader;
+ }
+
+ const getData = () => {
const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__;
const globalLoader = hasGlobalLoader ? (window as any).__ICE_DATA_LOADER__ : null;
let routeData: any;
if (globalLoader) {
- routeData = await globalLoader.getData(routeId, { renderMode });
+ routeData = globalLoader.getData(routeId, { renderMode });
} else {
- let loader: DataLoaderConfig;
- if (renderMode === 'SSG') {
- loader = staticDataLoader;
- } else if (renderMode === 'SSR') {
- loader = serverDataLoader || dataLoader;
+ routeData = loaderConfig && callDataLoader(loaderConfig[0], requestContext);
+ }
+ return routeData;
+ };
+
+ if (loaderConfig?.[1]?.defer) {
+ return async () => {
+ const promise = getData();
+
+ return defer({
+ data: promise,
+ routeConfig: pageConfig ? pageConfig({}) : {},
+ });
+ };
+ }
+
+ return async () => {
+ let routeData;
+ const promise = getData();
+
+ try {
+ if (Array.isArray(promise)) {
+ routeData = await Promise.all(promise);
+ } else if (promise instanceof Promise) {
+ routeData = await promise;
} else {
- loader = dataLoader;
+ routeData = promise;
}
- routeData = loader && await callDataLoader(loader, requestContext);
+ } catch (error) {
+ console.error('DataLoader: getData error.\n', error);
+
+ routeData = {
+ message: 'DataLoader: getData error.',
+ error,
+ };
}
+
const routeConfig = pageConfig ? pageConfig({ data: routeData }) : {};
const loaderData = { data: routeData, pageConfig: routeConfig };
// CSR and load next route data.
From e1eb822b3a9266b19bfab363e4b01e49468f7834 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 12:13:03 +0800
Subject: [PATCH 16/38] fix: update lock
---
packages/miniapp-runtime/src/app/App.tsx | 2 +-
pnpm-lock.yaml | 160 ++---------------------
2 files changed, 13 insertions(+), 149 deletions(-)
diff --git a/packages/miniapp-runtime/src/app/App.tsx b/packages/miniapp-runtime/src/app/App.tsx
index 1e359cc69e..02cd818647 100644
--- a/packages/miniapp-runtime/src/app/App.tsx
+++ b/packages/miniapp-runtime/src/app/App.tsx
@@ -7,7 +7,7 @@ export default function App() {
const { strict, errorBoundary } = appConfig.app;
const StrictMode = strict ? React.StrictMode : React.Fragment;
const ErrorBoundary = errorBoundary ? AppErrorBoundary : React.Fragment;
- console.log('ErrorBoundary', ErrorBoundary);
+
return (
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index a04861b4f0..eeae601fba 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1155,7 +1155,7 @@ importers:
dependencies:
'@ice/style-import': link:../style-import
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
packages/plugin-auth:
specifiers:
@@ -1165,7 +1165,7 @@ importers:
'@types/react-dom': ^18.0.0
regenerator-runtime: ^0.13.9
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
'@ice/runtime': link:../runtime
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
@@ -1180,7 +1180,7 @@ importers:
consola: 2.15.3
extract-css-assets-webpack-plugin: 0.2.10
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
packages/plugin-fusion:
specifiers:
@@ -1189,7 +1189,7 @@ importers:
dependencies:
'@ice/style-import': link:../style-import
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
packages/plugin-icestark:
specifiers:
@@ -1203,7 +1203,7 @@ importers:
'@ice/stark': 2.7.5
'@ice/stark-app': 1.5.0
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
'@ice/runtime': link:../runtime
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
@@ -1231,7 +1231,7 @@ importers:
babel-plugin-transform-jsx-slot: 0.1.2
babel-runtime-jsx-plus: 0.1.5
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
@@ -1265,14 +1265,14 @@ importers:
regenerator-runtime: 0.11.1
sax: 1.2.4
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
webpack: 5.76.2
packages/plugin-moment-locales:
specifiers:
'@ice/app': ^3.1.2
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
packages/plugin-pha:
specifiers:
@@ -1295,7 +1295,7 @@ importers:
humps: 2.0.1
lodash.clonedeep: 4.5.0
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
build-scripts: 2.1.0
esbuild: 0.16.17
webpack: 5.76.2_esbuild@0.16.17
@@ -1327,7 +1327,7 @@ importers:
style-unit: 3.0.5
stylesheet-loader: 0.9.1
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
webpack: 5.76.2
packages/plugin-request:
@@ -1343,7 +1343,7 @@ importers:
ahooks: 3.7.5
axios: 0.27.2
devDependencies:
- '@ice/app': 3.1.6
+ '@ice/app': link:../ice
'@ice/runtime': link:../runtime
'@types/react': 18.0.28
'@types/react-dom': 18.0.11
@@ -1367,7 +1367,7 @@ importers:
fast-glob: 3.2.12
micromatch: 4.0.5
devDependencies:
- '@ice/app': 3.1.6_biqbaboplfbrettd7655fr4n2y
+ '@ice/app': link:../ice
'@ice/runtime': link:../runtime
'@types/micromatch': 4.0.2
'@types/react': 18.0.28
@@ -5105,142 +5105,6 @@ packages:
resolution: {integrity: sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==}
dev: true
- /@ice/app/3.1.6:
- resolution: {integrity: sha512-ch0UwX9VJikgg+cd9Yh9KUtkdbZVzTmm1vSZU/HP3fzsLugTWkxQz1LoULIS0hAfUCvrruzvCaXqGKoLeqfHpw==}
- engines: {node: '>=14.19.0', npm: '>=3.0.0'}
- hasBin: true
- peerDependencies:
- react: '>=18.0.0'
- react-dom: '>=18.0.0'
- dependencies:
- '@babel/generator': 7.18.10
- '@babel/parser': 7.18.10
- '@babel/traverse': 7.18.10
- '@babel/types': 7.18.10
- '@ice/bundles': 0.1.8
- '@ice/route-manifest': 1.1.1
- '@ice/runtime': 1.1.5
- '@ice/webpack-config': 1.0.12
- '@swc/helpers': 0.4.14
- '@types/express': 4.17.17
- address: 1.2.2
- build-scripts: 2.1.0
- chalk: 4.1.2
- commander: 9.5.0
- consola: 2.15.3
- cross-spawn: 7.0.3
- detect-port: 1.5.1
- dotenv: 16.0.3
- dotenv-expand: 8.0.3
- ejs: 3.1.8
- estree-walker: 3.0.3
- fast-glob: 3.2.12
- find-up: 5.0.0
- fs-extra: 10.1.0
- micromatch: 4.0.5
- mlly: 1.1.1
- mrmime: 1.0.1
- open: 8.4.2
- path-to-regexp: 6.2.1
- regenerator-runtime: 0.13.11
- resolve.exports: 1.1.1
- semver: 7.3.8
- temp: 0.9.4
- yargs-parser: 21.1.1
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /@ice/app/3.1.6_biqbaboplfbrettd7655fr4n2y:
- resolution: {integrity: sha512-ch0UwX9VJikgg+cd9Yh9KUtkdbZVzTmm1vSZU/HP3fzsLugTWkxQz1LoULIS0hAfUCvrruzvCaXqGKoLeqfHpw==}
- engines: {node: '>=14.19.0', npm: '>=3.0.0'}
- hasBin: true
- peerDependencies:
- react: '>=18.0.0'
- react-dom: '>=18.0.0'
- dependencies:
- '@babel/generator': 7.18.10
- '@babel/parser': 7.18.10
- '@babel/traverse': 7.18.10
- '@babel/types': 7.18.10
- '@ice/bundles': 0.1.8
- '@ice/route-manifest': 1.1.1
- '@ice/runtime': 1.1.5_biqbaboplfbrettd7655fr4n2y
- '@ice/webpack-config': 1.0.12
- '@swc/helpers': 0.4.14
- '@types/express': 4.17.17
- address: 1.2.2
- build-scripts: 2.1.0
- chalk: 4.1.2
- commander: 9.5.0
- consola: 2.15.3
- cross-spawn: 7.0.3
- detect-port: 1.5.1
- dotenv: 16.0.3
- dotenv-expand: 8.0.3
- ejs: 3.1.8
- estree-walker: 3.0.3
- fast-glob: 3.2.12
- find-up: 5.0.0
- fs-extra: 10.1.0
- micromatch: 4.0.5
- mlly: 1.1.1
- mrmime: 1.0.1
- open: 8.4.2
- path-to-regexp: 6.2.1
- react: 18.2.0
- react-dom: 18.2.0_react@18.2.0
- regenerator-runtime: 0.13.11
- resolve.exports: 1.1.1
- semver: 7.3.8
- temp: 0.9.4
- yargs-parser: 21.1.1
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /@ice/bundles/0.1.8:
- resolution: {integrity: sha512-cjaUkwcVkVO5EOdZqyFyDFYAPs5nnNXdOVRAY3CegBMOxy8K5MgoQO5xJkOMmphUSm+X0pbb0xUza1b9wR2fYA==}
- dependencies:
- '@ice/swc-plugin-keep-export': 0.1.4
- '@ice/swc-plugin-node-transform': 0.1.0-5
- '@ice/swc-plugin-remove-export': 0.1.2
- '@swc/core': 1.3.19
- ansi-html-community: 0.0.8
- caniuse-lite: 1.0.30001462
- chokidar: 3.5.3
- core-js: 3.29.1
- core-js-pure: 3.29.0
- error-stack-parser: 2.1.4
- esbuild: 0.16.17
- events: 3.3.0
- html-entities: 2.3.3
- jest-worker: 27.5.1
- less: 4.1.2
- postcss: 8.4.12
- react-refresh: 0.14.0
- sass: 1.50.0
- transitivePeerDependencies:
- - supports-color
- dev: true
-
- /@ice/jsx-runtime/0.2.0:
- resolution: {integrity: sha512-yUfoWloQq2mdyCTsTTWwxtx0dmiwC4Xe2QEaQCg/yFqQKz7hOFBOrju6RodMl7K0fdpj6k1UBfjLd9gPyUBAbw==}
- peerDependencies:
- react: ^16 || ^17 || ^18
- dependencies:
- style-unit: 3.0.5
- dev: true
-
- /@ice/jsx-runtime/0.2.0_react@18.2.0:
- resolution: {integrity: sha512-yUfoWloQq2mdyCTsTTWwxtx0dmiwC4Xe2QEaQCg/yFqQKz7hOFBOrju6RodMl7K0fdpj6k1UBfjLd9gPyUBAbw==}
- peerDependencies:
- react: ^16 || ^17 || ^18
- dependencies:
- react: 18.2.0
- style-unit: 3.0.5
- dev: true
-
/@ice/pkg-plugin-component/1.0.0:
resolution: {integrity: sha512-Mff6Em1RwY2NOZgciQyy2NXL65u9ebHV2Jqb5746vUVEh0l7Xpokb+3djV1pKcgED4wU8pSuSQ6jj+hXd9Ht1Q==}
dependencies:
From 2df375ff363ae044ba6e07eb63a6400381abd10c Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 13:40:08 +0800
Subject: [PATCH 17/38] fix: hydration
---
packages/plugin-store/src/runtime.tsx | 2 +-
packages/runtime/src/ClientRouter.tsx | 2 +-
packages/runtime/src/runClientApp.tsx | 17 +++++++++++++++++
packages/runtime/tests/runClientApp.test.tsx | 7 +++++--
4 files changed, 24 insertions(+), 4 deletions(-)
diff --git a/packages/plugin-store/src/runtime.tsx b/packages/plugin-store/src/runtime.tsx
index 74cd1d169c..cfb6fc9b80 100644
--- a/packages/plugin-store/src/runtime.tsx
+++ b/packages/plugin-store/src/runtime.tsx
@@ -5,7 +5,7 @@ import type { StoreConfig } from './types.js';
const EXPORT_CONFIG_NAME = 'storeConfig';
-const runtime: RuntimePlugin = async ({ appContext, addWrapper, addProvider, useAppContext }, runtimeOptions) => {
+const runtime: RuntimePlugin = async ({ appContext, addWrapper, addProvider }, runtimeOptions) => {
const { appExport, appData } = appContext;
const exported = appExport[EXPORT_CONFIG_NAME];
const storeConfig: StoreConfig = (typeof exported === 'function' ? await exported(appData) : exported) || {};
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index a8fb607f66..eca9198a14 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -10,7 +10,7 @@ function ClientRouter(props: AppRouterProps) {
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
- element = >} />;
+ element = ;
} else {
element = (
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index f9023978eb..6a926412b6 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -18,6 +18,7 @@ import ClientRouter from './ClientRouter.js';
import { setFetcher } from './dataLoader.js';
import addLeadingSlash from './utils/addLeadingSlash.js';
import { AppContextProvider } from './AppContext.js';
+import matchRoutes from './matchRoutes.js';
export interface RunClientAppOptions {
app: AppExport;
@@ -149,6 +150,22 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
console.warn(`Root node #${rootId} is not found, current root is automatically created by the framework.`);
}
const hydrationData = needHydrate ? { loaderData } : undefined;
+ if (needHydrate) {
+ const lazyMatches = matchRoutes(routes, history.location, basename).filter((m) => m.route.lazy);
+ if (lazyMatches?.length > 0) {
+ // Load the lazy matches and update the routes before creating your router
+ // so we can hydrate the SSR-rendered content synchronously.
+ await Promise.all(
+ lazyMatches.map(async (m) => {
+ let routeModule = await m.route.lazy();
+ Object.assign(m.route, {
+ ...routeModule,
+ lazy: undefined,
+ });
+ }),
+ );
+ }
+ }
const routerOptions = {
basename,
routes,
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index 90f497f312..a4df760fb6 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -89,8 +89,11 @@ describe('run client app', () => {
id: 'home',
path: '/',
componentName: 'Home',
- // Make sure the component is loaded before render.
- Component: homeItem.default,
+ lazy: () => {
+ return {
+ Component: homeItem.default,
+ };
+ },
},
];
From 7bf894f79aa9125f99910734e771004c7bf86bc0 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 15:49:25 +0800
Subject: [PATCH 18/38] fix: router
---
.../src/components/FrameworkLayout.tsx | 2 +-
examples/icestark-layout/src/pages/about.tsx | 2 +-
.../plugin-icestark/src/runtime/framework.tsx | 20 +++++++++++-
packages/runtime/src/ClientRouter.tsx | 22 +++++++++++--
packages/runtime/src/ServerRouter.tsx | 32 +++++++++++++++++--
packages/runtime/src/runClientApp.tsx | 12 +++----
packages/runtime/src/runServerApp.tsx | 30 ++---------------
packages/runtime/src/types.ts | 3 +-
packages/runtime/tests/routes.test.tsx | 14 +++++---
9 files changed, 89 insertions(+), 48 deletions(-)
diff --git a/examples/icestark-layout/src/components/FrameworkLayout.tsx b/examples/icestark-layout/src/components/FrameworkLayout.tsx
index e8e34a3255..01a079d20d 100644
--- a/examples/icestark-layout/src/components/FrameworkLayout.tsx
+++ b/examples/icestark-layout/src/components/FrameworkLayout.tsx
@@ -13,4 +13,4 @@ export default function FrameworkLayout({ children }) {
>
);
-}
\ No newline at end of file
+}
diff --git a/examples/icestark-layout/src/pages/about.tsx b/examples/icestark-layout/src/pages/about.tsx
index 78d823e59a..764f4847be 100644
--- a/examples/icestark-layout/src/pages/about.tsx
+++ b/examples/icestark-layout/src/pages/about.tsx
@@ -7,4 +7,4 @@ export default function About() {
home
>
);
-}
\ No newline at end of file
+}
diff --git a/packages/plugin-icestark/src/runtime/framework.tsx b/packages/plugin-icestark/src/runtime/framework.tsx
index c801e1bf6a..b83b81b1c9 100644
--- a/packages/plugin-icestark/src/runtime/framework.tsx
+++ b/packages/plugin-icestark/src/runtime/framework.tsx
@@ -4,10 +4,12 @@ import type { RuntimePlugin, AppRouterProps } from '@ice/runtime/types';
import type { RouteInfo, AppConfig } from '../types';
const { useState, useEffect } = React;
+
const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
const { appExport, appData } = appContext;
const OriginalRouter = getAppRouter();
const { layout, getApps, appRouter } = appExport?.icestark || {};
+
if (getApps) {
const FrameworkRouter = (props: AppRouterProps) => {
const [routeInfo, setRouteInfo] = useState({});
@@ -62,7 +64,23 @@ const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
path="/"
location={props.location}
render={() => {
- return ;
+ const { routerContext } = props;
+ routerContext.routes = [
+ ...routerContext.routes,
+ {
+ path: '*',
+ Component: () => (
+ process.env.NODE_ENV === 'development'
+ ? Add $.tsx to folder pages as a 404 component
+ : null
+ ),
+ },
+ ];
+ const routerProps = {
+ ...props,
+ routerContext,
+ };
+ return ;
}}
/>
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index eca9198a14..f60cd04ee8 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -1,15 +1,33 @@
-import React from 'react';
+import React, { useEffect } from 'react';
import { RouterProvider } from 'react-router-dom';
+import { createRouter } from '@remix-run/router';
import type { AppRouterProps } from './types.js';
import App from './App.js';
import { DataContextProvider } from './single-router.js';
+// createRouter only needs to be called once.
+let router: ReturnType;
+
function ClientRouter(props: AppRouterProps) {
- const { router, Component, loaderData } = props;
+ const { Component, loaderData, routerContext } = props;
+
+ useEffect(() => {
+ return () => {
+ // In case of micro app, ClientRouter will be unmounted,
+ // duspose router before mount again.
+ router.dispose();
+ router = null;
+ };
+ }, []);
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
+ if (!router) {
+ // Do not call createRouter outside of the component,
+ // otherwise it cause error in the use case of micro app.
+ router = createRouter(routerContext).initialize();
+ }
element = ;
} else {
element = (
diff --git a/packages/runtime/src/ServerRouter.tsx b/packages/runtime/src/ServerRouter.tsx
index dad0dea0fa..9d0adf012f 100644
--- a/packages/runtime/src/ServerRouter.tsx
+++ b/packages/runtime/src/ServerRouter.tsx
@@ -1,11 +1,39 @@
import React from 'react';
-import { StaticRouterProvider } from 'react-router-dom/server.mjs';
+import { StaticRouterProvider, createStaticRouter } from 'react-router-dom/server.mjs';
+import type { RouteObject } from 'react-router-dom';
+import { RouteComponent } from './routes.js';
import type { AppRouterProps } from './types.js';
import App from './App.js';
+let router: ReturnType = null;
+
+function createServerRoutes(routes: RouteObject[]) {
+ return routes.map((route) => {
+ let dataRoute = {
+ // Static Router need element or Component when matched.
+ element: ,
+ id: route.id,
+ index: route.index,
+ path: route.path,
+ children: null,
+ };
+
+ if (route?.children?.length > 0) {
+ let children = createServerRoutes(
+ route.children,
+ );
+ dataRoute.children = children;
+ }
+ return dataRoute;
+ });
+}
function ServerRouter(props: AppRouterProps) {
- const { router, routerContext } = props;
+ const { routerContext, routes } = props;
+ if (!router) {
+ // API createStaticRouter only needs to be called once.
+ router = createStaticRouter(createServerRoutes(routes), routerContext);
+ }
return (
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index 6a926412b6..da14c41146 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -1,6 +1,6 @@
import React from 'react';
import * as ReactDOM from 'react-dom/client';
-import { createRouter, createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
+import { createHashHistory, createBrowserHistory, createMemoryHistory } from '@remix-run/router';
import type { History } from '@remix-run/router';
import type {
AppContext, WindowContext, AppExport, RouteItem, RuntimeModules, AppConfig, AssetsManifest,
@@ -9,9 +9,8 @@ import { createHistory as createHistorySingle } from './single-router.js';
import { setHistory } from './history.js';
import Runtime from './runtime.js';
import { getAppData } from './appData.js';
-import { getRoutesPath } from './routes.js';
+import { getRoutesPath, loadRouteModule } from './routes.js';
import type { RouteLoaderOptions } from './routes.js';
-import { loadRouteModule } from './routes.js';
import getRequestContext from './requestContext.js';
import getAppConfig from './appConfig.js';
import ClientRouter from './ClientRouter.js';
@@ -175,10 +174,7 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
let router = null;
let singleComponent = null;
let routeData = null;
- // Create router before render.
- if (process.env.ICE_CORE_ROUTER === 'true') {
- router = createRouter(routerOptions).initialize();
- } else {
+ if (process.env.ICE_CORE_ROUTER !== 'true') {
const { Component, loader } = await loadRouteModule(routes[0]);
singleComponent = Component || routes[0].Component;
routeData = loader && await loader();
@@ -188,7 +184,7 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
{
- let dataRoute = {
- // Static Router need element or Component when matched.
- element: ,
- id: route.id,
- index: route.index,
- path: route.path,
- children: null,
- };
-
- if (route?.children?.length > 0) {
- let children = createServerRoutes(
- route.children,
- );
- dataRoute.children = children;
- }
- return dataRoute;
- });
-}
-
interface RenderServerEntry {
runtime: Runtime;
matches: RouteMatch[];
@@ -341,10 +317,10 @@ async function renderServerEntry(
const routerContext = {
matches, basename, loaderData, location,
};
- const router = createStaticRouter(createServerRoutes(routes), routerContext);
+
const documentContext = {
main: (
-
+
),
};
const element = (
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index b6deb79a26..add0482189 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -1,5 +1,5 @@
import type { IncomingMessage, ServerResponse } from 'http';
-import type { InitialEntry, AgnosticRouteObject, Router, Location } from '@remix-run/router';
+import type { InitialEntry, AgnosticRouteObject, Location } from '@remix-run/router';
import type { ComponentType, PropsWithChildren } from 'react';
import type { HydrationOptions, Root } from 'react-dom/client';
import type { Params, RouteObject } from 'react-router-dom';
@@ -222,7 +222,6 @@ export interface RuntimeModules {
export interface AppRouterProps {
routes?: RouteObject[];
- router?: Router;
routerContext?: any;
location?: Location;
Component?: ComponentType;
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index beeb2e8c91..5012d87bcb 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -98,8 +98,11 @@ describe('routes', () => {
it('route WrapRouteComponent', () => {
const domstring = renderToString(
- // @ts-ignore
- wrapper{children}
, layout: false }] }}>
+
+ wrapper{children}
, layout: false }] }}
+ >
home
}} />
,
);
@@ -108,8 +111,11 @@ describe('routes', () => {
it('route WrapRouteComponent match layout', () => {
const domstring = renderToString(
- // @ts-ignore
- wrapper{children}
, layout: false }] }}>
+
+ wrapper{children}
, layout: false }] }}
+ >
home
}} />
,
);
From 4a9bb5c5f271c2389c81292e4cd41f8222e69aa2 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 18:05:38 +0800
Subject: [PATCH 19/38] fix: router
---
packages/runtime/src/ClientRouter.tsx | 16 +++++-----------
packages/runtime/src/ServerRouter.tsx | 8 ++++----
packages/runtime/src/utils/useHooks.ts | 14 ++++++++++++++
3 files changed, 23 insertions(+), 15 deletions(-)
create mode 100644 packages/runtime/src/utils/useHooks.ts
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index f60cd04ee8..ab9417cc0e 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -1,33 +1,27 @@
import React, { useEffect } from 'react';
import { RouterProvider } from 'react-router-dom';
import { createRouter } from '@remix-run/router';
-
+import { useOnce } from './utils/useHooks.js';
import type { AppRouterProps } from './types.js';
import App from './App.js';
import { DataContextProvider } from './single-router.js';
-// createRouter only needs to be called once.
-let router: ReturnType;
-
function ClientRouter(props: AppRouterProps) {
const { Component, loaderData, routerContext } = props;
-
+ // createRouter only needs to be called once.
+ const router = useOnce(() => {
+ return process.env.ICE_CORE_ROUTER === 'true' ? createRouter(routerContext).initialize() : {};
+ });
useEffect(() => {
return () => {
// In case of micro app, ClientRouter will be unmounted,
// duspose router before mount again.
router.dispose();
- router = null;
};
}, []);
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
- if (!router) {
- // Do not call createRouter outside of the component,
- // otherwise it cause error in the use case of micro app.
- router = createRouter(routerContext).initialize();
- }
element = ;
} else {
element = (
diff --git a/packages/runtime/src/ServerRouter.tsx b/packages/runtime/src/ServerRouter.tsx
index 9d0adf012f..3c9692f80c 100644
--- a/packages/runtime/src/ServerRouter.tsx
+++ b/packages/runtime/src/ServerRouter.tsx
@@ -2,6 +2,7 @@ import React from 'react';
import { StaticRouterProvider, createStaticRouter } from 'react-router-dom/server.mjs';
import type { RouteObject } from 'react-router-dom';
import { RouteComponent } from './routes.js';
+import { useOnce } from './utils/useHooks.js';
import type { AppRouterProps } from './types.js';
import App from './App.js';
@@ -30,10 +31,9 @@ function createServerRoutes(routes: RouteObject[]) {
function ServerRouter(props: AppRouterProps) {
const { routerContext, routes } = props;
- if (!router) {
- // API createStaticRouter only needs to be called once.
- router = createStaticRouter(createServerRoutes(routes), routerContext);
- }
+ const router = useOnce(() => {
+ return createStaticRouter(createServerRoutes(routes), routerContext);
+ });
return (
diff --git a/packages/runtime/src/utils/useHooks.ts b/packages/runtime/src/utils/useHooks.ts
new file mode 100644
index 0000000000..dab2a85d25
--- /dev/null
+++ b/packages/runtime/src/utils/useHooks.ts
@@ -0,0 +1,14 @@
+import { useRef } from 'react';
+
+const useOnce = (callback: Function) => {
+ const ref = useRef(null);
+
+ if (!ref.current) {
+ ref.current = callback();
+ }
+ return ref.current;
+};
+
+export {
+ useOnce,
+};
From 3adb0f6ebfddf4c18c320b363b6306b7388362cb Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Thu, 6 Apr 2023 18:10:57 +0800
Subject: [PATCH 20/38] chore: log
---
packages/runtime/src/runServerApp.tsx | 2 --
1 file changed, 2 deletions(-)
diff --git a/packages/runtime/src/runServerApp.tsx b/packages/runtime/src/runServerApp.tsx
index dfcb59c7c3..706406baf7 100644
--- a/packages/runtime/src/runServerApp.tsx
+++ b/packages/runtime/src/runServerApp.tsx
@@ -67,7 +67,6 @@ export async function renderToEntry(
requestContext: ServerContext,
renderOptions: RenderOptions,
) {
- console.log('renderToEntry.tsx');
const result = await renderToHTML(requestContext, renderOptions);
const { value } = result;
@@ -98,7 +97,6 @@ export async function renderToHTML(
renderOptions: RenderOptions,
): Promise {
const result = await doRender(requestContext, renderOptions);
-
const { value } = result;
if (typeof value === 'string') {
From afb842804302054633712030a7c87467d065142b Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 7 Apr 2023 11:04:35 +0800
Subject: [PATCH 21/38] fix: hmr
---
examples/multi-target/ice.config.mts | 1 -
examples/with-ssg/src/pages/index.tsx | 2 +-
packages/runtime/src/ClientRouter.tsx | 29 +++++++++++++++++++-------
packages/runtime/src/ServerRouter.tsx | 9 +++-----
packages/runtime/src/runClientApp.tsx | 9 +-------
packages/runtime/src/utils/useHooks.ts | 14 -------------
6 files changed, 27 insertions(+), 37 deletions(-)
delete mode 100644 packages/runtime/src/utils/useHooks.ts
diff --git a/examples/multi-target/ice.config.mts b/examples/multi-target/ice.config.mts
index d511ba9ed4..d86560cf30 100644
--- a/examples/multi-target/ice.config.mts
+++ b/examples/multi-target/ice.config.mts
@@ -2,7 +2,6 @@ import { defineConfig } from '@ice/app';
export default defineConfig(() => ({
ssr: true,
- ssg: true,
define: {
'process.env.NODE_ENV': JSON.stringify(true),
},
diff --git a/examples/with-ssg/src/pages/index.tsx b/examples/with-ssg/src/pages/index.tsx
index 5df7dc3e75..fd6781a8ca 100644
--- a/examples/with-ssg/src/pages/index.tsx
+++ b/examples/with-ssg/src/pages/index.tsx
@@ -39,4 +39,4 @@ export const staticDataLoader = defineStaticDataLoader(() => {
return {
price: '0.00',
};
-});
\ No newline at end of file
+});
diff --git a/packages/runtime/src/ClientRouter.tsx b/packages/runtime/src/ClientRouter.tsx
index ab9417cc0e..6417ef0589 100644
--- a/packages/runtime/src/ClientRouter.tsx
+++ b/packages/runtime/src/ClientRouter.tsx
@@ -1,24 +1,39 @@
import React, { useEffect } from 'react';
import { RouterProvider } from 'react-router-dom';
import { createRouter } from '@remix-run/router';
-import { useOnce } from './utils/useHooks.js';
import type { AppRouterProps } from './types.js';
import App from './App.js';
import { DataContextProvider } from './single-router.js';
+import { useAppContext } from './AppContext.js';
+let router: ReturnType = null;
function ClientRouter(props: AppRouterProps) {
const { Component, loaderData, routerContext } = props;
- // createRouter only needs to be called once.
- const router = useOnce(() => {
- return process.env.ICE_CORE_ROUTER === 'true' ? createRouter(routerContext).initialize() : {};
- });
+ const { revalidate } = useAppContext();
+
+ function clearRouter() {
+ if (router) {
+ router.dispose();
+ router = null;
+ }
+ }
+ // API createRouter only needs to be called once, and create before mount.
+ if (process.env.ICE_CORE_ROUTER === 'true') {
+ // Clear router before re-create in case of hot module replacement.
+ clearRouter();
+ router = createRouter(routerContext).initialize();
+ }
useEffect(() => {
+ if (revalidate) {
+ // Revalidate after render for SSG while staticDataLoader and dataLoader both defined.
+ router?.revalidate();
+ }
return () => {
// In case of micro app, ClientRouter will be unmounted,
// duspose router before mount again.
- router.dispose();
+ clearRouter();
};
- }, []);
+ }, [revalidate]);
let element: React.ReactNode;
if (process.env.ICE_CORE_ROUTER === 'true') {
diff --git a/packages/runtime/src/ServerRouter.tsx b/packages/runtime/src/ServerRouter.tsx
index 3c9692f80c..a7fa4d43fd 100644
--- a/packages/runtime/src/ServerRouter.tsx
+++ b/packages/runtime/src/ServerRouter.tsx
@@ -2,12 +2,9 @@ import React from 'react';
import { StaticRouterProvider, createStaticRouter } from 'react-router-dom/server.mjs';
import type { RouteObject } from 'react-router-dom';
import { RouteComponent } from './routes.js';
-import { useOnce } from './utils/useHooks.js';
import type { AppRouterProps } from './types.js';
import App from './App.js';
-let router: ReturnType = null;
-
function createServerRoutes(routes: RouteObject[]) {
return routes.map((route) => {
let dataRoute = {
@@ -31,15 +28,15 @@ function createServerRoutes(routes: RouteObject[]) {
function ServerRouter(props: AppRouterProps) {
const { routerContext, routes } = props;
- const router = useOnce(() => {
- return createStaticRouter(createServerRoutes(routes), routerContext);
- });
+ // Server router only be called once.
+ const router = createStaticRouter(createServerRoutes(routes), routerContext);
return (
);
diff --git a/packages/runtime/src/runClientApp.tsx b/packages/runtime/src/runClientApp.tsx
index da14c41146..0ca1bd3259 100644
--- a/packages/runtime/src/runClientApp.tsx
+++ b/packages/runtime/src/runClientApp.tsx
@@ -135,7 +135,7 @@ interface RenderOptions {
async function render({ history, runtime, needHydrate }: RenderOptions) {
const appContext = runtime.getAppContext();
- const { appConfig, loaderData, routes, revalidate, basename } = appContext;
+ const { appConfig, loaderData, routes, basename } = appContext;
const appRender = runtime.getRender();
const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment;
const AppRouter = runtime.getAppRouter();
@@ -171,7 +171,6 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
history,
hydrationData,
};
- let router = null;
let singleComponent = null;
let routeData = null;
if (process.env.ICE_CORE_ROUTER !== 'true') {
@@ -193,12 +192,6 @@ async function render({ history, runtime, needHydrate }: RenderOptions) {
,
);
- if (revalidate) {
- // Revalidate after render for SSG while staticDataLoader and dataLoader both defined.
- setTimeout(() => {
- router?.revalidate();
- });
- }
return renderRoot;
}
diff --git a/packages/runtime/src/utils/useHooks.ts b/packages/runtime/src/utils/useHooks.ts
deleted file mode 100644
index dab2a85d25..0000000000
--- a/packages/runtime/src/utils/useHooks.ts
+++ /dev/null
@@ -1,14 +0,0 @@
-import { useRef } from 'react';
-
-const useOnce = (callback: Function) => {
- const ref = useRef(null);
-
- if (!ref.current) {
- ref.current = callback();
- }
- return ref.current;
-};
-
-export {
- useOnce,
-};
From 86d89272176cfa583a1276ae7d4ca4405d5e7d14 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 7 Apr 2023 11:33:02 +0800
Subject: [PATCH 22/38] fix: test
---
examples/basic-project/ice.config.mts | 1 +
packages/runtime/tests/runClientApp.test.tsx | 10 ++++++++++
2 files changed, 11 insertions(+)
diff --git a/examples/basic-project/ice.config.mts b/examples/basic-project/ice.config.mts
index 8baac7c3c8..151f031488 100644
--- a/examples/basic-project/ice.config.mts
+++ b/examples/basic-project/ice.config.mts
@@ -9,6 +9,7 @@ export default defineConfig(() => ({
syntaxFeatures: {
exportDefaultFrom: true,
},
+ codeSplitting: false,
server: {
onDemand: true,
format: 'esm',
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index a4df760fb6..c2658b5471 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -5,18 +5,28 @@
import React from 'react';
import { renderToString } from 'react-dom/server';
import { expect, it, vi, describe, beforeEach, afterEach } from 'vitest';
+import { fetch, Request, Response } from '@remix-run/web-fetch';
import runClientApp from '../src/runClientApp';
import { useAppData } from '../src/AppContext';
describe('run client app', () => {
let windowSpy;
let documentSpy;
+ if (!globalThis.fetch) {
+ // @ts-expect-error
+ globalThis.fetch = fetch;
+ // @ts-expect-error
+ globalThis.Request = Request;
+ // @ts-expect-error
+ globalThis.Response = Response;
+ }
const mockData = {
location: new URL('http://localhost:4000/'),
history: {
replaceState: vi.fn(),
},
addEventListener: vi.fn(),
+ removeEventListener: vi.fn(),
};
beforeEach(() => {
process.env.ICE_CORE_ROUTER = 'true';
From 40fcdd90c1216e6a2bab1458ebd697059c2ef220 Mon Sep 17 00:00:00 2001
From: ClarkXia
Date: Fri, 7 Apr 2023 11:33:47 +0800
Subject: [PATCH 23/38] fix: test
---
examples/basic-project/ice.config.mts | 1 -
1 file changed, 1 deletion(-)
diff --git a/examples/basic-project/ice.config.mts b/examples/basic-project/ice.config.mts
index 151f031488..8baac7c3c8 100644
--- a/examples/basic-project/ice.config.mts
+++ b/examples/basic-project/ice.config.mts
@@ -9,7 +9,6 @@ export default defineConfig(() => ({
syntaxFeatures: {
exportDefaultFrom: true,
},
- codeSplitting: false,
server: {
onDemand: true,
format: 'esm',
From 3d4f08dc44db4812968d689774806c98fb6de7fb Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Mon, 10 Apr 2023 10:18:43 +0800
Subject: [PATCH 24/38] feat: await
---
examples/with-data-loader/package.json | 3 +-
.../src/pages/with-defer-loader.tsx | 27 +++-------
.../src/pages/with-defer-loaders.tsx | 50 ++++---------------
.../with-data-loader/src/pages/with-ssr.tsx | 27 +++-------
packages/ice/src/constant.ts | 1 +
packages/runtime/src/index.ts | 4 +-
packages/runtime/src/routes.tsx | 17 ++++++-
7 files changed, 43 insertions(+), 86 deletions(-)
diff --git a/examples/with-data-loader/package.json b/examples/with-data-loader/package.json
index 8ca8b75ad2..4cdfb8420e 100644
--- a/examples/with-data-loader/package.json
+++ b/examples/with-data-loader/package.json
@@ -13,8 +13,7 @@
"@ice/app": "workspace:*",
"@ice/runtime": "workspace:*",
"react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.10.0"
+ "react-dom": "^18.2.0"
},
"devDependencies": {
"@types/react": "^18.0.17",
diff --git a/examples/with-data-loader/src/pages/with-defer-loader.tsx b/examples/with-data-loader/src/pages/with-defer-loader.tsx
index 2be2ca0838..59d20750cc 100644
--- a/examples/with-data-loader/src/pages/with-defer-loader.tsx
+++ b/examples/with-data-loader/src/pages/with-defer-loader.tsx
@@ -1,6 +1,4 @@
-import { useData, defineDataLoader } from 'ice';
-import * as React from 'react';
-import { Await } from 'react-router-dom';
+import { useData, defineDataLoader, Await } from 'ice';
import styles from './index.module.css';
export default function Home() {
@@ -9,24 +7,11 @@ export default function Home() {
return (
<>
With dataLoader
- Loading item info...}
- >
- Error loading!
- }
- >
- {(itemInfo) => {
- return (
-
- Item id is {itemInfo.id}
-
- );
-}}
-
-
+ Loading item info...} errorElement={Error loading!
}>
+ {(itemInfo) => {
+ return Item id is {itemInfo.id}
;
+ }}
+
>
);
}
diff --git a/examples/with-data-loader/src/pages/with-defer-loaders.tsx b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
index ef49c93ebd..9dbcf0e973 100644
--- a/examples/with-data-loader/src/pages/with-defer-loaders.tsx
+++ b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
@@ -1,6 +1,4 @@
-import { useData, defineDataLoader } from 'ice';
-import * as React from 'react';
-import { Await } from 'react-router-dom';
+import { useData, defineDataLoader, Await } from 'ice';
import styles from './index.module.css';
export default function Home() {
@@ -9,42 +7,16 @@ export default function Home() {
return (
<>
With dataLoader
- Loading item info...}
- >
- Error loading!
- }
- >
- {(itemInfo) => {
- return (
-
- Item id is {itemInfo.id}
-
- );
-}}
-
-
- Loading item info...}
- >
- Error loading!
- }
- >
- {(itemInfo) => {
- return (
-
- Item price is {itemInfo.price}
-
- );
-}}
-
-
+ Loading item info...} errorElement={Error loading!
}>
+ {(itemInfo) => {
+ return Item id is {itemInfo.id}
;
+ }}
+
+ Loading item info...} errorElement={Error loading!
}>
+ {(itemInfo) => {
+ return Item price is {itemInfo.price}
;
+ }}
+
>
);
}
diff --git a/examples/with-data-loader/src/pages/with-ssr.tsx b/examples/with-data-loader/src/pages/with-ssr.tsx
index fe75f77874..70e690f0ea 100644
--- a/examples/with-data-loader/src/pages/with-ssr.tsx
+++ b/examples/with-data-loader/src/pages/with-ssr.tsx
@@ -1,6 +1,4 @@
-import { useData, defineDataLoader, defineServerDataLoader } from 'ice';
-import * as React from 'react';
-import { Await } from 'react-router-dom';
+import { useData, defineDataLoader, defineServerDataLoader, Await } from 'ice';
import styles from './index.module.css';
export default function Home() {
@@ -9,24 +7,11 @@ export default function Home() {
return (
<>
With dataLoader
- Loading item info...}
- >
- Error loading!
- }
- >
- {(itemInfo) => {
- return (
-
- Item id is {itemInfo.id}
-
- );
-}}
-
-
+ Loading item info...} errorElement={Error loading!
}>
+ {(itemInfo) => {
+ return Item id is {itemInfo.id}
;
+ }}
+
>
);
}
diff --git a/packages/ice/src/constant.ts b/packages/ice/src/constant.ts
index caeb9b6b15..ecb0e9d1b8 100644
--- a/packages/ice/src/constant.ts
+++ b/packages/ice/src/constant.ts
@@ -61,6 +61,7 @@ export const RUNTIME_EXPORTS = [
'ClientOnly',
'withSuspense',
'useSuspenseData',
+ 'Await',
'defineDataLoader',
'defineServerDataLoader',
'defineStaticDataLoader',
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index 879bdc5f72..601241036b 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -50,7 +50,7 @@ import KeepAliveOutlet from './KeepAliveOutlet.js';
import ClientOnly from './ClientOnly.js';
import useMounted from './useMounted.js';
import { withSuspense, useSuspenseData } from './Suspense.js';
-import { createRouteLoader, WrapRouteComponent, RouteErrorComponent } from './routes.js';
+import { createRouteLoader, WrapRouteComponent, RouteErrorComponent, Await } from './routes.js';
export {
getAppConfig,
@@ -92,6 +92,8 @@ export {
withSuspense,
useSuspenseData,
+ Await,
+
createRouteLoader,
WrapRouteComponent,
RouteErrorComponent,
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 70c5b7245c..daf5b9bd20 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,5 +1,5 @@
-import React from 'react';
-import { defer, useRouteError } from 'react-router-dom';
+import React, { Suspense } from 'react';
+import { defer, useRouteError, Await as ReactRouterAwait } from 'react-router-dom';
import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
@@ -89,6 +89,19 @@ export function RouteErrorComponent() {
return <>>;
}
+export function Await(props) {
+ return (
+
+
+ {props.children}
+
+
+ );
+}
+
/**
* Create loader function for route module.
*/
From 646e1b780c5a5bbc0254a6943848a447a77dd5ab Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Mon, 10 Apr 2023 11:01:16 +0800
Subject: [PATCH 25/38] fix: await component
---
packages/runtime/src/routes.tsx | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index daf5b9bd20..96ddf689bb 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -92,12 +92,12 @@ export function RouteErrorComponent() {
export function Await(props) {
return (
-
{props.children}
-
+
);
}
From 3db58ebbad5eb66d5a17e5b6200fd4bdef8ce265 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 11 Apr 2023 09:04:54 +0800
Subject: [PATCH 26/38] fix: lint
---
examples/with-data-loader/src/pages/with-defer-loader.tsx | 2 +-
examples/with-data-loader/src/pages/with-defer-loaders.tsx | 4 ++--
examples/with-data-loader/src/pages/with-ssr.tsx | 4 ++--
3 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/examples/with-data-loader/src/pages/with-defer-loader.tsx b/examples/with-data-loader/src/pages/with-defer-loader.tsx
index 59d20750cc..80d97f3ce4 100644
--- a/examples/with-data-loader/src/pages/with-defer-loader.tsx
+++ b/examples/with-data-loader/src/pages/with-defer-loader.tsx
@@ -23,7 +23,7 @@ export function pageConfig() {
}
export const dataLoader = defineDataLoader(async () => {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 1233,
diff --git a/examples/with-data-loader/src/pages/with-defer-loaders.tsx b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
index 9dbcf0e973..06f7eb5cdb 100644
--- a/examples/with-data-loader/src/pages/with-defer-loaders.tsx
+++ b/examples/with-data-loader/src/pages/with-defer-loaders.tsx
@@ -29,7 +29,7 @@ export function pageConfig() {
export const dataLoader = defineDataLoader([
async () => {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 1233,
@@ -39,7 +39,7 @@ export const dataLoader = defineDataLoader([
return await promise;
},
async () => {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
setTimeout(() => {
resolve({
price: 9.99,
diff --git a/examples/with-data-loader/src/pages/with-ssr.tsx b/examples/with-data-loader/src/pages/with-ssr.tsx
index 70e690f0ea..16c363547c 100644
--- a/examples/with-data-loader/src/pages/with-ssr.tsx
+++ b/examples/with-data-loader/src/pages/with-ssr.tsx
@@ -23,7 +23,7 @@ export function pageConfig() {
}
export const dataLoader = defineDataLoader(async () => {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 1233,
@@ -34,7 +34,7 @@ export const dataLoader = defineDataLoader(async () => {
}, { defer: true });
export const serverDataLoader = defineServerDataLoader(async () => {
- const promise = new Promise((resolve, reject) => {
+ const promise = new Promise((resolve) => {
setTimeout(() => {
resolve({
id: 1233,
From d254e5bc6c64505d2b059102c45f6c5e0ad86553 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 11 Apr 2023 09:20:51 +0800
Subject: [PATCH 27/38] refactor: type
---
packages/runtime/src/dataLoader.ts | 36 ++++++++++++++++--------------
packages/runtime/src/index.ts | 4 ++--
packages/runtime/src/types.ts | 8 +++----
3 files changed, 25 insertions(+), 23 deletions(-)
diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts
index dfea439acd..2bbab257f1 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -1,4 +1,4 @@
-import type { RequestContext, RenderMode, DataLoaderConfig, DataLoaderResult, RuntimeModules, AppExport, StaticRuntimePlugin, CommonJsRuntime, StaticDataLoader } from './types.js';
+import type { RequestContext, RenderMode, Loader, DataLoaderResult, RuntimeModules, AppExport, StaticRuntimePlugin, CommonJsRuntime, StaticDataLoader } from './types.js';
import getRequestContext from './requestContext.js';
interface Loaders {
@@ -15,26 +15,26 @@ interface LoaderOptions {
appExport: AppExport;
}
-interface DefineOptions {
+interface Options {
defer?: boolean;
}
-type DefineResult = [DataLoaderConfig, DefineOptions?];
+type DataLoaderConfig = [Loader, Options?];
export interface LoadRoutesDataOptions {
renderMode: RenderMode;
}
-export function defineDataLoader(dataLoaderConfig: DataLoaderConfig, options?: DefineOptions): DefineResult {
- return [dataLoaderConfig, options];
+export function defineDataLoader(dataLoader: Loader, options?: Options): DataLoaderConfig {
+ return [dataLoader, options];
}
-export function defineServerDataLoader(dataLoaderConfig: DataLoaderConfig, options?: DefineOptions): DefineResult {
- return [dataLoaderConfig, options];
+export function defineServerDataLoader(dataLoader: Loader, options?: Options): DataLoaderConfig {
+ return [dataLoader, options];
}
-export function defineStaticDataLoader(dataLoaderConfig: DataLoaderConfig): DefineResult {
- return [dataLoaderConfig];
+export function defineStaticDataLoader(dataLoader: Loader): DataLoaderConfig {
+ return [dataLoader];
}
/**
@@ -129,7 +129,7 @@ export function loadDataByCustomFetcher(config: StaticDataLoader) {
/**
* Handle for different dataLoader.
*/
-export function callDataLoader(dataLoader: DataLoaderConfig, requestContext: RequestContext): DataLoaderResult {
+export function callDataLoader(dataLoader: Loader, requestContext: RequestContext): DataLoaderResult {
if (Array.isArray(dataLoader)) {
const loaders = dataLoader.map(loader => {
return typeof loader === 'object' ? loadDataByCustomFetcher(loader) : loader(requestContext);
@@ -173,10 +173,11 @@ function loadInitialDataInClient(loaders: Loaders) {
if (dataLoaderConfig) {
const requestContext = getRequestContext(window.location);
- const loader = callDataLoader(dataLoaderConfig[0], requestContext);
+ const [dataLoader] = dataLoaderConfig;
+ const promise = callDataLoader(dataLoader, requestContext);
cache.set(id, {
- value: loader,
+ value: promise,
});
}
});
@@ -187,7 +188,7 @@ function loadInitialDataInClient(loaders: Loaders) {
* Load initial data and register global loader.
* In order to load data, JavaScript modules, CSS and other assets in parallel.
*/
-async function init(dataloaderConfig: Loaders, options: LoaderOptions) {
+async function init(loaders: Loaders, options: LoaderOptions) {
const {
fetcher,
runtimeModules,
@@ -212,7 +213,7 @@ async function init(dataloaderConfig: Loaders, options: LoaderOptions) {
}
try {
- loadInitialDataInClient(dataloaderConfig);
+ loadInitialDataInClient(loaders);
} catch (error) {
console.error('Load initial data error: ', error);
}
@@ -234,16 +235,17 @@ async function init(dataloaderConfig: Loaders, options: LoaderOptions) {
return result.value;
}
- const dataLoader = dataloaderConfig[id];
+ const dataLoaderConfig = loaders[id];
// No data loader.
- if (!dataLoader) {
+ if (!dataLoaderConfig) {
return null;
}
// Call dataLoader.
const requestContext = getRequestContext(window.location);
- return callDataLoader(dataLoader[0], requestContext);
+ const [loader] = dataLoaderConfig;
+ return callDataLoader(loader, requestContext);
},
};
}
diff --git a/packages/runtime/src/index.ts b/packages/runtime/src/index.ts
index 601241036b..38991adb37 100644
--- a/packages/runtime/src/index.ts
+++ b/packages/runtime/src/index.ts
@@ -16,7 +16,7 @@ import type {
RouteWrapper,
RenderMode,
DistType,
- DataLoaderConfig,
+ Loader,
RouteWrapperConfig,
} from './types.js';
import Runtime from './runtime.js';
@@ -111,7 +111,7 @@ export type {
RouteWrapper,
RenderMode,
DistType,
- DataLoaderConfig,
+ Loader,
RunClientAppOptions,
MetaType,
TitleType,
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index add0482189..86da7f116a 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -49,7 +49,7 @@ export interface StaticDataLoader {
// route.defineDataLoader
// route.defineServerDataLoader
// route.defineStaticDataLoader
-export type DataLoaderConfig = DataLoader | StaticDataLoader | Array;
+export type Loader = DataLoader | StaticDataLoader | Array;
// route.pageConfig
export type PageConfig = (args: { data?: RouteData }) => RouteConfig;
@@ -126,9 +126,9 @@ export interface RequestContext extends ServerContext {
export type ComponentModule = {
default?: ComponentType;
Component?: ComponentType;
- staticDataLoader?: DataLoaderConfig;
- serverDataLoader?: DataLoaderConfig;
- dataLoader?: DataLoaderConfig;
+ staticDataLoader?: Loader;
+ serverDataLoader?: Loader;
+ dataLoader?: Loader;
pageConfig?: PageConfig;
[key: string]: any;
};
From 6f4307436d697983047396245d863b88ec4105be Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 11 Apr 2023 10:00:53 +0800
Subject: [PATCH 28/38] fix: type
---
packages/runtime/src/dataLoader.ts | 21 +++++-----
packages/runtime/src/routes.tsx | 63 ++++++++++++++++++++----------
packages/runtime/src/types.ts | 12 ++++--
3 files changed, 61 insertions(+), 35 deletions(-)
diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts
index 2bbab257f1..db7f446a45 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -1,6 +1,9 @@
-import type { RequestContext, RenderMode, Loader, DataLoaderResult, RuntimeModules, AppExport, StaticRuntimePlugin, CommonJsRuntime, StaticDataLoader } from './types.js';
import getRequestContext from './requestContext.js';
-
+import type {
+ RequestContext, RenderMode, AppExport,
+ RuntimeModules, StaticRuntimePlugin, CommonJsRuntime,
+ Loader, DataLoaderResult, StaticDataLoader, DataLoaderConfig, DataLoaderOptions,
+} from './types.js';
interface Loaders {
[routeId: string]: DataLoaderConfig;
}
@@ -9,27 +12,21 @@ interface CachedResult {
value: any;
}
-interface LoaderOptions {
+interface Options {
fetcher: Function;
runtimeModules: RuntimeModules['statics'];
appExport: AppExport;
}
-interface Options {
- defer?: boolean;
-}
-
-type DataLoaderConfig = [Loader, Options?];
-
export interface LoadRoutesDataOptions {
renderMode: RenderMode;
}
-export function defineDataLoader(dataLoader: Loader, options?: Options): DataLoaderConfig {
+export function defineDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig {
return [dataLoader, options];
}
-export function defineServerDataLoader(dataLoader: Loader, options?: Options): DataLoaderConfig {
+export function defineServerDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig {
return [dataLoader, options];
}
@@ -188,7 +185,7 @@ function loadInitialDataInClient(loaders: Loaders) {
* Load initial data and register global loader.
* In order to load data, JavaScript modules, CSS and other assets in parallel.
*/
-async function init(loaders: Loaders, options: LoaderOptions) {
+async function init(loaders: Loaders, options: Options) {
const {
fetcher,
runtimeModules,
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 96ddf689bb..b867095bfb 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -1,6 +1,8 @@
import React, { Suspense } from 'react';
-import { defer, useRouteError, Await as ReactRouterAwait } from 'react-router-dom';
-import type { RouteItem, RouteModules, RenderMode, DataLoaderConfig, RequestContext, ComponentModule } from './types.js';
+import { useRouteError, defer, Await as ReactRouterAwait } from 'react-router-dom';
+// eslint-disable-next-line camelcase
+import type { UNSAFE_DeferredData } from '@remix-run/router';
+import type { RouteItem, RouteModules, RenderMode, RequestContext, ComponentModule, DataLoaderConfig } from './types.js';
import RouteWrapper from './RouteWrapper.js';
import { useAppContext } from './AppContext.js';
import { callDataLoader } from './dataLoader.js';
@@ -106,8 +108,8 @@ export function Await(props) {
* Create loader function for route module.
*/
interface LoaderData {
- data: any;
- pageConfig: any;
+ data?: any;
+ pageConfig?: any;
}
export interface RouteLoaderOptions {
@@ -117,19 +119,32 @@ export interface RouteLoaderOptions {
renderMode: RenderMode;
}
-export function createRouteLoader(options: RouteLoaderOptions): () => any {
+// eslint-disable-next-line camelcase
+type LoaderFunction = () => LoaderData | UNSAFE_DeferredData | Promise;
+
+export function createRouteLoader(options: RouteLoaderOptions): LoaderFunction {
const { dataLoader, pageConfig, staticDataLoader, serverDataLoader } = options.module;
const { requestContext, renderMode, routeId } = options;
- let loaderConfig: any;
+ let dataLoaderConfig: DataLoaderConfig;
if (renderMode === 'SSG') {
- loaderConfig = staticDataLoader;
+ dataLoaderConfig = staticDataLoader;
} else if (renderMode === 'SSR') {
- loaderConfig = serverDataLoader || dataLoader;
+ dataLoaderConfig = serverDataLoader || dataLoader;
} else {
- loaderConfig = dataLoader;
+ dataLoaderConfig = dataLoader;
+ }
+
+ if (!dataLoaderConfig) {
+ return () => {
+ return {
+ pageConfig: pageConfig ? pageConfig({}) : {},
+ };
+ };
}
+ const [loader, loaderOptions] = dataLoaderConfig;
+
const getData = () => {
const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__;
const globalLoader = hasGlobalLoader ? (window as any).__ICE_DATA_LOADER__ : null;
@@ -137,33 +152,36 @@ export function createRouteLoader(options: RouteLoaderOptions): () => any {
if (globalLoader) {
routeData = globalLoader.getData(routeId, { renderMode });
} else {
- routeData = loaderConfig && callDataLoader(loaderConfig[0], requestContext);
+ routeData = callDataLoader(loader, requestContext);
}
return routeData;
};
- if (loaderConfig?.[1]?.defer) {
+ // Async dataLoader.
+ if (loaderOptions?.defer) {
return async () => {
const promise = getData();
return defer({
data: promise,
- routeConfig: pageConfig ? pageConfig({}) : {},
+ // Call pageConfig without data.
+ pageConfig: pageConfig ? pageConfig({}) : {},
});
};
}
+ // Await dataLoader before render.
return async () => {
- let routeData;
- const promise = getData();
+ const result = getData();
+ let routeData;
try {
- if (Array.isArray(promise)) {
- routeData = await Promise.all(promise);
- } else if (promise instanceof Promise) {
- routeData = await promise;
+ if (Array.isArray(result)) {
+ routeData = await Promise.all(result);
+ } else if (result instanceof Promise) {
+ routeData = await result;
} else {
- routeData = promise;
+ routeData = result;
}
} catch (error) {
console.error('DataLoader: getData error.\n', error);
@@ -175,11 +193,16 @@ export function createRouteLoader(options: RouteLoaderOptions): () => any {
}
const routeConfig = pageConfig ? pageConfig({ data: routeData }) : {};
- const loaderData = { data: routeData, pageConfig: routeConfig };
+ const loaderData = {
+ data: routeData,
+ pageConfig: routeConfig,
+ };
+
// CSR and load next route data.
if (typeof window !== 'undefined') {
await updateRoutesConfig(loaderData);
}
+
return loaderData;
};
}
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index 86da7f116a..c36f3cb693 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -71,6 +71,12 @@ export interface RoutesData {
[routeId: string]: RouteData;
}
+export interface DataLoaderOptions {
+ defer?: boolean;
+}
+
+export type DataLoaderConfig = [Loader, DataLoaderOptions?];
+
export interface LoaderDatas {
[routeId: string]: LoaderData;
}
@@ -126,9 +132,9 @@ export interface RequestContext extends ServerContext {
export type ComponentModule = {
default?: ComponentType;
Component?: ComponentType;
- staticDataLoader?: Loader;
- serverDataLoader?: Loader;
- dataLoader?: Loader;
+ staticDataLoader?: DataLoaderConfig;
+ serverDataLoader?: DataLoaderConfig;
+ dataLoader?: DataLoaderConfig;
pageConfig?: PageConfig;
[key: string]: any;
};
From ae3a24085061a7e2b4f9498094c8993a7d0b773e Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 11 Apr 2023 10:14:53 +0800
Subject: [PATCH 29/38] fix: app data loader
---
packages/runtime/src/appData.ts | 11 +++++------
packages/runtime/src/types.ts | 2 +-
2 files changed, 6 insertions(+), 7 deletions(-)
diff --git a/packages/runtime/src/appData.ts b/packages/runtime/src/appData.ts
index 351b3a6ade..9b1da08cea 100644
--- a/packages/runtime/src/appData.ts
+++ b/packages/runtime/src/appData.ts
@@ -12,14 +12,13 @@ async function getAppData(appExport: AppExport, requestContext?: RequestContext)
return await globalLoader.getData('__app');
}
- if (appExport?.dataLoader) {
- return await appExport.dataLoader(requestContext);
- }
-
- const loader = appExport?.dataLoader;
+ const appDataLoaderConfig = appExport?.dataLoader;
- if (!loader) return null;
+ if (!appDataLoaderConfig) {
+ return null;
+ }
+ const [loader] = appDataLoaderConfig;
await callDataLoader(loader, requestContext);
}
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index c36f3cb693..96ebf4843b 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -31,7 +31,7 @@ export type RouteConfig = T & {
export interface AppExport {
default?: AppConfig;
[key: string]: any;
- dataLoader?: DataLoader;
+ dataLoader?: DataLoaderConfig;
}
export type DataLoaderResult = (Promise | RouteData) | RouteData;
From 0344c5e0925c8cd2fd2d5da12188d2aead723e98 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 07:45:17 +0800
Subject: [PATCH 30/38] fix: test
---
packages/runtime/src/appData.ts | 2 +-
packages/runtime/tests/runClientApp.test.tsx | 12 ++++++------
2 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/packages/runtime/src/appData.ts b/packages/runtime/src/appData.ts
index 9b1da08cea..cfa5fc654f 100644
--- a/packages/runtime/src/appData.ts
+++ b/packages/runtime/src/appData.ts
@@ -19,7 +19,7 @@ async function getAppData(appExport: AppExport, requestContext?: RequestContext)
}
const [loader] = appDataLoaderConfig;
- await callDataLoader(loader, requestContext);
+ return await callDataLoader(loader, requestContext);
}
export {
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index c2658b5471..710bb69de5 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -110,9 +110,9 @@ describe('run client app', () => {
it('run with static runtime', async () => {
await runClientApp({
app: {
- dataLoader: async () => {
+ dataLoader: [async () => {
return { msg: staticMsg };
- },
+ }],
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
@@ -261,10 +261,10 @@ describe('run client app', () => {
let executed = false;
await runClientApp({
app: {
- dataLoader: async () => {
+ dataLoader: [async () => {
executed = true;
return { msg: '-getAppData' };
- },
+ }],
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
@@ -290,10 +290,10 @@ describe('run client app', () => {
await runClientApp({
app: {
- dataLoader: async () => {
+ dataLoader: [async () => {
executed = true;
return { msg: 'app' };
- },
+ }],
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
From 58bc374d4f5debbec2798412332e210941060dcc Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 07:59:56 +0800
Subject: [PATCH 31/38] fix: test
---
packages/runtime/tests/routes.test.tsx | 7 +++----
packages/runtime/tests/runServerApp.test.tsx | 2 +-
2 files changed, 4 insertions(+), 5 deletions(-)
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index 5012d87bcb..b406fd30b3 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -28,9 +28,9 @@ describe('routes', () => {
const homeItem = {
default: () => <>>,
pageConfig: () => ({ title: 'home' }),
- dataLoader: async () => ({ type: 'getData' }),
- serverDataLoader: async () => ({ type: 'getServerData' }),
- staticDataLoader: async () => ({ type: 'getStaticData' }),
+ dataLoader: [async () => ({ type: 'getData' })],
+ serverDataLoader: [async () => ({ type: 'getServerData' })],
+ staticDataLoader: [async () => ({ type: 'getStaticData' })],
};
const aboutItem = {
default: () => <>>,
@@ -226,7 +226,6 @@ describe('routes', () => {
})();
expect(routesDataCSR).toStrictEqual({
- data: undefined,
pageConfig: {
title: 'about',
},
diff --git a/packages/runtime/tests/runServerApp.test.tsx b/packages/runtime/tests/runServerApp.test.tsx
index bfad032c4c..0810aa95f2 100644
--- a/packages/runtime/tests/runServerApp.test.tsx
+++ b/packages/runtime/tests/runServerApp.test.tsx
@@ -14,7 +14,7 @@ describe('run server app', () => {
const homeItem = {
default: () => home
,
pageConfig: () => ({ title: 'home' }),
- dataLoader: async () => ({ data: 'test' }),
+ dataLoader: [async () => ({ data: 'test' })],
};
const basicRoutes = [
{
From 5c9fda74aabc75cb7601fedd69fab1c8ff3467d5 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 08:22:42 +0800
Subject: [PATCH 32/38] test: async data
---
packages/runtime/tests/routes.test.tsx | 21 +++++++++++++++++++++
1 file changed, 21 insertions(+)
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index b406fd30b3..ad84a338b5 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -12,7 +12,9 @@ import {
createRouteLoader,
getRoutesPath,
WrapRouteComponent,
+ Await,
} from '../src/routes.js';
+import { useData } from '../src/singleRouter';
describe('routes', () => {
let windowSpy;
@@ -36,6 +38,12 @@ describe('routes', () => {
default: () => <>>,
pageConfig: () => ({ title: 'about' }),
};
+ const InfoItem = {
+ default: () => <>>,
+ pageConfig: () => ({ title: 'home' }),
+ dataLoader: [async () => ({ type: 'getAsyncData' }), { defer: true }],
+ };
+
const homeLazyItem = {
Component: homeItem.default,
loader: createRouteLoader({
@@ -145,6 +153,19 @@ describe('routes', () => {
});
});
+ it('load async route', async () => {
+ const { data: deferredResult } = await createRouteLoader({
+ routeId: 'home',
+ module: InfoItem,
+ })();
+
+ const data = await deferredResult.data;
+
+ expect(data).toStrictEqual({
+ type: 'getAsyncData',
+ });
+ });
+
it('load route data for SSG', async () => {
const routesDataSSG = await createRouteLoader({
routeId: 'home',
From b9f3ff0cf1e41f0f820b65214da08300b0107a28 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 08:27:41 +0800
Subject: [PATCH 33/38] test: async data
---
examples/with-data-loader/src/pages/with-defer-loader.tsx | 2 +-
tests/integration/with-data-loader.test.ts | 8 ++++++++
2 files changed, 9 insertions(+), 1 deletion(-)
diff --git a/examples/with-data-loader/src/pages/with-defer-loader.tsx b/examples/with-data-loader/src/pages/with-defer-loader.tsx
index 80d97f3ce4..0396facb93 100644
--- a/examples/with-data-loader/src/pages/with-defer-loader.tsx
+++ b/examples/with-data-loader/src/pages/with-defer-loader.tsx
@@ -9,7 +9,7 @@ export default function Home() {
With dataLoader
Loading item info...} errorElement={Error loading!
}>
{(itemInfo) => {
- return Item id is {itemInfo.id}
;
+ return Item id is {itemInfo.id}
;
}}
>
diff --git a/tests/integration/with-data-loader.test.ts b/tests/integration/with-data-loader.test.ts
index b5ae581329..1449443f06 100644
--- a/tests/integration/with-data-loader.test.ts
+++ b/tests/integration/with-data-loader.test.ts
@@ -49,6 +49,14 @@ describe(`start ${example}`, () => {
expect(timeStampForRouter3).not.toStrictEqual(timeStampForRouter1);
});
+ test('should work with deferred data loader', async () => {
+ await page.push('/with-defer-loader');
+ await page.waitForNetworkIdle();
+ const data = (await page.$$text('#itemId'))[0];
+
+ expect(data).toEqual('1233');
+ });
+
afterAll(async () => {
await browser.close();
});
From 2efed45c89d3107d2381772aa4a131a96ff8c1ad Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 08:45:13 +0800
Subject: [PATCH 34/38] docs: async data loader
---
website/docs/guide/basic/data-loader.md | 83 ++++++++++++++++++++++++-
1 file changed, 80 insertions(+), 3 deletions(-)
diff --git a/website/docs/guide/basic/data-loader.md b/website/docs/guide/basic/data-loader.md
index 5c0602178f..55d688e236 100644
--- a/website/docs/guide/basic/data-loader.md
+++ b/website/docs/guide/basic/data-loader.md
@@ -71,6 +71,51 @@ export const dataLoader = defineDataLoader(async () => {
受小程序环境限制,通过 `dataLoader` 定义的应用级数据加载将在 `App` 的 `onLaunch` 生命周期中进行,页面级数据加载则会在 `Page` 的 `onLoad` 生命周期中,二者均会阻塞页面的 UI 渲染。如果这不是你想要的效果,请按照常规方式进行数据请求。(比如在组件首次 `useEffect` 时发起数据请求)
:::
+
+## 异步消费数据
+
+默认情况下,页面会等待数据请求完成后,再开始渲染,在数据接口比较快的情况下,这可以避免页面的二次渲染。
+
+如果数据接口较慢,也可以选择先渲染不依赖于动态数据的部分,待数据回来后,再重新渲染依赖数据的页面内容。
+
+具体做法如下:
+- 1. 在定义 dataLoader 时标记 defer: true
+- 2. 在消费数据时,使用 Await 组件包裹依赖于数据的页面内容
+
+```tsx title="src/pages/index.tsx"
+import { useData, defineDataLoader, Await } from 'ice';
+
+// 页面组件的 UI 实现
+export default function Home() {
+ const data = useData();
+
+ return (
+ <>
+ Hello ICE
+ loading...} errorElement={Error!
} />
+ {
+ (data) => {JSON.stringify(data)}
+ }
+
+ >
+ );
+};
+
+// 在定义 dataLoader 时标记 defer: true
+export const dataLoader = defineDataLoader(async () => {
+ const data = await fetch('https://example.com/api/xxx');
+ return data;
+}, { defer: true });
+```
+
+注意:
+- 当 dataLoader 被声明为异步时,useData 返回的内容不可直接消费,需由 Await 组件处理
+
+Await 组件接收三个参数
+* resolve 数据请求对象
+* fallback 数据加载过程中展示的 UI
+* errorElement 请求失败时展示的 UI
+
## 静态 dataLoader
当开发者希望通过统一的发送函数处理静态配置以完成 `dataLoader` 时,可以通过自定义 `fetcher` 以完成发送逻辑的统一封装,在 `dataLoader` 中只需要传递一份配置即可。
@@ -186,7 +231,7 @@ export default function Home(props) {
import { useData, defineDataLoader } from 'ice';
export default function Home() {
- const [useInfo, itemInfo] = useData();
+ const [userInfo, itemInfo] = useData();
return (
<>
@@ -198,8 +243,8 @@ export default function Home() {
export const dataLoader = defineDataLoader([
async () => {
- const useInfo = await fetch('https://example.com/api/userInfo');
- return useInfo;
+ const userInfo = await fetch('https://example.com/api/userInfo');
+ return userInfo;
},
async (ctx) => {
const itemInfo = await fetch(`https://example.com/api/itemInfo${ctx?.query?.itemId}`);
@@ -209,3 +254,35 @@ export const dataLoader = defineDataLoader([
```
多个数据请求的情况下,`useData` 获取的数据也对应的为数组,数组元素和 `dataLoader` 中定义的数据请求的返回值一一对应。
+
+如果 dataLoader 被声明为异步,消费时可以分别 Await 不同的数据,这样可以做到先返回的数据,先渲染。
+
+```tsx
+import { useData, defineDataLoader } from 'ice';
+
+export default function Home() {
+ const [userInfo, itemInfo] = useData();
+
+ return (
+ <>
+
+ { (data) => Hello {data?.name}
}
+
+
+ { (data) => {JSON.stringify(data)}
}
+
+
+ );
+};
+
+export const dataLoader = defineDataLoader([
+ async () => {
+ const useInfo = await fetch('https://example.com/api/userInfo');
+ return useInfo;
+ },
+ async (ctx) => {
+ const itemInfo = await fetch(`https://example.com/api/itemInfo${ctx?.query?.itemId}`);
+ return itemInfo;
+ },
+], { defer: true });
+```
\ No newline at end of file
From 7aad1bfe91ecec232a720de4fa60d6df57a66e2f Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Tue, 18 Apr 2023 20:13:00 +0800
Subject: [PATCH 35/38] fix: lint
---
packages/runtime/src/RenderWrapper.ts | 0
packages/runtime/tests/routes.test.tsx | 2 --
2 files changed, 2 deletions(-)
delete mode 100644 packages/runtime/src/RenderWrapper.ts
diff --git a/packages/runtime/src/RenderWrapper.ts b/packages/runtime/src/RenderWrapper.ts
deleted file mode 100644
index e69de29bb2..0000000000
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index ad84a338b5..de51bd1f43 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -12,9 +12,7 @@ import {
createRouteLoader,
getRoutesPath,
WrapRouteComponent,
- Await,
} from '../src/routes.js';
-import { useData } from '../src/singleRouter';
describe('routes', () => {
let windowSpy;
From e1e900c1ec240bc18858c01e76cbe4f2c8409720 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Sat, 22 Apr 2023 11:54:06 +0800
Subject: [PATCH 36/38] refactor: loader config
---
packages/runtime/src/appData.ts | 2 +-
packages/runtime/src/dataLoader.ts | 20 ++++++++++++++------
packages/runtime/src/routes.tsx | 2 +-
packages/runtime/src/types.ts | 5 ++++-
4 files changed, 20 insertions(+), 9 deletions(-)
diff --git a/packages/runtime/src/appData.ts b/packages/runtime/src/appData.ts
index cfa5fc654f..a610505383 100644
--- a/packages/runtime/src/appData.ts
+++ b/packages/runtime/src/appData.ts
@@ -18,7 +18,7 @@ async function getAppData(appExport: AppExport, requestContext?: RequestContext)
return null;
}
- const [loader] = appDataLoaderConfig;
+ const { loader } = appDataLoaderConfig;
return await callDataLoader(loader, requestContext);
}
diff --git a/packages/runtime/src/dataLoader.ts b/packages/runtime/src/dataLoader.ts
index db7f446a45..ffb1d29134 100644
--- a/packages/runtime/src/dataLoader.ts
+++ b/packages/runtime/src/dataLoader.ts
@@ -23,15 +23,23 @@ export interface LoadRoutesDataOptions {
}
export function defineDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig {
- return [dataLoader, options];
+ return {
+ loader: dataLoader,
+ options,
+ };
}
export function defineServerDataLoader(dataLoader: Loader, options?: DataLoaderOptions): DataLoaderConfig {
- return [dataLoader, options];
+ return {
+ loader: dataLoader,
+ options,
+ };
}
export function defineStaticDataLoader(dataLoader: Loader): DataLoaderConfig {
- return [dataLoader];
+ return {
+ loader: dataLoader,
+ };
}
/**
@@ -170,8 +178,8 @@ function loadInitialDataInClient(loaders: Loaders) {
if (dataLoaderConfig) {
const requestContext = getRequestContext(window.location);
- const [dataLoader] = dataLoaderConfig;
- const promise = callDataLoader(dataLoader, requestContext);
+ const { loader } = dataLoaderConfig;
+ const promise = callDataLoader(loader, requestContext);
cache.set(id, {
value: promise,
@@ -241,7 +249,7 @@ async function init(loaders: Loaders, options: Options) {
// Call dataLoader.
const requestContext = getRequestContext(window.location);
- const [loader] = dataLoaderConfig;
+ const { loader } = dataLoaderConfig;
return callDataLoader(loader, requestContext);
},
};
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 8280e8b7eb..79e7fce153 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -143,7 +143,7 @@ export function createRouteLoader(options: RouteLoaderOptions): LoaderFunction {
};
}
- const [loader, loaderOptions] = dataLoaderConfig;
+ const { loader, options: loaderOptions } = dataLoaderConfig;
const getData = () => {
const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__;
diff --git a/packages/runtime/src/types.ts b/packages/runtime/src/types.ts
index b9a1b8c451..2242fd6718 100644
--- a/packages/runtime/src/types.ts
+++ b/packages/runtime/src/types.ts
@@ -75,7 +75,10 @@ export interface DataLoaderOptions {
defer?: boolean;
}
-export type DataLoaderConfig = [Loader, DataLoaderOptions?];
+export interface DataLoaderConfig {
+ loader: Loader;
+ options?: DataLoaderOptions;
+}
export interface LoadersData {
[routeId: string]: LoaderData;
From 970b94d7436e441ae6779ead9802e0ee20fe19a7 Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Mon, 24 Apr 2023 08:56:14 +0800
Subject: [PATCH 37/38] fix: test
---
packages/runtime/tests/routes.test.tsx | 8 ++---
packages/runtime/tests/runClientApp.test.tsx | 32 ++++++++++++--------
packages/runtime/tests/runServerApp.test.tsx | 4 ++-
3 files changed, 27 insertions(+), 17 deletions(-)
diff --git a/packages/runtime/tests/routes.test.tsx b/packages/runtime/tests/routes.test.tsx
index de51bd1f43..abcde3b6d6 100644
--- a/packages/runtime/tests/routes.test.tsx
+++ b/packages/runtime/tests/routes.test.tsx
@@ -28,9 +28,9 @@ describe('routes', () => {
const homeItem = {
default: () => <>>,
pageConfig: () => ({ title: 'home' }),
- dataLoader: [async () => ({ type: 'getData' })],
- serverDataLoader: [async () => ({ type: 'getServerData' })],
- staticDataLoader: [async () => ({ type: 'getStaticData' })],
+ dataLoader: { loader: async () => ({ type: 'getData' }) },
+ serverDataLoader: { loader: async () => ({ type: 'getServerData' }) },
+ staticDataLoader: { loader: async () => ({ type: 'getStaticData' }) },
};
const aboutItem = {
default: () => <>>,
@@ -39,7 +39,7 @@ describe('routes', () => {
const InfoItem = {
default: () => <>>,
pageConfig: () => ({ title: 'home' }),
- dataLoader: [async () => ({ type: 'getAsyncData' }), { defer: true }],
+ dataLoader: { loader: async () => ({ type: 'getAsyncData' }), options: { defer: true } },
};
const homeLazyItem = {
diff --git a/packages/runtime/tests/runClientApp.test.tsx b/packages/runtime/tests/runClientApp.test.tsx
index 710bb69de5..63c889813e 100644
--- a/packages/runtime/tests/runClientApp.test.tsx
+++ b/packages/runtime/tests/runClientApp.test.tsx
@@ -92,7 +92,9 @@ describe('run client app', () => {
);
},
pageConfig: () => ({ title: 'home' }),
- dataLoader: async () => ({ data: 'test' }),
+ dataLoader: {
+ loader: async () => ({ data: 'test' }),
+ },
};
const basicRoutes = [
{
@@ -110,9 +112,11 @@ describe('run client app', () => {
it('run with static runtime', async () => {
await runClientApp({
app: {
- dataLoader: [async () => {
- return { msg: staticMsg };
- }],
+ dataLoader: {
+ loader: async () => {
+ return { msg: staticMsg };
+ },
+ },
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
@@ -261,10 +265,12 @@ describe('run client app', () => {
let executed = false;
await runClientApp({
app: {
- dataLoader: [async () => {
- executed = true;
- return { msg: '-getAppData' };
- }],
+ dataLoader: {
+ loader: async () => {
+ executed = true;
+ return { msg: '-getAppData' };
+ },
+ },
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
@@ -290,10 +296,12 @@ describe('run client app', () => {
await runClientApp({
app: {
- dataLoader: [async () => {
- executed = true;
- return { msg: 'app' };
- }],
+ dataLoader: {
+ loader: async () => {
+ executed = true;
+ return { msg: 'app' };
+ },
+ },
},
// @ts-ignore don't need to pass params in test case.
createRoutes: () => basicRoutes,
diff --git a/packages/runtime/tests/runServerApp.test.tsx b/packages/runtime/tests/runServerApp.test.tsx
index 0810aa95f2..2ef60ee19d 100644
--- a/packages/runtime/tests/runServerApp.test.tsx
+++ b/packages/runtime/tests/runServerApp.test.tsx
@@ -14,7 +14,9 @@ describe('run server app', () => {
const homeItem = {
default: () => home
,
pageConfig: () => ({ title: 'home' }),
- dataLoader: [async () => ({ data: 'test' })],
+ dataLoader: {
+ loader: async () => ({ data: 'test' }),
+ },
};
const basicRoutes = [
{
From 3b7fcb9e326726d14eaf57b83157e9ea126e2e4b Mon Sep 17 00:00:00 2001
From: "shuilan.cj"
Date: Mon, 24 Apr 2023 19:00:21 +0800
Subject: [PATCH 38/38] fix: compat with old useage
---
packages/runtime/src/appData.ts | 9 ++++++++-
packages/runtime/src/routes.tsx | 11 ++++++++++-
2 files changed, 18 insertions(+), 2 deletions(-)
diff --git a/packages/runtime/src/appData.ts b/packages/runtime/src/appData.ts
index a610505383..57fb28e2f5 100644
--- a/packages/runtime/src/appData.ts
+++ b/packages/runtime/src/appData.ts
@@ -18,7 +18,14 @@ async function getAppData(appExport: AppExport, requestContext?: RequestContext)
return null;
}
- const { loader } = appDataLoaderConfig;
+ let loader;
+
+ if (typeof appDataLoaderConfig === 'function' || Array.isArray(appDataLoaderConfig)) {
+ loader = appDataLoaderConfig;
+ } else {
+ loader = appDataLoaderConfig.loader;
+ }
+
return await callDataLoader(loader, requestContext);
}
diff --git a/packages/runtime/src/routes.tsx b/packages/runtime/src/routes.tsx
index 79e7fce153..83db91842a 100644
--- a/packages/runtime/src/routes.tsx
+++ b/packages/runtime/src/routes.tsx
@@ -143,7 +143,16 @@ export function createRouteLoader(options: RouteLoaderOptions): LoaderFunction {
};
}
- const { loader, options: loaderOptions } = dataLoaderConfig;
+ let loader;
+ let loaderOptions;
+
+ // Compat dataLoaderConfig not return by defineDataLoader.
+ if (typeof dataLoaderConfig === 'function' || Array.isArray(dataLoaderConfig)) {
+ loader = dataLoaderConfig;
+ } else {
+ loader = dataLoaderConfig.loader;
+ loaderOptions = dataLoaderConfig.options;
+ }
const getData = () => {
const hasGlobalLoader = typeof window !== 'undefined' && (window as any).__ICE_DATA_LOADER__;