Skip to content

Latest commit

 

History

History
239 lines (193 loc) · 14 KB

04_documents.md

File metadata and controls

239 lines (193 loc) · 14 KB

Documents

In order to work with YAML features not directly supported by native JavaScript data types, such as comments, anchors and aliases, yaml provides the Document API.

Parsing Documents

import fs from 'fs'
import { parseAllDocuments, parseDocument } from 'yaml'

const file = fs.readFileSync('./file.yml', 'utf8')
const doc = parseDocument(file)
doc.contents
// YAMLMap {
//   items:
//    [ Pair {
//        key: Scalar { value: 'YAML', range: [ 0, 4 ] },
//        value:
//         YAMLSeq {
//           items:
//            [ Scalar {
//                value: 'A human-readable data serialization language',
//                range: [ 10, 55 ] },
//              Scalar {
//                value: 'https://en.wikipedia.org/wiki/YAML',
//                range: [ 59, 94 ] } ],
//           tag: 'tag:yaml.org,2002:seq',
//           range: [ 8, 94 ] } },
//      Pair {
//        key: Scalar { value: 'yaml', range: [ 94, 98 ] },
//        value:
//         YAMLSeq {
//           items:
//            [ Scalar {
//                value: 'A complete JavaScript implementation',
//                range: [ 104, 141 ] },
//              Scalar {
//                value: 'https://www.npmjs.com/package/yaml',
//                range: [ 145, 180 ] } ],
//           tag: 'tag:yaml.org,2002:seq',
//           range: [ 102, 180 ] } } ],
//   tag: 'tag:yaml.org,2002:map',
//   range: [ 0, 180 ] }

parseDocument(str, options = {}): Document

Parses a single Document from the input str; used internally by parse. Will include an error if str contains more than one document. See Options for more information on the second parameter.


parseAllDocuments(str, options = {}): Document[]

When parsing YAML, the input string str may consist of a stream of documents separated from each other by ... document end marker lines. parseAllDocuments will return an array of Document objects that allow these documents to be parsed and manipulated with more control. See Options for more information on the second parameter.


These functions should never throw; errors and warnings are included in the documents' errors and warnings arrays. In particular, if errors is not empty it's likely that the document's parsed contents are not entirely correct.

The contents of a parsed document will always consist of Scalar, Map, Seq or null values.

Creating Documents

new Document(value, replacer?, options = {})

Creates a new document. If value is defined, the document contents are initialised with that value, wrapped recursively in appropriate content nodes. 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. See Options for more information on the last argument.

Member Type Description
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 ⎮ 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.
import { Document } from 'yaml'

const doc = new Document(['some', 'values', { balloons: 99 }])
doc.version = true
doc.commentBefore = ' A commented document'

String(doc)
// # A commented document
// %YAML 1.2
// ---
// - some
// - values
// - balloons: 99

The Document members are all modifiable, though it's unlikely that you'll have reason to change errors, schema or warnings. In particular you may be interested in both reading and writing contents. Although parseDocument() and parseAllDocuments() will leave it with Map, Seq, Scalar or null contents, it can be set to anything.

During stringification, a document with a true-ish version value will include a %YAML directive; the version number will be set to 1.2 unless the yaml-1.1 schema is in use.

Document Methods

Method Returns Description
createNode(value, options?) Node Recursively wrap any input with appropriate Node containers. See Creating Nodes for more information.
createPair(key, value, options?) Pair Recursively wrap key and value into a Pair object. See Creating Nodes for more information.
setSchema(version, options?) void Change the YAML version and schema used by the document. version must be either '1.1' or '1.2'; accepts all Schema options.
setTagPrefix(handle, prefix) void Set handle as a shorthand string for the prefix tag namespace.
toJS(options?) any A plain JavaScript representation of the document contents.
toJSON() any A JSON representation of the document contents.
toString(options?) string A YAML representation of the document.
const doc = parseDocument('a: 1\nb: [2, 3]\n')
doc.get('a') // 1
doc.getIn([]) // YAMLMap { items: [Pair, Pair], ... }
doc.hasIn(['b', 0]) // true
doc.addIn(['b'], 4) // -> doc.get('b').items.length === 3
doc.deleteIn(['b', 1]) // true
doc.getIn(['b', 1]) // 4

In addition to the above, the document object also provides the same accessor methods as collections, based on the top-level collection: add, delete, get, has, and set, along with their deeper variants addIn, deleteIn, getIn, hasIn, and setIn. For the *In methods using an empty path value (i.e. null, undefined, or []) will refer to the document's top-level contents.

To define a tag prefix to use when stringifying, use setTagPrefix(handle, prefix) rather than setting a value directly in tagPrefixes. This will guarantee that the handle is valid (by throwing an error), and will overwrite any previous definition for the handle. Use an empty prefix value to remove a prefix.

Document#toJS(), Document#toJSON() and Document#toString()

const src = '1969-07-21T02:56:15Z'
const doc = parseDocument(src, { customTags: ['timestamp'] })

doc.toJS()
// Date { 1969-07-21T02:56:15.000Z }

doc.toJSON()
// '1969-07-21T02:56:15.000Z'

String(doc)
// '1969-07-21T02:56:15\n'

For a plain JavaScript representation of the document, toJS(options = {}) is your friend. Its output may include Map and Set collections (e.g. if the mapAsMap option is true) and complex scalar values like Date for !!timestamp, but all YAML nodes will be resolved. See Options for more information on the optional parameter.

For a representation consisting only of JSON values, use toJSON().

To stringify a document as YAML, use toString(options = {}). This will also be called by String(doc) (with no options). This method will throw if the errors array is not empty. See Options for more information on the optional parameter.

Working with Anchors

A description of alias and merge nodes is included in the next section.


Document#anchors

Method Returns Description
createAlias(node: Node, name?: string) Alias Create a new Alias node, adding the required anchor for node. If name is empty, a new anchor name will be generated.
createMergePair(...Node) Merge Create a new Merge node with the given source nodes. Non-Alias sources will be automatically wrapped.
getName(node: Node) string? The anchor name associated with node, if set.
getNames() string[] List of all defined anchor names.
getNode(name: string) Node? The node associated with the anchor name, if set.
newName(prefix: string) string Find an available anchor name with the given prefix and a numerical suffix.
setAnchor(node?: Node, name?: string) string? Associate an anchor with node. If name is empty, a new name will be generated.
const src = '[{ a: A }, { b: B }]'
const doc = parseDocument(src)
doc.anchors.setAnchor(doc.getIn([0, 'a'], true)) // 'a1'
doc.anchors.setAnchor(doc.getIn([1, 'b'], true)) // 'a2'
doc.anchors.setAnchor(null, 'a1') // 'a1'
doc.anchors.getNode('a2')
// { value: 'B', range: [ 16, 18 ], type: 'PLAIN' }
String(doc)
// [ { a: A }, { b: &a2 B } ]

const alias = doc.anchors.createAlias(doc.get(0, true), 'AA')
// Alias { source: YAMLMap { items: [ [Pair] ] } }
doc.add(alias)
doc.toJS()
// [ { a: 'A' }, { b: 'B' }, { a: 'A' } ]
String(doc)
// [ &AA { a: A }, { b: &a2 B }, *AA ]

const merge = doc.anchors.createMergePair(alias)
// Merge {
//   key: Scalar { value: '<<' },
//   value: YAMLSeq { items: [ [Alias] ] } }
doc.addIn([1], merge)
doc.toJS()
// [ { a: 'A' }, { b: 'B', a: 'A' }, { a: 'A' } ]
String(doc)
// [ &AA { a: A }, { b: &a2 B, <<: *AA }, *AA ]

// This creates a circular reference
merge.value.add(doc.anchors.createAlias(doc.get(1, true)))
doc.toJS() // [RangeError: Maximum call stack size exceeded]
String(doc)
// [
//   &AA { a: A },
//   &a3 {
//       b: &a2 B,
//       <<:
//         [ *AA, *a3 ]
//     },
//   *AA
// ]

The constructors for Alias and Merge are not directly exported by the library, as they depend on the document's anchors; instead you'll need to use createAlias(node, name) and createMergePair(...sources). You should make sure to only add alias and merge nodes to the document after the nodes to which they refer, or the document's YAML stringification will fail.

It is valid to have an anchor associated with a node even if it has no aliases. yaml will not allow you to associate the same name with more than one node, even though this is allowed by the YAML spec (all but the last instance will have numerical suffixes added). To add or reassign an anchor, use setAnchor(node, name). The second parameter is optional, and if left out either the pre-existing anchor name of the node will be used, or a new one generated. To remove an anchor, use setAnchor(null, name). The function will return the new anchor's name, or null if both of its arguments are null.

While the merge option needs to be true to parse Merge nodes as such, this is not required during stringification.