Play FrameworkでTypeScript, SCSSを動かしてみたので、
備忘録も兼ねて記録を残します。
目次
sbtプラグインを追加する
sbtには、sbt-webというWeb開発用のプラグイン向けライブラリがあり、
プロジェクトでSbtWebを有効にしてsbt-web用プラグインを追加すれば、対応するファイルがコンパイルされるようになります。
https://github.com/sbt/sbt-web
TypeScript, SCSS(Sass)にも、それぞれプラグインが作られています。
https://github.com/platypii/sbt-typescript
https://github.com/irundaia/sbt-sassify
以下のようにplugins.sbtにプラグインの記述を追加して、
addSbtPlugin("com.github.platypii" % "sbt-typescript" % "4.6.4") addSbtPlugin("io.github.irundaia" % "sbt-sassify" % "1.5.2")
SbtWebをプロジェクトで有効にします。
lazy val proj = (project in file(".")) .settings( name := "SampleProject" ) .enablePlugins(PlayScala, SbtWeb)
これで、assetsタスクでTypeScriptとSCSS(Sass)のコンパイルが有効になります。
(runで実行中のときも、ファイルの変更を検知して再コンパイルしてくれる)
プラグインの設定
sbt-typescriptは、プロジェクトのルートディレクトリにtsconfig.jsonを作成し、
"compilerOptions"の下にコンパイラオプションを記述することができます。
コンパイラオプションの一覧はこちら(https://www.typescriptlang.org/docs/handbook/compiler-options.html)
今回は、SourceMapを生成するようにしたかったので、以下の設定を入れました。
{ "compilerOptions": { "sourceMap": true, "mapRoot": "/assets", "sourceRoot": "/assets" } }
sbt-sassifyは、build.sbtからSassKeysを通して設定することができます。
sbt-sassifyを使う上で実は1点うまくいかなかった点があって、assetsのルート直下ではなくサブディレクトリ(たとえば/assets/styles)にSCSSを配置すると、
SourceMapの対応付けがおかしくなってしまいます("sources"が"styles/<ファイル名>"となってほしいところが"<ファイル名>"となる)
そこで、今回は"SassKeys.assetRootURL"をサブディレクトリに設定しました(ただし、この方法はSCSSをディレクトリ分けすると使えない……)
SassKeys.assetRootURL := "/assets/styles"
テンプレートからの参照
作成したTypeScriptやSCSSは、テンプレートから参照する必要があります。
TypeScriptやSCSSをコンパイルして生成されたJavaScript/CSSを参照するには、
アセット用のルーティングを作成し、controller.routes.Assetsに作成されるアセット用のリバースコントローラを利用します。
routesに以下の記述でルーティングを追加します。
GET /assets/*file controllers.Assets.at(path="/public", file)
これによりcontroller.routes.Assetsにリバースコントローラが生成され、以下のように@Assets.at("<生成したファイルの相対パス>")により参照できるようになります。
@import controllers.routes.Assets <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Test</title> <link href="@Assets.at("styles/test.css")" rel="stylesheet"> <script type="module" src="@Assets.at("scripts/test.js")"></script> </head> <body> ...... </body> </html>
実際に動かす
実際に簡単なページを作って動かしてみます。
まずは、テンプレートとControllerを作成します。
@import controllers.routes.Assets <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>Test</title> <link href="@Assets.at("styles/test.css")" rel="stylesheet"> <script type="module" src="@Assets.at("scripts/test.js")"></script> </head> <body> <main> <input id="testBtn" type="button" value="TEST"> </main> </body> </html>
import play.api.i18n.I18nSupport import play.api.mvc.{AbstractController, Action, AnyContent, ControllerComponents} import javax.inject.Inject class TestController @Inject()(cc: ControllerComponents)() extends AbstractController(cc) with I18nSupport { def test(): Action[AnyContent] = Action { implicit request => Ok(views.html.Test()) } }
次に、/assets/scriptsと/assets/stylesにそれぞれTypeScriptとSCSSを作成します。
document.querySelector('#testBtn').addEventListener('click', (event: Event) => { alert('TEST!'); });
body { margin: auto; width: 60vw; } input[type="button"] { background-color: #efefef; border: 1px solid #efefef; border-radius: unset; width: 100%; height: 40px; line-height: 1.2; &:focus { outline: none; } &:hover { color: #fdfdfd; background-color: #777777; border-color: #777777; cursor: pointer; } }
最後に、routesにルーティングを定義します。
GET /test TestController.test()
これで動くようになるので、実際に動かしてみます。
ボタンを押すと、こちらのようにポップアップが表示されます。
クライアント側のライブラリ依存関係を管理する
JavaScriptやCSSを書く上で、サードパーティのライブラリ(例えばnode.jsやjQueryなど)を使用する場合があると思います。
そうした場合、WebJarsというクライアント側のライブラリをMaven等で管理できるようにしたサービスを利用することができます。
build.sbtに以下のようにwebjarsのライブラリの依存関係を追加することで、クライアント側のライブラリについても依存関係を管理できます。
TypeScriptのコンパイル時に必要な情報も、これによって追加することができます。
libraryDependencies += "org.webjars.npm" % "jquery" % "3.6.4" libraryDependencies += "org.webjars.npm" % "types__jquery" % "3.5.16" libraryDependencies += "org.webjars.npm" % "types__sizzle" % "2.3.3"
注: @types/jqueryを追加すると@types/sizzleを解決できなくてエラーになりました。[0,)というversionの表記を解釈できていない模様。
範囲指定のバージョンには対応しているみたいですが、[0,)という記述には対応できていない?
https://github.com/sbt/sbt/issues/2647
また、@typesなど特殊な文字を含むライブラリを追加する場合、npmパッケージとWebJarsでの名前が若干異なるので、
以下の設定も追加する必要があるようです。
resolveFromWebjarsNodeModulesDir := true
WebJarsで追加したライブラリをPlayで扱うために、webjars-playを依存関係に追加します。
libraryDependencies += "org.webjars" %% "webjars-play" % "2.8.18"
webjars-playにWebJars用のルーティングが用意されているため、routesに以下を追加してインクルードします。
-> /webjars webjars.Routes
これで、リバースルーティングにより、@org.webjars.play.routes.WebJarAssets.at("<ライブラリ名>/<ライブラリのバージョン>/<パッケージ内のパス>")で参照できるようになります。
@import org.webjars.play.routes.WebJarAssets <script src="@WebJarAssets.at("jquery/3.6.4/dist/jquery.min.js")"></script>
jQueryを使えるようになりました。
$('#testBtn').on('click', (event: JQuery.Event) => { alert('TEST!'); });
おわり
今回はTypeScriptとSCSS(Sass)を使いましたが、CoffeeScriptやLESS等でもほぼ同様の手順で利用可能なはずです。
想像していたよりは手軽に導入ができました。素のJavaScriptやCSSを書くよりは絶対書きやすいと思うので、使っていきます。
参考文献
Assets - 2.8.x
https://www.playframework.com/documentation/2.8.x/Assets
Scala Routing - 2.8.x
https://www.playframework.com/documentation/2.8.x/ScalaRouting
webjars/webjars-play
https://github.com/webjars/webjars-play
platypii/sbt-typescript: An sbt plugin for compiling typescript
https://github.com/platypii/sbt-typescript
irundaia/sbt-sassify: sbt-web plugin for Sass files
https://github.com/irundaia/sbt-sassify
WebJars - Documentation
https://www.webjars.org/documentation
Play Framework with WebJars で管理画面をサクッと作ってみる | | AI tech studio
https://cyberagent.ai/blog/tech/scala/3334/
java - Play Framework: Arrow ("->") in routing - Stack Overflow
https://stackoverflow.com/questions/31009785/play-framework-arrow-in-routing