1
+ function Convert-PSMDMessage
2
+ {
3
+ <#
4
+ . SYNOPSIS
5
+ Converts a file's use of PSFramework messages to strings.
6
+
7
+ . DESCRIPTION
8
+ Converts a file's use of PSFramework messages to strings.
9
+
10
+ . PARAMETER Path
11
+ Path to the file to convert.
12
+
13
+ . PARAMETER OutPath
14
+ Folder in which to generate the output ps1 and psd1 file.
15
+
16
+ . PARAMETER EnableException
17
+ Replaces user friendly yellow warnings with bloody red exceptions of doom!
18
+ Use this if you want the function to throw terminating errors you want to catch.
19
+
20
+ . EXAMPLE
21
+ PS C:\> Convert-PSMDMessage -Path 'C:\Scripts\logrotate.ps1' -OutPath 'C:\output'
22
+
23
+ Converts all instances of writing messages in logrotate.ps1 to use strings instead.
24
+ #>
25
+ [CmdletBinding ()]
26
+ param (
27
+ [Parameter (Mandatory = $true , Position = 0 )]
28
+ [PsfValidateScript (' PSFramework.Validate.FSPath.File' , ErrorString = ' PSFramework.Validate.FSPath.File' )]
29
+ [string ]
30
+ $Path ,
31
+
32
+ [Parameter (Mandatory = $true , Position = 1 )]
33
+ [PsfValidateScript (' PSFramework.Validate.FSPath.Folder' , ErrorString = ' PSFramework.Validate.FSPath.Folder' )]
34
+ [string ]
35
+ $OutPath ,
36
+
37
+ [switch ]
38
+ $EnableException
39
+ )
40
+
41
+ begin
42
+ {
43
+ # region Utility Functions
44
+ function Get-Text
45
+ {
46
+ [CmdletBinding ()]
47
+ param (
48
+ $Value
49
+ )
50
+
51
+ if (-not $Value.NestedExpressions ) { return $Value.Extent.Text }
52
+
53
+ $expressions = @ { }
54
+ $expIndex = 0
55
+
56
+ $builder = [System.Text.StringBuilder ]::new()
57
+ $baseIndex = $Value.Extent.StartOffset
58
+ $astIndex = 0
59
+
60
+ foreach ($nestedExpression in $Value.NestedExpressions )
61
+ {
62
+ $null = $builder.Append ($Value.Extent.Text.SubString ($astIndex , ($nestedExpression.Extent.StartOffset - $baseIndex - $astIndex )).Replace(" {" , " {{" ).Replace(' }' , ' }}' ))
63
+ $astIndex = $nestedExpression.Extent.EndOffset - $baseIndex
64
+
65
+ if ($expressions.ContainsKey ($nestedExpression.Extent.Text )) { $effectiveIndex = $expressions [$nestedExpression.Extent.Text ] }
66
+ else
67
+ {
68
+ $expressions [$nestedExpression.Extent.Text ] = $expIndex
69
+ $effectiveIndex = $expIndex
70
+ $expIndex ++
71
+ }
72
+
73
+ $null = $builder.Append (" {$effectiveIndex }" )
74
+ }
75
+
76
+ $null = $builder.Append ($Value.Extent.Text.SubString ($astIndex ).Replace(" {" , " {{" ).Replace(' }' , ' }}' ))
77
+ $builder.ToString ()
78
+ }
79
+
80
+ function Get-Insert
81
+ {
82
+ [CmdletBinding ()]
83
+ param (
84
+ $Value
85
+ )
86
+
87
+ if (-not $Value.NestedExpressions ) { return " " }
88
+
89
+ $processed = @ { }
90
+ $elements = foreach ($nestedExpression in $Value.NestedExpressions )
91
+ {
92
+ if ($processed [$nestedExpression.Extent.Text ]) { continue }
93
+ else { $processed [$nestedExpression.Extent.Text ] = $true }
94
+
95
+ if ($nestedExpression -is [System.Management.Automation.Language.SubExpressionAst ])
96
+ {
97
+ if (
98
+ ($nestedExpression.SubExpression.Statements.Count -eq 1 ) -and
99
+ ($nestedExpression.SubExpression.Statements [0 ].PipelineElements.Count -eq 1 ) -and
100
+ ($nestedExpression.SubExpression.Statements [0 ].PipelineElements[0 ].Expression -is [System.Management.Automation.Language.MemberExpressionAst ])
101
+ ) { $nestedExpression.SubExpression.Extent.Text }
102
+ else { $nestedExpression.Extent.Text.SubString (1 ) }
103
+ }
104
+ else { $nestedExpression.Extent.Text }
105
+ }
106
+ $elements -join " , "
107
+ }
108
+ # endregion Utility Functions
109
+
110
+ $parameterMapping = @ {
111
+ ' Message' = ' String'
112
+ ' Action' = ' ActionString'
113
+ }
114
+ $insertMapping = @ {
115
+ ' String' = ' -StringValues'
116
+ ' Action' = ' -ActionStringValues'
117
+ }
118
+ }
119
+ process
120
+ {
121
+ $ast = (Read-PSMDScript - Path $Path ).Ast
122
+
123
+ # region Parse Input
124
+ $functionName = (Get-Item $Path ).BaseName
125
+
126
+ $commandAsts = $ast.FindAll ({
127
+ if ($args [0 ] -isnot [System.Management.Automation.Language.CommandAst ]) { return $false }
128
+ if ($args [0 ].CommandElements[0 ].Value -notmatch ' ^Invoke-PSFProtectedCommand$|^Write-PSFMessage$|^Stop-PSFFunction$' ) { return $false }
129
+ if (-not ($args [0 ].CommandElements.ParameterName -match ' ^Message$|^Action$' )) { return $false }
130
+ $true
131
+ }, $true )
132
+ if (-not $commandAsts )
133
+ {
134
+ Write-PSFMessage - Level Host - String ' Convert-PSMDMessage.Parameter.NonAffected' - StringValues $Path
135
+ return
136
+ }
137
+ # endregion Parse Input
138
+
139
+ # region Build Replacements table
140
+ $currentCount = 1
141
+ $replacements = foreach ($command in $commandAsts )
142
+ {
143
+ $parameter = $command.CommandElements | Where-Object ParameterName -in ' Message' , ' Action'
144
+ $paramIndex = $command.CommandElements.IndexOf ($parameter )
145
+ $parameterValue = $command.CommandElements [$paramIndex + 1 ]
146
+
147
+ [PSCustomObject ]@ {
148
+ OriginalText = $parameterValue.Value
149
+ Text = Get-Text - Value $parameterValue
150
+ Inserts = Get-Insert - Value $parameterValue
151
+ String = " $ ( $functionName ) .Message$ ( $currentCount ) "
152
+ StartOffset = $parameter.Extent.StartOffset
153
+ EndOffset = $parameterValue.Extent.EndOffset
154
+ OldParameterName = $parameter.ParameterName
155
+ NewParameterName = $parameterMapping [$parameter.ParameterName ]
156
+ Parameter = $parameter
157
+ ParameterValue = $parameterValue
158
+ }
159
+ $currentCount ++
160
+ }
161
+ # endregion Build Replacements table
162
+
163
+ # region Calculate new text body
164
+ $fileText = [System.IO.File ]::ReadAllText((Resolve-PSFPath - Path $Path ))
165
+ $builder = [System.Text.StringBuilder ]::new()
166
+ $index = 0
167
+ foreach ($replacement in $replacements )
168
+ {
169
+ $null = $builder.Append ($fileText.Substring ($index , ($replacement.StartOffset - $index )))
170
+ $null = $builder.Append (" -$ ( $replacement.NewParameterName ) '$ ( $replacement.String ) '" )
171
+ if ($replacement.Inserts ) { $null = $builder.Append (" $ ( $insertMapping [$replacement.NewParameterName ]) $ ( $replacement.Inserts ) " ) }
172
+ $index = $replacement.EndOffset
173
+ }
174
+ $null = $builder.Append ($fileText.Substring ($index ))
175
+ $newDefinition = $builder.ToString ()
176
+ $testResult = Read-PSMDScript - ScriptCode ([Scriptblock ]::create($newDefinition ))
177
+
178
+ if ($testResult.Errors )
179
+ {
180
+ Stop-PSFFunction - String ' Convert-PSMDMessage.SyntaxError' - StringValues $Path - Target $Path - EnableException $EnableException
181
+ return
182
+ }
183
+ # endregion Calculate new text body
184
+
185
+ $resolvedOutPath = Resolve-PSFPath - Path $OutPath
186
+ $encoding = [System.Text.UTF8Encoding ]::new($true )
187
+ $filePath = Join-Path - Path $resolvedOutPath - ChildPath " $functionName .ps1"
188
+ [System.IO.File ]::WriteAllText($filePath , $newDefinition , $encoding )
189
+ $stringsPath = Join-Path - Path $resolvedOutPath - ChildPath " $functionName .psd1"
190
+ $stringsText = @"
191
+ @{
192
+ $ ( $replacements | Format-String " `t '{0}' = {1} # {2}" - Property String, Text, Inserts | Join-String - Separator " `n " )
193
+ }
194
+ "@
195
+ [System.IO.File ]::WriteAllText($stringsPath , $stringsText , $encoding )
196
+ }
197
+ }
0 commit comments