summaryrefslogtreecommitdiff
path: root/cli/js
diff options
context:
space:
mode:
Diffstat (limited to 'cli/js')
-rw-r--r--cli/js/tests/console_test.ts301
-rw-r--r--cli/js/web/console.ts224
-rw-r--r--cli/js/web/util.ts25
3 files changed, 520 insertions, 30 deletions
diff --git a/cli/js/tests/console_test.ts b/cli/js/tests/console_test.ts
index b4848332f..c49c941f4 100644
--- a/cli/js/tests/console_test.ts
+++ b/cli/js/tests/console_test.ts
@@ -181,8 +181,11 @@ unitTest(function consoleTestStringifyCircular(): void {
stringify(async function* agf() {}),
"[AsyncGeneratorFunction: agf]"
);
- assertEquals(stringify(new Uint8Array([1, 2, 3])), "Uint8Array [ 1, 2, 3 ]");
- assertEquals(stringify(Uint8Array.prototype), "TypedArray []");
+ assertEquals(
+ stringify(new Uint8Array([1, 2, 3])),
+ "Uint8Array(3) [ 1, 2, 3 ]"
+ );
+ assertEquals(stringify(Uint8Array.prototype), "TypedArray {}");
assertEquals(
stringify({ a: { b: { c: { d: new Set([1]) } } } }),
"{ a: { b: { c: { d: [Set] } } } }"
@@ -283,6 +286,300 @@ unitTest(function consoleTestStringifyLargeObject(): void {
);
});
+unitTest(function consoleTestStringifyIterable() {
+ const shortArray = [1, 2, 3, 4, 5];
+ assertEquals(stringify(shortArray), "[ 1, 2, 3, 4, 5 ]");
+
+ const longArray = new Array(200).fill(0);
+ assertEquals(
+ stringify(longArray),
+ `[
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ... 100 more items
+]`
+ );
+
+ const obj = { a: "a", longArray };
+ assertEquals(
+ stringify(obj),
+ `{
+ a: "a",
+ longArray: [
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ ... 100 more items
+ ]
+}`
+ );
+
+ const shortMap = new Map([
+ ["a", 0],
+ ["b", 1],
+ ]);
+ assertEquals(stringify(shortMap), `Map { "a" => 0, "b" => 1 }`);
+
+ const longMap = new Map();
+ for (const key of Array(200).keys()) {
+ longMap.set(`${key}`, key);
+ }
+ assertEquals(
+ stringify(longMap),
+ `Map {
+ "0" => 0,
+ "1" => 1,
+ "2" => 2,
+ "3" => 3,
+ "4" => 4,
+ "5" => 5,
+ "6" => 6,
+ "7" => 7,
+ "8" => 8,
+ "9" => 9,
+ "10" => 10,
+ "11" => 11,
+ "12" => 12,
+ "13" => 13,
+ "14" => 14,
+ "15" => 15,
+ "16" => 16,
+ "17" => 17,
+ "18" => 18,
+ "19" => 19,
+ "20" => 20,
+ "21" => 21,
+ "22" => 22,
+ "23" => 23,
+ "24" => 24,
+ "25" => 25,
+ "26" => 26,
+ "27" => 27,
+ "28" => 28,
+ "29" => 29,
+ "30" => 30,
+ "31" => 31,
+ "32" => 32,
+ "33" => 33,
+ "34" => 34,
+ "35" => 35,
+ "36" => 36,
+ "37" => 37,
+ "38" => 38,
+ "39" => 39,
+ "40" => 40,
+ "41" => 41,
+ "42" => 42,
+ "43" => 43,
+ "44" => 44,
+ "45" => 45,
+ "46" => 46,
+ "47" => 47,
+ "48" => 48,
+ "49" => 49,
+ "50" => 50,
+ "51" => 51,
+ "52" => 52,
+ "53" => 53,
+ "54" => 54,
+ "55" => 55,
+ "56" => 56,
+ "57" => 57,
+ "58" => 58,
+ "59" => 59,
+ "60" => 60,
+ "61" => 61,
+ "62" => 62,
+ "63" => 63,
+ "64" => 64,
+ "65" => 65,
+ "66" => 66,
+ "67" => 67,
+ "68" => 68,
+ "69" => 69,
+ "70" => 70,
+ "71" => 71,
+ "72" => 72,
+ "73" => 73,
+ "74" => 74,
+ "75" => 75,
+ "76" => 76,
+ "77" => 77,
+ "78" => 78,
+ "79" => 79,
+ "80" => 80,
+ "81" => 81,
+ "82" => 82,
+ "83" => 83,
+ "84" => 84,
+ "85" => 85,
+ "86" => 86,
+ "87" => 87,
+ "88" => 88,
+ "89" => 89,
+ "90" => 90,
+ "91" => 91,
+ "92" => 92,
+ "93" => 93,
+ "94" => 94,
+ "95" => 95,
+ "96" => 96,
+ "97" => 97,
+ "98" => 98,
+ "99" => 99,
+ ... 100 more items
+}`
+ );
+
+ const shortSet = new Set([1, 2, 3]);
+ assertEquals(stringify(shortSet), `Set { 1, 2, 3 }`);
+ const longSet = new Set();
+ for (const key of Array(200).keys()) {
+ longSet.add(key);
+ }
+ assertEquals(
+ stringify(longSet),
+ `Set {
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 46,
+ 47,
+ 48,
+ 49,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 55,
+ 56,
+ 57,
+ 58,
+ 59,
+ 60,
+ 61,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 92,
+ 93,
+ 94,
+ 95,
+ 96,
+ 97,
+ 98,
+ 99,
+ ... 100 more items
+}`
+ );
+
+ const withEmptyEl = Array(10);
+ withEmptyEl.fill(0, 4, 6);
+ assertEquals(
+ stringify(withEmptyEl),
+ `[ <4 empty items>, 0, 0, <4 empty items> ]`
+ );
+
+ const lWithEmptyEl = Array(200);
+ lWithEmptyEl.fill(0, 50, 80);
+ assertEquals(
+ stringify(lWithEmptyEl),
+ `[
+ <50 empty items>, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, 0, 0,
+ 0, <120 empty items>
+]`
+ );
+});
+
unitTest(function consoleTestWithCustomInspector(): void {
class A {
[customInspect](): string {
diff --git a/cli/js/web/console.ts b/cli/js/web/console.ts
index 554c5a1b3..a9b4d53be 100644
--- a/cli/js/web/console.ts
+++ b/cli/js/web/console.ts
@@ -13,13 +13,11 @@ type InspectOptions = Partial<{
indentLevel: number;
}>;
-// Default depth of logging nested objects
-const DEFAULT_MAX_DEPTH = 4;
-
+const DEFAULT_MAX_DEPTH = 4; // Default depth of logging nested objects
const LINE_BREAKING_LENGTH = 80;
-
+const MAX_ITERABLE_LENGTH = 100;
+const MIN_GROUP_LENGTH = 6;
const STR_ABBREVIATE_SIZE = 100;
-
// Char codes
const CHAR_PERCENT = 37; /* % */
const CHAR_LOWERCASE_S = 115; /* s */
@@ -29,6 +27,7 @@ const CHAR_LOWERCASE_F = 102; /* f */
const CHAR_LOWERCASE_O = 111; /* o */
const CHAR_UPPERCASE_O = 79; /* O */
const CHAR_LOWERCASE_C = 99; /* c */
+
export class CSI {
static kClear = "\x1b[1;1H";
static kClearScreenDown = "\x1b[0J";
@@ -77,15 +76,19 @@ interface IterablePrintConfig<T> {
displayName: string;
delims: [string, string];
entryHandler: (
- entry: T,
+ entry: [unknown, T],
ctx: ConsoleContext,
level: number,
- maxLevel: number
+ maxLevel: number,
+ next: () => IteratorResult<[unknown, T], unknown>
) => string;
+ group: boolean;
}
-
+type IterableEntries<T> = Iterable<T> & {
+ entries(): IterableIterator<[unknown, T]>;
+};
function createIterableString<T>(
- value: Iterable<T>,
+ value: IterableEntries<T>,
ctx: ConsoleContext,
level: number,
maxLevel: number,
@@ -97,18 +100,165 @@ function createIterableString<T>(
ctx.add(value);
const entries: string[] = [];
- // In cases e.g. Uint8Array.prototype
- try {
- for (const el of value) {
- entries.push(config.entryHandler(el, ctx, level + 1, maxLevel));
+
+ const iter = value.entries();
+ let entriesLength = 0;
+ const next = (): IteratorResult<[unknown, T], unknown> => {
+ return iter.next();
+ };
+ for (const el of iter) {
+ if (entriesLength < MAX_ITERABLE_LENGTH) {
+ entries.push(
+ config.entryHandler(el, ctx, level + 1, maxLevel, next.bind(iter))
+ );
}
- } catch (e) {}
+ entriesLength++;
+ }
ctx.delete(value);
+
+ if (entriesLength > MAX_ITERABLE_LENGTH) {
+ const nmore = entriesLength - MAX_ITERABLE_LENGTH;
+ entries.push(`... ${nmore} more items`);
+ }
+
const iPrefix = `${config.displayName ? config.displayName + " " : ""}`;
- const iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `;
+
+ let iContent: string;
+ if (config.group && entries.length > MIN_GROUP_LENGTH) {
+ const groups = groupEntries(entries, level, value);
+ const initIndentation = `\n${" ".repeat(level + 1)}`;
+ const entryIndetation = `,\n${" ".repeat(level + 1)}`;
+ const closingIndentation = `\n${" ".repeat(level)}`;
+
+ iContent = `${initIndentation}${groups.join(
+ entryIndetation
+ )}${closingIndentation}`;
+ } else {
+ iContent = entries.length === 0 ? "" : ` ${entries.join(", ")} `;
+ if (iContent.length > LINE_BREAKING_LENGTH) {
+ const initIndentation = `\n${" ".repeat(level + 1)}`;
+ const entryIndetation = `,\n${" ".repeat(level + 1)}`;
+ const closingIndentation = `\n`;
+
+ iContent = `${initIndentation}${entries.join(
+ entryIndetation
+ )}${closingIndentation}`;
+ }
+ }
+
return `${iPrefix}${config.delims[0]}${iContent}${config.delims[1]}`;
}
+// Ported from Node.js
+// Copyright Node.js contributors. All rights reserved.
+function groupEntries<T>(
+ entries: string[],
+ level: number,
+ value: Iterable<T>
+): string[] {
+ let totalLength = 0;
+ let maxLength = 0;
+ let entriesLength = entries.length;
+ if (MAX_ITERABLE_LENGTH < entriesLength) {
+ // This makes sure the "... n more items" part is not taken into account.
+ entriesLength--;
+ }
+ const separatorSpace = 2; // Add 1 for the space and 1 for the separator.
+ const dataLen = new Array(entriesLength);
+ // Calculate the total length of all output entries and the individual max
+ // entries length of all output entries. In future colors should be taken
+ // here into the account
+ for (let i = 0; i < entriesLength; i++) {
+ const len = entries[i].length;
+ dataLen[i] = len;
+ totalLength += len + separatorSpace;
+ if (maxLength < len) maxLength = len;
+ }
+ // Add two to `maxLength` as we add a single whitespace character plus a comma
+ // in-between two entries.
+ const actualMax = maxLength + separatorSpace;
+ // Check if at least three entries fit next to each other and prevent grouping
+ // of arrays that contains entries of very different length (i.e., if a single
+ // entry is longer than 1/5 of all other entries combined). Otherwise the
+ // space in-between small entries would be enormous.
+ if (
+ actualMax * 3 + (level + 1) < LINE_BREAKING_LENGTH &&
+ (totalLength / actualMax > 5 || maxLength <= 6)
+ ) {
+ const approxCharHeights = 2.5;
+ const averageBias = Math.sqrt(actualMax - totalLength / entries.length);
+ const biasedMax = Math.max(actualMax - 3 - averageBias, 1);
+ // Dynamically check how many columns seem possible.
+ const columns = Math.min(
+ // Ideally a square should be drawn. We expect a character to be about 2.5
+ // times as high as wide. This is the area formula to calculate a square
+ // which contains n rectangles of size `actualMax * approxCharHeights`.
+ // Divide that by `actualMax` to receive the correct number of columns.
+ // The added bias increases the columns for short entries.
+ Math.round(
+ Math.sqrt(approxCharHeights * biasedMax * entriesLength) / biasedMax
+ ),
+ // Do not exceed the breakLength.
+ Math.floor((LINE_BREAKING_LENGTH - (level + 1)) / actualMax),
+ // Limit the columns to a maximum of fifteen.
+ 15
+ );
+ // Return with the original output if no grouping should happen.
+ if (columns <= 1) {
+ return entries;
+ }
+ const tmp = [];
+ const maxLineLength = [];
+ for (let i = 0; i < columns; i++) {
+ let lineMaxLength = 0;
+ for (let j = i; j < entries.length; j += columns) {
+ if (dataLen[j] > lineMaxLength) lineMaxLength = dataLen[j];
+ }
+ lineMaxLength += separatorSpace;
+ maxLineLength[i] = lineMaxLength;
+ }
+ let order = "padStart";
+ if (value !== undefined) {
+ for (let i = 0; i < entries.length; i++) {
+ //@ts-ignore
+ if (typeof value[i] !== "number" && typeof value[i] !== "bigint") {
+ order = "padEnd";
+ break;
+ }
+ }
+ }
+ // Each iteration creates a single line of grouped entries.
+ for (let i = 0; i < entriesLength; i += columns) {
+ // The last lines may contain less entries than columns.
+ const max = Math.min(i + columns, entriesLength);
+ let str = "";
+ let j = i;
+ for (; j < max - 1; j++) {
+ // In future, colors should be taken here into the account
+ const padding = maxLineLength[j - i];
+ //@ts-ignore
+ str += `${entries[j]}, `[order](padding, " ");
+ }
+ if (order === "padStart") {
+ const padding =
+ maxLineLength[j - i] +
+ entries[j].length -
+ dataLen[j] -
+ separatorSpace;
+ str += entries[j].padStart(padding, " ");
+ } else {
+ str += entries[j];
+ }
+ tmp.push(str);
+ }
+ if (MAX_ITERABLE_LENGTH < entries.length) {
+ tmp.push(entries[entriesLength]);
+ }
+ entries = tmp;
+ }
+ return entries;
+}
+
function stringify(
value: unknown,
ctx: ConsoleContext,
@@ -173,8 +323,23 @@ function createArrayString(
typeName: "Array",
displayName: "",
delims: ["[", "]"],
- entryHandler: (el, ctx, level, maxLevel): string =>
- stringifyWithQuotes(el, ctx, level + 1, maxLevel),
+ entryHandler: (entry, ctx, level, maxLevel, next): string => {
+ const [index, val] = entry as [number, unknown];
+ let i = index;
+ if (!value.hasOwnProperty(i)) {
+ i++;
+ while (!value.hasOwnProperty(i) && i < value.length) {
+ next();
+ i++;
+ }
+ const emptyItems = i - index;
+ const ending = emptyItems > 1 ? "s" : "";
+ return `<${emptyItems} empty item${ending}>`;
+ } else {
+ return stringifyWithQuotes(val, ctx, level + 1, maxLevel);
+ }
+ },
+ group: true,
};
return createIterableString(value, ctx, level, maxLevel, printConfig);
}
@@ -186,12 +351,16 @@ function createTypedArrayString(
level: number,
maxLevel: number
): string {
+ const valueLength = value.length;
const printConfig: IterablePrintConfig<unknown> = {
typeName: typedArrayName,
- displayName: typedArrayName,
+ displayName: `${typedArrayName}(${valueLength})`,
delims: ["[", "]"],
- entryHandler: (el, ctx, level, maxLevel): string =>
- stringifyWithQuotes(el, ctx, level + 1, maxLevel),
+ entryHandler: (entry, ctx, level, maxLevel): string => {
+ const [_, val] = entry;
+ return stringifyWithQuotes(val, ctx, level + 1, maxLevel);
+ },
+ group: true,
};
return createIterableString(value, ctx, level, maxLevel, printConfig);
}
@@ -206,8 +375,11 @@ function createSetString(
typeName: "Set",
displayName: "Set",
delims: ["{", "}"],
- entryHandler: (el, ctx, level, maxLevel): string =>
- stringifyWithQuotes(el, ctx, level + 1, maxLevel),
+ entryHandler: (entry, ctx, level, maxLevel): string => {
+ const [_, val] = entry;
+ return stringifyWithQuotes(val, ctx, level + 1, maxLevel);
+ },
+ group: false,
};
return createIterableString(value, ctx, level, maxLevel, printConfig);
}
@@ -218,12 +390,12 @@ function createMapString(
level: number,
maxLevel: number
): string {
- const printConfig: IterablePrintConfig<[unknown, unknown]> = {
+ const printConfig: IterablePrintConfig<[unknown]> = {
typeName: "Map",
displayName: "Map",
delims: ["{", "}"],
- entryHandler: (el, ctx, level, maxLevel): string => {
- const [key, val] = el;
+ entryHandler: (entry, ctx, level, maxLevel): string => {
+ const [key, val] = entry;
return `${stringifyWithQuotes(
key,
ctx,
@@ -231,7 +403,9 @@ function createMapString(
maxLevel
)} => ${stringifyWithQuotes(val, ctx, level + 1, maxLevel)}`;
},
+ group: false,
};
+ //@ts-ignore
return createIterableString(value, ctx, level, maxLevel, printConfig);
}
diff --git a/cli/js/web/util.ts b/cli/js/web/util.ts
index 19a30a675..2d63b4d60 100644
--- a/cli/js/web/util.ts
+++ b/cli/js/web/util.ts
@@ -1,9 +1,28 @@
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
-export type TypedArray = Uint8Array | Float32Array | Int32Array;
-const TypedArrayConstructor = Object.getPrototypeOf(Uint8Array);
+export type TypedArray =
+ | Int8Array
+ | Uint8Array
+ | Uint8ClampedArray
+ | Int16Array
+ | Uint16Array
+ | Int32Array
+ | Uint32Array
+ | Float32Array
+ | Float64Array;
+
export function isTypedArray(x: unknown): x is TypedArray {
- return x instanceof TypedArrayConstructor;
+ return (
+ x instanceof Int8Array ||
+ x instanceof Uint8Array ||
+ x instanceof Uint8ClampedArray ||
+ x instanceof Int16Array ||
+ x instanceof Uint16Array ||
+ x instanceof Int32Array ||
+ x instanceof Uint32Array ||
+ x instanceof Float32Array ||
+ x instanceof Float64Array
+ );
}
// @internal