diff --git a/lib/6to5/transformation/transformers/es6/constants.js b/lib/6to5/transformation/transformers/es6/constants.js index c9a16416e9..a92dfce39a 100644 --- a/lib/6to5/transformation/transformers/es6/constants.js +++ b/lib/6to5/transformation/transformers/es6/constants.js @@ -5,83 +5,38 @@ var t = require("../../../types"); var _ = require("lodash"); var visitor = { - enter: function (child, parent, scope, context, state) { - if (child._ignoreConstant) return; - if (t.isVariableDeclaration(child)) return; + enter: function (node, parent, scope, context, state) { + if (t.isDeclaration(node) || t.isAssignmentExpression(node)) { + var ids = t.getIds(node, true); - if (t.isVariableDeclarator(child) || t.isDeclaration(child) || t.isAssignmentExpression(child)) { - state.check(parent, state.getIds(child), scope); + for (var key in ids) { + var id = ids[key]; + + var constant = state.constants[key]; + + // no constant exists + if (!constant) continue; + + // check if the assignment id matches the constant declaration id + // if it does then it was the id used to initially declare the + // constant so we can just ignore it + if (id === constant) continue; + + throw state.file.errorWithNode(id, key + " is read-only"); + } + } else if (t.isScope(node)) { + context.skip(); } } }; -exports.Program = -exports.BlockStatement = -exports.ForInStatement = -exports.ForOfStatement = -exports.ForStatement = function (node, parent, scope, context, file) { - var hasConstants = false; - var constants = {}; - - /** - * Check the results of `util.getIds` as `names` generated from a - * node against it's parent. - */ - - var check = function (parent, names, scope) { - for (var name in names) { - var nameNode = names[name]; - if (!_.has(constants, name)) continue; - if (parent && t.isBlockStatement(parent) && parent !== constants[name]) continue; - - if (scope) { - var defined = scope.get(name); - if (defined && defined === nameNode) continue; - } - - throw file.errorWithNode(nameNode, name + " is read-only"); - } - }; - - var getIds = function (node) { - return t.getIds(node, true, ["MemberExpression"]); - }; - - /** - * Collect all constants in this scope. - */ - - _.each(node.body, function (child, parent) { - if (t.isExportDeclaration(child)) { - child = child.declaration; - } - - if (t.isVariableDeclaration(child, { kind: "const" })) { - for (var i = 0; i < child.declarations.length; i++) { - var declar = child.declarations[i]; - - var ids = getIds(declar); - for (var name in ids) { - var nameNode = ids[name]; - - var names = {}; - names[name] = nameNode; - check(parent, names); - - constants[name] = parent; - hasConstants = true; - } - - declar._ignoreConstant = true; - } - - child._ignoreConstant = true; - child.kind = "let"; - } +exports.Scope = function (node, parent, scope, context, file) { + traverse(node, visitor, scope, { + constants: scope.getAllOfKind("const"), + file: file }); - - if (!hasConstants) return; - - var state = { check: check, getIds: getIds }; - traverse(node, visitor, scope, state); +}; + +exports.VariableDeclaration = function (node) { + if (node.kind === "const") node.kind = "let"; }; diff --git a/lib/6to5/traverse/scope.js b/lib/6to5/traverse/scope.js index 950fe6508c..f22b5a0ecf 100644 --- a/lib/6to5/traverse/scope.js +++ b/lib/6to5/traverse/scope.js @@ -25,6 +25,7 @@ function Scope(block, parent, file) { var info = this.getInfo(); this.references = info.references; this.declarations = info.declarations; + this.declarationKinds = info.declarationKinds; } var vars = require("jshint/src/vars"); @@ -187,10 +188,15 @@ Scope.prototype.getInfo = function () { var info = block._scopeInfo = {}; var references = info.references = {}; var declarations = info.declarations = {}; + var declarationKinds = info.declarationKinds = {}; var add = function (node, reference, throwOnDuplicate) { self._add(node, references); - if (!reference) self._add(node, declarations, throwOnDuplicate); + + if (!reference) { + self._add(node, declarations, throwOnDuplicate); + self._add(node, declarationKinds[node.kind] = declarationKinds[node.kind] || {}); + } }; if (parent && t.isBlockStatement(block) && t.isFor(parent.block)) { @@ -240,7 +246,7 @@ Scope.prototype.getInfo = function () { // Function - params, rest if (t.isFunction(block)) { - add(block.rest); + if (block.rest) add(block.rest); _.each(block.params, function (param) { add(param); }); @@ -290,6 +296,24 @@ Scope.prototype.add = function (node) { scope._add(node, scope.references); }; +/** + * Walks the scope tree and gathers all declarations of `kind`. + * + * @returns {Object} + */ + +Scope.prototype.getAllOfKind = function (kind) { + var ids = {}; + + var scope = this; + do { + _.defaults(ids, scope.declarationKinds[kind]); + scope = scope.parent; + } while (scope); + + return ids; +}; + /** * Description *