LoginSignup
10
8

More than 5 years have passed since last update.

SBTを使わないNode.JS friendlyなScala.js開発環境を作成する

Last updated at Posted at 2016-01-31

Scala Matsuri 2016おもしろかったですね!
Scala.js推しな会社のブースにいたりしたので、久しぶりにモチベーションが上がって前からやりたかったScala系の依存の解決をnpmで解決できるようにしました。
これでSBTに頼らずにNode.JS friendlyな環境を作れるようになります!

話的には

の続きな感じです。

目的・モチベーション

Scala.jsをチュートリアルにある通りSBTを使用するとJSのエコシステムには乗れなくなります。

一応、Live Reloadingがあったり、WebJarsでJavaScriptの依存解決もできたりする(やったことはないけど)ので最低限開発には困らないです。
が、HTMLやCSSを含めたビルドフローを考えると、SBTだとやっぱり物足りなくてJSのエコシステムに乗せた方が話が早そうです。
(SBT+npmな構成にすれば解決できる問題ではありますが、役割が被るのでどちらかに寄せたいです)

全く関係ないけど、ScalaCSS+twirl+Scala.jsでフルスタックScala開発とか熱そうな気はする。

Scala.jsコンパイル環境の作成

とりあえずscalajs-standalone-binを入れて、Scala.jsをコンパイルできる環境を作成します。

package.json
"scripts": {
  "postinstall": "scalajs-standalone-bin-install"
}
$ npm install --save-dev scalajs-standalone-bin

なんとなくnode-javaを使えばnpmだけで完結して開発環境作れるかと思いましたが、そんなことは全くなくJDKが必要になるので事前にJDKをインストールしておいてください。

また、scalajs-standalone-binで使用しているbin-wrapperのバグだと思うのですが、postinstallの挙動がおかしくなるときがあります。
もし、上手くインストールできないというときは何度かnpm installを繰り返してみてください。(npm3になって大丈夫になった気もする)

src/example/ScalaJSExample.scala
package example

import scala.scalajs.js

object ScalaJSExample extends js.JSApp {
  def main(): Unit = {
    println("HelloWorld!!!")
  }
}
package.json
"scripts": {
  ...
  "build": "scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp"
}
$ mkdir -p .tmp dist
$ npm run build                  

> scalajs-skeleton@1.0.0 compile /Users/Kinzal/Downloads/scalajs-skeleton
> scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp

Fast optimizing dist/example.js

これでScalaライブラリへの依存のないScala.js開発環境が作成できました。

Scalaライブラリへの依存を解決する

依存の解決をするための題材としてscala-js-example-appのコードをコンパイルできるようにします。

src/example/ScalaJSExample.scala
package example

import scala.scalajs.js
import js.annotation.JSExport
import org.scalajs.dom

object ScalaJSExample extends js.JSApp {
  def main(): Unit = {
    val paragraph = dom.document.createElement("p")
    paragraph.innerHTML = "<strong>It works!</strong>"
    dom.document.getElementById("playground").appendChild(paragraph)
  }

  /** Computes the square of an integer.
   *  This demonstrates unit testing.
   */
  def square(x: Int): Int = x*x
}
$ npm run build

> scalajs-skeleton@1.0.0 compile /Users/Kinzal/Downloads/scalajs-skeleton
> scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp

src/example/ScalaJSExample.scala:5: error: object scalajs is not a member of package org
import org.scalajs.dom
           ^
src/example/ScalaJSExample.scala:9: error: not found: value dom
    val paragraph = dom.document.createElement("p")
                    ^
src/example/ScalaJSExample.scala:11: error: not found: value dom
    dom.document.getElementById("playground").appendChild(paragraph)
    ^
three errors found

npm ERR! Darwin 14.0.0
npm ERR! argv "/usr/local/bin/node" "/usr/local/bin/npm" "run" "compile"
npm ERR! node v5.1.1
npm ERR! npm  v3.3.12
npm ERR! code ELIFECYCLE
npm ERR! scalajs-skeleton@1.0.0 compile: `scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the scalajs-skeleton@1.0.0 compile script 'scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp'.
npm ERR! Make sure you have the latest version of node.js and npm installed.
npm ERR! If you do, this is most likely a problem with the scalajs-skeleton package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     scalajsc $(find src -name *.scala) -d .tmp  && scalajsld -o dist/example.js .tmp
npm ERR! You can get their info via:
npm ERR!     npm owner ls scalajs-skeleton
npm ERR! There is likely additional logging output above.

npm ERR! Please include the following file with any support request:
npm ERR!     /Users/Kinzal/Downloads/scalajs-skeleton/npm-debug.log

特に依存解決をしていないので、想定通りエラーが発生しましたね。
ここから依存するライブラリへのjarにパスを通してコンパイルできるようにします。

基本的には依存するjarをmavenからダウンロードして、scalajscclasspathに通すだけになります。
mavenからのダウンロードには

を使用します。
なんでこんなものが存在しているのかはわかりませんが、あるならありがたく使わせてもらいます。

package.json
  "scripts": {
    ...
    "postinstall": "scalajs-standalone-bin-install && node-java-maven"
  },
  "java": {
    "dependencies": [
      {
        "groupId": "org.scala-js",
        "artifactId": "scalajs-dom_sjs0.6_2.11",
        "version": "0.8.2"
      }
    ]
  }
$ npm install --save-dev node-java-maven

これで~/.m2以下に依存するjarがダウンロードされます。
ただ、ここでダウンロードしたjarの中にscala-libraryscalajs-libraryも含まれますが、少しバージョンが古いのでその2つは除外してclasspathに指定します。

package.json
  "scripts": {
    ...
    "build": "scalajsc -classpath $(find ~/.m2 -path '*scala-library*' -prune -o -path '*scalajs-library*' -prune -o -name '*.jar' -print | paste -s -d ':' -):$(find ./node_modules/scalajs-standalone-bin -name '*.jar' | paste -s -d ':' -) $(find src -name '*.scala') -d .tmp  && scalajsld -o dist/example.js .tmp"
  }
$ npm run build         

> scalajs-skeleton@1.0.0 build /Users/Kinzal/Downloads/scalajs-skeleton
> scalajsc -classpath $(find ~/.m2 -path '*scala-library*' -prune -o -path '*scalajs-library*' -prune -o -name '*.jar' -print | paste -s -d ':' -):$(find ./node_modules/scalajs-standalone-bin -name '*.jar' | paste -s -d ':' -) $(find src -name '*.scala') -d .tmp  && scalajsld -o dist/example.js .tmp

Fast optimizing dist/example.js

これでScalaライブラリの依存を解決して、Scala.jsを開発できる環境ができました。
あとは出力したファイルをwatchしたり、browserifyにかけたりで簡単にJSのエコシステムに載せれそうですね!!

ちなみにビルド速度は結構早いです。

$ time npm run build

> scalajs-skeleton@1.0.0 build /Users/Kinzal/Downloads/scalajs-skeleton
> scalajsc -classpath $(find ~/.m2 -path '*scala-library*' -prune -o -path '*scalajs-library*' -prune -o -name '*.jar' -print | paste -s -d ':' -):$(find ./node_modules/scalajs-standalone-bin -name '*.jar' | paste -s -d ':' -) $(find src -name '*.scala') -d .tmp  && scalajsld -o dist/example.js .tmp

Fast optimizing dist/example.js
npm run build  16.17s user 1.20s system 200% cpu 8.656 total

課題

  1. 複数プロジェクトで使用すると~/.m2以下に依存するライブラリが混在する
  2. scalajscで中間ファイルが出力される
    • 標準出力に中間ファイルを出力してscalajsldにパイプで渡したいけど渡し方がわからない
  3. scalajscscalajsldで出力するディレクトリが存在しないとエラーになる
  4. findを多用してて読みづらい

このあたりはtprで解決したい内容ではあったのですが、正直コマンドをラップして使いやすくするためだけのツールにモチベーションがわかなすぎて開発停滞してます。

おわりに

一応断っておきますが、今回は実験して動くの確認したレベルなので、これでちゃんとアプリケーションを開発できるかは知らないです。
たぶんいくつか地雷はあると思うので、誰か思いっきり踏み抜いてください。

10
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
8