Skip to content

Commit

Permalink
Return proxy redirecting keys for options-as-properties from Command …
Browse files Browse the repository at this point in the history
…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.
  • Loading branch information
aweebit committed Jul 30, 2023
1 parent 88d0cf8 commit 1b94efb
Showing 1 changed file with 65 additions and 9 deletions.
74 changes: 65 additions & 9 deletions lib/command.js
Expand Up @@ -95,6 +95,67 @@ Option value deletion is not supported`);
Options value configuration is not supported`);
}
});

// Because of how the returned proxy works, ideally, no prooerties should be defined outside the cinstructor.
// They can still be defined outside the constructor in subclasses, but only when _storeOptionsAsProperties is set to false.
this._version = undefined;
this._versionOptionName = undefined;

// The proxy only treats keys not present in the instance and its prototype chain as keys for _optionValues when _storeOptionsAsProperties is set to true.
// Setting option values for keys present in the instance and its prototype chain is still possible by calling .setOptionValue() or .setOptionValueWithSource(),
// but such values will not be accessible as instnace properties because the instance and its prototype chain has precedence.
// However, they will be accessible via .getOptionValue(), .opts() and .optsWithGlobals().
return new Proxy(this, {
get(target, key, receiver) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = receiver = receiver._optionValuesProxy;
}
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = receiver = receiver._optionValuesProxy;
}
return Reflect.set(target, key, value, receiver);
},
has(target, key) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = target._optionValuesProxy;
}
return Reflect.has(target, key);
},
deleteProperty(target, key) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = target._optionValuesProxy;
}
return Reflect.deleteProperty(target, key);
},
defineProperty(target, key, descriptor) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = target._optionValuesProxy;
}
return Reflect.defineProperty(target, key, descriptor);
},
getOwnPropertyDescriptor(target, key) {
if (target._storeOptionsAsProperties && !(key in target)) {
target = target._optionValuesProxy;
}
return Reflect.getOwnPropertyDescriptor(target, key);
},
ownKeys(target) {
const result = Reflect.ownKeys(target);
if (target._storeOptionsAsProperties) {
result.push(...Reflect.ownKeys(target._optionValuesProxy));
}
return result;
},
preventExtensions(target) {
if (target._storeOptionsAsProperties) {
Reflect.preventExtensions(target._optionValuesProxy);
}
return Reflect.preventExtensions(target);
}
});
}

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

getOptionValue(key) {
if (this._storeOptionsAsProperties) {
return this[key];
}
return this._optionValues[key];
}

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

setOptionValueWithSource(key, value, source) {
if (this._storeOptionsAsProperties) {
this[key] = value;
} else {
this._optionValues[key] = value;
}
this._optionValues[key] = value;
this._optionValueSources[key] = source;
return this;
}
Expand Down Expand Up @@ -1580,7 +1634,9 @@ Expecting one of '${allowedValues.join("', '")}'`);

for (let i = 0; i < len; i++) {
const key = this.options[i].attributeName();
result[key] = key === this._versionOptionName ? this._version : this[key];
result[key] = key === this._versionOptionName
? this._version
: this._optionValues[key];
}
return result;
}
Expand Down

0 comments on commit 1b94efb

Please sign in to comment.