jfather 0.3.0 → 0.5.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 CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2024 Sébastien Règne
3
+ Copyright (c) 2024-2026 Sébastien Règne
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -2,20 +2,24 @@
2
2
 
3
3
  <!-- Utiliser du HTML (avec l'attribut "align" obsolète) pour faire flotter
4
4
  l'image à droite. -->
5
- <!-- markdownlint-disable-next-line no-inline-html-->
6
- <img src="asset/logo.svg" align="right" alt="">
5
+ <!-- markdownlint-disable-next-line no-inline-html -->
6
+ <img src="asset/logo.svg" align="right" width="100" height="100" alt="">
7
7
 
8
- [![npm][img-npm]][link-npm]
9
- [![build][img-build]][link-build]
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 utility library to **merge**, **extend** and **override**
18
- [JSON](https://www.json.org/json-en.html "JavaScript Object Notation") objects.
15
+ JFather is a
16
+ [JSON](https://www.json.org/json-en.html "JavaScript Object Notation") utility
17
+ library to:
18
+
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.
19
23
 
20
24
  <!-- prettier-ignore-start -->
21
25
  ```javascript
@@ -48,8 +52,8 @@ console.log(extended);
48
52
  // "quote": "With great fist comes great KO"
49
53
  // }
50
54
 
51
- // Override an object.
52
- const overridden = await JFather.merge(
55
+ // Override arrays of an object.
56
+ const overridden = JFather.merge(
53
57
  { "foo": ["a", "alpha"] },
54
58
  { "$foo[0]": "A", "$foo[]": ["BETA"] }
55
59
  );
@@ -89,7 +93,7 @@ JFather is published on [npm][link-npm] (its CDN:
89
93
  [esm.sh](https://esm.sh/jfather),
90
94
  [jsDelivr](https://www.jsdelivr.com/package/npm/jfather),
91
95
  [UNPKG](https://unpkg.com/browse/jfather/)) and
92
- [Deno](https://deno.land/x/jfather).
96
+ [JSR](https://jsr.io/@regseb/jfather).
93
97
 
94
98
  ```javascript
95
99
  // Node.js and Bun (after `npm install jfather`):
@@ -100,263 +104,308 @@ import JFather from "https://esm.sh/jfather@0";
100
104
  import JFather from "https://cdn.jsdelivr.net/npm/jfather@0";
101
105
  import JFather from "https://unpkg.com/jfather@0";
102
106
 
103
- // Deno:
104
- import JFather from "https://deno.land/x/jfather/mod.js";
107
+ // Deno (after `deno add jsr:@regseb/jfather`):
108
+ import JFather from "jsr:@regseb/jfather";
105
109
  ```
106
110
 
107
111
  ## Features
108
112
 
109
113
  ### Merge
110
114
 
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 -->
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 -->
151
174
 
152
175
  ### Extend
153
176
 
154
- <!-- markdownlint-disable no-inline-html -->
155
- <table>
156
- <tr>
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>
171
- </tr>
172
- <tr>
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>
183
- </tr>
184
- <tr>
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>
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"
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
211
224
  }
212
- }</code></pre></td>
213
- </tr>
214
- <tr>
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>
229
- </tr>
230
- </table>
231
- <!-- markdownlint-enable no-inline-html -->
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 -->
232
258
 
233
259
  ### Override
234
260
 
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>{
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 = {
266
300
  "foo": [{
267
301
  "bar": ["a"]
268
302
  }]
269
- }</code></pre></td>
270
- <td><pre lang="json"><code>{
303
+ };
304
+ const child = {
271
305
  "$foo[0]": {
272
306
  "$bar[]": ["b", "c"]
273
307
  }
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 -->
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 -->
283
332
 
284
333
  ## API
285
334
 
286
- - [`merge()`](#merge)
287
- - [`extend()`](#extend)
288
- - [`load()`](#load)
289
- - [`parse()`](#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)
290
339
 
291
- ### `merge()`
340
+ ### `JFather.merge(parent, child)`
292
341
 
293
342
  Merge and override `parent` with `child`.
294
343
 
295
- ```javascript
296
- JFather.merge(parent, child);
297
- ```
298
-
299
344
  - Parameters:
300
- - `parent`: The parent object.
301
- - `child`: The child object.
302
- - 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.
303
348
 
304
- ### `extend()`
349
+ ### `JFather.extend(obj, [options])`
305
350
 
306
351
  Extend `obj`, merge and override.
307
352
 
308
- ```javascript
309
- JFather.extend(obj, [options]);
310
- ```
311
-
312
353
  - 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.
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.
320
360
 
321
- ### `load()`
361
+ ### `JFather.load(url, [options])`
322
362
 
323
- Load from a `url`, extend, merge and override.
324
-
325
- ```javascript
326
- JFather.load(url, [options]);
327
- ```
363
+ Load from an `url`, extend, merge and override.
328
364
 
329
365
  - 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.
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.
337
373
 
338
- ### `parse()`
374
+ ### `JFather.parse(text, [options])`
339
375
 
340
376
  Parse a `text`, extend, merge and override.
341
377
 
342
- ```javascript
343
- JFather.parse(text, [options]);
344
- ```
345
-
346
378
  - 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.
354
-
355
- [img-npm]: https://img.shields.io/npm/dm/jfather?label=npm&logo=npm&logoColor=whitesmoke
356
- [img-build]: https://img.shields.io/github/actions/workflow/status/regseb/jfather/ci.yml?branch=main&logo=github&logoColor=whitesmoke
357
- [img-coverage]: https://img.shields.io/endpoint?label=coverage&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fregseb%2Fjfather%2Fmain
358
- [img-semver]: https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&logoColor=whitesmoke
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
359
406
  [link-npm]: https://www.npmjs.com/package/jfather
360
- [link-build]: https://github.com/regseb/jfather/actions/workflows/ci.yml?query=branch%3Amain
361
- [link-coverage]: https://dashboard.stryker-mutator.io/reports/github.com/regseb/jfather/main
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
362
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.0",
3
+ "version": "0.5.0",
4
4
  "description": "JSON with merge, extend and override.",
5
5
  "keywords": [
6
6
  "jfather",
@@ -35,50 +35,59 @@
35
35
  },
36
36
  "main": "./src/index.js",
37
37
  "types": "./types/index.d.ts",
38
- "repository": "regseb/jfather",
38
+ "repository": {
39
+ "type": "git",
40
+ "url": "git+https://github.com/regseb/jfather.git"
41
+ },
39
42
  "type": "module",
40
43
  "scripts": {
44
+ "prepare": "tsc --project .tsconfig_types.json",
41
45
  "lint": "metalint",
42
46
  "lint:fix": "metalint --fix",
43
47
  "lint:types": "tsc --project .tsconfig_lint.json",
44
48
  "test": "npm run test:coverage",
45
- "test:unit": "mocha --config test/mocharc.json",
49
+ "test:unit": "npm run test:unit:node",
50
+ "test:unit:node": "node --test --import ./test/polyfills/node.js 'test/unit/**/*.test.js'",
51
+ "test:unit:bun": "bun test --preload ./test/polyfills/bun.js test/unit/",
46
52
  "test:coverage": "stryker run",
47
53
  "jsdocs": "typedoc --tsconfig .tsconfig_jsdocs.json",
48
- "prepare": "tsc --project .tsconfig_types.json",
49
54
  "clean": "node .script/clean.js"
50
55
  },
51
56
  "devDependencies": {
52
- "@prantlf/jsonlint": "14.0.3",
53
- "@prettier/plugin-xml": "3.4.1",
54
- "@stryker-mutator/core": "8.2.6",
55
- "@stryker-mutator/mocha-runner": "8.2.6",
56
- "@types/mocha": "10.0.6",
57
- "@types/node": "20.12.12",
58
- "@types/sinon": "17.0.3",
59
- "eslint": "8.57.0",
60
- "eslint-plugin-array-func": "4.0.0",
57
+ "@biomejs/js-api": "4.0.0",
58
+ "@biomejs/wasm-nodejs": "2.3.11",
59
+ "@prantlf/jsonlint": "17.0.0",
60
+ "@prettier/plugin-xml": "3.4.2",
61
+ "@secretlint/secretlint-rule-github": "11.2.5",
62
+ "@secretlint/secretlint-rule-npm": "11.2.5",
63
+ "@stryker-mutator/core": "9.4.0",
64
+ "@stryker-mutator/tap-runner": "9.4.0",
65
+ "@types/bun": "1.3.5",
66
+ "@types/node": "25.0.3",
67
+ "eslint": "9.39.2",
68
+ "eslint-plugin-array-func": "5.1.0",
61
69
  "eslint-plugin-eslint-comments": "3.2.0",
62
- "eslint-plugin-import": "2.29.1",
63
- "eslint-plugin-jsdoc": "48.2.5",
64
- "eslint-plugin-mocha": "10.4.3",
65
- "eslint-plugin-n": "17.7.0",
66
- "eslint-plugin-no-unsanitized": "4.0.2",
67
- "eslint-plugin-promise": "6.1.1",
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",
73
- "npm-package-json-lint": "7.1.0",
74
- "prettier": "3.2.5",
75
- "publint": "0.2.8",
76
- "sinon": "18.0.0",
77
- "typedoc": "0.25.13",
78
- "typescript": "5.4.5",
70
+ "eslint-plugin-import": "2.32.0",
71
+ "eslint-plugin-jsdoc": "61.5.0",
72
+ "eslint-plugin-mocha": "11.2.0",
73
+ "eslint-plugin-n": "17.23.1",
74
+ "eslint-plugin-no-unsanitized": "4.1.4",
75
+ "eslint-plugin-promise": "7.2.1",
76
+ "eslint-plugin-regexp": "2.10.0",
77
+ "eslint-plugin-unicorn": "62.0.0",
78
+ "globals": "17.0.0",
79
+ "jsr": "0.13.5",
80
+ "markdownlint": "0.40.0",
81
+ "metalint": "0.21.2",
82
+ "npm-package-json-lint": "9.1.0",
83
+ "prettier": "3.7.4",
84
+ "publint": "0.3.16",
85
+ "secretlint": "11.2.5",
86
+ "typedoc": "0.28.15",
87
+ "typescript": "5.9.3",
79
88
  "yaml-lint": "1.7.0"
80
89
  },
81
90
  "engines": {
82
- "node": ">=20.6.0"
91
+ "node": ">=20.18.0"
83
92
  }
84
93
  }
package/src/index.js CHANGED
@@ -6,4 +6,8 @@
6
6
 
7
7
  import { extend, load, merge, parse } from "./jfather.js";
8
8
 
9
+ /**
10
+ * @typedef {import('./jfather.js').Options} Options
11
+ */
12
+
9
13
  export default { extend, load, merge, parse };
package/src/jfather.js CHANGED
@@ -10,8 +10,7 @@
10
10
  * @typedef {Object} Options
11
11
  * @prop {Function} [request] La fonction pour récupérer un objet JSON à
12
12
  * distance. Par défaut, l'objet est récupéré avec
13
- * <code>fetch()</code> et
14
- * <code>Response.prototype.json()</code>
13
+ * `fetch()` et `Response.json()`.
15
14
  */
16
15
 
17
16
  /**
@@ -22,7 +21,7 @@
22
21
  * @param {Function} fn La fonction appliquée sur tous les objets.
23
22
  * @returns {any} Le retour de la fonction.
24
23
  */
25
- export const walk = function (obj, fn) {
24
+ export const walk = (obj, fn) => {
26
25
  if (Object === obj?.constructor) {
27
26
  return fn(
28
27
  Object.fromEntries(
@@ -46,7 +45,7 @@ export const walk = function (obj, fn) {
46
45
  * @param {Function} fn La fonction asynchrone appliquée sur tous les objets.
47
46
  * @returns {Promise<any>} Une promesse contenant le retour de la fonction.
48
47
  */
49
- export const walkAsync = async function (obj, fn) {
48
+ export const walkAsync = async (obj, fn) => {
50
49
  if (Object === obj?.constructor) {
51
50
  return await fn(
52
51
  Object.fromEntries(
@@ -73,7 +72,7 @@ export const walkAsync = async function (obj, fn) {
73
72
  * @param {any} obj Une variable quelconque.
74
73
  * @returns {any} Le clone de la variable d'entrée.
75
74
  */
76
- export const clone = function (obj) {
75
+ export const clone = (obj) => {
77
76
  return walk(obj, (/** @type {any} */ v) => v);
78
77
  };
79
78
 
@@ -85,16 +84,16 @@ export const clone = function (obj) {
85
84
  * @returns {any} L'élément extrait.
86
85
  * @throws {TypeError} Si le chemin est invalide.
87
86
  */
88
- export const query = function (obj, chain) {
87
+ export const query = (obj, chain) => {
89
88
  if ("" === chain) {
90
89
  return obj;
91
90
  }
92
91
 
93
- const re = /^\.(?<prop>\w+)|^\[(?<index>\d+)\]/u;
92
+ const re = /^\.(?<prop>\w+)|^\[(?<index>\d+)\]/v;
94
93
  const sub = {
95
94
  obj,
96
95
  // Préfixer le chemin avec un point si nécessaire.
97
- chain: /^[.[]/u.test(chain) ? chain : "." + chain,
96
+ chain: /^[.\[]/v.test(chain) ? chain : `.${chain}`,
98
97
  };
99
98
  while (0 !== sub.chain.length) {
100
99
  const result = re.exec(sub.chain);
@@ -118,8 +117,12 @@ export const query = function (obj, chain) {
118
117
  * @param {any} child L'objet enfant.
119
118
  * @returns {any} La fusion des deux objets.
120
119
  */
121
- export const merge = function (parent, child) {
122
- if (Object !== parent?.constructor || Object !== child?.constructor) {
120
+ export const merge = (parent, child) => {
121
+ if (
122
+ child === parent ||
123
+ Object !== parent?.constructor ||
124
+ Object !== child?.constructor
125
+ ) {
123
126
  return clone(child);
124
127
  }
125
128
 
@@ -149,8 +152,8 @@ export const merge = function (parent, child) {
149
152
  // surcharges d'éléments.
150
153
  if (Array.isArray(overridden[key])) {
151
154
  const overelemRegex = new RegExp(
152
- `^\\$${key}\\[(?<index>\\d*)\\]$`,
153
- "u",
155
+ String.raw`^\$${key}\[(?<index>\d*)\]$`,
156
+ "v",
154
157
  );
155
158
  const overelems = Object.entries(child)
156
159
  .map(([k, v]) => [overelemRegex.exec(k)?.groups?.index, v])
@@ -171,14 +174,14 @@ export const merge = function (parent, child) {
171
174
  };
172
175
 
173
176
  /**
174
- * Étendre un objet JSON en utilisant les propriétés <code>"$extends"</code>.
177
+ * Étendre un objet JSON en utilisant la propriété `"$extends"`.
175
178
  *
176
179
  * @param {Record<string, any>} obj L'objet qui sera étendu.
177
180
  * @param {Options} [options] Les options.
178
181
  * @returns {Promise<Record<string, any>>} Une promesse contenant l'objet
179
182
  * étendu.
180
183
  */
181
- export const inherit = async function (obj, options) {
184
+ export const inherit = async (obj, options) => {
182
185
  if (undefined === obj.$extends) {
183
186
  return obj;
184
187
  }
@@ -194,18 +197,18 @@ export const inherit = async function (obj, options) {
194
197
  * @param {Options} [options] Les options.
195
198
  * @returns {Promise<any>} Une promesse contenant l'objet étendu.
196
199
  */
197
- export const extend = function (obj, options) {
200
+ export const extend = (obj, options) => {
198
201
  return walkAsync(obj, (/** @type {any} */ v) => inherit(v, options));
199
202
  };
200
203
 
201
204
  /**
202
205
  * Charge un objet JSON depuis une URL.
203
206
  *
204
- * @param {string} url L'URL du fichier JSON.
205
- * @param {Options} [options] Les options.
207
+ * @param {string|URL} url L'URL du fichier JSON.
208
+ * @param {Options} [options] Les options.
206
209
  * @returns {Promise<any>} Une promesse contenant l'objet.
207
210
  */
208
- export const load = async function (url, options) {
211
+ export const load = async (url, options) => {
209
212
  let json;
210
213
  if (undefined === options?.request) {
211
214
  const response = await fetch(url);
@@ -224,6 +227,6 @@ export const load = async function (url, options) {
224
227
  * @param {Options} [options] Les options.
225
228
  * @returns {Promise<any>} L'objet.
226
229
  */
227
- export const parse = function (text, options) {
230
+ export const parse = (text, options) => {
228
231
  return extend(JSON.parse(text), options);
229
232
  };
package/types/index.d.ts CHANGED
@@ -5,6 +5,7 @@ declare namespace _default {
5
5
  export { parse };
6
6
  }
7
7
  export default _default;
8
+ export type Options = import("./jfather.js").Options;
8
9
  import { extend } from "./jfather.js";
9
10
  import { load } from "./jfather.js";
10
11
  import { merge } from "./jfather.js";
@@ -5,7 +5,7 @@ export function query(obj: Record<string, any>, chain: string): any;
5
5
  export function merge(parent: any, child: any): any;
6
6
  export function inherit(obj: Record<string, any>, options?: Options): Promise<Record<string, any>>;
7
7
  export function extend(obj: any, options?: Options): Promise<any>;
8
- export function load(url: string, options?: Options): Promise<any>;
8
+ export function load(url: string | URL, options?: Options): Promise<any>;
9
9
  export function parse(text: string, options?: Options): Promise<any>;
10
10
  /**
11
11
  * Les options des fonctions de JFather.
@@ -14,8 +14,7 @@ export type Options = {
14
14
  /**
15
15
  * La fonction pour récupérer un objet JSON à
16
16
  * distance. Par défaut, l'objet est récupéré avec
17
- * <code>fetch()</code> et
18
- * <code>Response.prototype.json()</code>
17
+ * `fetch()` et `Response.json()`.
19
18
  */
20
19
  request?: Function;
21
20
  };