@sylphx/synth-yaml
v0.2.3
Published
YAML parser using Synth's universal AST
Readme
@sylphx/synth-yaml
YAML parser using Synth's universal AST - converts YAML configuration files to queryable AST.
Features
- YAML 1.2 Compliant: Full YAML specification support
- Universal AST: Converts YAML to Synth's language-agnostic BaseNode
- Position Tracking: Range information for all nodes
- Plugin Support: Transform and analyze YAML using Synth plugins
- Real-World Ready: Handles GitHub Actions, Docker Compose, Kubernetes configs
- Fast: Built on the battle-tested
yamllibrary - TypeScript: Full type safety and IntelliSense
Installation
npm install @sylphx/synth-yamlUsage
Basic Parsing
import { parse } from '@sylphx/synth-yaml'
const yaml = `
name: my-app
version: 1.0.0
`
const tree = parse(yaml)
// Tree contains Synth AST nodes
console.log(tree.nodes.filter(n => n.type === 'Map'))
console.log(tree.nodes.filter(n => n.type === 'Pair'))
console.log(tree.nodes.filter(n => n.type === 'String'))Parse All YAML Types
import { parse } from '@sylphx/synth-yaml'
// Maps (objects)
parse('name: Alice\nage: 30')
// Sequences (arrays)
parse('- item1\n- item2\n- item3')
parse('[1, 2, 3]')
// Strings
parse('hello')
parse('"quoted string"')
parse('|\n multiline\n string')
// Numbers
parse('42')
parse('3.14')
parse('1.5e10')
// Booleans
parse('true')
parse('false')
// Null
parse('null')
parse('~')Real-World Configs
import { parse } from '@sylphx/synth-yaml'
// GitHub Actions
const workflow = `
name: CI
on:
push:
branches: [main]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- run: npm test
`
const tree = parse(workflow)
// Docker Compose
const compose = `
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
`
const tree2 = parse(compose)
// Kubernetes
const k8s = `
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
`
const tree3 = parse(k8s)Extract Data
import { parse } from '@sylphx/synth-yaml'
const yaml = `
database:
host: localhost
port: 5432
users:
- name: Alice
- name: Bob
`
const tree = parse(yaml)
// Find all string values
const strings = tree.nodes
.filter(n => n.type === 'String')
.map(n => n.data?.value)
console.log(strings) // ["database", "host", "localhost", "port", ...]
// Find all pairs (key-value)
const pairs = tree.nodes
.filter(n => n.type === 'Pair')
.map(n => n.data?.key)
console.log(pairs) // ["database", "host", "port", "users", "name", "name"]With Options
import { parse } from '@sylphx/synth-yaml'
const yaml = `
defaults: &defaults
timeout: 30
production:
<<: *defaults
timeout: 60
`
// Enable merge keys
const tree = parse(yaml, {
merge: true
})Using Parser Class
import { YAMLParser } from '@sylphx/synth-yaml'
const parser = new YAMLParser()
// Parse multiple documents
const tree1 = parser.parse('name: Alice')
const tree2 = parser.parse('items:\n - a\n - b')
// Get last parsed tree
const lastTree = parser.getTree()Async Parsing
import { parseAsync } from '@sylphx/synth-yaml'
const yaml = `
users:
- name: Alice
age: 30
`
const tree = await parseAsync(yaml)AST Node Types
The YAML parser generates these node types:
type YAMLNodeType =
| 'Map' // YAML mapping (object)
| 'Sequence' // YAML sequence (array)
| 'Pair' // Key-value pair in map
| 'String' // String scalar
| 'Number' // Number scalar
| 'Boolean' // Boolean scalar
| 'Null' // Null valueNode Structure
Map Node:
{
type: 'Map',
children: [/* Pair node IDs */],
data: {}
}Sequence Node:
{
type: 'Sequence',
children: [/* Value node IDs */],
data: {}
}Pair Node:
{
type: 'Pair',
children: [/* Value node ID */],
data: {
key: string // The key name
}
}Scalar Nodes:
// String
{
type: 'String',
children: [],
data: { value: string }
}
// Number
{
type: 'Number',
children: [],
data: { value: number }
}
// Boolean
{
type: 'Boolean',
children: [],
data: { value: boolean }
}
// Null
{
type: 'Null',
children: [],
data: { value: null }
}Options
interface YAMLParseOptions {
/** Build query index for AST (default: false) */
buildIndex?: boolean
/** Plugins to apply during parsing */
plugins?: Plugin[]
/** Merge keys (default: false) */
merge?: boolean
/** Schema to use (default: 'core') */
schema?: 'core' | 'failsafe' | 'json' | 'yaml-1.1'
}Examples
GitHub Actions Workflow
import { parse } from '@sylphx/synth-yaml'
const workflow = `
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Run tests
run: npm test
`
const tree = parse(workflow)
// Find the job name
const jobPairs = tree.nodes.filter(n =>
n.type === 'Pair' && n.data?.key === 'build'
)Docker Compose
import { parse } from '@sylphx/synth-yaml'
const compose = `
version: '3.8'
services:
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./html:/usr/share/nginx/html
db:
image: postgres:13
environment:
POSTGRES_PASSWORD: secret
`
const tree = parse(compose)
// Find all service names
const serviceMap = tree.nodes.find(n =>
n.type === 'Pair' && n.data?.key === 'services'
)
if (serviceMap) {
const servicesNode = tree.nodes[serviceMap.children[0]!]
const services = servicesNode?.children.map(id =>
tree.nodes[id]!.data?.key
)
console.log(services) // ["web", "db"]
}Kubernetes Config
import { parse } from '@sylphx/synth-yaml'
const k8s = `
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
`
const tree = parse(k8s)
// Find the pod kind
const kindPair = tree.nodes.find(n =>
n.type === 'Pair' && n.data?.key === 'kind'
)
if (kindPair) {
const valueNode = tree.nodes[kindPair.children[0]!]
console.log(valueNode?.data?.value) // "Pod"
}Config Validation
import { parse } from '@sylphx/synth-yaml'
function validateConfig(yaml: string): boolean {
try {
const tree = parse(yaml)
// Check for required keys
const pairs = tree.nodes.filter(n => n.type === 'Pair')
const keys = pairs.map(p => p.data?.key)
const required = ['database', 'port']
return required.every(key => keys.includes(key))
} catch (error) {
console.error('Invalid YAML:', error.message)
return false
}
}API Reference
Functions
parse(source, options?): Parse YAML string synchronouslyparseAsync(source, options?): Parse YAML string asynchronouslycreateParser(): Create a new YAMLParser instance
Classes
YAMLParser: Main parser classconstructor(): Create parser instanceparse(source, options?): Parse YAML stringparseAsync(source, options?): Parse YAML string asynchronouslygetTree(): Get last parsed treeuse(plugin): Register a plugin
Supported YAML Features
- ✅ Scalars (strings, numbers, booleans, null)
- ✅ Sequences (arrays, both flow and block style)
- ✅ Mappings (objects, both flow and block style)
- ✅ Nested structures
- ✅ Multiline strings (literal
|and folded>) - ✅ Quoted strings (single and double)
- ✅ Comments
- ✅ Anchors and aliases (
&anchor,*alias) - ✅ Merge keys (
<<: *anchor) - ✅ Explicit types (
!!int,!!str) - ✅ Special values (
~,null,true,false)
How It Works
- Parse: YAML string →
yamllibrary → Document AST - Convert: Document AST → Synth BaseNode universal format
- Use: Query, transform, and analyze using Synth tools
The parser works with the yaml library:
- Fast: Leverages battle-tested YAML parser
- Spec-compliant: Full YAML 1.2 support
- Universal: Converts to Synth's language-agnostic format
Why YAML in Universal AST?
YAML joins HTML, JavaScript, and JSON in proving Synth's universal AST works across:
- Markup (HTML)
- Code (JavaScript)
- Data (JSON, YAML)
- Config (YAML)
Same AST structure, same tools, different languages.
Use Cases
- Config Analysis: Parse and validate YAML configs
- CI/CD: Analyze GitHub Actions, GitLab CI workflows
- Infrastructure: Parse Docker Compose, Kubernetes manifests
- Code Generation: Generate code from YAML schemas
- Validation: Custom YAML validation rules
- Transformation: Transform YAML using Synth plugins
- Documentation: Extract documentation from YAML comments
Comparison with yaml library
yaml library:
- Parses YAML → JavaScript objects
- No AST access
- No position tracking
- No extensibility
@sylphx/synth-yaml:
- Parses YAML → Synth universal AST
- Full access to parse tree structure
- Position information for every node
- Plugin system for transformations
- Works with other Synth tools
License
MIT
