yaml 2.0.0-4 → 2.0.0-5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (210) 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 +20 -17
  5. package/browser/dist/compose/compose-scalar.js +20 -23
  6. package/browser/dist/compose/composer.js +105 -76
  7. package/browser/dist/compose/resolve-block-map.js +38 -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 +6 -7
  11. package/browser/dist/compose/resolve-flow-collection.js +153 -199
  12. package/browser/dist/compose/resolve-flow-scalar.js +49 -25
  13. package/browser/dist/compose/resolve-props.js +47 -35
  14. package/browser/dist/compose/util-contains-newline.js +15 -12
  15. package/browser/dist/doc/Document.js +50 -99
  16. package/browser/dist/doc/anchors.js +72 -0
  17. package/browser/dist/doc/createNode.js +27 -17
  18. package/browser/dist/errors.js +16 -12
  19. package/browser/dist/index.js +3 -3
  20. package/browser/dist/nodes/Alias.js +51 -28
  21. package/browser/dist/nodes/Collection.js +5 -83
  22. package/browser/dist/nodes/Node.js +2 -1
  23. package/browser/dist/nodes/Pair.js +9 -232
  24. package/browser/dist/nodes/YAMLMap.js +8 -4
  25. package/browser/dist/nodes/YAMLSeq.js +7 -4
  26. package/browser/dist/nodes/addPairToJSMap.js +103 -0
  27. package/browser/dist/nodes/toJS.js +12 -10
  28. package/browser/dist/options.js +0 -2
  29. package/browser/dist/parse/cst-scalar.js +219 -0
  30. package/browser/dist/parse/cst-stringify.js +61 -0
  31. package/browser/dist/parse/cst-visit.js +97 -0
  32. package/{dist/parse/tokens.js → browser/dist/parse/cst.js} +12 -7
  33. package/browser/dist/parse/lexer.js +99 -105
  34. package/browser/dist/parse/parser.js +219 -144
  35. package/browser/dist/public-api.js +11 -14
  36. package/browser/dist/schema/Schema.js +23 -0
  37. package/browser/dist/{tags/failsafe → schema/common}/map.js +0 -0
  38. package/browser/dist/schema/common/null.js +13 -0
  39. package/browser/dist/{tags/failsafe → schema/common}/seq.js +1 -1
  40. package/browser/dist/{tags/failsafe → schema/common}/string.js +0 -0
  41. package/browser/dist/schema/core/bool.js +19 -0
  42. package/browser/dist/schema/core/float.js +40 -0
  43. package/browser/dist/schema/core/int.js +38 -0
  44. package/browser/dist/schema/core/schema.js +23 -0
  45. package/browser/dist/{tags/json.js → schema/json/schema.js} +5 -6
  46. package/browser/dist/schema/tags.js +79 -0
  47. package/browser/dist/{tags → schema}/yaml-1.1/binary.js +0 -0
  48. package/browser/dist/schema/yaml-1.1/bool.js +26 -0
  49. package/browser/dist/schema/yaml-1.1/float.js +43 -0
  50. package/browser/dist/schema/yaml-1.1/int.js +71 -0
  51. package/browser/dist/{tags → schema}/yaml-1.1/omap.js +0 -0
  52. package/browser/dist/{tags → schema}/yaml-1.1/pairs.js +9 -6
  53. package/browser/dist/schema/yaml-1.1/schema.js +37 -0
  54. package/browser/dist/{tags → schema}/yaml-1.1/set.js +0 -0
  55. package/browser/dist/{tags → schema}/yaml-1.1/timestamp.js +0 -0
  56. package/browser/dist/stringify/addComment.js +1 -7
  57. package/browser/dist/stringify/stringify.js +8 -6
  58. package/browser/dist/stringify/stringifyCollection.js +108 -0
  59. package/browser/dist/stringify/stringifyDocument.js +61 -0
  60. package/browser/dist/stringify/stringifyPair.js +113 -0
  61. package/browser/dist/stringify/stringifyString.js +53 -52
  62. package/browser/dist/visit.js +20 -1
  63. package/dist/compose/compose-collection.d.ts +4 -4
  64. package/dist/compose/compose-collection.js +12 -9
  65. package/dist/compose/compose-doc.d.ts +4 -3
  66. package/dist/compose/compose-doc.js +23 -6
  67. package/dist/compose/compose-node.d.ts +15 -7
  68. package/dist/compose/compose-node.js +20 -17
  69. package/dist/compose/compose-scalar.d.ts +4 -3
  70. package/dist/compose/compose-scalar.js +19 -22
  71. package/dist/compose/composer.d.ts +28 -23
  72. package/dist/compose/composer.js +106 -77
  73. package/dist/compose/resolve-block-map.d.ts +4 -4
  74. package/dist/compose/resolve-block-map.js +38 -29
  75. package/dist/compose/resolve-block-scalar.d.ts +5 -3
  76. package/dist/compose/resolve-block-scalar.js +14 -17
  77. package/dist/compose/resolve-block-seq.d.ts +4 -4
  78. package/dist/compose/resolve-block-seq.js +21 -17
  79. package/dist/compose/resolve-end.d.ts +3 -2
  80. package/dist/compose/resolve-end.js +6 -7
  81. package/dist/compose/resolve-flow-collection.d.ts +4 -5
  82. package/dist/compose/resolve-flow-collection.js +152 -198
  83. package/dist/compose/resolve-flow-scalar.d.ts +5 -3
  84. package/dist/compose/resolve-flow-scalar.js +49 -25
  85. package/dist/compose/resolve-props.d.ts +17 -10
  86. package/dist/compose/resolve-props.js +47 -35
  87. package/dist/compose/util-contains-newline.d.ts +1 -1
  88. package/dist/compose/util-contains-newline.js +15 -12
  89. package/dist/compose/util-empty-scalar-position.d.ts +1 -1
  90. package/dist/doc/Document.d.ts +27 -15
  91. package/dist/doc/Document.js +48 -97
  92. package/dist/doc/anchors.d.ts +24 -0
  93. package/dist/doc/anchors.js +77 -0
  94. package/dist/doc/createNode.d.ts +8 -10
  95. package/dist/doc/createNode.js +26 -16
  96. package/dist/errors.d.ts +11 -8
  97. package/dist/errors.js +16 -12
  98. package/dist/index.d.ts +5 -3
  99. package/dist/index.js +3 -3
  100. package/dist/nodes/Alias.d.ts +19 -9
  101. package/dist/nodes/Alias.js +51 -28
  102. package/dist/nodes/Collection.d.ts +3 -17
  103. package/dist/nodes/Collection.js +4 -82
  104. package/dist/nodes/Node.d.ts +6 -4
  105. package/dist/nodes/Node.js +2 -0
  106. package/dist/nodes/Pair.d.ts +6 -11
  107. package/dist/nodes/Pair.js +8 -231
  108. package/dist/nodes/Scalar.d.ts +6 -3
  109. package/dist/nodes/YAMLMap.d.ts +3 -3
  110. package/dist/nodes/YAMLMap.js +8 -4
  111. package/dist/nodes/YAMLSeq.d.ts +3 -3
  112. package/dist/nodes/YAMLSeq.js +7 -4
  113. package/dist/nodes/addPairToJSMap.d.ts +3 -0
  114. package/dist/nodes/addPairToJSMap.js +105 -0
  115. package/dist/nodes/toJS.d.ts +4 -5
  116. package/dist/nodes/toJS.js +12 -10
  117. package/dist/options.d.ts +20 -26
  118. package/dist/options.js +0 -2
  119. package/dist/parse/cst-scalar.d.ts +58 -0
  120. package/dist/parse/cst-scalar.js +223 -0
  121. package/dist/parse/cst-stringify.d.ts +8 -0
  122. package/dist/parse/cst-stringify.js +63 -0
  123. package/dist/parse/cst-visit.d.ts +39 -0
  124. package/dist/parse/cst-visit.js +99 -0
  125. package/dist/parse/{tokens.d.ts → cst.d.ts} +23 -7
  126. package/{browser/dist/parse/tokens.js → dist/parse/cst.js} +27 -1
  127. package/dist/parse/lexer.d.ts +4 -13
  128. package/dist/parse/lexer.js +100 -106
  129. package/dist/parse/parser.d.ts +30 -24
  130. package/dist/parse/parser.js +221 -146
  131. package/dist/public-api.d.ts +7 -7
  132. package/dist/public-api.js +11 -14
  133. package/dist/{doc → schema}/Schema.d.ts +5 -3
  134. package/dist/schema/Schema.js +25 -0
  135. package/dist/{tags/failsafe → schema/common}/map.d.ts +0 -0
  136. package/dist/{tags/failsafe → schema/common}/map.js +0 -0
  137. package/dist/schema/common/null.d.ts +4 -0
  138. package/dist/schema/common/null.js +15 -0
  139. package/dist/{tags/failsafe → schema/common}/seq.d.ts +0 -0
  140. package/dist/{tags/failsafe → schema/common}/seq.js +1 -1
  141. package/dist/{tags/failsafe → schema/common}/string.d.ts +0 -0
  142. package/dist/{tags/failsafe → schema/common}/string.js +0 -0
  143. package/dist/schema/core/bool.d.ts +4 -0
  144. package/dist/schema/core/bool.js +21 -0
  145. package/dist/schema/core/float.d.ts +4 -0
  146. package/dist/schema/core/float.js +44 -0
  147. package/dist/schema/core/int.d.ts +4 -0
  148. package/dist/schema/core/int.js +42 -0
  149. package/dist/schema/core/schema.d.ts +1 -0
  150. package/dist/schema/core/schema.js +25 -0
  151. package/dist/schema/json/schema.d.ts +2 -0
  152. package/dist/{tags/json.js → schema/json/schema.js} +5 -6
  153. package/dist/schema/tags.d.ts +41 -0
  154. package/dist/schema/tags.js +82 -0
  155. package/dist/{tags → schema}/types.d.ts +3 -7
  156. package/dist/{tags → schema}/yaml-1.1/binary.d.ts +0 -0
  157. package/dist/{tags → schema}/yaml-1.1/binary.js +0 -0
  158. package/dist/schema/yaml-1.1/bool.d.ts +7 -0
  159. package/dist/schema/yaml-1.1/bool.js +29 -0
  160. package/dist/schema/yaml-1.1/float.d.ts +4 -0
  161. package/dist/schema/yaml-1.1/float.js +47 -0
  162. package/dist/schema/yaml-1.1/int.d.ts +5 -0
  163. package/dist/schema/yaml-1.1/int.js +76 -0
  164. package/dist/{tags → schema}/yaml-1.1/omap.d.ts +0 -0
  165. package/dist/{tags → schema}/yaml-1.1/omap.js +0 -0
  166. package/dist/{tags → schema}/yaml-1.1/pairs.d.ts +3 -2
  167. package/dist/{tags → schema}/yaml-1.1/pairs.js +9 -6
  168. package/dist/schema/yaml-1.1/schema.d.ts +1 -0
  169. package/dist/schema/yaml-1.1/schema.js +39 -0
  170. package/dist/{tags → schema}/yaml-1.1/set.d.ts +1 -1
  171. package/dist/{tags → schema}/yaml-1.1/set.js +0 -0
  172. package/dist/{tags → schema}/yaml-1.1/timestamp.d.ts +0 -0
  173. package/dist/{tags → schema}/yaml-1.1/timestamp.js +0 -0
  174. package/dist/stringify/addComment.js +0 -7
  175. package/dist/stringify/stringify.d.ts +1 -2
  176. package/dist/stringify/stringify.js +8 -6
  177. package/dist/stringify/stringifyCollection.d.ts +21 -0
  178. package/dist/stringify/stringifyCollection.js +110 -0
  179. package/dist/stringify/stringifyDocument.d.ts +3 -0
  180. package/dist/stringify/stringifyDocument.js +63 -0
  181. package/dist/stringify/stringifyPair.d.ts +3 -0
  182. package/dist/stringify/stringifyPair.js +115 -0
  183. package/dist/stringify/stringifyString.js +53 -52
  184. package/dist/test-events.d.ts +1 -2
  185. package/dist/test-events.js +25 -12
  186. package/dist/util.d.ts +1 -1
  187. package/dist/visit.d.ts +10 -1
  188. package/dist/visit.js +20 -1
  189. package/package.json +4 -3
  190. package/browser/dist/doc/Anchors.js +0 -102
  191. package/browser/dist/doc/Schema.js +0 -27
  192. package/browser/dist/doc/getSchemaTags.js +0 -30
  193. package/browser/dist/tags/core.js +0 -109
  194. package/browser/dist/tags/failsafe/index.js +0 -7
  195. package/browser/dist/tags/index.js +0 -34
  196. package/browser/dist/tags/yaml-1.1/index.js +0 -152
  197. package/dist/doc/Anchors.d.ts +0 -36
  198. package/dist/doc/Anchors.js +0 -104
  199. package/dist/doc/Schema.js +0 -29
  200. package/dist/doc/getSchemaTags.d.ts +0 -3
  201. package/dist/doc/getSchemaTags.js +0 -32
  202. package/dist/tags/core.d.ts +0 -14
  203. package/dist/tags/core.js +0 -119
  204. package/dist/tags/failsafe/index.d.ts +0 -1
  205. package/dist/tags/failsafe/index.js +0 -9
  206. package/dist/tags/index.d.ts +0 -31
  207. package/dist/tags/index.js +0 -37
  208. package/dist/tags/json.d.ts +0 -2
  209. package/dist/tags/yaml-1.1/index.d.ts +0 -2
  210. package/dist/tags/yaml-1.1/index.js +0 -154
@@ -1,10 +1,11 @@
1
1
  import { Scalar } from '../nodes/Scalar.js';
2
2
  import { resolveEnd } from './resolve-end.js';
3
3
 
4
- function resolveFlowScalar({ offset, type, source, end }, strict, onError) {
4
+ function resolveFlowScalar(scalar, strict, onError) {
5
+ const { offset, type, source, end } = scalar;
5
6
  let _type;
6
7
  let value;
7
- const _onError = (rel, msg) => onError(offset + rel, msg);
8
+ const _onError = (rel, code, msg) => onError(offset + rel, code, msg);
8
9
  switch (type) {
9
10
  case 'scalar':
10
11
  _type = Scalar.PLAIN;
@@ -20,68 +21,91 @@ function resolveFlowScalar({ offset, type, source, end }, strict, onError) {
20
21
  break;
21
22
  /* istanbul ignore next should not happen */
22
23
  default:
23
- onError(offset, `Expected a flow scalar value, but found: ${type}`);
24
+ onError(scalar, 'UNEXPECTED_TOKEN', `Expected a flow scalar value, but found: ${type}`);
24
25
  return {
25
26
  value: '',
26
27
  type: null,
27
28
  comment: '',
28
- length: source.length
29
+ range: [offset, offset + source.length, offset + source.length]
29
30
  };
30
31
  }
31
- const re = resolveEnd(end, 0, strict, _onError);
32
+ const valueEnd = offset + source.length;
33
+ const re = resolveEnd(end, valueEnd, strict, onError);
32
34
  return {
33
35
  value,
34
36
  type: _type,
35
37
  comment: re.comment,
36
- length: source.length + re.offset
38
+ range: [offset, valueEnd, re.offset]
37
39
  };
38
40
  }
39
41
  function plainValue(source, onError) {
42
+ let message = '';
40
43
  switch (source[0]) {
41
44
  /* istanbul ignore next should not happen */
42
45
  case '\t':
43
- onError(0, 'Plain value cannot start with a tab character');
46
+ message = 'Plain value cannot start with a tab character';
44
47
  break;
45
48
  case '|':
46
49
  case '>': {
47
- const message = `Plain value cannot start with block scalar indicator ${source[0]}`;
48
- onError(0, message);
50
+ message = `Plain value cannot start with block scalar indicator ${source[0]}`;
49
51
  break;
50
52
  }
51
53
  case '@':
52
54
  case '`': {
53
- const message = `Plain value cannot start with reserved character ${source[0]}`;
54
- onError(0, message);
55
+ message = `Plain value cannot start with reserved character ${source[0]}`;
55
56
  break;
56
57
  }
57
58
  }
58
- return foldLines(source.trim());
59
+ if (message)
60
+ onError(0, 'BAD_SCALAR_START', message);
61
+ return foldLines(source);
59
62
  }
60
63
  function singleQuotedValue(source, onError) {
61
64
  if (source[source.length - 1] !== "'" || source.length === 1)
62
- onError(source.length, "Missing closing 'quote");
65
+ onError(source.length, 'MISSING_CHAR', "Missing closing 'quote");
63
66
  return foldLines(source.slice(1, -1)).replace(/''/g, "'");
64
67
  }
65
68
  function foldLines(source) {
66
- const lines = source.split(/[ \t]*\r?\n[ \t]*/);
67
- let res = lines[0];
69
+ /**
70
+ * The negative lookbehind here and in the `re` RegExp is to
71
+ * prevent causing a polynomial search time in certain cases.
72
+ *
73
+ * The try-catch is for Safari, which doesn't support this yet:
74
+ * https://caniuse.com/js-regexp-lookbehind
75
+ */
76
+ let first, line;
77
+ try {
78
+ first = new RegExp('(.*?)(?<![ \t])[ \t]*\r?\n', 'sy');
79
+ line = new RegExp('[ \t]*(.*?)(?:(?<![ \t])[ \t]*)?\r?\n', 'sy');
80
+ }
81
+ catch (_) {
82
+ first = /(.*?)[ \t]*\r?\n/sy;
83
+ line = /[ \t]*(.*?)[ \t]*\r?\n/sy;
84
+ }
85
+ let match = first.exec(source);
86
+ if (!match)
87
+ return source;
88
+ let res = match[1];
68
89
  let sep = ' ';
69
- for (let i = 1; i < lines.length - 1; ++i) {
70
- const line = lines[i];
71
- if (line === '') {
90
+ let pos = first.lastIndex;
91
+ line.lastIndex = pos;
92
+ while ((match = line.exec(source))) {
93
+ if (match[1] === '') {
72
94
  if (sep === '\n')
73
95
  res += sep;
74
96
  else
75
97
  sep = '\n';
76
98
  }
77
99
  else {
78
- res += sep + line;
100
+ res += sep + match[1];
79
101
  sep = ' ';
80
102
  }
103
+ pos = line.lastIndex;
81
104
  }
82
- if (lines.length > 1)
83
- res += sep + lines[lines.length - 1];
84
- return res;
105
+ const last = /[ \t]*(.*)/sy;
106
+ last.lastIndex = pos;
107
+ match = last.exec(source);
108
+ return res + sep + ((match && match[1]) || '');
85
109
  }
86
110
  function doubleQuotedValue(source, onError) {
87
111
  let res = '';
@@ -112,7 +136,7 @@ function doubleQuotedValue(source, onError) {
112
136
  }
113
137
  else {
114
138
  const raw = source.substr(i - 1, 2);
115
- onError(i - 1, `Invalid escape sequence ${raw}`);
139
+ onError(i - 1, 'BAD_DQ_ESCAPE', `Invalid escape sequence ${raw}`);
116
140
  res += raw;
117
141
  }
118
142
  }
@@ -130,7 +154,7 @@ function doubleQuotedValue(source, onError) {
130
154
  }
131
155
  }
132
156
  if (source[source.length - 1] !== '"' || source.length === 1)
133
- onError(source.length, 'Missing closing "quote');
157
+ onError(source.length, 'MISSING_CHAR', 'Missing closing "quote');
134
158
  return res;
135
159
  }
136
160
  /**
@@ -178,7 +202,7 @@ function parseCharCode(source, offset, length, onError) {
178
202
  const code = ok ? parseInt(cc, 16) : NaN;
179
203
  if (isNaN(code)) {
180
204
  const raw = source.substr(offset - 2, length + 2);
181
- onError(offset - 2, `Invalid escape sequence ${raw}`);
205
+ onError(offset - 2, 'BAD_DQ_ESCAPE', `Invalid escape sequence ${raw}`);
182
206
  return raw;
183
207
  }
184
208
  return String.fromCodePoint(code);
@@ -1,90 +1,102 @@
1
- function resolveProps(doc, tokens, startOnNewline, indicator, offset, onError) {
2
- let length = 0;
1
+ function resolveProps(tokens, { ctx, flow, indicator, 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 anchor = null;
9
+ let tag = null;
10
+ let comma = null;
12
11
  let found = null;
13
12
  let start = null;
14
13
  for (const token of tokens) {
15
14
  switch (token.type) {
16
15
  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');
16
+ // At the doc level, tabs at line start may be parsed
17
+ // as leading white space rather than indentation.
18
+ // In a flow collection, only the parser handles indent.
19
+ if (!flow &&
20
+ atNewline &&
21
+ indicator !== 'doc-start' &&
22
+ token.source[0] === '\t')
23
+ onError(token, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
21
24
  hasSpace = true;
22
25
  break;
23
26
  case 'comment': {
24
- if (doc.options.strict && !hasSpace)
25
- onError(offset + length, 'Comments must be separated from other tokens by white space characters');
27
+ if (ctx.options.strict && !hasSpace)
28
+ onError(token, 'COMMENT_SPACE', 'Comments must be separated from other tokens by white space characters');
26
29
  const cb = token.source.substring(1);
27
- if (!hasComment)
30
+ if (!comment)
28
31
  comment = cb;
29
32
  else
30
- comment += sep + cb;
31
- hasComment = true;
32
- sep = '';
33
+ comment += commentSep + cb;
34
+ commentSep = '';
33
35
  break;
34
36
  }
35
37
  case 'newline':
36
- if (atNewline && !hasComment)
38
+ if (atNewline && !comment)
37
39
  spaceBefore = true;
38
40
  atNewline = true;
39
41
  hasNewline = true;
40
42
  hasSpace = true;
41
- sep += token.source;
43
+ commentSep += token.source;
42
44
  break;
43
45
  case 'anchor':
44
46
  if (anchor)
45
- onError(offset + length, 'A node can have at most one anchor');
46
- anchor = token.source.substring(1);
47
+ onError(token, 'MULTIPLE_ANCHORS', 'A node can have at most one anchor');
48
+ anchor = token;
47
49
  if (start === null)
48
- start = offset + length;
50
+ start = token.offset;
49
51
  atNewline = false;
50
52
  hasSpace = false;
51
53
  break;
52
54
  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;
55
+ if (tag)
56
+ onError(token, 'MULTIPLE_TAGS', 'A node can have at most one tag');
57
+ tag = token;
58
58
  if (start === null)
59
- start = offset + length;
59
+ start = token.offset;
60
60
  atNewline = false;
61
61
  hasSpace = false;
62
62
  break;
63
63
  }
64
64
  case indicator:
65
65
  // Could here handle preceding comments differently
66
- found = { indent: token.indent, offset: offset + length };
66
+ if (anchor || tag)
67
+ onError(token, 'BAD_PROP_ORDER', `Anchors and tags must be after the ${token.source} indicator`);
68
+ found = token;
67
69
  atNewline = false;
68
70
  hasSpace = false;
69
71
  break;
72
+ case 'comma':
73
+ if (flow) {
74
+ if (comma)
75
+ onError(token, 'UNEXPECTED_TOKEN', `Unexpected , in ${flow}`);
76
+ comma = token;
77
+ atNewline = false;
78
+ hasSpace = false;
79
+ break;
80
+ }
81
+ // else fallthrough
70
82
  default:
71
- onError(offset + length, `Unexpected ${token.type} token`);
83
+ onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.type} token`);
72
84
  atNewline = false;
73
85
  hasSpace = false;
74
86
  }
75
- /* istanbul ignore else should not happen */
76
- if (token.source)
77
- length += token.source.length;
78
87
  }
88
+ const last = tokens[tokens.length - 1];
89
+ const end = last ? last.offset + last.source.length : offset;
79
90
  return {
91
+ comma,
80
92
  found,
81
93
  spaceBefore,
82
94
  comment,
83
95
  hasNewline,
84
96
  anchor,
85
- tagName,
86
- length,
87
- start: start !== null && start !== void 0 ? start : offset + length
97
+ tag,
98
+ end,
99
+ start: start !== null && start !== void 0 ? start : end
88
100
  };
89
101
  }
90
102
 
@@ -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:
@@ -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, 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,11 @@ 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
50
  }
51
51
  /** Adds a value to the document. */
52
52
  add(value) {
@@ -59,48 +59,53 @@ class Document {
59
59
  this.contents.addIn(path, value);
60
60
  }
61
61
  /**
62
- * Convert any value into a `Node` using the current schema, recursively
63
- * turning objects into collections.
62
+ * Create a new `Alias` node, ensuring that the target `node` has the required anchor.
63
+ *
64
+ * If `node` already has an anchor, `name` is ignored.
65
+ * Otherwise, the `node.anchor` value will be set to `name`,
66
+ * or if an anchor with that name is already present in the document,
67
+ * `name` will be used as a prefix for a new unique anchor.
68
+ * If `name` is undefined, the generated anchor will use 'a' as a prefix.
64
69
  */
65
- createNode(value, { flow, keepUndefined, onTagObj, replacer, tag } = {}) {
66
- if (typeof replacer === 'function')
70
+ createAlias(node, name) {
71
+ if (!node.anchor) {
72
+ const prev = anchorNames(this);
73
+ node.anchor =
74
+ !name || prev.has(name) ? findNewAnchor(name || 'a', prev) : name;
75
+ }
76
+ return new Alias(node.anchor);
77
+ }
78
+ createNode(value, replacer, options) {
79
+ let _replacer = undefined;
80
+ if (typeof replacer === 'function') {
67
81
  value = replacer.call({ '': value }, '', value);
82
+ _replacer = replacer;
83
+ }
68
84
  else if (Array.isArray(replacer)) {
69
85
  const keyToStr = (v) => typeof v === 'number' || v instanceof String || v instanceof Number;
70
86
  const asStr = replacer.filter(keyToStr).map(String);
71
87
  if (asStr.length > 0)
72
88
  replacer = replacer.concat(asStr);
89
+ _replacer = replacer;
90
+ }
91
+ else if (options === undefined && replacer) {
92
+ options = replacer;
93
+ replacer = undefined;
73
94
  }
74
- if (typeof keepUndefined !== 'boolean')
75
- keepUndefined = !!this.options.keepUndefined;
76
- const aliasNodes = [];
95
+ const { anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
96
+ const { onAnchor, setAnchors, sourceObjects } = createNodeAnchors(this, anchorPrefix || 'a');
77
97
  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
- },
98
+ keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
99
+ onAnchor,
85
100
  onTagObj,
86
- prevObjects: new Map(),
87
- replacer,
88
- schema: this.schema
101
+ replacer: _replacer,
102
+ schema: this.schema,
103
+ sourceObjects
89
104
  };
90
105
  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
106
  if (flow && isCollection(node))
103
107
  node.flow = true;
108
+ setAnchors();
104
109
  return node;
105
110
  }
106
111
  /**
@@ -108,8 +113,8 @@ class Document {
108
113
  * recursively wrapping all values as `Scalar` or `Collection` nodes.
109
114
  */
110
115
  createPair(key, value, options = {}) {
111
- const k = this.createNode(key, options);
112
- const v = this.createNode(value, options);
116
+ const k = this.createNode(key, null, options);
117
+ const v = this.createNode(value, null, options);
113
118
  return new Pair(k, v);
114
119
  }
115
120
  /**
@@ -223,10 +228,8 @@ class Document {
223
228
  }
224
229
  // json & jsonArg are only used from toJSON()
225
230
  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
231
  const ctx = {
229
- anchors,
232
+ anchors: new Map(),
230
233
  doc: this,
231
234
  keep: !json,
232
235
  mapAsMap: mapAsMap === true,
@@ -235,8 +238,8 @@ class Document {
235
238
  stringify
236
239
  };
237
240
  const res = toJS(this.contents, jsonArg || '', ctx);
238
- if (typeof onAnchor === 'function' && anchors)
239
- for (const { count, res } of anchors.values())
241
+ if (typeof onAnchor === 'function')
242
+ for (const { count, res } of ctx.anchors.values())
240
243
  onAnchor(res, count);
241
244
  return typeof reviver === 'function'
242
245
  ? applyReviver(reviver, { '': res }, '', res)
@@ -260,59 +263,7 @@ class Document {
260
263
  const s = JSON.stringify(options.indent);
261
264
  throw new Error(`"indent" option must be a positive integer, not ${s}`);
262
265
  }
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';
266
+ return stringifyDocument(this, options);
316
267
  }
317
268
  }
318
269
  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 };