ApexLibrarian
Apex クラスの司書。床に散らばった本を拾い、あるべき棚に戻します。
Salesforce の org には Apex クラスのフォルダという概念がありません。リポジトリには Usecases/opportunity/ や Handlers/account/ といった階層があるのに、クラスが org を経由した瞬間 (同僚が Setup 画面で作った、別リポジトリに retrieve した、パッケージをインストールして取得した) にその配置は剥がれ落ち、ファイルは classes/ 直下にフラットに着地します。
ApexLibrarian は、置き場所を クラス自身の doc コメントに宣言させる ことでこの問題を解決する npm 製 CLI です。宣言はこう書きます。
/**
* 商談の値引き額を明細から再計算する。
* @directory Usecases/opportunity
*/
public with sharing class ReconcileDiscount {
doc コメントはクラス本体の一部なので、@directory タグは org との往復を何度でも生き延びます。retrieve のあとにコマンドを 1 回叩けば、すべてのクラスがあるべき棚に戻ります。
Install
npm install --save-dev apex-librarian
# または直接実行
npx apex-librarian check
ファイルを動かすツールなので、チーム全員と CI で同じ挙動になるよう devDependency としてバージョンを固定するのがおすすめです。
Commands
タグが 宣言、ファイルシステムが 現実 です。各コマンドは両者のズレを一方向に解消します。
| Command | 方向 | 使う場面 |
|---|---|---|
apex-librarian check | 比較のみ | CI / pre-commit。ズレがあれば exit 1 |
apex-librarian arrange | タグ → 配置 | sf project retrieve のあと。タグの示すディレクトリへファイルを移動する (.cls-meta.xml も一緒に) |
apex-librarian stamp | 配置 → タグ | 既存プロジェクトへの導入時、意図的な git mv のあと |
Options
--root <dir> 対象のクラスルート (複数指定可。省略時は見つかった "classes" ディレクトリすべて)
--dry-run ファイルに触れず、何が起きるかだけ表示する
--require-tag check のみ: サブディレクトリにある未タグのクラスも失敗にする
--include-submodules
stamp/check のみ: git submodule 内のファイルも対象にする (下記「For framework authors」)
Behavior notes
.cls-meta.xmlは 必ずクラスとペアで 移動します。- git submodule は自動で除外されます (
.gitmodulesのパスを見ます)。取り込んだフレームワークの配置は upstream の管轄なので、司書は submodule 内のファイルを動かしたりタグを書き込んだりしません。 arrangeは 決して上書きしません。移動先が埋まっていたら報告してスキップします (exit 1)。- タグは トップレベルの型宣言に付いた doc ブロック にのみ結び付きます。ファイル先頭のライセンスヘッダは数えず、inner class は無視します。
@directory .はクラスルート直下を意味します。絶対パスと..は拒否されます。- タグの無いクラスは
arrange/checkでは放置されます (--require-tagを付ければタグ必須にできます)。stampはサブディレクトリに既に置かれているクラスへタグを追記します。 - できればコミット済みのワーキングツリーで実行してください。移動はすべて単純な rename なので、
git statusで司書がやったことがそのまま見え、git checkout .で取り消せます。
Typical flows
既存プロジェクトへの導入
現在の配置を全クラスに一度だけ書き込みます。
npx apex-librarian stamp
git add -A && git commit -m "Stamp @directory tags"
retrieve のあと
同僚が org 上で作ったクラスはフラットに落ちてきます。まとめて棚に戻します。
sf project retrieve start -o my-org ...
npx apex-librarian arrange
クラスを意図的に移動したあと
新しい置き場所をタグへ書き戻します。
git mv force-app/main/default/classes/Foo.cls force-app/main/default/classes/Services/
npx apex-librarian stamp
CI example (GitHub Actions)
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx apex-librarian check --require-tag
For framework authors
フレームワークを unlocked package として配布すると、利用者が retrieve したクラスはフラットに落ちます。行き先をソース自体に焼き込んでおきましょう。利用者に置いてほしい形 (classes/<YourFramework>/ 配下の submodule) でフレームワークを配置したチェックアウトから、実際の階層をそのまま刻印します。
npx apex-librarian stamp --include-submodules
npx apex-librarian check --include-submodules # 確認
これで各クラスが @directory MyFramework/Eloquents/tests のように実際の位置を持ちます。submodule 側でタグをコミットしてリリースすれば、パッケージをインストールした利用者は npx apex-librarian arrange を叩くだけで、クラスが classes/MyFramework/... に自動で並びます。どの経路で導入しても配置は同じになり、その配置はクラス自身が宣言しています。
取り込んだフレームワークのファイルは利用者が動かしてよいものではないので、submodule のパスは既定で除外されています。--include-submodules は、その除外をフレームワーク作者側のチェックアウトでだけ解除するためのフラグです。arrange はこのフラグ自体を受け付けません (submodule のワーキングツリーからファイルを持ち出すと submodule が壊れるためです)。
Apex Stem の 4 フレームワーク (ApexEloquent v3.7.0 / ApexBlueprint v2.1.0 / ApexTrace v1.5.0 / ApexTools v1.1.0 以降) は、この方法で全クラスに刻印済みです。インストールガイド も参照してください。
Links
- GitHub: krile136/ApexLibrarian
- npm: apex-librarian
- CLI の全機能は通常の関数 (
analyze,planArrange,applyMovesなど) としても export されているので、ライブラリとして他のツールに組み込めます