Help us understand the problem. What is going on with this article?

PureScript の build tool`pulp` を動かしてみる

More than 3 years have passed since last update.

この記事は (bouzuya) PureScript Advent Calendar 2016 の 3 日目。 (bouzuya) PureScript Advent Calendar 2016 は bouzuya の PureScript 学習の記録だ。

概要

今日は PureScript のためのビルドツール pulp を試してみる。

pulp の repository は bodil/pulp だ。こんな記事を読むより 1 次ソースを参照するほうが早そうだ。

pulp をインストール

$ # Node.js がインストールされている前提で
$ mkdir project2/
$ cd project2/
$ npm init -y
$ npm install -D pulp purescript bower
$ npm list --depth 0
project2@1.0.0 /Users/bouzuya/project2
├── bower@1.8.0
├── pulp@10.0.0
└── purescript@0.10.2

今回は pulp@10.0.0 のようだ。とりあえず動かしてみる。

$ $(npm bin)/pulp
* Error: Expected command
Usage: pulp [global-options] <command> [command-options]

Global options:
  --before <string>       Run a shell command before the operation begins. Useful with `--watch`, eg. `--watch --before clear`.
  --bower-file -b <file>  Read this bower.json file instead of autodetecting it.
  --else <string>         Run a shell command if an operation finishes. Useful with `--watch`, eg. `--watch --then 'say Done' --else 'say Failed'`
  --help -h               Show this help message.
  --monochrome            Don't colourise log output.
  --then <string>         Run a shell command after the operation finishes successfully. Useful with `--watch`, eg. `--watch --then 'say Done'`
  --version -v            Show current pulp version.
  --watch -w              Watch source directories and re-run command if something changes.

Commands:
  browserify  Produce a deployable bundle using Browserify.
  build       Build the project.
  docs        Generate project documentation.
  init        Generate an example PureScript project.
  login       Obtain and store a token for uploading packages to Pursuit.
  psci        Launch a PureScript REPL configured for the project.
  publish     Publish a previously tagged version to Bower and Pursuit.
  run         Compile and run the project.
  server      Launch a development server.
  test        Run project tests.
  version     Bump and tag a new version in preparation for release.

Use `pulp <command> --help` to learn about command specific options.

README とは違って、今回はインストール時に npm install --global にしていないので、このまま pulp を使うと、次のようなエラーが表示される。

$ $(npm bin)/pulp --version
* ERROR: `psc` executable not found.

pulpnpm install --global purescript bower を前提にしているので、 PATH に psc などを含めるよう注意が必要だ。ぼくは global を維持でも回避したいので、ここは npm run-scripts を活用して PATH に psc などを含めることにする。

package.jsonscripts に次のように設定する。

{
  "name": "project2",
  "version": "1.0.0",
  "author": "bouzuya <m@bouzuya.net> (http://bouzuya.net/)",
  "devDependencies": {
    "bower": "^1.8.0",
    "pulp": "^10.0.0",
    "purescript": "^0.10.2"
  },
  "keywords": [],
  "license": "MIT",
  "main": "index.js",
  "scripts": {
    "pulp": "pulp",
    "pulp:browserify": "pulp browserify",
    "pulp:build": "pulp build",
    "pulp:docs": "pulp docs",
    "pulp:init": "pulp init",
    "pulp:login": "pulp login",
    "pulp:psci": "pulp psci",
    "pulp:publish": "pulp publish",
    "pulp:run": "pulp run",
    "pulp:server": "pulp server",
    "pulp:test": "pulp test",
    "pulp:version": "pulp version"
  }
}

これで改めて動かしてみる。

$ npm run pulp -- --version
> project2@1.0.0 pulp /Users/bouzuya/project2
> pulp "--version"

Pulp version 10.0.0
psc version 0.10.2 using /Users/bouzuya/project2/node_modules/.bin/psc

よさそう。

pulp init

pulp はいくつかのコマンドがあるようだが、まずはいかにも最初に実行すべきっぽい pulp init を実行してみる。

Generate an example PureScript project.

とのことなので PureScript プロジェクトの例を生成してくれるのだろう。

$ npm run pulp:init
> project2@1.0.0 pulp:init /Users/bouzuya/project2
> pulp init

* Generating project skeleton in /Users/bouzuya/project2
bower purescript-prelude#*      cached https://github.com/purescript/purescript-prelude.git#2.1.0
bower purescript-prelude#*    validate 2.1.0 against https://github.com/purescript/purescript-prelude.git#*
bower purescript-console#*      cached https://github.com/purescript/purescript-console.git#2.0.0
bower purescript-console#*    validate 2.0.0 against https://github.com/purescript/purescript-console.git#*
bower purescript-eff#^2.0.0     cached https://github.com/purescript/purescript-eff.git#2.0.0
bower purescript-eff#^2.0.0   validate 2.0.0 against https://github.com/purescript/purescript-eff.git#^2.0.0
bower purescript-console#^2.0.0          install purescript-console#2.0.0
bower purescript-prelude#^2.1.0          install purescript-prelude#2.1.0
bower purescript-eff#^2.0.0              install purescript-eff#2.0.0

purescript-console#2.0.0 bower_components/purescript-console
└── purescript-eff#2.0.0

purescript-prelude#2.1.0 bower_components/purescript-prelude

purescript-eff#2.0.0 bower_components/purescript-eff
└── purescript-prelude#2.1.0
bower purescript-psci-support#* cached https://github.com/purescript/purescript-psci-support.git#2.0.0
bower purescript-psci-support#*         validate 2.0.0 against https://github.com/purescript/purescript-psci-support.git#*
bower purescript-psci-support#^2.0.0     install purescript-psci-support#2.0.0

purescript-psci-support#2.0.0 bower_components/purescript-psci-support
└── purescript-console#2.0.0

何やら bower が動いているようだ。調べた感じ、いくつかのファイルが生成され、bower install が動いたようだ。

pulp init で生成されたファイル

pulp init で生成されたファイルは次のとおりだ。たったの 4 つ。

.gitignore
bower.json
src/Main.purs
test/Main.purs
$ cat .gitignore
/bower_components/
/node_modules/
/.pulp-cache/
/output/
/.psc*
/.psa*

ふむ。

$ cat bower.json
{
  "name": "project2",
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "output"
  ],
  "dependencies": {
    "purescript-prelude": "^2.1.0",
    "purescript-console": "^2.0.0"
  },
  "devDependencies": {
    "purescript-psci-support": "^2.0.0"
  }
}

ふむふむ。 purescript-prelude#2.1.0 / purescript-console#2.0.0 / purescript-psci-support#2.0.0 はとても一般的な依存関係のようだ。

$ cat src/Main.purs
module Main where

import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)

main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
  log "Hello sailor!"

ふむふむふむ。

ファイル名は大文字から開始 (モジュール名と一致している (?)) され、.purs という拡張子を持っているみたいだ。

Main というモジュールに main 関数が生えている。

import Preludepurescript-prelude が提供するモジュールだろう。

import Control.Monad.Eff.Console はおそらく purescript-console が提供するモジュールだろう。

まあ、後回しかな。

$ cat test/Main.purs
module Test.Main where

import Prelude
import Control.Monad.Eff (Eff)
import Control.Monad.Eff.Console (CONSOLE, log)

main :: forall e. Eff (console :: CONSOLE | e) Unit
main = do
  log "You should add some tests."

ふむふむふむふむ。特にテストフレームワークなどは組み込まれないっぽい。

pulp build

bodil/pulp の 10.0.0 の README に従って、次は pulp build してみよう。

$ npm run pulp:build

> project2@1.0.0 pulp:build /Users/bouzuya/project2
> pulp build

* Building project in /Users/bouzuya/project2
Compiling Data.NaturalTransformation
Compiling Data.Show
Compiling Control.Semigroupoid
Compiling Data.Boolean
Compiling Control.Category
Compiling Data.Void
Compiling Data.Function
Compiling Data.Unit
Compiling Data.HeytingAlgebra
Compiling Data.Eq
Compiling Data.Semiring
Compiling Data.Semigroup
Compiling Data.Functor
Compiling Data.Ring
Compiling Data.Ordering
Compiling Data.BooleanAlgebra
Compiling Data.CommutativeRing
Compiling Data.Ord.Unsafe
Compiling Data.Ord
Compiling Data.EuclideanRing
Compiling Control.Apply
Compiling Data.Field
Compiling Control.Applicative
Compiling Control.Bind
Compiling Data.Bounded
Compiling Control.Monad
Compiling Control.Monad.Eff
Compiling Prelude
Compiling Control.Monad.Eff.Class
Compiling Control.Monad.Eff.Unsafe
Compiling Control.Monad.Eff.Console
Compiling PSCI.Support
Compiling Main
* Build successful.

ビルドされた (雑) 。

output/ にコンパイルされたファイルがあるようだ。

$ ls output/
Control.Applicative/
Control.Apply/
Control.Bind/
Control.Category/
Control.Monad/
Control.Monad.Eff/
Control.Monad.Eff.Class/
Control.Monad.Eff.Console/
Control.Monad.Eff.Unsafe/
Control.Semigroupoid/
Data.Boolean/
Data.BooleanAlgebra/
Data.Bounded/
Data.CommutativeRing/
Data.Eq/
Data.EuclideanRing/
Data.Field/
Data.Function/
Data.Functor/
Data.HeytingAlgebra/
Data.NaturalTransformation/
Data.Ord/
Data.Ord.Unsafe/
Data.Ordering/
Data.Ring/
Data.Semigroup/
Data.Semiring/
Data.Show/
Data.Unit/
Data.Void/
Main/
PSCI.Support/
Prelude/

モジュール単位でコンパイル済みの JavaScript ファイルがあるようだ。

$ cat output/Main/index.js
// Generated by psc version 0.10.2
"use strict";
var Prelude = require("../Prelude");
var Control_Monad_Eff = require("../Control.Monad.Eff");
var Control_Monad_Eff_Console = require("../Control.Monad.Eff.Console");
var main = Control_Monad_Eff_Console.log("Hello sailor!");
module.exports = {
    main: main
};

なるほど、commonjs 。

pulp run

次は pulp run で動かしてみる。

$ npm run pulp:run
> project2@1.0.0 pulp:run /Users/bouzuya/project2
> pulp run

* Building project in/Users/bouzuya/project2
* Build successful.
Hello sailor!

動くぞ! node に渡しているだけっぽい。 --runtime phantomjs のように別のコマンドも指定できるようだ。

pulp browserify

いかにもな pulp browserify は……

$ npm run pulp:browserify -- --to output/bundle.js

> project2@1.0.0 pulp:browserify /Users/bouzuya/project2
> pulp browserify "--to" "output/bundle.js"

* Browserifying project in /Users/bouzuya/project2
* Building project in /Users/bouzuya/project2
* Build successful.
* Browserifying...
* Browserified.

思ったよりは短い感じだけど、ここに貼るのは厳しいかな。普通に browserify しただけっぽい。

--optimise というオプションで未使用コードを削れるらしい。

$ npm run pulp:browserify -- --optimise --to output/bundle.min.js

> project2@1.0.0 pulp:browserify /Users/bouzuya/project2
> pulp browserify "--optimise" "--to" "output/bundle.min.js"

* Browserifying project in /Users/bouzuya/project2
* Building project in /Users/bouzuya/project2
* Build successful.
* Browserifying...
* Browserified.
$ cat output/bundle.min.js
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
// Generated by psc-bundle 0.10.2
var PS = {};
(function(exports) {
    "use strict";

  exports.log = function (s) {
    return function () {
      console.log(s);
      return {};
    };
  };
})(PS["Control.Monad.Eff.Console"] = PS["Control.Monad.Eff.Console"] || {});
(function(exports) {
  // Generated by psc version 0.10.2
  "use strict";
  var $foreign = PS["Control.Monad.Eff.Console"];
  var Control_Monad_Eff = PS["Control.Monad.Eff"];
  var Data_Show = PS["Data.Show"];
  var Data_Unit = PS["Data.Unit"];
  exports["log"] = $foreign.log;
})(PS["Control.Monad.Eff.Console"] = PS["Control.Monad.Eff.Console"] || {});
(function(exports) {
  // Generated by psc version 0.10.2
  "use strict";
  var Prelude = PS["Prelude"];
  var Control_Monad_Eff = PS["Control.Monad.Eff"];
  var Control_Monad_Eff_Console = PS["Control.Monad.Eff.Console"];
  var main = Control_Monad_Eff_Console.log("Hello sailor!");
  exports["main"] = main;
})(PS["Main"] = PS["Main"] || {});
PS["Main"].main();

},{}]},{},[1]);

確かに縮む。

pulp docs

$ npm run pulp:docs
> project2@1.0.0 pulp:docs /Users/bouzuya/project2
> pulp docs

* Generating documentation in /Users/bouzuya/project2
* Documentation generated.

ふむ。出力先を書いてほしいところだけど……、docs/ っぽい。

$ cat docs/Main.md
## Module Main

#### `main`

``` purescript
main :: forall e. Eff (console :: CONSOLE | e) Unit
```

Markdown だ。

pulp psci

REPL 。

npm run pulp:psci
> project2@1.0.0 pulp:psci /Users/bouzuya/project2
> pulp psci

PSCi, version 0.10.2
Type :? for help

>

引数に *.purs を指定しなくてもなんとかしてくれている。良い。

pulp server

/app.js に bundle したファイルを置いて server を立ててくれるっぽい。あまり凝ったことはできなさそうだけど、手軽でいいかも。

npm run pulp:server
> project2@1.0.0 pulp:server /Users/bouzuya/project2
> pulp server

* Server listening on http://localhost:1337/
* Building project in /Users/bouzuya/project2
* Build successful.
* Bundling JavaScript...
* Bundled.
$ curl http://localhost:1337/app.js
# (中略)
PS["Main"].main();

たしかに。 root は package.json のあるディレクトリみたいなので index.html を置くと普通に見れる。

pulp test

npm run pulp:test

> project2@1.0.0 pulp:test /Users/bouzuya/project2
> pulp test

* Building project in /Users/bouzuya/project2
Compiling Test.Main
* Build successful.
* Running tests...
You should add some tests.
* Tests OK.

うん。なるほど。

その他

pulp buildpulp test あたりは --watch--before / --then / --else などのオプションを取れるようだ。

ほかのオプションも結構スルーしているので、詳しくは README を参照。

まとめ

$ npm run pulp -- -h

> project2@1.0.0 pulp /Users/bouzuya/project2
> pulp "-h"

Usage: pulp [global-options] <command> [command-options]

Global options:
  --before <string>       Run a shell command before the operation begins. Useful with `--watch`, eg. `--watch --before clear`.
  --bower-file -b <file>  Read this bower.json file instead of autodetecting it.
  --else <string>         Run a shell command if an operation finishes. Useful with `--watch`, eg. `--watch --then 'say Done' --else 'say Failed'`
  --help -h               Show this help message.
  --monochrome            Don't colourise log output.
  --then <string>         Run a shell command after the operation finishes successfully. Useful with `--watch`, eg. `--watch --then 'say Done'`
  --version -v            Show current pulp version.
  --watch -w              Watch source directories and re-run command if something changes.

Commands:
  browserify  Produce a deployable bundle using Browserify.
  build       Build the project.
  docs        Generate project documentation.
  init        Generate an example PureScript project.
  login       Obtain and store a token for uploading packages to Pursuit.
  psci        Launch a PureScript REPL configured for the project.
  publish     Publish a previously tagged version to Bower and Pursuit.
  run         Compile and run the project.
  server      Launch a development server.
  test        Run project tests.
  version     Bump and tag a new version in preparation for release.

Use `pulp <command> --help` to learn about command specific options.

参考

次回以降の TODO

  • psc-package を試す
  • psci の未使用コマンドを試す
  • PureScript ドキュメントを読む
bouzuya
ぼく、ぼうずや。なさけはひとのためならず。たのしいはせいぎ。
http://bouzuya.net/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした