Private/Core/Invoke-InGlobalScope.ps1
|
# The engine's top-level (global) session state, resolved once at import. Tool-init scripts # (zoxide, oh-my-posh, fnm, xh, Cobra completions) are run against this so the functions and # aliases they emit are created in the *real* global scope — not tagged to this module. A # plain Invoke-Expression from a module function would attribute every function it defines to # the module (even ones declared `function global:`), which is what made __zoxide_* and the # completion helpers surface in `Get-Command -Module ScrewCitySoftware.PwshProfile`. # # The session state lives behind private engine internals, so we reach it via reflection. # $null here means "couldn't resolve" — Invoke-InGlobalScope then degrades to a plain # Invoke-Expression so a future pwsh build that moves these internals can never break startup. $script:GlobalSessionState = $null try { $flags = [System.Reflection.BindingFlags]'NonPublic, Instance' $context = $ExecutionContext.GetType().GetField('_context', $flags).GetValue($ExecutionContext) $topSessionState = $context.GetType().GetProperty('TopLevelSessionState', $flags).GetValue($context) $script:GlobalSessionState = $topSessionState.GetType().GetProperty('PublicSessionState', $flags).GetValue($topSessionState) } catch { $script:GlobalSessionState = $null } function Invoke-InGlobalScope { <# .SYNOPSIS Runs a script string in the engine's top-level (global) scope, unattributed to this module. .DESCRIPTION Tool integrations (zoxide, oh-my-posh, fnm, xh, Cobra completions) hand back a script of function/alias definitions meant to be Invoke-Expressed at the profile's global scope. When a module function Invoke-Expresses that text directly, PowerShell tags every function it defines with the module's name — even `function global:` ones — so they leak into `Get-Command -Module ScrewCitySoftware.PwshProfile`. This helper instead executes the script against the engine's top-level session state ($script:GlobalSessionState, resolved once at import), so the definitions land in the true global scope with no module attribution, exactly as if the user had run the init line themselves at the prompt. If the global session state couldn't be resolved (reflection into engine internals failed), it falls back to a plain Invoke-Expression: the init still runs, its helpers are just tagged to the module again. Failure tolerance — this must never throw out of profile startup. .PARAMETER Expression The script text to execute (typically a tool's init/completion output piped through Out-String). .EXAMPLE Invoke-InGlobalScope (zoxide init powershell --cmd cd | Out-String) .NOTES Use this for any tool-init output that defines functions or aliases the user calls later from the prompt; it replaces the older `function __` -> `function global:__` textual rewrite, since running in global scope makes such helpers global without rewriting the source. #> [Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSAvoidUsingInvokeExpression', '', Justification = 'Intentional degraded fallback: this helper exists to execute trusted tool-init script text in the global scope; Invoke-Expression is the documented path used only when reflecting the engine''s top-level session state fails.')] [CmdletBinding()] param( [Parameter(Mandatory, Position = 0)] [string]$Expression ) if ($script:GlobalSessionState) { $ExecutionContext.InvokeCommand.InvokeScript($script:GlobalSessionState, [scriptblock]::Create($Expression), $null) } else { Invoke-Expression $Expression } } |