14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

ClojureAdvent Calendar 2019

Day 22

clojurescriptのshadow-cljsについて

Last updated at Posted at 2019-12-22

shadow-cljsとは

shadow-cljsはclojurescriptのコードをコンパイルするなど、ビルドするためのツールです。clojurescriptのビルドツールはfigwheel-mainなど他にもいくつかありますが、npmやyarnなど標準のjavascriptの環境と親和性がよいことが特徴で、npmのパッケージが直接利用できることはとても便利です。

shadow-cljsはThomas Hellerによって開発されています。

shadow-cljsに必要な環境は以下の通りです。

  • node.js (v6.0.0+, most recent version preferred)
  • npm (comes bundled with node.js) or yarn
  • Java SDK (Version 8+, Hotspot)

shadow-cljsのプロジェクト作成

npxをインストールします。npxを用いるとプロジェクト、node_modulesフォルダ内のバイナリをパス指定無しで直接実行出来るようになります。

npm install npx -g

最新のnpmはnpxが付属しているため、インストールの必要はないかも知れません。
このnpxを利用して新規プロジェクトを作成します。

$ npx create-cljs-project my-app
npx: 1個のパッケージを2.051秒でインストールしました。
(中略)

Done. Actual project initialization will follow soon.
----

さて、新しいプロジェクトができたので、その構成をみてみます。

$ cd my-app
$ ls
node_modules/  package-lock.json  package.json	shadow-cljs.edn  src/

shadow-cljs.edn以外はjavascriptではお馴染のプロジェクトの構成はpackage.jsonとshadow-cljs.ednを用いて行います。

package.jsonはnodeの依存ファイルや実行コマンドを記述します。生成された内容は以下の通りです。


{
  "name": "my-app",
  "version": "0.0.1",
  "private": true,
  "devDependencies": {
    "shadow-cljs": "2.8.83"
  },
  "dependencies": {}
}

新たにライブラリを追加したい場合、npmもしくはyarnコマンドで追加すると、package.jsonにも依存関係が追加されます。

shadow-cljs.ednはclojurescriptに関する構成を記述します。デフォルトでのソースコード構成はsource-pathsに書かれていますが、変更したい場合はここを修正します。

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]
 :dependencies
 []
 :builds
 {}}

browser replの立ち上げ

npxを用いてbrowser replを起動することにより、ブラウザのjavascriptにてclojurescriptのreplを利用することができます。

$ npx shadow-cljs browser-repl
shadow-cljs - config: /export/clojure/advanced-clojure/7.advanced-cljs/my-app/shadow-cljs.edn  cli version: 2.8.83  node: v8.10.0
shadow-cljs - server version: 2.8.83 running at http://localhost:9630
shadow-cljs - nREPL server started on port 36415
[:browser-repl] Configuring build.
[:browser-repl] Compiling ...
[:browser-repl] Build completed. (135 files, 1 compiled, 0 warnings, 3.59s)
cljs.user=> 

次のようにブラウザが立ち上がります。

image.png

replのプロンプトが出たら、clojurescriptのプログラムを打ち込んで、結果が出ることを確認します。ブラウザにアラート表示を行うこともできます。

cljs.user=> (+ 1 1)
2
cljs.user=>  (js/alert "Hello world!")
nil

image.png

簡単なclojuescriptのコード作成と実行

今度はソースコードを作成して実行してみましょう。

src/mainにmy_appというディレクトリを作成して、そこにhelloworld.cljsという名前のファイルを作成して以下の内容にします。

src/main/my_app/
└── helloworld.cljs

コードはコンソールにHello Worldを表示するものです。

(ns my-app.helloworld)

(defn init []
  (println "Hello World!"))

publicという名前のディレクトリを作成して、その下にindex.htmlを作成し、以下の通りとします。

public/
└── index.html

HTMLのコードは以下の通りとなります。

<!doctype html>
<html>
  <head>
    <meta charset="utf-8" />
    <title>helloworld example</title>
  </head>
  <body>
    <div id="app"></div>
    <script src="/js/main.js" type="text/javascript"></script>
  </body>
</html>

書かれたソースコードを実行するために:dev-httpと:buildsのエントリーを追加します。
起動したときに動く関数を:modules->:mainのエントリーで記述します。:dev-httpエントリーではブラウザーにてアクセスするポートを指定します。

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 []
 :dev-http {8080 "public"}
 :builds
 {:hello
  {:target :browser
   :modules {:main {:init-fn my-app.helloworld/init}}
   }}
 }

npxを用いて実行します。

$ npx shadow-cljs watch hello
shadow-cljs - config: /export/clojure/advanced-clojure/7.advanced-cljs/my-app/shadow-cljs.edn  cli version: 2.8.83  node: v8.10.0
shadow-cljs - HTTP server available at http://localhost:8080
shadow-cljs - server version: 2.8.83 running at http://localhost:9630
shadow-cljs - nREPL server started on port 35655
shadow-cljs - watching build :hello
[:hello] Configuring build.
[:hello] Compiling ...
[:hello] Build completed. (135 files, 1 compiled, 0 warnings, 3.29s)

ブラウザを起動して、localhost:8080にアクセスしてみます。
そしてブラウザーのデベロッパーツールにてコンソールをみてみます。

image.png

そうするとHello World!の文字列が表示されていることが確認できます。

コンソールに出力するメッセージを変更してみましょう。

(ns my-app.helloworld)

(defn init []
  (js/alert "test")
  (println "こんにちは!"))

Browser replはソースコードの変更を検知して、自動的にコンパイルしてくれます。

[:hello] Compiling ...
[:hello] Build completed. (135 files, 1 compiled, 0 warnings, 0.17s)

ブラウザーをリロードするとメッセージが変更されていることが分ります。

image.png

reagentを使う

今度はブラウザーにHello World!の文字列を表示してみましょう。
package.jsonにreact関係のライブラリーを記述します。

{
  "name": "my-app",
  "version": "0.0.1",
  "private": true,
  "devDependencies": {
    "shadow-cljs": "2.8.83"
  },
  "dependencies": {
    "all": "^0.0.0",
    "create-react-class": "15.6.3",
    "react": "16.3.2",
    "react-dom": "16.3.2"
  }
}

shadow-cljs.ednにはreagentとdevtoolsの依存関係を記述します。また、新しく作成するreagentのためのコードの記述をします。新しくmy-appにcore.cljsを作成して、init関数を起動します。

;; shadow-cljs configuration
{:source-paths
 ["src/dev"
  "src/main"
  "src/test"]

 :dependencies
 [
  [binaryage/devtools "0.9.10"]
  [reagent "0.8.0"]
  ]
 
 :dev-http {8080 "public"}
 :builds
 {:hello
  {:target :browser
   :modules {:main {:init-fn my-app.helloworld/init}}
   }
  :app {:target :browser
        :output-dir "public/js"
        :asset-path "/js"

        :modules
        {:main
         {:entries [my-app.core]}}

        :devtools
        {:after-load  my-app.core/init
         :http-root   "public"
         :http-port   3000}}
  }
 }

core.cljsの中身です。ブラウザーにHello Worldを表示するためのreagentのコードです。

(ns my-app.core
  (:require [reagent.core :as r])
  )

(def greeting "Hello World!")

(defn app []
  [:div greeting])

(defn stop []
  (js/console.log "Stopping..."))

(defn start []
  (js/console.log "Starting...")
  (r/render [app]
            (.getElementById js/document "app")))

(defn ^:export init []
  (start))

npxにて起動します。

$ npx shadow-cljs watch app

今度はブラウザ上にHello Worldが表示されます。

image.png

14
5
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
14
5

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?