├── .travis.yml ├── LICENSE ├── README.md ├── package.json ├── spin.js └── test └── basic.js /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | before_script: npm install -g npm@latest 3 | node_js: 4 | - '0.8' 5 | - '0.10' 6 | - '0.12' 7 | - 'iojs' 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) Isaac Z. Schlueter 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # char-spinner 2 | 3 | Put a little spinner on process.stderr, as unobtrusively as possible. 4 | 5 | ## USAGE 6 | 7 | ```javascript 8 | var spinner = require("char-spinner") 9 | 10 | // All options are optional 11 | // even the options argument itself is optional 12 | var interval = spinner(options) 13 | 14 | // the return value is the interval. 15 | // to stop the spinner, you can just clearInterval on it 16 | clearInterval(interval) 17 | 18 | // then to restart it again, just call the function again 19 | interval = spinner(options) 20 | ``` 21 | 22 | ## OPTIONS 23 | 24 | Usually the defaults are what you want. Mostly they're just 25 | configurable for testing purposes. 26 | 27 | * `stream` Output stream. Default=`process.stderr` 28 | * `tty` Only show spinner if output stream has a truish `.isTTY`. Default=`true` 29 | * `string` String of chars to spin. Default=`'/-\\|'` 30 | * `interval` Number of ms between frames, bigger = slower. Default=`50` 31 | * `cleanup` Print `'\r \r'` to stream on process exit. Default=`true` 32 | * `unref` Unreference the spinner interval so that the process can 33 | exit normally. Default=`true` 34 | * `delay` Number of frames to "skip over" before printing the spinner. 35 | Useful if you want to avoid showing the spinner for very fast 36 | actions. Default=`2` 37 | 38 | Returns the generated interval, if one was created. 39 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "char-spinner", 3 | "version": "1.0.1", 4 | "description": "Put a little spinner on process.stderr, as unobtrusively as possible.", 5 | "main": "spin.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "dependencies": {}, 10 | "devDependencies": { 11 | "tap": "^1.2.0" 12 | }, 13 | "scripts": { 14 | "test": "tap test/*.js" 15 | }, 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/isaacs/char-spinner" 19 | }, 20 | "keywords": [ 21 | "char", 22 | "spinner" 23 | ], 24 | "author": "Isaac Z. Schlueter (http://blog.izs.me/)", 25 | "license": "ISC", 26 | "bugs": { 27 | "url": "https://github.com/isaacs/char-spinner/issues" 28 | }, 29 | "homepage": "https://github.com/isaacs/char-spinner" 30 | } 31 | -------------------------------------------------------------------------------- /spin.js: -------------------------------------------------------------------------------- 1 | module.exports = spinner 2 | 3 | function spinner(opt) { 4 | opt = opt || {} 5 | var str = opt.stream || process.stderr 6 | var tty = typeof opt.tty === 'boolean' ? opt.tty : true 7 | var string = opt.string || '/-\\|' 8 | var ms = typeof opt.interval === 'number' ? opt.interval : 50 9 | if (ms < 0) ms = 0 10 | if (tty && !str.isTTY) return false 11 | var CR = str.isTTY ? '\u001b[0G' : '\u000d'; 12 | var CLEAR = str.isTTY ? '\u001b[2K' : '\u000d \u000d'; 13 | 14 | var s = 0 15 | var sprite = string.split('') 16 | var wrote = false 17 | 18 | var delay = typeof opt.delay === 'number' ? opt.delay : 2 19 | 20 | var interval = setInterval(function() { 21 | if (--delay >= 0) return 22 | s = ++s % sprite.length 23 | var c = sprite[s] 24 | str.write(c + CR) 25 | wrote = true 26 | }, ms) 27 | 28 | var unref = typeof opt.unref === 'boolean' ? opt.unref : true 29 | if (unref && typeof interval.unref === 'function') { 30 | interval.unref() 31 | } 32 | 33 | var cleanup = typeof opt.cleanup === 'boolean' ? opt.cleanup : true 34 | if (cleanup) { 35 | process.on('exit', function() { 36 | if (wrote) { 37 | str.write(CLEAR); 38 | } 39 | }) 40 | } 41 | 42 | module.exports.clear = function () { 43 | str.write(CLEAR); 44 | }; 45 | 46 | return interval 47 | } 48 | 49 | module.exports.clear = function () {}; 50 | -------------------------------------------------------------------------------- /test/basic.js: -------------------------------------------------------------------------------- 1 | var test = require('tap').test 2 | var spinner = require('../spin.js') 3 | 4 | test('does nothing when not a tty', function(t) { 5 | var int = spinner({ 6 | stream: { write: function(c) { 7 | throw new Error('wrote something: ' + JSON.stringify(c)) 8 | }, isTTY: false }, 9 | }) 10 | t.notOk(int) 11 | t.end() 12 | }) 13 | 14 | test('write spinny stuff', function(t) { 15 | var output = '' 16 | var written = 0 17 | var expect = "b\u001b[0Gc\u001b[0Gd\u001b[0Ge\u001b[0Gf\u001b[0Gg\u001b[0Gh\u001b[0Gi\u001b[0Gj\u001b[0Gk\u001b[0Gl\u001b[0Gm\u001b[0Gn\u001b[0Go\u001b[0Gp\u001b[0Ga\u001b[0Gb\u001b[0Gc\u001b[0Gd\u001b[0Ge\u001b[0Gf\u001b[0Gg\u001b[0Gh\u001b[0Gi\u001b[0Gj\u001b[0Gk\u001b[0Gl\u001b[0Gm\u001b[0Gn\u001b[0Go\u001b[0Gp\u001b[0Ga\u001b[0Gb\u001b[0Gc\u001b[0Gd\u001b[0Ge\u001b[0Gf\u001b[0Gg\u001b[0Gh\u001b[0Gi\u001b[0Gj\u001b[0Gk\u001b[0Gl\u001b[0Gm\u001b[0Gn\u001b[0Go\u001b[0Gp\u001b[0Ga\u001b[0Gb\u001b[0Gc\u001b[0G" 18 | 19 | var int = spinner({ 20 | interval: 0, 21 | string: 'abcdefghijklmnop', 22 | stream: { 23 | write: function(c) { 24 | output += c 25 | if (++written == 50) { 26 | t.equal(output, expect) 27 | clearInterval(int) 28 | t.end() 29 | } 30 | }, 31 | isTTY: true 32 | }, 33 | cleanup: false 34 | }) 35 | }) 36 | --------------------------------------------------------------------------------