Recently I've expanded on this approach, and when my modules started to get larger, I started creating more and more folders within folders to make sure things are a bit more structured. While it may not be for everyone, it suits my development style, and I'm quite happy with it. A few weeks ago Jeff Hicks mentioned that he is going to start converting his scripts from multi-file modules into single file modules when publishing to PowerShellGallery. While at that time I didn't give it much thought there was later on a conversation on twitter about benefits it may have for speed to load module. It seemed a bit odd that there would be any noticeable impact, so I left it alone for the next couple of weeks. Today while working with my PSSharedGoods module, which is my drop everything that looks like a function into it that currently has 123 PowerShell (.ps1) files and 20 folders, I've noticed this module is taking a bit too long to load slowing using some functions down. While it shouldn't be a big deal if a module is loaded and then reused multiple times this time I was using 1 of its functions and it was taking way too long. I've decided to measure it.
The test above isn't anything fancy. Just starting fresh PowerShell session and loading my module. I was a bit surprised with results.
It means it takes 12 to 15 seconds to Import-Module PSSharedGoods and uses a single function. And that's on I7 6700K with 32GB RAM, and SSD drive with 2500MB Write/Read speeds. Not too good. I decided to test a few things. First I've checked if there would be any change if I explicitly define FunctionsToExport in PSD1 and PSM1 file to find out it doesn't change much for speed. Than I decided to go Jeff's way and see how would converting 123 files into 1 would do. I've prepared a small function that would do it for me.
function Merge-Module {
param (
[string] $ModuleName,
[string] $ModulePathSource,
[string] $ModulePathTarget
)
$ScriptFunctions = @( Get-ChildItem -Path $ModulePathSource\*.ps1 -ErrorAction SilentlyContinue -Recurse )
$ModulePSM = @( Get-ChildItem -Path $ModulePathSource\*.psm1 -ErrorAction SilentlyContinue -Recurse )
foreach ($FilePath in $ScriptFunctions) {
$Results = [System.Management.Automation.Language.Parser]::ParseFile($FilePath, [ref]$null, [ref]$null)
$Functions = $Results.EndBlock.Extent.Text
$Functions | Add-Content -Path "$ModulePathTarget\$ModuleName.psm1"
}
foreach ($FilePath in $ModulePSM) {
$Content = Get-Content $FilePath
$Content | Add-Content -Path "$ModulePathTarget\$ModuleName.psm1"
}
Copy-Item -Path "$ModulePathSource\$ModuleName.psd1" "$ModulePathTarget\$ModuleName.psd1"
}
Whichever method you will use doesn't matter as most likely it will need some tweaking depending on your module.