treeMerge Transform

Strict deep merge that errors on type conflicts. Use this when you want to catch configuration mistakes early.

Usage

Using in-memory data (recommended):

node:
  base:
    from_file: base.json
  override:
    from_file: override.json
  config:
    to_file: config.json
    transform:
      treeMerge:
        - "{{.base}}"
        - "{{.override}}"

Using file paths (via .meta):

node:
  base:
    from_file: base.json
  override:
    from_file: override.json
  config:
    to_file: config.json
    transform:
      treeMerge:
        - "{{.meta.base.path}}"
        - "{{.meta.override.path}}"

Behavior

Scenario Result
Objects Keys are merged recursively
Arrays Later array replaces earlier array
Scalars Later value replaces earlier value
Type conflicts Error with detailed message

Type Conflict Detection

treeMerge will error when types don't match:

base.json:

{
  "settings": {
    "debug": "false"
  }
}

override.json:

{
  "settings": {
    "debug": true
  }
}

Error:

type conflict at 'settings.debug': cannot merge string with bool

This catches common mistakes like:

  • String "false" vs boolean false
  • Number 8080 vs string "8080"
  • Scalar vs object/array
  • Object vs array

Examples

Basic Merge (No Conflicts)

base.yaml:

name: app
database:
  host: localhost

override.yaml:

database:
  port: 5432
  ssl: true

Result:

name: app
database:
  host: localhost
  port: 5432
  ssl: true

Type Conflict Error

base.json:

{"value": "string"}

override.json:

{"value": {"nested": "object"}}

Error:

node 'config': type conflict at 'value': cannot merge string with map

Multiple Files

transform:
  treeMerge:
    - "{{.schema}}"
    - "{{.defaults}}"
    - "{{.environment}}"

All inputs must have compatible types at each path.

CLI Usage

Run treeMerge directly from the command line:

panconf transform treeMerge base.json override.yaml

Output is YAML to stdout. Exits with error if type conflicts are detected.

Error Messages

treeMerge provides detailed error messages showing:

  • The path where the conflict occurred (e.g., settings.database.port)
  • The conflicting types