@jsenv/humanize 1.6.0 → 1.7.1

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 (91) hide show
  1. package/dist/browser/jsenv_humanize_browser.js +539 -482
  2. package/dist/browser/jsenv_humanize_browser.js.map +76 -0
  3. package/dist/node/jsenv_humanize_node.js +36 -37
  4. package/dist/node/jsenv_humanize_node.js.map +97 -0
  5. package/dist/node/jsenv_humanize_node_modules.js +23 -15
  6. package/dist/node/jsenv_humanize_node_modules.js.map +21 -0
  7. package/package.json +18 -19
  8. package/src/_tests/byte/file_size.test.js +0 -18
  9. package/src/_tests/byte/memory.test.js +0 -11
  10. package/src/_tests/content_frame/content_frame.test.js +0 -155
  11. package/src/_tests/content_frame/snapshots/10_line_and_column_are_zero.txt +0 -2
  12. package/src/_tests/content_frame/snapshots/11_line_is_last.txt +0 -3
  13. package/src/_tests/content_frame/snapshots/1_basic.txt +0 -4
  14. package/src/_tests/content_frame/snapshots/2_basic_2.txt +0 -6
  15. package/src/_tests/content_frame/snapshots/3_empty_last_line.txt +0 -7
  16. package/src/_tests/content_frame/snapshots/4_no_column.txt +0 -3
  17. package/src/_tests/content_frame/snapshots/5_line_1_and_2_digits.txt +0 -3
  18. package/src/_tests/content_frame/snapshots/6_line_too_long_column_undefined.txt +0 -3
  19. package/src/_tests/content_frame/snapshots/7_line_too_long_column_near_start.txt +0 -4
  20. package/src/_tests/content_frame/snapshots/8_line_too_long_column_near_middle.txt +0 -4
  21. package/src/_tests/content_frame/snapshots/9_line_too_long_column_near_end.txt +0 -4
  22. package/src/_tests/js_value/_inspect.test.html +0 -22
  23. package/src/_tests/js_value/array.test.js +0 -72
  24. package/src/_tests/js_value/biginit.test.js +0 -35
  25. package/src/_tests/js_value/boolean.test.js +0 -45
  26. package/src/_tests/js_value/date.test.js +0 -15
  27. package/src/_tests/js_value/error.test.js +0 -33
  28. package/src/_tests/js_value/function.test.js +0 -51
  29. package/src/_tests/js_value/instance.test.js +0 -12
  30. package/src/_tests/js_value/null.test.js +0 -6
  31. package/src/_tests/js_value/number.test.js +0 -182
  32. package/src/_tests/js_value/object.test.js +0 -173
  33. package/src/_tests/js_value/regexp.test.js +0 -15
  34. package/src/_tests/js_value/string.test.js +0 -106
  35. package/src/_tests/js_value/symbol.test.js +0 -20
  36. package/src/_tests/js_value/undefined.test.js +0 -6
  37. package/src/_tests/log/log_long.test_manual.mjs +0 -39
  38. package/src/_tests/log/log_modified.test_manual.mjs +0 -7
  39. package/src/_tests/log/logger.test.mjs +0 -90
  40. package/src/_tests/log/spinner.duration.test_manual.mjs +0 -25
  41. package/src/_tests/log/spinner.test.mjs +0 -33
  42. package/src/_tests/log/spinner.test_manual.mjs +0 -15
  43. package/src/_tests/percentage/percentage_distribution.test.mjs +0 -89
  44. package/src/_tests/time/decimals.test.js +0 -119
  45. package/src/_tests/time/duration.test.js +0 -20
  46. package/src/_tests/time/ellapsed.test.js +0 -10
  47. package/src/ansi/ansi_browser.js +0 -3
  48. package/src/ansi/ansi_node.js +0 -13
  49. package/src/ansi/ansi_runtime_agnostic.js +0 -107
  50. package/src/byte/byte.js +0 -44
  51. package/src/content_frame/content_frame.js +0 -157
  52. package/src/error/error_to_html.js +0 -22
  53. package/src/error/error_to_markdown.js +0 -14
  54. package/src/error/format_error.js +0 -53
  55. package/src/js_value/array.js +0 -58
  56. package/src/js_value/bigint.js +0 -3
  57. package/src/js_value/bigint_object.js +0 -5
  58. package/src/js_value/boolean.js +0 -1
  59. package/src/js_value/boolean_object.js +0 -12
  60. package/src/js_value/constructor.js +0 -10
  61. package/src/js_value/date.js +0 -8
  62. package/src/js_value/error.js +0 -34
  63. package/src/js_value/function.js +0 -20
  64. package/src/js_value/humanize.js +0 -142
  65. package/src/js_value/null.js +0 -1
  66. package/src/js_value/number.js +0 -101
  67. package/src/js_value/number_object.js +0 -9
  68. package/src/js_value/object.js +0 -78
  69. package/src/js_value/regexp.js +0 -1
  70. package/src/js_value/string.js +0 -141
  71. package/src/js_value/string_object.js +0 -10
  72. package/src/js_value/symbol.js +0 -24
  73. package/src/js_value/undefined.js +0 -1
  74. package/src/log/LOG_LEVELS.js +0 -9
  75. package/src/log/call_orderer.js +0 -39
  76. package/src/log/detailed_message.js +0 -29
  77. package/src/log/details.js +0 -26
  78. package/src/log/dynamic_log.js +0 -186
  79. package/src/log/logger.js +0 -85
  80. package/src/log/section.js +0 -50
  81. package/src/log/spinner.js +0 -72
  82. package/src/log/task_log.js +0 -49
  83. package/src/main_browser.js +0 -13
  84. package/src/main_node.js +0 -25
  85. package/src/percentage/distribute_percentages.js +0 -53
  86. package/src/time/time.js +0 -151
  87. package/src/unicode/unicode_browser.js +0 -7
  88. package/src/unicode/unicode_node.js +0 -8
  89. package/src/unicode/unicode_runtime_agnostic.js +0 -56
  90. package/src/utils/decimals.js +0 -95
  91. package/src/utils/indentation.js +0 -38
@@ -1,186 +0,0 @@
1
- /*
2
- * see also https://github.com/vadimdemedes/ink
3
- */
4
-
5
- import { measureTextWidth } from "@jsenv/terminal-text-size";
6
- import ansiEscapes from "ansi-escapes";
7
-
8
- export const createDynamicLog = ({
9
- stream = process.stdout,
10
- clearTerminalAllowed,
11
- onVerticalOverflow = () => {},
12
- onWriteFromOutside = () => {},
13
- } = {}) => {
14
- const { columns = 80, rows = 24 } = stream;
15
- const dynamicLog = {
16
- destroyed: false,
17
- onVerticalOverflow,
18
- onWriteFromOutside,
19
- };
20
-
21
- let lastOutput = "";
22
- let lastOutputFromOutside = "";
23
- let clearAttemptResult;
24
- let writing = false;
25
-
26
- const getErasePreviousOutput = () => {
27
- // nothing to clear
28
- if (!lastOutput) {
29
- return "";
30
- }
31
- if (clearAttemptResult !== undefined) {
32
- return "";
33
- }
34
-
35
- const logLines = lastOutput.split(/\r\n|\r|\n/);
36
- let visualLineCount = 0;
37
- for (const logLine of logLines) {
38
- const width = measureTextWidth(logLine);
39
- if (width === 0) {
40
- visualLineCount++;
41
- } else {
42
- visualLineCount += Math.ceil(width / columns);
43
- }
44
- }
45
-
46
- if (visualLineCount > rows) {
47
- if (clearTerminalAllowed) {
48
- clearAttemptResult = true;
49
- return ansiEscapes.clearTerminal;
50
- }
51
- // the whole log cannot be cleared because it's vertically to long
52
- // (longer than terminal height)
53
- // readline.moveCursor cannot move cursor higher than screen height
54
- // it means we would only clear the visible part of the log
55
- // better keep the log untouched
56
- clearAttemptResult = false;
57
- dynamicLog.onVerticalOverflow();
58
- return "";
59
- }
60
-
61
- clearAttemptResult = true;
62
- return ansiEscapes.eraseLines(visualLineCount);
63
- };
64
-
65
- const update = (string) => {
66
- if (dynamicLog.destroyed) {
67
- throw new Error("Cannot write log after destroy");
68
- }
69
- let stringToWrite = string;
70
- if (lastOutput) {
71
- if (lastOutputFromOutside) {
72
- // We don't want to clear logs written by other code,
73
- // it makes output unreadable and might erase precious information
74
- // To detect this we put a spy on the stream.
75
- // The spy is required only if we actually wrote something in the stream
76
- // something else than this code has written in the stream
77
- // so we just write without clearing (append instead of replacing)
78
- lastOutput = "";
79
- lastOutputFromOutside = "";
80
- } else {
81
- stringToWrite = `${getErasePreviousOutput()}${string}`;
82
- }
83
- }
84
- writing = true;
85
- stream.write(stringToWrite);
86
- lastOutput = string;
87
- writing = false;
88
- clearAttemptResult = undefined;
89
- };
90
-
91
- const clearDuringFunctionCall = (
92
- callback,
93
- ouputAfterCallback = lastOutput,
94
- ) => {
95
- // 1. Erase the current log
96
- // 2. Call callback (expect to write something on stdout)
97
- // 3. Restore the current log
98
- // During step 2. we expect a "write from outside" so we uninstall
99
- // the stream spy during function call
100
- update("");
101
-
102
- writing = true;
103
- callback(update);
104
- lastOutput = "";
105
- writing = false;
106
-
107
- update(ouputAfterCallback);
108
- };
109
-
110
- const writeFromOutsideEffect = (value) => {
111
- if (!lastOutput) {
112
- // we don't care if the log never wrote anything
113
- // or if last update() wrote an empty string
114
- return;
115
- }
116
- if (writing) {
117
- return;
118
- }
119
- lastOutputFromOutside = value;
120
- dynamicLog.onWriteFromOutside(value);
121
- };
122
-
123
- let removeStreamSpy;
124
- if (stream === process.stdout) {
125
- const removeStdoutSpy = spyStreamOutput(
126
- process.stdout,
127
- writeFromOutsideEffect,
128
- );
129
- const removeStderrSpy = spyStreamOutput(
130
- process.stderr,
131
- writeFromOutsideEffect,
132
- );
133
- removeStreamSpy = () => {
134
- removeStdoutSpy();
135
- removeStderrSpy();
136
- };
137
- } else {
138
- removeStreamSpy = spyStreamOutput(stream, writeFromOutsideEffect);
139
- }
140
-
141
- const destroy = () => {
142
- dynamicLog.destroyed = true;
143
- if (removeStreamSpy) {
144
- removeStreamSpy();
145
- removeStreamSpy = null;
146
- lastOutput = "";
147
- lastOutputFromOutside = "";
148
- }
149
- };
150
-
151
- Object.assign(dynamicLog, {
152
- update,
153
- destroy,
154
- stream,
155
- clearDuringFunctionCall,
156
- });
157
- return dynamicLog;
158
- };
159
-
160
- // maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
161
- // the problem with listening data on stdout
162
- // is that node.js will later throw error if stream gets closed
163
- // while something listening data on it
164
- const spyStreamOutput = (stream, callback) => {
165
- let output = "";
166
- let installed = true;
167
- const originalWrite = stream.write;
168
- stream.write = function (...args /* chunk, encoding, callback */) {
169
- output += args;
170
- callback(output);
171
- return originalWrite.call(this, ...args);
172
- };
173
-
174
- const uninstall = () => {
175
- if (!installed) {
176
- return;
177
- }
178
- stream.write = originalWrite;
179
- installed = false;
180
- };
181
-
182
- return () => {
183
- uninstall();
184
- return output;
185
- };
186
- };
package/src/log/logger.js DELETED
@@ -1,85 +0,0 @@
1
- import {
2
- LOG_LEVEL_DEBUG,
3
- LOG_LEVEL_ERROR,
4
- LOG_LEVEL_INFO,
5
- LOG_LEVEL_OFF,
6
- LOG_LEVEL_WARN,
7
- } from "./LOG_LEVELS.js";
8
-
9
- export const createLogger = ({ logLevel = LOG_LEVEL_INFO } = {}) => {
10
- if (logLevel === LOG_LEVEL_DEBUG) {
11
- return {
12
- level: "debug",
13
- levels: { debug: true, info: true, warn: true, error: true },
14
- debug,
15
- info,
16
- warn,
17
- error,
18
- };
19
- }
20
- if (logLevel === LOG_LEVEL_INFO) {
21
- return {
22
- level: "info",
23
- levels: { debug: false, info: true, warn: true, error: true },
24
- debug: debugDisabled,
25
- info,
26
- warn,
27
- error,
28
- };
29
- }
30
- if (logLevel === LOG_LEVEL_WARN) {
31
- return {
32
- level: "warn",
33
- levels: { debug: false, info: false, warn: true, error: true },
34
- debug: debugDisabled,
35
- info: infoDisabled,
36
- warn,
37
- error,
38
- };
39
- }
40
- if (logLevel === LOG_LEVEL_ERROR) {
41
- return {
42
- level: "error",
43
- levels: { debug: false, info: false, warn: false, error: true },
44
- debug: debugDisabled,
45
- info: infoDisabled,
46
- warn: warnDisabled,
47
- error,
48
- };
49
- }
50
- if (logLevel === LOG_LEVEL_OFF) {
51
- return {
52
- level: "off",
53
- levels: { debug: false, info: false, warn: false, error: false },
54
- debug: debugDisabled,
55
- info: infoDisabled,
56
- warn: warnDisabled,
57
- error: errorDisabled,
58
- };
59
- }
60
- throw new Error(`unexpected logLevel.
61
- --- logLevel ---
62
- ${logLevel}
63
- --- allowed log levels ---
64
- ${LOG_LEVEL_OFF}
65
- ${LOG_LEVEL_ERROR}
66
- ${LOG_LEVEL_WARN}
67
- ${LOG_LEVEL_INFO}
68
- ${LOG_LEVEL_DEBUG}`);
69
- };
70
-
71
- const debug = (...args) => console.debug(...args);
72
-
73
- const debugDisabled = () => {};
74
-
75
- const info = (...args) => console.info(...args);
76
-
77
- const infoDisabled = () => {};
78
-
79
- const warn = (...args) => console.warn(...args);
80
-
81
- const warnDisabled = () => {};
82
-
83
- const error = (...args) => console.error(...args);
84
-
85
- const errorDisabled = () => {};
@@ -1,50 +0,0 @@
1
- import stripAnsi from "strip-ansi";
2
- import { ANSI } from "../ansi/ansi_node.js";
3
-
4
- export const renderBigSection = (params) => {
5
- return renderSection({
6
- width: 45,
7
- ...params,
8
- });
9
- };
10
-
11
- export const renderSection = ({
12
- title,
13
- content,
14
- dashColor = ANSI.GREY,
15
- width = 38,
16
- bottomSeparator = true,
17
- }) => {
18
- let section = "";
19
-
20
- if (title) {
21
- const titleWidth = stripAnsi(title).length;
22
- const minWidthRequired = `--- … ---`.length;
23
- const needsTruncate = titleWidth + minWidthRequired >= width;
24
- if (needsTruncate) {
25
- const titleTruncated = title.slice(0, width - minWidthRequired);
26
- const leftDashes = ANSI.color("---", dashColor);
27
- const rightDashes = ANSI.color("---", dashColor);
28
- section += `${leftDashes} ${titleTruncated}… ${rightDashes}`;
29
- } else {
30
- const remainingWidth = width - titleWidth - 2; // 2 for spaces around the title
31
- const dashLeftCount = Math.floor(remainingWidth / 2);
32
- const dashRightCount = remainingWidth - dashLeftCount;
33
- const leftDashes = ANSI.color("-".repeat(dashLeftCount), dashColor);
34
- const rightDashes = ANSI.color("-".repeat(dashRightCount), dashColor);
35
- section += `${leftDashes} ${title} ${rightDashes}`;
36
- }
37
- section += "\n";
38
- } else {
39
- const topDashes = ANSI.color(`-`.repeat(width), dashColor);
40
- section += topDashes;
41
- section += "\n";
42
- }
43
- section += `${content}`;
44
- if (bottomSeparator) {
45
- section += "\n";
46
- const bottomDashes = ANSI.color(`-`.repeat(width), dashColor);
47
- section += bottomDashes;
48
- }
49
- return section;
50
- };
@@ -1,72 +0,0 @@
1
- import { ANSI } from "../ansi/ansi_node.js";
2
-
3
- export const startSpinner = ({
4
- dynamicLog,
5
- frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
6
- fps = 20,
7
- keepProcessAlive = false,
8
- stopOnWriteFromOutside = true,
9
- stopOnVerticalOverflow = true,
10
- render = () => "",
11
- effect = () => {},
12
- animated = dynamicLog.stream.isTTY,
13
- }) => {
14
- let frameIndex = 0;
15
- let interval;
16
- let running = true;
17
-
18
- const spinner = {
19
- message: undefined,
20
- };
21
-
22
- const update = (message) => {
23
- spinner.message = running
24
- ? `${frames[frameIndex]} ${message}\n`
25
- : `${message}\n`;
26
- return spinner.message;
27
- };
28
- spinner.update = update;
29
-
30
- let cleanup;
31
- if (animated && ANSI.supported) {
32
- running = true;
33
- cleanup = effect();
34
- dynamicLog.update(update(render()));
35
-
36
- interval = setInterval(() => {
37
- frameIndex = frameIndex === frames.length - 1 ? 0 : frameIndex + 1;
38
- dynamicLog.update(update(render()));
39
- }, 1000 / fps);
40
- if (!keepProcessAlive) {
41
- interval.unref();
42
- }
43
- } else {
44
- dynamicLog.update(update(render()));
45
- }
46
-
47
- const stop = (message) => {
48
- running = false;
49
- if (interval) {
50
- clearInterval(interval);
51
- interval = null;
52
- }
53
- if (cleanup) {
54
- cleanup();
55
- cleanup = null;
56
- }
57
- if (dynamicLog && message) {
58
- dynamicLog.update(update(message));
59
- dynamicLog = null;
60
- }
61
- };
62
- spinner.stop = stop;
63
-
64
- if (stopOnVerticalOverflow) {
65
- dynamicLog.onVerticalOverflow = stop;
66
- }
67
- if (stopOnWriteFromOutside) {
68
- dynamicLog.onWriteFromOutside = stop;
69
- }
70
-
71
- return spinner;
72
- };
@@ -1,49 +0,0 @@
1
- import { humanizeDuration } from "../time/time.js";
2
- import { UNICODE } from "../unicode/unicode_node.js";
3
- import { createDynamicLog } from "./dynamic_log.js";
4
- import { startSpinner } from "./spinner.js";
5
-
6
- export const createTaskLog = (
7
- label,
8
- { disabled = false, animated = true, stopOnWriteFromOutside } = {},
9
- ) => {
10
- if (disabled) {
11
- return {
12
- setRightText: () => {},
13
- done: () => {},
14
- happen: () => {},
15
- fail: () => {},
16
- };
17
- }
18
- if (animated && process.env.CAPTURING_SIDE_EFFECTS) {
19
- animated = false;
20
- }
21
- const startMs = Date.now();
22
- const dynamicLog = createDynamicLog();
23
- let message = label;
24
- const taskSpinner = startSpinner({
25
- dynamicLog,
26
- render: () => message,
27
- stopOnWriteFromOutside,
28
- animated,
29
- });
30
- return {
31
- setRightText: (value) => {
32
- message = `${label} ${value}`;
33
- },
34
- done: () => {
35
- const msEllapsed = Date.now() - startMs;
36
- taskSpinner.stop(
37
- `${UNICODE.OK} ${label} (done in ${humanizeDuration(msEllapsed)})`,
38
- );
39
- },
40
- happen: (message) => {
41
- taskSpinner.stop(
42
- `${UNICODE.INFO} ${message} (at ${new Date().toLocaleTimeString()})`,
43
- );
44
- },
45
- fail: (message = `failed to ${label}`) => {
46
- taskSpinner.stop(`${UNICODE.FAILURE} ${message}`);
47
- },
48
- };
49
- };
@@ -1,13 +0,0 @@
1
- // tslint:disable:ordered-imports
2
-
3
- export { createDetailedMessage } from "./log/detailed_message.js";
4
- export { ANSI } from "./ansi/ansi_browser.js";
5
- export { UNICODE } from "./unicode/unicode_browser.js";
6
- export { humanize, humanizeMethodSymbol } from "./js_value/humanize.js";
7
- export { humanizeDuration, humanizeEllapsedTime } from "./time/time.js";
8
- export { humanizeFileSize, humanizeMemory } from "./byte/byte.js";
9
- export { distributePercentages } from "./percentage/distribute_percentages.js";
10
- export { generateContentFrame } from "./content_frame/content_frame.js";
11
- export { createCallOrderer } from "./log/call_orderer.js";
12
- export { errorToMarkdown } from "./error/error_to_markdown.js";
13
- export { errorToHTML } from "./error/error_to_html.js";
package/src/main_node.js DELETED
@@ -1,25 +0,0 @@
1
- // tslint:disable:ordered-imports
2
-
3
- export {
4
- createDetailedMessage,
5
- renderNamedSections,
6
- } from "./log/detailed_message.js";
7
- export { ANSI } from "./ansi/ansi_node.js";
8
- export { UNICODE } from "./unicode/unicode_node.js";
9
- export { humanize, humanizeMethodSymbol } from "./js_value/humanize.js";
10
- export { humanizeDuration, humanizeEllapsedTime } from "./time/time.js";
11
- export { humanizeFileSize, humanizeMemory } from "./byte/byte.js";
12
- export { distributePercentages } from "./percentage/distribute_percentages.js";
13
- export { generateContentFrame } from "./content_frame/content_frame.js";
14
- export { createCallOrderer } from "./log/call_orderer.js";
15
- export { errorToMarkdown } from "./error/error_to_markdown.js";
16
- export { errorToHTML } from "./error/error_to_html.js";
17
- export { formatError } from "./error/format_error.js";
18
-
19
- // node only
20
- export { renderSection, renderBigSection } from "./log/section.js";
21
- export { renderDetails } from "./log/details.js";
22
- export { createLogger } from "./log/logger.js";
23
- export { createDynamicLog } from "./log/dynamic_log.js";
24
- export { startSpinner } from "./log/spinner.js";
25
- export { createTaskLog } from "./log/task_log.js";
@@ -1,53 +0,0 @@
1
- import { getPrecision, setRoundedPrecision } from "../utils/decimals.js";
2
-
3
- export const distributePercentages = (
4
- namedNumbers,
5
- { maxPrecisionHint = 2 } = {},
6
- ) => {
7
- const numberNames = Object.keys(namedNumbers);
8
- if (numberNames.length === 0) {
9
- return {};
10
- }
11
- if (numberNames.length === 1) {
12
- const firstNumberName = numberNames[0];
13
- return { [firstNumberName]: "100 %" };
14
- }
15
- const numbers = numberNames.map((name) => namedNumbers[name]);
16
- const total = numbers.reduce((sum, value) => sum + value, 0);
17
- const ratios = numbers.map((number) => number / total);
18
- const percentages = {};
19
- ratios.pop();
20
- ratios.forEach((ratio, index) => {
21
- const percentage = ratio * 100;
22
- percentages[numberNames[index]] = percentage;
23
- });
24
- const lowestPercentage = (1 / Math.pow(10, maxPrecisionHint)) * 100;
25
- let precision = 0;
26
- Object.keys(percentages).forEach((name) => {
27
- const percentage = percentages[name];
28
- if (percentage < lowestPercentage) {
29
- // check the amout of meaningful decimals
30
- // and that what we will use
31
- const percentageRounded = setRoundedPrecision(percentage);
32
- const percentagePrecision = getPrecision(percentageRounded);
33
- if (percentagePrecision > precision) {
34
- precision = percentagePrecision;
35
- }
36
- }
37
- });
38
- let remainingPercentage = 100;
39
-
40
- Object.keys(percentages).forEach((name) => {
41
- const percentage = percentages[name];
42
- const percentageAllocated = setRoundedPrecision(percentage, {
43
- decimals: precision,
44
- });
45
- remainingPercentage -= percentageAllocated;
46
- percentages[name] = percentageAllocated;
47
- });
48
- const lastName = numberNames[numberNames.length - 1];
49
- percentages[lastName] = setRoundedPrecision(remainingPercentage, {
50
- decimals: precision,
51
- });
52
- return percentages;
53
- };
package/src/time/time.js DELETED
@@ -1,151 +0,0 @@
1
- import { setPrecision, setRoundedPrecision } from "../utils/decimals.js";
2
-
3
- export const humanizeEllapsedTime = (ms, { short } = {}) => {
4
- if (ms < 1000) {
5
- return short ? "0s" : "0 second";
6
- }
7
- const { primary, remaining } = parseMs(ms);
8
- if (!remaining) {
9
- return inspectEllapsedUnit(primary, short);
10
- }
11
- return `${inspectEllapsedUnit(primary, short)} and ${inspectEllapsedUnit(
12
- remaining,
13
- short,
14
- )}`;
15
- };
16
- const inspectEllapsedUnit = (unit, short) => {
17
- const count =
18
- unit.name === "second" ? Math.floor(unit.count) : Math.round(unit.count);
19
- let name = unit.name;
20
- if (short) {
21
- name = unitShort[name];
22
- if (count <= 1) {
23
- return `${count}${name}`;
24
- }
25
- return `${count}${name}s`;
26
- }
27
- if (count <= 1) {
28
- return `${count} ${name}`;
29
- }
30
- return `${count} ${name}s`;
31
- };
32
- const unitShort = {
33
- year: "y",
34
- month: "m",
35
- week: "w",
36
- day: "d",
37
- hour: "h",
38
- minute: "m",
39
- second: "s",
40
- };
41
-
42
- export const humanizeDuration = (
43
- ms,
44
- { short, rounded = true, decimals } = {},
45
- ) => {
46
- // ignore ms below meaningfulMs so that:
47
- // humanizeDuration(0.5) -> "0 second"
48
- // humanizeDuration(1.1) -> "0.001 second" (and not "0.0011 second")
49
- // This tool is meant to be read by humans and it would be barely readable to see
50
- // "0.0001 second" (stands for 0.1 millisecond)
51
- // yes we could return "0.1 millisecond" but we choosed consistency over precision
52
- // so that the prefered unit is "second" (and does not become millisecond when ms is super small)
53
- if (ms < 1) {
54
- return short ? "0s" : "0 second";
55
- }
56
- const { primary, remaining } = parseMs(ms);
57
- if (!remaining) {
58
- return humanizeDurationUnit(primary, {
59
- decimals:
60
- decimals === undefined ? (primary.name === "second" ? 1 : 0) : decimals,
61
- short,
62
- rounded,
63
- });
64
- }
65
- return `${humanizeDurationUnit(primary, {
66
- decimals: decimals === undefined ? 0 : decimals,
67
- short,
68
- rounded,
69
- })} and ${humanizeDurationUnit(remaining, {
70
- decimals: decimals === undefined ? 0 : decimals,
71
- short,
72
- rounded,
73
- })}`;
74
- };
75
- const humanizeDurationUnit = (unit, { decimals, short, rounded }) => {
76
- const count = rounded
77
- ? setRoundedPrecision(unit.count, { decimals })
78
- : setPrecision(unit.count, { decimals });
79
- let name = unit.name;
80
- if (short) {
81
- name = unitShort[name];
82
- return `${count}${name}`;
83
- }
84
- if (count <= 1) {
85
- return `${count} ${name}`;
86
- }
87
- return `${count} ${name}s`;
88
- };
89
- const MS_PER_UNITS = {
90
- year: 31_557_600_000,
91
- month: 2_629_000_000,
92
- week: 604_800_000,
93
- day: 86_400_000,
94
- hour: 3_600_000,
95
- minute: 60_000,
96
- second: 1000,
97
- };
98
-
99
- const parseMs = (ms) => {
100
- const unitNames = Object.keys(MS_PER_UNITS);
101
- const smallestUnitName = unitNames[unitNames.length - 1];
102
- let firstUnitName = smallestUnitName;
103
- let firstUnitCount = ms / MS_PER_UNITS[smallestUnitName];
104
- const firstUnitIndex = unitNames.findIndex((unitName) => {
105
- if (unitName === smallestUnitName) {
106
- return false;
107
- }
108
- const msPerUnit = MS_PER_UNITS[unitName];
109
- const unitCount = Math.floor(ms / msPerUnit);
110
- if (unitCount) {
111
- firstUnitName = unitName;
112
- firstUnitCount = unitCount;
113
- return true;
114
- }
115
- return false;
116
- });
117
- if (firstUnitName === smallestUnitName) {
118
- return {
119
- primary: {
120
- name: firstUnitName,
121
- count: firstUnitCount,
122
- },
123
- };
124
- }
125
- const remainingMs = ms - firstUnitCount * MS_PER_UNITS[firstUnitName];
126
- const remainingUnitName = unitNames[firstUnitIndex + 1];
127
- const remainingUnitCount = remainingMs / MS_PER_UNITS[remainingUnitName];
128
- // - 1 year and 1 second is too much information
129
- // so we don't check the remaining units
130
- // - 1 year and 0.0001 week is awful
131
- // hence the if below
132
- if (Math.round(remainingUnitCount) < 1) {
133
- return {
134
- primary: {
135
- name: firstUnitName,
136
- count: firstUnitCount,
137
- },
138
- };
139
- }
140
- // - 1 year and 1 month is great
141
- return {
142
- primary: {
143
- name: firstUnitName,
144
- count: firstUnitCount,
145
- },
146
- remaining: {
147
- name: remainingUnitName,
148
- count: remainingUnitCount,
149
- },
150
- };
151
- };
@@ -1,7 +0,0 @@
1
- import { ANSI } from "../ansi/ansi_browser.js";
2
- import { createUnicode } from "./unicode_runtime_agnostic.js";
3
-
4
- export const UNICODE = createUnicode({
5
- supported: true,
6
- ANSI,
7
- });