= Twirl
:page-aliases: Plugin_Twirl.adoc


Twirl templates support.

To declare a module that needs to compile twirl templates you must extend the `mill.twirllib.TwirlModule` trait when defining your module.
Also note that twirl templates get compiled into scala code, so you also need to extend `ScalaModule`.

.`build.mill`
[source,scala]
----
//| mvnDeps: ["com.lihaoyi::mill-contrib-twirl:$MILL_VERSION"]
package build

import mill.scalalib.*
import mill.twirllib.*

object app extends ScalaModule with TwirlModule {
// ...
}
----

== Details

The following filesystem layout is expected:

[source,text]
----
build.mill
app/
  views/
    view1.scala.html
    view2.scala.html
----

`TwirlModule` adds the `compileTwirl` task to the module:

[source,sh]
----
mill app.compileTwirl
----

(it will be automatically run whenever you compile your module)

This task will compile `*.scala.html` templates (and others, like `*.scala.txt`) into the `out/app/compileTwirl/dest`
directory. This directory must be added to the generated sources of the module to be compiled and made accessible from the rest of the code:

.`build.mill`
[source,scala]
----
//| mvnDeps: ["com.lihaoyi::mill-contrib-twirl:$MILL_VERSION"]
package build

import mill.scalalib.*
import mill.twirllib.*

object app extends ScalaModule with TwirlModule {
  def twirlVersion = "1.5.1"
  def generatedSources = Task { Seq(compileTwirl().classes) }
}
----

== Twirl configuration options

=== +def twirlVersion: T[String]+

Mandatory - the version of the Twirl compiler to use.

=== +def twirlScalaVersion: T[String]+

Mandatory - the Scala version matching the `twirlVersion`. Older Twirl versions (before 1.4.2) only supported Scala 2.12.

=== +def twirlImports: T[Seq[String]]+

The imports that will be added by the twirl compiler to the top of all templates, defaults to https://github.com/playframework/twirl/blob/1.5.0/compiler/src/main/scala/play/twirl/compiler/TwirlCompiler.scala#L166-L173[twirl's default imports]:

[source,scala]
----
Seq(
  "_root_.play.twirl.api.TwirlFeatureImports._",
  "_root_.play.twirl.api.TwirlHelperImports._",
  "_root_.play.twirl.api.Html",
  "_root_.play.twirl.api.JavaScript",
  "_root_.play.twirl.api.Txt",
  "_root_.play.twirl.api.Xml"
)
----

To add additional imports to all of the twirl templates, override `twirlImports` in your build:

.`build.mill`
[source,scala]
----
//| mvnDeps: ["com.lihaoyi::mill-contrib-twirllib:$MILL_VERSION"]
package build

import mill.scalalib.*
import mill.twirllib.*

object app extends ScalaModule with TwirlModule {
  def twirlVersion = "1.5.1"
  def twirlScalaVersion = "2.13.16"
  override def twirlImports = super.twirlImports() ++ Seq("my.additional.stuff._", "my.other.stuff._")
  def generatedSources = Task { Seq(compileTwirl().classes) }
}

// out.template.scala
@import _root_.play.twirl.api.TwirlFeatureImports._
// ...
@import _root_.play.twirl.api.Xml
@import my.additional.stuff._
@import my.other.stuff._
----

To exclude the default imports, simply override `twirlImports` without calling `super`:

.`build.mill`
[source,scala]
----
package build
object app extends ScalaModule with TwirlModule {
  // ...
  override def twirlImports = Seq("my.stuff._")
}

// out.template.scala
@import my.stuff._
----

=== +def twirlFormats: Map[String, String]+

A mapping of file extensions to class names that will be compiled by twirl, e.g. `Map("html" \-> "play.twirl.api.HtmlFormat")`.
By default `html`, `xml`, `js`, and `txt` files will be compiled using the corresponding https://github.com/playframework/twirl/blob/1.5.0/api/shared/src/main/scala/play/twirl/api/Formats.scala[twirl format].

To add additional formats, override `twirlFormats` in your build:

.`build.mill`
[source,scala]
----
//| mvnDeps: ["com.lihaoyi::mill-contrib-twirllib:$MILL_VERSION"]
package build

import mill.scalalib.*
import mill.twirllib.*

object app extends ScalaModule with TwirlModule {
  def twirlVersion = "1.5.1"
  override def twirlFormats = super.twirlFormats() ++ Map("svg" -> "play.twirl.api.HtmlFormat")
  def generatedSources = Task { Seq(compileTwirl().classes) }
}
----

=== +def twirlConstructorAnnotations: Seq[String] = Nil+

Annotations added to the generated classes' constructors (note it only applies to templates with `@this(...)` constructors).

=== +def twirlCodec = Codec(Properties.sourceEncoding)+

The codec used to generate the files (the default is the same sbt plugin uses).

=== +def twirlInclusiveDot: Boolean = false+

Whether the twirl parser should parse with an inclusive dot.

== Example

There's an https://github.com/lihaoyi/cask/tree/master/example/twirl[example project]
