mrkeyoor.com_
Wed 23 Sept 00:35 UTC
npmUtilsupdated 22 Sept 2026

nlcst-to-string review

nlcst-to-string 4.0.0 extracts plain text from one nlcst node or an array of nodes. Its single named toString function returns a node's value when present; otherwise it recursively concatenates child text without adding separators. This makes it useful inside retext and other nlcst transforms, where whitespace and punctuation already exist as valued nodes. It does not parse language, reconstruct removed formatting, use source positions, or serialize arbitrary unist trees. Version 4 adds an exports map, raises the documented floor to Node 16, updates nlcst types, and removes the old separator option. Our Node 22 test found working import and require paths plus a 0.3 KB gzipped browser bundle.

Verdict

nlcst-to-string 4.0.0 installed in 1.3 seconds, used 1 MB across 3 packages, and bundled to 0.3 KB gzipped with 0 audit findings in our sandbox. Install it only when nlcst nodes already exist and exact value-first concatenation is the desired result.

We installed it

Lab card: what happened when we installed nlcst-to-stringScreenshot of nlcst-to-string documentation
Install✓ · 1.3s3 packages on disk · 1 MB
ImportESM import works · require() works · ESM package with exports map
Browser0.3 KBgzipped (0.4 KB minified), bundled with esbuild
TypesTypeScript types bundled
Known vulns00 critical · 0 high · 0 moderate · 0 low (npm audit)

Answers from our run

Does nlcst-to-string install cleanly?

Yes. In a fresh container with an empty cache, npm install nlcst-to-string finished in 1 seconds, leaving 3 packages and 1 MB on disk. npm audit reported no known vulnerabilities.

How much does nlcst-to-string add to a browser bundle?

0.3 KB gzipped (0.4 KB minified) when the whole package is bundled for the browser with esbuild. Importing only part of it is usually smaller.

Does nlcst-to-string work with both ESM and CommonJS?

Yes. Both import 'nlcst-to-string' and require('nlcst-to-string') worked in Node 22 in our run. The package is published as ESM with an exports map.

Does nlcst-to-string include TypeScript types?

Yes, type declarations ship inside the package, so no @types install is needed.

nlcst-to-string or mdast-util-to-string: which should you use?

mdast-util-to-string: Choose it for Markdown mdast nodes and their format-specific text fields. nlcst-to-string 4.0.0 installed in 1.3 seconds, used 1 MB across 3 packages, and bundled to 0.3 KB gzipped with 0 audit findings in our sandbox.

When should you not use nlcst-to-string?

You need a parser. nlcst-to-string accepts an existing tree and performs no tokenization or language analysis.

API stability5/5Version 4.0.0 exports one named function whose rule is small: return a node value, otherwise recurse through children or an input array and concatenate without a separator. The major release changed packaging, the Node floor, nlcst types, and removed the separator argument, so migration from 3.x needs a code check. Within 4.x, one function, one input shape, and no options leave little surface for accidental behavioral drift.
Docs4/5The README returned HTTP 200 and states that the package is ESM-only, names Node 16 as the 4.x compatibility floor, shows Node, Deno, and browser imports, identifies the sole named export, accepts Node or Node[], and explains value-before-children behavior. For a 40 KB unpacked package, that covers the public contract well. It does not spell out invalid-input behavior, empty arrays, source-position loss, or the exact consequence of a node carrying both value and children.
Maintenance3/5npm published 4.0.0 on July 17, 2023, and GitHub reports the last push on April 30, 2024. The repository is unarchived with 0 open issues and pull requests and 19 stars. The code can reasonably be finished rather than neglected, and it follows unified collective conventions. Still, the README's Node 16 floor is stale in 2026, and no later release has refreshed the compatibility statement or packaging.
Ecosystem4/5npm counted 5,467,439 downloads for the week ending August 24, 2026, despite only 19 GitHub stars, which is consistent with a small transitive utility in retext and unified dependency graphs. The package uses standard nlcst types, bundles declarations, has 0 peer dependencies, and produced a 0.3 KB gzipped browser bundle in our test. Its scope stops at nlcst text extraction and offers no value for mdast, hast, parsing, or language understanding.

Use it if

  • A retext rule or nlcst transform needs the exact text represented by a subtree.
  • The caller sometimes has one node and sometimes a contiguous array of sibling nodes.
  • Whitespace and punctuation already live in the tree and should be concatenated exactly as stored.
  • A small typed helper is preferable to repeating value-or-children recursion in each plugin.
Skip it if

Setup reality

We installed nlcst-to-string 4.0.0 in a fresh Node 22 Bookworm sandbox. npm completed in 1.3 seconds, left 3 packages, and used 1 MB. The package has 1 direct dependency, @types/nlcst, and 0 peer dependencies. It occupies 40 KB unpacked, bundles TypeScript declarations, and produced a 0.4 KB minified browser bundle, 0.3 KB gzipped. npm audit found 0 known vulnerabilities.

Version 4 declares ESM through type module and exposes one entry through an exports map. ESM import and require both worked in our Node 22 checks, though the README describes the package as ESM-only and documents a Node 16 floor. Use the named toString export; there is no default export. Older CommonJS runtimes and tools may not reproduce Node 22's require behavior, so import is the portable route described by the maintainer.

There is no credential, configuration file, native build, cache, or asynchronous path. The input tree is the configuration. A valued node returns that value immediately, even if children are present. A parent without value contributes its children in order and inserts nothing between them. An empty array returns an empty string. Invalid primitives, null, undefined, and objects without a node type are outside the documented Node or Node[] contract and can throw.

Version 4 removed the separator parameter, so callers that need spaces between selected nodes must include whitespace nodes or join separately. Source positions do not affect output. Once a transform deletes whitespace, normalizes punctuation, reorders children, or stores content in a format-specific field, toString cannot reconstruct the original source. The function is synchronous and tiny; performance concerns usually belong to the parser and tree walk that produced the subtree.

Patterns

Read one word subtree serialize-word

import { toString } from 'nlcst-to-string';

const word = {
  type: 'WordNode',
  children: [
    { type: 'TextNode', value: 'AT' },
    { type: 'SymbolNode', value: '&' },
    { type: 'TextNode', value: 'T' },
  ],
};

console.log(toString(word));

The 3 child values concatenate to AT&T; WordNode itself inserts no punctuation or separator.

Flatten adjacent sibling nodes serialize-siblings

const nodes = [
  { type: 'TextNode', value: 'hello' },
  { type: 'WhiteSpaceNode', value: ' ' },
  { type: 'TextNode', value: 'there' },
];

console.log(toString(nodes));

The output contains a space only because the 3-item array includes a WhiteSpaceNode with that value.

Keep stored punctuation and spacing serialize-sentence

const sentence = {
  type: 'SentenceNode',
  children: [
    { type: 'TextNode', value: 'Hello' },
    { type: 'PunctuationNode', value: ',' },
    { type: 'WhiteSpaceNode', value: ' ' },
    { type: 'TextNode', value: 'world' },
    { type: 'PunctuationNode', value: '!' },
  ],
};

toString(sentence);

The 5 child values produce Hello, world! exactly; the serializer does not infer either the comma or the space.

Extract text inside a retext rule inspect-rule-node

function checkWord(node, file) {
  const text = toString(node);
  if (text.toLowerCase() === 'utilize') {
    file.message('Prefer use', { ancestors: [node] });
  }
}

toString preserves the stored case and Unicode, so normalization belongs in the rule after extraction.

Detect a text-changing transform compare-transform

const before = toString(tree);
normalizeQuotes(tree);
const after = toString(tree);

if (before !== after) {
  console.log({ before, after });
}

Only value changes, child order, or child membership affect the result; editing position metadata alone does not.

See value win over children prefer-node-value

const node = {
  type: 'TextNode',
  value: 'kept',
  children: [{ type: 'TextNode', value: 'ignored' }],
};

console.log(toString(node));

A present value returns kept immediately, so the 1 child is never serialized.

Handle an empty node selection serialize-empty-array

const selected = [];
const text = toString(selected);
console.log(text === '');

An empty Node[] joins to an empty string; null and undefined are not documented node inputs.

Check unknown data before extraction guard-unknown-input

function textFromUnknown(value: unknown) {
  if (Array.isArray(value)) return toString(value);
  if (value && typeof value === 'object' && 'type' in value) {
    return toString(value);
  }
  return undefined;
}

The public contract accepts Node or Node[], so validate untrusted primitives and objects before calling the function.

Alternatives

PackageRegistryPick it when
mdast-util-to-stringnpmChoose it for Markdown mdast nodes and their format-specific text fields.
hast-util-to-textnpmChoose it for HTML hast when rendered-text rules such as whitespace and element behavior matter.
unist-util-visitnpmChoose it to inspect or collect selected nodes instead of flattening an entire subtree.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.