Skip to content

Lightweight and efficient Node.js spinner package using native features

License

Notifications You must be signed in to change notification settings

ManuelGil/nspin

Repository files navigation

nspin

NPM Version NPM Downloads GitHub Repo Stars GitHub License

Overview

nspin is a lightweight and efficient Node.js spinner package built entirely with native features for optimal performance. Designed with modern Node.js (v22+) in mind, nspin provides a simple, intuitive, and dependency-free API for creating and managing spinner animations in your applications.

nspin

Index

Why Choose nspin?

  • Modern & Native: Built using the latest Node.js APIs (e.g., styleText from node:util, performance.now(), and advanced console manipulation), nspin ensures high performance and reliability.

  • Requires Node.js 22+: Leveraging Node.js v22+ features guarantees full compatibility with the most modern versions of Node.js, reducing external dependencies and the overall footprint.

  • Zero Dependencies: Unlike many spinner libraries that rely on external packages (like chalk or ora), nspin is completely dependency-free, ensuring a lightweight installation and enhanced security.

  • Clean & Modular API: Following SOLID principles, nspin offers a chainable API that simplifies spinner management and supports multiple concurrent spinners.

  • Adaptive Output: Automatically adjusts for both TTY and non-TTY environments, ensuring clear output regardless of the terminal's capabilities.

  • Dynamic Update of Frames: Change the spinner animation frames on the fly using the updateFrames(newFrames: string[]) method.

  • Position Configuration: Configure the spinner to appear either on the left (default) or right of the text via the position option.

  • Enhanced Control & Extensibility: With new methods like pause(), resume(), and restart(), along with state inspection methods (getCurrentFrame(), getElapsedTime(), and getTimerId()), you can control the animation in real time, facilitate smooth transitions, and build robust extensions (like an ExtendedSpinner) without depender de soluciones internas o hacks.

Requirements

nspin requires Node.js version 22 or higher. This requirement ensures access to modern features such as:

  • styleText from node:util for elegant text styling without manual ANSI escape codes.
  • performance.now() for high-resolution timing.
  • Native console methods for precise cursor control.

Features

  • Lightweight & Efficient: Uses native Node.js features to achieve high performance with minimal overhead.

  • Native Styling: Leverages the modern styleText API for styling spinner frames, eliminating manual ANSI codes.

  • Chainable API: Methods like start, updateText, updateFrames, pause, resume, restart, setInterval, and stop return the spinner instance for fluent usage.

  • Multiple Spinner Support: Easily manage several concurrent spinners without interference.

  • Modular & Extensible: Designed following SOLID principles for clean, maintainable, and extendable code. The internal state is exposed via dedicated getters to support extensions like ExtendedSpinner.

  • Dynamic Update of Frames: Change the spinner animation frames on the fly using updateFrames(newFrames: string[]).

  • Position Configuration: Choose whether the spinner appears to the left or right of the message via the position option.

  • Enhanced Control: Use pause() to stop the spinner temporarily and resume() to continue the animation. Adjust the update interval dynamically with setInterval(newInterval: number) and inspect it with getInterval().

  • State Inspection: Access the internal state through:

    • getCurrentFrame(): Returns the current frame index.
    • getElapsedTime(): Returns the elapsed time in milliseconds.
    • getTimerId(): Returns the current timer identifier.
  • Restart Capability: The restart() method allows you to stop and restart the spinner without perder la configuración actual.

Installation

Install nspin via npm:

npm install nspin

Usage Examples

Below are several examples demonstrating the functionality of nspin.

Example 1: Simple Spinner Initialization

A basic example that shows how to initialize and run a spinner.

import { Spinner } from "nspin";

// Simple Rotation spinner frames
const simpleRotation = ["|", "/", "-", "\\"];

const spinner = new Spinner({
  frames: simpleRotation,
  interval: 100
}).start("Loading...");

setTimeout(() => {
  spinner.stop("Done!");
}, 3000);

Example 2: Spinner with Dynamic Message Updates

This example demonstrates how to update the spinner's message in real time.

import { Spinner } from "nspin";

// Blinking Dot spinner frames
const blinkingDot = ["   ", ".  ", ".. ", "...", " ..", "  ."];

const spinner = new Spinner({
  frames: blinkingDot,
  interval: 120,
  format: "green"
}).start("Initializing...");

let progress = 0;
const interval = setInterval(() => {
  progress += 10;
  spinner.updateText(`Progress: ${progress}%`);

  if (progress >= 100) {
    clearInterval(interval);
    spinner.stop("Task complete!");
  }
}, 500);

Example 3: Concurrent Spinners

This example shows how to run multiple spinners at the same time to simulate parallel tasks.

import { Spinner } from "nspin";

// Moon Phases Spinner
const moonPhasesSpinner = ["◐", "◓", "◑", "◒"];

const spinner1 = new Spinner({
  frames: moonPhasesSpinner,
  interval: 100,
  format: "blue"
}).start("Task 1: Downloading...");

// Parentheses Rotation spinner frames
const parenthesesRotation = ["(-)", "(\\)", "(|)", "(/)"];

const spinner2 = new Spinner({
  frames: parenthesesRotation,
  interval: 120,
  format: "magenta"
}).start("Task 2: Processing...");

setTimeout(() => {
  spinner1.stop("Task 1 complete!");
}, 4000);

setTimeout(() => {
  spinner2.stop("Task 2 complete!");
}, 5000);

Example 4: Dynamic Update of Spinner Frames

In this example, we demonstrate how to change the spinner frames dynamically during runtime.

import { Spinner } from "nspin";

// Bouncing Ball spinner frames
const bouncingBall = [
  "(o    )",
  "( o   )",
  "(  o  )",
  "(   o )",
  "(    o)",
  "(   o )",
  "(  o  )",
  "( o   )"
];

// Rotating Dot Spinner frames
const rotatingDotSpinner = [".", "o", "O", "o"];

const spinner = new Spinner({
  frames: bouncingBall,
  interval: 100
}).start("Task in progress...");

// Update spinner frames after 5 seconds
setTimeout(() => {
  spinner.updateFrames(rotatingDotSpinner);
  spinner.updateText("Now using a different spinner...");
}, 5000);

// Stop spinner after 10 seconds
setTimeout(() => spinner.stop("Done!"), 10000);

Example 5: Configuring Spinner Position

This example shows how to configure the spinner's position relative to the text.

import { Spinner } from "nspin";

// Progress Bar spinner frames
const progressBar = [
  "[    ]",
  "[=   ]",
  "[==  ]",
  "[=== ]",
  "[====]",
  "[ ===]",
  "[  ==]",
  "[   =]"
];

// Spinner with left alignment (default)
const spinnerLeft = new Spinner({
  frames: progressBar,
  interval: 100,
  position: 'left'
}).start("Left aligned spinner");

// Spinner with right alignment
const spinnerRight = new Spinner({
  frames: progressBar,
  interval: 100,
  position: 'right'
}).start("Right aligned spinner");

// Stop both spinners after 8 seconds
setTimeout(() => {
  spinnerLeft.stop("Left spinner done");
  spinnerRight.stop("Right spinner done");
}, 8000);

Example 6: Using Pause/Resume, Interval Updates, and State Inspection

This example shows how to use the new pause(), resume(), setInterval(), and state inspection methods to control the spinner animation dynamically.

import { Spinner } from "nspin";

const spinner = new Spinner({
  frames: ["|", "/", "-", "\\"],
  interval: 100
}).start("Processing...");

// Pause the spinner after 2 seconds
setTimeout(() => {
  spinner.pause();
  console.log("Spinner paused.");
  console.log(`Current Frame: ${spinner.getCurrentFrame()}`);
}, 2000);

// Resume the spinner after 4 seconds
setTimeout(() => {
  spinner.resume();
  console.log("Spinner resumed.");
}, 4000);

// Update the spinner interval after 6 seconds
setTimeout(() => {
  spinner.setInterval(50);
  console.log(`New interval: ${spinner.getInterval()}ms`);
}, 6000);

// Restart the spinner after 7 seconds (without losing configuration)
setTimeout(() => {
  spinner.restart();
  console.log("Spinner restarted.");
}, 7000);

// Stop the spinner after 8 seconds
setTimeout(() => spinner.stop("Process complete!"), 8000);

Example 7: Degraded Output in Non-TTY Environments

This example demonstrates how nspin degrades gracefully when running in a non-TTY environment.

import { Spinner } from "nspin";

// Crosshair Spinner
const crosshairSpinner = [
  "[+]",
  "[x]",
  "[-]",
  "[x]"
];

// Simulate non-TTY mode for demonstration (in practice, this is determined by process.stdout.isTTY)
if (!process.stdout.isTTY) {
  console.log("Non-TTY mode active.");
}

const spinner = new Spinner({
  frames: crosshairSpinner,
  interval: 100,
  format: "yellow"
}).start("Running in non-TTY mode...");

setTimeout(() => {
  spinner.stop("Finished in non-TTY mode.");
}, 3000);

API Reference

Spinner Class

  • new Spinner(options: SpinnerOptions): Spinner Creates a new spinner instance.

    Options:

    • frames: An array of spinner frames (e.g., ["-", "\\", "|", "/"]).
    • interval (optional): Time between frames in milliseconds (default is 80).
    • format (optional): Format options for styling the spinner (passed to styleText).
    • position (optional): Position of the spinner relative to the text. Accepted values are 'left' (default) or 'right'.
  • start(text?: string): this Starts the spinner with an optional initial message.

  • updateText(newText: string): this Updates the spinner's message in real time.

  • updateFrames(newFrames: string[]): this Dynamically updates the spinner frames and resets the frame counter.

  • pause(): this Temporarily pauses the spinner animation without resetting its state.

  • resume(): this Resumes the spinner animation if it was paused.

  • restart(): this Stops and restarts the spinner using the current configuration, preserving its state.

  • getCurrentFrame(): number Returns the current frame index of the spinner.

  • getInterval(): number Returns the current interval (in milliseconds) of the spinner.

  • setInterval(newInterval: number): this Sets a new interval for the spinner and resets the timer if active.

  • getElapsedTime(): number Returns the elapsed time in milliseconds since the spinner started.

  • getTimerId(): ReturnType<typeof setInterval> | null Returns the current timer identifier or null if no timer is active.

  • stop(finalText?: string): this Stops the spinner and displays the final message.

For detailed type definitions, please refer to SpinnerOptions and FormatOptions.

Build & Publication

nspin is built using standard Node.js tools and optimized for performance. Key commands include:

  • Build:

    npm run build
  • Prepublish: The prepublishOnly script ensures a production build is generated and compressed before publishing:

    npm run prepublishOnly

Development

For development, use your preferred Node.js environment along with the provided build and test scripts. Recommended commands include:

  • Build: npm run build
  • Test: npm run test
  • Run: npm start

Support

If you encounter any issues or have suggestions for improvements, please open an issue on GitHub.

Feedback

If you enjoy using nspin, please consider leaving a review on GitHub or sharing your feedback.

Contributing

Contributions are welcome! To contribute:

  1. Fork the repository.
  2. Create your feature branch (git checkout -b feature/my-new-feature).
  3. Commit your changes (git commit -am 'Add some feature').
  4. Push to the branch (git push origin feature/my-new-feature).
  5. Open a pull request.

Please refer to our Contributing Guidelines for more details.

Code of Conduct

We strive to create a welcoming, inclusive, and respectful community. Please review our Code of Conduct before contributing.

Changelog

For a complete list of changes, see the CHANGELOG.md.

License

This package is licensed under the MIT License.