@speclynx/apidom-core 1.12.2 → 2.1.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/CHANGELOG.md +25 -0
- package/NOTICE +16 -7
- package/README.md +102 -330
- package/dist/apidom-core.browser.js +13110 -10559
- package/dist/apidom-core.browser.min.js +1 -1
- package/package.json +7 -12
- package/src/identity/index.cjs +8 -6
- package/src/identity/index.mjs +6 -4
- package/src/index.cjs +13 -79
- package/src/index.mjs +3 -13
- package/src/merge/deepmerge.cjs +22 -20
- package/src/merge/deepmerge.mjs +9 -7
- package/src/merge/merge-right.cjs +2 -2
- package/src/merge/merge-right.mjs +1 -1
- package/src/namespace.cjs +4 -36
- package/src/namespace.mjs +2 -30
- package/src/refractor/plugins/dispatcher/index.cjs +5 -12
- package/src/refractor/plugins/dispatcher/index.mjs +5 -12
- package/src/refractor/plugins/element-identity.cjs +2 -2
- package/src/refractor/plugins/element-identity.mjs +2 -2
- package/src/refractor/plugins/semantic-element-identity.cjs +5 -10
- package/src/refractor/plugins/semantic-element-identity.mjs +5 -9
- package/src/refractor/toolbox.cjs +22 -5
- package/src/refractor/toolbox.mjs +25 -6
- package/src/specification.cjs +51 -0
- package/src/specification.mjs +47 -0
- package/src/transcluder/Transcluder.cjs +8 -8
- package/src/transcluder/Transcluder.mjs +1 -1
- package/src/transformers/from.mjs +2 -2
- package/src/transformers/serializers/json.cjs +2 -2
- package/src/transformers/serializers/json.mjs +1 -1
- package/src/transformers/serializers/value.cjs +50 -0
- package/src/transformers/serializers/value.mjs +47 -0
- package/src/transformers/serializers/yaml-1-2.cjs +22 -18
- package/src/transformers/serializers/yaml-1-2.mjs +22 -18
- package/src/transformers/sexprs.cjs +20 -23
- package/src/transformers/sexprs.mjs +20 -22
- package/src/transformers/to-string.mjs +2 -2
- package/types/apidom-core.d.ts +67 -519
- package/src/clone/errors/CloneError.cjs +0 -22
- package/src/clone/errors/CloneError.mjs +0 -19
- package/src/clone/errors/DeepCloneError.cjs +0 -11
- package/src/clone/errors/DeepCloneError.mjs +0 -6
- package/src/clone/errors/ShallowCloneError.cjs +0 -11
- package/src/clone/errors/ShallowCloneError.mjs +0 -6
- package/src/clone/index.cjs +0 -155
- package/src/clone/index.mjs +0 -146
- package/src/elements/Annotation.cjs +0 -23
- package/src/elements/Annotation.mjs +0 -20
- package/src/elements/Comment.cjs +0 -15
- package/src/elements/Comment.mjs +0 -12
- package/src/elements/ParseResult.cjs +0 -53
- package/src/elements/ParseResult.mjs +0 -50
- package/src/elements/SourceMap.cjs +0 -39
- package/src/elements/SourceMap.mjs +0 -36
- package/src/predicates/helpers.cjs +0 -85
- package/src/predicates/helpers.mjs +0 -77
- package/src/predicates/index.cjs +0 -208
- package/src/predicates/index.mjs +0 -198
- package/src/refractor/index.cjs +0 -46
- package/src/refractor/index.mjs +0 -40
- package/src/refractor/registration.cjs +0 -34
- package/src/refractor/registration.mjs +0 -19
- package/src/transformers/serializers/value/ast/ephemeral-array.cjs +0 -21
- package/src/transformers/serializers/value/ast/ephemeral-array.mjs +0 -17
- package/src/transformers/serializers/value/ast/ephemeral-object.cjs +0 -20
- package/src/transformers/serializers/value/ast/ephemeral-object.mjs +0 -16
- package/src/transformers/serializers/value/index.cjs +0 -85
- package/src/transformers/serializers/value/index.mjs +0 -80
- package/src/transformers/serializers/value/visitor.cjs +0 -52
- package/src/transformers/serializers/value/visitor.mjs +0 -47
- package/src/traversal/filter.cjs +0 -18
- package/src/traversal/filter.mjs +0 -14
- package/src/traversal/find.cjs +0 -19
- package/src/traversal/find.mjs +0 -15
- package/src/traversal/findAtOffset.cjs +0 -65
- package/src/traversal/findAtOffset.mjs +0 -60
- package/src/traversal/index.cjs +0 -19
- package/src/traversal/index.mjs +0 -7
- package/src/traversal/parents.cjs +0 -38
- package/src/traversal/parents.mjs +0 -34
- package/src/traversal/reject.cjs +0 -15
- package/src/traversal/reject.mjs +0 -10
- package/src/traversal/some.cjs +0 -15
- package/src/traversal/some.mjs +0 -10
- package/src/traversal/traverse.cjs +0 -60
- package/src/traversal/traverse.mjs +0 -53
- package/src/traversal/visitor.cjs +0 -123
- package/src/traversal/visitor.mjs +0 -115
- package/src/util.cjs +0 -28
- package/src/util.mjs +0 -24
- package/types/minim.d.ts +0 -236
package/README.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# @speclynx/apidom-core
|
|
2
2
|
|
|
3
3
|
`apidom-core` is a package that contains tools for manipulating the ApiDOM structures.
|
|
4
|
+
It provides utilities for transformation, merging, cloning, and refraction of ApiDOM elements.
|
|
4
5
|
|
|
5
6
|
## Installation
|
|
6
7
|
|
|
@@ -12,63 +13,26 @@ You can install this package via [npm CLI](https://docs.npmjs.com/cli) by runnin
|
|
|
12
13
|
|
|
13
14
|
---
|
|
14
15
|
|
|
15
|
-
##
|
|
16
|
+
## Relationship with other packages
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
of [primitive ones](https://github.com/refractproject/minim/tree/master/lib/primitives).
|
|
18
|
+
This package works together with:
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
const namespace = createNamespace();
|
|
24
|
-
|
|
25
|
-
const objectElement = new namespace.elements.Object();
|
|
26
|
-
const commentElement = new namespace.elements.Comment();
|
|
27
|
-
```
|
|
28
|
-
|
|
29
|
-
It's possible to create namespace instances using another namespaces.
|
|
20
|
+
- **@speclynx/apidom-datamodel** - Contains the core element primitives (`ObjectElement`, `ArrayElement`, `StringElement`, etc.), predicates, and the `Namespace` class
|
|
21
|
+
- **@speclynx/apidom-traverse** - Contains traversal utilities (`traverse`, `filter`, `find`, `forEach`, etc.)
|
|
22
|
+
- **@speclynx/apidom-core** - Contains utilities for working with elements: transformers, merging, cloning, and refractor plugins
|
|
30
23
|
|
|
31
24
|
```js
|
|
32
|
-
|
|
33
|
-
import
|
|
34
|
-
|
|
35
|
-
const namespace = createNamespace(openApi3_1Namespace);
|
|
36
|
-
|
|
37
|
-
const objectElement = new namespace.elements.Object();
|
|
38
|
-
const openApiElement = new namespace.elements.OpenApi3_1();
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
When namespace instance is created in this way, it will extend the base namespace
|
|
42
|
-
with the namespace provided as an argument.
|
|
25
|
+
// Elements and predicates come from datamodel
|
|
26
|
+
import { ObjectElement, StringElement, isObjectElement } from '@speclynx/apidom-datamodel';
|
|
43
27
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Predicates
|
|
47
|
-
|
|
48
|
-
This package exposes [predicates](https://github.com/speclynx/apidom/blob/main/packages/apidom-core/src/predicates/index.ts)
|
|
49
|
-
for all primitive elements and all higher order elements that are part of the base namespace.
|
|
50
|
-
|
|
51
|
-
```js
|
|
52
|
-
import { CommentElement, isCommentElement } from '@speclynx/apidom-core';
|
|
28
|
+
// Traversal utilities come from traverse
|
|
29
|
+
import { traverse, filter, find } from '@speclynx/apidom-traverse';
|
|
53
30
|
|
|
54
|
-
|
|
31
|
+
// Other utilities come from core
|
|
32
|
+
import { toValue, deepmerge, from } from '@speclynx/apidom-core';
|
|
55
33
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
[Predicate helpers](https://github.com/speclynx/apidom/blob/main/packages/apidom-core/src/predicates/helpers.ts)
|
|
60
|
-
helps in building predicates for this and other packages.
|
|
61
|
-
|
|
62
|
-
```js
|
|
63
|
-
import { createPredicate } from '@speclynx/apidom-core';
|
|
64
|
-
|
|
65
|
-
const isMyElement = createPredicate(
|
|
66
|
-
({ hasBasicElementProps, isElementType, primitiveEq }) => {
|
|
67
|
-
return (element) =>
|
|
68
|
-
element instanceof MyElement ||
|
|
69
|
-
(hasBasicElementProps(element) && isElementType('myElement', element) && primitiveEq('object', element));
|
|
70
|
-
},
|
|
71
|
-
);
|
|
34
|
+
const obj = new ObjectElement({ a: 'b' });
|
|
35
|
+
toValue(obj); // => { a: 'b' }
|
|
72
36
|
```
|
|
73
37
|
|
|
74
38
|
---
|
|
@@ -79,7 +43,8 @@ Transclusion is the inclusion of one ApiDOM fragment into another ApiDOM fragmen
|
|
|
79
43
|
Our [transcluder](https://github.com/speclynx/apidom/tree/main/packages/apidom-core/src/transcluder) does exactly that and is based on mutating algorithm.
|
|
80
44
|
|
|
81
45
|
```js
|
|
82
|
-
import {
|
|
46
|
+
import { ArrayElement, NumberElement } from '@speclynx/apidom-datamodel';
|
|
47
|
+
import { transclude } from '@speclynx/apidom-core';
|
|
83
48
|
|
|
84
49
|
const element = new ArrayElement([1, 2, 3]);
|
|
85
50
|
const search = element.get(1);
|
|
@@ -92,7 +57,8 @@ When multiple transclusions are going to be performed use [Transcluder stamp](ht
|
|
|
92
57
|
for optimal performance.
|
|
93
58
|
|
|
94
59
|
```js
|
|
95
|
-
import {
|
|
60
|
+
import { ArrayElement, NumberElement } from '@speclynx/apidom-datamodel';
|
|
61
|
+
import { Transcluder } from '@speclynx/apidom-core';
|
|
96
62
|
|
|
97
63
|
const element = new ArrayElement([1, 2, 3]);
|
|
98
64
|
const search = element.get(1);
|
|
@@ -119,7 +85,8 @@ the value from source will appear in the result. Merging creates a new ApiDOM el
|
|
|
119
85
|
so that neither target nor source is modified (operation is immutable).
|
|
120
86
|
|
|
121
87
|
```js
|
|
122
|
-
import {
|
|
88
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
89
|
+
import { mergeRight } from '@speclynx/apidom-core';
|
|
123
90
|
|
|
124
91
|
const x = new ObjectElement({
|
|
125
92
|
foo: { bar: 3 },
|
|
@@ -145,7 +112,8 @@ const output = mergeRight(x, y);
|
|
|
145
112
|
Merges shallowly any number of ApiDOM elements into a single ApiDOM element.
|
|
146
113
|
|
|
147
114
|
```js
|
|
148
|
-
import {
|
|
115
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
116
|
+
import { mergeRight } from '@speclynx/apidom-core';
|
|
149
117
|
|
|
150
118
|
const foobar = new ObjectElement({ foo: { bar: 3 } });
|
|
151
119
|
const foobaz = new ObjectElement({ foo: { baz: 4 } });
|
|
@@ -163,7 +131,8 @@ the value from source will appear in the result. Merging creates a new ApiDOM el
|
|
|
163
131
|
so that neither target nor source is modified (operation is immutable).
|
|
164
132
|
|
|
165
133
|
```js
|
|
166
|
-
import {
|
|
134
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
135
|
+
import { mergeLeft } from '@speclynx/apidom-core';
|
|
167
136
|
|
|
168
137
|
const x = new ObjectElement({
|
|
169
138
|
foo: { bar: 3 },
|
|
@@ -189,7 +158,8 @@ const output = mergeLeft(x, y);
|
|
|
189
158
|
Merges shallowly any number of ApiDOM elements into a single ApiDOM element.
|
|
190
159
|
|
|
191
160
|
```js
|
|
192
|
-
import {
|
|
161
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
162
|
+
import { mergeLeft } from '@speclynx/apidom-core';
|
|
193
163
|
|
|
194
164
|
const foobar = new ObjectElement({ foo: { bar: 3 } });
|
|
195
165
|
const foobaz = new ObjectElement({ foo: { baz: 4 } });
|
|
@@ -220,7 +190,8 @@ the value from source will appear in the result. Merging creates a new ApiDOM el
|
|
|
220
190
|
so that neither target nor source is modified (operation is immutable).
|
|
221
191
|
|
|
222
192
|
```js
|
|
223
|
-
import {
|
|
193
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
194
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
224
195
|
|
|
225
196
|
const x = new ObjectElement({
|
|
226
197
|
foo: { bar: 3 },
|
|
@@ -275,7 +246,8 @@ const output = deepmerge(x, y);
|
|
|
275
246
|
Merges deeply any number of ApiDOM elements into a single ApiDOM element.
|
|
276
247
|
|
|
277
248
|
```js
|
|
278
|
-
import {
|
|
249
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
250
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
279
251
|
|
|
280
252
|
const foobar = new ObjectElement({ foo: { bar: 3 } });
|
|
281
253
|
const foobaz = new ObjectElement({ foo: { baz: 4 } });
|
|
@@ -295,7 +267,8 @@ Your `arrayElementMerge` function will be called with three arguments: a `target
|
|
|
295
267
|
and an `options` object.
|
|
296
268
|
|
|
297
269
|
```js
|
|
298
|
-
import {
|
|
270
|
+
import { ArrayElement } from '@speclynx/apidom-datamodel';
|
|
271
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
299
272
|
|
|
300
273
|
const arrayElementMerge = (destination, source, options) => source;
|
|
301
274
|
|
|
@@ -313,7 +286,8 @@ Your `objectElementMerge` function will be called with three arguments: a `targe
|
|
|
313
286
|
and an `options` object.
|
|
314
287
|
|
|
315
288
|
```js
|
|
316
|
-
import {
|
|
289
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
290
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
317
291
|
|
|
318
292
|
const objectElementMerge = (destination, source, options) => source;
|
|
319
293
|
|
|
@@ -332,10 +306,14 @@ and you want to copy the whole ObjectElement instead of just copying its member.
|
|
|
332
306
|
You can accomplish this by passing in a function for the `isMergeableElement` option.
|
|
333
307
|
|
|
334
308
|
```js
|
|
335
|
-
import {
|
|
309
|
+
import { ObjectElement, Element, isObjectElement } from '@speclynx/apidom-datamodel';
|
|
310
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
336
311
|
|
|
337
312
|
class CustomObjectElement extends ObjectElement {
|
|
338
|
-
|
|
313
|
+
constructor(...args) {
|
|
314
|
+
super(...args);
|
|
315
|
+
this.element = 'custom';
|
|
316
|
+
}
|
|
339
317
|
}
|
|
340
318
|
const instantiatedCustomObjectElement = new CustomObjectElement({ special: 'oh yeah' });
|
|
341
319
|
|
|
@@ -365,7 +343,8 @@ be used to merge the values for that member.
|
|
|
365
343
|
It may also return undefined, in which case the default merge behaviour will be used.
|
|
366
344
|
|
|
367
345
|
```js
|
|
368
|
-
import {
|
|
346
|
+
import { ObjectElement, StringElement, Element } from '@speclynx/apidom-datamodel';
|
|
347
|
+
import { deepmerge, toValue } from '@speclynx/apidom-core';
|
|
369
348
|
|
|
370
349
|
const alex = new ObjectElement({
|
|
371
350
|
name: {
|
|
@@ -387,7 +366,7 @@ const mergeNames = (nameA: ObjectElement, nameB: ObjectElement) =>
|
|
|
387
366
|
const customMerge = (key: Element) => (toValue(key) === 'name' ? mergeNames : undefined);
|
|
388
367
|
|
|
389
368
|
const output = deepmerge(alex, tony, { customMerge });
|
|
390
|
-
// output.get('name'); // =>
|
|
369
|
+
// output.get('name'); // => StringElement('Alex and Tony')
|
|
391
370
|
// output.get('pets'); // => ArrayElement(['Cat', 'Parrot', 'Dog'])
|
|
392
371
|
```
|
|
393
372
|
|
|
@@ -398,7 +377,8 @@ The `customMetaMerge` function will be passed target and source metadata. If not
|
|
|
398
377
|
the default behavior is to deep copy metadata from target to new merged element.
|
|
399
378
|
|
|
400
379
|
```js
|
|
401
|
-
import {
|
|
380
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
381
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
402
382
|
|
|
403
383
|
const alex = new ObjectElement({ name: { first: 'Alex' } }, { metaKey: true });
|
|
404
384
|
const tony = new ObjectElement({ name: { first: 'Tony' } }, { metaKey: false });
|
|
@@ -416,7 +396,8 @@ The `customAttributesMerge` function will be passed target and source attributes
|
|
|
416
396
|
the default behavior is to deep copy attributes from target to new merged element.
|
|
417
397
|
|
|
418
398
|
```js
|
|
419
|
-
import {
|
|
399
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
400
|
+
import { deepmerge } from '@speclynx/apidom-core';
|
|
420
401
|
|
|
421
402
|
const alex = new ObjectElement({ name: { first: 'Alex' } }, undefined, { attributeKey: true });
|
|
422
403
|
const tony = new ObjectElement({ name: { first: 'Tony' } }, undefined, { attributeKey: false });
|
|
@@ -424,7 +405,7 @@ const tony = new ObjectElement({ name: { first: 'Tony' } }, undefined, { attribu
|
|
|
424
405
|
const customAttributesMerge = (targetMeta, sourceMeta) => deepmerge(targetMeta, sourceMeta);
|
|
425
406
|
|
|
426
407
|
const output = deepmerge(alex, tony, { customAttributesMerge });
|
|
427
|
-
// output.
|
|
408
|
+
// output.attributes.get('attributeKey') // => BooleanElement(false)
|
|
428
409
|
```
|
|
429
410
|
|
|
430
411
|
#### clone
|
|
@@ -435,41 +416,17 @@ If `clone` is false then child elements will be copied directly instead of being
|
|
|
435
416
|
|
|
436
417
|
---
|
|
437
418
|
|
|
438
|
-
##
|
|
439
|
-
|
|
440
|
-
Refractor is a special layer inside the base namespace that can transform JavaScript structures
|
|
441
|
-
into generic ApiDOM structures built from elements of this base namespace.
|
|
419
|
+
## Refractor plugins
|
|
442
420
|
|
|
443
|
-
|
|
421
|
+
Refractor plugins allow you to transform ApiDOM structures during traversal.
|
|
422
|
+
Use `dispatchRefractorPlugins` to apply plugins to an element tree.
|
|
444
423
|
|
|
445
424
|
```js
|
|
446
|
-
import { ObjectElement } from '@speclynx/apidom-
|
|
447
|
-
|
|
448
|
-
const object = {
|
|
449
|
-
title: 'my title',
|
|
450
|
-
description: 'my description',
|
|
451
|
-
version: '0.1.0',
|
|
452
|
-
};
|
|
453
|
-
|
|
454
|
-
ObjectElement.refract(object); // => ObjectElement({ title, description, version })
|
|
455
|
-
```
|
|
456
|
-
|
|
457
|
-
```js
|
|
458
|
-
import { CommentElement } from '@speclynx/apidom-core';
|
|
459
|
-
|
|
460
|
-
const comment = 'this is comment';
|
|
461
|
-
|
|
462
|
-
CommentElement.refract(comment); // => CommentElement('this is comment')
|
|
463
|
-
```
|
|
464
|
-
|
|
465
|
-
### Refractor plugins
|
|
466
|
-
|
|
467
|
-
Refractors can accept plugins as a second argument of refract static method.
|
|
468
|
-
|
|
469
|
-
```js
|
|
470
|
-
import { ObjectElement, StringElement } from '@speclynx/apidom-core';
|
|
425
|
+
import { ObjectElement, StringElement } from '@speclynx/apidom-datamodel';
|
|
426
|
+
import { dispatchRefractorPlugins, from } from '@speclynx/apidom-core';
|
|
471
427
|
|
|
472
428
|
const object = { a: 'b' };
|
|
429
|
+
const objectElement = from(object);
|
|
473
430
|
|
|
474
431
|
const plugin = ({ predicates, namespace }) => ({
|
|
475
432
|
name: 'plugin',
|
|
@@ -486,24 +443,22 @@ const plugin = ({ predicates, namespace }) => ({
|
|
|
486
443
|
},
|
|
487
444
|
});
|
|
488
445
|
|
|
489
|
-
|
|
446
|
+
dispatchRefractorPlugins(objectElement, [plugin]); // => ObjectElement({ a = 'c' })
|
|
490
447
|
```
|
|
491
|
-
You can define as many plugins as needed to enhance the resulting
|
|
448
|
+
You can define as many plugins as needed to enhance the resulting ApiDOM structure.
|
|
492
449
|
If multiple plugins with the same visitor method are defined, they run in parallel (just like in Babel).
|
|
493
450
|
|
|
494
|
-
|
|
451
|
+
### Element identity plugin
|
|
495
452
|
|
|
496
|
-
`apidom` package comes with `refractorPluginElementIdentity`. When used, this plugin will
|
|
453
|
+
`apidom-core` package comes with `refractorPluginElementIdentity`. When used, this plugin will
|
|
497
454
|
assign unique ID to all elements in ApiDOM tree.
|
|
498
455
|
|
|
499
456
|
```js
|
|
500
|
-
import {
|
|
457
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
458
|
+
import { refractorPluginElementIdentity, dispatchRefractorPlugins, from } from '@speclynx/apidom-core';
|
|
501
459
|
|
|
502
|
-
const objectElement =
|
|
503
|
-
|
|
504
|
-
refractorPluginElementIdentity(),
|
|
505
|
-
]
|
|
506
|
-
});
|
|
460
|
+
const objectElement = from({ a: 'b' });
|
|
461
|
+
dispatchRefractorPlugins(objectElement, [refractorPluginElementIdentity()]);
|
|
507
462
|
|
|
508
463
|
objectElement.id; // 8RaWF9
|
|
509
464
|
objectElement.getMember('a').key.id; // NdHHV7
|
|
@@ -513,217 +468,55 @@ objectElement.getMember('a').value.id; // rFGVFP
|
|
|
513
468
|
You can configure the plugin to generate unique IDs in the specific length:
|
|
514
469
|
|
|
515
470
|
```js
|
|
516
|
-
import {
|
|
471
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
472
|
+
import { refractorPluginElementIdentity, dispatchRefractorPlugins, from } from '@speclynx/apidom-core';
|
|
517
473
|
|
|
518
|
-
const objectElement =
|
|
519
|
-
|
|
520
|
-
refractorPluginElementIdentity({ length: 36}),
|
|
521
|
-
]
|
|
522
|
-
});
|
|
474
|
+
const objectElement = from({ a: 'b' });
|
|
475
|
+
dispatchRefractorPlugins(objectElement, [refractorPluginElementIdentity({ length: 36 })]);
|
|
523
476
|
|
|
524
477
|
objectElement.id; // OnReGGrO7fMd9ztacvGfwGbOdGKuOFLiQQ1W
|
|
525
478
|
objectElement.getMember('a').key.id; // BN6rHsmqI56SMQ1elshtbgRVECtEWNYS9lmd
|
|
526
479
|
objectElement.getMember('a').value.id; // Ki4tWmf9xw9Lwb8MxkXJq1uONmJrmhXifmsI
|
|
527
480
|
```
|
|
528
481
|
|
|
529
|
-
|
|
482
|
+
### Semantic element identity plugin
|
|
530
483
|
|
|
531
|
-
`apidom` package comes with `refractorPluginSemanticElementIdentity`. When used, this plugin will
|
|
484
|
+
`apidom-core` package comes with `refractorPluginSemanticElementIdentity`. When used, this plugin will
|
|
532
485
|
assign unique ID to all non-primitive elements in ApiDOM tree. Primitive elements include
|
|
533
|
-
`ObjectElement`, `ArrayElement`, `StringElement`, `BooleanElement`, `NullElement` and `
|
|
486
|
+
`ObjectElement`, `ArrayElement`, `StringElement`, `BooleanElement`, `NullElement`, `NumberElement`, and `MemberElement`.
|
|
534
487
|
|
|
535
488
|
```js
|
|
536
|
-
import {
|
|
537
|
-
import {
|
|
538
|
-
|
|
539
|
-
const infoElement = InfoElement.refract({ title: 'title' });
|
|
540
|
-
const objectElement = ObjectElement.refract({ a: 'b', info: infoElement }, {
|
|
541
|
-
plugins: [
|
|
542
|
-
refractorPluginSemanticElementIdentity(),
|
|
543
|
-
]
|
|
544
|
-
});
|
|
489
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
490
|
+
import { refractorPluginSemanticElementIdentity, dispatchRefractorPlugins, from } from '@speclynx/apidom-core';
|
|
545
491
|
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
You can configure the plugin to generate unique IDs in the specific length:
|
|
492
|
+
// Create a semantic element (element with non-primitive name)
|
|
493
|
+
class InfoElement extends ObjectElement {
|
|
494
|
+
constructor(...args) {
|
|
495
|
+
super(...args);
|
|
496
|
+
this.element = 'info';
|
|
497
|
+
}
|
|
498
|
+
}
|
|
554
499
|
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
const infoElement = InfoElement.refract({ title: 'title' });
|
|
560
|
-
const objectElement = ObjectElement.refract({ a: 'b', info: infoElement }, {
|
|
561
|
-
plugins: [
|
|
562
|
-
refractorPluginSemanticElementIdentity({ length: 36 }),
|
|
563
|
-
]
|
|
564
|
-
});
|
|
500
|
+
const infoElement = new InfoElement({ title: 'title' });
|
|
501
|
+
const objectElement = from({ a: 'b', info: infoElement });
|
|
502
|
+
dispatchRefractorPlugins(objectElement, [refractorPluginSemanticElementIdentity()]);
|
|
565
503
|
|
|
566
504
|
objectElement.id; // ''
|
|
567
505
|
objectElement.getMember('a').key.id; // ''
|
|
568
506
|
objectElement.getMember('a').value.id; // ''
|
|
569
507
|
objectElement.getMember('info').key.id; // ''
|
|
570
|
-
objectElement.getMember('info').value.id; // '
|
|
571
|
-
```
|
|
572
|
-
|
|
573
|
-
---
|
|
574
|
-
|
|
575
|
-
## Traversal
|
|
576
|
-
|
|
577
|
-
`apidom` comes with its own traversal algorithm along with couple of convenient abstractions on top of it.
|
|
578
|
-
|
|
579
|
-
### visit
|
|
580
|
-
|
|
581
|
-
[visit](https://github.com/speclynx/apidom/blob/main/packages/apidom-core/src/traversal/visitor.ts#L104-L103) will walk through an AST using a depth first traversal, calling
|
|
582
|
-
the visitor's enter function at each node in the traversal, and calling the
|
|
583
|
-
leave function after visiting that node and all of its child nodes.
|
|
584
|
-
|
|
585
|
-
By returning different values from the enter and leave functions, the
|
|
586
|
-
behavior of the visitor can be altered, including skipping over a sub-tree of
|
|
587
|
-
the ApiDOM (by returning false), editing the ApiDOM by returning a value or null
|
|
588
|
-
to remove the value, or to stop the whole traversal by returning [BREAK](https://github.com/speclynx/apidom/blob/main/packages/apidom-core/src/index.ts#L52).
|
|
589
|
-
|
|
590
|
-
When using `visit` to edit an ApiDOM, the original ApiDOM will not be modified, and
|
|
591
|
-
a new version of the ApiDOM with the changes applied will be returned from the
|
|
592
|
-
visit function.
|
|
593
|
-
|
|
594
|
-
```js
|
|
595
|
-
import { visit, ObjectElement, NumberElement } from '@speclynx/apidom-core';
|
|
596
|
-
|
|
597
|
-
const visitor = {
|
|
598
|
-
NumberElement(numberElement) {
|
|
599
|
-
return new NumberElement(2);
|
|
600
|
-
},
|
|
601
|
-
};
|
|
602
|
-
const element = new ObjectElement({ a: 1 });
|
|
603
|
-
|
|
604
|
-
const newElement = visit(element, visitor); // => ObjectElement<{a: 2}>
|
|
605
|
-
```
|
|
606
|
-
|
|
607
|
-
This function originally comes from [@speclynx/apidom-ast package](https://github.com/speclynx/apidom/blob/main/packages/apidom-ast/src/visitor.ts)
|
|
608
|
-
and is originally designed to work with [CST](https://en.wikipedia.org/wiki/Parse_tree). `apidom` package
|
|
609
|
-
imports it, specializes it to work with ApiDOM and re-export it.
|
|
610
|
-
|
|
611
|
-
All following algorithms are based on `visit` function.
|
|
612
|
-
|
|
613
|
-
### filter
|
|
614
|
-
|
|
615
|
-
Finds all elements matching the predicate.
|
|
616
|
-
|
|
617
|
-
```js
|
|
618
|
-
import { ObjectElement, filter, isNumberElement } from '@speclynx/apidom-core'
|
|
619
|
-
|
|
620
|
-
const objElement = new ObjectElement({ a: 'b', c: 2 });
|
|
621
|
-
|
|
622
|
-
filter(isNumberElement, objElement); // => ArraySlice<[NumberElement<2>]>
|
|
623
|
-
```
|
|
624
|
-
|
|
625
|
-
### find
|
|
626
|
-
|
|
627
|
-
Find first element that satisfies the provided predicate.
|
|
628
|
-
|
|
629
|
-
```js
|
|
630
|
-
import { ObjectElement, find, isMemberElement } from '@speclynx/apidom-core'
|
|
631
|
-
|
|
632
|
-
const objElement = new ObjectElement({ a: 'b', c: 2 });
|
|
633
|
-
|
|
634
|
-
find(isNumberElement, objElement); // => NumberElement<2>
|
|
635
|
-
```
|
|
636
|
-
|
|
637
|
-
### findAtOffset
|
|
638
|
-
|
|
639
|
-
ApiDOM nodes can be associated with source maps. This function finds the most inner node at the given offset.
|
|
640
|
-
If includeRightBound is set, also finds nodes that end at the given offset.
|
|
641
|
-
|
|
642
|
-
```js
|
|
643
|
-
import { findAtOffset } from '@speclynx/apidom-core'
|
|
644
|
-
|
|
645
|
-
findAtOffset(3, elementWithSourceMaps); // => returns most inner node at offset 3
|
|
646
|
-
```
|
|
647
|
-
|
|
648
|
-
### reject
|
|
649
|
-
|
|
650
|
-
Complement of [filter](#filter).
|
|
651
|
-
|
|
652
|
-
```js
|
|
653
|
-
import { ArrayElement, reject, isNumberElement } from '@speclynx/apidom-core'
|
|
654
|
-
|
|
655
|
-
const arrayElement = new ArrayElement([1, 'a']);
|
|
656
|
-
|
|
657
|
-
reject(isNumberElement, arrayElement); // => ArraySlice<[StringElement<'a'>]>
|
|
658
|
-
```
|
|
659
|
-
|
|
660
|
-
### some
|
|
661
|
-
|
|
662
|
-
Tests whether at least one element passes the predicate.
|
|
663
|
-
|
|
664
|
-
```js
|
|
665
|
-
import { ArrayElement, some, isNumberElement } from '@speclynx/apidom-core'
|
|
666
|
-
|
|
667
|
-
const arrayElement = new ArrayElement([1, 'a']);
|
|
668
|
-
|
|
669
|
-
some(isNumberElement, arrayElement); // => true
|
|
670
|
-
```
|
|
671
|
-
|
|
672
|
-
### traverse
|
|
673
|
-
|
|
674
|
-
Executes the callback on this element and all descendants.
|
|
675
|
-
|
|
676
|
-
```js
|
|
677
|
-
import { ArrayElement, traverse } from '@speclynx/apidom-core'
|
|
678
|
-
|
|
679
|
-
const arrayElement = new ArrayElement([1, 'a']);
|
|
680
|
-
|
|
681
|
-
traverse(console.dir, arrayElement); // => prints ArrayElement, NumberElement, StringElement in this order
|
|
682
|
-
```
|
|
683
|
-
|
|
684
|
-
The execution of the callback can be controlled further by providing a predicate.
|
|
685
|
-
|
|
686
|
-
```js
|
|
687
|
-
import { ArrayElement, traverse, isNumberElement } from '@speclynx/apidom-core'
|
|
688
|
-
|
|
689
|
-
const arrayElement = new ArrayElement([1, 'a']);
|
|
690
|
-
|
|
691
|
-
traverse({ callback: console.dir, predicate: isNumberElement }, arrayElement); // => prints NumberElement<1>
|
|
692
|
-
```
|
|
693
|
-
|
|
694
|
-
### parents
|
|
695
|
-
|
|
696
|
-
Computes upwards edges from every child to its parent.
|
|
697
|
-
|
|
698
|
-
#### ObjectElement example
|
|
699
|
-
|
|
700
|
-
```js
|
|
701
|
-
import { parents, ObjectElement } from '@speclynx/apidom-core';
|
|
702
|
-
|
|
703
|
-
const objectElement = new ObjectElement({ key: 'value' });
|
|
704
|
-
const memberElement = objectElement.getMember('key');
|
|
705
|
-
const { key: keyElement, value: valueElement } = memberElement;
|
|
706
|
-
|
|
707
|
-
const parentEdges = parents(objectElement); // => WeakMap<ChildElement, ParentElement>
|
|
708
|
-
|
|
709
|
-
parentEdges.get(memberElement) === objectElement; // => true
|
|
710
|
-
parentEdges.get(keyElement) === memberElement; // => true
|
|
711
|
-
parentEdges.get(valueElement) === memberElement; // => true
|
|
508
|
+
objectElement.getMember('info').value.id; // '8RaWF9'
|
|
712
509
|
```
|
|
713
510
|
|
|
714
|
-
|
|
511
|
+
You can configure the plugin to generate unique IDs in the specific length:
|
|
715
512
|
|
|
716
513
|
```js
|
|
717
|
-
import {
|
|
718
|
-
|
|
719
|
-
const itemElement1 = new StringElement('item1');
|
|
720
|
-
const itemElement2 = new StringElement('item2');
|
|
721
|
-
const arrayElement = new ArrayElement([itemElement1, itemElement2]);
|
|
514
|
+
import { refractorPluginSemanticElementIdentity, dispatchRefractorPlugins, from } from '@speclynx/apidom-core';
|
|
722
515
|
|
|
723
|
-
const
|
|
516
|
+
const objectElement = from({ a: 'b', info: infoElement });
|
|
517
|
+
dispatchRefractorPlugins(objectElement, [refractorPluginSemanticElementIdentity({ length: 36 })]);
|
|
724
518
|
|
|
725
|
-
|
|
726
|
-
parentEdges.get(itemElement2) === arrayElement; // => true
|
|
519
|
+
objectElement.getMember('info').value.id; // 'OnReGGrO7fMd9ztacvGfwGbOdGKuOFLiQQ1W'
|
|
727
520
|
```
|
|
728
521
|
|
|
729
522
|
---
|
|
@@ -773,7 +566,8 @@ Transforms the ApiDOM into JavaScript POJO. This POJO would be the result of int
|
|
|
773
566
|
into JavaScript structure. This function can handle cycles in ApiDOM structure.
|
|
774
567
|
|
|
775
568
|
```js
|
|
776
|
-
import {
|
|
569
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
570
|
+
import { toValue } from '@speclynx/apidom-core';
|
|
777
571
|
|
|
778
572
|
const objElement = new ObjectElement({ a: 'b' });
|
|
779
573
|
|
|
@@ -785,7 +579,8 @@ toValue(objElement); // => { a: 'b' }
|
|
|
785
579
|
Transforms the ApiDOM into JSON string.
|
|
786
580
|
|
|
787
581
|
```js
|
|
788
|
-
import {
|
|
582
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
583
|
+
import { toJSON } from '@speclynx/apidom-core';
|
|
789
584
|
|
|
790
585
|
const objElement = new ObjectElement({ a: 'b' });
|
|
791
586
|
|
|
@@ -794,10 +589,11 @@ toJSON(objElement); // => '{"a":"b"}'
|
|
|
794
589
|
|
|
795
590
|
### toYAML
|
|
796
591
|
|
|
797
|
-
Transforms the ApiDOM into
|
|
592
|
+
Transforms the ApiDOM into YAML string.
|
|
798
593
|
|
|
799
594
|
```js
|
|
800
|
-
import {
|
|
595
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
596
|
+
import { toYAML } from '@speclynx/apidom-core';
|
|
801
597
|
|
|
802
598
|
const objElement = new ObjectElement({ a: 'b' });
|
|
803
599
|
|
|
@@ -812,14 +608,15 @@ toYAML(objElement);
|
|
|
812
608
|
|
|
813
609
|
### dehydrate
|
|
814
610
|
|
|
815
|
-
Creates a [refract representation](https://github.com/refractproject/refract-spec) of
|
|
611
|
+
Creates a [refract representation](https://github.com/refractproject/refract-spec) of an Element.
|
|
816
612
|
|
|
817
613
|
```js
|
|
818
|
-
import {
|
|
614
|
+
import { NumberElement } from '@speclynx/apidom-datamodel';
|
|
615
|
+
import { dehydrate } from '@speclynx/apidom-core';
|
|
819
616
|
|
|
820
617
|
const numberElement = new NumberElement(1);
|
|
821
618
|
|
|
822
|
-
|
|
619
|
+
dehydrate(numberElement); // => { element: 'number', content: 1 }
|
|
823
620
|
```
|
|
824
621
|
|
|
825
622
|
### S-Expression
|
|
@@ -827,7 +624,8 @@ dehyrate(numberElement); // => { element: 'number', content: 1 }
|
|
|
827
624
|
Transforms ApiDOM into [symbolic expression](https://en.wikipedia.org/wiki/S-expression).
|
|
828
625
|
|
|
829
626
|
```js
|
|
830
|
-
import {
|
|
627
|
+
import { ObjectElement } from '@speclynx/apidom-datamodel';
|
|
628
|
+
import { sexprs } from '@speclynx/apidom-core';
|
|
831
629
|
|
|
832
630
|
const objectElement = new ObjectElement({ a: 1 });
|
|
833
631
|
|
|
@@ -846,36 +644,10 @@ sexprs(objectElement);
|
|
|
846
644
|
Create a [refracted string](https://github.com/refractproject/refract-spec) representation of an Element.
|
|
847
645
|
|
|
848
646
|
```js
|
|
849
|
-
import {
|
|
647
|
+
import { NumberElement } from '@speclynx/apidom-datamodel';
|
|
648
|
+
import { toString } from '@speclynx/apidom-core';
|
|
850
649
|
|
|
851
650
|
const numberElement = new NumberElement(1);
|
|
852
651
|
|
|
853
652
|
toString(numberElement); // => '{"element":"number","content":1}'
|
|
854
653
|
```
|
|
855
|
-
|
|
856
|
-
## Cloning
|
|
857
|
-
|
|
858
|
-
Following functions provide mechanism for creating shallow and deep copies of ApiDOM elements.
|
|
859
|
-
|
|
860
|
-
### Shallow cloning
|
|
861
|
-
|
|
862
|
-
Creates shallow clone of ApiDOM element.
|
|
863
|
-
|
|
864
|
-
```js
|
|
865
|
-
import { cloneShallow, ObjectElement } from '@speclynx/apidom-core';
|
|
866
|
-
|
|
867
|
-
const objectElement = new ObjectElement({ a: 'b' });
|
|
868
|
-
const objectElementShallowClone = cloneShallow(objectElement);
|
|
869
|
-
```
|
|
870
|
-
|
|
871
|
-
### Deep cloning
|
|
872
|
-
|
|
873
|
-
Creates deep clone of ApiDOM Element.
|
|
874
|
-
|
|
875
|
-
```js
|
|
876
|
-
import { cloneDeep, ObjectElement } from '@speclynx/apidom-core';
|
|
877
|
-
|
|
878
|
-
const objectElement = new ObjectElement({ a: 'b' });
|
|
879
|
-
const objectElementDeepClone = cloneDeep(objectElement);
|
|
880
|
-
```
|
|
881
|
-
|