You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

143 lines
2.7 KiB

'use strict';
const isObj = require('is-obj');
const disallowedKeys = [
'__proto__',
'prototype',
'constructor'
];
const isValidPath = pathSegments => !pathSegments.some(segment => disallowedKeys.includes(segment));
function getPathSegments(path) {
const pathArray = path.split('.');
const parts = [];
for (let i = 0; i < pathArray.length; i++) {
let p = pathArray[i];
while (p[p.length - 1] === '\\' && pathArray[i + 1] !== undefined) {
p = p.slice(0, -1) + '.';
p += pathArray[++i];
}
parts.push(p);
}
if (!isValidPath(parts)) {
return [];
}
return parts;
}
module.exports = {
get(object, path, value) {
if (!isObj(object) || typeof path !== 'string') {
return value === undefined ? object : value;
}
const pathArray = getPathSegments(path);
if (pathArray.length === 0) {
return;
}
for (let i = 0; i < pathArray.length; i++) {
if (!Object.prototype.propertyIsEnumerable.call(object, pathArray[i])) {
return value;
}
object = object[pathArray[i]];
if (object === undefined || object === null) {
// `object` is either `undefined` or `null` so we want to stop the loop, and
// if this is not the last bit of the path, and
// if it did't return `undefined`
// it would return `null` if `object` is `null`
// but we want `get({foo: null}, 'foo.bar')` to equal `undefined`, or the supplied value, not `null`
if (i !== pathArray.length - 1) {
return value;
}
break;
}
}
return object;
},
set(object, path, value) {
if (!isObj(object) || typeof path !== 'string') {
return object;
}
const root = object;
const pathArray = getPathSegments(path);
for (let i = 0; i < pathArray.length; i++) {
const p = pathArray[i];
if (!isObj(object[p])) {
object[p] = {};
}
if (i === pathArray.length - 1) {
object[p] = value;
}
object = object[p];
}
return root;
},
delete(object, path) {
if (!isObj(object) || typeof path !== 'string') {
return;
}
const pathArray = getPathSegments(path);
for (let i = 0; i < pathArray.length; i++) {
const p = pathArray[i];
if (i === pathArray.length - 1) {
delete object[p];
return;
}
object = object[p];
if (!isObj(object)) {
return;
}
}
},
has(object, path) {
if (!isObj(object) || typeof path !== 'string') {
return false;
}
const pathArray = getPathSegments(path);
if (pathArray.length === 0) {
return false;
}
// eslint-disable-next-line unicorn/no-for-loop
for (let i = 0; i < pathArray.length; i++) {
if (isObj(object)) {
if (!(pathArray[i] in object)) {
return false;
}
object = object[pathArray[i]];
} else {
return false;
}
}
return true;
}
};