Skip to main content
Back to contributions
Pull Request
Merged
381

Replace kleur with native Node.js styleText

NodeSecure/cli

Replaced the kleur package with Node.js 20+ native styleText API using a Proxy-based chainable utility

The Problem

The NodeSecure CLI relied on kleur, an external package for terminal styling (colors, bold, underline, etc.). While kleur is lightweight and performant, Node.js 20+ introduced the native styleText function in the node:util module, making the external dependency unnecessary.

Reducing external dependencies is important for security-focused tools like NodeSecure because:

  • Fewer dependencies means a smaller attack surface
  • Native APIs are maintained by the Node.js core team
  • Eliminates supply chain risk from third-party packages
  • Reduces bundle size and installation time

The challenge was that kleur’s chainable API (kleur.green().bold('text')) was used throughout the codebase, so a simple find-and-replace wouldn’t work.

The Solution

I created a drop-in replacement utility that wraps Node.js’s native styleText function while preserving kleur’s chainable API. This allowed all existing code to work unchanged while eliminating the external dependency.

The Proxy Pattern

The key insight was using JavaScript’s Proxy to intercept property access and build up a chain of styles dynamically:

import { styleText } from "node:util";

function createStyleChain(styles = []) {
  return new Proxy(() => {}, {
    get(_, prop) {
      // Return a new chain with the added style
      return createStyleChain([...styles, prop]);
    },
    apply(_, __, [text]) {
      // Apply all accumulated styles to the text
      return styleText(styles, String(text));
    }
  });
}

export const style = createStyleChain();

This enables the same chainable syntax developers were already using:

// Before (kleur)
import kleur from "kleur";
console.log(kleur.green().bold("Success!"));

// After (native styleText wrapper)
import { style } from "./utils/styleText.js";
console.log(style.green.bold("Success!"));

Files Changed

FileChange TypeDescription
src/utils/styleText.jsAddedNew Proxy-based utility wrapper
test/utils/styleText.test.jsAdded12 unit tests for comprehensive coverage
bin/index.jsModifiedUpdated imports to use new utility
src/commands/cwd.jsModifiedReplaced kleur with styleText
src/commands/lang.jsModifiedReplaced kleur with styleText
src/commands/scorecard.jsModifiedReplaced kleur with styleText
src/commands/summary.jsModifiedReplaced kleur with styleText
src/commands/verify.jsModifiedReplaced kleur with styleText
src/commands/vulnerability.jsModifiedReplaced kleur with styleText
src/commands/scanner.jsModifiedReplaced kleur with styleText
package.jsonModifiedRemoved kleur dependency

Test Coverage

The new utility includes 12 unit tests covering:

  • Basic single-style application (style.green("text"))
  • Chained multi-style application (style.bold.underline.red("text"))
  • Numeric value handling (auto-conversion to string)
  • Destructuring support (const { green, bold } = style)
  • Edge cases and error handling

Timeline

DateEvent
2025-12-18Issue #637 opened requesting kleur removal
2025-12-18Implemented Proxy-based solution with chainable API
2025-12-18Added comprehensive unit tests (12 tests)
2025-12-18Submitted PR #639
2025-12-19PR approved by @fraxken and @clemgbld
2025-12-19PR merged into main branch

Impact

  • Dependencies reduced: Removed 1 external package
  • Code coverage: 57.93% overall, with modified lines at 100% coverage
  • API compatibility: Zero breaking changes for existing code
  • Node.js alignment: Leverages native APIs introduced in Node.js 20+