Private/Graph.ps1

function New-PSClassGraphModel
{
    $graph = (New-Object -TypeName PSObject -Property @{
        Classes = @{}
    })

    $graph | Add-Member -MemberType ScriptMethod -Name 'DetectCyclicDependency' -Value {
        param (
            [Parameter()]
            [string]
            $Namespace
        )

        $this.Classes.Values | Where-Object {
            [string]::IsNullOrWhiteSpace($Namespace) -or $_.Namespace -ieq $Namespace
        } | ForEach-Object {
            $class = $_

            # get all dependencies for the current class
            $dep = ($class.AllDependencies() | Where-Object { $_.Class.Namespace -ieq $class.Namespace })

            # if the list contains this class, error
            if ($null -ne $dep) {
                $message = Get-PSClassCyclicErrorMessage -Links ($dep.Links + $class.Namespace)
                throw "Cyclic dependency found on class '$($class.Path)':`n`n- - - - - - -`n$($message)`n- - - - - - -"
            }
        }
    }

    $graph | Add-Member -MemberType ScriptMethod -Name 'GetClassOrder' -Value {
        $order = @()

        do {
            # get first class where all dependencies are added
            $class = ($this.Classes.Values | Where-Object {
                !$_.Added -and (($_.DependentOn.Length -eq 0) -or @($_.DependentOn | Where-Object { !$_.Added }).Length -eq 0)
            } | Select-Object -First 1)

            # add the class
            Write-Verbose "Adding class: $($class.Namespace)"
            $order += $class.Path

            # flag as added
            $class.Added = $true

        } while (@($this.Classes.Values | Where-Object { !$_.Added }).Length -gt 0)

        return $order
    }

    $graph | Add-Member -MemberType ScriptMethod -Name 'ToString' -Force -Value {
        param (
            [Parameter()]
            [string]
            $Namespace
        )

        $this.Classes.Values | Where-Object {
            [string]::IsNullOrWhiteSpace($Namespace) -or $_.Namespace -ieq $Namespace
        } | ForEach-Object {
            Write-PSClassDependencyTree -Class $_ -Level 0
            Write-Host ([string]::Empty)
        }
    }

    return $graph
}

function Write-PSClassDependencyTree
{
    param (
        [Parameter(Mandatory=$true)]
        $Class,

        [Parameter(Mandatory=$true)]
        [int]
        $Level
    )

    $prefix = "$(' ' * $Level)| ->"
    Write-Host "$($prefix) $($Class.Namespace)"

    if ($Class.DependentOn.Length -eq 0) {
        return
    }

    $Class.DependentOn | ForEach-Object {
        Write-PSClassDependencyTree -Class $_ -Level ($Level + 1)
    }
}

function New-PSClassGraph
{
    param (
        [Parameter(Mandatory=$true)]
        [string]
        $RootPath
    )

    # append class and test path
    $RootPath = (Join-Path (Resolve-Path $RootPath) 'Classes')
    if (!(Test-Path $RootPath)) {
        throw "The Classes directory path does not exist: $($RootPath)"
    }

    # intialise the graph
    $graph = New-PSClassGraphModel

    # get all classes, and build graph
    (Get-PSClassModels -RootPath $RootPath) | ForEach-Object {
        $graph.Classes[$_.Name] = $_
    }

    # regex for using
    $regex = '\[(?<name>\w+)\]'

    # now, get all class dependencies
    foreach ($class in $graph.Classes.Values) {
        # get file content
        $content = $class.Content()

        # get all classes being used
        $classes = @($content -imatch $regex)

        # if there are no dependencies, move along
        if ($classes.Length -eq 0) {
            continue
        }

        # loop through each class, adding dependencies and used by
        foreach ($dep in $classes) {
            $dep -imatch $regex | Out-Null
            $className = $Matches['name']

            # skip if class isn't custom
            if (!$graph.Classes.ContainsKey($className)) {
                continue
            }

            # skip if it is a self reference
            if ($className -ieq $class.Name) {
                continue
            }

            # add dependency
            $graph.Classes[$class.Name].Link($graph.Classes[$className])
        }
    }

    # return the graph
    return $graph
}