├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── example ├── example.js ├── example.sh └── example.svg ├── index.js ├── package.json └── test.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example 2 | .travis.yml 3 | test.js -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - '9' 4 | - '8' 5 | - '7' 6 | - '6' 7 | - '5' 8 | - '4' 9 | - '0.12' 10 | - '0.10' 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016-2017 Thomas Watson Steen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # progress-string 2 | 3 | Generate a CLI progress bar as a string that you can then output in any 4 | way you like. 5 | 6 | 7 | 8 | [![Build status](https://travis-ci.org/watson/progress-string.svg?branch=master)](https://travis-ci.org/watson/progress-string) 9 | [![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) 10 | 11 | ## Installation 12 | 13 | ``` 14 | npm install progress-string --save 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```js 20 | var progress = require('progress-string') 21 | var diff = require('ansi-diff-stream')() 22 | 23 | var value = 0 24 | var total = 42 25 | var bar = progress({width: 50, total: total}) 26 | 27 | setInterval(function () { 28 | diff.write( 29 | 'The progress of the program is:\n' + 30 | bar(++value) 31 | ) 32 | if (value === total) process.exit() 33 | }, 250) 34 | 35 | diff.pipe(process.stdout) 36 | ``` 37 | 38 | ## API 39 | 40 | ### `var bar = progress(options)` 41 | 42 | This module exposes a function that takes a single `options` argument 43 | and retuns a bar function. 44 | 45 | These are the options: 46 | 47 | - `total` - (integer) The max value of the progress bar 48 | - `width` - (integer, default: 42) The width of the progress bar in chars 49 | - `incomplete` - (string, default: `-`) The char used to indicate the 50 | incomplete part of the progress bar 51 | - `complete` - (string, default: `=`) The char used to indicate the 52 | completed part of the progress bar 53 | - `style` - (function, optional) See `options.style` below for details 54 | 55 | #### `options.style` 56 | 57 | You can provide a custom styling function to style the progress bar 58 | returned by the `bar` function. 59 | 60 | It will be called with two arguments: `complete` and `incomplete`. Each 61 | a string representing its part of the progress bar. 62 | 63 | Whatever the style function returns will be returned by the `bar` 64 | function. 65 | 66 | ```js 67 | var bar = progress({ 68 | width: 10, 69 | total: 100, 70 | style: function (complete, incomplete) { 71 | // add an arrow at the head of the completed part 72 | return complete + '>' + incomplete 73 | } 74 | }) 75 | 76 | console.log(bar(50)) // =====>----- 77 | ``` 78 | 79 | ### `var str = bar(value)` 80 | 81 | Call the `bar` function with the `value` you want to the generated 82 | progress bar to have. 83 | 84 | The `bar` function will return a string representation of the progress 85 | bar. 86 | 87 | ## License 88 | 89 | MIT 90 | -------------------------------------------------------------------------------- /example/example.js: -------------------------------------------------------------------------------- 1 | var progress = require('../') 2 | var diff = require('ansi-diff-stream')() 3 | 4 | var value = 0 5 | var total = 1024 6 | var bar = progress({width: 50, total: total}) 7 | var arrow = progress({ 8 | width: 50, 9 | total: total, 10 | style: function (complete, incomplete) { 11 | return complete.slice(0, complete.length - 1) + '>' + incomplete 12 | } 13 | }) 14 | 15 | var custom = progress({ 16 | width: 48, 17 | total: total, 18 | style: function (complete, incomplete) { 19 | return '#'.repeat(complete.length) + '' + ' '.repeat(incomplete.length) 20 | } 21 | }) 22 | 23 | var int = setInterval(function () { 24 | value += 32 25 | diff.write( 26 | 'default progress bar: ' + 27 | bar(value) + '\n' + 28 | 'but you can style it: ' + 29 | arrow(value) + '\n' + 30 | 'in any way you want: ' + 31 | '[' + custom(value) + ']' + ' (' + value + '/' + total + ')' 32 | ) 33 | if (value === total) { 34 | clearInterval(int) 35 | setTimeout(() => { 36 | process.exit() 37 | }, 1000) 38 | } 39 | }, 250) 40 | 41 | diff.pipe(process.stdout) 42 | -------------------------------------------------------------------------------- /example/example.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -u 3 | 4 | svg-term --command="node example/example" \ 5 | --out example/example.svg \ 6 | --height=3 \ 7 | --no-cursor \ 8 | --padding=10 \ 9 | --width=84 \ 10 | --term=terminal \ 11 | --profile=Pro 12 | -------------------------------------------------------------------------------- /example/example.svg: -------------------------------------------------------------------------------- 1 | defaultprogressbar:=-------------------------------------------------butyoucanstyleit:>-------------------------------------------------inanywayyouwant:[#](32/1024)defaultprogressbar:===-----------------------------------------------butyoucanstyleit:==>-----------------------------------------------inanywayyouwant:[###](64/1024)defaultprogressbar:====----------------------------------------------butyoucanstyleit:===>----------------------------------------------inanywayyouwant:[####](96/1024)defaultprogressbar:======--------------------------------------------butyoucanstyleit:=====>--------------------------------------------inanywayyouwant:[######](128/1024)defaultprogressbar:=======-------------------------------------------butyoucanstyleit:======>-------------------------------------------inanywayyouwant:[#######](160/1024)defaultprogressbar:=========-----------------------------------------butyoucanstyleit:========>-----------------------------------------inanywayyouwant:[#########](192/1024)defaultprogressbar:==========----------------------------------------butyoucanstyleit:=========>----------------------------------------inanywayyouwant:[##########](224/1024)defaultprogressbar:============--------------------------------------butyoucanstyleit:===========>--------------------------------------inanywayyouwant:[############](256/1024)defaultprogressbar:==============------------------------------------butyoucanstyleit:=============>------------------------------------inanywayyouwant:[#############](288/1024)defaultprogressbar:===============-----------------------------------butyoucanstyleit:==============>-----------------------------------inanywayyouwant:[###############](320/1024)defaultprogressbar:=================---------------------------------butyoucanstyleit:================>---------------------------------inanywayyouwant:[################](352/1024)defaultprogressbar:==================--------------------------------butyoucanstyleit:=================>--------------------------------inanywayyouwant:[##################](384/1024)defaultprogressbar:====================------------------------------butyoucanstyleit:===================>------------------------------inanywayyouwant:[###################](416/1024)defaultprogressbar:=====================-----------------------------butyoucanstyleit:====================>-----------------------------inanywayyouwant:[#####################](448/1024)defaultprogressbar:=======================---------------------------butyoucanstyleit:======================>---------------------------inanywayyouwant:[######################](480/1024)defaultprogressbar:=========================-------------------------butyoucanstyleit:========================>-------------------------inanywayyouwant:[########################](512/1024)defaultprogressbar:==========================------------------------butyoucanstyleit:=========================>------------------------inanywayyouwant:[#########################](544/1024)defaultprogressbar:============================----------------------butyoucanstyleit:===========================>----------------------inanywayyouwant:[###########################](576/1024)defaultprogressbar:=============================---------------------butyoucanstyleit:============================>---------------------inanywayyouwant:[############################](608/1024)defaultprogressbar:===============================-------------------butyoucanstyleit:==============================>-------------------inanywayyouwant:[##############################](640/1024)defaultprogressbar:================================------------------butyoucanstyleit:===============================>------------------inanywayyouwant:[###############################](672/1024)defaultprogressbar:==================================----------------butyoucanstyleit:=================================>----------------inanywayyouwant:[#################################](704/1024)defaultprogressbar:===================================---------------butyoucanstyleit:==================================>---------------inanywayyouwant:[##################################](736/1024)defaultprogressbar:=====================================-------------butyoucanstyleit:====================================>-------------inanywayyouwant:[####################################](768/1024)defaultprogressbar:=======================================-----------butyoucanstyleit:======================================>-----------inanywayyouwant:[#####################################](800/1024)defaultprogressbar:========================================----------butyoucanstyleit:=======================================>----------inanywayyouwant:[#######################################](832/1024)defaultprogressbar:==========================================--------butyoucanstyleit:=========================================>--------inanywayyouwant:[########################################](864/1024)defaultprogressbar:===========================================-------butyoucanstyleit:==========================================>-------inanywayyouwant:[##########################################](896/1024)defaultprogressbar:=============================================-----butyoucanstyleit:============================================>-----inanywayyouwant:[###########################################](928/1024)defaultprogressbar:==============================================----butyoucanstyleit:=============================================>----inanywayyouwant:[#############################################](960/1024)defaultprogressbar:================================================--butyoucanstyleit:===============================================>--inanywayyouwant:[##############################################](992/1024)defaultprogressbar:==================================================butyoucanstyleit:=================================================>inanywayyouwant:[################################################](1024/1024 -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | module.exports = function (opts) { 4 | if (!Number.isFinite(opts.total)) throw new Error('missing required opts.total') 5 | 6 | var width = opts.width || 42 7 | var total = opts.total 8 | var incomplete = Array(width + 1).join(opts.incomplete || '-') 9 | var complete = Array(width + 1).join(opts.complete || '=') 10 | var style = opts.style || defaultStyle 11 | var unit = total / width 12 | 13 | return function (value) { 14 | var chars = unit === 0 ? width : Math.floor(value / unit) 15 | if (value >= total) chars = complete.length 16 | return style(complete.slice(0, chars), incomplete.slice(chars)) 17 | } 18 | } 19 | 20 | function defaultStyle (complete, incomplete) { 21 | return complete + incomplete 22 | } 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "progress-string", 3 | "version": "1.2.2", 4 | "description": "Get a progess bar as a string", 5 | "main": "index.js", 6 | "scripts": { 7 | "example": "sh example/example.sh", 8 | "test": "standard && node test.js" 9 | }, 10 | "keywords": [ 11 | "progress", 12 | "bar", 13 | "cli" 14 | ], 15 | "author": "Thomas Watson Steen (https://twitter.com/wa7son)", 16 | "license": "MIT", 17 | "dependencies": {}, 18 | "devDependencies": { 19 | "ansi-diff-stream": "^1.2.0", 20 | "standard": "^8.5.0", 21 | "svg-term-cli": "^2.1.1", 22 | "tape": "^4.6.2" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "git+https://github.com/watson/progress-string.git" 27 | }, 28 | "bugs": { 29 | "url": "https://github.com/watson/progress-string/issues" 30 | }, 31 | "homepage": "https://github.com/watson/progress-string#readme", 32 | "coordinates": [ 33 | 52.1630635, 34 | -7.1482208 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | 'use strict' 2 | 3 | var test = require('tape') 4 | var progress = require('./') 5 | 6 | test('no options', function (t) { 7 | t.throws(function () { 8 | progress() 9 | }) 10 | t.end() 11 | }) 12 | 13 | test('empty options object', function (t) { 14 | t.throws(function () { 15 | progress({}) 16 | }) 17 | t.end() 18 | }) 19 | 20 | test('opts.total === 0', function (t) { 21 | t.doesNotThrow(function () { 22 | progress({total: 0}) 23 | }) 24 | t.end() 25 | }) 26 | 27 | test('0 of 0', function (t) { 28 | var bar = progress({width: 10, total: 0}) 29 | t.equal(bar(1), '==========') 30 | t.end() 31 | }) 32 | 33 | test('1 of 0', function (t) { 34 | var bar = progress({width: 10, total: 0}) 35 | t.equal(bar(1), '==========') 36 | t.end() 37 | }) 38 | 39 | test('0% of 100', function (t) { 40 | var bar = progress({width: 10, total: 100}) 41 | t.equal(bar(0), '----------') 42 | t.end() 43 | }) 44 | 45 | test('1% of 100', function (t) { 46 | var bar = progress({width: 10, total: 100}) 47 | t.equal(bar(1), '----------') 48 | t.end() 49 | }) 50 | 51 | test('50% of 100', function (t) { 52 | var bar = progress({width: 10, total: 100}) 53 | t.equal(bar(50), '=====-----') 54 | t.end() 55 | }) 56 | 57 | test('99% of 100', function (t) { 58 | var bar = progress({width: 10, total: 100}) 59 | t.equal(bar(99), '=========-') 60 | t.end() 61 | }) 62 | 63 | test('100% of 100', function (t) { 64 | var bar = progress({width: 10, total: 100}) 65 | t.equal(bar(100), '==========') 66 | t.end() 67 | }) 68 | 69 | test('custom chars', function (t) { 70 | var bar = progress({width: 10, total: 100, incomplete: '*', complete: '#'}) 71 | t.equal(bar(50), '#####*****') 72 | t.end() 73 | }) 74 | 75 | test('options.style', function (t) { 76 | var bar = progress({width: 10, total: 100, style: style}) 77 | t.equal(bar(50), '=====>-----') 78 | t.end() 79 | 80 | function style (complete, incomplete) { 81 | return complete + '>' + incomplete 82 | } 83 | }) 84 | --------------------------------------------------------------------------------