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