Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 24 additions & 1 deletion Plaster/Plaster.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,9 @@ try {
[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$PlasterVersion = (Test-ModuleManifest -Path (Join-Path $PSScriptRoot 'Plaster.psd1')).Version

[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$JsonSchemaPath = Join-Path $PSScriptRoot "Schema\plaster-manifest-v2.json"

[System.Diagnostics.CodeAnalysis.SuppressMessage('PSUseDeclaredVarsMoreThanAssigments', '')]
$LatestSupportedSchemaVersion = [System.Version]'1.2'

Expand Down Expand Up @@ -136,6 +139,26 @@ if (-not $script:XmlSchemaValidationSupported) {
# Module logging configuration
$script:LogLevel = if ($env:PLASTER_LOG_LEVEL) { $env:PLASTER_LOG_LEVEL } else { 'Information' }

# Global variables and constants for Plaster 2.0

# Enhanced $TargetNamespace definition with proper scoping
if (-not (Get-Variable -Name 'TargetNamespace' -Scope Script -ErrorAction SilentlyContinue)) {
Set-Variable -Name 'TargetNamespace' -Value 'http://www.microsoft.com/schemas/PowerShell/Plaster/v1' -Scope Script -Option ReadOnly
}

# Enhanced $DefaultEncoding definition
if (-not (Get-Variable -Name 'DefaultEncoding' -Scope Script -ErrorAction SilentlyContinue)) {
Set-Variable -Name 'DefaultEncoding' -Value 'UTF8-NoBOM' -Scope Script -Option ReadOnly
}

# JSON Schema version for new manifests
if (-not (Get-Variable -Name 'JsonSchemaVersion' -Scope Script -ErrorAction SilentlyContinue)) {
Set-Variable -Name 'JsonSchemaVersion' -Value '2.0' -Scope Script -Option ReadOnly
}

# Export the variables that need to be available globally
Export-ModuleMember -Variable @('TargetNamespace', 'DefaultEncoding', 'JsonSchemaVersion')

# Module cleanup on removal
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
Write-PlasterLog -Level Information -Message "Plaster module is being removed"
Expand All @@ -149,4 +172,4 @@ $MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
}

# Module initialization complete
Write-PlasterLog -Level Information -Message "Plaster v$PlasterVersion module loaded successfully (PowerShell $($PSVersionTable.PSVersion))"
Write-PlasterLog -Level Information -Message "Plaster v$PlasterVersion module loaded successfully (PowerShell $($PSVersionTable.PSVersion))"
128 changes: 128 additions & 0 deletions Plaster/Private/ConvertFrom-JsonContentAction.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
function ConvertFrom-JsonContentAction {
[CmdletBinding()]
[OutputType([System.Xml.XmlElement])]
param(
[Parameter(Mandatory)]
[object]$Action,

[Parameter(Mandatory)]
[System.Xml.XmlDocument]$XmlDocument
)

switch ($Action.type) {
'message' {
$element = $XmlDocument.CreateElement('message', $TargetNamespace)
$element.InnerText = $Action.text

if ($Action.noNewline) {
$element.SetAttribute('nonewline', 'true')
}
}
'file' {
$element = $XmlDocument.CreateElement('file', $TargetNamespace)
$element.SetAttribute('source', $Action.source)
$element.SetAttribute('destination', $Action.destination)

if ($Action.encoding) {
$element.SetAttribute('encoding', $Action.encoding)
}

if ($Action.openInEditor) {
$element.SetAttribute('openInEditor', 'true')
}
}
'templateFile' {
$element = $XmlDocument.CreateElement('templateFile', $TargetNamespace)
$element.SetAttribute('source', $Action.source)
$element.SetAttribute('destination', $Action.destination)

if ($Action.encoding) {
$element.SetAttribute('encoding', $Action.encoding)
}

if ($Action.openInEditor) {
$element.SetAttribute('openInEditor', 'true')
}
}
'directory' {
$element = $XmlDocument.CreateElement('file', $TargetNamespace)
$element.SetAttribute('source', '')
$element.SetAttribute('destination', $Action.destination)
}
'newModuleManifest' {
$element = $XmlDocument.CreateElement('newModuleManifest', $TargetNamespace)
$element.SetAttribute('destination', $Action.destination)

$manifestProperties = @('moduleVersion', 'rootModule', 'author', 'companyName', 'description', 'powerShellVersion', 'copyright', 'encoding')
foreach ($property in $manifestProperties) {
if ($Action.PSObject.Properties[$property]) {
$element.SetAttribute($property, $Action.$property)
}
}

if ($Action.openInEditor) {
$element.SetAttribute('openInEditor', 'true')
}
}
'modify' {
$element = $XmlDocument.CreateElement('modify', $TargetNamespace)
$element.SetAttribute('path', $Action.path)

if ($Action.encoding) {
$element.SetAttribute('encoding', $Action.encoding)
}

# Add modifications
foreach ($modification in $Action.modifications) {
if ($modification.type -eq 'replace') {
$replaceElement = $XmlDocument.CreateElement('replace', $TargetNamespace)

$originalElement = $XmlDocument.CreateElement('original', $TargetNamespace)
$originalElement.InnerText = $modification.search
if ($modification.isRegex) {
$originalElement.SetAttribute('expand', 'true')
}
$replaceElement.AppendChild($originalElement)

$substituteElement = $XmlDocument.CreateElement('substitute', $TargetNamespace)
$substituteElement.InnerText = $modification.replace
$substituteElement.SetAttribute('expand', 'true')
$replaceElement.AppendChild($substituteElement)

if ($modification.condition) {
$replaceElement.SetAttribute('condition', $modification.condition)
}

$element.AppendChild($replaceElement)
}
}
}
'requireModule' {
$element = $XmlDocument.CreateElement('requireModule', $TargetNamespace)
$element.SetAttribute('name', $Action.name)

$moduleProperties = @('minimumVersion', 'maximumVersion', 'requiredVersion', 'message')
foreach ($property in $moduleProperties) {
if ($Action.PSObject.Properties[$property]) {
$element.SetAttribute($property, $Action.$property)
}
}
}
'execute' {
# Execute action doesn't have direct XML equivalent, convert to message with warning
$element = $XmlDocument.CreateElement('message', $TargetNamespace)
$element.InnerText = "Warning: Execute action not supported in XML format. Script: $($Action.script)"
Write-PlasterLog -Level Warning -Message "Execute action converted to message - not supported in XML format"
}
default {
throw "Unknown action type: $($Action.type)"
}
}

# Add condition if present
if ($Action.condition) {
$element.SetAttribute('condition', $Action.condition)
}

return $element
}
126 changes: 126 additions & 0 deletions Plaster/Private/ConvertFrom-JsonManifest.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
function ConvertFrom-JsonManifest {
[CmdletBinding()]
[OutputType([System.Xml.XmlDocument])]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string]$JsonContent,

[Parameter()]
[switch]$Validate = $true
)

begin {
Write-PlasterLog -Level Debug -Message "Converting JSON manifest to internal format"
}

process {
try {
# Validate JSON if requested
if ($Validate) {
$isValid = Test-JsonManifest -JsonContent $JsonContent -Detailed
if (-not $isValid) {
throw "JSON manifest validation failed"
}
}

# Parse JSON
$jsonObject = $JsonContent | ConvertFrom-Json

# Create XML document
$xmlDoc = New-Object System.Xml.XmlDocument
$xmlDoc.LoadXml('<?xml version="1.0" encoding="utf-8"?><plasterManifest xmlns="http://www.microsoft.com/schemas/PowerShell/Plaster/v1"></plasterManifest>')

$manifest = $xmlDoc.DocumentElement
$manifest.SetAttribute('schemaVersion', '1.2') # Use XML schema version for compatibility

if ($jsonObject.metadata.templateType) {
$manifest.SetAttribute('templateType', $jsonObject.metadata.templateType)
}

# Add metadata
$metadataElement = $xmlDoc.CreateElement('metadata', $TargetNamespace)
$manifest.AppendChild($metadataElement)

# Add metadata properties
$metadataProperties = @('name', 'id', 'version', 'title', 'description', 'author', 'tags')
foreach ($property in $metadataProperties) {
if ($jsonObject.metadata.PSObject.Properties[$property]) {
$element = $xmlDoc.CreateElement($property, $TargetNamespace)
$value = $jsonObject.metadata.$property

if ($property -eq 'tags' -and $value -is [array]) {
$element.InnerText = $value -join ', '
} else {
$element.InnerText = $value
}
$metadataElement.AppendChild($element)
}
}

# Add parameters
$parametersElement = $xmlDoc.CreateElement('parameters', $TargetNamespace)
$manifest.AppendChild($parametersElement)

if ($jsonObject.parameters) {
foreach ($param in $jsonObject.parameters) {
$paramElement = $xmlDoc.CreateElement('parameter', $TargetNamespace)
$paramElement.SetAttribute('name', $param.name)
$paramElement.SetAttribute('type', $param.type)

if ($param.prompt) {
$paramElement.SetAttribute('prompt', $param.prompt)
}

if ($param.default) {
if ($param.default -is [array]) {
$paramElement.SetAttribute('default', ($param.default -join ','))
} else {
$paramElement.SetAttribute('default', $param.default)
}
}

if ($param.condition) {
$paramElement.SetAttribute('condition', $param.condition)
}

if ($param.store) {
$paramElement.SetAttribute('store', $param.store)
}

# Add choices for choice/multichoice parameters
if ($param.choices) {
foreach ($choice in $param.choices) {
$choiceElement = $xmlDoc.CreateElement('choice', $TargetNamespace)
$choiceElement.SetAttribute('label', $choice.label)
$choiceElement.SetAttribute('value', $choice.value)

if ($choice.help) {
$choiceElement.SetAttribute('help', $choice.help)
}

$paramElement.AppendChild($choiceElement)
}
}

$parametersElement.AppendChild($paramElement)
}
}

# Add content
$contentElement = $xmlDoc.CreateElement('content', $TargetNamespace)
$manifest.AppendChild($contentElement)

foreach ($action in $jsonObject.content) {
$actionElement = ConvertFrom-JsonContentAction -Action $action -XmlDocument $xmlDoc
$contentElement.AppendChild($actionElement)
}

Write-PlasterLog -Level Debug -Message "JSON to XML conversion completed successfully"
return $xmlDoc
} catch {
$errorMessage = "Failed to convert JSON manifest: $($_.Exception.Message)"
Write-PlasterLog -Level Error -Message $errorMessage
throw $_
}
}
}
Loading
Loading