powershellpowershell-5.1powershell-module

How do I read the "using module" statements in a powershell script?


Powershell 5 has a nice declarative "using module" statement that can be provided at the top of a file to declare the dependencies of the script. Presumably one should be able to use this programmatically to determine what the dependencies of a given powershell script or module are. But I can't find anything on how to consume that - does only powershell use that internally? Is there no developer-API to read the requirements-list of a .ps1 file?


Solution

  • To complement your own effective solution with some background information:

    Preface:

    In short: Static analysis via using statements isn't guaranteed to find all dependencies.


    The following puts it all together:

    $scriptPath = './test.ps1'
    [System.Management.Automation.Language.Parser]::ParseFile(
      (Convert-Path $scriptPath), 
      [ref] $null, # `out` parameter that receives the array of tokens; not used here
      [ref] $null # `out` parameter that receives an array of errors, if any; not used here.
    ).UsingStatements | 
      Where-Object UsingStatementKind -ne Namespace | # Filter out `using namespace` statements
      ForEach-Object {
        [pscustomobject] @{
          Kind       = $_.UsingStatementKind
          NameOrSpec = if ($_.ModuleSpecification) { 
                         Invoke-Expression $_.ModuleSpecification 
                       } else { 
                         Invoke-Expression ('Write-Output ' + $_.Name)
                       }
          SourceCode = $_.Extent
        }
      }
    

    If you fill test.ps1 with the following content...

    using module PSReadLine
    # Variations with quoting
    using module 'PSReadLine'
    using module "PSReadLine"
    
    # Module with escaped embedded '
    using module Foo`'Bar
    
    # FQMN module spec
    using module @{ ModuleName = 'Foo'; ModuleVersion = '2.0.0' }
    
    # Reference to built-in assembly.
    # Note: Broken in PowerShell (Core) as of v7.3.6 - see https://github.com/PowerShell/PowerShell/issues/11856
    using assembly System.Windows.Forms
    # Variation with quoting
    using assembly 'System.Windows.Forms'
    
    # Reference to assembly relative to the script's location.
    using assembly ./path/to/some/assembly.dll
    # Variation with quoting
    using assembly './path/to/some/assembly.dll'
    
    # ... 
    

    ... then running the code above yields the following:

        Kind NameOrSpec                                  SourceCode
        ---- ----------                                  ----------
      Module PSReadLine                                  using module PSReadLine
      Module PSReadLine                                  using module 'PSReadLine'
      Module PSReadLine                                  using module "PSReadLine"
      Module Foo'Bar                                     using module Foo`'Bar
      Module {[ModuleName, Foo], [ModuleVersion, 2.0.0]} using module @{ ModuleName = 'Foo'; ModuleVersion = '2.0.0' }
    Assembly System.Windows.Forms                        using assembly System.Windows.Forms
    Assembly System.Windows.Forms                        using assembly 'System.Windows.Forms'
    Assembly ./path/to/some/assembly.dll                 using assembly ./path/to/some/assembly.dll
    Assembly ./path/to/some/assembly.dll                 using assembly './path/to/some/assembly.dll'