vfile-message

A tiny error-like message class for creating vfile lint warnings and errors even without a file instance.

Library
npm
v4.0.3
10stars
MIT License

Repository Health

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

Technical Analysis

AI-assessed by reading the actual repository — architecture, code quality, innovation, and documentation.How we score it →
64/100Good
Architecture78
Code Quality92
Innovation42
Learning Curve45

vfile-message provides the VFileMessage class used throughout the unified and vfile ecosystem to represent a single lint message — a warning, error, or informational note tied to a specific location in a file. It extends the native Error class, so any tool that already handles JavaScript errors can also consume vfile-message instances, while adding fields like place, ruleId, source, ancestors, and cause that linters and compilers need to report precise, attributable diagnostics.

Most consumers never construct these messages directly — file.message() from VFile itself creates them under the hood — but tools like remark, rehype, and other unified-based linters use vfile-message when they need to emit a warning without an existing file object. The package is ESM-only, has a single runtime dependency beyond @types/unist, and ships with 100% type coverage, making it a stable, minimal building block for anyone writing a custom lint rule or compiler plugin in the unified ecosystem.

What You Get

  • VFileMessage class - an Error subclass carrying line/column/place, ruleId, source, ancestors, and cause fields for precise diagnostics
  • Flexible constructor overloads - accepts a plain reason string, a Node, a Point/Position, an existing Error, or an Options object interchangeably
  • Pretty toString() output - automatically formats messages as line:column: reason via unist-util-stringify-position
  • Full TypeScript types - ships with complete type coverage (100% via type-coverage) and JSDoc-based type annotations, no separate @types package needed

Common Use Cases

  • Writing a custom remark/rehype lint plugin that needs to report a warning at a specific node position without going through file.message()
  • Building a standalone linter or compiler on the unified/vfile stack that needs a consistent, Error-compatible message format
  • Attaching structured metadata (ruleId, source, expected/actual values) to diagnostics so downstream reporters can group and filter them
  • Wrapping a caught JavaScript Error into a VFileMessage to preserve its stack trace while adding file-position context

Under The Hood

Architecture The package is deliberately flat: index.js re-exports a single class, VFileMessage, defined in lib/index.js. The constructor resolves seven overloaded argument shapes (a string reason, a Node/NodeLike, a Point, a Position, an Options object, or an Error/VFileMessage cause, each optionally paired with an origin string) through a single sequential if/else chain, then assigns every public field — ancestors, cause, column, line, place, ruleId, source, stack — directly onto the instance. Default values are additionally declared on VFileMessage.prototype so the object shape and console.log output stay consistent even when a field is never set. There are no internal modules, layers, or abstractions beyond this one class; the only external dependency, unist-util-stringify-position, is called once to derive the human-readable name (e.g. 1:8-2:3).

Tech Stack Written as plain ESM JavaScript with JSDoc-based typing (@overload, @typedef) rather than a .ts source file; tsc --build generates the published .d.ts declarations, and type-coverage --atLeast 100 enforces that every expression is fully typed. Its only runtime dependency is unist-util-stringify-position (plus the @types/unist type-only package); dev tooling includes xo (ESLint-based linting) with Prettier formatting, remark-cli + remark-preset-wooorm for linting the README itself, and c8 for coverage. Targets Node.js 16+, is published ESM-only, and is consumable from Deno/browsers via esm.sh.

Code Quality A single test.js file uses Node’s built-in node:test and node:assert/strict to exercise nine distinct constructor invocations (string reason, caught Error, Error with mutated/multiline message, AST Node, Position, Point, string origin, and combined source:ruleId origin), and the test-coverage script runs c8 --100, meaning the suite is required to hit full statement coverage before it passes. There are no separate integration or type-only test suites, but the combination of 100% runtime coverage, 100% type coverage, and xo-enforced lint rules gives this small surface area strong quality guarantees; CI runs via GitHub Actions (referenced by the build badge).

What Makes It Unique The package is not attempting anything novel — it is a narrowly-scoped Error subclass — but its constructor design is notable for supporting a wide matrix of legacy and current calling conventions (bare reason, Node-as-position-source, Error-as-cause, string-as-origin) without breaking backward compatibility across major versions of the wider vfile/unified ecosystem, while still keeping the implementation to a single file.

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