mirror of https://github.com/alibaba/ice.git
				
				
				
			feat: support remove router even if route count is greater than 1 (#6382)
* feat: support remove router even if route count is greater than 1 * fix: add test case * Update createService.ts * Update createService.ts
This commit is contained in:
		
							parent
							
								
									018238f904
								
							
						
					
					
						commit
						b691b9e96c
					
				|  | @ -0,0 +1,6 @@ | |||
| --- | ||||
| '@ice/runtime': patch | ||||
| '@ice/app': patch | ||||
| --- | ||||
| 
 | ||||
| feat: support remove router even if route count is greater than 1 | ||||
|  | @ -3,6 +3,6 @@ import { defineConfig } from '@ice/app'; | |||
| export default defineConfig(() => ({ | ||||
|   publicPath: '/', | ||||
|   optimization: { | ||||
|     router: true, | ||||
|     disableRouter: true, | ||||
|   }, | ||||
| })); | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| const home = () => { | ||||
|   return ( | ||||
|     <>home</> | ||||
|   ); | ||||
| }; | ||||
| 
 | ||||
| export default home; | ||||
|  | @ -232,9 +232,10 @@ async function createService({ rootDir, command, commandArgs }: CreateServiceOpt | |||
|   const hasExportAppData = (await getFileExports({ rootDir, file: 'src/app' })).includes('dataLoader'); | ||||
|   const csr = !userConfig.ssr && !userConfig.ssg; | ||||
| 
 | ||||
|   const disableRouter = userConfig?.optimization?.router && routesInfo.routesCount <= 1; | ||||
|   const disableRouter = (userConfig?.optimization?.router && routesInfo.routesCount <= 1) || | ||||
|     userConfig?.optimization?.disableRouter; | ||||
|   if (disableRouter) { | ||||
|     logger.info('`optimization.router` is enabled and only have one route, ice build will remove react-router and history which is unnecessary.'); | ||||
|     logger.info('`optimization.router` is enabled, ice build will remove react-router and history which is unnecessary.'); | ||||
|     taskConfigs = mergeTaskConfig(taskConfigs, { | ||||
|       alias: { | ||||
|         '@ice/runtime/router': '@ice/runtime/single-router', | ||||
|  |  | |||
|  | @ -13,9 +13,15 @@ interface SyntaxFeatures { | |||
| 
 | ||||
| interface Optimization { | ||||
|   /** | ||||
|    * Optimize code by remove react-router dependencies when set to true. | ||||
|    * Optimize code by remove react-router dependencies when set to true, | ||||
|    * it only works when route count is 1. | ||||
|    */ | ||||
|   router?: boolean; | ||||
|   /** | ||||
|    * @private | ||||
|    * Remove react-router dependencies by force, even if route count is greater than 1. | ||||
|    */ | ||||
|   disableRouter?: boolean; | ||||
| } | ||||
| 
 | ||||
| interface MinifyOptions { | ||||
|  |  | |||
|  | @ -139,7 +139,7 @@ interface RenderOptions { | |||
| 
 | ||||
| async function render({ history, runtime, needHydrate }: RenderOptions) { | ||||
|   const appContext = runtime.getAppContext(); | ||||
|   const { appConfig, loaderData, routes, basename } = appContext; | ||||
|   const { appConfig, loaderData, routes, basename, routePath } = appContext; | ||||
|   const appRender = runtime.getRender(); | ||||
|   const AppRuntimeProvider = runtime.composeAppProvider() || React.Fragment; | ||||
|   const AppRouter = runtime.getAppRouter<ClientAppRouterProps>(); | ||||
|  | @ -154,8 +154,9 @@ async function render({ history, runtime, needHydrate }: RenderOptions) { | |||
|   } | ||||
|   const hydrationData = needHydrate ? { loaderData } : undefined; | ||||
|   const routeModuleCache = {}; | ||||
|   const location = history.location ? history.location : { pathname: routePath || window.location.pathname }; | ||||
|   if (needHydrate) { | ||||
|     const lazyMatches = matchRoutes(routes, history.location, basename).filter((m) => m.route.lazy); | ||||
|     const lazyMatches = matchRoutes(routes, 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.
 | ||||
|  | @ -182,8 +183,9 @@ async function render({ history, runtime, needHydrate }: RenderOptions) { | |||
|   let singleComponent = null; | ||||
|   let routeData = null; | ||||
|   if (process.env.ICE_CORE_ROUTER !== 'true') { | ||||
|     const { Component, loader } = await loadRouteModule(routes[0], routeModuleCache); | ||||
|     singleComponent = Component || routes[0].Component; | ||||
|     const singleRoute = matchRoutes(routes, location, basename)[0]; | ||||
|     const { Component, loader } = await loadRouteModule(singleRoute.route, routeModuleCache); | ||||
|     singleComponent = Component || singleRoute.route.Component; | ||||
|     routeData = loader && await loader(); | ||||
|   } | ||||
|   const renderRoot = appRender( | ||||
|  |  | |||
|  | @ -42,15 +42,30 @@ export const createHistory = (): History => { | |||
|   }; | ||||
| }; | ||||
| 
 | ||||
| export const matchRoutes = (routes: any[]) => { | ||||
|   return routes.map(item => { | ||||
|     return { | ||||
|       params: {}, | ||||
|       pathname: '', | ||||
|       pathnameBase: '', | ||||
|       route: item, | ||||
|     }; | ||||
| const stripString = (str: string) => { | ||||
|   const regexForSlash = /^\/|\/$/g; | ||||
|   return str.replace(regexForSlash, ''); | ||||
| }; | ||||
| 
 | ||||
| export const matchRoutes = (routes: any[], location: Partial<Location> | string, basename: string) => { | ||||
|   const stripedBasename = stripString(basename); | ||||
|   const pathname = typeof location === 'string' ? location : location.pathname; | ||||
|   let stripedPathname = stripString(pathname); | ||||
|   if (stripedBasename) { | ||||
|     stripedPathname = stripedPathname.replace(new RegExp(`^${stripedBasename}/`), ''); | ||||
|   } | ||||
|   const route = routes.length === 1 ? routes[0] : routes.find(item => { | ||||
|     return stripString(item.path || '') === stripedPathname; | ||||
|   }); | ||||
|   if (!route) { | ||||
|     throw new Error(`No route matched pathname: ${pathname}`); | ||||
|   } | ||||
|   return [{ | ||||
|     route, | ||||
|     params: {}, | ||||
|     pathname, | ||||
|     pathnameBase: '', | ||||
|   }]; | ||||
| }; | ||||
| 
 | ||||
| export const Link = () => null; | ||||
|  |  | |||
|  | @ -37,8 +37,54 @@ describe('single route api', () => { | |||
|     expect(createHistory().location).toBe(''); | ||||
|   }); | ||||
| 
 | ||||
|   it('matchRoutes', () => { | ||||
|     expect(matchRoutes([{}])[0].pathname).toBe(''); | ||||
|   it('matchRoutes - single route', () => { | ||||
|     const routes = [ | ||||
|       { | ||||
|         path: 'users', | ||||
|         element: <div>user</div>, | ||||
|       }, | ||||
|     ]; | ||||
|     const location = { | ||||
|       pathname: '/test', | ||||
|     }; | ||||
|     const matchedRoutes = matchRoutes(routes, location, '/'); | ||||
|     expect(matchedRoutes).toHaveLength(1); | ||||
|     expect(matchedRoutes[0].route.path).toBe('users'); | ||||
|   }); | ||||
| 
 | ||||
|   it('matchRoutes - mutiple route', () => { | ||||
|     const routes = [ | ||||
|       { | ||||
|         path: 'users', | ||||
|         element: <div>user</div>, | ||||
|       }, | ||||
|       { | ||||
|         path: 'posts', | ||||
|         element: <div>post</div>, | ||||
|       }, | ||||
|     ]; | ||||
|     const location = { | ||||
|       pathname: '/posts', | ||||
|     }; | ||||
|     const matchedRoutes = matchRoutes(routes, location, '/'); | ||||
|     expect(matchedRoutes).toHaveLength(1); | ||||
|     expect(matchedRoutes[0].route.path).toBe('posts'); | ||||
|   }); | ||||
| 
 | ||||
|   it('matchRoutes - basename', () => { | ||||
|     const routes = [ | ||||
|       { | ||||
|         path: 'users', | ||||
|         element: <div>user</div>, | ||||
|       }, | ||||
|       { | ||||
|         path: 'posts', | ||||
|         element: <div>post</div>, | ||||
|       }, | ||||
|     ]; | ||||
|     const matchedRoutes = matchRoutes(routes, '/basename/posts', '/basename'); | ||||
|     expect(matchedRoutes).toHaveLength(1); | ||||
|     expect(matchedRoutes[0].route.path).toBe('posts'); | ||||
|   }); | ||||
| 
 | ||||
|   it('Link', () => { | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue