From b4c9cb0222e52966491226524d4e396c6b19c280 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Wed, 29 May 2019 05:07:55 +0800 Subject: [PATCH] Fixed computed keys for class expression (#10029) * test case for insertBefore for jsx * fix unshiftContainer and insertBefore * use path.scope.push * add test making sure computedKeys var declaration at the right block * add comment * nit [skip ci] --- .../src/misc.js | 12 ++++-- .../nested-class/super-call-in-key/output.js | 5 ++- .../super-property-in-key/output.js | 5 ++- .../fixtures/public-loose/computed/output.js | 27 ++++++------- .../public-loose/instance-computed/output.js | 4 +- .../public/computed-without-block/exec.js | 5 +++ .../public/computed-without-block/input.js | 1 + .../public/computed-without-block/output.js | 18 +++++++++ .../test/fixtures/public/computed/output.js | 27 ++++++------- .../public/instance-computed/output.js | 4 +- .../test/fixtures/regression/7371/output.js | 4 +- .../test/fixtures/regression/8882/exec.js | 23 +++++++++++ .../test/fixtures/regression/8882/input.js | 13 +++++++ .../fixtures/regression/8882/options.json | 3 ++ .../test/fixtures/regression/8882/output.js | 23 +++++++++++ .../decorator-interop/output.js | 4 +- .../static-property-tdz/edgest-case/output.js | 4 +- .../static-property-tdz/general/output.js | 4 +- .../static-property-tdz/loose/output.js | 4 +- .../babel-traverse/src/path/modification.js | 6 +-- packages/babel-traverse/test/modification.js | 39 +++++++++++++++++++ 21 files changed, 185 insertions(+), 50 deletions(-) create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/output.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/exec.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/input.js create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/options.json create mode 100644 packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/output.js diff --git a/packages/babel-helper-create-class-features-plugin/src/misc.js b/packages/babel-helper-create-class-features-plugin/src/misc.js index 2468167fe940..ff5178374a56 100644 --- a/packages/babel-helper-create-class-features-plugin/src/misc.js +++ b/packages/babel-helper-create-class-features-plugin/src/misc.js @@ -98,10 +98,16 @@ export function extractComputedKeys(ref, path, computedPaths, file) { const ident = path.scope.generateUidIdentifierBasedOnNode( computedNode.key, ); + // Declaring in the same block scope + // Ref: https://github.com/babel/babel/pull/10029/files#diff-fbbdd83e7a9c998721c1484529c2ce92 + path.scope.push({ + id: ident, + kind: "let", + }); declarations.push( - t.variableDeclaration("var", [ - t.variableDeclarator(ident, computedNode.key), - ]), + t.expressionStatement( + t.assignmentExpression("=", t.cloneNode(ident), computedNode.key), + ), ); computedNode.key = t.cloneNode(ident); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-call-in-key/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-call-in-key/output.js index 2ded153f1e81..117802306abc 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-call-in-key/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-call-in-key/output.js @@ -16,11 +16,12 @@ function (_Hello) { babelHelpers.inherits(Outer, _Hello); function Outer() { + let _this2; + var _this; babelHelpers.classCallCheck(this, Outer); - - var _this2 = _this = babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(Outer).call(this)); + _this2 = _this = babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(Outer).call(this)); let Inner = function Inner() { babelHelpers.classCallCheck(this, Inner); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-property-in-key/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-property-in-key/output.js index 6df7880cd395..ba9e8dd36f5e 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-property-in-key/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/nested-class/super-property-in-key/output.js @@ -22,12 +22,13 @@ function (_Hello) { babelHelpers.inherits(Outer, _Hello); function Outer() { + let _babelHelpers$get$cal; + var _this; babelHelpers.classCallCheck(this, Outer); _this = babelHelpers.possibleConstructorReturn(this, babelHelpers.getPrototypeOf(Outer).call(this)); - - var _babelHelpers$get$cal = babelHelpers.get(babelHelpers.getPrototypeOf(Outer.prototype), "toString", babelHelpers.assertThisInitialized(_this)).call(babelHelpers.assertThisInitialized(_this)); + _babelHelpers$get$cal = babelHelpers.get(babelHelpers.getPrototypeOf(Outer.prototype), "toString", babelHelpers.assertThisInitialized(_this)).call(babelHelpers.assertThisInitialized(_this)); let Inner = function Inner() { babelHelpers.classCallCheck(this, Inner); diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js index 0cd7c14b4559..20509438f6a1 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/computed/output.js @@ -1,24 +1,19 @@ +var _one, _ref, _undefined, _computed, _computed2, _ref2, _ref3, _baz, _ref4; + var foo = "foo"; var bar = () => {}; var four = 4; - -var _one = one(); - -var _ref = 2 * four + seven; - -var _undefined = undefined; - -var _computed = computed(); - -var _computed2 = computed(); - -var _ref2 = "test" + one; - -var _ref3 = /regex/; -var _baz = baz; -var _ref4 = `template${expression}`; +_one = one(); +_ref = 2 * four + seven; +_undefined = undefined; +_computed = computed(); +_computed2 = computed(); +_ref2 = "test" + one; +_ref3 = /regex/; +_baz = baz; +_ref4 = `template${expression}`; var MyClass = /*#__PURE__*/ diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/instance-computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/instance-computed/output.js index 0e98b482c741..c7e13f2d1b6e 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/instance-computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public-loose/instance-computed/output.js @@ -1,5 +1,7 @@ function test(x) { - var _x = x; + var _x; + + _x = x; var F = function F() { "use strict"; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/exec.js new file mode 100644 index 000000000000..40eee7319366 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/exec.js @@ -0,0 +1,5 @@ +const createClass = (k) => class { [k()] = 2 }; + +const clazz = createClass(() => 'foo'); +const instance = new clazz(); +expect(instance.foo).toBe(2); \ No newline at end of file diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/input.js new file mode 100644 index 000000000000..4b2d583d4ec6 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/input.js @@ -0,0 +1 @@ +const createClass = (k) => class { [k()] = 2 }; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/output.js new file mode 100644 index 000000000000..535a47955ecf --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed-without-block/output.js @@ -0,0 +1,18 @@ +var createClass = k => { + var _temp; + + var _k; + + return _temp = (_k = k(), + /*#__PURE__*/ + function () { + "use strict"; + + function _class2() { + babelHelpers.classCallCheck(this, _class2); + babelHelpers.defineProperty(this, _k, 2); + } + + return _class2; + }()), _temp; +}; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js index bc9a90b21252..259d3c96476d 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/computed/output.js @@ -1,24 +1,19 @@ +var _one, _ref, _undefined, _computed, _computed2, _ref2, _ref3, _baz, _ref4; + var foo = "foo"; var bar = () => {}; var four = 4; - -var _one = one(); - -var _ref = 2 * four + seven; - -var _undefined = undefined; - -var _computed = computed(); - -var _computed2 = computed(); - -var _ref2 = "test" + one; - -var _ref3 = /regex/; -var _baz = baz; -var _ref4 = `template${expression}`; +_one = one(); +_ref = 2 * four + seven; +_undefined = undefined; +_computed = computed(); +_computed2 = computed(); +_ref2 = "test" + one; +_ref3 = /regex/; +_baz = baz; +_ref4 = `template${expression}`; var MyClass = /*#__PURE__*/ diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/instance-computed/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/instance-computed/output.js index 79bfad744af4..5a311f2ad9d4 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/public/instance-computed/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/public/instance-computed/output.js @@ -1,5 +1,7 @@ function test(x) { - var _x = x; + var _x; + + _x = x; var F = function F() { "use strict"; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js index 5c6ac57eeba1..0e8cc4e60e0e 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/7371/output.js @@ -75,9 +75,11 @@ new ComputedMethod(); // ensure ComputedKey Field is still transformed class ComputedField extends Obj { constructor() { + let _ref; + var _temp3; - var _ref = (_temp3 = super(), babelHelpers.defineProperty(this, "field", 1), _temp3); + _ref = (_temp3 = super(), babelHelpers.defineProperty(this, "field", 1), _temp3); class B extends Obj { constructor() { diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/exec.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/exec.js new file mode 100644 index 000000000000..a16b8048dcaf --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/exec.js @@ -0,0 +1,23 @@ +const classes = []; +for (let i = 0; i <= 10; ++i) { + classes.push( + class A { + [i] = `computed field ${i}`; + static foo = `static field ${i}`; + #bar = `private field ${i}`; + getBar() { + return this.#bar; + } + } + ); +} + +for(let i=0; i<= 10; ++i) { + const clazz = classes[i]; + expect(clazz.foo).toBe('static field ' + i); + + const instance = new clazz(); + expect(Object.getOwnPropertyNames(instance)).toEqual([String(i)]) + expect(instance[i]).toBe('computed field ' + i); + expect(instance.getBar()).toBe('private field ' + i); +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/input.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/input.js new file mode 100644 index 000000000000..f7ca46cc407d --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/input.js @@ -0,0 +1,13 @@ +const classes = []; +for (let i = 0; i <= 10; ++i) { + classes.push( + class A { + [i] = `computed field ${i}`; + static foo = `static field ${i}`; + #bar = `private field ${i}`; + getBar() { + return this.#bar; + } + } + ); +} \ No newline at end of file diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/options.json b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/options.json new file mode 100644 index 000000000000..9d30185b9bff --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/options.json @@ -0,0 +1,3 @@ +{ + "plugins": ["external-helpers", "proposal-class-properties"] +} diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/output.js new file mode 100644 index 000000000000..deac2db7fd23 --- /dev/null +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/regression/8882/output.js @@ -0,0 +1,23 @@ +const classes = []; + +for (let i = 0; i <= 10; ++i) { + var _class, _temp, _bar; + + let _i; + + classes.push((_temp = (_i = i, _class = class A { + constructor() { + babelHelpers.defineProperty(this, _i, `computed field ${i}`); + + _bar.set(this, { + writable: true, + value: `private field ${i}` + }); + } + + getBar() { + return babelHelpers.classPrivateFieldGet(this, _bar); + } + + }), _bar = new WeakMap(), babelHelpers.defineProperty(_class, "foo", `static field ${i}`), _temp)); +} 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 8f3aff871cf0..2cd978b722c9 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 @@ -1,4 +1,6 @@ -var _class, _descriptor, _Symbol$search, _temp; +let _Symbol$search; + +var _class, _descriptor, _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 }); } diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/edgest-case/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/edgest-case/output.js index c3fc9ecd57e0..f87ef987e6a4 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/edgest-case/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/edgest-case/output.js @@ -1,10 +1,12 @@ +let _x$x; + 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 _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); } -var _x$x = { +_x$x = { x: (_classNameTDZError("A"), A) || 0 }.x; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/general/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/general/output.js index 21c823a89aae..024a8241721c 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/general/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/general/output.js @@ -1,10 +1,12 @@ +let _ref; + 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 _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); } -var _ref = (_classNameTDZError("C"), C) + 3; +_ref = (_classNameTDZError("C"), C) + 3; let C = function C() { "use strict"; diff --git a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/loose/output.js b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/loose/output.js index a6acc92159f9..9ab3df7368f7 100644 --- a/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/loose/output.js +++ b/packages/babel-plugin-proposal-class-properties/test/fixtures/static-property-tdz/loose/output.js @@ -1,6 +1,8 @@ +let _ref; + function _classNameTDZError(name) { throw new Error("Class \"" + name + "\" cannot be referenced in computed property keys."); } -var _ref = (_classNameTDZError("C"), C) + 3; +_ref = (_classNameTDZError("C"), C) + 3; class C {} diff --git a/packages/babel-traverse/src/path/modification.js b/packages/babel-traverse/src/path/modification.js index add972ee4327..6039a94c6090 100644 --- a/packages/babel-traverse/src/path/modification.js +++ b/packages/babel-traverse/src/path/modification.js @@ -24,9 +24,7 @@ export function insertBefore(nodes) { ) { return parentPath.insertBefore(nodes); } else if ( - (this.isNodeType("Expression") && - this.listKey !== "params" && - this.listKey !== "arguments") || + (this.isNodeType("Expression") && !this.isJSXElement()) || (parentPath.isForStatement() && this.key === "init") ) { if (this.node) nodes.push(this.node); @@ -220,7 +218,7 @@ export function unshiftContainer(listKey, nodes) { key: 0, }); - return path.insertBefore(nodes); + return path._containerInsertBefore(nodes); } export function pushContainer(listKey, nodes) { diff --git a/packages/babel-traverse/test/modification.js b/packages/babel-traverse/test/modification.js index ad5d40c69a8a..a4ccc90823ab 100644 --- a/packages/babel-traverse/test/modification.js +++ b/packages/babel-traverse/test/modification.js @@ -36,6 +36,19 @@ describe("modification", function() { expect(generateCode(rootPath)).toBe("function test(a) {\n b;\n}"); }); + + it("properly handles more than one arguments", function() { + const code = "foo(a, b);"; + const ast = parse(code); + traverse(ast, { + CallExpression: function(path) { + path.pushContainer("arguments", t.identifier("d")); + expect(generateCode(path)).toBe("foo(a, b, d);"); + path.pushContainer("arguments", t.stringLiteral("s")); + expect(generateCode(path)).toBe(`foo(a, b, d, "s");`); + }, + }); + }); }); describe("unshiftContainer", function() { it("unshifts identifier into params", function() { @@ -116,6 +129,32 @@ describe("modification", function() { ); }); + it("returns inserted path with nested JSXElement", function() { + const ast = parse("
foo
", { + plugins: ["jsx"], + }); + let path; + traverse(ast, { + Program: function(_path) { + path = _path.get("body.0"); + }, + JSXElement: function(path) { + const tagName = path.node.openingElement.name.name; + if (tagName !== "span") return; + path.insertBefore( + t.JSXElement( + t.JSXOpeningElement(t.JSXIdentifier("div"), [], false), + t.JSXClosingElement(t.JSXIdentifier("div")), + [], + ), + ); + }, + }); + expect(generateCode(path)).toBe( + "
foo
;", + ); + }); + describe("when the parent is an export declaration inserts the node before", function() { it("the ExportNamedDeclaration", function() { const bodyPath = getPath("export function a() {}", {