index.js 2.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. 'use strict';
  2. const BYTE_UNITS = [
  3. 'B',
  4. 'kB',
  5. 'MB',
  6. 'GB',
  7. 'TB',
  8. 'PB',
  9. 'EB',
  10. 'ZB',
  11. 'YB'
  12. ];
  13. const BIBYTE_UNITS = [
  14. 'B',
  15. 'kiB',
  16. 'MiB',
  17. 'GiB',
  18. 'TiB',
  19. 'PiB',
  20. 'EiB',
  21. 'ZiB',
  22. 'YiB'
  23. ];
  24. const BIT_UNITS = [
  25. 'b',
  26. 'kbit',
  27. 'Mbit',
  28. 'Gbit',
  29. 'Tbit',
  30. 'Pbit',
  31. 'Ebit',
  32. 'Zbit',
  33. 'Ybit'
  34. ];
  35. const BIBIT_UNITS = [
  36. 'b',
  37. 'kibit',
  38. 'Mibit',
  39. 'Gibit',
  40. 'Tibit',
  41. 'Pibit',
  42. 'Eibit',
  43. 'Zibit',
  44. 'Yibit'
  45. ];
  46. /*
  47. Formats the given number using `Number#toLocaleString`.
  48. - If locale is a string, the value is expected to be a locale-key (for example: `de`).
  49. - If locale is true, the system default locale is used for translation.
  50. - If no value for locale is specified, the number is returned unmodified.
  51. */
  52. const toLocaleString = (number, locale, options) => {
  53. let result = number;
  54. if (typeof locale === 'string' || Array.isArray(locale)) {
  55. result = number.toLocaleString(locale, options);
  56. } else if (locale === true || options !== undefined) {
  57. result = number.toLocaleString(undefined, options);
  58. }
  59. return result;
  60. };
  61. module.exports = (number, options) => {
  62. if (!Number.isFinite(number)) {
  63. throw new TypeError(`Expected a finite number, got ${typeof number}: ${number}`);
  64. }
  65. options = Object.assign({bits: false, binary: false}, options);
  66. const UNITS = options.bits ?
  67. (options.binary ? BIBIT_UNITS : BIT_UNITS) :
  68. (options.binary ? BIBYTE_UNITS : BYTE_UNITS);
  69. if (options.signed && number === 0) {
  70. return ` 0 ${UNITS[0]}`;
  71. }
  72. const isNegative = number < 0;
  73. const prefix = isNegative ? '-' : (options.signed ? '+' : '');
  74. if (isNegative) {
  75. number = -number;
  76. }
  77. let localeOptions;
  78. if (options.minimumFractionDigits !== undefined) {
  79. localeOptions = {minimumFractionDigits: options.minimumFractionDigits};
  80. }
  81. if (options.maximumFractionDigits !== undefined) {
  82. localeOptions = Object.assign({maximumFractionDigits: options.maximumFractionDigits}, localeOptions);
  83. }
  84. if (number < 1) {
  85. const numberString = toLocaleString(number, options.locale, localeOptions);
  86. return prefix + numberString + ' ' + UNITS[0];
  87. }
  88. const exponent = Math.min(Math.floor(options.binary ? Math.log(number) / Math.log(1024) : Math.log10(number) / 3), UNITS.length - 1);
  89. // eslint-disable-next-line unicorn/prefer-exponentiation-operator
  90. number /= Math.pow(options.binary ? 1024 : 1000, exponent);
  91. if (!localeOptions) {
  92. number = number.toPrecision(3);
  93. }
  94. const numberString = toLocaleString(Number(number), options.locale, localeOptions);
  95. const unit = UNITS[exponent];
  96. return prefix + numberString + ' ' + unit;
  97. };