source: node_modules/@swagger-api/apidom-core/README.md@ e48199a

main
Last change on this file since e48199a was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 20.9 KB
Line 
1# @swagger-api/apidom-core
2
3`apidom-core` is a package that contains tools for manipulating the ApiDOM structures.
4
5## Installation
6
7You can install this package via [npm CLI](https://docs.npmjs.com/cli) by running the following command:
8
9```sh
10 $ npm install @swagger-api/apidom-core
11```
12
13---
14
15## Base namespace
16
17Base namespace consists of [four higher order elements](https://github.com/swagger-api/apidom/tree/main/packages/apidom-core/src/elements) implemented on top
18of [primitive ones](https://github.com/refractproject/minim/tree/master/lib/primitives).
19
20```js
21import { createNamespace } from '@swagger-api/apidom-core';
22
23const namespace = createNamespace();
24
25const objectElement = new namespace.elements.Object();
26const commentElement = new namespace.elements.Comment();
27```
28
29It's possible to create namespace instances using another namespaces.
30
31```js
32import { createNamespace } from '@swagger-api/apidom-core';
33import openApi3_1Namespace from '@swagger-api/apidom-ns-openapi-3-1';
34
35const namespace = createNamespace(openApi3_1Namespace);
36
37const objectElement = new namespace.elements.Object();
38const openApiElement = new namespace.elements.OpenApi3_1();
39```
40
41When namespace instance is created in this way, it will extend the base namespace
42with the namespace provided as an argument.
43
44---
45
46## Predicates
47
48This package exposes [predicates](https://github.com/swagger-api/apidom/blob/main/packages/apidom-core/src/predicates/index.ts)
49for all primitive elements and all higher order elements that are part of the base namespace.
50
51```js
52import { CommentElement, isCommentElement } from '@swagger-api/apidom-core';
53
54const commentElement = new CommentElement();
55
56isCommentElement(commentElement); // => true
57```
58
59[Predicate helpers](https://github.com/swagger-api/apidom/blob/main/packages/apidom-core/src/predicates/helpers.ts)
60helps in building predicates for this and other packages.
61
62```js
63import { createPredicate } from '@swagger-api/apidom-core';
64
65const isMyElement = createPredicate(
66 ({ hasBasicElementProps, isElementType, primitiveEq }) => {
67 return (element) =>
68 element instanceof MyElement ||
69 (hasBasicElementProps(element) && isElementType('myElement', element) && primitiveEq('object', element));
70 },
71);
72```
73
74---
75
76## Transcluder
77
78Transclusion is the inclusion of one ApiDOM fragment into another ApiDOM fragment.
79Our [transcluder](https://github.com/swagger-api/apidom/tree/main/packages/apidom-core/src/transcluder) does exactly that and is based on mutating algorithm.
80
81```js
82import { transclude, ArrayElement, NumberElement } from '@swagger-api/apidom-core';
83
84const element = new ArrayElement([1, 2, 3]);
85const search = element.get(1);
86const replace = new NumberElement(4);
87
88transclude(search, replace, element); // => ArrayElement<[1, 4, 3]>
89```
90
91When multiple transclusions are going to be performed use [Transcluder stamp](https://github.com/swagger-api/apidom/blob/main/packages/apidom-core/src/transcluder/Transcluder.ts)
92for optimal performance.
93
94```js
95import { Transcluder, ArrayElement, NumberElement } from '@swagger-api/apidom-core';
96
97const element = new ArrayElement([1, 2, 3]);
98const search = element.get(1);
99const replace = new NumberElement(4);
100const transcluder = Transcluder({ element });
101
102transcluder.transclude(search, replace); // => ArrayElement<[1, 4, 3]>
103```
104
105---
106
107## Deep merging
108
109`deepmerge` functions merged members of two or more ObjectElements deeply
110and handles deep merging of ArrayElements as well. This deep merge implementation
111is a functional equivalent of [deepmerge](https://www.npmjs.com/package/deepmerge)
112that works equivalently on ApiDOM structures.
113
114### API
115
116#### deepmerge(target, source, [options])
117
118Merge two ApiDOM elements target and source deeply, returning a new merged ApiDOM element with the elements
119from both target and source. If an element at the same key is present for both target and source,
120the value from source will appear in the result. Merging creates a new ApiDOM element,
121so that neither target nor source is modified (operation is immutable).
122
123```js
124import { deepmerge, ObjectElement } from '@swagger-api/apidom-core';
125
126const x = new ObjectElement({
127 foo: { bar: 3 },
128 array: [
129 {
130 does: 'work',
131 too: [1, 2, 3],
132 },
133 ],
134});
135
136const y = new ObjectElement({
137 foo: { baz: 4 },
138 quux: 5,
139 array: [
140 {
141 does: 'work',
142 too: [4, 5, 6],
143 },
144 {
145 really: 'yes',
146 },
147 ],
148});
149
150const output = deepmerge(x, y);
151// =>
152// ObjectElement({
153// foo: ObjectElement({
154// bar: 3,
155// baz: 4,
156// }),
157// array: ArrayElement([
158// ObjectElement({
159// does: 'work',
160// too: [1, 2, 3],
161// }),
162// ObjectElement({
163// does: 'work',
164// too: [4, 5, 6],
165// }),
166// ObjectElement({
167// really: 'yes',
168// }),
169// ]),
170// quux: 5,
171// })
172```
173
174#### deepmerge.all([element1, element2, ...], [options])
175
176Merges any number of ApiDOM elements into a single ApiDOM element.
177
178```js
179import { deepmerge, ObjectElement } from '@swagger-api/apidom-core';
180
181const foobar = new ObjectElement({ foo: { bar: 3 } });
182const foobaz = new ObjectElement({ foo: { baz: 4 } });
183const bar = new ObjectElement({ bar: 'yay!' });
184
185const output = deepmerge.all([ foobar, foobaz, bar ]);
186// => ObjectElement({ foo: { bar: 3, baz: 4 }, bar: 'yay!' })
187```
188
189### Options
190
191#### arrayElementMerge
192
193There are multiple ways to merge two ArrayElements, below are a few examples, but you can also create your own custom function.
194
195Your `arrayElementMerge` function will be called with three arguments: a `target` ArrayElement, the `source` ArrayElement,
196and an `options` object.
197
198```js
199import { deepmerge, ArrayElement } from '@swagger-api/apidom-core';
200
201const arrayElementMerge = (destination, source, options) => source;
202
203const target = new ArrayElement([1, 2, 3]);
204const source = new ArrayElement([3, 2, 1]);
205const output = deepmerge(target, source, { arrayElementMerge });
206// => ArrayElement([3, 2, 1]);
207```
208
209#### objectElementMerge
210
211There are multiple ways to merge two ObjectElements, below are a few examples, but you can also create your own custom function.
212
213Your `objectElementMerge` function will be called with three arguments: a `target` ObjectElement, the `source` ObjectElement,
214and an `options` object.
215
216```js
217import { deepmerge, ObjectElement } from '@swagger-api/apidom-core';
218
219const objectElementMerge = (destination, source, options) => source;
220
221const target = new ObjectElement({a: 1, b: 2});
222const source = new ObjectElement({c: 3, d: 4});
223const output = deepmerge(target ,source, { objectElementMerge });
224// => ObjectElement({c: 3, d: 4});
225```
226
227#### isMergeableElement
228
229By default, deepmerge clones every member from ObjectElement and ArrayElement.
230You may not want this, if your ObjectElements are of special types,
231and you want to copy the whole ObjectElement instead of just copying its member.
232
233You can accomplish this by passing in a function for the `isMergeableElement` option.
234
235```js
236import { deepmerge, ObjectElement, isObjectElement } from '@swagger-api/apidom-core';
237
238class CustomObjectElement extends ObjectElement {
239 element = 'custom';
240}
241const instantiatedCustomObjectElement = new CustomObjectElement({ special: 'oh yeah' });
242
243const target = new ObjectElement({
244 someProperty: {
245 cool: 'oh for sure',
246 },
247});
248const source = new ObjectElement({
249 someProperty: instantiatedCustomObjectElement,
250});
251const isMergeableElement = (element: Element) => isObjectElement(element) && !(element instanceof CustomObjectElement);
252
253const output = deepmerge(target, source, {
254 isMergeableElement,
255});
256// output.get('someProperty').get('cool'); // => undefined
257// output.get('someProperty').get('special'); // => 'oh yeah'
258// output.get('someProperty') instanceof CustomObjectElement // => true
259```
260
261#### customMerge
262
263Specifies a function which can be used to override the default merge behavior for a member, based on the key name.
264The `customMerge` function will be passed the key for each member, and should return the function which should
265be used to merge the values for that member.
266It may also return undefined, in which case the default merge behaviour will be used.
267
268```js
269import { deepmerge, ObjectElement } from '@swagger-api/apidom-core';
270
271const alex = new ObjectElement({
272 name: {
273 first: 'Alex',
274 last: 'Alexson'
275 },
276 pets: ['Cat', 'Parrot']
277});
278const tony = new ObjectElement({
279 name: {
280 first: 'Tony',
281 last: 'Tonison'
282 },
283 pets: ['Dog']
284});
285
286const mergeNames = (nameA: ObjectElement, nameB: ObjectElement) =>
287 new StringElement(`${toValue(nameA.get('first'))} and ${toValue(nameB.get('first'))}`);
288const customMerge = (key: Element) => (toValue(key) === 'name' ? mergeNames : undefined);
289
290const output = deepmerge(alex, tony, { customMerge });
291// output.get('name'); // => StrignElement('Alex and Tony')
292// output.get('pets'); // => ArrayElement(['Cat', 'Parrot', 'Dog'])
293```
294
295#### clone
296
297Defaults to `true`.
298
299If `clone` is false then child elements will be copied directly instead of being cloned.
300
301---
302
303## Refractors
304
305Refractor is a special layer inside the base namespace that can transform JavaScript structures
306into generic ApiDOM structures built from elements of this base namespace.
307
308**Refracting JavaScript structures**:
309
310```js
311import { ObjectElement } from '@swagger-api/apidom-core';
312
313const object = {
314 title: 'my title',
315 description: 'my description',
316 version: '0.1.0',
317};
318
319ObjectElement.refract(object); // => ObjectElement({ title, description, version })
320```
321
322```js
323import { CommentElement } from '@swagger-api/apidom-core';
324
325const comment = 'this is comment';
326
327CommentElement.refract(comment); // => CommentElement('this is comment')
328```
329
330### Refractor plugins
331
332Refractors can accept plugins as a second argument of refract static method.
333
334```js
335import { ObjectElement, StringElement } from '@swagger-api/apidom-core';
336
337const object = { a: 'b' };
338
339const plugin = ({ predicates, namespace }) => ({
340 name: 'plugin',
341 pre() {
342 console.dir('runs before traversal');
343 },
344 visitor: {
345 ObjectElement(objectElement) {
346 objectElement.getMember('a').value = new StringElement('c');
347 },
348 },
349 post() {
350 console.dir('runs after traversal');
351 },
352});
353
354ObjectElement.refract(object, { plugins: [plugin] }); // => ObjectElement({ a = 'c' })
355```
356You can define as many plugins as needed to enhance the resulting namespaced ApiDOM structure.
357If multiple plugins with the same visitor method are defined, they run in parallel (just like in Babel).
358
359#### Element identity plugin
360
361`apidom` package comes with `refractorPluginElementIdentity`. When used, this plugin will
362assign unique ID to all elements in ApiDOM tree.
363
364```js
365import { refractorPluginElementIdentity, ObjectElement } from '@swagger-api/apidom-core';
366
367const objectElement = ObjectElement.refract({ a: 'b' }, {
368 plugins: [
369 refractorPluginElementIdentity(),
370 ]
371});
372
373objectElement.id; // 8RaWF9
374objectElement.getMember('a').key.id; // NdHHV7
375objectElement.getMember('a').value.id; // rFGVFP
376```
377
378You can configure the plugin to generate unique IDs in the specific length:
379
380```js
381import { refractorPluginElementIdentity, ObjectElement } from '@swagger-api/apidom-core';
382
383const objectElement = ObjectElement.refract({ a: 'b' }, {
384 plugins: [
385 refractorPluginElementIdentity({ length: 36}),
386 ]
387});
388
389objectElement.id; // OnReGGrO7fMd9ztacvGfwGbOdGKuOFLiQQ1W
390objectElement.getMember('a').key.id; // BN6rHsmqI56SMQ1elshtbgRVECtEWNYS9lmd
391objectElement.getMember('a').value.id; // Ki4tWmf9xw9Lwb8MxkXJq1uONmJrmhXifmsI
392```
393
394#### Semantic element identity plugin
395
396`apidom` package comes with `refractorPluginSemanticElementIdentity`. When used, this plugin will
397assign unique ID to all non-primitive elements in ApiDOM tree. Primitive elements include
398`ObjectElement`, `ArrayElement`, `StringElement`, `BooleanElement`, `NullElement` and `NumberElement`.
399
400```js
401import { refractorPluginSemanticElementIdentity, ObjectElement } from '@swagger-api/apidom-core';
402import { InfoElement } from '@swagger-api/apidom-ns-openapi-3-1';
403
404const infoElement = InfoElement.refract({ title: 'title' });
405const objectElement = ObjectElement.refract({ a: 'b', info: infoElement }, {
406 plugins: [
407 refractorPluginSemanticElementIdentity(),
408 ]
409});
410
411objectElement.id; // ''
412objectElement.getMember('a').key.id; // ''
413objectElement.getMember('a').value.id; // ''
414objectElement.getMember('info').key.id; // ''
415objectElement.getMember('info').value.id; // '8RaWF9'
416```
417
418You can configure the plugin to generate unique IDs in the specific length:
419
420```js
421import { refractorPluginSemanticElementIdentity, ObjectElement } from '@swagger-api/apidom-core';
422import { InfoElement } from '@swagger-api/apidom-ns-openapi-3-1';
423
424const infoElement = InfoElement.refract({ title: 'title' });
425const objectElement = ObjectElement.refract({ a: 'b', info: infoElement }, {
426 plugins: [
427 refractorPluginSemanticElementIdentity({ length: 36 }),
428 ]
429});
430
431objectElement.id; // ''
432objectElement.getMember('a').key.id; // ''
433objectElement.getMember('a').value.id; // ''
434objectElement.getMember('info').key.id; // ''
435objectElement.getMember('info').value.id; // 'OnReGGrO7fMd9ztacvGfwGbOdGKuOFLiQQ1W'
436```
437
438---
439
440## Traversal
441
442`apidom` comes with its own traversal algorithm along with couple of convenient abstractions on top of it.
443
444### visit
445
446[visit](https://github.com/swagger-api/apidom/blob/main/packages/apidom-core/src/traversal/visitor.ts#L104-L103) will walk through an AST using a depth first traversal, calling
447the visitor's enter function at each node in the traversal, and calling the
448leave function after visiting that node and all of its child nodes.
449
450By returning different values from the enter and leave functions, the
451behavior of the visitor can be altered, including skipping over a sub-tree of
452the ApiDOM (by returning false), editing the ApiDOM by returning a value or null
453to remove the value, or to stop the whole traversal by returning [BREAK](https://github.com/swagger-api/apidom/blob/main/packages/apidom-core/src/index.ts#L52).
454
455When using `visit` to edit an ApiDOM, the original ApiDOM will not be modified, and
456a new version of the ApiDOM with the changes applied will be returned from the
457visit function.
458
459```js
460import { visit, ObjectElement, NumberElement } from '@swagger-api/apidom-core';
461
462const visitor = {
463 NumberElement(numberElement) {
464 return new NumberElement(2);
465 },
466};
467const element = new ObjectElement({ a: 1 });
468
469const newElement = visit(element, visitor); // => ObjectElement<{a: 2}>
470```
471
472This function originally comes from [@swagger-api/apidom-ast package](https://github.com/swagger-api/apidom/blob/main/packages/apidom-ast/src/visitor.ts)
473and is originally designed to work with [CST](https://en.wikipedia.org/wiki/Parse_tree). `apidom` package
474imports it, specializes it to work with ApiDOM and re-export it.
475
476All following algorithms are based on `visit` function.
477
478### filter
479
480Finds all elements matching the predicate.
481
482```js
483import { ObjectElement, filter, isNumberElement } from '@swagger-api/apidom-core'
484
485const objElement = new ObjectElement({ a: 'b', c: 2 });
486
487filter(isNumberElement, objElement); // => ArraySlice<[NumberElement<2>]>
488```
489
490### find
491
492Find first element that satisfies the provided predicate.
493
494```js
495import { ObjectElement, find, isMemberElement } from '@swagger-api/apidom-core'
496
497const objElement = new ObjectElement({ a: 'b', c: 2 });
498
499find(isNumberElement, objElement); // => NumberElement<2>
500```
501
502### findAtOffset
503
504ApiDOM nodes can be associated with source maps. This function finds the most inner node at the given offset.
505If includeRightBound is set, also finds nodes that end at the given offset.
506
507```js
508import { findAtOffset } from '@swagger-api/apidom-core'
509
510findAtOffset(3, elementWithSourceMaps); // => returns most inner node at offset 3
511```
512
513### reject
514
515Complement of [filter](#filter).
516
517```js
518import { ArrayElement, reject, isNumberElement } from '@swagger-api/apidom-core'
519
520const arrayElement = new ArrayElement([1, 'a']);
521
522reject(isNumberElement, arrayElement); // => ArraySlice<[StringElement<'a'>]>
523```
524
525### some
526
527Tests whether at least one element passes the predicate.
528
529```js
530import { ArrayElement, some, isNumberElement } from '@swagger-api/apidom-core'
531
532const arrayElement = new ArrayElement([1, 'a']);
533
534some(isNumberElement, arrayElement); // => true
535```
536
537### traverse
538
539Executes the callback on this element and all descendants.
540
541```js
542import { ArrayElement, traverse } from '@swagger-api/apidom-core'
543
544const arrayElement = new ArrayElement([1, 'a']);
545
546traverse(console.dir, arrayElement); // => prints ArrayElement, NumberElement, StringElement in this order
547```
548
549The execution of the callback can be controlled further by providing a predicate.
550
551```js
552import { ArrayElement, traverse, isNumberElement } from '@swagger-api/apidom-core'
553
554const arrayElement = new ArrayElement([1, 'a']);
555
556traverse({ callback: console.dir, predicate: isNumberElement }, arrayElement); // => prints NumberElement<1>
557```
558
559### parents
560
561Computes upwards edges from every child to its parent.
562
563#### ObjectElement example
564
565```js
566import { parents, ObjectElement } from '@swagger-api/apidom-core';
567
568const objectElement = new ObjectElement({ key: 'value' });
569const memberElement = objectElement.getMember('key');
570const { key: keyElement, value: valueElement } = memberElement;
571
572const parentEdges = parents(objectElement); // => WeakMap<ChildElement, ParentElement>
573
574parentEdges.get(memberElement) === objectElement; // => true
575parentEdges.get(keyElement) === memberElement; // => true
576parentEdges.get(valueElement) === memberElement; // => true
577```
578
579#### ArrayElement example
580
581```js
582import { parents, ArrayElement, StringElement } from '@swagger-api/apidom-core';
583
584const itemElement1 = new StringElement('item1');
585const itemElement2 = new StringElement('item2');
586const arrayElement = new ArrayElement([itemElement1, itemElement2]);
587
588const parentEdges = parents(arrayElement); // => WeakMap<ChildElement, ParentElement>
589
590parentEdges.get(itemElement1) === arrayElement; // => true
591parentEdges.get(itemElement2) === arrayElement; // => true
592```
593
594---
595
596## Transformers
597
598Following functions transforms ApiDOM between its various forms. All transformers (except `toValue`) can accept
599ApiDOM namespace instance as a second argument.
600
601### from
602
603Transforms data to an Element from a particular namespace.
604
605From a [refracted string](https://github.com/refractproject/refract-spec) form:
606
607```js
608import { from } from '@swagger-api/apidom-core';
609
610const refractedString = '{"element":"number","content":1}';
611
612from(refractedString); // => NumberElement<1>
613```
614
615From a [refracted](https://github.com/refractproject/refract-spec) form:
616
617```js
618import { from } from '@swagger-api/apidom-core';
619
620const refracted = { element: 'number', content: 1 };
621
622from(refracted); // => NumberElement<1>
623```
624
625From a JavaScript form:
626
627```js
628import { from } from '@swagger-api/apidom-core';
629
630const javascriptForm = 1;
631
632from(javascriptForm); // => NumberElement<1>
633```
634
635### toValue
636
637Transforms the ApiDOM into JavaScript POJO. This POJO would be the result of interpreting the ApiDOM
638into JavaScript structure. This function can handle cycles in ApiDOM structure.
639
640```js
641import { toValue, ObjectElement } from '@swagger-api/apidom-core';
642
643const objElement = new ObjectElement({ a: 'b' });
644
645toValue(objElement); // => { a: 'b' }
646```
647
648### toJSON
649
650Transforms the ApiDOM into JSON string.
651
652```js
653import { toJSON, ObjectElement } from '@swagger-api/apidom-core';
654
655const objElement = new ObjectElement({ a: 'b' });
656
657toJSON(objElement); // => '{"a":"b"}'
658```
659
660### toYAML
661
662Transforms the ApiDOM into JSON string.
663
664```js
665import { toYAML, ObjectElement } from '@swagger-api/apidom-core';
666
667const objElement = new ObjectElement({ a: 'b' });
668
669toYAML(objElement);
670/**
671 * %YAML 1.2
672 * ---
673 *
674 * "a": "b"
675 */
676```
677
678### dehydrate
679
680Creates a [refract representation](https://github.com/refractproject/refract-spec) of the an Element.
681
682```js
683import { dehyrate, NumberElement } from '@swagger-api/apidom-core';
684
685const numberElement = new NumberElement(1);
686
687dehyrate(numberElement); // => { element: 'number', content: 1 }
688```
689
690### S-Expression
691
692Transforms ApiDOM into [symbolic expression](https://en.wikipedia.org/wiki/S-expression).
693
694```js
695import { sexprs, ObjectElement } from '@swagger-api/apidom-core';
696
697const objectElement = new ObjectElement({ a: 1 });
698
699sexprs(objectElement);
700// =>
701// (ObjectElement
702// (MemberElement
703// (StringElement)
704// (NumberElement)))
705
706
707```
708
709### toString
710
711Create a [refracted string](https://github.com/refractproject/refract-spec) representation of an Element.
712
713```js
714import { toString, NumberElement } from '@swagger-api/apidom-core';
715
716const numberElement = new NumberElement(1);
717
718toString(numberElement); // => '{"element":"number","content":1}'
719```
720
721## Cloning
722
723Following functions provide mechanism for creating shallow and deep copies of ApiDOM elements.
724
725### Shallow cloning
726
727Creates shallow clone of ApiDOM element.
728
729```js
730import { cloneShallow, ObjectElement } from '@swagger-api/apidom-core';
731
732const objectElement = new ObjectElement({ a: 'b' });
733const objectElementShallowClone = cloneShallow(objectElement);
734```
735
736### Deep cloning
737
738Creates deep clone of ApiDOM Element.
739
740```js
741import { cloneDeep, ObjectElement } from '@swagger-api/apidom-core';
742
743const objectElement = new ObjectElement({ a: 'b' });
744const objectElementDeepClone = cloneDeep(objectElement);
745```
746
Note: See TracBrowser for help on using the repository browser.