blob: b97cc5ba268930a8da4137cb030be91167ed3548 [file] [log] [blame] [edit]
/* This file is a part of @mdn/browser-compat-data
* See LICENSE file for more information. */
/**
* Error class for duplicate compat statements during object merge
*/
class DuplicateCompatError extends Error {
/**
* Construct the error
* @param feature The feature path
*/
constructor(feature: string) {
super(`${feature} already exists! Remove duplicate entries.`);
this.name = 'DuplicateCompatError';
}
}
/**
* Check if the variable is an object ({})
* @param v The object to test
* @returns Whether the object is a plain object
*/
const isPlainObject = (v): v is object =>
typeof v === 'object' && v !== null && !Array.isArray(v);
/**
* Combine two objects containing browser compat data together
* @param target The object to extend
* @param source The object to copy from
* @param feature The feature path so far (internal for recursive calls)
*/
const extend = (target, source, feature = ''): void => {
if (!isPlainObject(target) || !isPlainObject(source)) {
throw new Error('Both target and source must be plain objects');
}
// iterate over own enumerable properties
for (const [key, value] of Object.entries(source)) {
// recursively extend if target has the same key, otherwise just assign
if (Object.prototype.hasOwnProperty.call(target, key)) {
if (key == '__compat') {
// If attempting to merge __compat, we have a double-entry
throw new DuplicateCompatError(feature);
}
extend(target[key], value, feature + `${feature ? '.' : ''}${key}`);
} else {
target[key] = value;
}
}
};
export default extend;