yaml 2.0.0-5 → 2.0.0-9

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 (94) hide show
  1. package/browser/dist/compose/compose-doc.js +1 -1
  2. package/browser/dist/compose/compose-node.js +10 -1
  3. package/browser/dist/compose/compose-scalar.js +2 -1
  4. package/browser/dist/compose/composer.js +3 -2
  5. package/browser/dist/compose/resolve-block-map.js +15 -5
  6. package/browser/dist/compose/resolve-block-scalar.js +2 -2
  7. package/browser/dist/compose/resolve-block-seq.js +1 -1
  8. package/browser/dist/compose/resolve-end.js +2 -2
  9. package/browser/dist/compose/resolve-flow-collection.js +14 -6
  10. package/browser/dist/compose/resolve-flow-scalar.js +19 -7
  11. package/browser/dist/compose/resolve-props.js +30 -7
  12. package/browser/dist/compose/util-map-includes.js +17 -0
  13. package/browser/dist/doc/Document.js +26 -2
  14. package/browser/dist/doc/createNode.js +11 -5
  15. package/browser/dist/doc/directives.js +5 -0
  16. package/browser/dist/node_modules/tslib/tslib.es6.js +164 -0
  17. package/browser/dist/nodes/Collection.js +28 -15
  18. package/browser/dist/nodes/Node.js +7 -0
  19. package/browser/dist/nodes/Pair.js +9 -1
  20. package/browser/dist/nodes/addPairToJSMap.js +4 -3
  21. package/browser/dist/options.js +2 -0
  22. package/browser/dist/parse/cst-scalar.js +2 -6
  23. package/browser/dist/parse/lexer.js +64 -31
  24. package/browser/dist/parse/parser.js +53 -52
  25. package/browser/dist/schema/Schema.js +5 -0
  26. package/browser/dist/schema/tags.js +9 -5
  27. package/browser/dist/stringify/stringify.js +2 -1
  28. package/browser/dist/stringify/stringifyCollection.js +23 -9
  29. package/browser/dist/stringify/stringifyComment.js +14 -0
  30. package/browser/dist/stringify/stringifyDocument.js +8 -5
  31. package/browser/dist/stringify/stringifyPair.js +5 -7
  32. package/browser/dist/stringify/stringifyString.js +35 -34
  33. package/browser/dist/util.js +3 -0
  34. package/dist/compose/compose-doc.js +1 -1
  35. package/dist/compose/compose-node.js +10 -1
  36. package/dist/compose/compose-scalar.js +2 -1
  37. package/dist/compose/composer.js +3 -2
  38. package/dist/compose/resolve-block-map.d.ts +2 -1
  39. package/dist/compose/resolve-block-map.js +15 -5
  40. package/dist/compose/resolve-block-scalar.js +2 -2
  41. package/dist/compose/resolve-block-seq.js +1 -1
  42. package/dist/compose/resolve-end.js +2 -2
  43. package/dist/compose/resolve-flow-collection.js +14 -6
  44. package/dist/compose/resolve-flow-scalar.js +19 -7
  45. package/dist/compose/resolve-props.d.ts +3 -4
  46. package/dist/compose/resolve-props.js +30 -7
  47. package/dist/compose/util-map-includes.d.ts +4 -0
  48. package/dist/compose/util-map-includes.js +19 -0
  49. package/dist/doc/Document.d.ts +10 -2
  50. package/dist/doc/Document.js +25 -1
  51. package/dist/doc/createNode.d.ts +2 -1
  52. package/dist/doc/createNode.js +10 -4
  53. package/dist/doc/directives.d.ts +1 -0
  54. package/dist/doc/directives.js +5 -0
  55. package/dist/errors.d.ts +1 -1
  56. package/dist/index.d.ts +2 -2
  57. package/dist/node_modules/tslib/tslib.es6.js +76 -0
  58. package/dist/nodes/Alias.d.ts +7 -3
  59. package/dist/nodes/Collection.d.ts +10 -4
  60. package/dist/nodes/Collection.js +27 -14
  61. package/dist/nodes/Node.d.ts +10 -3
  62. package/dist/nodes/Node.js +7 -0
  63. package/dist/nodes/Pair.d.ts +7 -2
  64. package/dist/nodes/Pair.js +8 -0
  65. package/dist/nodes/Scalar.d.ts +2 -0
  66. package/dist/nodes/YAMLMap.d.ts +2 -0
  67. package/dist/nodes/YAMLSeq.d.ts +2 -0
  68. package/dist/nodes/addPairToJSMap.js +3 -2
  69. package/dist/options.d.ts +40 -3
  70. package/dist/options.js +2 -0
  71. package/dist/parse/cst-scalar.d.ts +6 -0
  72. package/dist/parse/cst-scalar.js +2 -6
  73. package/dist/parse/lexer.d.ts +3 -1
  74. package/dist/parse/lexer.js +64 -31
  75. package/dist/parse/parser.d.ts +1 -0
  76. package/dist/parse/parser.js +53 -52
  77. package/dist/schema/Schema.d.ts +1 -0
  78. package/dist/schema/Schema.js +5 -0
  79. package/dist/schema/tags.js +9 -5
  80. package/dist/stringify/stringify.js +2 -1
  81. package/dist/stringify/stringifyCollection.d.ts +1 -1
  82. package/dist/stringify/stringifyCollection.js +24 -10
  83. package/dist/stringify/stringifyComment.d.ts +2 -0
  84. package/dist/stringify/{addComment.js → stringifyComment.js} +5 -1
  85. package/dist/stringify/stringifyDocument.js +9 -6
  86. package/dist/stringify/stringifyPair.js +9 -11
  87. package/dist/stringify/stringifyString.js +35 -34
  88. package/dist/test-events.d.ts +1 -1
  89. package/dist/util.d.ts +3 -0
  90. package/dist/util.js +6 -0
  91. package/package.json +9 -8
  92. package/util.d.ts +3 -0
  93. package/browser/dist/stringify/addComment.js +0 -11
  94. package/dist/stringify/addComment.d.ts +0 -2
@@ -12,8 +12,8 @@ function composeDoc(options, directives, { offset, start, value, end }, onError)
12
12
  schema: doc.schema
13
13
  };
14
14
  const props = resolveProps(start, {
15
- ctx,
16
15
  indicator: 'doc-start',
16
+ next: value || (end === null || end === void 0 ? void 0 : end[0]),
17
17
  offset,
18
18
  onError,
19
19
  startOnNewline: true
@@ -33,6 +33,8 @@ function composeNode(ctx, token, props, onError) {
33
33
  console.log(token);
34
34
  throw new Error(`Unsupporten token type: ${token.type}`);
35
35
  }
36
+ if (anchor && node.anchor === '')
37
+ onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');
36
38
  if (spaceBefore)
37
39
  node.spaceBefore = true;
38
40
  if (comment) {
@@ -41,6 +43,8 @@ function composeNode(ctx, token, props, onError) {
41
43
  else
42
44
  node.commentBefore = comment;
43
45
  }
46
+ if (ctx.options.keepSourceTokens)
47
+ node.srcToken = token;
44
48
  return node;
45
49
  }
46
50
  function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anchor, tag }, onError) {
@@ -51,8 +55,11 @@ function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anch
51
55
  source: ''
52
56
  };
53
57
  const node = composeScalar(ctx, token, tag, onError);
54
- if (anchor)
58
+ if (anchor) {
55
59
  node.anchor = anchor.source.substring(1);
60
+ if (node.anchor === '')
61
+ onError(anchor, 'BAD_ALIAS', 'Anchor cannot be an empty string');
62
+ }
56
63
  if (spaceBefore)
57
64
  node.spaceBefore = true;
58
65
  if (comment)
@@ -61,6 +68,8 @@ function composeEmptyNode(ctx, offset, before, pos, { spaceBefore, comment, anch
61
68
  }
62
69
  function composeAlias({ options }, { offset, source, end }, onError) {
63
70
  const alias = new Alias(source.substring(1));
71
+ if (alias.source === '')
72
+ onError(offset, 'BAD_ALIAS', 'Alias cannot be an empty string');
64
73
  const valueEnd = offset + source.length;
65
74
  const re = resolveEnd(end, valueEnd, options.strict, onError);
66
75
  alias.range = [offset, valueEnd, re.offset];
@@ -19,7 +19,8 @@ function composeScalar(ctx, token, tagToken, onError) {
19
19
  scalar = isScalar(res) ? res : new Scalar(res);
20
20
  }
21
21
  catch (error) {
22
- onError(tagToken || token, 'TAG_RESOLVE_FAILED', error.message);
22
+ const msg = error instanceof Error ? error.message : String(error);
23
+ onError(tagToken || token, 'TAG_RESOLVE_FAILED', msg);
23
24
  scalar = new Scalar(value);
24
25
  }
25
26
  scalar.range = range;
@@ -15,6 +15,7 @@ function getErrorPos(src) {
15
15
  return [offset, offset + (typeof source === 'string' ? source.length : 1)];
16
16
  }
17
17
  function parsePrelude(prelude) {
18
+ var _a;
18
19
  let comment = '';
19
20
  let atComment = false;
20
21
  let afterEmptyLine = false;
@@ -24,12 +25,12 @@ function parsePrelude(prelude) {
24
25
  case '#':
25
26
  comment +=
26
27
  (comment === '' ? '' : afterEmptyLine ? '\n\n' : '\n') +
27
- source.substring(1);
28
+ (source.substring(1) || ' ');
28
29
  atComment = true;
29
30
  afterEmptyLine = false;
30
31
  break;
31
32
  case '%':
32
- if (prelude[i + 1][0] !== '#')
33
+ if (((_a = prelude[i + 1]) === null || _a === void 0 ? void 0 : _a[0]) !== '#')
33
34
  i += 1;
34
35
  atComment = false;
35
36
  break;
@@ -2,17 +2,19 @@ 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 { mapIncludes } from './util-map-includes.js';
5
6
 
6
7
  const startColMsg = 'All mapping items must start at the same column';
7
8
  function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
8
9
  var _a;
9
10
  const map = new YAMLMap(ctx.schema);
10
11
  let offset = bm.offset;
11
- for (const { start, key, sep, value } of bm.items) {
12
+ for (const collItem of bm.items) {
13
+ const { start, key, sep, value } = collItem;
12
14
  // key properties
13
15
  const keyProps = resolveProps(start, {
14
- ctx,
15
16
  indicator: 'explicit-key-ind',
17
+ next: key || (sep === null || sep === void 0 ? void 0 : sep[0]),
16
18
  offset,
17
19
  onError,
18
20
  startOnNewline: true
@@ -46,10 +48,12 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
46
48
  const keyNode = key
47
49
  ? composeNode(ctx, key, keyProps, onError)
48
50
  : composeEmptyNode(ctx, keyStart, start, null, keyProps, onError);
51
+ if (mapIncludes(ctx, map.items, keyNode))
52
+ onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');
49
53
  // value properties
50
54
  const valueProps = resolveProps(sep || [], {
51
- ctx,
52
55
  indicator: 'map-value-ind',
56
+ next: value,
53
57
  offset: keyNode.range[2],
54
58
  onError,
55
59
  startOnNewline: !key || key.type === 'block-scalar'
@@ -68,7 +72,10 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
68
72
  ? composeNode(ctx, value, valueProps, onError)
69
73
  : composeEmptyNode(ctx, offset, sep, null, valueProps, onError);
70
74
  offset = valueNode.range[2];
71
- map.items.push(new Pair(keyNode, valueNode));
75
+ const pair = new Pair(keyNode, valueNode);
76
+ if (ctx.options.keepSourceTokens)
77
+ pair.srcToken = collItem;
78
+ map.items.push(pair);
72
79
  }
73
80
  else {
74
81
  // key with no value
@@ -80,7 +87,10 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError) {
80
87
  else
81
88
  keyNode.comment = valueProps.comment;
82
89
  }
83
- map.items.push(new Pair(keyNode));
90
+ const pair = new Pair(keyNode);
91
+ if (ctx.options.keepSourceTokens)
92
+ pair.srcToken = collItem;
93
+ map.items.push(pair);
84
94
  }
85
95
  }
86
96
  map.range = [bm.offset, offset, offset];
@@ -18,7 +18,7 @@ function resolveBlockScalar(scalar, strict, onError) {
18
18
  }
19
19
  // shortcut for empty contents
20
20
  if (!scalar.source || chompStart === 0) {
21
- const value = header.chomp === '+' ? lines.map(line => line[0]).join('\n') : '';
21
+ const value = header.chomp === '+' ? '\n'.repeat(Math.max(0, lines.length - 1)) : '';
22
22
  let end = start + header.length;
23
23
  if (scalar.source)
24
24
  end += scalar.source.length;
@@ -149,7 +149,7 @@ function parseBlockScalarHeader({ offset, props }, strict, onError) {
149
149
  case 'comment':
150
150
  if (strict && !hasSpace) {
151
151
  const message = 'Comments must be separated from other tokens by white space characters';
152
- onError(token, 'COMMENT_SPACE', message);
152
+ onError(token, 'MISSING_CHAR', message);
153
153
  }
154
154
  length += token.source.length;
155
155
  comment = token.source.substring(1);
@@ -6,8 +6,8 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError) {
6
6
  let offset = bs.offset;
7
7
  for (const { start, value } of bs.items) {
8
8
  const props = resolveProps(start, {
9
- ctx,
10
9
  indicator: 'seq-item-ind',
10
+ next: value,
11
11
  offset,
12
12
  onError,
13
13
  startOnNewline: true
@@ -11,8 +11,8 @@ function resolveEnd(end, offset, reqSpace, onError) {
11
11
  break;
12
12
  case 'comment': {
13
13
  if (reqSpace && !hasSpace)
14
- onError(token, 'COMMENT_SPACE', 'Comments must be separated from other tokens by white space characters');
15
- const cb = source.substring(1);
14
+ onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');
15
+ const cb = source.substring(1) || ' ';
16
16
  if (!comment)
17
17
  comment = cb;
18
18
  else
@@ -5,6 +5,7 @@ import { YAMLSeq } from '../nodes/YAMLSeq.js';
5
5
  import { resolveEnd } from './resolve-end.js';
6
6
  import { resolveProps } from './resolve-props.js';
7
7
  import { containsNewline } from './util-contains-newline.js';
8
+ import { mapIncludes } from './util-map-includes.js';
8
9
 
9
10
  const blockMsg = 'Block collections are not allowed within flow collections';
10
11
  const isBlock = (token) => token && (token.type === 'block-map' || token.type === 'block-seq');
@@ -17,11 +18,12 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
17
18
  coll.flow = true;
18
19
  let offset = fc.offset;
19
20
  for (let i = 0; i < fc.items.length; ++i) {
20
- const { start, key, sep, value } = fc.items[i];
21
+ const collItem = fc.items[i];
22
+ const { start, key, sep, value } = collItem;
21
23
  const props = resolveProps(start, {
22
- ctx,
23
24
  flow: fcName,
24
25
  indicator: 'explicit-key-ind',
26
+ next: key || (sep === null || sep === void 0 ? void 0 : sep[0]),
25
27
  offset,
26
28
  onError,
27
29
  startOnNewline: false
@@ -99,9 +101,9 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
99
101
  onError(keyNode.range, 'BLOCK_IN_FLOW', blockMsg);
100
102
  // value properties
101
103
  const valueProps = resolveProps(sep || [], {
102
- ctx,
103
104
  flow: fcName,
104
105
  indicator: 'map-value-ind',
106
+ next: value,
105
107
  offset: keyNode.range[2],
106
108
  onError,
107
109
  startOnNewline: false
@@ -144,8 +146,14 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
144
146
  keyNode.comment = valueProps.comment;
145
147
  }
146
148
  const pair = new Pair(keyNode, valueNode);
147
- if (isMap)
148
- coll.items.push(pair);
149
+ if (ctx.options.keepSourceTokens)
150
+ pair.srcToken = collItem;
151
+ if (isMap) {
152
+ const map = coll;
153
+ if (mapIncludes(ctx, map.items, keyNode))
154
+ onError(keyStart, 'DUPLICATE_KEY', 'Map keys must be unique');
155
+ map.items.push(pair);
156
+ }
149
157
  else {
150
158
  const map = new YAMLMap(ctx.schema);
151
159
  map.flow = true;
@@ -159,7 +167,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
159
167
  const [ce, ...ee] = fc.end;
160
168
  let cePos = offset;
161
169
  if (ce && ce.source === expectedEnd)
162
- cePos += ce.source.length;
170
+ cePos = ce.offset + ce.source.length;
163
171
  else {
164
172
  onError(offset + 1, 'MISSING_CHAR', `Expected ${fcName} to end with ${expectedEnd}`);
165
173
  if (ce && ce.source.length !== 1)
@@ -39,25 +39,31 @@ function resolveFlowScalar(scalar, strict, onError) {
39
39
  };
40
40
  }
41
41
  function plainValue(source, onError) {
42
- let message = '';
42
+ let badChar = '';
43
43
  switch (source[0]) {
44
44
  /* istanbul ignore next should not happen */
45
45
  case '\t':
46
- message = 'Plain value cannot start with a tab character';
46
+ badChar = 'a tab character';
47
+ break;
48
+ case ',':
49
+ badChar = 'flow indicator character ,';
50
+ break;
51
+ case '%':
52
+ badChar = 'directive indicator character %';
47
53
  break;
48
54
  case '|':
49
55
  case '>': {
50
- message = `Plain value cannot start with block scalar indicator ${source[0]}`;
56
+ badChar = `block scalar indicator ${source[0]}`;
51
57
  break;
52
58
  }
53
59
  case '@':
54
60
  case '`': {
55
- message = `Plain value cannot start with reserved character ${source[0]}`;
61
+ badChar = `reserved character ${source[0]}`;
56
62
  break;
57
63
  }
58
64
  }
59
- if (message)
60
- onError(0, 'BAD_SCALAR_START', message);
65
+ if (badChar)
66
+ onError(0, 'BAD_SCALAR_START', `Plain value cannot start with ${badChar}`);
61
67
  return foldLines(source);
62
68
  }
63
69
  function singleQuotedValue(source, onError) {
@@ -129,6 +135,12 @@ function doubleQuotedValue(source, onError) {
129
135
  while (next === ' ' || next === '\t')
130
136
  next = source[++i + 1];
131
137
  }
138
+ else if (next === '\r' && source[i + 1] === '\n') {
139
+ // skip escaped CRLF newlines, but still trim the following line
140
+ next = source[++i + 1];
141
+ while (next === ' ' || next === '\t')
142
+ next = source[++i + 1];
143
+ }
132
144
  else if (next === 'x' || next === 'u' || next === 'U') {
133
145
  const length = { x: 2, u: 4, U: 8 }[next];
134
146
  res += parseCharCode(source, i + 1, length, onError);
@@ -146,7 +158,7 @@ function doubleQuotedValue(source, onError) {
146
158
  let next = source[i + 1];
147
159
  while (next === ' ' || next === '\t')
148
160
  next = source[++i + 1];
149
- if (next !== '\n')
161
+ if (next !== '\n' && !(next === '\r' && source[i + 2] === '\n'))
150
162
  res += i > wsStart ? source.slice(wsStart, i + 1) : ch;
151
163
  }
152
164
  else {
@@ -1,16 +1,24 @@
1
- function resolveProps(tokens, { ctx, flow, indicator, offset, onError, startOnNewline }) {
1
+ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
2
2
  let spaceBefore = false;
3
3
  let atNewline = startOnNewline;
4
4
  let hasSpace = startOnNewline;
5
5
  let comment = '';
6
6
  let commentSep = '';
7
7
  let hasNewline = false;
8
+ let reqSpace = false;
8
9
  let anchor = null;
9
10
  let tag = null;
10
11
  let comma = null;
11
12
  let found = null;
12
13
  let start = null;
13
14
  for (const token of tokens) {
15
+ if (reqSpace) {
16
+ if (token.type !== 'space' &&
17
+ token.type !== 'newline' &&
18
+ token.type !== 'comma')
19
+ onError(token.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
20
+ reqSpace = false;
21
+ }
14
22
  switch (token.type) {
15
23
  case 'space':
16
24
  // At the doc level, tabs at line start may be parsed
@@ -24,23 +32,29 @@ function resolveProps(tokens, { ctx, flow, indicator, offset, onError, startOnNe
24
32
  hasSpace = true;
25
33
  break;
26
34
  case 'comment': {
27
- if (ctx.options.strict && !hasSpace)
28
- onError(token, 'COMMENT_SPACE', 'Comments must be separated from other tokens by white space characters');
29
- const cb = token.source.substring(1);
35
+ if (!hasSpace)
36
+ onError(token, 'MISSING_CHAR', 'Comments must be separated from other tokens by white space characters');
37
+ const cb = token.source.substring(1) || ' ';
30
38
  if (!comment)
31
39
  comment = cb;
32
40
  else
33
41
  comment += commentSep + cb;
34
42
  commentSep = '';
43
+ atNewline = false;
35
44
  break;
36
45
  }
37
46
  case 'newline':
38
- if (atNewline && !comment)
39
- spaceBefore = true;
47
+ if (atNewline) {
48
+ if (comment)
49
+ comment += token.source;
50
+ else
51
+ spaceBefore = true;
52
+ }
53
+ else
54
+ commentSep += token.source;
40
55
  atNewline = true;
41
56
  hasNewline = true;
42
57
  hasSpace = true;
43
- commentSep += token.source;
44
58
  break;
45
59
  case 'anchor':
46
60
  if (anchor)
@@ -50,6 +64,7 @@ function resolveProps(tokens, { ctx, flow, indicator, offset, onError, startOnNe
50
64
  start = token.offset;
51
65
  atNewline = false;
52
66
  hasSpace = false;
67
+ reqSpace = true;
53
68
  break;
54
69
  case 'tag': {
55
70
  if (tag)
@@ -59,6 +74,7 @@ function resolveProps(tokens, { ctx, flow, indicator, offset, onError, startOnNe
59
74
  start = token.offset;
60
75
  atNewline = false;
61
76
  hasSpace = false;
77
+ reqSpace = true;
62
78
  break;
63
79
  }
64
80
  case indicator:
@@ -87,6 +103,13 @@ function resolveProps(tokens, { ctx, flow, indicator, offset, onError, startOnNe
87
103
  }
88
104
  const last = tokens[tokens.length - 1];
89
105
  const end = last ? last.offset + last.source.length : offset;
106
+ if (reqSpace &&
107
+ next &&
108
+ next.type !== 'space' &&
109
+ next.type !== 'newline' &&
110
+ next.type !== 'comma' &&
111
+ (next.type !== 'scalar' || next.source !== ''))
112
+ onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
90
113
  return {
91
114
  comma,
92
115
  found,
@@ -0,0 +1,17 @@
1
+ import { isScalar } from '../nodes/Node.js';
2
+
3
+ function mapIncludes(ctx, items, search) {
4
+ const { uniqueKeys } = ctx.options;
5
+ if (uniqueKeys === false)
6
+ return false;
7
+ const isEqual = typeof uniqueKeys === 'function'
8
+ ? uniqueKeys
9
+ : (a, b) => a === b ||
10
+ (isScalar(a) &&
11
+ isScalar(b) &&
12
+ a.value === b.value &&
13
+ !(a.value === '<<' && ctx.schema.merge));
14
+ return items.some(pair => isEqual(pair.key, search));
15
+ }
16
+
17
+ export { mapIncludes };
@@ -1,6 +1,6 @@
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
6
  import { defaultOptions } from '../options.js';
@@ -48,6 +48,29 @@ class Document {
48
48
  this.contents = this.createNode(value, _replacer, options);
49
49
  }
50
50
  }
51
+ /**
52
+ * Create a deep copy of this Document and its contents.
53
+ *
54
+ * Custom Node values that inherit from `Object` still refer to their original instances.
55
+ */
56
+ clone() {
57
+ const copy = Object.create(Document.prototype, {
58
+ [NODE_TYPE]: { value: DOC }
59
+ });
60
+ copy.commentBefore = this.commentBefore;
61
+ copy.comment = this.comment;
62
+ copy.errors = this.errors.slice();
63
+ copy.warnings = this.warnings.slice();
64
+ copy.options = Object.assign({}, this.options);
65
+ copy.directives = this.directives.clone();
66
+ copy.schema = this.schema.clone();
67
+ copy.contents = isNode(this.contents)
68
+ ? this.contents.clone(copy.schema)
69
+ : this.contents;
70
+ if (this.range)
71
+ copy.range = this.range.slice();
72
+ return copy;
73
+ }
51
74
  /** Adds a value to the document. */
52
75
  add(value) {
53
76
  if (assertCollection(this.contents))
@@ -92,9 +115,10 @@ class Document {
92
115
  options = replacer;
93
116
  replacer = undefined;
94
117
  }
95
- const { anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
118
+ const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
96
119
  const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this, anchorPrefix || 'a');
97
120
  const ctx = {
121
+ aliasDuplicateObjects: aliasDuplicateObjects !== null && aliasDuplicateObjects !== void 0 ? aliasDuplicateObjects : true,
98
122
  keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
99
123
  onAnchor,
100
124
  onTagObj,
@@ -1,5 +1,5 @@
1
1
  import { Alias } from '../nodes/Alias.js';
2
- import { isNode, isPair, MAP, SEQ } from '../nodes/Node.js';
2
+ import { isNode, isPair, MAP, SEQ, isDocument } from '../nodes/Node.js';
3
3
  import { Scalar } from '../nodes/Scalar.js';
4
4
 
5
5
  const defaultTagPrefix = 'tag:yaml.org,2002:';
@@ -15,6 +15,8 @@ function findTagObject(value, tagName, tags) {
15
15
  }
16
16
  function createNode(value, tagName, ctx) {
17
17
  var _a, _b;
18
+ if (isDocument(value))
19
+ value = value.contents;
18
20
  if (isNode(value))
19
21
  return value;
20
22
  if (isPair(value)) {
@@ -30,11 +32,11 @@ function createNode(value, tagName, ctx) {
30
32
  // https://tc39.es/ecma262/#sec-serializejsonproperty
31
33
  value = value.valueOf();
32
34
  }
33
- const { onAnchor, onTagObj, schema, sourceObjects } = ctx;
35
+ const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx;
34
36
  // Detect duplicate references to the same object & use Alias nodes for all
35
37
  // after first. The `ref` wrapper allows for circular references to resolve.
36
38
  let ref = undefined;
37
- if (value && typeof value === 'object') {
39
+ if (aliasDuplicateObjects && value && typeof value === 'object') {
38
40
  ref = sourceObjects.get(value);
39
41
  if (ref) {
40
42
  if (!ref.anchor)
@@ -52,8 +54,12 @@ function createNode(value, tagName, ctx) {
52
54
  if (!tagObj) {
53
55
  if (value && typeof value.toJSON === 'function')
54
56
  value = value.toJSON();
55
- if (!value || typeof value !== 'object')
56
- return new Scalar(value);
57
+ if (!value || typeof value !== 'object') {
58
+ const node = new Scalar(value);
59
+ if (ref)
60
+ ref.node = node;
61
+ return node;
62
+ }
57
63
  tagObj =
58
64
  value instanceof Map
59
65
  ? schema[MAP]
@@ -20,6 +20,11 @@ class Directives {
20
20
  this.yaml = Object.assign({}, Directives.defaultYaml, yaml);
21
21
  this.tags = Object.assign({}, Directives.defaultTags, tags);
22
22
  }
23
+ clone() {
24
+ const copy = new Directives(this.yaml, this.tags);
25
+ copy.marker = this.marker;
26
+ return copy;
27
+ }
23
28
  /**
24
29
  * During parsing, get a Directives instance for the current document and
25
30
  * update the stream state according to the current version's spec.