@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,140 @@
|
|
|
1
|
+
// primitives
|
|
2
|
+
import { inspectBoolean } from "./boolean.js";
|
|
3
|
+
import { inspectNull } from "./null.js";
|
|
4
|
+
import { inspectNumber } from "./number.js";
|
|
5
|
+
import { inspectString } from "./string.js";
|
|
6
|
+
import { inspectSymbol } from "./symbol.js";
|
|
7
|
+
import { inspectUndefined } from "./undefined.js";
|
|
8
|
+
import { inspectBigInt } from "./bigint.js";
|
|
9
|
+
import { inspectArray } from "./array.js";
|
|
10
|
+
// composites
|
|
11
|
+
import { inspectBigIntObject } from "./bigint_object.js";
|
|
12
|
+
import { inspectBooleanObject } from "./boolean_object.js";
|
|
13
|
+
import { inspectError } from "./error.js";
|
|
14
|
+
import { inspectDate } from "./date.js";
|
|
15
|
+
import { inspectFunction } from "./function.js";
|
|
16
|
+
import { inspectNumberObject } from "./number_object.js";
|
|
17
|
+
import { inspectObject } from "./object.js";
|
|
18
|
+
import { inspectRegExp } from "./regexp.js";
|
|
19
|
+
import { inspectStringObject } from "./string_object.js";
|
|
20
|
+
import { inspectConstructor } from "./constructor.js";
|
|
21
|
+
|
|
22
|
+
export const humanize = (
|
|
23
|
+
value,
|
|
24
|
+
{
|
|
25
|
+
parenthesis = false,
|
|
26
|
+
quote = "auto",
|
|
27
|
+
canUseTemplateString = true,
|
|
28
|
+
useNew = false,
|
|
29
|
+
objectConstructor = false,
|
|
30
|
+
showFunctionBody = false,
|
|
31
|
+
indentUsingTab = false,
|
|
32
|
+
indentSize = 2,
|
|
33
|
+
numericSeparator = true,
|
|
34
|
+
preserveLineBreaks = false,
|
|
35
|
+
} = {},
|
|
36
|
+
) => {
|
|
37
|
+
const scopedHumanize = (scopedValue, scopedOptions) => {
|
|
38
|
+
const options = {
|
|
39
|
+
...scopedOptions,
|
|
40
|
+
nestedHumanize: (nestedValue, nestedOptions = {}) => {
|
|
41
|
+
return scopedHumanize(nestedValue, {
|
|
42
|
+
...scopedOptions,
|
|
43
|
+
depth: scopedOptions.depth + 1,
|
|
44
|
+
...nestedOptions,
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
return humanizeValue(scopedValue, options);
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return scopedHumanize(value, {
|
|
52
|
+
parenthesis,
|
|
53
|
+
quote,
|
|
54
|
+
canUseTemplateString,
|
|
55
|
+
useNew,
|
|
56
|
+
objectConstructor,
|
|
57
|
+
showFunctionBody,
|
|
58
|
+
indentUsingTab,
|
|
59
|
+
indentSize,
|
|
60
|
+
numericSeparator,
|
|
61
|
+
preserveLineBreaks,
|
|
62
|
+
depth: 0,
|
|
63
|
+
});
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
export const humanizeMethodSymbol = Symbol.for("inspect");
|
|
67
|
+
|
|
68
|
+
export const humanizeValue = (value, options) => {
|
|
69
|
+
const customHumanize = value && value[humanizeMethodSymbol];
|
|
70
|
+
if (customHumanize) {
|
|
71
|
+
return customHumanize(options);
|
|
72
|
+
}
|
|
73
|
+
const primitiveType = primitiveTypeFromValue(value);
|
|
74
|
+
const primitiveStringifier = primitiveStringifiers[primitiveType];
|
|
75
|
+
if (primitiveStringifier) {
|
|
76
|
+
return primitiveStringifier(value, options);
|
|
77
|
+
}
|
|
78
|
+
const compositeType = compositeTypeFromObject(value);
|
|
79
|
+
const compositeStringifier = compositeStringifiers[compositeType];
|
|
80
|
+
if (compositeStringifier) {
|
|
81
|
+
return compositeStringifier(value, options);
|
|
82
|
+
}
|
|
83
|
+
return inspectConstructor(
|
|
84
|
+
`${compositeType}(${inspectObject(value, options)})`,
|
|
85
|
+
{
|
|
86
|
+
...options,
|
|
87
|
+
parenthesis: false,
|
|
88
|
+
},
|
|
89
|
+
);
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const primitiveTypeFromValue = (value) => {
|
|
93
|
+
if (value === null) {
|
|
94
|
+
return "null";
|
|
95
|
+
}
|
|
96
|
+
if (value === undefined) {
|
|
97
|
+
return "undefined";
|
|
98
|
+
}
|
|
99
|
+
return typeof value;
|
|
100
|
+
};
|
|
101
|
+
const primitiveStringifiers = {
|
|
102
|
+
boolean: inspectBoolean,
|
|
103
|
+
function: inspectFunction,
|
|
104
|
+
null: inspectNull,
|
|
105
|
+
number: inspectNumber,
|
|
106
|
+
string: inspectString,
|
|
107
|
+
symbol: inspectSymbol,
|
|
108
|
+
undefined: inspectUndefined,
|
|
109
|
+
bigint: inspectBigInt,
|
|
110
|
+
};
|
|
111
|
+
const compositeTypeFromObject = (object) => {
|
|
112
|
+
if (typeof object === "object" && Object.getPrototypeOf(object) === null) {
|
|
113
|
+
return "Object";
|
|
114
|
+
}
|
|
115
|
+
const toStringResult = toString.call(object);
|
|
116
|
+
// returns format is '[object ${tagName}]';
|
|
117
|
+
// and we want ${tagName}
|
|
118
|
+
const tagName = toStringResult.slice("[object ".length, -1);
|
|
119
|
+
if (tagName === "Object") {
|
|
120
|
+
const objectConstructorName = object.constructor.name;
|
|
121
|
+
if (objectConstructorName !== "Object") {
|
|
122
|
+
return objectConstructorName;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
return tagName;
|
|
126
|
+
};
|
|
127
|
+
const { toString } = Object.prototype;
|
|
128
|
+
|
|
129
|
+
const compositeStringifiers = {
|
|
130
|
+
Array: inspectArray,
|
|
131
|
+
BigInt: inspectBigIntObject,
|
|
132
|
+
Boolean: inspectBooleanObject,
|
|
133
|
+
Error: inspectError,
|
|
134
|
+
Date: inspectDate,
|
|
135
|
+
Function: inspectFunction,
|
|
136
|
+
Number: inspectNumberObject,
|
|
137
|
+
Object: inspectObject,
|
|
138
|
+
RegExp: inspectRegExp,
|
|
139
|
+
String: inspectStringObject,
|
|
140
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const inspectNull = () => "null";
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
// https://github.com/sindresorhus/eslint-plugin-unicorn/blob/main/rules/numeric-separators-style.js
|
|
2
|
+
|
|
3
|
+
export const inspectNumber = (value, { numericSeparator }) => {
|
|
4
|
+
if (isNegativeZero(value)) {
|
|
5
|
+
return "-0";
|
|
6
|
+
}
|
|
7
|
+
// isNaN
|
|
8
|
+
// eslint-disable-next-line no-self-compare
|
|
9
|
+
if (value !== value) {
|
|
10
|
+
return "NaN";
|
|
11
|
+
}
|
|
12
|
+
if (value === Infinity) {
|
|
13
|
+
return "Infinity";
|
|
14
|
+
}
|
|
15
|
+
if (value === -Infinity) {
|
|
16
|
+
return "-Infinity";
|
|
17
|
+
}
|
|
18
|
+
const numberString = String(value);
|
|
19
|
+
if (!numericSeparator) {
|
|
20
|
+
return numberString;
|
|
21
|
+
}
|
|
22
|
+
const {
|
|
23
|
+
number,
|
|
24
|
+
mark = "",
|
|
25
|
+
sign = "",
|
|
26
|
+
power = "",
|
|
27
|
+
} = numberString.match(
|
|
28
|
+
/^(?<number>.*?)(?:(?<mark>e)(?<sign>[+-])?(?<power>\d+))?$/i,
|
|
29
|
+
).groups;
|
|
30
|
+
const numberWithSeparators = formatNumber(number);
|
|
31
|
+
const powerWithSeparators = addSeparator(power, {
|
|
32
|
+
minimumDigits: 5,
|
|
33
|
+
groupLength: 3,
|
|
34
|
+
});
|
|
35
|
+
return `${numberWithSeparators}${mark}${sign}${powerWithSeparators}`;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
// Use this and instead of Object.is(value, -0)
|
|
39
|
+
// because in some corner cases firefox returns false
|
|
40
|
+
// for Object.is(-0, -0)
|
|
41
|
+
const isNegativeZero = (value) => {
|
|
42
|
+
return value === 0 && 1 / value === -Infinity;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const formatNumber = (numberString) => {
|
|
46
|
+
const parts = numberString.split(".");
|
|
47
|
+
const [integer, fractional] = parts;
|
|
48
|
+
|
|
49
|
+
if (parts.length === 2) {
|
|
50
|
+
const integerWithSeparators = addSeparator(integer, {
|
|
51
|
+
minimumDigits: 5,
|
|
52
|
+
groupLength: 3,
|
|
53
|
+
});
|
|
54
|
+
return `${integerWithSeparators}.${fractional}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return addSeparator(integer, {
|
|
58
|
+
minimumDigits: 5,
|
|
59
|
+
groupLength: 3,
|
|
60
|
+
});
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const addSeparator = (numberString, { minimumDigits, groupLength }) => {
|
|
64
|
+
if (numberString[0] === "-") {
|
|
65
|
+
return `-${groupDigits(numberString.slice(1), {
|
|
66
|
+
minimumDigits,
|
|
67
|
+
groupLength,
|
|
68
|
+
})}`;
|
|
69
|
+
}
|
|
70
|
+
return groupDigits(numberString, { minimumDigits, groupLength });
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const groupDigits = (digits, { minimumDigits, groupLength }) => {
|
|
74
|
+
const digitCount = digits.length;
|
|
75
|
+
if (digitCount < minimumDigits) {
|
|
76
|
+
return digits;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
let digitsWithSeparator = digits.slice(-groupLength);
|
|
80
|
+
let remainingDigits = digits.slice(0, -groupLength);
|
|
81
|
+
while (remainingDigits.length) {
|
|
82
|
+
const group = remainingDigits.slice(-groupLength);
|
|
83
|
+
remainingDigits = remainingDigits.slice(0, -groupLength);
|
|
84
|
+
digitsWithSeparator = `${group}_${digitsWithSeparator}`;
|
|
85
|
+
}
|
|
86
|
+
return digitsWithSeparator;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
// const addSeparatorFromLeft = (value, { minimumDigits, groupLength }) => {
|
|
90
|
+
// const { length } = value;
|
|
91
|
+
// if (length < minimumDigits) {
|
|
92
|
+
// return value;
|
|
93
|
+
// }
|
|
94
|
+
|
|
95
|
+
// const parts = [];
|
|
96
|
+
// for (let start = 0; start < length; start += groupLength) {
|
|
97
|
+
// const end = Math.min(start + groupLength, length);
|
|
98
|
+
// parts.push(value.slice(start, end));
|
|
99
|
+
// }
|
|
100
|
+
// return parts.join("_");
|
|
101
|
+
// };
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { inspectConstructor } from "./constructor.js";
|
|
2
|
+
|
|
3
|
+
export const inspectNumberObject = (
|
|
4
|
+
value,
|
|
5
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
6
|
+
) => {
|
|
7
|
+
const numberSource = nestedHumanize(value.valueOf());
|
|
8
|
+
return inspectConstructor(`Number(${numberSource})`, { useNew, parenthesis });
|
|
9
|
+
};
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
preNewLineAndIndentation,
|
|
3
|
+
wrapNewLineAndIndentation,
|
|
4
|
+
} from "../utils/indentation.js";
|
|
5
|
+
import { inspectConstructor } from "./constructor.js";
|
|
6
|
+
|
|
7
|
+
export const inspectObject = (
|
|
8
|
+
value,
|
|
9
|
+
{
|
|
10
|
+
nestedHumanize,
|
|
11
|
+
seen = [],
|
|
12
|
+
depth,
|
|
13
|
+
indentUsingTab,
|
|
14
|
+
indentSize,
|
|
15
|
+
objectConstructor,
|
|
16
|
+
parenthesis,
|
|
17
|
+
useNew,
|
|
18
|
+
},
|
|
19
|
+
) => {
|
|
20
|
+
if (seen.indexOf(value) > -1) return "Symbol.for('circular')";
|
|
21
|
+
|
|
22
|
+
seen.push(value);
|
|
23
|
+
|
|
24
|
+
const propertySourceArray = [];
|
|
25
|
+
Object.getOwnPropertyNames(value).forEach((propertyName) => {
|
|
26
|
+
const propertyNameAsNumber = parseInt(propertyName, 10);
|
|
27
|
+
const propertyNameSource = nestedHumanize(
|
|
28
|
+
Number.isInteger(propertyNameAsNumber)
|
|
29
|
+
? propertyNameAsNumber
|
|
30
|
+
: propertyName,
|
|
31
|
+
);
|
|
32
|
+
propertySourceArray.push({
|
|
33
|
+
nameOrSymbolSource: propertyNameSource,
|
|
34
|
+
valueSource: nestedHumanize(value[propertyName], { seen }),
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
Object.getOwnPropertySymbols(value).forEach((symbol) => {
|
|
38
|
+
propertySourceArray.push({
|
|
39
|
+
nameOrSymbolSource: `[${nestedHumanize(symbol)}]`,
|
|
40
|
+
valueSource: nestedHumanize(value[symbol], { seen }),
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
let propertiesSource = "";
|
|
45
|
+
propertySourceArray.forEach(({ nameOrSymbolSource, valueSource }, index) => {
|
|
46
|
+
if (index === 0) {
|
|
47
|
+
propertiesSource += `${nameOrSymbolSource}: ${valueSource}`;
|
|
48
|
+
} else {
|
|
49
|
+
propertiesSource += `,${preNewLineAndIndentation(
|
|
50
|
+
`${nameOrSymbolSource}: ${valueSource}`,
|
|
51
|
+
{
|
|
52
|
+
depth,
|
|
53
|
+
indentUsingTab,
|
|
54
|
+
indentSize,
|
|
55
|
+
},
|
|
56
|
+
)}`;
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
let objectSource;
|
|
61
|
+
if (propertiesSource.length) {
|
|
62
|
+
objectSource = `${wrapNewLineAndIndentation(propertiesSource, {
|
|
63
|
+
depth,
|
|
64
|
+
indentUsingTab,
|
|
65
|
+
indentSize,
|
|
66
|
+
})}`;
|
|
67
|
+
} else {
|
|
68
|
+
objectSource = "";
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (objectConstructor) {
|
|
72
|
+
objectSource = `Object({${objectSource}})`;
|
|
73
|
+
} else {
|
|
74
|
+
objectSource = `{${objectSource}}`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return inspectConstructor(objectSource, { parenthesis, useNew });
|
|
78
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const inspectRegExp = (value) => value.toString();
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
const DOUBLE_QUOTE = `"`;
|
|
2
|
+
const SINGLE_QUOTE = `'`;
|
|
3
|
+
const BACKTICK = "`";
|
|
4
|
+
|
|
5
|
+
export const inspectString = (
|
|
6
|
+
value,
|
|
7
|
+
{
|
|
8
|
+
quote = "auto",
|
|
9
|
+
canUseTemplateString = false,
|
|
10
|
+
preserveLineBreaks = false,
|
|
11
|
+
quoteDefault = DOUBLE_QUOTE,
|
|
12
|
+
} = {},
|
|
13
|
+
) => {
|
|
14
|
+
quote =
|
|
15
|
+
quote === "auto"
|
|
16
|
+
? determineQuote(value, { canUseTemplateString, quoteDefault })
|
|
17
|
+
: quote;
|
|
18
|
+
if (quote === BACKTICK) {
|
|
19
|
+
return `\`${escapeTemplateStringSpecialCharacters(value)}\``;
|
|
20
|
+
}
|
|
21
|
+
return surroundStringWith(value, { quote, preserveLineBreaks });
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
// https://github.com/mgenware/string-to-template-literal/blob/main/src/main.ts#L1
|
|
25
|
+
const escapeTemplateStringSpecialCharacters = (string) => {
|
|
26
|
+
string = String(string);
|
|
27
|
+
let i = 0;
|
|
28
|
+
let escapedString = "";
|
|
29
|
+
while (i < string.length) {
|
|
30
|
+
const char = string[i];
|
|
31
|
+
i++;
|
|
32
|
+
escapedString += isTemplateStringSpecialChar(char) ? `\\${char}` : char;
|
|
33
|
+
}
|
|
34
|
+
return escapedString;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const isTemplateStringSpecialChar = (char) =>
|
|
38
|
+
templateStringSpecialChars.indexOf(char) > -1;
|
|
39
|
+
const templateStringSpecialChars = ["\\", "`", "$"];
|
|
40
|
+
|
|
41
|
+
export const determineQuote = (
|
|
42
|
+
string,
|
|
43
|
+
{ canUseTemplateString, quoteDefault = DOUBLE_QUOTE } = {},
|
|
44
|
+
) => {
|
|
45
|
+
const containsDoubleQuote = string.includes(DOUBLE_QUOTE);
|
|
46
|
+
if (!containsDoubleQuote) {
|
|
47
|
+
return DOUBLE_QUOTE;
|
|
48
|
+
}
|
|
49
|
+
const containsSimpleQuote = string.includes(SINGLE_QUOTE);
|
|
50
|
+
if (!containsSimpleQuote) {
|
|
51
|
+
return SINGLE_QUOTE;
|
|
52
|
+
}
|
|
53
|
+
if (canUseTemplateString) {
|
|
54
|
+
const containsBackTick = string.includes(BACKTICK);
|
|
55
|
+
if (!containsBackTick) {
|
|
56
|
+
return BACKTICK;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const doubleQuoteCount = string.split(DOUBLE_QUOTE).length - 1;
|
|
60
|
+
const singleQuoteCount = string.split(SINGLE_QUOTE).length - 1;
|
|
61
|
+
if (singleQuoteCount > doubleQuoteCount) {
|
|
62
|
+
return DOUBLE_QUOTE;
|
|
63
|
+
}
|
|
64
|
+
if (doubleQuoteCount > singleQuoteCount) {
|
|
65
|
+
return SINGLE_QUOTE;
|
|
66
|
+
}
|
|
67
|
+
return quoteDefault;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export const inspectChar = (char, { quote, preserveLineBreaks }) => {
|
|
71
|
+
const point = char.charCodeAt(0);
|
|
72
|
+
if (preserveLineBreaks && (char === "\n" || char === "\r")) {
|
|
73
|
+
return char;
|
|
74
|
+
}
|
|
75
|
+
if (
|
|
76
|
+
char === quote ||
|
|
77
|
+
point === 92 ||
|
|
78
|
+
point < 32 ||
|
|
79
|
+
(point > 126 && point < 160) ||
|
|
80
|
+
// line separators
|
|
81
|
+
point === 8232 ||
|
|
82
|
+
point === 8233
|
|
83
|
+
) {
|
|
84
|
+
const replacement =
|
|
85
|
+
char === quote
|
|
86
|
+
? `\\${quote}`
|
|
87
|
+
: point === 8232
|
|
88
|
+
? "\\u2028"
|
|
89
|
+
: point === 8233
|
|
90
|
+
? "\\u2029"
|
|
91
|
+
: meta[point];
|
|
92
|
+
return replacement;
|
|
93
|
+
}
|
|
94
|
+
return char;
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// https://github.com/jsenv/jsenv-uneval/blob/6c97ef9d8f2e9425a66f2c88347e0a118d427f3a/src/internal/escapeString.js#L3
|
|
98
|
+
// https://github.com/jsenv/jsenv-inspect/blob/bb11de3adf262b68f71ed82b0a37d4528dd42229/src/internal/string.js#L3
|
|
99
|
+
// https://github.com/joliss/js-string-escape/blob/master/index.js
|
|
100
|
+
// http://javascript.crockford.com/remedial.html
|
|
101
|
+
const surroundStringWith = (string, { quote, preserveLineBreaks }) => {
|
|
102
|
+
let result = "";
|
|
103
|
+
let last = 0;
|
|
104
|
+
const lastIndex = string.length;
|
|
105
|
+
let i = 0;
|
|
106
|
+
while (i < lastIndex) {
|
|
107
|
+
const char = string[i];
|
|
108
|
+
const replacement = inspectChar(char, { quote, preserveLineBreaks });
|
|
109
|
+
if (char !== replacement) {
|
|
110
|
+
if (last === i) {
|
|
111
|
+
result += replacement;
|
|
112
|
+
} else {
|
|
113
|
+
result += `${string.slice(last, i)}${replacement}`;
|
|
114
|
+
}
|
|
115
|
+
last = i + 1;
|
|
116
|
+
}
|
|
117
|
+
i++;
|
|
118
|
+
}
|
|
119
|
+
if (last !== lastIndex) {
|
|
120
|
+
result += string.slice(last);
|
|
121
|
+
}
|
|
122
|
+
return `${quote}${result}${quote}`;
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
// prettier-ignore
|
|
126
|
+
const meta = [
|
|
127
|
+
'\\x00', '\\x01', '\\x02', '\\x03', '\\x04', '\\x05', '\\x06', '\\x07', // x07
|
|
128
|
+
'\\b', '\\t', '\\n', '\\x0B', '\\f', '\\r', '\\x0E', '\\x0F', // x0F
|
|
129
|
+
'\\x10', '\\x11', '\\x12', '\\x13', '\\x14', '\\x15', '\\x16', '\\x17', // x17
|
|
130
|
+
'\\x18', '\\x19', '\\x1A', '\\x1B', '\\x1C', '\\x1D', '\\x1E', '\\x1F', // x1F
|
|
131
|
+
'', '', '', '', '', '', '', "\\'", '', '', '', '', '', '', '', '', // x2F
|
|
132
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x3F
|
|
133
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x4F
|
|
134
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '\\\\', '', '', '', // x5F
|
|
135
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', // x6F
|
|
136
|
+
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '\\x7F', // x7F
|
|
137
|
+
'\\x80', '\\x81', '\\x82', '\\x83', '\\x84', '\\x85', '\\x86', '\\x87', // x87
|
|
138
|
+
'\\x88', '\\x89', '\\x8A', '\\x8B', '\\x8C', '\\x8D', '\\x8E', '\\x8F', // x8F
|
|
139
|
+
'\\x90', '\\x91', '\\x92', '\\x93', '\\x94', '\\x95', '\\x96', '\\x97', // x97
|
|
140
|
+
'\\x98', '\\x99', '\\x9A', '\\x9B', '\\x9C', '\\x9D', '\\x9E', '\\x9F', // x9F
|
|
141
|
+
];
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { inspectConstructor } from "./constructor.js";
|
|
2
|
+
|
|
3
|
+
export const inspectStringObject = (
|
|
4
|
+
value,
|
|
5
|
+
{ nestedHumanize, useNew, parenthesis },
|
|
6
|
+
) => {
|
|
7
|
+
const stringSource = nestedHumanize(value.valueOf());
|
|
8
|
+
|
|
9
|
+
return inspectConstructor(`String(${stringSource})`, { useNew, parenthesis });
|
|
10
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const inspectSymbol = (value, { nestedHumanize, parenthesis }) => {
|
|
2
|
+
const symbolDescription = symbolToDescription(value);
|
|
3
|
+
const symbolDescriptionSource = symbolDescription
|
|
4
|
+
? nestedHumanize(symbolDescription)
|
|
5
|
+
: "";
|
|
6
|
+
const symbolSource = `Symbol(${symbolDescriptionSource})`;
|
|
7
|
+
|
|
8
|
+
if (parenthesis) return `${symbolSource}`;
|
|
9
|
+
return symbolSource;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
const symbolToDescription =
|
|
13
|
+
"description" in Symbol.prototype
|
|
14
|
+
? (symbol) => symbol.description
|
|
15
|
+
: (symbol) => {
|
|
16
|
+
const toStringResult = symbol.toString();
|
|
17
|
+
const openingParenthesisIndex = toStringResult.indexOf("(");
|
|
18
|
+
const closingParenthesisIndex = toStringResult.indexOf(")");
|
|
19
|
+
const symbolDescription = toStringResult.slice(
|
|
20
|
+
openingParenthesisIndex + 1,
|
|
21
|
+
closingParenthesisIndex,
|
|
22
|
+
);
|
|
23
|
+
return symbolDescription;
|
|
24
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const inspectUndefined = () => "undefined";
|
package/src/log/ansi.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { createSupportsColor } from "supports-color";
|
|
2
|
+
|
|
3
|
+
const processSupportsBasicColor = createSupportsColor(process.stdout).hasBasic;
|
|
4
|
+
// https://github.com/Marak/colors.js/blob/master/lib/styles.js
|
|
5
|
+
// https://stackoverflow.com/a/75985833/2634179
|
|
6
|
+
const RESET = "\x1b[0m";
|
|
7
|
+
|
|
8
|
+
export const ANSI = {
|
|
9
|
+
supported: processSupportsBasicColor,
|
|
10
|
+
|
|
11
|
+
RED: "\x1b[31m",
|
|
12
|
+
GREEN: "\x1b[32m",
|
|
13
|
+
YELLOW: "\x1b[33m",
|
|
14
|
+
BLUE: "\x1b[34m",
|
|
15
|
+
MAGENTA: "\x1b[35m",
|
|
16
|
+
GREY: "\x1b[90m",
|
|
17
|
+
color: (text, ANSI_COLOR) => {
|
|
18
|
+
return ANSI.supported ? `${ANSI_COLOR}${text}${RESET}` : text;
|
|
19
|
+
},
|
|
20
|
+
|
|
21
|
+
BOLD: "\x1b[1m",
|
|
22
|
+
effect: (text, ANSI_EFFECT) => {
|
|
23
|
+
return ANSI.supported ? `${ANSI_EFFECT}${text}${RESET}` : text;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// GitHub workflow does support ANSI but "supports-color" returns false
|
|
28
|
+
// because stream.isTTY returns false, see https://github.com/actions/runner/issues/241
|
|
29
|
+
if (
|
|
30
|
+
process.env.GITHUB_WORKFLOW &&
|
|
31
|
+
// Check on FORCE_COLOR is to ensure it is prio over GitHub workflow check
|
|
32
|
+
// in unit test we use process.env.FORCE_COLOR = 'false' to fake
|
|
33
|
+
// that colors are not supported. Let it have priority
|
|
34
|
+
process.env.FORCE_COLOR !== "false"
|
|
35
|
+
) {
|
|
36
|
+
ANSI.supported = true;
|
|
37
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const createDetailedMessage = (message, details = {}) => {
|
|
2
|
+
let string = `${message}`;
|
|
3
|
+
|
|
4
|
+
Object.keys(details).forEach((key) => {
|
|
5
|
+
const value = details[key];
|
|
6
|
+
string += `
|
|
7
|
+
--- ${key} ---
|
|
8
|
+
${
|
|
9
|
+
Array.isArray(value)
|
|
10
|
+
? value.join(`
|
|
11
|
+
`)
|
|
12
|
+
: value
|
|
13
|
+
}`;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
return string;
|
|
17
|
+
};
|