{"id":17057,"library":"resin-cli-visuals","title":"Resin CLI Visuals","description":"resin-cli-visuals is a JavaScript/TypeScript library providing a collection of command-line interface (CLI) widgets designed primarily for the Balena ecosystem's CLI tools. It offers components such as spinners, progress bars, interactive tables (horizontal and vertical), and a drive scanner to dynamically detect storage device changes. The current stable version is 4.0.4, released in March 2026, indicating an active development cadence with several minor and major releases in the last year. Key differentiators include its focus on CLI UX within the Balena context, offering ready-to-use visual components for common CLI interactions, and its recent migration to TypeScript and ESM, ensuring modern JavaScript practices. The library is built to enhance user interaction in console applications, making operations like long-running tasks or data display more visually intuitive.","status":"active","version":"4.0.4","language":"javascript","source_language":"en","source_url":"git://github.com/balena-io-modules/resin-cli-visuals","tags":["javascript","resin","cli","widgets","text","visuals","typescript"],"install":[{"cmd":"npm install resin-cli-visuals","lang":"bash","label":"npm"},{"cmd":"yarn add resin-cli-visuals","lang":"bash","label":"yarn"},{"cmd":"pnpm add resin-cli-visuals","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Used for colored terminal output, replaced 'chalk' in v3.0.1.","package":"ansis","optional":false}],"imports":[{"note":"Package transitioned to ES Modules (ESM) in v4.0.0. CommonJS `require` is no longer supported.","wrong":"const visuals = require('resin-cli-visuals');","symbol":"visuals","correct":"import { visuals } from 'resin-cli-visuals';"},{"note":"DriveScanner is a named export. All sources were converted to TypeScript and ESM in v4.0.0.","wrong":"import DriveScanner from 'resin-cli-visuals/build/DriveScanner';","symbol":"DriveScanner","correct":"import { DriveScanner } from 'resin-cli-visuals';"},{"note":"Spinner is a member of the `visuals` namespace object, not a top-level export.","wrong":"import { Spinner } from 'resin-cli-visuals';","symbol":"Spinner","correct":"import { visuals } from 'resin-cli-visuals';\nconst spinner = new visuals.Spinner('Loading...');"}],"quickstart":{"code":"import { visuals, DriveScanner } from 'resin-cli-visuals';\n\nasync function runCliDemo() {\n  // Simulate a long-running operation with a spinner\n  const spinner = new visuals.Spinner('Processing task...');\n  spinner.start();\n  await new Promise(resolve => setTimeout(resolve, 3000));\n  spinner.stop();\n  console.log('Task completed.');\n\n  // Display data in a horizontal table\n  const tableData = [\n    { label: 'Name', value: 'John Doe' },\n    { label: 'Email', value: 'john.doe@example.com' },\n    { label: 'Role', value: 'Developer' }\n  ];\n  const tableOrdering = ['label', 'value'];\n  console.log('\\nUser Details:');\n  console.log(visuals.table.horizontal(tableData, tableOrdering));\n\n  // Example of a DriveScanner (requires a driveFinder function)\n  // In a real scenario, driveFinder would be an async function\n  // that returns an array of drive objects.\n  const mockDriveFinder = async () => [\n    { device: '/dev/sda', size: '10GB', description: 'USB Drive' },\n    { device: '/dev/sdb', size: '500GB', description: 'Internal HDD' }\n  ];\n  const scanner = new DriveScanner(mockDriveFinder, { interval: 2000 });\n  scanner.on('change', (drives) => {\n    console.log('\\nDrive change detected:', drives);\n  });\n  console.log('\\nDrive scanner started. Waiting for changes (runs for 5s)...');\n  await new Promise(resolve => setTimeout(resolve, 5000));\n  scanner.stop();\n  console.log('Drive scanner stopped.');\n}\n\nrunCliDemo().catch(console.error);","lang":"typescript","description":"This quickstart demonstrates creating a spinner, displaying data in a horizontal table, and using the DriveScanner to detect drive changes."},"warnings":[{"fix":"Update all `require()` calls to `import` statements. Ensure your project is configured for ESM (e.g., `\"type\": \"module\"` in `package.json` or `.mjs` file extension) and transpiles TypeScript if necessary.","message":"The package migrated entirely to ES Modules (ESM) and TypeScript in v4.0.0. CommonJS `require()` statements will no longer work, and consumers must use `import` syntax.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"Ensure your Node.js environment meets or exceeds the minimum version specified (>=20.0 for v4.x). Upgrade Node.js if running an older version.","message":"Node.js version requirements increased significantly. v4.x requires Node.js >=20.0, v3.x required >=18.0, and v2.x also required >=18.0.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Replace `new visuals.SpinnerPromise(...)` with `visuals.createSpinnerPromise(...)`.","message":"The `SpinnerPromise` class was replaced with a `createSpinnerPromise` factory function in v4.0.0. The direct `new SpinnerPromise()` constructor is no longer available.","severity":"breaking","affected_versions":">=4.0.0"},{"fix":"If you directly used `chalk` via `resin-cli-visuals` exports (which is unlikely as it was an internal dependency) or had custom `chalk` setups interacting with `resin-cli-visuals`, you might need to adjust for `ansis`. Check `ansis` documentation for differences if issues arise.","message":"The `chalk` dependency was dropped and replaced with `ansis` in v3.0.1. While `ansis` aims for `chalk` compatibility, direct imports or specific configurations related to `chalk` might break.","severity":"breaking","affected_versions":">=3.0.1"},{"fix":"Ensure `lodash` is explicitly listed as a dependency in your project's `package.json` if your code directly depends on it, rather than relying on transitive inclusion.","message":"The internal `lodash` dependency was removed in v4.0.4. While this should primarily affect internal implementation, if any consumers relied on transitive `lodash` access via `resin-cli-visuals` (an anti-pattern), it would now be missing.","severity":"breaking","affected_versions":">=4.0.4"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Change `const visuals = require('resin-cli-visuals');` to `import { visuals } from 'resin-cli-visuals';`. Ensure your `package.json` has `\"type\": \"module\"` or use `.mjs` file extensions.","cause":"Attempting to use `require()` to import an ES Module package (resin-cli-visuals >= v4.0.0).","error":"ERR_REQUIRE_ESM"},{"fix":"Replace `new visuals.SpinnerPromise(options)` with `visuals.createSpinnerPromise(options)` as it was changed to a factory function.","cause":"Using the `SpinnerPromise` class constructor directly after v4.0.0.","error":"TypeError: visuals.SpinnerPromise is not a constructor"},{"fix":"Check your Node.js version (`node -v`) and ensure it meets the `>=20.0` requirement for v4.x of `resin-cli-visuals`. Upgrade Node.js if needed.","cause":"Incorrect Node.js version. The package specifies `engines.node` requirements.","error":"Error: Cannot find module 'resin-cli-visuals'"}],"ecosystem":"npm","meta_description":null}