Skip to content

Commit 4029378

Browse files
committed
When packaging, copy the code and inline __DEV__
Fixes facebook#812. Previously, this code module.exports = moo(); function moo() { return __DEV__; } would be transformed to module.exports = moo(); function moo() { return "production" !== process.env.NODE_ENV; } Now, it's transformed to: if ("production" !== process.env.NODE_ENV) { var moo = function() { return true; }; module.exports = moo(); } else { var moo = function() { return false; }; module.exports = moo(); } which reduces the getter cost to one test at require time instead of inline for every `__DEV__` check, warning, and invariant. The unminified build is about twice as large now (but it's about the same after gzipping) and the minified build is the same size.
1 parent c913c95 commit 4029378

File tree

2 files changed

+128
-20
lines changed

2 files changed

+128
-20
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@
7373
},
7474
"preferGlobal": true,
7575
"commonerConfig": {
76-
"version": 4
76+
"version": 5
7777
},
7878
"scripts": {
7979
"test": "./node_modules/.bin/grunt test"

vendor/constants.js

Lines changed: 127 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,96 @@ var DEV_EXPRESSION = builders.binaryExpression(
4242
function transform(ast, constants) {
4343
constants = constants || {};
4444

45+
var devProgram = copyAst(ast.program);
46+
var prodProgram = copyAst(ast.program);
47+
48+
devProgram = transformDev(hoistFunctions(devProgram), constants);
49+
prodProgram = transformProd(hoistFunctions(prodProgram), constants);
50+
51+
return builders.program([
52+
builders.ifStatement(
53+
DEV_EXPRESSION,
54+
builders.blockStatement(devProgram.body),
55+
builders.blockStatement(prodProgram.body)
56+
)
57+
]);
58+
}
59+
60+
function copyAst(node) {
61+
if (node instanceof RegExp) {
62+
return node;
63+
} else if (node instanceof Array) {
64+
return node.map(copyAst);
65+
} else if (typeof node === "object" && node != null) {
66+
var newNode = Object.create(Object.getPrototypeOf(node));
67+
for (var key in node) {
68+
if (!Object.prototype.hasOwnProperty.call(node, key)) {
69+
continue;
70+
}
71+
if (namedTypes.Node.check(node)) {
72+
newNode[key] = copyAst(node[key]);
73+
} else {
74+
newNode[key] = node[key];
75+
}
76+
}
77+
78+
Object.defineProperty(newNode, "original", {
79+
value: node.original,
80+
configurable: false,
81+
enumerable: false,
82+
writable: true
83+
});
84+
85+
return newNode;
86+
} else {
87+
return node;
88+
}
89+
}
90+
91+
function isUseStrict(node) {
92+
return node &&
93+
namedTypes.ExpressionStatement.check(node) &&
94+
namedTypes.Literal.check(node.expression) &&
95+
node.expression.value === "use strict";
96+
}
97+
98+
function hoistFunctions(program) {
99+
var functionVariableDeclarations = [];
100+
101+
var body = program.body.slice();
102+
for (var i = 0; i < body.length; i++) {
103+
var node = body[i];
104+
if (namedTypes.FunctionDeclaration.check(node)) {
105+
functionVariableDeclarations.push(
106+
builders.variableDeclaration("var", [
107+
builders.variableDeclarator(
108+
node.id,
109+
builders.functionExpression(
110+
null,
111+
node.params,
112+
node.body,
113+
node.generator,
114+
node.expression,
115+
// Switch to node.async after upgrading esprima-fb
116+
false
117+
)
118+
)
119+
])
120+
);
121+
body.splice(i, 1);
122+
i--;
123+
}
124+
}
125+
126+
// Insert functions after "use strict", if present
127+
body.splice.apply(
128+
body,
129+
[isUseStrict(body[0]) ? 1 : 0, 0].concat(functionVariableDeclarations)
130+
);
131+
return builders.program(body);
132+
}
133+
134+
function transformDev(ast, constants) {
45135
return types.traverse(ast, function(node, traverse) {
46136
if (namedTypes.Identifier.check(node)) {
47137
// If the identifier is the property of a member expression
@@ -53,12 +143,40 @@ function transform(ast, constants) {
53143
return false;
54144
}
55145

146+
if (node.name === '__DEV__') {
147+
// Replace __DEV__ with 'true'
148+
this.replace(builders.literal(true));
149+
return false;
150+
56151
// There could in principle be a constant called "hasOwnProperty",
57152
// so be careful always to use Object.prototype.hasOwnProperty.
153+
} else if (hasOwn.call(constants, node.name)) {
154+
this.replace(builders.literal(constants[node.name]));
155+
return false;
156+
}
157+
}
158+
});
159+
}
160+
161+
function transformProd(ast, constants) {
162+
return types.traverse(ast, function(node, traverse) {
163+
if (namedTypes.Identifier.check(node)) {
164+
// If the identifier is the property of a member expression
165+
// (e.g. object.property), then it definitely is not a constant
166+
// expression that we want to replace.
167+
if (namedTypes.MemberExpression.check(this.parent.node) &&
168+
this.name === 'property' &&
169+
!this.parent.node.computed) {
170+
return false;
171+
}
172+
58173
if (node.name === '__DEV__') {
59-
// replace __DEV__ with process.env.NODE_ENV !== 'production'
60-
this.replace(DEV_EXPRESSION);
174+
// Replace __DEV__ with 'false'
175+
this.replace(builders.literal(false));
61176
return false;
177+
178+
// There could in principle be a constant called "hasOwnProperty",
179+
// so be careful always to use Object.prototype.hasOwnProperty.
62180
} else if (hasOwn.call(constants, node.name)) {
63181
this.replace(builders.literal(constants[node.name]));
64182
return false;
@@ -67,30 +185,20 @@ function transform(ast, constants) {
67185
} else if (namedTypes.CallExpression.check(node)) {
68186
if (namedTypes.Identifier.check(node.callee) &&
69187
node.callee.name === 'invariant') {
70-
// Truncate the arguments of invariant(condition, ...)
71-
// statements to just the condition based on NODE_ENV
72-
// (dead code removal will remove the extra bytes).
188+
// Truncate the arguments of invariant(condition, ...) statements to
189+
// just the condition
73190
this.replace(
74-
builders.conditionalExpression(
75-
DEV_EXPRESSION,
76-
node,
77-
builders.callExpression(
78-
node.callee,
79-
[node.arguments[0]]
80-
)
191+
builders.callExpression(
192+
node.callee,
193+
[node.arguments[0]]
81194
)
82195
);
83196
return false;
84197
} else if (namedTypes.Identifier.check(node.callee) &&
85198
node.callee.name === 'warning') {
86-
// Eliminate warning(condition, ...) statements based on NODE_ENV
87-
// (dead code removal will remove the extra bytes).
199+
// Eliminate warning(condition, ...) statements
88200
this.replace(
89-
builders.conditionalExpression(
90-
DEV_EXPRESSION,
91-
node,
92-
builders.literal(null)
93-
)
201+
builders.literal(null)
94202
);
95203
}
96204
}

0 commit comments

Comments
 (0)