-
Notifications
You must be signed in to change notification settings - Fork 2
/
index.js
157 lines (147 loc) · 4.98 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
const SUPPORTS_DATATRANSFER = (() => {
try {
new DataTransfer();
return true;
} catch (_) {
return false;
}
})();
const SUPPORTS_FILE = typeof File !== "undefined";
const SUPPORTS_BLOB = typeof Blob !== "undefined";
const SUPPORTS_FILELIST = typeof FileList !== "undefined";
const SUPPORTS_ARRAYBUFFER = typeof ArrayBuffer !== "undefined";
const SUPPORTS_DATAVIEW = typeof DataView !== "undefined";
const SUPPORTS_IMAGEDATA = typeof ImageData !== "undefined";
const SUPPORTS_MAP = typeof Map !== "undefined";
const SUPPORTS_SET = typeof Set !== "undefined";
const SUPPORTS_DOMMATRIX = typeof DOMMatrix !== "undefined";
const SUPPORTS_DOMPOINT = typeof DOMPoint !== "undefined";
const SUPPORTS_DOMQUAD = typeof DOMQuad !== "undefined";
const SUPPORTS_DOMRECT = typeof DOMRect !== "undefined";
const SUPPORTS_SHAREDARRAYBUFFER = typeof SharedArrayBuffer !== "undefined";
const SUPPORTS_BIGINT = typeof BigInt !== "undefined";
const SUPPORTS_DOMEXCEPTION = typeof DOMException !== "undefined";
// Primitives types except Symbol
const PRIMITIVE_TYPES = ["undefined", "boolean", "number", "string", "bigint"];
// For cyclic objects
const map = new Map();
export default function structuredClone(obj) {
try {
return clone(obj);
} finally {
map.clear();
}
}
function clone(obj) {
if (isPrimitive(obj)) {
return obj;
}
if (!map.has(obj)) {
cloneObject(obj, newObj => {
map.set(obj, newObj);
});
}
return map.get(obj);
}
function cloneObject(obj, set) {
if (obj instanceof Date) {
set(new Date(obj));
} else if (obj instanceof String) {
set(new String(obj));
} else if (obj instanceof Boolean) {
set(new Boolean(obj.valueOf()));
} else if (obj instanceof Number) {
set(new Number(obj));
} else if (obj instanceof RegExp) {
set(new RegExp(obj));
} else if (SUPPORTS_DOMEXCEPTION && obj instanceof DOMException) {
set(new DOMException(obj.message, obj.name));
} else if (obj instanceof Error) {
const Constructor = globalThis[obj.name] || Error;
const newError = new Constructor();
set(newError);
if (hasOwn(obj, "message")) {
const message = Object.getOwnPropertyDescriptor(obj, "message");
if ("value" in message) {
newError.message = String(obj.message);
}
}
if ("stack" in obj) newError.stack = clone(obj.stack);
if (hasOwn(obj, "cause")) newError.cause = clone(obj.cause);
} else if (SUPPORTS_BIGINT && obj instanceof BigInt) {
set(Object(obj.valueOf()));
} else if (SUPPORTS_FILE && obj instanceof File) {
set(
new File([obj], obj.name, {
type: obj.type,
lastModified: obj.lastModified
})
);
} else if (SUPPORTS_BLOB && obj instanceof Blob) {
set(obj.slice(0, obj.size, obj.type));
} else if (
SUPPORTS_FILELIST &&
SUPPORTS_DATATRANSFER &&
obj instanceof FileList
) {
const dataTransfer = new DataTransfer();
for (const file of obj) {
dataTransfer.items.add(clone(file));
}
set(dataTransfer.files);
} else if (SUPPORTS_ARRAYBUFFER && obj instanceof ArrayBuffer) {
set(obj.slice(0));
} else if (SUPPORTS_SHAREDARRAYBUFFER && obj instanceof SharedArrayBuffer) {
set(obj.slice(0));
} else if (SUPPORTS_DATAVIEW && obj instanceof DataView) {
set(new DataView(obj.buffer, obj.byteOffset, obj.byteLength));
} else if (SUPPORTS_ARRAYBUFFER && ArrayBuffer.isView(obj)) {
set(obj.slice());
} else if (SUPPORTS_IMAGEDATA && obj instanceof ImageData) {
set(new ImageData(obj.data.slice(0), obj.width, obj.height));
} else if (SUPPORTS_DOMMATRIX && obj instanceof DOMMatrix) {
set(obj.scale(1));
} else if (SUPPORTS_DOMPOINT && obj instanceof DOMPoint) {
set(new DOMPoint(obj.x, obj.y, obj.z, obj.w));
} else if (SUPPORTS_DOMQUAD && obj instanceof DOMQuad) {
set(new DOMQuad(obj.p1, obj.p2, obj.p3, obj.p4));
} else if (SUPPORTS_DOMRECT && obj instanceof DOMRect) {
set(DOMRect.fromRect(obj));
} else if (Array.isArray(obj)) {
const newObj = new Array(obj.length);
set(newObj);
for (const key in obj) {
newObj[key] = clone(obj[key]);
}
} else if (SUPPORTS_MAP && obj instanceof Map) {
const newObj = new Map();
set(newObj);
Array.from(obj.entries()).forEach(([key, value]) => {
newObj.set(clone(key), clone(value));
});
} else if (SUPPORTS_SET && obj instanceof Set) {
const newObj = new Set();
set(newObj);
Array.from(obj.values()).forEach(item => newObj.add(clone(item)));
} else if (isObject(obj)) {
const newObj = {};
set(newObj);
Object.keys(obj).forEach(key => {
newObj[key] = clone(obj[key]);
});
} else {
throw new Error(`Unsupported object ${String(obj)}`);
}
}
function hasOwn(obj, name) {
return Object.prototype.hasOwnProperty.call(obj, name);
}
function isObject(obj) {
const proto = Object.getPrototypeOf(obj);
return (
proto === null || Object.prototype.toString.call(obj) === "[object Object]"
);
}
function isPrimitive(item) {
return item === null || PRIMITIVE_TYPES.includes(typeof item);
}