mirror of
https://github.com/denoland/deno.git
synced 2025-03-03 09:31:22 -05:00
feat(ext/console): better circular information in object inspection (#13555)
This commit is contained in:
parent
3ec07f4e06
commit
2f438f4106
2 changed files with 98 additions and 31 deletions
|
@ -225,7 +225,7 @@ Deno.test(function consoleTestStringifyCircular() {
|
|||
};
|
||||
|
||||
nestedObj.o = circularObj;
|
||||
const nestedObjExpected = `{
|
||||
const nestedObjExpected = `<ref *1> {
|
||||
num: 1,
|
||||
bool: true,
|
||||
str: "a",
|
||||
|
@ -245,9 +245,9 @@ Deno.test(function consoleTestStringifyCircular() {
|
|||
method: [Function: method],
|
||||
un: undefined,
|
||||
nu: null,
|
||||
nested: [Circular],
|
||||
nested: [Circular *1],
|
||||
emptyObj: {},
|
||||
arr: [ 1, "s", false, null, [Circular] ],
|
||||
arr: [ 1, "s", false, null, [Circular *1] ],
|
||||
baseClass: Base { a: 1 }
|
||||
}
|
||||
}`;
|
||||
|
@ -350,12 +350,23 @@ Deno.test(function consoleTestStringifyCircular() {
|
|||
return Deno.inspect(this);
|
||||
},
|
||||
}),
|
||||
"[Circular]",
|
||||
"[Circular *1]",
|
||||
);
|
||||
// test inspect is working the same
|
||||
assertEquals(stripColor(Deno.inspect(nestedObj)), nestedObjExpected);
|
||||
});
|
||||
|
||||
Deno.test(function consoleTestStringifyMultipleCircular() {
|
||||
const y = { a: { b: {} }, foo: { bar: {} } };
|
||||
y.a.b = y.a;
|
||||
y.foo.bar = y.foo;
|
||||
console.log(y);
|
||||
assertEquals(
|
||||
stringify(y),
|
||||
"{ a: <ref *1> { b: [Circular *1] }, foo: <ref *2> { bar: [Circular *2] } }",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test(function consoleTestStringifyFunctionWithPrototypeRemoved() {
|
||||
const f = function f() {};
|
||||
Reflect.setPrototypeOf(f, null);
|
||||
|
@ -392,14 +403,14 @@ Deno.test(function consoleTestStringifyFunctionWithProperties() {
|
|||
assertEquals(
|
||||
stringify({ f }),
|
||||
`{
|
||||
f: [Function: f] {
|
||||
f: <ref *1> [Function: f] {
|
||||
x: [Function],
|
||||
y: 3,
|
||||
z: [Function],
|
||||
b: [Function: bar],
|
||||
a: Map {},
|
||||
s: [Circular],
|
||||
t: [Function: t] { x: [Circular] }
|
||||
s: [Circular *1],
|
||||
t: [Function: t] { x: [Circular *1] }
|
||||
}
|
||||
}`,
|
||||
);
|
||||
|
@ -1864,12 +1875,16 @@ Deno.test(function inspectErrorCircular() {
|
|||
);
|
||||
assertStringIncludes(
|
||||
stripColor(Deno.inspect(error2)),
|
||||
"Error: This is an error",
|
||||
"<ref *1> Error: This is an error",
|
||||
);
|
||||
assertStringIncludes(
|
||||
stripColor(Deno.inspect(error2)),
|
||||
"Caused by Error: This is a cause error",
|
||||
);
|
||||
assertStringIncludes(
|
||||
stripColor(Deno.inspect(error2)),
|
||||
"Caused by [Circular *1]",
|
||||
);
|
||||
});
|
||||
|
||||
Deno.test(function inspectColors() {
|
||||
|
|
|
@ -72,6 +72,7 @@
|
|||
ArrayPrototypePop,
|
||||
ArrayPrototypeSort,
|
||||
ArrayPrototypeSlice,
|
||||
ArrayPrototypeShift,
|
||||
ArrayPrototypeIncludes,
|
||||
ArrayPrototypeFill,
|
||||
ArrayPrototypeFilter,
|
||||
|
@ -340,11 +341,17 @@
|
|||
// If we didn't find any properties, we will just append an
|
||||
// empty suffix.
|
||||
let suffix = ``;
|
||||
let refStr = "";
|
||||
if (
|
||||
ObjectKeys(value).length > 0 ||
|
||||
ObjectGetOwnPropertySymbols(value).length > 0
|
||||
) {
|
||||
const propString = inspectRawObject(value, level, inspectOptions);
|
||||
const [propString, refIndex] = inspectRawObject(
|
||||
value,
|
||||
level,
|
||||
inspectOptions,
|
||||
);
|
||||
refStr = refIndex;
|
||||
// Filter out the empty string for the case we only have
|
||||
// non-enumerable symbols.
|
||||
if (
|
||||
|
@ -357,9 +364,9 @@
|
|||
|
||||
if (value.name && value.name !== "anonymous") {
|
||||
// from MDN spec
|
||||
return cyan(`[${cstrName}: ${value.name}]`) + suffix;
|
||||
return cyan(`${refStr}[${cstrName}: ${value.name}]`) + suffix;
|
||||
}
|
||||
return cyan(`[${cstrName}]`) + suffix;
|
||||
return cyan(`${refStr}[${cstrName}]`) + suffix;
|
||||
}
|
||||
|
||||
function inspectIterable(
|
||||
|
@ -589,6 +596,23 @@
|
|||
return entries;
|
||||
}
|
||||
|
||||
let circular;
|
||||
function handleCircular(value, cyan) {
|
||||
let index = 1;
|
||||
if (circular === undefined) {
|
||||
circular = new Map();
|
||||
MapPrototypeSet(circular, value, index);
|
||||
} else {
|
||||
index = MapPrototypeGet(circular, value);
|
||||
if (index === undefined) {
|
||||
index = circular.size + 1;
|
||||
MapPrototypeSet(circular, value, index);
|
||||
}
|
||||
}
|
||||
// Circular string is cyan
|
||||
return cyan(`[Circular *${index}]`);
|
||||
}
|
||||
|
||||
function _inspectValue(
|
||||
value,
|
||||
level,
|
||||
|
@ -623,7 +647,7 @@
|
|||
case "function": // Function string is cyan
|
||||
if (ctxHas(value)) {
|
||||
// Circular string is cyan
|
||||
return cyan("[Circular]");
|
||||
return handleCircular(value, cyan);
|
||||
}
|
||||
|
||||
return inspectFunction(value, level, inspectOptions);
|
||||
|
@ -633,8 +657,7 @@
|
|||
}
|
||||
|
||||
if (ctxHas(value)) {
|
||||
// Circular string is cyan
|
||||
return cyan("[Circular]");
|
||||
return handleCircular(value, cyan);
|
||||
}
|
||||
return inspectObject(value, level, inspectOptions);
|
||||
default:
|
||||
|
@ -892,25 +915,41 @@
|
|||
return red(RegExpPrototypeToString(value)); // RegExps are red
|
||||
}
|
||||
|
||||
function inspectError(value) {
|
||||
const causes = [];
|
||||
function inspectError(value, cyan) {
|
||||
const causes = [value];
|
||||
|
||||
let err = value;
|
||||
while (
|
||||
ObjectPrototypeIsPrototypeOf(ErrorPrototype, err.cause) &&
|
||||
err.cause !== value &&
|
||||
!ArrayPrototypeIncludes(causes, err.cause) // circular check
|
||||
) {
|
||||
ArrayPrototypePush(causes, err.cause);
|
||||
err = err.cause;
|
||||
while (err.cause) {
|
||||
if (ArrayPrototypeIncludes(causes, err.cause)) {
|
||||
ArrayPrototypePush(causes, handleCircular(err.cause, cyan));
|
||||
break;
|
||||
} else {
|
||||
ArrayPrototypePush(causes, err.cause);
|
||||
err = err.cause;
|
||||
}
|
||||
}
|
||||
|
||||
return `${value.stack}${
|
||||
const refMap = new Map();
|
||||
for (const cause of causes) {
|
||||
if (circular !== undefined) {
|
||||
const index = MapPrototypeGet(circular, cause);
|
||||
if (index !== undefined) {
|
||||
MapPrototypeSet(refMap, cause, cyan(`<ref *${index}> `));
|
||||
}
|
||||
}
|
||||
}
|
||||
ArrayPrototypeShift(causes);
|
||||
|
||||
return (MapPrototypeGet(refMap, value) ?? "") + value.stack +
|
||||
ArrayPrototypeJoin(
|
||||
ArrayPrototypeMap(causes, (cause) => `\nCaused by ${cause.stack}`),
|
||||
ArrayPrototypeMap(
|
||||
causes,
|
||||
(cause) =>
|
||||
"\nCaused by " + (MapPrototypeGet(refMap, cause) ?? "") +
|
||||
(cause?.stack ?? cause),
|
||||
),
|
||||
"",
|
||||
)
|
||||
}`;
|
||||
);
|
||||
}
|
||||
|
||||
function inspectStringObject(value, inspectOptions) {
|
||||
|
@ -1002,7 +1041,7 @@
|
|||
const cyan = maybeColor(colors.cyan, inspectOptions);
|
||||
|
||||
if (level >= inspectOptions.depth) {
|
||||
return cyan("[Object]"); // wrappers are in cyan
|
||||
return [cyan("[Object]"), ""]; // wrappers are in cyan
|
||||
}
|
||||
|
||||
let baseString;
|
||||
|
@ -1144,7 +1183,15 @@
|
|||
baseString = `${displayName} ${baseString}`;
|
||||
}
|
||||
|
||||
return baseString;
|
||||
let refIndex = "";
|
||||
if (circular !== undefined) {
|
||||
const index = MapPrototypeGet(circular, value);
|
||||
if (index !== undefined) {
|
||||
refIndex = `<ref *${index}> `;
|
||||
}
|
||||
}
|
||||
|
||||
return [baseString, refIndex];
|
||||
}
|
||||
|
||||
function inspectObject(
|
||||
|
@ -1171,7 +1218,7 @@
|
|||
return String(value[privateCustomInspect](inspect));
|
||||
}
|
||||
if (ObjectPrototypeIsPrototypeOf(ErrorPrototype, value)) {
|
||||
return inspectError(value);
|
||||
return inspectError(value, maybeColor(colors.cyan, inspectOptions));
|
||||
} else if (ArrayIsArray(value)) {
|
||||
return inspectArray(value, level, inspectOptions);
|
||||
} else if (ObjectPrototypeIsPrototypeOf(NumberPrototype, value)) {
|
||||
|
@ -1207,7 +1254,9 @@
|
|||
);
|
||||
} else {
|
||||
// Otherwise, default object formatting
|
||||
return inspectRawObject(value, level, inspectOptions);
|
||||
let [insp, refIndex] = inspectRawObject(value, level, inspectOptions);
|
||||
insp = refIndex + insp;
|
||||
return insp;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1660,6 +1709,8 @@
|
|||
}
|
||||
|
||||
function inspectArgs(args, inspectOptions = {}) {
|
||||
circular = undefined;
|
||||
|
||||
const noColor = colors.getNoColor();
|
||||
const rInspectOptions = { ...DEFAULT_INSPECT_OPTIONS, ...inspectOptions };
|
||||
const first = args[0];
|
||||
|
@ -2076,6 +2127,7 @@
|
|||
value,
|
||||
inspectOptions = {},
|
||||
) {
|
||||
circular = undefined;
|
||||
return inspectValue(value, 0, {
|
||||
...DEFAULT_INSPECT_OPTIONS,
|
||||
...inspectOptions,
|
||||
|
|
Loading…
Add table
Reference in a new issue