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.
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 ] }
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.
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.
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.
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.
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.
A description of alias and merge nodes is included in the next section.
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.