以前調査したtypescript-generator、その後もパラメーター調整しながら使用しているが、困ったのがヒアドキュメント。
Java側ではJavadocでクラスやプロパティの説明を書いているが、生成したTypeScriptのインターフェース定義ファイルにはデフォルトだと出力されないため、フロント実装時にJava側の定義を見ないと使いづらい。
Swaggerアノテーションの @ApiModel をクラスに、 @ApiModelProperty をプロパティに付与すると、TypeScript定義ファイルに出力されるが、JavadocとSwaggerアノテーション、両方書くのは面倒。
JavadocからSwagger UIを生成することはSwagger Docletでできそうだが、Swaggerアノテーションを生成するわけではないし、Javadoc書かずにSwaggerアノテーションだけ書くのも何だし、と思っていたら、typescript-generator自体が、xml-docletを使ってJavadocからTSDocを生成する機能を持っていたのでメモ。
環境
OpenJDK 8 8.275.01, typescript-generator 2.27.744, xml-doclet 1.0.5
TSDoc生成処理の概要
Javadoc · vojtechhabarta/typescript-generator Wiki · GitHub に設定方法の記載がある。
まず、xml-docletを使ってXML形式のJavadocを出力し、それをtypescript-generatorに読み込ませれば、パースしてTSDocとして出力してくれる。
設定
Gradleへのxml-docletの導入
まずはxml-docletの導入から。
プロジェクト管理ツールにMavenを使っている場合は、xml-docletのREADME、およびtypescript-generatorのWikiを見ればいいが、Gradleの場合の例がないのでやや面倒。
Javadocツールはドックレットを切り替えることで、出力形式を変更できる。GradleでJavadocタスクにカスタムドックレットを指定する方法はこちら。
javadocタスクを有効化するため、javaプラグインを有効化configurationsでドックレット名を指定dependenciesでドックレットの依存性を追加- この時、
implementationなど指定するところをドックレット名にする
- この時、
type: Javadocとなるタスクを追加
java プラグインはもともと入っていたため、以下の設定を追加。
configurations {
xmlDoclet
}
dependencies {
xmlDoclet 'com.github.markusbernhardt:xml-doclet:1.0.5'
}
final def tsOutputDir = 'build/typescript-generator'
final def tsXmlDocletDir = "${tsOutputDir}/xml-doclet"
task xmlDoclet(type: Javadoc) {
source = sourceSets.main.allJava
destinationDir = (tsXmlDocletDir as File).absoluteFile
title = null // 「-doctitleは無効なフラグです」でエラーとなるため抑制
options.noTimestamp = false // 既に廃止済みのオプションがデフォルトで付与され、エラーとなるため抑制
options.encoding = 'UTF-8' // XMLの出力文字コード
options.showAll() // publicなフィールド・メソッド以外のJavadocも出力
options.doclet = 'com.github.markusbernhardt.xmldoclet.XmlDoclet'
options.docletpath = configurations.xmlDoclet.files as List
}
xml-docletは3年前で更新が止まっており、デフォルトだとOpenJDKのjavadocツールと非互換が出ていたため、一部のオプションを指定して抑制している。
また、 showAll() しているのは、getterがあるがJavadocがない場合、フィールドのJavadocを元にTSDocを出力してくれるため。Lombokを使っている場合に有効。
出力ファイル名のデフォルトは javadoc.xml 。前述の設定で gradle xmlDoclet を実行すると、 build/typescript-generator/xml-doclet/javadoc.xml が出力される。
ちなみに、処理の詳細としては、 build/tmp/xmlDoclet/javadoc.options にファイルが出力され、 javadoc @build/tmp/xmlDoclet/javadoc.options のようにJavadocがオプションファイル指定で実行される模様。
typescript-generatorにjavadoc.xmlを読み込ませる
generateTypeScript タスクの javadocXmlFiles に、読み込ませるXMLファイルのパスをFileのListとして設定するだけで、TSDocを出力してくれる。
また、 generateTypeScript.dependsOn にXML出力タスクを追加しておくと gradle generateTypeScript で合わせて実行できる。
generateTypeScript {
...
javadocXmlFiles = ["${tsXmlDocletDir}/javadoc.xml" as File]
}
generateTypeScript.dependsOn compileJava, xmlDoclet
build.gradleの例
typescript-generatorの設定を含めた build.gradle は、以下のようになる。いまだに apply plugin なのがちょっと残念。
apply plugin: 'java' apply plugin: 'cz.habarta.typescript-generator' buildscript { dependencies { classpath 'cz.habarta.typescript-generator:typescript-generator-gradle-plugin:2.27.744' } } configurations { xmlDoclet } dependencies { xmlDoclet 'com.github.markusbernhardt:xml-doclet:1.0.5' } final def tsOutputDir = 'build/typescript-generator' final def tsXmlDocletDir = "${tsOutputDir}/xml-doclet" generateTypeScript { ... javadocXmlFiles = ["${tsXmlDocletDir}/javadoc.xml" as File] } task xmlDoclet(type: Javadoc) { source = sourceSets.main.allJava destinationDir = (tsXmlDocletDir as File).absoluteFile title = null options.noTimestamp = false options.encoding = 'UTF-8' options.showAll() options.doclet = 'com.github.markusbernhardt.xmldoclet.XmlDoclet' options.doclet = 'com.github.markusbernhardt.xmldoclet.XmlDoclet' } generateTypeScript.dependsOn compileJava, xmlDoclet
出力例
以下のクラスを用意。
/** インターフェース */ public interface ExampleInterface { /** * インターフェースのメソッド * * @return 文字列 */ String getInterfaceMethod(); } /** クラス */ @lombok.Data public class ExampleClass implements ExampleInterface { /** クラスのフィールド */ private String classField; private String interfaceMethod; } /** 列挙型 */ public enum ExampleEnum { /** 1 */ ONE, /** 2 */ TWO, /** 3 */ THREE; }
これに対して、前述の build.gradle の generateTypeScript タスクを以下のように変更して、 gradle generateTypeScript してみる。
generateTypeScript {
jsonLibrary = 'jackson2'
classPatterns = [各クラスのパッケージ]
outputKind = 'module'
outputFileType = 'implementationFile'
outputFile = "${tsOutputDir}/example.ts"
sortDeclarations = true
stringQuotes = 'singleQuotes'
indentString = ' '
javadocXmlFiles = ["${tsXmlDocletDir}/javadoc.xml" as File]
}
結果は以下のようになる(ヘッダのコメント部分は省略)。
/** * クラス */ export interface ExampleClass extends ExampleInterface { /** * クラスのフィールド */ classField: string; } /** * インターフェース */ export interface ExampleInterface { /** * インターフェースのメソッド * @return 文字列 */ interfaceMethod: string; } /** * 列挙型 * * Values: * - `ONE` - 1 * - `TWO` - 2 * - `THREE` - 3 */ export type ExampleEnum = 'ONE' | 'TWO' | 'THREE';
XMLをパースしているためか、元のJavadocをワンライナーで記述しても、TSDocの出力は複数行になっているなど、多少形式に違いはあるが、概要以外に @return なども出力される。
EnumはデフォルトではUnionにマッピングされるため、typeのTSDocとして各値の説明が出力される。
generateTypeScript のタスク定義に mapEnum = 'asEnum' を追加し、Enumにマッピングすると、以下のように、各値にもTSDocが追加される。
/** * 列挙型 * * Values: * - `ONE` - 1 * - `TWO` - 2 * - `THREE` - 3 */ export const enum ExampleEnum { /** * 1 */ ONE = 'ONE', /** * 2 */ TWO = 'TWO', /** * 3 */ THREE = 'THREE', }
振り返り
Java側のコードに手を入れることなく、Gradleタスクを追加するだけでJavadocからTSDocへの変換を行えるようになった。
ただ、xml-docletが更新停止状態で、JDK11以降の新しいJavadoc APIに対応していない模様。
typescript-generatorの開発者の方がPull Request出したりしているが動きなし。
先々まで使い続けることはできないかもしれないのが難点か。