[d24f17c] | 1 | "use strict";
|
---|
| 2 | Object.defineProperty(exports, "__esModule", { value: true });
|
---|
| 3 | exports.flatten = exports.unique = exports.hardMixProtos = exports.nearestCommonProto = exports.protoChain = exports.copyProps = void 0;
|
---|
| 4 | /**
|
---|
| 5 | * Utility function that works like `Object.apply`, but copies getters and setters properly as well. Additionally gives
|
---|
| 6 | * the option to exclude properties by name.
|
---|
| 7 | */
|
---|
| 8 | const copyProps = (dest, src, exclude = []) => {
|
---|
| 9 | const props = Object.getOwnPropertyDescriptors(src);
|
---|
| 10 | for (let prop of exclude)
|
---|
| 11 | delete props[prop];
|
---|
| 12 | Object.defineProperties(dest, props);
|
---|
| 13 | };
|
---|
| 14 | exports.copyProps = copyProps;
|
---|
| 15 | /**
|
---|
| 16 | * Returns the full chain of prototypes up until Object.prototype given a starting object. The order of prototypes will
|
---|
| 17 | * be closest to farthest in the chain.
|
---|
| 18 | */
|
---|
| 19 | const protoChain = (obj, currentChain = [obj]) => {
|
---|
| 20 | const proto = Object.getPrototypeOf(obj);
|
---|
| 21 | if (proto === null)
|
---|
| 22 | return currentChain;
|
---|
| 23 | return (0, exports.protoChain)(proto, [...currentChain, proto]);
|
---|
| 24 | };
|
---|
| 25 | exports.protoChain = protoChain;
|
---|
| 26 | /**
|
---|
| 27 | * Identifies the nearest ancestor common to all the given objects in their prototype chains. For most unrelated
|
---|
| 28 | * objects, this function should return Object.prototype.
|
---|
| 29 | */
|
---|
| 30 | const nearestCommonProto = (...objs) => {
|
---|
| 31 | if (objs.length === 0)
|
---|
| 32 | return undefined;
|
---|
| 33 | let commonProto = undefined;
|
---|
| 34 | const protoChains = objs.map(obj => (0, exports.protoChain)(obj));
|
---|
| 35 | while (protoChains.every(protoChain => protoChain.length > 0)) {
|
---|
| 36 | const protos = protoChains.map(protoChain => protoChain.pop());
|
---|
| 37 | const potentialCommonProto = protos[0];
|
---|
| 38 | if (protos.every(proto => proto === potentialCommonProto))
|
---|
| 39 | commonProto = potentialCommonProto;
|
---|
| 40 | else
|
---|
| 41 | break;
|
---|
| 42 | }
|
---|
| 43 | return commonProto;
|
---|
| 44 | };
|
---|
| 45 | exports.nearestCommonProto = nearestCommonProto;
|
---|
| 46 | /**
|
---|
| 47 | * Creates a new prototype object that is a mixture of the given prototypes. The mixing is achieved by first
|
---|
| 48 | * identifying the nearest common ancestor and using it as the prototype for a new object. Then all properties/methods
|
---|
| 49 | * downstream of this prototype (ONLY downstream) are copied into the new object.
|
---|
| 50 | *
|
---|
| 51 | * The resulting prototype is more performant than softMixProtos(...), as well as ES5 compatible. However, it's not as
|
---|
| 52 | * flexible as updates to the source prototypes aren't captured by the mixed result. See softMixProtos for why you may
|
---|
| 53 | * want to use that instead.
|
---|
| 54 | */
|
---|
| 55 | const hardMixProtos = (ingredients, constructor, exclude = []) => {
|
---|
| 56 | var _a;
|
---|
| 57 | const base = (_a = (0, exports.nearestCommonProto)(...ingredients)) !== null && _a !== void 0 ? _a : Object.prototype;
|
---|
| 58 | const mixedProto = Object.create(base);
|
---|
| 59 | // Keeps track of prototypes we've already visited to avoid copying the same properties multiple times. We init the
|
---|
| 60 | // list with the proto chain below the nearest common ancestor because we don't want any of those methods mixed in
|
---|
| 61 | // when they will already be accessible via prototype access.
|
---|
| 62 | const visitedProtos = (0, exports.protoChain)(base);
|
---|
| 63 | for (let prototype of ingredients) {
|
---|
| 64 | let protos = (0, exports.protoChain)(prototype);
|
---|
| 65 | // Apply the prototype chain in reverse order so that old methods don't override newer ones.
|
---|
| 66 | for (let i = protos.length - 1; i >= 0; i--) {
|
---|
| 67 | let newProto = protos[i];
|
---|
| 68 | if (visitedProtos.indexOf(newProto) === -1) {
|
---|
| 69 | (0, exports.copyProps)(mixedProto, newProto, ['constructor', ...exclude]);
|
---|
| 70 | visitedProtos.push(newProto);
|
---|
| 71 | }
|
---|
| 72 | }
|
---|
| 73 | }
|
---|
| 74 | mixedProto.constructor = constructor;
|
---|
| 75 | return mixedProto;
|
---|
| 76 | };
|
---|
| 77 | exports.hardMixProtos = hardMixProtos;
|
---|
| 78 | const unique = (arr) => arr.filter((e, i) => arr.indexOf(e) == i);
|
---|
| 79 | exports.unique = unique;
|
---|
| 80 | const flatten = (arr) => arr.length === 0
|
---|
| 81 | ? []
|
---|
| 82 | : arr.length === 1
|
---|
| 83 | ? arr[0]
|
---|
| 84 | : arr.reduce((a1, a2) => [...a1, ...a2]);
|
---|
| 85 | exports.flatten = flatten;
|
---|