Repository: eemeli/yaml
Pull request #337 (2021-11-15 08:50:21)

Add toStringDefaults option
Add a schema option `toStringDefaults: ToStringOptions` which may set overrides for the default values used for `toString()` options.

The intent with this is to allow for the construction of a Document which stringifies a certain way, without needing to add said options on each `doc.toString()` call.


Patch:
diff --git a/docs/03_options.md b/docs/03_options.md
index 7615a93..d589e86 100644
--- a/docs/03_options.md
+++ b/docs/03_options.md
@@ -71,14 +71,15 @@ The `!!value` and `!!yaml` types are not supported.
 
 Used by: `parse()`, `parseDocument()`, `parseAllDocuments()`, `stringify()`, `new Composer()`, `new Document()`, and `doc.setSchema()`
 
-| Name             | Type                                 | Default                                  | Description                                                                                                                                                                                                         |
-| ---------------- | ------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
-| compat           | `string ⎮ Tag[] ⎮ null`              | `null`                                   | When parsing, warn about compatibility issues with the given schema. When stringifying, use scalar styles that are parsed correctly by the `compat` schema as well as the actual schema.                            |
-| customTags       | `Tag[] ⎮ function`                   |                                          | Array of [additional tags](#custom-data-types) to include in the schema                                                                                                                                             |
-| merge            | `boolean`                            | 1.1:&nbsp;`true` 1.2:&nbsp;`false`       | Enable support for `<<` merge keys. Default value depends on YAML version.                                                                                                                                          |
-| resolveKnownTags | `boolean`                            | `true`                                   | When using the `'core'` schema, support parsing values with these explicit [YAML 1.1 tags]: `!!binary`, `!!omap`, `!!pairs`, `!!set`, `!!timestamp`. By default `true`.                                             |
+| Name             | Type                                 | Default                                  | Description                                                                                                                                                                                                        |
+| ---------------- | ------------------------------------ | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
+| compat           | `string ⎮ Tag[] ⎮ null`              | `null`                                   | When parsing, warn about compatibility issues with the given schema. When stringifying, use scalar styles that are parsed correctly by the `compat` schema as well as the actual schema.                           |
+| customTags       | `Tag[] ⎮ function`                   |                                          | Array of [additional tags](#custom-data-types) to include in the schema                                                                                                                                            |
+| merge            | `boolean`                            | 1.1:&nbsp;`true` 1.2:&nbsp;`false`       | Enable support for `<<` merge keys. Default value depends on YAML version.                                                                                                                                         |
+| resolveKnownTags | `boolean`                            | `true`                                   | When using the `'core'` schema, support parsing values with these explicit [YAML 1.1 tags]: `!!binary`, `!!omap`, `!!pairs`, `!!set`, `!!timestamp`. By default `true`.                                            |
 | schema           | `string`                             | 1.1:&nbsp;`'yaml-1.1` 1.2:&nbsp;`'core'` | The base schema to use. Default value depends on YAML version. Built-in support is provided for `'core'`, `'failsafe'`, `'json'`, and `'yaml-1.1'`. If using another value, `customTags` must be an array of tags. |
-| sortMapEntries   | `boolean ⎮` `(a, b: Pair) => number` | `false`                                  | When stringifying, sort map entries. If `true`, sort by comparing key values using the native less-than `<` operator.                                                                                               |
+| sortMapEntries   | `boolean ⎮` `(a, b: Pair) => number` | `false`                                  | When stringifying, sort map entries. If `true`, sort by comparing key values using the native less-than `<` operator.                                                                                              |
+| toStringDefaults | `ToStringOptions`                    |                                          | Override default values for `toString()` options.                                                                                                                                                                  |
 
 [yaml 1.1 tags]: https://yaml.org/type/
 
diff --git a/src/options.ts b/src/options.ts
index 4055aa4..2766680 100644
--- a/src/options.ts
+++ b/src/options.ts
@@ -141,6 +141,11 @@ export type SchemaOptions = {
    * Default: `false`
    */
   sortMapEntries?: boolean | ((a: Pair, b: Pair) => number)
+
+  /**
+   * Override default values for `toString()` options.
+   */
+  toStringDefaults?: ToStringOptions
 }
 
 export type CreateNodeOptions = {
diff --git a/src/schema/Schema.ts b/src/schema/Schema.ts
index 8ebeb37..26bba25 100644
--- a/src/schema/Schema.ts
+++ b/src/schema/Schema.ts
@@ -1,6 +1,6 @@
 import { MAP, SCALAR, SEQ } from '../nodes/Node.js'
 import type { Pair } from '../nodes/Pair.js'
-import type { SchemaOptions } from '../options.js'
+import type { SchemaOptions, ToStringOptions } from '../options.js'
 import { map } from './common/map.js'
 import { seq } from './common/seq.js'
 import { string } from './common/string.js'
@@ -16,7 +16,8 @@ export class Schema {
   merge: boolean
   name: string
   sortMapEntries: ((a: Pair, b: Pair) => number) | null
-  tags: Array<CollectionTag | ScalarTag>;
+  tags: Array<CollectionTag | ScalarTag>
+  toStringOptions: Readonly<ToStringOptions> | null;
 
   // Used by createNode() and composeScalar()
   [MAP]: CollectionTag;
@@ -29,7 +30,8 @@ export class Schema {
     merge,
     resolveKnownTags,
     schema,
-    sortMapEntries
+    sortMapEntries,
+    toStringDefaults
   }: SchemaOptions) {
     this.compat = Array.isArray(compat)
       ? getTags(compat, 'compat')
@@ -40,6 +42,7 @@ export class Schema {
     this.name = schema || 'core'
     this.knownTags = resolveKnownTags ? coreKnownTags : {}
     this.tags = getTags(customTags, this.name)
+    this.toStringOptions = toStringDefaults || null
 
     Object.defineProperty(this, MAP, { value: map })
     Object.defineProperty(this, SCALAR, { value: string })
diff --git a/src/stringify/stringify.ts b/src/stringify/stringify.ts
index d692959..d929224 100644
--- a/src/stringify/stringify.ts
+++ b/src/stringify/stringify.ts
@@ -29,16 +29,11 @@ export type StringifyContext = {
   options: Readonly<Required<Omit<ToStringOptions, 'indent'>>>
 }
 
-export const createStringifyContext = (
+export function createStringifyContext(
   doc: Document,
   options: ToStringOptions
-): StringifyContext => ({
-  anchors: new Set(),
-  doc,
-  indent: '',
-  indentStep:
-    typeof options.indent === 'number' ? ' '.repeat(options.indent) : '  ',
-  options: Object.assign(
+): StringifyContext {
+  const opt = Object.assign(
     {
       blockQuote: true,
       commentString: stringifyComment,
@@ -57,9 +52,18 @@ export const createStringifyContext = (
       trueStr: 'true',
       verifyAliasOrder: true
     },
+    doc.schema.toStringOptions,
     options
   )
-})
+
+  return {
+    anchors: new Set(),
+    doc,
+    indent: '',
+    indentStep: typeof opt.indent === 'number' ? ' '.repeat(opt.indent) : '  ',
+    options: opt
+  }
+}
 
 function getTagObject(tags: Array<ScalarTag | CollectionTag>, item: Node) {
   if (item.tag) {


Test patch:
diff --git a/tests/doc/stringify.js b/tests/doc/stringify.js
index 5d70f44..553b67d 100644
--- a/tests/doc/stringify.js
+++ b/tests/doc/stringify.js
@@ -870,6 +870,24 @@ describe('Scalar options', () => {
   }
 })
 
+describe('toStringDefaults', () => {
+  test('Set on Document creation', () => {
+    const doc = new YAML.Document(
+      { foo: null },
+      { toStringDefaults: { nullStr: '~' } }
+    )
+    expect(doc.toString()).toBe('foo: ~\n')
+  })
+
+  test('Override in toString()', () => {
+    const doc = new YAML.Document(
+      { foo: null },
+      { toStringDefaults: { nullStr: '~' } }
+    )
+    expect(doc.toString({ nullStr: '' })).toBe('foo:\n')
+  })
+})
+
 describe('Document markers in top-level scalars', () => {
   test('---', () => {
     const str = YAML.stringify('---')