import * as React from 'react';
import { useEffect, useState, useMemo, memo } from 'react';
import * as ReactDOM from 'react-dom';
import { AppConfig, AppRouter, AppRoute } from '@ice/stark';
import {
isInIcestark,
getMountNode,
registerAppEnter,
registerAppLeave,
getBasename,
} from '@ice/stark-app';
// @ts-ignore
import { IceRouter } from '$ice/Router';
// @ts-ignore
import DefaultLayout from '$ice/Layout';
import removeRootLayout from './runtime/removeLayout';
import { IPrivateIceStark, IIceStark, IStarkAppConfig } from './types';
const module = ({
appConfig,
addDOMRender,
buildConfig,
setRenderApp,
setRenderRouter,
wrapperRouterRender,
modifyRoutes,
applyRuntimeAPI,
createHistory,
wrapperPageComponent,
wrapperRouteComponent
}) => {
const { icestark, router } = appConfig;
const {
type: appType = process.env.__ICESTARK_TYPE__,
registerAppEnter: enterRegistration,
registerAppLeave: leaveRegistration,
getApps,
$$props
} = (icestark || {}) as IPrivateIceStark;
const {
type,
basename,
modifyRoutes: runtimeModifyRoutes,
fallback,
initialIndex,
initialEntries,
} = router;
// compatible with deprecated runtime API
const wrapperComponent = wrapperPageComponent || wrapperRouteComponent;
const createAppHistory = createHistory || ((options: any) => applyRuntimeAPI('createHistory', options));
const setRenderComponent = setRenderApp || setRenderRouter;
if (runtimeModifyRoutes) {
modifyRoutes(runtimeModifyRoutes);
}
if (appType === 'child') {
const { icestarkUMD, icestarkType } = buildConfig;
const localIcestarkType = icestarkType || (icestarkUMD ? 'umd' : 'normal');
const childBasename = isInIcestark() ? getBasename() : basename;
addDOMRender(({ App, appMountNode }) => {
return new Promise(resolve => {
if (isInIcestark()) {
if (localIcestarkType === 'normal') {
// @ts-ignore remove this next time for https://github.com/ice-lab/icestark/pull/440
registerAppEnter((props) => {
const container = (props && props.container) || getMountNode();
if (enterRegistration) {
enterRegistration(container, App, resolve);
} else {
ReactDOM.render(, container, () => {
resolve(true);
});
}
});
// make sure the unmount event is triggered
// @ts-ignore remove this next time for https://github.com/ice-lab/icestark/pull/440
registerAppLeave((props) => {
const container = (props && props.container) || getMountNode();
if (leaveRegistration) {
leaveRegistration(container);
} else {
ReactDOM.unmountComponentAtNode(container);
}
});
} else {
let { container } = $$props ?? {};
if (!container) {
container = getMountNode() as HTMLElement;
}
ReactDOM.render(, container, () => {
resolve(true);
});
}
} else {
ReactDOM.render(, appMountNode, () => {
resolve(true);
});
}
});
});
const wrapperPageFn = (PageComponent) => (props) => {
const { customProps = {} } = $$props ?? {};
const combinedProps = {
...props,
frameworkProps: customProps,
};
return ;
};
// get props by props
wrapperComponent(wrapperPageFn);
// `buildConfig.router` is false when configrated in build.json.
if (buildConfig.router !== false) {
const history = createAppHistory({ type, basename: childBasename, initialIndex, initialEntries });
const routerProps = {
type,
basename: childBasename,
history,
fallback
};
// compatible with the case which lock icejs version
if (wrapperRouterRender && !!process.env.__FRAMEWORK_VERSION__) {
wrapperRouterRender((originRender) => (routes, RoutesComponent) => {
return originRender(routes, RoutesComponent, routerProps);
});
} else {
setRenderComponent((routes) => () => {
return ;
});
}
}
} else if (appType === 'framework' && getApps) {
const { appRouter, Layout, AppRoute: CustomAppRoute, removeRoutesLayout } = (icestark || {}) as IIceStark;
if (removeRoutesLayout) {
modifyRoutes(removeRootLayout);
}
const RootApp = ({ routes }) => {
const [routerHistory] = useState(createAppHistory({ type, basename, initialEntries, initialIndex }));
const routerProps = {
type,
routes,
basename,
history: routerHistory,
fallback
};
return ;
};
const frameworkRouter = (routes) => () => {
const [appPathname, setAppPathname] = useState(window.location.pathname);
const [routeInfo, setRouteInfo] = useState({});
const [appEnter, setAppEnter] = useState({});
const [appLeave, setAppLeave] = useState({});
const [apps, setApps] = useState(null);
const BasicLayout = Layout || DefaultLayout || ((props) => (<>{props.children}>));
const RenderAppRoute = (CustomAppRoute || AppRoute) as typeof AppRoute;
useEffect(() => {
(async () => {
// 异步 apps 获取
const appList = await getApps();
setApps(appList);
})();
}, []);
function handleRouteChange(pathname, query, hash, routeType) {
setRouteInfo({ pathname, query, hash, routeType });
setAppPathname(pathname);
}
function handleAppLeave(config) {
setAppLeave(config);
}
function handleAppEnter(config) {
setAppEnter(config);
}
const appInfo = {
pathname: appPathname,
routeInfo,
appEnter,
appLeave,
updateApps: setApps,
};
// RootApp will re-render on every AppRoute's update if RootApp were matched.
const MemoRootApp = useMemo(
() => memo(() => ),
[]
);
return (
{apps && (
{apps.map((item: AppConfig, idx: number) => {
return (
);
})}
{routes && routes.length && (
{
return ;
}}
/>
)}
)}
);
};
setRenderComponent(frameworkRouter);
}
if (appType === 'framework' && !getApps) {
console.warn(`
[plugin-icestark]: appConfig.icestark.getApps should be not empty if this is an framework app; If not,please make sure appConfgi.icestark.type exist.
see https://ice.work/docs/guide/advanced/icestark/
`);
}
};
export default module;