Skip to content

Commit eaf4864

Browse files
Adding command
1 parent 49cec1d commit eaf4864

File tree

2 files changed

+260
-0
lines changed

2 files changed

+260
-0
lines changed

PSModuleDevelopment/PSModuleDevelopment.psd1

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
'Expand-PSMDTypeName',
6666
'Find-PSMDFileContent',
6767
'Find-PSMDType',
68+
'Format-PSMDParameter',
6869
'Get-PSMDAssembly',
6970
'Get-PSMDConstructor',
7071
'Get-PSMDHelpEx',
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
function Format-PSMDParameter
2+
{
3+
<#
4+
.SYNOPSIS
5+
Formats the parameter block on commands.
6+
7+
.DESCRIPTION
8+
Formats the parameter block on commands.
9+
This function will convert legacy functions that have their parameters straight behind their command name.
10+
It also fixes missing CmdletBinding attributes.
11+
12+
Nested commands will also be affected.
13+
14+
.PARAMETER FullName
15+
The file to process
16+
17+
.PARAMETER DisableCache
18+
By default, this command caches the results of its execution in the PSFramework result cache.
19+
This information can then be retrieved for the last command to do so by running Get-PSFResultCache.
20+
Setting this switch disables the caching of data in the cache.
21+
22+
.PARAMETER Confirm
23+
If this switch is enabled, you will be prompted for confirmation before executing any operations that change state.
24+
25+
.PARAMETER WhatIf
26+
If this switch is enabled, no actions are performed but informational messages will be displayed that explain what would happen if the command were to run.
27+
28+
.EXAMPLE
29+
PS C:\> Get-ChildItem .\functions\*\*.ps1 | Set-PSMDCmdletBinding
30+
31+
Updates all commands in the module to have a cmdletbinding attribute.
32+
#>
33+
[CmdletBinding(SupportsShouldProcess = $true)]
34+
param (
35+
[Parameter(Mandatory = $true, ValueFromPipeline = $true, ValueFromPipelineByPropertyName = $true)]
36+
[string[]]
37+
$FullName,
38+
39+
[switch]
40+
$DisableCache
41+
)
42+
43+
begin
44+
{
45+
#region Utility functions
46+
function Invoke-AstWalk
47+
{
48+
[CmdletBinding()]
49+
param (
50+
$Ast,
51+
52+
[string[]]
53+
$Command,
54+
55+
[string[]]
56+
$Name,
57+
58+
[string]
59+
$NewName,
60+
61+
[bool]
62+
$IsCommand,
63+
64+
[bool]
65+
$NoAlias
66+
)
67+
68+
#Write-PSFMessage -Level Host -Message "Processing $($Ast.Extent.StartLineNumber) | $($Ast.Extent.File) | $($Ast.GetType().FullName)"
69+
$typeName = $Ast.GetType().FullName
70+
71+
switch ($typeName)
72+
{
73+
"System.Management.Automation.Language.FunctionDefinitionAst"
74+
{
75+
#region Has no param block
76+
if ($null -eq $Ast.Body.ParamBlock)
77+
{
78+
$baseIndent = $Ast.Extent.Text.Split("`n")[0] -replace "^(\s{0,}).*", '$1'
79+
$indent = $baseIndent + "`t"
80+
81+
# Kill explicit parameter section behind name
82+
$startIndex = "function ".Length + $Ast.Name.Length
83+
$endIndex = $Ast.Extent.Text.IndexOf("{")
84+
Add-FileReplacement -Path $ast.Extent.File -Start ($Ast.Extent.StartOffset + $startIndex) -Length ($endIndex - $startIndex) -NewContent "`n"
85+
86+
$baseParam = @"
87+
$($indent)[CmdletBinding()]
88+
$($indent)param (
89+
{0}
90+
$($indent))
91+
"@
92+
$parameters = @()
93+
$paramIndent = $indent + "`t"
94+
foreach ($parameter in $Ast.Parameters)
95+
{
96+
$defaultValue = ""
97+
if ($parameter.DefaultValue) { $defaultValue = " = $($parameter.DefaultValue.Extent.Text)" }
98+
$values = @()
99+
foreach ($attribute in $parameter.Attributes)
100+
{
101+
$values += "$($paramIndent)$($attribute.Extent.Text)"
102+
}
103+
$values += "$($paramIndent)$($parameter.Name.Extent.Text)$($defaultValue)"
104+
$parameters += $values -join "`n"
105+
}
106+
$baseParam = $baseParam -f ($parameters -join ",`n`n")
107+
108+
Add-FileReplacement -Path $ast.Extent.File -Start $Ast.Body.Extent.StartOffset -Length 1 -NewContent "{`n$($baseParam)"
109+
}
110+
#endregion Has no param block
111+
112+
#region Has a param block, but no cmdletbinding
113+
if (($null -ne $Ast.Body.ParamBlock) -and (-not ($Ast.Body.ParamBlock.Attributes | Where-Object TypeName -Like "CmdletBinding")))
114+
{
115+
$text = [System.IO.File]::ReadAllText($Ast.Extent.File)
116+
117+
$index = $Ast.Body.ParamBlock.Extent.StartOffset
118+
while (($index -gt 0) -and ($text.Substring($index, 1) -ne "`n")) { $index = $index - 1 }
119+
120+
$indentIndex = $index + 1
121+
$indent = $text.Substring($indentIndex, ($Ast.Body.ParamBlock.Extent.StartOffset - $indentIndex))
122+
Add-FileReplacement -Path $Ast.Body.ParamBlock.Extent.File -Start $indentIndex -Length ($Ast.Body.ParamBlock.Extent.StartOffset - $indentIndex) -NewContent "$($indent)[CmdletBinding()]`n$($indent)"
123+
}
124+
#endregion Has a param block, but no cmdletbinding
125+
126+
Invoke-AstWalk -Ast $Ast.Body -Command $Command -Name $Name -NewName $NewName -IsCommand $false
127+
}
128+
default
129+
{
130+
foreach ($property in $Ast.PSObject.Properties)
131+
{
132+
if ($property.Name -eq "Parent") { continue }
133+
if ($null -eq $property.Value) { continue }
134+
135+
if (Get-Member -InputObject $property.Value -Name GetEnumerator -MemberType Method)
136+
{
137+
foreach ($item in $property.Value)
138+
{
139+
if ($item.PSObject.TypeNames -contains "System.Management.Automation.Language.Ast")
140+
{
141+
Invoke-AstWalk -Ast $item -Command $Command -Name $Name -NewName $NewName -IsCommand $IsCommand
142+
}
143+
}
144+
continue
145+
}
146+
147+
if ($property.Value.PSObject.TypeNames -contains "System.Management.Automation.Language.Ast")
148+
{
149+
Invoke-AstWalk -Ast $property.Value -Command $Command -Name $Name -NewName $NewName -IsCommand $IsCommand
150+
}
151+
}
152+
}
153+
}
154+
}
155+
156+
function Add-FileReplacement
157+
{
158+
[CmdletBinding()]
159+
param (
160+
[string]
161+
$Path,
162+
163+
[int]
164+
$Start,
165+
166+
[int]
167+
$Length,
168+
169+
[string]
170+
$NewContent
171+
)
172+
Write-PSFMessage -Level Verbose -Message "Change Submitted: $Path | $Start | $Length | $NewContent" -Tag 'update', 'change', 'file'
173+
174+
if (-not $globalFunctionHash.ContainsKey($Path))
175+
{
176+
$globalFunctionHash[$Path] = @()
177+
}
178+
179+
$globalFunctionHash[$Path] += New-Object PSObject -Property @{
180+
Content = $NewContent
181+
Start = $Start
182+
Length = $Length
183+
}
184+
}
185+
186+
function Apply-FileReplacement
187+
{
188+
[Diagnostics.CodeAnalysis.SuppressMessageAttribute("PSUseApprovedVerbs", "")]
189+
[CmdletBinding()]
190+
param (
191+
192+
)
193+
194+
foreach ($key in $globalFunctionHash.Keys)
195+
{
196+
$value = $globalFunctionHash[$key] | Sort-Object Start
197+
$content = [System.IO.File]::ReadAllText($key)
198+
199+
$newString = ""
200+
$currentIndex = 0
201+
202+
foreach ($item in $value)
203+
{
204+
$newString += $content.SubString($currentIndex, ($item.Start - $currentIndex))
205+
$newString += $item.Content
206+
$currentIndex = $item.Start + $item.Length
207+
}
208+
209+
$newString += $content.SubString($currentIndex)
210+
211+
[System.IO.File]::WriteAllText($key, $newString)
212+
#$newString
213+
}
214+
}
215+
216+
function Write-Issue
217+
{
218+
[CmdletBinding()]
219+
param (
220+
$Extent,
221+
222+
$Data,
223+
224+
[string]
225+
$Type
226+
)
227+
228+
New-Object PSObject -Property @{
229+
Type = $Type
230+
Data = $Data
231+
File = $Extent.File
232+
StartLine = $Extent.StartLineNumber
233+
Text = $Extent.Text
234+
}
235+
}
236+
#endregion Utility functions
237+
}
238+
process
239+
{
240+
foreach ($path in $FullName)
241+
{
242+
$globalFunctionHash = @{ }
243+
244+
$tokens = $null
245+
$parsingError = $null
246+
$ast = [System.Management.Automation.Language.Parser]::ParseFile($path, [ref]$tokens, [ref]$parsingError)
247+
248+
Write-PSFMessage -Level VeryVerbose -Message "Ensuring Cmdletbinding for all functions in $path" -Tag 'start' -Target $Name
249+
$issues += Invoke-AstWalk -Ast $ast -Command $Command -Name $Name -NewName $NewName -IsCommand $false
250+
251+
Set-PSFResultCache -InputObject $issues -DisableCache $DisableCache
252+
if ($PSCmdlet.ShouldProcess($path, "Set CmdletBinding attribute"))
253+
{
254+
Apply-FileReplacement
255+
}
256+
$issues
257+
}
258+
}
259+
}

0 commit comments

Comments
 (0)