jfather 0.2.0 → 0.4.0
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.
- package/LICENSE +1 -1
- package/README.md +286 -214
- package/package.json +31 -29
- package/src/jfather.js +41 -19
- package/types/jfather.d.ts +15 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -5,19 +5,24 @@
|
|
|
5
5
|
<!-- markdownlint-disable-next-line no-inline-html-->
|
|
6
6
|
<img src="asset/logo.svg" align="right" alt="">
|
|
7
7
|
|
|
8
|
-
[![npm][img-npm]][link-npm]
|
|
9
|
-
[![
|
|
10
|
-
[![coverage][img-coverage]][link-coverage]
|
|
11
|
-
[![semver][img-semver]][link-semver]
|
|
8
|
+
[![npm][img-npm]][link-npm] [![build][img-build]][link-build]
|
|
9
|
+
[![coverage][img-coverage]][link-coverage] [![semver][img-semver]][link-semver]
|
|
12
10
|
|
|
13
11
|
> _Boys use JSON; Men use JFather._
|
|
14
12
|
|
|
15
13
|
## Overview
|
|
16
14
|
|
|
17
|
-
JFather is a
|
|
18
|
-
[JSON](https://www.json.org/json-
|
|
15
|
+
JFather is a
|
|
16
|
+
[JSON](https://www.json.org/json-en.html "JavaScript Object Notation") utility
|
|
17
|
+
library to:
|
|
19
18
|
|
|
20
|
-
|
|
19
|
+
- [**merge**](#merge) deeply two JSON objects.
|
|
20
|
+
- [**extend**](#extend) a JSON objects with `"$extends"` property.
|
|
21
|
+
- [**override**](#override) an array with `"$foo[0]"` (replace a value) or
|
|
22
|
+
`"$foo[]"` (append values) properties.
|
|
23
|
+
|
|
24
|
+
<!-- prettier-ignore-start -->
|
|
25
|
+
```javascript
|
|
21
26
|
import JFather from "jfather";
|
|
22
27
|
|
|
23
28
|
// Merge two objects.
|
|
@@ -47,8 +52,8 @@ console.log(extended);
|
|
|
47
52
|
// "quote": "With great fist comes great KO"
|
|
48
53
|
// }
|
|
49
54
|
|
|
50
|
-
// Override an object.
|
|
51
|
-
const overridden =
|
|
55
|
+
// Override arrays of an object.
|
|
56
|
+
const overridden = JFather.merge(
|
|
52
57
|
{ "foo": ["a", "alpha"] },
|
|
53
58
|
{ "$foo[0]": "A", "$foo[]": ["BETA"] }
|
|
54
59
|
);
|
|
@@ -80,16 +85,17 @@ console.log(allIn);
|
|
|
80
85
|
// "quote": "I'm no God. I'm not even a man. I'm just Molecule Man."
|
|
81
86
|
// }
|
|
82
87
|
```
|
|
88
|
+
<!-- prettier-ignore-end -->
|
|
83
89
|
|
|
84
90
|
## Installation
|
|
85
91
|
|
|
86
92
|
JFather is published on [npm][link-npm] (its CDN:
|
|
87
93
|
[esm.sh](https://esm.sh/jfather),
|
|
88
94
|
[jsDelivr](https://www.jsdelivr.com/package/npm/jfather),
|
|
89
|
-
[UNPKG](https://unpkg.com/browse/jfather/))
|
|
90
|
-
[Deno](https://deno.land/x/jfather).
|
|
95
|
+
[UNPKG](https://unpkg.com/browse/jfather/)),
|
|
96
|
+
[JSR](https://jsr.io/@regseb/jfather) and [Deno](https://deno.land/x/jfather).
|
|
91
97
|
|
|
92
|
-
```
|
|
98
|
+
```javascript
|
|
93
99
|
// Node.js and Bun (after `npm install jfather`):
|
|
94
100
|
import JFather from "jfather";
|
|
95
101
|
|
|
@@ -98,242 +104,308 @@ import JFather from "https://esm.sh/jfather@0";
|
|
|
98
104
|
import JFather from "https://cdn.jsdelivr.net/npm/jfather@0";
|
|
99
105
|
import JFather from "https://unpkg.com/jfather@0";
|
|
100
106
|
|
|
101
|
-
// Deno:
|
|
102
|
-
import JFather from "
|
|
107
|
+
// Deno (after `deno add jsr:@regseb/jfather`):
|
|
108
|
+
import JFather from "jsr:@regseb/jfather";
|
|
103
109
|
```
|
|
104
110
|
|
|
105
111
|
## Features
|
|
106
112
|
|
|
107
113
|
### Merge
|
|
108
114
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
"baz": "BETA"
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
"
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
"foo":
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
115
|
+
With any two variable types (except objects), merge returns the value of the
|
|
116
|
+
child. The following example shows how to use it with numbers: the result is `2`
|
|
117
|
+
(retrieved from the child value).
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const parent = 1;
|
|
121
|
+
const child = 2;
|
|
122
|
+
console.log(JFather.merge(parent, child));
|
|
123
|
+
// 2
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
If both variables are objects, the object properties are merged one by one. In
|
|
127
|
+
this example, the `"foo"` property overwrites that of the parent. The properties
|
|
128
|
+
`"bar"` and `"baz"` are simply copied, as they are only in either the parent or
|
|
129
|
+
the child.
|
|
130
|
+
|
|
131
|
+
<!-- prettier-ignore-start -->
|
|
132
|
+
```javascript
|
|
133
|
+
const parent = { "foo": "alpha", "bar": "ALPHA" };
|
|
134
|
+
const child = { "foo": "beta", "baz": "BETA" };
|
|
135
|
+
console.log(JFather.merge(parent, child));
|
|
136
|
+
// { "foo": "beta", "bar": "ALPHA", "baz": "BETA" }
|
|
137
|
+
```
|
|
138
|
+
<!-- prettier-ignore-end -->
|
|
139
|
+
|
|
140
|
+
Merging is done recursively. The following example shows the merging of two
|
|
141
|
+
objects, which in turn contains the merging of the `"foo"` sub-objects.
|
|
142
|
+
|
|
143
|
+
<!-- prettier-ignore-start -->
|
|
144
|
+
```javascript
|
|
145
|
+
const parent = {
|
|
146
|
+
"foo": { "bar": 1, "baz": 2 },
|
|
147
|
+
"qux": "a"
|
|
148
|
+
};
|
|
149
|
+
const child = {
|
|
150
|
+
"foo": { "bar": 10, "quux": 20 },
|
|
151
|
+
"corge": "b"
|
|
152
|
+
};
|
|
153
|
+
console.log(JFather.merge(parent, child));
|
|
154
|
+
// {
|
|
155
|
+
// "foo": { "bar": 10, "baz": 2, "quux": 20 },
|
|
156
|
+
// "qux": "a",
|
|
157
|
+
// "corge": "b"
|
|
158
|
+
// }
|
|
159
|
+
```
|
|
160
|
+
<!-- prettier-ignore-end -->
|
|
161
|
+
|
|
162
|
+
Arrays are processed like any other type: the value of the child overrides that
|
|
163
|
+
of the parent. For more detailed merging, see the [_Override_](#override)
|
|
164
|
+
chapter, which shows how to merge arrays.
|
|
165
|
+
|
|
166
|
+
<!-- prettier-ignore-start -->
|
|
167
|
+
```javascript
|
|
168
|
+
const parent = { "foo": [1, 10, 11] };
|
|
169
|
+
const child = { "foo": [2, 20, 22] };
|
|
170
|
+
console.log(JFather.merge(parent, child));
|
|
171
|
+
// { "foo": [2, 20, 22] }
|
|
172
|
+
```
|
|
173
|
+
<!-- prettier-ignore-end -->
|
|
149
174
|
|
|
150
175
|
### Extend
|
|
151
176
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
"
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
<td><pre lang="JSON"><code>{
|
|
200
|
-
"quux": {
|
|
201
|
-
"$extends": "https://foo.bar/parent.json",
|
|
202
|
-
"corge": "grault"
|
|
203
|
-
}
|
|
204
|
-
}</code></pre></td>
|
|
205
|
-
<td><pre lang="JSON"><code>{
|
|
206
|
-
"quux": {
|
|
207
|
-
"baz": "qux",
|
|
208
|
-
"corge": "grault"
|
|
177
|
+
You can extend an object using the `"$extends"` property, which must link to a
|
|
178
|
+
JSON file. The remote JSON file and the current object will be merged.
|
|
179
|
+
|
|
180
|
+
In this example, the child object is empty (except for the `"$extends"`
|
|
181
|
+
property). The result therefore contains the parent object.
|
|
182
|
+
|
|
183
|
+
<!-- prettier-ignore-start -->
|
|
184
|
+
```javascript
|
|
185
|
+
// https://example.com/parent.json
|
|
186
|
+
// { "foo": 42 }
|
|
187
|
+
|
|
188
|
+
const obj = { "$extends": "https://example.com/parent.json" };
|
|
189
|
+
console.log(await JFather.extend(obj));
|
|
190
|
+
// { "foo": 42 }
|
|
191
|
+
```
|
|
192
|
+
<!-- prettier-ignore-end -->
|
|
193
|
+
|
|
194
|
+
As with merge, if a property is in both parent and child, the child's value is
|
|
195
|
+
used. Otherwise, both parent and child properties are added to the result.
|
|
196
|
+
|
|
197
|
+
<!-- prettier-ignore-start -->
|
|
198
|
+
```javascript
|
|
199
|
+
// https://example.com/parent.json
|
|
200
|
+
// { "foo": "A", "bar": "Alpha" }
|
|
201
|
+
|
|
202
|
+
const obj = {
|
|
203
|
+
"$extends": "https://example.com/parent.json",
|
|
204
|
+
"foo": "B",
|
|
205
|
+
"baz": "Beta"
|
|
206
|
+
};
|
|
207
|
+
console.log(await JFather.extend(obj));
|
|
208
|
+
// { "foo": "B", "bar": "Alpha", "baz": "Beta" }
|
|
209
|
+
```
|
|
210
|
+
<!-- prettier-ignore-end -->
|
|
211
|
+
|
|
212
|
+
It is possible to extend a child's sub-object. In the example below, the parent
|
|
213
|
+
is merged with the child's `"bar"` sub-object.
|
|
214
|
+
|
|
215
|
+
<!-- prettier-ignore-start -->
|
|
216
|
+
```javascript
|
|
217
|
+
// https://example.com/parent.json
|
|
218
|
+
// { "foo": 42 }
|
|
219
|
+
|
|
220
|
+
const obj = {
|
|
221
|
+
"bar": {
|
|
222
|
+
"$extends": "https://example.com/parent.json",
|
|
223
|
+
"baz": 3.14
|
|
209
224
|
}
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
225
|
+
};
|
|
226
|
+
console.log(await JFather.extend(obj));
|
|
227
|
+
// {
|
|
228
|
+
// "bar": { "foo": 42, "baz": 3.14 }
|
|
229
|
+
// }
|
|
230
|
+
```
|
|
231
|
+
<!-- prettier-ignore-end -->
|
|
232
|
+
|
|
233
|
+
In the parent link, you can define a path to retrieve a sub-object from the
|
|
234
|
+
parent. The path is set in the URL hash:
|
|
235
|
+
|
|
236
|
+
- `#foo`: the value of the `"foo"` property;
|
|
237
|
+
- `#foo.bar`: the value of the `"bar"` sub-property in the `"foo"` property;
|
|
238
|
+
- `#foo[42]`: the value of the forty-third array element in the `"foo"`
|
|
239
|
+
property;
|
|
240
|
+
- `#foo[0].bar`: the value of the sub-property `"bar"` in the first element of
|
|
241
|
+
the array in the property `"foo"`.
|
|
242
|
+
|
|
243
|
+
This example merges the `"foo"` property of the parent with the child.
|
|
244
|
+
|
|
245
|
+
<!-- prettier-ignore-start -->
|
|
246
|
+
```javascript
|
|
247
|
+
// https://example.com/parent.json
|
|
248
|
+
// {
|
|
249
|
+
// "foo": { "bar": [1, 2], "baz": "a" },
|
|
250
|
+
// "qux": true
|
|
251
|
+
// }
|
|
252
|
+
|
|
253
|
+
const obj = { "$extends": "https://example.com/parent.json#foo" };
|
|
254
|
+
console.log(await JFather.extend(obj));
|
|
255
|
+
// { "bar": [1, 2], "baz": "a" }
|
|
256
|
+
```
|
|
257
|
+
<!-- prettier-ignore-end -->
|
|
230
258
|
|
|
231
259
|
### Override
|
|
232
260
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
261
|
+
If an object has arrays, the merge overwrites the parent's array with the
|
|
262
|
+
child's. With the properties `"$foo[42]"` and `"$foo[]"`, you can refine the
|
|
263
|
+
merge.
|
|
264
|
+
|
|
265
|
+
In this example, the array `"foo"` is not overwritten. The first value of the
|
|
266
|
+
`"foo"` array is merged with the value of the child's `"$foo[0]"` property. And
|
|
267
|
+
the second value of `"foo"` is copied into the result.
|
|
268
|
+
|
|
269
|
+
<!-- prettier-ignore-start -->
|
|
270
|
+
```javascript
|
|
271
|
+
const parent = { "foo": ["a", "Alpha"] };
|
|
272
|
+
const child = { "$foo[0]": "B" };
|
|
273
|
+
console.log(JFather.merge(parent, child));
|
|
274
|
+
// { "foo": ["B", "Alpha"] }
|
|
275
|
+
```
|
|
276
|
+
<!-- prettier-ignore-end -->
|
|
277
|
+
|
|
278
|
+
With `"$foo[]"`, the child's values are added to those of the parent. In the
|
|
279
|
+
example below, the values `"b"` and `"Beta"` are added to the array of the
|
|
280
|
+
`"foo"` property.
|
|
281
|
+
|
|
282
|
+
<!-- prettier-ignore-start -->
|
|
283
|
+
```javascript
|
|
284
|
+
const parent = { "foo": ["a", "Alpha"] };
|
|
285
|
+
const child = { "$foo[]": ["b", "Beta"] };
|
|
286
|
+
console.log(JFather.merge(parent, child));
|
|
287
|
+
// { "foo": ["a", "Alpha", "b", "Beta"] }
|
|
288
|
+
```
|
|
289
|
+
<!-- prettier-ignore-end -->
|
|
290
|
+
|
|
291
|
+
You can combine the two overloads to, for example:
|
|
292
|
+
|
|
293
|
+
- merge the first value of the parent's `"foo"` array with the value of the
|
|
294
|
+
child's `"$foo[0]"` property;
|
|
295
|
+
- add the values `"b"` and `"c"` to the array of the sub-property `"bar"`.
|
|
296
|
+
|
|
297
|
+
<!-- prettier-ignore-start -->
|
|
298
|
+
```javascript
|
|
299
|
+
const parent = {
|
|
300
|
+
"foo": [{
|
|
301
|
+
"bar": ["a"]
|
|
302
|
+
}]
|
|
303
|
+
};
|
|
304
|
+
const child = {
|
|
305
|
+
"$foo[0]": {
|
|
306
|
+
"$bar[]": ["b", "c"]
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
console.log(JFather.merge(parent, child));
|
|
310
|
+
// {
|
|
311
|
+
// "foo": [{
|
|
312
|
+
// "bar": ["a", "b", "c"]
|
|
313
|
+
// }]
|
|
314
|
+
// }
|
|
315
|
+
```
|
|
316
|
+
<!-- prettier-ignore-end -->
|
|
317
|
+
|
|
318
|
+
If the child overloads a property that does not exist in the parent, the
|
|
319
|
+
overload is ignored. The overload is also ignored if the parent object is not an
|
|
320
|
+
array. In the following example, the child has two overloads which are ignored:
|
|
321
|
+
the overload on the property `"bar"` which is not an array, and the overload on
|
|
322
|
+
`"baz"` which does not exist in the parent.
|
|
323
|
+
|
|
324
|
+
<!-- prettier-ignore-start -->
|
|
325
|
+
```javascript
|
|
326
|
+
const parent = { "foo": ["a", "A"], "bar": 42 };
|
|
327
|
+
const child = { "$bar[0]": 3.14, "$baz[]": ["beta"] };
|
|
328
|
+
console.log(JFather.merge(parent, child));
|
|
329
|
+
// { "foo": ["a", "A"], "bar": 42 }
|
|
330
|
+
```
|
|
331
|
+
<!-- prettier-ignore-end -->
|
|
275
332
|
|
|
276
333
|
## API
|
|
277
334
|
|
|
278
|
-
- [`merge()`](#
|
|
279
|
-
- [`extend()`](#
|
|
280
|
-
- [`load()`](#
|
|
281
|
-
- [`parse()`](#
|
|
335
|
+
- [`JFather.merge(parent, child)`](#jfathermergeparent-child)
|
|
336
|
+
- [`JFather.extend(obj, [options])`](#jfatherextendobj-options)
|
|
337
|
+
- [`JFather.load(url, [options])`](#jfatherloadurl-options)
|
|
338
|
+
- [`JFather.parse(text, [options])`](#jfatherparsetext-options)
|
|
282
339
|
|
|
283
|
-
### `merge()`
|
|
340
|
+
### `JFather.merge(parent, child)`
|
|
284
341
|
|
|
285
342
|
Merge and override `parent` with `child`.
|
|
286
343
|
|
|
287
|
-
```JavaScript
|
|
288
|
-
JFather.merge(parent, child);
|
|
289
|
-
```
|
|
290
|
-
|
|
291
344
|
- Parameters:
|
|
292
|
-
- `parent
|
|
293
|
-
- `child
|
|
294
|
-
- Returns: The merged object.
|
|
345
|
+
- `parent` [`<any>`][mdn-any] The parent object.
|
|
346
|
+
- `child` [`<any>`][mdn-any] The child object.
|
|
347
|
+
- Returns: [`<any>`][mdn-any] The merged object.
|
|
295
348
|
|
|
296
|
-
### `extend()`
|
|
349
|
+
### `JFather.extend(obj, [options])`
|
|
297
350
|
|
|
298
351
|
Extend `obj`, merge and override.
|
|
299
352
|
|
|
300
|
-
```JavaScript
|
|
301
|
-
JFather.extend(obj);
|
|
302
|
-
```
|
|
303
|
-
|
|
304
353
|
- Parameter:
|
|
305
|
-
- `obj
|
|
306
|
-
-
|
|
354
|
+
- `obj` [`<any>`][mdn-any] The object with any `$extends` properties.
|
|
355
|
+
- `options` [`<Object>`][mdn-object]
|
|
356
|
+
- `request` [`<Function>`][mdn-function] The function for getting a JSON
|
|
357
|
+
object remotely. By default, the object is got with [`fetch()`][mdn-fetch]
|
|
358
|
+
and [`Response.json()`][mdn-response-json].
|
|
359
|
+
- Returns: [`<Promise>`][mdn-promise] A promise with the extended object.
|
|
307
360
|
|
|
308
|
-
### `load()`
|
|
361
|
+
### `JFather.load(url, [options])`
|
|
309
362
|
|
|
310
|
-
Load from
|
|
311
|
-
|
|
312
|
-
```JavaScript
|
|
313
|
-
JFather.load(url);
|
|
314
|
-
```
|
|
363
|
+
Load from an `url`, extend, merge and override.
|
|
315
364
|
|
|
316
365
|
- Parameter:
|
|
317
|
-
- `url
|
|
318
|
-
|
|
366
|
+
- `url` [`<String>`][mdn-string] | [`<URL>`][mdn-url] The string containing
|
|
367
|
+
the URL of a JSON file.
|
|
368
|
+
- `options` [`<Object>`][mdn-object]
|
|
369
|
+
- `request` [`<Function>`][mdn-function] The function for getting a JSON
|
|
370
|
+
object remotely. By default, the object is got with [`fetch()`][mdn-fetch]
|
|
371
|
+
and [`Response.json()`][mdn-response-json].
|
|
372
|
+
- Returns: [`<Promise>`][mdn-promise] A promise with the loaded object.
|
|
319
373
|
|
|
320
|
-
### `parse()`
|
|
374
|
+
### `JFather.parse(text, [options])`
|
|
321
375
|
|
|
322
376
|
Parse a `text`, extend, merge and override.
|
|
323
377
|
|
|
324
|
-
```JavaScript
|
|
325
|
-
JFather.parse(text);
|
|
326
|
-
```
|
|
327
|
-
|
|
328
378
|
- Parameter:
|
|
329
|
-
- `text
|
|
330
|
-
-
|
|
331
|
-
|
|
332
|
-
[
|
|
333
|
-
[
|
|
334
|
-
[
|
|
335
|
-
|
|
379
|
+
- `text` [`<String>`][mdn-string] The string containing a JSON object.
|
|
380
|
+
- `options` [`<Object>`][mdn-object]
|
|
381
|
+
- `request` [`<Function>`][mdn-function] The function for getting a JSON
|
|
382
|
+
object remotely. By default, the object is got with [`fetch()`][mdn-fetch]
|
|
383
|
+
and [`Response.json()`][mdn-response-json].
|
|
384
|
+
- Returns: [`<Promise>`][mdn-promise] A promise with the parsed object.
|
|
385
|
+
|
|
386
|
+
[mdn-any]: https://developer.mozilla.org/Web/JavaScript/Data_structures
|
|
387
|
+
[mdn-function]:
|
|
388
|
+
https://developer.mozilla.org/JavaScript/Reference/Global_Objects/Function
|
|
389
|
+
[mdn-object]:
|
|
390
|
+
https://developer.mozilla.org/JavaScript/Reference/Global_Objects/Object
|
|
391
|
+
[mdn-promise]:
|
|
392
|
+
https://developer.mozilla.org/JavaScript/Reference/Global_Objects/Promise
|
|
393
|
+
[mdn-string]:
|
|
394
|
+
https://developer.mozilla.org/JavaScript/Reference/Global_Objects/String
|
|
395
|
+
[mdn-fetch]: https://developer.mozilla.org/Web/API/fetch
|
|
396
|
+
[mdn-response-json]: https://developer.mozilla.org/Web/API/Response/json
|
|
397
|
+
[mdn-url]: https://developer.mozilla.org/Web/API/URL
|
|
398
|
+
[img-npm]:
|
|
399
|
+
https://img.shields.io/npm/dm/jfather?label=npm&logo=npm&logoColor=whitesmoke
|
|
400
|
+
[img-build]:
|
|
401
|
+
https://img.shields.io/github/actions/workflow/status/regseb/jfather/ci.yml?branch=main&logo=github&logoColor=whitesmoke
|
|
402
|
+
[img-coverage]:
|
|
403
|
+
https://img.shields.io/endpoint?label=coverage&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fregseb%2Fjfather%2Fmain
|
|
404
|
+
[img-semver]:
|
|
405
|
+
https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&logoColor=whitesmoke
|
|
336
406
|
[link-npm]: https://www.npmjs.com/package/jfather
|
|
337
|
-
[link-build]:
|
|
338
|
-
|
|
407
|
+
[link-build]:
|
|
408
|
+
https://github.com/regseb/jfather/actions/workflows/ci.yml?query=branch%3Amain
|
|
409
|
+
[link-coverage]:
|
|
410
|
+
https://dashboard.stryker-mutator.io/reports/github.com/regseb/jfather/main
|
|
339
411
|
[link-semver]: https://semver.org/spec/v2.0.0.html "Semantic Versioning 2.0.0"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jfather",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "JSON with merge, extend and override.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"jfather",
|
|
@@ -18,7 +18,10 @@
|
|
|
18
18
|
},
|
|
19
19
|
"license": "MIT",
|
|
20
20
|
"author": "Sébastien Règne <regseb@gmail.com> (https://github.com/regseb)",
|
|
21
|
-
"funding":
|
|
21
|
+
"funding": [
|
|
22
|
+
"https://buymeacoffee.com/regseb",
|
|
23
|
+
"https://www.paypal.me/sebastienregne"
|
|
24
|
+
],
|
|
22
25
|
"files": [
|
|
23
26
|
"./src/",
|
|
24
27
|
"./types/"
|
|
@@ -39,42 +42,41 @@
|
|
|
39
42
|
"lint:fix": "metalint --fix",
|
|
40
43
|
"lint:types": "tsc --project .tsconfig_lint.json",
|
|
41
44
|
"test": "npm run test:coverage",
|
|
42
|
-
"test:unit": "
|
|
45
|
+
"test:unit": "node --test",
|
|
43
46
|
"test:coverage": "stryker run",
|
|
44
47
|
"jsdocs": "typedoc --tsconfig .tsconfig_jsdocs.json",
|
|
45
48
|
"prepare": "tsc --project .tsconfig_types.json",
|
|
46
49
|
"clean": "node .script/clean.js"
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
|
-
"@prantlf/jsonlint": "
|
|
50
|
-
"@prettier/plugin-xml": "3.
|
|
51
|
-
"@stryker-mutator/core": "8.
|
|
52
|
-
"@stryker-mutator/
|
|
53
|
-
"@types/mocha": "10.0
|
|
54
|
-
"@types/node": "
|
|
55
|
-
"
|
|
56
|
-
"eslint": "
|
|
57
|
-
"eslint-plugin-array-func": "4.0.0",
|
|
52
|
+
"@prantlf/jsonlint": "16.0.0",
|
|
53
|
+
"@prettier/plugin-xml": "3.4.1",
|
|
54
|
+
"@stryker-mutator/core": "8.7.1",
|
|
55
|
+
"@stryker-mutator/tap-runner": "8.7.1",
|
|
56
|
+
"@types/eslint-plugin-mocha": "10.4.0",
|
|
57
|
+
"@types/node": "22.10.5",
|
|
58
|
+
"eslint": "9.17.0",
|
|
59
|
+
"eslint-plugin-array-func": "5.0.2",
|
|
58
60
|
"eslint-plugin-eslint-comments": "3.2.0",
|
|
59
|
-
"eslint-plugin-import": "2.
|
|
60
|
-
"eslint-plugin-jsdoc": "
|
|
61
|
-
"eslint-plugin-mocha": "10.
|
|
62
|
-
"eslint-plugin-n": "
|
|
63
|
-
"eslint-plugin-no-unsanitized": "4.
|
|
64
|
-
"eslint-plugin-promise": "
|
|
65
|
-
"eslint-plugin-regexp": "2.
|
|
66
|
-
"eslint-plugin-unicorn": "
|
|
67
|
-
"
|
|
68
|
-
"
|
|
69
|
-
"
|
|
70
|
-
"npm-package-json-lint": "
|
|
71
|
-
"prettier": "3.2
|
|
72
|
-
"
|
|
73
|
-
"typedoc": "0.
|
|
74
|
-
"typescript": "5.
|
|
61
|
+
"eslint-plugin-import": "2.31.0",
|
|
62
|
+
"eslint-plugin-jsdoc": "50.6.1",
|
|
63
|
+
"eslint-plugin-mocha": "10.5.0",
|
|
64
|
+
"eslint-plugin-n": "17.15.1",
|
|
65
|
+
"eslint-plugin-no-unsanitized": "4.1.2",
|
|
66
|
+
"eslint-plugin-promise": "7.2.1",
|
|
67
|
+
"eslint-plugin-regexp": "2.7.0",
|
|
68
|
+
"eslint-plugin-unicorn": "56.0.1",
|
|
69
|
+
"globals": "15.14.0",
|
|
70
|
+
"markdownlint": "0.37.3",
|
|
71
|
+
"metalint": "0.19.0",
|
|
72
|
+
"npm-package-json-lint": "8.0.0",
|
|
73
|
+
"prettier": "3.4.2",
|
|
74
|
+
"publint": "0.2.12",
|
|
75
|
+
"typedoc": "0.27.6",
|
|
76
|
+
"typescript": "5.7.2",
|
|
75
77
|
"yaml-lint": "1.7.0"
|
|
76
78
|
},
|
|
77
79
|
"engines": {
|
|
78
|
-
"node": ">=20.
|
|
80
|
+
"node": ">=20.18"
|
|
79
81
|
}
|
|
80
82
|
}
|
package/src/jfather.js
CHANGED
|
@@ -4,6 +4,15 @@
|
|
|
4
4
|
* @author Sébastien Règne
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Les options des fonctions de JFather.
|
|
9
|
+
*
|
|
10
|
+
* @typedef {Object} Options
|
|
11
|
+
* @prop {Function} [request] La fonction pour récupérer un objet JSON à
|
|
12
|
+
* distance. Par défaut, l'objet est récupéré avec
|
|
13
|
+
* `fetch()` et `Response.json()`.
|
|
14
|
+
*/
|
|
15
|
+
|
|
7
16
|
/**
|
|
8
17
|
* Exécute une fonction sur un objet et tous ses sous-objets (en partant des
|
|
9
18
|
* objets les plus profonds).
|
|
@@ -80,11 +89,11 @@ export const query = function (obj, chain) {
|
|
|
80
89
|
return obj;
|
|
81
90
|
}
|
|
82
91
|
|
|
83
|
-
const re = /^\.(?<prop>\w+)|^\[(?<index>\d+)\]/
|
|
92
|
+
const re = /^\.(?<prop>\w+)|^\[(?<index>\d+)\]/v;
|
|
84
93
|
const sub = {
|
|
85
94
|
obj,
|
|
86
95
|
// Préfixer le chemin avec un point si nécessaire.
|
|
87
|
-
chain: /^[
|
|
96
|
+
chain: /^[.\[]/v.test(chain) ? chain : "." + chain,
|
|
88
97
|
};
|
|
89
98
|
while (0 !== sub.chain.length) {
|
|
90
99
|
const result = re.exec(sub.chain);
|
|
@@ -109,7 +118,11 @@ export const query = function (obj, chain) {
|
|
|
109
118
|
* @returns {any} La fusion des deux objets.
|
|
110
119
|
*/
|
|
111
120
|
export const merge = function (parent, child) {
|
|
112
|
-
if (
|
|
121
|
+
if (
|
|
122
|
+
child === parent ||
|
|
123
|
+
Object !== parent?.constructor ||
|
|
124
|
+
Object !== child?.constructor
|
|
125
|
+
) {
|
|
113
126
|
return clone(child);
|
|
114
127
|
}
|
|
115
128
|
|
|
@@ -140,7 +153,7 @@ export const merge = function (parent, child) {
|
|
|
140
153
|
if (Array.isArray(overridden[key])) {
|
|
141
154
|
const overelemRegex = new RegExp(
|
|
142
155
|
`^\\$${key}\\[(?<index>\\d*)\\]$`,
|
|
143
|
-
"
|
|
156
|
+
"v",
|
|
144
157
|
);
|
|
145
158
|
const overelems = Object.entries(child)
|
|
146
159
|
.map(([k, v]) => [overelemRegex.exec(k)?.groups?.index, v])
|
|
@@ -161,50 +174,59 @@ export const merge = function (parent, child) {
|
|
|
161
174
|
};
|
|
162
175
|
|
|
163
176
|
/**
|
|
164
|
-
* Étendre un objet JSON en utilisant
|
|
177
|
+
* Étendre un objet JSON en utilisant la propriété `"$extends"`.
|
|
165
178
|
*
|
|
166
|
-
* @param {Record<string, any>} obj
|
|
179
|
+
* @param {Record<string, any>} obj L'objet qui sera étendu.
|
|
180
|
+
* @param {Options} [options] Les options.
|
|
167
181
|
* @returns {Promise<Record<string, any>>} Une promesse contenant l'objet
|
|
168
182
|
* étendu.
|
|
169
183
|
*/
|
|
170
|
-
export const inherit = async function (obj) {
|
|
184
|
+
export const inherit = async function (obj, options) {
|
|
171
185
|
if (undefined === obj.$extends) {
|
|
172
186
|
return obj;
|
|
173
187
|
}
|
|
174
188
|
|
|
175
189
|
// eslint-disable-next-line no-use-before-define
|
|
176
|
-
return merge(await load(obj.$extends), obj);
|
|
190
|
+
return merge(await load(obj.$extends, options), obj);
|
|
177
191
|
};
|
|
178
192
|
|
|
179
193
|
/**
|
|
180
194
|
* Étendre un objet récursivement.
|
|
181
195
|
*
|
|
182
|
-
* @param {any}
|
|
196
|
+
* @param {any} obj L'objet qui sera étendu.
|
|
197
|
+
* @param {Options} [options] Les options.
|
|
183
198
|
* @returns {Promise<any>} Une promesse contenant l'objet étendu.
|
|
184
199
|
*/
|
|
185
|
-
export const extend = function (obj) {
|
|
186
|
-
return walkAsync(obj, inherit);
|
|
200
|
+
export const extend = function (obj, options) {
|
|
201
|
+
return walkAsync(obj, (/** @type {any} */ v) => inherit(v, options));
|
|
187
202
|
};
|
|
188
203
|
|
|
189
204
|
/**
|
|
190
205
|
* Charge un objet JSON depuis une URL.
|
|
191
206
|
*
|
|
192
|
-
* @param {string} url
|
|
207
|
+
* @param {string|URL} url L'URL du fichier JSON.
|
|
208
|
+
* @param {Options} [options] Les options.
|
|
193
209
|
* @returns {Promise<any>} Une promesse contenant l'objet.
|
|
194
210
|
*/
|
|
195
|
-
export const load = async function (url) {
|
|
196
|
-
|
|
197
|
-
|
|
211
|
+
export const load = async function (url, options) {
|
|
212
|
+
let json;
|
|
213
|
+
if (undefined === options?.request) {
|
|
214
|
+
const response = await fetch(url);
|
|
215
|
+
json = await response.json();
|
|
216
|
+
} else {
|
|
217
|
+
json = await options.request(url);
|
|
218
|
+
}
|
|
198
219
|
// Enlever le "#" dans le hash de l'URL.
|
|
199
|
-
return await extend(query(json, new URL(url).hash.slice(1)));
|
|
220
|
+
return await extend(query(json, new URL(url).hash.slice(1)), options);
|
|
200
221
|
};
|
|
201
222
|
|
|
202
223
|
/**
|
|
203
224
|
* Parse une chaine de caractères.
|
|
204
225
|
*
|
|
205
|
-
* @param {string}
|
|
226
|
+
* @param {string} text La chaine de caractères qui sera parsée.
|
|
227
|
+
* @param {Options} [options] Les options.
|
|
206
228
|
* @returns {Promise<any>} L'objet.
|
|
207
229
|
*/
|
|
208
|
-
export const parse = function (text) {
|
|
209
|
-
return extend(JSON.parse(text));
|
|
230
|
+
export const parse = function (text, options) {
|
|
231
|
+
return extend(JSON.parse(text), options);
|
|
210
232
|
};
|
package/types/jfather.d.ts
CHANGED
|
@@ -3,7 +3,18 @@ export function walkAsync(obj: any, fn: Function): Promise<any>;
|
|
|
3
3
|
export function clone(obj: any): any;
|
|
4
4
|
export function query(obj: Record<string, any>, chain: string): any;
|
|
5
5
|
export function merge(parent: any, child: any): any;
|
|
6
|
-
export function inherit(obj: Record<string, any
|
|
7
|
-
export function extend(obj: any): Promise<any>;
|
|
8
|
-
export function load(url: string): Promise<any>;
|
|
9
|
-
export function parse(text: string): Promise<any>;
|
|
6
|
+
export function inherit(obj: Record<string, any>, options?: Options): Promise<Record<string, any>>;
|
|
7
|
+
export function extend(obj: any, options?: Options): Promise<any>;
|
|
8
|
+
export function load(url: string | URL, options?: Options): Promise<any>;
|
|
9
|
+
export function parse(text: string, options?: Options): Promise<any>;
|
|
10
|
+
/**
|
|
11
|
+
* Les options des fonctions de JFather.
|
|
12
|
+
*/
|
|
13
|
+
export type Options = {
|
|
14
|
+
/**
|
|
15
|
+
* La fonction pour récupérer un objet JSON à
|
|
16
|
+
* distance. Par défaut, l'objet est récupéré avec
|
|
17
|
+
* `fetch()` et `Response.json()`.
|
|
18
|
+
*/
|
|
19
|
+
request?: Function;
|
|
20
|
+
};
|