1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105 | 1×
1×
1×
1×
1×
1×
357×
89×
89×
89×
89×
89×
31×
31×
31×
65×
65×
1×
357×
357×
357×
357×
357×
357×
359×
357×
357×
1×
775×
775×
775×
6975×
1135×
1135×
775×
775×
775×
775×
775×
775×
775×
775×
775×
775×
95×
137×
119×
137×
775×
1×
357×
357×
| import isFunction from './isFunction';
import isObject from './isObject';
import slice from './slice';
import {merge, assign} from './merge';
const isDescriptor = isObject;
/**
* Creates new factory instance.
* @param {object} descriptor The information about the object the factory will be creating.
* @returns {Function} The new factory function.
*/
function createFactory(descriptor) {
return function Stamp(options) {
const obj = Object.create(descriptor.methods || {});
merge(obj, descriptor.deepProperties);
assign(obj, descriptor.properties);
Object.defineProperties(obj, descriptor.propertyDescriptors || {});
if (!descriptor.initializers || descriptor.initializers.length === 0) return obj;
const args = slice.call(arguments, 1);
if (options === undefined) options = {};
return descriptor.initializers.filter(isFunction).reduce((resultingObj, initializer) => {
const returnedValue = initializer.call(resultingObj, options,
{instance: resultingObj, stamp: Stamp, args: [options].concat(args)});
return returnedValue === undefined ? resultingObj : returnedValue;
}, obj);
};
}
/**
* Returns a new stamp given a descriptor and a compose function implementation.
* @param {object} [descriptor={}] The information about the object the stamp will be creating.
* @param {Function} composeFunction The "compose" function implementation.
* @returns {Function}
*/
function createStamp(descriptor, composeFunction) {
const Stamp = createFactory(descriptor);
merge(Stamp, descriptor.staticDeepProperties);
assign(Stamp, descriptor.staticProperties);
Object.defineProperties(Stamp, descriptor.staticPropertyDescriptors || {});
const composeImplementation = isFunction(Stamp.compose) ? Stamp.compose : composeFunction;
Stamp.compose = function _compose() {
return composeImplementation.apply(this, slice.call(arguments));
};
assign(Stamp.compose, descriptor);
return Stamp;
}
/**
* Mutates the dstDescriptor by merging the srcComposable data into it.
* @param {object} dstDescriptor The descriptor object to merge into.
* @param {object} [srcComposable] The composable (either descriptor or stamp) to merge data form.
* @returns {object} Returns the dstDescriptor argument.
*/
function mergeComposable(dstDescriptor, srcComposable) {
const srcDescriptor = (srcComposable && srcComposable.compose) || srcComposable;
Iif (!isDescriptor(srcDescriptor)) return dstDescriptor;
const combineProperty = (propName, action) => {
if (!isObject(srcDescriptor[propName])) return;
if (!isObject(dstDescriptor[propName])) dstDescriptor[propName] = {};
action(dstDescriptor[propName], srcDescriptor[propName]);
};
combineProperty('methods', assign);
combineProperty('properties', assign);
combineProperty('deepProperties', merge);
combineProperty('propertyDescriptors', assign);
combineProperty('staticProperties', assign);
combineProperty('staticDeepProperties', merge);
combineProperty('staticPropertyDescriptors', assign);
combineProperty('configuration', assign);
combineProperty('deepConfiguration', merge);
if (Array.isArray(srcDescriptor.initializers)) {
dstDescriptor.initializers = srcDescriptor.initializers.reduce((result, init) => {
if (isFunction(init) && result.indexOf(init) < 0) {
result.push(init);
}
return result;
}, Array.isArray(dstDescriptor.initializers) ? dstDescriptor.initializers : []);
}
return dstDescriptor;
}
/**
* Given the list of composables (stamp descriptors and stamps) returns
* a new stamp (composable factory function).
* @param {...(object|Function)} [composables] The list of composables.
* @returns {Function} A new stamp (aka composable factory function).
*/
export default function compose() {
const descriptor = [this]
.concat(slice.call(arguments))
.filter(isObject)
.reduce(mergeComposable, {});
return createStamp(descriptor, compose);
}
|