A build tool for PureScript projects
A build tool for PureScript.
$ npm install -g purescript pulp bower
This installs the PureScript compiler, the Pulp build tool, and the Bower package manager.
The short version:
$ mkdir purescript-hello $ cd purescript-hello $ pulp init $ pulp run
The structure of your project folder, after running
pulp init, will look like this:
purescript-hello - bower.json - src/ - test/
pulpworks by convention. It expects all projects to contain a manifest file for package management (usually
bower.json, since package management in PureScript is usually handled by Bower).
Your project source files go in the
srcfolder. Your test files go in the
testfolder. Project dependencies will be installed under the Bower standard
bower_componentsfolder, and are expected to have the same basic
teststructure. That's all there is to a
We employ the
purescript-prefix as a convention to identify PureScript projects when they're used as dependencies. You're welcome to call your project anything you like, but without the
purescript-prefix it won't be picked up by
pulpas a dependency.
If you want to change any of these defaults, you can—
pulpoffers a number of command line flags to alter its behaviour—but try to avoid using them unless you have a good reason to.
If you get fed up with having to remember long
pulpinvocations, try using
npmas your build tool.
pulp's numerous command line flags make it well suited for this.
To get a quick overview of the things
pulpcan do, you can ask it to give you a list of its available commands:
$ pulp --help
This will print a list of
pulp's global command line options, and a list of commands it will accept.
To see the available options for a specific command, you can invoke the command with the
--helpflag, like this:
$ pulp build --help
This will give you an exhaustive list of ways you can modify the basic behaviour of the command.
Notice that there's a distinction between global command line options and command specific options. Global options must appear before the name of the command, and command specific options must appear after it.
Thus, if you want to run the
buildcommand in watch mode (where it will run the command once, then wait and re-run the command whenever you change a source file) you need to put the
--watchflag before the command itself, like so:
$ pulp --watch build
On the other hand, if you want to tell the build command to produce optimised code (performing dead code elimination), using the command specific option
--optimise, the flag needs to come after the command name:
$ pulp build --optimise
pulpcommands sometimes allows you to pass flags through to the
purscompiler. Any options appearing after
--will be passed through to the compiler, or whichever process a
pulpcommand spawns. For instance, if you want to tell
pursto skip applying tail call optimisations, you would invoke
pulp buildlike this:
$ pulp build -- --no-tco
pulpis just a frontend for the PureScript compiler,
purs. Its basic function is to compile your project, which you can do by running
pulp build. This will simply run
outputfolder. These files will all be CommonJS modules, which you can
require()using anything which supports CommonJS, such as
However, you will usually want to do more with your project than just compile your PureScript code into a jumble of CommonJS modules.
pulpprovides a number of commands and options for the most common use cases.
pulp buildcan also call
purs bundlefor you, which is a compiler tool whose job it is to take the output from
There are two command line options you can give
pulp buildto accomplish this, depending on where you want the resulting code. You can use the
--optimiseflag (or its shorthand alias,
-O), which will send the bundled result to standard output, or you can use the
-t) option, passing it a file name, and
pulpwill store the bundle in a file of that name.
So, you can use either of these methods, which in this example will both have the same effect:
$ pulp build --optimise > hello.js $ pulp build --to hello.js
Note that using both options (
pulp build --optimise --to hello.js) is superfluous. The presence of
--toimplies the presence of
If you're developing a Node project using PureScript, you can tell
pulpto run it after compiling using the
pulp runcommand. This command will first run
pulp buildfor you, if necessary, then launch your compiled code using
node. If you have used any pass-through command line options, these will be passed to the
So, to run the hello world project you get from
pulp init, you would simply:
$ pulp run
If you want to pass command line arguments to your application,
pulplets you do that too:
$ pulp run -- file1.txt file2.txt file3.txt
If you want to run your application using something other than
pulplets you do that too, with the
--runtimeoption. For instance, if you've written an application which runs on PhantomJS, you might launch it like this:
$ pulp run --runtime phantomjs
pulphas a command
pulp test, which works much like
pulp run, except it will also compile the code you've placed in your
testfolder, and instead of running the
mainfunction in your
Mainmodule, it will use
Test.Main. This module should be located in your
pulpdoesn't care what test framework you've chosen, as long as there's a
mainfunction in your
Test.Mainmodule to be run. If the process exits with a non-zero return code, that means your test suite failed, as far as
pulpis concerned, and it will itself exit with an error.
In short, to run your tests:
$ pulp test
To continuously run your tests when you change the source code:
$ pulp --watch test
It's sometimes useful to kick off a command before or after an action, particularly in combination with the
--watchoption above. To do this, you can use
--elsefor successful or failing actions respectively:
$ pulp --watch --before clear build # Clears the screen before builds. $ pulp --watch --then 'say Done' build # On OS X, announces 'Done' after a successful build. $ pulp --watch --else 'say Failed' build # Announces 'Failed' if a build failed.
A more long-winded example combining the three:
$ pulp --watch --before clear --then "say $(basename
pwd) succeeded." --else 'say $(basename
pwd) failed.' build
pulpprovides a command for it:
pulp browserify. As the name suggests, this uses Browserify to bundle up your PureScript code with Node style CommonJS dependencies.
For instance, the majority of web UI libraries for PureScript these days depend on either virtual-dom or React as a CommonJS dependency. Here is how you would add React to your project and build a JS bundle with React included (assuming your PureScript code
$ npm install react $ pulp browserify --to hello.js
pulp browserify --toworks exactly like
pulp build --to, except it also resolves CommonJS dependencies and includes them in the bundle. The resulting JS file can now be loaded directly into the browser, and everything you need to run your application should be included.
If you omit the
--tooption, the bundle is piped to standard output. This would thus have the same effect as the example above:
$ pulp browserify > hello.js
pulp browserifywill pull code in at the module level by default, so every file
required from your entry point will appear in the bundle. The PureScript compiler, as we know, is able to perform dead code elimination on your compiled PureScript code, and we can leverage this in
pulp browserifyusing the
$ pulp browserify --optimise --to hello.js
Note that, unlike
--todoesn't automatically imply
--optimise. In fact, if you omit
pulp browserifywill not only omit the dead code elimination step, it will also run Browserify as an incremental build, which means it will run considerably faster. You should use
--optimiseonly when you're building production code—when you're developing, you'll probably prefer the much faster compile times provided by Browserify's incremental mode.
While browserified bundles are intended to be consumed directly by browsers, you may sometimes prefer to access the bundle from some external code. While it's generally preferable to consume CommonJS modules directly, there are use cases where you might want to provide a single JS file ready to be
required by a consumer without needing to deal with installing and resolving dependencies. Browserify provides the
--standalonemechanism for that, and
pulp browserifysupports it:
$ pulp browserify --standalone myBundle --to myBundle.js
This makes a bundle which comes wrapped in a UMD header (meaning it supports both CommonJS and AMD, and will install itself in the global namespace under the name you provided if neither is present), and the exports it provides will be the same as those you export in your
So, given the example above produces a bundle where a PureScript function
var myBundle = require("./myBundle"); myBundle.main();
PureScript has an inline syntax for documentation, which can be extracted into Markdown or HTML files using the
pulp docscommand to make this process easy:
$ pulp docs [--with-dependencies]
This extracts the documentation from your source files, and places it in the
generated-docsfolder under your project's root folder. By default, dependencies are not included, but this can be enabled with the
You can also extract documentation from your tests, if you like:
$ pulp docs --with-tests
purs docscommand itself also accepts some options to modify its behaviour, which can be specified by using pass-through options. The
--formatoption is particularly useful, as it allows you to specify the desired output format. In particular, you can generate nice hyperlinked Pursuit-style HTML docs with the following command:
$ pulp docs -- --format html
It is a good idea to run this command and browse the generated HTML documentation before publishing a library to Pursuit, as doing so will allow you to spot any formatting issues or any declarations which are missing documentation.
purs replinteractive shell for PureScript is fantastically useful, but setting it up can be a bit of a chore, especially with a large number of dependencies. That's where
pulp replcomes in.
pulp replwill generate a
.purs-replfile for your project automatically whenever you invoke it, and launch
purs replfor you directly. It's as simple as:
$ pulp repl
A common need when developing client side web apps is a tightly integrated development web server, which takes care of compilation for you on the fly. This is what
pulp serveris for: whenever you make a change to your source files, you just switch to your browser and hit the refresh button, and the server will compile and deliver your assets on the fly. No need to wait for the PureScript compiler to finish before switching to the browser.
pulp serveronly provides the most basic functionality: it will serve static assets from your project root, and it will serve your compiled JS bundle from
To see how this works, let's set up a project for serving the default hello world app through
$ mkdir hello-server $ cd hello-server $ pulp init
We need an
index.htmlfile to load our compiled PureScript code. Place this in your new
<h1>Hello sailor!</h1> <script src="/app.js"></script>
Now, start the server:
$ pulp server
It will tell you that it's launched a web server at http://localhost:1337/, and after a little while it will tell you that it's finished compiling:
If you browse to http://localhost:1337/, you should, in addition to the "Hello sailor!" header on the webpage, see that your PureScript code has printed the text "Hello sailor!" to the console.
As mentioned, this is a very bare bones development server, since
pulp serveris intended as a starting point only. You're likely to quickly need more features if you plan on doing any kind of serious web development. At this point, you'll need to look further afield; one option is to use Webpack together with purs-loader.
pulpis not a package manager, only a build tool. The PureScript community has standardised on Bower as the default package manager, but there are alternatives such as psc-package. Currently,
pulpsupports both Bower and psc-package.
Pulp expects the presence of a project manifest file in your project root, in which your project’s dependencies and other metadata are recorded. If you're using Bower, that file will be
bower.json; if you're using psc-package, it will be
When you run commands like
pulp build, Pulp will locate PureScript source files from installed dependencies based on which of these two files it finds in your project, and pass these files on to the relevant program (e.g.
purs compile). If your project has both
psc-package.jsonfiles, Pulp uses the dependencies installed via Bower by default; if you want to use dependencies installed via psc-package, you can use the
$ pulp --psc-package build
You can also run
pulp --psc-package initto initialize a project with a
psc-package.jsonfile instead of a
This document isn't going to explain how Bower works, or go into details about PureScript dependency management. However, a tl;dr is often enough to get you started and productive without having to dive into yet another package management system. It's going to be especially easy if you're already used to
npm. So, here we go.
To install the
purescript-profunctorpackage into your project:
$ bower install purescript-profunctor
To also record this as a dependency in the
$ bower install --save purescript-profunctor
To install every dependency which has been recorded in
bower.jsonas needed by your project:
$ bower install
To remove an installed package:
$ bower uninstall purescript-profunctor
To remove it from
$ bower uninstall --save purescript-profunctor
To list all packages installed in your project:
$ bower ls
To update all installed packages to the most recent version allowed by
$ bower update
Imagine you've created a new PureScript library for working with zygohistomorphic prepromorphisms (because who doesn't need zygohistomorphic prepromorphisms), called
pulp initwill have installed a basic
bower.jsonfile for you along with the project skeleton, but before you continue, you should read the Bower documentation on the file format and make sure you’ve configured it to your satisfaction before you publish your package. In particular, mind that you’ve added a
Note that there is a convention of prefixing PureScript package names with
purescript-. Please stick with that unless you have an especially good reason not to, as
pulpand many other tools expect installed dependencies to follow this convention.
You would start by tagging an initial version:
$ cd /path/to/purescript-zygo $ pulp version 0.1.0
This runs a few checks to ensure that your package is properly set up for publishing, and if they pass, creates a Git tag
Bower packages are installed directly from Git repositories, and versioning follows Git tags. This means that once you've tagged a version, all you need to do to make a new release is push that tag to GitHub, register your package in the Bower registry, and upload your package's documentation to Pursuit. Pulp is able to do all of this for you:
$ pulp publish
For subsequent releases, the process is the same:
pulp versionfollowed by
pulp publish. When tagging a new version,
pulp versionalso allows you to supply an argument of the form
major, in addition to specific versions. If you run
pulp version patch, for example, Pulp will look through your Git tags to find the version number for the latest release, and then generate the new verision number by bumping the patch component. The
majorarguments respectively perform minor and major version bumps in the same way.
Pulp does not currently support publishing packages which use psc-package exclusively, because without having submitted your package to a registry such as the Bower registry, there is no way of making sure that people agree which package a given package name refers to. This may change in the future.
To work on
pulp, after cloning the repository, run:
$ npm install $ bower install
to install dependencies. Then, you can run
$ npm run -s build
$ npm test
to run the tests.
Copyright 2014-2017 Bodil Stokke, Harry Garrood
This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
See the LICENSE file for further details.