yaml 2.0.0-7 → 2.0.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.
Files changed (123) hide show
  1. package/README.md +1 -1
  2. package/browser/dist/compose/compose-doc.js +3 -2
  3. package/browser/dist/compose/compose-node.js +14 -3
  4. package/browser/dist/compose/compose-scalar.js +17 -10
  5. package/browser/dist/compose/composer.js +6 -7
  6. package/browser/dist/compose/resolve-block-map.js +19 -5
  7. package/browser/dist/compose/resolve-block-scalar.js +12 -3
  8. package/browser/dist/compose/resolve-block-seq.js +5 -0
  9. package/browser/dist/compose/resolve-flow-collection.js +18 -6
  10. package/browser/dist/compose/resolve-flow-scalar.js +9 -2
  11. package/browser/dist/compose/resolve-props.js +4 -0
  12. package/browser/dist/compose/util-flow-indent-check.js +15 -0
  13. package/browser/dist/doc/Document.js +73 -16
  14. package/browser/dist/doc/anchors.js +2 -2
  15. package/browser/dist/doc/createNode.js +12 -7
  16. package/browser/dist/doc/directives.js +11 -3
  17. package/browser/dist/index.js +1 -2
  18. package/browser/dist/node_modules/tslib/tslib.es6.js +164 -0
  19. package/browser/dist/nodes/Collection.js +28 -15
  20. package/browser/dist/nodes/Node.js +7 -0
  21. package/browser/dist/nodes/Pair.js +11 -3
  22. package/browser/dist/nodes/Scalar.js +1 -1
  23. package/browser/dist/nodes/YAMLMap.js +6 -5
  24. package/browser/dist/nodes/YAMLSeq.js +2 -2
  25. package/browser/dist/nodes/addPairToJSMap.js +3 -2
  26. package/browser/dist/nodes/toJS.js +3 -1
  27. package/browser/dist/parse/cst-scalar.js +2 -6
  28. package/browser/dist/parse/cst-visit.js +2 -2
  29. package/browser/dist/parse/lexer.js +50 -22
  30. package/browser/dist/parse/parser.js +121 -45
  31. package/browser/dist/public-api.js +4 -5
  32. package/browser/dist/schema/Schema.js +19 -4
  33. package/browser/dist/schema/common/null.js +3 -1
  34. package/browser/dist/schema/core/float.js +4 -1
  35. package/browser/dist/schema/tags.js +17 -13
  36. package/browser/dist/schema/yaml-1.1/float.js +4 -1
  37. package/browser/dist/schema/yaml-1.1/omap.js +1 -1
  38. package/browser/dist/schema/yaml-1.1/pairs.js +2 -1
  39. package/browser/dist/stringify/stringify.js +53 -23
  40. package/browser/dist/stringify/stringifyCollection.js +115 -85
  41. package/browser/dist/stringify/stringifyComment.js +18 -12
  42. package/browser/dist/stringify/stringifyDocument.js +37 -15
  43. package/browser/dist/stringify/stringifyPair.js +35 -21
  44. package/browser/dist/stringify/stringifyString.js +41 -43
  45. package/browser/dist/util.js +3 -0
  46. package/browser/dist/visit.js +150 -62
  47. package/dist/compose/compose-doc.js +3 -2
  48. package/dist/compose/compose-node.d.ts +1 -0
  49. package/dist/compose/compose-node.js +14 -3
  50. package/dist/compose/compose-scalar.js +16 -9
  51. package/dist/compose/composer.d.ts +1 -1
  52. package/dist/compose/composer.js +8 -9
  53. package/dist/compose/resolve-block-map.js +19 -5
  54. package/dist/compose/resolve-block-scalar.js +12 -3
  55. package/dist/compose/resolve-block-seq.js +5 -0
  56. package/dist/compose/resolve-flow-collection.js +18 -6
  57. package/dist/compose/resolve-flow-scalar.js +9 -2
  58. package/dist/compose/resolve-props.d.ts +1 -1
  59. package/dist/compose/resolve-props.js +4 -0
  60. package/dist/compose/util-flow-indent-check.d.ts +3 -0
  61. package/dist/compose/util-flow-indent-check.js +17 -0
  62. package/dist/doc/Document.d.ts +13 -4
  63. package/dist/doc/Document.js +79 -22
  64. package/dist/doc/anchors.d.ts +2 -2
  65. package/dist/doc/anchors.js +2 -2
  66. package/dist/doc/createNode.d.ts +3 -2
  67. package/dist/doc/createNode.js +11 -6
  68. package/dist/doc/directives.d.ts +5 -2
  69. package/dist/doc/directives.js +11 -3
  70. package/dist/index.d.ts +4 -4
  71. package/dist/index.js +1 -2
  72. package/dist/node_modules/tslib/tslib.es6.js +76 -0
  73. package/dist/nodes/Alias.d.ts +7 -3
  74. package/dist/nodes/Collection.d.ts +10 -4
  75. package/dist/nodes/Collection.js +27 -14
  76. package/dist/nodes/Node.d.ts +6 -1
  77. package/dist/nodes/Node.js +7 -0
  78. package/dist/nodes/Pair.d.ts +7 -2
  79. package/dist/nodes/Pair.js +10 -2
  80. package/dist/nodes/Scalar.d.ts +2 -0
  81. package/dist/nodes/Scalar.js +1 -1
  82. package/dist/nodes/YAMLMap.d.ts +5 -1
  83. package/dist/nodes/YAMLMap.js +6 -5
  84. package/dist/nodes/YAMLSeq.d.ts +2 -0
  85. package/dist/nodes/YAMLSeq.js +2 -2
  86. package/dist/nodes/addPairToJSMap.js +2 -1
  87. package/dist/nodes/toJS.js +3 -1
  88. package/dist/options.d.ts +67 -15
  89. package/dist/parse/cst-scalar.d.ts +6 -0
  90. package/dist/parse/cst-scalar.js +2 -6
  91. package/dist/parse/cst-visit.js +2 -2
  92. package/dist/parse/lexer.d.ts +2 -0
  93. package/dist/parse/lexer.js +50 -22
  94. package/dist/parse/parser.js +121 -45
  95. package/dist/public-api.js +4 -5
  96. package/dist/schema/Schema.d.ts +6 -4
  97. package/dist/schema/Schema.js +18 -3
  98. package/dist/schema/common/null.js +3 -1
  99. package/dist/schema/core/float.js +4 -1
  100. package/dist/schema/tags.d.ts +1 -2
  101. package/dist/schema/tags.js +17 -13
  102. package/dist/schema/yaml-1.1/float.js +4 -1
  103. package/dist/schema/yaml-1.1/omap.js +1 -1
  104. package/dist/schema/yaml-1.1/pairs.js +2 -1
  105. package/dist/stringify/stringify.d.ts +5 -3
  106. package/dist/stringify/stringify.js +53 -23
  107. package/dist/stringify/stringifyCollection.d.ts +2 -6
  108. package/dist/stringify/stringifyCollection.js +114 -84
  109. package/dist/stringify/stringifyComment.d.ts +10 -2
  110. package/dist/stringify/stringifyComment.js +19 -12
  111. package/dist/stringify/stringifyDocument.js +36 -14
  112. package/dist/stringify/stringifyPair.js +34 -20
  113. package/dist/stringify/stringifyString.js +41 -43
  114. package/dist/test-events.d.ts +1 -1
  115. package/dist/test-events.js +13 -12
  116. package/dist/util.d.ts +3 -0
  117. package/dist/util.js +6 -0
  118. package/dist/visit.d.ts +49 -10
  119. package/dist/visit.js +149 -60
  120. package/package.json +12 -10
  121. package/util.d.ts +3 -0
  122. package/browser/dist/options.js +0 -17
  123. package/dist/options.js +0 -19
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 --save-exact yaml@next
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.
@@ -7,19 +7,20 @@ function composeDoc(options, directives, { offset, start, value, end }, onError)
7
7
  const opts = Object.assign({ 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 || (end === null || end === void 0 ? void 0 : end[0]),
17
+ next: value !== null && value !== void 0 ? value : end === null || end === void 0 ? void 0 : end[0],
17
18
  offset,
18
19
  onError,
19
20
  startOnNewline: true
20
21
  });
21
22
  if (props.found) {
22
- doc.directives.marker = true;
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
- console.log(token);
34
- throw new Error(`Unsupporten token type: ${token.type}`);
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,6 +49,9 @@ function composeNode(ctx, token, props, onError) {
43
49
  else
44
50
  node.commentBefore = comment;
45
51
  }
52
+ // @ts-expect-error Type checking misses meaning of isSrcToken
53
+ if (ctx.options.keepSourceTokens && isSrcToken)
54
+ node.srcToken = token;
46
55
  return node;
47
56
  }
48
57
  function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag }, onError) {
@@ -68,6 +77,8 @@ function composeAlias({ options }, { offset, source, end }, onError) {
68
77
  const alias = new Alias(source.substring(1));
69
78
  if (alias.source === '')
70
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);
71
82
  const valueEnd = offset + source.length;
72
83
  const re = resolveEnd(end, valueEnd, options.strict, onError);
73
84
  alias.range = [offset, valueEnd, re.offset];
@@ -1,4 +1,4 @@
1
- import { isScalar, SCALAR } from '../nodes/Node.js';
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,14 +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
- : findScalarTagByTest(ctx.schema, value, token.type === 'scalar');
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 || token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
20
+ const res = tag.resolve(value, msg => onError(tagToken !== null && tagToken !== void 0 ? tagToken : token, 'TAG_RESOLVE_FAILED', msg), ctx.options);
19
21
  scalar = isScalar(res) ? res : new Scalar(res);
20
22
  }
21
23
  catch (error) {
22
- onError(tagToken || token, 'TAG_RESOLVE_FAILED', error.message);
24
+ const msg = error instanceof Error ? error.message : String(error);
25
+ onError(tagToken !== null && tagToken !== void 0 ? tagToken : token, 'TAG_RESOLVE_FAILED', msg);
23
26
  scalar = new Scalar(value);
24
27
  }
25
28
  scalar.range = range;
@@ -60,15 +63,19 @@ function findScalarTagByName(schema, value, tagName, tagToken, onError) {
60
63
  onError(tagToken, 'TAG_RESOLVE_FAILED', `Unresolved tag: ${tagName}`, tagName !== 'tag:yaml.org,2002:str');
61
64
  return schema[SCALAR];
62
65
  }
63
- function findScalarTagByTest(schema, value, apply) {
66
+ function findScalarTagByTest({ directives, schema }, value, token, onError) {
64
67
  var _a;
65
- if (apply) {
66
- for (const tag of schema.tags) {
67
- if (tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)))
68
- return tag;
68
+ const tag = schema.tags.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); }) || schema[SCALAR];
69
+ if (schema.compat) {
70
+ const compat = (_a = schema.compat.find(tag => { var _a; return tag.default && ((_a = tag.test) === null || _a === void 0 ? void 0 : _a.test(value)); })) !== null && _a !== void 0 ? _a : schema[SCALAR];
71
+ if (tag.tag !== compat.tag) {
72
+ const ts = directives.tagString(tag.tag);
73
+ const cs = directives.tagString(compat.tag);
74
+ const msg = `Value may be parsed as either ${ts} or ${cs}`;
75
+ onError(token, 'TAG_RESOLVE_FAILED', msg, true);
69
76
  }
70
77
  }
71
- return schema[SCALAR];
78
+ return tag;
72
79
  }
73
80
 
74
81
  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
 
@@ -68,9 +67,8 @@ class Composer {
68
67
  else
69
68
  this.errors.push(new YAMLParseError(pos, code, message));
70
69
  };
71
- this.directives = new Directives({
72
- version: options.version || defaultOptions.version
73
- });
70
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
71
+ this.directives = new Directives({ version: options.version || '1.2' });
74
72
  this.options = options;
75
73
  }
76
74
  decorate(doc, afterDoc) {
@@ -81,7 +79,7 @@ class Composer {
81
79
  if (afterDoc) {
82
80
  doc.comment = doc.comment ? `${doc.comment}\n${comment}` : comment;
83
81
  }
84
- else if (afterEmptyLine || doc.directives.marker || !dc) {
82
+ else if (afterEmptyLine || doc.directives.docStart || !dc) {
85
83
  doc.commentBefore = comment;
86
84
  }
87
85
  else if (isCollection(dc) && !dc.flow && dc.items.length > 0) {
@@ -146,8 +144,8 @@ class Composer {
146
144
  break;
147
145
  case 'document': {
148
146
  const doc = composeDoc(this.options, this.directives, token, this.onError);
149
- if (this.atDirectives && !doc.directives.marker)
150
- this.onError(token, 'MISSING_CHAR', 'Missing directives-end indicator line');
147
+ if (this.atDirectives && !doc.directives.docStart)
148
+ this.onError(token, 'MISSING_CHAR', 'Missing directives-end/doc-start indicator line');
151
149
  this.decorate(doc, false);
152
150
  if (this.doc)
153
151
  yield this.doc;
@@ -179,6 +177,7 @@ class Composer {
179
177
  this.errors.push(new YAMLParseError(getErrorPos(token), 'UNEXPECTED_TOKEN', msg));
180
178
  break;
181
179
  }
180
+ this.doc.directives.docEnd = true;
182
181
  const end = resolveEnd(token.end, token.offset + token.source.length, this.doc.options.strict, this.onError);
183
182
  this.decorate(this.doc, true);
184
183
  if (end.comment) {
@@ -2,18 +2,22 @@ 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
10
  var _a;
10
11
  const map = new YAMLMap(ctx.schema);
12
+ if (ctx.atRoot)
13
+ ctx.atRoot = false;
11
14
  let offset = bm.offset;
12
- for (const { start, key, sep, value } of bm.items) {
15
+ for (const collItem of bm.items) {
16
+ const { start, key, sep, value } = collItem;
13
17
  // key properties
14
18
  const keyProps = resolveProps(start, {
15
19
  indicator: 'explicit-key-ind',
16
- next: key || (sep === null || sep === void 0 ? void 0 : sep[0]),
20
+ next: key !== null && key !== void 0 ? key : sep === null || sep === void 0 ? void 0 : sep[0],
17
21
  offset,
18
22
  onError,
19
23
  startOnNewline: true
@@ -47,10 +51,12 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
47
51
  const keyNode = key
48
52
  ? composeNode(ctx, key, keyProps, onError)
49
53
  : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError);
54
+ if (ctx.schema.compat)
55
+ flowIndentCheck(bm.indent, key, onError);
50
56
  if (mapIncludes(ctx, map.items, keyNode))
51
57
  onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');
52
58
  // value properties
53
- const valueProps = resolveProps(sep || [], {
59
+ const valueProps = resolveProps(sep !== null && sep !== void 0 ? sep : [], {
54
60
  indicator: 'map-value-ind',
55
61
  next: value,
56
62
  offset: keyNode.range[2],
@@ -70,8 +76,13 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
70
76
  const valueNode = value
71
77
  ? composeNode(ctx, value, valueProps, onError)
72
78
  : composeEmptyNode(ctx, offset, sep, null, valueProps, onError);
79
+ if (ctx.schema.compat)
80
+ flowIndentCheck(bm.indent, value, onError);
73
81
  offset = valueNode.range[2];
74
- map.items.push(new Pair(keyNode, valueNode));
82
+ const pair = new Pair(keyNode, valueNode);
83
+ if (ctx.options.keepSourceTokens)
84
+ pair.srcToken = collItem;
85
+ map.items.push(pair);
75
86
  }
76
87
  else {
77
88
  // key with no value
@@ -83,7 +94,10 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
83
94
  else
84
95
  keyNode.comment = valueProps.comment;
85
96
  }
86
- map.items.push(new Pair(keyNode));
97
+ const pair = new Pair(keyNode);
98
+ if (ctx.options.keepSourceTokens)
99
+ pair.srcToken = collItem;
100
+ map.items.push(pair);
87
101
  }
88
102
  }
89
103
  map.range = [bm.offset, offset, offset];
@@ -17,8 +17,10 @@ function resolveBlockScalar(scalar, strict, onError) {
17
17
  break;
18
18
  }
19
19
  // shortcut for empty contents
20
- if (!scalar.source || chompStart === 0) {
21
- const value = header.chomp === '+' ? lines.map(line => line[0]).join('\n') : '';
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 && m[1] ? [m[1], first.slice(m[1].length)] : ['', first];
185
+ const line0 = (m === null || m === void 0 ? void 0 : 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
  }
@@ -10,19 +10,24 @@ import { mapIncludes } from './util-map-includes.js';
10
10
  const blockMsg = 'Block collections are not allowed within flow collections';
11
11
  const isBlock = (token) => token && (token.type === 'block-map' || token.type === 'block-seq');
12
12
  function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onError) {
13
+ var _a;
13
14
  const isMap = fc.start.source === '{';
14
15
  const fcName = isMap ? 'flow map' : 'flow sequence';
15
16
  const coll = isMap
16
17
  ? new YAMLMap(ctx.schema)
17
18
  : new YAMLSeq(ctx.schema);
18
19
  coll.flow = true;
19
- let offset = fc.offset;
20
+ const atRoot = ctx.atRoot;
21
+ if (atRoot)
22
+ ctx.atRoot = false;
23
+ let offset = fc.offset + fc.start.source.length;
20
24
  for (let i = 0; i < fc.items.length; ++i) {
21
- const { start, key, sep, value } = fc.items[i];
25
+ const collItem = fc.items[i];
26
+ const { start, key, sep, value } = collItem;
22
27
  const props = resolveProps(start, {
23
28
  flow: fcName,
24
29
  indicator: 'explicit-key-ind',
25
- next: key || (sep === null || sep === void 0 ? void 0 : sep[0]),
30
+ next: key !== null && key !== void 0 ? key : sep === null || sep === void 0 ? void 0 : sep[0],
26
31
  offset,
27
32
  onError,
28
33
  startOnNewline: false
@@ -39,6 +44,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
39
44
  else
40
45
  coll.comment = props.comment;
41
46
  }
47
+ offset = props.end;
42
48
  continue;
43
49
  }
44
50
  if (!isMap && ctx.options.strict && containsNewline(key))
@@ -69,7 +75,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
69
75
  if (prevItemComment) {
70
76
  let prev = coll.items[coll.items.length - 1];
71
77
  if (isPair(prev))
72
- prev = prev.value || prev.key;
78
+ prev = (_a = prev.value) !== null && _a !== void 0 ? _a : prev.key;
73
79
  if (prev.comment)
74
80
  prev.comment += '\n' + prevItemComment;
75
81
  else
@@ -99,7 +105,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
99
105
  if (isBlock(key))
100
106
  onError(keyNode.range, 'BLOCK_IN_FLOW', blockMsg);
101
107
  // value properties
102
- const valueProps = resolveProps(sep || [], {
108
+ const valueProps = resolveProps(sep !== null && sep !== void 0 ? sep : [], {
103
109
  flow: fcName,
104
110
  indicator: 'map-value-ind',
105
111
  next: value,
@@ -145,6 +151,8 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
145
151
  keyNode.comment = valueProps.comment;
146
152
  }
147
153
  const pair = new Pair(keyNode, valueNode);
154
+ if (ctx.options.keepSourceTokens)
155
+ pair.srcToken = collItem;
148
156
  if (isMap) {
149
157
  const map = coll;
150
158
  if (mapIncludes(ctx, map.items, keyNode))
@@ -166,7 +174,11 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
166
174
  if (ce && ce.source === expectedEnd)
167
175
  cePos = ce.offset + ce.source.length;
168
176
  else {
169
- onError(offset + 1, 'MISSING_CHAR', `Expected ${fcName} to end with ${expectedEnd}`);
177
+ const name = fcName[0].toUpperCase() + fcName.substring(1);
178
+ const msg = atRoot
179
+ ? `${name} must end with a ${expectedEnd}`
180
+ : `${name} in block collection must be sufficiently indented and end with a ${expectedEnd}`;
181
+ onError(offset, atRoot ? 'MISSING_CHAR' : 'BAD_INDENT', msg);
170
182
  if (ce && ce.source.length !== 1)
171
183
  ee.unshift(ce);
172
184
  }
@@ -72,6 +72,7 @@ function singleQuotedValue(source, onError) {
72
72
  return foldLines(source.slice(1, -1)).replace(/''/g, "'");
73
73
  }
74
74
  function foldLines(source) {
75
+ var _a;
75
76
  /**
76
77
  * The negative lookbehind here and in the `re` RegExp is to
77
78
  * prevent causing a polynomial search time in certain cases.
@@ -111,7 +112,7 @@ function foldLines(source) {
111
112
  const last = /[ \t]*(.*)/sy;
112
113
  last.lastIndex = pos;
113
114
  match = last.exec(source);
114
- return res + sep + ((match && match[1]) || '');
115
+ return res + sep + ((_a = match === null || match === void 0 ? void 0 : match[1]) !== null && _a !== void 0 ? _a : '');
115
116
  }
116
117
  function doubleQuotedValue(source, onError) {
117
118
  let res = '';
@@ -135,6 +136,12 @@ function doubleQuotedValue(source, onError) {
135
136
  while (next === ' ' || next === '\t')
136
137
  next = source[++i + 1];
137
138
  }
139
+ else if (next === '\r' && source[i + 1] === '\n') {
140
+ // skip escaped CRLF newlines, but still trim the following line
141
+ next = source[++i + 1];
142
+ while (next === ' ' || next === '\t')
143
+ next = source[++i + 1];
144
+ }
138
145
  else if (next === 'x' || next === 'u' || next === 'U') {
139
146
  const length = { x: 2, u: 4, U: 8 }[next];
140
147
  res += parseCharCode(source, i + 1, length, onError);
@@ -152,7 +159,7 @@ function doubleQuotedValue(source, onError) {
152
159
  let next = source[i + 1];
153
160
  while (next === ' ' || next === '\t')
154
161
  next = source[++i + 1];
155
- if (next !== '\n')
162
+ if (next !== '\n' && !(next === '\r' && source[i + 2] === '\n'))
156
163
  res += i > wsStart ? source.slice(wsStart, i + 1) : ch;
157
164
  }
158
165
  else {
@@ -59,6 +59,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
59
59
  case 'anchor':
60
60
  if (anchor)
61
61
  onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
62
+ if (token.source.endsWith(':'))
63
+ onError(token.offset + token.source.length - 1, 'BAD_ALIAS', 'Anchor ending in : is ambiguous', true);
62
64
  anchor = token;
63
65
  if (start === null)
64
66
  start = token.offset;
@@ -81,6 +83,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
81
83
  // Could here handle preceding comments differently
82
84
  if (anchor || tag)
83
85
  onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
86
+ if (found)
87
+ onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow !== null && flow !== void 0 ? flow : 'collection'}`);
84
88
  found = token;
85
89
  atNewline = false;
86
90
  hasSpace = false;
@@ -0,0 +1,15 @@
1
+ import { containsNewline } from './util-contains-newline.js';
2
+
3
+ function flowIndentCheck(indent, fc, onError) {
4
+ if ((fc === null || fc === void 0 ? void 0 : 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 };
@@ -1,9 +1,8 @@
1
1
  import { Alias } from '../nodes/Alias.js';
2
2
  import { isEmptyPath, collectionFromPath } from '../nodes/Collection.js';
3
- import { NODE_TYPE, DOC, isCollection, isScalar } from '../nodes/Node.js';
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,7 +30,15 @@ class Document {
31
30
  options = replacer;
32
31
  replacer = undefined;
33
32
  }
34
- const opt = Object.assign({}, defaultOptions, options);
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
44
  if (options === null || options === void 0 ? void 0 : options.directives) {
@@ -48,6 +55,30 @@ class Document {
48
55
  this.contents = this.createNode(value, _replacer, options);
49
56
  }
50
57
  }
58
+ /**
59
+ * Create a deep copy of this Document and its contents.
60
+ *
61
+ * Custom Node values that inherit from `Object` still refer to their original instances.
62
+ */
63
+ clone() {
64
+ const copy = Object.create(Document.prototype, {
65
+ [NODE_TYPE]: { value: DOC }
66
+ });
67
+ copy.commentBefore = this.commentBefore;
68
+ copy.comment = this.comment;
69
+ copy.errors = this.errors.slice();
70
+ copy.warnings = this.warnings.slice();
71
+ copy.options = Object.assign({}, this.options);
72
+ if (this.directives)
73
+ copy.directives = this.directives.clone();
74
+ copy.schema = this.schema.clone();
75
+ copy.contents = isNode(this.contents)
76
+ ? this.contents.clone(copy.schema)
77
+ : this.contents;
78
+ if (this.range)
79
+ copy.range = this.range.slice();
80
+ return copy;
81
+ }
51
82
  /** Adds a value to the document. */
52
83
  add(value) {
53
84
  if (assertCollection(this.contents))
@@ -71,6 +102,7 @@ class Document {
71
102
  if (!node.anchor) {
72
103
  const prev = anchorNames(this);
73
104
  node.anchor =
105
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
74
106
  !name || prev.has(name) ? findNewAnchor(name || 'a', prev) : name;
75
107
  }
76
108
  return new Alias(node.anchor);
@@ -92,9 +124,12 @@ class Document {
92
124
  options = replacer;
93
125
  replacer = undefined;
94
126
  }
95
- const { anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
96
- const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this, anchorPrefix || 'a');
127
+ const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options !== null && options !== void 0 ? options : {};
128
+ const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this,
129
+ // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
130
+ anchorPrefix || 'a');
97
131
  const ctx = {
132
+ aliasDuplicateObjects: aliasDuplicateObjects !== null && aliasDuplicateObjects !== void 0 ? aliasDuplicateObjects : true,
98
133
  keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
99
134
  onAnchor,
100
135
  onTagObj,
@@ -205,26 +240,48 @@ class Document {
205
240
  }
206
241
  /**
207
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.
208
245
  *
209
- * Overrides all previously set schema options
246
+ * Overrides all previously set schema options.
210
247
  */
211
- setSchema(version, options) {
212
- let _options;
213
- switch (String(version)) {
248
+ setSchema(version, options = {}) {
249
+ if (typeof version === 'number')
250
+ version = String(version);
251
+ let opt;
252
+ switch (version) {
214
253
  case '1.1':
215
- this.directives.yaml.version = '1.1';
216
- _options = Object.assign({ merge: true, resolveKnownTags: false, schema: 'yaml-1.1' }, options);
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' };
217
259
  break;
218
260
  case '1.2':
219
- this.directives.yaml.version = '1.2';
220
- _options = Object.assign({ merge: false, resolveKnownTags: true, schema: 'core' }, options);
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;
221
272
  break;
222
273
  default: {
223
274
  const sv = JSON.stringify(version);
224
- throw new Error(`Expected '1.1' or '1.2' as version, but found: ${sv}`);
275
+ throw new Error(`Expected '1.1', '1.2' or null as first argument, but found: ${sv}`);
225
276
  }
226
277
  }
227
- this.schema = new Schema(_options);
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`);
228
285
  }
229
286
  // json & jsonArg are only used from toJSON()
230
287
  toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
@@ -237,7 +294,7 @@ class Document {
237
294
  maxAliasCount: typeof maxAliasCount === 'number' ? maxAliasCount : 100,
238
295
  stringify
239
296
  };
240
- const res = toJS(this.contents, jsonArg || '', ctx);
297
+ const res = toJS(this.contents, jsonArg !== null && jsonArg !== void 0 ? jsonArg : '', ctx);
241
298
  if (typeof onAnchor === 'function')
242
299
  for (const { count, res } of ctx.anchors.values())
243
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' &&