I have two PowerShell class definitions (File and Directory) and each class definition in a separate psm1 file:
File.psm1
class File
{
[String]$Path
File([String]$Path)
{
$this.Path = $Path
}
}
Directory.psm1
using namespace System.Collections.Generic
using module .\File.psm1
class Directory
{
[List[File]]$Files
[String]$Path
Directory([String]$Path)
{
$this.Path = $Path
$this.Files = [List[File]]::new()
if (Test-Path -Path $Path)
{
(Get-ChildItem -Path $Path -File).ForEach{
$this.Files.Add([File]::new($_.FullName))
}
}
}
}
So the directory class needs the definition of the file class.
I would like use the two classes inside a module Testmodule1.psm1.
The psm1 file Testmodule1.psm1 only contains a using module statement:
using module .\Directory.psm1
And the psd1 file Testmodule1.psd1 references Testmodule1.psm1 as the root module:
@{
ModuleVersion = "0.0.1"
RootModule = "TestModule1.psm1"
}
When I import the module like so:
import-module .\TestModule1.psd1 -Force -Verbose
the directory class is not known.
But when I load the module like it has been suggested by Michael Phillips here on SO, it works:
I have to import the module like so:
$m1 = import-module .\TestModule1.psd1 -Force -PassThru
and
& $m1 { [Directory]::new("G:\Skriptkiste") }
Now the Directory type is found.
I have tested this with Windows PowerShell and PowerShell 7.4.5.
My question is: Is there a cleaner way to achieve what I want?
I know this question had been the topic of many discussions on SO. But I can't believe that there is no simple and clean solution for the requirement of having multiple class definitions (with a "dependency") in PowerShell module.
Building on the helpful comments:
The non-support for exporting class
(and enum
) definitions other than those placed directly in the root script module file (*.psm1
) of a PowerShell module is a known limitation (as of PowerShell (Core) 7 v7.4.x, though a fix in the near future seems unlikely).
Quoting from the about_Using help topic; emphasis added:
The
using module
statement imports classes and enumerations from the root module (ModuleToProcess) of a script module or binary module. It doesn't consistently import classes or enumerations defined in nested modules or in scripts that are dot-sourced into the root module. Define classes and enumerations that you want to be available to users outside of the module directly in the root module.
As has been noted:
PowerShell's support for class
definitions is incomplete and there are many feature requests and bug reports; GitHub issue #6652 is a meta issue that tracks them all (there has been little activity around these issues in quite some time as of this writing).
As a workaround - apart from putting all class
and enum
definitions directly in the root module, as shown below - you can employ a build tool, such as psake
, in order to merge your distinct *.psm1
files into a single one that serve as the one referenced in the RootModule
entry of your module manifest.
To spell it out:
Given your module manifest (TestModule.psd1
) whose RootModule
entry references TestModule.psm1
, the latter's content must be as follows in order to make both [File]
and [Directory]
available to importers (who must use using module
(rather than Import-Module
/ module autoloading) to import the module, as that is the fundamental prerequisite for seeing a module's class
and enum
definitions):
# Content of TestModule.psm1,
# which must *directly* contain the definitions of *both* [File] and [Directory].
using namespace System.Collections.Generic
class File
{
[String]$Path
File([String]$Path)
{
$this.Path = $Path
}
}
class Directory
{
[List[File]]$Files
[String]$Path
Directory([String]$Path)
{
$this.Path = $Path
$this.Files = [List[File]]::new()
if (Test-Path -Path $Path)
{
(Get-ChildItem -Path $Path -File).ForEach{
$this.Files.Add([File]::new($_.FullName))
}
}
}
}