blob: e4fd820e4a6886c09f3a5683b934df7ed4954ac0 [file] [log] [blame]
/**
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
*/
// contains PLIST utility functions
const util = require('node:util');
const plist = require('plist');
// adds node to doc at selector
module.exports.graftPLIST = graftPLIST;
function graftPLIST (doc, xml, selector) {
const obj = plist.parse(`<plist>${xml}</plist>`);
const node = doc[selector];
if (node && Array.isArray(node) && Array.isArray(obj)) {
const isNew = item => !node.some(nodeChild => nodeEqual(item, nodeChild));
doc[selector] = node.concat(obj.filter(isNew));
} else {
// plist uses objects for <dict>. If we have two dicts we merge them instead of
// overriding the old one. See CB-6472
const isDict = o => typeof o === 'object' && !util.types.isDate(o); // arrays checked above
if (isDict(node) && isDict(obj)) {
Object.assign(obj, node);
}
doc[selector] = obj;
}
return true;
}
// removes node from doc at selector
module.exports.prunePLIST = prunePLIST;
function prunePLIST (doc, xml, selector) {
const obj = plist.parse(`<plist>${xml}</plist>`);
pruneObject(doc, selector, obj);
return true;
}
function pruneObject (doc, selector, fragment) {
if (Array.isArray(fragment) && Array.isArray(doc[selector])) {
let empty = true;
for (const i in fragment) {
for (const j in doc[selector]) {
empty = pruneObject(doc[selector], j, fragment[i]) && empty;
}
}
if (empty) {
delete doc[selector];
return true;
}
} else if (nodeEqual(doc[selector], fragment)) {
delete doc[selector];
return true;
}
return false;
}
function nodeEqual (node1, node2) {
if (typeof node1 !== typeof node2) {
return false;
} else if (typeof node1 === 'string') {
node2 = escapeRE(node2).replace(/\\\$\(\S+\)/gm, '(.*?)');
return new RegExp(`^${node2}$`).test(node1);
} else {
for (const key in node2) {
if (!nodeEqual(node1[key], node2[key])) return false;
}
return true;
}
}
// escape string for use in regex
function escapeRE (str) {
return str.replace(/[-[\]/{}()*+?.\\^$|]/g, '\\$&');
}