Skip to content

Commit b5316be

Browse files
authored
fix(core): utilize proper YAML 1.2 serialization (#27)
1 parent 3d4d6dd commit b5316be

File tree

5 files changed

+56
-180
lines changed

5 files changed

+56
-180
lines changed

package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/apidom-core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,8 @@
4646
"ramda": "~0.32.0",
4747
"ramda-adjunct": "^6.0.0",
4848
"short-unique-id": "^5.3.2",
49-
"ts-mixer": "^6.0.4"
49+
"ts-mixer": "^6.0.4",
50+
"yaml": "^2.8.2"
5051
},
5152
"files": [
5253
"src/**/*.mjs",

packages/apidom-core/src/index.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export { default as toJSON } from './transformers/serializers/json.ts';
4141
/**
4242
* Transforms the ApiDOM into YAML string.
4343
*/
44-
export { default as toYAML } from './transformers/serializers/yaml-1-2.ts';
44+
export {
45+
default as toYAML,
46+
type YamlSerializerOptions,
47+
} from './transformers/serializers/yaml-1-2.ts';
4548

4649
/**
4750
* Creates a refract representation of an Element.
Lines changed: 34 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,43 @@
1-
import { Element, ArrayElement, ObjectElement } from '@speclynx/apidom-datamodel';
2-
import { traverse, type Path } from '@speclynx/apidom-traverse';
1+
import {
2+
Document,
3+
stringify,
4+
type CreateNodeOptions,
5+
type DocumentOptions,
6+
type SchemaOptions,
7+
type ToStringOptions,
8+
} from 'yaml';
9+
import { Element } from '@speclynx/apidom-datamodel';
10+
11+
import toValue from './value.ts';
312

4-
import serializeValue from './value.ts';
5-
6-
interface YamlVisitorOptions {
7-
readonly directive?: boolean;
8-
readonly indent?: number;
9-
}
10-
11-
class YamlVisitor {
12-
protected static readonly indentChar = ' ';
13-
14-
public result: string;
15-
16-
protected readonly indent: number;
17-
18-
constructor({ directive = false, indent = 0 }: YamlVisitorOptions = {}) {
19-
this.result = directive ? '%YAML 1.2\n---\n' : '';
20-
this.indent = indent;
21-
}
22-
23-
public NumberElement(path: Path<Element>): void {
24-
this.result += serializeValue(path.node);
25-
}
26-
27-
public BooleanElement(path: Path<Element>): void {
28-
const value = serializeValue(path.node);
29-
this.result += value ? 'true' : 'false';
30-
}
31-
32-
public StringElement(path: Path<Element>): void {
33-
// for simplicity and avoiding ambiguity we always wrap strings in quotes
34-
this.result += JSON.stringify(serializeValue(path.node));
35-
}
36-
37-
public NullElement(): void {
38-
this.result += 'null';
39-
}
40-
41-
public ArrayElement(path: Path<Element>): void {
42-
const element = path.node as ArrayElement;
43-
44-
if (element.length === 0) {
45-
this.result += '[]';
46-
path.skip();
47-
return;
48-
}
49-
50-
element.forEach((item) => {
51-
const visitor = new YamlVisitor({ indent: this.indent + 1 });
52-
const indent = YamlVisitor.indentChar.repeat(this.indent);
53-
54-
traverse(item, visitor);
55-
56-
const { result } = visitor;
57-
58-
this.result += result.startsWith('\n') ? `\n${indent}-${result}` : `\n${indent}- ${result}`;
59-
});
60-
61-
path.skip();
62-
}
63-
64-
public ObjectElement(path: Path<Element>): void {
65-
const element = path.node as ObjectElement;
66-
67-
if (element.length === 0) {
68-
this.result += '{}';
69-
path.skip();
70-
return;
71-
}
72-
73-
element.forEach((value, key) => {
74-
const keyVisitor = new YamlVisitor({ indent: this.indent + 1 });
75-
const valueVisitor = new YamlVisitor({ indent: this.indent + 1 });
76-
const indent = YamlVisitor.indentChar.repeat(this.indent);
77-
78-
traverse(key, keyVisitor);
79-
traverse(value, valueVisitor);
80-
81-
const { result: keyResult } = keyVisitor;
82-
const { result: valueResult } = valueVisitor;
83-
84-
this.result += valueResult.startsWith('\n')
85-
? `\n${indent}${keyResult}:${valueResult}`
86-
: `\n${indent}${keyResult}: ${valueResult}`;
87-
});
88-
89-
path.skip();
90-
}
13+
/**
14+
* @public
15+
*/
16+
export interface YamlSerializerOptions
17+
extends
18+
DocumentOptions,
19+
Pick<CreateNodeOptions, 'aliasDuplicateObjects'>,
20+
Pick<SchemaOptions, 'sortMapEntries'>,
21+
ToStringOptions {
22+
/** Include %YAML directive and document marker */
23+
directive?: boolean;
9124
}
9225

9326
/**
9427
* @public
9528
*/
96-
const serializer = (element: Element, { directive = false } = {}): string => {
97-
const visitor = new YamlVisitor({ directive });
98-
99-
traverse(element, visitor);
100-
101-
return visitor.result;
29+
const serializer = (
30+
element: Element,
31+
{ directive = false, aliasDuplicateObjects = false, ...options }: YamlSerializerOptions = {},
32+
): string => {
33+
const allOptions = { aliasDuplicateObjects, ...options };
34+
35+
if (directive) {
36+
const doc = new Document(toValue(element), allOptions);
37+
doc.directives!.yaml.explicit = true;
38+
return doc.toString(allOptions);
39+
}
40+
return stringify(toValue(element), allOptions);
10241
};
10342

10443
export default serializer;

packages/apidom-core/test/transformers/serializers/yaml1-2.ts

Lines changed: 14 additions & 82 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import dedent from 'dedent';
21
import { assert } from 'chai';
32

43
import { from } from '../../../src/index.ts';
@@ -10,44 +9,34 @@ describe('serializers', function () {
109
specify('should serialize to YAML 1.2', function () {
1110
const apidom = from(true)!;
1211
const serialized = serialize(apidom);
13-
const expected = dedent`
14-
true
15-
`;
1612

17-
assert.strictEqual(serialized, expected);
13+
assert.strictEqual(serialized, 'true\n');
1814
});
1915
});
2016

2117
context('given NumberElement', function () {
2218
specify('should serialize to YAML 1.2', function () {
2319
const apidom = from(1)!;
2420
const serialized = serialize(apidom);
25-
const expected = dedent`
26-
1
27-
`;
2821

29-
assert.strictEqual(serialized, expected);
22+
assert.strictEqual(serialized, '1\n');
3023
});
3124
});
3225

3326
context('given StringElement', function () {
3427
specify('should serialize to YAML 1.2', function () {
3528
const apidom = from('test')!;
3629
const serialized = serialize(apidom);
37-
const expected = dedent`
38-
"test"
39-
`;
4030

41-
assert.strictEqual(serialized, expected);
31+
assert.strictEqual(serialized, 'test\n');
4232
});
4333

4434
context('and is multiline', function () {
4535
specify('should serialize to YAML 1.2', function () {
4636
const apidom = from('test\n\ntest\n')!;
4737
const serialized = serialize(apidom);
48-
const expected = String.raw`"test\n\ntest\n"`;
4938

50-
assert.strictEqual(serialized, expected);
39+
assert.strictEqual(serialized, '|\ntest\n\ntest\n');
5140
});
5241
});
5342
});
@@ -56,137 +45,80 @@ describe('serializers', function () {
5645
specify('should serialize to YAML 1.2', function () {
5746
const apidom = from(null)!;
5847
const serialized = serialize(apidom);
59-
const expected = dedent`
60-
null
61-
`;
6248

63-
assert.strictEqual(serialized, expected);
49+
assert.strictEqual(serialized, 'null\n');
6450
});
6551
});
6652

6753
context('given empty ArrayElement', function () {
6854
specify('should serialize to YAML 1.2', function () {
6955
const apidom = from([])!;
7056
const serialized = serialize(apidom);
71-
const expected = dedent`
72-
[]
73-
`;
7457

75-
assert.strictEqual(serialized, expected);
58+
assert.strictEqual(serialized, '[]\n');
7659
});
7760
});
7861

7962
context('given simple ArrayElement', function () {
8063
specify('should serialize to YAML 1.2', function () {
8164
const apidom = from([1, true, 'test', null])!;
8265
const serialized = serialize(apidom, { directive: true });
83-
const expected = dedent`
84-
%YAML 1.2
85-
---
8666

87-
- 1
88-
- true
89-
- "test"
90-
- null
91-
`;
92-
93-
assert.strictEqual(serialized, expected);
67+
assert.strictEqual(serialized, '%YAML 1.2\n---\n- 1\n- true\n- test\n- null\n');
9468
});
9569
});
9670

9771
context('given nested ArrayElement', function () {
9872
specify('should serialize to YAML 1.2', function () {
9973
const apidom = from([1, [true, 'test', null]])!;
10074
const serialized = serialize(apidom, { directive: true });
101-
const expected = dedent`
102-
%YAML 1.2
103-
---
104-
105-
- 1
106-
-
107-
- true
108-
- "test"
109-
- null
110-
`;
111-
112-
assert.strictEqual(serialized, expected);
75+
76+
assert.strictEqual(serialized, '%YAML 1.2\n---\n- 1\n- - true\n - test\n - null\n');
11377
});
11478
});
11579

11680
context('given ArrayElement in ObjectElement', function () {
11781
specify('should serialize to YAML 1.2', function () {
11882
const apidom = from({ a: [1] })!;
11983
const serialized = serialize(apidom, { directive: true });
120-
const expected = dedent`
121-
%YAML 1.2
122-
---
123-
124-
"a":
125-
- 1
126-
`;
12784

128-
assert.strictEqual(serialized, expected);
85+
assert.strictEqual(serialized, '%YAML 1.2\n---\na:\n - 1\n');
12986
});
13087
});
13188

13289
context('given empty ObjectElement', function () {
13390
specify('should serialize to YAML 1.2', function () {
13491
const apidom = from({})!;
13592
const serialized = serialize(apidom);
136-
const expected = dedent`
137-
{}
138-
`;
13993

140-
assert.strictEqual(serialized, expected);
94+
assert.strictEqual(serialized, '{}\n');
14195
});
14296
});
14397

14498
context('given simple ObjectElement', function () {
14599
specify('should serialize to YAML 1.2', function () {
146100
const apidom = from({ a: 1, b: true })!;
147101
const serialized = serialize(apidom, { directive: true });
148-
const expected = dedent`
149-
%YAML 1.2
150-
---
151102

152-
"a": 1
153-
"b": true
154-
`;
155-
156-
assert.strictEqual(serialized, expected);
103+
assert.strictEqual(serialized, '%YAML 1.2\n---\na: 1\nb: true\n');
157104
});
158105
});
159106

160107
context('given nested ObjectElement', function () {
161108
specify('should serialize to YAML 1.2', function () {
162109
const apidom = from({ a: 1, b: { c: true } })!;
163110
const serialized = serialize(apidom, { directive: true });
164-
const expected = dedent`
165-
%YAML 1.2
166-
---
167-
168-
"a": 1
169-
"b":
170-
"c": true
171-
`;
172111

173-
assert.strictEqual(serialized, expected);
112+
assert.strictEqual(serialized, '%YAML 1.2\n---\na: 1\nb:\n c: true\n');
174113
});
175114
});
176115

177116
context('given ObjectElement in ArrayElement', function () {
178117
specify('should serialize to YAML 1.2', function () {
179118
const apidom = from([{ a: true }])!;
180119
const serialized = serialize(apidom, { directive: true });
181-
const expected = dedent`
182-
%YAML 1.2
183-
---
184-
185-
-
186-
"a": true
187-
`;
188120

189-
assert.strictEqual(serialized, expected);
121+
assert.strictEqual(serialized, '%YAML 1.2\n---\n- a: true\n');
190122
});
191123
});
192124
});

0 commit comments

Comments
 (0)