/
githubmirror
/
angular
Обзор
Документация
Войти
/
githubmirror
/
angular
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
devtools/projects/ng-devtools-backend/src/lib/router-tree/router-tree.ts
301 строка
9 KB
hawkgs
refactor(devtools): reorganize backend code
20 июл 2026, 12:15
20 июл 2026, 12:15
a7ebf1c
Код
Авторство
О чём код?
/** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ import {Route} from '../../../../protocol'; import type {Route as AngularRoute, ActivatedRoute} from '@angular/router'; export type RoutePropertyType = | RouteGuard | 'providers' | 'component' | 'redirectTo' | 'title' | 'resolvers' | 'matcher' | 'runGuardsAndResolvers'; export type RouteGuard = 'canActivate' | 'canActivateChild' | 'canDeactivate' | 'canMatch'; type Routes = any; type Router = any; /** * Recursively traverses the ActivatedRoute tree and collects all routeConfig objects. * @param activatedRoute - The ActivatedRoute to start traversal from * @param activeRoutes - Set to collect active Route configuration objects * @returns Set of active Route configuration objects */ function collectActiveRouteConfigs( activatedRoute: ActivatedRoute, activeRoutes: Set<AngularRoute> = new Set(), ): Set<AngularRoute> { // Get the routeConfig for this ActivatedRoute const routeConfig = activatedRoute.routeConfig; if (routeConfig) { activeRoutes.add(routeConfig); } // Recursively process all children const children = activatedRoute.children || []; for (const child of children) { collectActiveRouteConfigs(child, activeRoutes); } return activeRoutes; } /** * Gets the set of currently active Route configuration objects from the router state. * This function synchronously reads the current router state without waiting for navigation events. * * @param router - The Angular Router instance * @returns A Set containing all Route configuration objects that are currently active * * @example * ```ts * const activeRoutes = getActiveRouteConfigs(router); * // activeRoutes is a Set<Route> containing all currently active route configurations * ``` */ export function getActiveRouteConfigs(router: Router): Set<AngularRoute> { const rootActivatedRoute = router.routerState?.root; if (!rootActivatedRoute) { return new Set(); } return collectActiveRouteConfigs(rootActivatedRoute); } export function parseRoutes(router: Router): Route { const rootName = 'App Root'; const rootChildren = router.config; // Get the set of active Route configuration objects from the router state const activeRouteConfigs = getActiveRouteConfigs(router); const root: Route = { component: rootName, path: rootName, children: rootChildren ? assignChildrenToParent(null, rootChildren, activeRouteConfigs) : [], isAux: false, isLazy: false, isActive: true, // Root is always active. }; return root; } function getGuardNames(child: AngularRoute, type: RouteGuard): string[] { const guards = child?.[type] || []; const names = guards.map((g: any) => getClassOrFunctionName(g)); return names || []; } function getProviderName(child: any): string[] { const providers = child?.providers || []; const names = providers.map((p: any) => p.name); return names || []; } function assignChildrenToParent( parentPath: string | null, children: Routes, activeRouteConfigs: Set<AngularRoute>, ): Route[] { return children.map((child: AngularRoute) => { const childName = childRouteName(child); const loadedRoutes = (window as any).ng?.ɵgetLoadedRoutes?.(child as any); const childDescendents: [AngularRoute] = loadedRoutes || child.children; const pathFragment = child.outlet ? `(${child.outlet}:${child.path})` : child.path; const routePath = `${parentPath ?? ''}/${pathFragment}`.split('//').join('/'); // only found in aux routes, otherwise property will be undefined const isAux = Boolean(child.outlet); const isLazy = Boolean(child.loadChildren || child.loadComponent); // Check if this route configuration object is in the active routes set // This is the direct reference to the Route object from router.config const isActive = activeRouteConfigs.has(child); const routeConfig: Route = { pathMatch: child.pathMatch, component: childName, canActivateGuards: getGuardNames(child, 'canActivate'), canActivateChildGuards: getGuardNames(child, 'canActivateChild'), canMatchGuards: getGuardNames(child, 'canMatch'), canDeactivateGuards: getGuardNames(child, 'canDeactivate'), providers: getProviderName(child), path: routePath, isAux, isLazy, isActive, }; if (child.title) { routeConfig.title = getPropertyName(child, 'title'); } if (child.redirectTo) { routeConfig.redirectTo = getPropertyName(child, 'redirectTo'); } if (child.matcher) { routeConfig.matcher = getPropertyName(child, 'matcher'); // For custom matchers, override the path to indicate it's a matcher // Since the path be undefined when using a matcher, because the matcher defines the path matching routeConfig.path = '[Matcher]'; } if (child.runGuardsAndResolvers) { routeConfig.runGuardsAndResolvers = getPropertyName(child, 'runGuardsAndResolvers'); } if (childDescendents) { routeConfig.children = assignChildrenToParent( routeConfig.path, childDescendents, activeRouteConfigs, ); } if (child.resolve) { routeConfig.resolvers = {}; for (const [name, resolver] of Object.entries(child.resolve)) { routeConfig.resolvers[name] = getClassOrFunctionName(resolver); } } if (child.data) { routeConfig.data = child.data; } return routeConfig; }); } /** * Get the display name for a function or class. * @param fn - The function or class to get the name from * @param defaultName - Optional name to check against. If the function name matches this value, * '[Function]' is returned instead * @returns The formatted name: class name, function name with '()', or '[Function]' for anonymous/arrow functions */ function getClassOrFunctionName(fn: Function, defaultName?: string) { const isArrow = !fn.hasOwnProperty('prototype'); const isEmptyName = fn.name === ''; if ((isArrow && isEmptyName) || isEmptyName) { return '[Function]'; } const hasDefaultName = fn.name === defaultName; if (hasDefaultName) { return '[Function]'; } return fn.name; } function getPropertyName( child: AngularRoute, property: 'title' | 'redirectTo' | 'matcher' | 'runGuardsAndResolvers', ) { if (child[property] instanceof Function) { return getClassOrFunctionName(child[property], property); } return child[property]; } function childRouteName(child: AngularRoute): string { if (child.component) { return child.component.name; } else if (child.loadChildren || child.loadComponent) { return `${child.path} [Lazy]`; } else { return 'no-name-route'; } } /** * Find a named callable construct (class or function) by type & name in a source routes tree. * * @param routes - Source routes tree to search through. * @param type - Type of construct to search for (e.g. canActivate, providers, redirectTo, title, etc.). * @param constructName - Name of the construct to search for. * @returns - The callable construct reference (`Function`) if found; otherwise, `null`. */ export function getRouterCallableConstructRef( routes: AngularRoute[], type: RoutePropertyType, constructName: string, ): Function | null { for (const route of routes) { switch (type) { case 'component': if (route.component?.name === constructName) { return route.component; } break; case 'resolvers': if (route.resolve) { for (const key of Object.keys(route.resolve)) { const functionName = getClassOrFunctionName(route.resolve[key]); if (functionName === constructName) { return route.resolve[key]; } } } break; case 'title': case 'redirectTo': case 'matcher': case 'runGuardsAndResolvers': if (route[type] instanceof Function) { const functionName = getClassOrFunctionName(route[type]); if (functionName === constructName) { return route[type]; } } break; case 'canActivate': case 'canActivateChild': case 'canDeactivate': case 'canMatch': case 'providers': if (route[type]) { for (const callable of route[type]) { if (callable instanceof Function && callable.name === constructName) { return callable; } } } break; } // _loadedRoutes is internal, we can't access it with the dot notation const loadedRoutes = (route as any)['_loadedRoutes'] as AngularRoute[] | undefined; const childrenRoutes = loadedRoutes || route.children; if (childrenRoutes) { const result = getRouterCallableConstructRef(childrenRoutes, type, constructName); if (result !== null) { return result; } } } return null; }