Skip to content

ModuleTraversal

ModuleTraversal is a read-only view over the module tree, registered as a provider in every module. The semantics are on Modules; this page is the API surface.

Signature

ts
class ModuleTraversal {
    parent(): Module | null
    ancestors(): Module[]
    children(): Module[]
    descendants(): Module[]
    findRoot(): Module

    findAncestorById(id: string): Module | null
    findDescendantById(id: string): Module | null
    findAncestorByProvider(token: InjectionToken): Module | null
    findDescendantsByProvider(token: InjectionToken): Module[]
}

Methods

MethodReturns
parent()The module directly above. null on an App.
ancestors()The chain above, nearest first, up to the root.
children()Direct children only, in attach order.
descendants()The whole subtree below, depth-first.
findRoot()The outermost module in this tree - the App.
findAncestorById(id)The nearest module above with this id, or null.
findDescendantById(id)The first module below with this id, depth-first, or null.
findAncestorByProvider(token)The nearest module above that declares token, or null.
findDescendantsByProvider(token)Every module below that declares token.

It answers relative to the module it came from

Every result is relative to the module the instance belongs to, and excludes that module. A service that injects ModuleTraversal gets its own module's view, so ancestors() starts at the parent and descendants() starts at the children.

findRoot() is the one exception: on a module that is already the root, it returns that module.

Two ways in

tsx
import { ModuleTraversal, type Module } from "@remodulo/react"

class OrdersDevtools {
    private readonly traversal = inject(ModuleTraversal)

    // The modules open below the one that declares this service
    openModules(): Module[] {
        return this.traversal.descendants()
    }
}

// The same object from outside React, off a module you already hold
const owner: Module | null = app.traversal.findDescendantsByProvider(OrdersStore).at(0) ?? null

inject(ModuleTraversal) gives OrdersDevtools the view of the module that declares it. app.traversal is the same object reached from outside React, off a Module you already hold.

ByProvider means own declarations

findAncestorByProvider and findDescendantsByProvider match modules that register the token themselves. A module that only resolves the token from an ancestor does not match - which is what makes these the way to find the module that owns an instance.

The check runs against the module's live registrations, so a provider registered after the module was built is still found.

Nothing is cached

Every answer is derived from parent and children on the modules at call time. There is no snapshot to go stale, and no subscription: call again to see the tree again.

A child joins its parent's tree on mount. A module that React built during a discarded render attempt never appears in children() or descendants().

Where to go next

Guides

Reference

MIT licensed.