JSXについて改めて理解する

## はじめに
ReactのJSXについて、なんとなく使っている感があったので改めて整理してみました。

## JSXってなんなの？
ReactのcreateElementをhtmlライクにかけるようにするものです。
JSXはBabelによって最終的にcreateElementに変換されるようです。
[babel-plugin-transform-react-jsx](https://github.com/babel/babel/blob/3e4723fd07a0ef22039b508ec4da71ce73be1e6e/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L300)で変換を行なっていそうです。
```ts
    // Builds JSX into:
    // Production: React.createElement(type, arguments, children)
    // Development: React.createElement(type, arguments, children, source, self)
    function buildCreateElementCall(
      path: NodePath<JSXElement>,
      file: PluginPass,
    ) {
      const openingPath = path.get("openingElement");

      return call(file, "createElement", [
        getTag(openingPath),
        buildCreateElementOpeningElementAttributes(
          file,
          path,
          openingPath.get("attributes"),
        ),
        // @ts-expect-error JSXSpreadChild has been transformed in convertAttributeValue
        ...t.react.buildChildren(path.node),
      ]);
    }
```
このbabelプラグインでは[JSXFragment](https://github.com/babel/babel/blob/3e4723fd07a0ef22039b508ec4da71ce73be1e6e/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L287)、[JSXElement](https://github.com/babel/babel/blob/3e4723fd07a0ef22039b508ec4da71ce73be1e6e/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L300C9-L300C19)、[JSXAttribute](https://github.com/babel/babel/blob/3e4723fd07a0ef22039b508ec4da71ce73be1e6e/packages/babel-plugin-transform-react-jsx/src/create-plugin.ts#L316)について変換を行なっています。

### JSXFragment/JSXElement
JSXFragmentは[`<Fragment>(<>...</>)`](https://ja.react.dev/reference/react/Fragment)の構文で使用されるものです。
JSXElementは`<div>`のようなhtml風のelementです。
変換の中は似たような動きをしているのでいっぺんに紹介します。
```ts
        JSXFragment: {
          exit(path, file) {
            let callExpr;
            if (get(file, "runtime") === "classic") {
              callExpr = buildCreateElementFragmentCall(path, file);
            } else {
              callExpr = buildJSXFragmentCall(path, file);
            }

            path.replaceWith(t.inherits(callExpr, path.node));
          },
        },
        JSXElement: {
          exit(path, file) {
            let callExpr;
            if (
              get(file, "runtime") === "classic" ||
              shouldUseCreateElement(path)
            ) {
              callExpr = buildCreateElementCall(path, file);
            } else {
              callExpr = buildJSXElementCall(path, file);
            }

            path.replaceWith(t.inherits(callExpr, path.node));
          },
        },
```
runtimeがclassicかautomaticかで変わるようです。デフォルトはautomaticのようです。
classicの場合、JSXで記述されたコードは直接`React.createElement`に変換されます。
automaticの場合、JSXで記述されたコードは`_jsx`や`_jsxs`に変換されます。
`React.createElement`に変換されるのと、`_jsx`に変換される時の挙動の違いとして、`_jsx`はJSXの記載時に`import React from 'react';`の記述が不要になります。詳しくはは[新しい JSX トランスフォーム](https://ja.legacy.reactjs.org/blog/2020/09/22/introducing-the-new-jsx-transform.html)に載っています。

buildJSXFragmentCallやbuildJSXElementCallの延長で小要素を取得して`_jsx`に置き換えをしています。

### JSXAttribute
JSXAttributeはJSXで記述した際の属性を示しています。`<a href={linkTarget()}>link</a>`とかでいうと`href={linkTarget()}`がattributeです。

```ts
        JSXAttribute(path) {
          if (t.isJSXElement(path.node.value)) {
            path.node.value = t.jsxExpressionContainer(path.node.value);
          }
        },
```
JSXAttributeのnodeがJSXElementの場合、[`jsxExpressionContainer`](https://github.com/babel/babel/blob/026c8b39bca2a4a6f2524a8df20af9fa2c18dbb6/packages/babel-types/src/builders/generated/index.ts#L1627)でvalidationをかけて、結果をnodeに格納しているようです。
[NodeTypeがExpressionかJSXEmptyExpressionであればvalidationがpassしそうです。](https://github.com/babel/babel/blob/026c8b39bca2a4a6f2524a8df20af9fa2c18dbb6/packages/babel-types/src/definitions/jsx.ts#L90)
ExpressionはJavascriptの式であれば基本的にExpressionに当たるようです。JSXEmptyExpressionはJSXの中括弧 {} の間に何もない場合に生成されるようです。
以下のようなコードの`{ }`がそれに当たります。
```tsx
<>
 { }
</>
```
結果、JSXAttributeはajavascriptの式か、空の式であるかどうかをチェックして、問題がなければそのまま値を格納するような動きをしていそうです。

## おわりに
ここまでで一通りJSXの変換の断片を見てきました。
JSXはBabelによってJSXFragment/JSXElement/JSXAttributeなどに分類され、ツリー状のASTとして表現されます。
JSXFragment/JSXElementの場合は`_jsx`（その先はcreateElement）または、createElementに変換され、JSXAttributeはvalidationのみをされていることがわかりました。
また、runtimeをautomaticに設定することで、直接createElementに変換されず、`_jsx`に変換されるため、`import React from 'react';`を記述することなくJSXの構文で記述できることがわかりました。

次はcreateElementについて読んでみていきたいと思います。