class-privacy
Version:
Simple way to define private members on ES6 classes, keep their code clean.
131 lines (102 loc) • 5.34 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports["default"] = void 0;
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
/**
* @type {decide} defaults to a function always returning true
* @type {revealIsProxy} defaults to false
* @type {referenceClass} defaults to false
*/
var defaultOptions = {};
defaultOptions.decide = function () {
return true;
};
defaultOptions.revealIsProxy = false;
defaultOptions.referenceClass = false;
var IS_PROXY = 'isProxy';
var CLASS = 'class';
/**
* @private checks for a prototype constructor to match an expected value
*/
var check = function check(value, expected) {
var proto = value && Object.getPrototypeOf(value);
var constructor = proto && proto.constructor;
if (constructor !== expected) {
var constructorName = constructor && constructor.name;
var expectedName = expected && expected.name;
throw new TypeError("Expected ".concat(expectedName, ", got ").concat(constructorName, "."));
}
};
/**
* Creates a factory function that produces proxies to instances of a {class} definition, based on
* @param ClassDefinition
* @param options.decide {function} A function that checks the instance's given {key} and {value} of a property and
* returns true, if the property is public or false if private.
* @param options.revealIsProxy {boolean} if true it allows external code to ask for {isProxy} which then returns true
* @param options.referenceClass {boolean} if true it allows external code to ask for {class} which then returns
* the referenced original class definition but never the instance
* @returns {function} A factory function to produce proxies to an instance
*/
var createFactory = function createFactory(ClassDefinition) {
var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : defaultOptions;
check(ClassDefinition, Function);
check(options, Object); // we flat-merge the options with the default options
// to ensure that there are no options missing and the factory
// doesn't crash at runtime, for example because an options has
// been explicitly set to null
var factoryOptions = Object.assign({}, defaultOptions, options);
var decide = factoryOptions.decide || defaultOptions.decide;
var referenceClass = factoryOptions.referenceClass || defaultOptions.referenceClass;
var revealIsProxy = factoryOptions.revealIsProxy || defaultOptions.revealIsProxy;
/**
* Creates an instance of the given ClassDefinition
* @param invocationArgs arguments of arbitrary length, determined by ClassDefinition
* @return {proxy}
*/
var factory = function factory() {
for (var _len = arguments.length, invocationArgs = new Array(_len), _key = 0; _key < _len; _key++) {
invocationArgs[_key] = arguments[_key];
}
var instance = _construct(ClassDefinition, invocationArgs);
var handler = {
get: function get(target, property
/*, receiver */
) {
// get proxy by symbol
if (revealIsProxy && property === IS_PROXY) {
return true;
} // get class by symbol
if (referenceClass && property === CLASS) {
return ClassDefinition;
} // skip any request to unowned properties
// using 'in' as a good trade-off between validity and performance
if (!(property in instance)) {
return;
} // skip members, that don't pass the test,
// let developer decide how to design tests
var member = instance[property];
var type = _typeof(member);
var includeMember = decide(property, type, ClassDefinition);
if (!includeMember) {
return;
} // bind functions to the instance to avoid unintended
// blocking the member function's internals
if (type === 'function') {
return member.bind(instance);
} else {
return member;
}
}
};
return new Proxy({}, handler);
};
return factory;
};
var _default = createFactory;
exports["default"] = _default;
//# sourceMappingURL=index.js.map