mrkeyoor.com_
Sat 08 Aug 21:01 UTC
npmUtilsupdated 08 Aug 2026

@stoplight/yaml-ast-parser

A TypeScript-friendly YAML parser that returns a concrete syntax-aware tree instead of only turning YAML into plain JavaScript values. Every node carries character offsets, kind information, parent links and collected parse errors; the document also retains comments. It began as a js-yaml fork and adds error recovery plus a dedicated node kind for RAML-style !include references, which makes it useful inside editors, linters and API-description tooling.

Verdict

Keep it for existing Stoplight or RAML tooling that depends on this exact AST, especially when recoverable errors and source ranges matter. For a new YAML editor or validator, yaml has a more current model and a healthier documentation story.

API stability3/5The exported load, loadAll, node interfaces and Kind enum have stayed recognizable across this fork, and the tiny release history suggests consumers rarely face churn. The counterweight is that the current version remains 0.0.50, declarations expose several any fields, and numeric enum values form part of the practical contract without a formal compatibility promise. Stability here comes more from limited change than from a declared mature API policy.
Docs2/5The README explains the six node kinds, the important fields on each node and the two headline features, which is enough to understand the purpose. It does not document most loader options, diagnostic handling, comment semantics, ESM interop, dumping, scalar helpers or traversal. The README explicitly sends users to source and unit tests for fuller behavior, so productive use requires reading implementation artifacts.
Maintenance2/5The npm package is not marked deprecated and version 0.0.50 arrived in March 2024, so it is not an abandoned registry stub. Still, the GitHub repository was last pushed in May 2024 and shows only a few public signals of active ownership. Its dev toolchain still names Travis CI, Mocha 3, Node 4-era type definitions and TypeScript 3.8, evidence that maintenance is narrow rather than continuous modernization.
Ecosystem3/5The package recorded 4,858,240 downloads in the measured week and is embedded in established API-description tooling, so compatibility matters far beyond its three GitHub stars. Its direct ecosystem is much smaller: the README documents a bespoke AST and RAML include support, there is no plugin catalog, and general YAML libraries use different document or CST models. High transitive use should not be mistaken for a broad extension community.

Use it if

  • You are building a YAML editor, linter or language tool and need exact source ranges for nodes and comments
  • You need a best-effort tree plus diagnostics after malformed YAML instead of a parser that stops at the first error
  • You process RAML or another format that uses !include and want include references represented in the tree
  • You maintain Stoplight-era tooling that already consumes its Kind enum and YAMLNode interfaces
Skip it if

Setup reality

Installation is just npm install @stoplight/yaml-ast-parser. Version 0.0.50 declares no runtime dependencies and ships JavaScript plus .d.ts files, so there is no native build, peer dependency or config file. The friction is in the old API shape. The package is CommonJS-first, its README uses require, and its declarations were emitted by TypeScript 3.8, so ESM projects may need a namespace import or a default-interop setting that matches their bundler. load() does not return ordinary objects. You must branch on the numeric Kind enum and cast to YAMLScalar, YAMLMapping, YamlMap or YAMLSequence before reading type-specific fields. Parse failures are normally accumulated in node.errors so callers must inspect diagnostics explicitly; a returned tree does not prove the input was valid. Ranges are zero-based character offsets into the original JavaScript string, not byte offsets or line-column pairs. Comments live on the document as separate ranges and are not attached semantically to a mapping. The package recognizes RAML !include but does not read the referenced file for you. Finally, safeLoad and safeLoadAll are inherited names from the js-yaml lineage; do not assume they sanitize application-level data or make untrusted YAML safe to execute in some broader sense.

Patterns

Parse YAML into an ASTparse-document

import * as YAML from '@stoplight/yaml-ast-parser';

const source = 'name: demo\nenabled: true\n';
const root = YAML.load(source);

if (root.kind !== YAML.Kind.MAP) {
  throw new Error('Expected a mapping document');
}
console.log(root.startPosition, root.endPosition, root.errors);

load returns YAMLNode, not a plain JavaScript object. Check root.errors even when a tree was returned because the parser recovers from many syntax errors.

Find a value in a YAML mapread-mapping

import { Kind, YAMLMapping, YAMLScalar, YamlMap } from '@stoplight/yaml-ast-parser';

const map = root as YamlMap;
const pair = map.mappings.find((item: YAMLMapping) => item.key.value === 'name');
if (pair?.value.kind === Kind.SCALAR) {
  console.log((pair.value as YAMLScalar).value);
}

A map contains YAMLMapping pairs; the key and value are nodes. The declarations leave some convenience fields as any, so explicit kind checks preserve type safety.

Walk every AST nodewalk-tree

import { Kind, YAMLNode, YamlMap, YAMLSequence } from '@stoplight/yaml-ast-parser';

function* walk(node: YAMLNode): Generator<YAMLNode> {
  yield node;
  if (node.kind === Kind.MAP) {
    for (const pair of (node as YamlMap).mappings) yield* walk(pair);
  } else if (node.kind === Kind.MAPPING) {
    yield* walk(node.key);
    yield* walk(node.value);
  } else if (node.kind === Kind.SEQ) {
    for (const item of (node as YAMLSequence).items) yield* walk(item);
  }
}

for (const node of walk(root)) console.log(Kind[node.kind]);

Nodes do not expose an accept or children method. The repository tests implement the same switch-on-Kind visitor pattern.

Recover the original source for a nodeslice-source-range

const pair = (root as YAML.YamlMap).mappings[0];
const originalPairText = source.slice(pair.startPosition, pair.endPosition);
const originalValueText = source.slice(
  pair.value.startPosition,
  pair.value.endPosition
);

Positions index the original JavaScript string. They are UTF-16 code-unit offsets, so do not treat them as UTF-8 byte positions when applying edits to a file buffer.

Report parser errors and warningscollect-diagnostics

const document = YAML.load(source, { filename: 'config.yaml' });

const diagnostics = document.errors.map((error) => ({
  message: error.reason,
  warning: error.isWarning,
  line: error.mark?.line ?? 0,
  column: error.mark?.column ?? 0,
  position: error.mark?.position ?? 0
}));

Line and column values in Mark are zero-based. Diagnostics are collected on the document rather than reliably thrown, which is central to the parser's editor-oriented recovery behavior.

Extract comments with their rangesread-comments

const document = YAML.safeLoad('name: demo # shown in UI\n');

for (const comment of document.comments ?? []) {
  console.log({
    text: comment.value.trim(),
    range: [comment.startPosition, comment.endPosition],
  });
}

Comments are stored as a flat document-level list. The parser does not decide which mapping or sequence item a comment belongs to.

Parse a multi-document YAML streamparse-multiple-documents

const documents: YAML.YAMLNode[] = [];

YAML.loadAll('name: first\n---\nname: second\n', (document) => {
  documents.push(document);
});

console.log(documents.length); // 2

loadAll reports documents through a callback and returns void. Inspect errors on every collected document separately.

Classify and convert a scalarinfer-scalar-type

const scalar = pair.value as YAML.YAMLScalar;
const type = YAML.determineScalarType(scalar);

let value: unknown = scalar.value;
if (type === YAML.ScalarType.bool) value = YAML.parseYamlBoolean(scalar.value);
if (type === YAML.ScalarType.int) value = YAML.parseYamlBigInteger(scalar.value);
if (type === YAML.ScalarType.float) value = YAML.parseYamlFloat(scalar.value);

YAMLScalar.value is declared as a string. Type inference and conversion are separate steps, and parseYamlBigInteger may return either number or bigint.

Choose how duplicate keys are treatedhandle-duplicate-keys

const strictDoc = YAML.load('name: first\nname: second\n');
console.log(strictDoc.errors.map((error) => error.reason));

const permissiveDoc = YAML.load('name: first\nname: second\n', {
  ignoreDuplicateKeys: true,
});

Ignoring duplicate-key diagnostics does not make duplicate keys unambiguous for downstream consumers. Prefer rejecting them in configuration and API-description files.

Find RAML include referencesfind-include-references

const document = YAML.load('schema: !include schema.json\n');
const includes = [...walk(document)].filter(
  (node) => node.kind === YAML.Kind.INCLUDE_REF
);

for (const include of includes) {
  console.log(source.slice(include.startPosition, include.endPosition));
}

INCLUDE_REF records the syntax only. The package does not resolve paths, read files, enforce a project root or prevent directory traversal for you.

Alternatives

PackageRegistryPick it when
yamlnpmChoose it for a maintained YAML 1.2 parser with Document and CST APIs plus practical round-trip editing
yaml-eslint-parsernpmChoose it when the target consumer is ESLint and you need an ESTree-like YAML AST with visitor keys
yaml-ast-parsernpmChoose the original MuleSoft package only when an existing dependency requires that exact package name