@amonratcha/minimal-spinner
v0.0.1
Published
A small, zero-dependency CLI spinner for Node.js
Maintainers
Readme
@ash-alt-max/spinner
A tiny, zero-dependency spinner library for Node.js and browsers. This package provides:
- A CLI (Node) spinner (Node/TTY).
- A browser/DOM spinner (for web apps).
- A small API for start/stop/update/succeed/fail that works in both environments.
Note: For Angular and other frontend frameworks, use the browser bundle (ESM) or import the browser entry path.
Install
# npm
npm install @ash-alt-max/spinner
# or yarn
yarn add @ash-alt-max/spinnerQuick usage — Browser (vanilla JS)
This library ships a browser-friendly ES module (e.g. dist/browser.js). If your package.json exports / browser field points to it, you can simply:
<script type="module">
import Spinner from '/node_modules/@ash-alt-max/spinner/dist/browser.js';
const area = document.getElementById('demo-area');
const s = new Spinner({ container: area, text: 'Loading...', interval: 100 });
s.start();
setTimeout(() => s.succeed('Done!'), 2000);
</script>If you bundle your app (Vite/Rollup/webpack), you can import:
import Spinner from '@ash-alt-max/spinner/dist/browser.js';
// or, if package exports are configured:
// import Spinner from '@ash-alt-max/spinner';Quick usage — Node (CLI)
The Node spinner is designed for TTY (terminal). Example:
// CommonJS
const Spinner = require('@ash-alt-max/spinner').default;
const s = new Spinner({ text: 'Processing...', interval: 80 });
s.start();
setTimeout(() => s.succeed('Completed'), 2000);If you use TypeScript / ESM:
import Spinner from '@ash-alt-max/spinner';
const s = new Spinner({ text: 'Processing...' });
s.start();Using in Angular
- Install the package (see Install section).
- Use the browser build inside a component (ensure client-side only; guard for SSR).
Example component (TypeScript):
// src/app/spinner-demo/spinner-demo.component.ts
import { Component, ElementRef, OnDestroy, OnInit, ViewChild } from '@angular/core';
// Import browser bundle path; change if your package exports browser entry
import Spinner from '@ash-alt-max/spinner/dist/browser.js';
@Component({
selector: 'app-spinner-demo',
templateUrl: './spinner-demo.component.html',
})
export class SpinnerDemoComponent implements OnInit, OnDestroy {
@ViewChild('spinnerHost', { static: true }) host!: ElementRef<HTMLDivElement>;
private spinner!: any;
ngOnInit(): void {
this.spinner = new Spinner({
container: this.host.nativeElement,
text: 'Loading...',
interval: 100
});
}
start(): void { this.spinner.start(); }
stop(): void { this.spinner.stop(); }
succeed(): void { this.spinner.succeed('Completed'); }
fail(): void { this.spinner.fail('Failed'); }
updateText(t: string): void { this.spinner.update(t); }
ngOnDestroy(): void {
this.spinner?.stop(true);
}
}Template:
<!-- src/app/spinner-demo/spinner-demo.component.html -->
<div #spinnerHost></div>
<button (click)="start()">Start</button>
<button (click)="stop()">Stop</button>
<button (click)="succeed()">Succeed</button>
<button (click)="fail()">Fail</button>Important: If your Angular app uses Server-Side Rendering (Angular Universal), ensure you only construct/start the spinner on the client. Use isPlatformBrowser or ngIf to prevent server execution.
API
Constructor options:
- frames?: string[] — custom frames
- interval?: number — ms between frames (default: 80)
- text?: string — initial text
- container?: HTMLElement — browser DOM container (browser build)
- stream?: NodeJS.WriteStream — stdout or custom stream (Node build)
- hideCursor?: boolean — (Node) hide cursor while spinning
Instance methods:
- start(text?: string): this
- stop(clear = true): this
- succeed(text?: string): this
- fail(text?: string): this
- update(text: string): this
- isSpinning(): boolean
Build (author)
If you maintain this package locally and want to produce a browser bundle:
Using esbuild (example):
# install dev deps
npm install -D esbuild
# build browser bundle (ESM)
npx esbuild src/browser.ts --bundle --format=esm --target=es2017 --outfile=dist/browser.jsSuggested package.json snippets:
{
"main": "dist/index.cjs.js",
"module": "dist/index.esm.js",
"browser": "dist/browser.js",
"types": "dist/index.d.ts",
"exports": {
".": {
"import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js",
"default": "./dist/browser.js"
}
},
"scripts": {
"build": "tsc -p tsconfig.json && npm run build:browser",
"build:browser": "esbuild src/browser.ts --bundle --format=esm --target=es2017 --outfile=dist/browser.js",
"prepare": "npm run build"
}
}When browser / exports are set, bundlers and frameworks will prefer the browser entry for frontend builds.
Examples
- examples/index.html — demo for the browser spinner
- examples/run.js — demo for Node (uses compiled
dist/index.js)
Run the browser demo locally:
# start a simple static server in the project root
npx serve examples
# or
python -m http.server --directory examples 8000
# then open http://localhost:5000/examples/index.html (port depends on tool)Testing
We use Vitest for unit tests (running against source TypeScript):
npm install
npm test # runs vitest --run
npm run test:watchNotes, SSR and compatibility
- The Node spinner requires a TTY (stdout.isTTY). When used in non-TTY environments, it falls back to printing a single-line message.
- The browser build manipulates the DOM and must only be used in client-side contexts.
- Provide type declarations (
.d.ts) so TypeScript consumers (including Angular) get typing support.
Contributing
PRs welcome — please add tests and keep the bundle small. See LICENSE for licensing.
License
MIT
