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