import JSON5 from "json5"
/**
* @description Stringifies literally anything
* @param {unknown} thing The thing to stringify
* @param {number} [indent] The indent depth, 0 by default
* @param {number} [depth] The space depth, 0 by default
* @param {number} [currentDepth] The total depth offset to base other depths off of, 0 by default
* @returns {string} Your weirdest thing, stringified
*/
export function stringifyAnything(
thing: unknown,
indent: number = 0,
depth: number = 0,
currentDepth: number = 0
): string {
if (typeof thing === "string" && thing.includes("\n"))
return `\`${thing.replace(/`/g, "\\`")}\``
if (typeof thing === "function" || typeof thing === "symbol") return String(thing)
if (typeof thing !== "object" || thing == null) return String(JSON5.stringify(thing))
const omit = currentDepth > depth
const space = " ".repeat(indent)
const level = space + " ".repeat(4)
if (!thing.constructor) {
return "{}"
} else if (thing.constructor.name === "Object") {
const propertyNames = Object.getOwnPropertyNames(thing)
if (!propertyNames.length) return "{}"
const properties = omit
? " ... "
: `\n${level}` +
propertyNames
.map(name => {
const value = thing[name]
return `"${name}": ${stringifyAnything(
value,
indent + 4,
depth,
currentDepth + 1
)}`
})
.join(",\n" + level) +
`\n${space}`
return `{${properties}}`
} else if (thing instanceof Array && thing.constructor.name === "Array") {
if (!thing.length) return "[]"
const items = omit
? " ... "
: "\n" +
thing
.map(
thing =>
level +
stringifyAnything(thing, indent + 4, depth, currentDepth + 1)
)
.join(",\n") +
`\n${space}`
return `[${items}]`
} else {
const propertyNames = Object.getOwnPropertyNames(thing)
if (!propertyNames.length) return `${thing.constructor.name} {}`
const properties = omit
? " ... "
: "\n" +
propertyNames
.map(name => {
const value = thing[name]
if (value === undefined) return null
return `${level}${name} = ${stringifyAnything(
value,
indent + 4,
depth,
currentDepth + 1
)}`
})
.filter(thing => thing !== null && typeof thing !== "function")
.join("\n") +
"\n"
return `${thing.constructor.name} {${properties}}`
}
}