-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathdraft.ts
119 lines (105 loc) · 3.27 KB
/
draft.ts
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
import { DraftType, Mark, ProxyDraft } from '../interface';
import { dataTypes, PROXY_DRAFT } from '../constant';
export function latest<T = any>(proxyDraft: ProxyDraft): T {
return proxyDraft.copy ?? proxyDraft.original;
}
/**
* Check if the value is a draft
*/
export function isDraft(target: any) {
return !!getProxyDraft(target);
}
export function getProxyDraft<T extends any>(value: T): ProxyDraft | null {
if (typeof value !== 'object') return null;
return (value as { [PROXY_DRAFT]: any })?.[PROXY_DRAFT];
}
export function getValue<T extends object>(value: T): T {
const proxyDraft = getProxyDraft(value);
return proxyDraft ? proxyDraft.copy ?? proxyDraft.original : value;
}
/**
* Check if a value is draftable
*/
export function isDraftable(value: any, options?: { mark?: Mark<any, any> }) {
if (!value || typeof value !== 'object') return false;
let markResult: any;
return (
Object.getPrototypeOf(value) === Object.prototype ||
Array.isArray(value) ||
value instanceof Map ||
value instanceof Set ||
(!!options?.mark &&
((markResult = options.mark(value, dataTypes)) === dataTypes.immutable ||
typeof markResult === 'function'))
);
}
export function getPath(
target: ProxyDraft,
path: any[] = []
): (string | number | object)[] {
if (Object.hasOwnProperty.call(target, 'key'))
path.push(
target.parent!.type === DraftType.Set
? Array.from(target.parent!.setMap!.keys()).indexOf(target.key)
: target.key
);
if (target.parent) {
return getPath(target.parent, path);
}
return path.reverse();
}
export function getType(target: any) {
if (Array.isArray(target)) return DraftType.Array;
if (target instanceof Map) return DraftType.Map;
if (target instanceof Set) return DraftType.Set;
return DraftType.Object;
}
export function get(target: any, key: PropertyKey) {
return getType(target) === DraftType.Map ? target.get(key) : target[key];
}
export function set(target: any, key: PropertyKey, value: any) {
if (getType(target) === DraftType.Map) {
target.set(key, value);
} else {
target[key] = value;
}
}
export function peek(target: any, key: PropertyKey) {
const state = getProxyDraft(target);
const source = state ? latest(state) : target;
return source[key];
}
export function isEqual(x: any, y: any) {
if (x === y) {
return x !== 0 || 1 / x === 1 / y;
} else {
return x !== x && y !== y;
}
}
export function revokeProxy(proxyDraft: ProxyDraft | null) {
if (!proxyDraft) return;
while (proxyDraft.finalities.revoke.length > 0) {
const revoke = proxyDraft.finalities.revoke.pop()!;
revoke();
}
}
// handle JSON Pointer path with spec https://www.rfc-editor.org/rfc/rfc6901
export function escapePath(path: string[], pathAsArray: boolean) {
return pathAsArray
? path
: ['']
.concat(path)
.map((_item) => {
const item = `${_item}`;
if (item.indexOf('/') === -1 && item.indexOf('~') === -1) return item;
return item.replace(/~/g, '~0').replace(/\//g, '~1');
})
.join('/');
}
export function unescapePath(path: string | (string | number)[]) {
if (Array.isArray(path)) return path;
return path
.split('/')
.map((_item) => _item.replace(/~1/g, '/').replace(/~0/g, '~'))
.slice(1);
}