getTree - greydragon888/real-router GitHub Wiki

getPluginApi().getTree

Method for plugin authors

1. Overview

  • What it does: Returns the current route tree — the hierarchical structure of all registered routes
  • When to use:
    • Inspect the registered route hierarchy (route names, paths, parameters)
    • Plugin infrastructure that needs to analyze the route structure
    • Debugging and introspection of route definitions
    • Building route-aware UI (breadcrumbs, navigation menus)
  • Access: getPluginApi(router).getTree() — this is a plugin API method, not a router method

2. Signature

import { getPluginApi } from "@real-router/core/api";
import type { RouteTree } from "route-tree";
// RouteTree is also re-exported from @real-router/core for convenience:
import type { RouteTree } from "@real-router/core";

const pluginApi = getPluginApi(router);

pluginApi.getTree(): RouteTree

Note: getTree() returns a properly typed RouteTree. The type is re-exported from @real-router/core for convenience — no need to import from internal route-tree package.

Return Value

Returns the root RouteTree node of the route hierarchy:

interface RouteTree {
  readonly name: string; // Route segment name (e.g., "users")
  readonly path: string; // Route path pattern (e.g., "/users/:id")
  readonly absolute: boolean; // Whether path uses absolute matching (~)
  readonly children: ReadonlyMap<string, RouteTree>; // Child routes (O(1) lookup by name)
  readonly paramMeta: ParamMeta; // Parameter metadata from path
  readonly parent: RouteTree | null; // Parent node (null for root)
  readonly nonAbsoluteChildren: readonly RouteTree[]; // Children without absolute paths
  readonly fullName: string; // Pre-computed full name (e.g., "users.profile")
  readonly staticPath: string | null; // Pre-computed static path (if no params)
  readonly paramTypeMap: Readonly<Record<string, "url" | "query">>; // Param type map
}

3. Possible Errors

The getTree() method does not throw errors. It always returns the current route tree.

Note: getTree() does NOT check disposed state. It can be called even after router.dispose().

4. Related Methods

Method Description
getRoutesApi().get() Get a single route definition by name
getRoutesApi().has() Check if a route exists
buildState Validate route and get segment metadata
matchPath Match a URL path against the tree
getRouteUtils Create cached RouteUtils from tree

5. Behavior

Tree Structure

The returned tree is a hierarchical structure where each node represents a route segment:

import { getPluginApi, getRoutesApi } from "@real-router/core/api";

const routes = getRoutesApi(router);
const pluginApi = getPluginApi(router);

routes.add({ name: "users", path: "/users" });
routes.add({ name: "users.profile", path: "/:id" });
routes.add({ name: "users.settings", path: "/settings" });

const tree = pluginApi.getTree();
// tree.name === "" (root)
// tree.children.get("users") → RouteTree for "users"
// tree.children.get("users").children.get("profile") → RouteTree for "users.profile"

Traversing the Tree

Walk the tree to inspect all routes:

import { getPluginApi } from "@real-router/core/api";

const pluginApi = getPluginApi(router);
const tree = pluginApi.getTree();

function printRoutes(node: RouteTree, indent = 0) {
  if (node.fullName) {
    console.log(" ".repeat(indent) + node.fullName + " → " + node.path);
  }

  for (const child of node.children.values()) {
    printRoutes(child, indent + 2);
  }
}

printRoutes(tree);
// Output:
// users → /users
//   profile → /:id
//   settings → /settings

Immutability

The returned tree is deeply frozen — all nodes and their properties are read-only. Do not attempt to mutate the tree:

const tree = pluginApi.getTree();
tree.name = "modified"; // TypeError: Cannot assign to read only property 'name'

Tree Updates

The tree is rebuilt when routes are added, updated, or removed:

import { getPluginApi, getRoutesApi } from "@real-router/core/api";

const pluginApi = getPluginApi(router);
const routes = getRoutesApi(router);

const tree1 = pluginApi.getTree();
routes.add({ name: "admin", path: "/admin" });
const tree2 = pluginApi.getTree();

// tree1 !== tree2 (new tree instance after route change)
// tree2.children.has("admin") === true

Root Node

The returned tree is the root node (empty name, no parent):

const tree = pluginApi.getTree();
tree.name === ""; // Root has empty name
tree.parent === null; // Root has no parent
tree.fullName === ""; // Root has empty fullName

6. Plugin Example

import { getPluginApi, getRoutesApi } from "@real-router/core/api";
import type { PluginFactory, RouteTree } from "@real-router/core";

const routeInspectorPlugin: PluginFactory = (router) => {
  const pluginApi = getPluginApi(router);

  function findRoute(name: string): RouteTree | undefined {
    const tree = pluginApi.getTree();
    const segments = name.split(".");
    let current = tree;

    for (const segment of segments) {
      const child = current.children.get(segment);
      if (!child) return undefined;
      current = child;
    }

    return current;
  }

  function getRouteDepth(name: string): number {
    const route = findRoute(name);
    return route ? name.split(".").length : -1;
  }

  return {
    onStart() {
      const tree = pluginApi.getTree();
      console.log("Routes registered:", tree.children.size);
    },
  };
};

7. Migration

The getTree() method moved from the router instance to the plugin API:

// Before
const tree = router.getTree();

// After
import { getPluginApi } from "@real-router/core/api";

const pluginApi = getPluginApi(router);
const tree = pluginApi.getTree();