Skip to content

Commit 2b763fc

Browse files
committed
Rewrite vendor/constants.js to use require("ast-types").traverse.
Most notably, this new style of transformation gives us access to this.parent.node, which allows us to avoid replacing identifiers that are not actually free variables, such as member expression properties. Closes react#496.
1 parent b137a33 commit 2b763fc

2 files changed

Lines changed: 42 additions & 40 deletions

File tree

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
"grunt-contrib-jshint": "~0.6.0",
5050
"optimist": "~0.6.0",
5151
"phantomjs": "~1.9",
52-
"recast": "~0.4.16",
52+
"recast": "~0.4.24",
5353
"semver": "~2.1.0",
5454
"uglify-js": "~2.4.0",
5555
"grunt-contrib-clean": "~0.5.0",

vendor/constants.js

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -16,58 +16,60 @@
1616
'use strict';
1717

1818
var recast = require('recast');
19+
var types = recast.types;
20+
var namedTypes = types.namedTypes;
21+
var builders = types.builders;
22+
var hasOwn = Object.prototype.hasOwnProperty;
1923

20-
exports.propagate = function(constants, source) {
21-
var ast = recast.parse(source);
22-
ast = new ConstantVisitor(constants).visit(ast);
23-
return recast.print(ast);
24-
};
24+
function propagate(constants, source) {
25+
return recast.print(transform(recast.parse(source), constants));
26+
}
2527

26-
var ConstantVisitor = recast.Visitor.extend({
27-
init: function(constants) {
28-
this.constants = constants || {};
29-
},
28+
function transform(ast, constants) {
29+
constants = constants || {};
3030

31-
visitIdentifier: function(ident) {
32-
if (this.constants.hasOwnProperty(ident.name)) {
33-
return recast.builder.literal(this.constants[ident.name]);
34-
}
35-
},
31+
return types.traverse(ast, function(node, traverse) {
32+
if (namedTypes.Identifier.check(node)) {
33+
if (namedTypes.MemberExpression.check(this.parent.node) &&
34+
this.name === 'property' &&
35+
!this.parent.node.computed) {
36+
return false;
37+
}
3638

37-
visitCallExpression: function(call) {
38-
if (!this.constants.__DEV__) {
39-
if (call.callee.type === 'Identifier' && call.callee.name === 'invariant') {
40-
call.arguments.length = 1;
39+
if (hasOwn.call(constants, node.name)) {
40+
this.replace(builders.literal(constants[node.name]));
41+
return false;
4142
}
42-
}
43-
this.genericVisit(call);
44-
},
4543

46-
visitIfStatement: function(stmt) {
47-
// Replaces all identifiers in this.constants with literal values.
48-
this.genericVisit(stmt);
44+
} else if (namedTypes.CallExpression.check(node)) {
45+
if (!constants.__DEV__) {
46+
if (namedTypes.Identifier.check(node.callee) &&
47+
node.callee.name === 'invariant') {
48+
node.arguments.length = 1;
49+
}
50+
}
4951

50-
if (stmt.test.type === recast.Syntax.Literal) {
51-
if (stmt.test.value) {
52-
stmt.alternate = null;
53-
} else if (stmt.alternate) {
54-
return stmt.alternate;
52+
} else if (namedTypes.IfStatement.check(node) &&
53+
namedTypes.Literal.check(node.test)) {
54+
if (node.test.value) {
55+
node.alternate = null;
56+
} else if (node.alternate) {
57+
this.replace(node.alternate);
58+
return false;
5559
} else {
56-
// In case this if statement is an alternate clause for another
57-
// if-statement, replacing that alternate with null will have the
58-
// effect of pruning the unnecessary clause. If this is just a
59-
// free-floating if statement, replacing it with null will have
60-
// the effect of removing it from the enclosing list of
61-
// statements.
62-
return null;
60+
this.replace(); // Remove the if-statement.
61+
return false;
6362
}
6463
}
65-
}
66-
});
64+
});
65+
}
6766

6867
if (!module.parent) {
6968
var constants = JSON.parse(process.argv[3]);
7069
recast.run(function(ast, callback) {
71-
callback(new ConstantVisitor(constants).visit(ast));
70+
callback(transform(ast, constants));
7271
});
7372
}
73+
74+
exports.propagate = propagate;
75+
exports.transform = transform;

0 commit comments

Comments
 (0)