1'use strict'; 2 3const { 4 Array, 5 ArrayIsArray, 6 BigInt64Array, 7 BigIntPrototypeValueOf, 8 BigUint64Array, 9 BooleanPrototypeValueOf, 10 DatePrototypeGetTime, 11 DatePrototypeToISOString, 12 DatePrototypeToString, 13 ErrorPrototypeToString, 14 Float32Array, 15 FunctionPrototypeCall, 16 FunctionPrototypeToString, 17 Int16Array, 18 JSONStringify, 19 Map, 20 MapPrototype, 21 MapPrototypeEntries, 22 MathFloor, 23 MathMax, 24 MathMin, 25 MathRound, 26 MathSqrt, 27 Number, 28 NumberIsNaN, 29 NumberPrototypeValueOf, 30 Object, 31 ObjectAssign, 32 ObjectCreate, 33 ObjectDefineProperty, 34 ObjectGetOwnPropertyDescriptor, 35 ObjectGetOwnPropertyNames, 36 ObjectGetOwnPropertySymbols, 37 ObjectGetPrototypeOf, 38 ObjectIs, 39 ObjectKeys, 40 ObjectPrototypeHasOwnProperty, 41 ObjectPrototypePropertyIsEnumerable, 42 ObjectSeal, 43 ObjectSetPrototypeOf, 44 RegExp, 45 RegExpPrototypeToString, 46 Set, 47 SetPrototype, 48 SetPrototypeValues, 49 StringPrototypeValueOf, 50 SymbolPrototypeToString, 51 SymbolPrototypeValueOf, 52 SymbolIterator, 53 SymbolToStringTag, 54 Uint16Array, 55 Uint32Array, 56 Uint8Array, 57 Uint8ArrayPrototype, 58 Uint8ClampedArray, 59 uncurryThis, 60} = primordials; 61 62const { 63 getOwnNonIndexProperties, 64 getPromiseDetails, 65 getProxyDetails, 66 kPending, 67 kRejected, 68 previewEntries, 69 getConstructorName: internalGetConstructorName, 70 getExternalValue, 71 propertyFilter: { 72 ALL_PROPERTIES, 73 ONLY_ENUMERABLE 74 } 75} = internalBinding('util'); 76 77const { 78 customInspectSymbol, 79 isError, 80 join, 81 removeColors 82} = require('internal/util'); 83 84const { 85 codes: { 86 ERR_INVALID_ARG_TYPE 87 }, 88 isStackOverflowError 89} = require('internal/errors'); 90 91const { 92 isAsyncFunction, 93 isGeneratorFunction, 94 isAnyArrayBuffer, 95 isArrayBuffer, 96 isArgumentsObject, 97 isBoxedPrimitive, 98 isDataView, 99 isExternal, 100 isMap, 101 isMapIterator, 102 isModuleNamespaceObject, 103 isNativeError, 104 isPromise, 105 isSet, 106 isSetIterator, 107 isWeakMap, 108 isWeakSet, 109 isRegExp, 110 isDate, 111 isTypedArray, 112 isStringObject, 113 isNumberObject, 114 isBooleanObject, 115 isBigIntObject, 116 isUint8Array, 117 isUint8ClampedArray, 118 isUint16Array, 119 isUint32Array, 120 isInt8Array, 121 isInt16Array, 122 isInt32Array, 123 isFloat32Array, 124 isFloat64Array, 125 isBigInt64Array, 126 isBigUint64Array 127} = require('internal/util/types'); 128 129const assert = require('internal/assert'); 130 131const { NativeModule } = require('internal/bootstrap/loaders'); 132 133const setSizeGetter = uncurryThis( 134 ObjectGetOwnPropertyDescriptor(SetPrototype, 'size').get); 135const mapSizeGetter = uncurryThis( 136 ObjectGetOwnPropertyDescriptor(MapPrototype, 'size').get); 137const typedArraySizeGetter = uncurryThis( 138 ObjectGetOwnPropertyDescriptor( 139 ObjectGetPrototypeOf(Uint8ArrayPrototype), 'length').get); 140 141let hexSlice; 142 143const builtInObjects = new Set( 144 ObjectGetOwnPropertyNames(global).filter((e) => /^[A-Z][a-zA-Z0-9]+$/.test(e)) 145); 146 147// https://tc39.es/ecma262/#sec-IsHTMLDDA-internal-slot 148const isUndetectableObject = (v) => typeof v === 'undefined' && v !== undefined; 149 150// These options must stay in sync with `getUserOptions`. So if any option will 151// be added or removed, `getUserOptions` must also be updated accordingly. 152const inspectDefaultOptions = ObjectSeal({ 153 showHidden: false, 154 depth: 2, 155 colors: false, 156 customInspect: true, 157 showProxy: false, 158 maxArrayLength: 100, 159 maxStringLength: Infinity, 160 breakLength: 80, 161 compact: 3, 162 sorted: false, 163 getters: false 164}); 165 166const kObjectType = 0; 167const kArrayType = 1; 168const kArrayExtrasType = 2; 169 170/* eslint-disable no-control-regex */ 171const strEscapeSequencesRegExp = /[\x00-\x1f\x27\x5c]/; 172const strEscapeSequencesReplacer = /[\x00-\x1f\x27\x5c]/g; 173const strEscapeSequencesRegExpSingle = /[\x00-\x1f\x5c]/; 174const strEscapeSequencesReplacerSingle = /[\x00-\x1f\x5c]/g; 175/* eslint-enable no-control-regex */ 176 177const keyStrRegExp = /^[a-zA-Z_][a-zA-Z_0-9]*$/; 178const numberRegExp = /^(0|[1-9][0-9]*)$/; 179 180const coreModuleRegExp = /^ at (?:[^/\\(]+ \(|)((?<![/\\]).+)\.js:\d+:\d+\)?$/; 181const nodeModulesRegExp = /[/\\]node_modules[/\\](.+?)(?=[/\\])/g; 182 183const classRegExp = /^(\s+[^(]*?)\s*{/; 184// eslint-disable-next-line node-core/no-unescaped-regexp-dot 185const stripCommentsRegExp = /(\/\/.*?\n)|(\/\*(.|\n)*?\*\/)/g; 186 187const kMinLineLength = 16; 188 189// Constants to map the iterator state. 190const kWeak = 0; 191const kIterator = 1; 192const kMapEntries = 2; 193 194// Escaped special characters. Use empty strings to fill up unused entries. 195const meta = [ 196 '\\u0000', '\\u0001', '\\u0002', '\\u0003', '\\u0004', 197 '\\u0005', '\\u0006', '\\u0007', '\\b', '\\t', 198 '\\n', '\\u000b', '\\f', '\\r', '\\u000e', 199 '\\u000f', '\\u0010', '\\u0011', '\\u0012', '\\u0013', 200 '\\u0014', '\\u0015', '\\u0016', '\\u0017', '\\u0018', 201 '\\u0019', '\\u001a', '\\u001b', '\\u001c', '\\u001d', 202 '\\u001e', '\\u001f', '', '', '', 203 '', '', '', '', "\\'", '', '', '', '', '', 204 '', '', '', '', '', '', '', '', '', '', 205 '', '', '', '', '', '', '', '', '', '', 206 '', '', '', '', '', '', '', '', '', '', 207 '', '', '', '', '', '', '', '', '', '', 208 '', '', '', '', '', '', '', '\\\\' 209]; 210 211// Regex used for ansi escape code splitting 212// Adopted from https://github.com/chalk/ansi-regex/blob/master/index.js 213// License: MIT, authors: @sindresorhus, Qix-, arjunmehta and LitoMore 214// Matches all ansi escape code sequences in a string 215const ansiPattern = '[\\u001B\\u009B][[\\]()#;?]*' + 216 '(?:(?:(?:[a-zA-Z\\d]*(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?\\u0007)' + 217 '|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-ntqry=><~]))'; 218const ansi = new RegExp(ansiPattern, 'g'); 219 220let getStringWidth; 221 222function getUserOptions(ctx, isCrossContext) { 223 const ret = { 224 stylize: ctx.stylize, 225 showHidden: ctx.showHidden, 226 depth: ctx.depth, 227 colors: ctx.colors, 228 customInspect: ctx.customInspect, 229 showProxy: ctx.showProxy, 230 maxArrayLength: ctx.maxArrayLength, 231 maxStringLength: ctx.maxStringLength, 232 breakLength: ctx.breakLength, 233 compact: ctx.compact, 234 sorted: ctx.sorted, 235 getters: ctx.getters, 236 ...ctx.userOptions 237 }; 238 239 // Typically, the target value will be an instance of `Object`. If that is 240 // *not* the case, the object may come from another vm.Context, and we want 241 // to avoid passing it objects from this Context in that case, so we remove 242 // the prototype from the returned object itself + the `stylize()` function, 243 // and remove all other non-primitives, including non-primitive user options. 244 if (isCrossContext) { 245 ObjectSetPrototypeOf(ret, null); 246 for (const key of ObjectKeys(ret)) { 247 if ((typeof ret[key] === 'object' || typeof ret[key] === 'function') && 248 ret[key] !== null) { 249 delete ret[key]; 250 } 251 } 252 ret.stylize = ObjectSetPrototypeOf((value, flavour) => { 253 let stylized; 254 try { 255 stylized = `${ctx.stylize(value, flavour)}`; 256 } catch {} 257 258 if (typeof stylized !== 'string') return value; 259 // `stylized` is a string as it should be, which is safe to pass along. 260 return stylized; 261 }, null); 262 } 263 264 return ret; 265} 266 267/** 268 * Echos the value of any input. Tries to print the value out 269 * in the best way possible given the different types. 270 * 271 * @param {any} value The value to print out. 272 * @param {Object} opts Optional options object that alters the output. 273 */ 274/* Legacy: value, showHidden, depth, colors */ 275function inspect(value, opts) { 276 // Default options 277 const ctx = { 278 budget: {}, 279 indentationLvl: 0, 280 seen: [], 281 currentDepth: 0, 282 stylize: stylizeNoColor, 283 showHidden: inspectDefaultOptions.showHidden, 284 depth: inspectDefaultOptions.depth, 285 colors: inspectDefaultOptions.colors, 286 customInspect: inspectDefaultOptions.customInspect, 287 showProxy: inspectDefaultOptions.showProxy, 288 maxArrayLength: inspectDefaultOptions.maxArrayLength, 289 maxStringLength: inspectDefaultOptions.maxStringLength, 290 breakLength: inspectDefaultOptions.breakLength, 291 compact: inspectDefaultOptions.compact, 292 sorted: inspectDefaultOptions.sorted, 293 getters: inspectDefaultOptions.getters 294 }; 295 if (arguments.length > 1) { 296 // Legacy... 297 if (arguments.length > 2) { 298 if (arguments[2] !== undefined) { 299 ctx.depth = arguments[2]; 300 } 301 if (arguments.length > 3 && arguments[3] !== undefined) { 302 ctx.colors = arguments[3]; 303 } 304 } 305 // Set user-specified options 306 if (typeof opts === 'boolean') { 307 ctx.showHidden = opts; 308 } else if (opts) { 309 const optKeys = ObjectKeys(opts); 310 for (const key of optKeys) { 311 // TODO(BridgeAR): Find a solution what to do about stylize. Either make 312 // this function public or add a new API with a similar or better 313 // functionality. 314 if ( 315 ObjectPrototypeHasOwnProperty(inspectDefaultOptions, key) || 316 key === 'stylize') { 317 ctx[key] = opts[key]; 318 } else if (ctx.userOptions === undefined) { 319 // This is required to pass through the actual user input. 320 ctx.userOptions = opts; 321 } 322 } 323 } 324 } 325 if (ctx.colors) ctx.stylize = stylizeWithColor; 326 if (ctx.maxArrayLength === null) ctx.maxArrayLength = Infinity; 327 if (ctx.maxStringLength === null) ctx.maxStringLength = Infinity; 328 return formatValue(ctx, value, 0); 329} 330inspect.custom = customInspectSymbol; 331 332ObjectDefineProperty(inspect, 'defaultOptions', { 333 get() { 334 return inspectDefaultOptions; 335 }, 336 set(options) { 337 if (options === null || typeof options !== 'object') { 338 throw new ERR_INVALID_ARG_TYPE('options', 'Object', options); 339 } 340 return ObjectAssign(inspectDefaultOptions, options); 341 } 342}); 343 344// Set Graphics Rendition https://en.wikipedia.org/wiki/ANSI_escape_code#graphics 345// Each color consists of an array with the color code as first entry and the 346// reset code as second entry. 347const defaultFG = 39; 348const defaultBG = 49; 349inspect.colors = ObjectAssign(ObjectCreate(null), { 350 reset: [0, 0], 351 bold: [1, 22], 352 dim: [2, 22], // Alias: faint 353 italic: [3, 23], 354 underline: [4, 24], 355 blink: [5, 25], 356 // Swap forground and background colors 357 inverse: [7, 27], // Alias: swapcolors, swapColors 358 hidden: [8, 28], // Alias: conceal 359 strikethrough: [9, 29], // Alias: strikeThrough, crossedout, crossedOut 360 doubleunderline: [21, 24], // Alias: doubleUnderline 361 black: [30, defaultFG], 362 red: [31, defaultFG], 363 green: [32, defaultFG], 364 yellow: [33, defaultFG], 365 blue: [34, defaultFG], 366 magenta: [35, defaultFG], 367 cyan: [36, defaultFG], 368 white: [37, defaultFG], 369 bgBlack: [40, defaultBG], 370 bgRed: [41, defaultBG], 371 bgGreen: [42, defaultBG], 372 bgYellow: [43, defaultBG], 373 bgBlue: [44, defaultBG], 374 bgMagenta: [45, defaultBG], 375 bgCyan: [46, defaultBG], 376 bgWhite: [47, defaultBG], 377 framed: [51, 54], 378 overlined: [53, 55], 379 gray: [90, defaultFG], // Alias: grey, blackBright 380 redBright: [91, defaultFG], 381 greenBright: [92, defaultFG], 382 yellowBright: [93, defaultFG], 383 blueBright: [94, defaultFG], 384 magentaBright: [95, defaultFG], 385 cyanBright: [96, defaultFG], 386 whiteBright: [97, defaultFG], 387 bgGray: [100, defaultBG], // Alias: bgGrey, bgBlackBright 388 bgRedBright: [101, defaultBG], 389 bgGreenBright: [102, defaultBG], 390 bgYellowBright: [103, defaultBG], 391 bgBlueBright: [104, defaultBG], 392 bgMagentaBright: [105, defaultBG], 393 bgCyanBright: [106, defaultBG], 394 bgWhiteBright: [107, defaultBG], 395}); 396 397function defineColorAlias(target, alias) { 398 ObjectDefineProperty(inspect.colors, alias, { 399 get() { 400 return this[target]; 401 }, 402 set(value) { 403 this[target] = value; 404 }, 405 configurable: true, 406 enumerable: false 407 }); 408} 409 410defineColorAlias('gray', 'grey'); 411defineColorAlias('gray', 'blackBright'); 412defineColorAlias('bgGray', 'bgGrey'); 413defineColorAlias('bgGray', 'bgBlackBright'); 414defineColorAlias('dim', 'faint'); 415defineColorAlias('strikethrough', 'crossedout'); 416defineColorAlias('strikethrough', 'strikeThrough'); 417defineColorAlias('strikethrough', 'crossedOut'); 418defineColorAlias('hidden', 'conceal'); 419defineColorAlias('inverse', 'swapColors'); 420defineColorAlias('inverse', 'swapcolors'); 421defineColorAlias('doubleunderline', 'doubleUnderline'); 422 423// TODO(BridgeAR): Add function style support for more complex styles. 424// Don't use 'blue' not visible on cmd.exe 425inspect.styles = ObjectAssign(ObjectCreate(null), { 426 special: 'cyan', 427 number: 'yellow', 428 bigint: 'yellow', 429 boolean: 'yellow', 430 undefined: 'grey', 431 null: 'bold', 432 string: 'green', 433 symbol: 'green', 434 date: 'magenta', 435 // "name": intentionally not styling 436 // TODO(BridgeAR): Highlight regular expressions properly. 437 regexp: 'red', 438 module: 'underline' 439}); 440 441function addQuotes(str, quotes) { 442 if (quotes === -1) { 443 return `"${str}"`; 444 } 445 if (quotes === -2) { 446 return `\`${str}\``; 447 } 448 return `'${str}'`; 449} 450 451const escapeFn = (str) => meta[str.charCodeAt(0)]; 452 453// Escape control characters, single quotes and the backslash. 454// This is similar to JSON stringify escaping. 455function strEscape(str) { 456 let escapeTest = strEscapeSequencesRegExp; 457 let escapeReplace = strEscapeSequencesReplacer; 458 let singleQuote = 39; 459 460 // Check for double quotes. If not present, do not escape single quotes and 461 // instead wrap the text in double quotes. If double quotes exist, check for 462 // backticks. If they do not exist, use those as fallback instead of the 463 // double quotes. 464 if (str.includes("'")) { 465 // This invalidates the charCode and therefore can not be matched for 466 // anymore. 467 if (!str.includes('"')) { 468 singleQuote = -1; 469 } else if (!str.includes('`') && !str.includes('${')) { 470 singleQuote = -2; 471 } 472 if (singleQuote !== 39) { 473 escapeTest = strEscapeSequencesRegExpSingle; 474 escapeReplace = strEscapeSequencesReplacerSingle; 475 } 476 } 477 478 // Some magic numbers that worked out fine while benchmarking with v8 6.0 479 if (str.length < 5000 && !escapeTest.test(str)) 480 return addQuotes(str, singleQuote); 481 if (str.length > 100) { 482 str = str.replace(escapeReplace, escapeFn); 483 return addQuotes(str, singleQuote); 484 } 485 486 let result = ''; 487 let last = 0; 488 const lastIndex = str.length; 489 for (let i = 0; i < lastIndex; i++) { 490 const point = str.charCodeAt(i); 491 if (point === singleQuote || point === 92 || point < 32) { 492 if (last === i) { 493 result += meta[point]; 494 } else { 495 result += `${str.slice(last, i)}${meta[point]}`; 496 } 497 last = i + 1; 498 } 499 } 500 501 if (last !== lastIndex) { 502 result += str.slice(last); 503 } 504 return addQuotes(result, singleQuote); 505} 506 507function stylizeWithColor(str, styleType) { 508 const style = inspect.styles[styleType]; 509 if (style !== undefined) { 510 const color = inspect.colors[style]; 511 if (color !== undefined) 512 return `\u001b[${color[0]}m${str}\u001b[${color[1]}m`; 513 } 514 return str; 515} 516 517function stylizeNoColor(str) { 518 return str; 519} 520 521// Return a new empty array to push in the results of the default formatter. 522function getEmptyFormatArray() { 523 return []; 524} 525 526function getConstructorName(obj, ctx, recurseTimes, protoProps) { 527 let firstProto; 528 const tmp = obj; 529 while (obj || isUndetectableObject(obj)) { 530 const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor'); 531 if (descriptor !== undefined && 532 typeof descriptor.value === 'function' && 533 descriptor.value.name !== '') { 534 if (protoProps !== undefined && 535 (firstProto !== obj || 536 !builtInObjects.has(descriptor.value.name))) { 537 addPrototypeProperties( 538 ctx, tmp, firstProto || tmp, recurseTimes, protoProps); 539 } 540 return descriptor.value.name; 541 } 542 543 obj = ObjectGetPrototypeOf(obj); 544 if (firstProto === undefined) { 545 firstProto = obj; 546 } 547 } 548 549 if (firstProto === null) { 550 return null; 551 } 552 553 const res = internalGetConstructorName(tmp); 554 555 if (recurseTimes > ctx.depth && ctx.depth !== null) { 556 return `${res} <Complex prototype>`; 557 } 558 559 const protoConstr = getConstructorName( 560 firstProto, ctx, recurseTimes + 1, protoProps); 561 562 if (protoConstr === null) { 563 return `${res} <${inspect(firstProto, { 564 ...ctx, 565 customInspect: false, 566 depth: -1 567 })}>`; 568 } 569 570 return `${res} <${protoConstr}>`; 571} 572 573// This function has the side effect of adding prototype properties to the 574// `output` argument (which is an array). This is intended to highlight user 575// defined prototype properties. 576function addPrototypeProperties(ctx, main, obj, recurseTimes, output) { 577 let depth = 0; 578 let keys; 579 let keySet; 580 do { 581 if (depth !== 0 || main === obj) { 582 obj = ObjectGetPrototypeOf(obj); 583 // Stop as soon as a null prototype is encountered. 584 if (obj === null) { 585 return; 586 } 587 // Stop as soon as a built-in object type is detected. 588 const descriptor = ObjectGetOwnPropertyDescriptor(obj, 'constructor'); 589 if (descriptor !== undefined && 590 typeof descriptor.value === 'function' && 591 builtInObjects.has(descriptor.value.name)) { 592 return; 593 } 594 } 595 596 if (depth === 0) { 597 keySet = new Set(); 598 } else { 599 keys.forEach((key) => keySet.add(key)); 600 } 601 // Get all own property names and symbols. 602 keys = ObjectGetOwnPropertyNames(obj); 603 const symbols = ObjectGetOwnPropertySymbols(obj); 604 if (symbols.length !== 0) { 605 keys.push(...symbols); 606 } 607 for (const key of keys) { 608 // Ignore the `constructor` property and keys that exist on layers above. 609 if (key === 'constructor' || 610 ObjectPrototypeHasOwnProperty(main, key) || 611 (depth !== 0 && keySet.has(key))) { 612 continue; 613 } 614 const desc = ObjectGetOwnPropertyDescriptor(obj, key); 615 if (typeof desc.value === 'function') { 616 continue; 617 } 618 const value = formatProperty( 619 ctx, obj, recurseTimes, key, kObjectType, desc); 620 if (ctx.colors) { 621 // Faint! 622 output.push(`\u001b[2m${value}\u001b[22m`); 623 } else { 624 output.push(value); 625 } 626 } 627 // Limit the inspection to up to three prototype layers. Using `recurseTimes` 628 // is not a good choice here, because it's as if the properties are declared 629 // on the current object from the users perspective. 630 } while (++depth !== 3); 631} 632 633function getPrefix(constructor, tag, fallback, size = '') { 634 if (constructor === null) { 635 if (tag !== '') { 636 return `[${fallback}${size}: null prototype] [${tag}] `; 637 } 638 return `[${fallback}${size}: null prototype] `; 639 } 640 641 if (tag !== '' && constructor !== tag) { 642 return `${constructor}${size} [${tag}] `; 643 } 644 return `${constructor}${size} `; 645} 646 647// Look up the keys of the object. 648function getKeys(value, showHidden) { 649 let keys; 650 const symbols = ObjectGetOwnPropertySymbols(value); 651 if (showHidden) { 652 keys = ObjectGetOwnPropertyNames(value); 653 if (symbols.length !== 0) 654 keys.push(...symbols); 655 } else { 656 // This might throw if `value` is a Module Namespace Object from an 657 // unevaluated module, but we don't want to perform the actual type 658 // check because it's expensive. 659 // TODO(devsnek): track https://github.com/tc39/ecma262/issues/1209 660 // and modify this logic as needed. 661 try { 662 keys = ObjectKeys(value); 663 } catch (err) { 664 assert(isNativeError(err) && err.name === 'ReferenceError' && 665 isModuleNamespaceObject(value)); 666 keys = ObjectGetOwnPropertyNames(value); 667 } 668 if (symbols.length !== 0) { 669 const filter = (key) => ObjectPrototypePropertyIsEnumerable(value, key); 670 keys.push(...symbols.filter(filter)); 671 } 672 } 673 return keys; 674} 675 676function getCtxStyle(value, constructor, tag) { 677 let fallback = ''; 678 if (constructor === null) { 679 fallback = internalGetConstructorName(value); 680 if (fallback === tag) { 681 fallback = 'Object'; 682 } 683 } 684 return getPrefix(constructor, tag, fallback); 685} 686 687function formatProxy(ctx, proxy, recurseTimes) { 688 if (recurseTimes > ctx.depth && ctx.depth !== null) { 689 return ctx.stylize('Proxy [Array]', 'special'); 690 } 691 recurseTimes += 1; 692 ctx.indentationLvl += 2; 693 const res = [ 694 formatValue(ctx, proxy[0], recurseTimes), 695 formatValue(ctx, proxy[1], recurseTimes) 696 ]; 697 ctx.indentationLvl -= 2; 698 return reduceToSingleString( 699 ctx, res, '', ['Proxy [', ']'], kArrayExtrasType, recurseTimes); 700} 701 702function findTypedConstructor(value) { 703 for (const [check, clazz] of [ 704 [isUint8Array, Uint8Array], 705 [isUint8ClampedArray, Uint8ClampedArray], 706 [isUint16Array, Uint16Array], 707 [isUint32Array, Uint32Array], 708 [isInt8Array, Int8Array], 709 [isInt16Array, Int16Array], 710 [isInt32Array, Int32Array], 711 [isFloat32Array, Float32Array], 712 [isFloat64Array, Float64Array], 713 [isBigInt64Array, BigInt64Array], 714 [isBigUint64Array, BigUint64Array] 715 ]) { 716 if (check(value)) { 717 return clazz; 718 } 719 } 720} 721 722// Note: using `formatValue` directly requires the indentation level to be 723// corrected by setting `ctx.indentationLvL += diff` and then to decrease the 724// value afterwards again. 725function formatValue(ctx, value, recurseTimes, typedArray) { 726 // Primitive types cannot have properties. 727 if (typeof value !== 'object' && 728 typeof value !== 'function' && 729 !isUndetectableObject(value)) { 730 return formatPrimitive(ctx.stylize, value, ctx); 731 } 732 if (value === null) { 733 return ctx.stylize('null', 'null'); 734 } 735 736 // Memorize the context for custom inspection on proxies. 737 const context = value; 738 // Always check for proxies to prevent side effects and to prevent triggering 739 // any proxy handlers. 740 const proxy = getProxyDetails(value, !!ctx.showProxy); 741 if (proxy !== undefined) { 742 if (ctx.showProxy) { 743 return formatProxy(ctx, proxy, recurseTimes); 744 } 745 value = proxy; 746 } 747 748 // Provide a hook for user-specified inspect functions. 749 // Check that value is an object with an inspect function on it. 750 if (ctx.customInspect) { 751 const maybeCustom = value[customInspectSymbol]; 752 if (typeof maybeCustom === 'function' && 753 // Filter out the util module, its inspect function is special. 754 maybeCustom !== inspect && 755 // Also filter out any prototype objects using the circular check. 756 !(value.constructor && value.constructor.prototype === value)) { 757 // This makes sure the recurseTimes are reported as before while using 758 // a counter internally. 759 const depth = ctx.depth === null ? null : ctx.depth - recurseTimes; 760 const isCrossContext = 761 proxy !== undefined || !(context instanceof Object); 762 const ret = FunctionPrototypeCall( 763 maybeCustom, context, depth, getUserOptions(ctx, isCrossContext)); 764 // If the custom inspection method returned `this`, don't go into 765 // infinite recursion. 766 if (ret !== context) { 767 if (typeof ret !== 'string') { 768 return formatValue(ctx, ret, recurseTimes); 769 } 770 return ret.replace(/\n/g, `\n${' '.repeat(ctx.indentationLvl)}`); 771 } 772 } 773 } 774 775 // Using an array here is actually better for the average case than using 776 // a Set. `seen` will only check for the depth and will never grow too large. 777 if (ctx.seen.includes(value)) { 778 let index = 1; 779 if (ctx.circular === undefined) { 780 ctx.circular = new Map([[value, index]]); 781 } else { 782 index = ctx.circular.get(value); 783 if (index === undefined) { 784 index = ctx.circular.size + 1; 785 ctx.circular.set(value, index); 786 } 787 } 788 return ctx.stylize('[Circular]', 'special'); 789 } 790 791 return formatRaw(ctx, value, recurseTimes, typedArray); 792} 793 794function formatRaw(ctx, value, recurseTimes, typedArray) { 795 let keys; 796 let protoProps; 797 if (ctx.showHidden && (recurseTimes <= ctx.depth || ctx.depth === null)) { 798 protoProps = []; 799 } 800 801 const constructor = getConstructorName(value, ctx, recurseTimes, protoProps); 802 // Reset the variable to check for this later on. 803 if (protoProps !== undefined && protoProps.length === 0) { 804 protoProps = undefined; 805 } 806 807 let tag = value[SymbolToStringTag]; 808 // Only list the tag in case it's non-enumerable / not an own property. 809 // Otherwise we'd print this twice. 810 if (typeof tag !== 'string' || 811 (tag !== '' && 812 (ctx.showHidden ? 813 ObjectPrototypeHasOwnProperty : 814 ObjectPrototypePropertyIsEnumerable)( 815 value, SymbolToStringTag 816 ))) { 817 tag = ''; 818 } 819 let base = ''; 820 let formatter = getEmptyFormatArray; 821 let braces; 822 let noIterator = true; 823 let i = 0; 824 const filter = ctx.showHidden ? ALL_PROPERTIES : ONLY_ENUMERABLE; 825 826 let extrasType = kObjectType; 827 828 // Iterators and the rest are split to reduce checks. 829 // We have to check all values in case the constructor is set to null. 830 // Otherwise it would not possible to identify all types properly. 831 if (value[SymbolIterator] || constructor === null) { 832 noIterator = false; 833 if (ArrayIsArray(value)) { 834 // Only set the constructor for non ordinary ("Array [...]") arrays. 835 const prefix = (constructor !== 'Array' || tag !== '') ? 836 getPrefix(constructor, tag, 'Array', `(${value.length})`) : 837 ''; 838 keys = getOwnNonIndexProperties(value, filter); 839 braces = [`${prefix}[`, ']']; 840 if (value.length === 0 && keys.length === 0 && protoProps === undefined) 841 return `${braces[0]}]`; 842 extrasType = kArrayExtrasType; 843 formatter = formatArray; 844 } else if (isSet(value)) { 845 const size = setSizeGetter(value); 846 const prefix = getPrefix(constructor, tag, 'Set'); 847 keys = getKeys(value, ctx.showHidden); 848 formatter = constructor !== null ? 849 formatSet.bind(null, value, size) : 850 formatSet.bind(null, SetPrototypeValues(value), size); 851 if (size === 0 && keys.length === 0 && protoProps === undefined) 852 return `${prefix}{}`; 853 braces = [`${prefix}{`, '}']; 854 } else if (isMap(value)) { 855 const size = mapSizeGetter(value); 856 const prefix = getPrefix(constructor, tag, 'Map'); 857 keys = getKeys(value, ctx.showHidden); 858 formatter = constructor !== null ? 859 formatMap.bind(null, value, size) : 860 formatMap.bind(null, MapPrototypeEntries(value), size); 861 if (size === 0 && keys.length === 0 && protoProps === undefined) 862 return `${prefix}{}`; 863 braces = [`${prefix}{`, '}']; 864 } else if (isTypedArray(value)) { 865 keys = getOwnNonIndexProperties(value, filter); 866 let bound = value; 867 let fallback = ''; 868 if (constructor === null) { 869 const constr = findTypedConstructor(value); 870 fallback = constr.name; 871 // Reconstruct the array information. 872 bound = new constr(value); 873 } 874 const size = typedArraySizeGetter(value); 875 const prefix = getPrefix(constructor, tag, fallback, `(${size})`); 876 braces = [`${prefix}[`, ']']; 877 if (value.length === 0 && keys.length === 0 && !ctx.showHidden) 878 return `${braces[0]}]`; 879 // Special handle the value. The original value is required below. The 880 // bound function is required to reconstruct missing information. 881 formatter = formatTypedArray.bind(null, bound, size); 882 extrasType = kArrayExtrasType; 883 } else if (isMapIterator(value)) { 884 keys = getKeys(value, ctx.showHidden); 885 braces = getIteratorBraces('Map', tag); 886 // Add braces to the formatter parameters. 887 formatter = formatIterator.bind(null, braces); 888 } else if (isSetIterator(value)) { 889 keys = getKeys(value, ctx.showHidden); 890 braces = getIteratorBraces('Set', tag); 891 // Add braces to the formatter parameters. 892 formatter = formatIterator.bind(null, braces); 893 } else { 894 noIterator = true; 895 } 896 } 897 if (noIterator) { 898 keys = getKeys(value, ctx.showHidden); 899 braces = ['{', '}']; 900 if (constructor === 'Object') { 901 if (isArgumentsObject(value)) { 902 braces[0] = '[Arguments] {'; 903 } else if (tag !== '') { 904 braces[0] = `${getPrefix(constructor, tag, 'Object')}{`; 905 } 906 if (keys.length === 0 && protoProps === undefined) { 907 return `${braces[0]}}`; 908 } 909 } else if (typeof value === 'function') { 910 base = getFunctionBase(value, constructor, tag); 911 if (keys.length === 0 && protoProps === undefined) 912 return ctx.stylize(base, 'special'); 913 } else if (isRegExp(value)) { 914 // Make RegExps say that they are RegExps 915 base = RegExpPrototypeToString( 916 constructor !== null ? value : new RegExp(value) 917 ); 918 const prefix = getPrefix(constructor, tag, 'RegExp'); 919 if (prefix !== 'RegExp ') 920 base = `${prefix}${base}`; 921 if ((keys.length === 0 && protoProps === undefined) || 922 (recurseTimes > ctx.depth && ctx.depth !== null)) { 923 return ctx.stylize(base, 'regexp'); 924 } 925 } else if (isDate(value)) { 926 // Make dates with properties first say the date 927 base = NumberIsNaN(DatePrototypeGetTime(value)) ? 928 DatePrototypeToString(value) : 929 DatePrototypeToISOString(value); 930 const prefix = getPrefix(constructor, tag, 'Date'); 931 if (prefix !== 'Date ') 932 base = `${prefix}${base}`; 933 if (keys.length === 0 && protoProps === undefined) { 934 return ctx.stylize(base, 'date'); 935 } 936 } else if (isError(value)) { 937 base = formatError(value, constructor, tag, ctx, keys); 938 if (keys.length === 0 && protoProps === undefined) 939 return base; 940 } else if (isAnyArrayBuffer(value)) { 941 // Fast path for ArrayBuffer and SharedArrayBuffer. 942 // Can't do the same for DataView because it has a non-primitive 943 // .buffer property that we need to recurse for. 944 const arrayType = isArrayBuffer(value) ? 'ArrayBuffer' : 945 'SharedArrayBuffer'; 946 const prefix = getPrefix(constructor, tag, arrayType); 947 if (typedArray === undefined) { 948 formatter = formatArrayBuffer; 949 } else if (keys.length === 0 && protoProps === undefined) { 950 return prefix + 951 `{ byteLength: ${formatNumber(ctx.stylize, value.byteLength)} }`; 952 } 953 braces[0] = `${prefix}{`; 954 keys.unshift('byteLength'); 955 } else if (isDataView(value)) { 956 braces[0] = `${getPrefix(constructor, tag, 'DataView')}{`; 957 // .buffer goes last, it's not a primitive like the others. 958 keys.unshift('byteLength', 'byteOffset', 'buffer'); 959 } else if (isPromise(value)) { 960 braces[0] = `${getPrefix(constructor, tag, 'Promise')}{`; 961 formatter = formatPromise; 962 } else if (isWeakSet(value)) { 963 braces[0] = `${getPrefix(constructor, tag, 'WeakSet')}{`; 964 formatter = ctx.showHidden ? formatWeakSet : formatWeakCollection; 965 } else if (isWeakMap(value)) { 966 braces[0] = `${getPrefix(constructor, tag, 'WeakMap')}{`; 967 formatter = ctx.showHidden ? formatWeakMap : formatWeakCollection; 968 } else if (isModuleNamespaceObject(value)) { 969 braces[0] = `[${tag}] {`; 970 // Special handle keys for namespace objects. 971 formatter = formatNamespaceObject.bind(null, keys); 972 } else if (isBoxedPrimitive(value)) { 973 base = getBoxedBase(value, ctx, keys, constructor, tag); 974 if (keys.length === 0 && protoProps === undefined) { 975 return base; 976 } 977 } else { 978 if (keys.length === 0 && protoProps === undefined) { 979 if (isExternal(value)) { 980 const address = getExternalValue(value).toString(16); 981 return ctx.stylize(`[External: ${address}]`, 'special'); 982 } 983 return `${getCtxStyle(value, constructor, tag)}{}`; 984 } 985 braces[0] = `${getCtxStyle(value, constructor, tag)}{`; 986 } 987 } 988 989 if (recurseTimes > ctx.depth && ctx.depth !== null) { 990 let constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); 991 if (constructor !== null) 992 constructorName = `[${constructorName}]`; 993 return ctx.stylize(constructorName, 'special'); 994 } 995 recurseTimes += 1; 996 997 ctx.seen.push(value); 998 ctx.currentDepth = recurseTimes; 999 let output; 1000 const indentationLvl = ctx.indentationLvl; 1001 try { 1002 output = formatter(ctx, value, recurseTimes); 1003 for (i = 0; i < keys.length; i++) { 1004 output.push( 1005 formatProperty(ctx, value, recurseTimes, keys[i], extrasType)); 1006 } 1007 if (protoProps !== undefined) { 1008 output.push(...protoProps); 1009 } 1010 } catch (err) { 1011 const constructorName = getCtxStyle(value, constructor, tag).slice(0, -1); 1012 return handleMaxCallStackSize(ctx, err, constructorName, indentationLvl); 1013 } 1014 if (ctx.circular !== undefined) { 1015 const index = ctx.circular.get(value); 1016 if (index !== undefined) { 1017 // Add reference always to the very beginning of the output. 1018 if (ctx.compact !== true) { 1019 base = base === '' ? '' : `${base}`; 1020 } else { 1021 braces[0] = `${braces[0]}`; 1022 } 1023 } 1024 } 1025 ctx.seen.pop(); 1026 1027 if (ctx.sorted) { 1028 const comparator = ctx.sorted === true ? undefined : ctx.sorted; 1029 if (extrasType === kObjectType) { 1030 output = output.sort(comparator); 1031 } else if (keys.length > 1) { 1032 const sorted = output.slice(output.length - keys.length).sort(comparator); 1033 output.splice(output.length - keys.length, keys.length, ...sorted); 1034 } 1035 } 1036 1037 const res = reduceToSingleString( 1038 ctx, output, base, braces, extrasType, recurseTimes, value); 1039 const budget = ctx.budget[ctx.indentationLvl] || 0; 1040 const newLength = budget + res.length; 1041 ctx.budget[ctx.indentationLvl] = newLength; 1042 // If any indentationLvl exceeds this limit, limit further inspecting to the 1043 // minimum. Otherwise the recursive algorithm might continue inspecting the 1044 // object even though the maximum string size (~2 ** 28 on 32 bit systems and 1045 // ~2 ** 30 on 64 bit systems) exceeded. The actual output is not limited at 1046 // exactly 2 ** 27 but a bit higher. This depends on the object shape. 1047 // This limit also makes sure that huge objects don't block the event loop 1048 // significantly. 1049 if (newLength > 2 ** 27) { 1050 ctx.depth = -1; 1051 } 1052 return res; 1053} 1054 1055function getIteratorBraces(type, tag) { 1056 if (tag !== `${type} Iterator`) { 1057 if (tag !== '') 1058 tag += '] ['; 1059 tag += `${type} Iterator`; 1060 } 1061 return [`[${tag}] {`, '}']; 1062} 1063 1064function getBoxedBase(value, ctx, keys, constructor, tag) { 1065 let fn; 1066 let type; 1067 if (isNumberObject(value)) { 1068 fn = NumberPrototypeValueOf; 1069 type = 'Number'; 1070 } else if (isStringObject(value)) { 1071 fn = StringPrototypeValueOf; 1072 type = 'String'; 1073 // For boxed Strings, we have to remove the 0-n indexed entries, 1074 // since they just noisy up the output and are redundant 1075 // Make boxed primitive Strings look like such 1076 keys.splice(0, value.length); 1077 } else if (isBooleanObject(value)) { 1078 fn = BooleanPrototypeValueOf; 1079 type = 'Boolean'; 1080 } else if (isBigIntObject(value)) { 1081 fn = BigIntPrototypeValueOf; 1082 type = 'BigInt'; 1083 } else { 1084 fn = SymbolPrototypeValueOf; 1085 type = 'Symbol'; 1086 } 1087 let base = `[${type}`; 1088 if (type !== constructor) { 1089 if (constructor === null) { 1090 base += ' (null prototype)'; 1091 } else { 1092 base += ` (${constructor})`; 1093 } 1094 } 1095 base += `: ${formatPrimitive(stylizeNoColor, fn(value), ctx)}]`; 1096 if (tag !== '' && tag !== constructor) { 1097 base += ` [${tag}]`; 1098 } 1099 if (keys.length !== 0 || ctx.stylize === stylizeNoColor) 1100 return base; 1101 return ctx.stylize(base, type.toLowerCase()); 1102} 1103 1104function getClassBase(value, constructor, tag) { 1105 const hasName = ObjectPrototypeHasOwnProperty(value, 'name'); 1106 const name = (hasName && value.name) || '(anonymous)'; 1107 let base = `class ${name}`; 1108 if (constructor !== 'Function' && constructor !== null) { 1109 base += ` [${constructor}]`; 1110 } 1111 if (tag !== '' && constructor !== tag) { 1112 base += ` [${tag}]`; 1113 } 1114 if (constructor !== null) { 1115 const superName = ObjectGetPrototypeOf(value).name; 1116 if (superName) { 1117 base += ` extends ${superName}`; 1118 } 1119 } else { 1120 base += ' extends [null prototype]'; 1121 } 1122 return `[${base}]`; 1123} 1124 1125function getFunctionBase(value, constructor, tag) { 1126 const stringified = FunctionPrototypeToString(value); 1127 if (stringified.slice(0, 5) === 'class' && stringified.endsWith('}')) { 1128 const slice = stringified.slice(5, -1); 1129 const bracketIndex = slice.indexOf('{'); 1130 if (bracketIndex !== -1 && 1131 (!slice.slice(0, bracketIndex).includes('(') || 1132 // Slow path to guarantee that it's indeed a class. 1133 classRegExp.test(slice.replace(stripCommentsRegExp)))) { 1134 return getClassBase(value, constructor, tag); 1135 } 1136 } 1137 let type = 'Function'; 1138 if (isGeneratorFunction(value)) { 1139 type = `Generator${type}`; 1140 } 1141 if (isAsyncFunction(value)) { 1142 type = `Async${type}`; 1143 } 1144 let base = `[${type}`; 1145 if (constructor === null) { 1146 base += ' (null prototype)'; 1147 } 1148 if (value.name !== '') { 1149 base += `: ${value.name}`; 1150 } 1151 base += ']'; 1152 if (constructor !== type && constructor !== null) { 1153 base += ` ${constructor}`; 1154 } 1155 if (tag !== '' && constructor !== tag) { 1156 base += ` [${tag}]`; 1157 } 1158 return base; 1159} 1160 1161function formatError(err, constructor, tag, ctx, keys) { 1162 const name = err.name != null ? String(err.name) : 'Error'; 1163 let len = name.length; 1164 let stack = err.stack ? String(err.stack) : ErrorPrototypeToString(err); 1165 1166 // Do not "duplicate" error properties that are already included in the output 1167 // otherwise. 1168 if (!ctx.showHidden && keys.length !== 0) { 1169 for (const name of ['name', 'message', 'stack']) { 1170 const index = keys.indexOf(name); 1171 // Only hide the property in case it's part of the original stack 1172 if (index !== -1 && stack.includes(err[name])) { 1173 keys.splice(index, 1); 1174 } 1175 } 1176 } 1177 1178 // A stack trace may contain arbitrary data. Only manipulate the output 1179 // for "regular errors" (errors that "look normal") for now. 1180 if (constructor === null || 1181 (name.endsWith('Error') && 1182 stack.startsWith(name) && 1183 (stack.length === len || stack[len] === ':' || stack[len] === '\n'))) { 1184 let fallback = 'Error'; 1185 if (constructor === null) { 1186 const start = stack.match(/^([A-Z][a-z_ A-Z0-9[\]()-]+)(?::|\n {4}at)/) || 1187 stack.match(/^([a-z_A-Z0-9-]*Error)$/); 1188 fallback = (start && start[1]) || ''; 1189 len = fallback.length; 1190 fallback = fallback || 'Error'; 1191 } 1192 const prefix = getPrefix(constructor, tag, fallback).slice(0, -1); 1193 if (name !== prefix) { 1194 if (prefix.includes(name)) { 1195 if (len === 0) { 1196 stack = `${prefix}: ${stack}`; 1197 } else { 1198 stack = `${prefix}${stack.slice(len)}`; 1199 } 1200 } else { 1201 stack = `${prefix} [${name}]${stack.slice(len)}`; 1202 } 1203 } 1204 } 1205 // Ignore the error message if it's contained in the stack. 1206 let pos = (err.message && stack.indexOf(err.message)) || -1; 1207 if (pos !== -1) 1208 pos += err.message.length; 1209 // Wrap the error in brackets in case it has no stack trace. 1210 const stackStart = stack.indexOf('\n at', pos); 1211 if (stackStart === -1) { 1212 stack = `[${stack}]`; 1213 } else if (ctx.colors) { 1214 // Highlight userland code and node modules. 1215 let newStack = stack.slice(0, stackStart); 1216 const lines = stack.slice(stackStart + 1).split('\n'); 1217 for (const line of lines) { 1218 const core = line.match(coreModuleRegExp); 1219 if (core !== null && NativeModule.exists(core[1])) { 1220 newStack += `\n${ctx.stylize(line, 'undefined')}`; 1221 } else { 1222 // This adds underscores to all node_modules to quickly identify them. 1223 let nodeModule; 1224 newStack += '\n'; 1225 let pos = 0; 1226 while (nodeModule = nodeModulesRegExp.exec(line)) { 1227 // '/node_modules/'.length === 14 1228 newStack += line.slice(pos, nodeModule.index + 14); 1229 newStack += ctx.stylize(nodeModule[1], 'module'); 1230 pos = nodeModule.index + nodeModule[0].length; 1231 } 1232 newStack += pos === 0 ? line : line.slice(pos); 1233 } 1234 } 1235 stack = newStack; 1236 } 1237 // The message and the stack have to be indented as well! 1238 if (ctx.indentationLvl !== 0) { 1239 const indentation = ' '.repeat(ctx.indentationLvl); 1240 stack = stack.replace(/\n/g, `\n${indentation}`); 1241 } 1242 return stack; 1243} 1244 1245function groupArrayElements(ctx, output, value) { 1246 let totalLength = 0; 1247 let maxLength = 0; 1248 let i = 0; 1249 let outputLength = output.length; 1250 if (ctx.maxArrayLength < output.length) { 1251 // This makes sure the "... n more items" part is not taken into account. 1252 outputLength--; 1253 } 1254 const separatorSpace = 2; // Add 1 for the space and 1 for the separator. 1255 const dataLen = new Array(outputLength); 1256 // Calculate the total length of all output entries and the individual max 1257 // entries length of all output entries. We have to remove colors first, 1258 // otherwise the length would not be calculated properly. 1259 for (; i < outputLength; i++) { 1260 const len = getStringWidth(output[i], ctx.colors); 1261 dataLen[i] = len; 1262 totalLength += len + separatorSpace; 1263 if (maxLength < len) 1264 maxLength = len; 1265 } 1266 // Add two to `maxLength` as we add a single whitespace character plus a comma 1267 // in-between two entries. 1268 const actualMax = maxLength + separatorSpace; 1269 // Check if at least three entries fit next to each other and prevent grouping 1270 // of arrays that contains entries of very different length (i.e., if a single 1271 // entry is longer than 1/5 of all other entries combined). Otherwise the 1272 // space in-between small entries would be enormous. 1273 if (actualMax * 3 + ctx.indentationLvl < ctx.breakLength && 1274 (totalLength / actualMax > 5 || maxLength <= 6)) { 1275 1276 const approxCharHeights = 2.5; 1277 const averageBias = MathSqrt(actualMax - totalLength / output.length); 1278 const biasedMax = MathMax(actualMax - 3 - averageBias, 1); 1279 // Dynamically check how many columns seem possible. 1280 const columns = MathMin( 1281 // Ideally a square should be drawn. We expect a character to be about 2.5 1282 // times as high as wide. This is the area formula to calculate a square 1283 // which contains n rectangles of size `actualMax * approxCharHeights`. 1284 // Divide that by `actualMax` to receive the correct number of columns. 1285 // The added bias increases the columns for short entries. 1286 MathRound( 1287 MathSqrt( 1288 approxCharHeights * biasedMax * outputLength 1289 ) / biasedMax 1290 ), 1291 // Do not exceed the breakLength. 1292 MathFloor((ctx.breakLength - ctx.indentationLvl) / actualMax), 1293 // Limit array grouping for small `compact` modes as the user requested 1294 // minimal grouping. 1295 ctx.compact * 4, 1296 // Limit the columns to a maximum of fifteen. 1297 15 1298 ); 1299 // Return with the original output if no grouping should happen. 1300 if (columns <= 1) { 1301 return output; 1302 } 1303 const tmp = []; 1304 const maxLineLength = []; 1305 for (let i = 0; i < columns; i++) { 1306 let lineMaxLength = 0; 1307 for (let j = i; j < output.length; j += columns) { 1308 if (dataLen[j] > lineMaxLength) 1309 lineMaxLength = dataLen[j]; 1310 } 1311 lineMaxLength += separatorSpace; 1312 maxLineLength[i] = lineMaxLength; 1313 } 1314 let order = 'padStart'; 1315 if (value !== undefined) { 1316 for (let i = 0; i < output.length; i++) { 1317 if (typeof value[i] !== 'number' && typeof value[i] !== 'bigint') { 1318 order = 'padEnd'; 1319 break; 1320 } 1321 } 1322 } 1323 // Each iteration creates a single line of grouped entries. 1324 for (let i = 0; i < outputLength; i += columns) { 1325 // The last lines may contain less entries than columns. 1326 const max = MathMin(i + columns, outputLength); 1327 let str = ''; 1328 let j = i; 1329 for (; j < max - 1; j++) { 1330 // Calculate extra color padding in case it's active. This has to be 1331 // done line by line as some lines might contain more colors than 1332 // others. 1333 const padding = maxLineLength[j - i] + output[j].length - dataLen[j]; 1334 str += `${output[j]}, `[order](padding, ' '); 1335 } 1336 if (order === 'padStart') { 1337 const padding = maxLineLength[j - i] + 1338 output[j].length - 1339 dataLen[j] - 1340 separatorSpace; 1341 str += output[j].padStart(padding, ' '); 1342 } else { 1343 str += output[j]; 1344 } 1345 tmp.push(str); 1346 } 1347 if (ctx.maxArrayLength < output.length) { 1348 tmp.push(output[outputLength]); 1349 } 1350 output = tmp; 1351 } 1352 return output; 1353} 1354 1355function handleMaxCallStackSize(ctx, err, constructorName, indentationLvl) { 1356 if (isStackOverflowError(err)) { 1357 ctx.seen.pop(); 1358 ctx.indentationLvl = indentationLvl; 1359 return ctx.stylize( 1360 `[${constructorName}: Inspection interrupted ` + 1361 'prematurely. Maximum call stack size exceeded.]', 1362 'special' 1363 ); 1364 } 1365 throw err; 1366} 1367 1368function formatNumber(fn, value) { 1369 // Format -0 as '-0'. Checking `value === -0` won't distinguish 0 from -0. 1370 return fn(ObjectIs(value, -0) ? '-0' : `${value}`, 'number'); 1371} 1372 1373function formatBigInt(fn, value) { 1374 return fn(`${value}n`, 'bigint'); 1375} 1376 1377function formatPrimitive(fn, value, ctx) { 1378 if (typeof value === 'string') { 1379 let trailer = ''; 1380 if (value.length > ctx.maxStringLength) { 1381 const remaining = value.length - ctx.maxStringLength; 1382 value = value.slice(0, ctx.maxStringLength); 1383 trailer = `... ${remaining} more character${remaining > 1 ? 's' : ''}`; 1384 } 1385 if (ctx.compact !== true && 1386 // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth 1387 // function. 1388 value.length > kMinLineLength && 1389 value.length > ctx.breakLength - ctx.indentationLvl - 4) { 1390 return value 1391 .split(/(?<=\n)/) 1392 .map((line) => fn(strEscape(line), 'string')) 1393 .join(` +\n${' '.repeat(ctx.indentationLvl + 2)}`) + trailer; 1394 } 1395 return fn(strEscape(value), 'string') + trailer; 1396 } 1397 if (typeof value === 'number') 1398 return formatNumber(fn, value); 1399 if (typeof value === 'bigint') 1400 return formatBigInt(fn, value); 1401 if (typeof value === 'boolean') 1402 return fn(`${value}`, 'boolean'); 1403 if (typeof value === 'undefined') 1404 return fn('undefined', 'undefined'); 1405 // es6 symbol primitive 1406 return fn(SymbolPrototypeToString(value), 'symbol'); 1407} 1408 1409function formatNamespaceObject(keys, ctx, value, recurseTimes) { 1410 const output = new Array(keys.length); 1411 for (let i = 0; i < keys.length; i++) { 1412 try { 1413 output[i] = formatProperty(ctx, value, recurseTimes, keys[i], 1414 kObjectType); 1415 } catch (err) { 1416 if (!(isNativeError(err) && err.name === 'ReferenceError')) { 1417 throw err; 1418 } 1419 // Use the existing functionality. This makes sure the indentation and 1420 // line breaks are always correct. Otherwise it is very difficult to keep 1421 // this aligned, even though this is a hacky way of dealing with this. 1422 const tmp = { [keys[i]]: '' }; 1423 output[i] = formatProperty(ctx, tmp, recurseTimes, keys[i], kObjectType); 1424 const pos = output[i].lastIndexOf(' '); 1425 // We have to find the last whitespace and have to replace that value as 1426 // it will be visualized as a regular string. 1427 output[i] = output[i].slice(0, pos + 1) + 1428 ctx.stylize('<uninitialized>', 'special'); 1429 } 1430 } 1431 // Reset the keys to an empty array. This prevents duplicated inspection. 1432 keys.length = 0; 1433 return output; 1434} 1435 1436// The array is sparse and/or has extra keys 1437function formatSpecialArray(ctx, value, recurseTimes, maxLength, output, i) { 1438 const keys = ObjectKeys(value); 1439 let index = i; 1440 for (; i < keys.length && output.length < maxLength; i++) { 1441 const key = keys[i]; 1442 const tmp = +key; 1443 // Arrays can only have up to 2^32 - 1 entries 1444 if (tmp > 2 ** 32 - 2) { 1445 break; 1446 } 1447 if (`${index}` !== key) { 1448 if (!numberRegExp.test(key)) { 1449 break; 1450 } 1451 const emptyItems = tmp - index; 1452 const ending = emptyItems > 1 ? 's' : ''; 1453 const message = `<${emptyItems} empty item${ending}>`; 1454 output.push(ctx.stylize(message, 'undefined')); 1455 index = tmp; 1456 if (output.length === maxLength) { 1457 break; 1458 } 1459 } 1460 output.push(formatProperty(ctx, value, recurseTimes, key, kArrayType)); 1461 index++; 1462 } 1463 const remaining = value.length - index; 1464 if (output.length !== maxLength) { 1465 if (remaining > 0) { 1466 const ending = remaining > 1 ? 's' : ''; 1467 const message = `<${remaining} empty item${ending}>`; 1468 output.push(ctx.stylize(message, 'undefined')); 1469 } 1470 } else if (remaining > 0) { 1471 output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); 1472 } 1473 return output; 1474} 1475 1476function formatArrayBuffer(ctx, value) { 1477 let buffer; 1478 try { 1479 buffer = new Uint8Array(value); 1480 } catch { 1481 return [ctx.stylize('(detached)', 'special')]; 1482 } 1483 if (hexSlice === undefined) 1484 hexSlice = uncurryThis(require('buffer').Buffer.prototype.hexSlice); 1485 let str = hexSlice(buffer, 0, MathMin(ctx.maxArrayLength, buffer.length)) 1486 .replace(/(.{2})/g, '$1 ').trim(); 1487 const remaining = buffer.length - ctx.maxArrayLength; 1488 if (remaining > 0) 1489 str += ` ... ${remaining} more byte${remaining > 1 ? 's' : ''}`; 1490 return [`${ctx.stylize('[Uint8Contents]', 'special')}: <${str}>`]; 1491} 1492 1493function formatArray(ctx, value, recurseTimes) { 1494 const valLen = value.length; 1495 const len = MathMin(MathMax(0, ctx.maxArrayLength), valLen); 1496 1497 const remaining = valLen - len; 1498 const output = []; 1499 for (let i = 0; i < len; i++) { 1500 // Special handle sparse arrays. 1501 if (!ObjectPrototypeHasOwnProperty(value, i)) { 1502 return formatSpecialArray(ctx, value, recurseTimes, len, output, i); 1503 } 1504 output.push(formatProperty(ctx, value, recurseTimes, i, kArrayType)); 1505 } 1506 if (remaining > 0) 1507 output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); 1508 return output; 1509} 1510 1511function formatTypedArray(value, length, ctx, ignored, recurseTimes) { 1512 const maxLength = MathMin(MathMax(0, ctx.maxArrayLength), length); 1513 const remaining = value.length - maxLength; 1514 const output = new Array(maxLength); 1515 const elementFormatter = value.length > 0 && typeof value[0] === 'number' ? 1516 formatNumber : 1517 formatBigInt; 1518 for (let i = 0; i < maxLength; ++i) 1519 output[i] = elementFormatter(ctx.stylize, value[i]); 1520 if (remaining > 0) { 1521 output[maxLength] = `... ${remaining} more item${remaining > 1 ? 's' : ''}`; 1522 } 1523 if (ctx.showHidden) { 1524 // .buffer goes last, it's not a primitive like the others. 1525 // All besides `BYTES_PER_ELEMENT` are actually getters. 1526 ctx.indentationLvl += 2; 1527 for (const key of [ 1528 'BYTES_PER_ELEMENT', 1529 'length', 1530 'byteLength', 1531 'byteOffset', 1532 'buffer' 1533 ]) { 1534 const str = formatValue(ctx, value[key], recurseTimes, true); 1535 output.push(`[${key}]: ${str}`); 1536 } 1537 ctx.indentationLvl -= 2; 1538 } 1539 return output; 1540} 1541 1542function formatSet(value, size, ctx, ignored, recurseTimes) { 1543 const output = []; 1544 ctx.indentationLvl += 2; 1545 for (const v of value) { 1546 output.push(formatValue(ctx, v, recurseTimes)); 1547 } 1548 ctx.indentationLvl -= 2; 1549 // With `showHidden`, `length` will display as a hidden property for 1550 // arrays. For consistency's sake, do the same for `size`, even though this 1551 // property isn't selected by ObjectGetOwnPropertyNames(). 1552 if (ctx.showHidden) 1553 output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); 1554 return output; 1555} 1556 1557function formatMap(value, size, ctx, ignored, recurseTimes) { 1558 const output = []; 1559 ctx.indentationLvl += 2; 1560 for (const [k, v] of value) { 1561 output.push(`${formatValue(ctx, k, recurseTimes)} => ` + 1562 formatValue(ctx, v, recurseTimes)); 1563 } 1564 ctx.indentationLvl -= 2; 1565 // See comment in formatSet 1566 if (ctx.showHidden) 1567 output.push(`[size]: ${ctx.stylize(`${size}`, 'number')}`); 1568 return output; 1569} 1570 1571function formatSetIterInner(ctx, recurseTimes, entries, state) { 1572 const maxArrayLength = MathMax(ctx.maxArrayLength, 0); 1573 const maxLength = MathMin(maxArrayLength, entries.length); 1574 let output = new Array(maxLength); 1575 ctx.indentationLvl += 2; 1576 for (let i = 0; i < maxLength; i++) { 1577 output[i] = formatValue(ctx, entries[i], recurseTimes); 1578 } 1579 ctx.indentationLvl -= 2; 1580 if (state === kWeak && !ctx.sorted) { 1581 // Sort all entries to have a halfway reliable output (if more entries than 1582 // retrieved ones exist, we can not reliably return the same output) if the 1583 // output is not sorted anyway. 1584 output = output.sort(); 1585 } 1586 const remaining = entries.length - maxLength; 1587 if (remaining > 0) { 1588 output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); 1589 } 1590 return output; 1591} 1592 1593function formatMapIterInner(ctx, recurseTimes, entries, state) { 1594 const maxArrayLength = MathMax(ctx.maxArrayLength, 0); 1595 // Entries exist as [key1, val1, key2, val2, ...] 1596 const len = entries.length / 2; 1597 const remaining = len - maxArrayLength; 1598 const maxLength = MathMin(maxArrayLength, len); 1599 let output = new Array(maxLength); 1600 let i = 0; 1601 ctx.indentationLvl += 2; 1602 if (state === kWeak) { 1603 for (; i < maxLength; i++) { 1604 const pos = i * 2; 1605 output[i] = `${formatValue(ctx, entries[pos], recurseTimes)}` + 1606 ` => ${formatValue(ctx, entries[pos + 1], recurseTimes)}`; 1607 } 1608 // Sort all entries to have a halfway reliable output (if more entries than 1609 // retrieved ones exist, we can not reliably return the same output) if the 1610 // output is not sorted anyway. 1611 if (!ctx.sorted) 1612 output = output.sort(); 1613 } else { 1614 for (; i < maxLength; i++) { 1615 const pos = i * 2; 1616 const res = [ 1617 formatValue(ctx, entries[pos], recurseTimes), 1618 formatValue(ctx, entries[pos + 1], recurseTimes) 1619 ]; 1620 output[i] = reduceToSingleString( 1621 ctx, res, '', ['[', ']'], kArrayExtrasType, recurseTimes); 1622 } 1623 } 1624 ctx.indentationLvl -= 2; 1625 if (remaining > 0) { 1626 output.push(`... ${remaining} more item${remaining > 1 ? 's' : ''}`); 1627 } 1628 return output; 1629} 1630 1631function formatWeakCollection(ctx) { 1632 return [ctx.stylize('<items unknown>', 'special')]; 1633} 1634 1635function formatWeakSet(ctx, value, recurseTimes) { 1636 const entries = previewEntries(value); 1637 return formatSetIterInner(ctx, recurseTimes, entries, kWeak); 1638} 1639 1640function formatWeakMap(ctx, value, recurseTimes) { 1641 const entries = previewEntries(value); 1642 return formatMapIterInner(ctx, recurseTimes, entries, kWeak); 1643} 1644 1645function formatIterator(braces, ctx, value, recurseTimes) { 1646 const [entries, isKeyValue] = previewEntries(value, true); 1647 if (isKeyValue) { 1648 // Mark entry iterators as such. 1649 braces[0] = braces[0].replace(/ Iterator] {$/, ' Entries] {'); 1650 return formatMapIterInner(ctx, recurseTimes, entries, kMapEntries); 1651 } 1652 1653 return formatSetIterInner(ctx, recurseTimes, entries, kIterator); 1654} 1655 1656function formatPromise(ctx, value, recurseTimes) { 1657 let output; 1658 const [state, result] = getPromiseDetails(value); 1659 if (state === kPending) { 1660 output = [ctx.stylize('<pending>', 'special')]; 1661 } else { 1662 ctx.indentationLvl += 2; 1663 const str = formatValue(ctx, result, recurseTimes); 1664 ctx.indentationLvl -= 2; 1665 output = [ 1666 state === kRejected ? 1667 `${ctx.stylize('<rejected>', 'special')} ${str}` : 1668 str 1669 ]; 1670 } 1671 return output; 1672} 1673 1674function formatProperty(ctx, value, recurseTimes, key, type, desc) { 1675 let name, str; 1676 let extra = ' '; 1677 desc = desc || ObjectGetOwnPropertyDescriptor(value, key) || 1678 { value: value[key], enumerable: true }; 1679 if (desc.value !== undefined) { 1680 const diff = (ctx.compact !== true || type !== kObjectType) ? 2 : 3; 1681 ctx.indentationLvl += diff; 1682 str = formatValue(ctx, desc.value, recurseTimes); 1683 if (diff === 3 && ctx.breakLength < getStringWidth(str, ctx.colors)) { 1684 extra = `\n${' '.repeat(ctx.indentationLvl)}`; 1685 } 1686 ctx.indentationLvl -= diff; 1687 } else if (desc.get !== undefined) { 1688 const label = desc.set !== undefined ? 'Getter/Setter' : 'Getter'; 1689 const s = ctx.stylize; 1690 const sp = 'special'; 1691 if (ctx.getters && (ctx.getters === true || 1692 (ctx.getters === 'get' && desc.set === undefined) || 1693 (ctx.getters === 'set' && desc.set !== undefined))) { 1694 try { 1695 const tmp = value[key]; 1696 ctx.indentationLvl += 2; 1697 if (tmp === null) { 1698 str = `${s(`[${label}:`, sp)} ${s('null', 'null')}${s(']', sp)}`; 1699 } else if (typeof tmp === 'object') { 1700 str = `${s(`[${label}]`, sp)} ${formatValue(ctx, tmp, recurseTimes)}`; 1701 } else { 1702 const primitive = formatPrimitive(s, tmp, ctx); 1703 str = `${s(`[${label}:`, sp)} ${primitive}${s(']', sp)}`; 1704 } 1705 ctx.indentationLvl -= 2; 1706 } catch (err) { 1707 const message = `<Inspection threw (${err.message})>`; 1708 str = `${s(`[${label}:`, sp)} ${message}${s(']', sp)}`; 1709 } 1710 } else { 1711 str = ctx.stylize(`[${label}]`, sp); 1712 } 1713 } else if (desc.set !== undefined) { 1714 str = ctx.stylize('[Setter]', 'special'); 1715 } else { 1716 str = ctx.stylize('undefined', 'undefined'); 1717 } 1718 if (type === kArrayType) { 1719 return str; 1720 } 1721 if (typeof key === 'symbol') { 1722 const tmp = key.toString().replace(strEscapeSequencesReplacer, escapeFn); 1723 name = `[${ctx.stylize(tmp, 'symbol')}]`; 1724 } else if (desc.enumerable === false) { 1725 name = `[${key.replace(strEscapeSequencesReplacer, escapeFn)}]`; 1726 } else if (keyStrRegExp.test(key)) { 1727 name = ctx.stylize(key, 'name'); 1728 } else { 1729 name = ctx.stylize(strEscape(key), 'string'); 1730 } 1731 return `${name}:${extra}${str}`; 1732} 1733 1734function isBelowBreakLength(ctx, output, start, base) { 1735 // Each entry is separated by at least a comma. Thus, we start with a total 1736 // length of at least `output.length`. In addition, some cases have a 1737 // whitespace in-between each other that is added to the total as well. 1738 // TODO(BridgeAR): Add unicode support. Use the readline getStringWidth 1739 // function. Check the performance overhead and make it an opt-in in case it's 1740 // significant. 1741 let totalLength = output.length + start; 1742 if (totalLength + output.length > ctx.breakLength) 1743 return false; 1744 for (let i = 0; i < output.length; i++) { 1745 if (ctx.colors) { 1746 totalLength += removeColors(output[i]).length; 1747 } else { 1748 totalLength += output[i].length; 1749 } 1750 if (totalLength > ctx.breakLength) { 1751 return false; 1752 } 1753 } 1754 // Do not line up properties on the same line if `base` contains line breaks. 1755 return base === '' || !base.includes('\n'); 1756} 1757 1758function reduceToSingleString( 1759 ctx, output, base, braces, extrasType, recurseTimes, value) { 1760 if (ctx.compact !== true) { 1761 if (typeof ctx.compact === 'number' && ctx.compact >= 1) { 1762 // Memorize the original output length. In case the the output is grouped, 1763 // prevent lining up the entries on a single line. 1764 const entries = output.length; 1765 // Group array elements together if the array contains at least six 1766 // separate entries. 1767 if (extrasType === kArrayExtrasType && entries > 6) { 1768 output = groupArrayElements(ctx, output, value); 1769 } 1770 // `ctx.currentDepth` is set to the most inner depth of the currently 1771 // inspected object part while `recurseTimes` is the actual current depth 1772 // that is inspected. 1773 // 1774 // Example: 1775 // 1776 // const a = { first: [ 1, 2, 3 ], second: { inner: [ 1, 2, 3 ] } } 1777 // 1778 // The deepest depth of `a` is 2 (a.second.inner) and `a.first` has a max 1779 // depth of 1. 1780 // 1781 // Consolidate all entries of the local most inner depth up to 1782 // `ctx.compact`, as long as the properties are smaller than 1783 // `ctx.breakLength`. 1784 if (ctx.currentDepth - recurseTimes < ctx.compact && 1785 entries === output.length) { 1786 // Line up all entries on a single line in case the entries do not 1787 // exceed `breakLength`. Add 10 as constant to start next to all other 1788 // factors that may reduce `breakLength`. 1789 const start = output.length + ctx.indentationLvl + 1790 braces[0].length + base.length + 10; 1791 if (isBelowBreakLength(ctx, output, start, base)) { 1792 return `${base ? `${base} ` : ''}${braces[0]} ${join(output, ', ')}` + 1793 ` ${braces[1]}`; 1794 } 1795 } 1796 } 1797 // Line up each entry on an individual line. 1798 const indentation = `\n${' '.repeat(ctx.indentationLvl)}`; 1799 return `${base ? `${base} ` : ''}${braces[0]}${indentation} ` + 1800 `${join(output, `,${indentation} `)}${indentation}${braces[1]}`; 1801 } 1802 // Line up all entries on a single line in case the entries do not exceed 1803 // `breakLength`. 1804 if (isBelowBreakLength(ctx, output, 0, base)) { 1805 return `${braces[0]}${base ? ` ${base}` : ''} ${join(output, ', ')} ` + 1806 braces[1]; 1807 } 1808 const indentation = ' '.repeat(ctx.indentationLvl); 1809 // If the opening "brace" is too large, like in the case of "Set {", 1810 // we need to force the first item to be on the next line or the 1811 // items will not line up correctly. 1812 const ln = base === '' && braces[0].length === 1 ? 1813 ' ' : `${base ? ` ${base}` : ''}\n${indentation} `; 1814 // Line up each entry on an individual line. 1815 return `${braces[0]}${ln}${join(output, `,\n${indentation} `)} ${braces[1]}`; 1816} 1817 1818function format(...args) { 1819 return formatWithOptions(undefined, ...args); 1820} 1821 1822function hasBuiltInToString(value) { 1823 // Prevent triggering proxy traps. 1824 const getFullProxy = false; 1825 const proxyTarget = getProxyDetails(value, getFullProxy); 1826 if (proxyTarget !== undefined) { 1827 value = proxyTarget; 1828 } 1829 1830 // Count objects that have no `toString` function as built-in. 1831 if (typeof value.toString !== 'function') { 1832 return true; 1833 } 1834 1835 // The object has a own `toString` property. Thus it's not not a built-in one. 1836 if (ObjectPrototypeHasOwnProperty(value, 'toString')) { 1837 return false; 1838 } 1839 1840 // Find the object that has the `toString` property as own property in the 1841 // prototype chain. 1842 let pointer = value; 1843 do { 1844 pointer = ObjectGetPrototypeOf(pointer); 1845 } while (!ObjectPrototypeHasOwnProperty(pointer, 'toString')); 1846 1847 // Check closer if the object is a built-in. 1848 const descriptor = ObjectGetOwnPropertyDescriptor(pointer, 'constructor'); 1849 return descriptor !== undefined && 1850 typeof descriptor.value === 'function' && 1851 builtInObjects.has(descriptor.value.name); 1852} 1853 1854const firstErrorLine = (error) => error.message.split('\n')[0]; 1855let CIRCULAR_ERROR_MESSAGE; 1856function tryStringify(arg) { 1857 try { 1858 return JSONStringify(arg); 1859 } catch (err) { 1860 // Populate the circular error message lazily 1861 if (!CIRCULAR_ERROR_MESSAGE) { 1862 try { 1863 const a = {}; a.a = a; JSONStringify(a); 1864 } catch (err) { 1865 CIRCULAR_ERROR_MESSAGE = firstErrorLine(err); 1866 } 1867 } 1868 if (err.name === 'TypeError' && 1869 firstErrorLine(err) === CIRCULAR_ERROR_MESSAGE) { 1870 return '[Circular]'; 1871 } 1872 throw err; 1873 } 1874} 1875 1876function formatWithOptions(inspectOptions, ...args) { 1877 const first = args[0]; 1878 let a = 0; 1879 let str = ''; 1880 let join = ''; 1881 1882 if (typeof first === 'string') { 1883 if (args.length === 1) { 1884 return first; 1885 } 1886 let tempStr; 1887 let lastPos = 0; 1888 1889 for (let i = 0; i < first.length - 1; i++) { 1890 if (first.charCodeAt(i) === 37) { // '%' 1891 const nextChar = first.charCodeAt(++i); 1892 if (a + 1 !== args.length) { 1893 switch (nextChar) { 1894 case 115: // 's' 1895 const tempArg = args[++a]; 1896 if (typeof tempArg === 'number') { 1897 tempStr = formatNumber(stylizeNoColor, tempArg); 1898 } else if (typeof tempArg === 'bigint') { 1899 tempStr = `${tempArg}n`; 1900 } else if (typeof tempArg !== 'object' || 1901 tempArg === null || 1902 !hasBuiltInToString(tempArg)) { 1903 tempStr = String(tempArg); 1904 } else { 1905 tempStr = inspect(tempArg, { 1906 ...inspectOptions, 1907 compact: 3, 1908 colors: false, 1909 depth: 0 1910 }); 1911 } 1912 break; 1913 case 106: // 'j' 1914 tempStr = tryStringify(args[++a]); 1915 break; 1916 case 100: // 'd' 1917 const tempNum = args[++a]; 1918 if (typeof tempNum === 'bigint') { 1919 tempStr = `${tempNum}n`; 1920 } else if (typeof tempNum === 'symbol') { 1921 tempStr = 'NaN'; 1922 } else { 1923 tempStr = formatNumber(stylizeNoColor, Number(tempNum)); 1924 } 1925 break; 1926 case 79: // 'O' 1927 tempStr = inspect(args[++a], inspectOptions); 1928 break; 1929 case 111: // 'o' 1930 tempStr = inspect(args[++a], { 1931 ...inspectOptions, 1932 showHidden: true, 1933 showProxy: true, 1934 depth: 4 1935 }); 1936 break; 1937 case 105: // 'i' 1938 const tempInteger = args[++a]; 1939 if (typeof tempInteger === 'bigint') { 1940 tempStr = `${tempInteger}n`; 1941 } else if (typeof tempInteger === 'symbol') { 1942 tempStr = 'NaN'; 1943 } else { 1944 tempStr = formatNumber(stylizeNoColor, parseInt(tempInteger)); 1945 } 1946 break; 1947 case 102: // 'f' 1948 const tempFloat = args[++a]; 1949 if (typeof tempFloat === 'symbol') { 1950 tempStr = 'NaN'; 1951 } else { 1952 tempStr = formatNumber(stylizeNoColor, parseFloat(tempFloat)); 1953 } 1954 break; 1955 case 99: // 'c' 1956 a += 1; 1957 tempStr = ''; 1958 break; 1959 case 37: // '%' 1960 str += first.slice(lastPos, i); 1961 lastPos = i + 1; 1962 continue; 1963 default: // Any other character is not a correct placeholder 1964 continue; 1965 } 1966 if (lastPos !== i - 1) { 1967 str += first.slice(lastPos, i - 1); 1968 } 1969 str += tempStr; 1970 lastPos = i + 1; 1971 } else if (nextChar === 37) { 1972 str += first.slice(lastPos, i); 1973 lastPos = i + 1; 1974 } 1975 } 1976 } 1977 if (lastPos !== 0) { 1978 a++; 1979 join = ' '; 1980 if (lastPos < first.length) { 1981 str += first.slice(lastPos); 1982 } 1983 } 1984 } 1985 1986 while (a < args.length) { 1987 const value = args[a]; 1988 str += join; 1989 str += typeof value !== 'string' ? inspect(value, inspectOptions) : value; 1990 join = ' '; 1991 a++; 1992 } 1993 return str; 1994} 1995 1996if (internalBinding('config').hasIntl) { 1997 const icu = internalBinding('icu'); 1998 // icu.getStringWidth(string, ambiguousAsFullWidth, expandEmojiSequence) 1999 // Defaults: ambiguousAsFullWidth = false; expandEmojiSequence = true; 2000 // TODO(BridgeAR): Expose the options to the user. That is probably the 2001 // best thing possible at the moment, since it's difficult to know what 2002 // the receiving end supports. 2003 getStringWidth = function getStringWidth(str, removeControlChars = true) { 2004 let width = 0; 2005 2006 if (removeControlChars) 2007 str = stripVTControlCharacters(str); 2008 for (let i = 0; i < str.length; i++) { 2009 // Try to avoid calling into C++ by first handling the ASCII portion of 2010 // the string. If it is fully ASCII, we skip the C++ part. 2011 const code = str.charCodeAt(i); 2012 if (code >= 127) { 2013 width += icu.getStringWidth(str.slice(i).normalize('NFC')); 2014 break; 2015 } 2016 width += code >= 32 ? 1 : 0; 2017 } 2018 return width; 2019 }; 2020} else { 2021 /** 2022 * Returns the number of columns required to display the given string. 2023 */ 2024 getStringWidth = function getStringWidth(str, removeControlChars = true) { 2025 let width = 0; 2026 2027 if (removeControlChars) 2028 str = stripVTControlCharacters(str); 2029 str = str.normalize('NFC'); 2030 for (const char of str) { 2031 const code = char.codePointAt(0); 2032 if (isFullWidthCodePoint(code)) { 2033 width += 2; 2034 } else if (!isZeroWidthCodePoint(code)) { 2035 width++; 2036 } 2037 } 2038 2039 return width; 2040 }; 2041 2042 /** 2043 * Returns true if the character represented by a given 2044 * Unicode code point is full-width. Otherwise returns false. 2045 */ 2046 const isFullWidthCodePoint = (code) => { 2047 // Code points are partially derived from: 2048 // https://www.unicode.org/Public/UNIDATA/EastAsianWidth.txt 2049 return code >= 0x1100 && ( 2050 code <= 0x115f || // Hangul Jamo 2051 code === 0x2329 || // LEFT-POINTING ANGLE BRACKET 2052 code === 0x232a || // RIGHT-POINTING ANGLE BRACKET 2053 // CJK Radicals Supplement .. Enclosed CJK Letters and Months 2054 (code >= 0x2e80 && code <= 0x3247 && code !== 0x303f) || 2055 // Enclosed CJK Letters and Months .. CJK Unified Ideographs Extension A 2056 (code >= 0x3250 && code <= 0x4dbf) || 2057 // CJK Unified Ideographs .. Yi Radicals 2058 (code >= 0x4e00 && code <= 0xa4c6) || 2059 // Hangul Jamo Extended-A 2060 (code >= 0xa960 && code <= 0xa97c) || 2061 // Hangul Syllables 2062 (code >= 0xac00 && code <= 0xd7a3) || 2063 // CJK Compatibility Ideographs 2064 (code >= 0xf900 && code <= 0xfaff) || 2065 // Vertical Forms 2066 (code >= 0xfe10 && code <= 0xfe19) || 2067 // CJK Compatibility Forms .. Small Form Variants 2068 (code >= 0xfe30 && code <= 0xfe6b) || 2069 // Halfwidth and Fullwidth Forms 2070 (code >= 0xff01 && code <= 0xff60) || 2071 (code >= 0xffe0 && code <= 0xffe6) || 2072 // Kana Supplement 2073 (code >= 0x1b000 && code <= 0x1b001) || 2074 // Enclosed Ideographic Supplement 2075 (code >= 0x1f200 && code <= 0x1f251) || 2076 // Miscellaneous Symbols and Pictographs 0x1f300 - 0x1f5ff 2077 // Emoticons 0x1f600 - 0x1f64f 2078 (code >= 0x1f300 && code <= 0x1f64f) || 2079 // CJK Unified Ideographs Extension B .. Tertiary Ideographic Plane 2080 (code >= 0x20000 && code <= 0x3fffd) 2081 ); 2082 }; 2083 2084 const isZeroWidthCodePoint = (code) => { 2085 return code <= 0x1F || // C0 control codes 2086 (code >= 0x7F && code <= 0x9F) || // C1 control codes 2087 (code >= 0x300 && code <= 0x36F) || // Combining Diacritical Marks 2088 (code >= 0x200B && code <= 0x200F) || // Modifying Invisible Characters 2089 // Combining Diacritical Marks for Symbols 2090 (code >= 0x20D0 && code <= 0x20FF) || 2091 (code >= 0xFE00 && code <= 0xFE0F) || // Variation Selectors 2092 (code >= 0xFE20 && code <= 0xFE2F) || // Combining Half Marks 2093 (code >= 0xE0100 && code <= 0xE01EF); // Variation Selectors 2094 }; 2095} 2096 2097/** 2098 * Remove all VT control characters. Use to estimate displayed string width. 2099 */ 2100function stripVTControlCharacters(str) { 2101 return str.replace(ansi, ''); 2102} 2103 2104module.exports = { 2105 inspect, 2106 format, 2107 formatWithOptions, 2108 getStringWidth, 2109 inspectDefaultOptions, 2110 stripVTControlCharacters 2111}; 2112