Skip to content

Commit

Permalink
Add flow: boolean option to CreateNodeOptions
Browse files Browse the repository at this point in the history
  • Loading branch information
eemeli committed Mar 7, 2021
1 parent 24383fc commit 4e8d437
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 19 deletions.
18 changes: 14 additions & 4 deletions docs/05_content_nodes.md
Expand Up @@ -164,13 +164,23 @@ String(doc)

#### `YAML.Document#createNode(value, options?): Node`

To create a new node, use the `createNode(value, options?)` document method. This will recursively wrap any input with appropriate `Node` containers. Generic JS `Object` values as well as `Map` and its descendants become mappings, while arrays and other iterable objects result in sequences. With `Object`, entries that have an `undefined` value are dropped.
To create a new node, use the `createNode(value, options?)` document method.
This will recursively wrap any input with appropriate `Node` containers.
Generic JS `Object` values as well as `Map` and its descendants become mappings, while arrays and other iterable objects result in sequences.
With `Object`, entries that have an `undefined` value are dropped.

Use `options.replacer` to apply a replacer array or function, following the [JSON implementation](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter). To specify the collection type, set `options.tag` to its identifying string, e.g. `"!!omap"`. Note that this requires the corresponding tag to be available in the document's schema. If `options.wrapScalars` is undefined or `true`, plain values are wrapped in `Scalar` objects.
To force flow styling on a collection, use `options.flow = true`
Use `options.replacer` to apply a replacer array or function, following the [JSON implementation][replacer].
To specify the collection type, set `options.tag` to its identifying string, e.g. `"!!omap"`.
Note that this requires the corresponding tag to be available in the document's schema.

As a possible side effect, this method may add entries to the document's [`anchors`](#working-with-anchors)
[replacer]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify#The_replacer_parameter

The primary purpose of this method is to enable attaching comments or other metadata to a value, or to otherwise exert more fine-grained control over the stringified output. To that end, you'll need to assign its return value to the `contents` of a document (or somewhere within said contents), as the document's schema is required for YAML string output. If you're not interested in working with such metadata, document `contents` may also include non-`Node` values at any level.
As a possible side effect, this method may add entries to the document's [`anchors`](#working-with-anchors).

The primary purpose of this method is to enable attaching comments or other metadata to a value, or to otherwise exert more fine-grained control over the stringified output.
To that end, you'll need to assign its return value to the `contents` of a document (or somewhere within said contents), as the document's schema is required for YAML string output.
If you're not interested in working with such metadata, document `contents` may also include non-`Node` values at any level.

<h4 style="clear:both"><code>new YAMLMap(), new YAMLSeq(), doc.createPair(key, value)</code></h4>

Expand Down
10 changes: 9 additions & 1 deletion src/doc/Document.ts
Expand Up @@ -5,8 +5,10 @@ import { collectionFromPath, isEmptyPath } from '../nodes/Collection.js'
import {
DOC,
isCollection,
isMap,
isNode,
isScalar,
isSeq,
Node,
NODE_TYPE,
ParsedNode
Expand Down Expand Up @@ -157,7 +159,7 @@ export class Document<T = unknown> {
*/
createNode(
value: unknown,
{ keepUndefined, onTagObj, replacer, tag }: CreateNodeOptions = {}
{ flow, keepUndefined, onTagObj, replacer, tag }: CreateNodeOptions = {}
): Node {
if (typeof replacer === 'function')
value = replacer.call({ '': value }, '', value)
Expand All @@ -183,6 +185,7 @@ export class Document<T = unknown> {
replacer,
schema: this.schema
}

const node = createNode(value, tag, ctx)
for (const alias of aliasNodes) {
// With circular references, the source node is only resolved after all of
Expand All @@ -195,6 +198,11 @@ export class Document<T = unknown> {
this.anchors.map[name] = alias.source
}
}
if (flow) {
if (isMap(node)) node.type = Type.FLOW_MAP
else if (isSeq(node)) node.type = Type.FLOW_SEQ
}

return node
}

Expand Down
1 change: 1 addition & 0 deletions src/nodes/Scalar.ts
Expand Up @@ -31,6 +31,7 @@ export class Scalar<T = unknown> extends NodeBase {
*/
declare format?: string

/** If `value` is a number, use this value when stringifying this node. */
declare minFractionDigits?: number

/** Set during parsing to the source string value */
Expand Down
27 changes: 13 additions & 14 deletions src/options.ts
@@ -1,9 +1,10 @@
import { LogLevelId, Type } from './constants.js'
import type { LogLevelId } from './constants.js'
import type { Reviver } from './doc/applyReviver.js'
import type { Directives } from './doc/directives.js'
import type { Replacer } from './doc/Document.js'
import type { SchemaName } from './doc/Schema.js'
import type { Pair } from './nodes/Pair.js'
import type { Scalar } from './nodes/Scalar.js'
import type { LineCounter } from './parse/line-counter.js'
import type { CollectionTag, ScalarTag, TagValue } from './tags/types.js'

Expand Down Expand Up @@ -117,6 +118,15 @@ export type SchemaOptions = {
}

export type CreateNodeOptions = {
/** Force the top-level collection node to use flow style. */
flow?: boolean

/**
* Keep `undefined` object values when creating mappings, rather than
* discarding them.
*
* Default: `false`
*/
keepUndefined?: boolean | null

onTagObj?: (tagObj: ScalarTag | CollectionTag) => void
Expand Down Expand Up @@ -174,26 +184,15 @@ export type ToStringOptions = {
*
* Default: `null`
*/
defaultKeyType?:
| null
| Type.BLOCK_FOLDED
| Type.BLOCK_LITERAL
| Type.PLAIN
| Type.QUOTE_DOUBLE
| Type.QUOTE_SINGLE
defaultKeyType?: Scalar.Type | null

/**
* The default type of string literal used to stringify values in general.
* Output may use other types if required to fully represent the value.
*
* Default: `'PLAIN'`
*/
defaultStringType?:
| Type.BLOCK_FOLDED
| Type.BLOCK_LITERAL
| Type.PLAIN
| Type.QUOTE_DOUBLE
| Type.QUOTE_SINGLE
defaultStringType?: Scalar.Type

/**
* Restrict double-quoted strings to use JSON-compatible syntax.
Expand Down
28 changes: 28 additions & 0 deletions tests/doc/createNode.js
Expand Up @@ -54,11 +54,23 @@ describe('arrays', () => {
expect(s).toBeInstanceOf(YAMLSeq)
expect(s.items).toHaveLength(0)
})

test('createNode([true])', () => {
const s = doc.createNode([true])
expect(s).toBeInstanceOf(YAMLSeq)
expect(s.items).toMatchObject([{ value: true }])
doc.contents = s
expect(String(doc)).toBe('- true\n')
})

test('flow: true', () => {
const s = doc.createNode([true], { flow: true })
expect(s).toBeInstanceOf(YAMLSeq)
expect(s.items).toMatchObject([{ value: true }])
doc.contents = s
expect(String(doc)).toBe('[ true ]\n')
})

describe('[3, ["four", 5]]', () => {
const array = [3, ['four', 5]]
test('createNode(value)', () => {
Expand Down Expand Up @@ -89,20 +101,35 @@ describe('objects', () => {
expect(s).toBeInstanceOf(YAMLMap)
expect(s.items).toHaveLength(0)
})

test('createNode({ x: true })', () => {
const s = doc.createNode({ x: true })
expect(s).toBeInstanceOf(YAMLMap)
expect(s.items).toMatchObject([
{ key: { value: 'x' }, value: { value: true } }
])
doc.contents = s
expect(String(doc)).toBe('x: true\n')
})

test('flow: true', () => {
const s = doc.createNode({ x: true }, { flow: true })
expect(s).toBeInstanceOf(YAMLMap)
expect(s.items).toMatchObject([
{ key: { value: 'x' }, value: { value: true } }
])
doc.contents = s
expect(String(doc)).toBe('{ x: true }\n')
})

test('createNode({ x: true, y: undefined })', () => {
const s = doc.createNode({ x: true, y: undefined })
expect(s).toBeInstanceOf(YAMLMap)
expect(s.items).toMatchObject([
{ type: PairType.PAIR, key: { value: 'x' }, value: { value: true } }
])
})

test('createNode({ x: true, y: undefined }, { keepUndefined: true })', () => {
const s = doc.createNode({ x: true, y: undefined }, { keepUndefined: true })
expect(s).toBeInstanceOf(YAMLMap)
Expand All @@ -111,6 +138,7 @@ describe('objects', () => {
{ type: PairType.PAIR, key: { value: 'y' }, value: { value: null } }
])
})

describe('{ x: 3, y: [4], z: { w: "five", v: 6 } }', () => {
const object = { x: 3, y: [4], z: { w: 'five', v: 6 } }
test('createNode(value)', () => {
Expand Down

0 comments on commit 4e8d437

Please sign in to comment.