Skip to content

JSON Merge

The mergeJson function performs a deep merge of two JavaScript objects using the defu library. It’s used whenever Xtarterize needs to modify JSON configuration files like tsconfig.json, biome.json, or .vscode/settings.json.

import { mergeJson } from '@xtarterize/patchers'
const existing = { compilerOptions: { strict: true, target: "ES2022" } }
const incoming = { compilerOptions: { incremental: true } }
const merged = mergeJson(existing, incoming)
// { compilerOptions: { strict: true, target: "ES2022", incremental: true } }
ParameterTypeDescription
existingobjectThe current configuration (takes precedence)
incomingobjectThe new configuration to merge in (fills gaps)

Returns the merged object. Existing keys always take precedence over incoming keys.

Nested objects are merged recursively:

const existing = {
compilerOptions: {
strict: true,
target: "ES2022"
}
}
const incoming = {
compilerOptions: {
incremental: true,
tsBuildInfoFile: ".tsbuildinfo"
}
}
const merged = mergeJson(existing, incoming)
// {
// compilerOptions: {
// strict: true, // preserved from existing
// target: "ES2022", // preserved from existing
// incremental: true, // added from incoming
// tsBuildInfoFile: ".tsbuildinfo" // added from incoming
// }
// }
flowchart TD
    E[Existing config] --> M{mergeJson}
    I[Incoming config] --> M
    M --> P{Key exists?}
    P -->|Yes| K[Keep existing value]
    P -->|No| A[Add incoming value]
    P -->|Nested object| R[Recurse merge]
    K --> O[Merged result]
    A --> O
    R --> O
    
    style E fill:#6366f1,color:#fff
    style I fill:#f59e0b,color:#fff
    style O fill:#22c55e,color:#fff
const existing = { plugins: ['pluginA'] }
const incoming = { plugins: ['pluginB'] }
const merged = mergeJson(existing, incoming)
// { plugins: ['pluginA'] } — existing wins
// Existing
const existing = { compilerOptions: { strict: true, target: "ES2022" } }
// Incoming: add incremental builds
const incoming = { compilerOptions: { incremental: true } }
// Result: both preserved