@jsenv/humanize 1.3.0 → 1.4.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.
@@ -1,27 +1,73 @@
1
1
  const createDetailedMessage = (message, details = {}) => {
2
- let string = "".concat(message);
3
- Object.keys(details).forEach(key => {
4
- const value = details[key];
5
- string += "\n--- ".concat(key, " ---\n").concat(Array.isArray(value) ? value.join("\n") : value);
6
- });
7
- return string;
2
+ let text = "".concat(message);
3
+ const namedSectionsText = renderNamedSections(details);
4
+ if (namedSectionsText) {
5
+ text += "\n".concat(namedSectionsText);
6
+ }
7
+ return text;
8
+ };
9
+ const renderNamedSections = namedSections => {
10
+ let text = "";
11
+ let keys = Object.keys(namedSections);
12
+ for (const key of keys) {
13
+ const isLastKey = key === keys[keys.length - 1];
14
+ const value = namedSections[key];
15
+ text += "--- ".concat(key, " ---\n").concat(Array.isArray(value) ? value.join("\n") : value);
16
+ if (!isLastKey) {
17
+ text += "\n";
18
+ }
19
+ }
20
+ return text;
8
21
  };
9
22
 
10
23
  // https://github.com/Marak/colors.js/blob/master/lib/styles.js
11
24
  // https://stackoverflow.com/a/75985833/2634179
12
25
  const RESET = "\x1b[0m";
26
+ const RED = "red";
27
+ const GREEN = "green";
28
+ const YELLOW = "yellow";
29
+ const BLUE = "blue";
30
+ const MAGENTA = "magenta";
31
+ const CYAN = "cyan";
32
+ const GREY = "grey";
33
+ const WHITE = "white";
34
+ const BLACK = "black";
35
+ const TEXT_COLOR_ANSI_CODES = {
36
+ [RED]: "\x1b[31m",
37
+ [GREEN]: "\x1b[32m",
38
+ [YELLOW]: "\x1b[33m",
39
+ [BLUE]: "\x1b[34m",
40
+ [MAGENTA]: "\x1b[35m",
41
+ [CYAN]: "\x1b[36m",
42
+ [GREY]: "\x1b[90m",
43
+ [WHITE]: "\x1b[37m",
44
+ [BLACK]: "\x1b[30m"
45
+ };
46
+ const BACKGROUND_COLOR_ANSI_CODES = {
47
+ [RED]: "\x1b[41m",
48
+ [GREEN]: "\x1b[42m",
49
+ [YELLOW]: "\x1b[43m",
50
+ [BLUE]: "\x1b[44m",
51
+ [MAGENTA]: "\x1b[45m",
52
+ [CYAN]: "\x1b[46m",
53
+ [GREY]: "\x1b[100m",
54
+ [WHITE]: "\x1b[47m",
55
+ [BLACK]: "\x1b[40m"
56
+ };
13
57
  const createAnsi = ({
14
58
  supported
15
59
  }) => {
16
60
  const ANSI = {
17
61
  supported,
18
- RED: "\x1b[31m",
19
- GREEN: "\x1b[32m",
20
- YELLOW: "\x1b[33m",
21
- BLUE: "\x1b[34m",
22
- MAGENTA: "\x1b[35m",
23
- CYAN: "\x1b[36m",
24
- GREY: "\x1b[90m",
62
+ RED,
63
+ GREEN,
64
+ YELLOW,
65
+ BLUE,
66
+ MAGENTA,
67
+ CYAN,
68
+ GREY,
69
+ WHITE,
70
+ BLACK,
25
71
  color: (text, color) => {
26
72
  if (!ANSI.supported) {
27
73
  return text;
@@ -33,7 +79,28 @@ const createAnsi = ({
33
79
  // cannot set color of blank chars
34
80
  return text;
35
81
  }
36
- return "".concat(color).concat(text).concat(RESET);
82
+ const ansiEscapeCodeForTextColor = TEXT_COLOR_ANSI_CODES[color];
83
+ if (!ansiEscapeCodeForTextColor) {
84
+ return text;
85
+ }
86
+ return "".concat(ansiEscapeCodeForTextColor).concat(text).concat(RESET);
87
+ },
88
+ backgroundColor: (text, color) => {
89
+ if (!ANSI.supported) {
90
+ return text;
91
+ }
92
+ if (!color) {
93
+ return text;
94
+ }
95
+ if (typeof text === "string" && text.trim() === "") {
96
+ // cannot set background color of blank chars
97
+ return text;
98
+ }
99
+ const ansiEscapeCodeForBackgroundColor = BACKGROUND_COLOR_ANSI_CODES[color];
100
+ if (!ansiEscapeCodeForBackgroundColor) {
101
+ return text;
102
+ }
103
+ return "".concat(ansiEscapeCodeForBackgroundColor).concat(text).concat(RESET);
37
104
  },
38
105
  BOLD: "\x1b[1m",
39
106
  UNDERLINE: "\x1b[4m",
@@ -49,7 +116,8 @@ const createAnsi = ({
49
116
  if (text === "") {
50
117
  return text;
51
118
  }
52
- return "".concat(effect).concat(text).concat(RESET);
119
+ const ansiEscapeCodeForEffect = effect;
120
+ return "".concat(ansiEscapeCodeForEffect).concat(text).concat(RESET);
53
121
  }
54
122
  };
55
123
  return ANSI;
@@ -209,11 +277,11 @@ const groupDigits = (digits, {
209
277
  if (digitCount < minimumDigits) {
210
278
  return digits;
211
279
  }
212
- let digitsWithSeparator = digits.slice(-groupLength);
213
- let remainingDigits = digits.slice(0, -groupLength);
280
+ let digitsWithSeparator = digits.slice(-3);
281
+ let remainingDigits = digits.slice(0, -3);
214
282
  while (remainingDigits.length) {
215
- const group = remainingDigits.slice(-groupLength);
216
- remainingDigits = remainingDigits.slice(0, -groupLength);
283
+ const group = remainingDigits.slice(-3);
284
+ remainingDigits = remainingDigits.slice(0, -3);
217
285
  digitsWithSeparator = "".concat(group, "_").concat(digitsWithSeparator);
218
286
  }
219
287
  return digitsWithSeparator;
@@ -1272,4 +1340,43 @@ const fillLeft = (value, biggestValue, char = " ") => {
1272
1340
  return padded;
1273
1341
  };
1274
1342
 
1275
- export { ANSI, UNICODE, createDetailedMessage, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemory, humanizeMethodSymbol };
1343
+ const createCallOrderer = () => {
1344
+ const queue = [];
1345
+ const callWhenPreviousExecutionAreDone = (executionIndex, callback) => {
1346
+ if (queue[executionIndex]) {
1347
+ throw new Error("".concat(executionIndex, " already used"));
1348
+ }
1349
+ let allBeforeAreDone = true;
1350
+ if (executionIndex > 0) {
1351
+ let beforeIndex = executionIndex - 1;
1352
+ do {
1353
+ const value = queue[beforeIndex];
1354
+ if (!value) {
1355
+ allBeforeAreDone = false;
1356
+ break;
1357
+ }
1358
+ } while (beforeIndex--);
1359
+ }
1360
+ if (!allBeforeAreDone) {
1361
+ queue[executionIndex] = callback;
1362
+ return;
1363
+ }
1364
+ queue[executionIndex] = true;
1365
+ callback();
1366
+ let afterIndex = executionIndex + 1;
1367
+ while (afterIndex < queue.length) {
1368
+ const value = queue[afterIndex];
1369
+ if (value === undefined) {
1370
+ break;
1371
+ }
1372
+ if (typeof value === "function") {
1373
+ queue[afterIndex] = true;
1374
+ value();
1375
+ }
1376
+ afterIndex++;
1377
+ }
1378
+ };
1379
+ return callWhenPreviousExecutionAreDone;
1380
+ };
1381
+
1382
+ export { ANSI, UNICODE, createCallOrderer, createDetailedMessage, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemory, humanizeMethodSymbol };
@@ -1,42 +1,89 @@
1
- import { createSupportsColor, isUnicodeSupported, clearTerminal, eraseLines } from "./js/vendors.js";
2
- import stringWidth from "string-width";
1
+ import { createSupportsColor, isUnicodeSupported, stripAnsi, emojiRegex, eastAsianWidth, clearTerminal, eraseLines } from "./jsenv_humanize_node_modules.js";
2
+ import { stripVTControlCharacters } from "node:util";
3
3
  import "node:process";
4
4
  import "node:os";
5
5
  import "node:tty";
6
6
 
7
7
  const createDetailedMessage = (message, details = {}) => {
8
- let string = `${message}`;
8
+ let text = `${message}`;
9
+ const namedSectionsText = renderNamedSections(details);
10
+ if (namedSectionsText) {
11
+ text += `
12
+ ${namedSectionsText}`;
13
+ }
14
+ return text;
15
+ };
9
16
 
10
- Object.keys(details).forEach((key) => {
11
- const value = details[key];
12
- string += `
13
- --- ${key} ---
17
+ const renderNamedSections = (namedSections) => {
18
+ let text = "";
19
+ let keys = Object.keys(namedSections);
20
+ for (const key of keys) {
21
+ const isLastKey = key === keys[keys.length - 1];
22
+ const value = namedSections[key];
23
+ text += `--- ${key} ---
14
24
  ${
15
25
  Array.isArray(value)
16
26
  ? value.join(`
17
27
  `)
18
28
  : value
19
29
  }`;
20
- });
21
-
22
- return string;
30
+ if (!isLastKey) {
31
+ text += "\n";
32
+ }
33
+ }
34
+ return text;
23
35
  };
24
36
 
25
37
  // https://github.com/Marak/colors.js/blob/master/lib/styles.js
26
38
  // https://stackoverflow.com/a/75985833/2634179
27
39
  const RESET = "\x1b[0m";
28
40
 
41
+ const RED = "red";
42
+ const GREEN = "green";
43
+ const YELLOW = "yellow";
44
+ const BLUE = "blue";
45
+ const MAGENTA = "magenta";
46
+ const CYAN = "cyan";
47
+ const GREY = "grey";
48
+ const WHITE = "white";
49
+ const BLACK = "black";
50
+
51
+ const TEXT_COLOR_ANSI_CODES = {
52
+ [RED]: "\x1b[31m",
53
+ [GREEN]: "\x1b[32m",
54
+ [YELLOW]: "\x1b[33m",
55
+ [BLUE]: "\x1b[34m",
56
+ [MAGENTA]: "\x1b[35m",
57
+ [CYAN]: "\x1b[36m",
58
+ [GREY]: "\x1b[90m",
59
+ [WHITE]: "\x1b[37m",
60
+ [BLACK]: "\x1b[30m",
61
+ };
62
+ const BACKGROUND_COLOR_ANSI_CODES = {
63
+ [RED]: "\x1b[41m",
64
+ [GREEN]: "\x1b[42m",
65
+ [YELLOW]: "\x1b[43m",
66
+ [BLUE]: "\x1b[44m",
67
+ [MAGENTA]: "\x1b[45m",
68
+ [CYAN]: "\x1b[46m",
69
+ [GREY]: "\x1b[100m",
70
+ [WHITE]: "\x1b[47m",
71
+ [BLACK]: "\x1b[40m",
72
+ };
73
+
29
74
  const createAnsi = ({ supported }) => {
30
75
  const ANSI = {
31
76
  supported,
32
77
 
33
- RED: "\x1b[31m",
34
- GREEN: "\x1b[32m",
35
- YELLOW: "\x1b[33m",
36
- BLUE: "\x1b[34m",
37
- MAGENTA: "\x1b[35m",
38
- CYAN: "\x1b[36m",
39
- GREY: "\x1b[90m",
78
+ RED,
79
+ GREEN,
80
+ YELLOW,
81
+ BLUE,
82
+ MAGENTA,
83
+ CYAN,
84
+ GREY,
85
+ WHITE,
86
+ BLACK,
40
87
  color: (text, color) => {
41
88
  if (!ANSI.supported) {
42
89
  return text;
@@ -48,7 +95,29 @@ const createAnsi = ({ supported }) => {
48
95
  // cannot set color of blank chars
49
96
  return text;
50
97
  }
51
- return `${color}${text}${RESET}`;
98
+ const ansiEscapeCodeForTextColor = TEXT_COLOR_ANSI_CODES[color];
99
+ if (!ansiEscapeCodeForTextColor) {
100
+ return text;
101
+ }
102
+ return `${ansiEscapeCodeForTextColor}${text}${RESET}`;
103
+ },
104
+ backgroundColor: (text, color) => {
105
+ if (!ANSI.supported) {
106
+ return text;
107
+ }
108
+ if (!color) {
109
+ return text;
110
+ }
111
+ if (typeof text === "string" && text.trim() === "") {
112
+ // cannot set background color of blank chars
113
+ return text;
114
+ }
115
+ const ansiEscapeCodeForBackgroundColor =
116
+ BACKGROUND_COLOR_ANSI_CODES[color];
117
+ if (!ansiEscapeCodeForBackgroundColor) {
118
+ return text;
119
+ }
120
+ return `${ansiEscapeCodeForBackgroundColor}${text}${RESET}`;
52
121
  },
53
122
 
54
123
  BOLD: "\x1b[1m",
@@ -65,7 +134,8 @@ const createAnsi = ({ supported }) => {
65
134
  if (text === "") {
66
135
  return text;
67
136
  }
68
- return `${effect}${text}${RESET}`;
137
+ const ansiEscapeCodeForEffect = effect;
138
+ return `${ansiEscapeCodeForEffect}${text}${RESET}`;
69
139
  },
70
140
  };
71
141
 
@@ -227,11 +297,11 @@ const groupDigits = (digits, { minimumDigits, groupLength }) => {
227
297
  return digits;
228
298
  }
229
299
 
230
- let digitsWithSeparator = digits.slice(-groupLength);
231
- let remainingDigits = digits.slice(0, -groupLength);
300
+ let digitsWithSeparator = digits.slice(-3);
301
+ let remainingDigits = digits.slice(0, -3);
232
302
  while (remainingDigits.length) {
233
- const group = remainingDigits.slice(-groupLength);
234
- remainingDigits = remainingDigits.slice(0, -groupLength);
303
+ const group = remainingDigits.slice(-3);
304
+ remainingDigits = remainingDigits.slice(0, -3);
235
305
  digitsWithSeparator = `${group}_${digitsWithSeparator}`;
236
306
  }
237
307
  return digitsWithSeparator;
@@ -1297,6 +1367,119 @@ const fillLeft = (value, biggestValue, char = " ") => {
1297
1367
  return padded;
1298
1368
  };
1299
1369
 
1370
+ const createCallOrderer = () => {
1371
+ const queue = [];
1372
+ const callWhenPreviousExecutionAreDone = (executionIndex, callback) => {
1373
+ if (queue[executionIndex]) {
1374
+ throw new Error(`${executionIndex} already used`);
1375
+ }
1376
+
1377
+ let allBeforeAreDone = true;
1378
+ if (executionIndex > 0) {
1379
+ let beforeIndex = executionIndex - 1;
1380
+ do {
1381
+ const value = queue[beforeIndex];
1382
+ if (!value) {
1383
+ allBeforeAreDone = false;
1384
+ break;
1385
+ }
1386
+ } while (beforeIndex--);
1387
+ }
1388
+ if (!allBeforeAreDone) {
1389
+ queue[executionIndex] = callback;
1390
+ return;
1391
+ }
1392
+ queue[executionIndex] = true;
1393
+ callback();
1394
+ let afterIndex = executionIndex + 1;
1395
+ while (afterIndex < queue.length) {
1396
+ const value = queue[afterIndex];
1397
+ if (value === undefined) {
1398
+ break;
1399
+ }
1400
+ if (typeof value === "function") {
1401
+ queue[afterIndex] = true;
1402
+ value();
1403
+ }
1404
+ afterIndex++;
1405
+ }
1406
+ };
1407
+ return callWhenPreviousExecutionAreDone;
1408
+ };
1409
+
1410
+ const renderBigSection = (params) => {
1411
+ return renderSection({
1412
+ width: 45,
1413
+ ...params,
1414
+ });
1415
+ };
1416
+
1417
+ const renderSection = ({
1418
+ title,
1419
+ content,
1420
+ dashColor = ANSI.GREY,
1421
+ width = 38,
1422
+ bottomSeparator = true,
1423
+ }) => {
1424
+ let section = "";
1425
+
1426
+ if (title) {
1427
+ const titleWidth = stripAnsi(title).length;
1428
+ const minWidthRequired = `--- … ---`.length;
1429
+ const needsTruncate = titleWidth + minWidthRequired >= width;
1430
+ if (needsTruncate) {
1431
+ const titleTruncated = title.slice(0, width - minWidthRequired);
1432
+ const leftDashes = ANSI.color("---", dashColor);
1433
+ const rightDashes = ANSI.color("---", dashColor);
1434
+ section += `${leftDashes} ${titleTruncated}… ${rightDashes}`;
1435
+ } else {
1436
+ const remainingWidth = width - titleWidth - 2; // 2 for spaces around the title
1437
+ const dashLeftCount = Math.floor(remainingWidth / 2);
1438
+ const dashRightCount = remainingWidth - dashLeftCount;
1439
+ const leftDashes = ANSI.color("-".repeat(dashLeftCount), dashColor);
1440
+ const rightDashes = ANSI.color("-".repeat(dashRightCount), dashColor);
1441
+ section += `${leftDashes} ${title} ${rightDashes}`;
1442
+ }
1443
+ section += "\n";
1444
+ } else {
1445
+ const topDashes = ANSI.color(`-`.repeat(width), dashColor);
1446
+ section += topDashes;
1447
+ section += "\n";
1448
+ }
1449
+ section += `${content}`;
1450
+ if (bottomSeparator) {
1451
+ section += "\n";
1452
+ const bottomDashes = ANSI.color(`-`.repeat(width), dashColor);
1453
+ section += bottomDashes;
1454
+ }
1455
+ return section;
1456
+ };
1457
+
1458
+ const renderDetails = (data) => {
1459
+ const details = [];
1460
+ for (const key of Object.keys(data)) {
1461
+ const value = data[key];
1462
+ let valueString = "";
1463
+ valueString += ANSI.color(`${key}:`, ANSI.GREY);
1464
+ const useNonGreyAnsiColor =
1465
+ typeof value === "string" && value.includes("\x1b");
1466
+ valueString += " ";
1467
+ valueString += useNonGreyAnsiColor
1468
+ ? value
1469
+ : ANSI.color(String(value), ANSI.GREY);
1470
+ details.push(valueString);
1471
+ }
1472
+ if (details.length === 0) {
1473
+ return "";
1474
+ }
1475
+
1476
+ let string = "";
1477
+ string += ` ${ANSI.color("(", ANSI.GREY)}`;
1478
+ string += details.join(ANSI.color(", ", ANSI.GREY));
1479
+ string += ANSI.color(")", ANSI.GREY);
1480
+ return string;
1481
+ };
1482
+
1300
1483
  const LOG_LEVEL_OFF = "off";
1301
1484
 
1302
1485
  const LOG_LEVEL_DEBUG = "debug";
@@ -1385,6 +1568,101 @@ const error = (...args) => console.error(...args);
1385
1568
 
1386
1569
  const errorDisabled = () => {};
1387
1570
 
1571
+ const createMeasureTextWidth = ({ stripAnsi }) => {
1572
+ const segmenter = new Intl.Segmenter();
1573
+ const defaultIgnorableCodePointRegex = /^\p{Default_Ignorable_Code_Point}$/u;
1574
+
1575
+ const measureTextWidth = (
1576
+ string,
1577
+ {
1578
+ ambiguousIsNarrow = true,
1579
+ countAnsiEscapeCodes = false,
1580
+ skipEmojis = false,
1581
+ } = {},
1582
+ ) => {
1583
+ if (typeof string !== "string" || string.length === 0) {
1584
+ return 0;
1585
+ }
1586
+
1587
+ if (!countAnsiEscapeCodes) {
1588
+ string = stripAnsi(string);
1589
+ }
1590
+
1591
+ if (string.length === 0) {
1592
+ return 0;
1593
+ }
1594
+
1595
+ let width = 0;
1596
+ const eastAsianWidthOptions = { ambiguousAsWide: !ambiguousIsNarrow };
1597
+
1598
+ for (const { segment: character } of segmenter.segment(string)) {
1599
+ const codePoint = character.codePointAt(0);
1600
+
1601
+ // Ignore control characters
1602
+ if (codePoint <= 0x1f || (codePoint >= 0x7f && codePoint <= 0x9f)) {
1603
+ continue;
1604
+ }
1605
+
1606
+ // Ignore zero-width characters
1607
+ if (
1608
+ (codePoint >= 0x20_0b && codePoint <= 0x20_0f) || // Zero-width space, non-joiner, joiner, left-to-right mark, right-to-left mark
1609
+ codePoint === 0xfe_ff // Zero-width no-break space
1610
+ ) {
1611
+ continue;
1612
+ }
1613
+
1614
+ // Ignore combining characters
1615
+ if (
1616
+ (codePoint >= 0x3_00 && codePoint <= 0x3_6f) || // Combining diacritical marks
1617
+ (codePoint >= 0x1a_b0 && codePoint <= 0x1a_ff) || // Combining diacritical marks extended
1618
+ (codePoint >= 0x1d_c0 && codePoint <= 0x1d_ff) || // Combining diacritical marks supplement
1619
+ (codePoint >= 0x20_d0 && codePoint <= 0x20_ff) || // Combining diacritical marks for symbols
1620
+ (codePoint >= 0xfe_20 && codePoint <= 0xfe_2f) // Combining half marks
1621
+ ) {
1622
+ continue;
1623
+ }
1624
+
1625
+ // Ignore surrogate pairs
1626
+ if (codePoint >= 0xd8_00 && codePoint <= 0xdf_ff) {
1627
+ continue;
1628
+ }
1629
+
1630
+ // Ignore variation selectors
1631
+ if (codePoint >= 0xfe_00 && codePoint <= 0xfe_0f) {
1632
+ continue;
1633
+ }
1634
+
1635
+ // This covers some of the above cases, but we still keep them for performance reasons.
1636
+ if (defaultIgnorableCodePointRegex.test(character)) {
1637
+ continue;
1638
+ }
1639
+
1640
+ if (!skipEmojis && emojiRegex().test(character)) {
1641
+ if (process.env.CAPTURING_SIDE_EFFECTS) {
1642
+ if (character === "✔️") {
1643
+ width += 2;
1644
+ continue;
1645
+ }
1646
+ }
1647
+ width += measureTextWidth(character, {
1648
+ skipEmojis: true,
1649
+ countAnsiEscapeCodes: true, // to skip call to stripAnsi
1650
+ });
1651
+ continue;
1652
+ }
1653
+
1654
+ width += eastAsianWidth(codePoint, eastAsianWidthOptions);
1655
+ }
1656
+
1657
+ return width;
1658
+ };
1659
+ return measureTextWidth;
1660
+ };
1661
+
1662
+ const measureTextWidth = createMeasureTextWidth({
1663
+ stripAnsi: stripVTControlCharacters,
1664
+ });
1665
+
1388
1666
  /*
1389
1667
  * see also https://github.com/vadimdemedes/ink
1390
1668
  */
@@ -1420,7 +1698,7 @@ const createDynamicLog = ({
1420
1698
  const logLines = lastOutput.split(/\r\n|\r|\n/);
1421
1699
  let visualLineCount = 0;
1422
1700
  for (const logLine of logLines) {
1423
- const width = stringWidth(logLine);
1701
+ const width = measureTextWidth(logLine);
1424
1702
  if (width === 0) {
1425
1703
  visualLineCount++;
1426
1704
  } else {
@@ -1485,7 +1763,8 @@ const createDynamicLog = ({
1485
1763
  update("");
1486
1764
 
1487
1765
  writing = true;
1488
- callback();
1766
+ callback(update);
1767
+ lastOutput = "";
1489
1768
  writing = false;
1490
1769
 
1491
1770
  update(ouputAfterCallback);
@@ -1685,4 +1964,4 @@ const createTaskLog = (
1685
1964
  };
1686
1965
  };
1687
1966
 
1688
- export { ANSI, UNICODE, createDetailedMessage, createDynamicLog, createLogger, createTaskLog, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemory, humanizeMethodSymbol, startSpinner };
1967
+ export { ANSI, UNICODE, createCallOrderer, createDetailedMessage, createDynamicLog, createLogger, createTaskLog, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemory, humanizeMethodSymbol, renderBigSection, renderDetails, renderNamedSections, renderSection, startSpinner };