diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c1870cf..d36e1a8 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,10 +12,9 @@ jobs: node-version: - 14 - 12 - - 10 steps: - uses: actions/checkout@v2 - - uses: actions/setup-node@v1 + - uses: actions/setup-node@v2 with: node-version: ${{ matrix.node-version }} - run: npm install diff --git a/index.d.ts b/index.d.ts index f8401e1..00eeae3 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,82 +1,80 @@ -declare namespace prettyMilliseconds { - interface Options { - /** - Number of digits to appear after the seconds decimal point. +export interface Options { + /** + Number of digits to appear after the seconds decimal point. - @default 1 - */ - readonly secondsDecimalDigits?: number; + @default 1 + */ + readonly secondsDecimalDigits?: number; - /** - Number of digits to appear after the milliseconds decimal point. + /** + Number of digits to appear after the milliseconds decimal point. - Useful in combination with [`process.hrtime()`](https://nodejs.org/api/process.html#process_process_hrtime). + Useful in combination with [`process.hrtime()`](https://nodejs.org/api/process.html#process_process_hrtime). - @default 0 - */ - readonly millisecondsDecimalDigits?: number; + @default 0 + */ + readonly millisecondsDecimalDigits?: number; - /** - Keep milliseconds on whole seconds: `13s` → `13.0s`. + /** + Keep milliseconds on whole seconds: `13s` → `13.0s`. - Useful when you are showing a number of seconds spent on an operation and don't want the width of the output to change when hitting a whole number. + Useful when you are showing a number of seconds spent on an operation and don't want the width of the output to change when hitting a whole number. - @default false - */ - readonly keepDecimalsOnWholeSeconds?: boolean; + @default false + */ + readonly keepDecimalsOnWholeSeconds?: boolean; - /** - Only show the first unit: `1h 10m` → `1h`. + /** + Only show the first unit: `1h 10m` → `1h`. - Also ensures that `millisecondsDecimalDigits` and `secondsDecimalDigits` are both set to `0`. + Also ensures that `millisecondsDecimalDigits` and `secondsDecimalDigits` are both set to `0`. - @default false - */ - readonly compact?: boolean; + @default false + */ + readonly compact?: boolean; - /** - Number of units to show. Setting `compact` to `true` overrides this option. + /** + Number of units to show. Setting `compact` to `true` overrides this option. - @default Infinity - */ - readonly unitCount?: number; + @default Infinity + */ + readonly unitCount?: number; - /** - Use full-length units: `5h 1m 45s` → `5 hours 1 minute 45 seconds`. + /** + Use full-length units: `5h 1m 45s` → `5 hours 1 minute 45 seconds`. - @default false - */ - readonly verbose?: boolean; + @default false + */ + readonly verbose?: boolean; - /** - Show milliseconds separately. This means they won't be included in the decimal part of the seconds. + /** + Show milliseconds separately. This means they won't be included in the decimal part of the seconds. - @default false - */ - readonly separateMilliseconds?: boolean; + @default false + */ + readonly separateMilliseconds?: boolean; - /** - Show microseconds and nanoseconds. + /** + Show microseconds and nanoseconds. - @default false - */ - readonly formatSubMilliseconds?: boolean; + @default false + */ + readonly formatSubMilliseconds?: boolean; - /** - Display time using colon notation: `5h 1m 45s` → `5:01:45`. Always shows time in at least minutes: `1s` → `0:01` + /** + Display time using colon notation: `5h 1m 45s` → `5:01:45`. Always shows time in at least minutes: `1s` → `0:01` - Useful when you want to display time without the time units, similar to a digital watch. + Useful when you want to display time without the time units, similar to a digital watch. - Setting `colonNotation` to `true` overrides the following options to `false`: - - `compact` - - `formatSubMilliseconds` - - `separateMilliseconds` - - `verbose` + Setting `colonNotation` to `true` overrides the following options to `false`: + - `compact` + - `formatSubMilliseconds` + - `separateMilliseconds` + - `verbose` - @default false - */ - readonly colonNotation?: boolean; - } + @default false + */ + readonly colonNotation?: boolean; } /** @@ -86,7 +84,7 @@ Convert milliseconds to a human readable string: `1337000000` → `15d 11h 23m 2 @example ``` -import prettyMilliseconds = require('pretty-ms'); +import prettyMilliseconds from 'pretty-ms'; prettyMilliseconds(1337000000); //=> '15d 11h 23m 20s' @@ -118,9 +116,8 @@ prettyMilliseconds(new Date(2014, 0, 1, 10, 40) - new Date(2014, 0, 1, 10, 5)) //=> '35m' ``` */ -declare function prettyMilliseconds( +export default function prettyMilliseconds( milliseconds: number, - options?: prettyMilliseconds.Options + options?: Options ): string; -export = prettyMilliseconds; diff --git a/index.js b/index.js index 42e7679..dd9a31d 100644 --- a/index.js +++ b/index.js @@ -1,11 +1,10 @@ -'use strict'; -const parseMilliseconds = require('parse-ms'); +import parseMilliseconds from 'parse-ms'; const pluralize = (word, count) => count === 1 ? word : `${word}s`; -const SECOND_ROUNDING_EPSILON = 0.0000001; +const SECOND_ROUNDING_EPSILON = 0.000_000_1; -module.exports = (milliseconds, options = {}) => { +export default function prettyMilliseconds(milliseconds, options = {}) { if (!Number.isFinite(milliseconds)) { throw new TypeError('Expected a finite number'); } @@ -60,9 +59,9 @@ module.exports = (milliseconds, options = {}) => { add(parsed.minutes, 'minute', 'm'); if ( - options.separateMilliseconds || - options.formatSubMilliseconds || - (!options.colonNotation && milliseconds < 1000) + options.separateMilliseconds + || options.formatSubMilliseconds + || (!options.colonNotation && milliseconds < 1000) ) { add(parsed.seconds, 'second', 's'); if (options.formatSubMilliseconds) { @@ -70,42 +69,42 @@ module.exports = (milliseconds, options = {}) => { add(parsed.microseconds, 'microsecond', 'µs'); add(parsed.nanoseconds, 'nanosecond', 'ns'); } else { - const millisecondsAndBelow = - parsed.milliseconds + - (parsed.microseconds / 1000) + - (parsed.nanoseconds / 1e6); + const millisecondsAndBelow + = parsed.milliseconds + + (parsed.microseconds / 1000) + + (parsed.nanoseconds / 1e6); - const millisecondsDecimalDigits = - typeof options.millisecondsDecimalDigits === 'number' ? - options.millisecondsDecimalDigits : - 0; + const millisecondsDecimalDigits + = typeof options.millisecondsDecimalDigits === 'number' + ? options.millisecondsDecimalDigits + : 0; - const roundedMiliseconds = millisecondsAndBelow >= 1 ? - Math.round(millisecondsAndBelow) : - Math.ceil(millisecondsAndBelow); + const roundedMiliseconds = millisecondsAndBelow >= 1 + ? Math.round(millisecondsAndBelow) + : Math.ceil(millisecondsAndBelow); - const millisecondsString = millisecondsDecimalDigits ? - millisecondsAndBelow.toFixed(millisecondsDecimalDigits) : - roundedMiliseconds; + const millisecondsString = millisecondsDecimalDigits + ? millisecondsAndBelow.toFixed(millisecondsDecimalDigits) + : roundedMiliseconds; add( - Number.parseFloat(millisecondsString, 10), + Number.parseFloat(millisecondsString), 'millisecond', 'ms', - millisecondsString + millisecondsString, ); } } else { const seconds = (milliseconds / 1000) % 60; - const secondsDecimalDigits = - typeof options.secondsDecimalDigits === 'number' ? - options.secondsDecimalDigits : - 1; + const secondsDecimalDigits + = typeof options.secondsDecimalDigits === 'number' + ? options.secondsDecimalDigits + : 1; const secondsFixed = floorDecimals(seconds, secondsDecimalDigits); - const secondsString = options.keepDecimalsOnWholeSeconds ? - secondsFixed : - secondsFixed.replace(/\.0+$/, ''); - add(Number.parseFloat(secondsString, 10), 'second', 's', secondsString); + const secondsString = options.keepDecimalsOnWholeSeconds + ? secondsFixed + : secondsFixed.replace(/\.0+$/, ''); + add(Number.parseFloat(secondsString), 'second', 's', secondsString); } if (result.length === 0) { @@ -122,4 +121,4 @@ module.exports = (milliseconds, options = {}) => { } return options.colonNotation ? result.join('') : result.join(' '); -}; +} diff --git a/index.test-d.ts b/index.test-d.ts index fea7ada..7dd03d7 100644 --- a/index.test-d.ts +++ b/index.test-d.ts @@ -1,23 +1,23 @@ import {expectType} from 'tsd'; -import prettyMilliseconds = require('.'); +import prettyMilliseconds from './index.js'; -expectType(prettyMilliseconds(1335669000)); -expectType(prettyMilliseconds(1335669000, {secondsDecimalDigits: 1})); +expectType(prettyMilliseconds(1_335_669_000)); +expectType(prettyMilliseconds(1_335_669_000, {secondsDecimalDigits: 1})); expectType( - prettyMilliseconds(1335669000, {millisecondsDecimalDigits: 2}) + prettyMilliseconds(1_335_669_000, {millisecondsDecimalDigits: 2}), ); expectType( - prettyMilliseconds(1335669000, {keepDecimalsOnWholeSeconds: true}) + prettyMilliseconds(1_335_669_000, {keepDecimalsOnWholeSeconds: true}), ); expectType(prettyMilliseconds(1337, {compact: true})); -expectType(prettyMilliseconds(1335669000, {unitCount: 2})); -expectType(prettyMilliseconds(1335669000, {verbose: true})); +expectType(prettyMilliseconds(1_335_669_000, {unitCount: 2})); +expectType(prettyMilliseconds(1_335_669_000, {verbose: true})); expectType( - prettyMilliseconds(1335669000, {separateMilliseconds: true}) + prettyMilliseconds(1_335_669_000, {separateMilliseconds: true}), ); expectType( - prettyMilliseconds(1335669000, {formatSubMilliseconds: true}) + prettyMilliseconds(1_335_669_000, {formatSubMilliseconds: true}), ); expectType( - prettyMilliseconds(1335669000, {colonNotation: true}) + prettyMilliseconds(1_335_669_000, {colonNotation: true}), ); diff --git a/license b/license index e7af2f7..fa7ceba 100644 --- a/license +++ b/license @@ -1,6 +1,6 @@ MIT License -Copyright (c) Sindre Sorhus (sindresorhus.com) +Copyright (c) Sindre Sorhus (https://sindresorhus.com) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/package.json b/package.json index 5861c65..54f00c3 100644 --- a/package.json +++ b/package.json @@ -10,8 +10,10 @@ "email": "sindresorhus@gmail.com", "url": "https://sindresorhus.com" }, + "type": "module", + "exports": "./index.js", "engines": { - "node": ">=10" + "node": ">=12" }, "scripts": { "test": "xo && ava && tsd" @@ -39,11 +41,11 @@ "hrtime" ], "dependencies": { - "parse-ms": "^2.1.0" + "parse-ms": "^3.0.0" }, "devDependencies": { - "ava": "^2.4.0", - "tsd": "^0.11.0", - "xo": "^0.30.0" + "ava": "^3.15.0", + "tsd": "^0.19.0", + "xo": "^0.47.0" } } diff --git a/readme.md b/readme.md index b2e01f7..a231f8b 100644 --- a/readme.md +++ b/readme.md @@ -11,7 +11,7 @@ $ npm install pretty-ms ## Usage ```js -const prettyMilliseconds = require('pretty-ms'); +import prettyMilliseconds from 'pretty-ms'; prettyMilliseconds(1337000000); //=> '15d 11h 23m 20s' diff --git a/test.js b/test.js index ef07aab..4ca5a42 100644 --- a/test.js +++ b/test.js @@ -1,5 +1,5 @@ import test from 'ava'; -import prettyMilliseconds from '.'; +import prettyMilliseconds from './index.js'; test('prettify milliseconds', t => { t.is(prettyMilliseconds(0), '0ms'); @@ -18,8 +18,8 @@ test('prettify milliseconds', t => { t.is(prettyMilliseconds(1000 * 60 * 60 * 999), '41d 15h'); t.is(prettyMilliseconds(1000 * 60 * 60 * 24 * 465), '1y 100d'); t.is(prettyMilliseconds(1000 * 60 * 67 * 24 * 465), '1y 154d 6h'); - t.is(prettyMilliseconds(119999), '1m 59.9s'); - t.is(prettyMilliseconds(120000), '2m'); + t.is(prettyMilliseconds(119_999), '1m 59.9s'); + t.is(prettyMilliseconds(120_000), '2m'); }); test('have a compact option', t => { @@ -40,14 +40,14 @@ test('have a unitCount option', t => { }); test('have a secondsDecimalDigits option', t => { - t.is(prettyMilliseconds(10000), '10s'); - t.is(prettyMilliseconds(33333), '33.3s'); + t.is(prettyMilliseconds(10_000), '10s'); + t.is(prettyMilliseconds(33_333), '33.3s'); t.is(prettyMilliseconds(999, {secondsDecimalDigits: 0}), '999ms'); t.is(prettyMilliseconds(1000, {secondsDecimalDigits: 0}), '1s'); t.is(prettyMilliseconds(1999, {secondsDecimalDigits: 0}), '1s'); t.is(prettyMilliseconds(2000, {secondsDecimalDigits: 0}), '2s'); - t.is(prettyMilliseconds(33333, {secondsDecimalDigits: 0}), '33s'); - t.is(prettyMilliseconds(33333, {secondsDecimalDigits: 4}), '33.3330s'); + t.is(prettyMilliseconds(33_333, {secondsDecimalDigits: 0}), '33s'); + t.is(prettyMilliseconds(33_333, {secondsDecimalDigits: 4}), '33.3330s'); }); test('have a millisecondsDecimalDigits option', t => { @@ -58,7 +58,7 @@ test('have a millisecondsDecimalDigits option', t => { test('have a keepDecimalsOnWholeSeconds option', t => { t.is(prettyMilliseconds(1000 * 33, {secondsDecimalDigits: 2, keepDecimalsOnWholeSeconds: true}), '33.00s'); - t.is(prettyMilliseconds(1000 * 33.00004, {secondsDecimalDigits: 2, keepDecimalsOnWholeSeconds: true}), '33.00s'); + t.is(prettyMilliseconds(1000 * 33.000_04, {secondsDecimalDigits: 2, keepDecimalsOnWholeSeconds: true}), '33.00s'); }); test('have a verbose option', t => { @@ -89,20 +89,20 @@ test('have a separateMilliseconds option', t => { test('have a formatSubMilliseconds option', t => { t.is(prettyMilliseconds(0.4, {formatSubMilliseconds: true}), '400µs'); - t.is(prettyMilliseconds(0.123571, {formatSubMilliseconds: true}), '123µs 571ns'); - t.is(prettyMilliseconds(0.123456789, {formatSubMilliseconds: true}), '123µs 456ns'); + t.is(prettyMilliseconds(0.123_571, {formatSubMilliseconds: true}), '123µs 571ns'); + t.is(prettyMilliseconds(0.123_456_789, {formatSubMilliseconds: true}), '123µs 456ns'); t.is( - prettyMilliseconds((60 * 60 * 1000) + (23 * 1000) + 433 + 0.123456, { - formatSubMilliseconds: true + prettyMilliseconds((60 * 60 * 1000) + (23 * 1000) + 433 + 0.123_456, { + formatSubMilliseconds: true, }), - '1h 23s 433ms 123µs 456ns' + '1h 23s 433ms 123µs 456ns', ); }); test('work with verbose and compact options', t => { const fn = milliseconds => prettyMilliseconds(milliseconds, { verbose: true, - compact: true + compact: true, }); t.is(fn(1000), '1 second'); @@ -132,20 +132,20 @@ test('work with verbose and unitCount options', t => { test('work with verbose and secondsDecimalDigits options', t => { const fn = milliseconds => prettyMilliseconds(milliseconds, { verbose: true, - secondsDecimalDigits: 4 + secondsDecimalDigits: 4, }); t.is(fn(1000), '1 second'); t.is(fn(1000 + 400), '1.4000 seconds'); t.is(fn((1000 * 2) + 400), '2.4000 seconds'); t.is(fn((1000 * 5) + 254), '5.2540 seconds'); - t.is(fn(33333), '33.3330 seconds'); + t.is(fn(33_333), '33.3330 seconds'); }); test('work with verbose and millisecondsDecimalDigits options', t => { const fn = milliseconds => prettyMilliseconds(milliseconds, { verbose: true, - millisecondsDecimalDigits: 4 + millisecondsDecimalDigits: 4, }); t.is(fn(1), '1.0000 millisecond'); @@ -158,25 +158,25 @@ test('work with verbose and millisecondsDecimalDigits options', t => { test('work with verbose and formatSubMilliseconds options', t => { t.is( prettyMilliseconds(0.4, {formatSubMilliseconds: true, verbose: true}), - '400 microseconds' + '400 microseconds', ); t.is( - prettyMilliseconds(0.123571, { + prettyMilliseconds(0.123_571, { formatSubMilliseconds: true, - verbose: true + verbose: true, }), - '123 microseconds 571 nanoseconds' + '123 microseconds 571 nanoseconds', ); t.is( - prettyMilliseconds(0.123456789, { + prettyMilliseconds(0.123_456_789, { formatSubMilliseconds: true, - verbose: true + verbose: true, }), - '123 microseconds 456 nanoseconds' + '123 microseconds 456 nanoseconds', ); t.is( prettyMilliseconds(0.001, {formatSubMilliseconds: true, verbose: true}), - '1 microsecond' + '1 microsecond', ); }); @@ -188,18 +188,18 @@ test('compact option overrides unitCount option', t => { test('work with separateMilliseconds and formatSubMilliseconds options', t => { t.is( - prettyMilliseconds(1010.340067, { + prettyMilliseconds(1010.340_067, { separateMilliseconds: true, - formatSubMilliseconds: true + formatSubMilliseconds: true, }), - '1s 10ms 340µs 67ns' + '1s 10ms 340µs 67ns', ); t.is( - prettyMilliseconds((60 * 1000) + 34 + 0.000005, { + prettyMilliseconds((60 * 1000) + 34 + 0.000_005, { separateMilliseconds: true, - formatSubMilliseconds: true + formatSubMilliseconds: true, }), - '1m 34ms 5ns' + '1m 34ms 5ns', ); }); @@ -213,7 +213,7 @@ test('throw on invalid', t => { }); t.throws(() => { - prettyMilliseconds(Infinity); + prettyMilliseconds(Number.POSITIVE_INFINITY); }); }); @@ -221,7 +221,7 @@ test('properly rounds milliseconds with secondsDecimalDigits', t => { const fn = milliseconds => prettyMilliseconds(milliseconds, { verbose: true, - secondsDecimalDigits: 0 + secondsDecimalDigits: 0, }); t.is(fn(3 * 60 * 1000), '3 minutes'); t.is(fn((3 * 60 * 1000) - 1), '2 minutes 59 seconds'); @@ -241,7 +241,7 @@ test('`colonNotation` option', t => { t.is(prettyMilliseconds(1543, {colonNotation: true}), '0:01.5'); t.is(prettyMilliseconds(1000 * 60, {colonNotation: true}), '1:00'); t.is(prettyMilliseconds(1000 * 90, {colonNotation: true}), '1:30'); - t.is(prettyMilliseconds(95543, {colonNotation: true}), '1:35.5'); + t.is(prettyMilliseconds(95_543, {colonNotation: true}), '1:35.5'); t.is(prettyMilliseconds((1000 * 60 * 10) + 543, {colonNotation: true}), '10:00.5'); t.is(prettyMilliseconds((1000 * 60 * 59) + (1000 * 59) + 543, {colonNotation: true}), '59:59.5'); t.is(prettyMilliseconds((1000 * 60 * 60 * 15) + (1000 * 60 * 59) + (1000 * 59) + 543, {colonNotation: true}), '15:59:59.5'); @@ -263,10 +263,10 @@ test('`colonNotation` option', t => { t.is(prettyMilliseconds(1543, {colonNotation: true, secondsDecimalDigits: 1}), '0:01.5'); t.is(prettyMilliseconds(1543, {colonNotation: true, secondsDecimalDigits: 2}), '0:01.54'); t.is(prettyMilliseconds(1543, {colonNotation: true, secondsDecimalDigits: 3}), '0:01.543'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 0}), '1:35'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 1}), '1:35.5'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 2}), '1:35.54'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 3}), '1:35.543'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 0}), '1:35'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 1}), '1:35.5'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 2}), '1:35.54'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 3}), '1:35.543'); t.is(prettyMilliseconds((1000 * 60 * 10) + 543, {colonNotation: true, secondsDecimalDigits: 3}), '10:00.543'); t.is(prettyMilliseconds((1000 * 60 * 60 * 15) + (1000 * 60 * 59) + (1000 * 59) + 543, {colonNotation: true, secondsDecimalDigits: 3}), '15:59:59.543'); @@ -287,9 +287,9 @@ test('`colonNotation` option', t => { t.is(prettyMilliseconds(1000 * 90, {colonNotation: true, secondsDecimalDigits: 0, unitCount: 1}), '1'); t.is(prettyMilliseconds(1000 * 90, {colonNotation: true, secondsDecimalDigits: 0, unitCount: 2}), '1:30'); t.is(prettyMilliseconds(1000 * 60 * 90, {colonNotation: true, secondsDecimalDigits: 0, unitCount: 3}), '1:30:00'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 1, unitCount: 1}), '1'); - t.is(prettyMilliseconds(95543, {colonNotation: true, secondsDecimalDigits: 1, unitCount: 2}), '1:35.5'); - t.is(prettyMilliseconds(95543 + (1000 * 60 * 60), {colonNotation: true, secondsDecimalDigits: 1, unitCount: 3}), '1:01:35.5'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 1, unitCount: 1}), '1'); + t.is(prettyMilliseconds(95_543, {colonNotation: true, secondsDecimalDigits: 1, unitCount: 2}), '1:35.5'); + t.is(prettyMilliseconds(95_543 + (1000 * 60 * 60), {colonNotation: true, secondsDecimalDigits: 1, unitCount: 3}), '1:01:35.5'); // Make sure incompatible options fall back to `colonNotation` t.is(prettyMilliseconds((1000 * 60 * 59) + (1000 * 59) + 543, {colonNotation: true, formatSubMilliseconds: true}), '59:59.5');