#!/usr/bin/env node const fs = require('fs'); const path = require('path'); const json5 = require('json5'); const [srcFile, destFile] = process.argv.slice(2); if (!srcFile || !destFile) { console.error('Usage: merge-config.js '); process.exit(1); } function deepMerge(target, source) { const result = { ...source }; for (const key of Object.keys(target)) { if (key in source) { if (typeof source[key] === 'object' && !Array.isArray(source[key]) && typeof target[key] === 'object' && !Array.isArray(target[key])) { result[key] = deepMerge(target[key], source[key]); } else { result[key] = target[key]; } } else { result[key] = target[key]; } } return result; } const srcContent = fs.readFileSync(srcFile, 'utf8'); let srcData; try { srcData = json5.parse(srcContent); } catch (e) { console.error(`--> [SKIP] Could not parse ${srcFile}: ${e.message}`); process.exit(0); } if (typeof srcData !== 'object' || Array.isArray(srcData)) { console.error(`--> [SKIP] ${srcFile} is not a JSON object, copying directly`); fs.copyFileSync(srcFile, destFile); process.exit(0); } let destData = {}; let existingKeys = []; if (fs.existsSync(destFile)) { try { const destContent = fs.readFileSync(destFile, 'utf8'); destData = json5.parse(destContent); existingKeys = Object.keys(destData); } catch (e) { console.error(`--> Warning: could not parse ${destFile}, overwriting from example`); } } const fullMerged = deepMerge(destData, srcData); const ordered = {}; for (const key of Object.keys(srcData)) { ordered[key] = fullMerged[key]; } for (const key of Object.keys(destData)) { if (!(key in srcData)) { ordered[key] = destData[key]; } } const newKeys = Object.keys(ordered).filter(k => !existingKeys.includes(k)); if (newKeys.length > 0) { console.log(`--> Added ${newKeys.length} new key(s) to ${path.basename(destFile)}`); } fs.writeFileSync(destFile, JSON.stringify(ordered, null, 4) + '\n'); console.log(`--> [OK] ${path.basename(destFile)} is up to date`);