-
Notifications
You must be signed in to change notification settings - Fork 5.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add more console types formatting support #1299
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -9,7 +9,19 @@ type ConsoleOptions = Partial<{ | |
}>; | ||
|
||
// Default depth of logging nested objects | ||
const DEFAULT_MAX_DEPTH = 2; | ||
const DEFAULT_MAX_DEPTH = 4; | ||
|
||
const TYPEDARRAY_TYPES = [ | ||
Int8Array, | ||
Uint8Array, | ||
Uint8ClampedArray, | ||
Int16Array, | ||
Uint16Array, | ||
Int32Array, | ||
Uint32Array, | ||
Float32Array, | ||
Float64Array | ||
]; | ||
|
||
// tslint:disable-next-line:no-any | ||
function getClassInstanceName(instance: any): string { | ||
|
@@ -35,31 +47,170 @@ function createFunctionString(value: Function, ctx: ConsoleContext): string { | |
return `[${cstrName}]`; | ||
} | ||
|
||
function createArrayString( | ||
interface IterablePrintConfig { | ||
typeName: string; | ||
displayName: string; | ||
leftDelim: string; | ||
rightDelim: string; | ||
entryHandler: ( | ||
// tslint:disable-next-line:no-any | ||
entry: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
) => string; | ||
} | ||
|
||
function createIterableString( | ||
// tslint:disable-next-line:no-any | ||
value: any[], | ||
value: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
maxLevel: number, | ||
config: IterablePrintConfig | ||
): string { | ||
if (level >= maxLevel) { | ||
return `[${config.typeName}]`; | ||
} | ||
ctx.add(value); | ||
|
||
const entries: string[] = []; | ||
for (const el of value) { | ||
entries.push(stringifyWithQuotes(ctx, el, level + 1, maxLevel)); | ||
entries.push(config.entryHandler(el, ctx, level + 1, maxLevel)); | ||
} | ||
ctx.delete(value); | ||
if (entries.length === 0) { | ||
return "[]"; | ||
} | ||
return `[ ${entries.join(", ")} ]`; | ||
const iPrefix = `${config.displayName ? config.displayName + " " : ""}`; | ||
const iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `; | ||
return `${iPrefix}${config.leftDelim}${iContent}${config.rightDelim}`; | ||
} | ||
|
||
function createObjectString( | ||
function createArrayString( | ||
// tslint:disable-next-line:no-any | ||
value: any[], | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
const printConfig: IterablePrintConfig = { | ||
typeName: "Array", | ||
displayName: "", | ||
leftDelim: "[", | ||
rightDelim: "]", | ||
entryHandler: (el, ctx, level, maxLevel) => | ||
stringifyWithQuotes(el, ctx, level + 1, maxLevel) | ||
}; | ||
return createIterableString(value, ctx, level, maxLevel, printConfig); | ||
} | ||
|
||
function createTypedArrayString( | ||
typedArrayName: string, | ||
// tslint:disable-next-line:no-any | ||
value: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
const printConfig: IterablePrintConfig = { | ||
typeName: typedArrayName, | ||
displayName: typedArrayName, | ||
leftDelim: "[", | ||
rightDelim: "]", | ||
entryHandler: (el, ctx, level, maxLevel) => | ||
stringifyWithQuotes(el, ctx, level + 1, maxLevel) | ||
}; | ||
return createIterableString(value, ctx, level, maxLevel, printConfig); | ||
} | ||
|
||
function createSetString( | ||
// tslint:disable-next-line:no-any | ||
value: Set<any>, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
const printConfig: IterablePrintConfig = { | ||
typeName: "Set", | ||
displayName: "Set", | ||
leftDelim: "{", | ||
rightDelim: "}", | ||
entryHandler: (el, ctx, level, maxLevel) => | ||
stringifyWithQuotes(el, ctx, level + 1, maxLevel) | ||
}; | ||
return createIterableString(value, ctx, level, maxLevel, printConfig); | ||
} | ||
|
||
function createMapString( | ||
// tslint:disable-next-line:no-any | ||
value: Map<any, any>, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
const printConfig: IterablePrintConfig = { | ||
typeName: "Map", | ||
displayName: "Map", | ||
leftDelim: "{", | ||
rightDelim: "}", | ||
entryHandler: (el, ctx, level, maxLevel) => { | ||
const [key, val] = el; | ||
return `${stringifyWithQuotes( | ||
key, | ||
ctx, | ||
level + 1, | ||
maxLevel | ||
)} => ${stringifyWithQuotes(val, ctx, level + 1, maxLevel)}`; | ||
} | ||
}; | ||
return createIterableString(value, ctx, level, maxLevel, printConfig); | ||
} | ||
|
||
function createWeakSetString(): string { | ||
return "WeakSet { [items unknown] }"; // as seen in Node | ||
} | ||
|
||
function createWeakMapString(): string { | ||
return "WeakMap { [items unknown] }"; // as seen in Node | ||
} | ||
|
||
function createDateString(value: Date) { | ||
// without quotes, ISO format | ||
return value.toISOString(); | ||
} | ||
|
||
function createRegExpString(value: RegExp) { | ||
return value.toString(); | ||
} | ||
|
||
// tslint:disable-next-line:ban-types | ||
function createStringWrapperString(value: String) { | ||
return `[String: "${value.toString()}"]`; | ||
} | ||
|
||
// tslint:disable-next-line:ban-types | ||
function createBooleanWrapperString(value: Boolean) { | ||
return `[Boolean: ${value.toString()}]`; | ||
} | ||
|
||
// tslint:disable-next-line:ban-types | ||
function createNumberWrapperString(value: Number) { | ||
return `[Number: ${value.toString()}]`; | ||
} | ||
|
||
// TODO: Promise, requires v8 bindings to get info | ||
// TODO: Proxy | ||
|
||
function createRawObjectString( | ||
// tslint:disable-next-line:no-any | ||
value: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
if (level >= maxLevel) { | ||
return "[Object]"; | ||
} | ||
ctx.add(value); | ||
|
||
const entries: string[] = []; | ||
let baseString = ""; | ||
|
||
|
@@ -71,7 +222,7 @@ function createObjectString( | |
|
||
for (const key of Object.keys(value)) { | ||
entries.push( | ||
`${key}: ${stringifyWithQuotes(ctx, value[key], level + 1, maxLevel)}` | ||
`${key}: ${stringifyWithQuotes(value[key], ctx, level + 1, maxLevel)}` | ||
); | ||
} | ||
|
||
|
@@ -90,10 +241,59 @@ function createObjectString( | |
return baseString; | ||
} | ||
|
||
function createObjectString( | ||
// tslint:disable-next-line:no-any | ||
value: any, | ||
...args: [ConsoleContext, number, number] | ||
): string { | ||
if (value instanceof Error) { | ||
return value.stack! || ""; | ||
} else if (Array.isArray(value)) { | ||
return createArrayString(value, ...args); | ||
} else if (value instanceof Number) { | ||
// tslint:disable-next-line:ban-types | ||
return createNumberWrapperString(value as Number); | ||
} else if (value instanceof Boolean) { | ||
// tslint:disable-next-line:ban-types | ||
return createBooleanWrapperString(value as Boolean); | ||
} else if (value instanceof String) { | ||
// tslint:disable-next-line:ban-types | ||
return createStringWrapperString(value as String); | ||
} else if (value instanceof RegExp) { | ||
return createRegExpString(value as RegExp); | ||
} else if (value instanceof Date) { | ||
return createDateString(value as Date); | ||
} else if (value instanceof Set) { | ||
// tslint:disable-next-line:no-any | ||
return createSetString(value as Set<any>, ...args); | ||
} else if (value instanceof Map) { | ||
// tslint:disable-next-line:no-any | ||
return createMapString(value as Map<any, any>, ...args); | ||
} else if (value instanceof WeakSet) { | ||
return createWeakSetString(); | ||
} else if (value instanceof WeakMap) { | ||
return createWeakMapString(); | ||
} else { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The next line could be inlined for consistency with the other conditions, using There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh yeah... (I was too tired and totally ignored this...) |
||
// Check if it is a typed array | ||
for (const cstr of TYPEDARRAY_TYPES) { | ||
kevinkassimo marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if (value instanceof cstr) { | ||
return createTypedArrayString( | ||
cstr.prototype.constructor.name, | ||
value, | ||
...args | ||
); | ||
} | ||
} | ||
|
||
// Otherwise, default object formatting | ||
return createRawObjectString(value, ...args); | ||
} | ||
} | ||
|
||
function stringify( | ||
ctx: ConsoleContext, | ||
// tslint:disable-next-line:no-any | ||
value: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
|
@@ -118,38 +318,25 @@ function stringify( | |
return "[Circular]"; | ||
} | ||
|
||
if (level >= maxLevel) { | ||
return `[object]`; | ||
} | ||
|
||
ctx.add(value); | ||
|
||
if (value instanceof Error) { | ||
return value.stack! || ""; | ||
} else if (Array.isArray(value)) { | ||
// tslint:disable-next-line:no-any | ||
return createArrayString(value as any[], ctx, level, maxLevel); | ||
} else { | ||
return createObjectString(value, ctx, level, maxLevel); | ||
} | ||
return createObjectString(value, ctx, level, maxLevel); | ||
default: | ||
return "[Not Implemented]"; | ||
} | ||
} | ||
|
||
// Print strings when they are inside of arrays or objects with quotes | ||
function stringifyWithQuotes( | ||
ctx: ConsoleContext, | ||
// tslint:disable-next-line:no-any | ||
value: any, | ||
ctx: ConsoleContext, | ||
level: number, | ||
maxLevel: number | ||
): string { | ||
switch (typeof value) { | ||
case "string": | ||
return `"${value}"`; | ||
default: | ||
return stringify(ctx, value, level, maxLevel); | ||
return stringify(value, ctx, level, maxLevel); | ||
} | ||
} | ||
|
||
|
@@ -167,9 +354,9 @@ export function stringifyArgs( | |
out.push( | ||
// use default maximum depth for null or undefined argument | ||
stringify( | ||
a, | ||
// tslint:disable-next-line:no-any | ||
new Set<any>(), | ||
a, | ||
0, | ||
// tslint:disable-next-line:triple-equals | ||
options.depth != undefined ? options.depth : DEFAULT_MAX_DEPTH | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I saw somewhere in another commit of deno use
TypedArray
, which can be exposed by doingObject.getPrototypeof(Int8Array)
, which all typed arrays extend. It's shorter and provides support forBigInt64Array
andBigUint64Array
, which the version of V8 we use supports, and possibly any other typed array that gets implemented in the future.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Found it! It's in
util.ts
, we could use this function instead of running a loop for each typed array:deno/js/util.ts
Lines 129 to 133 in c427c2d
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great! Thanks for the suggestion. Will update right away
(Did not notice that I should import
TypedArray
fromtypes.ts
)There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, that too 👍