babel-plugin-web-dev-expression
Version:
A browser compatible fork of Facebook's dev-expression Babel plugin
151 lines (143 loc) • 4.75 kB
JavaScript
/**
* Copyright (c) 2013-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
;
module.exports = function(babel) {
var t = babel.types;
var SEEN_SYMBOL = Symbol();
var DEV_EXPRESSION =
t.conditionalExpression(
t.binaryExpression(
"!=",
t.unaryExpression("typeof", t.identifier("process"), true),
t.stringLiteral("object")
),
t.booleanLiteral(false),
t.conditionalExpression(
t.unaryExpression(
"!",
t.memberExpression(t.identifier("process"), t.identifier("env"), false),
true
),
t.booleanLiteral(false),
t.binaryExpression(
"!==",
t.memberExpression(
t.memberExpression(
t.identifier("process"),
t.identifier("env"),
false
),
t.identifier("NODE_ENV"),
false
),
t.stringLiteral("production")
)
)
);
return {
visitor: {
Identifier: {
enter: function(path) {
// Do nothing when testing
if (process.env.NODE_ENV === 'test') {
return;
}
// replace global __DEV__ with process.env.NODE_ENV !== 'production'
if (
path.isIdentifier({name: '__DEV__'}) &&
path.scope.hasGlobal('__DEV__')
) {
path.replaceWith(DEV_EXPRESSION);
}
},
},
CallExpression: {
exit: function(path) {
var node = path.node;
// Do nothing when testing
if (process.env.NODE_ENV === 'test') {
return;
}
// Ignore if it's already been processed
if (node[SEEN_SYMBOL]) {
return;
}
if (path.get('callee').isIdentifier({name: 'invariant'})) {
// Turns this code:
//
// invariant(condition, argument, argument);
//
// into this:
//
// if (!condition) {
// if (typeof process != "object" ? false : !process.env ? false : process.env.NODE_ENV !== "production") {
// invariant(false, argument, argument);
// } else {
// invariant(false);
// }
// }
//
// Specifically this does 2 things:
// 1. Checks the condition first, preventing an extra function call.
// 2. Adds an environment check so that verbose error messages aren't
// shipped to production.
// The generated code is longer than the original code but will dead
// code removal in a minifier will strip that out.
var condition = node.arguments[0];
var devInvariant = t.callExpression(
node.callee,
[t.booleanLiteral(false)].concat(node.arguments.slice(1))
);
devInvariant[SEEN_SYMBOL] = true;
var prodInvariant = t.callExpression(
node.callee,
[t.booleanLiteral(false)]
);
prodInvariant[SEEN_SYMBOL] = true;
path.replaceWith(t.ifStatement(
t.unaryExpression('!', condition),
t.blockStatement([
t.ifStatement(
DEV_EXPRESSION,
t.blockStatement([
t.expressionStatement(devInvariant),
]),
t.blockStatement([
t.expressionStatement(prodInvariant),
])
),
])
));
} else if (path.get('callee').isIdentifier({name: 'warning'})) {
// Turns this code:
//
// warning(condition, argument, argument);
//
// into this:
//
// if (typeof process != "object" ? false : !process.env ? false : process.env.NODE_ENV !== "production") {
// warning(condition, argument, argument);
// }
//
// The goal is to strip out warning calls entirely in production. We
// don't need the same optimizations for conditions that we use for
// invariant because we don't care about an extra call in __DEV__
node[SEEN_SYMBOL] = true;
path.replaceWith(t.ifStatement(
DEV_EXPRESSION,
t.blockStatement([
t.expressionStatement(
node
),
])
));
}
},
},
},
};
};