es6-arrow-function-visitors.js 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  1. /**
  2. * Copyright 2013-present, Facebook, Inc.
  3. * All rights reserved.
  4. *
  5. * This source code is licensed under the BSD-style license found in the
  6. * LICENSE file in the root directory of this source tree. An additional grant
  7. * of patent rights can be found in the PATENTS file in the same directory.
  8. */
  9. /*global exports:true*/
  10. /**
  11. * Desugars ES6 Arrow functions to ES3 function expressions.
  12. * If the function contains `this` expression -- automatically
  13. * binds the function to current value of `this`.
  14. *
  15. * Single parameter, simple expression:
  16. *
  17. * [1, 2, 3].map(x => x * x);
  18. *
  19. * [1, 2, 3].map(function(x) { return x * x; });
  20. *
  21. * Several parameters, complex block:
  22. *
  23. * this.users.forEach((user, idx) => {
  24. * return this.isActive(idx) && this.send(user);
  25. * });
  26. *
  27. * this.users.forEach(function(user, idx) {
  28. * return this.isActive(idx) && this.send(user);
  29. * }.bind(this));
  30. *
  31. */
  32. var restParamVisitors = require('./es6-rest-param-visitors');
  33. var destructuringVisitors = require('./es6-destructuring-visitors');
  34. var Syntax = require('esprima-fb').Syntax;
  35. var utils = require('../src/utils');
  36. /**
  37. * @public
  38. */
  39. function visitArrowFunction(traverse, node, path, state) {
  40. var notInExpression = (path[0].type === Syntax.ExpressionStatement);
  41. // Wrap a function into a grouping operator, if it's not
  42. // in the expression position.
  43. if (notInExpression) {
  44. utils.append('(', state);
  45. }
  46. utils.append('function', state);
  47. renderParams(traverse, node, path, state);
  48. // Skip arrow.
  49. utils.catchupWhiteSpace(node.body.range[0], state);
  50. var renderBody = node.body.type == Syntax.BlockStatement
  51. ? renderStatementBody
  52. : renderExpressionBody;
  53. path.unshift(node);
  54. renderBody(traverse, node, path, state);
  55. path.shift();
  56. // Bind the function only if `this` value is used
  57. // inside it or inside any sub-expression.
  58. var containsBindingSyntax =
  59. utils.containsChildMatching(node.body, function(node) {
  60. return node.type === Syntax.ThisExpression
  61. || (node.type === Syntax.Identifier
  62. && node.name === "super")
  63. || (node.type === Syntax.JSXIdentifier
  64. && node.name === 'this');
  65. });
  66. if (containsBindingSyntax) {
  67. utils.append('.bind(this)', state);
  68. }
  69. utils.catchupWhiteSpace(node.range[1], state);
  70. // Close wrapper if not in the expression.
  71. if (notInExpression) {
  72. utils.append(')', state);
  73. }
  74. return false;
  75. }
  76. function renderParams(traverse, node, path, state) {
  77. // To preserve inline typechecking directives, we
  78. // distinguish between parens-free and paranthesized single param.
  79. if (isParensFreeSingleParam(node, state) || !node.params.length) {
  80. utils.append('(', state);
  81. }
  82. if (node.params.length !== 0) {
  83. path.unshift(node);
  84. traverse(node.params, path, state);
  85. path.unshift();
  86. }
  87. utils.append(')', state);
  88. }
  89. function isParensFreeSingleParam(node, state) {
  90. return node.params.length === 1 &&
  91. state.g.source[state.g.position] !== '(';
  92. }
  93. function renderExpressionBody(traverse, node, path, state) {
  94. // Wrap simple expression bodies into a block
  95. // with explicit return statement.
  96. utils.append('{', state);
  97. // Special handling of rest param.
  98. if (node.rest) {
  99. utils.append(
  100. restParamVisitors.renderRestParamSetup(node, state),
  101. state
  102. );
  103. }
  104. // Special handling of destructured params.
  105. destructuringVisitors.renderDestructuredComponents(
  106. node,
  107. utils.updateState(state, {
  108. localScope: {
  109. parentNode: state.parentNode,
  110. parentScope: state.parentScope,
  111. identifiers: state.identifiers,
  112. tempVarIndex: 0
  113. }
  114. })
  115. );
  116. utils.append('return ', state);
  117. renderStatementBody(traverse, node, path, state);
  118. utils.append(';}', state);
  119. }
  120. function renderStatementBody(traverse, node, path, state) {
  121. traverse(node.body, path, state);
  122. utils.catchup(node.body.range[1], state);
  123. }
  124. visitArrowFunction.test = function(node, path, state) {
  125. return node.type === Syntax.ArrowFunctionExpression;
  126. };
  127. exports.visitorList = [
  128. visitArrowFunction
  129. ];