yaml 2.0.0-9 → 2.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/README.md +1 -1
- package/browser/dist/compose/compose-collection.js +1 -1
- package/browser/dist/compose/compose-doc.js +4 -3
- package/browser/dist/compose/compose-node.js +13 -4
- package/browser/dist/compose/compose-scalar.js +18 -13
- package/browser/dist/compose/composer.js +8 -10
- package/browser/dist/compose/resolve-block-map.js +15 -8
- package/browser/dist/compose/resolve-block-scalar.js +12 -3
- package/browser/dist/compose/resolve-block-seq.js +5 -0
- package/browser/dist/compose/resolve-flow-collection.js +13 -5
- package/browser/dist/compose/resolve-flow-scalar.js +1 -1
- package/browser/dist/compose/resolve-props.js +9 -1
- package/browser/dist/compose/util-empty-scalar-position.js +1 -1
- package/browser/dist/compose/util-flow-indent-check.js +15 -0
- package/browser/dist/doc/Document.js +53 -20
- package/browser/dist/doc/anchors.js +2 -2
- package/browser/dist/doc/createNode.js +8 -7
- package/browser/dist/doc/directives.js +7 -4
- package/browser/dist/index.js +1 -2
- package/browser/dist/nodes/Collection.js +2 -1
- package/browser/dist/nodes/Pair.js +2 -2
- package/browser/dist/nodes/Scalar.js +1 -1
- package/browser/dist/nodes/YAMLMap.js +7 -7
- package/browser/dist/nodes/YAMLSeq.js +2 -10
- package/browser/dist/nodes/addPairToJSMap.js +1 -1
- package/browser/dist/nodes/toJS.js +3 -1
- package/browser/dist/parse/cst-scalar.js +1 -2
- package/browser/dist/parse/cst-visit.js +2 -2
- package/browser/dist/parse/lexer.js +20 -8
- package/browser/dist/parse/parser.js +142 -68
- package/browser/dist/public-api.js +5 -7
- package/browser/dist/schema/Schema.js +14 -4
- package/browser/dist/schema/common/null.js +3 -1
- package/browser/dist/schema/core/float.js +4 -1
- package/browser/dist/schema/tags.js +9 -9
- package/browser/dist/schema/yaml-1.1/float.js +4 -1
- package/browser/dist/schema/yaml-1.1/omap.js +1 -1
- package/browser/dist/schema/yaml-1.1/pairs.js +1 -1
- package/browser/dist/schema/yaml-1.1/set.js +4 -0
- package/browser/dist/stringify/stringify.js +49 -22
- package/browser/dist/stringify/stringifyCollection.js +114 -85
- package/browser/dist/stringify/stringifyComment.js +18 -12
- package/browser/dist/stringify/stringifyDocument.js +36 -15
- package/browser/dist/stringify/stringifyPair.js +34 -20
- package/browser/dist/stringify/stringifyString.js +8 -11
- package/browser/dist/visit.js +149 -62
- package/dist/compose/compose-collection.js +1 -1
- package/dist/compose/compose-doc.js +4 -3
- package/dist/compose/compose-node.d.ts +1 -0
- package/dist/compose/compose-node.js +13 -4
- package/dist/compose/compose-scalar.js +17 -12
- package/dist/compose/composer.d.ts +1 -1
- package/dist/compose/composer.js +10 -12
- package/dist/compose/resolve-block-map.js +15 -8
- package/dist/compose/resolve-block-scalar.js +12 -3
- package/dist/compose/resolve-block-seq.js +5 -0
- package/dist/compose/resolve-flow-collection.js +13 -5
- package/dist/compose/resolve-flow-scalar.js +1 -1
- package/dist/compose/resolve-props.d.ts +2 -1
- package/dist/compose/resolve-props.js +9 -1
- package/dist/compose/util-empty-scalar-position.js +1 -1
- package/dist/compose/util-flow-indent-check.d.ts +3 -0
- package/dist/compose/util-flow-indent-check.js +17 -0
- package/dist/doc/Document.d.ts +17 -14
- package/dist/doc/Document.js +58 -25
- package/dist/doc/anchors.d.ts +3 -3
- package/dist/doc/anchors.js +2 -2
- package/dist/doc/createNode.d.ts +1 -1
- package/dist/doc/createNode.js +8 -7
- package/dist/doc/directives.d.ts +4 -2
- package/dist/doc/directives.js +7 -4
- package/dist/index.d.ts +4 -2
- package/dist/index.js +1 -2
- package/dist/nodes/Collection.d.ts +2 -2
- package/dist/nodes/Collection.js +2 -1
- package/dist/nodes/Node.d.ts +14 -10
- package/dist/nodes/Pair.d.ts +1 -1
- package/dist/nodes/Pair.js +2 -2
- package/dist/nodes/Scalar.js +1 -1
- package/dist/nodes/YAMLMap.d.ts +9 -4
- package/dist/nodes/YAMLMap.js +7 -7
- package/dist/nodes/YAMLSeq.d.ts +4 -1
- package/dist/nodes/YAMLSeq.js +2 -10
- package/dist/nodes/addPairToJSMap.js +1 -1
- package/dist/nodes/toJS.js +3 -1
- package/dist/options.d.ts +44 -13
- package/dist/parse/cst-scalar.js +1 -2
- package/dist/parse/cst-visit.js +2 -2
- package/dist/parse/lexer.js +20 -8
- package/dist/parse/parser.js +142 -68
- package/dist/public-api.js +5 -7
- package/dist/schema/Schema.d.ts +5 -4
- package/dist/schema/Schema.js +13 -3
- package/dist/schema/common/null.js +3 -1
- package/dist/schema/core/float.js +4 -1
- package/dist/schema/json-schema.d.ts +69 -0
- package/dist/schema/tags.d.ts +1 -2
- package/dist/schema/tags.js +9 -9
- package/dist/schema/yaml-1.1/float.js +4 -1
- package/dist/schema/yaml-1.1/omap.d.ts +7 -3
- package/dist/schema/yaml-1.1/omap.js +1 -1
- package/dist/schema/yaml-1.1/pairs.js +1 -1
- package/dist/schema/yaml-1.1/set.d.ts +6 -2
- package/dist/schema/yaml-1.1/set.js +4 -0
- package/dist/stringify/stringify.d.ts +5 -3
- package/dist/stringify/stringify.js +49 -22
- package/dist/stringify/stringifyCollection.d.ts +2 -6
- package/dist/stringify/stringifyCollection.js +113 -84
- package/dist/stringify/stringifyComment.d.ts +10 -2
- package/dist/stringify/stringifyComment.js +19 -12
- package/dist/stringify/stringifyDocument.js +35 -14
- package/dist/stringify/stringifyPair.js +33 -19
- package/dist/stringify/stringifyString.js +8 -11
- package/dist/test-events.js +14 -14
- package/dist/visit.d.ts +49 -10
- package/dist/visit.js +148 -60
- package/package.json +11 -10
- package/browser/dist/options.js +0 -18
- package/dist/node_modules/tslib/tslib.es6.js +0 -76
- package/dist/options.js +0 -20
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ For more information, see the project's documentation site: [**eemeli.org/yaml**
|
|
|
19
19
|
To install:
|
|
20
20
|
|
|
21
21
|
```sh
|
|
22
|
-
npm install
|
|
22
|
+
npm install yaml
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**Note:** These docs are for `yaml@2`. For v1, see the [v1.10.0 tag](https://github.com/eemeli/yaml/tree/v1.10.0) for the source and [eemeli.org/yaml/v1](https://eemeli.org/yaml/v1/) for the documentation.
|
|
@@ -51,7 +51,7 @@ function composeCollection(CN, ctx, token, tagToken, onError) {
|
|
|
51
51
|
: new Scalar(res);
|
|
52
52
|
node.range = coll.range;
|
|
53
53
|
node.tag = tagName;
|
|
54
|
-
if (tag
|
|
54
|
+
if (tag?.format)
|
|
55
55
|
node.format = tag.format;
|
|
56
56
|
return node;
|
|
57
57
|
}
|
|
@@ -4,22 +4,23 @@ import { resolveEnd } from './resolve-end.js';
|
|
|
4
4
|
import { resolveProps } from './resolve-props.js';
|
|
5
5
|
|
|
6
6
|
function composeDoc(options, directives, { offset, start, value, end }, onError) {
|
|
7
|
-
const opts = Object.assign({ directives }, options);
|
|
7
|
+
const opts = Object.assign({ _directives: directives }, options);
|
|
8
8
|
const doc = new Document(undefined, opts);
|
|
9
9
|
const ctx = {
|
|
10
|
+
atRoot: true,
|
|
10
11
|
directives: doc.directives,
|
|
11
12
|
options: doc.options,
|
|
12
13
|
schema: doc.schema
|
|
13
14
|
};
|
|
14
15
|
const props = resolveProps(start, {
|
|
15
16
|
indicator: 'doc-start',
|
|
16
|
-
next: value
|
|
17
|
+
next: value ?? end?.[0],
|
|
17
18
|
offset,
|
|
18
19
|
onError,
|
|
19
20
|
startOnNewline: true
|
|
20
21
|
});
|
|
21
22
|
if (props.found) {
|
|
22
|
-
doc.directives.
|
|
23
|
+
doc.directives.docStart = true;
|
|
23
24
|
if (value &&
|
|
24
25
|
(value.type === 'block-map' || value.type === 'block-seq') &&
|
|
25
26
|
!props.hasNewline)
|
|
@@ -8,6 +8,7 @@ const CN = { composeNode, composeEmptyNode };
|
|
|
8
8
|
function composeNode(ctx, token, props, onError) {
|
|
9
9
|
const { spaceBefore, comment, anchor, tag } = props;
|
|
10
10
|
let node;
|
|
11
|
+
let isSrcToken = true;
|
|
11
12
|
switch (token.type) {
|
|
12
13
|
case 'alias':
|
|
13
14
|
node = composeAlias(ctx, token, onError);
|
|
@@ -29,9 +30,14 @@ function composeNode(ctx, token, props, onError) {
|
|
|
29
30
|
if (anchor)
|
|
30
31
|
node.anchor = anchor.source.substring(1);
|
|
31
32
|
break;
|
|
32
|
-
default:
|
|
33
|
-
|
|
34
|
-
|
|
33
|
+
default: {
|
|
34
|
+
const message = token.type === 'error'
|
|
35
|
+
? token.message
|
|
36
|
+
: `Unsupported token (type: ${token.type})`;
|
|
37
|
+
onError(token, 'UNEXPECTED_TOKEN', message);
|
|
38
|
+
node = composeEmptyNode(ctx, token.offset, undefined, null, props, onError);
|
|
39
|
+
isSrcToken = false;
|
|
40
|
+
}
|
|
35
41
|
}
|
|
36
42
|
if (anchor && node.anchor === '')
|
|
37
43
|
onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');
|
|
@@ -43,7 +49,8 @@ function composeNode(ctx, token, props, onError) {
|
|
|
43
49
|
else
|
|
44
50
|
node.commentBefore = comment;
|
|
45
51
|
}
|
|
46
|
-
|
|
52
|
+
// @ts-expect-error Type checking misses meaning of isSrcToken
|
|
53
|
+
if (ctx.options.keepSourceTokens && isSrcToken)
|
|
47
54
|
node.srcToken = token;
|
|
48
55
|
return node;
|
|
49
56
|
}
|
|
@@ -70,6 +77,8 @@ function composeAlias({ options }, { offset, source, end }, onError) {
|
|
|
70
77
|
const alias = new Alias(source.substring(1));
|
|
71
78
|
if (alias.source === '')
|
|
72
79
|
onError(offset, 'BAD_ALIAS', 'Alias cannot be an empty string');
|
|
80
|
+
if (alias.source.endsWith(':'))
|
|
81
|
+
onError(offset + source.length - 1, 'BAD_ALIAS', 'Alias ending in : is ambiguous', true);
|
|
73
82
|
const valueEnd = offset + source.length;
|
|
74
83
|
const re = resolveEnd(end, valueEnd, options.strict, onError);
|
|
75
84
|
alias.range = [offset, valueEnd, re.offset];
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { SCALAR, isScalar } from '../nodes/Node.js';
|
|
2
2
|
import { Scalar } from '../nodes/Scalar.js';
|
|
3
3
|
import { resolveBlockScalar } from './resolve-block-scalar.js';
|
|
4
4
|
import { resolveFlowScalar } from './resolve-flow-scalar.js';
|
|
@@ -12,15 +12,17 @@ function composeScalar(ctx, token, tagToken, onError) {
|
|
|
12
12
|
: null;
|
|
13
13
|
const tag = tagToken && tagName
|
|
14
14
|
? findScalarTagByName(ctx.schema, value, tagName, tagToken, onError)
|
|
15
|
-
:
|
|
15
|
+
: token.type === 'scalar'
|
|
16
|
+
? findScalarTagByTest(ctx, value, token, onError)
|
|
17
|
+
: ctx.schema[SCALAR];
|
|
16
18
|
let scalar;
|
|
17
19
|
try {
|
|
18
|
-
const res = tag.resolve(value, msg => onError(tagToken
|
|
20
|
+
const res = tag.resolve(value, msg => onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
|
|
19
21
|
scalar = isScalar(res) ? res : new Scalar(res);
|
|
20
22
|
}
|
|
21
23
|
catch (error) {
|
|
22
24
|
const msg = error instanceof Error ? error.message : String(error);
|
|
23
|
-
onError(tagToken
|
|
25
|
+
onError(tagToken ?? token, 'TAG_RESOLVE_FAILED', msg);
|
|
24
26
|
scalar = new Scalar(value);
|
|
25
27
|
}
|
|
26
28
|
scalar.range = range;
|
|
@@ -36,7 +38,6 @@ function composeScalar(ctx, token, tagToken, onError) {
|
|
|
36
38
|
return scalar;
|
|
37
39
|
}
|
|
38
40
|
function findScalarTagByName(schema, value, tagName, tagToken, onError) {
|
|
39
|
-
var _a;
|
|
40
41
|
if (tagName === '!')
|
|
41
42
|
return schema[SCALAR]; // non-specific tag
|
|
42
43
|
const matchWithTest = [];
|
|
@@ -49,7 +50,7 @@ function findScalarTagByName(schema, value, tagName, tagToken, onError) {
|
|
|
49
50
|
}
|
|
50
51
|
}
|
|
51
52
|
for (const tag of matchWithTest)
|
|
52
|
-
if (
|
|
53
|
+
if (tag.test?.test(value))
|
|
53
54
|
return tag;
|
|
54
55
|
const kt = schema.knownTags[tagName];
|
|
55
56
|
if (kt && !kt.collection) {
|
|
@@ -61,15 +62,19 @@ function findScalarTagByName(schema, value, tagName, tagToken, onError) {
|
|
|
61
62
|
onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');
|
|
62
63
|
return schema[SCALAR];
|
|
63
64
|
}
|
|
64
|
-
function findScalarTagByTest(schema, value,
|
|
65
|
-
|
|
66
|
-
if (
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
65
|
+
function findScalarTagByTest({ directives, schema }, value, token, onError) {
|
|
66
|
+
const tag = schema.tags.find(tag => tag.default && tag.test?.test(value)) || schema[SCALAR];
|
|
67
|
+
if (schema.compat) {
|
|
68
|
+
const compat = schema.compat.find(tag => tag.default && tag.test?.test(value)) ??
|
|
69
|
+
schema[SCALAR];
|
|
70
|
+
if (tag.tag !== compat.tag) {
|
|
71
|
+
const ts = directives.tagString(tag.tag);
|
|
72
|
+
const cs = directives.tagString(compat.tag);
|
|
73
|
+
const msg = `Value may be parsed as either ${ts} or ${cs}`;
|
|
74
|
+
onError(token, 'TAG_RESOLVE_FAILED', msg, true);
|
|
70
75
|
}
|
|
71
76
|
}
|
|
72
|
-
return
|
|
77
|
+
return tag;
|
|
73
78
|
}
|
|
74
79
|
|
|
75
80
|
export { composeScalar };
|
|
@@ -2,7 +2,6 @@ import { Directives } from '../doc/directives.js';
|
|
|
2
2
|
import { Document } from '../doc/Document.js';
|
|
3
3
|
import { YAMLWarning, YAMLParseError } from '../errors.js';
|
|
4
4
|
import { isCollection, isPair } from '../nodes/Node.js';
|
|
5
|
-
import { defaultOptions } from '../options.js';
|
|
6
5
|
import { composeDoc } from './compose-doc.js';
|
|
7
6
|
import { resolveEnd } from './resolve-end.js';
|
|
8
7
|
|
|
@@ -15,7 +14,6 @@ function getErrorPos(src) {
|
|
|
15
14
|
return [offset, offset + (typeof source === 'string' ? source.length : 1)];
|
|
16
15
|
}
|
|
17
16
|
function parsePrelude(prelude) {
|
|
18
|
-
var _a;
|
|
19
17
|
let comment = '';
|
|
20
18
|
let atComment = false;
|
|
21
19
|
let afterEmptyLine = false;
|
|
@@ -30,7 +28,7 @@ function parsePrelude(prelude) {
|
|
|
30
28
|
afterEmptyLine = false;
|
|
31
29
|
break;
|
|
32
30
|
case '%':
|
|
33
|
-
if (
|
|
31
|
+
if (prelude[i + 1]?.[0] !== '#')
|
|
34
32
|
i += 1;
|
|
35
33
|
atComment = false;
|
|
36
34
|
break;
|
|
@@ -68,9 +66,8 @@ class Composer {
|
|
|
68
66
|
else
|
|
69
67
|
this.errors.push(new YAMLParseError(pos, code, message));
|
|
70
68
|
};
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
});
|
|
69
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
70
|
+
this.directives = new Directives({ version: options.version || '1.2' });
|
|
74
71
|
this.options = options;
|
|
75
72
|
}
|
|
76
73
|
decorate(doc, afterDoc) {
|
|
@@ -81,7 +78,7 @@ class Composer {
|
|
|
81
78
|
if (afterDoc) {
|
|
82
79
|
doc.comment = doc.comment ? `${doc.comment}\n${comment}` : comment;
|
|
83
80
|
}
|
|
84
|
-
else if (afterEmptyLine || doc.directives.
|
|
81
|
+
else if (afterEmptyLine || doc.directives.docStart || !dc) {
|
|
85
82
|
doc.commentBefore = comment;
|
|
86
83
|
}
|
|
87
84
|
else if (isCollection(dc) && !dc.flow && dc.items.length > 0) {
|
|
@@ -146,8 +143,8 @@ class Composer {
|
|
|
146
143
|
break;
|
|
147
144
|
case 'document': {
|
|
148
145
|
const doc = composeDoc(this.options, this.directives, token, this.onError);
|
|
149
|
-
if (this.atDirectives && !doc.directives.
|
|
150
|
-
this.onError(token, 'MISSING_CHAR', 'Missing directives-end indicator line');
|
|
146
|
+
if (this.atDirectives && !doc.directives.docStart)
|
|
147
|
+
this.onError(token, 'MISSING_CHAR', 'Missing directives-end/doc-start indicator line');
|
|
151
148
|
this.decorate(doc, false);
|
|
152
149
|
if (this.doc)
|
|
153
150
|
yield this.doc;
|
|
@@ -179,6 +176,7 @@ class Composer {
|
|
|
179
176
|
this.errors.push(new YAMLParseError(getErrorPos(token), 'UNEXPECTED_TOKEN', msg));
|
|
180
177
|
break;
|
|
181
178
|
}
|
|
179
|
+
this.doc.directives.docEnd = true;
|
|
182
180
|
const end = resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError);
|
|
183
181
|
this.decorate(this.doc, true);
|
|
184
182
|
if (end.comment) {
|
|
@@ -205,7 +203,7 @@ class Composer {
|
|
|
205
203
|
this.doc = null;
|
|
206
204
|
}
|
|
207
205
|
else if (forceDoc) {
|
|
208
|
-
const opts = Object.assign({
|
|
206
|
+
const opts = Object.assign({ _directives: this.directives }, this.options);
|
|
209
207
|
const doc = new Document(undefined, opts);
|
|
210
208
|
if (this.atDirectives)
|
|
211
209
|
this.onError(endOffset, 'MISSING_CHAR', 'Missing directives-end indicator line');
|
|
@@ -2,19 +2,21 @@ import { Pair } from '../nodes/Pair.js';
|
|
|
2
2
|
import { YAMLMap } from '../nodes/YAMLMap.js';
|
|
3
3
|
import { resolveProps } from './resolve-props.js';
|
|
4
4
|
import { containsNewline } from './util-contains-newline.js';
|
|
5
|
+
import { flowIndentCheck } from './util-flow-indent-check.js';
|
|
5
6
|
import { mapIncludes } from './util-map-includes.js';
|
|
6
7
|
|
|
7
8
|
const startColMsg = 'All mapping items must start at the same column';
|
|
8
9
|
function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
|
|
9
|
-
var _a;
|
|
10
10
|
const map = new YAMLMap(ctx.schema);
|
|
11
|
+
if (ctx.atRoot)
|
|
12
|
+
ctx.atRoot = false;
|
|
11
13
|
let offset = bm.offset;
|
|
12
14
|
for (const collItem of bm.items) {
|
|
13
15
|
const { start, key, sep, value } = collItem;
|
|
14
16
|
// key properties
|
|
15
17
|
const keyProps = resolveProps(start, {
|
|
16
18
|
indicator: 'explicit-key-ind',
|
|
17
|
-
next: key
|
|
19
|
+
next: key ?? sep?.[0],
|
|
18
20
|
offset,
|
|
19
21
|
onError,
|
|
20
22
|
startOnNewline: true
|
|
@@ -37,21 +39,24 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
|
|
|
37
39
|
}
|
|
38
40
|
continue;
|
|
39
41
|
}
|
|
42
|
+
if (keyProps.hasNewlineAfterProp || containsNewline(key)) {
|
|
43
|
+
onError(key ?? start[start.length - 1], 'MULTILINE_IMPLICIT_KEY', 'Implicit keys need to be on a single line');
|
|
44
|
+
}
|
|
40
45
|
}
|
|
41
|
-
else if (
|
|
46
|
+
else if (keyProps.found?.indent !== bm.indent) {
|
|
42
47
|
onError(offset, 'BAD_INDENT', startColMsg);
|
|
43
|
-
|
|
44
|
-
onError(key, // checked by containsNewline()
|
|
45
|
-
'MULTILINE_IMPLICIT_KEY', 'Implicit keys need to be on a single line');
|
|
48
|
+
}
|
|
46
49
|
// key value
|
|
47
50
|
const keyStart = keyProps.end;
|
|
48
51
|
const keyNode = key
|
|
49
52
|
? composeNode(ctx, key, keyProps, onError)
|
|
50
53
|
: composeEmptyNode(ctx, keyStart, start, null, keyProps, onError);
|
|
54
|
+
if (ctx.schema.compat)
|
|
55
|
+
flowIndentCheck(bm.indent, key, onError);
|
|
51
56
|
if (mapIncludes(ctx, map.items, keyNode))
|
|
52
57
|
onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');
|
|
53
58
|
// value properties
|
|
54
|
-
const valueProps = resolveProps(sep
|
|
59
|
+
const valueProps = resolveProps(sep ?? [], {
|
|
55
60
|
indicator: 'map-value-ind',
|
|
56
61
|
next: value,
|
|
57
62
|
offset: keyNode.range[2],
|
|
@@ -61,7 +66,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
|
|
|
61
66
|
offset = valueProps.end;
|
|
62
67
|
if (valueProps.found) {
|
|
63
68
|
if (implicitKey) {
|
|
64
|
-
if (
|
|
69
|
+
if (value?.type === 'block-map' && !valueProps.hasNewline)
|
|
65
70
|
onError(offset, 'BLOCK_AS_IMPLICIT_KEY', 'Nested mappings are not allowed in compact mappings');
|
|
66
71
|
if (ctx.options.strict &&
|
|
67
72
|
keyProps.start < valueProps.found.offset - 1024)
|
|
@@ -71,6 +76,8 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
|
|
|
71
76
|
const valueNode = value
|
|
72
77
|
? composeNode(ctx, value, valueProps, onError)
|
|
73
78
|
: composeEmptyNode(ctx, offset, sep, null, valueProps, onError);
|
|
79
|
+
if (ctx.schema.compat)
|
|
80
|
+
flowIndentCheck(bm.indent, value, onError);
|
|
74
81
|
offset = valueNode.range[2];
|
|
75
82
|
const pair = new Pair(keyNode, valueNode);
|
|
76
83
|
if (ctx.options.keepSourceTokens)
|
|
@@ -17,8 +17,10 @@ function resolveBlockScalar(scalar, strict, onError) {
|
|
|
17
17
|
break;
|
|
18
18
|
}
|
|
19
19
|
// shortcut for empty contents
|
|
20
|
-
if (
|
|
21
|
-
const value = header.chomp === '+'
|
|
20
|
+
if (chompStart === 0) {
|
|
21
|
+
const value = header.chomp === '+' && lines.length > 0
|
|
22
|
+
? '\n'.repeat(Math.max(1, lines.length - 1))
|
|
23
|
+
: '';
|
|
22
24
|
let end = start + header.length;
|
|
23
25
|
if (scalar.source)
|
|
24
26
|
end += scalar.source.length;
|
|
@@ -46,6 +48,11 @@ function resolveBlockScalar(scalar, strict, onError) {
|
|
|
46
48
|
}
|
|
47
49
|
offset += indent.length + content.length + 1;
|
|
48
50
|
}
|
|
51
|
+
// include trailing more-indented empty lines in content
|
|
52
|
+
for (let i = lines.length - 1; i >= chompStart; --i) {
|
|
53
|
+
if (lines[i][0].length > trimIndent)
|
|
54
|
+
chompStart = i + 1;
|
|
55
|
+
}
|
|
49
56
|
let value = '';
|
|
50
57
|
let sep = '';
|
|
51
58
|
let prevMoreIndented = false;
|
|
@@ -175,7 +182,9 @@ function splitLines(source) {
|
|
|
175
182
|
const split = source.split(/\n( *)/);
|
|
176
183
|
const first = split[0];
|
|
177
184
|
const m = first.match(/^( *)/);
|
|
178
|
-
const line0 = m
|
|
185
|
+
const line0 = m?.[1]
|
|
186
|
+
? [m[1], first.slice(m[1].length)]
|
|
187
|
+
: ['', first];
|
|
179
188
|
const lines = [line0];
|
|
180
189
|
for (let i = 1; i < split.length; i += 2)
|
|
181
190
|
lines.push([split[i], split[i + 1]]);
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { YAMLSeq } from '../nodes/YAMLSeq.js';
|
|
2
2
|
import { resolveProps } from './resolve-props.js';
|
|
3
|
+
import { flowIndentCheck } from './util-flow-indent-check.js';
|
|
3
4
|
|
|
4
5
|
function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError) {
|
|
5
6
|
const seq = new YAMLSeq(ctx.schema);
|
|
7
|
+
if (ctx.atRoot)
|
|
8
|
+
ctx.atRoot = false;
|
|
6
9
|
let offset = bs.offset;
|
|
7
10
|
for (const { start, value } of bs.items) {
|
|
8
11
|
const props = resolveProps(start, {
|
|
@@ -30,6 +33,8 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError) {
|
|
|
30
33
|
const node = value
|
|
31
34
|
? composeNode(ctx, value, props, onError)
|
|
32
35
|
: composeEmptyNode(ctx, offset, start, null, props, onError);
|
|
36
|
+
if (ctx.schema.compat)
|
|
37
|
+
flowIndentCheck(bs.indent, value, onError);
|
|
33
38
|
offset = node.range[2];
|
|
34
39
|
seq.items.push(node);
|
|
35
40
|
}
|
|
@@ -16,14 +16,17 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
16
16
|
? new YAMLMap(ctx.schema)
|
|
17
17
|
: new YAMLSeq(ctx.schema);
|
|
18
18
|
coll.flow = true;
|
|
19
|
-
|
|
19
|
+
const atRoot = ctx.atRoot;
|
|
20
|
+
if (atRoot)
|
|
21
|
+
ctx.atRoot = false;
|
|
22
|
+
let offset = fc.offset + fc.start.source.length;
|
|
20
23
|
for (let i = 0; i < fc.items.length; ++i) {
|
|
21
24
|
const collItem = fc.items[i];
|
|
22
25
|
const { start, key, sep, value } = collItem;
|
|
23
26
|
const props = resolveProps(start, {
|
|
24
27
|
flow: fcName,
|
|
25
28
|
indicator: 'explicit-key-ind',
|
|
26
|
-
next: key
|
|
29
|
+
next: key ?? sep?.[0],
|
|
27
30
|
offset,
|
|
28
31
|
onError,
|
|
29
32
|
startOnNewline: false
|
|
@@ -40,6 +43,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
40
43
|
else
|
|
41
44
|
coll.comment = props.comment;
|
|
42
45
|
}
|
|
46
|
+
offset = props.end;
|
|
43
47
|
continue;
|
|
44
48
|
}
|
|
45
49
|
if (!isMap && ctx.options.strict && containsNewline(key))
|
|
@@ -70,7 +74,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
70
74
|
if (prevItemComment) {
|
|
71
75
|
let prev = coll.items[coll.items.length - 1];
|
|
72
76
|
if (isPair(prev))
|
|
73
|
-
prev = prev.value
|
|
77
|
+
prev = prev.value ?? prev.key;
|
|
74
78
|
if (prev.comment)
|
|
75
79
|
prev.comment += '\n' + prevItemComment;
|
|
76
80
|
else
|
|
@@ -100,7 +104,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
100
104
|
if (isBlock(key))
|
|
101
105
|
onError(keyNode.range, 'BLOCK_IN_FLOW', blockMsg);
|
|
102
106
|
// value properties
|
|
103
|
-
const valueProps = resolveProps(sep
|
|
107
|
+
const valueProps = resolveProps(sep ?? [], {
|
|
104
108
|
flow: fcName,
|
|
105
109
|
indicator: 'map-value-ind',
|
|
106
110
|
next: value,
|
|
@@ -169,7 +173,11 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
|
|
|
169
173
|
if (ce && ce.source === expectedEnd)
|
|
170
174
|
cePos = ce.offset + ce.source.length;
|
|
171
175
|
else {
|
|
172
|
-
|
|
176
|
+
const name = fcName[0].toUpperCase() + fcName.substring(1);
|
|
177
|
+
const msg = atRoot
|
|
178
|
+
? `${name} must end with a ${expectedEnd}`
|
|
179
|
+
: `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`;
|
|
180
|
+
onError(offset, atRoot ? 'MISSING_CHAR' : 'BAD_INDENT', msg);
|
|
173
181
|
if (ce && ce.source.length !== 1)
|
|
174
182
|
ee.unshift(ce);
|
|
175
183
|
}
|
|
@@ -111,7 +111,7 @@ function foldLines(source) {
|
|
|
111
111
|
const last = /[ \t]*(.*)/sy;
|
|
112
112
|
last.lastIndex = pos;
|
|
113
113
|
match = last.exec(source);
|
|
114
|
-
return res + sep + (
|
|
114
|
+
return res + sep + (match?.[1] ?? '');
|
|
115
115
|
}
|
|
116
116
|
function doubleQuotedValue(source, onError) {
|
|
117
117
|
let res = '';
|
|
@@ -5,6 +5,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
5
5
|
let comment = '';
|
|
6
6
|
let commentSep = '';
|
|
7
7
|
let hasNewline = false;
|
|
8
|
+
let hasNewlineAfterProp = false;
|
|
8
9
|
let reqSpace = false;
|
|
9
10
|
let anchor = null;
|
|
10
11
|
let tag = null;
|
|
@@ -54,11 +55,15 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
54
55
|
commentSep += token.source;
|
|
55
56
|
atNewline = true;
|
|
56
57
|
hasNewline = true;
|
|
58
|
+
if (anchor || tag)
|
|
59
|
+
hasNewlineAfterProp = true;
|
|
57
60
|
hasSpace = true;
|
|
58
61
|
break;
|
|
59
62
|
case 'anchor':
|
|
60
63
|
if (anchor)
|
|
61
64
|
onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
|
|
65
|
+
if (token.source.endsWith(':'))
|
|
66
|
+
onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);
|
|
62
67
|
anchor = token;
|
|
63
68
|
if (start === null)
|
|
64
69
|
start = token.offset;
|
|
@@ -81,6 +86,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
81
86
|
// Could here handle preceding comments differently
|
|
82
87
|
if (anchor || tag)
|
|
83
88
|
onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
|
|
89
|
+
if (found)
|
|
90
|
+
onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
|
|
84
91
|
found = token;
|
|
85
92
|
atNewline = false;
|
|
86
93
|
hasSpace = false;
|
|
@@ -116,10 +123,11 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
|
|
|
116
123
|
spaceBefore,
|
|
117
124
|
comment,
|
|
118
125
|
hasNewline,
|
|
126
|
+
hasNewlineAfterProp,
|
|
119
127
|
anchor,
|
|
120
128
|
tag,
|
|
121
129
|
end,
|
|
122
|
-
start: start
|
|
130
|
+
start: start ?? end
|
|
123
131
|
};
|
|
124
132
|
}
|
|
125
133
|
|
|
@@ -14,7 +14,7 @@ function emptyScalarPosition(offset, before, pos) {
|
|
|
14
14
|
// Technically, an empty scalar is immediately after the last non-empty
|
|
15
15
|
// node, but it's more useful to place it after any whitespace.
|
|
16
16
|
st = before[++i];
|
|
17
|
-
while (
|
|
17
|
+
while (st?.type === 'space') {
|
|
18
18
|
offset += st.source.length;
|
|
19
19
|
st = before[++i];
|
|
20
20
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { containsNewline } from './util-contains-newline.js';
|
|
2
|
+
|
|
3
|
+
function flowIndentCheck(indent, fc, onError) {
|
|
4
|
+
if (fc?.type === 'flow-collection') {
|
|
5
|
+
const end = fc.end[0];
|
|
6
|
+
if (end.indent === indent &&
|
|
7
|
+
(end.source === ']' || end.source === '}') &&
|
|
8
|
+
containsNewline(fc)) {
|
|
9
|
+
const msg = 'Flow end indicator should be more indented than parent';
|
|
10
|
+
onError(end, 'BAD_INDENT', msg, true);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export { flowIndentCheck };
|
|
@@ -3,7 +3,6 @@ import { isEmptyPath, collectionFromPath } from '../nodes/Collection.js';
|
|
|
3
3
|
import { NODE_TYPE, DOC, isNode, isCollection, isScalar } from '../nodes/Node.js';
|
|
4
4
|
import { Pair } from '../nodes/Pair.js';
|
|
5
5
|
import { toJS } from '../nodes/toJS.js';
|
|
6
|
-
import { defaultOptions } from '../options.js';
|
|
7
6
|
import { Schema } from '../schema/Schema.js';
|
|
8
7
|
import { stringify } from '../stringify/stringify.js';
|
|
9
8
|
import { stringifyDocument } from '../stringify/stringifyDocument.js';
|
|
@@ -31,11 +30,19 @@ class Document {
|
|
|
31
30
|
options = replacer;
|
|
32
31
|
replacer = undefined;
|
|
33
32
|
}
|
|
34
|
-
const opt = Object.assign({
|
|
33
|
+
const opt = Object.assign({
|
|
34
|
+
intAsBigInt: false,
|
|
35
|
+
keepSourceTokens: false,
|
|
36
|
+
logLevel: 'warn',
|
|
37
|
+
prettyErrors: true,
|
|
38
|
+
strict: true,
|
|
39
|
+
uniqueKeys: true,
|
|
40
|
+
version: '1.2'
|
|
41
|
+
}, options);
|
|
35
42
|
this.options = opt;
|
|
36
43
|
let { version } = opt;
|
|
37
|
-
if (options
|
|
38
|
-
this.directives = options.
|
|
44
|
+
if (options?._directives) {
|
|
45
|
+
this.directives = options._directives.atDocument();
|
|
39
46
|
if (this.directives.yaml.explicit)
|
|
40
47
|
version = this.directives.yaml.version;
|
|
41
48
|
}
|
|
@@ -62,7 +69,8 @@ class Document {
|
|
|
62
69
|
copy.errors = this.errors.slice();
|
|
63
70
|
copy.warnings = this.warnings.slice();
|
|
64
71
|
copy.options = Object.assign({}, this.options);
|
|
65
|
-
|
|
72
|
+
if (this.directives)
|
|
73
|
+
copy.directives = this.directives.clone();
|
|
66
74
|
copy.schema = this.schema.clone();
|
|
67
75
|
copy.contents = isNode(this.contents)
|
|
68
76
|
? this.contents.clone(copy.schema)
|
|
@@ -94,6 +102,7 @@ class Document {
|
|
|
94
102
|
if (!node.anchor) {
|
|
95
103
|
const prev = anchorNames(this);
|
|
96
104
|
node.anchor =
|
|
105
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
97
106
|
!name || prev.has(name) ? findNewAnchor(name || 'a', prev) : name;
|
|
98
107
|
}
|
|
99
108
|
return new Alias(node.anchor);
|
|
@@ -115,11 +124,13 @@ class Document {
|
|
|
115
124
|
options = replacer;
|
|
116
125
|
replacer = undefined;
|
|
117
126
|
}
|
|
118
|
-
const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options
|
|
119
|
-
const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this,
|
|
127
|
+
const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options ?? {};
|
|
128
|
+
const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this,
|
|
129
|
+
// eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
|
|
130
|
+
anchorPrefix || 'a');
|
|
120
131
|
const ctx = {
|
|
121
|
-
aliasDuplicateObjects: aliasDuplicateObjects
|
|
122
|
-
keepUndefined: keepUndefined
|
|
132
|
+
aliasDuplicateObjects: aliasDuplicateObjects ?? true,
|
|
133
|
+
keepUndefined: keepUndefined ?? false,
|
|
123
134
|
onAnchor,
|
|
124
135
|
onTagObj,
|
|
125
136
|
replacer: _replacer,
|
|
@@ -229,26 +240,48 @@ class Document {
|
|
|
229
240
|
}
|
|
230
241
|
/**
|
|
231
242
|
* Change the YAML version and schema used by the document.
|
|
243
|
+
* A `null` version disables support for directives, explicit tags, anchors, and aliases.
|
|
244
|
+
* It also requires the `schema` option to be given as a `Schema` instance value.
|
|
232
245
|
*
|
|
233
|
-
* Overrides all previously set schema options
|
|
246
|
+
* Overrides all previously set schema options.
|
|
234
247
|
*/
|
|
235
|
-
setSchema(version, options) {
|
|
236
|
-
|
|
237
|
-
|
|
248
|
+
setSchema(version, options = {}) {
|
|
249
|
+
if (typeof version === 'number')
|
|
250
|
+
version = String(version);
|
|
251
|
+
let opt;
|
|
252
|
+
switch (version) {
|
|
238
253
|
case '1.1':
|
|
239
|
-
this.directives
|
|
240
|
-
|
|
254
|
+
if (this.directives)
|
|
255
|
+
this.directives.yaml.version = '1.1';
|
|
256
|
+
else
|
|
257
|
+
this.directives = new Directives({ version: '1.1' });
|
|
258
|
+
opt = { merge: true, resolveKnownTags: false, schema: 'yaml-1.1' };
|
|
241
259
|
break;
|
|
242
260
|
case '1.2':
|
|
243
|
-
|
|
244
|
-
|
|
261
|
+
case 'next':
|
|
262
|
+
if (this.directives)
|
|
263
|
+
this.directives.yaml.version = version;
|
|
264
|
+
else
|
|
265
|
+
this.directives = new Directives({ version });
|
|
266
|
+
opt = { merge: false, resolveKnownTags: true, schema: 'core' };
|
|
267
|
+
break;
|
|
268
|
+
case null:
|
|
269
|
+
if (this.directives)
|
|
270
|
+
delete this.directives;
|
|
271
|
+
opt = null;
|
|
245
272
|
break;
|
|
246
273
|
default: {
|
|
247
274
|
const sv = JSON.stringify(version);
|
|
248
|
-
throw new Error(`Expected '1.1'
|
|
275
|
+
throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`);
|
|
249
276
|
}
|
|
250
277
|
}
|
|
251
|
-
|
|
278
|
+
// Not using `instanceof Schema` to allow for duck typing
|
|
279
|
+
if (options.schema instanceof Object)
|
|
280
|
+
this.schema = options.schema;
|
|
281
|
+
else if (opt)
|
|
282
|
+
this.schema = new Schema(Object.assign(opt, options));
|
|
283
|
+
else
|
|
284
|
+
throw new Error(`With a null YAML version, the { schema: Schema } option is required`);
|
|
252
285
|
}
|
|
253
286
|
// json & jsonArg are only used from toJSON()
|
|
254
287
|
toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
|
|
@@ -261,7 +294,7 @@ class Document {
|
|
|
261
294
|
maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100,
|
|
262
295
|
stringify
|
|
263
296
|
};
|
|
264
|
-
const res = toJS(this.contents, jsonArg
|
|
297
|
+
const res = toJS(this.contents, jsonArg ?? '', ctx);
|
|
265
298
|
if (typeof onAnchor === 'function')
|
|
266
299
|
for (const { count, res } of ctx.anchors.values())
|
|
267
300
|
onAnchor(res, count);
|
|
@@ -37,7 +37,7 @@ function createNodeAnchors(doc, prefix) {
|
|
|
37
37
|
const sourceObjects = new Map();
|
|
38
38
|
let prevAnchors = null;
|
|
39
39
|
return {
|
|
40
|
-
onAnchor(source) {
|
|
40
|
+
onAnchor: (source) => {
|
|
41
41
|
aliasObjects.push(source);
|
|
42
42
|
if (!prevAnchors)
|
|
43
43
|
prevAnchors = anchorNames(doc);
|
|
@@ -50,7 +50,7 @@ function createNodeAnchors(doc, prefix) {
|
|
|
50
50
|
* of its child nodes are. This is why anchors are set only after all of
|
|
51
51
|
* the nodes have been created.
|
|
52
52
|
*/
|
|
53
|
-
setAnchors() {
|
|
53
|
+
setAnchors: () => {
|
|
54
54
|
for (const source of aliasObjects) {
|
|
55
55
|
const ref = sourceObjects.get(source);
|
|
56
56
|
if (typeof ref === 'object' &&
|