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