Private/Resolve-FilePath.ps1
function Resolve-FilePath { <# .SYNOPSIS Resolve FilePath .DESCRIPTION Gets the full Path of any file in a repo .INPUTS [string[]] .OUTPUTS [String[]] .EXAMPLE Resolve-FilePath * -Extensions ('.ps1', '.psm1') Will get paths of powershell files in current location; thus [psimport]::ParseFile("*") will parse any powershell file in current location. .EXAMPLE Resolve-FilePath "Tests\Resources\Test-H*", "Tests\Resources\Test-F*" .EXAMPLE REsolve-FilePath ..\*.Tests.ps1 .NOTES Created to work with the "PsImport" module. (Its not tested for other use cases) TopLevel directory search takes Priority. eg: REsolve-FilePath PsImport.ps1 will return ./PsImport.ps1 instead of ./BuildOutput/PsImport/0.1.0/PsImport.psm1 Unless ./PsImport.ps1 doesn't exist; In that case it will Recursively search for other Names in the repo. .LINK https://github.com/alainQtec/PsImport/blob/main/Private/Resolve-FilePath.ps1 #> [CmdletBinding(DefaultParameterSetName = 'Query')] [OutputType([System.Object[]])] param ( [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true, ParameterSetName = 'Query')] [ValidateNotNullOrEmpty()] [Alias('Path')] [string]$Query, [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true, ParameterSetName = 'Paths')] [ValidateNotNullOrEmpty()] [string[]]$Paths, [Parameter(Mandatory = $false, Position = 1, ValueFromPipeline = $false, ParameterSetName = '__AllParameterSets')] [ValidateNotNullOrEmpty()] [Alias('Extension')] [string[]]$Extensions, [Parameter(Mandatory = $false, Position = 2, ValueFromPipeline = $false, ParameterSetName = '__AllParameterSets')] [string[]]$Exclude, [switch]$throwOnFailure, [switch]$NoAmbiguous ) begin { $pathsToSearch = @(); $resolved = @(); $error_Msg = $null; $throwOnFailure = [string]$ErrorActionPreference -eq 'Stop' $pathsToSearch += if ($PSCmdlet.ParameterSetName.Equals('Query')) { @($Query) } else { $Paths } $GitHubRoot = $(if (Get-Command -Name git -CommandType Application -ErrorAction Ignore) { git rev-parse --show-toplevel }else { $null }) -as [IO.DirectoryInfo] $GetFiles = [scriptblock]::Create({ param ([Parameter(Mandatory)][string]$qr) $f = Get-ChildItem -Path $qr -File -ErrorAction Ignore if ($PSBoundParameters.ContainsKey('Extensions')) { return ($Files | Where-Object { $_.Extension -in $Extensions }) }; return $f } ) [string[]]$Exclude = @() $gitignore = [IO.Path]::Combine($ExecutionContext.SessionState.Path.CurrentLocation, '.gitignore') if ((Test-Path -Path $gitignore -PathType Leaf -ErrorAction Ignore)) { $Exclude += [IO.File]::ReadAllLines($gitignore).Where({ !$_.StartsWith('#') -and ![string]::IsNullOrWhiteSpace($_) }) } } process { forEach ($p in $pathsToSearch) { if ([Regex]::IsMatch($p, '^https?:\/\/[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}(:[0-9]+)?\/?.*$')) { $error_Msg += " '$p' is a Url! Please provide a valid File Path."; continue } # TopLevel directory search: $rslvdPaths, $error_Msg = $validPaths, $null [string[]]$rslvdPaths = (Resolve-Path $p -ErrorAction Ignore).Path [string[]]$validPaths = ($rslvdPaths | Where-Object { (Test-Path -Path "$_" -PathType Any -ErrorAction Ignore) }) if ($validPaths.Count -gt 1 -and $NoAmbiguous) { $error_Msg += "Path '$p' is ambiguous: $($validPaths -join ', ')" } $Files = $GetFiles.Invoke($p); if ($Files.FullName) { $resolved += $Files.FullName; Continue } $q = $p; $p = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($p); if ((Test-Path -Path $GitHubRoot.FullName -PathType Container -ErrorAction Ignore)) { $rslvdPaths = $( # Multi-Level directory search / -Recurse : switch ($true) { $([IO.Path]::IsPathFullyQualified($q)) { Get-Item -Path $q -ErrorAction Ignore break } $(![IO.Path]::IsPathFullyQualified($q) -and $q.Contains([IO.Path]::DirectorySeparatorChar)) { $relPath = '([IO.Path]::GetRelativePath($ExecutionContext.SessionState.Path.CurrentLocation, $_.FullName))' $IsMatch = if ($q.Contains('*')) { [scriptblock]::Create("$relPath -like `"$q`" -or `$_.FullName -like `"$q`"") } elseif ($q.EndsWith([IO.Path]::DirectorySeparatorChar)) { [scriptblock]::Create("$relPath -like `"$q*`" -or `$_.FullName -like `"$q*`"") } else { [scriptblock]::Create("$relPath -eq `"$q`" -or `$_.FullName -eq `"$q`"") } $(Get-ChildItem -Path $GitHubRoot.FullName -File -Recurse -ErrorAction Ignore).Where($IsMatch) break } $(![IO.Path]::IsPathFullyQualified($q) -and !$q.Contains([IO.Path]::DirectorySeparatorChar)) { $IsMatch = if ($q.Contains('*')) { [scriptblock]::Create('$_.Name -like $q -or $_.BaseName -like $q') } else { [scriptblock]::Create('$_.Name -eq $q -or $_.BaseName -eq $q') } $(Get-ChildItem -Path $GitHubRoot.FullName -File -Recurse -ErrorAction Ignore).Where($IsMatch) break } Default { Get-ChildItem -Path $GitHubRoot.FullName -File -Recurse -Filter $q -ErrorAction Ignore } } ) | Select-Object -ExpandProperty FullName }; if (!$rslvdPaths) { $error_Msg += "No files were found in Path '$p'."; Continue } $resolved += $rslvdPaths } $resolved = $resolved | Sort-Object -Unique if ($PSBoundParameters.ContainsKey('Extensions')) { $resolved = $($resolved -as [IO.FileInfo[]] | Where-Object { $_.Extension -in $Extensions }).FullName } if ($resolved.Count -gt 1 -and $NoAmbiguous) { $error_Msg += ' Error: Resolved to Multiple paths' } } end { if ($error_Msg) { if ($throwOnFailure) { $PSCmdlet.ThrowTerminatingError( [System.Management.Automation.ErrorRecord]::New( [System.Management.Automation.ItemNotFoundException]::new($error_Msg), 'ItemNotFoundException', 'OperationStopped', [PSCustomObject]@{ Params = $PSCmdlet.MyInvocation.BoundParameters } ) ) } else { Write-Verbose $error_Msg } } return $resolved } } |