Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor options #235

Merged
merged 20 commits into from Mar 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
cfc42ad
Add options argument to doc.toString()
eemeli Feb 27, 2021
ebd65fe
Collect external option interfaces in src/options.ts: SchemaOptions, …
eemeli Feb 28, 2021
3a984de
Drop the keepCstNodes and keepNodeTypes options
eemeli Feb 28, 2021
44d3593
Split ParseOptions from DocumentOptions; drop { [key: string]: unknow…
eemeli Feb 28, 2021
b64bc4d
Use type rather than interface for options
eemeli Feb 28, 2021
dfcc35b
Drop mapAsMap from DocumentOptions
eemeli Feb 28, 2021
d16d170
Move maxAliasCount from DocumentOptions to ToJSOptions
eemeli Feb 28, 2021
09a2175
Drop indent, indentSeq & simpleKeys from DocumentOptions
eemeli Feb 28, 2021
b76e620
Refactor doc.setSchema(); drop Document.defaults
eemeli Feb 28, 2021
fbe193e
Move BigInt option from scalarOptions to ParseOptions
eemeli Feb 28, 2021
37e6688
Move falseStr, nullStr & trueStr options from scalarOptions to ParseO…
eemeli Feb 28, 2021
bd3bec5
Simplify StringifyContext creation
eemeli Mar 1, 2021
3ccdb13
Move lineWidth & minContentWidth options from scalarOptions to ParseO…
eemeli Feb 28, 2021
5f1df49
Drop special options for !!binary
eemeli Mar 1, 2021
21d4060
Move double-quoted string options from scalarOptions to ParseOptions
eemeli Feb 28, 2021
67de2e1
Move remaining string options from scalarOptions to ParseOptions
eemeli Feb 28, 2021
f281157
Rewrite options section of documentation
eemeli Mar 1, 2021
24383fc
Cleanup: Drop scalarOptions (now empty) & options from tag interface
eemeli Mar 1, 2021
4e8d437
Add flow: boolean option to CreateNodeOptions
eemeli Mar 1, 2021
7f76212
Replace doc.directivesEndMarker with doc.directives.marker & directiv…
eemeli Mar 7, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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