diff --git a/packages/babel-helpers/src/helpers.js b/packages/babel-helpers/src/helpers.js index 23416e0022..8fb04a8953 100644 --- a/packages/babel-helpers/src/helpers.js +++ b/packages/babel-helpers/src/helpers.js @@ -1040,9 +1040,7 @@ helpers.initializerWarningHelper = helper("7.0.0-beta.0")` export default function _initializerWarningHelper(descriptor, context){ throw new Error( 'Decorating class property failed. Please ensure that ' + - 'proposal-class-properties is enabled and set to use loose mode. ' + - 'To use proposal-class-properties in spec mode with decorators, wait for ' + - 'the next major version of decorators in stage 2.' + 'proposal-class-properties is enabled and runs after the decorators transform.' ); } `; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/input.js new file mode 100644 index 0000000000..f939e63b3b --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/input.js @@ -0,0 +1,12 @@ +function dec() {} + +// Create a local function binding so babel has to change the name of the helper +function _defineProperty() {} + +class A { + @dec a; + + @dec b = 123; + + c = 456; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/options.json new file mode 100644 index 0000000000..6f03cd7aad --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["proposal-decorators", { "legacy": true }], + ["proposal-class-properties"], + "transform-classes" + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/output.js new file mode 100644 index 0000000000..641307b44d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/local-define-property/output.js @@ -0,0 +1,40 @@ +var _class, _descriptor, _descriptor2, _temp; + +function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperty2(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } + +function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and runs after the decorators transform.'); } + +function dec() {} // Create a local function binding so babel has to change the name of the helper + + +function _defineProperty() {} + +let A = (_class = (_temp = function A() { + "use strict"; + + _classCallCheck(this, A); + + _initializerDefineProperty(this, "a", _descriptor, this); + + _initializerDefineProperty(this, "b", _descriptor2, this); + + _defineProperty2(this, "c", 456); +}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "a", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: null +}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, "b", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: function () { + return 123; + } +})), _class); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/input.js new file mode 100644 index 0000000000..92298f2eb4 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/input.js @@ -0,0 +1,9 @@ +function dec() {} + +class A { + @dec a; + + @dec b = 123; + + c = 456; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/options.json new file mode 100644 index 0000000000..8c83198953 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["proposal-decorators", { "legacy": true }], + ["proposal-class-properties", { "loose": true }], + "transform-classes" + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/output.js new file mode 100644 index 0000000000..0a25e2b102 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/loose/output.js @@ -0,0 +1,35 @@ +var _class, _descriptor, _descriptor2, _temp; + +function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } + +function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and runs after the decorators transform.'); } + +function dec() {} + +let A = (_class = (_temp = function A() { + "use strict"; + + _classCallCheck(this, A); + + _initializerDefineProperty(this, "a", _descriptor, this); + + _initializerDefineProperty(this, "b", _descriptor2, this); + + this.c = 456; +}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "a", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: null +}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, "b", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: function () { + return 123; + } +})), _class); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/input.js new file mode 100644 index 0000000000..92298f2eb4 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/input.js @@ -0,0 +1,9 @@ +function dec() {} + +class A { + @dec a; + + @dec b = 123; + + c = 456; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/options.json new file mode 100644 index 0000000000..6f03cd7aad --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/options.json @@ -0,0 +1,7 @@ +{ + "plugins": [ + ["proposal-decorators", { "legacy": true }], + ["proposal-class-properties"], + "transform-classes" + ] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/output.js new file mode 100644 index 0000000000..11cce4ac92 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/decorators-legacy-interop/strict/output.js @@ -0,0 +1,37 @@ +var _class, _descriptor, _descriptor2, _temp; + +function _initializerDefineProperty(target, property, descriptor, context) { if (!descriptor) return; Object.defineProperty(target, property, { enumerable: descriptor.enumerable, configurable: descriptor.configurable, writable: descriptor.writable, value: descriptor.initializer ? descriptor.initializer.call(context) : void 0 }); } + +function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + +function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } + +function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } + +function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and runs after the decorators transform.'); } + +function dec() {} + +let A = (_class = (_temp = function A() { + "use strict"; + + _classCallCheck(this, A); + + _initializerDefineProperty(this, "a", _descriptor, this); + + _initializerDefineProperty(this, "b", _descriptor2, this); + + _defineProperty(this, "c", 456); +}, _temp), (_descriptor = _applyDecoratedDescriptor(_class.prototype, "a", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: null +}), _descriptor2 = _applyDecoratedDescriptor(_class.prototype, "b", [dec], { + configurable: true, + enumerable: true, + writable: true, + initializer: function () { + return 123; + } +})), _class); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js index 2cd978b722..9a71c17528 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/decorator-interop/output.js @@ -12,7 +12,7 @@ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _d function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } -function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.'); } +function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and runs after the decorators transform.'); } function dec() {} diff --git a/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js b/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js index d6160db14c..ca23bad248 100644 --- a/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js +++ b/packages/babel-plugin-proposal-decorators/src/transformer-legacy.js @@ -290,4 +290,24 @@ export default { ]), ); }, + + CallExpression(path, state) { + if (path.node.arguments.length !== 3) return; + if (!WARNING_CALLS.has(path.node.arguments[2])) return; + + // If the class properties plugin isn't enabled, this line will add an unused helper + // to the code. It's not ideal, but it's ok since the configuration is not valid anyway. + if (path.node.callee.name !== state.addHelper("defineProperty").name) { + return; + } + + path.replaceWith( + t.callExpression(state.addHelper("initializerDefineProperty"), [ + t.cloneNode(path.get("arguments")[0].node), + t.cloneNode(path.get("arguments")[1].node), + t.cloneNode(path.get("arguments.2.arguments")[0].node), + t.cloneNode(path.get("arguments.2.arguments")[1].node), + ]), + ); + }, }; diff --git a/packages/babel-plugin-transform-typescript/test/fixtures/class/abstract-class-decorated-parameter/output.mjs b/packages/babel-plugin-transform-typescript/test/fixtures/class/abstract-class-decorated-parameter/output.mjs index e8982550ef..58aa80d516 100644 --- a/packages/babel-plugin-transform-typescript/test/fixtures/class/abstract-class-decorated-parameter/output.mjs +++ b/packages/babel-plugin-transform-typescript/test/fixtures/class/abstract-class-decorated-parameter/output.mjs @@ -2,7 +2,7 @@ var _class, _descriptor; function _applyDecoratedDescriptor(target, property, decorators, descriptor, context) { var desc = {}; Object.keys(descriptor).forEach(function (key) { desc[key] = descriptor[key]; }); desc.enumerable = !!desc.enumerable; desc.configurable = !!desc.configurable; if ('value' in desc || desc.initializer) { desc.writable = true; } desc = decorators.slice().reverse().reduce(function (desc, decorator) { return decorator(target, property, desc) || desc; }, desc); if (context && desc.initializer !== void 0) { desc.value = desc.initializer ? desc.initializer.call(context) : void 0; desc.initializer = undefined; } if (desc.initializer === void 0) { Object.defineProperty(target, property, desc); desc = null; } return desc; } -function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and set to use loose mode. ' + 'To use proposal-class-properties in spec mode with decorators, wait for ' + 'the next major version of decorators in stage 2.'); } +function _initializerWarningHelper(descriptor, context) { throw new Error('Decorating class property failed. Please ensure that ' + 'proposal-class-properties is enabled and runs after the decorators transform.'); } import { observable } from 'mobx'; let Foo = (_class = class Foo {