* @param {string[]} [prefixes] - The prefixes for values, in resolution order.
* @param {object[]} [rootScopes] - The root option scopes
* @param {string|boolean} [fallback] - Parent scopes fallback
+ * @param {function} [getTarget] - callback for getting the target for changed values
* @returns Proxy
* @private
*/
-export function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback) {
+export function _createResolver(scopes, prefixes = [''], rootScopes = scopes, fallback, getTarget = () => scopes[0]) {
if (!defined(fallback)) {
fallback = _resolve('_fallback', scopes);
}
_scopes: scopes,
_rootScopes: rootScopes,
_fallback: fallback,
+ _getTarget: getTarget,
override: (scope) => _createResolver([scope, ...scopes], prefixes, rootScopes, fallback),
};
return new Proxy(cache, {
* A trap for setting property values.
*/
set(target, prop, value) {
- scopes[0][prop] = value; // set to top level scope
+ const storage = target._storage || (target._storage = getTarget());
+ storage[prop] = value; // set to top level scope
delete target[prop]; // remove from cache
delete target._keys; // remove cached keys
return true;
const fallback = resolveFallback(resolver._fallback, prop, value);
const allScopes = [...parentScopes, ...rootScopes];
const set = new Set();
- const firstParent = parentScopes[0];
- if (isObject(firstParent) && !(prop in firstParent)) {
- // create an empty scope for possible stored values, so we always set the values in top scope.
- set.add(firstParent[prop] = {});
- }
set.add(value);
let key = addScopesFromKey(set, allScopes, prop, fallback || prop);
if (key === null) {
return false;
}
}
- return _createResolver([...set], [''], rootScopes, fallback);
+ return _createResolver([...set], [''], rootScopes, fallback, () => {
+ const parent = resolver._getTarget();
+ if (!(prop in parent)) {
+ parent[prop] = {};
+ }
+ return parent[prop];
+ });
}
function addScopesFromKey(set, allScopes, key, fallback) {
expect(resolver.getter).toEqual('options getter');
});
+ it('should not fail on when options are frozen', function() {
+ function create() {
+ const defaults = Object.freeze({default: true});
+ const options = Object.freeze({value: true});
+ return _createResolver([options, defaults]);
+ }
+ expect(create).not.toThrow();
+ });
+
describe('_fallback', function() {
it('should follow simple _fallback', function() {
const defaults = {
expect(options.sub.value).toBeFalse();
expect(defaults.sub.value).toBeTrue();
});
+
+ it('should throw when setting a value and options is frozen', function() {
+ const defaults = Object.freeze({default: true});
+ const options = Object.freeze({value: true});
+ const resolver = _createResolver([options, defaults]);
+ function set() {
+ resolver.value = false;
+ }
+ expect(set).toThrow();
+ });
});
});