Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,8 @@ an exception on those.

options:

- `filename` _(default: null)_ - string to be used as a file path in
error/warning messages.
- `filename` _(default: null)_ - string to be used as a file path in error
messages.
- `schema` _(default: `CORE_SCHEMA`)_ - specifies a schema to use.
- `FAILSAFE_SCHEMA` - only strings, arrays and plain objects.
- `JSON_SCHEMA` - all JSON-supported types.
Expand Down
9 changes: 6 additions & 3 deletions src/ast/from_js.ts
Original file line number Diff line number Diff line change
Expand Up @@ -150,9 +150,12 @@ function build (state: FromJsState, object: unknown): Node | typeof INVALID {
return node
}

// A JS value is one YAML document. An unrepresentable root becomes an empty
// document, which the presenter renders as an empty string.
/** @category AST */
/**
* A JS value is one YAML document. An unrepresentable root becomes an empty
* document, which the presenter renders as an empty string.
*
* @category AST
*/
function jsToAst (input: unknown, schema: Schema, options: FromJsOptions = {}): Document[] {
const state: FromJsState = {
representTypes: buildRepresentTypes(schema),
Expand Down
33 changes: 22 additions & 11 deletions src/ast/nodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ class Style {

/** @category Nodes */
interface NodeBase {
// YAML tag. Untagged nodes carry the semantic resolved tag; tagged nodes carry
// the printable/verbatim tag spelling.
/**
* YAML tag. Untagged nodes carry the semantic resolved tag; tagged nodes carry
* the printable/verbatim tag spelling.
*/
tag: string
style: Style
anchor?: string

// Reserved for the formatting layer; not populated by the dumper yet.
/** Reserved for the formatting layer; not populated by the dumper yet. */
commentBefore?: string
comment?: string
commentAfter?: string
Expand Down Expand Up @@ -50,21 +52,30 @@ interface MappingNode extends NodeBase {
/** @category Nodes */
interface AliasNode extends NodeBase {
kind: 'alias'
// The anchor name this alias points at (`*name`).
/** The anchor name this alias points at (`*name`). */
anchor: string
}

/** @category Nodes */
type Node = ScalarNode | SequenceNode | MappingNode | AliasNode

// The layer above `Node`: each document wraps one content node plus its own
// markers/directives. Not a member of `Node` — the fields differ. Document
// directives are ordered presentation data.
/** @category Nodes */
/**
* The layer above {@link Node}: each document wraps one content node plus its
* own markers/directives. Not a member of {@link Node} — the fields differ.
* Document directives are ordered presentation data.
*
* @category Nodes
*/
interface Document {
contents: Node | null // null = empty document
explicitStart?: boolean // print '---'
explicitEnd?: boolean // print '...'
/** null = empty document */
contents: Node | null

/** print '---' */
explicitStart?: boolean

/** print '...' */
explicitEnd?: boolean

directives: DocumentDirective[]
}

Expand Down
82 changes: 80 additions & 2 deletions src/ast/presenter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,92 @@ ESCAPE_SEQUENCES[0x2029] = '\\P'
/** @category AST */
interface PresenterOptions {
schema: Schema

/**
* Indentation width in spaces.
*
* @defaultValue `2`
*/
indent?: number

/**
* Does not add an indentation level to array elements when enabled.
*
* @defaultValue `false`
*/
seqNoIndent?: boolean

/**
* Allows a nested collection to start on the same line after `-`.
*
* @defaultValue `true`
*/
seqInlineFirst?: boolean

/**
* Sorts mapping keys when `true`. A function can be provided to define the
* sort order.
*
* @defaultValue `false`
*/
sortKeys?: boolean | ((a: any, b: any) => number)

/**
* Maximum line width. Set to `-1` for unlimited width.
*
* @defaultValue `80`
*/
lineWidth?: number

/**
* Adds spaces inside flow collection brackets: `{a: 1}` becomes `{ a: 1 }`.
*
* @defaultValue `false`
*/
flowBracketPadding?: boolean

/**
* Omits the space after commas in flow collections: `[1, 2]` becomes
* `[1,2]`.
*
* @defaultValue `false`
*/
flowSkipCommaSpace?: boolean

/**
* Omits the space after `:` in flow mappings: `{a: 1}` becomes `{a:1}`.
*
* @defaultValue `false`
*/
flowSkipColonSpace?: boolean

/**
* Quotes flow mapping keys: `{a: 1}` becomes `{"a": 1}`.
*
* @defaultValue `false`
*/
quoteFlowKeys?: boolean

/**
* Quoting style to use when a string needs quotes.
*
* @defaultValue `'single'`
*/
quoteStyle?: 'single' | 'double'

/**
* Quotes all non-key strings using {@link quoteStyle}.
*
* @defaultValue `false`
*/
forceQuotes?: boolean

/**
* Prints an explicit tag before an anchor: `&ref_0 !!set` becomes
* `!!set &ref_0`.
*
* @defaultValue `false`
*/
tagBeforeAnchor?: boolean
}

Expand Down Expand Up @@ -976,8 +1051,11 @@ function writeDocumentDirectives (doc: Document) {
return result
}

// Documents → text, including the trailing newline.
/** @category AST */
/**
* Documents → text, including the trailing newline.
*
* @category AST
*/
function present (documents: Document[], options: PresenterOptions): string {
const state = createPresenterState(options)
let result = ''
Expand Down
31 changes: 22 additions & 9 deletions src/ast/visit.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,23 @@ const VISIT_SKIP = Symbol('visit:skip') // don't descend into this node's chil

type VisitControl = typeof VISIT_BREAK | typeof VISIT_SKIP | undefined | void

// Traversal-derived position of the current node. Kept off the node itself: a
// node may sit in several places (alias/dedup reuse), so depth/role belong to
// the walk, not the node. `parent.kind` + `isKey` pin the exact slot.
/** @category AST */
/**
* Traversal-derived position of the current node. Kept off the node itself: a
* node may sit in several places (alias/dedup reuse), so depth/role belong to
* the walk, not the node. {@link VisitContext.parent} `kind` +
* {@link VisitContext.isKey} pin the exact slot.
*
* @category AST
*/
interface VisitContext {
depth: number // 0 = document content root
parent: Node | null // enclosing sequence/mapping, null at the root
isKey: boolean // node sits in a mapping key position
/** 0 = document content root */
depth: number

/** Enclosing sequence/mapping, null at the root */
parent: Node | null

/** Node sits in a mapping key position */
isKey: boolean
}

/** @category AST */
Expand Down Expand Up @@ -54,8 +63,12 @@ function visitNode (node: Node, visitor: Visitor, ctx: VisitContext): boolean {
return false
}

// Walk every node in the documents, calling `visitor` once per node (pre-order).
/** @category AST */
/**
* Walk every node in the documents, calling {@link Visitor} once per
* node (pre-order).
*
* @category AST
*/
function visit (documents: Document[], visitor: Visitor): void {
for (const doc of documents) {
if (doc.contents && visitNode(doc.contents, visitor, { depth: 0, parent: null, isKey: false })) return
Expand Down
1 change: 1 addition & 0 deletions src/common/exception.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ function formatError (exception: YAMLException, compact?: boolean) {
return `${exception.reason} ${where}`
}

/** @category Main */
class YAMLException extends Error {
reason: string
mark?: SnippetMark
Expand Down
36 changes: 36 additions & 0 deletions src/dump.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,39 @@ import { intYaml11Tag } from './tag/scalar/int_yaml11.ts'
import { floatCoreTag } from './tag/scalar/float_core.ts'
import { floatYaml11Tag } from './tag/scalar/float_yaml11.ts'

/** @category Main */
interface DumpOptions extends Omit<PresenterOptions, 'schema'> {
/**
* Schema to use.
*
* @defaultValue A {@link YAML11_SCHEMA}-based schema.
*/
schema?: Schema

/**
* Skips invalid types instead of throwing. Invalid mapping pairs and sequence
* items are skipped; `undefined` sequence items are serialized as `null`.
*
* @defaultValue `false`
*/
skipInvalid?: boolean

/**
* Inlines duplicate objects instead of converting them into references.
*
* @defaultValue `false`
*/
noRefs?: boolean

/**
* Nesting level at which collections switch from block to flow style. Set to
* `-1` to never switch automatically.
*
* @defaultValue `-1`
*/
flowLevel?: number

/** Mutates the generated AST before it is rendered. */
transform?: (documents: Document[]) => void
}

Expand Down Expand Up @@ -52,6 +80,14 @@ const DEFAULT_DUMP_OPTIONS: Required<DumpOptions> = {

// Options that need the JS value (tags, format, dedup) go to `jsToAst`; purely
// presentational ones go to `present`.
/**
* Serializes `object` as a YAML document. By default it can dump every
* supported YAML type, so it throws an exception if you try to dump regexps or
* functions. However, you can disable exceptions by setting the
* {@link DumpOptions.skipInvalid} option to `true`.
*
* @category Main
*/
function dump (input: any, options: DumpOptions = {}) {
const opts = { ...DEFAULT_DUMP_OPTIONS, ...options }

Expand Down
47 changes: 46 additions & 1 deletion src/load.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
} from './parser/parser.ts'

// `source` is supplied by `loadDocuments` itself, not by the public caller.
/** @category Main */
interface LoadOptions extends ParserOptions, Omit<ConstructorOptions, 'source'> {}

type LoadAllIterator = (document: unknown) => void
Expand All @@ -34,9 +35,22 @@ function loadDocuments (input: string, options: LoadOptions = {}) {
return constructFromEvents(events, { ...pick(opts, CONSTRUCTOR_OPT_KEYS), source })
}

// Signatures with iterator are deprecated. Will be removed in the next versions.
/**
* Same as {@link load}, but understands multi-document sources.
* Returns an array of documents.
*
* @category Main
*/
function loadAll (input: string, options?: LoadOptions): unknown[]

/**
* @deprecated Iterator is not supported.
*/
function loadAll (input: string, iterator: null, options?: LoadOptions): unknown[]

/**
* @deprecated Iterator is not supported.
*/
function loadAll (input: string, iterator: LoadAllIterator, options?: LoadOptions): void
function loadAll (
input: string,
Expand All @@ -57,6 +71,37 @@ function loadAll (
for (const document of documents) iterator(document)
}

/**
* Parses `string` as a single YAML document. Throws {@link YAMLException} on
* error. This function does not understand multi-document or empty sources; it
* throws an exception on those.
*
* > [!WARNING]
* > When processing untrusted input, see the
* > [security considerations](../docs/safety.md).
*
* > [!NOTE]
* > The default {@link CORE_SCHEMA} comes without the `!!merge` tag. You can
* > easily enable it if needed.
*
* > [!WARNING]
* > The default {@link mapTag} is `{}`-object based and does not allow complex
* > keys (objects, arrays and so on). That's an intentional choice for
* > convenience. Also, non-string scalar keys, such as `null`, numbers or
* > booleans, are converted to strings. For non-string keys use
* > {@link realMapTag} instead (it uses native JS `Map`).
*
* @example
* Enable {@link mergeTag} and {@link realMapTag}:
*
* ```javascript
* import { load, CORE_SCHEMA, mergeTag, realMapTag } from 'js-yaml'
*
* load(data, { schema: CORE_SCHEMA.withTags(mergeTag, realMapTag) })
* ```
*
* @category Main
*/
function load (input: string, options?: LoadOptions) {
const documents = loadDocuments(input, options)

Expand Down
Loading
Loading