yaml 2.0.0-7 → 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.
@@ -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;
@@ -135,6 +135,12 @@ function doubleQuotedValue(source, onError) {
135
135
  while (next === ' ' || next === '\t')
136
136
  next = source[++i + 1];
137
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
+ }
138
144
  else if (next === 'x' || next === 'u' || next === 'U') {
139
145
  const length = { x: 2, u: 4, U: 8 }[next];
140
146
  res += parseCharCode(source, i + 1, length, onError);
@@ -152,7 +158,7 @@ function doubleQuotedValue(source, onError) {
152
158
  let next = source[i + 1];
153
159
  while (next === ' ' || next === '\t')
154
160
  next = source[++i + 1];
155
- if (next !== '\n')
161
+ if (next !== '\n' && !(next === '\r' && source[i + 2] === '\n'))
156
162
  res += i > wsStart ? source.slice(wsStart, i + 1) : ch;
157
163
  }
158
164
  else {
@@ -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)
@@ -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.
@@ -1,5 +1,5 @@
1
1
  import { createNode } from '../doc/createNode.js';
2
- import { NodeBase, isCollection, isScalar, isPair } from './Node.js';
2
+ import { NodeBase, isNode, isPair, isCollection, isScalar } from './Node.js';
3
3
 
4
4
  function collectionFromPath(schema, path, value) {
5
5
  let v = value;
@@ -11,19 +11,14 @@ function collectionFromPath(schema, path, value) {
11
11
  v = a;
12
12
  }
13
13
  else {
14
- const o = {};
15
- Object.defineProperty(o, typeof k === 'symbol' ? k : String(k), {
16
- value: v,
17
- writable: true,
18
- enumerable: true,
19
- configurable: true
20
- });
21
- v = o;
14
+ v = new Map([[k, v]]);
22
15
  }
23
16
  }
24
17
  return createNode(v, undefined, {
25
- onAnchor() {
26
- throw new Error('Repeated objects are not supported here');
18
+ aliasDuplicateObjects: false,
19
+ keepUndefined: false,
20
+ onAnchor: () => {
21
+ throw new Error('This should not happen, please report a bug.');
27
22
  },
28
23
  schema,
29
24
  sourceObjects: new Map()
@@ -42,6 +37,20 @@ class Collection extends NodeBase {
42
37
  writable: true
43
38
  });
44
39
  }
40
+ /**
41
+ * Create a copy of this collection.
42
+ *
43
+ * @param schema - If defined, overwrites the original's schema
44
+ */
45
+ clone(schema) {
46
+ const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
47
+ if (schema)
48
+ copy.schema = schema;
49
+ copy.items = copy.items.map(it => isNode(it) || isPair(it) ? it.clone(schema) : it);
50
+ if (this.range)
51
+ copy.range = this.range.slice();
52
+ return copy;
53
+ }
45
54
  /**
46
55
  * Adds a value to the collection. For `!!map` and `!!omap` the value must
47
56
  * be a Pair instance or a `{ key, value }` object, which may not have a key
@@ -36,6 +36,13 @@ class NodeBase {
36
36
  constructor(type) {
37
37
  Object.defineProperty(this, NODE_TYPE, { value: type });
38
38
  }
39
+ /** Create a copy of this node. */
40
+ clone() {
41
+ const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
42
+ if (this.range)
43
+ copy.range = this.range.slice();
44
+ return copy;
45
+ }
39
46
  }
40
47
 
41
48
  export { ALIAS, DOC, MAP, NODE_TYPE, NodeBase, PAIR, SCALAR, SEQ, hasAnchor, isAlias, isCollection, isDocument, isMap, isNode, isPair, isScalar, isSeq };
@@ -1,7 +1,7 @@
1
1
  import { createNode } from '../doc/createNode.js';
2
2
  import { stringifyPair } from '../stringify/stringifyPair.js';
3
3
  import { addPairToJSMap } from './addPairToJSMap.js';
4
- import { NODE_TYPE, PAIR } from './Node.js';
4
+ import { NODE_TYPE, PAIR, isNode } from './Node.js';
5
5
 
6
6
  function createPair(key, value, ctx) {
7
7
  const k = createNode(key, undefined, ctx);
@@ -14,6 +14,14 @@ class Pair {
14
14
  this.key = key;
15
15
  this.value = value;
16
16
  }
17
+ clone(schema) {
18
+ let { key, value } = this;
19
+ if (isNode(key))
20
+ key = key.clone(schema);
21
+ if (isNode(value))
22
+ value = value.clone(schema);
23
+ return new Pair(key, value);
24
+ }
17
25
  toJSON(_, ctx) {
18
26
  const pair = ctx && ctx.mapAsMap ? new Map() : {};
19
27
  return addPairToJSMap(ctx, pair, this);
@@ -1,6 +1,6 @@
1
1
  import { warn } from '../log.js';
2
2
  import { createStringifyContext } from '../stringify/stringify.js';
3
- import { isScalar, isSeq, isAlias, isMap, isNode } from './Node.js';
3
+ import { isSeq, isScalar, isAlias, isMap, isNode } from './Node.js';
4
4
  import { Scalar } from './Scalar.js';
5
5
  import { toJS } from './toJS.js';
6
6
 
@@ -134,6 +134,8 @@ class Lexer {
134
134
  this.indentNext = 0;
135
135
  /** Indentation level of the current line. */
136
136
  this.indentValue = 0;
137
+ /** Position of the next \n character. */
138
+ this.lineEndPos = null;
137
139
  /** Stores the state of the lexer if reaching the end of incpomplete input */
138
140
  this.next = null;
139
141
  /** A pointer to `buffer`; the current position of the lexer. */
@@ -146,8 +148,10 @@ class Lexer {
146
148
  * @returns A generator of lexical tokens
147
149
  */
148
150
  *lex(source, incomplete = false) {
149
- if (source)
151
+ if (source) {
150
152
  this.buffer = this.buffer ? this.buffer + source : source;
153
+ this.lineEndPos = null;
154
+ }
151
155
  this.atEnd = !incomplete;
152
156
  let next = this.next || 'stream';
153
157
  while (next && (incomplete || this.hasChars(1)))
@@ -190,7 +194,11 @@ class Lexer {
190
194
  return offset;
191
195
  }
192
196
  getLine() {
193
- let end = this.buffer.indexOf('\n', this.pos);
197
+ let end = this.lineEndPos;
198
+ if (typeof end !== 'number' || (end !== -1 && end < this.pos)) {
199
+ end = this.buffer.indexOf('\n', this.pos);
200
+ this.lineEndPos = end;
201
+ }
194
202
  if (end === -1)
195
203
  return this.atEnd ? this.buffer.substring(this.pos) : null;
196
204
  if (this.buffer[end - 1] === '\r')
@@ -203,6 +211,7 @@ class Lexer {
203
211
  setNext(state) {
204
212
  this.buffer = this.buffer.substring(this.pos);
205
213
  this.pos = 0;
214
+ this.lineEndPos = null;
206
215
  this.next = state;
207
216
  return null;
208
217
  }
@@ -433,17 +442,19 @@ class Lexer {
433
442
  end = this.buffer.indexOf('"', end + 1);
434
443
  }
435
444
  }
436
- let nl = this.buffer.indexOf('\n', this.pos);
437
- if (nl !== -1 && nl < end) {
438
- while (nl !== -1 && nl < end) {
445
+ // Only looking for newlines within the quotes
446
+ const qb = this.buffer.substring(0, end);
447
+ let nl = qb.indexOf('\n', this.pos);
448
+ if (nl !== -1) {
449
+ while (nl !== -1) {
439
450
  const cs = this.continueScalar(nl + 1);
440
451
  if (cs === -1)
441
452
  break;
442
- nl = this.buffer.indexOf('\n', cs);
453
+ nl = qb.indexOf('\n', cs);
443
454
  }
444
- if (nl !== -1 && nl < end) {
455
+ if (nl !== -1) {
445
456
  // this is an error caused by an unexpected unindent
446
- end = nl - 1;
457
+ end = nl - (qb[nl - 1] === '\r' ? 2 : 1);
447
458
  }
448
459
  }
449
460
  if (end === -1) {
@@ -543,17 +554,18 @@ class Lexer {
543
554
  end = i;
544
555
  }
545
556
  else if (isEmpty(ch)) {
546
- const next = this.buffer[i + 1];
547
- if (next === '#' || (inFlow && invalidFlowScalarChars.includes(next)))
548
- break;
557
+ let next = this.buffer[i + 1];
549
558
  if (ch === '\r') {
550
559
  if (next === '\n') {
551
560
  i += 1;
552
561
  ch = '\n';
562
+ next = this.buffer[i + 1];
553
563
  }
554
564
  else
555
565
  end = i;
556
566
  }
567
+ if (next === '#' || (inFlow && invalidFlowScalarChars.includes(next)))
568
+ break;
557
569
  if (ch === '\n') {
558
570
  const cs = this.continueScalar(i + 1);
559
571
  if (cs === -1)
@@ -18,6 +18,11 @@ class Schema {
18
18
  this.sortMapEntries =
19
19
  sortMapEntries === true ? sortMapEntriesByKey : sortMapEntries || null;
20
20
  }
21
+ clone() {
22
+ const copy = Object.create(Schema.prototype, Object.getOwnPropertyDescriptors(this));
23
+ copy.tags = this.tags.slice();
24
+ return copy;
25
+ }
21
26
  }
22
27
 
23
28
  export { Schema };
@@ -21,7 +21,8 @@ function composeScalar(ctx, token, tagToken, onError) {
21
21
  scalar = Node.isScalar(res) ? res : new Scalar.Scalar(res);
22
22
  }
23
23
  catch (error) {
24
- onError(tagToken || token, 'TAG_RESOLVE_FAILED', error.message);
24
+ const msg = error instanceof Error ? error.message : String(error);
25
+ onError(tagToken || token, 'TAG_RESOLVE_FAILED', msg);
25
26
  scalar = new Scalar.Scalar(value);
26
27
  }
27
28
  scalar.range = range;
@@ -137,6 +137,12 @@ function doubleQuotedValue(source, onError) {
137
137
  while (next === ' ' || next === '\t')
138
138
  next = source[++i + 1];
139
139
  }
140
+ else if (next === '\r' && source[i + 1] === '\n') {
141
+ // skip escaped CRLF newlines, but still trim the following line
142
+ next = source[++i + 1];
143
+ while (next === ' ' || next === '\t')
144
+ next = source[++i + 1];
145
+ }
140
146
  else if (next === 'x' || next === 'u' || next === 'U') {
141
147
  const length = { x: 2, u: 4, U: 8 }[next];
142
148
  res += parseCharCode(source, i + 1, length, onError);
@@ -154,7 +160,7 @@ function doubleQuotedValue(source, onError) {
154
160
  let next = source[i + 1];
155
161
  while (next === ' ' || next === '\t')
156
162
  next = source[++i + 1];
157
- if (next !== '\n')
163
+ if (next !== '\n' && !(next === '\r' && source[i + 2] === '\n'))
158
164
  res += i > wsStart ? source.slice(wsStart, i + 1) : ch;
159
165
  }
160
166
  else {
@@ -43,6 +43,12 @@ export declare class Document<T = unknown> {
43
43
  */
44
44
  constructor(value?: any, options?: DocumentOptions & SchemaOptions & ParseOptions & CreateNodeOptions);
45
45
  constructor(value: any, replacer: null | Replacer, options?: DocumentOptions & SchemaOptions & ParseOptions & CreateNodeOptions);
46
+ /**
47
+ * Create a deep copy of this Document and its contents.
48
+ *
49
+ * Custom Node values that inherit from `Object` still refer to their original instances.
50
+ */
51
+ clone(): Document<T>;
46
52
  /** Adds a value to the document. */
47
53
  add(value: any): void;
48
54
  /** Adds a value to the document. */
@@ -50,6 +50,29 @@ class Document {
50
50
  this.contents = this.createNode(value, _replacer, options$1);
51
51
  }
52
52
  }
53
+ /**
54
+ * Create a deep copy of this Document and its contents.
55
+ *
56
+ * Custom Node values that inherit from `Object` still refer to their original instances.
57
+ */
58
+ clone() {
59
+ const copy = Object.create(Document.prototype, {
60
+ [Node.NODE_TYPE]: { value: Node.DOC }
61
+ });
62
+ copy.commentBefore = this.commentBefore;
63
+ copy.comment = this.comment;
64
+ copy.errors = this.errors.slice();
65
+ copy.warnings = this.warnings.slice();
66
+ copy.options = Object.assign({}, this.options);
67
+ copy.directives = this.directives.clone();
68
+ copy.schema = this.schema.clone();
69
+ copy.contents = Node.isNode(this.contents)
70
+ ? this.contents.clone(copy.schema)
71
+ : this.contents;
72
+ if (this.range)
73
+ copy.range = this.range.slice();
74
+ return copy;
75
+ }
53
76
  /** Adds a value to the document. */
54
77
  add(value) {
55
78
  if (assertCollection(this.contents))
@@ -94,9 +117,10 @@ class Document {
94
117
  options = replacer;
95
118
  replacer = undefined;
96
119
  }
97
- const { anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
120
+ const { aliasDuplicateObjects, anchorPrefix, flow, keepUndefined, onTagObj, tag } = options || {};
98
121
  const { onAnchor, setAnchors, sourceObjects } = anchors.createNodeAnchors(this, anchorPrefix || 'a');
99
122
  const ctx = {
123
+ aliasDuplicateObjects: aliasDuplicateObjects !== null && aliasDuplicateObjects !== void 0 ? aliasDuplicateObjects : true,
100
124
  keepUndefined: keepUndefined !== null && keepUndefined !== void 0 ? keepUndefined : false,
101
125
  onAnchor,
102
126
  onTagObj,
@@ -3,7 +3,8 @@ import type { Schema } from '../schema/Schema.js';
3
3
  import type { CollectionTag, ScalarTag } from '../schema/types.js';
4
4
  import type { Replacer } from './Document.js';
5
5
  export interface CreateNodeContext {
6
- keepUndefined?: boolean;
6
+ aliasDuplicateObjects: boolean;
7
+ keepUndefined: boolean;
7
8
  onAnchor(source: unknown): string;
8
9
  onTagObj?: (tagObj: ScalarTag | CollectionTag) => void;
9
10
  sourceObjects: Map<unknown, {
@@ -17,6 +17,8 @@ function findTagObject(value, tagName, tags) {
17
17
  }
18
18
  function createNode(value, tagName, ctx) {
19
19
  var _a, _b;
20
+ if (Node.isDocument(value))
21
+ value = value.contents;
20
22
  if (Node.isNode(value))
21
23
  return value;
22
24
  if (Node.isPair(value)) {
@@ -32,11 +34,11 @@ function createNode(value, tagName, ctx) {
32
34
  // https://tc39.es/ecma262/#sec-serializejsonproperty
33
35
  value = value.valueOf();
34
36
  }
35
- const { onAnchor, onTagObj, schema, sourceObjects } = ctx;
37
+ const { aliasDuplicateObjects, onAnchor, onTagObj, schema, sourceObjects } = ctx;
36
38
  // Detect duplicate references to the same object & use Alias nodes for all
37
39
  // after first. The `ref` wrapper allows for circular references to resolve.
38
40
  let ref = undefined;
39
- if (value && typeof value === 'object') {
41
+ if (aliasDuplicateObjects && value && typeof value === 'object') {
40
42
  ref = sourceObjects.get(value);
41
43
  if (ref) {
42
44
  if (!ref.anchor)
@@ -20,6 +20,7 @@ export declare class Directives {
20
20
  */
21
21
  private atNextDocument?;
22
22
  constructor(yaml?: Directives['yaml'], tags?: Directives['tags']);
23
+ clone(): Directives;
23
24
  /**
24
25
  * During parsing, get a Directives instance for the current document and
25
26
  * update the stream state according to the current version's spec.
@@ -22,6 +22,11 @@ class Directives {
22
22
  this.yaml = Object.assign({}, Directives.defaultYaml, yaml);
23
23
  this.tags = Object.assign({}, Directives.defaultTags, tags);
24
24
  }
25
+ clone() {
26
+ const copy = new Directives(this.yaml, this.tags);
27
+ copy.marker = this.marker;
28
+ return copy;
29
+ }
25
30
  /**
26
31
  * During parsing, get a Directives instance for the current document and
27
32
  * update the stream state according to the current version's spec.
@@ -15,6 +15,12 @@ export declare abstract class Collection extends NodeBase {
15
15
  */
16
16
  flow?: boolean;
17
17
  constructor(type: symbol, schema?: Schema);
18
+ /**
19
+ * Create a copy of this collection.
20
+ *
21
+ * @param schema - If defined, overwrites the original's schema
22
+ */
23
+ clone(schema?: Schema): Collection;
18
24
  /** Adds a value to the collection. */
19
25
  abstract add(value: unknown): void;
20
26
  /**
@@ -13,19 +13,14 @@ function collectionFromPath(schema, path, value) {
13
13
  v = a;
14
14
  }
15
15
  else {
16
- const o = {};
17
- Object.defineProperty(o, typeof k === 'symbol' ? k : String(k), {
18
- value: v,
19
- writable: true,
20
- enumerable: true,
21
- configurable: true
22
- });
23
- v = o;
16
+ v = new Map([[k, v]]);
24
17
  }
25
18
  }
26
19
  return createNode.createNode(v, undefined, {
27
- onAnchor() {
28
- throw new Error('Repeated objects are not supported here');
20
+ aliasDuplicateObjects: false,
21
+ keepUndefined: false,
22
+ onAnchor: () => {
23
+ throw new Error('This should not happen, please report a bug.');
29
24
  },
30
25
  schema,
31
26
  sourceObjects: new Map()
@@ -44,6 +39,20 @@ class Collection extends Node.NodeBase {
44
39
  writable: true
45
40
  });
46
41
  }
42
+ /**
43
+ * Create a copy of this collection.
44
+ *
45
+ * @param schema - If defined, overwrites the original's schema
46
+ */
47
+ clone(schema) {
48
+ const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
49
+ if (schema)
50
+ copy.schema = schema;
51
+ copy.items = copy.items.map(it => Node.isNode(it) || Node.isPair(it) ? it.clone(schema) : it);
52
+ if (this.range)
53
+ copy.range = this.range.slice();
54
+ return copy;
55
+ }
47
56
  /**
48
57
  * Adds a value to the collection. For `!!map` and `!!omap` the value must
49
58
  * be a Pair instance or a `{ key, value }` object, which may not have a key
@@ -23,7 +23,7 @@ export declare const isScalar: (node: any) => node is Scalar<unknown>;
23
23
  export declare const isSeq: (node: any) => node is YAMLSeq<unknown>;
24
24
  export declare function isCollection(node: any): node is YAMLMap | YAMLSeq;
25
25
  export declare function isNode(node: any): node is Node;
26
- export declare const hasAnchor: (node: unknown) => node is YAMLMap<unknown, unknown> | YAMLSeq<unknown> | Scalar<unknown>;
26
+ export declare const hasAnchor: (node: unknown) => node is Scalar<unknown> | YAMLMap<unknown, unknown> | YAMLSeq<unknown>;
27
27
  export declare abstract class NodeBase {
28
28
  readonly [NODE_TYPE]: symbol;
29
29
  /** A comment on or immediately after this */
@@ -45,4 +45,6 @@ export declare abstract class NodeBase {
45
45
  abstract toJSON(): any;
46
46
  abstract toString(ctx?: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string;
47
47
  constructor(type: symbol);
48
+ /** Create a copy of this node. */
49
+ clone(): NodeBase;
48
50
  }
@@ -38,6 +38,13 @@ class NodeBase {
38
38
  constructor(type) {
39
39
  Object.defineProperty(this, NODE_TYPE, { value: type });
40
40
  }
41
+ /** Create a copy of this node. */
42
+ clone() {
43
+ const copy = Object.create(Object.getPrototypeOf(this), Object.getOwnPropertyDescriptors(this));
44
+ if (this.range)
45
+ copy.range = this.range.slice();
46
+ return copy;
47
+ }
41
48
  }
42
49
 
43
50
  exports.ALIAS = ALIAS;
@@ -1,8 +1,9 @@
1
1
  import { CreateNodeContext } from '../doc/createNode.js';
2
- import { StringifyContext } from '../stringify/stringify.js';
2
+ import type { Schema } from '../schema/Schema.js';
3
+ import type { StringifyContext } from '../stringify/stringify.js';
3
4
  import { addPairToJSMap } from './addPairToJSMap.js';
4
5
  import { NODE_TYPE } from './Node.js';
5
- import { ToJSContext } from './toJS.js';
6
+ import type { ToJSContext } from './toJS.js';
6
7
  export declare function createPair(key: unknown, value: unknown, ctx: CreateNodeContext): Pair<import("./Node.js").Node, import("./Alias.js").Alias | import("./Scalar.js").Scalar<unknown> | import("./YAMLMap.js").YAMLMap<unknown, unknown> | import("./YAMLSeq.js").YAMLSeq<unknown>>;
7
8
  export declare class Pair<K = unknown, V = unknown> {
8
9
  readonly [NODE_TYPE]: symbol;
@@ -11,6 +12,7 @@ export declare class Pair<K = unknown, V = unknown> {
11
12
  /** Always Node or null when parsed, but can be set to anything. */
12
13
  value: V | null;
13
14
  constructor(key: K, value?: V | null);
15
+ clone(schema?: Schema): Pair<K, V>;
14
16
  toJSON(_?: unknown, ctx?: ToJSContext): ReturnType<typeof addPairToJSMap>;
15
17
  toString(ctx?: StringifyContext, onComment?: () => void, onChompKeep?: () => void): string;
16
18
  }
@@ -16,6 +16,14 @@ class Pair {
16
16
  this.key = key;
17
17
  this.value = value;
18
18
  }
19
+ clone(schema) {
20
+ let { key, value } = this;
21
+ if (Node.isNode(key))
22
+ key = key.clone(schema);
23
+ if (Node.isNode(value))
24
+ value = value.clone(schema);
25
+ return new Pair(key, value);
26
+ }
19
27
  toJSON(_, ctx) {
20
28
  const pair = ctx && ctx.mapAsMap ? new Map() : {};
21
29
  return addPairToJSMap.addPairToJSMap(ctx, pair, this);
package/dist/options.d.ts CHANGED
@@ -105,6 +105,13 @@ export declare type SchemaOptions = {
105
105
  sortMapEntries?: boolean | ((a: Pair, b: Pair) => number);
106
106
  };
107
107
  export declare type CreateNodeOptions = {
108
+ /**
109
+ * During node construction, use anchors and aliases to keep strictly equal
110
+ * non-null objects as equivalent in YAML.
111
+ *
112
+ * Default: `true`
113
+ */
114
+ aliasDuplicateObjects?: boolean;
108
115
  /**
109
116
  * Default prefix for anchors.
110
117
  *
@@ -47,6 +47,8 @@ export declare class Lexer {
47
47
  private indentNext;
48
48
  /** Indentation level of the current line. */
49
49
  private indentValue;
50
+ /** Position of the next \n character. */
51
+ private lineEndPos;
50
52
  /** Stores the state of the lexer if reaching the end of incpomplete input */
51
53
  private next;
52
54
  /** A pointer to `buffer`; the current position of the lexer. */
@@ -136,6 +136,8 @@ class Lexer {
136
136
  this.indentNext = 0;
137
137
  /** Indentation level of the current line. */
138
138
  this.indentValue = 0;
139
+ /** Position of the next \n character. */
140
+ this.lineEndPos = null;
139
141
  /** Stores the state of the lexer if reaching the end of incpomplete input */
140
142
  this.next = null;
141
143
  /** A pointer to `buffer`; the current position of the lexer. */
@@ -148,8 +150,10 @@ class Lexer {
148
150
  * @returns A generator of lexical tokens
149
151
  */
150
152
  *lex(source, incomplete = false) {
151
- if (source)
153
+ if (source) {
152
154
  this.buffer = this.buffer ? this.buffer + source : source;
155
+ this.lineEndPos = null;
156
+ }
153
157
  this.atEnd = !incomplete;
154
158
  let next = this.next || 'stream';
155
159
  while (next && (incomplete || this.hasChars(1)))
@@ -192,7 +196,11 @@ class Lexer {
192
196
  return offset;
193
197
  }
194
198
  getLine() {
195
- let end = this.buffer.indexOf('\n', this.pos);
199
+ let end = this.lineEndPos;
200
+ if (typeof end !== 'number' || (end !== -1 && end < this.pos)) {
201
+ end = this.buffer.indexOf('\n', this.pos);
202
+ this.lineEndPos = end;
203
+ }
196
204
  if (end === -1)
197
205
  return this.atEnd ? this.buffer.substring(this.pos) : null;
198
206
  if (this.buffer[end - 1] === '\r')
@@ -205,6 +213,7 @@ class Lexer {
205
213
  setNext(state) {
206
214
  this.buffer = this.buffer.substring(this.pos);
207
215
  this.pos = 0;
216
+ this.lineEndPos = null;
208
217
  this.next = state;
209
218
  return null;
210
219
  }
@@ -435,17 +444,19 @@ class Lexer {
435
444
  end = this.buffer.indexOf('"', end + 1);
436
445
  }
437
446
  }
438
- let nl = this.buffer.indexOf('\n', this.pos);
439
- if (nl !== -1 && nl < end) {
440
- while (nl !== -1 && nl < end) {
447
+ // Only looking for newlines within the quotes
448
+ const qb = this.buffer.substring(0, end);
449
+ let nl = qb.indexOf('\n', this.pos);
450
+ if (nl !== -1) {
451
+ while (nl !== -1) {
441
452
  const cs = this.continueScalar(nl + 1);
442
453
  if (cs === -1)
443
454
  break;
444
- nl = this.buffer.indexOf('\n', cs);
455
+ nl = qb.indexOf('\n', cs);
445
456
  }
446
- if (nl !== -1 && nl < end) {
457
+ if (nl !== -1) {
447
458
  // this is an error caused by an unexpected unindent
448
- end = nl - 1;
459
+ end = nl - (qb[nl - 1] === '\r' ? 2 : 1);
449
460
  }
450
461
  }
451
462
  if (end === -1) {
@@ -545,17 +556,18 @@ class Lexer {
545
556
  end = i;
546
557
  }
547
558
  else if (isEmpty(ch)) {
548
- const next = this.buffer[i + 1];
549
- if (next === '#' || (inFlow && invalidFlowScalarChars.includes(next)))
550
- break;
559
+ let next = this.buffer[i + 1];
551
560
  if (ch === '\r') {
552
561
  if (next === '\n') {
553
562
  i += 1;
554
563
  ch = '\n';
564
+ next = this.buffer[i + 1];
555
565
  }
556
566
  else
557
567
  end = i;
558
568
  }
569
+ if (next === '#' || (inFlow && invalidFlowScalarChars.includes(next)))
570
+ break;
559
571
  if (ch === '\n') {
560
572
  const cs = this.continueScalar(i + 1);
561
573
  if (cs === -1)
@@ -13,4 +13,5 @@ export declare class Schema {
13
13
  [SCALAR]: ScalarTag;
14
14
  [SEQ]: CollectionTag;
15
15
  constructor({ customTags, merge, resolveKnownTags, schema, sortMapEntries }: SchemaOptions);
16
+ clone(): Schema;
16
17
  }
@@ -20,6 +20,11 @@ class Schema {
20
20
  this.sortMapEntries =
21
21
  sortMapEntries === true ? sortMapEntriesByKey : sortMapEntries || null;
22
22
  }
23
+ clone() {
24
+ const copy = Object.create(Schema.prototype, Object.getOwnPropertyDescriptors(this));
25
+ copy.tags = this.tags.slice();
26
+ return copy;
27
+ }
23
28
  }
24
29
 
25
30
  exports.Schema = Schema;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml",
3
- "version": "2.0.0-7",
3
+ "version": "2.0.0-8",
4
4
  "license": "ISC",
5
5
  "author": "Eemeli Aro <eemeli@gmail.com>",
6
6
  "repository": "github:eemeli/yaml",
@@ -70,7 +70,7 @@
70
70
  "@rollup/plugin-babel": "^5.2.3",
71
71
  "@rollup/plugin-replace": "^2.3.4",
72
72
  "@rollup/plugin-typescript": "^8.1.1",
73
- "@types/jest": "^26.0.20",
73
+ "@types/jest": "^27.0.1",
74
74
  "@types/node": "^15.6.1",
75
75
  "@typescript-eslint/eslint-plugin": "^4.15.2",
76
76
  "@typescript-eslint/parser": "^4.15.2",
@@ -84,7 +84,7 @@
84
84
  "prettier": "^2.2.1",
85
85
  "rollup": "^2.38.2",
86
86
  "tslib": "^2.1.0",
87
- "typescript": "^4.1.3"
87
+ "typescript": "^4.3.5"
88
88
  },
89
89
  "engines": {
90
90
  "node": ">= 12"