babel-restore-jsx.cjs 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. 'use strict';
  2. /**
  3. * https://github.com/flying-sheep/babel-plugin-transform-react-createelement-to-jsx
  4. * @license GNU General Public License v3.0
  5. */
  6. function babelRestoreJsx({ types: t }, { reactAlias = "React" }) {
  7. function getJSXNode(node) {
  8. if (!isReactCreateElement(node)) {
  9. return null;
  10. }
  11. const [nameNode, propsNode, ...childNodes] = node.arguments;
  12. const name = getJSXName(nameNode);
  13. if (name == null) {
  14. return null;
  15. }
  16. const props = getJSXProps(propsNode);
  17. if (props == null) {
  18. return null;
  19. }
  20. const children = getJSXChildren(childNodes);
  21. if (children == null) {
  22. return null;
  23. }
  24. if (t.isJSXMemberExpression(name) && t.isJSXIdentifier(name.object) && name.object.name === reactAlias && name.property.name === "Fragment") {
  25. return t.jsxFragment(
  26. t.jsxOpeningFragment(),
  27. t.jsxClosingFragment(),
  28. children
  29. );
  30. }
  31. const selfClosing = children.length === 0;
  32. const startTag = t.jsxOpeningElement(name, props, selfClosing);
  33. startTag.loc = node.loc;
  34. const endTag = selfClosing ? null : t.jsxClosingElement(name);
  35. return t.jsxElement(startTag, endTag, children, selfClosing);
  36. }
  37. function getJSXName(node) {
  38. if (node == null) {
  39. return null;
  40. }
  41. const name = getJSXIdentifier(node, true);
  42. if (name != null) {
  43. return name;
  44. }
  45. if (!t.isMemberExpression(node)) {
  46. return null;
  47. }
  48. const object = getJSXName(node.object);
  49. const property = getJSXName(node.property);
  50. if (object == null || property == null) {
  51. return null;
  52. }
  53. return t.jsxMemberExpression(object, property);
  54. }
  55. function getJSXProps(node) {
  56. if (node == null || isNullLikeNode(node)) {
  57. return [];
  58. }
  59. if (t.isCallExpression(node) && t.isIdentifier(node.callee, { name: "_extends" })) {
  60. const props = node.arguments.map(getJSXProps);
  61. if (props.every((prop) => prop != null)) {
  62. return [].concat(...props);
  63. }
  64. }
  65. if (!t.isObjectExpression(node) && t.isExpression(node))
  66. return [t.jsxSpreadAttribute(node)];
  67. if (!isPlainObjectExpression(node)) {
  68. return null;
  69. }
  70. return node.properties.map(
  71. (prop) => t.isObjectProperty(prop) ? t.jsxAttribute(
  72. getJSXIdentifier(prop.key),
  73. getJSXAttributeValue(prop.value)
  74. ) : t.jsxSpreadAttribute(prop.argument)
  75. ).filter(
  76. (prop) => t.isJSXIdentifier(prop.name) ? prop.name.name !== "__self" && prop.name.name !== "__source" : true
  77. );
  78. }
  79. function getJSXChild(node) {
  80. if (t.isStringLiteral(node)) {
  81. return t.jsxText(node.value);
  82. }
  83. if (isReactCreateElement(node)) {
  84. return getJSXNode(node);
  85. }
  86. if (t.isExpression(node)) {
  87. return t.jsxExpressionContainer(node);
  88. }
  89. return null;
  90. }
  91. function getJSXChildren(nodes) {
  92. const children = nodes.filter((node) => !isNullLikeNode(node)).map(getJSXChild);
  93. if (children.some((child) => child == null)) {
  94. return null;
  95. }
  96. return children;
  97. }
  98. function getJSXIdentifier(node, tag = false) {
  99. if (t.isIdentifier(node) && (!tag || node.name.match(/^[A-Z]/))) {
  100. return t.jsxIdentifier(node.name);
  101. }
  102. if (t.isStringLiteral(node)) {
  103. return t.jsxIdentifier(node.value);
  104. }
  105. return null;
  106. }
  107. function getJSXAttributeValue(node) {
  108. if (t.isStringLiteral(node)) {
  109. return node;
  110. }
  111. if (t.isJSXElement(node)) {
  112. return node;
  113. }
  114. if (t.isExpression(node)) {
  115. return t.jsxExpressionContainer(node);
  116. }
  117. return null;
  118. }
  119. const isReactCreateElement = (node) => t.isCallExpression(node) && t.isMemberExpression(node.callee) && t.isIdentifier(node.callee.object, { name: reactAlias }) && t.isIdentifier(node.callee.property, { name: "createElement" }) && !node.callee.computed;
  120. const isNullLikeNode = (node) => t.isNullLiteral(node) || t.isIdentifier(node, { name: "undefined" });
  121. const isPlainObjectExpression = (node) => t.isObjectExpression(node) && node.properties.every(
  122. (property) => t.isSpreadElement(property) || t.isObjectProperty(property, { computed: false }) && getJSXIdentifier(property.key) != null && getJSXAttributeValue(property.value) != null
  123. );
  124. return {
  125. visitor: {
  126. CallExpression(path) {
  127. const node = getJSXNode(path.node);
  128. if (node == null) {
  129. return null;
  130. }
  131. path.replaceWith(node);
  132. }
  133. }
  134. };
  135. }
  136. exports.default = babelRestoreJsx;