yaml 2.4.3 → 2.4.5
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/browser/dist/compose/compose-doc.js +1 -0
- package/browser/dist/compose/compose-scalar.js +1 -1
- package/browser/dist/compose/resolve-block-map.js +2 -0
- package/browser/dist/compose/resolve-block-scalar.js +6 -2
- package/browser/dist/compose/resolve-block-seq.js +1 -0
- package/browser/dist/compose/resolve-flow-collection.js +2 -0
- package/browser/dist/compose/resolve-props.js +21 -7
- package/browser/dist/parse/cst-scalar.js +1 -1
- package/browser/dist/parse/lexer.js +29 -16
- package/browser/dist/parse/parser.js +11 -12
- package/dist/compose/compose-doc.js +1 -0
- package/dist/compose/compose-scalar.js +1 -1
- package/dist/compose/resolve-block-map.js +2 -0
- package/dist/compose/resolve-block-scalar.d.ts +2 -1
- package/dist/compose/resolve-block-scalar.js +6 -2
- package/dist/compose/resolve-block-seq.js +1 -0
- package/dist/compose/resolve-flow-collection.js +2 -0
- package/dist/compose/resolve-props.d.ts +2 -1
- package/dist/compose/resolve-props.js +21 -7
- package/dist/parse/cst-scalar.js +1 -1
- package/dist/parse/cst.d.ts +2 -0
- package/dist/parse/lexer.js +29 -16
- package/dist/parse/parser.js +11 -12
- package/package.json +3 -3
|
@@ -5,7 +5,7 @@ import { resolveFlowScalar } from './resolve-flow-scalar.js';
|
|
|
5
5
|
|
|
6
6
|
function composeScalar(ctx, token, tagToken, onError) {
|
|
7
7
|
const { value, type, comment, range } = token.type === 'block-scalar'
|
|
8
|
-
? resolveBlockScalar(
|
|
8
|
+
? resolveBlockScalar(ctx, token, onError)
|
|
9
9
|
: resolveFlowScalar(token, ctx.options.strict, onError);
|
|
10
10
|
const tagName = tagToken
|
|
11
11
|
? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
|
|
@@ -21,6 +21,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
|
|
|
21
21
|
next: key ?? sep?.[0],
|
|
22
22
|
offset,
|
|
23
23
|
onError,
|
|
24
|
+
parentIndent: bm.indent,
|
|
24
25
|
startOnNewline: true
|
|
25
26
|
});
|
|
26
27
|
const implicitKey = !keyProps.found;
|
|
@@ -63,6 +64,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
|
|
|
63
64
|
next: value,
|
|
64
65
|
offset: keyNode.range[2],
|
|
65
66
|
onError,
|
|
67
|
+
parentIndent: bm.indent,
|
|
66
68
|
startOnNewline: !key || key.type === 'block-scalar'
|
|
67
69
|
});
|
|
68
70
|
offset = valueProps.end;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Scalar } from '../nodes/Scalar.js';
|
|
2
2
|
|
|
3
|
-
function resolveBlockScalar(
|
|
3
|
+
function resolveBlockScalar(ctx, scalar, onError) {
|
|
4
4
|
const start = scalar.offset;
|
|
5
|
-
const header = parseBlockScalarHeader(scalar, strict, onError);
|
|
5
|
+
const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError);
|
|
6
6
|
if (!header)
|
|
7
7
|
return { value: '', type: null, comment: '', range: [start, start, start] };
|
|
8
8
|
const type = header.mode === '>' ? Scalar.BLOCK_FOLDED : Scalar.BLOCK_LITERAL;
|
|
@@ -44,6 +44,10 @@ function resolveBlockScalar(scalar, strict, onError) {
|
|
|
44
44
|
if (header.indent === 0)
|
|
45
45
|
trimIndent = indent.length;
|
|
46
46
|
contentStart = i;
|
|
47
|
+
if (trimIndent === 0 && !ctx.atRoot) {
|
|
48
|
+
const message = 'Block scalar values in collections must be indented';
|
|
49
|
+
onError(offset, 'BAD_INDENT', message);
|
|
50
|
+
}
|
|
47
51
|
break;
|
|
48
52
|
}
|
|
49
53
|
offset += indent.length + content.length + 1;
|
|
@@ -28,6 +28,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
28
28
|
next: key ?? sep?.[0],
|
|
29
29
|
offset,
|
|
30
30
|
onError,
|
|
31
|
+
parentIndent: fc.indent,
|
|
31
32
|
startOnNewline: false
|
|
32
33
|
});
|
|
33
34
|
if (!props.found) {
|
|
@@ -109,6 +110,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
109
110
|
next: value,
|
|
110
111
|
offset: keyNode.range[2],
|
|
111
112
|
onError,
|
|
113
|
+
parentIndent: fc.indent,
|
|
112
114
|
startOnNewline: false
|
|
113
115
|
});
|
|
114
116
|
if (valueProps.found) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
|
|
1
|
+
function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
|
|
2
2
|
let spaceBefore = false;
|
|
3
3
|
let atNewline = startOnNewline;
|
|
4
4
|
let hasSpace = startOnNewline;
|
|
@@ -7,6 +7,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
7
7
|
let hasNewline = false;
|
|
8
8
|
let hasNewlineAfterProp = false;
|
|
9
9
|
let reqSpace = false;
|
|
10
|
+
let tab = null;
|
|
10
11
|
let anchor = null;
|
|
11
12
|
let tag = null;
|
|
12
13
|
let comma = null;
|
|
@@ -20,16 +21,22 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
20
21
|
onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
|
|
21
22
|
reqSpace = false;
|
|
22
23
|
}
|
|
24
|
+
if (tab) {
|
|
25
|
+
if (atNewline && token.type !== 'comment' && token.type !== 'newline') {
|
|
26
|
+
onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
|
|
27
|
+
}
|
|
28
|
+
tab = null;
|
|
29
|
+
}
|
|
23
30
|
switch (token.type) {
|
|
24
31
|
case 'space':
|
|
25
32
|
// At the doc level, tabs at line start may be parsed
|
|
26
33
|
// as leading white space rather than indentation.
|
|
27
34
|
// In a flow collection, only the parser handles indent.
|
|
28
35
|
if (!flow &&
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
36
|
+
(indicator !== 'doc-start' || next?.type !== 'flow-collection') &&
|
|
37
|
+
token.source.includes('\t')) {
|
|
38
|
+
tab = token;
|
|
39
|
+
}
|
|
33
40
|
hasSpace = true;
|
|
34
41
|
break;
|
|
35
42
|
case 'comment': {
|
|
@@ -89,7 +96,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
89
96
|
if (found)
|
|
90
97
|
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
|
|
91
98
|
found = token;
|
|
92
|
-
atNewline =
|
|
99
|
+
atNewline =
|
|
100
|
+
indicator === 'seq-item-ind' || indicator === 'explicit-key-ind';
|
|
93
101
|
hasSpace = false;
|
|
94
102
|
break;
|
|
95
103
|
case 'comma':
|
|
@@ -115,8 +123,14 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
115
123
|
next.type !== 'space' &&
|
|
116
124
|
next.type !== 'newline' &&
|
|
117
125
|
next.type !== 'comma' &&
|
|
118
|
-
(next.type !== 'scalar' || next.source !== ''))
|
|
126
|
+
(next.type !== 'scalar' || next.source !== '')) {
|
|
119
127
|
onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
|
|
128
|
+
}
|
|
129
|
+
if (tab &&
|
|
130
|
+
((atNewline && tab.indent <= parentIndent) ||
|
|
131
|
+
next?.type === 'block-map' ||
|
|
132
|
+
next?.type === 'block-seq'))
|
|
133
|
+
onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
|
|
120
134
|
return {
|
|
121
135
|
comma,
|
|
122
136
|
found,
|
|
@@ -18,7 +18,7 @@ function resolveAsScalar(token, strict = true, onError) {
|
|
|
18
18
|
case 'double-quoted-scalar':
|
|
19
19
|
return resolveFlowScalar(token, strict, _onError);
|
|
20
20
|
case 'block-scalar':
|
|
21
|
-
return resolveBlockScalar(
|
|
21
|
+
return resolveBlockScalar({ options: { strict } }, token, _onError);
|
|
22
22
|
}
|
|
23
23
|
}
|
|
24
24
|
return null;
|
|
@@ -79,11 +79,11 @@ function isEmpty(ch) {
|
|
|
79
79
|
return false;
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
|
-
const hexDigits = '0123456789ABCDEFabcdef'
|
|
83
|
-
const tagChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"
|
|
84
|
-
const
|
|
85
|
-
const invalidAnchorChars = ' ,[]{}\n\r\t'
|
|
86
|
-
const isNotAnchorChar = (ch) => !ch || invalidAnchorChars.
|
|
82
|
+
const hexDigits = new Set('0123456789ABCDEFabcdef');
|
|
83
|
+
const tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()");
|
|
84
|
+
const flowIndicatorChars = new Set(',[]{}');
|
|
85
|
+
const invalidAnchorChars = new Set(' ,[]{}\n\r\t');
|
|
86
|
+
const isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch);
|
|
87
87
|
/**
|
|
88
88
|
* Splits an input string into lexical tokens, i.e. smaller strings that are
|
|
89
89
|
* easily identifiable by `tokens.tokenType()`.
|
|
@@ -525,8 +525,10 @@ class Lexer {
|
|
|
525
525
|
if (indent >= this.indentNext) {
|
|
526
526
|
if (this.blockScalarIndent === -1)
|
|
527
527
|
this.indentNext = indent;
|
|
528
|
-
else
|
|
529
|
-
this.indentNext
|
|
528
|
+
else {
|
|
529
|
+
this.indentNext =
|
|
530
|
+
this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext);
|
|
531
|
+
}
|
|
530
532
|
do {
|
|
531
533
|
const cs = this.continueScalar(nl + 1);
|
|
532
534
|
if (cs === -1)
|
|
@@ -539,14 +541,25 @@ class Lexer {
|
|
|
539
541
|
nl = this.buffer.length;
|
|
540
542
|
}
|
|
541
543
|
}
|
|
542
|
-
|
|
544
|
+
// Trailing insufficiently indented tabs are invalid.
|
|
545
|
+
// To catch that during parsing, we include them in the block scalar value.
|
|
546
|
+
let i = nl + 1;
|
|
547
|
+
ch = this.buffer[i];
|
|
548
|
+
while (ch === ' ')
|
|
549
|
+
ch = this.buffer[++i];
|
|
550
|
+
if (ch === '\t') {
|
|
551
|
+
while (ch === '\t' || ch === ' ' || ch === '\r' || ch === '\n')
|
|
552
|
+
ch = this.buffer[++i];
|
|
553
|
+
nl = i - 1;
|
|
554
|
+
}
|
|
555
|
+
else if (!this.blockScalarKeep) {
|
|
543
556
|
do {
|
|
544
557
|
let i = nl - 1;
|
|
545
558
|
let ch = this.buffer[i];
|
|
546
559
|
if (ch === '\r')
|
|
547
560
|
ch = this.buffer[--i];
|
|
548
561
|
const lastChar = i; // Drop the line if last char not more indented
|
|
549
|
-
while (ch === ' '
|
|
562
|
+
while (ch === ' ')
|
|
550
563
|
ch = this.buffer[--i];
|
|
551
564
|
if (ch === '\n' && i >= this.pos && i + 1 + indent > lastChar)
|
|
552
565
|
nl = i;
|
|
@@ -566,7 +579,7 @@ class Lexer {
|
|
|
566
579
|
while ((ch = this.buffer[++i])) {
|
|
567
580
|
if (ch === ':') {
|
|
568
581
|
const next = this.buffer[i + 1];
|
|
569
|
-
if (isEmpty(next) || (inFlow && next
|
|
582
|
+
if (isEmpty(next) || (inFlow && flowIndicatorChars.has(next)))
|
|
570
583
|
break;
|
|
571
584
|
end = i;
|
|
572
585
|
}
|
|
@@ -581,7 +594,7 @@ class Lexer {
|
|
|
581
594
|
else
|
|
582
595
|
end = i;
|
|
583
596
|
}
|
|
584
|
-
if (next === '#' || (inFlow &&
|
|
597
|
+
if (next === '#' || (inFlow && flowIndicatorChars.has(next)))
|
|
585
598
|
break;
|
|
586
599
|
if (ch === '\n') {
|
|
587
600
|
const cs = this.continueScalar(i + 1);
|
|
@@ -591,7 +604,7 @@ class Lexer {
|
|
|
591
604
|
}
|
|
592
605
|
}
|
|
593
606
|
else {
|
|
594
|
-
if (inFlow &&
|
|
607
|
+
if (inFlow && flowIndicatorChars.has(ch))
|
|
595
608
|
break;
|
|
596
609
|
end = i;
|
|
597
610
|
}
|
|
@@ -636,7 +649,7 @@ class Lexer {
|
|
|
636
649
|
case ':': {
|
|
637
650
|
const inFlow = this.flowLevel > 0;
|
|
638
651
|
const ch1 = this.charAt(1);
|
|
639
|
-
if (isEmpty(ch1) || (inFlow &&
|
|
652
|
+
if (isEmpty(ch1) || (inFlow && flowIndicatorChars.has(ch1))) {
|
|
640
653
|
if (!inFlow)
|
|
641
654
|
this.indentNext = this.indentValue + 1;
|
|
642
655
|
else if (this.flowKey)
|
|
@@ -661,11 +674,11 @@ class Lexer {
|
|
|
661
674
|
let i = this.pos + 1;
|
|
662
675
|
let ch = this.buffer[i];
|
|
663
676
|
while (ch) {
|
|
664
|
-
if (tagChars.
|
|
677
|
+
if (tagChars.has(ch))
|
|
665
678
|
ch = this.buffer[++i];
|
|
666
679
|
else if (ch === '%' &&
|
|
667
|
-
hexDigits.
|
|
668
|
-
hexDigits.
|
|
680
|
+
hexDigits.has(this.buffer[i + 1]) &&
|
|
681
|
+
hexDigits.has(this.buffer[i + 2])) {
|
|
669
682
|
ch = this.buffer[(i += 3)];
|
|
670
683
|
}
|
|
671
684
|
else
|
|
@@ -304,7 +304,7 @@ class Parser {
|
|
|
304
304
|
}
|
|
305
305
|
else {
|
|
306
306
|
Object.assign(it, { key: token, sep: [] });
|
|
307
|
-
this.onKeyLine = !
|
|
307
|
+
this.onKeyLine = !it.explicitKey;
|
|
308
308
|
return;
|
|
309
309
|
}
|
|
310
310
|
break;
|
|
@@ -513,9 +513,9 @@ class Parser {
|
|
|
513
513
|
return;
|
|
514
514
|
}
|
|
515
515
|
if (this.indent >= map.indent) {
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
it.sep &&
|
|
516
|
+
const atMapIndent = !this.onKeyLine && this.indent === map.indent;
|
|
517
|
+
const atNextItem = atMapIndent &&
|
|
518
|
+
(it.sep || it.explicitKey) &&
|
|
519
519
|
this.type !== 'seq-item-ind';
|
|
520
520
|
// For empty nodes, assign newline-separated not indented empty tokens to following node
|
|
521
521
|
let start = [];
|
|
@@ -556,25 +556,26 @@ class Parser {
|
|
|
556
556
|
}
|
|
557
557
|
return;
|
|
558
558
|
case 'explicit-key-ind':
|
|
559
|
-
if (!it.sep && !
|
|
559
|
+
if (!it.sep && !it.explicitKey) {
|
|
560
560
|
it.start.push(this.sourceToken);
|
|
561
|
+
it.explicitKey = true;
|
|
561
562
|
}
|
|
562
563
|
else if (atNextItem || it.value) {
|
|
563
564
|
start.push(this.sourceToken);
|
|
564
|
-
map.items.push({ start });
|
|
565
|
+
map.items.push({ start, explicitKey: true });
|
|
565
566
|
}
|
|
566
567
|
else {
|
|
567
568
|
this.stack.push({
|
|
568
569
|
type: 'block-map',
|
|
569
570
|
offset: this.offset,
|
|
570
571
|
indent: this.indent,
|
|
571
|
-
items: [{ start: [this.sourceToken] }]
|
|
572
|
+
items: [{ start: [this.sourceToken], explicitKey: true }]
|
|
572
573
|
});
|
|
573
574
|
}
|
|
574
575
|
this.onKeyLine = true;
|
|
575
576
|
return;
|
|
576
577
|
case 'map-value-ind':
|
|
577
|
-
if (
|
|
578
|
+
if (it.explicitKey) {
|
|
578
579
|
if (!it.sep) {
|
|
579
580
|
if (includesToken(it.start, 'newline')) {
|
|
580
581
|
Object.assign(it, { key: null, sep: [this.sourceToken] });
|
|
@@ -665,9 +666,7 @@ class Parser {
|
|
|
665
666
|
default: {
|
|
666
667
|
const bv = this.startBlockValue(map);
|
|
667
668
|
if (bv) {
|
|
668
|
-
if (
|
|
669
|
-
bv.type !== 'block-seq' &&
|
|
670
|
-
includesToken(it.start, 'explicit-key-ind')) {
|
|
669
|
+
if (atMapIndent && bv.type !== 'block-seq') {
|
|
671
670
|
map.items.push({ start });
|
|
672
671
|
}
|
|
673
672
|
this.stack.push(bv);
|
|
@@ -888,7 +887,7 @@ class Parser {
|
|
|
888
887
|
type: 'block-map',
|
|
889
888
|
offset: this.offset,
|
|
890
889
|
indent: this.indent,
|
|
891
|
-
items: [{ start }]
|
|
890
|
+
items: [{ start, explicitKey: true }]
|
|
892
891
|
};
|
|
893
892
|
}
|
|
894
893
|
case 'map-value-ind': {
|
|
@@ -7,7 +7,7 @@ var resolveFlowScalar = require('./resolve-flow-scalar.js');
|
|
|
7
7
|
|
|
8
8
|
function composeScalar(ctx, token, tagToken, onError) {
|
|
9
9
|
const { value, type, comment, range } = token.type === 'block-scalar'
|
|
10
|
-
? resolveBlockScalar.resolveBlockScalar(
|
|
10
|
+
? resolveBlockScalar.resolveBlockScalar(ctx, token, onError)
|
|
11
11
|
: resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);
|
|
12
12
|
const tagName = tagToken
|
|
13
13
|
? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
|
|
@@ -23,6 +23,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
|
|
|
23
23
|
next: key ?? sep?.[0],
|
|
24
24
|
offset,
|
|
25
25
|
onError,
|
|
26
|
+
parentIndent: bm.indent,
|
|
26
27
|
startOnNewline: true
|
|
27
28
|
});
|
|
28
29
|
const implicitKey = !keyProps.found;
|
|
@@ -65,6 +66,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
|
|
|
65
66
|
next: value,
|
|
66
67
|
offset: keyNode.range[2],
|
|
67
68
|
onError,
|
|
69
|
+
parentIndent: bm.indent,
|
|
68
70
|
startOnNewline: !key || key.type === 'block-scalar'
|
|
69
71
|
});
|
|
70
72
|
offset = valueProps.end;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { Range } from '../nodes/Node.js';
|
|
2
2
|
import { Scalar } from '../nodes/Scalar.js';
|
|
3
3
|
import type { BlockScalar } from '../parse/cst.js';
|
|
4
|
+
import type { ComposeContext } from './compose-node.js';
|
|
4
5
|
import type { ComposeErrorHandler } from './composer.js';
|
|
5
|
-
export declare function resolveBlockScalar(
|
|
6
|
+
export declare function resolveBlockScalar(ctx: ComposeContext, scalar: BlockScalar, onError: ComposeErrorHandler): {
|
|
6
7
|
value: string;
|
|
7
8
|
type: Scalar.BLOCK_FOLDED | Scalar.BLOCK_LITERAL | null;
|
|
8
9
|
comment: string;
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
var Scalar = require('../nodes/Scalar.js');
|
|
4
4
|
|
|
5
|
-
function resolveBlockScalar(
|
|
5
|
+
function resolveBlockScalar(ctx, scalar, onError) {
|
|
6
6
|
const start = scalar.offset;
|
|
7
|
-
const header = parseBlockScalarHeader(scalar, strict, onError);
|
|
7
|
+
const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError);
|
|
8
8
|
if (!header)
|
|
9
9
|
return { value: '', type: null, comment: '', range: [start, start, start] };
|
|
10
10
|
const type = header.mode === '>' ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL;
|
|
@@ -46,6 +46,10 @@ function resolveBlockScalar(scalar, strict, onError) {
|
|
|
46
46
|
if (header.indent === 0)
|
|
47
47
|
trimIndent = indent.length;
|
|
48
48
|
contentStart = i;
|
|
49
|
+
if (trimIndent === 0 && !ctx.atRoot) {
|
|
50
|
+
const message = 'Block scalar values in collections must be indented';
|
|
51
|
+
onError(offset, 'BAD_INDENT', message);
|
|
52
|
+
}
|
|
49
53
|
break;
|
|
50
54
|
}
|
|
51
55
|
offset += indent.length + content.length + 1;
|
|
@@ -30,6 +30,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
30
30
|
next: key ?? sep?.[0],
|
|
31
31
|
offset,
|
|
32
32
|
onError,
|
|
33
|
+
parentIndent: fc.indent,
|
|
33
34
|
startOnNewline: false
|
|
34
35
|
});
|
|
35
36
|
if (!props.found) {
|
|
@@ -111,6 +112,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
111
112
|
next: value,
|
|
112
113
|
offset: keyNode.range[2],
|
|
113
114
|
onError,
|
|
115
|
+
parentIndent: fc.indent,
|
|
114
116
|
startOnNewline: false
|
|
115
117
|
});
|
|
116
118
|
if (valueProps.found) {
|
|
@@ -6,9 +6,10 @@ export interface ResolvePropsArg {
|
|
|
6
6
|
next: Token | null | undefined;
|
|
7
7
|
offset: number;
|
|
8
8
|
onError: ComposeErrorHandler;
|
|
9
|
+
parentIndent: number;
|
|
9
10
|
startOnNewline: boolean;
|
|
10
11
|
}
|
|
11
|
-
export declare function resolveProps(tokens: SourceToken[], { flow, indicator, next, offset, onError, startOnNewline }: ResolvePropsArg): {
|
|
12
|
+
export declare function resolveProps(tokens: SourceToken[], { flow, indicator, next, offset, onError, parentIndent, startOnNewline }: ResolvePropsArg): {
|
|
12
13
|
comma: SourceToken | null;
|
|
13
14
|
found: SourceToken | null;
|
|
14
15
|
spaceBefore: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
|
|
3
|
+
function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
|
|
4
4
|
let spaceBefore = false;
|
|
5
5
|
let atNewline = startOnNewline;
|
|
6
6
|
let hasSpace = startOnNewline;
|
|
@@ -9,6 +9,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
9
9
|
let hasNewline = false;
|
|
10
10
|
let hasNewlineAfterProp = false;
|
|
11
11
|
let reqSpace = false;
|
|
12
|
+
let tab = null;
|
|
12
13
|
let anchor = null;
|
|
13
14
|
let tag = null;
|
|
14
15
|
let comma = null;
|
|
@@ -22,16 +23,22 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
22
23
|
onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
|
|
23
24
|
reqSpace = false;
|
|
24
25
|
}
|
|
26
|
+
if (tab) {
|
|
27
|
+
if (atNewline && token.type !== 'comment' && token.type !== 'newline') {
|
|
28
|
+
onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
|
|
29
|
+
}
|
|
30
|
+
tab = null;
|
|
31
|
+
}
|
|
25
32
|
switch (token.type) {
|
|
26
33
|
case 'space':
|
|
27
34
|
// At the doc level, tabs at line start may be parsed
|
|
28
35
|
// as leading white space rather than indentation.
|
|
29
36
|
// In a flow collection, only the parser handles indent.
|
|
30
37
|
if (!flow &&
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
38
|
+
(indicator !== 'doc-start' || next?.type !== 'flow-collection') &&
|
|
39
|
+
token.source.includes('\t')) {
|
|
40
|
+
tab = token;
|
|
41
|
+
}
|
|
35
42
|
hasSpace = true;
|
|
36
43
|
break;
|
|
37
44
|
case 'comment': {
|
|
@@ -91,7 +98,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
91
98
|
if (found)
|
|
92
99
|
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
|
|
93
100
|
found = token;
|
|
94
|
-
atNewline =
|
|
101
|
+
atNewline =
|
|
102
|
+
indicator === 'seq-item-ind' || indicator === 'explicit-key-ind';
|
|
95
103
|
hasSpace = false;
|
|
96
104
|
break;
|
|
97
105
|
case 'comma':
|
|
@@ -117,8 +125,14 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
117
125
|
next.type !== 'space' &&
|
|
118
126
|
next.type !== 'newline' &&
|
|
119
127
|
next.type !== 'comma' &&
|
|
120
|
-
(next.type !== 'scalar' || next.source !== ''))
|
|
128
|
+
(next.type !== 'scalar' || next.source !== '')) {
|
|
121
129
|
onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
|
|
130
|
+
}
|
|
131
|
+
if (tab &&
|
|
132
|
+
((atNewline && tab.indent <= parentIndent) ||
|
|
133
|
+
next?.type === 'block-map' ||
|
|
134
|
+
next?.type === 'block-seq'))
|
|
135
|
+
onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
|
|
122
136
|
return {
|
|
123
137
|
comma,
|
|
124
138
|
found,
|
package/dist/parse/cst-scalar.js
CHANGED
|
@@ -20,7 +20,7 @@ function resolveAsScalar(token, strict = true, onError) {
|
|
|
20
20
|
case 'double-quoted-scalar':
|
|
21
21
|
return resolveFlowScalar.resolveFlowScalar(token, strict, _onError);
|
|
22
22
|
case 'block-scalar':
|
|
23
|
-
return resolveBlockScalar.resolveBlockScalar(
|
|
23
|
+
return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError);
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
return null;
|
package/dist/parse/cst.d.ts
CHANGED
|
@@ -51,11 +51,13 @@ export interface BlockMap {
|
|
|
51
51
|
indent: number;
|
|
52
52
|
items: Array<{
|
|
53
53
|
start: SourceToken[];
|
|
54
|
+
explicitKey?: true;
|
|
54
55
|
key?: never;
|
|
55
56
|
sep?: never;
|
|
56
57
|
value?: never;
|
|
57
58
|
} | {
|
|
58
59
|
start: SourceToken[];
|
|
60
|
+
explicitKey?: true;
|
|
59
61
|
key: Token | null;
|
|
60
62
|
sep: SourceToken[];
|
|
61
63
|
value?: Token;
|
package/dist/parse/lexer.js
CHANGED
|
@@ -81,11 +81,11 @@ function isEmpty(ch) {
|
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
const hexDigits = '0123456789ABCDEFabcdef'
|
|
85
|
-
const tagChars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"
|
|
86
|
-
const
|
|
87
|
-
const invalidAnchorChars = ' ,[]{}\n\r\t'
|
|
88
|
-
const isNotAnchorChar = (ch) => !ch || invalidAnchorChars.
|
|
84
|
+
const hexDigits = new Set('0123456789ABCDEFabcdef');
|
|
85
|
+
const tagChars = new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()");
|
|
86
|
+
const flowIndicatorChars = new Set(',[]{}');
|
|
87
|
+
const invalidAnchorChars = new Set(' ,[]{}\n\r\t');
|
|
88
|
+
const isNotAnchorChar = (ch) => !ch || invalidAnchorChars.has(ch);
|
|
89
89
|
/**
|
|
90
90
|
* Splits an input string into lexical tokens, i.e. smaller strings that are
|
|
91
91
|
* easily identifiable by `tokens.tokenType()`.
|
|
@@ -527,8 +527,10 @@ class Lexer {
|
|
|
527
527
|
if (indent >= this.indentNext) {
|
|
528
528
|
if (this.blockScalarIndent === -1)
|
|
529
529
|
this.indentNext = indent;
|
|
530
|
-
else
|
|
531
|
-
this.indentNext
|
|
530
|
+
else {
|
|
531
|
+
this.indentNext =
|
|
532
|
+
this.blockScalarIndent + (this.indentNext === 0 ? 1 : this.indentNext);
|
|
533
|
+
}
|
|
532
534
|
do {
|
|
533
535
|
const cs = this.continueScalar(nl + 1);
|
|
534
536
|
if (cs === -1)
|
|
@@ -541,14 +543,25 @@ class Lexer {
|
|
|
541
543
|
nl = this.buffer.length;
|
|
542
544
|
}
|
|
543
545
|
}
|
|
544
|
-
|
|
546
|
+
// Trailing insufficiently indented tabs are invalid.
|
|
547
|
+
// To catch that during parsing, we include them in the block scalar value.
|
|
548
|
+
let i = nl + 1;
|
|
549
|
+
ch = this.buffer[i];
|
|
550
|
+
while (ch === ' ')
|
|
551
|
+
ch = this.buffer[++i];
|
|
552
|
+
if (ch === '\t') {
|
|
553
|
+
while (ch === '\t' || ch === ' ' || ch === '\r' || ch === '\n')
|
|
554
|
+
ch = this.buffer[++i];
|
|
555
|
+
nl = i - 1;
|
|
556
|
+
}
|
|
557
|
+
else if (!this.blockScalarKeep) {
|
|
545
558
|
do {
|
|
546
559
|
let i = nl - 1;
|
|
547
560
|
let ch = this.buffer[i];
|
|
548
561
|
if (ch === '\r')
|
|
549
562
|
ch = this.buffer[--i];
|
|
550
563
|
const lastChar = i; // Drop the line if last char not more indented
|
|
551
|
-
while (ch === ' '
|
|
564
|
+
while (ch === ' ')
|
|
552
565
|
ch = this.buffer[--i];
|
|
553
566
|
if (ch === '\n' && i >= this.pos && i + 1 + indent > lastChar)
|
|
554
567
|
nl = i;
|
|
@@ -568,7 +581,7 @@ class Lexer {
|
|
|
568
581
|
while ((ch = this.buffer[++i])) {
|
|
569
582
|
if (ch === ':') {
|
|
570
583
|
const next = this.buffer[i + 1];
|
|
571
|
-
if (isEmpty(next) || (inFlow && next
|
|
584
|
+
if (isEmpty(next) || (inFlow && flowIndicatorChars.has(next)))
|
|
572
585
|
break;
|
|
573
586
|
end = i;
|
|
574
587
|
}
|
|
@@ -583,7 +596,7 @@ class Lexer {
|
|
|
583
596
|
else
|
|
584
597
|
end = i;
|
|
585
598
|
}
|
|
586
|
-
if (next === '#' || (inFlow &&
|
|
599
|
+
if (next === '#' || (inFlow && flowIndicatorChars.has(next)))
|
|
587
600
|
break;
|
|
588
601
|
if (ch === '\n') {
|
|
589
602
|
const cs = this.continueScalar(i + 1);
|
|
@@ -593,7 +606,7 @@ class Lexer {
|
|
|
593
606
|
}
|
|
594
607
|
}
|
|
595
608
|
else {
|
|
596
|
-
if (inFlow &&
|
|
609
|
+
if (inFlow && flowIndicatorChars.has(ch))
|
|
597
610
|
break;
|
|
598
611
|
end = i;
|
|
599
612
|
}
|
|
@@ -638,7 +651,7 @@ class Lexer {
|
|
|
638
651
|
case ':': {
|
|
639
652
|
const inFlow = this.flowLevel > 0;
|
|
640
653
|
const ch1 = this.charAt(1);
|
|
641
|
-
if (isEmpty(ch1) || (inFlow &&
|
|
654
|
+
if (isEmpty(ch1) || (inFlow && flowIndicatorChars.has(ch1))) {
|
|
642
655
|
if (!inFlow)
|
|
643
656
|
this.indentNext = this.indentValue + 1;
|
|
644
657
|
else if (this.flowKey)
|
|
@@ -663,11 +676,11 @@ class Lexer {
|
|
|
663
676
|
let i = this.pos + 1;
|
|
664
677
|
let ch = this.buffer[i];
|
|
665
678
|
while (ch) {
|
|
666
|
-
if (tagChars.
|
|
679
|
+
if (tagChars.has(ch))
|
|
667
680
|
ch = this.buffer[++i];
|
|
668
681
|
else if (ch === '%' &&
|
|
669
|
-
hexDigits.
|
|
670
|
-
hexDigits.
|
|
682
|
+
hexDigits.has(this.buffer[i + 1]) &&
|
|
683
|
+
hexDigits.has(this.buffer[i + 2])) {
|
|
671
684
|
ch = this.buffer[(i += 3)];
|
|
672
685
|
}
|
|
673
686
|
else
|
package/dist/parse/parser.js
CHANGED
|
@@ -308,7 +308,7 @@ class Parser {
|
|
|
308
308
|
}
|
|
309
309
|
else {
|
|
310
310
|
Object.assign(it, { key: token, sep: [] });
|
|
311
|
-
this.onKeyLine = !
|
|
311
|
+
this.onKeyLine = !it.explicitKey;
|
|
312
312
|
return;
|
|
313
313
|
}
|
|
314
314
|
break;
|
|
@@ -517,9 +517,9 @@ class Parser {
|
|
|
517
517
|
return;
|
|
518
518
|
}
|
|
519
519
|
if (this.indent >= map.indent) {
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
it.sep &&
|
|
520
|
+
const atMapIndent = !this.onKeyLine && this.indent === map.indent;
|
|
521
|
+
const atNextItem = atMapIndent &&
|
|
522
|
+
(it.sep || it.explicitKey) &&
|
|
523
523
|
this.type !== 'seq-item-ind';
|
|
524
524
|
// For empty nodes, assign newline-separated not indented empty tokens to following node
|
|
525
525
|
let start = [];
|
|
@@ -560,25 +560,26 @@ class Parser {
|
|
|
560
560
|
}
|
|
561
561
|
return;
|
|
562
562
|
case 'explicit-key-ind':
|
|
563
|
-
if (!it.sep && !
|
|
563
|
+
if (!it.sep && !it.explicitKey) {
|
|
564
564
|
it.start.push(this.sourceToken);
|
|
565
|
+
it.explicitKey = true;
|
|
565
566
|
}
|
|
566
567
|
else if (atNextItem || it.value) {
|
|
567
568
|
start.push(this.sourceToken);
|
|
568
|
-
map.items.push({ start });
|
|
569
|
+
map.items.push({ start, explicitKey: true });
|
|
569
570
|
}
|
|
570
571
|
else {
|
|
571
572
|
this.stack.push({
|
|
572
573
|
type: 'block-map',
|
|
573
574
|
offset: this.offset,
|
|
574
575
|
indent: this.indent,
|
|
575
|
-
items: [{ start: [this.sourceToken] }]
|
|
576
|
+
items: [{ start: [this.sourceToken], explicitKey: true }]
|
|
576
577
|
});
|
|
577
578
|
}
|
|
578
579
|
this.onKeyLine = true;
|
|
579
580
|
return;
|
|
580
581
|
case 'map-value-ind':
|
|
581
|
-
if (
|
|
582
|
+
if (it.explicitKey) {
|
|
582
583
|
if (!it.sep) {
|
|
583
584
|
if (includesToken(it.start, 'newline')) {
|
|
584
585
|
Object.assign(it, { key: null, sep: [this.sourceToken] });
|
|
@@ -669,9 +670,7 @@ class Parser {
|
|
|
669
670
|
default: {
|
|
670
671
|
const bv = this.startBlockValue(map);
|
|
671
672
|
if (bv) {
|
|
672
|
-
if (
|
|
673
|
-
bv.type !== 'block-seq' &&
|
|
674
|
-
includesToken(it.start, 'explicit-key-ind')) {
|
|
673
|
+
if (atMapIndent && bv.type !== 'block-seq') {
|
|
675
674
|
map.items.push({ start });
|
|
676
675
|
}
|
|
677
676
|
this.stack.push(bv);
|
|
@@ -892,7 +891,7 @@ class Parser {
|
|
|
892
891
|
type: 'block-map',
|
|
893
892
|
offset: this.offset,
|
|
894
893
|
indent: this.indent,
|
|
895
|
-
items: [{ start }]
|
|
894
|
+
items: [{ start, explicitKey: true }]
|
|
896
895
|
};
|
|
897
896
|
}
|
|
898
897
|
case 'map-value-ind': {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yaml",
|
|
3
|
-
"version": "2.4.
|
|
3
|
+
"version": "2.4.5",
|
|
4
4
|
"license": "ISC",
|
|
5
5
|
"author": "Eemeli Aro <eemeli@gmail.com>",
|
|
6
6
|
"repository": "github:eemeli/yaml",
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"clean": "git clean -fdxe node_modules",
|
|
45
45
|
"lint": "eslint src/",
|
|
46
46
|
"prettier": "prettier --write .",
|
|
47
|
-
"prestart": "
|
|
48
|
-
"start": "node -i -e 'YAML=require(\"./dist/index.js\")'",
|
|
47
|
+
"prestart": "rollup --sourcemap -c config/rollup.node-config.mjs",
|
|
48
|
+
"start": "node --enable-source-maps -i -e 'YAML=require(\"./dist/index.js\");const{parse,parseDocument,parseAllDocuments}=YAML'",
|
|
49
49
|
"test": "jest --config config/jest.config.js",
|
|
50
50
|
"test:all": "npm test && npm run test:types && npm run test:dist && npm run test:dist:types",
|
|
51
51
|
"test:browsers": "cd playground && npm test",
|