diff --git a/src/babel/transformation/transformers/es7/abstract-references.js b/src/babel/transformation/transformers/es7/abstract-references.js new file mode 100644 index 0000000000..7713b41444 --- /dev/null +++ b/src/babel/transformation/transformers/es7/abstract-references.js @@ -0,0 +1,111 @@ +// https://github.com/zenparsing/es-abstract-refs + +import * as util from "../../../util"; +import * as t from "../../../types"; + +export var experimental = true; + +var container = function (parent, call, ret, file) { + if (t.isExpressionStatement(parent) && !file.isConsequenceExpressionStatement(parent)) { + // we don't need to worry about return values + return call; + } else { + var exprs = []; + if (t.isSequenceExpression(call)) { + exprs = call.expressions; + } else { + exprs.push(call); + } + exprs.push(ret); + return t.sequenceExpression(exprs); + } +}; + +export function AssignmentExpression(node, parent, scope, file) { + var left = node.left; + if (!t.isVirtualPropertyExpression(left)) return; + + var value = node.right; + var temp; + + // we need to return `node.right` + if (!t.isExpressionStatement(parent)) { + temp = scope.generateTempBasedOnNode(node.right); + if (temp) value = temp; + } + + if (node.operator !== "=") { + value = t.binaryExpression( + node.operator[0], + util.template("abstract-expression-get", { + PROPERTY: node.property, + OBJECT: node.object + }), + value + ); + } + + var call = util.template("abstract-expression-set", { + PROPERTY: left.property, + OBJECT: left.object, + VALUE: value + }); + + if (temp) { + call = t.sequenceExpression([ + t.assignmentExpression("=", temp, node.right), + call + ]); + } + + return container(parent, call, value, file); +} + +export function UnaryExpression(node, parent, scope, file) { + var arg = node.argument; + if (!t.isVirtualPropertyExpression(arg)) return; + if (node.operator !== "delete") return; + + var call = util.template("abstract-expression-delete", { + PROPERTY: arg.property, + OBJECT: arg.object + }); + + return container(parent, call, t.literal(true), file); +} + +export function CallExpression(node, parent, scope) { + var callee = node.callee; + if (!t.isVirtualPropertyExpression(callee)) return; + + var temp = scope.generateTempBasedOnNode(callee.object); + + var call = util.template("abstract-expression-call", { + PROPERTY: callee.property, + OBJECT: temp || callee.object + }); + + call.arguments = call.arguments.concat(node.arguments); + + if (temp) { + return t.sequenceExpression([ + t.assignmentExpression("=", temp, callee.object), + call + ]); + } else { + return call; + } +} + +export function VirtualPropertyExpression(node) { + return util.template("abstract-expression-get", { + PROPERTY: node.property, + OBJECT: node.object + }); +} + +export function PrivateDeclaration(node) { + return t.variableDeclaration("const", node.declarations.map(function (id) { + return t.variableDeclarator(id, t.newExpression(t.identifier("WeakMap"), [])); + })); +} diff --git a/src/babel/transformation/transformers/index.js b/src/babel/transformation/transformers/index.js index 7a24cfd438..9b008d23c6 100644 --- a/src/babel/transformation/transformers/index.js +++ b/src/babel/transformation/transformers/index.js @@ -15,7 +15,9 @@ export default { "es6.arrowFunctions": require("./es6/arrow-functions"), "playground.malletOperator": require("./playground/mallet-operator"), + "playground.methodBinding": require("./playground/method-binding"), "playground.memoizationOperator": require("./playground/memoization-operator"), + "playground.objectGetterMemoization": require("./playground/object-getter-memoization"), reactCompat: require("./other/react-compat"), react: require("./other/react"), @@ -47,6 +49,7 @@ export default { "es6.regex.sticky": require("./es6/regex.sticky"), "es6.regex.unicode": require("./es6/regex.unicode"), + "es7.abstractReferences": require("./es7/abstract-references"), "es6.constants": require("./es6/constants"), diff --git a/src/babel/transformation/transformers/playground/method-binding.js b/src/babel/transformation/transformers/playground/method-binding.js new file mode 100644 index 0000000000..5d4ddff3c7 --- /dev/null +++ b/src/babel/transformation/transformers/playground/method-binding.js @@ -0,0 +1,43 @@ +import * as t from "../../../types"; + +export var playground = true; + +export function BindMemberExpression(node, parent, scope) { + var object = node.object; + var prop = node.property; + + var temp = scope.generateTempBasedOnNode(node.object); + if (temp) object = temp; + + var call = t.callExpression( + t.memberExpression(t.memberExpression(object, prop), t.identifier("bind")), + [object, ...node.arguments] + ); + + if (temp) { + return t.sequenceExpression([ + t.assignmentExpression("=", temp, node.object), + call + ]); + } else { + return call; + } +} + +export function BindFunctionExpression(node, parent, scope) { + var buildCall = function (args) { + var param = scope.generateUidIdentifier("val"); + return t.functionExpression(null, [param], t.blockStatement([ + t.returnStatement(t.callExpression(t.memberExpression(param, node.callee), args)) + ])); + }; + + var temp = scope.generateTemp("args"); + + return t.sequenceExpression([ + t.assignmentExpression("=", temp, t.arrayExpression(node.arguments)), + buildCall(node.arguments.map(function (node, i) { + return t.memberExpression(temp, t.literal(i), true); + })) + ]); +} diff --git a/src/babel/transformation/transformers/playground/object-getter-memoization.js b/src/babel/transformation/transformers/playground/object-getter-memoization.js new file mode 100644 index 0000000000..dbbd3a065e --- /dev/null +++ b/src/babel/transformation/transformers/playground/object-getter-memoization.js @@ -0,0 +1,43 @@ +import * as t from "../../../types"; + +export var playground = true; + +var visitor = { + enter(node, parent, scope, state) { + if (this.isFunction()) return this.skip(); + + if (this.isReturnStatement() && node.argument) { + node.argument = t.memberExpression(t.callExpression(state.file.addHelper("define-property"), [ + t.thisExpression(), + state.key, + node.argument + ]), state.key, true); + } + } +}; + +export function MethodDefinition(node, parent, scope, file) { + if (node.kind !== "memo") return; + node.kind = "get"; + + console.error("Object getter memoization is deprecated and will be removed in 5.0.0"); + + t.ensureBlock(node.value); + + var key = node.key; + + if (t.isIdentifier(key) && !node.computed) { + key = t.literal(key.name); + } + + var state = { + key: key, + file: file + }; + + this.get("value").traverse(visitor, state); + + return node; +} + +export { MethodDefinition as Property }; diff --git a/test/fixtures/transformation/es7-abstract-references/call/actual.js b/test/fixtures/transformation/es7-abstract-references/call/actual.js new file mode 100644 index 0000000000..51355b9a69 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/actual.js @@ -0,0 +1,6 @@ +foo::bar(); +foo::bar("arg"); + +var test = "test"; +test::bar(); +test::bar("arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/call/expected.js b/test/fixtures/transformation/es7-abstract-references/call/expected.js new file mode 100644 index 0000000000..55bdecc949 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/call/expected.js @@ -0,0 +1,10 @@ +"use strict"; + +var _foo, _foo2; + +_foo = foo, bar[Symbol.referenceGet](_foo).call(_foo); +_foo2 = foo, bar[Symbol.referenceGet](_foo2).call(_foo2, "arg"); + +var test = "test"; +bar[Symbol.referenceGet](test).call(test); +bar[Symbol.referenceGet](test).call(test, "arg"); diff --git a/test/fixtures/transformation/es7-abstract-references/delete/actual.js b/test/fixtures/transformation/es7-abstract-references/delete/actual.js new file mode 100644 index 0000000000..af9f86c23c --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/actual.js @@ -0,0 +1,3 @@ +delete foo::bar; + +if (delete foo::bar) {} diff --git a/test/fixtures/transformation/es7-abstract-references/delete/expected.js b/test/fixtures/transformation/es7-abstract-references/delete/expected.js new file mode 100644 index 0000000000..489c0b7863 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/delete/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +bar[Symbol.referenceDelete](foo); + +if ((bar[Symbol.referenceDelete](foo), true)) {} diff --git a/test/fixtures/transformation/es7-abstract-references/get/actual.js b/test/fixtures/transformation/es7-abstract-references/get/actual.js new file mode 100644 index 0000000000..d6a275fb29 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/actual.js @@ -0,0 +1 @@ +foo::bar; diff --git a/test/fixtures/transformation/es7-abstract-references/get/expected.js b/test/fixtures/transformation/es7-abstract-references/get/expected.js new file mode 100644 index 0000000000..70419d5d74 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/get/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +bar[Symbol.referenceGet](foo); diff --git a/test/fixtures/transformation/es7-abstract-references/options.json b/test/fixtures/transformation/es7-abstract-references/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es7-abstract-references/private/actual.js b/test/fixtures/transformation/es7-abstract-references/private/actual.js new file mode 100644 index 0000000000..9643ff19c2 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/private/actual.js @@ -0,0 +1,12 @@ +private A; +private B, C; + +class D { + private E; + private F, G; +} + +var H = class { + private I; + private J, K; +}; diff --git a/test/fixtures/transformation/es7-abstract-references/private/expected.js b/test/fixtures/transformation/es7-abstract-references/private/expected.js new file mode 100644 index 0000000000..53ef631e1d --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/private/expected.js @@ -0,0 +1,30 @@ +"use strict"; + +var _classCallCheck = function (instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }; + +var A = new WeakMap(); +var B = new WeakMap(), + C = new WeakMap(); + +var D = (function () { + var F = new WeakMap(), + G = new WeakMap(); + var E = new WeakMap(); + + function D() { + _classCallCheck(this, D); + } + + return D; +})(); + +var H = (function () { + var _class = function H() { + _classCallCheck(this, _class); + }; + + var J = new WeakMap(), + K = new WeakMap(); + var I = new WeakMap(); + return _class; +})(); \ No newline at end of file diff --git a/test/fixtures/transformation/es7-abstract-references/set/actual.js b/test/fixtures/transformation/es7-abstract-references/set/actual.js new file mode 100644 index 0000000000..8abf694209 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/actual.js @@ -0,0 +1,3 @@ +var baz = "foo"; +foo::bar = baz; +if (foo::bar = baz) {} diff --git a/test/fixtures/transformation/es7-abstract-references/set/expected.js b/test/fixtures/transformation/es7-abstract-references/set/expected.js new file mode 100644 index 0000000000..be9e6ef9f3 --- /dev/null +++ b/test/fixtures/transformation/es7-abstract-references/set/expected.js @@ -0,0 +1,5 @@ +"use strict"; + +var baz = "foo"; +bar[Symbol.referenceSet](foo, baz); +if ((bar[Symbol.referenceSet](foo, baz), baz)) {} diff --git a/test/fixtures/transformation/playground/method-binding/actual.js b/test/fixtures/transformation/playground/method-binding/actual.js new file mode 100644 index 0000000000..12185e1a7c --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/actual.js @@ -0,0 +1,13 @@ +var fn = obj#method; +var fn = obj#method("foob"); +var fn = obj[foo]#method; +var fn = obj.foo#method; +var fn = obj[foo()]#method; + +["foo", "bar"].map(#toUpperCase); +[1.1234, 23.53245, 3].map(#toFixed(2)); + +var get = function () { + return 2; +}; +[1.1234, 23.53245, 3].map(#toFixed(get())); diff --git a/test/fixtures/transformation/playground/method-binding/exec.js b/test/fixtures/transformation/playground/method-binding/exec.js new file mode 100644 index 0000000000..b83e4b49aa --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/exec.js @@ -0,0 +1,30 @@ +var obj = { + foo: "foo", + bar: "bar", + getFoo: function () { + return this.foo; + }, + getBar: function (arg) { + return arg + " " + this.bar; + }, + getZoo: function (a, b) { + return this.foo + " " + this.bar + " " + a + " " + b; + } +}; + +var foo = obj#getFoo; +assert.equal(foo(), "foo"); + +var bar = obj#getBar("foo"); +assert.equal(bar(), "foo bar"); + +var zoo = obj#getZoo("foo"); +assert.equal(zoo("bar"), "foo bar foo bar"); + +assert.deepEqual(["foo", "bar"].map(#toUpperCase), ["FOO", "BAR"]); +assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(2)), ["1.12", "23.53", "3.00"]); + +var get = function () { + return 2; +} +assert.deepEqual([1.1234, 23.53245, 3].map(#toFixed(get())), ["1.12", "23.53", "3.00"]); diff --git a/test/fixtures/transformation/playground/method-binding/expected.js b/test/fixtures/transformation/playground/method-binding/expected.js new file mode 100644 index 0000000000..6f76e45d89 --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/expected.js @@ -0,0 +1,23 @@ +"use strict"; + +var _obj, _obj2, _obj$foo, _obj$foo2, _obj$foo3, _args, _args2, _args3; + +var fn = (_obj = obj, _obj.method.bind(_obj)); +var fn = (_obj2 = obj, _obj2.method.bind(_obj2, "foob")); +var fn = (_obj$foo = obj[foo], _obj$foo.method.bind(_obj$foo)); +var fn = (_obj$foo2 = obj.foo, _obj$foo2.method.bind(_obj$foo2)); +var fn = (_obj$foo3 = obj[foo()], _obj$foo3.method.bind(_obj$foo3)); + +["foo", "bar"].map((_args = [], function (_val) { + return _val.toUpperCase(); +})); +[1.1234, 23.53245, 3].map((_args2 = [2], function (_val2) { + return _val2.toFixed(_args2[0]); +})); + +var get = function get() { + return 2; +}; +[1.1234, 23.53245, 3].map((_args3 = [get()], function (_val3) { + return _val3.toFixed(_args3[0]); +})); diff --git a/test/fixtures/transformation/playground/method-binding/options.json b/test/fixtures/transformation/playground/method-binding/options.json new file mode 100644 index 0000000000..b8b90ecb11 --- /dev/null +++ b/test/fixtures/transformation/playground/method-binding/options.json @@ -0,0 +1,3 @@ +{ + "blacklist": ["es6.tailCall"] +} diff --git a/test/fixtures/transformation/playground/object-getter-memoization/actual.js b/test/fixtures/transformation/playground/object-getter-memoization/actual.js new file mode 100644 index 0000000000..9b0d317c04 --- /dev/null +++ b/test/fixtures/transformation/playground/object-getter-memoization/actual.js @@ -0,0 +1,19 @@ +class Foo { + memo bar() { + return complex(); + } + + memo [bar]() { + return complex(); + } +} + +var foo = { + memo bar() { + return complex(); + }, + + memo [bar]() { + return complex(); + } +}; diff --git a/test/fixtures/transformation/playground/object-getter-memoization/expected.js b/test/fixtures/transformation/playground/object-getter-memoization/expected.js new file mode 100644 index 0000000000..214f9235f5 --- /dev/null +++ b/test/fixtures/transformation/playground/object-getter-memoization/expected.js @@ -0,0 +1,36 @@ +"use strict"; + +var Foo = (function () { + function Foo() { + babelHelpers.classCallCheck(this, Foo); + } + + babelHelpers.createComputedClass(Foo, [{ + key: "bar", + get: function () { + return babelHelpers.defineProperty(this, "bar", complex()).bar; + } + }, { + key: bar, + get: function () { + return babelHelpers.defineProperty(this, bar, complex())[bar]; + } + }]); + return Foo; +})(); + +var foo = Object.defineProperties({}, babelHelpers.defineProperty({ + bar: { + get: function () { + return babelHelpers.defineProperty(this, "bar", complex()).bar; + }, + configurable: true, + enumerable: true + } +}, bar, { + get: function () { + return babelHelpers.defineProperty(this, bar, complex())[bar]; + }, + configurable: true, + enumerable: true +})); \ No newline at end of file