diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 4c3c896fcc..79f5255096 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -1213,23 +1213,430 @@ helpers.decorate = helper("7.1.5")` */ + /*:: + + type Placements = { + static: Key[], + prototype: Key[], + own: Key[], + }; + + */ + // ClassDefinitionEvaluation (Steps 26-*) export default function _decorate( decorators /*: ClassDecorator[] */, factory /*: ClassFactory */, superClass /*: ?Class<*> */, + mixins /*: ?Array */, ) /*: Class<*> */ { + var api = _getDecoratorsApi(); + if (mixins) { + for (var i = 0; i < mixins.length; i++) { + api = mixins[i](api); + } + } + var r = factory(function initialize(O) { - _initializeInstanceElements(O, decorated.elements); + api.initializeInstanceElements(O, decorated.elements); }, superClass); - var decorated = _decorateClass( + var decorated = api.decorateClass( _coalesceClassElements(r.d.map(_createElementDescriptor)), decorators, ); - _initializeClassElements(r.F, decorated.elements); + api.initializeClassElements(r.F, decorated.elements); - return _runClassFinishers(r.F, decorated.finishers); + return api.runClassFinishers(r.F, decorated.finishers); + } + + function _getDecoratorsApi() { + _getDecoratorsApi = function() { + return api; + }; + + var api = { + elementsDefinitionOrder: [["method"], ["field"]], + + // InitializeInstanceElements + initializeInstanceElements: function( + /*::*/ O /*: C */, + elements /*: ElementDescriptor[] */, + ) { + ["method", "field"].forEach(function(kind) { + elements.forEach(function(element /*: ElementDescriptor */) { + if (element.kind === kind && element.placement === "own") { + this.defineClassElement(O, element); + } + }, this); + }, this); + }, + + // InitializeClassElements + initializeClassElements: function( + /*::*/ F /*: Class */, + elements /*: ElementDescriptor[] */, + ) { + var proto = F.prototype; + + ["method", "field"].forEach(function(kind) { + elements.forEach(function(element /*: ElementDescriptor */) { + var placement = element.placement; + if ( + element.kind === kind && + (placement === "static" || placement === "prototype") + ) { + var receiver = placement === "static" ? F : proto; + this.defineClassElement(receiver, element); + } + }, this); + }, this); + }, + + // DefineClassElement + defineClassElement: function( + /*::*/ receiver /*: C | Class */, + element /*: ElementDescriptor */, + ) { + var descriptor /*: PropertyDescriptor */ = element.descriptor; + if (element.kind === "field") { + var initializer = element.initializer; + descriptor = { + enumerable: descriptor.enumerable, + writable: descriptor.writable, + configurable: descriptor.configurable, + value: initializer === void 0 ? void 0 : initializer.call(receiver), + }; + } + Object.defineProperty(receiver, element.key, descriptor); + }, + + // DecorateClass + decorateClass: function( + elements /*: ElementDescriptor[] */, + decorators /*: ClassDecorator[] */, + ) /*: ElementsFinishers */ { + var newElements /*: ElementDescriptor[] */ = []; + var finishers /*: ClassFinisher[] */ = []; + var placements /*: Placements */ = { + static: [], + prototype: [], + own: [], + }; + + elements.forEach(function(element /*: ElementDescriptor */) { + this.addElementPlacement(element, placements); + }, this); + + elements.forEach(function(element /*: ElementDescriptor */) { + if (!_hasDecorators(element)) return newElements.push(element); + + var elementFinishersExtras /*: ElementFinishersExtras */ = this.decorateElement( + element, + placements, + ); + newElements.push(elementFinishersExtras.element); + newElements.push.apply(newElements, elementFinishersExtras.extras); + finishers.push.apply(finishers, elementFinishersExtras.finishers); + }, this); + + if (!decorators) { + return { elements: newElements, finishers: finishers }; + } + + var result /*: ElementsFinishers */ = this.decorateConstructor( + newElements, + decorators, + ); + finishers.push.apply(finishers, result.finishers); + result.finishers = finishers; + + return result; + }, + + // AddElementPlacement + addElementPlacement: function( + element /*: ElementDescriptor */, + placements /*: Placements */, + silent /*: boolean */, + ) { + var keys = placements[element.placement]; + if (!silent && keys.indexOf(element.key) !== -1) { + throw new TypeError("Duplicated element (" + element.key + ")"); + } + keys.push(element.key); + }, + + // DecorateElement + decorateElement: function( + element /*: ElementDescriptor */, + placements /*: Placements */, + ) /*: ElementFinishersExtras */ { + var extras /*: ElementDescriptor[] */ = []; + var finishers /*: ClassFinisher[] */ = []; + + for ( + var decorators = element.decorators, i = decorators.length - 1; + i >= 0; + i-- + ) { + // (inlined) RemoveElementPlacement + var keys = placements[element.placement]; + keys.splice(keys.indexOf(element.key), 1); + + var elementObject /*: ElementObjectInput */ = this.fromElementDescriptor( + element, + ); + var elementFinisherExtras /*: ElementFinisherExtras */ = this.toElementFinisherExtras( + (0, decorators[i])(elementObject) /*: ElementObjectOutput */ || + elementObject, + ); + + element = elementFinisherExtras.element; + this.addElementPlacement(element, placements); + + if (elementFinisherExtras.finisher) { + finishers.push(elementFinisherExtras.finisher); + } + + var newExtras /*: ElementDescriptor[] | void */ = + elementFinisherExtras.extras; + if (newExtras) { + for (var j = 0; j < newExtras.length; j++) { + this.addElementPlacement(newExtras[j], placements); + } + extras.push.apply(extras, newExtras); + } + } + + return { element: element, finishers: finishers, extras: extras }; + }, + + // DecorateConstructor + decorateConstructor: function( + elements /*: ElementDescriptor[] */, + decorators /*: ClassDecorator[] */, + ) /*: ElementsFinishers */ { + var finishers /*: ClassFinisher[] */ = []; + + for (var i = decorators.length - 1; i >= 0; i--) { + var obj /*: ClassObject */ = this.fromClassDescriptor(elements); + var elementsAndFinisher /*: ElementsFinisher */ = this.toClassDescriptor( + (0, decorators[i])(obj) /*: ClassObject */ || obj, + ); + + if (elementsAndFinisher.finisher !== undefined) { + finishers.push(elementsAndFinisher.finisher); + } + + if (elementsAndFinisher.elements !== undefined) { + elements = elementsAndFinisher.elements; + + for (var j = 0; j < elements.length - 1; j++) { + for (var k = j + 1; k < elements.length; k++) { + if ( + elements[j].key === elements[k].key && + elements[j].placement === elements[k].placement + ) { + throw new TypeError( + "Duplicated element (" + elements[j].key + ")", + ); + } + } + } + } + } + + return { elements: elements, finishers: finishers }; + }, + + // FromElementDescriptor + fromElementDescriptor: function( + element /*: ElementDescriptor */, + ) /*: ElementObject */ { + var obj /*: ElementObject */ = { + kind: element.kind, + key: element.key, + placement: element.placement, + descriptor: element.descriptor, + }; + + var desc = { + value: "Descriptor", + configurable: true, + }; + Object.defineProperty(obj, Symbol.toStringTag, desc); + + if (element.kind === "field") obj.initializer = element.initializer; + + return obj; + }, + + // ToElementDescriptors + toElementDescriptors: function( + elementObjects /*: ElementObject[] */, + ) /*: ElementDescriptor[] */ { + if (elementObjects === undefined) return; + return toArray(elementObjects).map(function(elementObject) { + var element = this.toElementDescriptor(elementObject); + this.disallowProperty(elementObject, "finisher", "An element descriptor"); + this.disallowProperty(elementObject, "extras", "An element descriptor"); + return element; + }, this); + }, + + // ToElementDescriptor + toElementDescriptor: function( + elementObject /*: ElementObject */, + ) /*: ElementDescriptor */ { + var kind = String(elementObject.kind); + if (kind !== "method" && kind !== "field") { + 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 + + '"', + ); + } + + var key = toPropertyKey(elementObject.key); + + var placement = String(elementObject.placement); + if ( + placement !== "static" && + placement !== "prototype" && + placement !== "own" + ) { + 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 + + '"', + ); + } + + var descriptor /*: PropertyDescriptor */ = elementObject.descriptor; + + this.disallowProperty(elementObject, "elements", "An element descriptor"); + + var element /*: ElementDescriptor */ = { + kind: kind, + key: key, + placement: placement, + descriptor: Object.assign({}, descriptor), + }; + + if (kind !== "field") { + this.disallowProperty(elementObject, "initializer", "A method descriptor"); + } else { + this.disallowProperty( + descriptor, + "get", + "The property descriptor of a field descriptor", + ); + this.disallowProperty( + descriptor, + "set", + "The property descriptor of a field descriptor", + ); + this.disallowProperty( + descriptor, + "value", + "The property descriptor of a field descriptor", + ); + + element.initializer = elementObject.initializer; + } + + return element; + }, + + toElementFinisherExtras: function( + elementObject /*: ElementObject */, + ) /*: ElementFinisherExtras */ { + var element /*: ElementDescriptor */ = this.toElementDescriptor( + elementObject, + ); + var finisher /*: ClassFinisher */ = _optionalCallableProperty( + elementObject, + "finisher", + ); + var extras /*: ElementDescriptors[] */ = this.toElementDescriptors( + elementObject.extras, + ); + + return { element: element, finisher: finisher, extras: extras }; + }, + + // FromClassDescriptor + fromClassDescriptor: function( + elements /*: ElementDescriptor[] */, + ) /*: ClassObject */ { + var obj = { + kind: "class", + elements: elements.map(this.fromElementDescriptor, this), + }; + + var desc = { value: "Descriptor", configurable: true }; + Object.defineProperty(obj, Symbol.toStringTag, desc); + + return obj; + }, + + // ToClassDescriptor + toClassDescriptor: function( + obj /*: ClassObject */, + ) /*: ElementsFinisher */ { + var kind = String(obj.kind); + if (kind !== "class") { + throw new TypeError( + 'A class descriptor\\'s .kind property must be "class", but a decorator' + + ' created a class descriptor with .kind "' + + kind + + '"', + ); + } + + this.disallowProperty(obj, "key", "A class descriptor"); + this.disallowProperty(obj, "placement", "A class descriptor"); + this.disallowProperty(obj, "descriptor", "A class descriptor"); + this.disallowProperty(obj, "initializer", "A class descriptor"); + this.disallowProperty(obj, "extras", "A class descriptor"); + + var finisher = _optionalCallableProperty(obj, "finisher"); + var elements = this.toElementDescriptors(obj.elements); + + return { elements: elements, finisher: finisher }; + }, + + // RunClassFinishers + runClassFinishers: function( + constructor /*: Class<*> */, + finishers /*: ClassFinisher[] */, + ) /*: Class<*> */ { + for (var i = 0; i < finishers.length; i++) { + var newConstructor /*: ?Class<*> */ = (0, finishers[i])(constructor); + if (newConstructor !== undefined) { + // NOTE: This should check if IsConstructor(newConstructor) is false. + if (typeof newConstructor !== "function") { + throw new TypeError("Finishers must return a constructor."); + } + constructor = newConstructor; + } + } + return constructor; + }, + + disallowProperty: function(obj, name, objectType) { + if (obj[name] !== undefined) { + throw new TypeError(objectType + " can't have a ." + name + " property."); + } + } + }; + + return api; } // ClassElementEvaluation @@ -1264,8 +1671,8 @@ helpers.decorate = helper("7.1.5")` placement: def.static ? "static" : def.kind === "field" - ? "own" - : "prototype", + ? "own" + : "prototype", descriptor: descriptor, }; if (def.decorators) element.decorators = def.decorators; @@ -1292,7 +1699,9 @@ helpers.decorate = helper("7.1.5")` ) /*: ElementDescriptor[] */ { var newElements /*: ElementDescriptor[] */ = []; - var isSameElement = function(other /*: ElementDescriptor */) /*: boolean */ { + var isSameElement = function( + other /*: ElementDescriptor */, + ) /*: boolean */ { return ( other.kind === "method" && other.key === element.key && @@ -1351,365 +1760,6 @@ helpers.decorate = helper("7.1.5")` ); } - // InitializeClassElements - function _initializeClassElements /*::*/( - F /*: Class */, - elements /*: ElementDescriptor[] */, - ) { - var proto = F.prototype; - - ["method", "field"].forEach(function(kind) { - elements.forEach(function(element /*: ElementDescriptor */) { - var placement = element.placement; - if ( - element.kind === kind && - (placement === "static" || placement === "prototype") - ) { - var receiver = placement === "static" ? F : proto; - _defineClassElement(receiver, element); - } - }); - }); - } - - // InitializeInstanceElements - function _initializeInstanceElements /*::*/( - O /*: C */, - elements /*: ElementDescriptor[] */, - ) { - ["method", "field"].forEach(function(kind) { - elements.forEach(function(element /*: ElementDescriptor */) { - if (element.kind === kind && element.placement === "own") { - _defineClassElement(O, element); - } - }); - }); - } - - // DefineClassElement - function _defineClassElement /*::*/( - receiver /*: C | Class */, - element /*: ElementDescriptor */, - ) { - var descriptor /*: PropertyDescriptor */ = element.descriptor; - if (element.kind === "field") { - var initializer = element.initializer; - descriptor = { - enumerable: descriptor.enumerable, - writable: descriptor.writable, - configurable: descriptor.configurable, - value: initializer === void 0 ? void 0 : initializer.call(receiver), - }; - } - Object.defineProperty(receiver, element.key, descriptor); - } - - /*:: - - type Placements = { - static: Key[], - prototype: Key[], - own: Key[], - }; - - */ - - // DecorateClass - function _decorateClass( - elements /*: ElementDescriptor[] */, - decorators /*: ClassDecorator[] */, - ) /*: ElementsFinishers */ { - var newElements /*: ElementDescriptor[] */ = []; - var finishers /*: ClassFinisher[] */ = []; - var placements /*: Placements */ = { static: [], prototype: [], own: [] }; - - elements.forEach(function(element /*: ElementDescriptor */) { - _addElementPlacement(element, placements); - }); - - elements.forEach(function(element /*: ElementDescriptor */) { - if (!_hasDecorators(element)) return newElements.push(element); - - var elementFinishersExtras /*: ElementFinishersExtras */ = _decorateElement( - element, - placements, - ); - newElements.push(elementFinishersExtras.element); - newElements.push.apply(newElements, elementFinishersExtras.extras); - finishers.push.apply(finishers, elementFinishersExtras.finishers); - }); - - if (!decorators) { - return { elements: newElements, finishers: finishers }; - } - - var result /*: ElementsFinishers */ = _decorateConstructor( - newElements, - decorators, - ); - finishers.push.apply(finishers, result.finishers); - result.finishers = finishers; - - return result; - } - - // AddElementPlacement - function _addElementPlacement( - element /*: ElementDescriptor */, - placements /*: Placements */, - silent /*: boolean */, - ) { - var keys = placements[element.placement]; - if (!silent && keys.indexOf(element.key) !== -1) { - throw new TypeError("Duplicated element (" + element.key + ")"); - } - keys.push(element.key); - } - - // DecorateElement - function _decorateElement( - element /*: ElementDescriptor */, - placements /*: Placements */, - ) /*: ElementFinishersExtras */ { - var extras /*: ElementDescriptor[] */ = []; - var finishers /*: ClassFinisher[] */ = []; - - for ( - var decorators = element.decorators, i = decorators.length - 1; - i >= 0; - i-- - ) { - // (inlined) RemoveElementPlacement - var keys = placements[element.placement]; - keys.splice(keys.indexOf(element.key), 1); - - var elementObject /*: ElementObjectInput */ = _fromElementDescriptor( - element, - ); - var elementFinisherExtras /*: ElementFinisherExtras */ = _toElementFinisherExtras( - (0, decorators[i])(elementObject) /*: ElementObjectOutput */ || - elementObject, - ); - - element = elementFinisherExtras.element; - _addElementPlacement(element, placements); - - if (elementFinisherExtras.finisher) { - finishers.push(elementFinisherExtras.finisher); - } - - var newExtras /*: ElementDescriptor[] | void */ = - elementFinisherExtras.extras; - if (newExtras) { - for (var j = 0; j < newExtras.length; j++) { - _addElementPlacement(newExtras[j], placements); - } - extras.push.apply(extras, newExtras); - } - } - - return { element: element, finishers: finishers, extras: extras }; - } - - // DecorateConstructor - function _decorateConstructor( - elements /*: ElementDescriptor[] */, - decorators /*: ClassDecorator[] */, - ) /*: ElementsFinishers */ { - var finishers /*: ClassFinisher[] */ = []; - - for (var i = decorators.length - 1; i >= 0; i--) { - var obj /*: ClassObject */ = _fromClassDescriptor(elements); - var elementsAndFinisher /*: ElementsFinisher */ = _toClassDescriptor( - (0, decorators[i])(obj) /*: ClassObject */ || obj, - ); - - if (elementsAndFinisher.finisher !== undefined) { - finishers.push(elementsAndFinisher.finisher); - } - - if (elementsAndFinisher.elements !== undefined) { - elements = elementsAndFinisher.elements; - - for (var j = 0; j < elements.length - 1; j++) { - for (var k = j + 1; k < elements.length; k++) { - if ( - elements[j].key === elements[k].key && - elements[j].placement === elements[k].placement - ) { - throw new TypeError("Duplicated element (" + elements[j].key + ")"); - } - } - } - } - } - - return { elements: elements, finishers: finishers }; - } - - // FromElementDescriptor - function _fromElementDescriptor( - element /*: ElementDescriptor */, - ) /*: ElementObject */ { - var obj /*: ElementObject */ = { - kind: element.kind, - key: element.key, - placement: element.placement, - descriptor: element.descriptor, - }; - - var desc = { - value: "Descriptor", - configurable: true, - }; - Object.defineProperty(obj, Symbol.toStringTag, desc); - - if (element.kind === "field") obj.initializer = element.initializer; - - return obj; - } - - // ToElementDescriptors - function _toElementDescriptors( - elementObjects /*: ElementObject[] */, - ) /*: ElementDescriptor[] */ { - if (elementObjects === undefined) return; - return toArray(elementObjects).map(function(elementObject) { - var element = _toElementDescriptor(elementObject); - _disallowProperty(elementObject, "finisher", "An element descriptor"); - _disallowProperty(elementObject, "extras", "An element descriptor"); - return element; - }); - } - - // ToElementDescriptor - function _toElementDescriptor( - elementObject /*: ElementObject */, - ) /*: ElementDescriptor */ { - var kind = String(elementObject.kind); - if (kind !== "method" && kind !== "field") { - 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 + - '"', - ); - } - - var key = toPropertyKey(elementObject.key); - - var placement = String(elementObject.placement); - if ( - placement !== "static" && - placement !== "prototype" && - placement !== "own" - ) { - 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 + - '"', - ); - } - - var descriptor /*: PropertyDescriptor */ = elementObject.descriptor; - - _disallowProperty(elementObject, "elements", "An element descriptor"); - - var element /*: ElementDescriptor */ = { - kind: kind, - key: key, - placement: placement, - descriptor: Object.assign({}, descriptor), - }; - - if (kind !== "field") { - _disallowProperty(elementObject, "initializer", "A method descriptor"); - } else { - _disallowProperty( - descriptor, - "get", - "The property descriptor of a field descriptor", - ); - _disallowProperty( - descriptor, - "set", - "The property descriptor of a field descriptor", - ); - _disallowProperty( - descriptor, - "value", - "The property descriptor of a field descriptor", - ); - - element.initializer = elementObject.initializer; - } - - return element; - } - - function _toElementFinisherExtras( - elementObject /*: ElementObject */, - ) /*: ElementFinisherExtras */ { - var element /*: ElementDescriptor */ = _toElementDescriptor(elementObject); - var finisher /*: ClassFinisher */ = _optionalCallableProperty( - elementObject, - "finisher", - ); - var extras /*: ElementDescriptors[] */ = _toElementDescriptors( - elementObject.extras, - ); - - return { element: element, finisher: finisher, extras: extras }; - } - - // FromClassDescriptor - function _fromClassDescriptor( - elements /*: ElementDescriptor[] */, - ) /*: ClassObject */ { - var obj = { - kind: "class", - elements: elements.map(_fromElementDescriptor), - }; - - var desc = { value: "Descriptor", configurable: true }; - Object.defineProperty(obj, Symbol.toStringTag, desc); - - return obj; - } - - // ToClassDescriptor - function _toClassDescriptor(obj /*: ClassObject */) /*: ElementsFinisher */ { - var kind = String(obj.kind); - if (kind !== "class") { - throw new TypeError( - 'A class descriptor\\'s .kind property must be "class", but a decorator' + - ' created a class descriptor with .kind "' + - kind + - '"', - ); - } - - _disallowProperty(obj, "key", "A class descriptor"); - _disallowProperty(obj, "placement", "A class descriptor"); - _disallowProperty(obj, "descriptor", "A class descriptor"); - _disallowProperty(obj, "initializer", "A class descriptor"); - _disallowProperty(obj, "extras", "A class descriptor"); - - var finisher = _optionalCallableProperty(obj, "finisher"); - var elements = _toElementDescriptors(obj.elements); - - return { elements: elements, finisher: finisher }; - } - - function _disallowProperty(obj, name, objectType) { - if (obj[name] !== undefined) { - throw new TypeError(objectType + " can't have a ." + name + " property."); - } - } - function _optionalCallableProperty /*::*/( obj /*: T */, name /*: $Keys */, @@ -1721,23 +1771,6 @@ helpers.decorate = helper("7.1.5")` return value; } - // RunClassFinishers - function _runClassFinishers( - constructor /*: Class<*> */, - finishers /*: ClassFinisher[] */, - ) /*: Class<*> */ { - for (var i = 0; i < finishers.length; i++) { - var newConstructor /*: ?Class<*> */ = (0, finishers[i])(constructor); - if (newConstructor !== undefined) { - // NOTE: This should check if IsConstructor(newConstructor) is false. - if (typeof newConstructor !== "function") { - throw new TypeError("Finishers must return a constructor."); - } - constructor = newConstructor; - } - } - return constructor; - } `; helpers.classPrivateMethodGet = helper("7.1.6")`