概要
PureScript は活発に開発が行われているため、開発環境構築の方法を適宜まとめる必要があります。
この記事では PureScript のインストールから Web フロントエンドを開発できるところまでの環境構築をまとめます。
対象
- PureScript の環境構築を VSCode でしたい
- PureScript でフロントエンドの開発がしたい
TL;DR
にまとまってます
筆者の環境
細かくバージョン書いていますが多分気にしなくて大丈夫です。
- Windows 11
- 22H2
- WSL2
- 5.15.68.1
- node
- 18.12.0
- npm
- 8.19.2
PureScript のインストール
プロジェクトルートになるディレクトリを作って初期化します。
mkdir <project-name>
cd <project-name>
npm init -y
PureScript をインストールします
npm install --save-dev purescript
おおむね成功しますが、次のようにコケることがあります
...
npm ERR! [ FAILURE ] Check if 'stack' command is available
...
このあたりの事情にあまり詳しくないのですが、何回か npm install purescript
をすると通ります。(それでいいのか?)
それでもダメなときは stack
をインストールしてください。
stack
は Haskell のプロジェクト / パッケージ管理ツールです。
詳しくはこちらから
(結構インストールめんどくさいです)
その他ツールチェインのインストール
-
spago
- プロジェクト / パッケージ管理ツール
-
purs-tidy
- フォーマッタ
-
esbuild
-
spago
が専らバンドラとして間接的に使う。
-
をインストールします
npm install --save-dev spago purs-tidy esbuild
プロジェクトの初期化
npx spago init
npx spago run
...
[info] Build succeeded.
🍝
と出たら成功です!
VSCode の拡張機能のインストールと設定
VSCode 拡張 PureScript IDE をインス―ルします
VSCode の設定に次を追加しておきます
"purescript.formatter": "purs-tidy",
"purescript.addNpmPath": true,
"[purescript]": {
"editor.formatOnSave": true,
},
※ formatOnSave
はお好みで。addNpmPath
はローカルに purescript をインストールするなら必須です。
フォーマッタの設定をします
npx purs-tidy generate-config
できた .tidyrc.json
の JSON を書き換えます(これもお好みで)
{
"importSort": "ide",
"importWrap": "source",
"indent": 2,
"operatorsFile": null,
"ribbon": 1,
"typeArrowPlacement": "first",
"unicode": "source",
"width": null
}
これでフォーマットや補完が効くようになったはずです。単に PureScript を使いたいのであれば、これで環境構築は終了となります
Halogen を使う
Halogen は PureScript の SPA フレームワークです。PureScript で SPA を作るときは一番使われているでしょう。(統計はないですが)
まず Halogen をインストールします
npx spago install halogen
Main.purs を書き換えます
module Main where
import Prelude
import Effect (Effect)
import Halogen (Component, defaultEval, mkComponent, mkEval)
import Halogen.Aff (awaitBody, runHalogenAff)
import Halogen.HTML as HH
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = runHalogenAff do
body <- awaitBody
runUI component unit body
component :: forall q i o m. Component q i o m
component =
mkComponent
{ initialState: \_ -> unit
, render: \_ -> HH.div_ [ HH.text "Hello Halogen!" ]
, eval: mkEval $ defaultEval
}
バンドルします
npx spago bundle-app -y -t public/index.js
public/index.html
ファイルを作ります
<!DOCTYPE html>
<html>
<head>
<title>Halogen App</title>
<script src="./index.js"></script>
</head>
<body></body>
</html>
これを読み込めば Hello Halogen!
と表示されるはずです!
Tailwind を使う
ちょっと面倒です
npm install --save-dev tailwindcss
npx tailwindcss init
tailwind.config.js
を編集します
{
...
content: ["./src/**/*.{purs,html,js}"],
...
}
src/index.css
を作成します。
@tailwind base;
@tailwind components;
@tailwind utilities;
VSCode の Tailwind CSS IntelliSense 拡張をインストールします
VSCode の設定に諸々の追加をして tailwind の補完が出るようにします(任意の文字列で出てしまうので、うざいときは切ってください)
{
"tailwindCSS.includeLanguages": {
"purescript": ""
},
"[purescript]": {
"tailwindCSS.experimental.classRegex": ["\"([^\"]*)\""],
},
}
Main.purs
をよしなに書き換えてビルドします
module Main where
import Prelude
import Effect (Effect)
import Halogen (ClassName(..), Component, defaultEval, mkComponent, mkEval)
import Halogen.Aff (awaitBody, runHalogenAff)
import Halogen.HTML as HH
import Halogen.HTML.Properties as HP
import Halogen.VDom.Driver (runUI)
main :: Effect Unit
main = runHalogenAff do
body <- awaitBody
runUI component unit body
component :: forall q i o m. Component q i o m
component =
mkComponent
{ initialState: \_ -> unit
, render: \_ -> HH.div [ HP.class_ $ ClassName "w-screen h-screen text-5xl flex justify-center items-center" ] [ HH.text "Hello Halogen!" ]
, eval: mkEval $ defaultEval
}
npx spago bundle-app -y -t public/index.js
CSS ファイルをビルドします
npx tailwindcss -i ./src/index.css -o ./public/index.css
index.html
から読み込みます
<head>
...
<link rel="stylesheet" href="./index.css">
...
</head>
これで Hello Halogen!
が真ん中に移動します
ホットリロードできるようにする
live-server
を使います
npm install --save-dev live-server
次のコマンドをそれぞれ別のターミナルで同時に動かします
npx live-server public
npx spago bundle-app -y -t public/index.js -w
npx tailwindcss -i ./src/index.css -o ./public/index.css -w
これで localhost:8080
あたりを開くとファイルの変更がリアルタイムに反映される環境ができます
コマンドをまとめる
npm-run-all
を使います
npm install --save-dev npm-run-all
package.json
の script
を書き換えます
"scripts": {
"bundle:script": "npx spago bundle-app -t ./public/index.js -y",
"bundle:css": "tailwindcss -i ./src/index.css -o ./public/index.css -m",
"bundle": "run-s bundle:*",
"watch:script": "npx spago bundle-app -t ./public/index.js -w",
"watch:css": "tailwindcss -i ./src/index.css -o ./public/index.css -w",
"watch:server": "npx live-server ./public",
"watch": "run-p watch:*"
},
最終的にバンドルするには
npm run bundle
開発時にホットリロードするには
npm run watch
とします
余談
補完が効かないんだが?
PureScript の Laugage Server は output ディレクトリを読んで補完を行っているようです。
したがって、パッケージのインストール直後などはそのパッケージの補完が効きません
npx spago build --deps-only
をすると使っているパッケージのみビルドしてくれます。これをしてみましょう
これでもダメなら VSCode のコマンドパレットから PureScript: Restart/Reconnect purs IDE server
を実行してみてください
逆にすでにアンインストールしたパッケージの補完が出てうざいというときは、output
フォルダの該当するディレクトリを消すか、output
ディレクトリごと消してから再ビルドすればよいです。
purs-backend-optimizer を使う
purs-backend-optimizer は、purescript がビルドした後のコードを改変してからバンドルするツールで、動作速度の向上やファイルサイズの削減などが狙えます。(Halogen だとファイルサイズは変わらないか、大きくなることがあるそうです。backend-optimizer はどちらかというと Effect や ST モナドの最適化とインライン化による速度向上を重視しているようです)
npm install --save-dev purs-backend-es
spago-bundle.dhall
を作成します
./spago.dhall // { backend = "purs-backend-es build" }
.gitignore
に追加します
/output-es
バンドルスクリプトを変更します
"bundle:script": "npx spago -x spago-bundle.dhall build && npx purs-backend-es bundle-app -s -t ./public/index.js -y",
Jelly を使う
最近 Jelly というライブラリを作っています
これを使ってアプリケーションを作ります
npx spago install jelly aff foldable-traversable jelly-hooks
Main.purs
を書き換えます
module Main where
import Prelude
import Data.Foldable (traverse_)
import Effect (Effect)
import Effect.Aff (launchAff_)
import Effect.Class (liftEffect)
import Jelly.Aff (awaitBody)
import Jelly.Component (Component, text)
import Jelly.Element as JE
import Jelly.Hooks (runHooks_)
import Jelly.Hydrate (mount)
import Jelly.Prop ((:=))
main :: Effect Unit
main = launchAff_ do
bodyMaybe <- awaitBody
liftEffect $ traverse_ (runHooks_ <<< mount component) bodyMaybe
component :: forall m. Component m
component =
JE.div [ "class" := "w-screen h-screen text-5xl flex justify-center items-center" ] do
text "Hello Jelly!"
これで OK です。Jelly についての記事はこちら
https://zenn.dev/yukikurage/articles/4735819c3b421b
https://zenn.dev/yukikurage/articles/d78a464b815aec
https://zenn.dev/yukikurage/articles/367d844d79de20
0.8.0 で破壊的変更を入れています。最新のドキュメントはここにあります↓