-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathutils.js
116 lines (107 loc) · 3.57 KB
/
utils.js
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
// Regex to check if the property name is a reserved word
// avoiding prototype pollution and reassigning common functionalities
const reservedAccessorRegex = /^(constructor|prototype|__proto__|hasOwnProperty|isPrototypeOf|propertyIsEnumerable|toLocaleString|toString|valueOf)$/
/**
* Given a path, an object, and a value, attach the value to the object at the given path.
* @param {string} path
* @param {object} referenceObject
* @param {any} valueToAttach
*/
function attachThingToObjectGivenPath (path, referenceObject, valueToAttach) {
if (typeof path !== 'string') {
throw new Error('Path must be a string, unable to attach value to object')
}
if (path === '') {
throw new Error('Path cannot be empty, provide at least one property')
}
const pathArray = path.split('.')
// Use this to keep the instance of the object we are currently working with
let currentObject = referenceObject
for (let i = 0; i < pathArray.length; i++) {
if (reservedAccessorRegex.test(pathArray[i])) {
throw new Error(`The property name ${pathArray[i]} is a reserved word and cannot be used`)
}
if (i === pathArray.length - 1) {
if (Array.isArray(currentObject[pathArray[i]])) {
if (Array.isArray(valueToAttach)) {
currentObject[pathArray[i]].push(...valueToAttach)
} else {
currentObject[pathArray[i]].push(valueToAttach)
}
} else {
currentObject[pathArray[i]] = valueToAttach
}
} else {
if (!currentObject[pathArray[i]]) {
currentObject[pathArray[i]] = {}
}
currentObject = currentObject[pathArray[i]]
}
}
}
/**
* Given a path and an object, retrieve the value at the given path.
* @param {string} path
* @param {object} referenceObject
*/
function retriveThingFromObjectGivenPath (path, referenceObject) {
if (typeof path !== 'string') {
throw new Error('Path must be a string, unable to retrive value from object')
}
if (path === '') {
return referenceObject
}
const pathArray = path.split('.')
let currentObject = referenceObject
for (let i = 0; i < pathArray.length; i++) {
if (reservedAccessorRegex.test(pathArray[i])) {
throw new Error(`The property name ${pathArray[i]} is a reserved word and cannot be used`)
}
if (i === pathArray.length - 1) {
return currentObject[pathArray[i]]
} else {
if (!currentObject[pathArray[i]]) {
return undefined
}
currentObject = currentObject[pathArray[i]]
}
}
return currentObject
}
/**
* Delete given path from object
* @param {string} path
* @param {object} referenceObject
*/
function deleteThingFromObjectGivenPath (path, referenceObject) {
if (typeof path !== 'string') {
throw new Error('Path must be a string, unable to delete value on object')
}
if (path === '') {
throw new Error('Path cannot be empty, provide at least one property')
}
const pathArray = path.split('.')
let currentObject = referenceObject
for (let i = 0; i < pathArray.length; i++) {
if (reservedAccessorRegex.test(pathArray[i])) {
throw new Error(`The property name ${pathArray[i]} is a reserved word and cannot be used`)
}
if (i === pathArray.length - 1) {
if (Array.isArray(currentObject)) {
currentObject.splice(pathArray[i], 1)
} else {
delete currentObject[pathArray[i]]
}
} else {
if (!currentObject[pathArray[i]]) {
return
}
currentObject = currentObject[pathArray[i]]
}
}
}
module.exports = {
attachThingToObjectGivenPath,
retriveThingFromObjectGivenPath,
deleteThingFromObjectGivenPath
}