diff --git a/lib/6to5/transform.js b/lib/6to5/transform.js index 1233403165..9ca0d31d7f 100644 --- a/lib/6to5/transform.js +++ b/lib/6to5/transform.js @@ -30,12 +30,22 @@ transform.test = function (task, assert) { var actualAst = actualResult.ast; actualCode = recast.prettyPrint(actualAst).code; - var expectCode = expect.code.trim(); - var expectAst = util.parse(expect, expectCode); - var expectResult = recast.prettyPrint(expectAst); - expectCode = expectResult.code; + if (task.options.exec) { + try { + var fn = new Function("assert", actualCode); + fn(assert); + } catch (err) { + console.log(actualCode); + throw err; + } + } else { + var expectCode = expect.code.trim(); + var expectAst = util.parse(expect, expectCode); + var expectResult = recast.prettyPrint(expectAst); + expectCode = expectResult.code; - assert.equal(actualCode, expectCode); + assert.equal(actualCode, expectCode); + } if (task.sourceMap) { assert.deepEqual(task.sourceMap, actualResult.map); diff --git a/lib/6to5/transformers/constants.js b/lib/6to5/transformers/constants.js index 6257b8a985..ebb19b1938 100644 --- a/lib/6to5/transformers/constants.js +++ b/lib/6to5/transformers/constants.js @@ -18,28 +18,7 @@ exports.ForStatement = function (node) { _.each(node.body, function (child) { if (child && child.type === "VariableDeclaration" && child.kind === "const") { _.each(child.declarations, function (declar) { - var search = [declar.id]; - var names = []; - - while (search.length) { - var id = search.shift(); - - if (id.type === "Identifier") { - names.push(id.name); - } else if (id.type === "ArrayPattern") { - _.each(id.elements, function (elem) { - search.push(elem); - }); - } else if (id.type === "ObjectPattern") { - _.each(id.properties, function (prop) { - search.push(prop.value); - }); - } else { - throw new Error("unknown node " + id.type); - } - } - - _.each(names, function (name) { + _.each(util.getIds(declar.id), function (name) { check(declar, name); constants.push(name); }); diff --git a/lib/6to5/transformers/let-scoping.js b/lib/6to5/transformers/let-scoping.js index e69de29bb2..19bd70d0cd 100644 --- a/lib/6to5/transformers/let-scoping.js +++ b/lib/6to5/transformers/let-scoping.js @@ -0,0 +1,68 @@ +var traverse = require("../traverse"); +var util = require("../util"); +var b = require("ast-types").builders; +var _ = require("lodash"); + +exports.VariableDeclaration = function (node, parent, file) { + if (node.kind !== "let") return; + node.kind = "var"; + + var ids = {}; + + _.each(node.declarations, function (declar) { + _.each(util.getIds(declar.id), function (id) { + ids[id] = b.identifier(file.generateUid(id)); + }); + }); + + var replaceId = function (node, parent) { + // not an identifier so we have no use for this node + if (node.type !== "Identifier") return; + + // not a let reference + var id = ids[node.name]; + if (!id) return; + + // we're a property key + if (parent.type === "Property" && parent.key === node) return; + + if (parent.type !== "MemberExpression") { + // we aren't in a member expression + return id; + } else if (parent.type === "MemberExpression" && parent.property === node && parent.computed) { + // we're in a member expression but we're a computed property so we're referenced + return id; + } else if (parent.type === "MemberExpression" && parent.object === node) { + // we're in a member expression and we're the object so we're referenced + return id; + } + }; + + var replace = function (node, parent) { + if (_.contains(traverse.FUNCTION_TYPES, node.type)) { + var letReferences = []; + + traverse(node, function (node, parent) { + var id = replaceId(node, parent); + if (id) letReferences.push(id); + return id; + }); + + if (letReferences.length) { + return b.callExpression( + b.functionExpression(null, letReferences, + b.blockStatement([ + b.returnStatement(node) + ] + ) + ), letReferences) + } else { + return false; + } + } + + return replaceId(node, parent); + }; + + traverse(parent, replace); +}; diff --git a/lib/6to5/util.js b/lib/6to5/util.js index 702840a495..4e042bc748 100644 --- a/lib/6to5/util.js +++ b/lib/6to5/util.js @@ -44,6 +44,31 @@ exports.removeProperties = function (tree) { return tree; }; +exports.getIds = function (node) { + var search = [node]; + var ids = []; + + while (search.length) { + var id = search.shift(); + + if (id.type === "Identifier") { + ids.push(id.name); + } else if (id.type === "ArrayPattern") { + _.each(id.elements, function (elem) { + search.push(elem); + }); + } else if (id.type === "ObjectPattern") { + _.each(id.properties, function (prop) { + search.push(prop.value); + }); + } else { + throw new Error("unknown node " + id.type); + } + } + + return ids; +}; + exports.errorWithNode = function (node, msg) { var line = node.loc.start.line; var col = node.loc.start.column; diff --git a/test/fixtures/syntax/constants/block-statement/expected.js b/test/fixtures/syntax/constants/block-statement/expected.js index 551349fa3b..9914078fb6 100644 --- a/test/fixtures/syntax/constants/block-statement/expected.js +++ b/test/fixtures/syntax/constants/block-statement/expected.js @@ -1,4 +1,4 @@ -for (let i in arr) { - let MULTIPLIER = 5; - console.log(arr[i] * MULTIPLIER); +for (var _i in arr) { + var _MULTIPLIER = 5; + console.log(arr[_i] * _MULTIPLIER); } diff --git a/test/fixtures/syntax/constants/destructuring/expected.js b/test/fixtures/syntax/constants/destructuring/expected.js index 350ef2cf1a..e0990ba2d1 100644 --- a/test/fixtures/syntax/constants/destructuring/expected.js +++ b/test/fixtures/syntax/constants/destructuring/expected.js @@ -1,21 +1,21 @@ var _ref = [1, 2]; -let a = _ref[0]; -let b = _ref[1]; +var _a = _ref[0]; +var _b = _ref[1]; var _ref2 = [3, 4]; -let c = _ref2[0]; -let d = _ref2[1]; +var _c = _ref2[0]; +var _d = _ref2[1]; var _ref3 = { e: 5, f: 6 }; -let e = _ref3.e; -let f = _ref3.f; +var _e = _ref3.e; +var _f = _ref3.f; var _ref4 = { a: 7, b: 8 }; -let g = _ref4.a; -let h = _ref4.b; +var _g = _ref4.a; +var _h = _ref4.b; diff --git a/test/fixtures/syntax/constants/program/expected.js b/test/fixtures/syntax/constants/program/expected.js index 7b8fde94e5..94190e5922 100644 --- a/test/fixtures/syntax/constants/program/expected.js +++ b/test/fixtures/syntax/constants/program/expected.js @@ -1,5 +1,5 @@ -let MULTIPLIER = 5; +var _MULTIPLIER = 5; for (var i in arr) { - console.log(arr[i] * MULTIPLIER); + console.log(arr[i] * _MULTIPLIER); } diff --git a/test/fixtures/syntax/for-of/let/expected.js b/test/fixtures/syntax/for-of/let/expected.js index 914c1a2ec2..d7d9f90684 100644 --- a/test/fixtures/syntax/for-of/let/expected.js +++ b/test/fixtures/syntax/for-of/let/expected.js @@ -1,3 +1,3 @@ for (var _iterator = arr[Symbol.iterator](), _step; !(_step = _iterator.next()).done;) { - let i = _step.value; + var _i = _step.value; } diff --git a/test/fixtures/syntax/let-scoping/deep-nested/actual.js b/test/fixtures/syntax/let-scoping/deep-nested/actual.js new file mode 100644 index 0000000000..a2884c039e --- /dev/null +++ b/test/fixtures/syntax/let-scoping/deep-nested/actual.js @@ -0,0 +1,8 @@ +var a = 'var a'; +{ + var b = 'var b'; + { + var c = 'var c'; + let d = 'let d'; + } +} diff --git a/test/fixtures/syntax/let-scoping/for-in-initializers/actual.js b/test/fixtures/syntax/let-scoping/for-in-initializers/actual.js new file mode 100644 index 0000000000..9864e590c3 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/for-in-initializers/actual.js @@ -0,0 +1,18 @@ +var result; +{ + let let_result = []; + let let_array = ['one', 'two', 'three']; + for (var index in let_array) { + let let_index = index; + let let_value = let_array[let_index]; + let_result.push( + function() { + return [let_index, let_value]; + }); + } + result = let_result; +} + +assert.deepEqual(['0', 'one'], result[0]()); +assert.deepEqual(['1', 'two'], result[1]()); +assert.deepEqual(['2', 'three'], result[2]()); diff --git a/test/fixtures/syntax/let-scoping/for-initializers/actual.js b/test/fixtures/syntax/let-scoping/for-initializers/actual.js new file mode 100644 index 0000000000..7b491b4a8d --- /dev/null +++ b/test/fixtures/syntax/let-scoping/for-initializers/actual.js @@ -0,0 +1,10 @@ +var result; +{ + let let_x = 'let x'; + let let_l = []; + for (var var_x = 1, var_y = 2, var_z = 3; var_x < 10; var_x ++) { + let l_x = var_x, l_y = var_y, l_z = var_z; + let_l.push( function() { return [ l_x, l_y, l_z ]; } ); + } + result = let_l; +} diff --git a/test/fixtures/syntax/let-scoping/in-class/actual.js b/test/fixtures/syntax/let-scoping/in-class/actual.js new file mode 100644 index 0000000000..4b509ca2d6 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-class/actual.js @@ -0,0 +1,15 @@ +class LetInClass { + get z() { + let let_z = 10; + return let_z; + } + + set z(v) { + let let_zv = v; + } + + distance() { + let dist = this.y - this.x; + return dist; + } +} diff --git a/test/fixtures/syntax/let-scoping/in-closure/actual.js b/test/fixtures/syntax/let-scoping/in-closure/actual.js new file mode 100644 index 0000000000..5581d89af3 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-closure/actual.js @@ -0,0 +1,21 @@ +function letInClosure(n) { + var l = []; + for (var i = 0; i < n; i ++) { + let let_i = i; + if (i % 3 == 0) { + continue; + } + l.push(function() { + return let_i; + }); + } + return l; +} + +var result = letInClosure(10); +assert.deepEqual(1, result[0]()); +assert.deepEqual(2, result[1]()); +assert.deepEqual(4, result[2]()); +assert.deepEqual(5, result[3]()); +assert.deepEqual(7, result[4]()); +assert.deepEqual(8, result[5]()); diff --git a/test/fixtures/syntax/let-scoping/in-for-break-inner/actual.js b/test/fixtures/syntax/let-scoping/in-for-break-inner/actual.js new file mode 100644 index 0000000000..dac158d86a --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-break-inner/actual.js @@ -0,0 +1,8 @@ +for (var x = 0; x < 10; x ++) { + for (var y = 0; y < 10; y ++) { + let z = 'let z'; + if (x == 7) { + break; + } + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for-break-named/actual.js b/test/fixtures/syntax/let-scoping/in-for-break-named/actual.js new file mode 100644 index 0000000000..37c14692ba --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-break-named/actual.js @@ -0,0 +1,9 @@ +outer: +for (var x = 0; x < 10; x ++) { + for (var y = 0; y < 10; y ++) { + let z = 'let z'; + if (x == 7) { + break outer; + } + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for-break/actual.js b/test/fixtures/syntax/let-scoping/in-for-break/actual.js new file mode 100644 index 0000000000..69c2680c24 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-break/actual.js @@ -0,0 +1,6 @@ +for (var x = 0; x < 10; x ++) { + let y = 'let y'; + if (x % 2 == 0) { + break; + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for-continue-inner/actual.js b/test/fixtures/syntax/let-scoping/in-for-continue-inner/actual.js new file mode 100644 index 0000000000..569b022665 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-continue-inner/actual.js @@ -0,0 +1,8 @@ +for (var x = 0; x < 10; x ++) { + for (var y = 0; y < 10; y ++) { + let z = 'let z'; + if (x == 7) { + continue; + } + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for-continue-named/actual.js b/test/fixtures/syntax/let-scoping/in-for-continue-named/actual.js new file mode 100644 index 0000000000..e1a8d96964 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-continue-named/actual.js @@ -0,0 +1,9 @@ +outer: +for (var x = 0; x < 10; x ++) { + for (var y = 0; y < 10; y ++) { + let z = 'let z'; + if (x == 7) { + continue outer; + } + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for-continue/actual.js b/test/fixtures/syntax/let-scoping/in-for-continue/actual.js new file mode 100644 index 0000000000..8da940c38e --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for-continue/actual.js @@ -0,0 +1,6 @@ +for (var x = 0; x < 10; x ++) { + let y = 'let y'; + if (x % 2 == 0) { + continue; + } +} diff --git a/test/fixtures/syntax/let-scoping/in-for/in-for.js b/test/fixtures/syntax/let-scoping/in-for/in-for.js new file mode 100644 index 0000000000..de1630e09e --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-for/in-for.js @@ -0,0 +1,3 @@ +for (var x = 0; x < 10; x ++) { + let y = 'let y'; +} diff --git a/test/fixtures/syntax/let-scoping/in-properties/actual.js b/test/fixtures/syntax/let-scoping/in-properties/actual.js new file mode 100644 index 0000000000..bc3cfefa66 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/in-properties/actual.js @@ -0,0 +1,15 @@ +var object = { + get x() { + while (true) { + let let_x = 'let x'; + return let_x; + } + }, + + set x(v) { + do { + let let_v = v; + this.v = let_v; + } while (false); + } +} diff --git a/test/fixtures/syntax/let-scoping/initializer-for-1/actual.js b/test/fixtures/syntax/let-scoping/initializer-for-1/actual.js new file mode 100644 index 0000000000..dd2909dea2 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/initializer-for-1/actual.js @@ -0,0 +1,3 @@ +for (let x = 1; x < 10; x++) { + x; +} diff --git a/test/fixtures/syntax/let-scoping/initializer-for-2/actual.js b/test/fixtures/syntax/let-scoping/initializer-for-2/actual.js new file mode 100644 index 0000000000..a2bd89dbac --- /dev/null +++ b/test/fixtures/syntax/let-scoping/initializer-for-2/actual.js @@ -0,0 +1,4 @@ +for (let x = 1, y = 2, z = 3; x < 10; x ++) { + y++; + z++; +} diff --git a/test/fixtures/syntax/let-scoping/initializer-for-3/actual.js b/test/fixtures/syntax/let-scoping/initializer-for-3/actual.js new file mode 100644 index 0000000000..4d709fa3fe --- /dev/null +++ b/test/fixtures/syntax/let-scoping/initializer-for-3/actual.js @@ -0,0 +1,5 @@ +for (let x = 1, y = x + 1; x < 10 && y != 0; x ++, y *= 2) { + if (y > 300) { + continue; + } +} diff --git a/test/fixtures/syntax/let-scoping/initializer-for-in/actual.js b/test/fixtures/syntax/let-scoping/initializer-for-in/actual.js new file mode 100644 index 0000000000..b80574713d --- /dev/null +++ b/test/fixtures/syntax/let-scoping/initializer-for-in/actual.js @@ -0,0 +1,5 @@ +let sum = 0; +let a = [1,2,3]; +for (let x in a) { + sum = sum + a[x]; +} diff --git a/test/fixtures/syntax/let-scoping/nested-conflict/actual.js b/test/fixtures/syntax/let-scoping/nested-conflict/actual.js new file mode 100644 index 0000000000..808a556509 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/nested-conflict/actual.js @@ -0,0 +1,12 @@ +(function() { + var x = 1; + function f() { + assert.equal(x, 1); + { + let x = 2; + assert.equal(x, 2); + } + assert.equal(x, 1); + } + f(); +})(); diff --git a/test/fixtures/syntax/let-scoping/nested-function-1/actual.js b/test/fixtures/syntax/let-scoping/nested-function-1/actual.js new file mode 100644 index 0000000000..0910f17774 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/nested-function-1/actual.js @@ -0,0 +1,5 @@ +function nestedFunction1() { + return function() { + let let_x = 'let x'; + } +} diff --git a/test/fixtures/syntax/let-scoping/nested-function-2/actual.js b/test/fixtures/syntax/let-scoping/nested-function-2/actual.js new file mode 100644 index 0000000000..aadbf8e873 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/nested-function-2/actual.js @@ -0,0 +1,6 @@ +function nestedFunction2() { + let let_func = function() { + let let_x = 'let x'; + } + return let_func; +} diff --git a/test/fixtures/syntax/let-scoping/nested-function-3/actual.js b/test/fixtures/syntax/let-scoping/nested-function-3/actual.js new file mode 100644 index 0000000000..3e45d93ff5 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/nested-function-3/actual.js @@ -0,0 +1,4 @@ +function nestedFunction3() { + let let_x = 'let x'; + function function_foo() { } +} diff --git a/test/fixtures/syntax/let-scoping/nested/actual.js b/test/fixtures/syntax/let-scoping/nested/actual.js new file mode 100644 index 0000000000..bf9b8f57d0 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/nested/actual.js @@ -0,0 +1,7 @@ +var x = 'var'; +{ + let y = 'let 1'; +} +{ + let y = 'let 2'; +} diff --git a/test/fixtures/syntax/let-scoping/no-initializer-global/actual.js b/test/fixtures/syntax/let-scoping/no-initializer-global/actual.js new file mode 100644 index 0000000000..f01f21783d --- /dev/null +++ b/test/fixtures/syntax/let-scoping/no-initializer-global/actual.js @@ -0,0 +1,4 @@ +let x2; +assert.equal(undefined, x2); +x2 = 2; +assert.equal(2, x2); diff --git a/test/fixtures/syntax/let-scoping/no-initializer/actual.js b/test/fixtures/syntax/let-scoping/no-initializer/actual.js new file mode 100644 index 0000000000..f9d28fb681 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/no-initializer/actual.js @@ -0,0 +1,8 @@ +var x = 1; +{ + let x; + assert.equal(undefined, x); + x = 2; + assert.equal(2, x); +} +assert.equal(1, x); diff --git a/test/fixtures/syntax/let-scoping/options.json b/test/fixtures/syntax/let-scoping/options.json new file mode 100644 index 0000000000..37997efa87 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/options.json @@ -0,0 +1,3 @@ +{ + "exec": true +} diff --git a/test/fixtures/syntax/let-scoping/regress-1381/actual.js b/test/fixtures/syntax/let-scoping/regress-1381/actual.js new file mode 100644 index 0000000000..b6f3d193a0 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/regress-1381/actual.js @@ -0,0 +1,8 @@ +function test() { + let ret = true; + while (false) + for (let i = 0; i < 1; i++) + ret = () => i; + return ret +} +assert.ok(test()); diff --git a/test/fixtures/syntax/let-scoping/with-for-in/actual.js b/test/fixtures/syntax/let-scoping/with-for-in/actual.js new file mode 100644 index 0000000000..e17059b7b2 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/with-for-in/actual.js @@ -0,0 +1,6 @@ +for (var x in [1,2,3]) { + let let_y = x; + for (var for_in_z in [4,5,6]) { + continue; + } +} diff --git a/test/fixtures/syntax/let-scoping/with-for/actual.js b/test/fixtures/syntax/let-scoping/with-for/actual.js new file mode 100644 index 0000000000..c6c178f941 --- /dev/null +++ b/test/fixtures/syntax/let-scoping/with-for/actual.js @@ -0,0 +1,6 @@ +for (var x = 0; x < 10; x++) { + let let_y = 'let y'; + for (var for_z = 0; for_z < 2; for_z ++) { + break; + } +} diff --git a/test/fixtures/syntax/let-scoping/with-switch/actual.js b/test/fixtures/syntax/let-scoping/with-switch/actual.js new file mode 100644 index 0000000000..1df909156d --- /dev/null +++ b/test/fixtures/syntax/let-scoping/with-switch/actual.js @@ -0,0 +1,12 @@ +for (var i = 0; i < 5; i ++) { + let let_x = 'let x'; + + switch (i) { + case 0: + break; + case 2: + break; + default: + break; + } +} diff --git a/test/fixtures/syntax/modules/exports-variable/expected.js b/test/fixtures/syntax/modules/exports-variable/expected.js index fad2fb03e0..8613280809 100644 --- a/test/fixtures/syntax/modules/exports-variable/expected.js +++ b/test/fixtures/syntax/modules/exports-variable/expected.js @@ -9,14 +9,14 @@ exports.foo2 = foo2; var foo3; exports.foo3 = foo3; -let foo4 = 2; -exports.foo4 = foo4; +var _foo4 = 2; +exports.foo4 = _foo4; -let foo5; -exports.foo5 = foo5; +var _foo5; +exports.foo5 = _foo5; -let foo6 = 3; -exports.foo6 = foo6; +var _foo6 = 3; +exports.foo6 = _foo6; function foo7 () {}