@jsenv/humanize 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/README.md +35 -0
  2. package/dist/browser/jsenv_humanize_browser.js +1157 -0
  3. package/dist/node/jsenv_humanize_node.js +2003 -0
  4. package/package.json +49 -0
  5. package/src/.jsenv/jsenv_tests_output.txt +35 -0
  6. package/src/_tests/byte/file_size.test.js +18 -0
  7. package/src/_tests/byte/memory_usage.test.js +11 -0
  8. package/src/_tests/content_frame/content_frame.test.js +156 -0
  9. package/src/_tests/content_frame/snapshots/10_line_and_column_are_zero.txt +2 -0
  10. package/src/_tests/content_frame/snapshots/11_line_is_last.txt +3 -0
  11. package/src/_tests/content_frame/snapshots/1_basic.txt +4 -0
  12. package/src/_tests/content_frame/snapshots/2_basic_2.txt +6 -0
  13. package/src/_tests/content_frame/snapshots/3_empty_last_line.txt +7 -0
  14. package/src/_tests/content_frame/snapshots/4_no_column.txt +3 -0
  15. package/src/_tests/content_frame/snapshots/5_line_1_and_2_digits.txt +3 -0
  16. package/src/_tests/content_frame/snapshots/6_line_too_long_column_undefined.txt +3 -0
  17. package/src/_tests/content_frame/snapshots/7_line_too_long_column_near_start.txt +4 -0
  18. package/src/_tests/content_frame/snapshots/8_line_too_long_column_near_middle.txt +4 -0
  19. package/src/_tests/content_frame/snapshots/9_line_too_long_column_near_end.txt +4 -0
  20. package/src/_tests/js_value/_inspect.test.html +22 -0
  21. package/src/_tests/js_value/array.test.js +72 -0
  22. package/src/_tests/js_value/biginit.test.js +36 -0
  23. package/src/_tests/js_value/boolean.test.js +45 -0
  24. package/src/_tests/js_value/date.test.js +15 -0
  25. package/src/_tests/js_value/error.test.js +33 -0
  26. package/src/_tests/js_value/function.test.js +51 -0
  27. package/src/_tests/js_value/instance.test.js +12 -0
  28. package/src/_tests/js_value/null.test.js +6 -0
  29. package/src/_tests/js_value/number.test.js +182 -0
  30. package/src/_tests/js_value/object.test.js +173 -0
  31. package/src/_tests/js_value/regexp.test.js +14 -0
  32. package/src/_tests/js_value/string.test.js +106 -0
  33. package/src/_tests/js_value/symbol.test.js +20 -0
  34. package/src/_tests/js_value/undefined.test.js +6 -0
  35. package/src/_tests/log/log_long.test.mjs +39 -0
  36. package/src/_tests/log/log_modified.test.mjs +7 -0
  37. package/src/_tests/log/logger.test.mjs +91 -0
  38. package/src/_tests/log/spinner.duration.test_manual.mjs +25 -0
  39. package/src/_tests/log/spinner.test.mjs +33 -0
  40. package/src/_tests/log/spinner.test_manual.mjs +15 -0
  41. package/src/_tests/percentage/percentage_distribution.test.mjs +89 -0
  42. package/src/_tests/time/decimals.test.js +119 -0
  43. package/src/_tests/time/duration.test.js +20 -0
  44. package/src/_tests/time/ellapsed.test.js +10 -0
  45. package/src/byte/byte.js +44 -0
  46. package/src/content_frame/content_frame.js +157 -0
  47. package/src/js_value/array.js +58 -0
  48. package/src/js_value/bigint.js +3 -0
  49. package/src/js_value/bigint_object.js +5 -0
  50. package/src/js_value/boolean.js +1 -0
  51. package/src/js_value/boolean_object.js +12 -0
  52. package/src/js_value/constructor.js +10 -0
  53. package/src/js_value/date.js +8 -0
  54. package/src/js_value/error.js +34 -0
  55. package/src/js_value/function.js +20 -0
  56. package/src/js_value/humanize.js +140 -0
  57. package/src/js_value/null.js +1 -0
  58. package/src/js_value/number.js +101 -0
  59. package/src/js_value/number_object.js +9 -0
  60. package/src/js_value/object.js +78 -0
  61. package/src/js_value/regexp.js +1 -0
  62. package/src/js_value/string.js +141 -0
  63. package/src/js_value/string_object.js +10 -0
  64. package/src/js_value/symbol.js +24 -0
  65. package/src/js_value/undefined.js +1 -0
  66. package/src/log/LOG_LEVELS.js +9 -0
  67. package/src/log/ansi.js +37 -0
  68. package/src/log/detailed_message.js +17 -0
  69. package/src/log/dynamic_log.js +185 -0
  70. package/src/log/logger.js +85 -0
  71. package/src/log/spinner.js +72 -0
  72. package/src/log/task_log.js +46 -0
  73. package/src/log/unicode.js +52 -0
  74. package/src/main_browser.js +10 -0
  75. package/src/main_node.js +18 -0
  76. package/src/percentage/distribute_percentages.js +53 -0
  77. package/src/time/time.js +151 -0
  78. package/src/utils/decimals.js +95 -0
  79. package/src/utils/indentation.js +38 -0
@@ -0,0 +1,89 @@
1
+ import { assert } from "@jsenv/assert";
2
+
3
+ import { distributePercentages } from "@jsenv/humanize";
4
+
5
+ const test = (namedNumbers, expected) => {
6
+ const actual = distributePercentages(namedNumbers);
7
+ assert({ actual, expected });
8
+ };
9
+
10
+ test(
11
+ {
12
+ css: 24,
13
+ html: 707,
14
+ js: 6367,
15
+ json: 0,
16
+ other: 9997,
17
+ },
18
+ {
19
+ css: 0.1,
20
+ html: 4.1,
21
+ js: 37.2,
22
+ json: 0,
23
+ other: 58.6,
24
+ },
25
+ );
26
+ test(
27
+ {
28
+ a: 1,
29
+ b: 999,
30
+ },
31
+ {
32
+ a: 0.1,
33
+ b: 99.9,
34
+ },
35
+ );
36
+ test(
37
+ {
38
+ a: 1,
39
+ b: 9_999,
40
+ },
41
+ {
42
+ a: 0.01,
43
+ b: 99.99,
44
+ },
45
+ );
46
+ test(
47
+ {
48
+ a: 1,
49
+ b: 99_999,
50
+ },
51
+ {
52
+ a: 0.001,
53
+ b: 99.999,
54
+ },
55
+ );
56
+ test(
57
+ {
58
+ a: 56_052,
59
+ b: 222_878,
60
+ },
61
+ {
62
+ a: 20,
63
+ b: 80,
64
+ },
65
+ );
66
+ test(
67
+ {
68
+ a: 10,
69
+ b: 90,
70
+ },
71
+ {
72
+ a: 10,
73
+ b: 90,
74
+ },
75
+ );
76
+ test(
77
+ {
78
+ a: 1,
79
+ b: 10,
80
+ c: 100,
81
+ d: 889,
82
+ },
83
+ {
84
+ a: 0.1,
85
+ b: 1,
86
+ c: 10,
87
+ d: 88.9,
88
+ },
89
+ );
@@ -0,0 +1,119 @@
1
+ import { assert } from "@jsenv/assert";
2
+
3
+ import {
4
+ setRoundedPrecision,
5
+ setCeiledPrecision,
6
+ setFlooredPrecision,
7
+ setPrecision,
8
+ } from "@jsenv/humanize/src/utils/decimals.js";
9
+
10
+ /* numbers between -1 and 1 */
11
+ // rounds last decimal
12
+ {
13
+ const actual = setRoundedPrecision(0.54);
14
+ const expected = 0.5;
15
+ assert({ actual, expected });
16
+ }
17
+ {
18
+ const actual = setRoundedPrecision(0.55);
19
+ const expected = 0.6;
20
+ assert({ actual, expected });
21
+ }
22
+ // floor last decimal
23
+ {
24
+ const actual = setFlooredPrecision(0.54);
25
+ const expected = 0.5;
26
+ assert({ actual, expected });
27
+ }
28
+ {
29
+ const actual = setFlooredPrecision(0.55);
30
+ const expected = 0.5;
31
+ assert({ actual, expected });
32
+ }
33
+ {
34
+ const actual = setFlooredPrecision(0.501);
35
+ const expected = 0.5;
36
+ assert({ actual, expected });
37
+ }
38
+ // ceil last decimal
39
+ {
40
+ const actual = setCeiledPrecision(0.54);
41
+ const expected = 0.6;
42
+ assert({ actual, expected });
43
+ }
44
+ {
45
+ const actual = setCeiledPrecision(0.54);
46
+ const expected = 0.6;
47
+ assert({ actual, expected });
48
+ }
49
+ {
50
+ const actual = setCeiledPrecision(0.501);
51
+ const expected = 0.6;
52
+ assert({ actual, expected });
53
+ }
54
+ // truncate last decimal
55
+ {
56
+ const actual = setPrecision(0.501);
57
+ const expected = 0.5;
58
+ assert({ actual, expected });
59
+ }
60
+ {
61
+ const actual = setPrecision(0.56);
62
+ const expected = 0.5;
63
+ assert({ actual, expected });
64
+ }
65
+ // rounded tests
66
+ {
67
+ const actual = setRoundedPrecision(0.0101);
68
+ const expected = 0.01;
69
+ assert({ actual, expected });
70
+ }
71
+ {
72
+ const actual = setRoundedPrecision(0.012);
73
+ const expected = 0.01;
74
+ assert({ actual, expected });
75
+ }
76
+ {
77
+ const actual = setRoundedPrecision(0.016);
78
+ const expected = 0.02;
79
+ assert({ actual, expected });
80
+ }
81
+ {
82
+ const actual = setRoundedPrecision(0.016556, {
83
+ decimals: 3,
84
+ });
85
+ const expected = 0.0166;
86
+ assert({ actual, expected });
87
+ }
88
+ {
89
+ const actual = setRoundedPrecision(0);
90
+ const expected = 0;
91
+ assert({ actual, expected });
92
+ }
93
+ {
94
+ const actual = setRoundedPrecision(-0.0015);
95
+ const expected = -0.002;
96
+ assert({ actual, expected });
97
+ }
98
+
99
+ /* numbers above 1 */
100
+ {
101
+ const actual = setRoundedPrecision(37.04);
102
+ const expected = 37;
103
+ assert({ actual, expected });
104
+ }
105
+ {
106
+ const actual = setRoundedPrecision(37.05);
107
+ const expected = 37.1;
108
+ assert({ actual, expected });
109
+ }
110
+ {
111
+ const actual = setCeiledPrecision(37.01);
112
+ const expected = 37.1;
113
+ assert({ actual, expected });
114
+ }
115
+ {
116
+ const actual = setFlooredPrecision(37.01);
117
+ const expected = 37;
118
+ assert({ actual, expected });
119
+ }
@@ -0,0 +1,20 @@
1
+ import { assert } from "@jsenv/assert";
2
+
3
+ import { humanizeDuration } from "@jsenv/humanize";
4
+
5
+ const test = (durationInMs, expected) => {
6
+ const actual = humanizeDuration(durationInMs);
7
+ assert({ actual, expected });
8
+ };
9
+
10
+ test(0.1, "0 second");
11
+ test(1.02, "0.001 second");
12
+ test(1.52, "0.002 second");
13
+ test(52, "0.05 second");
14
+ test(55, "0.06 second");
15
+ test(99, "0.1 second");
16
+ test(999, "1 second");
17
+ test(1_421, "1.4 seconds");
18
+ test(61_421, "1 minute and 1 second");
19
+ test(3_601_200, "1 hour");
20
+ test(7_651_200, "2 hours and 8 minutes");
@@ -0,0 +1,10 @@
1
+ import { assert } from "@jsenv/assert";
2
+
3
+ import { humanizeEllapsedTime } from "@jsenv/humanize";
4
+
5
+ const test = (durationInMs, expected) => {
6
+ const actual = humanizeEllapsedTime(durationInMs);
7
+ assert({ actual, expected });
8
+ };
9
+
10
+ test(2200, "2 seconds");
@@ -0,0 +1,44 @@
1
+ import { setRoundedPrecision } from "../utils/decimals.js";
2
+
3
+ export const humanizeFileSize = (numberOfBytes, { decimals, short } = {}) => {
4
+ return inspectBytes(numberOfBytes, { decimals, short });
5
+ };
6
+
7
+ export const humanizeMemoryUsage = (metricValue, { decimals, short } = {}) => {
8
+ return inspectBytes(metricValue, { decimals, fixedDecimals: true, short });
9
+ };
10
+
11
+ const inspectBytes = (
12
+ number,
13
+ { fixedDecimals = false, decimals, short } = {},
14
+ ) => {
15
+ if (number === 0) {
16
+ return `0 B`;
17
+ }
18
+ const exponent = Math.min(
19
+ Math.floor(Math.log10(number) / 3),
20
+ BYTE_UNITS.length - 1,
21
+ );
22
+ const unitNumber = number / Math.pow(1000, exponent);
23
+ const unitName = BYTE_UNITS[exponent];
24
+ if (decimals === undefined) {
25
+ if (unitNumber < 100) {
26
+ decimals = 1;
27
+ } else {
28
+ decimals = 0;
29
+ }
30
+ }
31
+ const unitNumberRounded = setRoundedPrecision(unitNumber, {
32
+ decimals,
33
+ decimalsWhenSmall: 1,
34
+ });
35
+ const value = fixedDecimals
36
+ ? unitNumberRounded.toFixed(decimals)
37
+ : unitNumberRounded;
38
+ if (short) {
39
+ return `${value}${unitName}`;
40
+ }
41
+ return `${value} ${unitName}`;
42
+ };
43
+
44
+ const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
@@ -0,0 +1,157 @@
1
+ const formatDefault = (v) => v;
2
+
3
+ export const generateContentFrame = ({
4
+ content,
5
+ line,
6
+ column,
7
+
8
+ linesAbove = 3,
9
+ linesBelow = 0,
10
+ lineMaxWidth = 120,
11
+ lineNumbersOnTheLeft = true,
12
+ lineMarker = true,
13
+ columnMarker = true,
14
+ format = formatDefault,
15
+ } = {}) => {
16
+ const lineStrings = content.split(/\r?\n/);
17
+ if (line === 0) line = 1;
18
+ if (column === undefined) {
19
+ columnMarker = false;
20
+ column = 1;
21
+ }
22
+ if (column === 0) column = 1;
23
+
24
+ let lineStartIndex = line - 1 - linesAbove;
25
+ if (lineStartIndex < 0) {
26
+ lineStartIndex = 0;
27
+ }
28
+ let lineEndIndex = line - 1 + linesBelow;
29
+ if (lineEndIndex > lineStrings.length - 1) {
30
+ lineEndIndex = lineStrings.length - 1;
31
+ }
32
+ if (columnMarker) {
33
+ // human reader deduce the line when there is a column marker
34
+ lineMarker = false;
35
+ }
36
+ if (line - 1 === lineEndIndex) {
37
+ lineMarker = false; // useless because last line
38
+ }
39
+ let lineIndex = lineStartIndex;
40
+
41
+ let columnsBefore;
42
+ let columnsAfter;
43
+ if (column > lineMaxWidth) {
44
+ columnsBefore = column - Math.ceil(lineMaxWidth / 2);
45
+ columnsAfter = column + Math.floor(lineMaxWidth / 2);
46
+ } else {
47
+ columnsBefore = 0;
48
+ columnsAfter = lineMaxWidth;
49
+ }
50
+ let columnMarkerIndex = column - 1 - columnsBefore;
51
+
52
+ let source = "";
53
+ while (lineIndex <= lineEndIndex) {
54
+ const lineString = lineStrings[lineIndex];
55
+ const lineNumber = lineIndex + 1;
56
+ const isLastLine = lineIndex === lineEndIndex;
57
+ const isMainLine = lineNumber === line;
58
+ lineIndex++;
59
+
60
+ write_aside: {
61
+ if (lineMarker) {
62
+ if (isMainLine) {
63
+ source += `${format(">", "marker_line")} `;
64
+ } else {
65
+ source += " ";
66
+ }
67
+ }
68
+ if (lineNumbersOnTheLeft) {
69
+ // fill with spaces to ensure if line moves from 7,8,9 to 10 the display is still great
70
+ const asideSource = `${fillLeft(lineNumber, lineEndIndex + 1)} |`;
71
+ source += `${format(asideSource, "line_number_aside")} `;
72
+ }
73
+ }
74
+ write_line: {
75
+ source += truncateLine(lineString, {
76
+ start: columnsBefore,
77
+ end: columnsAfter,
78
+ prefix: "…",
79
+ suffix: "…",
80
+ format,
81
+ });
82
+ }
83
+ write_column_marker: {
84
+ if (columnMarker && isMainLine) {
85
+ source += `\n`;
86
+ if (lineMarker) {
87
+ source += " ";
88
+ }
89
+ if (lineNumbersOnTheLeft) {
90
+ const asideSpaces = `${fillLeft(lineNumber, lineEndIndex + 1)} | `
91
+ .length;
92
+ source += " ".repeat(asideSpaces);
93
+ }
94
+ source += " ".repeat(columnMarkerIndex);
95
+ source += format("^", "marker_column");
96
+ }
97
+ }
98
+ if (!isLastLine) {
99
+ source += "\n";
100
+ }
101
+ }
102
+ return source;
103
+ };
104
+
105
+ const truncateLine = (line, { start, end, prefix, suffix, format }) => {
106
+ const lastIndex = line.length;
107
+
108
+ if (line.length === 0) {
109
+ // don't show any ellipsis if the line is empty
110
+ // because it's not truncated in that case
111
+ return "";
112
+ }
113
+
114
+ const startTruncated = start > 0;
115
+ const endTruncated = lastIndex > end;
116
+
117
+ let from = startTruncated ? start + prefix.length : start;
118
+ let to = endTruncated ? end - suffix.length : end;
119
+ if (to > lastIndex) to = lastIndex;
120
+
121
+ if (start >= lastIndex || from === to) {
122
+ return "";
123
+ }
124
+ let result = "";
125
+ while (from < to) {
126
+ result += format(line[from], "char");
127
+ from++;
128
+ }
129
+ if (result.length === 0) {
130
+ return "";
131
+ }
132
+ if (startTruncated && endTruncated) {
133
+ return `${format(prefix, "marker_overflow_left")}${result}${format(
134
+ suffix,
135
+ "marker_overflow_right",
136
+ )}`;
137
+ }
138
+ if (startTruncated) {
139
+ return `${format(prefix, "marker_overflow_left")}${result}`;
140
+ }
141
+ if (endTruncated) {
142
+ return `${result}${format(suffix, "marker_overflow_right")}`;
143
+ }
144
+ return result;
145
+ };
146
+
147
+ const fillLeft = (value, biggestValue, char = " ") => {
148
+ const width = String(value).length;
149
+ const biggestWidth = String(biggestValue).length;
150
+ let missingWidth = biggestWidth - width;
151
+ let padded = "";
152
+ while (missingWidth--) {
153
+ padded += char;
154
+ }
155
+ padded += value;
156
+ return padded;
157
+ };
@@ -0,0 +1,58 @@
1
+ import {
2
+ preNewLineAndIndentation,
3
+ wrapNewLineAndIndentation,
4
+ } from "../utils/indentation.js";
5
+ import { inspectConstructor } from "./constructor.js";
6
+
7
+ export const inspectArray = (
8
+ value,
9
+ {
10
+ seen = [],
11
+ nestedHumanize,
12
+ depth,
13
+ indentUsingTab,
14
+ indentSize,
15
+ parenthesis,
16
+ useNew,
17
+ },
18
+ ) => {
19
+ if (seen.indexOf(value) > -1) {
20
+ return "Symbol.for('circular')";
21
+ }
22
+ seen.push(value);
23
+
24
+ let valuesSource = "";
25
+ let i = 0;
26
+ const j = value.length;
27
+
28
+ while (i < j) {
29
+ const valueSource = value.hasOwnProperty(i)
30
+ ? nestedHumanize(value[i], { seen })
31
+ : "";
32
+ if (i === 0) {
33
+ valuesSource += valueSource;
34
+ } else {
35
+ valuesSource += `,${preNewLineAndIndentation(valueSource, {
36
+ depth,
37
+ indentUsingTab,
38
+ indentSize,
39
+ })}`;
40
+ }
41
+ i++;
42
+ }
43
+
44
+ let arraySource;
45
+ if (valuesSource.length) {
46
+ arraySource = wrapNewLineAndIndentation(valuesSource, {
47
+ depth,
48
+ indentUsingTab,
49
+ indentSize,
50
+ });
51
+ } else {
52
+ arraySource = "";
53
+ }
54
+
55
+ arraySource = `[${arraySource}]`;
56
+
57
+ return inspectConstructor(arraySource, { parenthesis, useNew });
58
+ };
@@ -0,0 +1,3 @@
1
+ export const inspectBigInt = (value) => {
2
+ return `${value}n`;
3
+ };
@@ -0,0 +1,5 @@
1
+ export const inspectBigIntObject = (value, { nestedHumanize }) => {
2
+ const bigIntSource = nestedHumanize(value.valueOf());
3
+
4
+ return `BigInt(${bigIntSource})`;
5
+ };
@@ -0,0 +1 @@
1
+ export const inspectBoolean = (value) => value.toString();
@@ -0,0 +1,12 @@
1
+ import { inspectConstructor } from "./constructor.js";
2
+
3
+ export const inspectBooleanObject = (
4
+ value,
5
+ { nestedHumanize, useNew, parenthesis },
6
+ ) => {
7
+ const booleanSource = nestedHumanize(value.valueOf());
8
+ return inspectConstructor(`Boolean(${booleanSource})`, {
9
+ useNew,
10
+ parenthesis,
11
+ });
12
+ };
@@ -0,0 +1,10 @@
1
+ export const inspectConstructor = (value, { parenthesis, useNew }) => {
2
+ let formattedString = value;
3
+ if (parenthesis) {
4
+ formattedString = `(${value})`;
5
+ }
6
+ if (useNew) {
7
+ formattedString = `new ${formattedString}`;
8
+ }
9
+ return formattedString;
10
+ };
@@ -0,0 +1,8 @@
1
+ import { inspectConstructor } from "./constructor.js";
2
+
3
+ export const inspectDate = (value, { nestedHumanize, useNew, parenthesis }) => {
4
+ const dateSource = nestedHumanize(value.valueOf(), {
5
+ numericSeparator: false,
6
+ });
7
+ return inspectConstructor(`Date(${dateSource})`, { useNew, parenthesis });
8
+ };
@@ -0,0 +1,34 @@
1
+ import { inspectConstructor } from "./constructor.js";
2
+
3
+ export const inspectError = (
4
+ error,
5
+ { nestedHumanize, useNew, parenthesis },
6
+ ) => {
7
+ const messageSource = nestedHumanize(error.message);
8
+
9
+ const errorSource = inspectConstructor(
10
+ `${errorToConstructorName(error)}(${messageSource})`,
11
+ {
12
+ useNew,
13
+ parenthesis,
14
+ },
15
+ );
16
+ return errorSource;
17
+ };
18
+
19
+ const errorToConstructorName = ({ name }) => {
20
+ if (derivedErrorNameArray.includes(name)) {
21
+ return name;
22
+ }
23
+ return "Error";
24
+ };
25
+
26
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
27
+ const derivedErrorNameArray = [
28
+ "EvalError",
29
+ "RangeError",
30
+ "ReferenceError",
31
+ "SyntaxError",
32
+ "TypeError",
33
+ "URIError",
34
+ ];
@@ -0,0 +1,20 @@
1
+ export const inspectFunction = (
2
+ value,
3
+ { showFunctionBody, parenthesis, depth },
4
+ ) => {
5
+ let functionSource;
6
+ if (showFunctionBody) {
7
+ functionSource = value.toString();
8
+ } else {
9
+ const isArrowFunction = value.prototype === undefined;
10
+ const head = isArrowFunction
11
+ ? "() =>"
12
+ : `function ${depth === 0 ? value.name : ""}()`;
13
+ functionSource = `${head} {/* hidden */}`;
14
+ }
15
+
16
+ if (parenthesis) {
17
+ return `(${functionSource})`;
18
+ }
19
+ return functionSource;
20
+ };