# 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,
    					});
    			}
    		}
    	}
    }