Public/Read-ModuleData.ps1

function Read-ModuleData {
  # .SYNOPSIS
  # Reads a specific value from a PowerShell metdata file (e.g. a module manifest)
  # .DESCRIPTION
  # By default Get-ModuleManifest gets the ModuleVersion, but it can read any key in the metadata file
  # .LINK
  # https://github.com/alainQtec/PsCraft/blob/main/Public/Read-ModuleData.ps1
  # .EXAMPLE
  # Read-ModuleData .
  # Reads the Moduledata from the current directory, assumes that the module name is the same as the directory name
  [CmdletBinding()]
  [OutputType([PsObject])]
  param (
    [Parameter(Position = 0, Mandatory = $false, ValueFromPipeline = $true)]
    [ValidateNotNullOrWhiteSpace()][string]
    $Path,

    [Parameter(Position = 1, Mandatory = $false)]
    [Alias('r')][string]
    $RootPath = (Resolve-Path .).Path,

    [Parameter(Position = 2, Mandatory = $false)]
    [AllowNull()][Alias('p')][string]
    $Property = $null,

    [Parameter(Position = 3, Mandatory = $false)]
    [ValidateNotNullOrEmpty()][Alias('m')][string]
    $ModuleName = [IO.Directory]::GetParent([IO.Directory]::GetFiles((Get-Location))[0]).Name
  )
  begin {
    if (!$PSBoundParameters.ContainsKey("Path")) {
      $CultureName = [System.Threading.Thread]::CurrentThread.CurrentCulture.Name
      $Path = [IO.Path]::Combine($RootPath, $CultureName, "$ModuleName.strings.psd1")
    }
  }
  process {
    if (!(Test-Path $Path)) {
      $Error_params = @{
        ExceptionName    = "ItemNotFoundException"
        ExceptionMessage = "Can't find file $Path"
        ErrorId          = "PathNotFound,Metadata\Import-Metadata"
        Caller           = $PSCmdlet
        ErrorCategory    = "ObjectNotFound"
      }
      Write-TerminatingError @Error_params
    }
    if ([string]::IsNullOrWhiteSpace($Property)) {
      $null = Get-Item -Path $Path -ErrorAction Stop
      $data = New-Object PsObject; $text = [IO.File]::ReadAllText("$Path")
      $data = [scriptblock]::Create("$text").Invoke()
      return $data
    }
    $Tokens = $Null; $ParseErrors = $Null
    # Search the Manifest root properties, and also the nested hashtable properties.
    if ([IO.Path]::GetExtension($_) -ne ".psd1") { throw "Path must point to a .psd1 file" }
    $AST = [Parser]::ParseFile($Path, [ref]$Tokens, [ref]$ParseErrors)
    $KeyValue = $Ast.EndBlock.Statements
    $KeyValue = @([PsCraft]::FindHashKeyValue($Property, $KeyValue))
    if ($KeyValue.Count -eq 0) {
      $Error_params = @{
        ExceptionName    = "ItemNotFoundException"
        ExceptionMessage = "Can't find '$Property' in $Path"
        ErrorId          = "PropertyNotFound,Metadata\Get-Metadata"
        Caller           = $PSCmdlet
        ErrorCategory    = "ObjectNotFound"
      }
      Write-TerminatingError @Error_params
    }
    if ($KeyValue.Count -gt 1) {
      $SingleKey = @($KeyValue | Where-Object { $_.HashKeyPath -eq $Property })
      if ($SingleKey.Count -gt 1) {
        $Error_params = @{
          ExceptionName    = "System.Reflection.AmbiguousMatchException"
          ExceptionMessage = "Found more than one '$Property' in $Path. Please specify a dotted path instead. Matching paths include: '{0}'" -f ($KeyValue.HashKeyPath -join "', '")
          ErrorId          = "AmbiguousMatch,Metadata\Get-Metadata"
          Caller           = $PSCmdlet
          ErrorCategory    = "InvalidArgument"
        }
        Write-TerminatingError @Error_params
      } else {
        $KeyValue = $SingleKey
      }
    }
    $KeyValue = $KeyValue[0]
    # $KeyValue.SafeGetValue()
    return $KeyValue
  }
}