diff --git a/doc/caveats.md b/doc/caveats.md index 3d70e833d0..08fc3196fc 100644 --- a/doc/caveats.md +++ b/doc/caveats.md @@ -4,14 +4,15 @@ In order for certain features to work they require certain polyfills. You can satisfy **all** 6to5 feature requirements by using the included [polyfill](polyfill.md). You may alternatively selectively include what you need: -| Feature | Requirements | -| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | -| Abstract References | `Symbol` | -| Array destructuring | `Array.isArray`, `Array.from` | -| Async functions, Generators | [experimental option](usage.md#experimental), [regenerator runtime](https://github.com/facebook/regenerator/blob/master/runtime.js) | -| Comprehensions | [experimental option](usage.md#experimental), `Array.isArray`, `Array.from` | -| For..Of | `Symbol`, `prototype[Symbol.iterator]` | -| Spread | `Array.isArray`, `Array.from` | +| Feature | Requirements | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| Abstract References | [experimental](usage.md#experimental), `Symbol` | +| Array destructuring | `Array.isArray`, `Array.from` | +| Async functions, Generators | [experimental](usage.md#experimental), [regenerator runtime](https://github.com/facebook/regenerator/blob/master/runtime.js) | +| Comprehensions | [experimental](usage.md#experimental), `Array.isArray`, `Array.from` | +| For Of | `Symbol`, `prototype[Symbol.iterator]` | +| Spread | `Array.isArray`, `Array.from` | +| Object spread/rest | [experimental](usage.md#experimental), `Object.assign` | ## Classes diff --git a/doc/differences.md b/doc/differences.md index 81a07e0b27..8698103818 100644 --- a/doc/differences.md +++ b/doc/differences.md @@ -88,13 +88,14 @@ better suited if you'd like a full ES6 environment with polyfills and all. | Rest parameters | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | | Spread | ✓ | ✓ | ✓ | ✓ | ✓ | | | Template literals | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | +| Object rest/spread | ✓ | | | | | ✓ | | Unicode regex | ✓ | ✓ | ✓ | | | | ### [Traceur](https://github.com/google/traceur-compiler) Traceur requires quite a bulky runtime (~75KB) and produces quite verbose code. While this can be trimmed down by selectively building the runtime, it's an -unneccesary step when a runtime can be eliminated entirely. +unneccesary step when a large runtime can be eliminated entirely. ### [es6now](https://github.com/zenparsing/es6now) diff --git a/doc/features.md b/doc/features.md index 08f55b657f..ab33d0333c 100644 --- a/doc/features.md +++ b/doc/features.md @@ -8,7 +8,7 @@ foo::bar = baz; delete foo::bar; ``` -## Array comprehension ([experimental](usage.md#experimental)) +## Array comprehensions ([experimental](usage.md#experimental)) ```javascript var results = [for (c of customers) if (c.city == "Seattle") { name: c.name, age: c.age }] @@ -158,7 +158,7 @@ for (var n of fibonacci()) { } ``` -## Generator comprehension ([experimental](usage.md#experimental)) +## Generator comprehensions ([experimental](usage.md#experimental)) ```javascript var nums = [1, 2, 3, 4, 5, 6]; @@ -253,6 +253,13 @@ var y = 10; console.log(`${x} + ${y} = ${x + y}`); // "5 + 10 = 15" ``` +## Object spread/rest ([experimental](usage.md#experimental)) ([spec](https://github.com/sebmarkbage/ecmascript-rest-spread)) + +```javascript +var n = { x, y, ...z }; +var { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; +``` + ## Unicode regex ```javascript diff --git a/doc/index.md b/doc/index.md index 45325bbfde..3a0ef4d384 100644 --- a/doc/index.md +++ b/doc/index.md @@ -52,4 +52,5 @@ And it doesn't end here! To see all the ways you can use 6to5, check out the - [Rest parameters](features.md#rest-parameters) - [Spread](features.md#spread) - [Template literals](features.md#template-literals) + - [Object Rest/Spread](features.md#object-rest-spread) ([experimental](usage.md#experimental)) - [Unicode regex](features.md#unicode-regex) diff --git a/lib/6to5/file.js b/lib/6to5/file.js index 6c4b1586e5..600024323d 100644 --- a/lib/6to5/file.js +++ b/lib/6to5/file.js @@ -18,8 +18,16 @@ function File(opts) { this.ast = {}; } -File.declarations = ["extends", "class-props", "slice", "apply-constructor", - "tagged-template-literal", "interop-require", "to-array"]; +File.declarations = [ + "extends", + "class-props", + "apply-constructor", + "tagged-template-literal", + "interop-require", + "to-array", + "arguments-to-array", + "object-spread" +]; File.normaliseOptions = function (opts) { opts = _.cloneDeep(opts || {}); @@ -73,11 +81,14 @@ File.normaliseOptions = function (opts) { File.prototype.toArray = function (node) { if (t.isArrayExpression(node)) { return node; - } else if (t.isIdentifier(node) && node.name === "arguments") { - return t.callExpression(t.memberExpression(this.addDeclaration("slice"), t.identifier("call")), [node]); - } else { - return t.callExpression(this.addDeclaration("to-array"), [node]); } + + var templateName = "to-array"; + if (t.isIdentifier(node) && node.name === "arguments") { + templateName = "arguments-to-array"; + } + + return t.callExpression(this.addDeclaration(templateName), [node]); }; File.prototype.getModuleFormatter = function (type) { diff --git a/lib/6to5/generation/generators/types.js b/lib/6to5/generation/generators/types.js index f9f79fcc2e..b9fa822ce3 100644 --- a/lib/6to5/generation/generators/types.js +++ b/lib/6to5/generation/generators/types.js @@ -4,7 +4,8 @@ exports.Identifier = function (node) { this.push(node.name); }; -exports.SpreadElement = function (node, print) { +exports.SpreadElement = +exports.SpreadProperty = function (node, print) { this.push("..."); print(node.argument); }; diff --git a/lib/6to5/generation/node/whitespace.js b/lib/6to5/generation/node/whitespace.js index 62442dbc42..3ebb225b76 100644 --- a/lib/6to5/generation/node/whitespace.js +++ b/lib/6to5/generation/node/whitespace.js @@ -9,6 +9,10 @@ exports.before = { } }, + SpreadProperty: function (node, parent) { + return exports.before.nodes.Property(node, parent); + }, + SwitchCase: function (node, parent) { if (parent.cases[0] === node) { return 1; diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index 67fa614fa9..f799391d9d 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -38,6 +38,7 @@ _.each({ _propertyLiterals: require("./transformers/_property-literals"), computedPropertyNames: require("./transformers/es6-computed-property-names"), + objectSpread: require("./transformers/es7-object-spread"), spread: require("./transformers/es6-spread"), templateLiterals: require("./transformers/es6-template-literals"), propertyMethodAssignment: require("./transformers/es5-property-method-assignment"), diff --git a/lib/6to5/transformation/transformers/es6-destructuring.js b/lib/6to5/transformation/transformers/es6-destructuring.js index 0ce4bd0056..15c90b4171 100644 --- a/lib/6to5/transformation/transformers/es6-destructuring.js +++ b/lib/6to5/transformation/transformers/es6-destructuring.js @@ -24,14 +24,33 @@ var push = function (opts, nodes, elem, parentId) { }; var pushObjectPattern = function (opts, nodes, pattern, parentId) { - _.each(pattern.properties, function (prop) { - var pattern2 = prop.value; - var patternId2 = t.memberExpression(parentId, prop.key, prop.computed); + _.each(pattern.properties, function (prop, i) { + if (t.isSpreadProperty(prop)) { + // get all the keys that appear in this object before the current spread + var keys = []; + _.each(pattern.properties, function (prop2, i2) { + if (i2 >= i) return false; + if (t.isSpreadProperty(prop2)) return; - if (t.isPattern(pattern2)) { - push(opts, nodes, pattern2, patternId2); + var key = prop2.key; + if (t.isIdentifier(key)) { + key = t.literal(prop2.key.name); + } + keys.push(key); + }); + keys = t.arrayExpression(keys); + + var value = t.callExpression(opts.file.addDeclaration("object-spread"), [parentId, keys]); + nodes.push(buildVariableAssign(opts.kind, prop.argument, value)); } else { - nodes.push(buildVariableAssign(opts.kind, pattern2, patternId2)); + var pattern2 = prop.value; + var patternId2 = t.memberExpression(parentId, prop.key, prop.computed); + + if (t.isPattern(pattern2)) { + push(opts, nodes, pattern2, patternId2); + } else { + nodes.push(buildVariableAssign(opts.kind, pattern2, patternId2)); + } } }); }; diff --git a/lib/6to5/transformation/transformers/es7-object-spread.js b/lib/6to5/transformation/transformers/es7-object-spread.js new file mode 100644 index 0000000000..9af9bd863b --- /dev/null +++ b/lib/6to5/transformation/transformers/es7-object-spread.js @@ -0,0 +1,41 @@ +// https://github.com/sebmarkbage/ecmascript-rest-spread + +var t = require("../../types"); +var _ = require("lodash"); + +exports.ObjectExpression = function (node, parent, file) { + var hasSpread = false; + _.each(node.properties, function (prop) { + if (t.isSpreadProperty(prop)) { + hasSpread = true; + return false; + } + }); + if (!hasSpread) return; + + var args = []; + var props = []; + + var push = function () { + if (!props.length) return; + args.push(t.objectExpression(props)); + props = []; + }; + + _.each(node.properties, function (prop) { + if (t.isSpreadProperty(prop)) { + push(); + args.push(prop.argument); + } else { + props.push(prop); + } + }); + + push(); + + if (!t.isObjectExpression(args[0])) { + args.unshift(t.objectExpression([])); + } + + return t.callExpression(t.memberExpression(t.identifier("Object"), t.identifier("assign")), args); +}; diff --git a/lib/6to5/types/visitor-keys.json b/lib/6to5/types/visitor-keys.json index 44b32401de..c896d95a09 100644 --- a/lib/6to5/types/visitor-keys.json +++ b/lib/6to5/types/visitor-keys.json @@ -48,6 +48,7 @@ "ReturnStatement": ["argument"], "SequenceExpression": ["expressions"], "SpreadElement": ["argument"], + "SpreadProperty": ["argument"], "SwitchCase": ["test", "consequent"], "SwitchStatement": ["discriminant", "cases"], "TaggedTemplateExpression": ["tag", "quasi"], diff --git a/test/fixtures/generation/types/SpreadElement/actual.js b/test/fixtures/generation/types/SpreadElement-SpreadProperty/actual.js similarity index 100% rename from test/fixtures/generation/types/SpreadElement/actual.js rename to test/fixtures/generation/types/SpreadElement-SpreadProperty/actual.js diff --git a/test/fixtures/generation/types/SpreadElement/expected.js b/test/fixtures/generation/types/SpreadElement-SpreadProperty/expected.js similarity index 100% rename from test/fixtures/generation/types/SpreadElement/expected.js rename to test/fixtures/generation/types/SpreadElement-SpreadProperty/expected.js diff --git a/test/fixtures/transformation/es6-destructuring/es7-object-rest/actual.js b/test/fixtures/transformation/es6-destructuring/es7-object-rest/actual.js new file mode 100644 index 0000000000..b4877d6e45 --- /dev/null +++ b/test/fixtures/transformation/es6-destructuring/es7-object-rest/actual.js @@ -0,0 +1,3 @@ +var { ...x } = z; +var { x, ...y } = z; +(function({ x, ...y }) { }) diff --git a/test/fixtures/transformation/es6-destructuring/es7-object-rest/expected.js b/test/fixtures/transformation/es6-destructuring/es7-object-rest/expected.js new file mode 100644 index 0000000000..f1333fb51e --- /dev/null +++ b/test/fixtures/transformation/es6-destructuring/es7-object-rest/expected.js @@ -0,0 +1,22 @@ +"use strict"; + +var _objectSpread = function (target, keys) { + var target = {}; + for (var i in target) { + if (keys.indexOf(i) >= 0) continue; + if (!Object.prototype.hasOwn.call(target)) continue; + target[i] = target[i]; + } + + return target; +}; + +var x = _objectSpread(z, []); + +var x = z.x; +var y = _objectSpread(z, ["x"]); + +(function (_ref) { + var x = _ref.x; + var y = _objectSpread(_ref, ["x"]); +}); diff --git a/test/fixtures/transformation/es6-destructuring/es7-object-rest/options.json b/test/fixtures/transformation/es6-destructuring/es7-object-rest/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es6-destructuring/es7-object-rest/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es7-object-spread/assignment/actual.js b/test/fixtures/transformation/es7-object-spread/assignment/actual.js new file mode 100644 index 0000000000..8d610842d8 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/assignment/actual.js @@ -0,0 +1 @@ +z = { x, ...y }; diff --git a/test/fixtures/transformation/es7-object-spread/assignment/expected.js b/test/fixtures/transformation/es7-object-spread/assignment/expected.js new file mode 100644 index 0000000000..bd7dab2734 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/assignment/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +z = Object.assign({ x: x }, y); diff --git a/test/fixtures/transformation/es7-object-spread/expression/actual.js b/test/fixtures/transformation/es7-object-spread/expression/actual.js new file mode 100644 index 0000000000..e03ef8bdb2 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/expression/actual.js @@ -0,0 +1 @@ +({ x, ...y, a, ...b, c }); diff --git a/test/fixtures/transformation/es7-object-spread/expression/expected.js b/test/fixtures/transformation/es7-object-spread/expression/expected.js new file mode 100644 index 0000000000..ae48d06c13 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/expression/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +(Object.assign({ x: x }, y, { a: a }, b, { c: c })); diff --git a/test/fixtures/transformation/es7-object-spread/options.json b/test/fixtures/transformation/es7-object-spread/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es7-object-spread/variable-declaration/actual.js b/test/fixtures/transformation/es7-object-spread/variable-declaration/actual.js new file mode 100644 index 0000000000..a16dd23cb9 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/variable-declaration/actual.js @@ -0,0 +1 @@ +var z = { ...x }; diff --git a/test/fixtures/transformation/es7-object-spread/variable-declaration/expected.js b/test/fixtures/transformation/es7-object-spread/variable-declaration/expected.js new file mode 100644 index 0000000000..2522635156 --- /dev/null +++ b/test/fixtures/transformation/es7-object-spread/variable-declaration/expected.js @@ -0,0 +1,3 @@ +"use strict"; + +var z = Object.assign({}, x);