@vltpkg/dot-prop 1.0.0-rc.23 → 1.0.0-rc.25

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,22 @@
1
+ export type ArrayArg = unknown[];
2
+ export type ObjectArg = Record<PropertyKey, unknown>;
3
+ export type Arg = ArrayArg | ObjectArg;
4
+ export declare const Characters: {
5
+ readonly Escape: "\\";
6
+ readonly Dot: ".";
7
+ readonly Empty: "";
8
+ readonly LeftBracket: "[";
9
+ readonly RightBracket: "]";
10
+ };
11
+ export type Character = (typeof Characters)[keyof typeof Characters] | (string & {});
12
+ export declare const Parts: {
13
+ readonly Start: "start";
14
+ readonly Index: "index";
15
+ readonly IndexEnd: "indexEnd";
16
+ readonly Property: "property";
17
+ };
18
+ export type Part = (typeof Parts)[keyof typeof Parts];
19
+ export declare const get: (ogObject: Arg, path: string, defaultValue?: unknown) => unknown;
20
+ export declare const set: <T extends Arg>(object: T, path: string, value: unknown) => T;
21
+ export declare const del: (object: Arg, path: string) => boolean;
22
+ export declare const has: (object: Arg, path: string) => boolean;
package/dist/index.js ADDED
@@ -0,0 +1,234 @@
1
+ const DISALLOWED_KEYS = new Set([
2
+ '__proto__',
3
+ 'prototype',
4
+ 'constructor',
5
+ ]);
6
+ const DIGITS = new Set('0123456789');
7
+ const ARRAY_PUSH = Symbol('ARRAY_PUSH');
8
+ export const Characters = {
9
+ Escape: '\\',
10
+ Dot: '.',
11
+ Empty: '',
12
+ LeftBracket: '[',
13
+ RightBracket: ']',
14
+ };
15
+ export const Parts = {
16
+ Start: 'start',
17
+ Index: 'index',
18
+ IndexEnd: 'indexEnd',
19
+ Property: 'property',
20
+ };
21
+ const checkInvalidCharacter = (part, current, msg) => {
22
+ if (current === part) {
23
+ if (msg === undefined) {
24
+ switch (current) {
25
+ case Parts.Index:
26
+ msg = 'character in an index';
27
+ break;
28
+ case Parts.IndexEnd:
29
+ msg = 'character after an index';
30
+ break;
31
+ /* c8 ignore next 3 */
32
+ default:
33
+ msg = '';
34
+ break;
35
+ }
36
+ }
37
+ throw new Error(`Invalid ${msg}`.trim());
38
+ }
39
+ };
40
+ const getPathSegments = (path, allowEmptyIndex = false) => {
41
+ const segments = [];
42
+ let currentSegment = Characters.Empty;
43
+ let currentPart = Parts.Start;
44
+ let isIgnoring = false;
45
+ for (const character of path.split('')) {
46
+ switch (character) {
47
+ case Characters.Escape: {
48
+ checkInvalidCharacter(Parts.Index, currentPart);
49
+ checkInvalidCharacter(Parts.IndexEnd, currentPart);
50
+ if (isIgnoring)
51
+ currentSegment += character;
52
+ currentPart = Parts.Property;
53
+ isIgnoring = !isIgnoring;
54
+ break;
55
+ }
56
+ case Characters.Dot: {
57
+ checkInvalidCharacter(Parts.Index, currentPart);
58
+ if (currentPart === Parts.IndexEnd) {
59
+ currentPart = Parts.Property;
60
+ break;
61
+ }
62
+ if (isIgnoring) {
63
+ isIgnoring = false;
64
+ currentSegment += character;
65
+ break;
66
+ }
67
+ if (DISALLOWED_KEYS.has(currentSegment))
68
+ return [];
69
+ segments.push(currentSegment);
70
+ currentSegment = Characters.Empty;
71
+ currentPart = Parts.Property;
72
+ break;
73
+ }
74
+ case Characters.LeftBracket: {
75
+ checkInvalidCharacter(Parts.Index, currentPart);
76
+ if (currentPart === Parts.IndexEnd) {
77
+ currentPart = Parts.Index;
78
+ break;
79
+ }
80
+ if (isIgnoring) {
81
+ isIgnoring = false;
82
+ currentSegment += character;
83
+ break;
84
+ }
85
+ if (currentPart === Parts.Property) {
86
+ if (DISALLOWED_KEYS.has(currentSegment))
87
+ return [];
88
+ segments.push(currentSegment);
89
+ currentSegment = Characters.Empty;
90
+ }
91
+ currentPart = Parts.Index;
92
+ break;
93
+ }
94
+ case Characters.RightBracket: {
95
+ if (currentPart === Parts.Index) {
96
+ if (allowEmptyIndex)
97
+ checkInvalidCharacter(Characters.Empty, currentSegment, 'empty index');
98
+ segments.push(currentSegment === Characters.Empty ?
99
+ ARRAY_PUSH
100
+ : Number.parseInt(currentSegment, 10));
101
+ currentPart = Parts.IndexEnd;
102
+ currentSegment = Characters.Empty;
103
+ break;
104
+ }
105
+ // Falls through
106
+ }
107
+ default: {
108
+ if (!DIGITS.has(character))
109
+ checkInvalidCharacter(Parts.Index, currentPart);
110
+ checkInvalidCharacter(Parts.IndexEnd, currentPart);
111
+ if (currentPart === Parts.Start)
112
+ currentPart = Parts.Property;
113
+ if (isIgnoring) {
114
+ isIgnoring = false;
115
+ currentSegment += Characters.Escape;
116
+ }
117
+ currentSegment += character;
118
+ }
119
+ }
120
+ }
121
+ if (isIgnoring)
122
+ currentSegment += Characters.Escape;
123
+ checkInvalidCharacter(Parts.Index, currentPart, 'index was not closed');
124
+ if (currentPart === Parts.Property) {
125
+ if (DISALLOWED_KEYS.has(currentSegment))
126
+ return [];
127
+ segments.push(currentSegment);
128
+ }
129
+ else if (currentPart === Parts.Start) {
130
+ segments.push(Characters.Empty);
131
+ }
132
+ return segments;
133
+ };
134
+ const isObject = (value) => value !== null && typeof value === 'object';
135
+ const isLast = (arr, i) => i === arr.length - 1;
136
+ const isStringIndex = (object, key) => {
137
+ if (typeof key !== 'symbol' &&
138
+ typeof key !== 'number' &&
139
+ Array.isArray(object)) {
140
+ const index = Number.parseInt(key, 10);
141
+ return (Number.isInteger(index) &&
142
+ object[index] === object[key]);
143
+ }
144
+ return false;
145
+ };
146
+ const assertNotStringIndex = (object, key) => {
147
+ if (isStringIndex(object, key)) {
148
+ throw new Error('Cannot use string index');
149
+ }
150
+ };
151
+ export const get = (ogObject, path, defaultValue) => {
152
+ let object = ogObject;
153
+ const pathArray = getPathSegments(path, true);
154
+ if (!pathArray.length) {
155
+ return defaultValue;
156
+ }
157
+ for (const [index, key] of pathArray.entries()) {
158
+ if (isStringIndex(object, key)) {
159
+ object = isLast(pathArray, index) ? undefined : null;
160
+ }
161
+ else {
162
+ object = object[key];
163
+ }
164
+ if ((object === undefined || object === null) &&
165
+ !isLast(pathArray, index)) {
166
+ // `object` is either `undefined` or `null` so we want to stop the loop,
167
+ // and if this is not the last bit of the path, and if it didn't return
168
+ // `undefined` it would return `null` if `object` is `null` but we want
169
+ // `get({foo: null}, 'foo.bar')` to equal `undefined`, or the supplied
170
+ // value, not `null`
171
+ return defaultValue;
172
+ }
173
+ }
174
+ return object === undefined ? defaultValue : object;
175
+ };
176
+ export const set = (object, path, value) => {
177
+ const root = object;
178
+ const pathArray = getPathSegments(path);
179
+ for (const [index, key] of pathArray.entries()) {
180
+ assertNotStringIndex(object, key);
181
+ if (isLast(pathArray, index)) {
182
+ if (key === ARRAY_PUSH) {
183
+ ;
184
+ object.push(value);
185
+ }
186
+ else {
187
+ ;
188
+ object[key] = value;
189
+ }
190
+ }
191
+ else if (!isObject(object[key])) {
192
+ const next = pathArray[index + 1];
193
+ object[key] =
194
+ typeof next === 'number' || next === ARRAY_PUSH ? [] : {};
195
+ }
196
+ object = object[key];
197
+ }
198
+ return root;
199
+ };
200
+ export const del = (object, path) => {
201
+ const pathArray = getPathSegments(path);
202
+ for (const [index, key] of pathArray.entries()) {
203
+ assertNotStringIndex(object, key);
204
+ if (isLast(pathArray, index)) {
205
+ if (Array.isArray(object)) {
206
+ object.splice(key, 1);
207
+ }
208
+ else {
209
+ delete object[key];
210
+ }
211
+ return true;
212
+ }
213
+ object = object[key];
214
+ if (!isObject(object)) {
215
+ return false;
216
+ }
217
+ }
218
+ return false;
219
+ };
220
+ export const has = (object, path) => {
221
+ const pathArray = getPathSegments(path);
222
+ if (!pathArray.length) {
223
+ return false;
224
+ }
225
+ for (const key of pathArray) {
226
+ if (!isObject(object) ||
227
+ !(key in object) ||
228
+ isStringIndex(object, key)) {
229
+ return false;
230
+ }
231
+ object = object[key];
232
+ }
233
+ return true;
234
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vltpkg/dot-prop",
3
3
  "description": "Get and manipulate an object using strings",
4
- "version": "1.0.0-rc.23",
4
+ "version": "1.0.0-rc.25",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/vltpkg/vltpkg.git",