JavaScript parser and stringifier for YAML documents with complete YAML 1.1 and 1.2 support
Comprehensive error reporting system with position information, warning messages, and configurable logging. The YAML library provides detailed error tracking for robust YAML processing and debugging capabilities.
The library provides three main error classes for different types of issues encountered during YAML processing.
/**
* Base error class for all YAML-related errors
*/
class YAMLError extends Error {
/** Error name identifier */
name: 'YAMLError';
/** Error code identifier */
code: ErrorCode;
/** Human-readable error message */
message: string;
/** Optional position information [start, end] */
pos?: [number, number];
constructor(pos: [number, number] | null, code: ErrorCode, message: string);
}
/**
* Parse-time errors with position information
*/
class YAMLParseError extends YAMLError {
/** Error name identifier */
name: 'YAMLParseError';
/** Position in source [start, end] */
pos: [number, number];
/** Line and column position */
linePos?: { line: number; col: number };
constructor(pos: [number, number], code: ErrorCode, message: string);
}
/**
* Warning messages for non-fatal issues
*/
class YAMLWarning extends YAMLError {
/** Warning name identifier */
name: 'YAMLWarning';
constructor(pos: [number, number] | null, code: ErrorCode, message: string);
}Comprehensive set of error codes covering all possible YAML parsing issues.
type ErrorCode =
| 'BAD_ALIAS' // Invalid alias reference
| 'BAD_DIRECTIVE' // Invalid document directive
| 'BAD_DQ_ESCAPE' // Invalid escape sequence in double-quoted string
| 'BAD_INDENT' // Incorrect indentation
| 'BAD_PROP_ORDER' // Properties in wrong order
| 'BAD_SCALAR_START' // Invalid scalar start character
| 'BLOCK_AS_IMPLICIT_KEY' // Block scalar used as implicit key
| 'BLOCK_IN_FLOW' // Block scalar in flow context
| 'DUPLICATE_KEY' // Duplicate key in mapping
| 'IMPOSSIBLE' // Internal parser error
| 'KEY_OVER_1024_CHARS' // Key exceeds maximum length
| 'MISSING_CHAR' // Expected character missing
| 'MULTILINE_IMPLICIT_KEY' // Multiline implicit key
| 'MULTIPLE_ANCHORS' // Multiple anchors on single node
| 'MULTIPLE_DOCS' // Multiple documents in single-doc context
| 'MULTIPLE_TAGS' // Multiple tags on single node
| 'TAB_AS_INDENT' // Tab character used for indentation
| 'TAG_RESOLVE_FAILED' // Tag resolution failed
| 'UNEXPECTED_TOKEN'; // Unexpected token encounteredUsage Examples:
import { parse, parseDocument, YAMLParseError, YAMLWarning } from "yaml";
// Handle parse errors
try {
const data = parse(`
invalid: [unclosed array
another: key
`);
} catch (error) {
if (error instanceof YAMLParseError) {
console.log('Parse Error Details:');
console.log('- Code:', error.code);
console.log('- Message:', error.message);
console.log('- Position:', error.pos);
console.log('- Line/Col:', error.linePos);
}
}
// Document-level error handling
const doc = parseDocument(`
key1: value1
key1: duplicate_value # DUPLICATE_KEY warning
invalid_alias: *nonexistent # BAD_ALIAS error
`);
// Check for errors
if (doc.errors.length > 0) {
console.log('Document Errors:');
doc.errors.forEach((error, index) => {
console.log(`${index + 1}. ${error.code}: ${error.message}`);
if (error.pos) {
console.log(` Position: ${error.pos[0]}-${error.pos[1]}`);
}
});
}
// Check for warnings
if (doc.warnings.length > 0) {
console.log('Document Warnings:');
doc.warnings.forEach((warning, index) => {
console.log(`${index + 1}. ${warning.code}: ${warning.message}`);
});
}Errors include detailed position information for precise error location.
interface LinePos {
/** Line number (1-based) */
line: number;
/** Column number (1-based) */
col: number;
}
type Range = [start: number, valueEnd: number, nodeEnd: number];Usage Examples:
import { parseDocument, prettifyError } from "yaml";
const source = `
name: John Doe
age: thirty # Invalid number
email: invalid-email # Missing @ symbol
items:
- item1
- item2
- nested_error # Invalid nesting
`;
const doc = parseDocument(source, { prettyErrors: true });
// Display errors with context
doc.errors.forEach(error => {
console.log('\n--- Error Details ---');
console.log('Code:', error.code);
console.log('Message:', error.message);
if (error.pos) {
console.log('Character range:', error.pos);
// Extract error context
const start = Math.max(0, error.pos[0] - 20);
const end = Math.min(source.length, error.pos[1] + 20);
const context = source.substring(start, end);
console.log('Context:', JSON.stringify(context));
}
if (error instanceof YAMLParseError && error.linePos) {
console.log(`Line ${error.linePos.line}, Column ${error.linePos.col}`);
}
});Pretty error formatting with source context for better debugging experience.
/**
* Add source context to error messages
* @param source - Original YAML source string
* @param lineCounter - Line position tracker
* @returns Function to format errors with context
*/
function prettifyError(source: string, lineCounter: LineCounter): (error: YAMLError) => void;
class LineCounter {
/**
* Add new line to position tracking
* @param offset - Character offset of new line
*/
addNewLine(offset: number): void;
/**
* Get line and column position for character offset
* @param pos - Character position
* @returns Line and column numbers
*/
linePos(pos: number): LinePos;
}Usage Examples:
import { parseDocument, prettifyError, LineCounter } from "yaml";
const source = `
config:
database:
host: localhost
port: "invalid_port" # Should be number
credentials:
username: admin
password: # Missing value
`;
// Parse with pretty errors enabled
const doc = parseDocument(source, {
prettyErrors: true,
lineCounter: true
});
// Errors are automatically prettified when prettyErrors: true
doc.errors.forEach(error => {
console.log(error.message); // Includes formatted context
});
// Manual error prettification
const lineCounter = new LineCounter();
const prettyFormatter = prettifyError(source, lineCounter);
// Simulate parsing with line counter
source.split('\n').forEach((line, index) => {
lineCounter.addNewLine(line.length + 1); // +1 for newline char
});
// Apply formatting to errors
doc.errors.forEach(prettyFormatter);Configurable logging for controlling error and warning output.
type LogLevelId = 'silent' | 'error' | 'warn' | 'debug';
/**
* Debug-level logging function
* @param logLevel - Current log level setting
* @param messages - Messages to log
*/
function debug(logLevel: LogLevelId, ...messages: any[]): void;
/**
* Warning-level logging function
* @param logLevel - Current log level setting
* @param messages - Messages to log
*/
function warn(logLevel: LogLevelId, ...messages: any[]): void;Usage Examples:
import { parseDocument, debug, warn } from "yaml";
// Configure logging levels
const quietDoc = parseDocument('key: value', {
logLevel: 'silent' // Suppress all output
});
const verboseDoc = parseDocument('key: value', {
logLevel: 'debug' // Show all messages
});
// Manual logging
warn('warn', 'This is a warning message');
debug('debug', 'This is a debug message', { additional: 'data' });
// Document with custom warning handling
const doc = parseDocument(`
key1: value1
key1: duplicate # Will generate warning
`);
// Process warnings manually
doc.warnings.forEach(warning => {
if (warning.code === 'DUPLICATE_KEY') {
console.log('Found duplicate key:', warning.message);
}
});The library continues parsing even when errors are encountered, providing maximum information about document issues.
import { parseDocument, parse } from "yaml";
// Malformed YAML with multiple errors
const malformedYaml = `
key1: value1
key2: [unclosed, array
key3:
nested: value
key2: duplicate
invalid_anchor_ref: *missing
key4: "unclosed string
`;
// parseDocument continues despite errors
const doc = parseDocument(malformedYaml);
console.log('Parsed content:', doc.toJS()); // Returns what could be parsed
console.log('Error count:', doc.errors.length);
console.log('Warning count:', doc.warnings.length);
// parse() throws on first error
try {
const data = parse(malformedYaml);
} catch (error) {
console.log('Parse failed:', error.message);
}
// Graceful error handling with fallback
function safeParseYaml(source: string, fallback: any = {}) {
try {
return parse(source);
} catch (error) {
console.warn('YAML parse failed, using fallback:', error.message);
return fallback;
}
}
const config = safeParseYaml(malformedYaml, { defaultConfig: true });import { parseDocument, YAMLError, YAMLParseError, YAMLWarning } from "yaml";
class YAMLProcessor {
private errors: YAMLError[] = [];
private warnings: YAMLWarning[] = [];
processDocument(source: string) {
const doc = parseDocument(source, { logLevel: 'silent' });
// Collect all issues
this.errors.push(...doc.errors);
this.warnings.push(...doc.warnings);
// Custom error categorization
const criticalErrors = this.errors.filter(e =>
['BAD_DIRECTIVE', 'IMPOSSIBLE'].includes(e.code)
);
const minorErrors = this.errors.filter(e =>
['DUPLICATE_KEY', 'TAB_AS_INDENT'].includes(e.code)
);
// Report based on severity
if (criticalErrors.length > 0) {
throw new Error(`Critical YAML errors: ${criticalErrors.length}`);
}
if (minorErrors.length > 0) {
console.warn(`Minor YAML issues: ${minorErrors.length}`);
}
return doc;
}
getErrorReport() {
return {
totalErrors: this.errors.length,
totalWarnings: this.warnings.length,
errorsByCode: this.groupByCode(this.errors),
warningsByCode: this.groupByCode(this.warnings)
};
}
private groupByCode(issues: YAMLError[]) {
return issues.reduce((acc, issue) => {
acc[issue.code] = (acc[issue.code] || 0) + 1;
return acc;
}, {} as Record<string, number>);
}
}
// Usage
const processor = new YAMLProcessor();
try {
const doc = processor.processDocument(malformedYaml);
console.log('Processing completed with warnings');
} catch (error) {
console.error('Processing failed:', error.message);
}
console.log('Error report:', processor.getErrorReport());Install with Tessl CLI
npx tessl i tessl/npm-yaml