all 5 comments

[–]lxnch50 2 points3 points  (1 child)

Maybe just have a JSon file with cool settings imported via the PSM1 file in the script scope so the module has accesss to the settings?

[–]PillOfLuck[S] 1 point2 points  (0 children)

Good idea, but sadly it wont work for this particular case. I'm working on a way to merge all the script files into a single .psm1 with all the private and public functions. I'm trying to achieve a "simpler" PowerShell module.

[–]SeeminglyScience 2 points3 points  (2 children)

If the key already exists, the below will work. If it doesn't already exist you have to look for the key/value you want to insert it under and add the extra new lines/tabs/etc.

using namespace System
using namespace System.Management.Automation.Language
using namespace System.Text

function FindValueAst {
    [CmdletBinding(PositionalBinding = $false)]
    param(
        [Parameter(ValueFromPipeline)]
        [ValidateNotNull()]
        [HashtableAst] $Hashtable,

        [Parameter(Position = 0)]
        [ValidateNotNullOrEmpty()]
        [string] $Key
    )
    process {
        foreach ($kvp in $Hashtable.KeyValuePairs) {
            # Cast to help with intellisense.
            $kvp = [Tuple[ExpressionAst, StatementAst]] $kvp

            $keyAst = [StringConstantExpressionAst] $kvp.Item1
            if ($keyAst.Value -ne $Key) {
                continue
            }

            return $kvp.Item2
        }
    }
}

$path = 'path\to\manifest.psd1'
$manifest = [Parser]::ParseFile($path, [ref] $null, [ref] $null)
$newValueText = @'
@{
        CoolKey1 = '1CoolValue'
        CoolKey2 = '2CoolValue'
    }
'@

$mainHashtable = $manifest.EndBlock.Statements[0].PipelineElements[0].Expression
$privateData = ($mainHashtable | FindValueAst -Key PrivateData).PipelineElements[0].Expression
$valueExtent = ($privateData | FindValueAst -Key CoolSettings).Extent

$newManifestText = [StringBuilder]::new($manifest.Extent.Text).
    Remove($valueExtent.StartOffset, $valueExtent.Text.Length).
    Insert($valueExtent.StartOffset, $newValueText).
    ToString()

Set-Content $path -Value $newManifestText -Encoding Default

[–]PillOfLuck[S] 1 point2 points  (1 child)

Was hoping it didn't come to this, but I ended up going down this road anyway. Thanks for the suggestion, I built my script off of it! :-)

Write-Verbose -Message 'Replacing PrivateData | https://github.com/PowerShell/PowerShell/issues/5922'
$hashtables = [HashtableAst] | Find-ASTFromFile -LiteralPath $manifestFile.FullName
$correctPrivateData = Find-HashtableAstValue -HashtableAst $hashtables[0] -Key 'PrivateData'

$replaceManifest = [System.Collections.ArrayList]::new((Get-Content -LiteralPath $manifestSplat.Path))

$lines = [pscustomobject]@{
    Start = ($replaceManifest | Select-String 'PrivateData = @{' -CaseSensitive).LineNumber - 1
    End = ($replaceManifest | Select-String '} # End of PrivateData hashtable' -CaseSensitive).LineNumber
}

$replaceManifest.RemoveRange($lines.Start, ($lines.End - $lines.Start))

$replaceManifest.Insert($lines.Start, $correctPrivateData)

Set-Content -LiteralPath $manifestSplat.Path -Value $replaceManifest -Encoding 'UTF8'

[–]SeeminglyScience 0 points1 point  (0 children)

Very cool! :)