毎回エディタからファイル作成したり、名前空間指定したりするのが面倒なので、コードを生成できるようにした。
コードの生成というか、ファイル生成に近いです。
github.com
使い方
名前空間とディレクトリのペアに対応させるジェネレータをマッピングする設定ファイルを書いて、
オートロードで読み込めるようにするだけで、あとはコマンド実行するだけでクラスファイルを出力してくれる。
設定ファイルを用意する
<?hh
namespace MyPackage\Generators;
use HHPack\Codegen\Cli\{ GeneratorProvider };
use HHPack\Codegen\HackUnit\{ TestClassGenerator };
use HHPack\Codegen\Project\{ PackageClassGenerator };
use function HHPack\Codegen\Cli\{ namespace_of, library, library_test };
final class Generators implements GeneratorProvider {
const string PACKAGE_NAMESPACE = 'MyPackage';
const string PACKAGE_TEST_NAMESPACE = 'MyPackage\Test';
public function generators(): Iterator<Pair<GenerateType, ClassFileGenerator>> {
yield library(
namespace_of(static::PACKAGE_NAMESPACE, 'src')
->map(PackageClassGenerator::class));
yield library_test(
namespace_of(static::PACKAGE_TEST_NAMESPACE, 'test')
->map(TestClassGenerator::class));
}
}
hh_autoload.jsonに設定を追加
次にhh_autoload.jsonのdevRootsにパスを設定します。
下記の設定だと、configディレクトリが読みこみ対象になります。
{
"roots": "src",
"devRoots": "config"
}
hhvm_autoloadが必要なので、インストールしてください。
github.com
これだけでコードが生成できるようになります。
コードの生成
パッケージのクラスを生成する
生成するタイプとクラス名を指定すると、設定ファイルで指定したディレクトリにファイルを生成できます。
ファイルにはあらかじめ名前空間が設定されるので、指定する手間が省けます。
クラスファイル
libを指定すると、クラスファイルを生成します。
vendor/bin/codegen lib Foo\LibClass
テストクラスファイル
testを指定すると、テスト用のクラスファイルを生成します。
vendor/bin/codegen test Foo\LibClassTest
独自ジェネレータ
ClassFileGeneratableインターフェースを実装すれば独自のジェネレータを定義できます。
下記は、プレーンなクラスファイルを生成するジェネレータです。
どういうファイルが生成可能かはhack-codegenを参考にしてください。
shapeを定義したり、trait定義できたり、結構いろんなものが生成できます。
github.com
<?hh
namespace MyPackage\Generators;
use HHPack\Codegen\{GenerateClass, ClassFileGeneratable};
use Facebook\HackCodegen\{ICodegenFactory, CodegenFile, CodegenClass};
final class CustomGenerator implements ClassFileGeneratable {
public function __construct(private ICodegenFactory $cg) {}
public static function from(ICodegenFactory $factory): this {
return new self($factory);
}
public function generate(GenerateClass $class): CodegenFile {
return $this->cg
->codegenFile($class->fileName())
->setIsStrict(true)
->setNamespace($class->belongsNamespace());
}
private function classOf(string $className): CodegenClass {
return $this->cg->codegenClass($className)->setIsFinal(true);
}
}