From bae1ed4fbc02beee68f4afe07473c464cef89dda Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 14:41:12 -0700 Subject: [PATCH 01/21] Updating Explicit Transpiler: Returning modified [ScriptBlock] (#102) --- Transpilers/Explicit.psx.ps1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Transpilers/Explicit.psx.ps1 b/Transpilers/Explicit.psx.ps1 index c1075bd39..39c350d9c 100644 --- a/Transpilers/Explicit.psx.ps1 +++ b/Transpilers/Explicit.psx.ps1 @@ -68,6 +68,7 @@ process { $astReplacements[$pipeline] = [ScriptBlock]::Create("`$null = $pipeline") } } - $astReplacements + + Update-PipeScript -ScriptBlock $ScriptBlock -AstReplacement $astReplacements # @{AstReplacement=$astReplacements} } From fedc977bf7bb91ccc7eb7aae96197bccc529c491 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sat, 2 Jul 2022 21:42:02 +0000 Subject: [PATCH 02/21] Updating Explicit Transpiler: Returning modified [ScriptBlock] (#102) --- PipeScript.psm1 | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index d01e75c3d..ad5c88985 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -1,7 +1,8 @@ foreach ($file in (Get-ChildItem -Path "$psScriptRoot" -Filter "*-*" -Recurse)) { - if ($file.Extension -ne '.ps1') { continue } + if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 + if ($file.Name -match '\.ps1\.ps1$') { continue } # Skip if the file is a source generator. . $file.FullName -} +} $aliasNames = @() foreach ($transpilerCmd in Get-Transpiler) { @@ -11,7 +12,9 @@ foreach ($transpilerCmd in Get-Transpiler) { Set-Alias ".<$($transpilerCmd.DisplayName)>" Use-PipeScript } +$MyModule = $MyInvocation.MyCommand.ScriptBlock.Module foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*','Function', $true)) { + if ($cmd.ScriptBlock.Module -ne $MyModule) { continue } if ($cmd.ScriptBlock.Attributes.AliasNames) { $aliasNames += $cmd.ScriptBlock.Attributes.AliasNames } From 288a04b5169f2302e1ac7437f8223102b99fe4cb Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 17:11:51 -0700 Subject: [PATCH 03/21] Adding ModuleExports.psx.ps1 (#104) --- Transpilers/Modules/ModuleExports.psx.ps1 | 65 +++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 Transpilers/Modules/ModuleExports.psx.ps1 diff --git a/Transpilers/Modules/ModuleExports.psx.ps1 b/Transpilers/Modules/ModuleExports.psx.ps1 new file mode 100644 index 000000000..8a44d0433 --- /dev/null +++ b/Transpilers/Modules/ModuleExports.psx.ps1 @@ -0,0 +1,65 @@ +<# +.Synopsis + Gets Module Exports +.Description + Gets Exported Commands from a module. +.EXAMPLE + .> { + $PipeScriptModule = Get-Module PipeScript + $exports = [ModuleExports()]$PipeScriptModule + $exports + } +#> +[CmdletBinding(DefaultParameterSetName='None',PositionalBinding=$false)] +[ValidateScript({ + $val = $_ + if ( + ($val.Parent -is [Management.Automation.Language.AttributedExpressionAst]) -and + ($val.Parent.Attribute.TypeName.Name -in 'ModuleExport', 'GetExport', 'GetExports', 'ListExports', 'ModuleExport', 'GetModuleExport', 'GetModuleExports') + ) { + return $true + } + return $false +})] +[Alias('GetExport','GetExports', 'ListExport','ListExports','ModuleExport','GetModuleExport', 'GetModuleExports')] +param( +# The command type +[Parameter(Position=0)] +[Management.Automation.CommandTypes[]] +$CommandType = @('Alias','Function','Cmdlet'), + +# A VariableExpression. This variable must contain a module. +[Parameter(Mandatory,ValueFromPipeline,ParameterSetName='VariableExpressionAST')] +[Management.Automation.Language.VariableExpressionAST] +$VariableAST +) + +process { + +$var = $VariableAST.Extent.ToString() +[scriptblock]::Create($( +{ +@( +if ($var -isnot [Management.Automation.PSModuleInfo]) { + Write-Error "'$var' must be a [Management.Automation.PSModuleInfo]" +} elseif ($var.ExportedCommands.Count) { + foreach ($cmd in $var.ExportedCommands.Values) { + if ($cmd.CommandType -in $CommandType) { + $cmd + } + } +} else { + foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*', 'Function,Cmdlet', $true)) { + if ($cmd.Module -ne $var) { continue } + if ($CommandType -contains 'Alias' -and $cmd.ScriptBlock.Attributes.AliasNames) { + foreach ($aliasName in $cmd.ScriptBlock.Attributes.AliasNames) { + $ExecutionContext.SessionState.InvokeCommand.GetCommand($aliasName, 'Alias') + } + } + if ($CommandType -contains $cmd.CommandType) { + $cmd + } + } +}) +} -replace '\$var', "$var" -replace '\$CommandType', "'$($CommandType -join "','")'")) +} \ No newline at end of file From ef53765b8190d57a2180c8b5af17d58ef73362bf Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 17:13:17 -0700 Subject: [PATCH 04/21] Updating PipeScript.ps1.psm1 to use ModuleExports (#104) --- PipeScript.ps1.psm1 | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/PipeScript.ps1.psm1 b/PipeScript.ps1.psm1 index 838b7fc22..e2eb11e27 100644 --- a/PipeScript.ps1.psm1 +++ b/PipeScript.ps1.psm1 @@ -9,11 +9,6 @@ foreach ($transpilerCmd in Get-Transpiler) { } $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*','Function', $true)) { - if ($cmd.ScriptBlock.Module -ne $MyModule) { continue } - if ($cmd.ScriptBlock.Attributes.AliasNames) { - $aliasNames += $cmd.ScriptBlock.Attributes.AliasNames - } -} +$aliasNames += [GetExports("Alias")]$MyModule Export-ModuleMember -Function * -Alias $aliasNames \ No newline at end of file From 182c73ff6b688e5b3a012686c357dcb7430e25d4 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 3 Jul 2022 00:14:31 +0000 Subject: [PATCH 05/21] Merge branch 'PipeScriptFunAndFixes' of https://github.com/StartAutomating/PipeScript into PipeScriptFunAndFixes --- PipeScript.psm1 | 30 ++++++++++++++++++++++++------ 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index ad5c88985..b6125120a 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -13,11 +13,29 @@ foreach ($transpilerCmd in Get-Transpiler) { } $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*','Function', $true)) { - if ($cmd.ScriptBlock.Module -ne $MyModule) { continue } - if ($cmd.ScriptBlock.Attributes.AliasNames) { - $aliasNames += $cmd.ScriptBlock.Attributes.AliasNames - } -} +$aliasNames += + @( + if ($MyModule -isnot [Management.Automation.PSModuleInfo]) { + Write-Error "'$MyModule' must be a [Management.Automation.PSModuleInfo]" + } elseif ($MyModule.ExportedCommands.Count) { + foreach ($cmd in $MyModule.ExportedCommands.Values) { + if ($cmd.CommandType -in 'Alias') { + $cmd + } + } + } else { + foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*', 'Function,Cmdlet', $true)) { + if ($cmd.Module -ne $MyModule) { continue } + if ('Alias' -contains 'Alias' -and $cmd.ScriptBlock.Attributes.AliasNames) { + foreach ($aliasName in $cmd.ScriptBlock.Attributes.AliasNames) { + $ExecutionContext.SessionState.InvokeCommand.GetCommand($aliasName, 'Alias') + } + } + if ('Alias' -contains $cmd.CommandType) { + $cmd + } + } + }) + Export-ModuleMember -Function * -Alias $aliasNames From 791d0511664cb204763b93efb877e264cd1dda12 Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 17:15:21 -0700 Subject: [PATCH 06/21] Updating PipeScript.ps1.psm1 (alignment of ModuleExports) (#104) --- PipeScript.ps1.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PipeScript.ps1.psm1 b/PipeScript.ps1.psm1 index e2eb11e27..120504947 100644 --- a/PipeScript.ps1.psm1 +++ b/PipeScript.ps1.psm1 @@ -9,6 +9,7 @@ foreach ($transpilerCmd in Get-Transpiler) { } $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -$aliasNames += [GetExports("Alias")]$MyModule +$aliasNames += + [GetExports("Alias")]$MyModule Export-ModuleMember -Function * -Alias $aliasNames \ No newline at end of file From eb3ae516708875818262bfe2aee2f6257d2ce09b Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 3 Jul 2022 00:15:47 +0000 Subject: [PATCH 07/21] Updating PipeScript.ps1.psm1 (alignment of ModuleExports) (#104) --- PipeScript.psm1 | 49 +++++++++++++++++++++++++------------------------ 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index b6125120a..3ea09a9f8 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -13,29 +13,30 @@ foreach ($transpilerCmd in Get-Transpiler) { } $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -$aliasNames += - @( - if ($MyModule -isnot [Management.Automation.PSModuleInfo]) { - Write-Error "'$MyModule' must be a [Management.Automation.PSModuleInfo]" - } elseif ($MyModule.ExportedCommands.Count) { - foreach ($cmd in $MyModule.ExportedCommands.Values) { - if ($cmd.CommandType -in 'Alias') { - $cmd - } - } - } else { - foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*', 'Function,Cmdlet', $true)) { - if ($cmd.Module -ne $MyModule) { continue } - if ('Alias' -contains 'Alias' -and $cmd.ScriptBlock.Attributes.AliasNames) { - foreach ($aliasName in $cmd.ScriptBlock.Attributes.AliasNames) { - $ExecutionContext.SessionState.InvokeCommand.GetCommand($aliasName, 'Alias') - } - } - if ('Alias' -contains $cmd.CommandType) { - $cmd - } - } - }) - +$aliasNames += + + @( + if ($MyModule -isnot [Management.Automation.PSModuleInfo]) { + Write-Error "'$MyModule' must be a [Management.Automation.PSModuleInfo]" + } elseif ($MyModule.ExportedCommands.Count) { + foreach ($cmd in $MyModule.ExportedCommands.Values) { + if ($cmd.CommandType -in 'Alias') { + $cmd + } + } + } else { + foreach ($cmd in $ExecutionContext.SessionState.InvokeCommand.GetCommands('*', 'Function,Cmdlet', $true)) { + if ($cmd.Module -ne $MyModule) { continue } + if ('Alias' -contains 'Alias' -and $cmd.ScriptBlock.Attributes.AliasNames) { + foreach ($aliasName in $cmd.ScriptBlock.Attributes.AliasNames) { + $ExecutionContext.SessionState.InvokeCommand.GetCommand($aliasName, 'Alias') + } + } + if ('Alias' -contains $cmd.CommandType) { + $cmd + } + } + }) + Export-ModuleMember -Function * -Alias $aliasNames From 9c0f9573c2def6da95b9165242ecac6457a153d8 Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 19:01:41 -0700 Subject: [PATCH 08/21] Updating [include] transpiler (avoiding unrelated files) (#96) --- Transpilers/Include.psx.ps1 | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Transpilers/Include.psx.ps1 b/Transpilers/Include.psx.ps1 index bc3fe9448..dba1eac9d 100644 --- a/Transpilers/Include.psx.ps1 +++ b/Transpilers/Include.psx.ps1 @@ -14,7 +14,7 @@ } | .>PipeScript .EXAMPLE { - [Include('*-*.ps1')]$psScriptRoot + [Include('*-*.ps1')]$psScriptRoot } | .>PipeScript #> param( @@ -62,8 +62,7 @@ function IncludeFileContents { "'@" + @' -split "[\r\n]{1,2}" -replace "^@''", "@'" -replace "^''@", "'@" -join [Environment]::NewLine '@ - ) - + ) } } } @@ -131,7 +130,7 @@ if ($psCmdlet.ParameterSetName -eq 'ScriptBlock' -or [ScriptBlock]::Create(@" foreach (`$file in (Get-ChildItem -Path "$($VariableAst)" -Filter "$FilePath" -Recurse)) { if (`$file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 - if (`$file.Name -match '\.ps1\.ps1$') { continue } # Skip if the file is a source generator. + if (`$file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file. . `$file.FullName } "@) From e9e62ec1159a0511512a9b9588c8a8b356d3f9f4 Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 19:15:46 -0700 Subject: [PATCH 09/21] Adding Aliases Transpiler (#106) --- Transpilers/Parameters/Aliases.psx.ps1 | 79 ++++++++++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 Transpilers/Parameters/Aliases.psx.ps1 diff --git a/Transpilers/Parameters/Aliases.psx.ps1 b/Transpilers/Parameters/Aliases.psx.ps1 new file mode 100644 index 000000000..653c3deb3 --- /dev/null +++ b/Transpilers/Parameters/Aliases.psx.ps1 @@ -0,0 +1,79 @@ +<# +.SYNOPSIS + Dynamically Defines Aliases +.DESCRIPTION + Can Dynamically Define Aliases. + + When uses in a parameter attribute, -Aliases will define a list of aliases. + + When used with a variable, [Aliases] will Set-Alias on each value in the variable. +.EXAMPLE + { + $aliases = "Foo", "Bar", "Baz" + [Aliases(Command="Get-Process")]$aliases + } | .>PipeScript +.Example + { + param( + [Aliases(Aliases={ + ([char]'a'..[char]'z') + })] + [string] + $Drive + ) + } | .>PipeScript +#> +[CmdletBinding(DefaultParameterSetName='AliasNames')] +[Alias('SmartAlias','DynamicAlais')] +param( +# A list of aliases +[Parameter(Mandatory,ParameterSetName='AliasNames')] +[Alias('Alias')] +[string[]] +$Aliases, + +# If provided, will prefix each alias +[string] +$Prefix, + +# If provided, will add a suffix to each alias +[string] +$Suffix, + +# The command being aliased. This is only required when transpiling a variable. +[Parameter(Mandatory,ParameterSetName='VariableExpressionAST')] +[string] +$Command, + +[Parameter(ParameterSetName='VariableExpressionAST')] +[switch] +$PassThru, + +# A VariableExpression. +# If provided, this will be treated as the alias name or list of alias names. +[Parameter(Mandatory,ValueFromPipeline,ParameterSetName='VariableExpressionAST')] +[Management.Automation.Language.VariableExpressionAST] +$VariableAST +) + +process { + if ($PSCmdlet.ParameterSetName -eq 'VariableExpressionAST') { +[ScriptBlock]::Create($({ +@(foreach ($alias in @($aliasNames)) { + Set-Alias "${Prefix}$alias${Suffix}" "$Command" -PassThru:$PassThru +}) +} -replace '\${Prefix}', $Prefix -replace + '\${Suffix}', $Suffix -replace + '\$Command', $Command -replace + '\$AliasNames', "$variableAst" -replace + '\$PassThru', ('$' + ($PassThru -as [bool])) +)) + } else { + $aliasValues = + @(foreach ($alias in $aliasNames) { + "$(if ($Prefix) { $Prefix })$alias$(if ($Suffix) { $Suffix })" + }) +[scriptblock]::Create("[Alias('$($Aliases -join "','")')]param()") + } +} + From 865b5393562009300d0f7df8bd3826bca8d88fff Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 19:23:41 -0700 Subject: [PATCH 10/21] Adding ModuleRelationship Transpiler (#105) --- .../Modules/ModuleRelationship.psx.ps1 | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 Transpilers/Modules/ModuleRelationship.psx.ps1 diff --git a/Transpilers/Modules/ModuleRelationship.psx.ps1 b/Transpilers/Modules/ModuleRelationship.psx.ps1 new file mode 100644 index 000000000..0f1a0c875 --- /dev/null +++ b/Transpilers/Modules/ModuleRelationship.psx.ps1 @@ -0,0 +1,84 @@ + +<# +.SYNOPSIS + Gets Module Relationships +.DESCRIPTION + Gets Modules that are related to a given module. + + Modules can be related to each other by a few mechanisms: + + * A Module Can Include another Module's Name in it's ```.PrivateData.PSData.Tags``` + * A Module Can include data for another module it it's ```.PrivataData.``` +.EXAMPLE + .> { + $Module = Get-Module PipeScript + [ModuleRelationships()]$Module + } +#> +[ValidateScript({ + $val = $_ + if ( + ($val.Parent -is [Management.Automation.Language.AttributedExpressionAst]) -and + ($val.Parent.Attribute.TypeName.Name -in 'RelatedModules', 'RelatedModule', 'ModuleRelationships') + ) { + return $true + } + return $false +})] +[Alias('RelatedModules', 'RelatedModule','ModuleRelationships')] +param( +# A VariableExpression. This variable must contain a module or name of module. +[Parameter(Mandatory,ValueFromPipeline,ParameterSetName='VariableExpressionAST')] +[Management.Automation.Language.VariableExpressionAST] +$VariableAST +) + + +process { + +[scriptblock]::Create($({ + +@( + +$MyModuleName, $myModule = + if ($targetModule -is [string]) { + $targetModule, (Get-Module $targetModule) + } elseif ($targetModule -is [Management.Automation.PSModuleInfo]) { + $targetModule.Name, $targetModule + } else { + Write-Error "$targetModule must be a [string] or [Management.Automation.PSModuleInfo]" + } + + +#region Search for Module Relationships +if ($myModule -and $MyModuleName) { + foreach ($loadedModule in Get-Module) { # Walk over all modules. + if ( # If the module has PrivateData keyed to this module + $loadedModule.PrivateData.$myModuleName + ) { + # Determine the root of the module with private data. + $relationshipData = $loadedModule.PrivateData.$myModuleName + [PSCustomObject][Ordered]@{ + PSTypeName = 'Module.Relationship' + Module = $myModule + RelatedModule = $loadedModule + PrivateData = $loadedModule.PrivateData.$myModuleName + } + } + elseif ($loadedModule.PrivateData.PSData.Tags -contains $myModuleName) { + [PSCustomObject][Ordered]@{ + PSTypeName = 'Module.Relationship' + Module = $myModule + RelatedModule = $loadedModule + PrivateData = @{} + } + } + } +} +#endregion Search for Module Relationships + +) + +} -replace '\$TargetModule', "$VariableAST")) + +} \ No newline at end of file From 27099cb21300a0e157cd6cc28883a51f22f622a6 Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sat, 2 Jul 2022 19:24:32 -0700 Subject: [PATCH 11/21] Updating PipeScript module source (using #105, #106) --- PipeScript.ps1.psm1 | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/PipeScript.ps1.psm1 b/PipeScript.ps1.psm1 index 120504947..34139f0bb 100644 --- a/PipeScript.ps1.psm1 +++ b/PipeScript.ps1.psm1 @@ -1,15 +1,14 @@ [Include('*-*')]$psScriptRoot -$aliasNames = @() -foreach ($transpilerCmd in Get-Transpiler) { - $aliasNames += ".>$($transpilerCmd.DisplayName)" - Set-Alias ".>$($transpilerCmd.DisplayName)" Use-PipeScript - $aliasNames += ".<$($transpilerCmd.DisplayName)>" - Set-Alias ".<$($transpilerCmd.DisplayName)>" Use-PipeScript -} +$transpilerNames = Get-Transpiler | Select-Object -ExpandProperty DisplayName +$aliasList += + [SmartAlias(Command='Use-PipeScript',Prefix='.>',PassThru)]$transpilerNames + +$aliasList += + [SmartAlias(Command='Use-PipeScript',Prefix='.<',Suffix='>',PassThru)]$transpilerNames $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -$aliasNames += +$aliasList += [GetExports("Alias")]$MyModule Export-ModuleMember -Function * -Alias $aliasNames \ No newline at end of file From 8e7af5ad5bf99d4426d8027614d7e54d99fcac98 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 3 Jul 2022 02:25:03 +0000 Subject: [PATCH 12/21] Updating PipeScript module source (using #105, #106) --- PipeScript.psm1 | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index 3ea09a9f8..116a26229 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -1,19 +1,26 @@ foreach ($file in (Get-ChildItem -Path "$psScriptRoot" -Filter "*-*" -Recurse)) { if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 - if ($file.Name -match '\.ps1\.ps1$') { continue } # Skip if the file is a source generator. + if ($file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file. . $file.FullName } -$aliasNames = @() -foreach ($transpilerCmd in Get-Transpiler) { - $aliasNames += ".>$($transpilerCmd.DisplayName)" - Set-Alias ".>$($transpilerCmd.DisplayName)" Use-PipeScript - $aliasNames += ".<$($transpilerCmd.DisplayName)>" - Set-Alias ".<$($transpilerCmd.DisplayName)>" Use-PipeScript -} +$transpilerNames = Get-Transpiler | Select-Object -ExpandProperty DisplayName +$aliasList += + + @(foreach ($alias in @($transpilerNames)) { + Set-Alias ".>$alias" "Use-PipeScript" -PassThru:$True + }) + + +$aliasList += + + @(foreach ($alias in @($transpilerNames)) { + Set-Alias ".<$alias>" "Use-PipeScript" -PassThru:$True + }) + $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module -$aliasNames += +$aliasList += @( if ($MyModule -isnot [Management.Automation.PSModuleInfo]) { From fc7ce4267d25eb0e1a9dcfe5adc30a0f19387db1 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 3 Jul 2022 02:25:03 +0000 Subject: [PATCH 13/21] Updating PipeScript module source (using #105, #106) --- Transpilers/Parameters/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/Transpilers/Parameters/README.md b/Transpilers/Parameters/README.md index d436f7b58..4a0d8d561 100644 --- a/Transpilers/Parameters/README.md +++ b/Transpilers/Parameters/README.md @@ -17,6 +17,7 @@ When this is the case it is common for the transpiler to add a ```[ValidateScrip |DisplayName |Synopsis | |----------------------------------------------------|---------------------------------------------------------------------| +|[Aliases](Aliases.psx.ps1) |[Dynamically Defines Aliases](Aliases.psx.ps1) | |[ValidateExtension](ValidateExtension.psx.ps1) |[Validates Extensions](ValidateExtension.psx.ps1) | |[ValidatePlatform](ValidatePlatform.psx.ps1) |[Validates the Platform](ValidatePlatform.psx.ps1) | |[ValidatePropertyName](ValidatePropertyName.psx.ps1)|[Validates Property Names](ValidatePropertyName.psx.ps1) | From 4230b66b71ebd036906775c3885b1756652135bf Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Sun, 3 Jul 2022 15:24:27 -0700 Subject: [PATCH 14/21] Updating PipeScript module source (typofix) (using #105, #106) --- PipeScript.ps1.psm1 | 2 +- PipeScript.psm1 | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/PipeScript.ps1.psm1 b/PipeScript.ps1.psm1 index 34139f0bb..ea054d708 100644 --- a/PipeScript.ps1.psm1 +++ b/PipeScript.ps1.psm1 @@ -11,4 +11,4 @@ $MyModule = $MyInvocation.MyCommand.ScriptBlock.Module $aliasList += [GetExports("Alias")]$MyModule -Export-ModuleMember -Function * -Alias $aliasNames \ No newline at end of file +Export-ModuleMember -Function * -Alias $aliasList \ No newline at end of file diff --git a/PipeScript.psm1 b/PipeScript.psm1 index 116a26229..025f7b1f9 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -1,6 +1,6 @@ foreach ($file in (Get-ChildItem -Path "$psScriptRoot" -Filter "*-*" -Recurse)) { if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 - if ($file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file. + if ($file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file . $file.FullName } @@ -46,4 +46,4 @@ $aliasList += }) -Export-ModuleMember -Function * -Alias $aliasNames +Export-ModuleMember -Function * -Alias $aliasList From da99d407baebabb60adc14a0b16ed487ef526816 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Sun, 3 Jul 2022 22:25:06 +0000 Subject: [PATCH 15/21] Updating PipeScript module source (typofix) (using #105, #106) --- PipeScript.psm1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index 025f7b1f9..a503f727e 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -1,6 +1,6 @@ foreach ($file in (Get-ChildItem -Path "$psScriptRoot" -Filter "*-*" -Recurse)) { if ($file.Extension -ne '.ps1') { continue } # Skip if the extension is not .ps1 - if ($file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file + if ($file.Name -match '\.[^\.]\.ps1$') { continue } # Skip if the file is an unrelated file. . $file.FullName } From fe2876131328201a56352607755908db6eab41d2 Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Mon, 4 Jul 2022 14:49:04 -0700 Subject: [PATCH 16/21] Adding support for Dotting (#107) --- Transpilers/Syntax/Dot.psx.ps1 | 276 +++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) create mode 100644 Transpilers/Syntax/Dot.psx.ps1 diff --git a/Transpilers/Syntax/Dot.psx.ps1 b/Transpilers/Syntax/Dot.psx.ps1 new file mode 100644 index 000000000..4dec5cfe2 --- /dev/null +++ b/Transpilers/Syntax/Dot.psx.ps1 @@ -0,0 +1,276 @@ +<# +.SYNOPSIS + Dot Notation +.DESCRIPTION + Dot Notation simplifies multiple operations on one or more objects. + + Any command named . (followed by a letter) will be treated as the name of a method or property. + + .Name will be considered the name of a property or method + + If it is followed by parenthesis, these will be treated as method arguments. + + If it is followed by a ScriptBlock, a dynamic property will be created that will return the result of that script block. + + If any other arguments are found before the next .Name, they will be considered arguments to a method. +.EXAMPLE + .> { + [DateTime]::now | .Month .Day .Year + } +.EXAMPLE + .> { + "abc", "123", "abc123" | .Length + } +.EXAMPLE + .> { 1.99 | .ToString 'C' [CultureInfo]'gb-gb' } +.EXAMPLE + .> { 1.99 | .ToString('C') } +.EXAMPLE + 1..5 | .Number { $_ } .Even { -not ($_ % 2) } .Odd { ($_ % 2) -as [bool]} +#> +[ValidateScript({ + $commandAst = $_ + $DotChainPattern = '^\.\p{L}' + if ($commandAst.CommandElements[0].Value -match '^\.\p{L}') { + return $true + } + return $false +})] +param( +[Parameter(Mandatory,ParameterSetName='Command',ValueFromPipeline)] +[Management.Automation.Language.CommandAst] +$CommandAst +) + +begin { + $DotProperty = { +if ($in.PSObject.Methods[$PropertyName].OverloadDefinitions -match '\(\)$') { + $in.$PropertyName.Invoke() +} elseif ($in.PSObject.Properties[$PropertyName]) { + $in.$PropertyName +} + } + + $DotMethod = { $in.$MethodName($MethodArguments) } +} + +process { + + # Create a collection for the entire chain of operations and their arguments. + $DotChain = @() + $DotArgsAst = @() + $DotChainPart = '' + $DotChainPattern = '^\.\p{L}' + + # Then, walk over each element of the commands + $CommandElements = $CommandAst.CommandElements + for ( $elementIndex =0 ; $elementIndex -lt $CommandElements.Count; $elementIndex++) { + # If we are on the first element, or the command element starts with the DotChainPattern + if ($elementIndex -eq 0 -or $CommandElements[$elementIndex].Value -match $DotChainPattern) { + if ($DotChainPart) { + $DotChain += [PSCustomObject]@{ + PSTypeName = 'PipeScript.Dot.Chain' + Name = $DotChainPart + Arguments = $DotArgsAst + } + } + + $DotArgsAst = @() + + # A given step started with dots can have more than one step in the chain specified. + $elementDotChain = $CommandElements[$elementIndex].Value.Split('.') + [Array]::Reverse($elementDotChain) + $LastElement, $otherElements = $elementDotChain + if ($otherElements) { + foreach ($element in $otherElements) { + $DotChain += [PSCustomObject]@{ + PSTypeName = 'PipeScript.Dot.Chain' + Name = $element + Arguments = @() + } + } + } + + $DotChainPart = $LastElement + } + # If we are not on the first index or the element does not start with a dot, it is an argument. + else { + $DotArgsAst += $CommandElements[$elementIndex] + } + } + + if ($DotChainPart) { + $DotChain += [PSCustomObject]@{ + PSTypeName = 'PipeScript.Dot.Chain' + Name = $DotChainPart + Arguments = $DotArgsAst + } + } + + + $NewScript = @() + $indent = 0 + $WasPipedTo = + $CommandAst.Parent -and + $CommandAst.Parent.PipelineElements -and + $CommandAst.Parent.PipelineElements.IndexOf($CommandAst) -gt 0 + + + # By default, we are not creating a property bag. + # This default will change if: + # * More than one property is defined + # * A property is explicitly assigned + $isPropertyBag = $false + + # If we were piped to, adjust indent (for now) + if ($WasPipedTo ) { + $indent += 4 + } + + # Declare the start of the chain (even if we don't use it) + $propertyBagStart = (' ' * $indent) + '[PSCustomObject][Ordered]@{' + # and keep track of all items we must post process. + $PostProcess = @() + + # If more than one item was in the chain + if ($DotChain.Length -ge 0) { + $indent += 4 # adjust indentation + } + + # Walk thru all items in the chain of properties. + foreach ($Dot in $DotChain) { + $firstDotArg, $secondDotArg, $restDotArgs = $dot.Arguments + # Determine what will be the segment of the dot chain. + $thisSegement = + # If the dot chain has no arguments, treat it as a property + if (-not $dot.Arguments) { + $DotProperty -replace '\$PropertyName', "'$($dot.Name)'" + } + # If the dot chain's first argument is an assignment + elseif ($firstDotArg -is [Management.Automation.Language.StringConstantExpressionAst] -and + $firstDotArg.Value -eq '=') { + $isPropertyBag = $true + # and the second is a script block + if ($secondDotArg -is [Management.Automation.Language.ScriptBlockExpressionAst]) { + # it will become either a [ScriptMethod] or [ScriptProperty] + $secondScriptBlock = [ScriptBlock]::Create( + $secondDotArg.Extent.ToString() -replace '^\{' -replace '\}$' + ) + + # If the script block had parameters (even if they were empty parameters) + # It should become a ScriptMethod + if ($secondScriptBlock.Ast.ParamBlock) { + "[PSScriptMethod]::New('$($dot.Name)', $secondDotArg)" + } else { + # Otherwise, it will become a ScriptProperty + "[PSScriptProperty]::New('$($dot.Name)', $secondDotArg)" + } + $PostProcess += $dot.Name + } + # If we had an array of arguments, and both elements were ScriptBlocks + elseif ($secondDotArg -is [Management.Automation.Language.ArrayLiteralAst] -and + $secondDotArg.Elements.Count -eq 2 -and + $secondDotArg.Elements[0] -is [Management.Automation.Language.ScriptBlockExpressionAst] -and + $secondDotArg.Elements[1] -is [Management.Automation.Language.ScriptBlockExpressionAst] + ) { + # Then we will treat this as a settable script block + $PostProcess += $dot.Name + "[PSScriptProperty]::New('$($dot.Name)', $($secondDotArg.Elements[0]), $($secondDotArg.Elements[1]))" + } + elseif (-not $restDotArgs) { + # Otherwise, if we only have one argument, use the expression directly + $secondDotArg.Extent.ToString() + } elseif ($restDotArgs) { + # Otherwise, if we had multiple values, create a list. + @( + $secondDotArg.Extent.ToString() + foreach ($otherDotArg in $restDotArgs) { + $otherDotArg.Extent.Tostring() + } + ) -join ',' + } + } + # If the dot chain's first argument is a ScriptBlock + elseif ($firstDotArg -is [Management.Automation.Language.ScriptBlockExpressionAst]) + { + # Run that script block + "& $($firstDotArg.Extent.ToString())" + } + elseif ($firstDotArg -is [Management.Automation.Language.ParenExpressionAst]) { + # If the first argument is a parenthesis, assume the contents to be method arguments + $DotMethod -replace '\$MethodName', $dot.Name -replace '\(\$MethodArguments\)',$firstDotArg.ToString() + } + else { + # If the first argument is anything else, assume all remaining arguments to be method parameters. + $DotMethod -replace '\$MethodName', $dot.Name -replace '\(\$MethodArguments\)',( + '(' + ($dot.Arguments -join ',') + ')' + ) + } + + # Now we add the segment to the total script + $NewScript += + if (-not $isPropertyBag -and $DotChain.Length -eq 1 -and $thisSegement -notmatch '^\[PS') { + # If the dot chain is a single item, and not part of a property bag, include it directly + "$(' ' * ($indent - 4))$thisSegement" + } else { + + $isPropertyBag = $true + # Otherwise include this segment as a hashtable assignment with the correct indentation. + $thisSegement = @($thisSegement -split '[\r\n]+' -ne '' -replace '$', (' ' * 8)) -join [Environment]::NewLine + @" +$(' ' * $indent)'$($dot.Name.Replace("'","''"))' = +$(' ' * ($indent + 4))$thisSegement +"@ + } + } + + + # If we were generating a property bag + if ($isPropertyBag) { + if ($WasPipedTo) { # and it was piped to + # Add the start of the pipeline and the property bag start to the top of the script. + $NewScript = @('& { process {') + ((' ' * $indent) + '$in = $this = $_') + $propertyBagStart + $NewScript + } else { + # If it was not piped to, just add the start of the property bag + $newScript = @($propertyBagStart) + $NewScript + } + } elseif ($WasPipedTo) { + # If we were piped to (but were not a property bag) + $indent -= 4 + # add the start of the pipeline to the top of the script. + $newScript = @('& { process {') + ((' ' * $indent) + '$in = $this = $_') + $NewScript + } + + # If we were a property bag + if ($isPropertyBag) { + # close out the script + $NewScript += ($(' ' * $indent) + '}') + $indent -= 4 + } + + # If there was post processing + if ($PostProcess) { + # Change the property bag start to assign it to a variable + $NewScript = $newScript -replace ($propertyBagStart -replace '\W', '\$0'), "`$Out = $propertyBagStart" + foreach ($post in $PostProcess) { + # and change any [PSScriptProperty] or [PSScriptMethod] into a method on that object. + $newScript += "`$Out.PSObject.Members.Add(`$out.$Post)" + } + # Then output. + $NewScript += '$Out' + } + + # If we were piped to + if ($WasPipedTo) { + # close off the script. + $NewScript += '} }' + } else { + # otherwise, make it a subexpression + $NewScript = '$(' + ($NewScript -join [Environment]::NewLine) + ')' + } + + $NewScript = $NewScript -join [Environment]::Newline + + # Return the created script. + [scriptblock]::Create($NewScript) +} \ No newline at end of file From 0ec1153b58fb237745f3f616add47ac608edbe31 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 4 Jul 2022 21:49:43 +0000 Subject: [PATCH 17/21] Merge branch 'PipeScriptFunAndFixes' of https://github.com/StartAutomating/PipeScript into PipeScriptFunAndFixes --- Transpilers/Syntax/README.md | 38 ++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/Transpilers/Syntax/README.md b/Transpilers/Syntax/README.md index c0f6d1be3..124d0a545 100644 --- a/Transpilers/Syntax/README.md +++ b/Transpilers/Syntax/README.md @@ -3,12 +3,50 @@ This directory and it's subdirectories contain syntax changes that enable common |DisplayName |Synopsis | |------------------------------------------|------------------------------------------------------| +|[Dot](Dot.psx.ps1) |[Dot Notation](Dot.psx.ps1) | |[PipedAssignment](PipedAssignment.psx.ps1)|[Piped Assignment Transpiler](PipedAssignment.psx.ps1)| |[RegexLiteral](RegexLiteral.psx.ps1) |[Regex Literal Transpiler](RegexLiteral.psx.ps1) | +## Dot Example 1 + + +~~~PowerShell + .> { + [DateTime]::now | +~~~ + +## Dot Example 2 + + +~~~PowerShell + .> { + "abc", "123", "abc123" | +~~~ + +## Dot Example 3 + + +~~~PowerShell + .> { 1 +~~~ + +## Dot Example 4 + + +~~~PowerShell + .> { 1 +~~~ + +## Dot Example 5 + + +~~~PowerShell + 1. +~~~ + ## PipedAssignment Example 1 From 22d829e42f171a022197dd016b192b4158e5108c Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Mon, 4 Jul 2022 14:59:58 -0700 Subject: [PATCH 18/21] More Dotting Examples (#107) --- Transpilers/Syntax/Dot.psx.ps1 | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/Transpilers/Syntax/Dot.psx.ps1 b/Transpilers/Syntax/Dot.psx.ps1 index 4dec5cfe2..3eb30b5cc 100644 --- a/Transpilers/Syntax/Dot.psx.ps1 +++ b/Transpilers/Syntax/Dot.psx.ps1 @@ -26,7 +26,21 @@ .EXAMPLE .> { 1.99 | .ToString('C') } .EXAMPLE - 1..5 | .Number { $_ } .Even { -not ($_ % 2) } .Odd { ($_ % 2) -as [bool]} + .> { 1..5 | .Number { $_ } .Even { -not ($_ % 2) } .Odd { ($_ % 2) -as [bool]} } +.EXAMPLE + .> { .ID { Get-Random } .Count { 0 } .Total { 10 }} +.EXAMPLE + .> { + # Declare a new object + .Property = "ConstantValue" .Numbers = 1..100 .Double = { + param($n) + $n * 2 + } .EvenNumbers = { + $this.Numbers | Where-Object { -not ($_ % 2)} + } .OddNumbers = { + $this.Numbers | Where-Object { $_ % 2} + } + } #> [ValidateScript({ $commandAst = $_ From 66844b2f0ab27b4107324bfcb98497fabd9dd92d Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 4 Jul 2022 22:01:05 +0000 Subject: [PATCH 19/21] More Dotting Examples (#107) --- Transpilers/Syntax/README.md | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Transpilers/Syntax/README.md b/Transpilers/Syntax/README.md index 124d0a545..5b859fbc1 100644 --- a/Transpilers/Syntax/README.md +++ b/Transpilers/Syntax/README.md @@ -44,7 +44,22 @@ This directory and it's subdirectories contain syntax changes that enable common ~~~PowerShell - 1. + .> { 1. +~~~ + +## Dot Example 6 + + +~~~PowerShell + .> { +~~~ + +## Dot Example 7 + + +~~~PowerShell + .> { + # Declare a new object ~~~ ## PipedAssignment Example 1 From 2d4801baba9b042a6f73ee9ea8d2b37a29690f6c Mon Sep 17 00:00:00 2001 From: James Brundage <@github.com> Date: Mon, 4 Jul 2022 15:01:51 -0700 Subject: [PATCH 20/21] Updating Module Version [0.0.7] and CHANGELOG --- CHANGELOG.md | 14 ++++++++++++++ PipeScript.psd1 | 16 +++++++++++++++- 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d046a0b83..d8a2f08bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,17 @@ +## 0.0.7: +* Syntax Improvements: + * Support for Dot Notation (#107) +* New Transpilers: + * .>ModuleRelationships (#105) + * .>ModuleExports (#104) + * .>Aliases (#106) +* Fixes: + * Invoke-PipeScript improved error behavior (#103) + * Explicit Transpiler returns modified ScriptBlock (#102) + * .psm1 alias export fix (#100) + * Include improvements (#96) +--- + ## 0.0.6: * New Transpilers: * ValidateScriptBlock diff --git a/PipeScript.psd1 b/PipeScript.psd1 index a6e66bada..9a6304ca1 100644 --- a/PipeScript.psd1 +++ b/PipeScript.psd1 @@ -1,5 +1,5 @@ @{ - ModuleVersion = '0.0.6' + ModuleVersion = '0.0.7' Description = 'An Extensible Transpiler for PowerShell (and anything else)' RootModule = 'PipeScript.psm1' PowerShellVersion = '4.0' @@ -17,6 +17,20 @@ Tags = 'PipeScript','PowerShell', 'Transpilation', 'Compiler' ReleaseNotes = @' +## 0.0.7: +* Syntax Improvements: + * Support for Dot Notation (#107) +* New Transpilers: + * .>ModuleRelationships (#105) + * .>ModuleExports (#104) + * .>Aliases (#106) +* Fixes: + * Invoke-PipeScript improved error behavior (#103) + * Explicit Transpiler returns modified ScriptBlock (#102) + * .psm1 alias export fix (#100) + * Include improvements (#96) +--- + ## 0.0.6: * New Transpilers: * ValidateScriptBlock From 76ecbe7ab4ad47e1e2251f4927210d02c6c24d80 Mon Sep 17 00:00:00 2001 From: StartAutomating Date: Mon, 4 Jul 2022 22:03:40 +0000 Subject: [PATCH 21/21] Merge branch 'main' into PipeScriptFunAndFixes --- PipeScript.psm1 | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/PipeScript.psm1 b/PipeScript.psm1 index f9c3b248a..a503f727e 100644 --- a/PipeScript.psm1 +++ b/PipeScript.psm1 @@ -43,6 +43,7 @@ $aliasList += $cmd } } - }) + }) + Export-ModuleMember -Function * -Alias $aliasList