diff --git a/packages/babel-helper-create-class-features-plugin/src/fields.js b/packages/babel-helper-create-class-features-plugin/src/fields.js index e12514786b..de121f9cf5 100644 --- a/packages/babel-helper-create-class-features-plugin/src/fields.js +++ b/packages/babel-helper-create-class-features-plugin/src/fields.js @@ -1,5 +1,7 @@ import { template, traverse, types as t } from "@babel/core"; -import { environmentVisitor } from "@babel/helper-replace-supers"; +import ReplaceSupers, { + environmentVisitor, +} from "@babel/helper-replace-supers"; import memberExpressionToFunctions from "@babel/helper-member-expression-to-functions"; import optimiseCall from "@babel/helper-optimise-call-expression"; @@ -445,8 +447,39 @@ function buildPrivateInstanceMethodDeclaration(prop, privateNamesMap) { ]); } +const thisContextVisitor = traverse.visitors.merge([ + { + ThisExpression(path, state) { + state.needsClassRef = true; + path.replaceWith(t.cloneNode(state.classRef)); + }, + }, + environmentVisitor, +]); + +function replaceThisContext(path, ref, superRef, file, loose) { + const state = { classRef: ref, needsClassRef: false }; + + const replacer = new ReplaceSupers({ + methodPath: path, + isLoose: loose, + superRef, + file, + getObjectRef() { + state.needsClassRef = true; + return ref; + }, + }); + replacer.isStatic = true; + replacer.replace(); + + path.traverse(thisContextVisitor, state); + return state.needsClassRef; +} + export function buildFieldsInitNodes( ref, + superRef, props, privateNamesMap, state, @@ -464,6 +497,11 @@ export function buildFieldsInitNodes( const isField = prop.isProperty(); const isMethod = !isField; + if (isStatic && isField) { + const replaced = replaceThisContext(prop, ref, superRef, state, loose); + needsClassRef = needsClassRef || replaced; + } + switch (true) { case isStatic && isPrivate && isField && loose: needsClassRef = true; diff --git a/packages/babel-helper-create-class-features-plugin/src/index.js b/packages/babel-helper-create-class-features-plugin/src/index.js index dacd5fa00c..760be4af96 100644 --- a/packages/babel-helper-create-class-features-plugin/src/index.js +++ b/packages/babel-helper-create-class-features-plugin/src/index.js @@ -159,6 +159,7 @@ export function createClassFeaturePlugin({ keysNodes = extractComputedKeys(ref, path, computedPaths, this.file); ({ staticNodes, instanceNodes, wrapClass } = buildFieldsInitNodes( ref, + path.node.superClass, props, privateNamesMap, state, diff --git a/packages/babel-helper-replace-supers/src/index.js b/packages/babel-helper-replace-supers/src/index.js index e6d45b7464..89b7cc70d0 100644 --- a/packages/babel-helper-replace-supers/src/index.js +++ b/packages/babel-helper-replace-supers/src/index.js @@ -51,13 +51,7 @@ export const environmentVisitor = { path.skip(); }, - Method(path) { - skipAllButComputedKey(path); - }, - - "ClassProperty|ClassPrivateProperty"(path) { - // If the property is computed, we need to visit everything. - if (path.node.static) return; + "Method|ClassProperty|ClassPrivateProperty"(path) { skipAllButComputedKey(path); }, }; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/exec.js new file mode 100644 index 0000000000..9eee96e79f --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/exec.js @@ -0,0 +1,13 @@ +class A { + static #self = this; + static #getA = () => this; + + static extract() { + return { self: A.#self, getA: A.#getA }; + } +} + +const { self, getA } = A.extract(); + +expect(self).toBe(A); +expect(getA()).toBe(A); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/input.js new file mode 100644 index 0000000000..bdfaf4424d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/input.js @@ -0,0 +1,4 @@ +class A { + static #self = this; + static #getA = () => this; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/output.js new file mode 100644 index 0000000000..d0c5da7be8 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private-loose/static-this/output.js @@ -0,0 +1,18 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +var _self = babelHelpers.classPrivateFieldLooseKey("self"); + +var _getA = babelHelpers.classPrivateFieldLooseKey("getA"); + +Object.defineProperty(A, _self, { + writable: true, + value: A +}); +Object.defineProperty(A, _getA, { + writable: true, + value: () => A +}); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/exec.js new file mode 100644 index 0000000000..9eee96e79f --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/exec.js @@ -0,0 +1,13 @@ +class A { + static #self = this; + static #getA = () => this; + + static extract() { + return { self: A.#self, getA: A.#getA }; + } +} + +const { self, getA } = A.extract(); + +expect(self).toBe(A); +expect(getA()).toBe(A); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/input.js new file mode 100644 index 0000000000..bdfaf4424d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/input.js @@ -0,0 +1,4 @@ +class A { + static #self = this; + static #getA = () => this; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/output.js new file mode 100644 index 0000000000..75e42cd43f --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/private/static-this/output.js @@ -0,0 +1,14 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +var _self = { + writable: true, + value: A +}; +var _getA = { + writable: true, + value: () => A +}; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/exec.js new file mode 100644 index 0000000000..4b4e3fcf1a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/exec.js @@ -0,0 +1,15 @@ +class A { + static prop = 1; +} + +class B extends A { + static prop = 2; + static propA = super.prop; + static getPropA = () => super.prop; +} + +const { prop, propA, getPropA } = B; + +expect(prop).toBe(2); +expect(propA).toBe(1); +expect(getPropA()).toBe(1); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/input.js new file mode 100644 index 0000000000..9f649ab392 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/input.js @@ -0,0 +1,9 @@ +class A { + static prop = 1; +} + +class B extends A { + static prop = 2; + static propA = super.prop; + static getPropA = () => super.prop; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/output.js new file mode 100644 index 0000000000..d41e2ff851 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-super/output.js @@ -0,0 +1,27 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +A.prop = 1; + +var B = +/*#__PURE__*/ +function (_A) { + "use strict"; + + babelHelpers.inherits(B, _A); + + function B() { + babelHelpers.classCallCheck(this, B); + return babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(B).apply(this, arguments)); + } + + return B; +}(A); + +B.prop = 2; +B.propA = A.prop; + +B.getPropA = () => A.prop; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/exec.js new file mode 100644 index 0000000000..0dbdf74918 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/exec.js @@ -0,0 +1,9 @@ +class A { + static self = this; + static getA = () => this; +} + +const { self, getA } = A; + +expect(self).toBe(A); +expect(getA()).toBe(A); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/input.js new file mode 100644 index 0000000000..59f5c38c2c --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/input.js @@ -0,0 +1,4 @@ +class A { + static self = this; + static getA = () => this; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/output.js new file mode 100644 index 0000000000..4b6e55a118 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/static-this/output.js @@ -0,0 +1,9 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +A.self = A; + +A.getA = () => A; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/exec.js new file mode 100644 index 0000000000..4b4e3fcf1a --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/exec.js @@ -0,0 +1,15 @@ +class A { + static prop = 1; +} + +class B extends A { + static prop = 2; + static propA = super.prop; + static getPropA = () => super.prop; +} + +const { prop, propA, getPropA } = B; + +expect(prop).toBe(2); +expect(propA).toBe(1); +expect(getPropA()).toBe(1); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/input.js new file mode 100644 index 0000000000..9f649ab392 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/input.js @@ -0,0 +1,9 @@ +class A { + static prop = 1; +} + +class B extends A { + static prop = 2; + static propA = super.prop; + static getPropA = () => super.prop; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/output.js new file mode 100644 index 0000000000..bb05940c90 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-super/output.js @@ -0,0 +1,26 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +babelHelpers.defineProperty(A, "prop", 1); + +var B = +/*#__PURE__*/ +function (_A) { + "use strict"; + + babelHelpers.inherits(B, _A); + + function B() { + babelHelpers.classCallCheck(this, B); + return babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(B).apply(this, arguments)); + } + + return B; +}(A); + +babelHelpers.defineProperty(B, "prop", 2); +babelHelpers.defineProperty(B, "propA", babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B)); +babelHelpers.defineProperty(B, "getPropA", () => babelHelpers.get(babelHelpers.getPrototypeOf(B), "prop", B)); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/exec.js new file mode 100644 index 0000000000..0dbdf74918 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/exec.js @@ -0,0 +1,9 @@ +class A { + static self = this; + static getA = () => this; +} + +const { self, getA } = A; + +expect(self).toBe(A); +expect(getA()).toBe(A); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/input.js new file mode 100644 index 0000000000..59f5c38c2c --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/input.js @@ -0,0 +1,4 @@ +class A { + static self = this; + static getA = () => this; +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/output.js new file mode 100644 index 0000000000..282c9e3442 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/static-this/output.js @@ -0,0 +1,8 @@ +var A = function A() { + "use strict"; + + babelHelpers.classCallCheck(this, A); +}; + +babelHelpers.defineProperty(A, "self", A); +babelHelpers.defineProperty(A, "getA", () => A); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6153/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6153/output.js index 6212f5c3d7..0fdb59a757 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6153/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6153/output.js @@ -1,19 +1,17 @@ -var _this = this; - (function () { class Foo { constructor() { - var _this2 = this; + var _this = this; babelHelpers.defineProperty(this, "fn", function () { - return console.log(_this2); + return console.log(_this); }); } } babelHelpers.defineProperty(Foo, "fn", function () { - return console.log(_this); + return console.log(Foo); }); }); @@ -22,25 +20,25 @@ var _this = this; return _temp = _class = class Bar { constructor() { - var _this3 = this; + var _this2 = this; babelHelpers.defineProperty(this, "fn", function () { - return console.log(_this3); + return console.log(_this2); }); } }, babelHelpers.defineProperty(_class, "fn", function () { - return console.log(_this); + return console.log(_class); }), _temp; }); (function () { class Baz { constructor(_force) { - var _this4 = this; + var _this3 = this; babelHelpers.defineProperty(this, "fn", function () { - return console.log(_this4); + return console.log(_this3); }); babelHelpers.defineProperty(this, "force", force); } @@ -48,25 +46,23 @@ var _this = this; } babelHelpers.defineProperty(Baz, "fn", function () { - return console.log(_this); + return console.log(Baz); }); }); var qux = function () { - var _this6 = this; - class Qux { constructor() { - var _this5 = this; + var _this4 = this; babelHelpers.defineProperty(this, "fn", function () { - return console.log(_this5); + return console.log(_this4); }); } } babelHelpers.defineProperty(Qux, "fn", function () { - return console.log(_this6); + return console.log(Qux); }); }.bind(this); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/output.js index b72cd0ee5d..6c8d4cd1a2 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/6154/output.js @@ -8,21 +8,19 @@ function _inherits(subClass, superClass) { if (typeof superClass !== "function" function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); } -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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } - function _get(target, property, receiver) { if (typeof Reflect !== "undefined" && Reflect.get) { _get = Reflect.get; } else { _get = function _get(target, property, receiver) { var base = _superPropBase(target, property); if (!base) return; var desc = Object.getOwnPropertyDescriptor(base, property); if (desc.get) { return desc.get.call(receiver); } return desc.value; }; } return _get(target, property, receiver || target); } function _superPropBase(object, property) { while (!Object.prototype.hasOwnProperty.call(object, property)) { object = _getPrototypeOf(object); if (object === null) break; } return object; } function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); } +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 _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } + var Test = function Test() { "use strict"; - var _this2 = this; - _classCallCheck(this, Test); var Other = @@ -54,6 +52,6 @@ var Test = function Test() { }(Test); _defineProperty(Other, "a", function () { - return _get(_getPrototypeOf(Test.prototype), "test", _this2); + return _get(_getPrototypeOf(Other), "test", Other); }); }; diff --git a/packages/babel-traverse/src/path/conversion.js b/packages/babel-traverse/src/path/conversion.js index cc1f1003a4..99a2b00a73 100644 --- a/packages/babel-traverse/src/path/conversion.js +++ b/packages/babel-traverse/src/path/conversion.js @@ -200,7 +200,6 @@ function hoistFunctionEnvironment( child.skip(); }, ClassProperty(child) { - if (child.node.static) return; child.skip(); }, CallExpression(child) { @@ -453,7 +452,6 @@ function getThisBinding(thisEnvFn, inConstructor) { child.skip(); }, ClassProperty(child) { - if (child.node.static) return; child.skip(); }, CallExpression(child) { @@ -581,7 +579,6 @@ function getScopeInformation(fnPath) { fnPath.traverse({ ClassProperty(child) { - if (child.node.static) return; child.skip(); }, Function(child) {