yaml 2.4.4 → 2.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/browser/dist/compose/compose-collection.js +15 -3
  2. package/browser/dist/compose/compose-doc.js +1 -0
  3. package/browser/dist/compose/compose-node.js +1 -1
  4. package/browser/dist/compose/compose-scalar.js +1 -1
  5. package/browser/dist/compose/resolve-block-map.js +3 -1
  6. package/browser/dist/compose/resolve-block-scalar.js +6 -2
  7. package/browser/dist/compose/resolve-block-seq.js +1 -0
  8. package/browser/dist/compose/resolve-flow-collection.js +2 -0
  9. package/browser/dist/compose/resolve-props.js +12 -9
  10. package/browser/dist/nodes/Collection.js +0 -1
  11. package/browser/dist/parse/cst-scalar.js +1 -1
  12. package/browser/dist/parse/lexer.js +15 -8
  13. package/browser/dist/stringify/foldFlowLines.js +2 -0
  14. package/dist/cli.d.ts +1 -2
  15. package/dist/cli.mjs +8 -4
  16. package/dist/compose/compose-collection.d.ts +7 -1
  17. package/dist/compose/compose-collection.js +15 -3
  18. package/dist/compose/compose-doc.js +1 -0
  19. package/dist/compose/compose-node.d.ts +1 -0
  20. package/dist/compose/compose-node.js +1 -1
  21. package/dist/compose/compose-scalar.js +1 -1
  22. package/dist/compose/resolve-block-map.d.ts +1 -2
  23. package/dist/compose/resolve-block-map.js +3 -1
  24. package/dist/compose/resolve-block-scalar.d.ts +2 -1
  25. package/dist/compose/resolve-block-scalar.js +6 -2
  26. package/dist/compose/resolve-block-seq.d.ts +1 -1
  27. package/dist/compose/resolve-block-seq.js +1 -0
  28. package/dist/compose/resolve-flow-collection.js +2 -0
  29. package/dist/compose/resolve-props.d.ts +3 -2
  30. package/dist/compose/resolve-props.js +12 -9
  31. package/dist/nodes/Collection.d.ts +0 -1
  32. package/dist/nodes/Collection.js +0 -1
  33. package/dist/nodes/identity.d.ts +1 -1
  34. package/dist/parse/cst-scalar.js +1 -1
  35. package/dist/parse/lexer.js +15 -8
  36. package/dist/schema/yaml-1.1/omap.d.ts +3 -3
  37. package/dist/stringify/foldFlowLines.js +2 -0
  38. package/package.json +1 -1
@@ -23,10 +23,23 @@ function resolveCollection(CN, ctx, token, onError, tagName, tag) {
23
23
  coll.tag = tagName;
24
24
  return coll;
25
25
  }
26
- function composeCollection(CN, ctx, token, tagToken, onError) {
26
+ function composeCollection(CN, ctx, token, props, onError) {
27
+ const tagToken = props.tag;
27
28
  const tagName = !tagToken
28
29
  ? null
29
30
  : ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg));
31
+ if (token.type === 'block-seq') {
32
+ const { anchor, newlineAfterProp: nl } = props;
33
+ const lastProp = anchor && tagToken
34
+ ? anchor.offset > tagToken.offset
35
+ ? anchor
36
+ : tagToken
37
+ : (anchor ?? tagToken);
38
+ if (lastProp && (!nl || nl.offset < lastProp.offset)) {
39
+ const message = 'Missing newline after block sequence props';
40
+ onError(lastProp, 'MISSING_CHAR', message);
41
+ }
42
+ }
30
43
  const expType = token.type === 'block-map'
31
44
  ? 'map'
32
45
  : token.type === 'block-seq'
@@ -40,8 +53,7 @@ function composeCollection(CN, ctx, token, tagToken, onError) {
40
53
  !tagName ||
41
54
  tagName === '!' ||
42
55
  (tagName === YAMLMap.tagName && expType === 'map') ||
43
- (tagName === YAMLSeq.tagName && expType === 'seq') ||
44
- !expType) {
56
+ (tagName === YAMLSeq.tagName && expType === 'seq')) {
45
57
  return resolveCollection(CN, ctx, token, onError, tagName);
46
58
  }
47
59
  let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);
@@ -17,6 +17,7 @@ function composeDoc(options, directives, { offset, start, value, end }, onError)
17
17
  next: value ?? end?.[0],
18
18
  offset,
19
19
  onError,
20
+ parentIndent: 0,
20
21
  startOnNewline: true
21
22
  });
22
23
  if (props.found) {
@@ -26,7 +26,7 @@ function composeNode(ctx, token, props, onError) {
26
26
  case 'block-map':
27
27
  case 'block-seq':
28
28
  case 'flow-collection':
29
- node = composeCollection(CN, ctx, token, tag, onError);
29
+ node = composeCollection(CN, ctx, token, props, onError);
30
30
  if (anchor)
31
31
  node.anchor = anchor.source.substring(1);
32
32
  break;
@@ -5,7 +5,7 @@ import { resolveFlowScalar } from './resolve-flow-scalar.js';
5
5
 
6
6
  function composeScalar(ctx, token, tagToken, onError) {
7
7
  const { value, type, comment, range } = token.type === 'block-scalar'
8
- ? resolveBlockScalar(token, ctx.options.strict, onError)
8
+ ? resolveBlockScalar(ctx, token, onError)
9
9
  : resolveFlowScalar(token, ctx.options.strict, onError);
10
10
  const tagName = tagToken
11
11
  ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
@@ -21,6 +21,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
21
21
  next: key ?? sep?.[0],
22
22
  offset,
23
23
  onError,
24
+ parentIndent: bm.indent,
24
25
  startOnNewline: true
25
26
  });
26
27
  const implicitKey = !keyProps.found;
@@ -41,7 +42,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
41
42
  }
42
43
  continue;
43
44
  }
44
- if (keyProps.hasNewlineAfterProp || containsNewline(key)) {
45
+ if (keyProps.newlineAfterProp || containsNewline(key)) {
45
46
  onError(key ?? start[start.length - 1], 'MULTILINE_IMPLICIT_KEY', 'Implicit keys need to be on a single line');
46
47
  }
47
48
  }
@@ -63,6 +64,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
63
64
  next: value,
64
65
  offset: keyNode.range[2],
65
66
  onError,
67
+ parentIndent: bm.indent,
66
68
  startOnNewline: !key || key.type === 'block-scalar'
67
69
  });
68
70
  offset = valueProps.end;
@@ -1,8 +1,8 @@
1
1
  import { Scalar } from '../nodes/Scalar.js';
2
2
 
3
- function resolveBlockScalar(scalar, strict, onError) {
3
+ function resolveBlockScalar(ctx, scalar, onError) {
4
4
  const start = scalar.offset;
5
- const header = parseBlockScalarHeader(scalar, strict, onError);
5
+ const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError);
6
6
  if (!header)
7
7
  return { value: '', type: null, comment: '', range: [start, start, start] };
8
8
  const type = header.mode === '>' ? Scalar.BLOCK_FOLDED : Scalar.BLOCK_LITERAL;
@@ -44,6 +44,10 @@ function resolveBlockScalar(scalar, strict, onError) {
44
44
  if (header.indent === 0)
45
45
  trimIndent = indent.length;
46
46
  contentStart = i;
47
+ if (trimIndent === 0 && !ctx.atRoot) {
48
+ const message = 'Block scalar values in collections must be indented';
49
+ onError(offset, 'BAD_INDENT', message);
50
+ }
47
51
  break;
48
52
  }
49
53
  offset += indent.length + content.length + 1;
@@ -15,6 +15,7 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, ta
15
15
  next: value,
16
16
  offset,
17
17
  onError,
18
+ parentIndent: bs.indent,
18
19
  startOnNewline: true
19
20
  });
20
21
  if (!props.found) {
@@ -28,6 +28,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
28
28
  next: key ?? sep?.[0],
29
29
  offset,
30
30
  onError,
31
+ parentIndent: fc.indent,
31
32
  startOnNewline: false
32
33
  });
33
34
  if (!props.found) {
@@ -109,6 +110,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
109
110
  next: value,
110
111
  offset: keyNode.range[2],
111
112
  onError,
113
+ parentIndent: fc.indent,
112
114
  startOnNewline: false
113
115
  });
114
116
  if (valueProps.found) {
@@ -1,15 +1,15 @@
1
- function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
1
+ function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
2
2
  let spaceBefore = false;
3
3
  let atNewline = startOnNewline;
4
4
  let hasSpace = startOnNewline;
5
5
  let comment = '';
6
6
  let commentSep = '';
7
7
  let hasNewline = false;
8
- let hasNewlineAfterProp = false;
9
8
  let reqSpace = false;
10
9
  let tab = null;
11
10
  let anchor = null;
12
11
  let tag = null;
12
+ let newlineAfterProp = null;
13
13
  let comma = null;
14
14
  let found = null;
15
15
  let start = null;
@@ -22,7 +22,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
22
22
  reqSpace = false;
23
23
  }
24
24
  if (tab) {
25
- if (token.type !== 'comment') {
25
+ if (atNewline && token.type !== 'comment' && token.type !== 'newline') {
26
26
  onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
27
27
  }
28
28
  tab = null;
@@ -33,9 +33,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
33
33
  // as leading white space rather than indentation.
34
34
  // In a flow collection, only the parser handles indent.
35
35
  if (!flow &&
36
- atNewline &&
37
36
  (indicator !== 'doc-start' || next?.type !== 'flow-collection') &&
38
- token.source[0] === '\t') {
37
+ token.source.includes('\t')) {
39
38
  tab = token;
40
39
  }
41
40
  hasSpace = true;
@@ -64,7 +63,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
64
63
  atNewline = true;
65
64
  hasNewline = true;
66
65
  if (anchor || tag)
67
- hasNewlineAfterProp = true;
66
+ newlineAfterProp = token;
68
67
  hasSpace = true;
69
68
  break;
70
69
  case 'anchor':
@@ -97,7 +96,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
97
96
  if (found)
98
97
  onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
99
98
  found = token;
100
- atNewline = false;
99
+ atNewline =
100
+ indicator === 'seq-item-ind' || indicator === 'explicit-key-ind';
101
101
  hasSpace = false;
102
102
  break;
103
103
  case 'comma':
@@ -126,7 +126,10 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
126
126
  (next.type !== 'scalar' || next.source !== '')) {
127
127
  onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
128
128
  }
129
- if (tab)
129
+ if (tab &&
130
+ ((atNewline && tab.indent <= parentIndent) ||
131
+ next?.type === 'block-map' ||
132
+ next?.type === 'block-seq'))
130
133
  onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
131
134
  return {
132
135
  comma,
@@ -134,9 +137,9 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
134
137
  spaceBefore,
135
138
  comment,
136
139
  hasNewline,
137
- hasNewlineAfterProp,
138
140
  anchor,
139
141
  tag,
142
+ newlineAfterProp,
140
143
  end,
141
144
  start: start ?? end
142
145
  };
@@ -143,6 +143,5 @@ class Collection extends NodeBase {
143
143
  }
144
144
  }
145
145
  }
146
- Collection.maxFlowStringSingleLineLength = 60;
147
146
 
148
147
  export { Collection, collectionFromPath, isEmptyPath };
@@ -18,7 +18,7 @@ function resolveAsScalar(token, strict = true, onError) {
18
18
  case 'double-quoted-scalar':
19
19
  return resolveFlowScalar(token, strict, _onError);
20
20
  case 'block-scalar':
21
- return resolveBlockScalar(token, strict, _onError);
21
+ return resolveBlockScalar({ options: { strict } }, token, _onError);
22
22
  }
23
23
  }
24
24
  return null;
@@ -290,15 +290,11 @@ class Lexer {
290
290
  if (!this.atEnd && !this.hasChars(4))
291
291
  return this.setNext('line-start');
292
292
  const s = this.peek(3);
293
- if (s === '---' && isEmpty(this.charAt(3))) {
293
+ if ((s === '---' || s === '...') && isEmpty(this.charAt(3))) {
294
294
  yield* this.pushCount(3);
295
295
  this.indentValue = 0;
296
296
  this.indentNext = 0;
297
- return 'doc';
298
- }
299
- else if (s === '...' && isEmpty(this.charAt(3))) {
300
- yield* this.pushCount(3);
301
- return 'stream';
297
+ return s === '---' ? 'doc' : 'stream';
302
298
  }
303
299
  }
304
300
  this.indentValue = yield* this.pushSpaces(false);
@@ -541,14 +537,25 @@ class Lexer {
541
537
  nl = this.buffer.length;
542
538
  }
543
539
  }
544
- if (!this.blockScalarKeep) {
540
+ // Trailing insufficiently indented tabs are invalid.
541
+ // To catch that during parsing, we include them in the block scalar value.
542
+ let i = nl + 1;
543
+ ch = this.buffer[i];
544
+ while (ch === ' ')
545
+ ch = this.buffer[++i];
546
+ if (ch === '\t') {
547
+ while (ch === '\t' || ch === ' ' || ch === '\r' || ch === '\n')
548
+ ch = this.buffer[++i];
549
+ nl = i - 1;
550
+ }
551
+ else if (!this.blockScalarKeep) {
545
552
  do {
546
553
  let i = nl - 1;
547
554
  let ch = this.buffer[i];
548
555
  if (ch === '\r')
549
556
  ch = this.buffer[--i];
550
557
  const lastChar = i; // Drop the line if last char not more indented
551
- while (ch === ' ' || ch === '\t')
558
+ while (ch === ' ')
552
559
  ch = this.buffer[--i];
553
560
  if (ch === '\n' && i >= this.pos && i + 1 + indent > lastChar)
554
561
  nl = i;
@@ -9,6 +9,8 @@ const FOLD_QUOTED = 'quoted';
9
9
  function foldFlowLines(text, indent, mode = 'flow', { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
10
10
  if (!lineWidth || lineWidth < 0)
11
11
  return text;
12
+ if (lineWidth < minContentWidth)
13
+ minContentWidth = 0;
12
14
  const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
13
15
  if (text.length <= endStep)
14
16
  return text;
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,4 @@
1
- /// <reference types="node" />
2
- export declare const help = "yaml: A command-line YAML processor and inspector\n\nReads stdin and writes output to stdout and errors & warnings to stderr.\n\nUsage:\n yaml Process a YAML stream, outputting it as YAML\n yaml cst Parse the CST of a YAML stream\n yaml lex Parse the lexical tokens of a YAML stream\n yaml valid Validate a YAML stream, returning 0 on success\n\nOptions:\n --help, -h Show this message.\n --json, -j Output JSON.\n\nAdditional options for bare \"yaml\" command:\n --doc, -d Output pretty-printed JS Document objects.\n --single, -1 Require the input to consist of a single YAML document.\n --strict, -s Stop on errors.\n --visit, -v Apply a visitor to each document (requires a path to import)\n --yaml 1.1 Set the YAML version. (default: 1.2)";
1
+ export declare const help = "yaml: A command-line YAML processor and inspector\n\nReads stdin and writes output to stdout and errors & warnings to stderr.\n\nUsage:\n yaml Process a YAML stream, outputting it as YAML\n yaml cst Parse the CST of a YAML stream\n yaml lex Parse the lexical tokens of a YAML stream\n yaml valid Validate a YAML stream, returning 0 on success\n\nOptions:\n --help, -h Show this message.\n --json, -j Output JSON.\n --indent 2 Output pretty-printed data, indented by the given number of spaces.\n\nAdditional options for bare \"yaml\" command:\n --doc, -d Output pretty-printed JS Document objects.\n --single, -1 Require the input to consist of a single YAML document.\n --strict, -s Stop on errors.\n --visit, -v Apply a visitor to each document (requires a path to import)\n --yaml 1.1 Set the YAML version. (default: 1.2)";
3
2
  export declare class UserError extends Error {
4
3
  static ARGS: number;
5
4
  static SINGLE: number;
package/dist/cli.mjs CHANGED
@@ -22,6 +22,7 @@ Usage:
22
22
  Options:
23
23
  --help, -h Show this message.
24
24
  --json, -j Output JSON.
25
+ --indent 2 Output pretty-printed data, indented by the given number of spaces.
25
26
 
26
27
  Additional options for bare "yaml" command:
27
28
  --doc, -d Output pretty-printed JS Document objects.
@@ -46,6 +47,7 @@ async function cli(stdin, done, argv) {
46
47
  options: {
47
48
  doc: { type: 'boolean', short: 'd' },
48
49
  help: { type: 'boolean', short: 'h' },
50
+ indent: { type: 'string', short: 'i' },
49
51
  json: { type: 'boolean', short: 'j' },
50
52
  single: { type: 'boolean', short: '1' },
51
53
  strict: { type: 'boolean', short: 's' },
@@ -58,6 +60,7 @@ async function cli(stdin, done, argv) {
58
60
  return done(new UserError(UserError.ARGS, error.message));
59
61
  }
60
62
  const { positionals: [mode], values: opt } = args;
63
+ let indent = Number(opt.indent);
61
64
  stdin.setEncoding('utf-8');
62
65
  // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
63
66
  switch (opt.help || mode) {
@@ -82,7 +85,7 @@ async function cli(stdin, done, argv) {
82
85
  for (const tok of lexer.lex('', false))
83
86
  add(tok);
84
87
  if (opt.json)
85
- console.log(JSON.stringify(data));
88
+ console.log(JSON.stringify(data, null, indent));
86
89
  done();
87
90
  });
88
91
  break;
@@ -104,7 +107,7 @@ async function cli(stdin, done, argv) {
104
107
  for (const tok of parser.parse('', false))
105
108
  add(tok);
106
109
  if (opt.json)
107
- console.log(JSON.stringify(data));
110
+ console.log(JSON.stringify(data, null, indent));
108
111
  done();
109
112
  });
110
113
  break;
@@ -153,7 +156,8 @@ async function cli(stdin, done, argv) {
153
156
  if (reqDocEnd)
154
157
  console.log('...');
155
158
  try {
156
- const str = String(doc);
159
+ indent || (indent = 2);
160
+ const str = doc.toString({ indent });
157
161
  console.log(str.endsWith('\n') ? str.slice(0, -1) : str);
158
162
  }
159
163
  catch (error) {
@@ -181,7 +185,7 @@ async function cli(stdin, done, argv) {
181
185
  return done(new UserError(UserError.SINGLE, 'Input stream contained no documents'));
182
186
  }
183
187
  if (mode !== 'valid' && opt.json) {
184
- console.log(JSON.stringify(opt.single ? data[0] : data));
188
+ console.log(JSON.stringify(opt.single ? data[0] : data, null, indent));
185
189
  }
186
190
  done();
187
191
  });
@@ -2,4 +2,10 @@ import type { ParsedNode } from '../nodes/Node.js';
2
2
  import type { BlockMap, BlockSequence, FlowCollection, SourceToken } from '../parse/cst.js';
3
3
  import type { ComposeContext, ComposeNode } from './compose-node.js';
4
4
  import type { ComposeErrorHandler } from './composer.js';
5
- export declare function composeCollection(CN: ComposeNode, ctx: ComposeContext, token: BlockMap | BlockSequence | FlowCollection, tagToken: SourceToken | null, onError: ComposeErrorHandler): ParsedNode;
5
+ interface Props {
6
+ anchor: SourceToken | null;
7
+ tag: SourceToken | null;
8
+ newlineAfterProp: SourceToken | null;
9
+ }
10
+ export declare function composeCollection(CN: ComposeNode, ctx: ComposeContext, token: BlockMap | BlockSequence | FlowCollection, props: Props, onError: ComposeErrorHandler): ParsedNode;
11
+ export {};
@@ -25,10 +25,23 @@ function resolveCollection(CN, ctx, token, onError, tagName, tag) {
25
25
  coll.tag = tagName;
26
26
  return coll;
27
27
  }
28
- function composeCollection(CN, ctx, token, tagToken, onError) {
28
+ function composeCollection(CN, ctx, token, props, onError) {
29
+ const tagToken = props.tag;
29
30
  const tagName = !tagToken
30
31
  ? null
31
32
  : ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg));
33
+ if (token.type === 'block-seq') {
34
+ const { anchor, newlineAfterProp: nl } = props;
35
+ const lastProp = anchor && tagToken
36
+ ? anchor.offset > tagToken.offset
37
+ ? anchor
38
+ : tagToken
39
+ : (anchor ?? tagToken);
40
+ if (lastProp && (!nl || nl.offset < lastProp.offset)) {
41
+ const message = 'Missing newline after block sequence props';
42
+ onError(lastProp, 'MISSING_CHAR', message);
43
+ }
44
+ }
32
45
  const expType = token.type === 'block-map'
33
46
  ? 'map'
34
47
  : token.type === 'block-seq'
@@ -42,8 +55,7 @@ function composeCollection(CN, ctx, token, tagToken, onError) {
42
55
  !tagName ||
43
56
  tagName === '!' ||
44
57
  (tagName === YAMLMap.YAMLMap.tagName && expType === 'map') ||
45
- (tagName === YAMLSeq.YAMLSeq.tagName && expType === 'seq') ||
46
- !expType) {
58
+ (tagName === YAMLSeq.YAMLSeq.tagName && expType === 'seq')) {
47
59
  return resolveCollection(CN, ctx, token, onError, tagName);
48
60
  }
49
61
  let tag = ctx.schema.tags.find(t => t.tag === tagName && t.collection === expType);
@@ -19,6 +19,7 @@ function composeDoc(options, directives, { offset, start, value, end }, onError)
19
19
  next: value ?? end?.[0],
20
20
  offset,
21
21
  onError,
22
+ parentIndent: 0,
22
23
  startOnNewline: true
23
24
  });
24
25
  if (props.found) {
@@ -15,6 +15,7 @@ interface Props {
15
15
  comment: string;
16
16
  anchor: SourceToken | null;
17
17
  tag: SourceToken | null;
18
+ newlineAfterProp: SourceToken | null;
18
19
  end: number;
19
20
  }
20
21
  declare const CN: {
@@ -28,7 +28,7 @@ function composeNode(ctx, token, props, onError) {
28
28
  case 'block-map':
29
29
  case 'block-seq':
30
30
  case 'flow-collection':
31
- node = composeCollection.composeCollection(CN, ctx, token, tag, onError);
31
+ node = composeCollection.composeCollection(CN, ctx, token, props, onError);
32
32
  if (anchor)
33
33
  node.anchor = anchor.source.substring(1);
34
34
  break;
@@ -7,7 +7,7 @@ var resolveFlowScalar = require('./resolve-flow-scalar.js');
7
7
 
8
8
  function composeScalar(ctx, token, tagToken, onError) {
9
9
  const { value, type, comment, range } = token.type === 'block-scalar'
10
- ? resolveBlockScalar.resolveBlockScalar(token, ctx.options.strict, onError)
10
+ ? resolveBlockScalar.resolveBlockScalar(ctx, token, onError)
11
11
  : resolveFlowScalar.resolveFlowScalar(token, ctx.options.strict, onError);
12
12
  const tagName = tagToken
13
13
  ? ctx.directives.tagName(tagToken.source, msg => onError(tagToken, 'TAG_RESOLVE_FAILED', msg))
@@ -1,7 +1,6 @@
1
- import type { ParsedNode } from '../nodes/Node.js';
2
1
  import { YAMLMap } from '../nodes/YAMLMap.js';
3
2
  import type { BlockMap } from '../parse/cst.js';
4
3
  import { CollectionTag } from '../schema/types.js';
5
4
  import type { ComposeContext, ComposeNode } from './compose-node.js';
6
5
  import type { ComposeErrorHandler } from './composer.js';
7
- export declare function resolveBlockMap({ composeNode, composeEmptyNode }: ComposeNode, ctx: ComposeContext, bm: BlockMap, onError: ComposeErrorHandler, tag?: CollectionTag): YAMLMap.Parsed<ParsedNode, ParsedNode | null>;
6
+ export declare function resolveBlockMap({ composeNode, composeEmptyNode }: ComposeNode, ctx: ComposeContext, bm: BlockMap, onError: ComposeErrorHandler, tag?: CollectionTag): YAMLMap.Parsed;
@@ -23,6 +23,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
23
23
  next: key ?? sep?.[0],
24
24
  offset,
25
25
  onError,
26
+ parentIndent: bm.indent,
26
27
  startOnNewline: true
27
28
  });
28
29
  const implicitKey = !keyProps.found;
@@ -43,7 +44,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
43
44
  }
44
45
  continue;
45
46
  }
46
- if (keyProps.hasNewlineAfterProp || utilContainsNewline.containsNewline(key)) {
47
+ if (keyProps.newlineAfterProp || utilContainsNewline.containsNewline(key)) {
47
48
  onError(key ?? start[start.length - 1], 'MULTILINE_IMPLICIT_KEY', 'Implicit keys need to be on a single line');
48
49
  }
49
50
  }
@@ -65,6 +66,7 @@ function resolveBlockMap({ composeNode, composeEmptyNode }, ctx, bm, onError, ta
65
66
  next: value,
66
67
  offset: keyNode.range[2],
67
68
  onError,
69
+ parentIndent: bm.indent,
68
70
  startOnNewline: !key || key.type === 'block-scalar'
69
71
  });
70
72
  offset = valueProps.end;
@@ -1,8 +1,9 @@
1
1
  import { Range } from '../nodes/Node.js';
2
2
  import { Scalar } from '../nodes/Scalar.js';
3
3
  import type { BlockScalar } from '../parse/cst.js';
4
+ import type { ComposeContext } from './compose-node.js';
4
5
  import type { ComposeErrorHandler } from './composer.js';
5
- export declare function resolveBlockScalar(scalar: BlockScalar, strict: boolean, onError: ComposeErrorHandler): {
6
+ export declare function resolveBlockScalar(ctx: ComposeContext, scalar: BlockScalar, onError: ComposeErrorHandler): {
6
7
  value: string;
7
8
  type: Scalar.BLOCK_FOLDED | Scalar.BLOCK_LITERAL | null;
8
9
  comment: string;
@@ -2,9 +2,9 @@
2
2
 
3
3
  var Scalar = require('../nodes/Scalar.js');
4
4
 
5
- function resolveBlockScalar(scalar, strict, onError) {
5
+ function resolveBlockScalar(ctx, scalar, onError) {
6
6
  const start = scalar.offset;
7
- const header = parseBlockScalarHeader(scalar, strict, onError);
7
+ const header = parseBlockScalarHeader(scalar, ctx.options.strict, onError);
8
8
  if (!header)
9
9
  return { value: '', type: null, comment: '', range: [start, start, start] };
10
10
  const type = header.mode === '>' ? Scalar.Scalar.BLOCK_FOLDED : Scalar.Scalar.BLOCK_LITERAL;
@@ -46,6 +46,10 @@ function resolveBlockScalar(scalar, strict, onError) {
46
46
  if (header.indent === 0)
47
47
  trimIndent = indent.length;
48
48
  contentStart = i;
49
+ if (trimIndent === 0 && !ctx.atRoot) {
50
+ const message = 'Block scalar values in collections must be indented';
51
+ onError(offset, 'BAD_INDENT', message);
52
+ }
49
53
  break;
50
54
  }
51
55
  offset += indent.length + content.length + 1;
@@ -3,4 +3,4 @@ import type { BlockSequence } from '../parse/cst.js';
3
3
  import { CollectionTag } from '../schema/types.js';
4
4
  import type { ComposeContext, ComposeNode } from './compose-node.js';
5
5
  import type { ComposeErrorHandler } from './composer.js';
6
- export declare function resolveBlockSeq({ composeNode, composeEmptyNode }: ComposeNode, ctx: ComposeContext, bs: BlockSequence, onError: ComposeErrorHandler, tag?: CollectionTag): YAMLSeq.Parsed<import("../index.js").ParsedNode>;
6
+ export declare function resolveBlockSeq({ composeNode, composeEmptyNode }: ComposeNode, ctx: ComposeContext, bs: BlockSequence, onError: ComposeErrorHandler, tag?: CollectionTag): YAMLSeq.Parsed;
@@ -17,6 +17,7 @@ function resolveBlockSeq({ composeNode, composeEmptyNode }, ctx, bs, onError, ta
17
17
  next: value,
18
18
  offset,
19
19
  onError,
20
+ parentIndent: bs.indent,
20
21
  startOnNewline: true
21
22
  });
22
23
  if (!props.found) {
@@ -30,6 +30,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
30
30
  next: key ?? sep?.[0],
31
31
  offset,
32
32
  onError,
33
+ parentIndent: fc.indent,
33
34
  startOnNewline: false
34
35
  });
35
36
  if (!props.found) {
@@ -111,6 +112,7 @@ function resolveFlowCollection({ composeNode, composeEmptyNode }, ctx, fc, onErr
111
112
  next: value,
112
113
  offset: keyNode.range[2],
113
114
  onError,
115
+ parentIndent: fc.indent,
114
116
  startOnNewline: false
115
117
  });
116
118
  if (valueProps.found) {
@@ -6,17 +6,18 @@ export interface ResolvePropsArg {
6
6
  next: Token | null | undefined;
7
7
  offset: number;
8
8
  onError: ComposeErrorHandler;
9
+ parentIndent: number;
9
10
  startOnNewline: boolean;
10
11
  }
11
- export declare function resolveProps(tokens: SourceToken[], { flow, indicator, next, offset, onError, startOnNewline }: ResolvePropsArg): {
12
+ export declare function resolveProps(tokens: SourceToken[], { flow, indicator, next, offset, onError, parentIndent, startOnNewline }: ResolvePropsArg): {
12
13
  comma: SourceToken | null;
13
14
  found: SourceToken | null;
14
15
  spaceBefore: boolean;
15
16
  comment: string;
16
17
  hasNewline: boolean;
17
- hasNewlineAfterProp: boolean;
18
18
  anchor: SourceToken | null;
19
19
  tag: SourceToken | null;
20
+ newlineAfterProp: SourceToken | null;
20
21
  end: number;
21
22
  start: number;
22
23
  };
@@ -1,17 +1,17 @@
1
1
  'use strict';
2
2
 
3
- function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnNewline }) {
3
+ function resolveProps(tokens, { flow, indicator, next, offset, onError, parentIndent, startOnNewline }) {
4
4
  let spaceBefore = false;
5
5
  let atNewline = startOnNewline;
6
6
  let hasSpace = startOnNewline;
7
7
  let comment = '';
8
8
  let commentSep = '';
9
9
  let hasNewline = false;
10
- let hasNewlineAfterProp = false;
11
10
  let reqSpace = false;
12
11
  let tab = null;
13
12
  let anchor = null;
14
13
  let tag = null;
14
+ let newlineAfterProp = null;
15
15
  let comma = null;
16
16
  let found = null;
17
17
  let start = null;
@@ -24,7 +24,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
24
24
  reqSpace = false;
25
25
  }
26
26
  if (tab) {
27
- if (token.type !== 'comment') {
27
+ if (atNewline && token.type !== 'comment' && token.type !== 'newline') {
28
28
  onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
29
29
  }
30
30
  tab = null;
@@ -35,9 +35,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
35
35
  // as leading white space rather than indentation.
36
36
  // In a flow collection, only the parser handles indent.
37
37
  if (!flow &&
38
- atNewline &&
39
38
  (indicator !== 'doc-start' || next?.type !== 'flow-collection') &&
40
- token.source[0] === '\t') {
39
+ token.source.includes('\t')) {
41
40
  tab = token;
42
41
  }
43
42
  hasSpace = true;
@@ -66,7 +65,7 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
66
65
  atNewline = true;
67
66
  hasNewline = true;
68
67
  if (anchor || tag)
69
- hasNewlineAfterProp = true;
68
+ newlineAfterProp = token;
70
69
  hasSpace = true;
71
70
  break;
72
71
  case 'anchor':
@@ -99,7 +98,8 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
99
98
  if (found)
100
99
  onError(token, 'UNEXPECTED_TOKEN', `Unexpected ${token.source} in ${flow ?? 'collection'}`);
101
100
  found = token;
102
- atNewline = false;
101
+ atNewline =
102
+ indicator === 'seq-item-ind' || indicator === 'explicit-key-ind';
103
103
  hasSpace = false;
104
104
  break;
105
105
  case 'comma':
@@ -128,7 +128,10 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
128
128
  (next.type !== 'scalar' || next.source !== '')) {
129
129
  onError(next.offset, 'MISSING_CHAR', 'Tags and anchors must be separated from the next token by white space');
130
130
  }
131
- if (tab)
131
+ if (tab &&
132
+ ((atNewline && tab.indent <= parentIndent) ||
133
+ next?.type === 'block-map' ||
134
+ next?.type === 'block-seq'))
132
135
  onError(tab, 'TAB_AS_INDENT', 'Tabs are not allowed as indentation');
133
136
  return {
134
137
  comma,
@@ -136,9 +139,9 @@ function resolveProps(tokens, { flow, indicator, next, offset, onError, startOnN
136
139
  spaceBefore,
137
140
  comment,
138
141
  hasNewline,
139
- hasNewlineAfterProp,
140
142
  anchor,
141
143
  tag,
144
+ newlineAfterProp,
142
145
  end,
143
146
  start: start ?? end
144
147
  };
@@ -4,7 +4,6 @@ import { NodeBase } from './Node.js';
4
4
  export declare function collectionFromPath(schema: Schema, path: unknown[], value: unknown): import("./Node.js").Node;
5
5
  export declare const isEmptyPath: (path: Iterable<unknown> | null | undefined) => path is null | undefined;
6
6
  export declare abstract class Collection extends NodeBase {
7
- static maxFlowStringSingleLineLength: number;
8
7
  schema: Schema | undefined;
9
8
  [NODE_TYPE]: symbol;
10
9
  items: unknown[];
@@ -145,7 +145,6 @@ class Collection extends Node.NodeBase {
145
145
  }
146
146
  }
147
147
  }
148
- Collection.maxFlowStringSingleLineLength = 60;
149
148
 
150
149
  exports.Collection = Collection;
151
150
  exports.collectionFromPath = collectionFromPath;
@@ -13,7 +13,7 @@ export declare const SCALAR: unique symbol;
13
13
  export declare const SEQ: unique symbol;
14
14
  export declare const NODE_TYPE: unique symbol;
15
15
  export declare const isAlias: (node: any) => node is Alias;
16
- export declare const isDocument: <T extends Node = Node>(node: any) => node is Document<T, true>;
16
+ export declare const isDocument: <T extends Node = Node>(node: any) => node is Document<T>;
17
17
  export declare const isMap: <K = unknown, V = unknown>(node: any) => node is YAMLMap<K, V>;
18
18
  export declare const isPair: <K = unknown, V = unknown>(node: any) => node is Pair<K, V>;
19
19
  export declare const isScalar: <T = unknown>(node: any) => node is Scalar<T>;
@@ -20,7 +20,7 @@ function resolveAsScalar(token, strict = true, onError) {
20
20
  case 'double-quoted-scalar':
21
21
  return resolveFlowScalar.resolveFlowScalar(token, strict, _onError);
22
22
  case 'block-scalar':
23
- return resolveBlockScalar.resolveBlockScalar(token, strict, _onError);
23
+ return resolveBlockScalar.resolveBlockScalar({ options: { strict } }, token, _onError);
24
24
  }
25
25
  }
26
26
  return null;
@@ -292,15 +292,11 @@ class Lexer {
292
292
  if (!this.atEnd && !this.hasChars(4))
293
293
  return this.setNext('line-start');
294
294
  const s = this.peek(3);
295
- if (s === '---' && isEmpty(this.charAt(3))) {
295
+ if ((s === '---' || s === '...') && isEmpty(this.charAt(3))) {
296
296
  yield* this.pushCount(3);
297
297
  this.indentValue = 0;
298
298
  this.indentNext = 0;
299
- return 'doc';
300
- }
301
- else if (s === '...' && isEmpty(this.charAt(3))) {
302
- yield* this.pushCount(3);
303
- return 'stream';
299
+ return s === '---' ? 'doc' : 'stream';
304
300
  }
305
301
  }
306
302
  this.indentValue = yield* this.pushSpaces(false);
@@ -543,14 +539,25 @@ class Lexer {
543
539
  nl = this.buffer.length;
544
540
  }
545
541
  }
546
- if (!this.blockScalarKeep) {
542
+ // Trailing insufficiently indented tabs are invalid.
543
+ // To catch that during parsing, we include them in the block scalar value.
544
+ let i = nl + 1;
545
+ ch = this.buffer[i];
546
+ while (ch === ' ')
547
+ ch = this.buffer[++i];
548
+ if (ch === '\t') {
549
+ while (ch === '\t' || ch === ' ' || ch === '\r' || ch === '\n')
550
+ ch = this.buffer[++i];
551
+ nl = i - 1;
552
+ }
553
+ else if (!this.blockScalarKeep) {
547
554
  do {
548
555
  let i = nl - 1;
549
556
  let ch = this.buffer[i];
550
557
  if (ch === '\r')
551
558
  ch = this.buffer[--i];
552
559
  const lastChar = i; // Drop the line if last char not more indented
553
- while (ch === ' ' || ch === '\t')
560
+ while (ch === ' ')
554
561
  ch = this.buffer[--i];
555
562
  if (ch === '\n' && i >= this.pos && i + 1 + indent > lastChar)
556
563
  nl = i;
@@ -9,12 +9,12 @@ export declare class YAMLOMap extends YAMLSeq {
9
9
  add: (pair: import("../../index.js").Pair<any, any> | {
10
10
  key: any;
11
11
  value: any;
12
- }, overwrite?: boolean | undefined) => void;
12
+ }, overwrite?: boolean) => void;
13
13
  delete: (key: unknown) => boolean;
14
14
  get: {
15
15
  (key: unknown, keepScalar: true): import("../../index.js").Scalar<any> | undefined;
16
- (key: unknown, keepScalar?: false | undefined): any;
17
- (key: unknown, keepScalar?: boolean | undefined): any;
16
+ (key: unknown, keepScalar?: false): any;
17
+ (key: unknown, keepScalar?: boolean): any;
18
18
  };
19
19
  has: (key: unknown) => boolean;
20
20
  set: (key: any, value: any) => void;
@@ -11,6 +11,8 @@ const FOLD_QUOTED = 'quoted';
11
11
  function foldFlowLines(text, indent, mode = 'flow', { indentAtStart, lineWidth = 80, minContentWidth = 20, onFold, onOverflow } = {}) {
12
12
  if (!lineWidth || lineWidth < 0)
13
13
  return text;
14
+ if (lineWidth < minContentWidth)
15
+ minContentWidth = 0;
14
16
  const endStep = Math.max(1 + minContentWidth, 1 + lineWidth - indent.length);
15
17
  if (text.length <= endStep)
16
18
  return text;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "yaml",
3
- "version": "2.4.4",
3
+ "version": "2.5.0",
4
4
  "license": "ISC",
5
5
  "author": "Eemeli Aro <eemeli@gmail.com>",
6
6
  "repository": "github:eemeli/yaml",