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
| File | Change Type | Description |
|---|---|---|
src/utils/styleText.js | Added | New Proxy-based utility wrapper |
test/utils/styleText.test.js | Added | 12 unit tests for comprehensive coverage |
bin/index.js | Modified | Updated imports to use new utility |
src/commands/cwd.js | Modified | Replaced kleur with styleText |
src/commands/lang.js | Modified | Replaced kleur with styleText |
src/commands/scorecard.js | Modified | Replaced kleur with styleText |
src/commands/summary.js | Modified | Replaced kleur with styleText |
src/commands/verify.js | Modified | Replaced kleur with styleText |
src/commands/vulnerability.js | Modified | Replaced kleur with styleText |
src/commands/scanner.js | Modified | Replaced kleur with styleText |
package.json | Modified | Removed 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
| Date | Event |
|---|---|
| 2025-12-18 | Issue #637 opened requesting kleur removal |
| 2025-12-18 | Implemented Proxy-based solution with chainable API |
| 2025-12-18 | Added comprehensive unit tests (12 tests) |
| 2025-12-18 | Submitted PR #639 |
| 2025-12-19 | PR approved by @fraxken and @clemgbld |
| 2025-12-19 | PR 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+