0
# Utilities
1
2
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.
3
4
## Capabilities
5
6
### Node Creation Utilities
7
8
Functions for creating and manipulating YAML AST nodes from JavaScript values.
9
10
```typescript { .api }
11
/**
12
* Create YAML node from JavaScript value
13
* @param value - Value to convert to node
14
* @param options - Node creation options
15
* @returns Created YAML node
16
*/
17
function createNode(value: unknown, options?: CreateNodeOptions): Node;
18
19
/**
20
* Create key-value pair node
21
* @param key - Key for the pair
22
* @param value - Value for the pair
23
* @param options - Node creation options
24
* @returns Pair node
25
*/
26
function createPair(key: any, value: any, options?: CreateNodeOptions): Pair;
27
28
interface CreateNodeContext {
29
/** Document schema */
30
schema: Schema;
31
/** Creation options */
32
options: CreateNodeOptions;
33
/** Anchor map for deduplication */
34
anchors?: Map<unknown, string>;
35
/** Current creation path */
36
path?: (string | number)[];
37
}
38
```
39
40
**Usage Examples:**
41
42
```typescript
43
import { createNode, createPair } from "yaml/util";
44
import { Schema } from "yaml";
45
46
const schema = new Schema({ version: '1.2' });
47
48
// Create scalar nodes
49
const stringNode = createNode('Hello World', { schema });
50
const numberNode = createNode(42, { schema });
51
const booleanNode = createNode(true, { schema });
52
53
// Create collection nodes
54
const arrayNode = createNode(['item1', 'item2', 'item3'], {
55
schema,
56
flow: true // Use flow style: [item1, item2, item3]
57
});
58
59
const objectNode = createNode({
60
name: 'John Doe',
61
age: 30,
62
active: true
63
}, {
64
schema,
65
sortMapEntries: true // Sort keys alphabetically
66
});
67
68
// Create pair nodes
69
const keyValuePair = createPair('config', { timeout: 30, retries: 3 }, { schema });
70
71
// Advanced node creation with anchors
72
const sharedConfig = { host: 'localhost', port: 5432 };
73
const node1 = createNode(sharedConfig, {
74
schema,
75
anchorPrefix: 'config'
76
});
77
78
const node2 = createNode(sharedConfig, {
79
schema,
80
anchorPrefix: 'config' // Will reuse anchor
81
});
82
83
console.log(stringNode.toString()); // "Hello World"
84
console.log(arrayNode.toString()); // [item1, item2, item3]
85
console.log(keyValuePair.toString()); // config: {timeout: 30, retries: 3}
86
```
87
88
### Type Conversion Utilities
89
90
Functions for converting between YAML nodes and JavaScript values.
91
92
```typescript { .api }
93
/**
94
* Convert YAML node to JavaScript value
95
* @param value - YAML node or value to convert
96
* @param arg - Conversion options or context
97
* @returns JavaScript representation
98
*/
99
function toJS(value: unknown, arg?: string | ToJSOptions): any;
100
101
interface ToJSContext {
102
/** Document being processed */
103
doc: Document;
104
/** Current conversion path */
105
path: (string | number)[];
106
/** Anchors encountered during conversion */
107
anchors: Map<Node, unknown>;
108
/** Maximum alias count */
109
maxAliasCount: number;
110
/** Keep scalar wrapper objects */
111
keepScalar?: boolean;
112
/** Map representation preference */
113
mapAsMap?: boolean;
114
}
115
```
116
117
**Usage Examples:**
118
119
```typescript
120
import { toJS } from "yaml/util";
121
import { parseDocument } from "yaml";
122
123
const doc = parseDocument(`
124
config:
125
database: &db
126
host: localhost
127
port: 5432
128
ssl: true
129
130
development:
131
<<: *db
132
name: dev_database
133
134
production:
135
<<: *db
136
name: prod_database
137
host: prod.example.com
138
`);
139
140
// Basic conversion
141
const basicJS = toJS(doc.contents);
142
console.log(basicJS);
143
144
// Conversion with options
145
const jsWithMaps = toJS(doc.contents, {
146
mapAsMap: true, // Use Map objects instead of plain objects
147
keepScalar: false, // Don't keep Scalar wrapper objects
148
maxAliasCount: 100 // Prevent infinite recursion
149
});
150
151
// Custom reviver function
152
const jsWithReviver = toJS(doc.contents, {
153
reviver: (key, value) => {
154
// Transform date strings to Date objects
155
if (typeof value === 'string' && /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/.test(value)) {
156
return new Date(value);
157
}
158
return value;
159
}
160
});
161
162
// Convert individual nodes
163
const configNode = doc.get(['config'], true);
164
if (configNode) {
165
const configJS = toJS(configNode, { mapAsMap: false });
166
console.log('Config as plain object:', configJS);
167
}
168
```
169
170
### Collection Utilities
171
172
Helper functions for working with YAML collections (maps and sequences).
173
174
```typescript { .api }
175
/**
176
* Find pair in map items by key
177
* @param items - Map items to search
178
* @param key - Key to find
179
* @returns Matching pair or undefined
180
*/
181
function findPair(items: Iterable<unknown>, key: unknown): Pair | undefined;
182
```
183
184
**Usage Examples:**
185
186
```typescript
187
import { findPair } from "yaml/util";
188
import { parseDocument, isPair, isSeq, isMap } from "yaml";
189
190
const doc = parseDocument(`
191
users:
192
- name: Alice
193
email: alice@example.com
194
role: admin
195
- name: Bob
196
email: bob@example.com
197
role: user
198
- name: Charlie
199
email: charlie@example.com
200
role: user
201
`);
202
203
const usersNode = doc.get(['users'], true);
204
if (isSeq(usersNode)) {
205
usersNode.items.forEach((userNode, index) => {
206
if (isMap(userNode)) {
207
// Find specific fields
208
const namePair = findPair(userNode.items, 'name');
209
const emailPair = findPair(userNode.items, 'email');
210
const rolePair = findPair(userNode.items, 'role');
211
212
if (namePair && emailPair && rolePair) {
213
console.log(`User ${index + 1}:`);
214
console.log(` Name: ${namePair.value}`);
215
console.log(` Email: ${emailPair.value}`);
216
console.log(` Role: ${rolePair.value}`);
217
}
218
}
219
});
220
}
221
222
// Modify found pairs
223
const configDoc = parseDocument(`
224
database:
225
host: localhost
226
port: 5432
227
ssl: false
228
`);
229
230
const dbNode = configDoc.get(['database'], true);
231
if (isMap(dbNode)) {
232
const sslPair = findPair(dbNode.items, 'ssl');
233
if (sslPair && isPair(sslPair)) {
234
sslPair.value = new Scalar(true); // Enable SSL
235
console.log('Updated config:', configDoc.toString());
236
}
237
}
238
```
239
240
### Schema Tag Utilities
241
242
Access to built-in schema tags for custom schema creation and manipulation.
243
244
```typescript { .api }
245
/**
246
* Built-in map tag definition
247
*/
248
declare const mapTag: ScalarTag;
249
250
/**
251
* Built-in sequence tag definition
252
*/
253
declare const seqTag: ScalarTag;
254
255
/**
256
* Built-in string tag definition
257
*/
258
declare const stringTag: ScalarTag;
259
```
260
261
**Usage Examples:**
262
263
```typescript
264
import { mapTag, seqTag, stringTag } from "yaml/util";
265
import { Schema } from "yaml";
266
267
// Create custom schema with built-in tags
268
const customSchema = new Schema({
269
version: '1.2',
270
tags: [
271
mapTag,
272
seqTag,
273
stringTag,
274
// Add custom tags
275
{
276
tag: '!date',
277
resolve: (str) => new Date(str),
278
stringify: (date) => date.toISOString().split('T')[0],
279
test: /^\d{4}-\d{2}-\d{2}$/
280
}
281
]
282
});
283
284
// Use tags directly
285
console.log('Map tag:', mapTag.tag); // !!map
286
console.log('Seq tag:', seqTag.tag); // !!seq
287
console.log('String tag:', stringTag.tag); // !!str
288
289
// Custom tag behavior
290
const testValue = "2023-12-01";
291
const customTag = customSchema.tags.find(tag => tag.tag === '!date');
292
if (customTag && customTag.test && customTag.resolve) {
293
if (customTag.test.test(testValue)) {
294
const resolved = customTag.resolve(testValue);
295
console.log('Resolved date:', resolved); // Date object
296
}
297
}
298
```
299
300
### String Processing Utilities
301
302
Advanced string formatting and processing functions for YAML output.
303
304
```typescript { .api }
305
/**
306
* Fold long lines in flow scalars
307
* @param text - Text to fold
308
* @param options - Folding options
309
* @returns Folded text
310
*/
311
function foldFlowLines(text: string, options?: FoldOptions): string;
312
313
interface FoldOptions {
314
/** Maximum line width */
315
lineWidth?: number;
316
/** Minimum content width */
317
minContentWidth?: number;
318
/** Handle lines with indicators */
319
indicatorWidth?: number;
320
/** More aggressive folding */
321
onFold?: () => void;
322
}
323
324
/**
325
* Convert number to YAML string representation
326
* @param value - Number to stringify
327
* @returns YAML string representation
328
*/
329
function stringifyNumber(value: number): string;
330
331
/**
332
* Convert string to YAML string representation
333
* @param value - String to convert
334
* @param ctx - Stringify context
335
* @param onComment - Comment handler
336
* @param onChompKeep - Chomp keep handler
337
* @returns YAML string representation
338
*/
339
function stringifyString(
340
value: string,
341
ctx?: StringifyContext,
342
onComment?: () => void,
343
onChompKeep?: () => void
344
): string;
345
346
interface StringifyContext {
347
/** Document being stringified */
348
doc: Document;
349
/** Indentation string */
350
indent: string;
351
/** Current indentation level */
352
indentStep: string;
353
/** Stringify options */
354
options: ToStringOptions;
355
/** Type of container */
356
type?: Scalar.Type;
357
}
358
```
359
360
**Usage Examples:**
361
362
```typescript
363
import {
364
foldFlowLines,
365
stringifyNumber,
366
stringifyString
367
} from "yaml/util";
368
369
// Fold long text
370
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.";
371
372
const folded = foldFlowLines(longText, {
373
lineWidth: 60,
374
minContentWidth: 20
375
});
376
377
console.log('Folded text:');
378
console.log(folded);
379
// Output:
380
// This is a very long line of text that should be folded
381
// across multiple lines to improve readability and conform to
382
// line width constraints in YAML documents.
383
384
// Number stringification
385
console.log(stringifyNumber(42)); // "42"
386
console.log(stringifyNumber(3.14159)); // "3.14159"
387
console.log(stringifyNumber(Infinity)); // ".inf"
388
console.log(stringifyNumber(-Infinity)); // "-.inf"
389
console.log(stringifyNumber(NaN)); // ".nan"
390
391
// String stringification with context
392
const ctx = {
393
doc: new Document(),
394
indent: '',
395
indentStep: ' ',
396
options: {
397
lineWidth: 80,
398
singleQuote: false,
399
blockQuote: false
400
}
401
};
402
403
// Simple string
404
console.log(stringifyString('Hello World', ctx)); // Hello World
405
406
// String requiring quotes
407
console.log(stringifyString('Hello: World', ctx)); // "Hello: World"
408
409
// Multiline string
410
const multiline = 'Line 1\nLine 2\nLine 3';
411
console.log(stringifyString(multiline, {
412
...ctx,
413
options: { ...ctx.options, blockQuote: 'literal' }
414
}));
415
// Output:
416
// |
417
// Line 1
418
// Line 2
419
// Line 3
420
```
421
422
### Logging Utilities
423
424
Logging functions for debugging and development.
425
426
```typescript { .api }
427
type LogLevelId = 'silent' | 'error' | 'warn' | 'debug';
428
429
/**
430
* Debug logging function
431
* @param logLevel - Current log level
432
* @param messages - Messages to log
433
*/
434
function debug(logLevel: LogLevelId, ...messages: any[]): void;
435
436
/**
437
* Warning logging function
438
* @param logLevel - Current log level
439
* @param messages - Messages to log
440
*/
441
function warn(logLevel: LogLevelId, ...messages: any[]): void;
442
```
443
444
**Usage Examples:**
445
446
```typescript
447
import { debug, warn } from "yaml/util";
448
449
// Configure logging level
450
const logLevel = 'debug';
451
452
// Debug messages (only shown when logLevel is 'debug')
453
debug(logLevel, 'Processing YAML document');
454
debug(logLevel, 'Found', 5, 'top-level keys');
455
456
// Warning messages (shown when logLevel is 'warn' or 'debug')
457
warn(logLevel, 'Deprecated YAML 1.1 syntax detected');
458
warn(logLevel, 'Duplicate key found:', 'database.host');
459
460
// Silent mode (nothing logged)
461
const silentLevel = 'silent';
462
debug(silentLevel, 'This will not be logged');
463
warn(silentLevel, 'This will also not be logged');
464
465
// Conditional logging
466
function processConfig(config: any, logLevel: LogLevelId) {
467
debug(logLevel, 'Starting config processing');
468
469
if (!config.database) {
470
warn(logLevel, 'No database configuration found');
471
}
472
473
debug(logLevel, 'Config processing completed');
474
}
475
476
processConfig({ app: 'MyApp' }, 'debug');
477
```
478
479
### Advanced Utility Patterns
480
481
Combine utilities for complex YAML processing workflows.
482
483
```typescript
484
import {
485
createNode,
486
createPair,
487
toJS,
488
findPair,
489
stringifyString
490
} from "yaml/util";
491
import {
492
Schema,
493
Document,
494
isMap,
495
isPair,
496
YAMLMap,
497
Scalar
498
} from "yaml";
499
500
class YAMLConfigManager {
501
private schema: Schema;
502
503
constructor() {
504
this.schema = new Schema({
505
version: '1.2',
506
sortMapEntries: true
507
});
508
}
509
510
// Merge configurations with proper YAML structure
511
mergeConfigs(base: any, override: any): Document {
512
const doc = new Document();
513
514
// Create base structure
515
const baseNode = createNode(base, {
516
schema: this.schema,
517
sortMapEntries: true
518
});
519
520
// Apply overrides
521
const overrideNode = createNode(override, {
522
schema: this.schema,
523
sortMapEntries: true
524
});
525
526
// Merge logic
527
const merged = this.deepMergeNodes(baseNode, overrideNode);
528
doc.contents = merged;
529
530
return doc;
531
}
532
533
private deepMergeNodes(base: any, override: any): any {
534
if (isMap(base) && isMap(override)) {
535
const result = new YAMLMap(this.schema);
536
537
// Add all base pairs
538
base.items.forEach(pair => {
539
if (isPair(pair)) {
540
result.items.push(createPair(pair.key, pair.value, {
541
schema: this.schema
542
}));
543
}
544
});
545
546
// Override/merge with override pairs
547
override.items.forEach(overridePair => {
548
if (isPair(overridePair)) {
549
const existingPair = findPair(result.items, overridePair.key);
550
551
if (existingPair && isPair(existingPair)) {
552
// Merge nested objects
553
existingPair.value = this.deepMergeNodes(
554
existingPair.value,
555
overridePair.value
556
);
557
} else {
558
// Add new pair
559
result.items.push(createPair(
560
overridePair.key,
561
overridePair.value,
562
{ schema: this.schema }
563
));
564
}
565
}
566
});
567
568
return result;
569
}
570
571
// For non-objects, override takes precedence
572
return override;
573
}
574
575
// Extract configuration section
576
extractSection(doc: Document, path: string[]): any {
577
let current = doc.contents;
578
579
for (const key of path) {
580
if (isMap(current)) {
581
const pair = findPair(current.items, key);
582
if (pair && isPair(pair)) {
583
current = pair.value;
584
} else {
585
return null;
586
}
587
} else {
588
return null;
589
}
590
}
591
592
return toJS(current);
593
}
594
595
// Validate configuration structure
596
validateConfig(doc: Document, requiredKeys: string[]): string[] {
597
const errors: string[] = [];
598
599
if (!isMap(doc.contents)) {
600
errors.push('Root must be a mapping');
601
return errors;
602
}
603
604
for (const key of requiredKeys) {
605
const pair = findPair(doc.contents.items, key);
606
if (!pair) {
607
errors.push(`Missing required key: ${key}`);
608
}
609
}
610
611
return errors;
612
}
613
}
614
615
// Usage example
616
const manager = new YAMLConfigManager();
617
618
const baseConfig = {
619
app: {
620
name: 'MyApp',
621
version: '1.0.0',
622
debug: false
623
},
624
database: {
625
host: 'localhost',
626
port: 5432,
627
ssl: false
628
}
629
};
630
631
const prodOverride = {
632
app: {
633
debug: false // Ensure debug is off
634
},
635
database: {
636
host: 'prod.example.com',
637
ssl: true
638
}
639
};
640
641
// Merge configurations
642
const prodConfig = manager.mergeConfigs(baseConfig, prodOverride);
643
console.log('Production config:');
644
console.log(prodConfig.toString());
645
646
// Extract and validate
647
const dbConfig = manager.extractSection(prodConfig, ['database']);
648
console.log('Database config:', dbConfig);
649
650
const errors = manager.validateConfig(prodConfig, ['app', 'database']);
651
if (errors.length > 0) {
652
console.log('Validation errors:', errors);
653
} else {
654
console.log('Configuration is valid');
655
}
656
```