4

I'm trying to make use of module-qualified names[1] and a DefaultCommandPrefix and not have it break if the module is imported with Import-Module -Prefix SomethingElse. Maybe I'm just doing something really wrong here, or those two features aren't meant to be used together.

Inside the main module file using "ModuleName\Verb-PrefixNoun args..." works as long as "Prefix" matches the DefaultCommandPrefix in the manifest (the module-qualified syntax seems to require the prefix used for the import[2]). But importing the module with a different prefix, all module-qualified references inside the module breaks.

After a bit of searching and trial and error, the least horrible solution I've managed to get working is to use something like the following hackish solution. But, I can't help wonder if there isn't some better way that automatically handles the prefix (just as Import-Module obviously manages to add the prefix, my first naive though was that using just ModuleName\Verb-Noun would automatically append any prefix to the noun, but evidently not[2].

So this is the hack I came up with, that looks up the modules prefix and appends it, then using "." or "&" to expand/invoke the command:

# (imagine this code in the `ModuleName.psm1`, and a manifest with some `DefaultCommandPrefix`)

Function MQ {
    param (
        [Parameter()][string]
        $Verb,
        [Parameter()][string]
        $Noun,
        [string]
        $Module='ModuleName'
    )
    "$Module\$Verb-$((Get-Module $Module).Prefix)$Noun"
}

Function Verb-Noun {
    # This works even when importing with a prefix,
    # but can I be guaranteed that it's not some
    # other module's cmdlet?
    Verb-OtherNoun 1 2 3 '...'

    #ModuleName\Verb-OtherNoun 1 2 3 '...'
    . (MQ 'Verb' 'OtherNoun') 1 2 3 '...'
    # or:
    & (MQ 'Verb' 'OtherNoun') 1 2 3 '...'
}

(MQ could be made more user friendly by also accepting a single string MQ "Verb-Noun" and split/recombine automatically, and so on, etc. and all the usual disclamers)

Note: I know it would be possible to hard-code the name instead of using DefaultCommandPrefix, e.g. as PSReadLine does (and a bunch of other modules). But, to be honest that feels like a workaround.

Just calling Verb-OtherNoun seems fragile to me, as the most recent one is used[3]. So I would imagine that for example just before the call adding an Import-Module statement with a module that exports a Verb-OtherNoun would cause the wrong (not this module's) cmdlet being called. (Perhaps a more real world scenario is a module being loaded after this module has been loaded, but before calling Verb-Noun.)

Is there perhaps some syntax for module-qualifiaction I'm not aware of that would do something akin to Import-Module(e.g. Module\\Verb-Noun or Module\Verb+Noun that would resolve and inject Module's prefix) and now that I think of it, is there some reason why Module\Verb-Noun doesn't handle prefixes, or just that no one wrote the code for it? (I can't see how it would break things more than how using DefaultCommandPrefix would break v2/v3[2])

[1] https://www.sapien.com/blog/2015/10/23/using-module-qualified-cmdlet-names/

[2] https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/23#issuecomment-106843619

[3] https://stackoverflow.com/a/22259706/13648152

scientica
  • 43
  • 3

1 Answers1

0

You can avoid the problem by not using a module qualifier not using a noun prefix when you call your module's own functions.

That is, call them as Verb-Noun, exactly as named in the target function's implementation.

  • This is generally safe, because your own module's functions take precedence over any commands of the same name defined outside your module.

    • The sole exception is if an alias defined in the global scope happens to have the same name as one of your functions - but that shouldn't normally be a concern, because aliases are used for short names that do not follow the verb-noun naming convention.
  • It also makes sense in that it allows you to call your functions module-internally with an invariable name - a name that doesn't situationally change, depending on what the caller decided to choose as a noun prefix via Import-Module -Prefix ....

    • Think of the prefix feature as being a caller-only feature that is unrelated to your module's implementation.

As an aside: As of PowerShell 7.0, declaring a default noun prefix via the DefaultCommandPrefix module-manifest property doesn't properly integrate with the module auto-loading and command-discovery features - see this GitHub issue.

mklement0
  • 382,024
  • 64
  • 607
  • 775