yaml 2.0.0-4 → 2.0.0-8

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 (220) hide show
  1. package/README.md +5 -5
  2. package/browser/dist/compose/compose-collection.js +12 -9
  3. package/browser/dist/compose/compose-doc.js +23 -6
  4. package/browser/dist/compose/compose-node.js +27 -17
  5. package/browser/dist/compose/compose-scalar.js +21 -23
  6. package/browser/dist/compose/composer.js +108 -78
  7. package/browser/dist/compose/resolve-block-map.js +41 -29
  8. package/browser/dist/compose/resolve-block-scalar.js +14 -17
  9. package/browser/dist/compose/resolve-block-seq.js +21 -17
  10. package/browser/dist/compose/resolve-end.js +7 -8
  11. package/browser/dist/compose/resolve-flow-collection.js +158 -199
  12. package/browser/dist/compose/resolve-flow-scalar.js +62 -26
  13. package/browser/dist/compose/resolve-props.js +72 -37
  14. package/browser/dist/compose/util-contains-newline.js +15 -12
  15. package/browser/dist/compose/util-map-includes.js +17 -0
  16. package/browser/dist/doc/Document.js +74 -99
  17. package/browser/dist/doc/anchors.js +72 -0
  18. package/browser/dist/doc/createNode.js +35 -19
  19. package/browser/dist/doc/directives.js +5 -0
  20. package/browser/dist/errors.js +16 -12
  21. package/browser/dist/index.js +3 -3
  22. package/browser/dist/nodes/Alias.js +51 -28
  23. package/browser/dist/nodes/Collection.js +23 -92
  24. package/browser/dist/nodes/Node.js +9 -1
  25. package/browser/dist/nodes/Pair.js +16 -231
  26. package/browser/dist/nodes/YAMLMap.js +8 -4
  27. package/browser/dist/nodes/YAMLSeq.js +7 -4
  28. package/browser/dist/nodes/addPairToJSMap.js +103 -0
  29. package/browser/dist/nodes/toJS.js +12 -10
  30. package/browser/dist/options.js +1 -2
  31. package/browser/dist/parse/cst-scalar.js +219 -0
  32. package/browser/dist/parse/cst-stringify.js +61 -0
  33. package/browser/dist/parse/cst-visit.js +97 -0
  34. package/{dist/parse/tokens.js → browser/dist/parse/cst.js} +12 -7
  35. package/browser/dist/parse/lexer.js +149 -125
  36. package/browser/dist/parse/parser.js +272 -196
  37. package/browser/dist/public-api.js +11 -14
  38. package/browser/dist/schema/Schema.js +28 -0
  39. package/browser/dist/{tags/failsafe → schema/common}/map.js +0 -0
  40. package/browser/dist/schema/common/null.js +13 -0
  41. package/browser/dist/{tags/failsafe → schema/common}/seq.js +1 -1
  42. package/browser/dist/{tags/failsafe → schema/common}/string.js +0 -0
  43. package/browser/dist/schema/core/bool.js +19 -0
  44. package/browser/dist/schema/core/float.js +40 -0
  45. package/browser/dist/schema/core/int.js +38 -0
  46. package/browser/dist/schema/core/schema.js +23 -0
  47. package/browser/dist/{tags/json.js → schema/json/schema.js} +5 -6
  48. package/browser/dist/schema/tags.js +79 -0
  49. package/browser/dist/{tags → schema}/yaml-1.1/binary.js +0 -0
  50. package/browser/dist/schema/yaml-1.1/bool.js +26 -0
  51. package/browser/dist/schema/yaml-1.1/float.js +43 -0
  52. package/browser/dist/schema/yaml-1.1/int.js +71 -0
  53. package/browser/dist/{tags → schema}/yaml-1.1/omap.js +0 -0
  54. package/browser/dist/{tags → schema}/yaml-1.1/pairs.js +9 -6
  55. package/browser/dist/schema/yaml-1.1/schema.js +37 -0
  56. package/browser/dist/{tags → schema}/yaml-1.1/set.js +0 -0
  57. package/browser/dist/{tags → schema}/yaml-1.1/timestamp.js +0 -0
  58. package/browser/dist/stringify/stringify.js +8 -6
  59. package/browser/dist/stringify/stringifyCollection.js +122 -0
  60. package/browser/dist/stringify/stringifyComment.js +14 -0
  61. package/browser/dist/stringify/stringifyDocument.js +64 -0
  62. package/browser/dist/stringify/stringifyPair.js +111 -0
  63. package/browser/dist/stringify/stringifyString.js +53 -52
  64. package/browser/dist/visit.js +20 -1
  65. package/dist/compose/compose-collection.d.ts +4 -4
  66. package/dist/compose/compose-collection.js +12 -9
  67. package/dist/compose/compose-doc.d.ts +4 -3
  68. package/dist/compose/compose-doc.js +23 -6
  69. package/dist/compose/compose-node.d.ts +15 -7
  70. package/dist/compose/compose-node.js +27 -17
  71. package/dist/compose/compose-scalar.d.ts +4 -3
  72. package/dist/compose/compose-scalar.js +20 -22
  73. package/dist/compose/composer.d.ts +28 -23
  74. package/dist/compose/composer.js +109 -79
  75. package/dist/compose/resolve-block-map.d.ts +5 -4
  76. package/dist/compose/resolve-block-map.js +41 -29
  77. package/dist/compose/resolve-block-scalar.d.ts +5 -3
  78. package/dist/compose/resolve-block-scalar.js +14 -17
  79. package/dist/compose/resolve-block-seq.d.ts +4 -4
  80. package/dist/compose/resolve-block-seq.js +21 -17
  81. package/dist/compose/resolve-end.d.ts +3 -2
  82. package/dist/compose/resolve-end.js +7 -8
  83. package/dist/compose/resolve-flow-collection.d.ts +4 -5
  84. package/dist/compose/resolve-flow-collection.js +157 -198
  85. package/dist/compose/resolve-flow-scalar.d.ts +5 -3
  86. package/dist/compose/resolve-flow-scalar.js +62 -26
  87. package/dist/compose/resolve-props.d.ts +16 -10
  88. package/dist/compose/resolve-props.js +72 -37
  89. package/dist/compose/util-contains-newline.d.ts +1 -1
  90. package/dist/compose/util-contains-newline.js +15 -12
  91. package/dist/compose/util-empty-scalar-position.d.ts +1 -1
  92. package/dist/compose/util-map-includes.d.ts +4 -0
  93. package/dist/compose/util-map-includes.js +19 -0
  94. package/dist/doc/Document.d.ts +35 -15
  95. package/dist/doc/Document.js +72 -97
  96. package/dist/doc/anchors.d.ts +24 -0
  97. package/dist/doc/anchors.js +77 -0
  98. package/dist/doc/createNode.d.ts +10 -11
  99. package/dist/doc/createNode.js +34 -18
  100. package/dist/doc/directives.d.ts +1 -0
  101. package/dist/doc/directives.js +5 -0
  102. package/dist/errors.d.ts +11 -8
  103. package/dist/errors.js +16 -12
  104. package/dist/index.d.ts +5 -3
  105. package/dist/index.js +3 -3
  106. package/dist/nodes/Alias.d.ts +19 -9
  107. package/dist/nodes/Alias.js +51 -28
  108. package/dist/nodes/Collection.d.ts +9 -17
  109. package/dist/nodes/Collection.js +22 -91
  110. package/dist/nodes/Node.d.ts +10 -4
  111. package/dist/nodes/Node.js +9 -0
  112. package/dist/nodes/Pair.d.ts +10 -13
  113. package/dist/nodes/Pair.js +15 -230
  114. package/dist/nodes/Scalar.d.ts +6 -3
  115. package/dist/nodes/YAMLMap.d.ts +3 -3
  116. package/dist/nodes/YAMLMap.js +8 -4
  117. package/dist/nodes/YAMLSeq.d.ts +3 -3
  118. package/dist/nodes/YAMLSeq.js +7 -4
  119. package/dist/nodes/addPairToJSMap.d.ts +3 -0
  120. package/dist/nodes/addPairToJSMap.js +105 -0
  121. package/dist/nodes/toJS.d.ts +4 -5
  122. package/dist/nodes/toJS.js +12 -10
  123. package/dist/options.d.ts +41 -25
  124. package/dist/options.js +1 -2
  125. package/dist/parse/cst-scalar.d.ts +58 -0
  126. package/dist/parse/cst-scalar.js +223 -0
  127. package/dist/parse/cst-stringify.d.ts +8 -0
  128. package/dist/parse/cst-stringify.js +63 -0
  129. package/dist/parse/cst-visit.d.ts +39 -0
  130. package/dist/parse/cst-visit.js +99 -0
  131. package/dist/parse/{tokens.d.ts → cst.d.ts} +23 -7
  132. package/{browser/dist/parse/tokens.js → dist/parse/cst.js} +27 -1
  133. package/dist/parse/lexer.d.ts +7 -14
  134. package/dist/parse/lexer.js +150 -126
  135. package/dist/parse/parser.d.ts +31 -24
  136. package/dist/parse/parser.js +274 -198
  137. package/dist/public-api.d.ts +7 -7
  138. package/dist/public-api.js +11 -14
  139. package/dist/{doc → schema}/Schema.d.ts +6 -3
  140. package/dist/schema/Schema.js +30 -0
  141. package/dist/{tags/failsafe → schema/common}/map.d.ts +0 -0
  142. package/dist/{tags/failsafe → schema/common}/map.js +0 -0
  143. package/dist/schema/common/null.d.ts +4 -0
  144. package/dist/schema/common/null.js +15 -0
  145. package/dist/{tags/failsafe → schema/common}/seq.d.ts +0 -0
  146. package/dist/{tags/failsafe → schema/common}/seq.js +1 -1
  147. package/dist/{tags/failsafe → schema/common}/string.d.ts +0 -0
  148. package/dist/{tags/failsafe → schema/common}/string.js +0 -0
  149. package/dist/schema/core/bool.d.ts +4 -0
  150. package/dist/schema/core/bool.js +21 -0
  151. package/dist/schema/core/float.d.ts +4 -0
  152. package/dist/schema/core/float.js +44 -0
  153. package/dist/schema/core/int.d.ts +4 -0
  154. package/dist/schema/core/int.js +42 -0
  155. package/dist/schema/core/schema.d.ts +1 -0
  156. package/dist/schema/core/schema.js +25 -0
  157. package/dist/schema/json/schema.d.ts +2 -0
  158. package/dist/{tags/json.js → schema/json/schema.js} +5 -6
  159. package/dist/schema/tags.d.ts +41 -0
  160. package/dist/schema/tags.js +82 -0
  161. package/dist/{tags → schema}/types.d.ts +3 -7
  162. package/dist/{tags → schema}/yaml-1.1/binary.d.ts +0 -0
  163. package/dist/{tags → schema}/yaml-1.1/binary.js +0 -0
  164. package/dist/schema/yaml-1.1/bool.d.ts +7 -0
  165. package/dist/schema/yaml-1.1/bool.js +29 -0
  166. package/dist/schema/yaml-1.1/float.d.ts +4 -0
  167. package/dist/schema/yaml-1.1/float.js +47 -0
  168. package/dist/schema/yaml-1.1/int.d.ts +5 -0
  169. package/dist/schema/yaml-1.1/int.js +76 -0
  170. package/dist/{tags → schema}/yaml-1.1/omap.d.ts +0 -0
  171. package/dist/{tags → schema}/yaml-1.1/omap.js +0 -0
  172. package/dist/{tags → schema}/yaml-1.1/pairs.d.ts +3 -2
  173. package/dist/{tags → schema}/yaml-1.1/pairs.js +9 -6
  174. package/dist/schema/yaml-1.1/schema.d.ts +1 -0
  175. package/dist/schema/yaml-1.1/schema.js +39 -0
  176. package/dist/{tags → schema}/yaml-1.1/set.d.ts +1 -1
  177. package/dist/{tags → schema}/yaml-1.1/set.js +0 -0
  178. package/dist/{tags → schema}/yaml-1.1/timestamp.d.ts +0 -0
  179. package/dist/{tags → schema}/yaml-1.1/timestamp.js +0 -0
  180. package/dist/stringify/stringify.d.ts +1 -2
  181. package/dist/stringify/stringify.js +8 -6
  182. package/dist/stringify/stringifyCollection.d.ts +21 -0
  183. package/dist/stringify/stringifyCollection.js +124 -0
  184. package/dist/stringify/stringifyComment.d.ts +2 -0
  185. package/dist/stringify/stringifyComment.js +17 -0
  186. package/dist/stringify/stringifyDocument.d.ts +3 -0
  187. package/dist/stringify/stringifyDocument.js +66 -0
  188. package/dist/stringify/stringifyPair.d.ts +3 -0
  189. package/dist/stringify/stringifyPair.js +113 -0
  190. package/dist/stringify/stringifyString.js +53 -52
  191. package/dist/test-events.d.ts +1 -2
  192. package/dist/test-events.js +25 -12
  193. package/dist/util.d.ts +1 -1
  194. package/dist/visit.d.ts +10 -1
  195. package/dist/visit.js +20 -1
  196. package/package.json +9 -8
  197. package/browser/dist/doc/Anchors.js +0 -102
  198. package/browser/dist/doc/Schema.js +0 -27
  199. package/browser/dist/doc/getSchemaTags.js +0 -30
  200. package/browser/dist/stringify/addComment.js +0 -17
  201. package/browser/dist/tags/core.js +0 -109
  202. package/browser/dist/tags/failsafe/index.js +0 -7
  203. package/browser/dist/tags/index.js +0 -34
  204. package/browser/dist/tags/yaml-1.1/index.js +0 -152
  205. package/dist/doc/Anchors.d.ts +0 -36
  206. package/dist/doc/Anchors.js +0 -104
  207. package/dist/doc/Schema.js +0 -29
  208. package/dist/doc/getSchemaTags.d.ts +0 -3
  209. package/dist/doc/getSchemaTags.js +0 -32
  210. package/dist/stringify/addComment.d.ts +0 -2
  211. package/dist/stringify/addComment.js +0 -20
  212. package/dist/tags/core.d.ts +0 -14
  213. package/dist/tags/core.js +0 -119
  214. package/dist/tags/failsafe/index.d.ts +0 -1
  215. package/dist/tags/failsafe/index.js +0 -9
  216. package/dist/tags/index.d.ts +0 -31
  217. package/dist/tags/index.js +0 -37
  218. package/dist/tags/json.d.ts +0 -2
  219. package/dist/tags/yaml-1.1/index.d.ts +0 -2
  220. package/dist/tags/yaml-1.1/index.js +0 -154
@@ -1,90 +1,125 @@
1
- function resolveProps(doc, tokens, startOnNewline, indicator, offset, onError) {
2
- let length = 0;
1
+ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
3
2
  let spaceBefore = false;
4
3
  let atNewline = startOnNewline;
5
4
  let hasSpace = startOnNewline;
6
5
  let comment = '';
7
- let hasComment = false;
6
+ let commentSep = '';
8
7
  let hasNewline = false;
9
- let sep = '';
10
- let anchor = '';
11
- let tagName = '';
8
+ let reqSpace = false;
9
+ let anchor = null;
10
+ let tag = null;
11
+ let comma = null;
12
12
  let found = null;
13
13
  let start = null;
14
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
+ }
15
22
  switch (token.type) {
16
23
  case 'space':
17
- // At the doc level, tabs at line start may be parsed as leading
18
- // white space rather than indentation.
19
- if (atNewline && indicator !== 'doc-start' && token.source[0] === '\t')
20
- onError(offset + length, 'Tabs are not allowed as indentation');
24
+ // At the doc level, tabs at line start may be parsed
25
+ // as leading white space rather than indentation.
26
+ // In a flow collection, only the parser handles indent.
27
+ if (!flow &&
28
+ atNewline &&
29
+ indicator !== 'doc-start' &&
30
+ token.source[0] === '\t')
31
+ onError(token, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
21
32
  hasSpace = true;
22
33
  break;
23
34
  case 'comment': {
24
- if (doc.options.strict && !hasSpace)
25
- onError(offset + length, 'Comments must be separated from other tokens by white space characters');
26
- const cb = token.source.substring(1);
27
- if (!hasComment)
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) || ' ';
38
+ if (!comment)
28
39
  comment = cb;
29
40
  else
30
- comment += sep + cb;
31
- hasComment = true;
32
- sep = '';
41
+ comment += commentSep + cb;
42
+ commentSep = '';
43
+ atNewline = false;
33
44
  break;
34
45
  }
35
46
  case 'newline':
36
- if (atNewline && !hasComment)
37
- spaceBefore = true;
47
+ if (atNewline) {
48
+ if (comment)
49
+ comment += token.source;
50
+ else
51
+ spaceBefore = true;
52
+ }
53
+ else
54
+ commentSep += token.source;
38
55
  atNewline = true;
39
56
  hasNewline = true;
40
57
  hasSpace = true;
41
- sep += token.source;
42
58
  break;
43
59
  case 'anchor':
44
60
  if (anchor)
45
- onError(offset + length, 'A node can have at most one anchor');
46
- anchor = token.source.substring(1);
61
+ onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
62
+ anchor = token;
47
63
  if (start === null)
48
- start = offset + length;
64
+ start = token.offset;
49
65
  atNewline = false;
50
66
  hasSpace = false;
67
+ reqSpace = true;
51
68
  break;
52
69
  case 'tag': {
53
- if (tagName)
54
- onError(offset + length, 'A node can have at most one tag');
55
- const tn = doc.directives.tagName(token.source, msg => onError(offset, msg));
56
- if (tn)
57
- tagName = tn;
70
+ if (tag)
71
+ onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');
72
+ tag = token;
58
73
  if (start === null)
59
- start = offset + length;
74
+ start = token.offset;
60
75
  atNewline = false;
61
76
  hasSpace = false;
77
+ reqSpace = true;
62
78
  break;
63
79
  }
64
80
  case indicator:
65
81
  // Could here handle preceding comments differently
66
- found = { indent: token.indent, offset: offset + length };
82
+ if (anchor || tag)
83
+ onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
84
+ found = token;
67
85
  atNewline = false;
68
86
  hasSpace = false;
69
87
  break;
88
+ case 'comma':
89
+ if (flow) {
90
+ if (comma)
91
+ onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`);
92
+ comma = token;
93
+ atNewline = false;
94
+ hasSpace = false;
95
+ break;
96
+ }
97
+ // else fallthrough
70
98
  default:
71
- onError(offset + length, `Unexpected ${token.type} token`);
99
+ onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`);
72
100
  atNewline = false;
73
101
  hasSpace = false;
74
102
  }
75
- /* istanbul ignore else should not happen */
76
- if (token.source)
77
- length += token.source.length;
78
103
  }
104
+ const last = tokens[tokens.length - 1];
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');
79
113
  return {
114
+ comma,
80
115
  found,
81
116
  spaceBefore,
82
117
  comment,
83
118
  hasNewline,
84
119
  anchor,
85
- tagName,
86
- length,
87
- start: start !== null && start !== void 0 ? start : offset + length
120
+ tag,
121
+ end,
122
+ start: start !== null && start !== void 0 ? start : end
88
123
  };
89
124
  }
90
125
 
@@ -6,21 +6,24 @@ function containsNewline(key) {
6
6
  case 'scalar':
7
7
  case 'double-quoted-scalar':
8
8
  case 'single-quoted-scalar':
9
- return key.source.includes('\n');
9
+ if (key.source.includes('\n'))
10
+ return true;
11
+ if (key.end)
12
+ for (const st of key.end)
13
+ if (st.type === 'newline')
14
+ return true;
15
+ return false;
10
16
  case 'flow-collection':
11
- for (const token of key.items) {
12
- switch (token.type) {
13
- case 'newline':
17
+ for (const it of key.items) {
18
+ for (const st of it.start)
19
+ if (st.type === 'newline')
14
20
  return true;
15
- case 'alias':
16
- case 'scalar':
17
- case 'double-quoted-scalar':
18
- case 'single-quoted-scalar':
19
- case 'flow-collection':
20
- if (containsNewline(token))
21
+ if (it.sep)
22
+ for (const st of it.sep)
23
+ if (st.type === 'newline')
21
24
  return true;
22
- break;
23
- }
25
+ if (containsNewline(it.key) || containsNewline(it.value))
26
+ return true;
24
27
  }
25
28
  return false;
26
29
  default:
@@ -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,13 +1,13 @@
1
1
  import { Alias } from '../nodes/Alias.js';
2
2
  import { isEmptyPath, collectionFromPath } from '../nodes/Collection.js';
3
- import { NODE_TYPE, DOC, isCollection, isScalar, isNode } 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';
7
- import { addComment } from '../stringify/addComment.js';
8
- import { createStringifyContext, stringify } from '../stringify/stringify.js';
9
- import { Anchors } from './Anchors.js';
10
- import { Schema } from './Schema.js';
7
+ import { Schema } from '../schema/Schema.js';
8
+ import { stringify } from '../stringify/stringify.js';
9
+ import { stringifyDocument } from '../stringify/stringifyDocument.js';
10
+ import { anchorNames, findNewAnchor, createNodeAnchors } from './anchors.js';
11
11
  import { applyReviver } from './applyReviver.js';
12
12
  import { createNode } from './createNode.js';
13
13
  import { Directives } from './directives.js';
@@ -23,7 +23,7 @@ class Document {
23
23
  /** Warnings encountered during parsing. */
24
24
  this.warnings = [];
25
25
  Object.defineProperty(this, NODE_TYPE, { value: DOC });
26
- let _replacer = undefined;
26
+ let _replacer = null;
27
27
  if (typeof replacer === 'function' || Array.isArray(replacer)) {
28
28
  _replacer = replacer;
29
29
  }
@@ -33,7 +33,6 @@ class Document {
33
33
  }
34
34
  const opt = Object.assign({}, defaultOptions, options);
35
35
  this.options = opt;
36
- this.anchors = new Anchors(this.options.anchorPrefix);
37
36
  let { version } = opt;
38
37
  if (options === null || options === void 0 ? void 0 : options.directives) {
39
38
  this.directives = options.directives.atDocument();
@@ -43,10 +42,34 @@ class Document {
43
42
  else
44
43
  this.directives = new Directives({ version });
45
44
  this.setSchema(version, options);
46
- this.contents =
47
- value === undefined
48
- ? null
49
- : this.createNode(value, { replacer: _replacer });
45
+ if (value === undefined)
46
+ this.contents = null;
47
+ else {
48
+ this.contents = this.createNode(value, _replacer, options);
49
+ }
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;
50
73
  }
51
74
  /** Adds a value to the document. */
52
75
  add(value) {
@@ -59,48 +82,54 @@ class Document {
59
82
  this.contents.addIn(path, value);
60
83
  }
61
84
  /**
62
- * Convert any value into a `Node` using the current schema, recursively
63
- * turning objects into collections.
85
+ * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
86
+ *
87
+ * If `node` already has an anchor, `name` is ignored.
88
+ * Otherwise, the `node.anchor` value will be set to `name`,
89
+ * or if an anchor with that name is already present in the document,
90
+ * `name` will be used as a prefix for a new unique anchor.
91
+ * If `name` is undefined, the generated anchor will use 'a' as a prefix.
64
92
  */
65
- createNode(value, { flow, keepUndefined, onTagObj, replacer, tag } = {}) {
66
- if (typeof replacer === 'function')
93
+ createAlias(node, name) {
94
+ if (!node.anchor) {
95
+ const prev = anchorNames(this);
96
+ node.anchor =
97
+ !name || prev.has(name) ? findNewAnchor(name || 'a', prev) : name;
98
+ }
99
+ return new Alias(node.anchor);
100
+ }
101
+ createNode(value, replacer, options) {
102
+ let _replacer = undefined;
103
+ if (typeof replacer === 'function') {
67
104
  value = replacer.call({ '': value }, '', value);
105
+ _replacer = replacer;
106
+ }
68
107
  else if (Array.isArray(replacer)) {
69
108
  const keyToStr = (v) => typeof v === 'number' || v instanceof String || v instanceof Number;
70
109
  const asStr = replacer.filter(keyToStr).map(String);
71
110
  if (asStr.length > 0)
72
111
  replacer = replacer.concat(asStr);
112
+ _replacer = replacer;
73
113
  }
74
- if (typeof keepUndefined !== 'boolean')
75
- keepUndefined = !!this.options.keepUndefined;
76
- const aliasNodes = [];
114
+ else if (options === undefined && replacer) {
115
+ options = replacer;
116
+ replacer = undefined;
117
+ }
118
+ const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
119
+ const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this, anchorPrefix || 'a');
77
120
  const ctx = {
78
- keepUndefined,
79
- onAlias(source) {
80
- // These get fixed later in createNode()
81
- const alias = new Alias(source);
82
- aliasNodes.push(alias);
83
- return alias;
84
- },
121
+ aliasDuplicateObjects: aliasDuplicateObjects !== null && aliasDuplicateObjects !== void 0 ? aliasDuplicateObjects : true,
122
+ keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
123
+ onAnchor,
85
124
  onTagObj,
86
- prevObjects: new Map(),
87
- replacer,
88
- schema: this.schema
125
+ replacer: _replacer,
126
+ schema: this.schema,
127
+ sourceObjects
89
128
  };
90
129
  const node = createNode(value, tag, ctx);
91
- for (const alias of aliasNodes) {
92
- // With circular references, the source node is only resolved after all of
93
- // its child nodes are. This is why anchors are set only after all of the
94
- // nodes have been created.
95
- alias.source = alias.source.node;
96
- let name = this.anchors.getName(alias.source);
97
- if (!name) {
98
- name = this.anchors.newName();
99
- this.anchors.map[name] = alias.source;
100
- }
101
- }
102
130
  if (flow && isCollection(node))
103
131
  node.flow = true;
132
+ setAnchors();
104
133
  return node;
105
134
  }
106
135
  /**
@@ -108,8 +137,8 @@ class Document {
108
137
  * recursively wrapping all values as `Scalar` or `Collection` nodes.
109
138
  */
110
139
  createPair(key, value, options = {}) {
111
- const k = this.createNode(key, options);
112
- const v = this.createNode(value, options);
140
+ const k = this.createNode(key, null, options);
141
+ const v = this.createNode(value, null, options);
113
142
  return new Pair(k, v);
114
143
  }
115
144
  /**
@@ -223,10 +252,8 @@ class Document {
223
252
  }
224
253
  // json & jsonArg are only used from toJSON()
225
254
  toJS({ json, jsonArg, mapAsMap, maxAliasCount, onAnchor, reviver } = {}) {
226
- const anchorNodes = Object.values(this.anchors.map).map(node => [node, { alias: [], aliasCount: 0, count: 1 }]);
227
- const anchors = anchorNodes.length > 0 ? new Map(anchorNodes) : null;
228
255
  const ctx = {
229
- anchors,
256
+ anchors: new Map(),
230
257
  doc: this,
231
258
  keep: !json,
232
259
  mapAsMap: mapAsMap === true,
@@ -235,8 +262,8 @@ class Document {
235
262
  stringify
236
263
  };
237
264
  const res = toJS(this.contents, jsonArg || '', ctx);
238
- if (typeof onAnchor === 'function' && anchors)
239
- for (const { count, res } of anchors.values())
265
+ if (typeof onAnchor === 'function')
266
+ for (const { count, res } of ctx.anchors.values())
240
267
  onAnchor(res, count);
241
268
  return typeof reviver === 'function'
242
269
  ? applyReviver(reviver, { '': res }, '', res)
@@ -260,59 +287,7 @@ class Document {
260
287
  const s = JSON.stringify(options.indent);
261
288
  throw new Error(`"indent" option must be a positive integer, not ${s}`);
262
289
  }
263
- const lines = [];
264
- let hasDirectives = options.directives === true;
265
- if (options.directives !== false) {
266
- const dir = this.directives.toString(this);
267
- if (dir) {
268
- lines.push(dir);
269
- hasDirectives = true;
270
- }
271
- else if (this.directives.marker)
272
- hasDirectives = true;
273
- }
274
- if (hasDirectives)
275
- lines.push('---');
276
- if (this.commentBefore) {
277
- if (lines.length !== 1)
278
- lines.unshift('');
279
- lines.unshift(this.commentBefore.replace(/^/gm, '#'));
280
- }
281
- const ctx = createStringifyContext(this, options);
282
- let chompKeep = false;
283
- let contentComment = null;
284
- if (this.contents) {
285
- if (isNode(this.contents)) {
286
- if (this.contents.spaceBefore && hasDirectives)
287
- lines.push('');
288
- if (this.contents.commentBefore)
289
- lines.push(this.contents.commentBefore.replace(/^/gm, '#'));
290
- // top-level block scalars need to be indented if followed by a comment
291
- ctx.forceBlockIndent = !!this.comment;
292
- contentComment = this.contents.comment;
293
- }
294
- const onChompKeep = contentComment ? undefined : () => (chompKeep = true);
295
- let body = stringify(this.contents, ctx, () => (contentComment = null), onChompKeep);
296
- if (contentComment)
297
- body = addComment(body, '', contentComment);
298
- if ((body[0] === '|' || body[0] === '>') &&
299
- lines[lines.length - 1] === '---') {
300
- // Top-level block scalars with a preceding doc marker ought to use the
301
- // same line for their header.
302
- lines[lines.length - 1] = `--- ${body}`;
303
- }
304
- else
305
- lines.push(body);
306
- }
307
- else {
308
- lines.push(stringify(this.contents, ctx));
309
- }
310
- if (this.comment) {
311
- if ((!chompKeep || contentComment) && lines[lines.length - 1] !== '')
312
- lines.push('');
313
- lines.push(this.comment.replace(/^/gm, '#'));
314
- }
315
- return lines.join('\n') + '\n';
290
+ return stringifyDocument(this, options);
316
291
  }
317
292
  }
318
293
  function assertCollection(contents) {
@@ -0,0 +1,72 @@
1
+ import { isScalar, isCollection } from '../nodes/Node.js';
2
+ import { visit } from '../visit.js';
3
+
4
+ /**
5
+ * Verify that the input string is a valid anchor.
6
+ *
7
+ * Will throw on errors.
8
+ */
9
+ function anchorIsValid(anchor) {
10
+ if (/[\x00-\x19\s,[\]{}]/.test(anchor)) {
11
+ const sa = JSON.stringify(anchor);
12
+ const msg = `Anchor must not contain whitespace or control characters: ${sa}`;
13
+ throw new Error(msg);
14
+ }
15
+ return true;
16
+ }
17
+ function anchorNames(root) {
18
+ const anchors = new Set();
19
+ visit(root, {
20
+ Value(_key, node) {
21
+ if (node.anchor)
22
+ anchors.add(node.anchor);
23
+ }
24
+ });
25
+ return anchors;
26
+ }
27
+ /** Find a new anchor name with the given `prefix` and a one-indexed suffix. */
28
+ function findNewAnchor(prefix, exclude) {
29
+ for (let i = 1; true; ++i) {
30
+ const name = `${prefix}${i}`;
31
+ if (!exclude.has(name))
32
+ return name;
33
+ }
34
+ }
35
+ function createNodeAnchors(doc, prefix) {
36
+ const aliasObjects = [];
37
+ const sourceObjects = new Map();
38
+ let prevAnchors = null;
39
+ return {
40
+ onAnchor(source) {
41
+ aliasObjects.push(source);
42
+ if (!prevAnchors)
43
+ prevAnchors = anchorNames(doc);
44
+ const anchor = findNewAnchor(prefix, prevAnchors);
45
+ prevAnchors.add(anchor);
46
+ return anchor;
47
+ },
48
+ /**
49
+ * With circular references, the source node is only resolved after all
50
+ * of its child nodes are. This is why anchors are set only after all of
51
+ * the nodes have been created.
52
+ */
53
+ setAnchors() {
54
+ for (const source of aliasObjects) {
55
+ const ref = sourceObjects.get(source);
56
+ if (typeof ref === 'object' &&
57
+ ref.anchor &&
58
+ (isScalar(ref.node) || isCollection(ref.node))) {
59
+ ref.node.anchor = ref.anchor;
60
+ }
61
+ else {
62
+ const error = new Error('Failed to resolve repeated object (this should not happen)');
63
+ error.source = source;
64
+ throw error;
65
+ }
66
+ }
67
+ },
68
+ sourceObjects
69
+ };
70
+ }
71
+
72
+ export { anchorIsValid, anchorNames, createNodeAnchors, findNewAnchor };
@@ -1,4 +1,5 @@
1
- import { isNode, isPair } from '../nodes/Node.js';
1
+ import { Alias } from '../nodes/Alias.js';
2
+ import { isNode, isPair, MAP, SEQ, isDocument } from '../nodes/Node.js';
2
3
  import { Scalar } from '../nodes/Scalar.js';
3
4
 
4
5
  const defaultTagPrefix = 'tag:yaml.org,2002:';
@@ -14,10 +15,12 @@ function findTagObject(value, tagName, tags) {
14
15
  }
15
16
  function createNode(value, tagName, ctx) {
16
17
  var _a, _b;
18
+ if (isDocument(value))
19
+ value = value.contents;
17
20
  if (isNode(value))
18
21
  return value;
19
22
  if (isPair(value)) {
20
- const map = (_b = (_a = ctx.schema.map).createNode) === null || _b === void 0 ? void 0 : _b.call(_a, ctx.schema, null, ctx);
23
+ const map = (_b = (_a = ctx.schema[MAP]).createNode) === null || _b === void 0 ? void 0 : _b.call(_a, ctx.schema, null, ctx);
21
24
  map.items.push(value);
22
25
  return map;
23
26
  }
@@ -29,39 +32,52 @@ function createNode(value, tagName, ctx) {
29
32
  // https://tc39.es/ecma262/#sec-serializejsonproperty
30
33
  value = value.valueOf();
31
34
  }
32
- const { onAlias, onTagObj, prevObjects } = ctx;
33
- const { map, seq, tags } = ctx.schema;
35
+ const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx;
36
+ // Detect duplicate references to the same object & use Alias nodes for all
37
+ // after first. The `ref` wrapper allows for circular references to resolve.
38
+ let ref = undefined;
39
+ if (aliasDuplicateObjects && value && typeof value === 'object') {
40
+ ref = sourceObjects.get(value);
41
+ if (ref) {
42
+ if (!ref.anchor)
43
+ ref.anchor = onAnchor(value);
44
+ return new Alias(ref.anchor);
45
+ }
46
+ else {
47
+ ref = { anchor: null, node: null };
48
+ sourceObjects.set(value, ref);
49
+ }
50
+ }
34
51
  if (tagName && tagName.startsWith('!!'))
35
52
  tagName = defaultTagPrefix + tagName.slice(2);
36
- let tagObj = findTagObject(value, tagName, tags);
53
+ let tagObj = findTagObject(value, tagName, schema.tags);
37
54
  if (!tagObj) {
38
55
  if (value && typeof value.toJSON === 'function')
39
56
  value = value.toJSON();
40
- if (!value || typeof value !== 'object')
41
- 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
+ }
42
63
  tagObj =
43
- value instanceof Map ? map : Symbol.iterator in Object(value) ? seq : map;
64
+ value instanceof Map
65
+ ? schema[MAP]
66
+ : Symbol.iterator in Object(value)
67
+ ? schema[SEQ]
68
+ : schema[MAP];
44
69
  }
45
70
  if (onTagObj) {
46
71
  onTagObj(tagObj);
47
72
  delete ctx.onTagObj;
48
73
  }
49
- // Detect duplicate references to the same object & use Alias nodes for all
50
- // after first. The `ref` wrapper allows for circular references to resolve.
51
- const ref = { value: undefined, node: undefined };
52
- if (value && typeof value === 'object') {
53
- const prev = prevObjects.get(value);
54
- if (prev)
55
- return onAlias(prev);
56
- ref.value = value;
57
- prevObjects.set(value, ref);
58
- }
59
74
  const node = (tagObj === null || tagObj === void 0 ? void 0 : tagObj.createNode)
60
75
  ? tagObj.createNode(ctx.schema, value, ctx)
61
76
  : new Scalar(value);
62
77
  if (tagName)
63
78
  node.tag = tagName;
64
- ref.node = node;
79
+ if (ref)
80
+ ref.node = node;
65
81
  return node;
66
82
  }
67
83
 
@@ -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.