diff --git a/docs/03_options.md b/docs/03_options.md index 664fa972..3c1cb65a 100644 --- a/docs/03_options.md +++ b/docs/03_options.md @@ -131,18 +131,19 @@ The `doc.toString()` method may be called with additional options to control the Used by: `stringify()` and `doc.toString()` -| Name | Type | Default | Description | -| ------------------------------ | ------------- | --------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| defaultKeyType | `Type ⎮ null` | `null` | If not `null`, overrides `defaultStringType` for implicit key values. | -| defaultStringType | `Type` | `'PLAIN'` | The default type of string literal used to stringify values. | -| doubleQuotedAsJSON | `boolean` | `false` | Restrict double-quoted strings to use JSON-compatible syntax. | -| doubleQuotedMinMultiLineLength | `number` | `40` | Minimum length for double-quoted strings to use multiple lines to represent the value. | -| falseStr | `string` | `'false'` | String representation for `false` values. | -| indent | `number` | `2` | The number of spaces to use when indenting code. Should be a strictly positive integer. | -| indentSeq | `boolean` | `true` | Whether block sequences should be indented. | -| lineWidth | `number` | `80` | Maximum line width (set to `0` to disable folding). This is a soft limit, as only double-quoted semantics allow for inserting a line break in the middle of a word. | -| minContentWidth | `number` | `20` | Minimum line width for highly-indented content (set to `0` to disable). | -| nullStr | `string` | `'null'` | String representation for `null` values. | -| simpleKeys | `boolean` | `false` | Require keys to be scalars and always use implicit rather than explicit notation. | -| singleQuote | `boolean` | `false` | Prefer 'single quote' rather than "double quote" where applicable. | -| trueStr | `string` | `'true'` | String representation for `true` values. | +| Name | Type | Default | Description | +| ------------------------------ | ---------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| defaultKeyType | `Type ⎮ null` | `null` | If not `null`, overrides `defaultStringType` for implicit key values. | +| defaultStringType | `Type` | `'PLAIN'` | The default type of string literal used to stringify values. | +| directives | `boolean ⎮ null` | `null` | Include directives in the output. If `true`, at least the document-start marker `---` is always included. If `false`, no directives or marker is ever included. If `null`, directives and marker may be included if required. | +| doubleQuotedAsJSON | `boolean` | `false` | Restrict double-quoted strings to use JSON-compatible syntax. | +| doubleQuotedMinMultiLineLength | `number` | `40` | Minimum length for double-quoted strings to use multiple lines to represent the value. | +| falseStr | `string` | `'false'` | String representation for `false` values. | +| indent | `number` | `2` | The number of spaces to use when indenting code. Should be a strictly positive integer. | +| indentSeq | `boolean` | `true` | Whether block sequences should be indented. | +| lineWidth | `number` | `80` | Maximum line width (set to `0` to disable folding). This is a soft limit, as only double-quoted semantics allow for inserting a line break in the middle of a word. | +| minContentWidth | `number` | `20` | Minimum line width for highly-indented content (set to `0` to disable). | +| nullStr | `string` | `'null'` | String representation for `null` values. | +| simpleKeys | `boolean` | `false` | Require keys to be scalars and always use implicit rather than explicit notation. | +| singleQuote | `boolean` | `false` | Prefer 'single quote' rather than "double quote" where applicable. | +| trueStr | `string` | `'true'` | String representation for `true` values. | diff --git a/docs/04_documents.md b/docs/04_documents.md index 8eaa0278..97812c16 100644 --- a/docs/04_documents.md +++ b/docs/04_documents.md @@ -73,18 +73,18 @@ If `value` is `undefined`, the document's `contents` is initialised as `null`. If defined, a `replacer` may filter or modify the initial document contents, following the same algorithm as the [JSON implementation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter). See [Options](#options) for more information on the last argument. -| Member | Type | Description | -| ------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| anchors | [`Anchors`](#anchors) | Anchors associated with the document's nodes; also provides alias & merge node creators. | -| commentBefore | `string?` | A comment at the very beginning of the document. If not empty, separated from the rest of the document by a blank line or the directives-end indicator when stringified. | -| comment | `string?` | A comment at the end of the document. If not empty, separated from the rest of the document by a blank line when stringified. | -| contents | [`Node`](#content-nodes)|`any` | The document contents. | -| directivesEndMarker | `boolean?` | Whether the document should always include a directives-end marker `---` at its start, even if it includes no directives. | -| errors | `Error[]` | Errors encountered during parsing. | -| schema | `Schema` | The schema used with the document. | -| tagPrefixes | `Prefix[]` | Array of prefixes; each will have a string `handle` that starts and ends with `!` and a string `prefix` that the handle will be replaced by. | -| version | `string?` | The parsed version of the source document; if true-ish, stringified output will include a `%YAML` directive. | -| warnings | `Error[]` | Warnings encountered during parsing. | +| Member | Type | Description | +| ------------- | -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| anchors | [`Anchors`](#anchors) | Anchors associated with the document's nodes; also provides alias & merge node creators. | +| commentBefore | `string?` | A comment at the very beginning of the document. If not empty, separated from the rest of the document by a blank line or the doc-start indicator when stringified. | +| comment | `string?` | A comment at the end of the document. If not empty, separated from the rest of the document by a blank line when stringified. | +| contents | [`Node`](#content-nodes) `⎮ any` | The document contents. | +| directives | `Directives` | Document directives `%YAML` and `%TAG`, as well as the doc-start marker `---`. | +| errors | `Error[]` | Errors encountered during parsing. | +| schema | `Schema` | The schema used with the document. | +| tagPrefixes | `Prefix[]` | Array of prefixes; each will have a string `handle` that starts and ends with `!` and a string `prefix` that the handle will be replaced by. | +| version | `string?` | The parsed version of the source document; if true-ish, stringified output will include a `%YAML` directive. | +| warnings | `Error[]` | Warnings encountered during parsing. | ```js import { Document } from 'yaml' diff --git a/src/compose/compose-doc.ts b/src/compose/compose-doc.ts index 63e8b090..bbc48a07 100644 --- a/src/compose/compose-doc.ts +++ b/src/compose/compose-doc.ts @@ -14,10 +14,8 @@ export function composeDoc( ) { const opts = Object.assign({ directives }, options) const doc = new Document(undefined, opts) as Document.Parsed - const props = resolveProps(doc, start, true, 'doc-start', offset, onError) - if (props.found) doc.directivesEndMarker = true - + if (props.found) doc.directives.marker = true doc.contents = value ? composeNode(doc, value, props, onError) : composeEmptyNode(doc, offset + props.length, start, null, props, onError) diff --git a/src/compose/composer.ts b/src/compose/composer.ts index 8d25394f..ab5b53af 100644 --- a/src/compose/composer.ts +++ b/src/compose/composer.ts @@ -76,7 +76,7 @@ export class Composer { const dc = doc.contents if (afterDoc) { doc.comment = doc.comment ? `${doc.comment}\n${comment}` : comment - } else if (afterEmptyLine || doc.directivesEndMarker || !dc) { + } else if (afterEmptyLine || doc.directives.marker || !dc) { doc.commentBefore = comment } else if ( isCollection(dc) && diff --git a/src/doc/Document.ts b/src/doc/Document.ts index 3007d522..2f127d9d 100644 --- a/src/doc/Document.ts +++ b/src/doc/Document.ts @@ -74,8 +74,6 @@ export class Document { directives: Directives - directivesEndMarker = false - /** Errors encountered during parsing. */ errors: YAMLError[] = [] @@ -98,12 +96,6 @@ export class Document { type: Type.DOCUMENT = Type.DOCUMENT - /** - * The parsed version of the source document; - * if true-ish, stringified output will include a `%YAML` directive. - */ - version?: string - /** Warnings encountered during parsing. */ warnings: YAMLWarning[] = [] @@ -420,15 +412,17 @@ export class Document { } const lines = [] - let hasDirectives = false - const dir = this.directives.toString(this) - if (dir) { - lines.push(dir) - hasDirectives = true + let hasDirectives = options.directives === true + if (options.directives !== false) { + const dir = this.directives.toString(this) + if (dir) { + lines.push(dir) + hasDirectives = true + } else if (this.directives.marker) hasDirectives = true } - if (hasDirectives || this.directivesEndMarker) lines.push('---') + if (hasDirectives) lines.push('---') if (this.commentBefore) { - if (hasDirectives || !this.directivesEndMarker) lines.unshift('') + if (lines.length !== 1) lines.unshift('') lines.unshift(this.commentBefore.replace(/^/gm, '#')) } @@ -437,11 +431,7 @@ export class Document { let contentComment = null if (this.contents) { if (isNode(this.contents)) { - if ( - this.contents.spaceBefore && - (hasDirectives || this.directivesEndMarker) - ) - lines.push('') + if (this.contents.spaceBefore && hasDirectives) lines.push('') if (this.contents.commentBefore) lines.push(this.contents.commentBefore.replace(/^/gm, '#')) // top-level block scalars need to be indented if followed by a comment diff --git a/src/doc/directives.ts b/src/doc/directives.ts index 8c24628a..c5744386 100644 --- a/src/doc/directives.ts +++ b/src/doc/directives.ts @@ -21,6 +21,12 @@ export class Directives { yaml: { version: '1.1' | '1.2'; explicit?: boolean } tags: Record + /** + * The directives-end/doc-start marker `---`. If `null`, a marker may still be + * included in the document's stringified representation. + */ + marker: true | null = null + /** * Used when parsing YAML 1.1, where: * > If the document specifies no directives, it is parsed using the same diff --git a/src/options.ts b/src/options.ts index fab3e969..70aff82a 100644 --- a/src/options.ts +++ b/src/options.ts @@ -194,6 +194,21 @@ export type ToStringOptions = { */ defaultStringType?: Scalar.Type + /** + * Include directives in the output. + * + * - If `true`, at least the document-start marker `---` is always included. + * This does not force the `%YAML` directive to be included. To do that, + * set `doc.directives.yaml.explicit = true`. + * - If `false`, no directives or marker is ever included. If using the `%TAG` + * directive, you are expected to include it manually in the stream before + * its use. + * - If `null`, directives and marker may be included if required. + * + * Default: `null` + */ + directives?: boolean | null + /** * Restrict double-quoted strings to use JSON-compatible syntax. * diff --git a/src/stringify/stringify.ts b/src/stringify/stringify.ts index ba224fb6..8dedf50d 100644 --- a/src/stringify/stringify.ts +++ b/src/stringify/stringify.ts @@ -34,6 +34,7 @@ export const createStringifyContext = ( { defaultKeyType: null, defaultStringType: Type.PLAIN, + directives: null, doubleQuotedAsJSON: false, doubleQuotedMinMultiLineLength: 40, falseStr: 'false', diff --git a/src/test-events.ts b/src/test-events.ts index 5e837a0f..2ee2684d 100644 --- a/src/test-events.ts +++ b/src/test-events.ts @@ -22,7 +22,7 @@ export function testEvents(src: string, options?: Options) { if (error && (!error.offset || error.offset < rootStart)) throw new Error() let docStart = '+DOC' - if (doc.directivesEndMarker) docStart += ' ---' + if (doc.directives.marker) docStart += ' ---' else if (doc.contents && doc.contents.range[1] === doc.contents.range[0]) continue events.push(docStart) diff --git a/tests/doc/comments.js b/tests/doc/comments.js index e0a899a1..e333b639 100644 --- a/tests/doc/comments.js +++ b/tests/doc/comments.js @@ -503,9 +503,8 @@ describe('blank lines', () => { test('before first node in document with directives', () => { const doc = YAML.parseDocument('str\n') - doc.directivesEndMarker = true doc.contents.spaceBefore = true - expect(String(doc)).toBe('---\n\nstr\n') + expect(doc.toString({ directives: true })).toBe('---\n\nstr\n') }) test('between seq items', () => { diff --git a/tests/doc/stringify.js b/tests/doc/stringify.js index f472ef2f..e757f811 100644 --- a/tests/doc/stringify.js +++ b/tests/doc/stringify.js @@ -866,8 +866,7 @@ describe('Document markers in top-level scalars', () => { test('use marker line for block scalar header', () => { const doc = YAML.parseDocument('|\nfoo\n') - doc.directivesEndMarker = true - expect(String(doc)).toBe('--- |\nfoo\n') + expect(doc.toString({ directives: true })).toBe('--- |\nfoo\n') }) })