@stoplight/yaml
@stoplight/yaml is a YAML parser and source-location toolkit built for editors, linters, and API-document tooling. It can return ordinary JavaScript data together with an abstract syntax tree, line offsets, diagnostics, and attached comments, then translate between zero-based line and character positions and JSON paths. It also handles anchors, optional merge keys, large integers, key-order metadata, and YAML serialization. If all you need is config-file parsing, most of that machinery is unnecessary.
A specialized choice for tooling that must connect YAML text, JSON paths, ranges, and diagnostics. For ordinary configuration loading, use yaml or js-yaml and avoid the older CommonJS packaging and Stoplight-specific result model.
Use it if
- You are building a YAML-aware editor, linter, language service, or API specification tool that must map data paths back to source ranges
- You need parsing errors as structured diagnostics with severity and zero-based ranges rather than only thrown strings
- You need the parsed data and the full YAML AST from one operation
- You work inside the Stoplight ecosystem and want the same @stoplight/types locations, diagnostics, and path segments used by its other packages
- You only load trusted YAML configuration into JavaScript: yaml or js-yaml has a larger user-facing community and a more conventional parse/stringify API
- You need ESM-first packaging: version 4.3.0 exposes only a CommonJS index.js entry with no module or exports map
- Client bundle size matters: the parser brings four runtime dependencies, including an AST parser and ordered-object helper, even when you only call parse()
- You expect YAML merge keys to work by default: parseWithPointers defaults mergeKeys to false, so << remains an ordinary property unless explicitly enabled
- You need a visibly active standalone project: 4.3.0 was published in March 2024, the last repository push was in April 2025, and GitHub currently lists 20 open issues and pull requests together on a 14-star repository
Setup reality
Install @stoplight/yaml with npm or yarn. Version 4.3.0 declares Node >=10.8, ships declarations, has no peer dependencies, and depends on tslib, @stoplight/types, @stoplight/yaml-ast-parser, and @stoplight/ordered-object-literal. Its package entry is CommonJS, so strict ESM projects rely on Node or bundler interop. parse() is only a convenience wrapper around parseWithPointers().data; the generic type parameter is a TypeScript assertion, not runtime schema validation. For editor work, keep the full parse result because lineMap, ast, diagnostics, metadata, and comments drive the location helpers. Line and character inputs are zero-based. Defaults are easy to misread: parsing is JSON-compatible by default, mergeKeys, bigInt, comment attachment, and key-order preservation are off. Large YAML integers are converted from bigint to Number unless bigInt: true, which can lose precision. Merge aliases are not folded into objects unless mergeKeys: true. Comments are not collected unless attachComments: true. preserveKeyOrder uses a symbol-backed ordered-object wrapper; use trapAccess() when consumers need reflection to follow that stored order. Duplicate-key behavior has an awkward edge: with no options it becomes a diagnostic in JSON mode, but the normalization of an explicit options object changes ignoreDuplicateKeys defaults, and json: false throws for repeated YAML mapping keys. safeStringify returns string inputs unchanged rather than quoting them as YAML scalars. Treat untrusted input as data only and add your own size limits and runtime schema validation.
Patterns
Parse YAML into JavaScript dataparse-yaml-data
import { parse } from '@stoplight/yaml';
const config = parse<{ port: number; debug: boolean }>(`
port: 8080
debug: false
`);
console.log(config.port);The generic controls only the compile-time return type; validate unknown input with a runtime schema before trusting it.
Collect data, AST, and diagnosticsparse-with-diagnostics
import { parseWithPointers } from '@stoplight/yaml';
const result = parseWithPointers('server:
port: [8080');
for (const diagnostic of result.diagnostics) {
console.error(diagnostic.message, diagnostic.range.start);
}
console.log(result.data, result.ast, result.lineMap);Many syntax problems are returned as diagnostics, but duplicate keys in YAML mode can throw, so boundary code should still catch exceptions.
Find the JSON path under a cursormap-position-to-path
const source = 'server:
port: 8080
';
const result = parseWithPointers(source);
const path = getJsonPathForPosition(result, { line: 1, character: 8 });
console.log(path); // ['server', 'port']Both line and character are zero-based, matching editor protocols rather than human line numbers.
Find the source range for a data pathmap-path-to-location
const location = getLocationForJsonPath(result, ['server', 'port']);
if (location) {
console.log(location.range.start, location.range.end);
}Keep the parser result that belongs to the exact source text; locations are derived from its AST and line map.
Keep large YAML integers as bigintpreserve-big-integers
const result = parseWithPointers<{ id: bigint }>(
'id: 9007199254740993',
{ bigInt: true },
);
console.log(result.data?.id === 9007199254740993n);bigInt defaults to false, which converts parsed bigint values to Number and can lose precision beyond the safe integer range.
Apply YAML merge keysresolve-merge-keys
const source = `
defaults: &defaults
retries: 3
service:
<<: *defaults
name: api
`;
const result = parseWithPointers<{ service: { retries: number } }>(
source,
{ mergeKeys: true },
);
console.log(result.data?.service.retries);mergeKeys is false by default; without the option, << is retained as an ordinary mapping key.
Capture YAML comments by pointerattach-comments
const result = parseWithPointers(
'# service settings
name: api # public name
',
{ attachComments: true },
);
console.log(result.comments);Comments are stored separately by pointer with placements such as leading, trailing, before-eol, or between; they are not inserted into data.
Retain explicit mapping orderpreserve-key-order
import { parseWithPointers, trapAccess } from '@stoplight/yaml';
const result = parseWithPointers<Record<string, number>>(
'third: 3
first: 1
second: 2',
{ preserveKeyOrder: true },
);
if (result.data) {
const ordered = trapAccess(result.data);
console.log(Reflect.ownKeys(ordered));
}Order is kept in symbol-backed metadata. trapAccess() changes the ownKeys reflection result; it does not deep-wrap nested mappings.
Report JSON-incompatible YAML constructsenforce-json-compatible-yaml
const result = parseWithPointers('1: one
name: api', {
json: true,
ignoreDuplicateKeys: false,
});
console.log(result.diagnostics.map(item => item.message));JSON mode expects string scalar keys. Set ignoreDuplicateKeys explicitly because providing an options object otherwise changes its normalized default.
Serialize an object as YAMLstringify-object
import { safeStringify } from '@stoplight/yaml';
const output = safeStringify(
{ server: { port: 8080 }, features: ['search', 'billing'] },
{ indent: 2, noRefs: true },
);
console.log(output);Options are passed to the underlying AST parser's safeDump; aliases may be emitted unless noRefs is enabled.
Handle an already serialized stringstringify-string-input
const source = 'name: api
';
const output = safeStringify(source);
console.log(output === source); // truesafeStringify returns any string unchanged. It does not quote or escape a string as a YAML scalar.
Build a path directly from an AST nodebuild-node-path
import { buildJsonPath, Kind, parseWithPointers } from '@stoplight/yaml';
const result = parseWithPointers('items:
- name: first');
if (result.ast?.kind === Kind.MAP) {
const value = result.ast.mappings[0].value;
if (value) console.log(buildJsonPath(value));
}AST unions are discriminated by Kind; narrow the node before accessing mappings, items, key, or value fields.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| yaml | npm | Choose it for a modern YAML 1.2 parser with document editing, CST access, aliases, comments, and ESM-friendly packaging |
| js-yaml | npm | Choose it for widely used parse and dump functions when source-to-path mapping is not required |
| @stoplight/yaml-ast-parser | npm | Choose the lower-level dependency when you need direct AST control and can build diagnostics and path mapping yourself |