Home Reference Source Repository

src/extendProperty.js

import has from "lodash.has";
import isFunction from "lodash.isfunction";

/**
	Extends a shadow property with the definition returned by the assoicated Property methods
	marked using the @shadow and @shadowBound decorators.

	NOTE: function must be called after subclasses are created.

	@ignore
*/
export default function extendProperty(property, impl, shadow) {
	const proto = Object.getPrototypeOf(property);
	const shadowDescriptors = proto.constructor.shadowDescriptors;

	if (shadowDescriptors) {
		defineProperties(shadowDescriptors, shadow, property, impl);
	}

	// define mixin shadow properties
	var mixins, mixinDescriptors, mixinDesc;

	if (property.__hasMixins()) {
		let mixins = property.__mixins();

		for (let i=0, len=mixins.length; i<len; i++) {
			mixinDescriptors = mixins[i].__shadowDescriptors__;

			if (mixinDescriptors) {
				defineProperties(mixinDescriptors, shadow, property, impl, true);
			}
		}
	}
}

function defineProperties(shadowDescriptors, shadow, property, impl, mixin) {
	for (let name in shadowDescriptors) {
		if (has(shadow, name) || shadow[name]) {
			console.warn(`Property name conflict: name=${name}, path=${impl.slashPath()}, mixin-source=${!!mixin}`);

			continue;
		}

		const { descriptor, bindProperty } = shadowDescriptors[name];

		if (isFunction(descriptor.value)) {
			const value = bindProperty ?descriptor.value.bind(property) :descriptor.value;

			// Functions are not enumerable in javascript classes so adher to convention
			Object.defineProperty(shadow, name, {
					enumerable: false,
					value: value
				});
		} else {
			const enumerable = descriptor.enumerable && !name.startsWith('_');
			const get = bindProperty && descriptor.get ?descriptor.get.bind(property) :descriptor.get
			const set = bindProperty && descriptor.set ?descriptor.set.bind(property) :descriptor.set

			if (descriptor.get) {
				Object.defineProperty(shadow, name, {
						enumerable: enumerable,
						get: get, // && descriptor.get.bind(property),
						set: set, // && descriptor.set.bind(property),
					});
			} else {
				Object.defineProperty(shadow, name, {
						enumerable: enumerable,
						value: descriptor.value,
						writable: false,
					});
			}
		}
	}
}