Skip to content

Commit 1b94efb

Browse files
committed
Return proxy redirecting keys for options-as-properties from Command constructor
Borrowed from tj#1919. Makes _optionValues the only true storage for option values. Has the added benefit of supporting option names conflicting with instance's properties even when options-as-properties are enabled.
1 parent 88d0cf8 commit 1b94efb

File tree

1 file changed

+65
-9
lines changed

1 file changed

+65
-9
lines changed

lib/command.js

Lines changed: 65 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,67 @@ Option value deletion is not supported`);
9595
Options value configuration is not supported`);
9696
}
9797
});
98+
99+
// Because of how the returned proxy works, ideally, no prooerties should be defined outside the cinstructor.
100+
// They can still be defined outside the constructor in subclasses, but only when _storeOptionsAsProperties is set to false.
101+
this._version = undefined;
102+
this._versionOptionName = undefined;
103+
104+
// The proxy only treats keys not present in the instance and its prototype chain as keys for _optionValues when _storeOptionsAsProperties is set to true.
105+
// Setting option values for keys present in the instance and its prototype chain is still possible by calling .setOptionValue() or .setOptionValueWithSource(),
106+
// but such values will not be accessible as instnace properties because the instance and its prototype chain has precedence.
107+
// However, they will be accessible via .getOptionValue(), .opts() and .optsWithGlobals().
108+
return new Proxy(this, {
109+
get(target, key, receiver) {
110+
if (target._storeOptionsAsProperties && !(key in target)) {
111+
target = receiver = receiver._optionValuesProxy;
112+
}
113+
return Reflect.get(target, key, receiver);
114+
},
115+
set(target, key, value, receiver) {
116+
if (target._storeOptionsAsProperties && !(key in target)) {
117+
target = receiver = receiver._optionValuesProxy;
118+
}
119+
return Reflect.set(target, key, value, receiver);
120+
},
121+
has(target, key) {
122+
if (target._storeOptionsAsProperties && !(key in target)) {
123+
target = target._optionValuesProxy;
124+
}
125+
return Reflect.has(target, key);
126+
},
127+
deleteProperty(target, key) {
128+
if (target._storeOptionsAsProperties && !(key in target)) {
129+
target = target._optionValuesProxy;
130+
}
131+
return Reflect.deleteProperty(target, key);
132+
},
133+
defineProperty(target, key, descriptor) {
134+
if (target._storeOptionsAsProperties && !(key in target)) {
135+
target = target._optionValuesProxy;
136+
}
137+
return Reflect.defineProperty(target, key, descriptor);
138+
},
139+
getOwnPropertyDescriptor(target, key) {
140+
if (target._storeOptionsAsProperties && !(key in target)) {
141+
target = target._optionValuesProxy;
142+
}
143+
return Reflect.getOwnPropertyDescriptor(target, key);
144+
},
145+
ownKeys(target) {
146+
const result = Reflect.ownKeys(target);
147+
if (target._storeOptionsAsProperties) {
148+
result.push(...Reflect.ownKeys(target._optionValuesProxy));
149+
}
150+
return result;
151+
},
152+
preventExtensions(target) {
153+
if (target._storeOptionsAsProperties) {
154+
Reflect.preventExtensions(target._optionValuesProxy);
155+
}
156+
return Reflect.preventExtensions(target);
157+
}
158+
});
98159
}
99160

100161
/**
@@ -783,9 +844,6 @@ Expecting one of '${allowedValues.join("', '")}'`);
783844
*/
784845

785846
getOptionValue(key) {
786-
if (this._storeOptionsAsProperties) {
787-
return this[key];
788-
}
789847
return this._optionValues[key];
790848
}
791849

@@ -811,11 +869,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
811869
*/
812870

813871
setOptionValueWithSource(key, value, source) {
814-
if (this._storeOptionsAsProperties) {
815-
this[key] = value;
816-
} else {
817-
this._optionValues[key] = value;
818-
}
872+
this._optionValues[key] = value;
819873
this._optionValueSources[key] = source;
820874
return this;
821875
}
@@ -1580,7 +1634,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
15801634

15811635
for (let i = 0; i < len; i++) {
15821636
const key = this.options[i].attributeName();
1583-
result[key] = key === this._versionOptionName ? this._version : this[key];
1637+
result[key] = key === this._versionOptionName
1638+
? this._version
1639+
: this._optionValues[key];
15841640
}
15851641
return result;
15861642
}

0 commit comments

Comments
 (0)