|
29 | 29 | [Parameter(ValueFromPipeline)] |
30 | 30 | [PSObject] |
31 | 31 | $InputObject, |
32 | | - |
| 32 | + |
33 | 33 | # The Command that will be run. |
34 | 34 | [ValidateScript({ |
35 | 35 | $PotentialCommand = $_ |
|
41 | 41 | $PotentialCommand.GetType().Name -in 'AttributeAST', 'TypeExpressionAST','InvokeMemberExpressionAst') { |
42 | 42 | return $true |
43 | 43 | } |
44 | | - |
| 44 | + |
45 | 45 | return $false |
46 | 46 | })] |
47 | 47 | [Alias('ScriptBlock','CommandName', 'CommandInfo', 'AttributeSyntaxTree','TypeConstraint')] |
48 | 48 | [Parameter(Position=0)] |
49 | 49 | [PSObject] |
50 | 50 | $Command, |
51 | | - |
| 51 | + |
52 | 52 | # A collection of named parameters. These will be directly passed to the underlying script. |
53 | 53 | [Alias('Parameters')] |
54 | 54 | [Collections.IDictionary] |
|
61 | 61 | $ArgumentList = @(), |
62 | 62 |
|
63 | 63 | # If this is not set, when a transpiler's parameters do not take a [ScriptBlock], ScriptBlock values will be evaluated. |
64 | | - # This can be a very useful capability, because it can enable dynamic transpilation. |
65 | | - # If this is set, will make ScriptBlockAst values will be run within data language, which significantly limits their capabilities. |
| 64 | + # This can be a very useful capability, because it can enable dynamic transpilation. |
| 65 | + # If this is set, will make ScriptBlockAst values will be run within data language, which significantly limits their capabilities. |
66 | 66 | [switch] |
67 | 67 | $SafeScriptBlockAttributeEvaluation |
68 | 68 | ) |
|
73 | 73 | # If we didn't, do that now. |
74 | 74 | $script:TypeAcceleratorsList = [PSObject].Assembly.GetType("System.Management.Automation.TypeAccelerators")::Get.Keys |
75 | 75 | } |
76 | | - |
| 76 | + |
77 | 77 | function TypeConstraintToArguments ( |
78 | 78 | [Parameter(ValueFromPipeline)] |
79 | 79 | $TypeName |
|
85 | 85 | process { |
86 | 86 |
|
87 | 87 | if ($TypeName.IsGeneric) { |
88 | | - $TypeNameParams[$typeName.Name] = |
| 88 | + $TypeNameParams[$typeName.Name] = |
89 | 89 | $typeName.GenericArguments | |
90 | 90 | TypeConstraintToArguments |
91 | 91 | } elseif (-not $TypeName.IsArray) { |
|
96 | 96 | [PSCustomObject]@{ |
97 | 97 | ArgumentList = $TypeNameArgs |
98 | 98 | Parameter = $TypeNameParams |
99 | | - } |
| 99 | + } |
100 | 100 | } |
101 | 101 | } |
102 | 102 |
|
|
143 | 143 | return |
144 | 144 | } |
145 | 145 |
|
146 | | - if ($TranspilerErrors) { |
147 | | - $failedMessage = @( |
| 146 | + if ($TranspilerErrors) { |
| 147 | + $failedMessage = @( |
148 | 148 | "$($command.Source): " + "$($TranspilerErrors.Count) error(s)" |
149 | 149 | if ($transpilerWarnings) { |
150 | 150 | "$($TranspilerWarnings.Count) warning(s)" |
|
156 | 156 | Errors = $TranspilerErrors |
157 | 157 | Warnings = $TranspilerWarnings |
158 | 158 | Command = $Command |
159 | | - Parameters = $InvokePipeScriptParameters |
| 159 | + Parameters = $InvokePipeScriptParameters |
160 | 160 | } |
161 | | - ) |
| 161 | + ) |
162 | 162 | } |
163 | 163 |
|
164 | | - # If it could not be transpiled into a [ScriptBlock] or [ScriptBlock[]] |
165 | | - if ($TranspilerErrors -or |
| 164 | + # If it could not be transpiled into a [ScriptBlock] or [ScriptBlock[]] |
| 165 | + if ($TranspilerErrors -or |
166 | 166 | ($transpiledScriptBlock -isnot [ScriptBlock] -and -not ($TranspiledScriptBlock -as [scriptblock[]]))) { |
167 | 167 | # error out. |
168 | 168 | Write-Error "Command {$command} could not be transpiled into [ScriptBlock]s" |
169 | 169 | return |
170 | 170 | } |
171 | | - |
| 171 | + |
172 | 172 | # Walk thru each resulting transpiled ScriptBlock |
173 | 173 | foreach ($transpiledScript in $transpiledScriptBlock) { |
174 | | - |
| 174 | + |
175 | 175 | # If we had an input object |
176 | 176 | if ($InputObject) { |
177 | 177 | # pipe it to the transpiled script. |
|
182 | 182 | } |
183 | 183 | } |
184 | 184 | } |
185 | | - |
186 | | - |
| 185 | + |
| 186 | + |
187 | 187 | # If the command is a string, |
188 | 188 | elseif ($Command -is [string]) |
189 | 189 | { |
|
214 | 214 |
|
215 | 215 |
|
216 | 216 | # If the command is a ```[Management.Automation.CommandInfo]``` |
217 | | - elseif ($command -is [Management.Automation.CommandInfo]) |
| 217 | + elseif ($command -is [Management.Automation.CommandInfo]) |
218 | 218 | { |
219 | 219 | # Determine if the Command is a SourceGenerator. |
220 | 220 | $IsSourceGenerator = '\.ps1{0,1}\.(?<ext>[^.]+$)' # if it matches the regex designating a SourceGenerator |
221 | | - |
| 221 | + |
222 | 222 | # If the command was not a source generator |
223 | 223 | if ($Command.Source -notmatch $IsSourceGenerator ) { |
224 | 224 | # invoke it normally. |
|
230 | 230 | } |
231 | 231 |
|
232 | 232 | # If the command was a source generator |
233 | | - else { |
234 | | - # predetermine the output path |
| 233 | + else { |
| 234 | + # predetermine the output path |
235 | 235 | $outputPath = $($Command.Source -replace $IsSourceGenerator, '.${ext}') |
236 | 236 | # and attempt to find a transpiler. |
237 | 237 | $foundTranspiler = Get-Transpiler -CouldPipe $Command -ValidateInput $Command -ErrorAction Ignore |
238 | | - |
| 238 | + |
239 | 239 | $ParamsAndArgs = [Ordered]@{Parameter=$Parameter;ArgumentList = $ArgumentList} |
240 | 240 | $transpilerErrors = @() |
241 | 241 | $transpilerWarnings = @() |
|
256 | 256 | SourcePath = $command.Source |
257 | 257 | }) |
258 | 258 |
|
259 | | - $transpilerOutput = $command | |
| 259 | + $transpilerOutput = $command | |
260 | 260 | & $ft.ExtensionCommand @ErrorsAndWarnings @ParamsAndArgs |
261 | | - |
| 261 | + |
262 | 262 | $null = |
263 | 263 | New-Event -SourceIdentifier 'PipeScript.SourceGenerator.Stop' -MessageData ([PSCustomObject][Ordered]@{ |
264 | 264 | Transpiler = $ft.ExtensionCommand |
265 | 265 | TranspilerOutput = $transpilerOutput |
266 | 266 | SourcePath = $command.Source |
267 | 267 | Errors = $TranspilerErrors |
268 | | - Warnings = $TranspilerWarnings |
| 268 | + Warnings = $TranspilerWarnings |
269 | 269 | }) |
270 | | - |
271 | | - $transpilerOutput = |
| 270 | + |
| 271 | + $transpilerOutput = |
272 | 272 | # If the transpiler returned a [ScriptBlock] |
273 | 273 | if ($transpilerOutput -is [Scriptblock]) { |
274 | 274 | # recursively invoke. |
|
278 | 278 | # otherwise, return the output of the transpiler. |
279 | 279 | $transpilerOutput |
280 | 280 | } |
281 | | - |
| 281 | + |
282 | 282 | # If the transpiler had output, |
283 | 283 | if ($transpilerOutput) { |
284 | | - $transpilerOutput # use that output |
| 284 | + $transpilerOutput # use that output |
285 | 285 | break # and stop processing additional transpilers. |
286 | 286 | } |
287 | | - } |
| 287 | + } |
288 | 288 | } else { |
289 | 289 | # If we did not find a transpiler, treat the source code as PipeScript/PowerShell. |
290 | 290 | $fileScriptBlock = |
|
297 | 297 | [scriptblock]::Create($fileText) | .>Pipescript @ErrorsAndWarnings |
298 | 298 | } catch { |
299 | 299 | $ex = $_ |
300 | | - Write-Error "[CommandInfo] -Command could not be made into a [ScriptBlock]: $ex" |
| 300 | + Write-Error "[CommandInfo] -Command could not be made into a [ScriptBlock]: $ex" |
301 | 301 | } |
302 | | - } else { |
| 302 | + } else { |
303 | 303 | $Command.ScriptBlock | .>Pipescript @ErrorsAndWarnings |
304 | 304 | } |
305 | 305 |
|
|
309 | 309 | } else { |
310 | 310 | $InvokePipeScriptParameters.Command = $fileScriptBlock |
311 | 311 | Invoke-PipeScript @InvokePipeScriptParameters |
312 | | - } |
| 312 | + } |
313 | 313 | } |
314 | 314 |
|
315 | 315 | # Now that the source generator has finished running, we can Pop-Location. |
316 | 316 | Pop-Location |
317 | | - |
318 | | - if ($TranspilerErrors) { |
319 | | - $failedMessage = @( |
| 317 | + |
| 318 | + if ($TranspilerErrors) { |
| 319 | + $failedMessage = @( |
320 | 320 | "$($command.Source): " + "$($TranspilerErrors.Count) error(s)" |
321 | 321 | if ($transpilerWarnings) { |
322 | 322 | "$($TranspilerWarnings.Count) warning(s)" |
|
328 | 328 | Errors = $TranspilerErrors |
329 | 329 | Warnings = $TranspilerWarnings |
330 | 330 | Command = $Command |
331 | | - Parameters = $InvokePipeScriptParameters |
| 331 | + Parameters = $InvokePipeScriptParameters |
332 | 332 | } |
333 | 333 | ) |
334 | 334 | return |
|
361 | 361 | # If it was all files. |
362 | 362 | if (-not ($allFiles -ne $true)) { |
363 | 363 | $pipeScriptOutput # return them |
364 | | - } else { |
365 | | - # Otherwise, join the content by a space |
366 | | - $pipescriptOutput -join ' ' | |
| 364 | + } else { |
| 365 | + # Otherwise, join the content by a space |
| 366 | + $pipescriptOutput -join ' ' | |
367 | 367 | Set-Content -LiteralPath $outputPath # save it to the output path |
368 | 368 | Get-Item -LiteralPath $outputPath # and return the file. |
369 | 369 | } |
370 | 370 | } |
371 | 371 |
|
372 | 372 | # If the source generator returned any other type |
373 | 373 | else { |
374 | | - |
| 374 | + |
375 | 375 | $pipescriptOutput | |
376 | 376 | Set-Content -LiteralPath $outputPath # save it to the output path |
377 | 377 | Get-Item -LiteralPath $outputPath # and return the file. |
378 | 378 | } |
379 | 379 | return |
380 | | - } |
| 380 | + } |
381 | 381 | } |
382 | 382 |
|
383 | 383 | # If the -Command is an ```[Management.Automation.Language.AttributeAST]``` |
|
394 | 394 |
|
395 | 395 | # Create a collection for stringified arguments. |
396 | 396 | $stringArguments = @() |
397 | | - |
| 397 | + |
398 | 398 | # Get the name of the transpiler. |
399 | | - $transpilerStepName = |
| 399 | + $transpilerStepName = |
400 | 400 | if ($AttributeSyntaxTree.TypeName.IsGeneric) { |
401 | 401 | $AttributeSyntaxTree.TypeName.TypeName.Name |
402 | 402 | } else { |
403 | 403 | $AttributeSyntaxTree.TypeName.Name |
404 | 404 | } |
405 | 405 |
|
406 | 406 | # See if we could find a transpiler that fits. |
407 | | - $foundTranspiler = |
| 407 | + $foundTranspiler = |
408 | 408 | if ($InputObject) { |
409 | | - Get-Transpiler -TranspilerName "$transpilerStepName" -CouldPipe $InputObject | |
| 409 | + Get-Transpiler -TranspilerName "$transpilerStepName" -CouldPipe $InputObject | |
410 | 410 | Select-Object -ExpandProperty ExtensionCommand |
411 | 411 | } else { |
412 | 412 | Get-Transpiler -TranspilerName "$transpilerStepName" |
413 | 413 | } |
414 | | - |
| 414 | + |
415 | 415 | # Collect all of the arguments of the attribute, in the order they were specified. |
416 | 416 | $argsInOrder = @( |
417 | 417 | @($AttributeSyntaxTree.PositionalArguments) + @($AttributeSyntaxTree.NamedArguments) | Sort-Object { $_.Extent.StartOffset}) |
418 | 418 |
|
419 | | - |
| 419 | + |
420 | 420 | # Now we need to map each of those arguments into either named or positional arguments. |
421 | 421 | foreach ($attributeArg in $argsInOrder) { |
422 | | - # Named arguments are fairly straightforward: |
| 422 | + # Named arguments are fairly straightforward: |
423 | 423 | if ($attributeArg -is [Management.Automation.Language.NamedAttributeArgumentAst]) { |
424 | 424 | $argName = $attributeArg.ArgumentName |
425 | 425 | $argAst = $attributeArg.Argument |
|
432 | 432 | # Turn it into a [ScriptBlock] |
433 | 433 | $argScriptBlock = [ScriptBlock]::Create($argAst.Extent.ToString() -replace '^\{' -replace '\}$') |
434 | 434 | # If the Transpiler had a parameter that took a [ScriptBlock] or [ScriptBlock[]] |
435 | | - if ($foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock] -or |
| 435 | + if ($foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock] -or |
436 | 436 | $foundTranspiler.parameters.$argName.ParameterType -eq [ScriptBlock[]]) { |
437 | 437 | $argScriptBlock # pass the [ScriptBlock] directly. |
438 | 438 | } |
|
449 | 449 | } |
450 | 450 | elseif ($argAst.Value) { |
451 | 451 | $argAst.Value.ToString() |
452 | | - } |
| 452 | + } |
453 | 453 | else { |
454 | 454 | $argAst.Extent.ToString() |
455 | | - } |
| 455 | + } |
456 | 456 | } else { |
457 | 457 | # If we are a positional parameter, for the moment: |
458 | 458 | if ($parameter.Count) { |
|
466 | 466 | # We _should_ get more intelligent over time here. |
467 | 467 | # See [the GitHub Issue](https://github.com/StartAutomating/PipeScript/issues/70) for more details. |
468 | 468 | } |
469 | | - } |
| 469 | + } |
470 | 470 |
|
471 | 471 | # If we have found a transpiler, run it. |
472 | 472 | if ($foundTranspiler) { |
|
480 | 480 | } |
481 | 481 | elseif ($( |
482 | 482 | $realCommandExists = $ExecutionContext.SessionState.InvokeCommand.GetCommand(($transpilerStepName -replace '_','-'), 'All') |
483 | | - $realCommandExists |
| 483 | + $realCommandExists |
484 | 484 | )) { |
485 | 485 | if ($inputObject) { |
486 | 486 | $canPipe = foreach ($param in $realCommandExists.Parameters.Values) { |
|
497 | 497 | } |
498 | 498 | } else { |
499 | 499 | & $realCommandExists @Parameter @ArgumentList |
500 | | - } |
| 500 | + } |
501 | 501 | } |
502 | 502 | elseif ($script:TypeAcceleratorsList -notcontains $transpilerStepName -and $transpilerStepName -notin 'Ordered') { |
503 | 503 | $psCmdlet.WriteError( |
|
508 | 508 | $command |
509 | 509 | ) |
510 | 510 | ) |
511 | | - |
| 511 | + |
512 | 512 | return |
513 | 513 | } |
514 | 514 |
|
|
525 | 525 | } |
526 | 526 | # Next, make sure it's not ```[ordered]``` (return it if is) |
527 | 527 | if ($command.TypeName.Name -eq 'ordered') { return} |
528 | | - |
| 528 | + |
529 | 529 | # Determine the name of the transpiler step. |
530 | | - $transpilerStepName = |
| 530 | + $transpilerStepName = |
531 | 531 | # If the typename is generic, the generic arguments will be treated as positional arguments |
532 | | - if ($command.TypeName.IsGeneric) { |
| 532 | + if ($command.TypeName.IsGeneric) { |
533 | 533 | $command.TypeName.TypeName.Name # and the name will the root typename. |
534 | 534 | } else { |
535 | 535 | $command.TypeName.Name # Otherwise, the step will have no positional arguments, and it's name is the typename. |
536 | 536 | } |
537 | | - |
538 | | - |
| 537 | + |
| 538 | + |
539 | 539 | # Attempt to find the transpiler |
540 | | - $foundTranspiler = |
| 540 | + $foundTranspiler = |
541 | 541 | if ($InputObject) { |
542 | 542 | # If we had an -InputObject, be sure we can pipe it in. |
543 | 543 | Get-Transpiler -TranspilerName "$transpilerStepName" | Where-Object { $_ | Get-Transpiler -CouldPipe $InputObject } |
|
548 | 548 |
|
549 | 549 | # If the TypeName was generic, treat the generic parameters as arguments |
550 | 550 | # ```[t[n,a]]``` would pass two positional parameters, n and a. |
551 | | - # ```[t[a[b,c],d[e]]]``` would pass two named parameters @{a='b,'c';d='e'} |
| 551 | + # ```[t[a[b,c],d[e]]]``` would pass two named parameters @{a='b,'c';d='e'} |
552 | 552 | if ($TypeConstraint.TypeName.IsGeneric) { |
553 | | - $TypeConstraint.TypeName.GenericArguments | |
554 | | - TypeConstraintToArguments | |
| 553 | + $TypeConstraint.TypeName.GenericArguments | |
| 554 | + TypeConstraintToArguments | |
555 | 555 | ForEach-Object { |
556 | 556 | if ($_.ArgumentList) { |
557 | 557 | $ArgumentList += $_.ArgumentList |
|
0 commit comments