@speclynx/apidom-parser-adapter-yaml-1-2 6.0.6 → 6.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 +41 -0
- package/dist/apidom-parser-adapter-yaml-1-2.browser.js +399 -1166
- package/dist/apidom-parser-adapter-yaml-1-2.browser.min.js +1 -1
- package/package.json +5 -6
- package/src/tree-sitter/lexical-analysis/index.cjs +1 -1
- package/src/tree-sitter/lexical-analysis/index.mjs +1 -1
- package/src/tree-sitter/syntactic-analysis/CstTransformer.cjs +113 -9
- package/src/tree-sitter/syntactic-analysis/CstTransformer.mjs +113 -9
- package/src/tree-sitter/syntactic-analysis/YamlAstTransformer.cjs +99 -18
- package/src/tree-sitter/syntactic-analysis/YamlAstTransformer.mjs +99 -18
- package/src/tree-sitter/syntactic-analysis/ast/anchors-aliases/ReferenceManager.cjs +17 -1
- package/src/tree-sitter/syntactic-analysis/ast/anchors-aliases/ReferenceManager.mjs +17 -1
- package/src/tree-sitter/syntactic-analysis/ast/nodes/YamlScalar.cjs +3 -0
- package/src/tree-sitter/syntactic-analysis/ast/nodes/YamlScalar.mjs +3 -0
- package/src/tree-sitter/syntactic-analysis/ast/nodes/YamlStream.cjs +3 -0
- package/src/tree-sitter/syntactic-analysis/ast/nodes/YamlStream.mjs +3 -0
- package/src/tree-sitter/syntactic-analysis/ast/schemas/canonical-format.cjs +144 -51
- package/src/tree-sitter/syntactic-analysis/ast/schemas/canonical-format.mjs +146 -53
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
import { tail,
|
|
2
|
-
import { isInteger,
|
|
3
|
-
import { unraw } from 'unraw';
|
|
1
|
+
import { tail, pathOr, pipe, trim, curry, replace } from 'ramda';
|
|
2
|
+
import { isInteger, isEmptyString, repeatStr } from 'ramda-adjunct';
|
|
4
3
|
|
|
5
4
|
/**
|
|
6
5
|
* Helpers.
|
|
7
6
|
*/
|
|
8
7
|
|
|
9
|
-
|
|
8
|
+
// indentation and chomping indicators may appear in either order (YAML 1.2 §8.1.1)
|
|
9
|
+
const blockStyleRegExp = /^(?<style>[|>])(?<indicators>[+-]?[0-9]?|[0-9][+-])(?:\s|$)/;
|
|
10
10
|
const getIndentationIndicator = content => {
|
|
11
11
|
const matches = content.match(blockStyleRegExp);
|
|
12
|
-
const
|
|
12
|
+
const indicators = pathOr('', ['groups', 'indicators'], matches);
|
|
13
|
+
const indicator = indicators.replace(/[+-]/, '');
|
|
13
14
|
return isEmptyString(indicator) ? undefined : parseInt(indicator, 10);
|
|
14
15
|
};
|
|
15
16
|
const getIndentation = content => {
|
|
@@ -20,30 +21,72 @@ const getIndentation = content => {
|
|
|
20
21
|
return repeatStr(' ', explicitIndentationIndicator);
|
|
21
22
|
}
|
|
22
23
|
|
|
23
|
-
// we assume indentation indicator from first line
|
|
24
|
-
|
|
24
|
+
// we assume indentation indicator from first non-empty line (YAML 1.2 §8.1.1.1);
|
|
25
|
+
// with no non-empty line, the first line is used so that its spaces are still stripped
|
|
26
|
+
const lines = tail(content.split('\n'));
|
|
27
|
+
const firstLine = lines.find(line => !/^ *\r?$/.test(line)) ?? pathOr('', [0], lines);
|
|
25
28
|
const implicitIndentationIndicator = pathOr(0, ['groups', 'indentation', 'length'], firstLine.match(/^(?<indentation>[ ]*)/));
|
|
26
29
|
return repeatStr(' ', implicitIndentationIndicator);
|
|
27
30
|
};
|
|
31
|
+
|
|
32
|
+
// splits a block scalar body into deindented content lines and trailing lines (YAML 1.2 §8.1.1.2):
|
|
33
|
+
// trailing lines are the whitespace-only lines after the last content line, minus any up to and
|
|
34
|
+
// including the last one indented deeper than the block, which are content; without any content
|
|
35
|
+
// line, all lines are trailing
|
|
36
|
+
const splitBlockScalar = (content, chomping) => {
|
|
37
|
+
const indentation = getIndentation(content).length;
|
|
38
|
+
const normalized = normalizeLineBreaks(content);
|
|
39
|
+
const body = normalized.includes('\n') ? normalized.slice(normalized.indexOf('\n') + 1) : '';
|
|
40
|
+
const lines = body === '' ? [] : body.split('\n');
|
|
41
|
+
const deindent = line => line.slice(Math.min(indentation, line.search(/[^ ]|$/)));
|
|
42
|
+
|
|
43
|
+
// without keep chomping, trailing whitespace-only lines not indented deeper than the first
|
|
44
|
+
// content line don't belong to the block scalar
|
|
45
|
+
const firstContent = lines.find(line => /[^ ]/.test(line));
|
|
46
|
+
if (chomping !== '+' && firstContent !== undefined) {
|
|
47
|
+
const contentIndentation = firstContent.search(/[^ ]/);
|
|
48
|
+
while (/^ *$/.test(lines.at(-1)) && lines.at(-1).length <= contentIndentation) lines.pop();
|
|
49
|
+
}
|
|
50
|
+
let chompStart = lines.length;
|
|
51
|
+
while (chompStart > 0 && /^ *$/.test(lines[chompStart - 1])) chompStart -= 1;
|
|
52
|
+
if (chompStart === 0) return {
|
|
53
|
+
lines: [],
|
|
54
|
+
trailing: lines.map(deindent)
|
|
55
|
+
};
|
|
56
|
+
for (let i = lines.length - 1; i >= chompStart; i -= 1) {
|
|
57
|
+
if (lines[i].length > indentation) {
|
|
58
|
+
chompStart = i + 1;
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return {
|
|
63
|
+
lines: lines.slice(0, chompStart).map(deindent),
|
|
64
|
+
trailing: lines.slice(chompStart).map(deindent)
|
|
65
|
+
};
|
|
66
|
+
};
|
|
28
67
|
const getChompingIndicator = content => {
|
|
29
68
|
const matches = content.match(blockStyleRegExp);
|
|
30
|
-
const
|
|
69
|
+
const indicators = pathOr('', ['groups', 'indicators'], matches);
|
|
70
|
+
const indicator = indicators.replace(/[0-9]/, '');
|
|
31
71
|
return isEmptyString(indicator) ? undefined : indicator;
|
|
32
72
|
};
|
|
33
|
-
const chomp = (indicator,
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
73
|
+
const chomp = (indicator, value, {
|
|
74
|
+
lines,
|
|
75
|
+
trailing
|
|
76
|
+
}) => {
|
|
77
|
+
// a block without content is empty, unless keep chomping preserves its line breaks
|
|
78
|
+
if (lines.length === 0) {
|
|
79
|
+
return indicator === '+' && trailing.length > 0 ? '\n'.repeat(Math.max(1, trailing.length - 1)) : '';
|
|
37
80
|
}
|
|
38
81
|
// strip (no newline at end)
|
|
39
|
-
if (indicator === '-')
|
|
40
|
-
|
|
41
|
-
}
|
|
42
|
-
// keep (all newlines from end)
|
|
82
|
+
if (indicator === '-') return value;
|
|
83
|
+
// keep (trailing lines and a newline at end)
|
|
43
84
|
if (indicator === '+') {
|
|
44
|
-
|
|
85
|
+
const kept = value + trailing.map(line => `\n${line}`).join('');
|
|
86
|
+
return kept.endsWith('\n') ? kept : `${kept}\n`;
|
|
45
87
|
}
|
|
46
|
-
|
|
88
|
+
// clip (single newline at end)
|
|
89
|
+
return `${value}\n`;
|
|
47
90
|
};
|
|
48
91
|
|
|
49
92
|
/**
|
|
@@ -53,56 +96,108 @@ const chomp = (indicator, content) => {
|
|
|
53
96
|
// @ts-ignore
|
|
54
97
|
const normalizeLineBreaks = val => val.replace(/\r\n/g, '\n');
|
|
55
98
|
|
|
56
|
-
//
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
//
|
|
60
|
-
const
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
99
|
+
// folds the line breaks of a flow scalar (YAML 1.2 §6.5, §7.3):
|
|
100
|
+
// white space around a line break is dropped, a single line break becomes a space and every
|
|
101
|
+
// further one a line break; with escapes (double-quoted style), escape sequences are left for
|
|
102
|
+
// unescaping and an escaped line break is dropped together with the next line's indentation
|
|
103
|
+
const foldFlowLines = curry((escapes, text) => text.replace(
|
|
104
|
+
// a white space run not followed by a line break is consumed whole, which keeps the search
|
|
105
|
+
// linear instead of rescanning the run from every position in it
|
|
106
|
+
escapes ? /\\(?:\n[ \t]*|[^])|[ \t]*\n(?:[ \t]*\n)*[ \t]*|[ \t]+/g : /[ \t]*\n(?:[ \t]*\n)*[ \t]*|[ \t]+/g, match => {
|
|
107
|
+
if (match.startsWith('\\')) return match[1] === '\n' ? '' : match;
|
|
108
|
+
if (!match.includes('\n')) return match;
|
|
109
|
+
const lineBreaks = match.split('\n').length - 1;
|
|
110
|
+
return lineBreaks === 1 ? ' ' : '\n'.repeat(lineBreaks - 1);
|
|
111
|
+
}));
|
|
112
|
+
|
|
113
|
+
// folds the line break between two lines into a space; empty lines between them stay as line
|
|
114
|
+
// breaks, and a line break next to a more-indented line isn't folded (YAML 1.2 §6.5)
|
|
115
|
+
const foldLines = text => {
|
|
116
|
+
const parts = text.split(/\n(\n*)/); // lines alternating with the empty lines between them
|
|
117
|
+
let folded = parts[0];
|
|
118
|
+
for (let i = 1; i < parts.length; i += 2) {
|
|
119
|
+
const moreIndented = /^[ \t]/.test(parts[i - 1]) || /^[ \t]/.test(parts[i + 1]);
|
|
120
|
+
folded += (moreIndented ? `\n${parts[i]}` : parts[i] || ' ') + parts[i + 1];
|
|
121
|
+
}
|
|
122
|
+
return folded;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// escaped characters of YAML 1.2 §5.7, productions [42] to [58]
|
|
126
|
+
const escapedCharacters = {
|
|
127
|
+
'0': '\u0000',
|
|
128
|
+
// null
|
|
129
|
+
a: '\u0007',
|
|
130
|
+
// bell
|
|
131
|
+
b: '\u0008',
|
|
132
|
+
// backspace
|
|
133
|
+
t: '\u0009',
|
|
134
|
+
// horizontal tab
|
|
135
|
+
'\t': '\u0009',
|
|
136
|
+
// horizontal tab
|
|
137
|
+
n: '\u000a',
|
|
138
|
+
// line feed
|
|
139
|
+
v: '\u000b',
|
|
140
|
+
// vertical tab
|
|
141
|
+
f: '\u000c',
|
|
142
|
+
// form feed
|
|
143
|
+
r: '\u000d',
|
|
144
|
+
// carriage return
|
|
145
|
+
e: '\u001b',
|
|
146
|
+
// escape
|
|
147
|
+
' ': '\u0020',
|
|
148
|
+
// space
|
|
149
|
+
'"': '\u0022',
|
|
150
|
+
// double quote
|
|
151
|
+
'/': '\u002f',
|
|
152
|
+
// slash
|
|
153
|
+
'\\': '\u005c',
|
|
154
|
+
// backslash
|
|
155
|
+
N: '\u0085',
|
|
156
|
+
// next line
|
|
157
|
+
_: '\u00a0',
|
|
158
|
+
// non-breaking space
|
|
159
|
+
L: '\u2028',
|
|
160
|
+
// line separator
|
|
161
|
+
P: '\u2029' // paragraph separator
|
|
71
162
|
};
|
|
163
|
+
|
|
164
|
+
// unescapes the escape sequences of a double-quoted scalar (YAML 1.2 §5.7); an escape sequence
|
|
165
|
+
// outside of the spec is kept as-is
|
|
166
|
+
const unescapeDoubleQuoted = text => text.replace(/\\(?:x([0-9a-fA-F]{2})|u([0-9a-fA-F]{4})|U([0-9a-fA-F]{8})|([^]))/g, (match, x, u, U, char) => {
|
|
167
|
+
const hex = x ?? u ?? U;
|
|
168
|
+
if (hex === undefined) return escapedCharacters[char] ?? match;
|
|
169
|
+
const codePoint = parseInt(hex, 16);
|
|
170
|
+
return codePoint <= 0x10ffff ? String.fromCodePoint(codePoint) : match;
|
|
171
|
+
});
|
|
72
172
|
const removeQuotes = curry((quoteType, val) => val.replace(new RegExp(`^${quoteType}`), '').replace(new RegExp(`${quoteType}$`), ''));
|
|
73
173
|
|
|
74
174
|
/**
|
|
75
175
|
* Formats Flow Scalar Plain style.
|
|
76
176
|
* https://yaml.org/spec/1.2/spec.html#id2788859
|
|
77
177
|
*/
|
|
78
|
-
export const formatFlowPlain = pipe(normalizeLineBreaks, trim,
|
|
178
|
+
export const formatFlowPlain = pipe(normalizeLineBreaks, trim, foldFlowLines(false));
|
|
79
179
|
|
|
80
180
|
/**
|
|
81
181
|
* Formats Flow Scalar Single-Quoted style.
|
|
82
182
|
* https://yaml.org/spec/1.2/spec.html#id2788097
|
|
83
183
|
*/
|
|
84
184
|
|
|
85
|
-
export const formatFlowSingleQuoted = pipe(normalizeLineBreaks, trim,
|
|
185
|
+
export const formatFlowSingleQuoted = pipe(normalizeLineBreaks, trim, foldFlowLines(false), removeQuotes("'"), replace(/''/g, "'"));
|
|
86
186
|
|
|
87
187
|
/**
|
|
88
188
|
* Formats Flow Scalar Double-Quoted style.
|
|
89
189
|
* https://yaml.org/spec/1.2/spec.html#id2787109
|
|
90
190
|
*/
|
|
91
|
-
export const formatFlowDoubleQuoted = pipe(normalizeLineBreaks, trim,
|
|
191
|
+
export const formatFlowDoubleQuoted = pipe(normalizeLineBreaks, trim, foldFlowLines(true), unescapeDoubleQuoted, removeQuotes('"'));
|
|
92
192
|
|
|
93
193
|
/**
|
|
94
194
|
* Formats Block Scalar Literal style.
|
|
95
195
|
* https://yaml.org/spec/1.2/spec.html#id2795688
|
|
96
196
|
*/
|
|
97
197
|
export const formatBlockLiteral = content => {
|
|
98
|
-
const
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
const lines = tail(normalized.split('\n')); // first line only contains indicators
|
|
102
|
-
const transducer = compose(map(trimCharsStart(indentation)), map(concatRight('\n')));
|
|
103
|
-
// @ts-ignore
|
|
104
|
-
const deindented = transduce(transducer, concat, '', lines);
|
|
105
|
-
return chomp(chompingIndicator, deindented);
|
|
198
|
+
const chomping = getChompingIndicator(content);
|
|
199
|
+
const split = splitBlockScalar(content, chomping);
|
|
200
|
+
return chomp(chomping, split.lines.join('\n'), split);
|
|
106
201
|
};
|
|
107
202
|
|
|
108
203
|
/**
|
|
@@ -110,13 +205,11 @@ export const formatBlockLiteral = content => {
|
|
|
110
205
|
* https://yaml.org/spec/1.2/spec.html#id2796251
|
|
111
206
|
*/
|
|
112
207
|
export const formatBlockFolded = content => {
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
const
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
const collapsed = collapseLineBreakToSpace(deindented);
|
|
121
|
-
return chomp(chompingIndicator, collapsed);
|
|
208
|
+
const chomping = getChompingIndicator(content);
|
|
209
|
+
const split = splitBlockScalar(content, chomping);
|
|
210
|
+
// leading line breaks are preserved as-is; folding applies only between content lines
|
|
211
|
+
const text = split.lines.join('\n');
|
|
212
|
+
const leadingLineBreaks = text.match(/^\n*/)[0];
|
|
213
|
+
const folded = leadingLineBreaks + foldLines(text.slice(leadingLineBreaks.length));
|
|
214
|
+
return chomp(chomping, folded, split);
|
|
122
215
|
};
|