@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.
- package/README.md +35 -0
- package/dist/browser/jsenv_humanize_browser.js +1157 -0
- package/dist/node/jsenv_humanize_node.js +2003 -0
- package/package.json +49 -0
- package/src/.jsenv/jsenv_tests_output.txt +35 -0
- package/src/_tests/byte/file_size.test.js +18 -0
- package/src/_tests/byte/memory_usage.test.js +11 -0
- package/src/_tests/content_frame/content_frame.test.js +156 -0
- package/src/_tests/content_frame/snapshots/10_line_and_column_are_zero.txt +2 -0
- package/src/_tests/content_frame/snapshots/11_line_is_last.txt +3 -0
- package/src/_tests/content_frame/snapshots/1_basic.txt +4 -0
- package/src/_tests/content_frame/snapshots/2_basic_2.txt +6 -0
- package/src/_tests/content_frame/snapshots/3_empty_last_line.txt +7 -0
- package/src/_tests/content_frame/snapshots/4_no_column.txt +3 -0
- package/src/_tests/content_frame/snapshots/5_line_1_and_2_digits.txt +3 -0
- package/src/_tests/content_frame/snapshots/6_line_too_long_column_undefined.txt +3 -0
- package/src/_tests/content_frame/snapshots/7_line_too_long_column_near_start.txt +4 -0
- package/src/_tests/content_frame/snapshots/8_line_too_long_column_near_middle.txt +4 -0
- package/src/_tests/content_frame/snapshots/9_line_too_long_column_near_end.txt +4 -0
- package/src/_tests/js_value/_inspect.test.html +22 -0
- package/src/_tests/js_value/array.test.js +72 -0
- package/src/_tests/js_value/biginit.test.js +36 -0
- package/src/_tests/js_value/boolean.test.js +45 -0
- package/src/_tests/js_value/date.test.js +15 -0
- package/src/_tests/js_value/error.test.js +33 -0
- package/src/_tests/js_value/function.test.js +51 -0
- package/src/_tests/js_value/instance.test.js +12 -0
- package/src/_tests/js_value/null.test.js +6 -0
- package/src/_tests/js_value/number.test.js +182 -0
- package/src/_tests/js_value/object.test.js +173 -0
- package/src/_tests/js_value/regexp.test.js +14 -0
- package/src/_tests/js_value/string.test.js +106 -0
- package/src/_tests/js_value/symbol.test.js +20 -0
- package/src/_tests/js_value/undefined.test.js +6 -0
- package/src/_tests/log/log_long.test.mjs +39 -0
- package/src/_tests/log/log_modified.test.mjs +7 -0
- package/src/_tests/log/logger.test.mjs +91 -0
- package/src/_tests/log/spinner.duration.test_manual.mjs +25 -0
- package/src/_tests/log/spinner.test.mjs +33 -0
- package/src/_tests/log/spinner.test_manual.mjs +15 -0
- package/src/_tests/percentage/percentage_distribution.test.mjs +89 -0
- package/src/_tests/time/decimals.test.js +119 -0
- package/src/_tests/time/duration.test.js +20 -0
- package/src/_tests/time/ellapsed.test.js +10 -0
- package/src/byte/byte.js +44 -0
- package/src/content_frame/content_frame.js +157 -0
- package/src/js_value/array.js +58 -0
- package/src/js_value/bigint.js +3 -0
- package/src/js_value/bigint_object.js +5 -0
- package/src/js_value/boolean.js +1 -0
- package/src/js_value/boolean_object.js +12 -0
- package/src/js_value/constructor.js +10 -0
- package/src/js_value/date.js +8 -0
- package/src/js_value/error.js +34 -0
- package/src/js_value/function.js +20 -0
- package/src/js_value/humanize.js +140 -0
- package/src/js_value/null.js +1 -0
- package/src/js_value/number.js +101 -0
- package/src/js_value/number_object.js +9 -0
- package/src/js_value/object.js +78 -0
- package/src/js_value/regexp.js +1 -0
- package/src/js_value/string.js +141 -0
- package/src/js_value/string_object.js +10 -0
- package/src/js_value/symbol.js +24 -0
- package/src/js_value/undefined.js +1 -0
- package/src/log/LOG_LEVELS.js +9 -0
- package/src/log/ansi.js +37 -0
- package/src/log/detailed_message.js +17 -0
- package/src/log/dynamic_log.js +185 -0
- package/src/log/logger.js +85 -0
- package/src/log/spinner.js +72 -0
- package/src/log/task_log.js +46 -0
- package/src/log/unicode.js +52 -0
- package/src/main_browser.js +10 -0
- package/src/main_node.js +18 -0
- package/src/percentage/distribute_percentages.js +53 -0
- package/src/time/time.js +151 -0
- package/src/utils/decimals.js +95 -0
- package/src/utils/indentation.js +38 -0
|
@@ -0,0 +1,2003 @@
|
|
|
1
|
+
import stringWidth from "string-width";
|
|
2
|
+
import process$1 from "node:process";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import tty from "node:tty";
|
|
5
|
+
|
|
6
|
+
const LOG_LEVEL_OFF = "off";
|
|
7
|
+
|
|
8
|
+
const LOG_LEVEL_DEBUG = "debug";
|
|
9
|
+
|
|
10
|
+
const LOG_LEVEL_INFO = "info";
|
|
11
|
+
|
|
12
|
+
const LOG_LEVEL_WARN = "warn";
|
|
13
|
+
|
|
14
|
+
const LOG_LEVEL_ERROR = "error";
|
|
15
|
+
|
|
16
|
+
const createLogger = ({ logLevel = LOG_LEVEL_INFO } = {}) => {
|
|
17
|
+
if (logLevel === LOG_LEVEL_DEBUG) {
|
|
18
|
+
return {
|
|
19
|
+
level: "debug",
|
|
20
|
+
levels: { debug: true, info: true, warn: true, error: true },
|
|
21
|
+
debug,
|
|
22
|
+
info,
|
|
23
|
+
warn,
|
|
24
|
+
error,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
if (logLevel === LOG_LEVEL_INFO) {
|
|
28
|
+
return {
|
|
29
|
+
level: "info",
|
|
30
|
+
levels: { debug: false, info: true, warn: true, error: true },
|
|
31
|
+
debug: debugDisabled,
|
|
32
|
+
info,
|
|
33
|
+
warn,
|
|
34
|
+
error,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (logLevel === LOG_LEVEL_WARN) {
|
|
38
|
+
return {
|
|
39
|
+
level: "warn",
|
|
40
|
+
levels: { debug: false, info: false, warn: true, error: true },
|
|
41
|
+
debug: debugDisabled,
|
|
42
|
+
info: infoDisabled,
|
|
43
|
+
warn,
|
|
44
|
+
error,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
if (logLevel === LOG_LEVEL_ERROR) {
|
|
48
|
+
return {
|
|
49
|
+
level: "error",
|
|
50
|
+
levels: { debug: false, info: false, warn: false, error: true },
|
|
51
|
+
debug: debugDisabled,
|
|
52
|
+
info: infoDisabled,
|
|
53
|
+
warn: warnDisabled,
|
|
54
|
+
error,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
if (logLevel === LOG_LEVEL_OFF) {
|
|
58
|
+
return {
|
|
59
|
+
level: "off",
|
|
60
|
+
levels: { debug: false, info: false, warn: false, error: false },
|
|
61
|
+
debug: debugDisabled,
|
|
62
|
+
info: infoDisabled,
|
|
63
|
+
warn: warnDisabled,
|
|
64
|
+
error: errorDisabled,
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
throw new Error(`unexpected logLevel.
|
|
68
|
+
--- logLevel ---
|
|
69
|
+
${logLevel}
|
|
70
|
+
--- allowed log levels ---
|
|
71
|
+
${LOG_LEVEL_OFF}
|
|
72
|
+
${LOG_LEVEL_ERROR}
|
|
73
|
+
${LOG_LEVEL_WARN}
|
|
74
|
+
${LOG_LEVEL_INFO}
|
|
75
|
+
${LOG_LEVEL_DEBUG}`);
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const debug = (...args) => console.debug(...args);
|
|
79
|
+
|
|
80
|
+
const debugDisabled = () => {};
|
|
81
|
+
|
|
82
|
+
const info = (...args) => console.info(...args);
|
|
83
|
+
|
|
84
|
+
const infoDisabled = () => {};
|
|
85
|
+
|
|
86
|
+
const warn = (...args) => console.warn(...args);
|
|
87
|
+
|
|
88
|
+
const warnDisabled = () => {};
|
|
89
|
+
|
|
90
|
+
const error = (...args) => console.error(...args);
|
|
91
|
+
|
|
92
|
+
const errorDisabled = () => {};
|
|
93
|
+
|
|
94
|
+
const ESC = '\u001B[';
|
|
95
|
+
const OSC = '\u001B]';
|
|
96
|
+
const BEL = '\u0007';
|
|
97
|
+
const SEP = ';';
|
|
98
|
+
|
|
99
|
+
/* global window */
|
|
100
|
+
const isBrowser = typeof window !== 'undefined' && typeof window.document !== 'undefined';
|
|
101
|
+
|
|
102
|
+
const isTerminalApp = !isBrowser && process$1.env.TERM_PROGRAM === 'Apple_Terminal';
|
|
103
|
+
const isWindows = !isBrowser && process$1.platform === 'win32';
|
|
104
|
+
const cwdFunction = isBrowser ? () => {
|
|
105
|
+
throw new Error('`process.cwd()` only works in Node.js, not the browser.');
|
|
106
|
+
} : process$1.cwd;
|
|
107
|
+
|
|
108
|
+
const ansiEscapes = {};
|
|
109
|
+
|
|
110
|
+
ansiEscapes.cursorTo = (x, y) => {
|
|
111
|
+
if (typeof x !== 'number') {
|
|
112
|
+
throw new TypeError('The `x` argument is required');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (typeof y !== 'number') {
|
|
116
|
+
return ESC + (x + 1) + 'G';
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return ESC + (y + 1) + SEP + (x + 1) + 'H';
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
ansiEscapes.cursorMove = (x, y) => {
|
|
123
|
+
if (typeof x !== 'number') {
|
|
124
|
+
throw new TypeError('The `x` argument is required');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let returnValue = '';
|
|
128
|
+
|
|
129
|
+
if (x < 0) {
|
|
130
|
+
returnValue += ESC + (-x) + 'D';
|
|
131
|
+
} else if (x > 0) {
|
|
132
|
+
returnValue += ESC + x + 'C';
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (y < 0) {
|
|
136
|
+
returnValue += ESC + (-y) + 'A';
|
|
137
|
+
} else if (y > 0) {
|
|
138
|
+
returnValue += ESC + y + 'B';
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return returnValue;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
ansiEscapes.cursorUp = (count = 1) => ESC + count + 'A';
|
|
145
|
+
ansiEscapes.cursorDown = (count = 1) => ESC + count + 'B';
|
|
146
|
+
ansiEscapes.cursorForward = (count = 1) => ESC + count + 'C';
|
|
147
|
+
ansiEscapes.cursorBackward = (count = 1) => ESC + count + 'D';
|
|
148
|
+
|
|
149
|
+
ansiEscapes.cursorLeft = ESC + 'G';
|
|
150
|
+
ansiEscapes.cursorSavePosition = isTerminalApp ? '\u001B7' : ESC + 's';
|
|
151
|
+
ansiEscapes.cursorRestorePosition = isTerminalApp ? '\u001B8' : ESC + 'u';
|
|
152
|
+
ansiEscapes.cursorGetPosition = ESC + '6n';
|
|
153
|
+
ansiEscapes.cursorNextLine = ESC + 'E';
|
|
154
|
+
ansiEscapes.cursorPrevLine = ESC + 'F';
|
|
155
|
+
ansiEscapes.cursorHide = ESC + '?25l';
|
|
156
|
+
ansiEscapes.cursorShow = ESC + '?25h';
|
|
157
|
+
|
|
158
|
+
ansiEscapes.eraseLines = count => {
|
|
159
|
+
let clear = '';
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < count; i++) {
|
|
162
|
+
clear += ansiEscapes.eraseLine + (i < count - 1 ? ansiEscapes.cursorUp() : '');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (count) {
|
|
166
|
+
clear += ansiEscapes.cursorLeft;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return clear;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
ansiEscapes.eraseEndLine = ESC + 'K';
|
|
173
|
+
ansiEscapes.eraseStartLine = ESC + '1K';
|
|
174
|
+
ansiEscapes.eraseLine = ESC + '2K';
|
|
175
|
+
ansiEscapes.eraseDown = ESC + 'J';
|
|
176
|
+
ansiEscapes.eraseUp = ESC + '1J';
|
|
177
|
+
ansiEscapes.eraseScreen = ESC + '2J';
|
|
178
|
+
ansiEscapes.scrollUp = ESC + 'S';
|
|
179
|
+
ansiEscapes.scrollDown = ESC + 'T';
|
|
180
|
+
|
|
181
|
+
ansiEscapes.clearScreen = '\u001Bc';
|
|
182
|
+
|
|
183
|
+
ansiEscapes.clearTerminal = isWindows
|
|
184
|
+
? `${ansiEscapes.eraseScreen}${ESC}0f`
|
|
185
|
+
// 1. Erases the screen (Only done in case `2` is not supported)
|
|
186
|
+
// 2. Erases the whole screen including scrollback buffer
|
|
187
|
+
// 3. Moves cursor to the top-left position
|
|
188
|
+
// More info: https://www.real-world-systems.com/docs/ANSIcode.html
|
|
189
|
+
: `${ansiEscapes.eraseScreen}${ESC}3J${ESC}H`;
|
|
190
|
+
|
|
191
|
+
ansiEscapes.enterAlternativeScreen = ESC + '?1049h';
|
|
192
|
+
ansiEscapes.exitAlternativeScreen = ESC + '?1049l';
|
|
193
|
+
|
|
194
|
+
ansiEscapes.beep = BEL;
|
|
195
|
+
|
|
196
|
+
ansiEscapes.link = (text, url) => [
|
|
197
|
+
OSC,
|
|
198
|
+
'8',
|
|
199
|
+
SEP,
|
|
200
|
+
SEP,
|
|
201
|
+
url,
|
|
202
|
+
BEL,
|
|
203
|
+
text,
|
|
204
|
+
OSC,
|
|
205
|
+
'8',
|
|
206
|
+
SEP,
|
|
207
|
+
SEP,
|
|
208
|
+
BEL,
|
|
209
|
+
].join('');
|
|
210
|
+
|
|
211
|
+
ansiEscapes.image = (buffer, options = {}) => {
|
|
212
|
+
let returnValue = `${OSC}1337;File=inline=1`;
|
|
213
|
+
|
|
214
|
+
if (options.width) {
|
|
215
|
+
returnValue += `;width=${options.width}`;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (options.height) {
|
|
219
|
+
returnValue += `;height=${options.height}`;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (options.preserveAspectRatio === false) {
|
|
223
|
+
returnValue += ';preserveAspectRatio=0';
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return returnValue + ':' + buffer.toString('base64') + BEL;
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
ansiEscapes.iTerm = {
|
|
230
|
+
setCwd: (cwd = cwdFunction()) => `${OSC}50;CurrentDir=${cwd}${BEL}`,
|
|
231
|
+
|
|
232
|
+
annotation(message, options = {}) {
|
|
233
|
+
let returnValue = `${OSC}1337;`;
|
|
234
|
+
|
|
235
|
+
const hasX = typeof options.x !== 'undefined';
|
|
236
|
+
const hasY = typeof options.y !== 'undefined';
|
|
237
|
+
if ((hasX || hasY) && !(hasX && hasY && typeof options.length !== 'undefined')) {
|
|
238
|
+
throw new Error('`x`, `y` and `length` must be defined when `x` or `y` is defined');
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
message = message.replace(/\|/g, '');
|
|
242
|
+
|
|
243
|
+
returnValue += options.isHidden ? 'AddHiddenAnnotation=' : 'AddAnnotation=';
|
|
244
|
+
|
|
245
|
+
if (options.length > 0) {
|
|
246
|
+
returnValue += (
|
|
247
|
+
hasX
|
|
248
|
+
? [message, options.length, options.x, options.y]
|
|
249
|
+
: [options.length, message]
|
|
250
|
+
).join('|');
|
|
251
|
+
} else {
|
|
252
|
+
returnValue += message;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return returnValue + BEL;
|
|
256
|
+
},
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/*
|
|
260
|
+
* see also https://github.com/vadimdemedes/ink
|
|
261
|
+
*/
|
|
262
|
+
|
|
263
|
+
|
|
264
|
+
const createDynamicLog = ({
|
|
265
|
+
stream = process.stdout,
|
|
266
|
+
clearTerminalAllowed,
|
|
267
|
+
onVerticalOverflow = () => {},
|
|
268
|
+
onWriteFromOutside = () => {},
|
|
269
|
+
} = {}) => {
|
|
270
|
+
const { columns = 80, rows = 24 } = stream;
|
|
271
|
+
const dynamicLog = {
|
|
272
|
+
destroyed: false,
|
|
273
|
+
onVerticalOverflow,
|
|
274
|
+
onWriteFromOutside,
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
let lastOutput = "";
|
|
278
|
+
let lastOutputFromOutside = "";
|
|
279
|
+
let clearAttemptResult;
|
|
280
|
+
let writing = false;
|
|
281
|
+
|
|
282
|
+
const getErasePreviousOutput = () => {
|
|
283
|
+
// nothing to clear
|
|
284
|
+
if (!lastOutput) {
|
|
285
|
+
return "";
|
|
286
|
+
}
|
|
287
|
+
if (clearAttemptResult !== undefined) {
|
|
288
|
+
return "";
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
const logLines = lastOutput.split(/\r\n|\r|\n/);
|
|
292
|
+
let visualLineCount = 0;
|
|
293
|
+
for (const logLine of logLines) {
|
|
294
|
+
const width = stringWidth(logLine);
|
|
295
|
+
if (width === 0) {
|
|
296
|
+
visualLineCount++;
|
|
297
|
+
} else {
|
|
298
|
+
visualLineCount += Math.ceil(width / columns);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (visualLineCount > rows) {
|
|
303
|
+
if (clearTerminalAllowed) {
|
|
304
|
+
clearAttemptResult = true;
|
|
305
|
+
return ansiEscapes.clearTerminal;
|
|
306
|
+
}
|
|
307
|
+
// the whole log cannot be cleared because it's vertically to long
|
|
308
|
+
// (longer than terminal height)
|
|
309
|
+
// readline.moveCursor cannot move cursor higher than screen height
|
|
310
|
+
// it means we would only clear the visible part of the log
|
|
311
|
+
// better keep the log untouched
|
|
312
|
+
clearAttemptResult = false;
|
|
313
|
+
dynamicLog.onVerticalOverflow();
|
|
314
|
+
return "";
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
clearAttemptResult = true;
|
|
318
|
+
return ansiEscapes.eraseLines(visualLineCount);
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
const update = (string) => {
|
|
322
|
+
if (dynamicLog.destroyed) {
|
|
323
|
+
throw new Error("Cannot write log after destroy");
|
|
324
|
+
}
|
|
325
|
+
let stringToWrite = string;
|
|
326
|
+
if (lastOutput) {
|
|
327
|
+
if (lastOutputFromOutside) {
|
|
328
|
+
// We don't want to clear logs written by other code,
|
|
329
|
+
// it makes output unreadable and might erase precious information
|
|
330
|
+
// To detect this we put a spy on the stream.
|
|
331
|
+
// The spy is required only if we actually wrote something in the stream
|
|
332
|
+
// something else than this code has written in the stream
|
|
333
|
+
// so we just write without clearing (append instead of replacing)
|
|
334
|
+
lastOutput = "";
|
|
335
|
+
lastOutputFromOutside = "";
|
|
336
|
+
} else {
|
|
337
|
+
stringToWrite = `${getErasePreviousOutput()}${string}`;
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
writing = true;
|
|
341
|
+
stream.write(stringToWrite);
|
|
342
|
+
lastOutput = string;
|
|
343
|
+
writing = false;
|
|
344
|
+
clearAttemptResult = undefined;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const clearDuringFunctionCall = (callback) => {
|
|
348
|
+
// 1. Erase the current log
|
|
349
|
+
// 2. Call callback (expected to write something on stdout)
|
|
350
|
+
// 3. Restore the current log
|
|
351
|
+
// During step 2. we expect a "write from outside" so we uninstall
|
|
352
|
+
// the stream spy during function call
|
|
353
|
+
const currentOutput = lastOutput;
|
|
354
|
+
update("");
|
|
355
|
+
|
|
356
|
+
writing = true;
|
|
357
|
+
callback();
|
|
358
|
+
writing = false;
|
|
359
|
+
|
|
360
|
+
update(currentOutput);
|
|
361
|
+
};
|
|
362
|
+
|
|
363
|
+
const writeFromOutsideEffect = (value) => {
|
|
364
|
+
if (!lastOutput) {
|
|
365
|
+
// we don't care if the log never wrote anything
|
|
366
|
+
// or if last update() wrote an empty string
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
if (writing) {
|
|
370
|
+
return;
|
|
371
|
+
}
|
|
372
|
+
lastOutputFromOutside = value;
|
|
373
|
+
dynamicLog.onWriteFromOutside(value);
|
|
374
|
+
};
|
|
375
|
+
|
|
376
|
+
let removeStreamSpy;
|
|
377
|
+
if (stream === process.stdout) {
|
|
378
|
+
const removeStdoutSpy = spyStreamOutput(
|
|
379
|
+
process.stdout,
|
|
380
|
+
writeFromOutsideEffect,
|
|
381
|
+
);
|
|
382
|
+
const removeStderrSpy = spyStreamOutput(
|
|
383
|
+
process.stderr,
|
|
384
|
+
writeFromOutsideEffect,
|
|
385
|
+
);
|
|
386
|
+
removeStreamSpy = () => {
|
|
387
|
+
removeStdoutSpy();
|
|
388
|
+
removeStderrSpy();
|
|
389
|
+
};
|
|
390
|
+
} else {
|
|
391
|
+
removeStreamSpy = spyStreamOutput(stream, writeFromOutsideEffect);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
const destroy = () => {
|
|
395
|
+
dynamicLog.destroyed = true;
|
|
396
|
+
if (removeStreamSpy) {
|
|
397
|
+
removeStreamSpy();
|
|
398
|
+
removeStreamSpy = null;
|
|
399
|
+
lastOutput = "";
|
|
400
|
+
lastOutputFromOutside = "";
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
Object.assign(dynamicLog, {
|
|
405
|
+
update,
|
|
406
|
+
destroy,
|
|
407
|
+
stream,
|
|
408
|
+
clearDuringFunctionCall,
|
|
409
|
+
});
|
|
410
|
+
return dynamicLog;
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
// maybe https://github.com/gajus/output-interceptor/tree/v3.0.0 ?
|
|
414
|
+
// the problem with listening data on stdout
|
|
415
|
+
// is that node.js will later throw error if stream gets closed
|
|
416
|
+
// while something listening data on it
|
|
417
|
+
const spyStreamOutput = (stream, callback) => {
|
|
418
|
+
const originalWrite = stream.write;
|
|
419
|
+
|
|
420
|
+
let output = "";
|
|
421
|
+
let installed = true;
|
|
422
|
+
|
|
423
|
+
stream.write = function (...args /* chunk, encoding, callback */) {
|
|
424
|
+
output += args;
|
|
425
|
+
callback(output);
|
|
426
|
+
return originalWrite.call(stream, ...args);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
const uninstall = () => {
|
|
430
|
+
if (!installed) {
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
stream.write = originalWrite;
|
|
434
|
+
installed = false;
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
return () => {
|
|
438
|
+
uninstall();
|
|
439
|
+
return output;
|
|
440
|
+
};
|
|
441
|
+
};
|
|
442
|
+
|
|
443
|
+
// From: https://github.com/sindresorhus/has-flag/blob/main/index.js
|
|
444
|
+
/// function hasFlag(flag, argv = globalThis.Deno?.args ?? process.argv) {
|
|
445
|
+
function hasFlag(flag, argv = globalThis.Deno ? globalThis.Deno.args : process$1.argv) {
|
|
446
|
+
const prefix = flag.startsWith('-') ? '' : (flag.length === 1 ? '-' : '--');
|
|
447
|
+
const position = argv.indexOf(prefix + flag);
|
|
448
|
+
const terminatorPosition = argv.indexOf('--');
|
|
449
|
+
return position !== -1 && (terminatorPosition === -1 || position < terminatorPosition);
|
|
450
|
+
}
|
|
451
|
+
|
|
452
|
+
const {env} = process$1;
|
|
453
|
+
|
|
454
|
+
let flagForceColor;
|
|
455
|
+
if (
|
|
456
|
+
hasFlag('no-color')
|
|
457
|
+
|| hasFlag('no-colors')
|
|
458
|
+
|| hasFlag('color=false')
|
|
459
|
+
|| hasFlag('color=never')
|
|
460
|
+
) {
|
|
461
|
+
flagForceColor = 0;
|
|
462
|
+
} else if (
|
|
463
|
+
hasFlag('color')
|
|
464
|
+
|| hasFlag('colors')
|
|
465
|
+
|| hasFlag('color=true')
|
|
466
|
+
|| hasFlag('color=always')
|
|
467
|
+
) {
|
|
468
|
+
flagForceColor = 1;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function envForceColor() {
|
|
472
|
+
if ('FORCE_COLOR' in env) {
|
|
473
|
+
if (env.FORCE_COLOR === 'true') {
|
|
474
|
+
return 1;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (env.FORCE_COLOR === 'false') {
|
|
478
|
+
return 0;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return env.FORCE_COLOR.length === 0 ? 1 : Math.min(Number.parseInt(env.FORCE_COLOR, 10), 3);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
function translateLevel(level) {
|
|
486
|
+
if (level === 0) {
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
return {
|
|
491
|
+
level,
|
|
492
|
+
hasBasic: true,
|
|
493
|
+
has256: level >= 2,
|
|
494
|
+
has16m: level >= 3,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
function _supportsColor(haveStream, {streamIsTTY, sniffFlags = true} = {}) {
|
|
499
|
+
const noFlagForceColor = envForceColor();
|
|
500
|
+
if (noFlagForceColor !== undefined) {
|
|
501
|
+
flagForceColor = noFlagForceColor;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
const forceColor = sniffFlags ? flagForceColor : noFlagForceColor;
|
|
505
|
+
|
|
506
|
+
if (forceColor === 0) {
|
|
507
|
+
return 0;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
if (sniffFlags) {
|
|
511
|
+
if (hasFlag('color=16m')
|
|
512
|
+
|| hasFlag('color=full')
|
|
513
|
+
|| hasFlag('color=truecolor')) {
|
|
514
|
+
return 3;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
if (hasFlag('color=256')) {
|
|
518
|
+
return 2;
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
// Check for Azure DevOps pipelines.
|
|
523
|
+
// Has to be above the `!streamIsTTY` check.
|
|
524
|
+
if ('TF_BUILD' in env && 'AGENT_NAME' in env) {
|
|
525
|
+
return 1;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
if (haveStream && !streamIsTTY && forceColor === undefined) {
|
|
529
|
+
return 0;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
const min = forceColor || 0;
|
|
533
|
+
|
|
534
|
+
if (env.TERM === 'dumb') {
|
|
535
|
+
return min;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
if (process$1.platform === 'win32') {
|
|
539
|
+
// Windows 10 build 10586 is the first Windows release that supports 256 colors.
|
|
540
|
+
// Windows 10 build 14931 is the first release that supports 16m/TrueColor.
|
|
541
|
+
const osRelease = os.release().split('.');
|
|
542
|
+
if (
|
|
543
|
+
Number(osRelease[0]) >= 10
|
|
544
|
+
&& Number(osRelease[2]) >= 10_586
|
|
545
|
+
) {
|
|
546
|
+
return Number(osRelease[2]) >= 14_931 ? 3 : 2;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return 1;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
if ('CI' in env) {
|
|
553
|
+
if ('GITHUB_ACTIONS' in env || 'GITEA_ACTIONS' in env) {
|
|
554
|
+
return 3;
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
if (['TRAVIS', 'CIRCLECI', 'APPVEYOR', 'GITLAB_CI', 'BUILDKITE', 'DRONE'].some(sign => sign in env) || env.CI_NAME === 'codeship') {
|
|
558
|
+
return 1;
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
return min;
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
if ('TEAMCITY_VERSION' in env) {
|
|
565
|
+
return /^(9\.(0*[1-9]\d*)\.|\d{2,}\.)/.test(env.TEAMCITY_VERSION) ? 1 : 0;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
if (env.COLORTERM === 'truecolor') {
|
|
569
|
+
return 3;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (env.TERM === 'xterm-kitty') {
|
|
573
|
+
return 3;
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if ('TERM_PROGRAM' in env) {
|
|
577
|
+
const version = Number.parseInt((env.TERM_PROGRAM_VERSION || '').split('.')[0], 10);
|
|
578
|
+
|
|
579
|
+
switch (env.TERM_PROGRAM) {
|
|
580
|
+
case 'iTerm.app': {
|
|
581
|
+
return version >= 3 ? 3 : 2;
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
case 'Apple_Terminal': {
|
|
585
|
+
return 2;
|
|
586
|
+
}
|
|
587
|
+
// No default
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (/-256(color)?$/i.test(env.TERM)) {
|
|
592
|
+
return 2;
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
if (/^screen|^xterm|^vt100|^vt220|^rxvt|color|ansi|cygwin|linux/i.test(env.TERM)) {
|
|
596
|
+
return 1;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if ('COLORTERM' in env) {
|
|
600
|
+
return 1;
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return min;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
function createSupportsColor(stream, options = {}) {
|
|
607
|
+
const level = _supportsColor(stream, {
|
|
608
|
+
streamIsTTY: stream && stream.isTTY,
|
|
609
|
+
...options,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
return translateLevel(level);
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
({
|
|
616
|
+
stdout: createSupportsColor({isTTY: tty.isatty(1)}),
|
|
617
|
+
stderr: createSupportsColor({isTTY: tty.isatty(2)}),
|
|
618
|
+
});
|
|
619
|
+
|
|
620
|
+
const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic;
|
|
621
|
+
// https://github.com/Marak/colors.js/blob/master/lib/styles.js
|
|
622
|
+
// https://stackoverflow.com/a/75985833/2634179
|
|
623
|
+
const RESET = "\x1b[0m";
|
|
624
|
+
|
|
625
|
+
const ANSI = {
|
|
626
|
+
supported: processSupportsBasicColor,
|
|
627
|
+
|
|
628
|
+
RED: "\x1b[31m",
|
|
629
|
+
GREEN: "\x1b[32m",
|
|
630
|
+
YELLOW: "\x1b[33m",
|
|
631
|
+
BLUE: "\x1b[34m",
|
|
632
|
+
MAGENTA: "\x1b[35m",
|
|
633
|
+
GREY: "\x1b[90m",
|
|
634
|
+
color: (text, ANSI_COLOR) => {
|
|
635
|
+
return ANSI.supported ? `${ANSI_COLOR}${text}${RESET}` : text;
|
|
636
|
+
},
|
|
637
|
+
|
|
638
|
+
BOLD: "\x1b[1m",
|
|
639
|
+
effect: (text, ANSI_EFFECT) => {
|
|
640
|
+
return ANSI.supported ? `${ANSI_EFFECT}${text}${RESET}` : text;
|
|
641
|
+
},
|
|
642
|
+
};
|
|
643
|
+
|
|
644
|
+
// GitHub workflow does support ANSI but "supports-color" returns false
|
|
645
|
+
// because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
|
|
646
|
+
if (
|
|
647
|
+
process.env.GITHUB_WORKFLOW &&
|
|
648
|
+
// Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
|
|
649
|
+
// in unit test we use process.env.FORCE_COLOR = 'false' to fake
|
|
650
|
+
// that colors are not supported. Let it have priority
|
|
651
|
+
process.env.FORCE_COLOR !== "false"
|
|
652
|
+
) {
|
|
653
|
+
ANSI.supported = true;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const startSpinner = ({
|
|
657
|
+
dynamicLog,
|
|
658
|
+
frames = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"],
|
|
659
|
+
fps = 20,
|
|
660
|
+
keepProcessAlive = false,
|
|
661
|
+
stopOnWriteFromOutside = true,
|
|
662
|
+
stopOnVerticalOverflow = true,
|
|
663
|
+
render = () => "",
|
|
664
|
+
effect = () => {},
|
|
665
|
+
animated = dynamicLog.stream.isTTY,
|
|
666
|
+
}) => {
|
|
667
|
+
let frameIndex = 0;
|
|
668
|
+
let interval;
|
|
669
|
+
let running = true;
|
|
670
|
+
|
|
671
|
+
const spinner = {
|
|
672
|
+
message: undefined,
|
|
673
|
+
};
|
|
674
|
+
|
|
675
|
+
const update = (message) => {
|
|
676
|
+
spinner.message = running
|
|
677
|
+
? `${frames[frameIndex]} ${message}\n`
|
|
678
|
+
: `${message}\n`;
|
|
679
|
+
return spinner.message;
|
|
680
|
+
};
|
|
681
|
+
spinner.update = update;
|
|
682
|
+
|
|
683
|
+
let cleanup;
|
|
684
|
+
if (animated && ANSI.supported) {
|
|
685
|
+
running = true;
|
|
686
|
+
cleanup = effect();
|
|
687
|
+
dynamicLog.update(update(render()));
|
|
688
|
+
|
|
689
|
+
interval = setInterval(() => {
|
|
690
|
+
frameIndex = frameIndex === frames.length - 1 ? 0 : frameIndex + 1;
|
|
691
|
+
dynamicLog.update(update(render()));
|
|
692
|
+
}, 1000 / fps);
|
|
693
|
+
if (!keepProcessAlive) {
|
|
694
|
+
interval.unref();
|
|
695
|
+
}
|
|
696
|
+
} else {
|
|
697
|
+
dynamicLog.update(update(render()));
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
const stop = (message) => {
|
|
701
|
+
running = false;
|
|
702
|
+
if (interval) {
|
|
703
|
+
clearInterval(interval);
|
|
704
|
+
interval = null;
|
|
705
|
+
}
|
|
706
|
+
if (cleanup) {
|
|
707
|
+
cleanup();
|
|
708
|
+
cleanup = null;
|
|
709
|
+
}
|
|
710
|
+
if (dynamicLog && message) {
|
|
711
|
+
dynamicLog.update(update(message));
|
|
712
|
+
dynamicLog = null;
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
spinner.stop = stop;
|
|
716
|
+
|
|
717
|
+
if (stopOnVerticalOverflow) {
|
|
718
|
+
dynamicLog.onVerticalOverflow = stop;
|
|
719
|
+
}
|
|
720
|
+
if (stopOnWriteFromOutside) {
|
|
721
|
+
dynamicLog.onWriteFromOutside = stop;
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
return spinner;
|
|
725
|
+
};
|
|
726
|
+
|
|
727
|
+
const getPrecision = (number) => {
|
|
728
|
+
if (Math.floor(number) === number) return 0;
|
|
729
|
+
const [, decimals] = number.toString().split(".");
|
|
730
|
+
return decimals.length || 0;
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
const setRoundedPrecision = (
|
|
734
|
+
number,
|
|
735
|
+
{ decimals = 1, decimalsWhenSmall = decimals } = {},
|
|
736
|
+
) => {
|
|
737
|
+
return setDecimalsPrecision(number, {
|
|
738
|
+
decimals,
|
|
739
|
+
decimalsWhenSmall,
|
|
740
|
+
transform: Math.round,
|
|
741
|
+
});
|
|
742
|
+
};
|
|
743
|
+
|
|
744
|
+
const setPrecision = (
|
|
745
|
+
number,
|
|
746
|
+
{ decimals = 1, decimalsWhenSmall = decimals } = {},
|
|
747
|
+
) => {
|
|
748
|
+
return setDecimalsPrecision(number, {
|
|
749
|
+
decimals,
|
|
750
|
+
decimalsWhenSmall,
|
|
751
|
+
transform: parseInt,
|
|
752
|
+
});
|
|
753
|
+
};
|
|
754
|
+
|
|
755
|
+
const setDecimalsPrecision = (
|
|
756
|
+
number,
|
|
757
|
+
{
|
|
758
|
+
transform,
|
|
759
|
+
decimals, // max decimals for number in [-Infinity, -1[]1, Infinity]
|
|
760
|
+
decimalsWhenSmall, // max decimals for number in [-1,1]
|
|
761
|
+
} = {},
|
|
762
|
+
) => {
|
|
763
|
+
if (number === 0) {
|
|
764
|
+
return 0;
|
|
765
|
+
}
|
|
766
|
+
let numberCandidate = Math.abs(number);
|
|
767
|
+
if (numberCandidate < 1) {
|
|
768
|
+
const integerGoal = Math.pow(10, decimalsWhenSmall - 1);
|
|
769
|
+
let i = 1;
|
|
770
|
+
while (numberCandidate < integerGoal) {
|
|
771
|
+
numberCandidate *= 10;
|
|
772
|
+
i *= 10;
|
|
773
|
+
}
|
|
774
|
+
const asInteger = transform(numberCandidate);
|
|
775
|
+
const asFloat = asInteger / i;
|
|
776
|
+
return number < 0 ? -asFloat : asFloat;
|
|
777
|
+
}
|
|
778
|
+
const coef = Math.pow(10, decimals);
|
|
779
|
+
const numberMultiplied = (number + Number.EPSILON) * coef;
|
|
780
|
+
const asInteger = transform(numberMultiplied);
|
|
781
|
+
const asFloat = asInteger / coef;
|
|
782
|
+
return number < 0 ? -asFloat : asFloat;
|
|
783
|
+
};
|
|
784
|
+
|
|
785
|
+
// https://www.codingem.com/javascript-how-to-limit-decimal-places/
|
|
786
|
+
// export const roundNumber = (number, maxDecimals) => {
|
|
787
|
+
// const decimalsExp = Math.pow(10, maxDecimals)
|
|
788
|
+
// const numberRoundInt = Math.round(decimalsExp * (number + Number.EPSILON))
|
|
789
|
+
// const numberRoundFloat = numberRoundInt / decimalsExp
|
|
790
|
+
// return numberRoundFloat
|
|
791
|
+
// }
|
|
792
|
+
|
|
793
|
+
// export const setPrecision = (number, precision) => {
|
|
794
|
+
// if (Math.floor(number) === number) return number
|
|
795
|
+
// const [int, decimals] = number.toString().split(".")
|
|
796
|
+
// if (precision <= 0) return int
|
|
797
|
+
// const numberTruncated = `${int}.${decimals.slice(0, precision)}`
|
|
798
|
+
// return numberTruncated
|
|
799
|
+
// }
|
|
800
|
+
|
|
801
|
+
const humanizeEllapsedTime = (ms, { short } = {}) => {
|
|
802
|
+
if (ms < 1000) {
|
|
803
|
+
return short ? "0s" : "0 second";
|
|
804
|
+
}
|
|
805
|
+
const { primary, remaining } = parseMs(ms);
|
|
806
|
+
if (!remaining) {
|
|
807
|
+
return inspectEllapsedUnit(primary, short);
|
|
808
|
+
}
|
|
809
|
+
return `${inspectEllapsedUnit(primary, short)} and ${inspectEllapsedUnit(
|
|
810
|
+
remaining,
|
|
811
|
+
short,
|
|
812
|
+
)}`;
|
|
813
|
+
};
|
|
814
|
+
const inspectEllapsedUnit = (unit, short) => {
|
|
815
|
+
const count =
|
|
816
|
+
unit.name === "second" ? Math.floor(unit.count) : Math.round(unit.count);
|
|
817
|
+
let name = unit.name;
|
|
818
|
+
if (short) {
|
|
819
|
+
name = unitShort[name];
|
|
820
|
+
if (count <= 1) {
|
|
821
|
+
return `${count}${name}`;
|
|
822
|
+
}
|
|
823
|
+
return `${count}${name}s`;
|
|
824
|
+
}
|
|
825
|
+
if (count <= 1) {
|
|
826
|
+
return `${count} ${name}`;
|
|
827
|
+
}
|
|
828
|
+
return `${count} ${name}s`;
|
|
829
|
+
};
|
|
830
|
+
const unitShort = {
|
|
831
|
+
year: "y",
|
|
832
|
+
month: "m",
|
|
833
|
+
week: "w",
|
|
834
|
+
day: "d",
|
|
835
|
+
hour: "h",
|
|
836
|
+
minute: "m",
|
|
837
|
+
second: "s",
|
|
838
|
+
};
|
|
839
|
+
|
|
840
|
+
const humanizeDuration = (
|
|
841
|
+
ms,
|
|
842
|
+
{ short, rounded = true, decimals } = {},
|
|
843
|
+
) => {
|
|
844
|
+
// ignore ms below meaningfulMs so that:
|
|
845
|
+
// humanizeDuration(0.5) -> "0 second"
|
|
846
|
+
// humanizeDuration(1.1) -> "0.001 second" (and not "0.0011 second")
|
|
847
|
+
// This tool is meant to be read by humans and it would be barely readable to see
|
|
848
|
+
// "0.0001 second" (stands for 0.1 millisecond)
|
|
849
|
+
// yes we could return "0.1 millisecond" but we choosed consistency over precision
|
|
850
|
+
// so that the prefered unit is "second" (and does not become millisecond when ms is super small)
|
|
851
|
+
if (ms < 1) {
|
|
852
|
+
return short ? "0s" : "0 second";
|
|
853
|
+
}
|
|
854
|
+
const { primary, remaining } = parseMs(ms);
|
|
855
|
+
if (!remaining) {
|
|
856
|
+
return humanizeDurationUnit(primary, {
|
|
857
|
+
decimals:
|
|
858
|
+
decimals === undefined ? (primary.name === "second" ? 1 : 0) : decimals,
|
|
859
|
+
short,
|
|
860
|
+
rounded,
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
return `${humanizeDurationUnit(primary, {
|
|
864
|
+
decimals: decimals === undefined ? 0 : decimals,
|
|
865
|
+
short,
|
|
866
|
+
rounded,
|
|
867
|
+
})} and ${humanizeDurationUnit(remaining, {
|
|
868
|
+
decimals: decimals === undefined ? 0 : decimals,
|
|
869
|
+
short,
|
|
870
|
+
rounded,
|
|
871
|
+
})}`;
|
|
872
|
+
};
|
|
873
|
+
const humanizeDurationUnit = (unit, { decimals, short, rounded }) => {
|
|
874
|
+
const count = rounded
|
|
875
|
+
? setRoundedPrecision(unit.count, { decimals })
|
|
876
|
+
: setPrecision(unit.count, { decimals });
|
|
877
|
+
let name = unit.name;
|
|
878
|
+
if (short) {
|
|
879
|
+
name = unitShort[name];
|
|
880
|
+
return `${count}${name}`;
|
|
881
|
+
}
|
|
882
|
+
if (count <= 1) {
|
|
883
|
+
return `${count} ${name}`;
|
|
884
|
+
}
|
|
885
|
+
return `${count} ${name}s`;
|
|
886
|
+
};
|
|
887
|
+
const MS_PER_UNITS = {
|
|
888
|
+
year: 31_557_600_000,
|
|
889
|
+
month: 2_629_000_000,
|
|
890
|
+
week: 604_800_000,
|
|
891
|
+
day: 86_400_000,
|
|
892
|
+
hour: 3_600_000,
|
|
893
|
+
minute: 60_000,
|
|
894
|
+
second: 1000,
|
|
895
|
+
};
|
|
896
|
+
|
|
897
|
+
const parseMs = (ms) => {
|
|
898
|
+
const unitNames = Object.keys(MS_PER_UNITS);
|
|
899
|
+
const smallestUnitName = unitNames[unitNames.length - 1];
|
|
900
|
+
let firstUnitName = smallestUnitName;
|
|
901
|
+
let firstUnitCount = ms / MS_PER_UNITS[smallestUnitName];
|
|
902
|
+
const firstUnitIndex = unitNames.findIndex((unitName) => {
|
|
903
|
+
if (unitName === smallestUnitName) {
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
const msPerUnit = MS_PER_UNITS[unitName];
|
|
907
|
+
const unitCount = Math.floor(ms / msPerUnit);
|
|
908
|
+
if (unitCount) {
|
|
909
|
+
firstUnitName = unitName;
|
|
910
|
+
firstUnitCount = unitCount;
|
|
911
|
+
return true;
|
|
912
|
+
}
|
|
913
|
+
return false;
|
|
914
|
+
});
|
|
915
|
+
if (firstUnitName === smallestUnitName) {
|
|
916
|
+
return {
|
|
917
|
+
primary: {
|
|
918
|
+
name: firstUnitName,
|
|
919
|
+
count: firstUnitCount,
|
|
920
|
+
},
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
const remainingMs = ms - firstUnitCount * MS_PER_UNITS[firstUnitName];
|
|
924
|
+
const remainingUnitName = unitNames[firstUnitIndex + 1];
|
|
925
|
+
const remainingUnitCount = remainingMs / MS_PER_UNITS[remainingUnitName];
|
|
926
|
+
// - 1 year and 1 second is too much information
|
|
927
|
+
// so we don't check the remaining units
|
|
928
|
+
// - 1 year and 0.0001 week is awful
|
|
929
|
+
// hence the if below
|
|
930
|
+
if (Math.round(remainingUnitCount) < 1) {
|
|
931
|
+
return {
|
|
932
|
+
primary: {
|
|
933
|
+
name: firstUnitName,
|
|
934
|
+
count: firstUnitCount,
|
|
935
|
+
},
|
|
936
|
+
};
|
|
937
|
+
}
|
|
938
|
+
// - 1 year and 1 month is great
|
|
939
|
+
return {
|
|
940
|
+
primary: {
|
|
941
|
+
name: firstUnitName,
|
|
942
|
+
count: firstUnitCount,
|
|
943
|
+
},
|
|
944
|
+
remaining: {
|
|
945
|
+
name: remainingUnitName,
|
|
946
|
+
count: remainingUnitCount,
|
|
947
|
+
},
|
|
948
|
+
};
|
|
949
|
+
};
|
|
950
|
+
|
|
951
|
+
function isUnicodeSupported() {
|
|
952
|
+
if (process$1.platform !== 'win32') {
|
|
953
|
+
return process$1.env.TERM !== 'linux'; // Linux console (kernel)
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
return Boolean(process$1.env.WT_SESSION) // Windows Terminal
|
|
957
|
+
|| Boolean(process$1.env.TERMINUS_SUBLIME) // Terminus (<0.2.27)
|
|
958
|
+
|| process$1.env.ConEmuTask === '{cmd::Cmder}' // ConEmu and cmder
|
|
959
|
+
|| process$1.env.TERM_PROGRAM === 'Terminus-Sublime'
|
|
960
|
+
|| process$1.env.TERM_PROGRAM === 'vscode'
|
|
961
|
+
|| process$1.env.TERM === 'xterm-256color'
|
|
962
|
+
|| process$1.env.TERM === 'alacritty'
|
|
963
|
+
|| process$1.env.TERMINAL_EMULATOR === 'JetBrains-JediTerm';
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
// see also https://github.com/sindresorhus/figures
|
|
967
|
+
|
|
968
|
+
|
|
969
|
+
const UNICODE = {
|
|
970
|
+
supported: isUnicodeSupported(),
|
|
971
|
+
|
|
972
|
+
get COMMAND_RAW() {
|
|
973
|
+
return UNICODE.supported ? `❯` : `>`;
|
|
974
|
+
},
|
|
975
|
+
get OK_RAW() {
|
|
976
|
+
return UNICODE.supported ? `✔` : `√`;
|
|
977
|
+
},
|
|
978
|
+
get FAILURE_RAW() {
|
|
979
|
+
return UNICODE.supported ? `✖` : `×`;
|
|
980
|
+
},
|
|
981
|
+
get DEBUG_RAW() {
|
|
982
|
+
return UNICODE.supported ? `◆` : `♦`;
|
|
983
|
+
},
|
|
984
|
+
get INFO_RAW() {
|
|
985
|
+
return UNICODE.supported ? `ℹ` : `i`;
|
|
986
|
+
},
|
|
987
|
+
get WARNING_RAW() {
|
|
988
|
+
return UNICODE.supported ? `⚠` : `‼`;
|
|
989
|
+
},
|
|
990
|
+
get CIRCLE_CROSS_RAW() {
|
|
991
|
+
return UNICODE.supported ? `ⓧ` : `(×)`;
|
|
992
|
+
},
|
|
993
|
+
get COMMAND() {
|
|
994
|
+
return ANSI.color(UNICODE.COMMAND_RAW, ANSI.GREY); // ANSI_MAGENTA)
|
|
995
|
+
},
|
|
996
|
+
get OK() {
|
|
997
|
+
return ANSI.color(UNICODE.OK_RAW, ANSI.GREEN);
|
|
998
|
+
},
|
|
999
|
+
get FAILURE() {
|
|
1000
|
+
return ANSI.color(UNICODE.FAILURE_RAW, ANSI.RED);
|
|
1001
|
+
},
|
|
1002
|
+
get DEBUG() {
|
|
1003
|
+
return ANSI.color(UNICODE.DEBUG_RAW, ANSI.GREY);
|
|
1004
|
+
},
|
|
1005
|
+
get INFO() {
|
|
1006
|
+
return ANSI.color(UNICODE.INFO_RAW, ANSI.BLUE);
|
|
1007
|
+
},
|
|
1008
|
+
get WARNING() {
|
|
1009
|
+
return ANSI.color(UNICODE.WARNING_RAW, ANSI.YELLOW);
|
|
1010
|
+
},
|
|
1011
|
+
get CIRCLE_CROSS() {
|
|
1012
|
+
return ANSI.color(UNICODE.CIRCLE_CROSS_RAW, ANSI.RED);
|
|
1013
|
+
},
|
|
1014
|
+
};
|
|
1015
|
+
|
|
1016
|
+
const createTaskLog = (
|
|
1017
|
+
label,
|
|
1018
|
+
{ disabled = false, animated = true, stopOnWriteFromOutside } = {},
|
|
1019
|
+
) => {
|
|
1020
|
+
if (disabled) {
|
|
1021
|
+
return {
|
|
1022
|
+
setRightText: () => {},
|
|
1023
|
+
done: () => {},
|
|
1024
|
+
happen: () => {},
|
|
1025
|
+
fail: () => {},
|
|
1026
|
+
};
|
|
1027
|
+
}
|
|
1028
|
+
const startMs = Date.now();
|
|
1029
|
+
const dynamicLog = createDynamicLog();
|
|
1030
|
+
let message = label;
|
|
1031
|
+
const taskSpinner = startSpinner({
|
|
1032
|
+
dynamicLog,
|
|
1033
|
+
render: () => message,
|
|
1034
|
+
stopOnWriteFromOutside,
|
|
1035
|
+
animated,
|
|
1036
|
+
});
|
|
1037
|
+
return {
|
|
1038
|
+
setRightText: (value) => {
|
|
1039
|
+
message = `${label} ${value}`;
|
|
1040
|
+
},
|
|
1041
|
+
done: () => {
|
|
1042
|
+
const msEllapsed = Date.now() - startMs;
|
|
1043
|
+
taskSpinner.stop(
|
|
1044
|
+
`${UNICODE.OK} ${label} (done in ${humanizeDuration(msEllapsed)})`,
|
|
1045
|
+
);
|
|
1046
|
+
},
|
|
1047
|
+
happen: (message) => {
|
|
1048
|
+
taskSpinner.stop(
|
|
1049
|
+
`${UNICODE.INFO} ${message} (at ${new Date().toLocaleTimeString()})`,
|
|
1050
|
+
);
|
|
1051
|
+
},
|
|
1052
|
+
fail: (message = `failed to ${label}`) => {
|
|
1053
|
+
taskSpinner.stop(`${UNICODE.FAILURE} ${message}`);
|
|
1054
|
+
},
|
|
1055
|
+
};
|
|
1056
|
+
};
|
|
1057
|
+
|
|
1058
|
+
const createDetailedMessage = (message, details = {}) => {
|
|
1059
|
+
let string = `${message}`;
|
|
1060
|
+
|
|
1061
|
+
Object.keys(details).forEach((key) => {
|
|
1062
|
+
const value = details[key];
|
|
1063
|
+
string += `
|
|
1064
|
+
--- ${key} ---
|
|
1065
|
+
${
|
|
1066
|
+
Array.isArray(value)
|
|
1067
|
+
? value.join(`
|
|
1068
|
+
`)
|
|
1069
|
+
: value
|
|
1070
|
+
}`;
|
|
1071
|
+
});
|
|
1072
|
+
|
|
1073
|
+
return string;
|
|
1074
|
+
};
|
|
1075
|
+
|
|
1076
|
+
const inspectBoolean = (value) => value.toString();
|
|
1077
|
+
|
|
1078
|
+
const inspectNull = () => "null";
|
|
1079
|
+
|
|
1080
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/rules/numeric-separators-style.js
|
|
1081
|
+
|
|
1082
|
+
const inspectNumber = (value, { numericSeparator }) => {
|
|
1083
|
+
if (isNegativeZero(value)) {
|
|
1084
|
+
return "-0";
|
|
1085
|
+
}
|
|
1086
|
+
// isNaN
|
|
1087
|
+
// eslint-disable-next-line no-self-compare
|
|
1088
|
+
if (value !== value) {
|
|
1089
|
+
return "NaN";
|
|
1090
|
+
}
|
|
1091
|
+
if (value === Infinity) {
|
|
1092
|
+
return "Infinity";
|
|
1093
|
+
}
|
|
1094
|
+
if (value === -Infinity) {
|
|
1095
|
+
return "-Infinity";
|
|
1096
|
+
}
|
|
1097
|
+
const numberString = String(value);
|
|
1098
|
+
if (!numericSeparator) {
|
|
1099
|
+
return numberString;
|
|
1100
|
+
}
|
|
1101
|
+
const {
|
|
1102
|
+
number,
|
|
1103
|
+
mark = "",
|
|
1104
|
+
sign = "",
|
|
1105
|
+
power = "",
|
|
1106
|
+
} = numberString.match(
|
|
1107
|
+
/^(?<number>.*?)(?:(?<mark>e)(?<sign>[+-])?(?<power>\d+))?$/i,
|
|
1108
|
+
).groups;
|
|
1109
|
+
const numberWithSeparators = formatNumber(number);
|
|
1110
|
+
const powerWithSeparators = addSeparator(power, {
|
|
1111
|
+
minimumDigits: 5,
|
|
1112
|
+
groupLength: 3,
|
|
1113
|
+
});
|
|
1114
|
+
return `${numberWithSeparators}${mark}${sign}${powerWithSeparators}`;
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
// Use this and instead of Object.is(value, -0)
|
|
1118
|
+
// because in some corner cases firefox returns false
|
|
1119
|
+
// for Object.is(-0, -0)
|
|
1120
|
+
const isNegativeZero = (value) => {
|
|
1121
|
+
return value === 0 && 1 / value === -Infinity;
|
|
1122
|
+
};
|
|
1123
|
+
|
|
1124
|
+
const formatNumber = (numberString) => {
|
|
1125
|
+
const parts = numberString.split(".");
|
|
1126
|
+
const [integer, fractional] = parts;
|
|
1127
|
+
|
|
1128
|
+
if (parts.length === 2) {
|
|
1129
|
+
const integerWithSeparators = addSeparator(integer, {
|
|
1130
|
+
minimumDigits: 5,
|
|
1131
|
+
groupLength: 3,
|
|
1132
|
+
});
|
|
1133
|
+
return `${integerWithSeparators}.${fractional}`;
|
|
1134
|
+
}
|
|
1135
|
+
|
|
1136
|
+
return addSeparator(integer, {
|
|
1137
|
+
minimumDigits: 5,
|
|
1138
|
+
groupLength: 3,
|
|
1139
|
+
});
|
|
1140
|
+
};
|
|
1141
|
+
|
|
1142
|
+
const addSeparator = (numberString, { minimumDigits, groupLength }) => {
|
|
1143
|
+
if (numberString[0] === "-") {
|
|
1144
|
+
return `-${groupDigits(numberString.slice(1), {
|
|
1145
|
+
minimumDigits,
|
|
1146
|
+
groupLength,
|
|
1147
|
+
})}`;
|
|
1148
|
+
}
|
|
1149
|
+
return groupDigits(numberString, { minimumDigits, groupLength });
|
|
1150
|
+
};
|
|
1151
|
+
|
|
1152
|
+
const groupDigits = (digits, { minimumDigits, groupLength }) => {
|
|
1153
|
+
const digitCount = digits.length;
|
|
1154
|
+
if (digitCount < minimumDigits) {
|
|
1155
|
+
return digits;
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
let digitsWithSeparator = digits.slice(-groupLength);
|
|
1159
|
+
let remainingDigits = digits.slice(0, -groupLength);
|
|
1160
|
+
while (remainingDigits.length) {
|
|
1161
|
+
const group = remainingDigits.slice(-groupLength);
|
|
1162
|
+
remainingDigits = remainingDigits.slice(0, -groupLength);
|
|
1163
|
+
digitsWithSeparator = `${group}_${digitsWithSeparator}`;
|
|
1164
|
+
}
|
|
1165
|
+
return digitsWithSeparator;
|
|
1166
|
+
};
|
|
1167
|
+
|
|
1168
|
+
// const addSeparatorFromLeft = (value, { minimumDigits, groupLength }) => {
|
|
1169
|
+
// const { length } = value;
|
|
1170
|
+
// if (length < minimumDigits) {
|
|
1171
|
+
// return value;
|
|
1172
|
+
// }
|
|
1173
|
+
|
|
1174
|
+
// const parts = [];
|
|
1175
|
+
// for (let start = 0; start < length; start += groupLength) {
|
|
1176
|
+
// const end = Math.min(start + groupLength, length);
|
|
1177
|
+
// parts.push(value.slice(start, end));
|
|
1178
|
+
// }
|
|
1179
|
+
// return parts.join("_");
|
|
1180
|
+
// };
|
|
1181
|
+
|
|
1182
|
+
const DOUBLE_QUOTE = `"`;
|
|
1183
|
+
const SINGLE_QUOTE = `'`;
|
|
1184
|
+
const BACKTICK = "`";
|
|
1185
|
+
|
|
1186
|
+
const inspectString = (
|
|
1187
|
+
value,
|
|
1188
|
+
{
|
|
1189
|
+
quote = "auto",
|
|
1190
|
+
canUseTemplateString = false,
|
|
1191
|
+
preserveLineBreaks = false,
|
|
1192
|
+
quoteDefault = DOUBLE_QUOTE,
|
|
1193
|
+
} = {},
|
|
1194
|
+
) => {
|
|
1195
|
+
quote =
|
|
1196
|
+
quote === "auto"
|
|
1197
|
+
? determineQuote(value, { canUseTemplateString, quoteDefault })
|
|
1198
|
+
: quote;
|
|
1199
|
+
if (quote === BACKTICK) {
|
|
1200
|
+
return `\`${escapeTemplateStringSpecialCharacters(value)}\``;
|
|
1201
|
+
}
|
|
1202
|
+
return surroundStringWith(value, { quote, preserveLineBreaks });
|
|
1203
|
+
};
|
|
1204
|
+
|
|
1205
|
+
// https://github.com/mgenware/string-to-template-literal/blob/main/src/main.ts#L1
|
|
1206
|
+
const escapeTemplateStringSpecialCharacters = (string) => {
|
|
1207
|
+
string = String(string);
|
|
1208
|
+
let i = 0;
|
|
1209
|
+
let escapedString = "";
|
|
1210
|
+
while (i < string.length) {
|
|
1211
|
+
const char = string[i];
|
|
1212
|
+
i++;
|
|
1213
|
+
escapedString += isTemplateStringSpecialChar(char) ? `\\${char}` : char;
|
|
1214
|
+
}
|
|
1215
|
+
return escapedString;
|
|
1216
|
+
};
|
|
1217
|
+
|
|
1218
|
+
const isTemplateStringSpecialChar = (char) =>
|
|
1219
|
+
templateStringSpecialChars.indexOf(char) > -1;
|
|
1220
|
+
const templateStringSpecialChars = ["\\", "`", "$"];
|
|
1221
|
+
|
|
1222
|
+
const determineQuote = (
|
|
1223
|
+
string,
|
|
1224
|
+
{ canUseTemplateString, quoteDefault = DOUBLE_QUOTE } = {},
|
|
1225
|
+
) => {
|
|
1226
|
+
const containsDoubleQuote = string.includes(DOUBLE_QUOTE);
|
|
1227
|
+
if (!containsDoubleQuote) {
|
|
1228
|
+
return DOUBLE_QUOTE;
|
|
1229
|
+
}
|
|
1230
|
+
const containsSimpleQuote = string.includes(SINGLE_QUOTE);
|
|
1231
|
+
if (!containsSimpleQuote) {
|
|
1232
|
+
return SINGLE_QUOTE;
|
|
1233
|
+
}
|
|
1234
|
+
if (canUseTemplateString) {
|
|
1235
|
+
const containsBackTick = string.includes(BACKTICK);
|
|
1236
|
+
if (!containsBackTick) {
|
|
1237
|
+
return BACKTICK;
|
|
1238
|
+
}
|
|
1239
|
+
}
|
|
1240
|
+
const doubleQuoteCount = string.split(DOUBLE_QUOTE).length - 1;
|
|
1241
|
+
const singleQuoteCount = string.split(SINGLE_QUOTE).length - 1;
|
|
1242
|
+
if (singleQuoteCount > doubleQuoteCount) {
|
|
1243
|
+
return DOUBLE_QUOTE;
|
|
1244
|
+
}
|
|
1245
|
+
if (doubleQuoteCount > singleQuoteCount) {
|
|
1246
|
+
return SINGLE_QUOTE;
|
|
1247
|
+
}
|
|
1248
|
+
return quoteDefault;
|
|
1249
|
+
};
|
|
1250
|
+
|
|
1251
|
+
const inspectChar = (char, { quote, preserveLineBreaks }) => {
|
|
1252
|
+
const point = char.charCodeAt(0);
|
|
1253
|
+
if (preserveLineBreaks && (char === "\n" || char === "\r")) {
|
|
1254
|
+
return char;
|
|
1255
|
+
}
|
|
1256
|
+
if (
|
|
1257
|
+
char === quote ||
|
|
1258
|
+
point === 92 ||
|
|
1259
|
+
point < 32 ||
|
|
1260
|
+
(point > 126 && point < 160) ||
|
|
1261
|
+
// line separators
|
|
1262
|
+
point === 8232 ||
|
|
1263
|
+
point === 8233
|
|
1264
|
+
) {
|
|
1265
|
+
const replacement =
|
|
1266
|
+
char === quote
|
|
1267
|
+
? `\\${quote}`
|
|
1268
|
+
: point === 8232
|
|
1269
|
+
? "\\u2028"
|
|
1270
|
+
: point === 8233
|
|
1271
|
+
? "\\u2029"
|
|
1272
|
+
: meta[point];
|
|
1273
|
+
return replacement;
|
|
1274
|
+
}
|
|
1275
|
+
return char;
|
|
1276
|
+
};
|
|
1277
|
+
|
|
1278
|
+
// https://github.com/jsenv/jsenv-uneval/blob/6c97ef9d8f2e9425a66f2c88347e0a118d427f3a/src/internal/escapeString.js#L3
|
|
1279
|
+
// https://github.com/jsenv/jsenv-inspect/blob/bb11de3adf262b68f71ed82b0a37d4528dd42229/src/internal/string.js#L3
|
|
1280
|
+
// https://github.com/joliss/js-string-escape/blob/master/index.js
|
|
1281
|
+
// http://javascript.crockford.com/remedial.html
|
|
1282
|
+
const surroundStringWith = (string, { quote, preserveLineBreaks }) => {
|
|
1283
|
+
let result = "";
|
|
1284
|
+
let last = 0;
|
|
1285
|
+
const lastIndex = string.length;
|
|
1286
|
+
let i = 0;
|
|
1287
|
+
while (i < lastIndex) {
|
|
1288
|
+
const char = string[i];
|
|
1289
|
+
const replacement = inspectChar(char, { quote, preserveLineBreaks });
|
|
1290
|
+
if (char !== replacement) {
|
|
1291
|
+
if (last === i) {
|
|
1292
|
+
result += replacement;
|
|
1293
|
+
} else {
|
|
1294
|
+
result += `${string.slice(last, i)}${replacement}`;
|
|
1295
|
+
}
|
|
1296
|
+
last = i + 1;
|
|
1297
|
+
}
|
|
1298
|
+
i++;
|
|
1299
|
+
}
|
|
1300
|
+
if (last !== lastIndex) {
|
|
1301
|
+
result += string.slice(last);
|
|
1302
|
+
}
|
|
1303
|
+
return `${quote}${result}${quote}`;
|
|
1304
|
+
};
|
|
1305
|
+
|
|
1306
|
+
// prettier-ignore
|
|
1307
|
+
const meta = [
|
|
1308
|
+
'\\x00', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\x07', // x07
|
|
1309
|
+
'\\b', '\\t', '\\n', '\\x0B', '\\f', '\\r', '\\x0E', '\\x0F', // x0F
|
|
1310
|
+
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', // x17
|
|
1311
|
+
'\\x18', '\\x19', '\\x1A', '\\x1B', '\\x1C', '\\x1D', '\\x1E', '\\x1F', // x1F
|
|
1312
|
+
'', '', '', '', '', '', '', "\\'", '', '', '', '', '', '', '', '', // x2F
|
|
1313
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x3F
|
|
1314
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x4F
|
|
1315
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '', // x5F
|
|
1316
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x6F
|
|
1317
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\x7F', // x7F
|
|
1318
|
+
'\\x80', '\\x81', '\\x82', '\\x83', '\\x84', '\\x85', '\\x86', '\\x87', // x87
|
|
1319
|
+
'\\x88', '\\x89', '\\x8A', '\\x8B', '\\x8C', '\\x8D', '\\x8E', '\\x8F', // x8F
|
|
1320
|
+
'\\x90', '\\x91', '\\x92', '\\x93', '\\x94', '\\x95', '\\x96', '\\x97', // x97
|
|
1321
|
+
'\\x98', '\\x99', '\\x9A', '\\x9B', '\\x9C', '\\x9D', '\\x9E', '\\x9F', // x9F
|
|
1322
|
+
];
|
|
1323
|
+
|
|
1324
|
+
const inspectSymbol = (value, { nestedHumanize, parenthesis }) => {
|
|
1325
|
+
const symbolDescription = symbolToDescription(value);
|
|
1326
|
+
const symbolDescriptionSource = symbolDescription
|
|
1327
|
+
? nestedHumanize(symbolDescription)
|
|
1328
|
+
: "";
|
|
1329
|
+
const symbolSource = `Symbol(${symbolDescriptionSource})`;
|
|
1330
|
+
|
|
1331
|
+
if (parenthesis) return `${symbolSource}`;
|
|
1332
|
+
return symbolSource;
|
|
1333
|
+
};
|
|
1334
|
+
|
|
1335
|
+
const symbolToDescription =
|
|
1336
|
+
"description" in Symbol.prototype
|
|
1337
|
+
? (symbol) => symbol.description
|
|
1338
|
+
: (symbol) => {
|
|
1339
|
+
const toStringResult = symbol.toString();
|
|
1340
|
+
const openingParenthesisIndex = toStringResult.indexOf("(");
|
|
1341
|
+
const closingParenthesisIndex = toStringResult.indexOf(")");
|
|
1342
|
+
const symbolDescription = toStringResult.slice(
|
|
1343
|
+
openingParenthesisIndex + 1,
|
|
1344
|
+
closingParenthesisIndex,
|
|
1345
|
+
);
|
|
1346
|
+
return symbolDescription;
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1349
|
+
const inspectUndefined = () => "undefined";
|
|
1350
|
+
|
|
1351
|
+
const inspectBigInt = (value) => {
|
|
1352
|
+
return `${value}n`;
|
|
1353
|
+
};
|
|
1354
|
+
|
|
1355
|
+
const preNewLineAndIndentation = (
|
|
1356
|
+
value,
|
|
1357
|
+
{ depth, indentUsingTab, indentSize },
|
|
1358
|
+
) => {
|
|
1359
|
+
return `${newLineAndIndent({
|
|
1360
|
+
count: depth + 1,
|
|
1361
|
+
useTabs: indentUsingTab,
|
|
1362
|
+
size: indentSize,
|
|
1363
|
+
})}${value}`;
|
|
1364
|
+
};
|
|
1365
|
+
|
|
1366
|
+
const postNewLineAndIndentation = ({ depth, indentUsingTab, indentSize }) => {
|
|
1367
|
+
return newLineAndIndent({
|
|
1368
|
+
count: depth,
|
|
1369
|
+
useTabs: indentUsingTab,
|
|
1370
|
+
size: indentSize,
|
|
1371
|
+
});
|
|
1372
|
+
};
|
|
1373
|
+
|
|
1374
|
+
const newLineAndIndent = ({ count, useTabs, size }) => {
|
|
1375
|
+
if (useTabs) {
|
|
1376
|
+
// eslint-disable-next-line prefer-template
|
|
1377
|
+
return "\n" + "\t".repeat(count);
|
|
1378
|
+
}
|
|
1379
|
+
// eslint-disable-next-line prefer-template
|
|
1380
|
+
return "\n" + " ".repeat(count * size);
|
|
1381
|
+
};
|
|
1382
|
+
|
|
1383
|
+
const wrapNewLineAndIndentation = (
|
|
1384
|
+
value,
|
|
1385
|
+
{ depth, indentUsingTab, indentSize },
|
|
1386
|
+
) => {
|
|
1387
|
+
return `${preNewLineAndIndentation(value, {
|
|
1388
|
+
depth,
|
|
1389
|
+
indentUsingTab,
|
|
1390
|
+
indentSize,
|
|
1391
|
+
})}${postNewLineAndIndentation({ depth, indentUsingTab, indentSize })}`;
|
|
1392
|
+
};
|
|
1393
|
+
|
|
1394
|
+
const inspectConstructor = (value, { parenthesis, useNew }) => {
|
|
1395
|
+
let formattedString = value;
|
|
1396
|
+
if (parenthesis) {
|
|
1397
|
+
formattedString = `(${value})`;
|
|
1398
|
+
}
|
|
1399
|
+
if (useNew) {
|
|
1400
|
+
formattedString = `new ${formattedString}`;
|
|
1401
|
+
}
|
|
1402
|
+
return formattedString;
|
|
1403
|
+
};
|
|
1404
|
+
|
|
1405
|
+
const inspectArray = (
|
|
1406
|
+
value,
|
|
1407
|
+
{
|
|
1408
|
+
seen = [],
|
|
1409
|
+
nestedHumanize,
|
|
1410
|
+
depth,
|
|
1411
|
+
indentUsingTab,
|
|
1412
|
+
indentSize,
|
|
1413
|
+
parenthesis,
|
|
1414
|
+
useNew,
|
|
1415
|
+
},
|
|
1416
|
+
) => {
|
|
1417
|
+
if (seen.indexOf(value) > -1) {
|
|
1418
|
+
return "Symbol.for('circular')";
|
|
1419
|
+
}
|
|
1420
|
+
seen.push(value);
|
|
1421
|
+
|
|
1422
|
+
let valuesSource = "";
|
|
1423
|
+
let i = 0;
|
|
1424
|
+
const j = value.length;
|
|
1425
|
+
|
|
1426
|
+
while (i < j) {
|
|
1427
|
+
const valueSource = value.hasOwnProperty(i)
|
|
1428
|
+
? nestedHumanize(value[i], { seen })
|
|
1429
|
+
: "";
|
|
1430
|
+
if (i === 0) {
|
|
1431
|
+
valuesSource += valueSource;
|
|
1432
|
+
} else {
|
|
1433
|
+
valuesSource += `,${preNewLineAndIndentation(valueSource, {
|
|
1434
|
+
depth,
|
|
1435
|
+
indentUsingTab,
|
|
1436
|
+
indentSize,
|
|
1437
|
+
})}`;
|
|
1438
|
+
}
|
|
1439
|
+
i++;
|
|
1440
|
+
}
|
|
1441
|
+
|
|
1442
|
+
let arraySource;
|
|
1443
|
+
if (valuesSource.length) {
|
|
1444
|
+
arraySource = wrapNewLineAndIndentation(valuesSource, {
|
|
1445
|
+
depth,
|
|
1446
|
+
indentUsingTab,
|
|
1447
|
+
indentSize,
|
|
1448
|
+
});
|
|
1449
|
+
} else {
|
|
1450
|
+
arraySource = "";
|
|
1451
|
+
}
|
|
1452
|
+
|
|
1453
|
+
arraySource = `[${arraySource}]`;
|
|
1454
|
+
|
|
1455
|
+
return inspectConstructor(arraySource, { parenthesis, useNew });
|
|
1456
|
+
};
|
|
1457
|
+
|
|
1458
|
+
const inspectBigIntObject = (value, { nestedHumanize }) => {
|
|
1459
|
+
const bigIntSource = nestedHumanize(value.valueOf());
|
|
1460
|
+
|
|
1461
|
+
return `BigInt(${bigIntSource})`;
|
|
1462
|
+
};
|
|
1463
|
+
|
|
1464
|
+
const inspectBooleanObject = (
|
|
1465
|
+
value,
|
|
1466
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
1467
|
+
) => {
|
|
1468
|
+
const booleanSource = nestedHumanize(value.valueOf());
|
|
1469
|
+
return inspectConstructor(`Boolean(${booleanSource})`, {
|
|
1470
|
+
useNew,
|
|
1471
|
+
parenthesis,
|
|
1472
|
+
});
|
|
1473
|
+
};
|
|
1474
|
+
|
|
1475
|
+
const inspectError = (
|
|
1476
|
+
error,
|
|
1477
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
1478
|
+
) => {
|
|
1479
|
+
const messageSource = nestedHumanize(error.message);
|
|
1480
|
+
|
|
1481
|
+
const errorSource = inspectConstructor(
|
|
1482
|
+
`${errorToConstructorName(error)}(${messageSource})`,
|
|
1483
|
+
{
|
|
1484
|
+
useNew,
|
|
1485
|
+
parenthesis,
|
|
1486
|
+
},
|
|
1487
|
+
);
|
|
1488
|
+
return errorSource;
|
|
1489
|
+
};
|
|
1490
|
+
|
|
1491
|
+
const errorToConstructorName = ({ name }) => {
|
|
1492
|
+
if (derivedErrorNameArray.includes(name)) {
|
|
1493
|
+
return name;
|
|
1494
|
+
}
|
|
1495
|
+
return "Error";
|
|
1496
|
+
};
|
|
1497
|
+
|
|
1498
|
+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error#Error_types
|
|
1499
|
+
const derivedErrorNameArray = [
|
|
1500
|
+
"EvalError",
|
|
1501
|
+
"RangeError",
|
|
1502
|
+
"ReferenceError",
|
|
1503
|
+
"SyntaxError",
|
|
1504
|
+
"TypeError",
|
|
1505
|
+
"URIError",
|
|
1506
|
+
];
|
|
1507
|
+
|
|
1508
|
+
const inspectDate = (value, { nestedHumanize, useNew, parenthesis }) => {
|
|
1509
|
+
const dateSource = nestedHumanize(value.valueOf(), {
|
|
1510
|
+
numericSeparator: false,
|
|
1511
|
+
});
|
|
1512
|
+
return inspectConstructor(`Date(${dateSource})`, { useNew, parenthesis });
|
|
1513
|
+
};
|
|
1514
|
+
|
|
1515
|
+
const inspectFunction = (
|
|
1516
|
+
value,
|
|
1517
|
+
{ showFunctionBody, parenthesis, depth },
|
|
1518
|
+
) => {
|
|
1519
|
+
let functionSource;
|
|
1520
|
+
if (showFunctionBody) {
|
|
1521
|
+
functionSource = value.toString();
|
|
1522
|
+
} else {
|
|
1523
|
+
const isArrowFunction = value.prototype === undefined;
|
|
1524
|
+
const head = isArrowFunction
|
|
1525
|
+
? "() =>"
|
|
1526
|
+
: `function ${depth === 0 ? value.name : ""}()`;
|
|
1527
|
+
functionSource = `${head} {/* hidden */}`;
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
if (parenthesis) {
|
|
1531
|
+
return `(${functionSource})`;
|
|
1532
|
+
}
|
|
1533
|
+
return functionSource;
|
|
1534
|
+
};
|
|
1535
|
+
|
|
1536
|
+
const inspectNumberObject = (
|
|
1537
|
+
value,
|
|
1538
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
1539
|
+
) => {
|
|
1540
|
+
const numberSource = nestedHumanize(value.valueOf());
|
|
1541
|
+
return inspectConstructor(`Number(${numberSource})`, { useNew, parenthesis });
|
|
1542
|
+
};
|
|
1543
|
+
|
|
1544
|
+
const inspectObject = (
|
|
1545
|
+
value,
|
|
1546
|
+
{
|
|
1547
|
+
nestedHumanize,
|
|
1548
|
+
seen = [],
|
|
1549
|
+
depth,
|
|
1550
|
+
indentUsingTab,
|
|
1551
|
+
indentSize,
|
|
1552
|
+
objectConstructor,
|
|
1553
|
+
parenthesis,
|
|
1554
|
+
useNew,
|
|
1555
|
+
},
|
|
1556
|
+
) => {
|
|
1557
|
+
if (seen.indexOf(value) > -1) return "Symbol.for('circular')";
|
|
1558
|
+
|
|
1559
|
+
seen.push(value);
|
|
1560
|
+
|
|
1561
|
+
const propertySourceArray = [];
|
|
1562
|
+
Object.getOwnPropertyNames(value).forEach((propertyName) => {
|
|
1563
|
+
const propertyNameAsNumber = parseInt(propertyName, 10);
|
|
1564
|
+
const propertyNameSource = nestedHumanize(
|
|
1565
|
+
Number.isInteger(propertyNameAsNumber)
|
|
1566
|
+
? propertyNameAsNumber
|
|
1567
|
+
: propertyName,
|
|
1568
|
+
);
|
|
1569
|
+
propertySourceArray.push({
|
|
1570
|
+
nameOrSymbolSource: propertyNameSource,
|
|
1571
|
+
valueSource: nestedHumanize(value[propertyName], { seen }),
|
|
1572
|
+
});
|
|
1573
|
+
});
|
|
1574
|
+
Object.getOwnPropertySymbols(value).forEach((symbol) => {
|
|
1575
|
+
propertySourceArray.push({
|
|
1576
|
+
nameOrSymbolSource: `[${nestedHumanize(symbol)}]`,
|
|
1577
|
+
valueSource: nestedHumanize(value[symbol], { seen }),
|
|
1578
|
+
});
|
|
1579
|
+
});
|
|
1580
|
+
|
|
1581
|
+
let propertiesSource = "";
|
|
1582
|
+
propertySourceArray.forEach(({ nameOrSymbolSource, valueSource }, index) => {
|
|
1583
|
+
if (index === 0) {
|
|
1584
|
+
propertiesSource += `${nameOrSymbolSource}: ${valueSource}`;
|
|
1585
|
+
} else {
|
|
1586
|
+
propertiesSource += `,${preNewLineAndIndentation(
|
|
1587
|
+
`${nameOrSymbolSource}: ${valueSource}`,
|
|
1588
|
+
{
|
|
1589
|
+
depth,
|
|
1590
|
+
indentUsingTab,
|
|
1591
|
+
indentSize,
|
|
1592
|
+
},
|
|
1593
|
+
)}`;
|
|
1594
|
+
}
|
|
1595
|
+
});
|
|
1596
|
+
|
|
1597
|
+
let objectSource;
|
|
1598
|
+
if (propertiesSource.length) {
|
|
1599
|
+
objectSource = `${wrapNewLineAndIndentation(propertiesSource, {
|
|
1600
|
+
depth,
|
|
1601
|
+
indentUsingTab,
|
|
1602
|
+
indentSize,
|
|
1603
|
+
})}`;
|
|
1604
|
+
} else {
|
|
1605
|
+
objectSource = "";
|
|
1606
|
+
}
|
|
1607
|
+
|
|
1608
|
+
if (objectConstructor) {
|
|
1609
|
+
objectSource = `Object({${objectSource}})`;
|
|
1610
|
+
} else {
|
|
1611
|
+
objectSource = `{${objectSource}}`;
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
return inspectConstructor(objectSource, { parenthesis, useNew });
|
|
1615
|
+
};
|
|
1616
|
+
|
|
1617
|
+
const inspectRegExp = (value) => value.toString();
|
|
1618
|
+
|
|
1619
|
+
const inspectStringObject = (
|
|
1620
|
+
value,
|
|
1621
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
1622
|
+
) => {
|
|
1623
|
+
const stringSource = nestedHumanize(value.valueOf());
|
|
1624
|
+
|
|
1625
|
+
return inspectConstructor(`String(${stringSource})`, { useNew, parenthesis });
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
// primitives
|
|
1629
|
+
|
|
1630
|
+
const humanize = (
|
|
1631
|
+
value,
|
|
1632
|
+
{
|
|
1633
|
+
parenthesis = false,
|
|
1634
|
+
quote = "auto",
|
|
1635
|
+
canUseTemplateString = true,
|
|
1636
|
+
useNew = false,
|
|
1637
|
+
objectConstructor = false,
|
|
1638
|
+
showFunctionBody = false,
|
|
1639
|
+
indentUsingTab = false,
|
|
1640
|
+
indentSize = 2,
|
|
1641
|
+
numericSeparator = true,
|
|
1642
|
+
preserveLineBreaks = false,
|
|
1643
|
+
} = {},
|
|
1644
|
+
) => {
|
|
1645
|
+
const scopedHumanize = (scopedValue, scopedOptions) => {
|
|
1646
|
+
const options = {
|
|
1647
|
+
...scopedOptions,
|
|
1648
|
+
nestedHumanize: (nestedValue, nestedOptions = {}) => {
|
|
1649
|
+
return scopedHumanize(nestedValue, {
|
|
1650
|
+
...scopedOptions,
|
|
1651
|
+
depth: scopedOptions.depth + 1,
|
|
1652
|
+
...nestedOptions,
|
|
1653
|
+
});
|
|
1654
|
+
},
|
|
1655
|
+
};
|
|
1656
|
+
return humanizeValue(scopedValue, options);
|
|
1657
|
+
};
|
|
1658
|
+
|
|
1659
|
+
return scopedHumanize(value, {
|
|
1660
|
+
parenthesis,
|
|
1661
|
+
quote,
|
|
1662
|
+
canUseTemplateString,
|
|
1663
|
+
useNew,
|
|
1664
|
+
objectConstructor,
|
|
1665
|
+
showFunctionBody,
|
|
1666
|
+
indentUsingTab,
|
|
1667
|
+
indentSize,
|
|
1668
|
+
numericSeparator,
|
|
1669
|
+
preserveLineBreaks,
|
|
1670
|
+
depth: 0,
|
|
1671
|
+
});
|
|
1672
|
+
};
|
|
1673
|
+
|
|
1674
|
+
const humanizeMethodSymbol = Symbol.for("inspect");
|
|
1675
|
+
|
|
1676
|
+
const humanizeValue = (value, options) => {
|
|
1677
|
+
const customHumanize = value && value[humanizeMethodSymbol];
|
|
1678
|
+
if (customHumanize) {
|
|
1679
|
+
return customHumanize(options);
|
|
1680
|
+
}
|
|
1681
|
+
const primitiveType = primitiveTypeFromValue(value);
|
|
1682
|
+
const primitiveStringifier = primitiveStringifiers[primitiveType];
|
|
1683
|
+
if (primitiveStringifier) {
|
|
1684
|
+
return primitiveStringifier(value, options);
|
|
1685
|
+
}
|
|
1686
|
+
const compositeType = compositeTypeFromObject(value);
|
|
1687
|
+
const compositeStringifier = compositeStringifiers[compositeType];
|
|
1688
|
+
if (compositeStringifier) {
|
|
1689
|
+
return compositeStringifier(value, options);
|
|
1690
|
+
}
|
|
1691
|
+
return inspectConstructor(
|
|
1692
|
+
`${compositeType}(${inspectObject(value, options)})`,
|
|
1693
|
+
{
|
|
1694
|
+
...options,
|
|
1695
|
+
parenthesis: false,
|
|
1696
|
+
},
|
|
1697
|
+
);
|
|
1698
|
+
};
|
|
1699
|
+
|
|
1700
|
+
const primitiveTypeFromValue = (value) => {
|
|
1701
|
+
if (value === null) {
|
|
1702
|
+
return "null";
|
|
1703
|
+
}
|
|
1704
|
+
if (value === undefined) {
|
|
1705
|
+
return "undefined";
|
|
1706
|
+
}
|
|
1707
|
+
return typeof value;
|
|
1708
|
+
};
|
|
1709
|
+
const primitiveStringifiers = {
|
|
1710
|
+
boolean: inspectBoolean,
|
|
1711
|
+
function: inspectFunction,
|
|
1712
|
+
null: inspectNull,
|
|
1713
|
+
number: inspectNumber,
|
|
1714
|
+
string: inspectString,
|
|
1715
|
+
symbol: inspectSymbol,
|
|
1716
|
+
undefined: inspectUndefined,
|
|
1717
|
+
bigint: inspectBigInt,
|
|
1718
|
+
};
|
|
1719
|
+
const compositeTypeFromObject = (object) => {
|
|
1720
|
+
if (typeof object === "object" && Object.getPrototypeOf(object) === null) {
|
|
1721
|
+
return "Object";
|
|
1722
|
+
}
|
|
1723
|
+
const toStringResult = toString.call(object);
|
|
1724
|
+
// returns format is '[object ${tagName}]';
|
|
1725
|
+
// and we want ${tagName}
|
|
1726
|
+
const tagName = toStringResult.slice("[object ".length, -1);
|
|
1727
|
+
if (tagName === "Object") {
|
|
1728
|
+
const objectConstructorName = object.constructor.name;
|
|
1729
|
+
if (objectConstructorName !== "Object") {
|
|
1730
|
+
return objectConstructorName;
|
|
1731
|
+
}
|
|
1732
|
+
}
|
|
1733
|
+
return tagName;
|
|
1734
|
+
};
|
|
1735
|
+
const { toString } = Object.prototype;
|
|
1736
|
+
|
|
1737
|
+
const compositeStringifiers = {
|
|
1738
|
+
Array: inspectArray,
|
|
1739
|
+
BigInt: inspectBigIntObject,
|
|
1740
|
+
Boolean: inspectBooleanObject,
|
|
1741
|
+
Error: inspectError,
|
|
1742
|
+
Date: inspectDate,
|
|
1743
|
+
Function: inspectFunction,
|
|
1744
|
+
Number: inspectNumberObject,
|
|
1745
|
+
Object: inspectObject,
|
|
1746
|
+
RegExp: inspectRegExp,
|
|
1747
|
+
String: inspectStringObject,
|
|
1748
|
+
};
|
|
1749
|
+
|
|
1750
|
+
const humanizeFileSize = (numberOfBytes, { decimals, short } = {}) => {
|
|
1751
|
+
return inspectBytes(numberOfBytes, { decimals, short });
|
|
1752
|
+
};
|
|
1753
|
+
|
|
1754
|
+
const humanizeMemoryUsage = (metricValue, { decimals, short } = {}) => {
|
|
1755
|
+
return inspectBytes(metricValue, { decimals, fixedDecimals: true, short });
|
|
1756
|
+
};
|
|
1757
|
+
|
|
1758
|
+
const inspectBytes = (
|
|
1759
|
+
number,
|
|
1760
|
+
{ fixedDecimals = false, decimals, short } = {},
|
|
1761
|
+
) => {
|
|
1762
|
+
if (number === 0) {
|
|
1763
|
+
return `0 B`;
|
|
1764
|
+
}
|
|
1765
|
+
const exponent = Math.min(
|
|
1766
|
+
Math.floor(Math.log10(number) / 3),
|
|
1767
|
+
BYTE_UNITS.length - 1,
|
|
1768
|
+
);
|
|
1769
|
+
const unitNumber = number / Math.pow(1000, exponent);
|
|
1770
|
+
const unitName = BYTE_UNITS[exponent];
|
|
1771
|
+
if (decimals === undefined) {
|
|
1772
|
+
if (unitNumber < 100) {
|
|
1773
|
+
decimals = 1;
|
|
1774
|
+
} else {
|
|
1775
|
+
decimals = 0;
|
|
1776
|
+
}
|
|
1777
|
+
}
|
|
1778
|
+
const unitNumberRounded = setRoundedPrecision(unitNumber, {
|
|
1779
|
+
decimals,
|
|
1780
|
+
decimalsWhenSmall: 1,
|
|
1781
|
+
});
|
|
1782
|
+
const value = fixedDecimals
|
|
1783
|
+
? unitNumberRounded.toFixed(decimals)
|
|
1784
|
+
: unitNumberRounded;
|
|
1785
|
+
if (short) {
|
|
1786
|
+
return `${value}${unitName}`;
|
|
1787
|
+
}
|
|
1788
|
+
return `${value} ${unitName}`;
|
|
1789
|
+
};
|
|
1790
|
+
|
|
1791
|
+
const BYTE_UNITS = ["B", "kB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
|
|
1792
|
+
|
|
1793
|
+
const distributePercentages = (
|
|
1794
|
+
namedNumbers,
|
|
1795
|
+
{ maxPrecisionHint = 2 } = {},
|
|
1796
|
+
) => {
|
|
1797
|
+
const numberNames = Object.keys(namedNumbers);
|
|
1798
|
+
if (numberNames.length === 0) {
|
|
1799
|
+
return {};
|
|
1800
|
+
}
|
|
1801
|
+
if (numberNames.length === 1) {
|
|
1802
|
+
const firstNumberName = numberNames[0];
|
|
1803
|
+
return { [firstNumberName]: "100 %" };
|
|
1804
|
+
}
|
|
1805
|
+
const numbers = numberNames.map((name) => namedNumbers[name]);
|
|
1806
|
+
const total = numbers.reduce((sum, value) => sum + value, 0);
|
|
1807
|
+
const ratios = numbers.map((number) => number / total);
|
|
1808
|
+
const percentages = {};
|
|
1809
|
+
ratios.pop();
|
|
1810
|
+
ratios.forEach((ratio, index) => {
|
|
1811
|
+
const percentage = ratio * 100;
|
|
1812
|
+
percentages[numberNames[index]] = percentage;
|
|
1813
|
+
});
|
|
1814
|
+
const lowestPercentage = (1 / Math.pow(10, maxPrecisionHint)) * 100;
|
|
1815
|
+
let precision = 0;
|
|
1816
|
+
Object.keys(percentages).forEach((name) => {
|
|
1817
|
+
const percentage = percentages[name];
|
|
1818
|
+
if (percentage < lowestPercentage) {
|
|
1819
|
+
// check the amout of meaningful decimals
|
|
1820
|
+
// and that what we will use
|
|
1821
|
+
const percentageRounded = setRoundedPrecision(percentage);
|
|
1822
|
+
const percentagePrecision = getPrecision(percentageRounded);
|
|
1823
|
+
if (percentagePrecision > precision) {
|
|
1824
|
+
precision = percentagePrecision;
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
});
|
|
1828
|
+
let remainingPercentage = 100;
|
|
1829
|
+
|
|
1830
|
+
Object.keys(percentages).forEach((name) => {
|
|
1831
|
+
const percentage = percentages[name];
|
|
1832
|
+
const percentageAllocated = setRoundedPrecision(percentage, {
|
|
1833
|
+
decimals: precision,
|
|
1834
|
+
});
|
|
1835
|
+
remainingPercentage -= percentageAllocated;
|
|
1836
|
+
percentages[name] = percentageAllocated;
|
|
1837
|
+
});
|
|
1838
|
+
const lastName = numberNames[numberNames.length - 1];
|
|
1839
|
+
percentages[lastName] = setRoundedPrecision(remainingPercentage, {
|
|
1840
|
+
decimals: precision,
|
|
1841
|
+
});
|
|
1842
|
+
return percentages;
|
|
1843
|
+
};
|
|
1844
|
+
|
|
1845
|
+
const formatDefault = (v) => v;
|
|
1846
|
+
|
|
1847
|
+
const generateContentFrame = ({
|
|
1848
|
+
content,
|
|
1849
|
+
line,
|
|
1850
|
+
column,
|
|
1851
|
+
|
|
1852
|
+
linesAbove = 3,
|
|
1853
|
+
linesBelow = 0,
|
|
1854
|
+
lineMaxWidth = 120,
|
|
1855
|
+
lineNumbersOnTheLeft = true,
|
|
1856
|
+
lineMarker = true,
|
|
1857
|
+
columnMarker = true,
|
|
1858
|
+
format = formatDefault,
|
|
1859
|
+
} = {}) => {
|
|
1860
|
+
const lineStrings = content.split(/\r?\n/);
|
|
1861
|
+
if (line === 0) line = 1;
|
|
1862
|
+
if (column === undefined) {
|
|
1863
|
+
columnMarker = false;
|
|
1864
|
+
column = 1;
|
|
1865
|
+
}
|
|
1866
|
+
if (column === 0) column = 1;
|
|
1867
|
+
|
|
1868
|
+
let lineStartIndex = line - 1 - linesAbove;
|
|
1869
|
+
if (lineStartIndex < 0) {
|
|
1870
|
+
lineStartIndex = 0;
|
|
1871
|
+
}
|
|
1872
|
+
let lineEndIndex = line - 1 + linesBelow;
|
|
1873
|
+
if (lineEndIndex > lineStrings.length - 1) {
|
|
1874
|
+
lineEndIndex = lineStrings.length - 1;
|
|
1875
|
+
}
|
|
1876
|
+
if (columnMarker) {
|
|
1877
|
+
// human reader deduce the line when there is a column marker
|
|
1878
|
+
lineMarker = false;
|
|
1879
|
+
}
|
|
1880
|
+
if (line - 1 === lineEndIndex) {
|
|
1881
|
+
lineMarker = false; // useless because last line
|
|
1882
|
+
}
|
|
1883
|
+
let lineIndex = lineStartIndex;
|
|
1884
|
+
|
|
1885
|
+
let columnsBefore;
|
|
1886
|
+
let columnsAfter;
|
|
1887
|
+
if (column > lineMaxWidth) {
|
|
1888
|
+
columnsBefore = column - Math.ceil(lineMaxWidth / 2);
|
|
1889
|
+
columnsAfter = column + Math.floor(lineMaxWidth / 2);
|
|
1890
|
+
} else {
|
|
1891
|
+
columnsBefore = 0;
|
|
1892
|
+
columnsAfter = lineMaxWidth;
|
|
1893
|
+
}
|
|
1894
|
+
let columnMarkerIndex = column - 1 - columnsBefore;
|
|
1895
|
+
|
|
1896
|
+
let source = "";
|
|
1897
|
+
while (lineIndex <= lineEndIndex) {
|
|
1898
|
+
const lineString = lineStrings[lineIndex];
|
|
1899
|
+
const lineNumber = lineIndex + 1;
|
|
1900
|
+
const isLastLine = lineIndex === lineEndIndex;
|
|
1901
|
+
const isMainLine = lineNumber === line;
|
|
1902
|
+
lineIndex++;
|
|
1903
|
+
|
|
1904
|
+
{
|
|
1905
|
+
if (lineMarker) {
|
|
1906
|
+
if (isMainLine) {
|
|
1907
|
+
source += `${format(">", "marker_line")} `;
|
|
1908
|
+
} else {
|
|
1909
|
+
source += " ";
|
|
1910
|
+
}
|
|
1911
|
+
}
|
|
1912
|
+
if (lineNumbersOnTheLeft) {
|
|
1913
|
+
// fill with spaces to ensure if line moves from 7,8,9 to 10 the display is still great
|
|
1914
|
+
const asideSource = `${fillLeft(lineNumber, lineEndIndex + 1)} |`;
|
|
1915
|
+
source += `${format(asideSource, "line_number_aside")} `;
|
|
1916
|
+
}
|
|
1917
|
+
}
|
|
1918
|
+
{
|
|
1919
|
+
source += truncateLine(lineString, {
|
|
1920
|
+
start: columnsBefore,
|
|
1921
|
+
end: columnsAfter,
|
|
1922
|
+
prefix: "…",
|
|
1923
|
+
suffix: "…",
|
|
1924
|
+
format,
|
|
1925
|
+
});
|
|
1926
|
+
}
|
|
1927
|
+
{
|
|
1928
|
+
if (columnMarker && isMainLine) {
|
|
1929
|
+
source += `\n`;
|
|
1930
|
+
if (lineMarker) {
|
|
1931
|
+
source += " ";
|
|
1932
|
+
}
|
|
1933
|
+
if (lineNumbersOnTheLeft) {
|
|
1934
|
+
const asideSpaces = `${fillLeft(lineNumber, lineEndIndex + 1)} | `
|
|
1935
|
+
.length;
|
|
1936
|
+
source += " ".repeat(asideSpaces);
|
|
1937
|
+
}
|
|
1938
|
+
source += " ".repeat(columnMarkerIndex);
|
|
1939
|
+
source += format("^", "marker_column");
|
|
1940
|
+
}
|
|
1941
|
+
}
|
|
1942
|
+
if (!isLastLine) {
|
|
1943
|
+
source += "\n";
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
return source;
|
|
1947
|
+
};
|
|
1948
|
+
|
|
1949
|
+
const truncateLine = (line, { start, end, prefix, suffix, format }) => {
|
|
1950
|
+
const lastIndex = line.length;
|
|
1951
|
+
|
|
1952
|
+
if (line.length === 0) {
|
|
1953
|
+
// don't show any ellipsis if the line is empty
|
|
1954
|
+
// because it's not truncated in that case
|
|
1955
|
+
return "";
|
|
1956
|
+
}
|
|
1957
|
+
|
|
1958
|
+
const startTruncated = start > 0;
|
|
1959
|
+
const endTruncated = lastIndex > end;
|
|
1960
|
+
|
|
1961
|
+
let from = startTruncated ? start + prefix.length : start;
|
|
1962
|
+
let to = endTruncated ? end - suffix.length : end;
|
|
1963
|
+
if (to > lastIndex) to = lastIndex;
|
|
1964
|
+
|
|
1965
|
+
if (start >= lastIndex || from === to) {
|
|
1966
|
+
return "";
|
|
1967
|
+
}
|
|
1968
|
+
let result = "";
|
|
1969
|
+
while (from < to) {
|
|
1970
|
+
result += format(line[from], "char");
|
|
1971
|
+
from++;
|
|
1972
|
+
}
|
|
1973
|
+
if (result.length === 0) {
|
|
1974
|
+
return "";
|
|
1975
|
+
}
|
|
1976
|
+
if (startTruncated && endTruncated) {
|
|
1977
|
+
return `${format(prefix, "marker_overflow_left")}${result}${format(
|
|
1978
|
+
suffix,
|
|
1979
|
+
"marker_overflow_right",
|
|
1980
|
+
)}`;
|
|
1981
|
+
}
|
|
1982
|
+
if (startTruncated) {
|
|
1983
|
+
return `${format(prefix, "marker_overflow_left")}${result}`;
|
|
1984
|
+
}
|
|
1985
|
+
if (endTruncated) {
|
|
1986
|
+
return `${result}${format(suffix, "marker_overflow_right")}`;
|
|
1987
|
+
}
|
|
1988
|
+
return result;
|
|
1989
|
+
};
|
|
1990
|
+
|
|
1991
|
+
const fillLeft = (value, biggestValue, char = " ") => {
|
|
1992
|
+
const width = String(value).length;
|
|
1993
|
+
const biggestWidth = String(biggestValue).length;
|
|
1994
|
+
let missingWidth = biggestWidth - width;
|
|
1995
|
+
let padded = "";
|
|
1996
|
+
while (missingWidth--) {
|
|
1997
|
+
padded += char;
|
|
1998
|
+
}
|
|
1999
|
+
padded += value;
|
|
2000
|
+
return padded;
|
|
2001
|
+
};
|
|
2002
|
+
|
|
2003
|
+
export { ANSI, UNICODE, createDetailedMessage, createDynamicLog, createLogger, createTaskLog, determineQuote, distributePercentages, generateContentFrame, humanize, humanizeDuration, humanizeEllapsedTime, humanizeFileSize, humanizeMemoryUsage, humanizeMethodSymbol, inspectChar, startSpinner };
|