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+ [OutputType ([string ])]
47+ [CmdletBinding ()]
48+ param (
49+ $Value
50+ )
51+
52+ if (-not $Value.NestedExpressions ) { return $Value.Extent.Text }
53+
54+ $expressions = @ { }
55+ $expIndex = 0
56+
57+ $builder = [System.Text.StringBuilder ]::new()
58+ $baseIndex = $Value.Extent.StartOffset
59+ $astIndex = 0
60+
61+ foreach ($nestedExpression in $Value.NestedExpressions )
62+ {
63+ $null = $builder.Append ($Value.Extent.Text.SubString ($astIndex , ($nestedExpression.Extent.StartOffset - $baseIndex - $astIndex )).Replace(" {" , " {{" ).Replace(' }' , ' }}' ))
64+ $astIndex = $nestedExpression.Extent.EndOffset - $baseIndex
65+
66+ if ($expressions.ContainsKey ($nestedExpression.Extent.Text )) { $effectiveIndex = $expressions [$nestedExpression.Extent.Text ] }
67+ else
68+ {
69+ $expressions [$nestedExpression.Extent.Text ] = $expIndex
70+ $effectiveIndex = $expIndex
71+ $expIndex ++
72+ }
73+
74+ $null = $builder.Append (" {$effectiveIndex }" )
75+ }
76+
77+ $null = $builder.Append ($Value.Extent.Text.SubString ($astIndex ).Replace(" {" , " {{" ).Replace(' }' , ' }}' ))
78+ $builder.ToString ()
79+ }
80+
81+ function Get-Insert
82+ {
83+ [OutputType ([string ])]
84+ [CmdletBinding ()]
85+ param (
86+ $Value
87+ )
88+
89+ if (-not $Value.NestedExpressions ) { return " " }
90+
91+ $processed = @ { }
92+ $elements = foreach ($nestedExpression in $Value.NestedExpressions )
93+ {
94+ if ($processed [$nestedExpression.Extent.Text ]) { continue }
95+ else { $processed [$nestedExpression.Extent.Text ] = $true }
96+
97+ if ($nestedExpression -is [System.Management.Automation.Language.SubExpressionAst ])
98+ {
99+ if (
100+ ($nestedExpression.SubExpression.Statements.Count -eq 1 ) -and
101+ ($nestedExpression.SubExpression.Statements [0 ].PipelineElements.Count -eq 1 ) -and
102+ ($nestedExpression.SubExpression.Statements [0 ].PipelineElements[0 ].Expression -is [System.Management.Automation.Language.MemberExpressionAst ])
103+ ) { $nestedExpression.SubExpression.Extent.Text }
104+ else { $nestedExpression.Extent.Text.SubString (1 ) }
105+ }
106+ else { $nestedExpression.Extent.Text }
107+ }
108+ $elements -join " , "
109+ }
110+ # endregion Utility Functions
111+
112+ $parameterMapping = @ {
113+ ' Message' = ' String'
114+ ' Action' = ' ActionString'
115+ }
116+ $insertMapping = @ {
117+ ' String' = ' -StringValues'
118+ ' Action' = ' -ActionStringValues'
119+ }
120+ }
121+ process
122+ {
123+ $ast = (Read-PSMDScript - Path $Path ).Ast
124+
125+ # region Parse Input
126+ $functionName = (Get-Item $Path ).BaseName
127+
128+ $commandAsts = $ast.FindAll ({
129+ if ($args [0 ] -isnot [System.Management.Automation.Language.CommandAst ]) { return $false }
130+ if ($args [0 ].CommandElements[0 ].Value -notmatch ' ^Invoke-PSFProtectedCommand$|^Write-PSFMessage$|^Stop-PSFFunction$' ) { return $false }
131+ if (-not ($args [0 ].CommandElements.ParameterName -match ' ^Message$|^Action$' )) { return $false }
132+ $true
133+ }, $true )
134+ if (-not $commandAsts )
135+ {
136+ Write-PSFMessage - Level Host - String ' Convert-PSMDMessage.Parameter.NonAffected' - StringValues $Path
137+ return
138+ }
139+ # endregion Parse Input
140+
141+ # region Build Replacements table
142+ $currentCount = 1
143+ $replacements = foreach ($command in $commandAsts )
144+ {
145+ $parameter = $command.CommandElements | Where-Object ParameterName -in ' Message' , ' Action'
146+ $paramIndex = $command.CommandElements.IndexOf ($parameter )
147+ $parameterValue = $command.CommandElements [$paramIndex + 1 ]
148+
149+ [PSCustomObject ]@ {
150+ OriginalText = $parameterValue.Value
151+ Text = Get-Text - Value $parameterValue
152+ Inserts = Get-Insert - Value $parameterValue
153+ String = " $ ( $functionName ) .Message$ ( $currentCount ) "
154+ StartOffset = $parameter.Extent.StartOffset
155+ EndOffset = $parameterValue.Extent.EndOffset
156+ OldParameterName = $parameter.ParameterName
157+ NewParameterName = $parameterMapping [$parameter.ParameterName ]
158+ Parameter = $parameter
159+ ParameterValue = $parameterValue
160+ }
161+ $currentCount ++
162+ }
163+ # endregion Build Replacements table
164+
165+ # region Calculate new text body
166+ $fileText = [System.IO.File ]::ReadAllText((Resolve-PSFPath - Path $Path ))
167+ $builder = [System.Text.StringBuilder ]::new()
168+ $index = 0
169+ foreach ($replacement in $replacements )
170+ {
171+ $null = $builder.Append ($fileText.Substring ($index , ($replacement.StartOffset - $index )))
172+ $null = $builder.Append (" -$ ( $replacement.NewParameterName ) '$ ( $replacement.String ) '" )
173+ if ($replacement.Inserts ) { $null = $builder.Append (" $ ( $insertMapping [$replacement.NewParameterName ]) $ ( $replacement.Inserts ) " ) }
174+ $index = $replacement.EndOffset
175+ }
176+ $null = $builder.Append ($fileText.Substring ($index ))
177+ $newDefinition = $builder.ToString ()
178+ $testResult = Read-PSMDScript - ScriptCode ([Scriptblock ]::create($newDefinition ))
179+
180+ if ($testResult.Errors )
181+ {
182+ Stop-PSFFunction - String ' Convert-PSMDMessage.SyntaxError' - StringValues $Path - Target $Path - EnableException $EnableException
183+ return
184+ }
185+ # endregion Calculate new text body
186+
187+ $resolvedOutPath = Resolve-PSFPath - Path $OutPath
188+ $encoding = [System.Text.UTF8Encoding ]::new($true )
189+ $filePath = Join-Path - Path $resolvedOutPath - ChildPath " $functionName .ps1"
190+ [System.IO.File ]::WriteAllText($filePath , $newDefinition , $encoding )
191+ $stringsPath = Join-Path - Path $resolvedOutPath - ChildPath " $functionName .psd1"
192+ $stringsText = @"
193+ @{
194+ $ ( $replacements | Format-String " `t '{0}' = {1} # {2}" - Property String, Text, Inserts | Join-String - Separator " `n " )
195+ }
196+ "@
197+ [System.IO.File ]::WriteAllText($stringsPath , $stringsText , $encoding )
198+ }
199+ }
0 commit comments