unist-util-stringify-position

Serializes a unist node, position, or point into a human-readable location string for error and warning messages.

Library
npm
v4.0.0
6stars
MIT License

Repository Health

Pre-computed score based on development activity, maintenance, community, maturity, and trend momentum.How we score it →
23/100Needs Attention
Development Activity0
Maintenance20
Community12
Maturity60
Momentum0

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
76/100Good
Architecture85
Code Quality90
Innovation40
Learning Curve90

unist-util-stringify-position is a small unist ecosystem utility that turns positional information — a node’s position, a standalone position object, or a single point — into a compact, human-readable string such as 2:5-2:6 (a range) or 5:2 (a single point). It is one of the foundational utilities used across the unified/remark/rehype/mdast toolchain wherever a tool needs to tell a user where in the original source document something happened.

The package exports a single function, stringifyPosition, which accepts any of the three positional shapes (or null/undefined) and always returns a string, falling back to '' for unrecognized input and to 1:1 style defaults for missing line/column indices rather than throwing. This defensive, never-throw design is what makes it safe to call from deep inside linter and compiler error-reporting paths without adding new failure modes to already-failing code.

What You Get

  • A single stringifyPosition function - takes a node, position, or point and returns a formatted location string
  • Range formatting - renders position-shaped input (or a node’s .position) as ls:cs-le:ce, e.g. 2:5-2:6
  • Point formatting - renders a single point-shaped input as l:c, e.g. 5:2
  • Graceful fallback behavior - returns an empty string for unrecognized input and defaults missing line/column values to 1, never throwing
  • Full TypeScript types - ships with complete type coverage (enforced at 100% via type-coverage) and JSDoc-based type annotations
  • Zero runtime dependencies beyond @types/unist - the single production dependency is a types-only package, keeping the runtime footprint minimal

Common Use Cases

  • Linter/compiler error messages - a remark/rehype/eslint-style plugin reports “unexpected token at 3:14” by calling stringifyPosition on the offending node
  • Custom lint rule authoring - plugin authors building unified/remark ecosystem lint rules use it to format the location prefix of warning messages
  • AST debugging tooling - developer tools that print syntax trees use it to annotate each node with a readable source location
  • Building higher-level unist utilities - other unist-util-* packages (and unified itself) call it internally whenever they need to render a position as text rather than work with the raw object

Under The Hood

Architecture The entire implementation lives in lib/index.js (re-exported from the package root via index.js) as three small, purely functional helpers: stringifyPosition (the public entry point), and two private helpers position() and point() that recursively reduce a node/position down to its two constituent points and then to their line:column pairs. There is no internal state, no classes, and no branching complexity beyond a small cascade of 'position' in value / 'start' in value / 'line' in value duck-type checks that let the same function transparently accept a full AST node, a bare position object, or a bare point object. Because the function is a pure transform with no I/O or side effects, the entire module could be inlined anywhere without changing behavior — its only “architecture” is the deliberate flatness that keeps it embeddable in any code path, including hot error-handling paths.

Tech Stack The package is ESM-only (Node.js 16+), written in plain JavaScript with JSDoc type annotations rather than a .ts source file, and compiled to declaration files only via tsc --build with emitDeclarationOnly. Its sole runtime dependency is @types/unist (a types-only package, so it adds no runtime code). Dev tooling is the standard syntax-tree/unified project template: xo (ESLint preset) plus prettier for formatting, remark-cli with remark-preset-wooorm for linting the README itself, c8 for coverage, and type-coverage to enforce 100% type coverage as a build gate.

Code Quality Testing uses Node’s built-in node:test and node:assert/strict (no external test framework) in a single test.js file that exhaustively covers every input shape: undefined, null, string, number, empty object, node without position, node with several kinds of malformed position, node with valid position, a position without points, a position with invalid points, and every point-only variant — effectively enumerating the function’s full branch space. c8 --100 enforces 100% coverage as part of npm test. There are no explicit error/exception paths in the source at all (the function is designed to never throw), so “error handling” quality here is really about the exhaustive defaulting logic being fully tested, which it is. Naming is short and consistent with the rest of the syntax-tree org’s utilities.

What Makes It Unique The package makes no novel technical claims — it is deliberately the smallest possible implementation of one very narrow formatting concern, and its value is in being the single shared, exhaustively-tested implementation that the entire unified/remark/rehype ecosystem relies on instead of every downstream plugin reinventing its own position-to-string logic (and inevitably handling the missing/partial-position edge cases differently).

Join founders buildingwith open source

Opinionated takes, migration guides, cost-saving tips, and insights from the open source ecosystem.

Subscribe on Substack
Join 750+ subscribers

Search