function mergeXml()

in src/util/xml-helpers.js [268:339]


function mergeXml (src, dest, platform, clobber) {
    // Do nothing for denied tags.
    if (DENIED_TAGS.includes(String(src.tag))) return;

    // Handle attributes
    const omitAttrs = new Set(clobber ? [] : dest.keys());
    const xferAttrs = src.keys().filter(k => !omitAttrs.has(k));
    xferAttrs.forEach(attr => { dest.attrib[attr] = src.attrib[attr]; });

    // Handle text
    if (src.text && (clobber || !dest.text)) {
        dest.text = src.text;
    }
    // Handle children
    src.getchildren().forEach(mergeChild);

    // Handle platform
    if (platform) {
        src.findall(`platform[@name="${platform}"]`).forEach(platformElement => {
            platformElement.getchildren().forEach(mergeChild);
        });
    }

    // Handle duplicate preference tags (by name attribute)
    removeDuplicatePreferences(dest);

    /** @param {et.Element} srcChild */
    function mergeChild (srcChild) {
        const srcTag = String(srcChild.tag);
        const query = srcTag + '';
        let destChild;
        let shouldMerge = true;

        if (DENIED_TAGS.includes(srcTag)) return;

        if (SINGLETONS.includes(srcTag)) {
            destChild = dest.find(query);
        } else {
            // Check for an exact match and if you find one don't add
            destChild = dest.findall(query).find(el =>
                textMatch(srcChild, el) && attribMatch(srcChild, el)
            );
            if (destChild) shouldMerge = false;
        }

        if (destChild) {
            dest.remove(destChild);
        } else {
            destChild = et.Element(srcTag);
        }
        mergeXml(srcChild, destChild, platform, clobber && shouldMerge);
        dest.append(destChild);
    }

    /** @param {et.Element} xml */
    function removeDuplicatePreferences (xml) {
        const prefs = xml.findall('preference[@name][@value]');

        // reduce preference tags to a hashtable to remove dupes
        const prefMap = new Map(
            prefs.map(({ attrib: { name, value } }) => [name, value])
        );

        // remove all preferences
        prefs.forEach(pref => xml.remove(pref));

        // write new preferences
        prefMap.forEach((value, name) => {
            et.SubElement(xml, 'preference', { name, value });
        });
    }
}