Skip to content

Commit 0271f14

Browse files
authored
backport formatUnknown from v4 (#5422)
1 parent bddb3e9 commit 0271f14

File tree

6 files changed

+234
-139
lines changed

6 files changed

+234
-139
lines changed

.changeset/legal-masks-lay.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"effect": patch
3+
---
4+
5+
backport `formatUnknown` from v4

packages/effect/src/ParseResult.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1146,7 +1146,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => {
11461146
const output: Record<PropertyKey, unknown> = {}
11471147
let inputKeys: Array<PropertyKey> | undefined
11481148
if (onExcessPropertyError || onExcessPropertyPreserve) {
1149-
inputKeys = util_.ownKeys(input)
1149+
inputKeys = Reflect.ownKeys(input)
11501150
for (const key of inputKeys) {
11511151
const te = expected(key, options)
11521152
if (isEither(te) && Either.isLeft(te)) {
@@ -1311,7 +1311,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => {
13111311
}
13121312
if (options?.propertyOrder === "original") {
13131313
// preserve input keys order
1314-
const keys = inputKeys || util_.ownKeys(input)
1314+
const keys = inputKeys || Reflect.ownKeys(input)
13151315
for (const name of expectedKeys) {
13161316
if (keys.indexOf(name) === -1) {
13171317
keys.push(name)
@@ -1345,7 +1345,7 @@ const go = (ast: AST.AST, isDecoding: boolean): Parser => {
13451345
}
13461346
case "Union": {
13471347
const searchTree = getSearchTree(ast.types, isDecoding)
1348-
const ownKeys = util_.ownKeys(searchTree.keys)
1348+
const ownKeys = Reflect.ownKeys(searchTree.keys)
13491349
const ownKeysLen = ownKeys.length
13501350
const astTypesLen = ast.types.length
13511351
const map = new Map<any, Parser>()

packages/effect/src/Schema.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2768,7 +2768,7 @@ const getDefaultTypeLiteralAST = <
27682768
Fields extends Struct.Fields,
27692769
const Records extends IndexSignature.Records
27702770
>(fields: Fields, records: Records) => {
2771-
const ownKeys = util_.ownKeys(fields)
2771+
const ownKeys = Reflect.ownKeys(fields)
27722772
const pss: Array<AST.PropertySignature> = []
27732773
if (ownKeys.length > 0) {
27742774
const from: Array<AST.PropertySignature> = []
@@ -2845,7 +2845,7 @@ const lazilyMergeDefaults = (
28452845
fields: Struct.Fields,
28462846
out: Record<PropertyKey, unknown>
28472847
): { [x: string | symbol]: unknown } => {
2848-
const ownKeys = util_.ownKeys(fields)
2848+
const ownKeys = Reflect.ownKeys(fields)
28492849
for (const key of ownKeys) {
28502850
const field = fields[key]
28512851
if (out[key] === undefined && isPropertySignature(field)) {
@@ -8654,7 +8654,7 @@ type HasFields<Fields extends Struct.Fields> = Struct<Fields> | {
86548654
const isField = (u: unknown) => isSchema(u) || isPropertySignature(u)
86558655
86568656
const isFields = <Fields extends Struct.Fields>(fields: object): fields is Fields =>
8657-
util_.ownKeys(fields).every((key) => isField((fields as any)[key]))
8657+
Reflect.ownKeys(fields).every((key) => isField((fields as any)[key]))
86588658
86598659
const getFields = <Fields extends Struct.Fields>(hasFields: HasFields<Fields>): Fields =>
86608660
"fields" in hasFields ? hasFields.fields : getFields(hasFields[RefineSchemaId])
@@ -8838,7 +8838,7 @@ export const TaggedError = <Self = never>(identifier?: string) =>
88388838
Object.defineProperty(TaggedErrorClass.prototype, "message", {
88398839
get() {
88408840
return `{ ${
8841-
util_.ownKeys(fields)
8841+
Reflect.ownKeys(fields)
88428842
.map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown((this)[p])}`)
88438843
.join(", ")
88448844
} }`
@@ -8853,7 +8853,7 @@ export const TaggedError = <Self = never>(identifier?: string) =>
88538853
88548854
const extendFields = (a: Struct.Fields, b: Struct.Fields): Struct.Fields => {
88558855
const out = { ...a }
8856-
for (const key of util_.ownKeys(b)) {
8856+
for (const key of Reflect.ownKeys(b)) {
88578857
if (key in a) {
88588858
throw new Error(errors_.getASTDuplicatePropertySignatureErrorMessage(key))
88598859
}
@@ -9105,7 +9105,7 @@ const makeClass = <Fields extends Struct.Fields>(
91059105
Object.defineProperty(klass.prototype, "toString", {
91069106
value() {
91079107
return `${identifier}({ ${
9108-
util_.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`)
9108+
Reflect.ownKeys(fields).map((p: any) => `${util_.formatPropertyKey(p)}: ${util_.formatUnknown(this[p])}`)
91099109
.join(", ")
91109110
} })`
91119111
},
@@ -10795,7 +10795,7 @@ const go = (ast: AST.AST, path: ReadonlyArray<PropertyKey>): Equivalence.Equival
1079510795
}
1079610796
case "Union": {
1079710797
const searchTree = ParseResult.getSearchTree(ast.types, true)
10798-
const ownKeys = util_.ownKeys(searchTree.keys)
10798+
const ownKeys = Reflect.ownKeys(searchTree.keys)
1079910799
const len = ownKeys.length
1080010800
return Equivalence.make((a, b) => {
1080110801
let candidates: Array<AST.AST> = []

packages/effect/src/SchemaAST.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2926,7 +2926,7 @@ export const rename = (ast: AST, mapping: { readonly [K in PropertyKey]?: Proper
29262926
switch (ast._tag) {
29272927
case "TypeLiteral": {
29282928
const propertySignatureTransformations: Array<PropertySignatureTransformation> = []
2929-
for (const key of util_.ownKeys(mapping)) {
2929+
for (const key of Reflect.ownKeys(mapping)) {
29302930
const name = mapping[key]
29312931
if (name !== undefined) {
29322932
propertySignatureTransformations.push(

packages/effect/src/internal/schema/util.ts

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,6 @@ export const getKeysForIndexSignature = (
1919
}
2020
}
2121

22-
/**
23-
* JavaScript does not store the insertion order of properties in a way that
24-
* combines both string and symbol keys. The internal order groups string keys
25-
* and symbol keys separately. Hence concatenating the keys is fine.
26-
*
27-
* @internal
28-
*/
29-
export const ownKeys = (o: object): Array<PropertyKey> =>
30-
(Object.keys(o) as Array<PropertyKey>).concat(Object.getOwnPropertySymbols(o))
31-
3222
/** @internal */
3323
export const memoizeThunk = <A>(f: () => A): () => A => {
3424
let done = false
@@ -52,58 +42,94 @@ export const formatDate = (date: Date): string => {
5242
}
5343
}
5444

45+
const CIRCULAR = "[Circular]"
46+
5547
/** @internal */
56-
export const formatUnknown = (u: unknown, checkCircular: boolean = true): string => {
57-
if (Array.isArray(u)) {
58-
return `[${u.map((i) => formatUnknown(i, checkCircular)).join(",")}]`
59-
}
60-
if (Predicate.isDate(u)) {
61-
return formatDate(u)
62-
}
63-
if (
64-
Predicate.hasProperty(u, "toString")
65-
&& Predicate.isFunction(u["toString"])
66-
&& u["toString"] !== Object.prototype.toString
67-
) {
68-
return u["toString"]()
69-
}
70-
if (Predicate.isString(u)) {
71-
return JSON.stringify(u)
72-
}
73-
if (
74-
Predicate.isNumber(u)
75-
|| u == null
76-
|| Predicate.isBoolean(u)
77-
|| Predicate.isSymbol(u)
78-
) {
79-
return String(u)
48+
export function formatUnknown(input: unknown, whitespace: number | string | undefined = 0): string {
49+
const seen = new WeakSet<object>()
50+
const gap = !whitespace ? "" : (typeof whitespace === "number" ? " ".repeat(whitespace) : whitespace)
51+
const ind = (d: number) => gap.repeat(d)
52+
53+
const safeToString = (x: any): string => {
54+
try {
55+
const s = x.toString()
56+
return typeof s === "string" ? s : String(s)
57+
} catch {
58+
return "[toString threw]"
59+
}
8060
}
81-
if (Predicate.isBigInt(u)) {
82-
return String(u) + "n"
61+
62+
const wrap = (v: unknown, body: string): string => {
63+
const ctor = (v as any)?.constructor
64+
return ctor && ctor !== Object.prototype.constructor && ctor.name ? `${ctor.name}(${body})` : body
8365
}
84-
if (Predicate.isIterable(u)) {
85-
return `${u.constructor.name}(${formatUnknown(Array.from(u), checkCircular)})`
66+
67+
const ownKeys = (o: object): Array<PropertyKey> => {
68+
try {
69+
return Reflect.ownKeys(o)
70+
} catch {
71+
return ["[ownKeys threw]"]
72+
}
8673
}
87-
try {
88-
if (checkCircular) {
89-
JSON.stringify(u) // check for circular references
74+
75+
function go(v: unknown, d = 0): string {
76+
if (Array.isArray(v)) {
77+
if (seen.has(v)) return CIRCULAR
78+
seen.add(v)
79+
if (!gap || v.length <= 1) return `[${v.map((x) => go(x, d)).join(",")}]`
80+
const inner = v.map((x) => go(x, d + 1)).join(",\n" + ind(d + 1))
81+
return `[\n${ind(d + 1)}${inner}\n${ind(d)}]`
9082
}
91-
const pojo = `{${
92-
ownKeys(u).map((k) =>
93-
`${Predicate.isString(k) ? JSON.stringify(k) : String(k)}:${formatUnknown((u as any)[k], false)}`
94-
)
95-
.join(",")
96-
}}`
97-
const name = u.constructor.name
98-
return u.constructor !== Object.prototype.constructor ? `${name}(${pojo})` : pojo
99-
} catch {
100-
return "<circular structure>"
83+
84+
if (Predicate.isDate(v)) return formatDate(v)
85+
86+
if (
87+
Predicate.hasProperty(v, "toString") &&
88+
Predicate.isFunction((v as any)["toString"]) &&
89+
(v as any)["toString"] !== Object.prototype.toString
90+
) return safeToString(v)
91+
92+
if (Predicate.isString(v)) return JSON.stringify(v)
93+
94+
if (
95+
Predicate.isNumber(v) ||
96+
v == null ||
97+
Predicate.isBoolean(v) ||
98+
Predicate.isSymbol(v)
99+
) return String(v)
100+
101+
if (Predicate.isBigInt(v)) return String(v) + "n"
102+
103+
if (v instanceof Set || v instanceof Map) {
104+
if (seen.has(v)) return CIRCULAR
105+
seen.add(v)
106+
return `${v.constructor.name}(${go(Array.from(v), d)})`
107+
}
108+
109+
if (Predicate.isObject(v)) {
110+
if (seen.has(v)) return CIRCULAR
111+
seen.add(v)
112+
const keys = ownKeys(v)
113+
if (!gap || keys.length <= 1) {
114+
const body = `{${keys.map((k) => `${formatPropertyKey(k)}:${go((v as any)[k], d)}`).join(",")}}`
115+
return wrap(v, body)
116+
}
117+
const body = `{\n${
118+
keys.map((k) => `${ind(d + 1)}${formatPropertyKey(k)}: ${go((v as any)[k], d + 1)}`).join(",\n")
119+
}\n${ind(d)}}`
120+
return wrap(v, body)
121+
}
122+
123+
return String(v)
101124
}
125+
126+
return go(input, 0)
102127
}
103128

104129
/** @internal */
105-
export const formatPropertyKey = (name: PropertyKey): string =>
106-
typeof name === "string" ? JSON.stringify(name) : String(name)
130+
export function formatPropertyKey(name: PropertyKey): string {
131+
return Predicate.isString(name) ? JSON.stringify(name) : String(name)
132+
}
107133

108134
/** @internal */
109135
export type SingleOrArray<A> = A | ReadonlyArray<A>

0 commit comments

Comments
 (0)