mirror of https://github.com/alibaba/ice.git
Feat: enhance plugin icestark and refactor webpack bundle exports (#7068)
* feat: support custom AppRoute * feat: support framework provider * fix: failed to resolve context * fix: add export type * fix: add the library name to global * feat: add export of webpack * fix: require hooks * fix: export webpack module * feat: add export of webpack * fix: support resolve webpack * fix: revert bundles version * fix: update hook path * fix: render AppRouter * fix: init default value
This commit is contained in:
parent
b930600a17
commit
2f73084d61
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@ice/plugin-icestark': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: support framework provider
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@ice/bundles': patch
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: export ModuleNotFoundError of webpack
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
---
|
||||||
|
'@ice/plugin-icestark': minor
|
||||||
|
---
|
||||||
|
|
||||||
|
feat: support custom AppRoute
|
||||||
|
|
@ -18,6 +18,24 @@ module.exports = {
|
||||||
StringXor: require('webpack/lib/util/StringXor'),
|
StringXor: require('webpack/lib/util/StringXor'),
|
||||||
NormalModule: require('webpack/lib/NormalModule'),
|
NormalModule: require('webpack/lib/NormalModule'),
|
||||||
EntryDependency: require('webpack/lib/dependencies/EntryDependency'),
|
EntryDependency: require('webpack/lib/dependencies/EntryDependency'),
|
||||||
|
ModuleNotFoundError: require('webpack/lib/ModuleNotFoundError'),
|
||||||
|
LazySet: require('webpack/lib/util/LazySet'),
|
||||||
|
makeSerializable: require('webpack/lib/util/makeSerializable'),
|
||||||
|
SortableSet: require('webpack/lib/util/SortableSet'),
|
||||||
|
StaticExportsDependency: require('webpack/lib/dependencies/StaticExportsDependency'),
|
||||||
|
ModuleFactory: require('webpack/lib/ModuleFactory'),
|
||||||
|
ModuleDependency: require('webpack/lib/dependencies/ModuleDependency'),
|
||||||
|
createSchemaValidation: require('webpack/lib/util/create-schema-validation'),
|
||||||
|
extractUrlAndGlobal: require('webpack/lib/util/extractUrlAndGlobal'),
|
||||||
|
Compilation: require('webpack/lib/Compilation'),
|
||||||
|
semver: require('webpack/lib/util/semver'),
|
||||||
|
WebpackError: require('webpack/lib/WebpackError'),
|
||||||
|
comparators: require('webpack/lib/util/comparators'),
|
||||||
|
StartupEntrypointRuntimeModule: require('webpack/lib/runtime/StartupEntrypointRuntimeModule'),
|
||||||
|
SetHelpers: require('webpack/lib/util/SetHelpers'),
|
||||||
|
ChunkHelpers: require('webpack/lib/javascript/ChunkHelpers'),
|
||||||
|
HotUpdateChunk: require('webpack/lib/HotUpdateChunk'),
|
||||||
|
fs: require('webpack/lib/util/fs'),
|
||||||
sources: require('webpack').sources,
|
sources: require('webpack').sources,
|
||||||
webpack: require('webpack'),
|
webpack: require('webpack'),
|
||||||
package: {
|
package: {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').ChunkHelpers;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').Compilation;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').HotUpdateChunk;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').LazySet;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').ModuleDependency;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').ModuleFactory;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').ModuleNotFoundError;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').SetHelpers;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').SortableSet;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').StartupEntrypointRuntimeModule;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').StaticExportsDependency;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').WebpackError;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').comparators;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').createSchemaValidation;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').extractUrlAndGlobal;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').fs;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').makeSerializable;
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
module.exports = require('./bundle').semver;
|
||||||
|
|
@ -12,7 +12,6 @@ export function getFileName(filePath: string) {
|
||||||
return filePath.split('/').slice(-1)[0];
|
return filePath.split('/').slice(-1)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getHookFiles() {
|
|
||||||
const webpackPlugins = [
|
const webpackPlugins = [
|
||||||
// plugins require the same webpack instance
|
// plugins require the same webpack instance
|
||||||
'webpack/lib/LibraryTemplatePlugin',
|
'webpack/lib/LibraryTemplatePlugin',
|
||||||
|
|
@ -33,20 +32,36 @@ export function getHookFiles() {
|
||||||
'webpack/lib/javascript/StartupHelpers',
|
'webpack/lib/javascript/StartupHelpers',
|
||||||
'webpack/lib/util/identifier',
|
'webpack/lib/util/identifier',
|
||||||
'webpack/lib/util/compileBooleanMatcher',
|
'webpack/lib/util/compileBooleanMatcher',
|
||||||
|
'webpack/lib/ModuleNotFoundError',
|
||||||
|
'webpack/lib/util/LazySet',
|
||||||
|
'webpack/lib/util/fs',
|
||||||
|
'webpack/lib/util/makeSerializable',
|
||||||
|
'webpack/lib/util/SortableSet',
|
||||||
|
'webpack/lib/dependencies/StaticExportsDependency',
|
||||||
|
'webpack/lib/dependencies/EntryDependency',
|
||||||
|
'webpack/lib/ModuleFactory',
|
||||||
|
'webpack/lib/dependencies/ModuleDependency',
|
||||||
|
'webpack/lib/util/create-schema-validation',
|
||||||
|
'webpack/lib/util/extractUrlAndGlobal',
|
||||||
|
'webpack/lib/Compilation',
|
||||||
|
'webpack/lib/util/semver',
|
||||||
|
'webpack/lib/WebpackError',
|
||||||
|
'webpack/lib/util/comparators',
|
||||||
|
'webpack/lib/runtime/StartupEntrypointRuntimeModule',
|
||||||
|
'webpack/lib/util/SetHelpers',
|
||||||
|
'webpack/lib/javascript/ChunkHelpers',
|
||||||
|
'webpack/lib/HotUpdateChunk',
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export function getHookFiles() {
|
||||||
const webpackDir = path.join(require.resolve('@ice/bundles/compiled/webpack'), '../');
|
const webpackDir = path.join(require.resolve('@ice/bundles/compiled/webpack'), '../');
|
||||||
const pluginMap = webpackPlugins.map((pluginPath) => {
|
const createPluginMapping = (pluginPath: string, withJsExtension = false) => [
|
||||||
return [
|
withJsExtension ? `${pluginPath}.js` : pluginPath,
|
||||||
pluginPath,
|
pluginPath.replace(/^webpack\/lib\/((web|node|optimize|webworker|runtime|javascript|util|dependencies)\/)?/, webpackDir),
|
||||||
pluginPath.replace(/^webpack\/lib\/((web|node|optimize|webworker|runtime|javascript|util)\/)?/, webpackDir),
|
|
||||||
];
|
];
|
||||||
});
|
|
||||||
const pluginMapWithJs = webpackPlugins.map((pluginPath) => {
|
const pluginMap = webpackPlugins.map(pluginPath => createPluginMapping(pluginPath));
|
||||||
return [
|
const pluginMapWithJs = webpackPlugins.map(pluginPath => createPluginMapping(pluginPath, true));
|
||||||
`${pluginPath}.js`,
|
|
||||||
pluginPath.replace(/^webpack\/lib\/((web|node|optimize|webworker|runtime|javascript|util)\/)?/, webpackDir),
|
|
||||||
];
|
|
||||||
});
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
['webpack', `${webpackDir}webpack-lib`],
|
['webpack', `${webpackDir}webpack-lib`],
|
||||||
|
|
@ -69,7 +84,9 @@ function hijackWebpack() {
|
||||||
const resolveFilename = mod._resolveFilename;
|
const resolveFilename = mod._resolveFilename;
|
||||||
mod._resolveFilename = function (request: string, parent: any, isMain: boolean, options: any) {
|
mod._resolveFilename = function (request: string, parent: any, isMain: boolean, options: any) {
|
||||||
const hookResolved = hookPropertyMap.get(request);
|
const hookResolved = hookPropertyMap.get(request);
|
||||||
if (hookResolved) request = hookResolved;
|
if (hookResolved) {
|
||||||
|
request = hookResolved;
|
||||||
|
}
|
||||||
return resolveFilename.call(mod, request, parent, isMain, options);
|
return resolveFilename.call(mod, request, parent, isMain, options);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
export * from './esm/runtime/Context';
|
||||||
|
|
@ -18,6 +18,11 @@
|
||||||
"import": "./esm/index.js",
|
"import": "./esm/index.js",
|
||||||
"default": "./esm/index.js"
|
"default": "./esm/index.js"
|
||||||
},
|
},
|
||||||
|
"./Context": {
|
||||||
|
"types": "./esm/runtime/Context.d.ts",
|
||||||
|
"import": "./esm/runtime/Context.js",
|
||||||
|
"default": "./esm/runtime/Context.js"
|
||||||
|
},
|
||||||
"./esm/runtime/child": {
|
"./esm/runtime/child": {
|
||||||
"types": "./esm/runtime/child.d.ts",
|
"types": "./esm/runtime/child.d.ts",
|
||||||
"import": "./esm/runtime/child.js",
|
"import": "./esm/runtime/child.js",
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ const PLUGIN_NAME = '@ice/plugin-icestark';
|
||||||
const plugin: Plugin<PluginOptions> = ({ type, library }) => ({
|
const plugin: Plugin<PluginOptions> = ({ type, library }) => ({
|
||||||
name: PLUGIN_NAME,
|
name: PLUGIN_NAME,
|
||||||
setup: ({ onGetConfig, context, generator, modifyUserConfig }) => {
|
setup: ({ onGetConfig, context, generator, modifyUserConfig }) => {
|
||||||
|
const libraryName = library || context.pkg?.name as string || 'microApp';
|
||||||
onGetConfig((config) => {
|
onGetConfig((config) => {
|
||||||
config.configureWebpack ??= [];
|
config.configureWebpack ??= [];
|
||||||
config.configureWebpack.push((webpackConfig) => {
|
config.configureWebpack.push((webpackConfig) => {
|
||||||
if (type === 'child') {
|
if (type === 'child') {
|
||||||
const { pkg } = context;
|
webpackConfig.output.library = libraryName;
|
||||||
webpackConfig.output.library = library || pkg.name as string || 'microApp';
|
|
||||||
webpackConfig.output.libraryTarget = 'umd';
|
webpackConfig.output.libraryTarget = 'umd';
|
||||||
}
|
}
|
||||||
return webpackConfig;
|
return webpackConfig;
|
||||||
|
|
@ -34,6 +34,10 @@ const plugin: Plugin<PluginOptions> = ({ type, library }) => ({
|
||||||
if (!window.ICESTARK?.root && !window.__POWERED_BY_QIANKUN__) {
|
if (!window.ICESTARK?.root && !window.__POWERED_BY_QIANKUN__) {
|
||||||
root = render();
|
root = render();
|
||||||
}
|
}
|
||||||
|
// Set library name
|
||||||
|
if (typeof window !== 'undefined' && window.ICESTARK) {
|
||||||
|
window.ICESTARK.library = ${JSON.stringify(libraryName)};
|
||||||
|
}
|
||||||
|
|
||||||
// For qiankun lifecycle validation.
|
// For qiankun lifecycle validation.
|
||||||
export async function bootstrap(props) {
|
export async function bootstrap(props) {
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
import { createContext, useContext } from 'react';
|
||||||
|
|
||||||
|
export const FrameworkContext = createContext({});
|
||||||
|
|
||||||
|
export const useFrameworkContext = <T extends object>(): T => {
|
||||||
|
return useContext(FrameworkContext) as T;
|
||||||
|
};
|
||||||
|
|
@ -1,13 +1,19 @@
|
||||||
|
import * as React from 'react';
|
||||||
import * as ReactDOM from 'react-dom/client';
|
import * as ReactDOM from 'react-dom/client';
|
||||||
import type { RuntimePlugin } from '@ice/runtime/types';
|
import type { RuntimePlugin } from '@ice/runtime/types';
|
||||||
import type { LifecycleOptions } from '../types';
|
import type { LifecycleOptions } from '../types';
|
||||||
|
import { FrameworkContext } from './Context.js';
|
||||||
|
|
||||||
const runtime: RuntimePlugin<LifecycleOptions> = ({ setRender }, runtimeOptions) => {
|
const runtime: RuntimePlugin<LifecycleOptions> = ({ setRender }, runtimeOptions) => {
|
||||||
if (runtimeOptions?.container) {
|
if (runtimeOptions?.container) {
|
||||||
setRender((_, element) => {
|
setRender((_, element) => {
|
||||||
// Replace render root when app rendered as a child app.
|
// Replace render root when app rendered as a child app.
|
||||||
const root = ReactDOM.createRoot(runtimeOptions.container);
|
const root = ReactDOM.createRoot(runtimeOptions.container);
|
||||||
root.render(element);
|
root.render(
|
||||||
|
<FrameworkContext.Provider value={{ ...(runtimeOptions.customProps || {}) }}>
|
||||||
|
{element}
|
||||||
|
</FrameworkContext.Provider>,
|
||||||
|
);
|
||||||
return root;
|
return root;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,67 +1,74 @@
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { AppRouter, AppRoute } from '@ice/stark';
|
import { AppRouter, AppRoute } from '@ice/stark';
|
||||||
import type { RuntimePlugin, ClientAppRouterProps } from '@ice/runtime/types';
|
import type { RuntimePlugin, ClientAppRouterProps } from '@ice/runtime/types';
|
||||||
import type { RouteInfo, AppConfig } from '../types';
|
import type { AppRouterProps } from '@ice/stark/lib/AppRouter';
|
||||||
|
import type { RouteInfo, AppConfig, FrameworkConfig } from '../types';
|
||||||
|
|
||||||
const { useState, useEffect } = React;
|
const { useState, useEffect } = React;
|
||||||
|
|
||||||
const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
|
const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
|
||||||
const { appExport, appData } = appContext;
|
const { appExport, appData } = appContext;
|
||||||
const OriginalRouter = getAppRouter();
|
const OriginalRouter = getAppRouter();
|
||||||
const { layout, getApps, appRouter } = appExport?.icestark || {};
|
const { layout, getApps, appRouter, AppRoute: CustomizeAppRoute } = (appExport?.icestark || {}) as FrameworkConfig;
|
||||||
|
|
||||||
|
if (!getApps) {
|
||||||
|
console.warn(`
|
||||||
|
[plugin-icestark]: appConfig.icestark.getApps should be not empty if this is an framework app.
|
||||||
|
see https://ice.work/docs/guide/advanced/icestark/
|
||||||
|
`);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (getApps) {
|
|
||||||
const FrameworkRouter = (props: ClientAppRouterProps) => {
|
const FrameworkRouter = (props: ClientAppRouterProps) => {
|
||||||
const [routeInfo, setRouteInfo] = useState<RouteInfo>({});
|
const [routeInfo, setRouteInfo] = useState<RouteInfo>({});
|
||||||
const [appEnter, setAppEnter] = useState<AppConfig>({});
|
const [appEnter, setAppEnter] = useState<AppConfig>({});
|
||||||
const [appLeave, setAppLeave] = useState<AppConfig>({});
|
const [appLeave, setAppLeave] = useState<AppConfig>({});
|
||||||
const [apps, setApps] = useState([]);
|
const [apps, setApps] = useState<AppConfig[] | null>(null);
|
||||||
const FrameworkLayout = layout || (({ children }) => (<>{children}</>));
|
const FrameworkLayout = layout || React.Fragment;
|
||||||
const appInfo = {
|
const appInfo = {
|
||||||
pathname: routeInfo.pathname ||
|
pathname: routeInfo.pathname || (typeof window !== 'undefined' ? window.location.pathname : ''),
|
||||||
(typeof window !== 'undefined' && window.location.pathname),
|
|
||||||
routeInfo,
|
routeInfo,
|
||||||
appEnter,
|
appEnter,
|
||||||
appLeave,
|
appLeave,
|
||||||
updateApps: setApps,
|
updateApps: setApps,
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
(async () => {
|
const fetchApps = async () => {
|
||||||
|
try {
|
||||||
const appList = await getApps(appData);
|
const appList = await getApps(appData);
|
||||||
setApps(appList);
|
setApps(appList);
|
||||||
})();
|
} catch (error) {
|
||||||
|
console.error('[plugin-icestark]: Failed to fetch apps', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchApps();
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
function handleRouteChange(pathname: string, query: Record<string, string>, hash: string, routeType: string) {
|
const handleRouteChange: AppRouterProps['onRouteChange'] = (pathname, query, hash, routeType) => {
|
||||||
setRouteInfo({ pathname, query, hash, routeType });
|
setRouteInfo({ pathname, query, hash, routeType });
|
||||||
}
|
};
|
||||||
|
|
||||||
function handleAppLeave(config: AppConfig) {
|
const handleAppLeave: AppRouterProps['onAppLeave'] = (config) => setAppLeave(config);
|
||||||
setAppLeave(config);
|
const handleAppEnter: AppRouterProps['onAppEnter'] = (config) => setAppEnter(config);
|
||||||
}
|
const AppRouteComponent = CustomizeAppRoute || AppRoute;
|
||||||
|
|
||||||
function handleAppEnter(config: AppConfig) {
|
const appRouterProps: AppRouterProps = {
|
||||||
setAppEnter(config);
|
...appRouter,
|
||||||
}
|
onRouteChange: handleRouteChange,
|
||||||
|
onAppEnter: handleAppEnter,
|
||||||
|
onAppLeave: handleAppLeave,
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<FrameworkLayout {...appInfo}>
|
<FrameworkLayout {...appInfo}>
|
||||||
{apps && (
|
{apps && (
|
||||||
<AppRouter
|
<AppRouter {...appRouterProps}>
|
||||||
{...appRouter}
|
{apps?.map((item: AppConfig, idx: number) => (
|
||||||
onRouteChange={handleRouteChange}
|
<AppRouteComponent key={idx} {...item} />
|
||||||
onAppEnter={handleAppEnter}
|
))}
|
||||||
onAppLeave={handleAppLeave}
|
<AppRouteComponent
|
||||||
>
|
activePath="/"
|
||||||
{apps.map((item: AppConfig, idx: number) => {
|
|
||||||
return (
|
|
||||||
<AppRoute
|
|
||||||
key={idx}
|
|
||||||
{...item}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
<AppRoute
|
|
||||||
path="/"
|
|
||||||
location={props.location}
|
location={props.location}
|
||||||
render={() => {
|
render={() => {
|
||||||
const { routerContext } = props;
|
const { routerContext } = props;
|
||||||
|
|
@ -76,11 +83,7 @@ const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
const routerProps = {
|
return <OriginalRouter {...props} routerContext={routerContext} />;
|
||||||
...props,
|
|
||||||
routerContext,
|
|
||||||
};
|
|
||||||
return <OriginalRouter {...routerProps} />;
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</AppRouter>
|
</AppRouter>
|
||||||
|
|
@ -88,13 +91,8 @@ const runtime: RuntimePlugin = ({ getAppRouter, setAppRouter, appContext }) => {
|
||||||
</FrameworkLayout>
|
</FrameworkLayout>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
setAppRouter(FrameworkRouter);
|
setAppRouter(FrameworkRouter);
|
||||||
} else {
|
|
||||||
console.warn(`
|
|
||||||
[plugin-icestark]: appConfig.icestark.getApps should be not empty if this is an framework app.
|
|
||||||
see https://ice.work/docs/guide/advanced/icestark/
|
|
||||||
`);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default runtime;
|
export default runtime;
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
import type { ComponentType } from 'react';
|
import type { ComponentType } from 'react';
|
||||||
import type { CompatibleAppConfig } from '@ice/stark/lib/AppRoute';
|
import type { CompatibleAppConfig } from '@ice/stark/lib/AppRoute';
|
||||||
import type { AppRouterProps } from '@ice/stark/lib/AppRouter';
|
import type { AppRouterProps } from '@ice/stark/lib/AppRouter';
|
||||||
|
import type { AppRoute } from '@ice/stark';
|
||||||
|
|
||||||
export interface RouteInfo {
|
export interface RouteInfo {
|
||||||
pathname?: string;
|
pathname?: string;
|
||||||
query?: Record<string, string>;
|
query?: object;
|
||||||
hash?: string;
|
hash?: string;
|
||||||
routeType?: string;
|
routeType?: string;
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +18,7 @@ export interface FrameworkConfig {
|
||||||
getApps?: (data?: any) => (AppConfig[] | Promise<AppConfig[]>);
|
getApps?: (data?: any) => (AppConfig[] | Promise<AppConfig[]>);
|
||||||
appRouter?: Omit<AppRouterProps, 'onRouteChange' | 'onAppEnter' | 'onAppLeave'>;
|
appRouter?: Omit<AppRouterProps, 'onRouteChange' | 'onAppEnter' | 'onAppLeave'>;
|
||||||
layout?: ComponentType<any>;
|
layout?: ComponentType<any>;
|
||||||
|
AppRoute?: typeof AppRoute;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LifecycleOptions {
|
export interface LifecycleOptions {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue