@@ -19,4 +19,91 @@ $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module
1919$aliasList +=
2020 [GetExports (" Alias" )]$MyModule
2121
22- Export-ModuleMember - Function * - Alias $aliasList
22+ Export-ModuleMember - Function * - Alias $aliasList
23+
24+ $global :ExecutionContext.SessionState.InvokeCommand.CommandNotFoundAction = {
25+ param ($sender , $eventArgs )
26+
27+ # Rather than be the only thing that can handle command not found, we start by broadcasting an event.
28+ New-Event - SourceIdentifier " PowerShell.CommandNotFound" - MessageData $notFoundArgs - Sender $global :ExecutionContext - EventArguments $notFoundArgs
29+
30+ # Then, we do a bit of callstack peeking
31+ $callstack = @ (Get-PSCallStack )
32+ $callstackPeek = $callstack [-1 ]
33+ # When peeking in on a dynamic script block, the offsets may lie.
34+ $column = [Math ]::Max($callstackPeek.InvocationInfo.OffsetInLine , 1 )
35+ $line = [Math ]::Max($callstackPeek.InvocationInfo.ScriptLineNumber , 1 )
36+ $callingScriptBlock = $callstackPeek.InvocationInfo.MyCommand.ScriptBlock
37+ # Now find all of the AST elements at this location.
38+ $astFound = @ ($callingScriptBlock.Ast.FindAll ({
39+ param ($ast )
40+ $ast.Extent.StartLineNumber -eq $line -and
41+ $ast.Extent.StartColumnNumber -eq $column
42+ }, $true ))
43+ if (-not $script :LastCommandNotFoundScript ) {
44+ $script :LastCommandNotFoundScript = $callingScriptBlock
45+ } elseif ($script :LastCommandNotFoundScript -eq $callingScriptBlock ) {
46+ return
47+ } else {
48+ $script :LastCommandNotFoundScript = $callingScriptBlock
49+ }
50+
51+ if (-not $callingScriptBlock ) {
52+ return
53+ }
54+
55+
56+ $transpiledScriptBlock =
57+ try {
58+ $callingScriptBlock.Transpile ()
59+ } catch {
60+ Write-Error $_
61+ return
62+ }
63+ if ($transpiledScriptBlock -and
64+ ($transpiledScriptBlock.ToString ().Length -ne $callingScriptBlock.ToString ().Length)) {
65+
66+ $endStatements = $transpiledScriptBlock.Ast.EndBlock.Statements
67+ $FirstExpression =
68+ if ($endStatements -and (
69+ $endStatements [0 ] -is
70+ [Management.Automation.Language.PipelineAst ]
71+ ) -and (
72+ $endStatements [0 ].PipelineElements[0 ] -is
73+ [Management.Automation.Language.CommandExpressionAst ]
74+ )
75+ ) {
76+ $endStatements [0 ].PipelineElements[0 ].Expression
77+ } else { $null }
78+
79+ if ($astFound -and
80+ $astFound [-1 ].Parent -is [Management.Automation.Language.AssignmentStatementAst ] -and
81+ (
82+ $FirstExpression -is [Management.Automation.Language.BinaryExpressionAst ] -or
83+ $FirstExpression -is [Management.Automation.Language.ParenExpressionAst ]
84+ )
85+ ) {
86+ Write-Error "
87+ Will not interactively transpile {$callingScriptBlock } ( because it would overwrite $ ( $astFound [-1 ].Parent.Left.Extent) )"
88+ return
89+ }
90+
91+ if ($astFound -and
92+ $astFound [-1 ].Parent -is [Management.Automation.Language.AssignmentStatementAst ] -and
93+ $endStatements -and
94+ $endStatements [0 ] -is [Management.Automation.Language.AssignmentStatementAst ] -and
95+ $astFound [-1 ].Parent.Left.ToString() -eq $endStatements [0 ].Left.ToString()) {
96+ $eventArgs.CommandScriptBlock = [ScriptBlock ]::Create($endStatements [0 ].Right.ToString())
97+ $eventArgs.StopSearch = $true
98+ } else {
99+ $eventArgs.CommandScriptBlock = $transpiledScriptBlock
100+ $eventArgs.StopSearch = $true
101+ }
102+ }
103+
104+ return
105+ }
106+
107+ $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
108+ $global :ExecutionContext.SessionState.InvokeCommand.CommandNotFoundAction = $null
109+ }
0 commit comments