-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
/
alias.ts
123 lines (100 loc) · 3.56 KB
/
alias.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
120
121
122
123
import type { Meta } from '@ember/-internals/meta';
import { meta as metaFor } from '@ember/-internals/meta';
import { inspect } from '@ember/-internals/utils';
import { assert } from '@ember/debug';
import type { UpdatableTag } from '@glimmer/validator';
import {
consumeTag,
tagFor,
tagMetaFor,
untrack,
updateTag,
validateTag,
valueForTag,
} from '@glimmer/validator';
import { CHAIN_PASS_THROUGH, finishLazyChains, getChainTagsForKey } from './chain-tags';
import type { ExtendedMethodDecorator } from './decorator';
import {
ComputedDescriptor,
descriptorForDecorator,
isElementDescriptor,
makeComputedDecorator,
} from './decorator';
import { defineProperty } from './properties';
import { get } from './property_get';
import { set } from './property_set';
export type AliasDecorator = ExtendedMethodDecorator & PropertyDecorator & AliasDecoratorImpl;
export default function alias(altKey: string): AliasDecorator {
assert(
'You attempted to use @alias as a decorator directly, but it requires a `altKey` parameter',
!isElementDescriptor(Array.prototype.slice.call(arguments))
);
// SAFETY: We passed in the impl for this class
return makeComputedDecorator(new AliasedProperty(altKey), AliasDecoratorImpl) as AliasDecorator;
}
// TODO: This class can be svelted once `meta` has been deprecated
class AliasDecoratorImpl extends Function {
readOnly(this: ExtendedMethodDecorator) {
(descriptorForDecorator(this) as AliasedProperty).readOnly();
return this;
}
oneWay(this: ExtendedMethodDecorator) {
(descriptorForDecorator(this) as AliasedProperty).oneWay();
return this;
}
meta(this: ExtendedMethodDecorator, meta?: any): any {
let prop = descriptorForDecorator(this) as AliasedProperty;
if (arguments.length === 0) {
return prop._meta || {};
} else {
prop._meta = meta;
}
}
}
export class AliasedProperty extends ComputedDescriptor {
readonly altKey: string;
constructor(altKey: string) {
super();
this.altKey = altKey;
}
setup(obj: object, keyName: string, propertyDesc: PropertyDescriptor, meta: Meta): void {
assert(`Setting alias '${keyName}' on self`, this.altKey !== keyName);
super.setup(obj, keyName, propertyDesc, meta);
CHAIN_PASS_THROUGH.add(this);
}
get(obj: object, keyName: string): any {
let ret: any;
let meta = metaFor(obj);
let tagMeta = tagMetaFor(obj);
let propertyTag = tagFor(obj, keyName, tagMeta) as UpdatableTag;
// We don't use the tag since CPs are not automatic, we just want to avoid
// anything tracking while we get the altKey
untrack(() => {
ret = get(obj, this.altKey);
});
let lastRevision = meta.revisionFor(keyName);
if (lastRevision === undefined || !validateTag(propertyTag, lastRevision)) {
updateTag(propertyTag, getChainTagsForKey(obj, this.altKey, tagMeta, meta));
meta.setRevisionFor(keyName, valueForTag(propertyTag));
finishLazyChains(meta, keyName, ret);
}
consumeTag(propertyTag);
return ret;
}
set(obj: object, _keyName: string, value: any): any {
return set(obj, this.altKey, value);
}
readOnly(): void {
this.set = AliasedProperty_readOnlySet;
}
oneWay(): void {
this.set = AliasedProperty_oneWaySet;
}
}
function AliasedProperty_readOnlySet(obj: object, keyName: string): never {
throw new Error(`Cannot set read-only property '${keyName}' on object: ${inspect(obj)}`);
}
function AliasedProperty_oneWaySet(obj: object, keyName: string, value: any): any {
defineProperty(obj, keyName, null);
return set(obj, keyName, value);
}