Repository: angular/angular
File: devtools/projects/ng-devtools-backend/src/lib/router-tree.ts (TypeScript)

Code before:
    }

    if (childDescendents) {
      routeConfig.children = assignChildrenToParent(routeConfig.path, childDescendents, currentUrl);
    }

    if (child.data) {
      for (const el in child.data) {
        if (child.data.hasOwnProperty(el)) {
          routeConfig?.data?.push({
            key: el,
            value: child.data[el],
          });
        }
      }
    }

    return routeConfig;
  });
}

function getFunctionName(fn: Function) {
  const isArrow = !fn.hasOwnProperty('prototype');
  if (isArrow) return '[Function]';
  return fn.name === 'redirectTo' ? '[Function]' : fn.name;
}

function getRedirectToName(child: AngularRoute) {
  if (child.redirectTo instanceof Function) {
    return getFunctionName(child.redirectTo);
  }

  return `${child.path} -> redirecting to -> "${child.redirectTo}"`;
}

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';
  }
}

/**
 *  Get the element reference by type & name from the routes array. Called recursively to search through all children.
 * @param type - type of element to search for (canActivate, canActivateChild, canDeactivate, canLoad, providers, redirectTo)
 * @param routes - array of routes to search through
 * @param name - name of the element to search for refers to the name of the guard or provider
 * @returns - the element reference if found, otherwise null

Review comment (suggestion):
Regular anonymous functions have a prototype but not a name (i.e. it will be an empty string), so we'll have to take into account this case.

Also, why are we checking if `fn.name` is `redirectTo`? Is it merely to avoid displaying "RedirectTo: redirectTo"?

~Lastly, although using a named function is probably a very rare instance, I would append `()` to the names to make it clear that this isn't a route name.~

Code after:
      routeConfig.title = getPropertyName(child, 'title');
    }

    if (child.redirectTo) {
      routeConfig.redirectTo = getPropertyName(child, 'redirectTo');
    }

    if (childDescendents) {
      routeConfig.children = assignChildrenToParent(routeConfig.path, childDescendents, currentUrl);
    }

    if (child.data) {
      for (const el in child.data) {
        if (child.data.hasOwnProperty(el)) {
          routeConfig?.data?.push({
            key: el,
            value: child.data[el],
          });
        }
      }
    }

    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 isArrowWithNoName = !fn.hasOwnProperty('prototype') && fn.name === '';

  if (isArrowWithNoName) {
    return '[Function]';
  }

  const hasDefaultName = fn.name === defaultName;
  if (hasDefaultName) {
    return '[Function]';
  }

  // Check if it's a class by examining the function's string representation
  const isClass = /^class\s/.test(fn.toString());

  // Return class name without parentheses, function name with parentheses
  return isClass ? fn.name : `${fn.name}()`;
}