rewrite constants transformer
This commit is contained in:
parent
6959e60e2c
commit
870954c6be
@ -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";
|
||||
};
|
||||
|
||||
@ -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
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user