個人的に興味があって触る機会があったので、公式サイトで勉強しながらまとめた。
Laikaとは
- Laika*1とは、Scalaで記述された静的サイトジェネレータ。
- LaikaのサイトもLaikaで生成されている。
- 高度にモジュール化されているため、例えばプログラム上から変換処理だけ呼び出すといったこともできる。このため一部だけ拡張させるといったことが容易にできる。
- Markdown入力に標準で対応している。
- HTML/PDF/EPUB出力に標準で対応している。
- sbtのプラグインとして動作させるとScalaのコードを書かずに最小限の設定で静的サイトを生成できる。
- Scala.js上でも動作するため、フロントエンドサイドで動的にHTMLを生成することもできる。
Laikaを試す
Scala CLIを使ったScala Scriptを書いて最小限のHTML生成を行うには、laika.scala.sc
というファイルを作成し、以下のように書く:
//> using scala 3.3.1 //> using dep "org.typelevel::laika-core::1.0.0" import laika.api._ import laika.format._ val transformer = Transformer .from(Markdown) .to(HTML) .build val md = """# Hello, Laika! This is exercise for **Laika**. """ val result = transformer.transform(md) println(result.right.get)
このコードを実行する:
% scala-cli laika.scala.sc
すると、Markdownが変換されたHTMLが標準出力に出てくる:
<h1 id="hello-laika" class="section">Hello, Laika!</h1> <p>This is exercise for <strong>Laika</strong>.</p>
Laikaがデフォルトで対応しているフォーマット
- Laikaは入出力を
Format
という概念で抽象化している。 - 入力、出力に対して
Format
を指定することで、Laikaはこれを利用して入力形式から中間形式を生成し、そして出力形式に変換する。 - Laikaが提供する
Format
は、laika.format
以下で提供される。 - 入力として使える
Format
として、LaikaはデフォルトでMarkdown
とReStructuredText
を提供する。 - 出力として使える
Format
として、LaikaはデフォルトでAST
とEPUB
とHTML
とPDF
とXSLFO
を提供する。AST
はデバッグ用。
例えば、MarkdownのASTを出力してみる:
//> using scala 3.3.1 //> using dep "org.typelevel::laika-core::1.0.0" import laika.api._ import laika.format._ val transformer = Transformer .from(Markdown) .to(AST) .build val md = """# Hello, Laika! This is exercise for **Laika**. """ val result = transformer.transform(md) println(result.right.get)
コードを実行すると、Laikaが内部で扱っている内部表現が得られる:
RootElement - Blocks: 1 . Section . . Header(1,Id(hello-laika) + Styles(section)) - Spans: 1 . . . Text - 'Hello, Laika!' . . Content - Blocks: 1 . . . Paragraph - Spans: 3 . . . . Text - 'This is exercise for ' . . . . Strong - Spans: 1 . . . . . Text - 'Laika' . . . . Text - '.'
デバッグするときはこれを利用する。
ExtensionBundle
- Laikaでは、
Format
が文書をパースした後の抽象構文木に対して、追加で後処理をかけることができる仕組み(ExtensionBundle
)がある。 ExtensionBundle
を利用するには、.using(extensionBundle)
を使う。- Laikaは、デフォルトで
Markdown.GitHubFlavor
やSyntaxHighlighting
などのExtensionBundle
を提供する。
例えば、Scalaのコードをシンタックスハイライトしてもらう:
//> using scala 3.3.1 //> using dep "org.typelevel::laika-core::1.0.0" import laika.api._ import laika.format._ import laika.config.SyntaxHighlighting val transformer = Transformer .from(Markdown) .to(HTML) .using(Markdown.GitHubFlavor) .using(SyntaxHighlighting) .build val md = """# Hello, Laika! ```scala val to = "Laika" println(s"Hello, $to!") ``` """ val result = transformer.transform(md) println(result.right.get)
SyntaxHighlighting
を使うにはいったん言語を認識させる必要があるのでGitHubFlavor
も必要になる。
Laikaと副作用
- Laikaは関数型のスタイルで記述されているため、ファイルに対する入出力やテーマの適用といった副作用を伴う仕組みは追加のモジュールで提供される。
- Laikaで副作用まわりを扱うには、
laika-io
を利用する。 laika-io
は、Cats Effectという副作用を専門に扱うライブラリをその基盤としている。
例えば、変換結果をファイルに出力するには以下のように書く:
//> using scala 3.3.1 //> using dep "org.typelevel::laika-core::1.0.0" //> using dep "org.typelevel::laika-io::1.0.0" import cats.effect.{IO, IOApp, ExitCode} import laika.api._ import laika.format._ import laika.io.syntax._ // Laikaのテーマを使うために必要 import laika.helium.Helium import laika.helium.config._ import laika.ast.Path.Root // IOApp.Simpleを使うとIO[Unit]を返すrunメソッドを実装するだけで良い object Main extends IOApp.Simple: // テーマが必要なので、Laikaがデフォルトで提供するHeliumテーマを使う val theme = Helium.defaults.site .topNavigationBar( // ホームを設定しないとテーマが動作しないのでここで設定する homeLink = IconLink.internal(Root / "example.md", HeliumIcon.home) ) .build def makeTransformer = Transformer .from(Markdown) .to(HTML) // Cats Effectが提供するIO型を使って副作用を扱う .parallel[IO] // テーマを設定する .withTheme(theme) .build def run: IO[Unit] = makeTransformer.use { transformer => transformer .fromDirectory("laikaFrom") .toDirectory("laikaTarget") .transform }.void // voidはIO[Unit]に変換する(runメソッドの戻り値がIO[Unit]でないとコンパイルエラーになる) // 明示的に呼び出してやる Main.main(args)
./laikaFrom/example.md
に以下のようなMarkdownファイルを書く:
# Example Document This is example document to describe Laika.
スクリプトを実行すると./laikaTarget/example.html
にHTMLが生成されている:
.parallel[IO]
によってCats Effect向けの型に包まれてTransformer
が返されるので、直接これを利用するのではなく、.use
メソッドを使ってその中で利用する形になる。
Laikaをsbtプラグインから利用する
- Laikaはsbtプラグインとしても利用できる。
- MarkdownからHTMLサイトを生成するといった一般的なドキュメント生成のために、sbtからLaikaを呼び出すために利用する。
下準備として、sbtプロジェクトを作成する。
Laikaをsbtプラグインとして呼び出すには、sbtプロジェクト(空のディレクトリでもよい)のproject/plugins.sbt
に以下のように記載する:
addSbtPlugin("org.typelevel" % "laika-sbt" % "1.0.0")
次に、build.sbt
に以下のように追記する:
import laika.helium.Helium import laika.helium.config._ import laika.ast.Path.Root enablePlugins(LaikaPlugin) laikaTheme := Helium.defaults.site .topNavigationBar( // ホームを設定しないとテーマが動作しないのでここで設定する homeLink = IconLink.internal(Root / "hello.md", HeliumIcon.home) ) .build
デフォルトではLaika sbtプラグインはsrc/docs
をドキュメントディレクトリだと認識する(設定によって変更可)ので、src/docs/hello.md
を作成する:
# Hello, Laika!
This is example file for _Laika sbt plugin_.
最後にsbt laikaSite
を実行するとHTMLがtarget/docs/site/
以下に生成される。
% sbt laikaSite ... % cat target/docs/site/hello.html <!DOCTYPE html> ...
今日のところはここまで。
応用
Laikaは拡張性の高い構造になっているため、以下のような拡張ができる:
- テーマを自作する
- Formatを自作して好きなフォーマットから好きなフォーマットへと出力する
- ExtensionBundleを自作する
- Scaladocへのリンクを作る
今回はまだ勉強しきっていないので割愛するが、これらの内容についてもおいおい触れていこうと思う。
*1:公式サイトを見るに、名前はたぶん宇宙犬から来ている