@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,1157 @@
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;
8
+ };
9
+
10
+ const inspectBoolean = value => value.toString();
11
+
12
+ const inspectNull = () => "null";
13
+
14
+ // https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/rules/numeric-separators-style.js
15
+
16
+ const inspectNumber = (value, {
17
+ numericSeparator
18
+ }) => {
19
+ if (isNegativeZero(value)) {
20
+ return "-0";
21
+ }
22
+ // isNaN
23
+ // eslint-disable-next-line no-self-compare
24
+ if (value !== value) {
25
+ return "NaN";
26
+ }
27
+ if (value === Infinity) {
28
+ return "Infinity";
29
+ }
30
+ if (value === -Infinity) {
31
+ return "-Infinity";
32
+ }
33
+ const numberString = String(value);
34
+ if (!numericSeparator) {
35
+ return numberString;
36
+ }
37
+ const {
38
+ number,
39
+ mark = "",
40
+ sign = "",
41
+ power = ""
42
+ } = numberString.match(/^(?<number>.*?)(?:(?<mark>e)(?<sign>[+-])?(?<power>\d+))?$/i).groups;
43
+ const numberWithSeparators = formatNumber(number);
44
+ const powerWithSeparators = addSeparator(power, {
45
+ minimumDigits: 5,
46
+ groupLength: 3
47
+ });
48
+ return "".concat(numberWithSeparators).concat(mark).concat(sign).concat(powerWithSeparators);
49
+ };
50
+
51
+ // Use this and instead of Object.is(value, -0)
52
+ // because in some corner cases firefox returns false
53
+ // for Object.is(-0, -0)
54
+ const isNegativeZero = value => {
55
+ return value === 0 && 1 / value === -Infinity;
56
+ };
57
+ const formatNumber = numberString => {
58
+ const parts = numberString.split(".");
59
+ const [integer, fractional] = parts;
60
+ if (parts.length === 2) {
61
+ const integerWithSeparators = addSeparator(integer, {
62
+ minimumDigits: 5,
63
+ groupLength: 3
64
+ });
65
+ return "".concat(integerWithSeparators, ".").concat(fractional);
66
+ }
67
+ return addSeparator(integer, {
68
+ minimumDigits: 5,
69
+ groupLength: 3
70
+ });
71
+ };
72
+ const addSeparator = (numberString, {
73
+ minimumDigits,
74
+ groupLength
75
+ }) => {
76
+ if (numberString[0] === "-") {
77
+ return "-".concat(groupDigits(numberString.slice(1), {
78
+ minimumDigits,
79
+ groupLength
80
+ }));
81
+ }
82
+ return groupDigits(numberString, {
83
+ minimumDigits,
84
+ groupLength
85
+ });
86
+ };
87
+ const groupDigits = (digits, {
88
+ minimumDigits,
89
+ groupLength
90
+ }) => {
91
+ const digitCount = digits.length;
92
+ if (digitCount < minimumDigits) {
93
+ return digits;
94
+ }
95
+ let digitsWithSeparator = digits.slice(-groupLength);
96
+ let remainingDigits = digits.slice(0, -groupLength);
97
+ while (remainingDigits.length) {
98
+ const group = remainingDigits.slice(-groupLength);
99
+ remainingDigits = remainingDigits.slice(0, -groupLength);
100
+ digitsWithSeparator = "".concat(group, "_").concat(digitsWithSeparator);
101
+ }
102
+ return digitsWithSeparator;
103
+ };
104
+
105
+ // const addSeparatorFromLeft = (value, { minimumDigits, groupLength }) => {
106
+ // const { length } = value;
107
+ // if (length < minimumDigits) {
108
+ // return value;
109
+ // }
110
+
111
+ // const parts = [];
112
+ // for (let start = 0; start < length; start += groupLength) {
113
+ // const end = Math.min(start + groupLength, length);
114
+ // parts.push(value.slice(start, end));
115
+ // }
116
+ // return parts.join("_");
117
+ // };
118
+
119
+ const DOUBLE_QUOTE = "\"";
120
+ const SINGLE_QUOTE = "'";
121
+ const BACKTICK = "`";
122
+ const inspectString = (value, {
123
+ quote = "auto",
124
+ canUseTemplateString = false,
125
+ preserveLineBreaks = false,
126
+ quoteDefault = DOUBLE_QUOTE
127
+ } = {}) => {
128
+ quote = quote === "auto" ? determineQuote(value, {
129
+ canUseTemplateString,
130
+ quoteDefault
131
+ }) : quote;
132
+ if (quote === BACKTICK) {
133
+ return "`".concat(escapeTemplateStringSpecialCharacters(value), "`");
134
+ }
135
+ return surroundStringWith(value, {
136
+ quote,
137
+ preserveLineBreaks
138
+ });
139
+ };
140
+
141
+ // https://github.com/mgenware/string-to-template-literal/blob/main/src/main.ts#L1
142
+ const escapeTemplateStringSpecialCharacters = string => {
143
+ string = String(string);
144
+ let i = 0;
145
+ let escapedString = "";
146
+ while (i < string.length) {
147
+ const char = string[i];
148
+ i++;
149
+ escapedString += isTemplateStringSpecialChar(char) ? "\\".concat(char) : char;
150
+ }
151
+ return escapedString;
152
+ };
153
+ const isTemplateStringSpecialChar = char => templateStringSpecialChars.indexOf(char) > -1;
154
+ const templateStringSpecialChars = ["\\", "`", "$"];
155
+ const determineQuote = (string, {
156
+ canUseTemplateString,
157
+ quoteDefault = DOUBLE_QUOTE
158
+ } = {}) => {
159
+ const containsDoubleQuote = string.includes(DOUBLE_QUOTE);
160
+ if (!containsDoubleQuote) {
161
+ return DOUBLE_QUOTE;
162
+ }
163
+ const containsSimpleQuote = string.includes(SINGLE_QUOTE);
164
+ if (!containsSimpleQuote) {
165
+ return SINGLE_QUOTE;
166
+ }
167
+ if (canUseTemplateString) {
168
+ const containsBackTick = string.includes(BACKTICK);
169
+ if (!containsBackTick) {
170
+ return BACKTICK;
171
+ }
172
+ }
173
+ const doubleQuoteCount = string.split(DOUBLE_QUOTE).length - 1;
174
+ const singleQuoteCount = string.split(SINGLE_QUOTE).length - 1;
175
+ if (singleQuoteCount > doubleQuoteCount) {
176
+ return DOUBLE_QUOTE;
177
+ }
178
+ if (doubleQuoteCount > singleQuoteCount) {
179
+ return SINGLE_QUOTE;
180
+ }
181
+ return quoteDefault;
182
+ };
183
+ const inspectChar = (char, {
184
+ quote,
185
+ preserveLineBreaks
186
+ }) => {
187
+ const point = char.charCodeAt(0);
188
+ if (preserveLineBreaks && (char === "\n" || char === "\r")) {
189
+ return char;
190
+ }
191
+ if (char === quote || point === 92 || point < 32 || point > 126 && point < 160 ||
192
+ // line separators
193
+ point === 8232 || point === 8233) {
194
+ const replacement = char === quote ? "\\".concat(quote) : point === 8232 ? "\\u2028" : point === 8233 ? "\\u2029" : meta[point];
195
+ return replacement;
196
+ }
197
+ return char;
198
+ };
199
+
200
+ // https://github.com/jsenv/jsenv-uneval/blob/6c97ef9d8f2e9425a66f2c88347e0a118d427f3a/src/internal/escapeString.js#L3
201
+ // https://github.com/jsenv/jsenv-inspect/blob/bb11de3adf262b68f71ed82b0a37d4528dd42229/src/internal/string.js#L3
202
+ // https://github.com/joliss/js-string-escape/blob/master/index.js
203
+ // http://javascript.crockford.com/remedial.html
204
+ const surroundStringWith = (string, {
205
+ quote,
206
+ preserveLineBreaks
207
+ }) => {
208
+ let result = "";
209
+ let last = 0;
210
+ const lastIndex = string.length;
211
+ let i = 0;
212
+ while (i < lastIndex) {
213
+ const char = string[i];
214
+ const replacement = inspectChar(char, {
215
+ quote,
216
+ preserveLineBreaks
217
+ });
218
+ if (char !== replacement) {
219
+ if (last === i) {
220
+ result += replacement;
221
+ } else {
222
+ result += "".concat(string.slice(last, i)).concat(replacement);
223
+ }
224
+ last = i + 1;
225
+ }
226
+ i++;
227
+ }
228
+ if (last !== lastIndex) {
229
+ result += string.slice(last);
230
+ }
231
+ return "".concat(quote).concat(result).concat(quote);
232
+ };
233
+
234
+ // prettier-ignore
235
+ const meta = ['\\x00', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\x07',
236
+ // x07
237
+ '\\b', '\\t', '\\n', '\\x0B', '\\f', '\\r', '\\x0E', '\\x0F',
238
+ // x0F
239
+ '\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17',
240
+ // x17
241
+ '\\x18', '\\x19', '\\x1A', '\\x1B', '\\x1C', '\\x1D', '\\x1E', '\\x1F',
242
+ // x1F
243
+ '', '', '', '', '', '', '', "\\'", '', '', '', '', '', '', '', '',
244
+ // x2F
245
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
246
+ // x3F
247
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
248
+ // x4F
249
+ '', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '',
250
+ // x5F
251
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
252
+ // x6F
253
+ '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\x7F',
254
+ // x7F
255
+ '\\x80', '\\x81', '\\x82', '\\x83', '\\x84', '\\x85', '\\x86', '\\x87',
256
+ // x87
257
+ '\\x88', '\\x89', '\\x8A', '\\x8B', '\\x8C', '\\x8D', '\\x8E', '\\x8F',
258
+ // x8F
259
+ '\\x90', '\\x91', '\\x92', '\\x93', '\\x94', '\\x95', '\\x96', '\\x97',
260
+ // x97
261
+ '\\x98', '\\x99', '\\x9A', '\\x9B', '\\x9C', '\\x9D', '\\x9E', '\\x9F' // x9F
262
+ ];
263
+
264
+ const inspectSymbol = (value, {
265
+ nestedHumanize,
266
+ parenthesis
267
+ }) => {
268
+ const symbolDescription = symbolToDescription(value);
269
+ const symbolDescriptionSource = symbolDescription ? nestedHumanize(symbolDescription) : "";
270
+ const symbolSource = "Symbol(".concat(symbolDescriptionSource, ")");
271
+ if (parenthesis) return "".concat(symbolSource);
272
+ return symbolSource;
273
+ };
274
+ const symbolToDescription = "description" in Symbol.prototype ? symbol => symbol.description : symbol => {
275
+ const toStringResult = symbol.toString();
276
+ const openingParenthesisIndex = toStringResult.indexOf("(");
277
+ const closingParenthesisIndex = toStringResult.indexOf(")");
278
+ const symbolDescription = toStringResult.slice(openingParenthesisIndex + 1, closingParenthesisIndex);
279
+ return symbolDescription;
280
+ };
281
+
282
+ const inspectUndefined = () => "undefined";
283
+
284
+ const inspectBigInt = value => {
285
+ return "".concat(value, "n");
286
+ };
287
+
288
+ const preNewLineAndIndentation = (value, {
289
+ depth,
290
+ indentUsingTab,
291
+ indentSize
292
+ }) => {
293
+ return "".concat(newLineAndIndent({
294
+ count: depth + 1,
295
+ useTabs: indentUsingTab,
296
+ size: indentSize
297
+ })).concat(value);
298
+ };
299
+ const postNewLineAndIndentation = ({
300
+ depth,
301
+ indentUsingTab,
302
+ indentSize
303
+ }) => {
304
+ return newLineAndIndent({
305
+ count: depth,
306
+ useTabs: indentUsingTab,
307
+ size: indentSize
308
+ });
309
+ };
310
+ const newLineAndIndent = ({
311
+ count,
312
+ useTabs,
313
+ size
314
+ }) => {
315
+ if (useTabs) {
316
+ // eslint-disable-next-line prefer-template
317
+ return "\n" + "\t".repeat(count);
318
+ }
319
+ // eslint-disable-next-line prefer-template
320
+ return "\n" + " ".repeat(count * size);
321
+ };
322
+ const wrapNewLineAndIndentation = (value, {
323
+ depth,
324
+ indentUsingTab,
325
+ indentSize
326
+ }) => {
327
+ return "".concat(preNewLineAndIndentation(value, {
328
+ depth,
329
+ indentUsingTab,
330
+ indentSize
331
+ })).concat(postNewLineAndIndentation({
332
+ depth,
333
+ indentUsingTab,
334
+ indentSize
335
+ }));
336
+ };
337
+
338
+ const inspectConstructor = (value, {
339
+ parenthesis,
340
+ useNew
341
+ }) => {
342
+ let formattedString = value;
343
+ if (parenthesis) {
344
+ formattedString = "(".concat(value, ")");
345
+ }
346
+ if (useNew) {
347
+ formattedString = "new ".concat(formattedString);
348
+ }
349
+ return formattedString;
350
+ };
351
+
352
+ const inspectArray = (value, {
353
+ seen = [],
354
+ nestedHumanize,
355
+ depth,
356
+ indentUsingTab,
357
+ indentSize,
358
+ parenthesis,
359
+ useNew
360
+ }) => {
361
+ if (seen.indexOf(value) > -1) {
362
+ return "Symbol.for('circular')";
363
+ }
364
+ seen.push(value);
365
+ let valuesSource = "";
366
+ let i = 0;
367
+ const j = value.length;
368
+ while (i < j) {
369
+ const valueSource = value.hasOwnProperty(i) ? nestedHumanize(value[i], {
370
+ seen
371
+ }) : "";
372
+ if (i === 0) {
373
+ valuesSource += valueSource;
374
+ } else {
375
+ valuesSource += ",".concat(preNewLineAndIndentation(valueSource, {
376
+ depth,
377
+ indentUsingTab,
378
+ indentSize
379
+ }));
380
+ }
381
+ i++;
382
+ }
383
+ let arraySource;
384
+ if (valuesSource.length) {
385
+ arraySource = wrapNewLineAndIndentation(valuesSource, {
386
+ depth,
387
+ indentUsingTab,
388
+ indentSize
389
+ });
390
+ } else {
391
+ arraySource = "";
392
+ }
393
+ arraySource = "[".concat(arraySource, "]");
394
+ return inspectConstructor(arraySource, {
395
+ parenthesis,
396
+ useNew
397
+ });
398
+ };
399
+
400
+ const inspectBigIntObject = (value, {
401
+ nestedHumanize
402
+ }) => {
403
+ const bigIntSource = nestedHumanize(value.valueOf());
404
+ return "BigInt(".concat(bigIntSource, ")");
405
+ };
406
+
407
+ const inspectBooleanObject = (value, {
408
+ nestedHumanize,
409
+ useNew,
410
+ parenthesis
411
+ }) => {
412
+ const booleanSource = nestedHumanize(value.valueOf());
413
+ return inspectConstructor("Boolean(".concat(booleanSource, ")"), {
414
+ useNew,
415
+ parenthesis
416
+ });
417
+ };
418
+
419
+ const inspectError = (error, {
420
+ nestedHumanize,
421
+ useNew,
422
+ parenthesis
423
+ }) => {
424
+ const messageSource = nestedHumanize(error.message);
425
+ const errorSource = inspectConstructor("".concat(errorToConstructorName(error), "(").concat(messageSource, ")"), {
426
+ useNew,
427
+ parenthesis
428
+ });
429
+ return errorSource;
430
+ };
431
+ const errorToConstructorName = ({
432
+ name
433
+ }) => {
434
+ if (derivedErrorNameArray.includes(name)) {
435
+ return name;
436
+ }
437
+ return "Error";
438
+ };
439
+
440
+ // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
441
+ const derivedErrorNameArray = ["EvalError", "RangeError", "ReferenceError", "SyntaxError", "TypeError", "URIError"];
442
+
443
+ const inspectDate = (value, {
444
+ nestedHumanize,
445
+ useNew,
446
+ parenthesis
447
+ }) => {
448
+ const dateSource = nestedHumanize(value.valueOf(), {
449
+ numericSeparator: false
450
+ });
451
+ return inspectConstructor("Date(".concat(dateSource, ")"), {
452
+ useNew,
453
+ parenthesis
454
+ });
455
+ };
456
+
457
+ const inspectFunction = (value, {
458
+ showFunctionBody,
459
+ parenthesis,
460
+ depth
461
+ }) => {
462
+ let functionSource;
463
+ if (showFunctionBody) {
464
+ functionSource = value.toString();
465
+ } else {
466
+ const isArrowFunction = value.prototype === undefined;
467
+ const head = isArrowFunction ? "() =>" : "function ".concat(depth === 0 ? value.name : "", "()");
468
+ functionSource = "".concat(head, " {/* hidden */}");
469
+ }
470
+ if (parenthesis) {
471
+ return "(".concat(functionSource, ")");
472
+ }
473
+ return functionSource;
474
+ };
475
+
476
+ const inspectNumberObject = (value, {
477
+ nestedHumanize,
478
+ useNew,
479
+ parenthesis
480
+ }) => {
481
+ const numberSource = nestedHumanize(value.valueOf());
482
+ return inspectConstructor("Number(".concat(numberSource, ")"), {
483
+ useNew,
484
+ parenthesis
485
+ });
486
+ };
487
+
488
+ const inspectObject = (value, {
489
+ nestedHumanize,
490
+ seen = [],
491
+ depth,
492
+ indentUsingTab,
493
+ indentSize,
494
+ objectConstructor,
495
+ parenthesis,
496
+ useNew
497
+ }) => {
498
+ if (seen.indexOf(value) > -1) return "Symbol.for('circular')";
499
+ seen.push(value);
500
+ const propertySourceArray = [];
501
+ Object.getOwnPropertyNames(value).forEach(propertyName => {
502
+ const propertyNameAsNumber = parseInt(propertyName, 10);
503
+ const propertyNameSource = nestedHumanize(Number.isInteger(propertyNameAsNumber) ? propertyNameAsNumber : propertyName);
504
+ propertySourceArray.push({
505
+ nameOrSymbolSource: propertyNameSource,
506
+ valueSource: nestedHumanize(value[propertyName], {
507
+ seen
508
+ })
509
+ });
510
+ });
511
+ Object.getOwnPropertySymbols(value).forEach(symbol => {
512
+ propertySourceArray.push({
513
+ nameOrSymbolSource: "[".concat(nestedHumanize(symbol), "]"),
514
+ valueSource: nestedHumanize(value[symbol], {
515
+ seen
516
+ })
517
+ });
518
+ });
519
+ let propertiesSource = "";
520
+ propertySourceArray.forEach(({
521
+ nameOrSymbolSource,
522
+ valueSource
523
+ }, index) => {
524
+ if (index === 0) {
525
+ propertiesSource += "".concat(nameOrSymbolSource, ": ").concat(valueSource);
526
+ } else {
527
+ propertiesSource += ",".concat(preNewLineAndIndentation("".concat(nameOrSymbolSource, ": ").concat(valueSource), {
528
+ depth,
529
+ indentUsingTab,
530
+ indentSize
531
+ }));
532
+ }
533
+ });
534
+ let objectSource;
535
+ if (propertiesSource.length) {
536
+ objectSource = "".concat(wrapNewLineAndIndentation(propertiesSource, {
537
+ depth,
538
+ indentUsingTab,
539
+ indentSize
540
+ }));
541
+ } else {
542
+ objectSource = "";
543
+ }
544
+ if (objectConstructor) {
545
+ objectSource = "Object({".concat(objectSource, "})");
546
+ } else {
547
+ objectSource = "{".concat(objectSource, "}");
548
+ }
549
+ return inspectConstructor(objectSource, {
550
+ parenthesis,
551
+ useNew
552
+ });
553
+ };
554
+
555
+ const inspectRegExp = value => value.toString();
556
+
557
+ const inspectStringObject = (value, {
558
+ nestedHumanize,
559
+ useNew,
560
+ parenthesis
561
+ }) => {
562
+ const stringSource = nestedHumanize(value.valueOf());
563
+ return inspectConstructor("String(".concat(stringSource, ")"), {
564
+ useNew,
565
+ parenthesis
566
+ });
567
+ };
568
+
569
+ // primitives
570
+ const humanize = (value, {
571
+ parenthesis = false,
572
+ quote = "auto",
573
+ canUseTemplateString = true,
574
+ useNew = false,
575
+ objectConstructor = false,
576
+ showFunctionBody = false,
577
+ indentUsingTab = false,
578
+ indentSize = 2,
579
+ numericSeparator = true,
580
+ preserveLineBreaks = false
581
+ } = {}) => {
582
+ const scopedHumanize = (scopedValue, scopedOptions) => {
583
+ const options = {
584
+ ...scopedOptions,
585
+ nestedHumanize: (nestedValue, nestedOptions = {}) => {
586
+ return scopedHumanize(nestedValue, {
587
+ ...scopedOptions,
588
+ depth: scopedOptions.depth + 1,
589
+ ...nestedOptions
590
+ });
591
+ }
592
+ };
593
+ return humanizeValue(scopedValue, options);
594
+ };
595
+ return scopedHumanize(value, {
596
+ parenthesis,
597
+ quote,
598
+ canUseTemplateString,
599
+ useNew,
600
+ objectConstructor,
601
+ showFunctionBody,
602
+ indentUsingTab,
603
+ indentSize,
604
+ numericSeparator,
605
+ preserveLineBreaks,
606
+ depth: 0
607
+ });
608
+ };
609
+ const humanizeMethodSymbol = Symbol.for("inspect");
610
+ const humanizeValue = (value, options) => {
611
+ const customHumanize = value && value[humanizeMethodSymbol];
612
+ if (customHumanize) {
613
+ return customHumanize(options);
614
+ }
615
+ const primitiveType = primitiveTypeFromValue(value);
616
+ const primitiveStringifier = primitiveStringifiers[primitiveType];
617
+ if (primitiveStringifier) {
618
+ return primitiveStringifier(value, options);
619
+ }
620
+ const compositeType = compositeTypeFromObject(value);
621
+ const compositeStringifier = compositeStringifiers[compositeType];
622
+ if (compositeStringifier) {
623
+ return compositeStringifier(value, options);
624
+ }
625
+ return inspectConstructor("".concat(compositeType, "(").concat(inspectObject(value, options), ")"), {
626
+ ...options,
627
+ parenthesis: false
628
+ });
629
+ };
630
+ const primitiveTypeFromValue = value => {
631
+ if (value === null) {
632
+ return "null";
633
+ }
634
+ if (value === undefined) {
635
+ return "undefined";
636
+ }
637
+ return typeof value;
638
+ };
639
+ const primitiveStringifiers = {
640
+ boolean: inspectBoolean,
641
+ function: inspectFunction,
642
+ null: inspectNull,
643
+ number: inspectNumber,
644
+ string: inspectString,
645
+ symbol: inspectSymbol,
646
+ undefined: inspectUndefined,
647
+ bigint: inspectBigInt
648
+ };
649
+ const compositeTypeFromObject = object => {
650
+ if (typeof object === "object" && Object.getPrototypeOf(object) === null) {
651
+ return "Object";
652
+ }
653
+ const toStringResult = toString.call(object);
654
+ // returns format is '[object ${tagName}]';
655
+ // and we want ${tagName}
656
+ const tagName = toStringResult.slice("[object ".length, -1);
657
+ if (tagName === "Object") {
658
+ const objectConstructorName = object.constructor.name;
659
+ if (objectConstructorName !== "Object") {
660
+ return objectConstructorName;
661
+ }
662
+ }
663
+ return tagName;
664
+ };
665
+ const {
666
+ toString
667
+ } = Object.prototype;
668
+ const compositeStringifiers = {
669
+ Array: inspectArray,
670
+ BigInt: inspectBigIntObject,
671
+ Boolean: inspectBooleanObject,
672
+ Error: inspectError,
673
+ Date: inspectDate,
674
+ Function: inspectFunction,
675
+ Number: inspectNumberObject,
676
+ Object: inspectObject,
677
+ RegExp: inspectRegExp,
678
+ String: inspectStringObject
679
+ };
680
+
681
+ const getPrecision = number => {
682
+ if (Math.floor(number) === number) return 0;
683
+ const [, decimals] = number.toString().split(".");
684
+ return decimals.length || 0;
685
+ };
686
+ const setRoundedPrecision = (number, {
687
+ decimals = 1,
688
+ decimalsWhenSmall = decimals
689
+ } = {}) => {
690
+ return setDecimalsPrecision(number, {
691
+ decimals,
692
+ decimalsWhenSmall,
693
+ transform: Math.round
694
+ });
695
+ };
696
+ const setPrecision = (number, {
697
+ decimals = 1,
698
+ decimalsWhenSmall = decimals
699
+ } = {}) => {
700
+ return setDecimalsPrecision(number, {
701
+ decimals,
702
+ decimalsWhenSmall,
703
+ transform: parseInt
704
+ });
705
+ };
706
+ const setDecimalsPrecision = (number, {
707
+ transform,
708
+ decimals,
709
+ // max decimals for number in [-Infinity, -1[]1, Infinity]
710
+ decimalsWhenSmall // max decimals for number in [-1,1]
711
+ } = {}) => {
712
+ if (number === 0) {
713
+ return 0;
714
+ }
715
+ let numberCandidate = Math.abs(number);
716
+ if (numberCandidate < 1) {
717
+ const integerGoal = Math.pow(10, decimalsWhenSmall - 1);
718
+ let i = 1;
719
+ while (numberCandidate < integerGoal) {
720
+ numberCandidate *= 10;
721
+ i *= 10;
722
+ }
723
+ const asInteger = transform(numberCandidate);
724
+ const asFloat = asInteger / i;
725
+ return number < 0 ? -asFloat : asFloat;
726
+ }
727
+ const coef = Math.pow(10, decimals);
728
+ const numberMultiplied = (number + Number.EPSILON) * coef;
729
+ const asInteger = transform(numberMultiplied);
730
+ const asFloat = asInteger / coef;
731
+ return number < 0 ? -asFloat : asFloat;
732
+ };
733
+
734
+ // https://www.codingem.com/javascript-how-to-limit-decimal-places/
735
+ // export const roundNumber = (number, maxDecimals) => {
736
+ // const decimalsExp = Math.pow(10, maxDecimals)
737
+ // const numberRoundInt = Math.round(decimalsExp * (number + Number.EPSILON))
738
+ // const numberRoundFloat = numberRoundInt / decimalsExp
739
+ // return numberRoundFloat
740
+ // }
741
+
742
+ // export const setPrecision = (number, precision) => {
743
+ // if (Math.floor(number) === number) return number
744
+ // const [int, decimals] = number.toString().split(".")
745
+ // if (precision <= 0) return int
746
+ // const numberTruncated = `${int}.${decimals.slice(0, precision)}`
747
+ // return numberTruncated
748
+ // }
749
+
750
+ const humanizeEllapsedTime = (ms, {
751
+ short
752
+ } = {}) => {
753
+ if (ms < 1000) {
754
+ return short ? "0s" : "0 second";
755
+ }
756
+ const {
757
+ primary,
758
+ remaining
759
+ } = parseMs(ms);
760
+ if (!remaining) {
761
+ return inspectEllapsedUnit(primary, short);
762
+ }
763
+ return "".concat(inspectEllapsedUnit(primary, short), " and ").concat(inspectEllapsedUnit(remaining, short));
764
+ };
765
+ const inspectEllapsedUnit = (unit, short) => {
766
+ const count = unit.name === "second" ? Math.floor(unit.count) : Math.round(unit.count);
767
+ let name = unit.name;
768
+ if (short) {
769
+ name = unitShort[name];
770
+ if (count <= 1) {
771
+ return "".concat(count).concat(name);
772
+ }
773
+ return "".concat(count).concat(name, "s");
774
+ }
775
+ if (count <= 1) {
776
+ return "".concat(count, " ").concat(name);
777
+ }
778
+ return "".concat(count, " ").concat(name, "s");
779
+ };
780
+ const unitShort = {
781
+ year: "y",
782
+ month: "m",
783
+ week: "w",
784
+ day: "d",
785
+ hour: "h",
786
+ minute: "m",
787
+ second: "s"
788
+ };
789
+ const humanizeDuration = (ms, {
790
+ short,
791
+ rounded = true,
792
+ decimals
793
+ } = {}) => {
794
+ // ignore ms below meaningfulMs so that:
795
+ // humanizeDuration(0.5) -> "0 second"
796
+ // humanizeDuration(1.1) -> "0.001 second" (and not "0.0011 second")
797
+ // This tool is meant to be read by humans and it would be barely readable to see
798
+ // "0.0001 second" (stands for 0.1 millisecond)
799
+ // yes we could return "0.1 millisecond" but we choosed consistency over precision
800
+ // so that the prefered unit is "second" (and does not become millisecond when ms is super small)
801
+ if (ms < 1) {
802
+ return short ? "0s" : "0 second";
803
+ }
804
+ const {
805
+ primary,
806
+ remaining
807
+ } = parseMs(ms);
808
+ if (!remaining) {
809
+ return humanizeDurationUnit(primary, {
810
+ decimals: decimals === undefined ? primary.name === "second" ? 1 : 0 : decimals,
811
+ short,
812
+ rounded
813
+ });
814
+ }
815
+ return "".concat(humanizeDurationUnit(primary, {
816
+ decimals: decimals === undefined ? 0 : decimals,
817
+ short,
818
+ rounded
819
+ }), " and ").concat(humanizeDurationUnit(remaining, {
820
+ decimals: decimals === undefined ? 0 : decimals,
821
+ short,
822
+ rounded
823
+ }));
824
+ };
825
+ const humanizeDurationUnit = (unit, {
826
+ decimals,
827
+ short,
828
+ rounded
829
+ }) => {
830
+ const count = rounded ? setRoundedPrecision(unit.count, {
831
+ decimals
832
+ }) : setPrecision(unit.count, {
833
+ decimals
834
+ });
835
+ let name = unit.name;
836
+ if (short) {
837
+ name = unitShort[name];
838
+ return "".concat(count).concat(name);
839
+ }
840
+ if (count <= 1) {
841
+ return "".concat(count, " ").concat(name);
842
+ }
843
+ return "".concat(count, " ").concat(name, "s");
844
+ };
845
+ const MS_PER_UNITS = {
846
+ year: 31557600000,
847
+ month: 2629000000,
848
+ week: 604800000,
849
+ day: 86400000,
850
+ hour: 3600000,
851
+ minute: 60000,
852
+ second: 1000
853
+ };
854
+ const parseMs = ms => {
855
+ const unitNames = Object.keys(MS_PER_UNITS);
856
+ const smallestUnitName = unitNames[unitNames.length - 1];
857
+ let firstUnitName = smallestUnitName;
858
+ let firstUnitCount = ms / MS_PER_UNITS[smallestUnitName];
859
+ const firstUnitIndex = unitNames.findIndex(unitName => {
860
+ if (unitName === smallestUnitName) {
861
+ return false;
862
+ }
863
+ const msPerUnit = MS_PER_UNITS[unitName];
864
+ const unitCount = Math.floor(ms / msPerUnit);
865
+ if (unitCount) {
866
+ firstUnitName = unitName;
867
+ firstUnitCount = unitCount;
868
+ return true;
869
+ }
870
+ return false;
871
+ });
872
+ if (firstUnitName === smallestUnitName) {
873
+ return {
874
+ primary: {
875
+ name: firstUnitName,
876
+ count: firstUnitCount
877
+ }
878
+ };
879
+ }
880
+ const remainingMs = ms - firstUnitCount * MS_PER_UNITS[firstUnitName];
881
+ const remainingUnitName = unitNames[firstUnitIndex + 1];
882
+ const remainingUnitCount = remainingMs / MS_PER_UNITS[remainingUnitName];
883
+ // - 1 year and 1 second is too much information
884
+ // so we don't check the remaining units
885
+ // - 1 year and 0.0001 week is awful
886
+ // hence the if below
887
+ if (Math.round(remainingUnitCount) < 1) {
888
+ return {
889
+ primary: {
890
+ name: firstUnitName,
891
+ count: firstUnitCount
892
+ }
893
+ };
894
+ }
895
+ // - 1 year and 1 month is great
896
+ return {
897
+ primary: {
898
+ name: firstUnitName,
899
+ count: firstUnitCount
900
+ },
901
+ remaining: {
902
+ name: remainingUnitName,
903
+ count: remainingUnitCount
904
+ }
905
+ };
906
+ };
907
+
908
+ const humanizeFileSize = (numberOfBytes, {
909
+ decimals,
910
+ short
911
+ } = {}) => {
912
+ return inspectBytes(numberOfBytes, {
913
+ decimals,
914
+ short
915
+ });
916
+ };
917
+ const humanizeMemoryUsage = (metricValue, {
918
+ decimals,
919
+ short
920
+ } = {}) => {
921
+ return inspectBytes(metricValue, {
922
+ decimals,
923
+ fixedDecimals: true,
924
+ short
925
+ });
926
+ };
927
+ const inspectBytes = (number, {
928
+ fixedDecimals = false,
929
+ decimals,
930
+ short
931
+ } = {}) => {
932
+ if (number === 0) {
933
+ return "0 B";
934
+ }
935
+ const exponent = Math.min(Math.floor(Math.log10(number) / 3), BYTE_UNITS.length - 1);
936
+ const unitNumber = number / Math.pow(1000, exponent);
937
+ const unitName = BYTE_UNITS[exponent];
938
+ if (decimals === undefined) {
939
+ if (unitNumber < 100) {
940
+ decimals = 1;
941
+ } else {
942
+ decimals = 0;
943
+ }
944
+ }
945
+ const unitNumberRounded = setRoundedPrecision(unitNumber, {
946
+ decimals,
947
+ decimalsWhenSmall: 1
948
+ });
949
+ const value = fixedDecimals ? unitNumberRounded.toFixed(decimals) : unitNumberRounded;
950
+ if (short) {
951
+ return "".concat(value).concat(unitName);
952
+ }
953
+ return "".concat(value, " ").concat(unitName);
954
+ };
955
+ const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
956
+
957
+ const distributePercentages = (namedNumbers, {
958
+ maxPrecisionHint = 2
959
+ } = {}) => {
960
+ const numberNames = Object.keys(namedNumbers);
961
+ if (numberNames.length === 0) {
962
+ return {};
963
+ }
964
+ if (numberNames.length === 1) {
965
+ const firstNumberName = numberNames[0];
966
+ return {
967
+ [firstNumberName]: "100 %"
968
+ };
969
+ }
970
+ const numbers = numberNames.map(name => namedNumbers[name]);
971
+ const total = numbers.reduce((sum, value) => sum + value, 0);
972
+ const ratios = numbers.map(number => number / total);
973
+ const percentages = {};
974
+ ratios.pop();
975
+ ratios.forEach((ratio, index) => {
976
+ const percentage = ratio * 100;
977
+ percentages[numberNames[index]] = percentage;
978
+ });
979
+ const lowestPercentage = 1 / Math.pow(10, maxPrecisionHint) * 100;
980
+ let precision = 0;
981
+ Object.keys(percentages).forEach(name => {
982
+ const percentage = percentages[name];
983
+ if (percentage < lowestPercentage) {
984
+ // check the amout of meaningful decimals
985
+ // and that what we will use
986
+ const percentageRounded = setRoundedPrecision(percentage);
987
+ const percentagePrecision = getPrecision(percentageRounded);
988
+ if (percentagePrecision > precision) {
989
+ precision = percentagePrecision;
990
+ }
991
+ }
992
+ });
993
+ let remainingPercentage = 100;
994
+ Object.keys(percentages).forEach(name => {
995
+ const percentage = percentages[name];
996
+ const percentageAllocated = setRoundedPrecision(percentage, {
997
+ decimals: precision
998
+ });
999
+ remainingPercentage -= percentageAllocated;
1000
+ percentages[name] = percentageAllocated;
1001
+ });
1002
+ const lastName = numberNames[numberNames.length - 1];
1003
+ percentages[lastName] = setRoundedPrecision(remainingPercentage, {
1004
+ decimals: precision
1005
+ });
1006
+ return percentages;
1007
+ };
1008
+
1009
+ const formatDefault = v => v;
1010
+ const generateContentFrame = ({
1011
+ content,
1012
+ line,
1013
+ column,
1014
+ linesAbove = 3,
1015
+ linesBelow = 0,
1016
+ lineMaxWidth = 120,
1017
+ lineNumbersOnTheLeft = true,
1018
+ lineMarker = true,
1019
+ columnMarker = true,
1020
+ format = formatDefault
1021
+ } = {}) => {
1022
+ const lineStrings = content.split(/\r?\n/);
1023
+ if (line === 0) line = 1;
1024
+ if (column === undefined) {
1025
+ columnMarker = false;
1026
+ column = 1;
1027
+ }
1028
+ if (column === 0) column = 1;
1029
+ let lineStartIndex = line - 1 - linesAbove;
1030
+ if (lineStartIndex < 0) {
1031
+ lineStartIndex = 0;
1032
+ }
1033
+ let lineEndIndex = line - 1 + linesBelow;
1034
+ if (lineEndIndex > lineStrings.length - 1) {
1035
+ lineEndIndex = lineStrings.length - 1;
1036
+ }
1037
+ if (columnMarker) {
1038
+ // human reader deduce the line when there is a column marker
1039
+ lineMarker = false;
1040
+ }
1041
+ if (line - 1 === lineEndIndex) {
1042
+ lineMarker = false; // useless because last line
1043
+ }
1044
+ let lineIndex = lineStartIndex;
1045
+ let columnsBefore;
1046
+ let columnsAfter;
1047
+ if (column > lineMaxWidth) {
1048
+ columnsBefore = column - Math.ceil(lineMaxWidth / 2);
1049
+ columnsAfter = column + Math.floor(lineMaxWidth / 2);
1050
+ } else {
1051
+ columnsBefore = 0;
1052
+ columnsAfter = lineMaxWidth;
1053
+ }
1054
+ let columnMarkerIndex = column - 1 - columnsBefore;
1055
+ let source = "";
1056
+ while (lineIndex <= lineEndIndex) {
1057
+ const lineString = lineStrings[lineIndex];
1058
+ const lineNumber = lineIndex + 1;
1059
+ const isLastLine = lineIndex === lineEndIndex;
1060
+ const isMainLine = lineNumber === line;
1061
+ lineIndex++;
1062
+ {
1063
+ if (lineMarker) {
1064
+ if (isMainLine) {
1065
+ source += "".concat(format(">", "marker_line"), " ");
1066
+ } else {
1067
+ source += " ";
1068
+ }
1069
+ }
1070
+ if (lineNumbersOnTheLeft) {
1071
+ // fill with spaces to ensure if line moves from 7,8,9 to 10 the display is still great
1072
+ const asideSource = "".concat(fillLeft(lineNumber, lineEndIndex + 1), " |");
1073
+ source += "".concat(format(asideSource, "line_number_aside"), " ");
1074
+ }
1075
+ }
1076
+ {
1077
+ source += truncateLine(lineString, {
1078
+ start: columnsBefore,
1079
+ end: columnsAfter,
1080
+ prefix: "…",
1081
+ suffix: "…",
1082
+ format
1083
+ });
1084
+ }
1085
+ {
1086
+ if (columnMarker && isMainLine) {
1087
+ source += "\n";
1088
+ if (lineMarker) {
1089
+ source += " ";
1090
+ }
1091
+ if (lineNumbersOnTheLeft) {
1092
+ const asideSpaces = "".concat(fillLeft(lineNumber, lineEndIndex + 1), " | ").length;
1093
+ source += " ".repeat(asideSpaces);
1094
+ }
1095
+ source += " ".repeat(columnMarkerIndex);
1096
+ source += format("^", "marker_column");
1097
+ }
1098
+ }
1099
+ if (!isLastLine) {
1100
+ source += "\n";
1101
+ }
1102
+ }
1103
+ return source;
1104
+ };
1105
+ const truncateLine = (line, {
1106
+ start,
1107
+ end,
1108
+ prefix,
1109
+ suffix,
1110
+ format
1111
+ }) => {
1112
+ const lastIndex = line.length;
1113
+ if (line.length === 0) {
1114
+ // don't show any ellipsis if the line is empty
1115
+ // because it's not truncated in that case
1116
+ return "";
1117
+ }
1118
+ const startTruncated = start > 0;
1119
+ const endTruncated = lastIndex > end;
1120
+ let from = startTruncated ? start + prefix.length : start;
1121
+ let to = endTruncated ? end - suffix.length : end;
1122
+ if (to > lastIndex) to = lastIndex;
1123
+ if (start >= lastIndex || from === to) {
1124
+ return "";
1125
+ }
1126
+ let result = "";
1127
+ while (from < to) {
1128
+ result += format(line[from], "char");
1129
+ from++;
1130
+ }
1131
+ if (result.length === 0) {
1132
+ return "";
1133
+ }
1134
+ if (startTruncated && endTruncated) {
1135
+ return "".concat(format(prefix, "marker_overflow_left")).concat(result).concat(format(suffix, "marker_overflow_right"));
1136
+ }
1137
+ if (startTruncated) {
1138
+ return "".concat(format(prefix, "marker_overflow_left")).concat(result);
1139
+ }
1140
+ if (endTruncated) {
1141
+ return "".concat(result).concat(format(suffix, "marker_overflow_right"));
1142
+ }
1143
+ return result;
1144
+ };
1145
+ const fillLeft = (value, biggestValue, char = " ") => {
1146
+ const width = String(value).length;
1147
+ const biggestWidth = String(biggestValue).length;
1148
+ let missingWidth = biggestWidth - width;
1149
+ let padded = "";
1150
+ while (missingWidth--) {
1151
+ padded += char;
1152
+ }
1153
+ padded += value;
1154
+ return padded;
1155
+ };
1156
+
1157
+ export { createDetailedMessage, determineQuote, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemoryUsage, humanizeMethodSymbol, inspectChar };