HAST Utility for Class Names
hast-util-classnames is a utility package within the unified collective, designed to simplify the management and merging of CSS class names on HAST (HTML Abstract Syntax Tree) elements. It provides a programmatic API to concatenate, conditionally include, or remove classes, analogous to popular string-based classname utilities but operating directly on HAST nodes. The package is currently at version 3.0.0, which requires Node.js 16 or higher and is distributed exclusively as an ES module (ESM). New major versions typically coincide with dropping support for unmaintained Node.js versions or significant API changes, ensuring compatibility with current Node.js releases. Its key differentiator is its deep integration with the HAST ecosystem, allowing direct manipulation of `properties.className` arrays on HAST element nodes, which is more robust than string manipulation when working with syntax trees.
Common errors
-
ERR_REQUIRE_ESM
cause Attempting to import `hast-util-classnames` using CommonJS `require()` syntax.fixSwitch to ES module import syntax: `import { classnames } from 'hast-util-classnames';` -
TypeError: classnames is not a function
cause This error typically occurs if the package is incorrectly imported (e.g., attempting a default import `import classnames from '...'`) or if an outdated Node.js version (pre-16) is used with v3.0.0+.fixEnsure you are using a named import `import { classnames } from 'hast-util-classnames';` and that your Node.js version is 16 or higher for v3.0.0+.
Warnings
- breaking Version 3.0.0 and above now require Node.js 16 or higher. Older Node.js environments are no longer supported.
- breaking The package is now exclusively an ES module (ESM) since version 2.0.0. CommonJS `require()` statements will fail.
- breaking The `exports` field in `package.json` was changed in v3.0.0. Directly importing non-public APIs or internal paths is no longer supported and will likely break.
- gotcha The `classnames` function has two distinct behaviors based on its first argument. If the first argument is a HAST `Node` (specifically an `Element`), it modifies that node's `properties.className` in place and returns the node. If the first argument is not a node (i.e., it starts with `Conditional` arguments), it returns a new `Array<string>` of merged classes.
Install
-
npm install hast-util-classnames -
yarn add hast-util-classnames -
pnpm add hast-util-classnames
Imports
- classnames
const classnames = require('hast-util-classnames');import { classnames } from 'hast-util-classnames'; - Conditional
import type { Conditional } from 'hast-util-classnames';
Quickstart
import {h} from 'hastscript';
import {classnames} from 'hast-util-classnames';
// Example 1: Merging class names as an array of strings
const mergedClasses = classnames('alpha bravo', {bravo: false, charlie: true}, [123, 'delta']);
console.log('Merged Classes (array):', mergedClasses);
// Expected output: ['alpha', '123', 'charlie', 'delta']
// Example 2: Modifying classes on a HAST node in place
const node = h('p.initial-class', 'Hello, HAST!');
console.log('Original Node:', JSON.stringify(node, null, 2));
const modifiedNode = classnames(node, 'new-class', {conditional: true, 'old-class': false}, ['another-one']);
console.log('Modified Node:', JSON.stringify(modifiedNode, null, 2));
/*
Expected output (simplified):
{
"type": "element",
"tagName": "p",
"properties": {"className": ["initial-class", "new-class", "conditional", "another-one"]},
"children": [{"type": "text", "value": "Hello, HAST!"}]
}
*/