resource/scriptBlocks/Get-MFFolderItemDetails.scriptblock.ps1

param($Path,$folderItems)

function Get-MFScriptDetails {
    param(
        [Parameter(Mandatory)]
        [string]$Path,
        [Parameter()]
        [string]$RelativePath,
        [ValidateSet('Class','Function','All')]
        [Parameter()]
        [string]$Type = 'All',
        [Parameter()]
        [string]$FolderGroup
    )
    begin{
        write-verbose 'Checking Item'
        if($Path[-1] -eq '\' -or $Path[-1] -eq '/')
        {
            write-verbose 'Removing extra \ or / from path'
            $Path = $Path.Substring(0,$($Path.length-1))
            write-verbose "New Path $Path"
        }
        $file = get-item $Path
        if(!$file)
        {
            throw "File not found at: $Path"
        }
    }
    process{
        $AST = [System.Management.Automation.Language.Parser]::ParseFile($Path, [ref]$null, [ref]$null)

        # FindAll returns every function at every nesting depth.
        # We only want top-level functions, so we filter out any function whose
        # line range is entirely contained within another function's line range.
        $AllFunctions = $AST.FindAll({ $args[0] -is [System.Management.Automation.Language.FunctionDefinitionAst] }, $true)
        $TopLevelFunctions = New-Object System.Collections.Generic.List[Object]
        foreach($func in $allFunctions){
            $isNested = $false
            foreach($parentFunc in $allFunctions)
            {
                if($func -ne $parentFunc -and $func.Extent.StartLineNumber -ge $parentFunc.Extent.StartLineNumber -and $func.Extent.EndLineNumber -le $parentFunc.Extent.EndLineNumber)
                {
                    $isNested = $true
                    break
                }
            }
            if(-not $isNested) {
                $TopLevelFunctions.add($func)
            }
        }

        $Classes = $AST.FindAll({ $args[0] -is [System.Management.Automation.Language.TypeDefinitionAst] }, $true)
        if($Type -eq 'All' -or $Type -eq 'Function')
        {
            $functionDetails = foreach ($Function in $TopLevelFunctions) {
                $cmdletDependenciesList      = New-Object System.Collections.Generic.List[string]
                $TypeDependenciesList        = New-Object System.Collections.Generic.List[string]
                $paramTypeDependenciesList   = New-Object System.Collections.Generic.List[string]
                $validatorTypeDependenciesList = New-Object System.Collections.Generic.List[string]
                $FunctionName = $Function.Name
                $Cmdlets = $Function.FindAll({ $args[0] -is [System.Management.Automation.Language.CommandAst] }, $true)
                foreach($c in $Cmdlets)
                {
                    $cmdletDependenciesList.add($c.GetCommandName())
                }
                $TypeExpressions = $Function.FindAll({ $args[0] -is [System.Management.Automation.Language.TypeExpressionAst] }, $true)
                $TypeExpressions.TypeName.FullName.foreach{
                    $tname = $_
                    [string]$tnameReplace = $($tname.Replace('[','')).replace(']','')
                    $TypeDependenciesList.add($tnameReplace)
                }
                $Parameters = $Function.Body.ParamBlock.Parameters
                $Parameters.StaticType.Name.foreach{$paramTypeDependenciesList.add($_)}
                $attributes = $Parameters.Attributes
                foreach($att in $attributes)
                {
                    $refType = $att.TypeName.GetReflectionType()
                    if($refType -and ($refType.IsSubclassOf([System.Management.Automation.ValidateArgumentsAttribute]) -or [System.Management.Automation.ValidateArgumentsAttribute].IsAssignableFrom($refType))) {
                        [string]$tname = $Att.TypeName.FullName
                        [string]$tname = $($tname.Replace('[','')).replace(']','')
                        $validatorTypeDependenciesList.Add($tname)
                    }
                }

                [psCustomObject]@{
                    functionName   = $FunctionName
                    cmdLets        = $cmdletDependenciesList|group-object|Select-Object Name,Count
                    types          = $TypeDependenciesList|group-object|Select-Object Name,Count
                    parameterTypes = $paramTypeDependenciesList|group-object|Select-Object name,count
                    Validators     = $validatorTypeDependenciesList|Group-Object|Select-Object name,count
                }
            }
        }
        if($Type -eq 'all' -or $Type -eq 'Class')
        {
            $classDetails = foreach ($Class in $Classes) {
                $className          = $Class.Name
                $classMethodsList   = New-Object System.Collections.Generic.List[string]
                $classPropertiesList = New-Object System.Collections.Generic.List[string]
                $Methods = $Class.Members | Where-Object { $_ -is [System.Management.Automation.Language.FunctionMemberAst] }
                foreach($m in $Methods)
                {
                    $classMethodsList.add($m.Name)
                }
                $Properties = $Class.Members | Where-Object { $_ -is [System.Management.Automation.Language.PropertyMemberAst] }
                foreach($p in $Properties)
                {
                    $classPropertiesList.add($p.Name)
                }
                [psCustomObject]@{
                    className  = $className
                    methods    = $classMethodsList|group-object|Select-Object Name,Count
                    properties = $classPropertiesList|group-object|Select-Object Name,Count
                }
            }
        }
        $objectHash = @{
            Name            = $file.Name
            Path            = $file.FullName
            FileSize        = "$([math]::round($file.length / 1kb,2)) kb"
            FunctionDetails = $functionDetails
            ClassDetails    = $classDetails
            Content         = $AST.ToString()
        }
        if($RelativePath)
        {
            $objectHash.relativePath = $RelativePath
        }
        if($FolderGroup)
        {
            $objectHash.group = $FolderGroup
        }
        [psCustomObject]$objectHash
    }
}

$privateMatch  = "*$([IO.Path]::DirectorySeparatorChar)private$([IO.Path]::DirectorySeparatorChar)*"
$functionMatch = "*$([IO.Path]::DirectorySeparatorChar)functions$([IO.Path]::DirectorySeparatorChar)*"

$folderItems.ForEach{
    if($_.path -notlike $privateMatch -and $_.path -notlike $functionMatch)
    {
        # Dot-source non-function files so custom types are reflected correctly
        . $_.Path
    }
}

$thisPath   = (Get-Item $Path)
$relPathBase = ".$([IO.Path]::DirectorySeparatorChar)$($thisPath.name)"

$itemDetails = $folderItems.ForEach{
    $folderPath = join-path -path $_.folder -childpath $_.RelativePath.Substring(1)
    $relPath    = join-path -path $relPathBase -childpath $folderPath
    if($_.path -like $privateMatch -or $_.path -like $functionMatch -or $_.folder -eq $functionMatch -or $_.folder -eq $privateMatch)
    {
        write-verbose "$($_.Path) matched on type: Function"
        Get-mfScriptDetails -Path $_.Path -RelativePath $relPath -type Function -folderGroup $_.folder
    }else{
        write-verbose "$($_.Path) matched on type: Class"
        Get-mfScriptDetails -Path $_.Path -RelativePath $relPath -type Class -folderGroup $_.folder
    }
}

write-verbose 'Return items in Context'
$inContextList           = New-Object System.Collections.Generic.List[string]
$filenameReference       = @{}
$filenameRelativeReference = @{}

$itemDetails.foreach{
    $fullPath = $_.path
    $relPath  = $_.relativePath
    write-verbose "Getting details for $($_.name)"
    $_.FunctionDetails.Foreach{
        $inContextList.add($_.functionName)
        $filenameReference.add($_.functionName,$fullPath)
        $filenameRelativeReference.Add($_.functionName,$relPath)
    }
    $_.ClassDetails.Foreach{
        $inContextList.add($_.className)
        $filenameReference.add($_.className,$fullPath)
        $filenameRelativeReference.Add($_.className,$relPath)
    }
}

$checklist = $filenameReference.GetEnumerator().name

foreach($item in $itemDetails)
{
    write-verbose "Checking dependencies for file: $($item.name)"
    $compareList = New-Object System.Collections.Generic.List[string]
    $item.ClassDetails.methods.name.foreach{$compareList.add($_)}
    $item.FunctionDetails.cmdlets.name.foreach{$compareList.add($_)}
    $item.FunctionDetails.types.Name.foreach{$compareList.add($_)}
    $item.FunctionDetails.validators.name.foreach{$compareList.add($_)}
    $item.FunctionDetails.parameterTypes.name.foreach{$compareList.add($_)}

    $dependenciesList = New-Object System.Collections.Generic.List[object]

    foreach($c in $compareList)
    {
        write-verbose "Checking dependency of $c"
        if($c -in $checklist)
        {
            write-verbose "$c found in checklist"
            if($item.path -ne $filenameReference["$c"])
            {
                $dependenciesList.add([psCustomObject]@{Reference=$c;ReferenceFile=$filenameRelativeReference["$c"]})
            }else{
                write-verbose "$c found in checklist - but in same file, ignoring"
            }
        }
    }

    $item|add-member -MemberType NoteProperty -Name 'Dependencies' -Value $dependenciesList
    $item
}