Open4

Romeコードリーディング

erukitierukiti

Rome は興味はあるものの、今は何ができるのかがわからないので、まずは動かしてみる。

git clone git@github.com:rome/tools.git rome-tools
cd rome-tools

いきなり tools なんてディレクトリ作られても困るので rome-tools に展開。

rome というシェルスクリプトや rome.bat っていうバッチファイルがあるので、これを実行すればコンパイルを勝手にやった上で rome を実行してくれる。開発便利スクリプトってところかな。ただ、rome-tools ディレクトリでやるとだめっぽいので、実験用のディレクトリを作って実行するっていうちょいめんどい感じに。

mkdir <other dir>/rome-test
cd <other dir>/rome-test
<rome dir>/rome init

<other dir> で別のディレクトリに実験用ディレクトリを作成。<rome dir>でさっき git clone してきたディレクトリを指定。

.editorconfig と、.config/rome.rjson が生成される。前者は、EditorConfigの設定なので良しとしておいて後者はなぞの独自形式 Rome JSONによるProject Configuration — Rome Toolchain

// For configuration documentation see https://rome.tools/#project-configuration
name: "rome-test"
root: true

初期状態だとこんな感じ。

とりあえず、src/index.ts に適当なコードを書く。

const hoge = 1;
% ~/work/code-reading/rome-tools/rome check
 Building trunk 
! This command has been hidden. Consider its usage to be experimental and do not expect support or

 src/index.ts:1:6 lint/js/noUnusedVariables ━━━━━━━━━━━━

  ✖ The const variable hoge is unused.

    const hoge = 1;
          ^^^^

  ℹ Unused variables are dead code and usually the result of incomplete refactoring.

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

✖ Found 1 problem

横いっぱいに横線を表示するのはどうなんだ?とか思ったり思わなかったり。

とりあえず、unused variables で dead code やでーと言われる。

ひとまずここまで動けば、コードリーディングの足がかりにはちょうど良さそう

erukitierukiti

./rome

./rome は、shスクリプト(正確には bash スクリプト)で、やってることは node "$DIR/scripts/dev-rome.cjs" "$@" なだけなので、scripts/dev-rome.cjs を起動してるだけ。

  • //# Utils から、画面装飾用のユーティリティ
  • //# Validate Node version で、Node 12 より前のバージョンは蹴る
  • //# init で初期化
    • isDevDaemonRunning は、daemon mode 動いてる rome がいないか確認する関数
    • buildTrunkscripts/vendor/rome.cjsinternal/cli/bin/rome.ts を bundle して tempDevBuildFolder の指すテンポラリに index.js を吐き出す
    • execDevtempDevBuildFolder のテンポラリの index.js を実行する(コマンドラインの引数をそのまま受け取る)

という流れ。srcipts/vendor/rome.cjs は吐き出されたコードなので読んでも仕方ないから、素直に internal/cli/bin/rome.ts を読みに行く。

internal

% ls internal
ast                     codec-binary-serial     codec-websocket         fs                      node                    string-utils
ast-utils               codec-config            collections             html-parser             ob1                     test-helpers
async                   codec-js-manifest       commit-parser           js-analysis             parser-core             typescript-helpers
child-process           codec-js-regexp         compiler                js-ast-utils            path                    v8
cli                     codec-json              consume                 js-parser               path-match              vcs
cli-diagnostics         codec-semver            core                    js-parser-utils         pretty-format           virtual-packages
cli-environment         codec-source-map        css-parser              markdown-parser         project                 web-ui
cli-flags               codec-spdx-license      diagnostics             markup                  string-charcodes
cli-layout              codec-tar               events                  markup-syntax-highlight string-diff
cli-reporter            codec-url               formatter               messages                string-escape

Rome の売りは、ゼロ依存なので、もう何もかもここに入ってる。見た感じ、どのディレクトリにも package.json が置いてある。

internal/cli/bin/rome.ts

ROME_PROCESS_TYPE 環境変数に応じて、server worker testWorker cli が起動。

  • internal/cli/server.ts
  • internal/cli/worker.ts
  • internal/cli/testWorker.ts
  • internal/cli/cli.ts

CLIとして起動すると、internal/cli/cli.ts が呼ばれる。

internal/cli/cli.ts

どうでもいいけど、Node.js だと、process.title = "hoge" とかやれば、プロセスタイトルを変更できるのか。

はいきました。なんというかやってることは単純そうなのに、くっそめんどい感じのソース。まぁ必要に応じて真面目に読むとして、

@internal/core が基本。

実行可能なコマンドは、localCommands serverCommands に書いてある。./rome check を走らせる場合 serverCommandscheckinternal/core/server/commands/check.ts がそれに該当するっぽい。

実行するときに Client クラスから client オブジェクトを作成して最終的には、client.query メソッドを実行する。

真面目に開発するなら、ここらへんの詳細も読むべきなんだろうけど今は読み飛ばす。

とりあえず internal/core/server/commands/check.ts の最後で、Linter クラスの throwSingle() メソッドを実行してることまではわかるので、ひとまずそこを追いかける。

疲れたので、とりあえずここまで。続きは internal/core/server/linter/Linter.tsLinter クラス、特に runSingle メソッドを読む。

erukitierukiti

ぱっと読んだ感じは、まぁ watch モードを前提に組み立てていて、シングルショットで実行するのはどちらかというとその処理の中のごく一例みたいな実装