Feature: Support Structured Manifest.PrivateData
NovaModuleTools should support the standard PowerShell module manifest field:
The previous implementation attempt established an important PowerShell limitation:
New-ModuleManifest -PrivateData
does not correctly serialize arbitrary nested structured values.
Nested hashtables and arrays can be converted to string representations instead of valid PowerShell data literals.
The PowerShell .psd1 manifest format itself supports nested PrivateData, so implement the feature in NovaModuleTools without relying on New-ModuleManifest to serialize the structured PrivateData value.
Goal
Support configuration such as:
{
"Manifest": {
"PrivateData": {
"ExampleProduct": {
"Enabled": true,
"ApiVersion": "1",
"RetryCount": 3,
"Nested": {
"Mode": "Test"
}
}
}
}
}
and generate a valid PowerShell module manifest containing equivalent native PowerShell data:
PrivateData = @{
ExampleProduct = @{
Enabled = $true
ApiVersion = '1'
RetryCount = 3
Nested = @{
Mode = 'Test'
}
}
}
The generated manifest must successfully load through:
Import-PowerShellDataFile
and validate through:
Architecture
Keep the existing Nova manifest-generation architecture.
Continue using:
for the normal PowerShell module manifest generation.
However, do not pass arbitrary structured consumer data through:
New-ModuleManifest -PrivateData
when that would cause PowerShell to stringify or flatten the data.
Instead, Nova should own serialization of the configured structured Manifest.PrivateData value into valid PowerShell data syntax.
Implement this as a small, generic manifest-data serialization capability.
The implementation must not be specific to any particular consumer or metadata structure.
Schema
Add optional:
to the existing project schema.
It must support arbitrary consumer-defined keys and nested structures.
Existing projects without Manifest.PrivateData must remain unchanged.
Serialization
Implement a generic serializer capable of converting the JSON/PowerShell representation used by Nova into valid PowerShell data-file syntax.
At minimum support the value types that are valid and useful in module manifest private data:
object / hashtable
array
string
boolean
integer
number
null
Examples:
JSON true
→ $true
JSON false
→ $false
JSON null
→ $null
JSON string
→ correctly quoted PowerShell string
JSON number
→ PowerShell numeric literal
JSON array
→ @( ... )
JSON object
→ @{ ... }
Nested combinations must work recursively.
String escaping
String serialization must correctly escape values for PowerShell single-quoted string literals.
For example, an apostrophe must not result in an invalid or executable manifest.
Do not construct executable PowerShell expressions from consumer values.
The output must remain valid PowerShell data suitable for:
Import-PowerShellDataFile
Treat values as data, not code.
Deterministic output
Serialization should be deterministic.
Given the same project.json, Nova should generate the same PrivateData representation.
Preserve configuration/property order where the existing configuration representation provides a stable order. Otherwise use a deterministic ordering.
This avoids unnecessary changes in generated manifests.
Integration with New-ModuleManifest
Inspect the current generated manifest structure and choose the smallest robust integration point.
A suitable approach may be:
- Generate the normal manifest using
New-ModuleManifest.
- Locate the generated top-level
PrivateData assignment structurally and replace that generated block with Nova's serialized PrivateData block.
Do not use fragile line-number assumptions.
Do not use broad regular expressions that can accidentally match nested or commented content.
If a safer implementation is possible by composing the final manifest from the output/template produced by New-ModuleManifest, prefer that.
The important requirement is:
New-ModuleManifest
→ remains responsible for the standard manifest
Nova
→ becomes responsible for serializing configured structured PrivateData
Keep this responsibility isolated so it can easily be removed in the future if PowerShell gains correct structured PrivateData serialization.
Preserve PowerShell-generated PSData
Be careful with the relationship between:
and:
Nova already uses standard manifest properties such as:
Tags
ProjectUri
LicenseUri
ReleaseNotes
Prerelease
RequireLicenseAcceptance
ExternalModuleDependencies
which PowerShell represents under:
PrivateData = @{
PSData = @{
...
}
}
Adding consumer-defined Manifest.PrivateData must not accidentally remove or overwrite this PowerShell/Nova-generated PSData.
The final manifest may therefore need to merge:
PowerShell/Nova-generated PSData
+
consumer-defined Manifest.PrivateData
into one valid top-level PrivateData hashtable.
Define and test the collision behavior.
Do not silently overwrite existing generated metadata.
Tests
Add focused serializer tests covering:
string
string containing apostrophe
empty string
boolean true
boolean false
integer
number
null
empty object
nested object
array
nested arrays/objects
Also test values that could otherwise produce invalid PowerShell syntax.
Build-level integration test
Create or extend a fixture containing:
{
"Manifest": {
"PrivateData": {
"ExampleProduct": {
"Enabled": true,
"ApiVersion": "1",
"RetryCount": 3,
"Nested": {
"Mode": "Test"
},
"Values": [
"one",
"two"
]
}
}
}
}
Build through the normal Nova build pipeline.
Then load the actual generated .psd1:
$manifest = Import-PowerShellDataFile -Path $manifestPath
and verify:
$manifest.PrivateData.ExampleProduct.Enabled |
Should -BeTrue
$manifest.PrivateData.ExampleProduct.ApiVersion |
Should -Be '1'
$manifest.PrivateData.ExampleProduct.RetryCount |
Should -Be 3
$manifest.PrivateData.ExampleProduct.Nested.Mode |
Should -Be 'Test'
$manifest.PrivateData.ExampleProduct.Values |
Should -Be @('one', 'two')
Also run:
against the generated manifest.
PSData regression test
Add a fixture/test where both standard Nova manifest metadata and custom PrivateData exist.
For example:
{
"Manifest": {
"Tags": [
"Example"
],
"PrivateData": {
"ExampleProduct": {
"Enabled": true
}
}
}
}
Verify that the generated manifest contains both:
and:
PrivateData.ExampleProduct.Enabled
with the expected values.
Backward compatibility
Projects without:
must continue through the existing build path without unnecessary manifest rewriting or output changes.
Only projects that explicitly configure structured PrivateData should require the additional serialization behavior.
Documentation
Document:
as arbitrary consumer-defined module metadata.
Explain that Nova serializes this data into the generated module manifest because PowerShell's New-ModuleManifest does not correctly preserve arbitrary nested structured values through its -PrivateData parameter.
Keep this implementation detail concise.
Validation
Run the complete existing NovaModuleTools quality gates.
Requirements:
0 failed tests
no coverage regression
no new static-analysis findings
valid generated manifests
Completion report
Report:
- Schema changes.
- Serializer design.
- Supported value types.
- String escaping behavior.
- How deterministic output is achieved.
- How the generated
PrivateData block is integrated with New-ModuleManifest.
- How existing
PSData is preserved and merged.
- Collision behavior.
- Serializer tests.
- Build-level integration tests.
Import-PowerShellDataFile results.
Test-ModuleManifest results.
- Backward-compatibility results.
- Final test count and coverage.
- Static-analysis and build results.
- Any remaining PowerShell limitations discovered.
Feature: Support Structured
Manifest.PrivateDataNovaModuleTools should support the standard PowerShell module manifest field:
The previous implementation attempt established an important PowerShell limitation:
does not correctly serialize arbitrary nested structured values.
Nested hashtables and arrays can be converted to string representations instead of valid PowerShell data literals.
The PowerShell
.psd1manifest format itself supports nestedPrivateData, so implement the feature in NovaModuleTools without relying onNew-ModuleManifestto serialize the structuredPrivateDatavalue.Goal
Support configuration such as:
{ "Manifest": { "PrivateData": { "ExampleProduct": { "Enabled": true, "ApiVersion": "1", "RetryCount": 3, "Nested": { "Mode": "Test" } } } } }and generate a valid PowerShell module manifest containing equivalent native PowerShell data:
The generated manifest must successfully load through:
Import-PowerShellDataFileand validate through:
Test-ModuleManifestArchitecture
Keep the existing Nova manifest-generation architecture.
Continue using:
New-ModuleManifestfor the normal PowerShell module manifest generation.
However, do not pass arbitrary structured consumer data through:
when that would cause PowerShell to stringify or flatten the data.
Instead, Nova should own serialization of the configured structured
Manifest.PrivateDatavalue into valid PowerShell data syntax.Implement this as a small, generic manifest-data serialization capability.
The implementation must not be specific to any particular consumer or metadata structure.
Schema
Add optional:
to the existing project schema.
It must support arbitrary consumer-defined keys and nested structures.
Existing projects without
Manifest.PrivateDatamust remain unchanged.Serialization
Implement a generic serializer capable of converting the JSON/PowerShell representation used by Nova into valid PowerShell data-file syntax.
At minimum support the value types that are valid and useful in module manifest private data:
Examples:
Nested combinations must work recursively.
String escaping
String serialization must correctly escape values for PowerShell single-quoted string literals.
For example, an apostrophe must not result in an invalid or executable manifest.
Do not construct executable PowerShell expressions from consumer values.
The output must remain valid PowerShell data suitable for:
Import-PowerShellDataFileTreat values as data, not code.
Deterministic output
Serialization should be deterministic.
Given the same
project.json, Nova should generate the samePrivateDatarepresentation.Preserve configuration/property order where the existing configuration representation provides a stable order. Otherwise use a deterministic ordering.
This avoids unnecessary changes in generated manifests.
Integration with
New-ModuleManifestInspect the current generated manifest structure and choose the smallest robust integration point.
A suitable approach may be:
New-ModuleManifest.PrivateDataassignment structurally and replace that generated block with Nova's serializedPrivateDatablock.Do not use fragile line-number assumptions.
Do not use broad regular expressions that can accidentally match nested or commented content.
If a safer implementation is possible by composing the final manifest from the output/template produced by
New-ModuleManifest, prefer that.The important requirement is:
Keep this responsibility isolated so it can easily be removed in the future if PowerShell gains correct structured
PrivateDataserialization.Preserve PowerShell-generated PSData
Be careful with the relationship between:
and:
Nova already uses standard manifest properties such as:
which PowerShell represents under:
Adding consumer-defined
Manifest.PrivateDatamust not accidentally remove or overwrite this PowerShell/Nova-generatedPSData.The final manifest may therefore need to merge:
into one valid top-level
PrivateDatahashtable.Define and test the collision behavior.
Do not silently overwrite existing generated metadata.
Tests
Add focused serializer tests covering:
Also test values that could otherwise produce invalid PowerShell syntax.
Build-level integration test
Create or extend a fixture containing:
{ "Manifest": { "PrivateData": { "ExampleProduct": { "Enabled": true, "ApiVersion": "1", "RetryCount": 3, "Nested": { "Mode": "Test" }, "Values": [ "one", "two" ] } } } }Build through the normal Nova build pipeline.
Then load the actual generated
.psd1:and verify:
Also run:
Test-ModuleManifestagainst the generated manifest.
PSData regression test
Add a fixture/test where both standard Nova manifest metadata and custom
PrivateDataexist.For example:
{ "Manifest": { "Tags": [ "Example" ], "PrivateData": { "ExampleProduct": { "Enabled": true } } } }Verify that the generated manifest contains both:
and:
with the expected values.
Backward compatibility
Projects without:
must continue through the existing build path without unnecessary manifest rewriting or output changes.
Only projects that explicitly configure structured
PrivateDatashould require the additional serialization behavior.Documentation
Document:
as arbitrary consumer-defined module metadata.
Explain that Nova serializes this data into the generated module manifest because PowerShell's
New-ModuleManifestdoes not correctly preserve arbitrary nested structured values through its-PrivateDataparameter.Keep this implementation detail concise.
Validation
Run the complete existing NovaModuleTools quality gates.
Requirements:
Completion report
Report:
PrivateDatablock is integrated withNew-ModuleManifest.PSDatais preserved and merged.Import-PowerShellDataFileresults.Test-ModuleManifestresults.