simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

decorate.js (13341B)


      1 import toArray from "./toArray.js";
      2 import toPropertyKey from "./toPropertyKey.js";
      3 export default function _decorate(decorators, factory, superClass, mixins) {
      4   var api = _getDecoratorsApi();
      5 
      6   if (mixins) {
      7     for (var i = 0; i < mixins.length; i++) {
      8       api = mixins[i](api);
      9     }
     10   }
     11 
     12   var r = factory(function initialize(O) {
     13     api.initializeInstanceElements(O, decorated.elements);
     14   }, superClass);
     15   var decorated = api.decorateClass(_coalesceClassElements(r.d.map(_createElementDescriptor)), decorators);
     16   api.initializeClassElements(r.F, decorated.elements);
     17   return api.runClassFinishers(r.F, decorated.finishers);
     18 }
     19 
     20 function _getDecoratorsApi() {
     21   _getDecoratorsApi = function _getDecoratorsApi() {
     22     return api;
     23   };
     24 
     25   var api = {
     26     elementsDefinitionOrder: [["method"], ["field"]],
     27     initializeInstanceElements: function initializeInstanceElements(O, elements) {
     28       ["method", "field"].forEach(function (kind) {
     29         elements.forEach(function (element) {
     30           if (element.kind === kind && element.placement === "own") {
     31             this.defineClassElement(O, element);
     32           }
     33         }, this);
     34       }, this);
     35     },
     36     initializeClassElements: function initializeClassElements(F, elements) {
     37       var proto = F.prototype;
     38       ["method", "field"].forEach(function (kind) {
     39         elements.forEach(function (element) {
     40           var placement = element.placement;
     41 
     42           if (element.kind === kind && (placement === "static" || placement === "prototype")) {
     43             var receiver = placement === "static" ? F : proto;
     44             this.defineClassElement(receiver, element);
     45           }
     46         }, this);
     47       }, this);
     48     },
     49     defineClassElement: function defineClassElement(receiver, element) {
     50       var descriptor = element.descriptor;
     51 
     52       if (element.kind === "field") {
     53         var initializer = element.initializer;
     54         descriptor = {
     55           enumerable: descriptor.enumerable,
     56           writable: descriptor.writable,
     57           configurable: descriptor.configurable,
     58           value: initializer === void 0 ? void 0 : initializer.call(receiver)
     59         };
     60       }
     61 
     62       Object.defineProperty(receiver, element.key, descriptor);
     63     },
     64     decorateClass: function decorateClass(elements, decorators) {
     65       var newElements = [];
     66       var finishers = [];
     67       var placements = {
     68         "static": [],
     69         prototype: [],
     70         own: []
     71       };
     72       elements.forEach(function (element) {
     73         this.addElementPlacement(element, placements);
     74       }, this);
     75       elements.forEach(function (element) {
     76         if (!_hasDecorators(element)) return newElements.push(element);
     77         var elementFinishersExtras = this.decorateElement(element, placements);
     78         newElements.push(elementFinishersExtras.element);
     79         newElements.push.apply(newElements, elementFinishersExtras.extras);
     80         finishers.push.apply(finishers, elementFinishersExtras.finishers);
     81       }, this);
     82 
     83       if (!decorators) {
     84         return {
     85           elements: newElements,
     86           finishers: finishers
     87         };
     88       }
     89 
     90       var result = this.decorateConstructor(newElements, decorators);
     91       finishers.push.apply(finishers, result.finishers);
     92       result.finishers = finishers;
     93       return result;
     94     },
     95     addElementPlacement: function addElementPlacement(element, placements, silent) {
     96       var keys = placements[element.placement];
     97 
     98       if (!silent && keys.indexOf(element.key) !== -1) {
     99         throw new TypeError("Duplicated element (" + element.key + ")");
    100       }
    101 
    102       keys.push(element.key);
    103     },
    104     decorateElement: function decorateElement(element, placements) {
    105       var extras = [];
    106       var finishers = [];
    107 
    108       for (var decorators = element.decorators, i = decorators.length - 1; i >= 0; i--) {
    109         var keys = placements[element.placement];
    110         keys.splice(keys.indexOf(element.key), 1);
    111         var elementObject = this.fromElementDescriptor(element);
    112         var elementFinisherExtras = this.toElementFinisherExtras((0, decorators[i])(elementObject) || elementObject);
    113         element = elementFinisherExtras.element;
    114         this.addElementPlacement(element, placements);
    115 
    116         if (elementFinisherExtras.finisher) {
    117           finishers.push(elementFinisherExtras.finisher);
    118         }
    119 
    120         var newExtras = elementFinisherExtras.extras;
    121 
    122         if (newExtras) {
    123           for (var j = 0; j < newExtras.length; j++) {
    124             this.addElementPlacement(newExtras[j], placements);
    125           }
    126 
    127           extras.push.apply(extras, newExtras);
    128         }
    129       }
    130 
    131       return {
    132         element: element,
    133         finishers: finishers,
    134         extras: extras
    135       };
    136     },
    137     decorateConstructor: function decorateConstructor(elements, decorators) {
    138       var finishers = [];
    139 
    140       for (var i = decorators.length - 1; i >= 0; i--) {
    141         var obj = this.fromClassDescriptor(elements);
    142         var elementsAndFinisher = this.toClassDescriptor((0, decorators[i])(obj) || obj);
    143 
    144         if (elementsAndFinisher.finisher !== undefined) {
    145           finishers.push(elementsAndFinisher.finisher);
    146         }
    147 
    148         if (elementsAndFinisher.elements !== undefined) {
    149           elements = elementsAndFinisher.elements;
    150 
    151           for (var j = 0; j < elements.length - 1; j++) {
    152             for (var k = j + 1; k < elements.length; k++) {
    153               if (elements[j].key === elements[k].key && elements[j].placement === elements[k].placement) {
    154                 throw new TypeError("Duplicated element (" + elements[j].key + ")");
    155               }
    156             }
    157           }
    158         }
    159       }
    160 
    161       return {
    162         elements: elements,
    163         finishers: finishers
    164       };
    165     },
    166     fromElementDescriptor: function fromElementDescriptor(element) {
    167       var obj = {
    168         kind: element.kind,
    169         key: element.key,
    170         placement: element.placement,
    171         descriptor: element.descriptor
    172       };
    173       var desc = {
    174         value: "Descriptor",
    175         configurable: true
    176       };
    177       Object.defineProperty(obj, Symbol.toStringTag, desc);
    178       if (element.kind === "field") obj.initializer = element.initializer;
    179       return obj;
    180     },
    181     toElementDescriptors: function toElementDescriptors(elementObjects) {
    182       if (elementObjects === undefined) return;
    183       return toArray(elementObjects).map(function (elementObject) {
    184         var element = this.toElementDescriptor(elementObject);
    185         this.disallowProperty(elementObject, "finisher", "An element descriptor");
    186         this.disallowProperty(elementObject, "extras", "An element descriptor");
    187         return element;
    188       }, this);
    189     },
    190     toElementDescriptor: function toElementDescriptor(elementObject) {
    191       var kind = String(elementObject.kind);
    192 
    193       if (kind !== "method" && kind !== "field") {
    194         throw new TypeError('An element descriptor\'s .kind property must be either "method" or' + ' "field", but a decorator created an element descriptor with' + ' .kind "' + kind + '"');
    195       }
    196 
    197       var key = toPropertyKey(elementObject.key);
    198       var placement = String(elementObject.placement);
    199 
    200       if (placement !== "static" && placement !== "prototype" && placement !== "own") {
    201         throw new TypeError('An element descriptor\'s .placement property must be one of "static",' + ' "prototype" or "own", but a decorator created an element descriptor' + ' with .placement "' + placement + '"');
    202       }
    203 
    204       var descriptor = elementObject.descriptor;
    205       this.disallowProperty(elementObject, "elements", "An element descriptor");
    206       var element = {
    207         kind: kind,
    208         key: key,
    209         placement: placement,
    210         descriptor: Object.assign({}, descriptor)
    211       };
    212 
    213       if (kind !== "field") {
    214         this.disallowProperty(elementObject, "initializer", "A method descriptor");
    215       } else {
    216         this.disallowProperty(descriptor, "get", "The property descriptor of a field descriptor");
    217         this.disallowProperty(descriptor, "set", "The property descriptor of a field descriptor");
    218         this.disallowProperty(descriptor, "value", "The property descriptor of a field descriptor");
    219         element.initializer = elementObject.initializer;
    220       }
    221 
    222       return element;
    223     },
    224     toElementFinisherExtras: function toElementFinisherExtras(elementObject) {
    225       var element = this.toElementDescriptor(elementObject);
    226 
    227       var finisher = _optionalCallableProperty(elementObject, "finisher");
    228 
    229       var extras = this.toElementDescriptors(elementObject.extras);
    230       return {
    231         element: element,
    232         finisher: finisher,
    233         extras: extras
    234       };
    235     },
    236     fromClassDescriptor: function fromClassDescriptor(elements) {
    237       var obj = {
    238         kind: "class",
    239         elements: elements.map(this.fromElementDescriptor, this)
    240       };
    241       var desc = {
    242         value: "Descriptor",
    243         configurable: true
    244       };
    245       Object.defineProperty(obj, Symbol.toStringTag, desc);
    246       return obj;
    247     },
    248     toClassDescriptor: function toClassDescriptor(obj) {
    249       var kind = String(obj.kind);
    250 
    251       if (kind !== "class") {
    252         throw new TypeError('A class descriptor\'s .kind property must be "class", but a decorator' + ' created a class descriptor with .kind "' + kind + '"');
    253       }
    254 
    255       this.disallowProperty(obj, "key", "A class descriptor");
    256       this.disallowProperty(obj, "placement", "A class descriptor");
    257       this.disallowProperty(obj, "descriptor", "A class descriptor");
    258       this.disallowProperty(obj, "initializer", "A class descriptor");
    259       this.disallowProperty(obj, "extras", "A class descriptor");
    260 
    261       var finisher = _optionalCallableProperty(obj, "finisher");
    262 
    263       var elements = this.toElementDescriptors(obj.elements);
    264       return {
    265         elements: elements,
    266         finisher: finisher
    267       };
    268     },
    269     runClassFinishers: function runClassFinishers(constructor, finishers) {
    270       for (var i = 0; i < finishers.length; i++) {
    271         var newConstructor = (0, finishers[i])(constructor);
    272 
    273         if (newConstructor !== undefined) {
    274           if (typeof newConstructor !== "function") {
    275             throw new TypeError("Finishers must return a constructor.");
    276           }
    277 
    278           constructor = newConstructor;
    279         }
    280       }
    281 
    282       return constructor;
    283     },
    284     disallowProperty: function disallowProperty(obj, name, objectType) {
    285       if (obj[name] !== undefined) {
    286         throw new TypeError(objectType + " can't have a ." + name + " property.");
    287       }
    288     }
    289   };
    290   return api;
    291 }
    292 
    293 function _createElementDescriptor(def) {
    294   var key = toPropertyKey(def.key);
    295   var descriptor;
    296 
    297   if (def.kind === "method") {
    298     descriptor = {
    299       value: def.value,
    300       writable: true,
    301       configurable: true,
    302       enumerable: false
    303     };
    304   } else if (def.kind === "get") {
    305     descriptor = {
    306       get: def.value,
    307       configurable: true,
    308       enumerable: false
    309     };
    310   } else if (def.kind === "set") {
    311     descriptor = {
    312       set: def.value,
    313       configurable: true,
    314       enumerable: false
    315     };
    316   } else if (def.kind === "field") {
    317     descriptor = {
    318       configurable: true,
    319       writable: true,
    320       enumerable: true
    321     };
    322   }
    323 
    324   var element = {
    325     kind: def.kind === "field" ? "field" : "method",
    326     key: key,
    327     placement: def["static"] ? "static" : def.kind === "field" ? "own" : "prototype",
    328     descriptor: descriptor
    329   };
    330   if (def.decorators) element.decorators = def.decorators;
    331   if (def.kind === "field") element.initializer = def.value;
    332   return element;
    333 }
    334 
    335 function _coalesceGetterSetter(element, other) {
    336   if (element.descriptor.get !== undefined) {
    337     other.descriptor.get = element.descriptor.get;
    338   } else {
    339     other.descriptor.set = element.descriptor.set;
    340   }
    341 }
    342 
    343 function _coalesceClassElements(elements) {
    344   var newElements = [];
    345 
    346   var isSameElement = function isSameElement(other) {
    347     return other.kind === "method" && other.key === element.key && other.placement === element.placement;
    348   };
    349 
    350   for (var i = 0; i < elements.length; i++) {
    351     var element = elements[i];
    352     var other;
    353 
    354     if (element.kind === "method" && (other = newElements.find(isSameElement))) {
    355       if (_isDataDescriptor(element.descriptor) || _isDataDescriptor(other.descriptor)) {
    356         if (_hasDecorators(element) || _hasDecorators(other)) {
    357           throw new ReferenceError("Duplicated methods (" + element.key + ") can't be decorated.");
    358         }
    359 
    360         other.descriptor = element.descriptor;
    361       } else {
    362         if (_hasDecorators(element)) {
    363           if (_hasDecorators(other)) {
    364             throw new ReferenceError("Decorators can't be placed on different accessors with for " + "the same property (" + element.key + ").");
    365           }
    366 
    367           other.decorators = element.decorators;
    368         }
    369 
    370         _coalesceGetterSetter(element, other);
    371       }
    372     } else {
    373       newElements.push(element);
    374     }
    375   }
    376 
    377   return newElements;
    378 }
    379 
    380 function _hasDecorators(element) {
    381   return element.decorators && element.decorators.length;
    382 }
    383 
    384 function _isDataDescriptor(desc) {
    385   return desc !== undefined && !(desc.value === undefined && desc.writable === undefined);
    386 }
    387 
    388 function _optionalCallableProperty(obj, name) {
    389   var value = obj[name];
    390 
    391   if (value !== undefined && typeof value !== "function") {
    392     throw new TypeError("Expected '" + name + "' to be a function");
    393   }
    394 
    395   return value;
    396 }