Skip to content

Commit

Permalink
Merge pull request #235 from eemeli/new-options
Browse files Browse the repository at this point in the history
Refactor options
  • Loading branch information
eemeli committed Mar 8, 2021
2 parents 03838a9 + 7f76212 commit 5d03923
Show file tree
Hide file tree
Showing 42 changed files with 930 additions and 1,021 deletions.
1 change: 0 additions & 1 deletion README.md
Expand Up @@ -34,7 +34,6 @@ const YAML = require('yaml')

### YAML Documents

- [`YAML.defaultOptions`](https://eemeli.org/yaml/#options)
- [`YAML.Document`](https://eemeli.org/yaml/#yaml-documents)
- [`constructor(value, replacer?, options?)`](https://eemeli.org/yaml/#creating-documents)
- [`defaults`](https://eemeli.org/yaml/#options)
Expand Down
1 change: 0 additions & 1 deletion docs/01_intro.md
Expand Up @@ -39,7 +39,6 @@ const YAML = require('yaml')

<h3>Documents</h3>

- [`YAML.defaultOptions`](#options)
- [`YAML.Document`](#documents)
- [`constructor(value, replacer?, options?)`](#creating-documents)
- [`defaults`](#options)
Expand Down
208 changes: 119 additions & 89 deletions docs/03_options.md

Large diffs are not rendered by default.

97 changes: 56 additions & 41 deletions docs/04_documents.md

Large diffs are not rendered by default.

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
41 changes: 17 additions & 24 deletions docs/06_custom_tags.md
@@ -1,18 +1,15 @@
# Custom Data Types

```js
YAML.parse('!!timestamp 2001-12-15 2:59:43')
// YAMLWarning:
// The tag tag:yaml.org,2002:timestamp is unavailable,
// falling back to tag:yaml.org,2002:str
// '2001-12-15 2:59:43'
import { parse, parseDocument } from 'yaml'

YAML.defaultOptions.customTags = ['timestamp']
parse('2001-12-15 2:59:43')
// '2001-12-15 2:59:43'

YAML.parse('2001-12-15 2:59:43') // returns a Date instance
// 2001-12-15T02:59:43.000Z
parse('!!timestamp 2001-12-15 2:59:43')
// 2001-12-15T02:59:43.000Z (Date instance)

const doc = YAML.parseDocument('2001-12-15 2:59:43')
const doc = parseDocument('2001-12-15 2:59:43', { customTags: ['timestamp'] })
doc.contents.value.toDateString()
// 'Sat Dec 15 2001'
```
Expand All @@ -24,16 +21,14 @@ For further customisation, `customTags` may also be a function `(Tag[]) => (Tag[
## Built-in Custom Tags

```js
YAML.parse('[ one, true, 42 ]').map(v => typeof v)
// [ 'string', 'boolean', 'number' ]
parse('[ one, true, 42 ]')
// [ 'one', true, 42 ]

let opt = { schema: 'failsafe' }
YAML.parse('[ one, true, 42 ]', opt).map(v => typeof v)
// [ 'string', 'string', 'string' ]
parse('[ one, true, 42 ]', { schema: 'failsafe' })
// [ 'one', 'true', '42' ]

opt = { schema: 'failsafe', customTags: ['int'] }
YAML.parse('[ one, true, 42 ]', opt).map(v => typeof v)
// [ 'string', 'string', 'number' ]
parse('[ one, true, 42 ]', { schema: 'failsafe', customTags: ['int'] })
// [ 'one', 'true', 42 ]
```

### YAML 1.2 Core Schema
Expand Down Expand Up @@ -70,6 +65,7 @@ These tags are a part of the YAML 1.1 [language-independent types](https://yaml.
## Writing Custom Tags

```js
import { stringify } from 'yaml'
import { stringifyString } from 'yaml/util'

const regexp = {
Expand All @@ -92,12 +88,10 @@ const sharedSymbol = {
}
}

YAML.defaultOptions.customTags = [regexp, sharedSymbol]

YAML.stringify({
regexp: /foo/gi,
symbol: Symbol.for('bar')
})
stringify(
{ regexp: /foo/gi, symbol: Symbol.for('bar') },
{ customTags: [regexp, sharedSymbol] }
)
// regexp: !re /foo/gi
// symbol: !symbol/shared bar
```
Expand Down Expand Up @@ -143,7 +137,6 @@ To define your own tag, you'll need to define an object comprising of some of th
- `format: string` If a tag has multiple forms that should be parsed and/or stringified differently, use `format` to identify them. Used by `!!int` and `!!float`.
- **`identify(value): boolean`** is used by `doc.createNode()` to detect your data type, e.g. using `typeof` or `instanceof`. Required.
- `nodeClass: Node` is the `Node` child class that implements this tag. Required for collections and tags that have overlapping JS representations.
- `options: Object` is used by some tags to configure their stringification.
- **`resolve(value, onError): Node | any`** turns a parsed value into an AST node; `value` is either a `string`, a `YAMLMap` or a `YAMLSeq`. `onError(msg)` should be called with an error message string when encountering errors, as it'll allow you to still return some value for the node. If returning a non-`Node` value, the output will be wrapped as a `Scalar`. Required.
- `stringify(item, ctx, onComment, onChompKeep): string` is an optional function stringifying the `item` AST node in the current context `ctx`. `onComment` and `onChompKeep` are callback functions for a couple of special cases. If your data includes a suitable `.toString()` method, you can probably leave this undefined and use the default stringifier.
- **`tag: string`** is the identifier for your data type, with which its stringified form will be prefixed. Should either be a !-prefixed local `!tag`, or a fully qualified `tag:domain,date:foo`. Required.
Expand Down
2 changes: 1 addition & 1 deletion src/compose/compose-collection.ts
Expand Up @@ -79,7 +79,7 @@ export function composeCollection(
}
}

const res = tag.resolve(coll, msg => onError(coll.range[0], msg))
const res = tag.resolve(coll, msg => onError(coll.range[0], msg), doc.options)
const node = isNode(res)
? (res as ParsedNode)
: (new Scalar(res) as Scalar.Parsed)
Expand Down
4 changes: 1 addition & 3 deletions src/compose/compose-doc.ts
Expand Up @@ -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)
Expand Down
4 changes: 3 additions & 1 deletion src/compose/compose-scalar.ts
Expand Up @@ -26,7 +26,9 @@ export function composeScalar(

let scalar: Scalar
try {
const res = tag ? tag.resolve(value, msg => onError(offset, msg)) : value
const res = tag
? tag.resolve(value, msg => onError(offset, msg), doc.options)
: value
scalar = isScalar(res) ? res : new Scalar(res)
} catch (error) {
onError(offset, error.message)
Expand Down
2 changes: 1 addition & 1 deletion src/compose/composer.ts
Expand Up @@ -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) &&
Expand Down

0 comments on commit 5d03923

Please sign in to comment.