babel-restore-jsx.mjs 4.4 KB

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