1'use strict'; 2const isObj = require('is-obj'); 3 4const disallowedKeys = [ 5 '__proto__', 6 'prototype', 7 'constructor' 8]; 9 10const isValidPath = pathSegments => !pathSegments.some(segment => disallowedKeys.includes(segment)); 11 12function getPathSegments(path) { 13 const pathArr = path.split('.'); 14 const parts = []; 15 16 for (let i = 0; i < pathArr.length; i++) { 17 let p = pathArr[i]; 18 19 while (p[p.length - 1] === '\\' && pathArr[i + 1] !== undefined) { 20 p = p.slice(0, -1) + '.'; 21 p += pathArr[++i]; 22 } 23 24 parts.push(p); 25 } 26 27 if (!isValidPath(parts)) { 28 return []; 29 } 30 31 return parts; 32} 33 34module.exports = { 35 get(obj, path, value) { 36 if (!isObj(obj) || typeof path !== 'string') { 37 return value === undefined ? obj : value; 38 } 39 40 const pathArr = getPathSegments(path); 41 if (pathArr.length === 0) { 42 return; 43 } 44 45 for (let i = 0; i < pathArr.length; i++) { 46 if (!Object.prototype.propertyIsEnumerable.call(obj, pathArr[i])) { 47 return value; 48 } 49 50 obj = obj[pathArr[i]]; 51 52 if (obj === undefined || obj === null) { 53 // `obj` is either `undefined` or `null` so we want to stop the loop, and 54 // if this is not the last bit of the path, and 55 // if it did't return `undefined` 56 // it would return `null` if `obj` is `null` 57 // but we want `get({foo: null}, 'foo.bar')` to equal `undefined`, or the supplied value, not `null` 58 if (i !== pathArr.length - 1) { 59 return value; 60 } 61 62 break; 63 } 64 } 65 66 return obj; 67 }, 68 69 set(obj, path, value) { 70 if (!isObj(obj) || typeof path !== 'string') { 71 return obj; 72 } 73 74 const root = obj; 75 const pathArr = getPathSegments(path); 76 if (pathArr.length === 0) { 77 return; 78 } 79 80 for (let i = 0; i < pathArr.length; i++) { 81 const p = pathArr[i]; 82 83 if (!isObj(obj[p])) { 84 obj[p] = {}; 85 } 86 87 if (i === pathArr.length - 1) { 88 obj[p] = value; 89 } 90 91 obj = obj[p]; 92 } 93 94 return root; 95 }, 96 97 delete(obj, path) { 98 if (!isObj(obj) || typeof path !== 'string') { 99 return; 100 } 101 102 const pathArr = getPathSegments(path); 103 104 for (let i = 0; i < pathArr.length; i++) { 105 const p = pathArr[i]; 106 107 if (i === pathArr.length - 1) { 108 delete obj[p]; 109 return; 110 } 111 112 obj = obj[p]; 113 114 if (!isObj(obj)) { 115 return; 116 } 117 } 118 }, 119 120 has(obj, path) { 121 if (!isObj(obj) || typeof path !== 'string') { 122 return false; 123 } 124 125 const pathArr = getPathSegments(path); 126 127 for (let i = 0; i < pathArr.length; i++) { 128 if (isObj(obj)) { 129 if (!(pathArr[i] in obj)) { 130 return false; 131 } 132 133 obj = obj[pathArr[i]]; 134 } else { 135 return false; 136 } 137 } 138 139 return true; 140 } 141}; 142