Repository: nodeca/js-yaml (js)

Issue:
Incorrect dump of block scalar with leading newlines and spaces
The following code dumps invalid YAML:

```js
console.log(yaml.safeDump({ str: '\n    a\nb' }))
```

Invalid output due to indentation:

```yaml
str: |-

      a
  b
```

Leading spaces are handled in general by an indentation indicator (below), but leading newlines seem to thwart this (above):

```js
console.log(yaml.safeDump({ str: '    a\nb' }))
```

Valid output:

```yaml
str: |2-
      a
  b
```

The results above are from version 3.11.0.

Pull request:
Check for leading newlines when determining if block indentation indicator is needed
Fixes #403

Patch:
diff --git a/lib/js-yaml/dumper.js b/lib/js-yaml/dumper.js
index 025b185..67cf6ca 100644
--- a/lib/js-yaml/dumper.js
+++ b/lib/js-yaml/dumper.js
@@ -234,6 +234,12 @@ function isPlainSafeFirst(c) {
     && c !== CHAR_GRAVE_ACCENT;
 }
 
+// Determines whether block indentation indicator is required.
+function needIndentIndicator(string) {
+  var leadingSpaceRe = /^\n* /;
+  return leadingSpaceRe.test(string);
+}
+
 var STYLE_PLAIN   = 1,
     STYLE_SINGLE  = 2,
     STYLE_LITERAL = 3,
@@ -301,7 +307,7 @@ function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, te
       ? STYLE_PLAIN : STYLE_SINGLE;
   }
   // Edge case: block indentation indicator can only have one digit.
-  if (string[0] === ' ' && indentPerLevel > 9) {
+  if (indentPerLevel > 9 && needIndentIndicator(string)) {
     return STYLE_DOUBLE;
   }
   // At this point we know block styles are valid.
@@ -365,7 +371,7 @@ function writeScalar(state, string, level, iskey) {
 
 // Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9.
 function blockHeader(string, indentPerLevel) {
-  var indentIndicator = (string[0] === ' ') ? String(indentPerLevel) : '';
+  var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : '';
 
   // note the special case: the string '\n' counts as a "trailing" empty line.
   var clip =          string[string.length - 1] === '\n';


Test patch:
diff --git a/test/issues/0403.js b/test/issues/0403.js
new file mode 100644
index 0000000..43bc271
--- /dev/null
+++ b/test/issues/0403.js
@@ -0,0 +1,22 @@
+'use strict';
+
+
+var assert = require('assert');
+var yaml = require('../../');
+
+
+test('should properly dump leading newlines and spaces', function () {
+  var dump, src;
+
+  src = { str: '\n  a\nb' };
+  dump = yaml.dump(src);
+  assert.deepEqual(yaml.safeLoad(dump), src);
+
+  src = { str: '\n\n  a\nb' };
+  dump = yaml.dump(src);
+  assert.deepEqual(yaml.safeLoad(dump), src);
+
+  src = { str: '\n  a\nb' };
+  dump = yaml.dump(src, { indent: 10 });
+  assert.deepEqual(yaml.safeLoad(dump), src);
+});