dev-expression.js 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. /**
  2. * Copyright (c) 2013-present, Facebook, Inc.
  3. *
  4. * This source code is licensed under the MIT license found in the
  5. * LICENSE file in the root directory of this source tree.
  6. */
  7. 'use strict';
  8. module.exports = function(babel) {
  9. var t = babel.types;
  10. var SEEN_SYMBOL = Symbol();
  11. var DEV_EXPRESSION = t.binaryExpression(
  12. '!==',
  13. t.memberExpression(
  14. t.memberExpression(
  15. t.identifier('process'),
  16. t.identifier('env'),
  17. false
  18. ),
  19. t.identifier('NODE_ENV'),
  20. false
  21. ),
  22. t.stringLiteral('production')
  23. );
  24. return {
  25. visitor: {
  26. Identifier: {
  27. enter: function(path) {
  28. // Do nothing when testing
  29. if (process.env.NODE_ENV === 'test') {
  30. return;
  31. }
  32. // replace __DEV__ with process.env.NODE_ENV !== 'production'
  33. if (path.isIdentifier({name: '__DEV__'})) {
  34. path.replaceWith(DEV_EXPRESSION);
  35. }
  36. },
  37. },
  38. CallExpression: {
  39. exit: function(path) {
  40. var node = path.node;
  41. // Do nothing when testing
  42. if (process.env.NODE_ENV === 'test') {
  43. return;
  44. }
  45. // Ignore if it's already been processed
  46. if (node[SEEN_SYMBOL]) {
  47. return;
  48. }
  49. if (path.get('callee').isIdentifier({name: 'invariant'})) {
  50. // Turns this code:
  51. //
  52. // invariant(condition, argument, argument);
  53. //
  54. // into this:
  55. //
  56. // if (!condition) {
  57. // if ("production" !== process.env.NODE_ENV) {
  58. // invariant(false, argument, argument);
  59. // } else {
  60. // invariant(false);
  61. // }
  62. // }
  63. //
  64. // Specifically this does 2 things:
  65. // 1. Checks the condition first, preventing an extra function call.
  66. // 2. Adds an environment check so that verbose error messages aren't
  67. // shipped to production.
  68. // The generated code is longer than the original code but will dead
  69. // code removal in a minifier will strip that out.
  70. var condition = node.arguments[0];
  71. var devInvariant = t.callExpression(
  72. node.callee,
  73. [t.booleanLiteral(false)].concat(node.arguments.slice(1))
  74. );
  75. devInvariant[SEEN_SYMBOL] = true;
  76. var prodInvariant = t.callExpression(
  77. node.callee,
  78. [t.booleanLiteral(false)]
  79. );
  80. prodInvariant[SEEN_SYMBOL] = true;
  81. path.replaceWith(t.ifStatement(
  82. t.unaryExpression('!', condition),
  83. t.blockStatement([
  84. t.ifStatement(
  85. DEV_EXPRESSION,
  86. t.blockStatement([
  87. t.expressionStatement(devInvariant),
  88. ]),
  89. t.blockStatement([
  90. t.expressionStatement(prodInvariant),
  91. ])
  92. ),
  93. ])
  94. ));
  95. } else if (path.get('callee').isIdentifier({name: 'warning'})) {
  96. // Turns this code:
  97. //
  98. // warning(condition, argument, argument);
  99. //
  100. // into this:
  101. //
  102. // if ("production" !== process.env.NODE_ENV) {
  103. // warning(condition, argument, argument);
  104. // }
  105. //
  106. // The goal is to strip out warning calls entirely in production. We
  107. // don't need the same optimizations for conditions that we use for
  108. // invariant because we don't care about an extra call in __DEV__
  109. node[SEEN_SYMBOL] = true;
  110. path.replaceWith(t.ifStatement(
  111. DEV_EXPRESSION,
  112. t.blockStatement([
  113. t.expressionStatement(
  114. node
  115. ),
  116. ])
  117. ));
  118. }
  119. },
  120. },
  121. },
  122. };
  123. };