CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-yaml

JavaScript parser and stringifier for YAML documents with complete YAML 1.1 and 1.2 support

Overview
Eval results
Files

utilities.mddocs/

Utilities

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.

Capabilities

Node Creation Utilities

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}

Type Conversion Utilities

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);
}

Collection Utilities

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());
  }
}

Schema Tag Utilities

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
  }
}

String Processing Utilities

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 3

Logging Utilities

Logging 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');

Advanced Utility Patterns

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

docs

ast-nodes.md

document-processing.md

error-handling.md

index.md

parse-stringify.md

parser-infrastructure.md

schema-configuration.md

tree-traversal.md

type-guards.md

utilities.md

tile.json