JavaScript parser and stringifier for YAML documents with complete YAML 1.1 and 1.2 support
Helper functions for node creation, type conversion, string formatting, and advanced YAML operations exposed through the util export. These utilities provide convenient access to lower-level functionality for specialized use cases.
Functions for creating and manipulating YAML AST nodes from JavaScript values.
/**
* Create YAML node from JavaScript value
* @param value - Value to convert to node
* @param options - Node creation options
* @returns Created YAML node
*/
function createNode(value: unknown, options?: CreateNodeOptions): Node;
/**
* Create key-value pair node
* @param key - Key for the pair
* @param value - Value for the pair
* @param options - Node creation options
* @returns Pair node
*/
function createPair(key: any, value: any, options?: CreateNodeOptions): Pair;
interface CreateNodeContext {
/** Document schema */
schema: Schema;
/** Creation options */
options: CreateNodeOptions;
/** Anchor map for deduplication */
anchors?: Map<unknown, string>;
/** Current creation path */
path?: (string | number)[];
}Usage Examples:
import { createNode, createPair } from "yaml/util";
import { Schema } from "yaml";
const schema = new Schema({ version: '1.2' });
// Create scalar nodes
const stringNode = createNode('Hello World', { schema });
const numberNode = createNode(42, { schema });
const booleanNode = createNode(true, { schema });
// Create collection nodes
const arrayNode = createNode(['item1', 'item2', 'item3'], {
schema,
flow: true // Use flow style: [item1, item2, item3]
});
const objectNode = createNode({
name: 'John Doe',
age: 30,
active: true
}, {
schema,
sortMapEntries: true // Sort keys alphabetically
});
// Create pair nodes
const keyValuePair = createPair('config', { timeout: 30, retries: 3 }, { schema });
// Advanced node creation with anchors
const sharedConfig = { host: 'localhost', port: 5432 };
const node1 = createNode(sharedConfig, {
schema,
anchorPrefix: 'config'
});
const node2 = createNode(sharedConfig, {
schema,
anchorPrefix: 'config' // Will reuse anchor
});
console.log(stringNode.toString()); // "Hello World"
console.log(arrayNode.toString()); // [item1, item2, item3]
console.log(keyValuePair.toString()); // config: {timeout: 30, retries: 3}Functions for converting between YAML nodes and JavaScript values.
/**
* Convert YAML node to JavaScript value
* @param value - YAML node or value to convert
* @param arg - Conversion options or context
* @returns JavaScript representation
*/
function toJS(value: unknown, arg?: string | ToJSOptions): any;
interface ToJSContext {
/** Document being processed */
doc: Document;
/** Current conversion path */
path: (string | number)[];
/** Anchors encountered during conversion */
anchors: Map<Node, unknown>;
/** Maximum alias count */
maxAliasCount: number;
/** Keep scalar wrapper objects */
keepScalar?: boolean;
/** Map representation preference */
mapAsMap?: boolean;
}Usage Examples:
import { toJS } from "yaml/util";
import { parseDocument } from "yaml";
const doc = parseDocument(`
config:
database: &db
host: localhost
port: 5432
ssl: true
development:
<<: *db
name: dev_database
production:
<<: *db
name: prod_database
host: prod.example.com
`);
// Basic conversion
const basicJS = toJS(doc.contents);
console.log(basicJS);
// Conversion with options
const jsWithMaps = toJS(doc.contents, {
mapAsMap: true, // Use Map objects instead of plain objects
keepScalar: false, // Don't keep Scalar wrapper objects
maxAliasCount: 100 // Prevent infinite recursion
});
// Custom reviver function
const jsWithReviver = toJS(doc.contents, {
reviver: (key, value) => {
// Transform date strings to Date objects
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
return new Date(value);
}
return value;
}
});
// Convert individual nodes
const configNode = doc.get(['config'], true);
if (configNode) {
const configJS = toJS(configNode, { mapAsMap: false });
console.log('Config as plain object:', configJS);
}Helper functions for working with YAML collections (maps and sequences).
/**
* Find pair in map items by key
* @param items - Map items to search
* @param key - Key to find
* @returns Matching pair or undefined
*/
function findPair(items: Iterable<unknown>, key: unknown): Pair | undefined;Usage Examples:
import { findPair } from "yaml/util";
import { parseDocument, isPair, isSeq, isMap } from "yaml";
const doc = parseDocument(`
users:
- name: Alice
email: alice@example.com
role: admin
- name: Bob
email: bob@example.com
role: user
- name: Charlie
email: charlie@example.com
role: user
`);
const usersNode = doc.get(['users'], true);
if (isSeq(usersNode)) {
usersNode.items.forEach((userNode, index) => {
if (isMap(userNode)) {
// Find specific fields
const namePair = findPair(userNode.items, 'name');
const emailPair = findPair(userNode.items, 'email');
const rolePair = findPair(userNode.items, 'role');
if (namePair && emailPair && rolePair) {
console.log(`User ${index + 1}:`);
console.log(` Name: ${namePair.value}`);
console.log(` Email: ${emailPair.value}`);
console.log(` Role: ${rolePair.value}`);
}
}
});
}
// Modify found pairs
const configDoc = parseDocument(`
database:
host: localhost
port: 5432
ssl: false
`);
const dbNode = configDoc.get(['database'], true);
if (isMap(dbNode)) {
const sslPair = findPair(dbNode.items, 'ssl');
if (sslPair && isPair(sslPair)) {
sslPair.value = new Scalar(true); // Enable SSL
console.log('Updated config:', configDoc.toString());
}
}Access to built-in schema tags for custom schema creation and manipulation.
/**
* Built-in map tag definition
*/
declare const mapTag: ScalarTag;
/**
* Built-in sequence tag definition
*/
declare const seqTag: ScalarTag;
/**
* Built-in string tag definition
*/
declare const stringTag: ScalarTag;Usage Examples:
import { mapTag, seqTag, stringTag } from "yaml/util";
import { Schema } from "yaml";
// Create custom schema with built-in tags
const customSchema = new Schema({
version: '1.2',
tags: [
mapTag,
seqTag,
stringTag,
// Add custom tags
{
tag: '!date',
resolve: (str) => new Date(str),
stringify: (date) => date.toISOString().split('T')[0],
test: /^\d{4}-\d{2}-\d{2}$/
}
]
});
// Use tags directly
console.log('Map tag:', mapTag.tag); // !!map
console.log('Seq tag:', seqTag.tag); // !!seq
console.log('String tag:', stringTag.tag); // !!str
// Custom tag behavior
const testValue = "2023-12-01";
const customTag = customSchema.tags.find(tag => tag.tag === '!date');
if (customTag && customTag.test && customTag.resolve) {
if (customTag.test.test(testValue)) {
const resolved = customTag.resolve(testValue);
console.log('Resolved date:', resolved); // Date object
}
}Advanced string formatting and processing functions for YAML output.
/**
* Fold long lines in flow scalars
* @param text - Text to fold
* @param options - Folding options
* @returns Folded text
*/
function foldFlowLines(text: string, options?: FoldOptions): string;
interface FoldOptions {
/** Maximum line width */
lineWidth?: number;
/** Minimum content width */
minContentWidth?: number;
/** Handle lines with indicators */
indicatorWidth?: number;
/** More aggressive folding */
onFold?: () => void;
}
/**
* Convert number to YAML string representation
* @param value - Number to stringify
* @returns YAML string representation
*/
function stringifyNumber(value: number): string;
/**
* Convert string to YAML string representation
* @param value - String to convert
* @param ctx - Stringify context
* @param onComment - Comment handler
* @param onChompKeep - Chomp keep handler
* @returns YAML string representation
*/
function stringifyString(
value: string,
ctx?: StringifyContext,
onComment?: () => void,
onChompKeep?: () => void
): string;
interface StringifyContext {
/** Document being stringified */
doc: Document;
/** Indentation string */
indent: string;
/** Current indentation level */
indentStep: string;
/** Stringify options */
options: ToStringOptions;
/** Type of container */
type?: Scalar.Type;
}Usage Examples:
import {
foldFlowLines,
stringifyNumber,
stringifyString
} from "yaml/util";
// Fold long text
const longText = "This is a very long line of text that should be folded across multiple lines to improve readability and conform to line width constraints in YAML documents.";
const folded = foldFlowLines(longText, {
lineWidth: 60,
minContentWidth: 20
});
console.log('Folded text:');
console.log(folded);
// Output:
// This is a very long line of text that should be folded
// across multiple lines to improve readability and conform to
// line width constraints in YAML documents.
// Number stringification
console.log(stringifyNumber(42)); // "42"
console.log(stringifyNumber(3.14159)); // "3.14159"
console.log(stringifyNumber(Infinity)); // ".inf"
console.log(stringifyNumber(-Infinity)); // "-.inf"
console.log(stringifyNumber(NaN)); // ".nan"
// String stringification with context
const ctx = {
doc: new Document(),
indent: '',
indentStep: ' ',
options: {
lineWidth: 80,
singleQuote: false,
blockQuote: false
}
};
// Simple string
console.log(stringifyString('Hello World', ctx)); // Hello World
// String requiring quotes
console.log(stringifyString('Hello: World', ctx)); // "Hello: World"
// Multiline string
const multiline = 'Line 1\nLine 2\nLine 3';
console.log(stringifyString(multiline, {
...ctx,
options: { ...ctx.options, blockQuote: 'literal' }
}));
// Output:
// |
// Line 1
// Line 2
// Line 3Logging functions for debugging and development.
type LogLevelId = 'silent' | 'error' | 'warn' | 'debug';
/**
* Debug logging function
* @param logLevel - Current log level
* @param messages - Messages to log
*/
function debug(logLevel: LogLevelId, ...messages: any[]): void;
/**
* Warning logging function
* @param logLevel - Current log level
* @param messages - Messages to log
*/
function warn(logLevel: LogLevelId, ...messages: any[]): void;Usage Examples:
import { debug, warn } from "yaml/util";
// Configure logging level
const logLevel = 'debug';
// Debug messages (only shown when logLevel is 'debug')
debug(logLevel, 'Processing YAML document');
debug(logLevel, 'Found', 5, 'top-level keys');
// Warning messages (shown when logLevel is 'warn' or 'debug')
warn(logLevel, 'Deprecated YAML 1.1 syntax detected');
warn(logLevel, 'Duplicate key found:', 'database.host');
// Silent mode (nothing logged)
const silentLevel = 'silent';
debug(silentLevel, 'This will not be logged');
warn(silentLevel, 'This will also not be logged');
// Conditional logging
function processConfig(config: any, logLevel: LogLevelId) {
debug(logLevel, 'Starting config processing');
if (!config.database) {
warn(logLevel, 'No database configuration found');
}
debug(logLevel, 'Config processing completed');
}
processConfig({ app: 'MyApp' }, 'debug');Combine utilities for complex YAML processing workflows.
import {
createNode,
createPair,
toJS,
findPair,
stringifyString
} from "yaml/util";
import {
Schema,
Document,
isMap,
isPair,
YAMLMap,
Scalar
} from "yaml";
class YAMLConfigManager {
private schema: Schema;
constructor() {
this.schema = new Schema({
version: '1.2',
sortMapEntries: true
});
}
// Merge configurations with proper YAML structure
mergeConfigs(base: any, override: any): Document {
const doc = new Document();
// Create base structure
const baseNode = createNode(base, {
schema: this.schema,
sortMapEntries: true
});
// Apply overrides
const overrideNode = createNode(override, {
schema: this.schema,
sortMapEntries: true
});
// Merge logic
const merged = this.deepMergeNodes(baseNode, overrideNode);
doc.contents = merged;
return doc;
}
private deepMergeNodes(base: any, override: any): any {
if (isMap(base) && isMap(override)) {
const result = new YAMLMap(this.schema);
// Add all base pairs
base.items.forEach(pair => {
if (isPair(pair)) {
result.items.push(createPair(pair.key, pair.value, {
schema: this.schema
}));
}
});
// Override/merge with override pairs
override.items.forEach(overridePair => {
if (isPair(overridePair)) {
const existingPair = findPair(result.items, overridePair.key);
if (existingPair && isPair(existingPair)) {
// Merge nested objects
existingPair.value = this.deepMergeNodes(
existingPair.value,
overridePair.value
);
} else {
// Add new pair
result.items.push(createPair(
overridePair.key,
overridePair.value,
{ schema: this.schema }
));
}
}
});
return result;
}
// For non-objects, override takes precedence
return override;
}
// Extract configuration section
extractSection(doc: Document, path: string[]): any {
let current = doc.contents;
for (const key of path) {
if (isMap(current)) {
const pair = findPair(current.items, key);
if (pair && isPair(pair)) {
current = pair.value;
} else {
return null;
}
} else {
return null;
}
}
return toJS(current);
}
// Validate configuration structure
validateConfig(doc: Document, requiredKeys: string[]): string[] {
const errors: string[] = [];
if (!isMap(doc.contents)) {
errors.push('Root must be a mapping');
return errors;
}
for (const key of requiredKeys) {
const pair = findPair(doc.contents.items, key);
if (!pair) {
errors.push(`Missing required key: ${key}`);
}
}
return errors;
}
}
// Usage example
const manager = new YAMLConfigManager();
const baseConfig = {
app: {
name: 'MyApp',
version: '1.0.0',
debug: false
},
database: {
host: 'localhost',
port: 5432,
ssl: false
}
};
const prodOverride = {
app: {
debug: false // Ensure debug is off
},
database: {
host: 'prod.example.com',
ssl: true
}
};
// Merge configurations
const prodConfig = manager.mergeConfigs(baseConfig, prodOverride);
console.log('Production config:');
console.log(prodConfig.toString());
// Extract and validate
const dbConfig = manager.extractSection(prodConfig, ['database']);
console.log('Database config:', dbConfig);
const errors = manager.validateConfig(prodConfig, ['app', 'database']);
if (errors.length > 0) {
console.log('Validation errors:', errors);
} else {
console.log('Configuration is valid');
}Install with Tessl CLI
npx tessl i tessl/npm-yaml