@speclynx/apidom-core 2.11.0 → 2.12.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 +6 -0
- package/README.md +31 -0
- package/dist/apidom-core.browser.js +516 -37
- package/dist/apidom-core.browser.min.js +1 -1
- package/package.json +5 -5
- package/src/fields/fixed-fields.cjs +4 -0
- package/src/fields/fixed-fields.mjs +4 -0
- package/src/refractor/toolbox.cjs +1 -0
- package/src/refractor/toolbox.mjs +2 -1
- package/src/specification.cjs +13 -1
- package/src/specification.mjs +13 -1
- package/src/transformers/serializers/json.cjs +65 -1
- package/src/transformers/serializers/json.mjs +66 -2
- package/src/transformers/serializers/yaml-1-2.cjs +111 -0
- package/src/transformers/serializers/yaml-1-2.mjs +113 -1
- package/types/apidom-core.d.ts +25 -8
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@speclynx/apidom-core",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.12.0",
|
|
4
4
|
"description": "Tools for manipulating ApiDOM structures.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
@@ -40,9 +40,9 @@
|
|
|
40
40
|
"license": "Apache-2.0",
|
|
41
41
|
"dependencies": {
|
|
42
42
|
"@babel/runtime-corejs3": "^7.28.4",
|
|
43
|
-
"@speclynx/apidom-datamodel": "^2.
|
|
44
|
-
"@speclynx/apidom-error": "^2.
|
|
45
|
-
"@speclynx/apidom-traverse": "^2.
|
|
43
|
+
"@speclynx/apidom-datamodel": "^2.12.0",
|
|
44
|
+
"@speclynx/apidom-error": "^2.12.0",
|
|
45
|
+
"@speclynx/apidom-traverse": "^2.12.0",
|
|
46
46
|
"ramda": "~0.32.0",
|
|
47
47
|
"ramda-adjunct": "^6.0.0",
|
|
48
48
|
"short-unique-id": "^5.3.2",
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"README.md",
|
|
60
60
|
"CHANGELOG.md"
|
|
61
61
|
],
|
|
62
|
-
"gitHead": "
|
|
62
|
+
"gitHead": "672efe907afef6ae5b547a8028db077297a0c52e"
|
|
63
63
|
}
|
|
@@ -25,6 +25,7 @@ const predicates = {
|
|
|
25
25
|
isCommentElement: _apidomDatamodel.isCommentElement,
|
|
26
26
|
isParseResultElement: _apidomDatamodel.isParseResultElement,
|
|
27
27
|
isSourceMapElement: _apidomDatamodel.isSourceMapElement,
|
|
28
|
+
hasElementStyle: _apidomDatamodel.hasElementStyle,
|
|
28
29
|
hasElementSourceMap: _apidomDatamodel.hasElementSourceMap,
|
|
29
30
|
includesSymbols: _apidomDatamodel.includesSymbols,
|
|
30
31
|
includesClasses: _apidomDatamodel.includesClasses
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isElement, isStringElement, isNumberElement, isNullElement, isBooleanElement, isArrayElement, isObjectElement, isMemberElement, isPrimitiveElement, isLinkElement, isRefElement, isAnnotationElement, isCommentElement, isParseResultElement, isSourceMapElement, hasElementSourceMap, includesSymbols, includesClasses } from '@speclynx/apidom-datamodel';
|
|
1
|
+
import { isElement, isStringElement, isNumberElement, isNullElement, isBooleanElement, isArrayElement, isObjectElement, isMemberElement, isPrimitiveElement, isLinkElement, isRefElement, isAnnotationElement, isCommentElement, isParseResultElement, isSourceMapElement, hasElementStyle, hasElementSourceMap, includesSymbols, includesClasses } from '@speclynx/apidom-datamodel';
|
|
2
2
|
import defaultNamespace from "../namespace.mjs";
|
|
3
3
|
/**
|
|
4
4
|
* @public
|
|
@@ -19,6 +19,7 @@ const predicates = {
|
|
|
19
19
|
isCommentElement,
|
|
20
20
|
isParseResultElement,
|
|
21
21
|
isSourceMapElement,
|
|
22
|
+
hasElementStyle,
|
|
22
23
|
hasElementSourceMap,
|
|
23
24
|
includesSymbols,
|
|
24
25
|
includesClasses
|
package/src/specification.cjs
CHANGED
|
@@ -35,7 +35,19 @@ const resolveSpecification = specification => {
|
|
|
35
35
|
if ((0, _ramdaAdjunct.isPlainObject)(val) && (0, _ramda.has)('$ref', val) && (0, _ramda.propSatisfies)(_ramdaAdjunct.isString, '$ref', val)) {
|
|
36
36
|
const $ref = (0, _ramda.path)(['$ref'], val);
|
|
37
37
|
const pointer = (0, _ramdaAdjunct.trimCharsStart)('#/', $ref);
|
|
38
|
-
|
|
38
|
+
const resolved = (0, _ramda.path)(pointer.split('/'), root);
|
|
39
|
+
// merge extra properties (e.g. alias) from the $ref object into the resolved value
|
|
40
|
+
const {
|
|
41
|
+
$ref: _,
|
|
42
|
+
...rest
|
|
43
|
+
} = val;
|
|
44
|
+
if (Object.keys(rest).length > 0 && (0, _ramdaAdjunct.isPlainObject)(resolved)) {
|
|
45
|
+
return {
|
|
46
|
+
...resolved,
|
|
47
|
+
...rest
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
return resolved;
|
|
39
51
|
}
|
|
40
52
|
if ((0, _ramdaAdjunct.isPlainObject)(val)) {
|
|
41
53
|
return traverse(val, root, newPath);
|
package/src/specification.mjs
CHANGED
|
@@ -32,7 +32,19 @@ export const resolveSpecification = specification => {
|
|
|
32
32
|
if (isPlainObject(val) && has('$ref', val) && propSatisfies(isString, '$ref', val)) {
|
|
33
33
|
const $ref = path(['$ref'], val);
|
|
34
34
|
const pointer = trimCharsStart('#/', $ref);
|
|
35
|
-
|
|
35
|
+
const resolved = path(pointer.split('/'), root);
|
|
36
|
+
// merge extra properties (e.g. alias) from the $ref object into the resolved value
|
|
37
|
+
const {
|
|
38
|
+
$ref: _,
|
|
39
|
+
...rest
|
|
40
|
+
} = val;
|
|
41
|
+
if (Object.keys(rest).length > 0 && isPlainObject(resolved)) {
|
|
42
|
+
return {
|
|
43
|
+
...resolved,
|
|
44
|
+
...rest
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
return resolved;
|
|
36
48
|
}
|
|
37
49
|
if (isPlainObject(val)) {
|
|
38
50
|
return traverse(val, root, newPath);
|
|
@@ -3,9 +3,73 @@
|
|
|
3
3
|
var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequireDefault").default;
|
|
4
4
|
exports.__esModule = true;
|
|
5
5
|
exports.default = void 0;
|
|
6
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
6
7
|
var _value = _interopRequireDefault(require("./value.cjs"));
|
|
8
|
+
const getStyle = element => {
|
|
9
|
+
return element.style?.json ?? {};
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @public
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Builds a POJO from an ApiDOM element tree. Numbers with rawContent
|
|
18
|
+
* are replaced with sentinel strings; all other values go through toValue().
|
|
19
|
+
*/
|
|
20
|
+
const toPojo = (element, sentinels) => {
|
|
21
|
+
const visited = new WeakSet();
|
|
22
|
+
const convert = node => {
|
|
23
|
+
if (!(0, _apidomDatamodel.isElement)(node)) return node;
|
|
24
|
+
if (visited.has(node)) return null;
|
|
25
|
+
visited.add(node);
|
|
26
|
+
if ((0, _apidomDatamodel.isObjectElement)(node)) {
|
|
27
|
+
const obj = {};
|
|
28
|
+
node.forEach((value, key) => {
|
|
29
|
+
const k = (0, _apidomDatamodel.isElement)(key) ? (0, _value.default)(key) : key;
|
|
30
|
+
if (typeof k === 'string') obj[k] = convert(value);
|
|
31
|
+
});
|
|
32
|
+
return obj;
|
|
33
|
+
}
|
|
34
|
+
if ((0, _apidomDatamodel.isArrayElement)(node)) {
|
|
35
|
+
const arr = [];
|
|
36
|
+
node.forEach(item => arr.push(convert(item)));
|
|
37
|
+
return arr;
|
|
38
|
+
}
|
|
39
|
+
if ((0, _apidomDatamodel.isRefElement)(node)) return String((0, _value.default)(node));
|
|
40
|
+
if ((0, _apidomDatamodel.isLinkElement)(node)) return (0, _apidomDatamodel.isStringElement)(node.href) ? (0, _value.default)(node.href) : '';
|
|
41
|
+
|
|
42
|
+
// number with rawContent — substitute with sentinel
|
|
43
|
+
if ((0, _apidomDatamodel.isNumberElement)(node)) {
|
|
44
|
+
const style = getStyle(node);
|
|
45
|
+
if (typeof style.rawContent === 'string') {
|
|
46
|
+
const sentinel = `\0RAW${sentinels.size}\0`;
|
|
47
|
+
sentinels.set(sentinel, style.rawContent);
|
|
48
|
+
return sentinel;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return (0, _value.default)(node);
|
|
52
|
+
};
|
|
53
|
+
return convert(element);
|
|
54
|
+
};
|
|
55
|
+
|
|
7
56
|
/**
|
|
8
57
|
* @public
|
|
9
58
|
*/
|
|
10
|
-
const serializer = (element, replacer, space
|
|
59
|
+
const serializer = (element, replacer, space, options) => {
|
|
60
|
+
if (options?.preserveStyle) {
|
|
61
|
+
const style = getStyle(element);
|
|
62
|
+
const indent = typeof space === 'number' ? space : typeof style.indent === 'number' ? style.indent : 0;
|
|
63
|
+
const sentinels = new Map();
|
|
64
|
+
const pojo = toPojo(element, sentinels);
|
|
65
|
+
let serialized = JSON.stringify(pojo, null, indent);
|
|
66
|
+
|
|
67
|
+
// replace quoted sentinels with raw number representations
|
|
68
|
+
for (const [sentinel, raw] of sentinels) {
|
|
69
|
+
serialized = serialized.replace(JSON.stringify(sentinel), raw);
|
|
70
|
+
}
|
|
71
|
+
return serialized;
|
|
72
|
+
}
|
|
73
|
+
return JSON.stringify((0, _value.default)(element), replacer, space);
|
|
74
|
+
};
|
|
11
75
|
var _default = exports.default = serializer;
|
|
@@ -1,6 +1,70 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isElement, isObjectElement, isArrayElement, isRefElement, isLinkElement, isStringElement, isNumberElement } from '@speclynx/apidom-datamodel';
|
|
2
|
+
import toValue from "./value.mjs";
|
|
3
|
+
const getStyle = element => {
|
|
4
|
+
return element.style?.json ?? {};
|
|
5
|
+
};
|
|
6
|
+
|
|
2
7
|
/**
|
|
3
8
|
* @public
|
|
4
9
|
*/
|
|
5
|
-
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Builds a POJO from an ApiDOM element tree. Numbers with rawContent
|
|
13
|
+
* are replaced with sentinel strings; all other values go through toValue().
|
|
14
|
+
*/
|
|
15
|
+
const toPojo = (element, sentinels) => {
|
|
16
|
+
const visited = new WeakSet();
|
|
17
|
+
const convert = node => {
|
|
18
|
+
if (!isElement(node)) return node;
|
|
19
|
+
if (visited.has(node)) return null;
|
|
20
|
+
visited.add(node);
|
|
21
|
+
if (isObjectElement(node)) {
|
|
22
|
+
const obj = {};
|
|
23
|
+
node.forEach((value, key) => {
|
|
24
|
+
const k = isElement(key) ? toValue(key) : key;
|
|
25
|
+
if (typeof k === 'string') obj[k] = convert(value);
|
|
26
|
+
});
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
if (isArrayElement(node)) {
|
|
30
|
+
const arr = [];
|
|
31
|
+
node.forEach(item => arr.push(convert(item)));
|
|
32
|
+
return arr;
|
|
33
|
+
}
|
|
34
|
+
if (isRefElement(node)) return String(toValue(node));
|
|
35
|
+
if (isLinkElement(node)) return isStringElement(node.href) ? toValue(node.href) : '';
|
|
36
|
+
|
|
37
|
+
// number with rawContent — substitute with sentinel
|
|
38
|
+
if (isNumberElement(node)) {
|
|
39
|
+
const style = getStyle(node);
|
|
40
|
+
if (typeof style.rawContent === 'string') {
|
|
41
|
+
const sentinel = `\0RAW${sentinels.size}\0`;
|
|
42
|
+
sentinels.set(sentinel, style.rawContent);
|
|
43
|
+
return sentinel;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return toValue(node);
|
|
47
|
+
};
|
|
48
|
+
return convert(element);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* @public
|
|
53
|
+
*/
|
|
54
|
+
const serializer = (element, replacer, space, options) => {
|
|
55
|
+
if (options?.preserveStyle) {
|
|
56
|
+
const style = getStyle(element);
|
|
57
|
+
const indent = typeof space === 'number' ? space : typeof style.indent === 'number' ? style.indent : 0;
|
|
58
|
+
const sentinels = new Map();
|
|
59
|
+
const pojo = toPojo(element, sentinels);
|
|
60
|
+
let serialized = JSON.stringify(pojo, null, indent);
|
|
61
|
+
|
|
62
|
+
// replace quoted sentinels with raw number representations
|
|
63
|
+
for (const [sentinel, raw] of sentinels) {
|
|
64
|
+
serialized = serialized.replace(JSON.stringify(sentinel), raw);
|
|
65
|
+
}
|
|
66
|
+
return serialized;
|
|
67
|
+
}
|
|
68
|
+
return JSON.stringify(toValue(element), replacer, space);
|
|
69
|
+
};
|
|
6
70
|
export default serializer;
|
|
@@ -4,16 +4,111 @@ var _interopRequireDefault = require("@babel/runtime-corejs3/helpers/interopRequ
|
|
|
4
4
|
exports.__esModule = true;
|
|
5
5
|
exports.default = void 0;
|
|
6
6
|
var _yaml = require("yaml");
|
|
7
|
+
var _apidomDatamodel = require("@speclynx/apidom-datamodel");
|
|
7
8
|
var _value = _interopRequireDefault(require("./value.cjs"));
|
|
9
|
+
const getStyle = element => {
|
|
10
|
+
return element.style?.yaml ?? {};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// map our scalarStyle strings to YAML library Scalar.Type constants
|
|
14
|
+
const scalarStyleMap = {
|
|
15
|
+
Plain: _yaml.Scalar.PLAIN,
|
|
16
|
+
SingleQuoted: _yaml.Scalar.QUOTE_SINGLE,
|
|
17
|
+
DoubleQuoted: _yaml.Scalar.QUOTE_DOUBLE,
|
|
18
|
+
Literal: _yaml.Scalar.BLOCK_LITERAL,
|
|
19
|
+
Folded: _yaml.Scalar.BLOCK_FOLDED
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// the YAML library prefixes comments with '#' only (no space), so we add ' ' to get '# comment'
|
|
23
|
+
const applyComments = (node, style) => {
|
|
24
|
+
if (style.comment) node.comment = ` ${style.comment}`;
|
|
25
|
+
if (style.commentBefore) node.commentBefore = ` ${style.commentBefore}`;
|
|
26
|
+
};
|
|
27
|
+
|
|
8
28
|
/**
|
|
9
29
|
* @public
|
|
10
30
|
*/
|
|
11
31
|
|
|
32
|
+
/**
|
|
33
|
+
* Converts an ApiDOM element tree to YAML library AST nodes,
|
|
34
|
+
* preserving style information from `element.style.yaml`.
|
|
35
|
+
*/
|
|
36
|
+
const toYAMLNode = (element, visited) => {
|
|
37
|
+
if (!(0, _apidomDatamodel.isElement)(element)) return element;
|
|
38
|
+
|
|
39
|
+
// cycle detection
|
|
40
|
+
if (visited.has(element)) return undefined;
|
|
41
|
+
visited.add(element);
|
|
42
|
+
const style = getStyle(element);
|
|
43
|
+
if ((0, _apidomDatamodel.isObjectElement)(element)) {
|
|
44
|
+
const map = new _yaml.YAMLMap();
|
|
45
|
+
map.flow = style.styleGroup === 'Flow';
|
|
46
|
+
applyComments(map, style);
|
|
47
|
+
element.forEach((value, key, member) => {
|
|
48
|
+
const memberStyle = (0, _apidomDatamodel.isMemberElement)(member) ? getStyle(member) : {};
|
|
49
|
+
const keyNode = toYAMLNode(key, visited);
|
|
50
|
+
const valueNode = toYAMLNode(value, visited);
|
|
51
|
+
const pair = new _yaml.Pair(keyNode, valueNode);
|
|
52
|
+
if (memberStyle.commentBefore && (0, _yaml.isNode)(keyNode)) {
|
|
53
|
+
keyNode.commentBefore = ` ${memberStyle.commentBefore}`;
|
|
54
|
+
}
|
|
55
|
+
if (memberStyle.comment && (0, _yaml.isNode)(valueNode)) {
|
|
56
|
+
valueNode.comment = ` ${memberStyle.comment}`;
|
|
57
|
+
}
|
|
58
|
+
map.items.push(pair);
|
|
59
|
+
});
|
|
60
|
+
return map;
|
|
61
|
+
}
|
|
62
|
+
if ((0, _apidomDatamodel.isArrayElement)(element)) {
|
|
63
|
+
const seq = new _yaml.YAMLSeq();
|
|
64
|
+
seq.flow = style.styleGroup === 'Flow';
|
|
65
|
+
applyComments(seq, style);
|
|
66
|
+
element.forEach(item => {
|
|
67
|
+
seq.items.push(toYAMLNode(item, visited));
|
|
68
|
+
});
|
|
69
|
+
return seq;
|
|
70
|
+
}
|
|
71
|
+
if ((0, _apidomDatamodel.isRefElement)(element)) {
|
|
72
|
+
return new _yaml.Scalar(String((0, _value.default)(element)));
|
|
73
|
+
}
|
|
74
|
+
if ((0, _apidomDatamodel.isLinkElement)(element)) {
|
|
75
|
+
return new _yaml.Scalar((0, _apidomDatamodel.isStringElement)(element.href) ? (0, _value.default)(element.href) : '');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// scalar element (string, number, boolean, null)
|
|
79
|
+
const scalarType = style.scalarStyle ? scalarStyleMap[style.scalarStyle] : undefined;
|
|
80
|
+
const scalar = new _yaml.Scalar((0, _value.default)(element));
|
|
81
|
+
if (scalarType) {
|
|
82
|
+
scalar.type = scalarType;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// use rawContent to infer YAML library format hints for plain scalars that resolved to numbers;
|
|
86
|
+
// only applies to Plain style — quoted scalars are always strings in YAML.
|
|
87
|
+
// note: the YAML library may normalize the representation (e.g. 1.0e10 -> 1e+10, 0x1A -> 0x1a)
|
|
88
|
+
// while preserving the format category (exponential, hex, octal, fractional digits)
|
|
89
|
+
if (style.rawContent && style.scalarStyle === 'Plain' && typeof scalar.value === 'number') {
|
|
90
|
+
if (/[eE]/.test(style.rawContent)) {
|
|
91
|
+
scalar.format = 'EXP';
|
|
92
|
+
} else if (/^0x/i.test(style.rawContent)) {
|
|
93
|
+
scalar.format = 'HEX';
|
|
94
|
+
} else if (/^0o/i.test(style.rawContent)) {
|
|
95
|
+
scalar.format = 'OCT';
|
|
96
|
+
}
|
|
97
|
+
const dotMatch = style.rawContent.match(/\.(\d+)/);
|
|
98
|
+
if (dotMatch) {
|
|
99
|
+
scalar.minFractionDigits = dotMatch[1].length;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
applyComments(scalar, style);
|
|
103
|
+
return scalar;
|
|
104
|
+
};
|
|
105
|
+
|
|
12
106
|
/**
|
|
13
107
|
* @public
|
|
14
108
|
*/
|
|
15
109
|
const serializer = (element, {
|
|
16
110
|
directive = false,
|
|
111
|
+
preserveStyle = false,
|
|
17
112
|
aliasDuplicateObjects = false,
|
|
18
113
|
...options
|
|
19
114
|
} = {}) => {
|
|
@@ -21,6 +116,22 @@ const serializer = (element, {
|
|
|
21
116
|
aliasDuplicateObjects,
|
|
22
117
|
...options
|
|
23
118
|
};
|
|
119
|
+
if (preserveStyle) {
|
|
120
|
+
const style = getStyle(element);
|
|
121
|
+
if (options.indent === undefined && typeof style.indent === 'number') {
|
|
122
|
+
allOptions.indent = style.indent;
|
|
123
|
+
}
|
|
124
|
+
if (typeof style.flowCollectionPadding === 'boolean') {
|
|
125
|
+
allOptions.flowCollectionPadding = style.flowCollectionPadding;
|
|
126
|
+
}
|
|
127
|
+
const rootNode = toYAMLNode(element, new WeakSet());
|
|
128
|
+
const doc = new _yaml.Document(undefined, allOptions);
|
|
129
|
+
doc.contents = rootNode;
|
|
130
|
+
if (directive) {
|
|
131
|
+
doc.directives.yaml.explicit = true;
|
|
132
|
+
}
|
|
133
|
+
return doc.toString(allOptions);
|
|
134
|
+
}
|
|
24
135
|
if (directive) {
|
|
25
136
|
const doc = new _yaml.Document((0, _value.default)(element), allOptions);
|
|
26
137
|
doc.directives.yaml.explicit = true;
|
|
@@ -1,13 +1,109 @@
|
|
|
1
|
-
import { Document, stringify } from 'yaml';
|
|
1
|
+
import { Document, stringify, isNode, Scalar, YAMLMap, YAMLSeq, Pair } from 'yaml';
|
|
2
|
+
import { isElement, isObjectElement, isArrayElement, isRefElement, isLinkElement, isStringElement, isMemberElement } from '@speclynx/apidom-datamodel';
|
|
2
3
|
import toValue from "./value.mjs";
|
|
4
|
+
const getStyle = element => {
|
|
5
|
+
return element.style?.yaml ?? {};
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
// map our scalarStyle strings to YAML library Scalar.Type constants
|
|
9
|
+
const scalarStyleMap = {
|
|
10
|
+
Plain: Scalar.PLAIN,
|
|
11
|
+
SingleQuoted: Scalar.QUOTE_SINGLE,
|
|
12
|
+
DoubleQuoted: Scalar.QUOTE_DOUBLE,
|
|
13
|
+
Literal: Scalar.BLOCK_LITERAL,
|
|
14
|
+
Folded: Scalar.BLOCK_FOLDED
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
// the YAML library prefixes comments with '#' only (no space), so we add ' ' to get '# comment'
|
|
18
|
+
const applyComments = (node, style) => {
|
|
19
|
+
if (style.comment) node.comment = ` ${style.comment}`;
|
|
20
|
+
if (style.commentBefore) node.commentBefore = ` ${style.commentBefore}`;
|
|
21
|
+
};
|
|
22
|
+
|
|
3
23
|
/**
|
|
4
24
|
* @public
|
|
5
25
|
*/
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Converts an ApiDOM element tree to YAML library AST nodes,
|
|
29
|
+
* preserving style information from `element.style.yaml`.
|
|
30
|
+
*/
|
|
31
|
+
const toYAMLNode = (element, visited) => {
|
|
32
|
+
if (!isElement(element)) return element;
|
|
33
|
+
|
|
34
|
+
// cycle detection
|
|
35
|
+
if (visited.has(element)) return undefined;
|
|
36
|
+
visited.add(element);
|
|
37
|
+
const style = getStyle(element);
|
|
38
|
+
if (isObjectElement(element)) {
|
|
39
|
+
const map = new YAMLMap();
|
|
40
|
+
map.flow = style.styleGroup === 'Flow';
|
|
41
|
+
applyComments(map, style);
|
|
42
|
+
element.forEach((value, key, member) => {
|
|
43
|
+
const memberStyle = isMemberElement(member) ? getStyle(member) : {};
|
|
44
|
+
const keyNode = toYAMLNode(key, visited);
|
|
45
|
+
const valueNode = toYAMLNode(value, visited);
|
|
46
|
+
const pair = new Pair(keyNode, valueNode);
|
|
47
|
+
if (memberStyle.commentBefore && isNode(keyNode)) {
|
|
48
|
+
keyNode.commentBefore = ` ${memberStyle.commentBefore}`;
|
|
49
|
+
}
|
|
50
|
+
if (memberStyle.comment && isNode(valueNode)) {
|
|
51
|
+
valueNode.comment = ` ${memberStyle.comment}`;
|
|
52
|
+
}
|
|
53
|
+
map.items.push(pair);
|
|
54
|
+
});
|
|
55
|
+
return map;
|
|
56
|
+
}
|
|
57
|
+
if (isArrayElement(element)) {
|
|
58
|
+
const seq = new YAMLSeq();
|
|
59
|
+
seq.flow = style.styleGroup === 'Flow';
|
|
60
|
+
applyComments(seq, style);
|
|
61
|
+
element.forEach(item => {
|
|
62
|
+
seq.items.push(toYAMLNode(item, visited));
|
|
63
|
+
});
|
|
64
|
+
return seq;
|
|
65
|
+
}
|
|
66
|
+
if (isRefElement(element)) {
|
|
67
|
+
return new Scalar(String(toValue(element)));
|
|
68
|
+
}
|
|
69
|
+
if (isLinkElement(element)) {
|
|
70
|
+
return new Scalar(isStringElement(element.href) ? toValue(element.href) : '');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// scalar element (string, number, boolean, null)
|
|
74
|
+
const scalarType = style.scalarStyle ? scalarStyleMap[style.scalarStyle] : undefined;
|
|
75
|
+
const scalar = new Scalar(toValue(element));
|
|
76
|
+
if (scalarType) {
|
|
77
|
+
scalar.type = scalarType;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// use rawContent to infer YAML library format hints for plain scalars that resolved to numbers;
|
|
81
|
+
// only applies to Plain style — quoted scalars are always strings in YAML.
|
|
82
|
+
// note: the YAML library may normalize the representation (e.g. 1.0e10 -> 1e+10, 0x1A -> 0x1a)
|
|
83
|
+
// while preserving the format category (exponential, hex, octal, fractional digits)
|
|
84
|
+
if (style.rawContent && style.scalarStyle === 'Plain' && typeof scalar.value === 'number') {
|
|
85
|
+
if (/[eE]/.test(style.rawContent)) {
|
|
86
|
+
scalar.format = 'EXP';
|
|
87
|
+
} else if (/^0x/i.test(style.rawContent)) {
|
|
88
|
+
scalar.format = 'HEX';
|
|
89
|
+
} else if (/^0o/i.test(style.rawContent)) {
|
|
90
|
+
scalar.format = 'OCT';
|
|
91
|
+
}
|
|
92
|
+
const dotMatch = style.rawContent.match(/\.(\d+)/);
|
|
93
|
+
if (dotMatch) {
|
|
94
|
+
scalar.minFractionDigits = dotMatch[1].length;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
applyComments(scalar, style);
|
|
98
|
+
return scalar;
|
|
99
|
+
};
|
|
100
|
+
|
|
6
101
|
/**
|
|
7
102
|
* @public
|
|
8
103
|
*/
|
|
9
104
|
const serializer = (element, {
|
|
10
105
|
directive = false,
|
|
106
|
+
preserveStyle = false,
|
|
11
107
|
aliasDuplicateObjects = false,
|
|
12
108
|
...options
|
|
13
109
|
} = {}) => {
|
|
@@ -15,6 +111,22 @@ const serializer = (element, {
|
|
|
15
111
|
aliasDuplicateObjects,
|
|
16
112
|
...options
|
|
17
113
|
};
|
|
114
|
+
if (preserveStyle) {
|
|
115
|
+
const style = getStyle(element);
|
|
116
|
+
if (options.indent === undefined && typeof style.indent === 'number') {
|
|
117
|
+
allOptions.indent = style.indent;
|
|
118
|
+
}
|
|
119
|
+
if (typeof style.flowCollectionPadding === 'boolean') {
|
|
120
|
+
allOptions.flowCollectionPadding = style.flowCollectionPadding;
|
|
121
|
+
}
|
|
122
|
+
const rootNode = toYAMLNode(element, new WeakSet());
|
|
123
|
+
const doc = new Document(undefined, allOptions);
|
|
124
|
+
doc.contents = rootNode;
|
|
125
|
+
if (directive) {
|
|
126
|
+
doc.directives.yaml.explicit = true;
|
|
127
|
+
}
|
|
128
|
+
return doc.toString(allOptions);
|
|
129
|
+
}
|
|
18
130
|
if (directive) {
|
|
19
131
|
const doc = new Document(toValue(element), allOptions);
|
|
20
132
|
doc.directives.yaml.explicit = true;
|
package/types/apidom-core.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ import { CreateNodeOptions } from 'yaml';
|
|
|
5
5
|
import { DocumentOptions } from 'yaml';
|
|
6
6
|
import { Element as Element_2 } from '@speclynx/apidom-datamodel';
|
|
7
7
|
import { hasElementSourceMap } from '@speclynx/apidom-datamodel';
|
|
8
|
+
import { hasElementStyle } from '@speclynx/apidom-datamodel';
|
|
8
9
|
import { includesClasses } from '@speclynx/apidom-datamodel';
|
|
9
10
|
import { includesSymbols } from '@speclynx/apidom-datamodel';
|
|
10
11
|
import { isAnnotationElement } from '@speclynx/apidom-datamodel';
|
|
@@ -146,10 +147,6 @@ export declare interface DispatchPluginsSync {
|
|
|
146
147
|
*/
|
|
147
148
|
export declare const dispatchRefractorPlugins: DispatchPluginsSync;
|
|
148
149
|
|
|
149
|
-
declare type ElementClass = typeof Element_2 & {
|
|
150
|
-
fixedFields?: FixedField[];
|
|
151
|
-
};
|
|
152
|
-
|
|
153
150
|
/**
|
|
154
151
|
* @public
|
|
155
152
|
*/
|
|
@@ -197,10 +194,17 @@ export declare interface FixedField {
|
|
|
197
194
|
*
|
|
198
195
|
* @public
|
|
199
196
|
*/
|
|
200
|
-
export declare function fixedFields<T extends boolean = false>(elementOrClass: Element_2 |
|
|
197
|
+
export declare function fixedFields<T extends boolean = false>(elementOrClass: Element_2 | FixedFieldsElementClass, options?: {
|
|
201
198
|
indexed?: T;
|
|
202
199
|
}): T extends true ? Record<string, FixedField> : FixedField[];
|
|
203
200
|
|
|
201
|
+
/**
|
|
202
|
+
* @public
|
|
203
|
+
*/
|
|
204
|
+
export declare type FixedFieldsElementClass = typeof Element_2 & {
|
|
205
|
+
fixedFields?: FixedField[];
|
|
206
|
+
};
|
|
207
|
+
|
|
204
208
|
/**
|
|
205
209
|
* Transforms data to an Element from a particular namespace.
|
|
206
210
|
*
|
|
@@ -227,6 +231,14 @@ export declare class IdentityManager {
|
|
|
227
231
|
generateId(): string;
|
|
228
232
|
}
|
|
229
233
|
|
|
234
|
+
/**
|
|
235
|
+
* @public
|
|
236
|
+
*/
|
|
237
|
+
export declare interface JSONSerializerOptions {
|
|
238
|
+
/** Preserve original formatting styles from `element.style.json` */
|
|
239
|
+
preserveStyle?: boolean;
|
|
240
|
+
}
|
|
241
|
+
|
|
230
242
|
/**
|
|
231
243
|
* @public
|
|
232
244
|
*/
|
|
@@ -281,6 +293,7 @@ export declare interface Predicates {
|
|
|
281
293
|
isCommentElement: typeof isCommentElement;
|
|
282
294
|
isParseResultElement: typeof isParseResultElement;
|
|
283
295
|
isSourceMapElement: typeof isSourceMapElement;
|
|
296
|
+
hasElementStyle: typeof hasElementStyle;
|
|
284
297
|
hasElementSourceMap: typeof hasElementSourceMap;
|
|
285
298
|
includesSymbols: typeof includesSymbols;
|
|
286
299
|
includesClasses: typeof includesClasses;
|
|
@@ -342,7 +355,7 @@ export declare const sexprs: (element: Element_2) => string;
|
|
|
342
355
|
/**
|
|
343
356
|
* @public
|
|
344
357
|
*/
|
|
345
|
-
export declare const toJSON: (element: Element_2, replacer?: (this:
|
|
358
|
+
export declare const toJSON: (element: Element_2, replacer?: (this: unknown, key: string, value: unknown) => unknown, space?: string | number, options?: JSONSerializerOptions) => string;
|
|
346
359
|
|
|
347
360
|
/**
|
|
348
361
|
* @public
|
|
@@ -370,7 +383,7 @@ export declare const toValue: <T extends Element_2 | unknown>(element: T) => unk
|
|
|
370
383
|
/**
|
|
371
384
|
* @public
|
|
372
385
|
*/
|
|
373
|
-
export declare const toYAML: (element: Element_2, { directive, aliasDuplicateObjects, ...options }?:
|
|
386
|
+
export declare const toYAML: (element: Element_2, { directive, preserveStyle, aliasDuplicateObjects, ...options }?: YAMLSerializerOptions) => string;
|
|
374
387
|
|
|
375
388
|
/**
|
|
376
389
|
* This is a mutating function. If you don't want your Element to be mutated,
|
|
@@ -396,9 +409,13 @@ export declare class Transcluder {
|
|
|
396
409
|
/**
|
|
397
410
|
* @public
|
|
398
411
|
*/
|
|
399
|
-
export declare interface
|
|
412
|
+
export declare interface YAMLSerializerOptions extends DocumentOptions, Pick<CreateNodeOptions, 'aliasDuplicateObjects'>, Pick<SchemaOptions, 'sortMapEntries'>, ToStringOptions {
|
|
400
413
|
/** Include %YAML directive and document marker */
|
|
401
414
|
directive?: boolean;
|
|
415
|
+
/** Preserve original formatting styles from `element.style.yaml` */
|
|
416
|
+
preserveStyle?: boolean;
|
|
417
|
+
/** Padding inside flow collections (e.g. `{ a: 1 }` vs `{a: 1}`) */
|
|
418
|
+
flowCollectionPadding?: boolean;
|
|
402
419
|
}
|
|
403
420
|
|
|
404
421
|
export { }
|