/** * Copyright (c) Meta Platforms, Inc. and affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. * */ import type { CompareKeys, Config, Printer, Refs } from './types' function getKeysOfEnumerableProperties(object: Record, compareKeys: CompareKeys) { const rawKeys = Object.keys(object) const keys: Array = compareKeys === null ? rawKeys : rawKeys.sort(compareKeys) if (Object.getOwnPropertySymbols) { for (const symbol of Object.getOwnPropertySymbols(object)) { if (Object.getOwnPropertyDescriptor(object, symbol)!.enumerable) { keys.push(symbol) } } } return keys } /** * Return entries (for example, of a map) * with spacing, indentation, and comma * without surrounding punctuation (for example, braces) */ export function printIteratorEntries( iterator: Iterator<[unknown, unknown]>, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, separator = ': ', length?: number, ): string { let result = '' let width = 0 let current = iterator.next() if (!current.done) { result += config.spacingOuter const indentationNext = indentation + config.indent while (!current.done) { result += indentationNext if (width++ === config.maxWidth) { result += typeof length === 'number' ? `…(${length - width + 1})` : '…' break } const name = printer( current.value[0], config, indentationNext, depth, refs, ) const value = printer( current.value[1], config, indentationNext, depth, refs, ) result += name + separator + value current = iterator.next() if (!current.done) { result += `,${config.spacingInner}` } else if (!config.min) { result += ',' } } result += config.spacingOuter + indentation } return result } /** * Return values (for example, of a set) * with spacing, indentation, and comma * without surrounding punctuation (braces or brackets) */ export function printIteratorValues( iterator: Iterator, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, length?: number, ): string { let result = '' let width = 0 let current = iterator.next() if (!current.done) { result += config.spacingOuter const indentationNext = indentation + config.indent while (!current.done) { result += indentationNext if (width++ === config.maxWidth) { result += typeof length === 'number' ? `…(${length - width + 1})` : '…' break } result += printer(current.value, config, indentationNext, depth, refs) current = iterator.next() if (!current.done) { result += `,${config.spacingInner}` } else if (!config.min) { result += ',' } } result += config.spacingOuter + indentation } return result } /** * Return items (for example, of an array) * with spacing, indentation, and comma * without surrounding punctuation (for example, brackets) */ export function printListItems( list: ArrayLike | DataView | ArrayBuffer, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, ): string { let result = '' list = list instanceof ArrayBuffer ? new DataView(list) : list const isDataView = (l: unknown): l is DataView => l instanceof DataView const length = isDataView(list) ? list.byteLength : list.length if (length > 0) { result += config.spacingOuter const indentationNext = indentation + config.indent for (let i = 0; i < length; i++) { result += indentationNext if (i === config.maxWidth) { result += `…(${length - i})` break } if (isDataView(list) || i in list) { result += printer( isDataView(list) ? list.getInt8(i) : list[i], config, indentationNext, depth, refs, ) } if (i < length - 1) { result += `,${config.spacingInner}` } else if (!config.min) { result += ',' } } result += config.spacingOuter + indentation } return result } /** * Return properties of an object * with spacing, indentation, and comma * without surrounding punctuation (for example, braces) */ export function printObjectProperties( val: Record, config: Config, indentation: string, depth: number, refs: Refs, printer: Printer, compareKeysOverride: CompareKeys = config.compareKeys, ): string { let result = '' const keys = getKeysOfEnumerableProperties(val, compareKeysOverride) if (keys.length > 0) { result += config.spacingOuter const indentationNext = indentation + config.indent for (let i = 0; i < keys.length; i++) { result += indentationNext if (i === config.maxWidth) { result += `…(${keys.length - i})` break } const key = keys[i] const name = !config.quoteKeys && isUnquotableKey(key) ? key as string : printer(key, config, indentationNext, depth, refs) const value = printer(val[key], config, indentationNext, depth, refs) result += `${name}: ${value}` if (i < keys.length - 1) { result += `,${config.spacingInner}` } else if (!config.min) { result += ',' } } result += config.spacingOuter + indentation } return result } // https://github.com/nodejs/node/blob/61102cdbb3d59155ad5bb4fc9419627a31e63f7a/lib/internal/util/inspect.js#L249 // /^[a-zA-Z_][a-zA-Z_0-9]*$/ const keyStrRegExp = /^[a-z_]\w*$/i function isUnquotableKey(key: string | symbol): boolean { return typeof key === 'string' && key !== '__proto__' && keyStrRegExp.test(key) }