React のコンポーネントのソースコードをプログラムで変更する
前の記事でHTMLElementからFiberを取得する方法がわかったのでFiberから取得できる _debugSource というプロパティには下記のような情報が入っています。
{
"fileName": "/Users/kyohei/Develop/labelmake.jp/src/components/Page.tsx",
"lineNumber": 74,
"columnNumber": 17
}
今回作成するものはこの情報を使ってコンポーネントのclassNameを変更するというプログラムです。
この情報のlineNumber, colmunNumberはコンポーネントの始まりなのでそこと同じ行にclassNameがあるとは限らない。
なので一度ソースコードをパースする必要がある。
参考になったのは下記のアプリ

このアプリはカーソルがある要素のに対応したASTのNodeに対してハイライトできているので、FIberの_debugSourceの情報を使えばコンポーネントのASTのNodeを特定できると思った。
いろいろと調べていくとソースコードをパースして変更して、変更したソースコードを返却するには下記のBabelのモジュールを使うことで実現できるということがわかった。
Parsing
@babel/parser を用いて、ソースコードをASTに変換
Transformation
@babel/traverseを用いて、ASTを変換する、
Code Generat
@babel/generator を用いて ASTをソースコードに変換する

今回作成したのはあくまで動作確認程度のスクリプトですが、下記のレポジトリにコードがあります。
内容はこんな感じ。
inputとしてFiberのソースコードの情報から取れるfileNameとコンポーネントの開始行としてlineNumberをcomponentStartLineに名前変更しています。
あとはそのコンポーネントのattribute(classNameやidなどを変更できます。)、変更前の文字列と新しい値。
やっていることは簡単で、parseしてtraverseしてgenerateすれば実現できました。
traverseの部分でパースしたコンポーネントの木構造をもれなく検索していき、条件に合ったNodeに対してなにかしらの処理を追加することができます。
条件についてですが、下記のような条件で合致したNodeの値を書き換えています。
path.parent.loc?.start.line===componentStartLine&&
path.node.name.name===attribute&&(path.node.valueasStringLiteral).value===oldValue
親要素(コンポーネント自体)の開始行がFiberのlineNumberと一致しているか
input.attributeとNodeのnameが一致しているか
Nodeの値がoldValueと一致しているか
という条件です。
一致していれば値をnewValueと変更します。
このようにbabelを使えばfiberの情報からReact のコンポーネントのソースコードをプログラムで変更することができました。
次はVSCodeのExtensionの作り方を見ていきます。
最初に言い忘れましたが、これまで技術検証してきたコードを使ってVSCodeのExtentionを作るつもりです。
具体的にはビジュアルエディターでtailwindのclass名を変更し、ソースコードにも反映できるというものをプロトタイプとして作成します。
最終的にはFIgmaを触る感覚でReactのコンポーネントを作成していくことのできるビジュアルエディターを作ることを目標にしています。