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