Module Nesting

Hi,
I would like to have multiple modules/scripts nested and imported with one module, the problem is that I want to keep the actual files across multiple directories:

BuildTests[dir]
|BuildTests.psd1
|GenericTests[dir]
||GenericTests.psd1
||GenericTest1.ps1
||GenericTest2.ps1
|SpecificTests[dir]
||SpecificTests.psd1
||SpecificTest1.ps1
||SpecificTest2.ps1

I want to keep 1 function per .ps1 file.
I have tried the following:
in BuildTests.psd1 define NestedModules = @(‘.\GenericTests\GenericTests.psd1’,‘.\SpecificTests\SpecificTests.psd1’)
in GenericTests.psd1 define ScriptsToProcess = @(‘GenericTest1.ps1’,‘GenericTest2.ps1’)
accordingly configure SpecificTests.psd1

Basically this works, however when checking get-module, I see all the functions from .ps1 files imported twice, checking further import-module with -verbose parameter, I can see that one time it is “loading module …test1.ps1” and next line is “Dot-Sourcing the script file …test1.ps1”.
I have tried different variations in .psd1 (changing from .ps1 to .psm1 and including them in nestedmodules), but I always seem to end up with duplicates imported or no functions imported at all.

None of your files are explicitly loading or dot-sourcing these outside of what you’ve mentioned here? I ask because, just sitting on this end, the logic you’re describing is a little tough for me to follow. It might be easier (for me) if you started with what you want to happen rather than what you’ve done…?

None of my files are loading nor dot sourcing anything.

I would like multiple functions, each defined in its own .ps1 file across multiple folders, to be available in a single module.
I would also prefer to maintain one manifest per folder and have all of those manifests included in a “root” manifest, if that is possible.

or in other words:
RootModule - includes nested modules from different folders.
Each nested module includes .ps1 files in their respective folder.

So… I suppose I’ll start with how I would do this.

I would build all fo your functions as modules. .psm1 files, with .psd1 manifests, stored in folders as discrete, standalone modules. I wouldn’t try using .ps1’s - I’m not sure I see any advantage to doing so. Then, in my “parent” modules, I could simply specify all the ones I wanted at that one level.

The nesting functionality wasn’t really meant to support “sub-sub” multiple levels of nesting, and by combining modules and scripts, I feel you’re making it unnecessarily complicated.

Now, if the only way you want to live life is by having the “child” modules use a .ps1 for some reason, then I’d just have them dot-source their respective .ps1 files in the module code itself, rather than in a manifest. Although I still don’t see the advantage of building a module that way, I suppose it’s a free world ;).

It looks like it’s working just fine, what got me confused was the verbose output listing exporting/importing same function multiple times:

PS D:\DevRepository\ParentModule> Import-Module .\ParentModule.psd1 -Verbose
VERBOSE: Loading module from path 'D:\DevRepository\ParentModule\ParentModule.psd1'.
VERBOSE: Loading module from path 'D:\DevRepository\ParentModule\Modules1\Modules1.psd1'.
VERBOSE: Loading module from path 'D:\DevRepository\ParentModule\Modules1\TestCase1.ps1'.
VERBOSE: Dot-sourcing the script file 'D:\DevRepository\ParentModule\Modules1\TestCase1.ps1'.
VERBOSE: Exporting function 'test-case1'.
VERBOSE: Importing function 'test-case1'.
VERBOSE: Exporting function 'test-case1'.
VERBOSE: Importing function 'test-case1'.

And then checking the module ExportedFunctions property:

PS D:\DevRepository\ParentModule> Get-Module ParentModule | select ExportedFunctions

ExportedFunctions
-----------------
{[test-case1, test-case1]}

Which actually lists key/value pairs:

PS D:\DevRepository\ParentModule> (Get-Module ParentModule).ExportedFunctions

Key        Value
---        -----
test-case1 test-case1

Regarding .ps1 vs .psm1 it just felt unnatural to have a .psm1 with just a single function, but otherwise I really have no preference, so might as well rename those to psm1.

Thanks.