diff --git a/CHANGELOG.md b/CHANGELOG.md index fbabf70b45..5c9b75f08d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,23 @@ _Note: Gaps between patch versions are faulty/broken releases._ * Removed the `coreAliasing` transformer in favor of `selfContained`. * Remove `includeRegenerator` option. +## 2.13.5 + + * **Bug Fix** + * Allow rest parameters to be destructuring patterns. + * **Internal** + * Upgrade `kexec` to `1.1.0`. + +## 2.13.4 + + * **Bug Fix** + * Fix ordering of destructuring and default params. + * Fix loop body scope. + * Fix `for...of` transformation not retaining it's scope. + * Add destructuring assignment support to scope tracking. + * **Polish** + * More reliable newlines for pretty printing variable declarations. + ## 2.13.3 * **Internal** diff --git a/lib/6to5/transformation/templates/array-comprehension-for-each.js b/lib/6to5/transformation/templates/array-comprehension-for-each.js deleted file mode 100644 index cd22eab009..0000000000 --- a/lib/6to5/transformation/templates/array-comprehension-for-each.js +++ /dev/null @@ -1,3 +0,0 @@ -ARRAY.forEach(function (KEY) { - -}); diff --git a/lib/6to5/transformation/transform.js b/lib/6to5/transformation/transform.js index 20ac0ef7a8..99607a39f1 100644 --- a/lib/6to5/transformation/transform.js +++ b/lib/6to5/transformation/transform.js @@ -71,8 +71,9 @@ _.each({ templateLiterals: require("./transformers/es6-template-literals"), propertyMethodAssignment: require("./transformers/es6-property-method-assignment"), computedPropertyNames: require("./transformers/es6-computed-property-names"), - destructuring: require("./transformers/es6-destructuring"), defaultParameters: require("./transformers/es6-default-parameters"), + restParameters: require("./transformers/es6-rest-parameters"), + destructuring: require("./transformers/es6-destructuring"), forOf: require("./transformers/es6-for-of"), unicodeRegex: require("./transformers/es6-unicode-regex"), abstractReferences: require("./transformers/es7-abstract-references"), @@ -80,11 +81,10 @@ _.each({ constants: require("./transformers/es6-constants"), letScoping: require("./transformers/es6-let-scoping"), blockScopingTDZ: require("./transformers/optional-block-scoping-tdz"), - generators: require("./transformers/es6-generators"), _blockHoist: require("./transformers/_block-hoist"), - restParameters: require("./transformers/es6-rest-parameters"), + generators: require("./transformers/es6-generators"), protoToAssign: require("./transformers/optional-proto-to-assign"), diff --git a/lib/6to5/transformation/transformers/es6-default-parameters.js b/lib/6to5/transformation/transformers/es6-default-parameters.js index af1b163e5b..acb29933d0 100644 --- a/lib/6to5/transformation/transformers/es6-default-parameters.js +++ b/lib/6to5/transformation/transformers/es6-default-parameters.js @@ -15,7 +15,7 @@ exports.Function = function (node, parent, scope, context, file) { var iife = false; var def; - var checkTDZ = function (ids) { + var checkTDZ = function (param, def, ids) { var check = function (node, parent) { if (!t.isReferencedIdentifier(node, parent)) return; @@ -29,7 +29,10 @@ exports.Function = function (node, parent, scope, context, file) { }; check(def, node); - traverse(def, { enter: check }); + + if (!t.isPattern(param)) { + traverse(def, { enter: check }); + } }; for (var i = 0; i < node.defaults.length; i++) { @@ -42,7 +45,7 @@ exports.Function = function (node, parent, scope, context, file) { // are to the right - ie. uninitialized parameters var rightIds = ids.slice(i); for (var i2 = 0; i2 < rightIds.length; i2++) { - checkTDZ(rightIds[i2]); + checkTDZ(param, def, rightIds[i2]); } // we're accessing a variable that's already defined within this function @@ -66,12 +69,14 @@ exports.Function = function (node, parent, scope, context, file) { continue; } - body.push(util.template("default-parameter", { + var defNode = util.template("default-parameter", { VARIABLE_NAME: node.params[i], DEFAULT_VALUE: def, ARGUMENT_KEY: t.literal(+i), ARGUMENTS: argsIdentifier - }, true)); + }, true); + defNode._blockHoist = node.defaults.length - i; + body.push(defNode); } // we need to cut off all trailing default parameters diff --git a/lib/6to5/transformation/transformers/es6-destructuring.js b/lib/6to5/transformation/transformers/es6-destructuring.js index 3a3e27c4b5..35d5ae886b 100644 --- a/lib/6to5/transformation/transformers/es6-destructuring.js +++ b/lib/6to5/transformation/transformers/es6-destructuring.js @@ -8,13 +8,27 @@ var buildVariableAssign = function (opts, id, init) { var op = opts.operator; if (t.isMemberExpression(id)) op = "="; + var node; + if (op) { - return t.expressionStatement(t.assignmentExpression(op, id, init)); + node = t.expressionStatement(t.assignmentExpression(op, id, init)); } else { - return t.variableDeclaration(opts.kind, [ + node = t.variableDeclaration(opts.kind, [ t.variableDeclarator(id, init) ]); } + + node._blockHoist = opts.blockHoist; + + return node; +}; + +var buildVariableDeclar = function (opts, id, init) { + var declar = t.variableDeclaration("var", [ + t.variableDeclarator(id, init) + ]); + declar._blockHoist = opts.blockHoist; + return declar; }; var push = function (opts, nodes, elem, parentId) { @@ -100,9 +114,7 @@ var pushArrayPattern = function (opts, nodes, pattern, parentId) { var toArray = opts.file.toArray(parentId, !hasSpreadElement && pattern.elements.length); var _parentId = opts.scope.generateUidBasedOnNode(parentId, opts.file); - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(_parentId, toArray) - ])); + nodes.push(buildVariableDeclar(opts, _parentId, toArray)); parentId = _parentId; for (i = 0; i < pattern.elements.length; i++) { @@ -138,11 +150,7 @@ var pushPattern = function (opts) { if (!t.isArrayExpression(parentId) && !t.isMemberExpression(parentId) && !t.isIdentifier(parentId)) { var key = scope.generateUidBasedOnNode(parentId, file); - - nodes.push(t.variableDeclaration("var", [ - t.variableDeclarator(key, parentId) - ])); - + nodes.push(buildVariableDeclar(opts, key, parentId)); parentId = key; } @@ -181,19 +189,20 @@ exports.Function = function (node, parent, scope, context, file) { var hasDestructuring = false; - node.params = node.params.map(function (pattern) { + node.params = node.params.map(function (pattern, i) { if (!t.isPattern(pattern)) return pattern; hasDestructuring = true; var parentId = file.generateUidIdentifier("ref", scope); pushPattern({ - kind: "var", - nodes: nodes, - pattern: pattern, - id: parentId, - file: file, - scope: scope + blockHoist: node.params.length - i, + pattern: pattern, + nodes: nodes, + scope: scope, + file: file, + kind: "var", + id: parentId }); return parentId; @@ -294,12 +303,12 @@ exports.VariableDeclaration = function (node, parent, scope, context, file) { var patternId = declar.init; var pattern = declar.id; var opts = { - kind: node.kind, - nodes: nodes, pattern: pattern, - id: patternId, + nodes: nodes, + scope: scope, + kind: node.kind, file: file, - scope: scope + id: patternId, }; if (t.isPattern(pattern) && patternId) { diff --git a/lib/6to5/transformation/transformers/es6-for-of.js b/lib/6to5/transformation/transformers/es6-for-of.js index 3eed21d15d..3e3f9ac0f3 100644 --- a/lib/6to5/transformation/transformers/es6-for-of.js +++ b/lib/6to5/transformation/transformers/es6-for-of.js @@ -30,6 +30,9 @@ exports.ForOfStatement = function (node, parent, scope, context, file) { // push the rest of the original loop body onto our new body block.body = block.body.concat(node.body.body); + // todo: find out why this is necessary? #538 + loop._scopeInfo = node._scopeInfo; + return loop; }; diff --git a/lib/6to5/transformation/transformers/es6-let-scoping.js b/lib/6to5/transformation/transformers/es6-let-scoping.js index 4d0f13910e..044be99030 100644 --- a/lib/6to5/transformation/transformers/es6-let-scoping.js +++ b/lib/6to5/transformation/transformers/es6-let-scoping.js @@ -117,9 +117,10 @@ LetScoping.prototype.run = function () { */ LetScoping.prototype.remap = function () { - var letRefs = this.letReferences; - var scope = this.scope; - var file = this.file; + var hasRemaps = false; + var letRefs = this.letReferences; + var scope = this.scope; + var file = this.file; // alright, so since we aren't wrapping this block in a closure // we have to check if any of our let variables collide with @@ -136,6 +137,7 @@ LetScoping.prototype.remap = function () { var uid = file.generateUidIdentifier(ref.name, scope).name; ref.name = uid; + hasRemaps = true; remaps[key] = remaps[uid] = { node: ref, uid: uid @@ -143,6 +145,8 @@ LetScoping.prototype.remap = function () { } } + if (!hasRemaps) return; + // var replace = function (node, parent, scope, context, remaps) { diff --git a/lib/6to5/transformation/transformers/es6-rest-parameters.js b/lib/6to5/transformation/transformers/es6-rest-parameters.js index 89bafc063a..dac44731c0 100644 --- a/lib/6to5/transformation/transformers/es6-rest-parameters.js +++ b/lib/6to5/transformation/transformers/es6-rest-parameters.js @@ -41,6 +41,22 @@ exports.Function = function (node, parent, scope, context, file) { ); } + // support patterns + if (t.isPattern(rest)) { + var pattern = rest; + rest = file.generateUidIdentifier("ref", scope); + + // let the destructuring transformer handle this + var restDeclar = t.variableDeclaration("var", [ + t.variableDeclarator(pattern, rest) + ]); + + // retain evaluation position + restDeclar._blockHoist = node.params.length + 1; + + node.body.body.unshift(restDeclar); + } + node.body.body.unshift( util.template("rest", { ARGUMENTS: argsId, diff --git a/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js b/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js index 6640c0217f..ad6dbeb06c 100644 --- a/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js +++ b/lib/6to5/transformation/transformers/optional-undeclared-variable-check.js @@ -1,5 +1,3 @@ -var t = require("../../types"); - exports.optional = true; exports.Identifier = function (node, parent, scope, context, file) { diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index f100615f12..55296c8c50 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -120,6 +120,7 @@ Scope.prototype.generateTempBasedOnNode = function (node, file) { }; Scope.prototype.getInfo = function () { + var parent = this.parent; var block = this.block; if (block._scopeInfo) return block._scopeInfo; @@ -132,6 +133,10 @@ Scope.prototype.getInfo = function () { if (!reference) Scope.add(node, declarations); }; + if (parent && t.isBlockStatement(block) && t.isFor(parent.block)) { + return info; + } + // ForStatement - left, init if (t.isFor(block)) { @@ -139,6 +144,10 @@ Scope.prototype.getInfo = function () { var node = block[key]; if (t.isBlockScoped(node)) add(node); }); + + if (t.isBlockStatement(block.body)) { + block = block.body; + } } // Program, BlockStatement - let variables diff --git a/lib/6to5/types/index.js b/lib/6to5/types/index.js index 8bc6cb5d75..8e719cf369 100644 --- a/lib/6to5/types/index.js +++ b/lib/6to5/types/index.js @@ -487,7 +487,8 @@ t.getIds.nodes = { MemeberExpression: ["object"], SpreadElement: ["argument"], Property: ["value"], - ComprehensionBlock: ["left"] + ComprehensionBlock: ["left"], + AssignmentPattern: ["left"] }; t.getIds.arrays = { diff --git a/package.json b/package.json index a283cf4061..59691adede 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "6to5", "description": "Turn ES6 code into readable vanilla ES5 with source maps", - "version": "2.13.3", + "version": "2.13.5", "author": "Sebastian McKenzie ", "homepage": "https://6to5.org/", "repository": "6to5/6to5", @@ -62,6 +62,6 @@ "uglify-js": "2.4.16" }, "optionalDependencies": { - "kexec": "0.2.0" + "kexec": "1.1.0" } } diff --git a/test/fixtures/transformation/es6-default-parameters/destructuring/exec.js b/test/fixtures/transformation/es6-default-parameters/destructuring/exec.js new file mode 100644 index 0000000000..3148c98145 --- /dev/null +++ b/test/fixtures/transformation/es6-default-parameters/destructuring/exec.js @@ -0,0 +1,19 @@ +function required(msg) { + throw new Error(msg); +} + +function sum( + { arr = required('arr is required') } = { arr: arr = [] }, + length = arr.length +) { + let i = 0; + let acc = 0; + for (let item of arr) { + if (i >= length) return acc; + acc += item; + i++; + } + return acc; +} + +assert.equal(sum({arr:[1,2]}), 3); diff --git a/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/exec.js b/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/exec.js new file mode 100644 index 0000000000..0ce1188288 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/exec.js @@ -0,0 +1,2 @@ +var foo = []; +assert.deepEqual([for (foo of [1, 2, 3]) foo], [1, 2, 3]); diff --git a/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/options.json b/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/options.json new file mode 100644 index 0000000000..252f473a73 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/exec-collision-array-comprehension/options.json @@ -0,0 +1,3 @@ +{ + "experimental": true +} diff --git a/test/fixtures/transformation/es6-let-scoping/exec-collision-for/exec.js b/test/fixtures/transformation/es6-let-scoping/exec-collision-for/exec.js new file mode 100644 index 0000000000..367c09b2e5 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/exec-collision-for/exec.js @@ -0,0 +1,6 @@ +let x = 0; +for (;;) { + let x = 1; + assert.equal(x, 1); + break; +} diff --git a/test/fixtures/transformation/es6-let-scoping/exec-destructuring-defaults/exec.js b/test/fixtures/transformation/es6-let-scoping/exec-destructuring-defaults/exec.js new file mode 100644 index 0000000000..a440bcb047 --- /dev/null +++ b/test/fixtures/transformation/es6-let-scoping/exec-destructuring-defaults/exec.js @@ -0,0 +1,5 @@ +var fields = [{ name: "title" }, { name: "content" }]; + +for (let { name, value = "Default value" } of fields) { + assert.equal(value, "Default value"); +}