From 1b94efb5472642f6cffd1225aa6cf11864a902eb Mon Sep 17 00:00:00 2001 From: Wee Bit Date: Sun, 30 Jul 2023 23:47:15 +0300 Subject: [PATCH] Return proxy redirecting keys for options-as-properties from Command constructor Borrowed from #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. --- lib/command.js | 74 ++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 65 insertions(+), 9 deletions(-) diff --git a/lib/command.js b/lib/command.js index c19197787..636c06172 100644 --- a/lib/command.js +++ b/lib/command.js @@ -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); + } + }); } /** @@ -783,9 +844,6 @@ Expecting one of '${allowedValues.join("', '")}'`); */ getOptionValue(key) { - if (this._storeOptionsAsProperties) { - return this[key]; - } return this._optionValues[key]; } @@ -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; } @@ -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; }