jfather 0.1.0 → 0.3.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 +301 -54
- package/package.json +22 -18
- package/src/index.js +2 -2
- package/src/jfather.js +57 -29
- package/types/index.d.ts +2 -0
- package/types/jfather.d.ts +16 -4
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -10,104 +10,351 @@
|
|
|
10
10
|
[![coverage][img-coverage]][link-coverage]
|
|
11
11
|
[![semver][img-semver]][link-semver]
|
|
12
12
|
|
|
13
|
-
>
|
|
13
|
+
> _Boys use JSON; Men use JFather._
|
|
14
14
|
|
|
15
15
|
## Overview
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
[JSON](https://www.json.org/json-
|
|
19
|
-
features to merge, extend and override JSON objects.
|
|
17
|
+
JFather is a utility library to **merge**, **extend** and **override**
|
|
18
|
+
[JSON](https://www.json.org/json-en.html "JavaScript Object Notation") objects.
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
<!-- prettier-ignore-start -->
|
|
21
|
+
```javascript
|
|
22
22
|
import JFather from "jfather";
|
|
23
23
|
|
|
24
|
+
// Merge two objects.
|
|
24
25
|
const merged = JFather.merge(
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
{ "foo": "a", "bar": "alpha" },
|
|
27
|
+
{ "foo": "b", "baz": "beta" }
|
|
27
28
|
);
|
|
28
|
-
|
|
29
29
|
console.log(merged);
|
|
30
|
-
// { foo: "b", bar: "alpha", baz: "beta" }
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## Install
|
|
30
|
+
// { "foo": "b", "bar": "alpha", "baz": "beta" }
|
|
34
31
|
|
|
35
|
-
|
|
32
|
+
// Extend an object.
|
|
33
|
+
const extended = await JFather.extend({
|
|
34
|
+
"$extends": "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json#members[1]",
|
|
35
|
+
"age": 34,
|
|
36
|
+
"quote": "With great fist comes great KO"
|
|
37
|
+
});
|
|
38
|
+
console.log(extended);
|
|
39
|
+
// {
|
|
40
|
+
// "name": "Madame Uppercut",
|
|
41
|
+
// "age": 34,
|
|
42
|
+
// "secretIdentity": "Jane Wilson",
|
|
43
|
+
// "powers": [
|
|
44
|
+
// "Million tonne punch",
|
|
45
|
+
// "Damage resistance",
|
|
46
|
+
// "Superhuman reflexes"
|
|
47
|
+
// ],
|
|
48
|
+
// "quote": "With great fist comes great KO"
|
|
49
|
+
// }
|
|
36
50
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
51
|
+
// Override an object.
|
|
52
|
+
const overridden = await JFather.merge(
|
|
53
|
+
{ "foo": ["a", "alpha"] },
|
|
54
|
+
{ "$foo[0]": "A", "$foo[]": ["BETA"] }
|
|
55
|
+
);
|
|
56
|
+
console.log(overridden);
|
|
57
|
+
// {
|
|
58
|
+
// "foo": ["A", "alpha", "BETA"]
|
|
59
|
+
// }
|
|
46
60
|
|
|
47
|
-
|
|
48
|
-
|
|
61
|
+
// Extend, merge and override an object.
|
|
62
|
+
const allIn = await JFather.extend({
|
|
63
|
+
"$extends": "https://mdn.github.io/learning-area/javascript/oojs/json/superheroes.json#members[0]",
|
|
64
|
+
"age": 27,
|
|
65
|
+
"$powers[2]": "Atomic breath",
|
|
66
|
+
"$powers[]": ["Matter Creation", "Reality Warping"],
|
|
67
|
+
"quote": "I'm no God. I'm not even a man. I'm just Molecule Man."
|
|
68
|
+
});
|
|
69
|
+
console.log(allIn);
|
|
70
|
+
// {
|
|
71
|
+
// "name": "Molecule Man",
|
|
72
|
+
// "age": 27,
|
|
73
|
+
// "secretIdentity": "Dan Jukes",
|
|
74
|
+
// "powers": [
|
|
75
|
+
// "Radiation resistance",
|
|
76
|
+
// "Turning tiny",
|
|
77
|
+
// "Atomic breath",
|
|
78
|
+
// "Matter Creation",
|
|
79
|
+
// "Reality Warping
|
|
80
|
+
// ],
|
|
81
|
+
// "quote": "I'm no God. I'm not even a man. I'm just Molecule Man."
|
|
82
|
+
// }
|
|
49
83
|
```
|
|
84
|
+
<!-- prettier-ignore-end -->
|
|
50
85
|
|
|
51
|
-
|
|
86
|
+
## Installation
|
|
52
87
|
|
|
53
|
-
|
|
54
|
-
[esm.sh](https://esm.sh/jfather)
|
|
88
|
+
JFather is published on [npm][link-npm] (its CDN:
|
|
89
|
+
[esm.sh](https://esm.sh/jfather),
|
|
55
90
|
[jsDelivr](https://www.jsdelivr.com/package/npm/jfather),
|
|
56
|
-
[UNPKG](https://unpkg.com/browse/jfather/))
|
|
91
|
+
[UNPKG](https://unpkg.com/browse/jfather/)) and
|
|
92
|
+
[Deno](https://deno.land/x/jfather).
|
|
93
|
+
|
|
94
|
+
```javascript
|
|
95
|
+
// Node.js and Bun (after `npm install jfather`):
|
|
96
|
+
import JFather from "jfather";
|
|
57
97
|
|
|
58
|
-
|
|
98
|
+
// Browsers:
|
|
59
99
|
import JFather from "https://esm.sh/jfather@0";
|
|
60
|
-
|
|
61
|
-
|
|
100
|
+
import JFather from "https://cdn.jsdelivr.net/npm/jfather@0";
|
|
101
|
+
import JFather from "https://unpkg.com/jfather@0";
|
|
102
|
+
|
|
103
|
+
// Deno:
|
|
104
|
+
import JFather from "https://deno.land/x/jfather/mod.js";
|
|
62
105
|
```
|
|
63
106
|
|
|
64
|
-
##
|
|
107
|
+
## Features
|
|
65
108
|
|
|
66
|
-
|
|
67
|
-
- [`load()`](#load)
|
|
68
|
-
- [`parse()`](#parse)
|
|
109
|
+
### Merge
|
|
69
110
|
|
|
70
|
-
|
|
111
|
+
<!-- markdownlint-disable no-inline-html -->
|
|
112
|
+
<table>
|
|
113
|
+
<tr>
|
|
114
|
+
<th><code>parent</code></th>
|
|
115
|
+
<th><code>child</code></th>
|
|
116
|
+
<th><code>JFather.merge(parent, child)</code></th>
|
|
117
|
+
</tr>
|
|
118
|
+
<tr>
|
|
119
|
+
<td><pre lang="json"><code>1</code></pre></td>
|
|
120
|
+
<td><pre lang="json"><code>2</code></pre></td>
|
|
121
|
+
<td><pre lang="json"><code>2</code></pre></td>
|
|
122
|
+
</tr>
|
|
123
|
+
<tr>
|
|
124
|
+
<td><pre lang="json"><code>{
|
|
125
|
+
"foo": "alpha",
|
|
126
|
+
"bar": "ALPHA"
|
|
127
|
+
}</code></pre></td>
|
|
128
|
+
<td><pre lang="json"><code>{
|
|
129
|
+
"foo": "beta",
|
|
130
|
+
"baz": "BETA"
|
|
131
|
+
}</code></pre></td>
|
|
132
|
+
<td><pre lang="json"><code>{
|
|
133
|
+
"foo": "beta",
|
|
134
|
+
"bar": "ALPHA",
|
|
135
|
+
"baz": "BETA"
|
|
136
|
+
}</code></pre></td>
|
|
137
|
+
</tr>
|
|
138
|
+
<tr>
|
|
139
|
+
<td><pre lang="json"><code>{
|
|
140
|
+
"foo": [1, 10, 11]
|
|
141
|
+
}</code></pre></td>
|
|
142
|
+
<td><pre lang="json"><code>{
|
|
143
|
+
"foo": [2, 20, 22]
|
|
144
|
+
}</code></pre></td>
|
|
145
|
+
<td><pre lang="json"><code>{
|
|
146
|
+
"foo": [2, 20, 22]
|
|
147
|
+
}</code></pre></td>
|
|
148
|
+
</tr>
|
|
149
|
+
</table>
|
|
150
|
+
<!-- markdownlint-enable no-inline-html -->
|
|
71
151
|
|
|
72
|
-
|
|
152
|
+
### Extend
|
|
73
153
|
|
|
74
154
|
<!-- markdownlint-disable no-inline-html -->
|
|
75
155
|
<table>
|
|
76
156
|
<tr>
|
|
77
|
-
<th><code>
|
|
78
|
-
<th><code>
|
|
79
|
-
<th><code>JFather.
|
|
157
|
+
<th><code>https://foo.bar/parent.json</code></th>
|
|
158
|
+
<th><code>child</code></th>
|
|
159
|
+
<th><code>await JFather.extend(child)</code></th>
|
|
160
|
+
</tr>
|
|
161
|
+
<tr>
|
|
162
|
+
<td><pre lang="json"><code>{
|
|
163
|
+
"baz": "qux"
|
|
164
|
+
}</code></pre></td>
|
|
165
|
+
<td><pre lang="json"><code>{
|
|
166
|
+
"$extends": "https://foo.bar/parent.json"
|
|
167
|
+
}</code></pre></td>
|
|
168
|
+
<td><pre lang="json"><code>{
|
|
169
|
+
"baz": "qux"
|
|
170
|
+
}</code></pre></td>
|
|
80
171
|
</tr>
|
|
81
172
|
<tr>
|
|
82
|
-
<td><code>
|
|
83
|
-
|
|
84
|
-
|
|
173
|
+
<td><pre lang="json"><code>{
|
|
174
|
+
"baz": "qux"
|
|
175
|
+
}</code></pre></td>
|
|
176
|
+
<td><pre lang="json"><code>{
|
|
177
|
+
"$extends": "https://foo.bar/parent.json",
|
|
178
|
+
"baz": "quux"
|
|
179
|
+
}</code></pre></td>
|
|
180
|
+
<td><pre lang="json"><code>{
|
|
181
|
+
"baz": "quux"
|
|
182
|
+
}</code></pre></td>
|
|
85
183
|
</tr>
|
|
86
184
|
<tr>
|
|
87
|
-
<td><
|
|
88
|
-
|
|
89
|
-
|
|
185
|
+
<td><pre lang="json"><code>{
|
|
186
|
+
"baz": "qux"
|
|
187
|
+
}</code></pre></td>
|
|
188
|
+
<td><pre lang="json"><code>{
|
|
189
|
+
"$extends": "https://foo.bar/parent.json",
|
|
190
|
+
"quux": "corge"
|
|
191
|
+
}</code></pre></td>
|
|
192
|
+
<td><pre lang="json"><code>{
|
|
193
|
+
"baz": "qux",
|
|
194
|
+
"quux": "corge"
|
|
195
|
+
}</code></pre></td>
|
|
90
196
|
</tr>
|
|
197
|
+
<tr>
|
|
198
|
+
<td><pre lang="json"><code>{
|
|
199
|
+
"baz": "qux"
|
|
200
|
+
}</code></pre></td>
|
|
201
|
+
<td><pre lang="json"><code>{
|
|
202
|
+
"quux": {
|
|
203
|
+
"$extends": "https://foo.bar/parent.json",
|
|
204
|
+
"corge": "grault"
|
|
205
|
+
}
|
|
206
|
+
}</code></pre></td>
|
|
207
|
+
<td><pre lang="json"><code>{
|
|
208
|
+
"quux": {
|
|
209
|
+
"baz": "qux",
|
|
210
|
+
"corge": "grault"
|
|
211
|
+
}
|
|
212
|
+
}</code></pre></td>
|
|
91
213
|
</tr>
|
|
92
214
|
<tr>
|
|
93
|
-
<td><code>{
|
|
94
|
-
|
|
95
|
-
|
|
215
|
+
<td><pre lang="json"><code>{
|
|
216
|
+
"baz": {
|
|
217
|
+
"qux": [1, 2],
|
|
218
|
+
"quux": "a"
|
|
219
|
+
},
|
|
220
|
+
"corge": true
|
|
221
|
+
}</code></pre></td>
|
|
222
|
+
<td><pre lang="json"><code>{
|
|
223
|
+
"$extends": "https://foo.bar/parent.json#baz"
|
|
224
|
+
}</code></pre></td>
|
|
225
|
+
<td><pre lang="json"><code>{
|
|
226
|
+
"qux": [1, 2],
|
|
227
|
+
"quux": "a"
|
|
228
|
+
}</code></pre></td>
|
|
96
229
|
</tr>
|
|
97
230
|
</table>
|
|
98
231
|
<!-- markdownlint-enable no-inline-html -->
|
|
99
232
|
|
|
100
|
-
###
|
|
233
|
+
### Override
|
|
101
234
|
|
|
102
|
-
|
|
235
|
+
<!-- markdownlint-disable no-inline-html -->
|
|
236
|
+
<table>
|
|
237
|
+
<tr>
|
|
238
|
+
<th><code>parent</code></th>
|
|
239
|
+
<th><code>child</code></th>
|
|
240
|
+
<th><code>JFather.merge(parent, child)</code></th>
|
|
241
|
+
</tr>
|
|
242
|
+
<tr>
|
|
243
|
+
<td><pre lang="json"><code>{
|
|
244
|
+
"foo": ["a", "Alpha"]
|
|
245
|
+
}</code></pre></td>
|
|
246
|
+
<td><pre lang="json"><code>{
|
|
247
|
+
"$foo[]": ["b", "Beta"]
|
|
248
|
+
}</code></pre></td>
|
|
249
|
+
<td><pre lang="json"><code>{
|
|
250
|
+
"foo": ["a", "Alpha", "b", "Beta"]
|
|
251
|
+
}</code></pre></td>
|
|
252
|
+
</tr>
|
|
253
|
+
<tr>
|
|
254
|
+
<td><pre lang="json"><code>{
|
|
255
|
+
"foo": ["a", "Alpha"]
|
|
256
|
+
}</code></pre></td>
|
|
257
|
+
<td><pre lang="json"><code>{
|
|
258
|
+
"$foo[0]": "A"
|
|
259
|
+
}</code></pre></td>
|
|
260
|
+
<td><pre lang="json"><code>{
|
|
261
|
+
"foo": ["A", "Alpha"]
|
|
262
|
+
}</code></pre></td>
|
|
263
|
+
</tr>
|
|
264
|
+
<tr>
|
|
265
|
+
<td><pre lang="json"><code>{
|
|
266
|
+
"foo": [{
|
|
267
|
+
"bar": ["a"]
|
|
268
|
+
}]
|
|
269
|
+
}</code></pre></td>
|
|
270
|
+
<td><pre lang="json"><code>{
|
|
271
|
+
"$foo[0]": {
|
|
272
|
+
"$bar[]": ["b", "c"]
|
|
273
|
+
}
|
|
274
|
+
}</code></pre></td>
|
|
275
|
+
<td><pre lang="json"><code>{
|
|
276
|
+
"foo": [{
|
|
277
|
+
"bar": ["a", "b", "c"]
|
|
278
|
+
}]
|
|
279
|
+
}</code></pre></td>
|
|
280
|
+
</tr>
|
|
281
|
+
</table>
|
|
282
|
+
<!-- markdownlint-enable no-inline-html -->
|
|
283
|
+
|
|
284
|
+
## API
|
|
285
|
+
|
|
286
|
+
- [`merge()`](#merge)
|
|
287
|
+
- [`extend()`](#extend)
|
|
288
|
+
- [`load()`](#load)
|
|
289
|
+
- [`parse()`](#parse)
|
|
290
|
+
|
|
291
|
+
### `merge()`
|
|
103
292
|
|
|
104
|
-
|
|
293
|
+
Merge and override `parent` with `child`.
|
|
294
|
+
|
|
295
|
+
```javascript
|
|
296
|
+
JFather.merge(parent, child);
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
- Parameters:
|
|
300
|
+
- `parent`: The parent object.
|
|
301
|
+
- `child`: The child object.
|
|
302
|
+
- Returns: The merged object.
|
|
303
|
+
|
|
304
|
+
### `extend()`
|
|
305
|
+
|
|
306
|
+
Extend `obj`, merge and override.
|
|
307
|
+
|
|
308
|
+
```javascript
|
|
309
|
+
JFather.extend(obj, [options]);
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
- Parameter:
|
|
313
|
+
- `obj`: The object with any `$extends` properties.
|
|
314
|
+
- `options`:
|
|
315
|
+
- `request`: The function for getting a JSON object remotely. By default,
|
|
316
|
+
the object is got with
|
|
317
|
+
[`fetch()`](https://developer.mozilla.org/Web/API/fetch) and
|
|
318
|
+
[`Response.json()`](https://developer.mozilla.org/Web/API/Response/json).
|
|
319
|
+
- Returns: A promise with the extended object.
|
|
320
|
+
|
|
321
|
+
### `load()`
|
|
322
|
+
|
|
323
|
+
Load from a `url`, extend, merge and override.
|
|
324
|
+
|
|
325
|
+
```javascript
|
|
326
|
+
JFather.load(url, [options]);
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
- Parameter:
|
|
330
|
+
- `url`: The string containing the URL of a JSON file.
|
|
331
|
+
- `options`:
|
|
332
|
+
- `request`: The function for getting a JSON object remotely. By default,
|
|
333
|
+
the object is got with
|
|
334
|
+
[`fetch()`](https://developer.mozilla.org/Web/API/fetch) and
|
|
335
|
+
[`Response.json()`](https://developer.mozilla.org/Web/API/Response/json).
|
|
336
|
+
- Returns: A promise with the loaded object.
|
|
337
|
+
|
|
338
|
+
### `parse()`
|
|
339
|
+
|
|
340
|
+
Parse a `text`, extend, merge and override.
|
|
341
|
+
|
|
342
|
+
```javascript
|
|
343
|
+
JFather.parse(text, [options]);
|
|
344
|
+
```
|
|
105
345
|
|
|
106
|
-
|
|
346
|
+
- Parameter:
|
|
347
|
+
- `text`: The string containing a JSON object.
|
|
348
|
+
- `options`:
|
|
349
|
+
- `request`: The function for getting a JSON object remotely. By default,
|
|
350
|
+
the object is got with
|
|
351
|
+
[`fetch()`](https://developer.mozilla.org/Web/API/fetch) and
|
|
352
|
+
[`Response.json()`](https://developer.mozilla.org/Web/API/Response/json).
|
|
353
|
+
- Returns: A promise with the parsed object.
|
|
107
354
|
|
|
108
355
|
[img-npm]: https://img.shields.io/npm/dm/jfather?label=npm&logo=npm&logoColor=whitesmoke
|
|
109
356
|
[img-build]: https://img.shields.io/github/actions/workflow/status/regseb/jfather/ci.yml?branch=main&logo=github&logoColor=whitesmoke
|
|
110
|
-
[img-coverage]: https://img.shields.io/endpoint?label=coverage&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fregseb%2Fjfather%2Fmain
|
|
357
|
+
[img-coverage]: https://img.shields.io/endpoint?label=coverage&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fregseb%2Fjfather%2Fmain
|
|
111
358
|
[img-semver]: https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&logoColor=whitesmoke
|
|
112
359
|
[link-npm]: https://www.npmjs.com/package/jfather
|
|
113
360
|
[link-build]: https://github.com/regseb/jfather/actions/workflows/ci.yml?query=branch%3Amain
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jfather",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.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/"
|
|
@@ -47,31 +50,32 @@
|
|
|
47
50
|
},
|
|
48
51
|
"devDependencies": {
|
|
49
52
|
"@prantlf/jsonlint": "14.0.3",
|
|
50
|
-
"@prettier/plugin-xml": "3.
|
|
51
|
-
"@stryker-mutator/core": "8.2.
|
|
52
|
-
"@stryker-mutator/mocha-runner": "8.2.
|
|
53
|
+
"@prettier/plugin-xml": "3.4.1",
|
|
54
|
+
"@stryker-mutator/core": "8.2.6",
|
|
55
|
+
"@stryker-mutator/mocha-runner": "8.2.6",
|
|
53
56
|
"@types/mocha": "10.0.6",
|
|
54
|
-
"@types/node": "20.
|
|
57
|
+
"@types/node": "20.12.12",
|
|
55
58
|
"@types/sinon": "17.0.3",
|
|
56
|
-
"eslint": "8.
|
|
59
|
+
"eslint": "8.57.0",
|
|
57
60
|
"eslint-plugin-array-func": "4.0.0",
|
|
58
61
|
"eslint-plugin-eslint-comments": "3.2.0",
|
|
59
62
|
"eslint-plugin-import": "2.29.1",
|
|
60
|
-
"eslint-plugin-jsdoc": "48.
|
|
61
|
-
"eslint-plugin-mocha": "10.
|
|
62
|
-
"eslint-plugin-n": "
|
|
63
|
+
"eslint-plugin-jsdoc": "48.2.5",
|
|
64
|
+
"eslint-plugin-mocha": "10.4.3",
|
|
65
|
+
"eslint-plugin-n": "17.7.0",
|
|
63
66
|
"eslint-plugin-no-unsanitized": "4.0.2",
|
|
64
67
|
"eslint-plugin-promise": "6.1.1",
|
|
65
|
-
"eslint-plugin-regexp": "2.
|
|
66
|
-
"eslint-plugin-unicorn": "
|
|
67
|
-
"markdownlint": "0.
|
|
68
|
-
"metalint": "0.
|
|
69
|
-
"mocha": "10.
|
|
68
|
+
"eslint-plugin-regexp": "2.6.0",
|
|
69
|
+
"eslint-plugin-unicorn": "53.0.0",
|
|
70
|
+
"markdownlint": "0.34.0",
|
|
71
|
+
"metalint": "0.17.0",
|
|
72
|
+
"mocha": "10.4.0",
|
|
70
73
|
"npm-package-json-lint": "7.1.0",
|
|
71
74
|
"prettier": "3.2.5",
|
|
72
|
-
"
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
+
"publint": "0.2.8",
|
|
76
|
+
"sinon": "18.0.0",
|
|
77
|
+
"typedoc": "0.25.13",
|
|
78
|
+
"typescript": "5.4.5",
|
|
75
79
|
"yaml-lint": "1.7.0"
|
|
76
80
|
},
|
|
77
81
|
"engines": {
|
package/src/index.js
CHANGED
package/src/jfather.js
CHANGED
|
@@ -4,6 +4,16 @@
|
|
|
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
|
+
* <code>fetch()</code> et
|
|
14
|
+
* <code>Response.prototype.json()</code>
|
|
15
|
+
*/
|
|
16
|
+
|
|
7
17
|
/**
|
|
8
18
|
* Exécute une fonction sur un objet et tous ses sous-objets (en partant des
|
|
9
19
|
* objets les plus profonds).
|
|
@@ -38,7 +48,7 @@ export const walk = function (obj, fn) {
|
|
|
38
48
|
*/
|
|
39
49
|
export const walkAsync = async function (obj, fn) {
|
|
40
50
|
if (Object === obj?.constructor) {
|
|
41
|
-
return fn(
|
|
51
|
+
return await fn(
|
|
42
52
|
Object.fromEntries(
|
|
43
53
|
await Promise.all(
|
|
44
54
|
Object.entries(obj).map(async ([key, value]) => [
|
|
@@ -51,7 +61,7 @@ export const walkAsync = async function (obj, fn) {
|
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
if (Array.isArray(obj)) {
|
|
54
|
-
return Promise.all(obj.map((v) => walkAsync(v, fn)));
|
|
64
|
+
return await Promise.all(obj.map((v) => walkAsync(v, fn)));
|
|
55
65
|
}
|
|
56
66
|
|
|
57
67
|
return obj;
|
|
@@ -76,8 +86,16 @@ export const clone = function (obj) {
|
|
|
76
86
|
* @throws {TypeError} Si le chemin est invalide.
|
|
77
87
|
*/
|
|
78
88
|
export const query = function (obj, chain) {
|
|
89
|
+
if ("" === chain) {
|
|
90
|
+
return obj;
|
|
91
|
+
}
|
|
92
|
+
|
|
79
93
|
const re = /^\.(?<prop>\w+)|^\[(?<index>\d+)\]/u;
|
|
80
|
-
const sub = {
|
|
94
|
+
const sub = {
|
|
95
|
+
obj,
|
|
96
|
+
// Préfixer le chemin avec un point si nécessaire.
|
|
97
|
+
chain: /^[.[]/u.test(chain) ? chain : "." + chain,
|
|
98
|
+
};
|
|
81
99
|
while (0 !== sub.chain.length) {
|
|
82
100
|
const result = re.exec(sub.chain);
|
|
83
101
|
if (undefined !== result?.groups?.prop) {
|
|
@@ -105,7 +123,7 @@ export const merge = function (parent, child) {
|
|
|
105
123
|
return clone(child);
|
|
106
124
|
}
|
|
107
125
|
|
|
108
|
-
const
|
|
126
|
+
const overridden = /** @type {Record<string, any>} */ ({});
|
|
109
127
|
for (const key of new Set([
|
|
110
128
|
...Object.keys(parent),
|
|
111
129
|
...Object.keys(child),
|
|
@@ -118,18 +136,18 @@ export const merge = function (parent, child) {
|
|
|
118
136
|
// Si la propriété est dans les deux objets : fusionner les deux
|
|
119
137
|
// valeurs.
|
|
120
138
|
if (key in parent && key in child) {
|
|
121
|
-
|
|
139
|
+
overridden[key] = merge(parent[key], child[key]);
|
|
122
140
|
// Si la propriété est seulement dans l'objet parent.
|
|
123
141
|
} else if (key in parent) {
|
|
124
|
-
|
|
142
|
+
overridden[key] = clone(parent[key]);
|
|
125
143
|
// Si la propriété est seulement dans l'objet enfant.
|
|
126
144
|
} else {
|
|
127
|
-
|
|
145
|
+
overridden[key] = clone(child[key]);
|
|
128
146
|
}
|
|
129
147
|
|
|
130
148
|
// Si la valeur est un tableau : chercher si l'objet enfant a des
|
|
131
149
|
// surcharges d'éléments.
|
|
132
|
-
if (Array.isArray(
|
|
150
|
+
if (Array.isArray(overridden[key])) {
|
|
133
151
|
const overelemRegex = new RegExp(
|
|
134
152
|
`^\\$${key}\\[(?<index>\\d*)\\]$`,
|
|
135
153
|
"u",
|
|
@@ -139,63 +157,73 @@ export const merge = function (parent, child) {
|
|
|
139
157
|
.filter(([i]) => undefined !== i);
|
|
140
158
|
for (const [index, value] of overelems) {
|
|
141
159
|
if ("" === index) {
|
|
142
|
-
|
|
160
|
+
overridden[key].push(...clone(value));
|
|
143
161
|
} else {
|
|
144
|
-
|
|
145
|
-
|
|
162
|
+
overridden[key][Number(index)] = merge(
|
|
163
|
+
overridden[key][Number(index)],
|
|
146
164
|
value,
|
|
147
165
|
);
|
|
148
166
|
}
|
|
149
167
|
}
|
|
150
168
|
}
|
|
151
169
|
}
|
|
152
|
-
return
|
|
170
|
+
return overridden;
|
|
153
171
|
};
|
|
154
172
|
|
|
155
173
|
/**
|
|
156
|
-
* Étendre un objet JSON en utilisant les
|
|
174
|
+
* Étendre un objet JSON en utilisant les propriétés <code>"$extends"</code>.
|
|
157
175
|
*
|
|
158
|
-
* @param {Record<string, any>} obj
|
|
176
|
+
* @param {Record<string, any>} obj L'objet qui sera étendu.
|
|
177
|
+
* @param {Options} [options] Les options.
|
|
159
178
|
* @returns {Promise<Record<string, any>>} Une promesse contenant l'objet
|
|
160
179
|
* étendu.
|
|
161
180
|
*/
|
|
162
|
-
export const inherit = async function (obj) {
|
|
163
|
-
if (undefined === obj
|
|
181
|
+
export const inherit = async function (obj, options) {
|
|
182
|
+
if (undefined === obj.$extends) {
|
|
164
183
|
return obj;
|
|
165
184
|
}
|
|
166
185
|
|
|
167
186
|
// eslint-disable-next-line no-use-before-define
|
|
168
|
-
return merge(await load(obj.$extends), obj);
|
|
187
|
+
return merge(await load(obj.$extends, options), obj);
|
|
169
188
|
};
|
|
170
189
|
|
|
171
190
|
/**
|
|
172
191
|
* Étendre un objet récursivement.
|
|
173
192
|
*
|
|
174
|
-
* @param {any}
|
|
193
|
+
* @param {any} obj L'objet qui sera étendu.
|
|
194
|
+
* @param {Options} [options] Les options.
|
|
175
195
|
* @returns {Promise<any>} Une promesse contenant l'objet étendu.
|
|
176
196
|
*/
|
|
177
|
-
export const
|
|
178
|
-
return walkAsync(obj, inherit);
|
|
197
|
+
export const extend = function (obj, options) {
|
|
198
|
+
return walkAsync(obj, (/** @type {any} */ v) => inherit(v, options));
|
|
179
199
|
};
|
|
180
200
|
|
|
181
201
|
/**
|
|
182
202
|
* Charge un objet JSON depuis une URL.
|
|
183
203
|
*
|
|
184
|
-
* @param {string}
|
|
204
|
+
* @param {string} url L'URL du fichier JSON.
|
|
205
|
+
* @param {Options} [options] Les options.
|
|
185
206
|
* @returns {Promise<any>} Une promesse contenant l'objet.
|
|
186
207
|
*/
|
|
187
|
-
export const load = async function (url) {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
208
|
+
export const load = async function (url, options) {
|
|
209
|
+
let json;
|
|
210
|
+
if (undefined === options?.request) {
|
|
211
|
+
const response = await fetch(url);
|
|
212
|
+
json = await response.json();
|
|
213
|
+
} else {
|
|
214
|
+
json = await options.request(url);
|
|
215
|
+
}
|
|
216
|
+
// Enlever le "#" dans le hash de l'URL.
|
|
217
|
+
return await extend(query(json, new URL(url).hash.slice(1)), options);
|
|
191
218
|
};
|
|
192
219
|
|
|
193
220
|
/**
|
|
194
221
|
* Parse une chaine de caractères.
|
|
195
222
|
*
|
|
196
|
-
* @param {string}
|
|
197
|
-
* @
|
|
223
|
+
* @param {string} text La chaine de caractères qui sera parsée.
|
|
224
|
+
* @param {Options} [options] Les options.
|
|
225
|
+
* @returns {Promise<any>} L'objet.
|
|
198
226
|
*/
|
|
199
|
-
export const parse = function (text) {
|
|
200
|
-
return
|
|
227
|
+
export const parse = function (text, options) {
|
|
228
|
+
return extend(JSON.parse(text), options);
|
|
201
229
|
};
|
package/types/index.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
declare namespace _default {
|
|
2
|
+
export { extend };
|
|
2
3
|
export { load };
|
|
3
4
|
export { merge };
|
|
4
5
|
export { parse };
|
|
5
6
|
}
|
|
6
7
|
export default _default;
|
|
8
|
+
import { extend } from "./jfather.js";
|
|
7
9
|
import { load } from "./jfather.js";
|
|
8
10
|
import { merge } from "./jfather.js";
|
|
9
11
|
import { parse } from "./jfather.js";
|
package/types/jfather.d.ts
CHANGED
|
@@ -3,7 +3,19 @@ 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
|
|
8
|
-
export function load(url: string): Promise<any>;
|
|
9
|
-
export function parse(text: string): 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, 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
|
+
* <code>fetch()</code> et
|
|
18
|
+
* <code>Response.prototype.json()</code>
|
|
19
|
+
*/
|
|
20
|
+
request?: Function;
|
|
21
|
+
};
|