There is now a version of this guide for 2020:
https://discourse.purescript.org/t/recommended-tooling-for-purescript-in-2020
I often see questions in Slack about what tools are supposed to be used in PureScript projects. This post is an attempt to clarify tooling for the common case: a PureScript app run in the browser, possibly with a mix of JavaScript and PureScript code.
There are a few sections here, feel free to skip around:
- Summary of tools
- Starting a new project, installing tools
- Installing PureScript libraries, building the project
- Installing JavaScript libraries
- Bundling our application to a single
.js
file, including JavaScript and PureScript code - Using Yarn +
package.json
as a script runner
Side note: all of this stuff is used in practice in the Real World Halogen example application, if youād like to see it in action.
Summary of Tools
If you are starting a new application in PureScript today, I recommend the following tools:-
yarn
to install all the other tools (including the PureScript compiler), install JavaScript libraries, and run scripts from apackage.json
file. As of Feb 2020: The new Yarn 2 doesnāt support installing the PureScript compiler. Yarn 1 is still fine, but you should prefer NPM to Yarn 2. -
spago
to install PureScript libraries, build the PureScript project, generate documentation, and perform other miscellaneous PureScript tasks -
webpack
orparcel
to perform bundling and optimization steps on the JavaScript compiled by PureScript and any JavaScript dependencies, to ultimately produce a singleapp.js
file.
Some other tools worth mentioning:
-
psa
is an error / warning reporting frontend for the compiler which lets you customize warnings, get better source spans in errors, and more. If you are using Spago, then Spago will usepsa
by default if you have it installed. -
npx
, which comes with your Node /npm
installation, lets you run all commands using locally-installed tools instead of globally-installed ones. For example, to use a local Spago install instead of a global one:npx spago build
instead ofspago build
. Note: @cprussin mentioned that Yarn supports the same functionality without using npx:yarn spago build
will look forspago
in your scripts and then in your local installed modules. -
If you use
nix
, then you can just usenix
to install all these tools instead, skippingnpx
. In fact, withspago2nix
andyarn2nix
you can generate Nix expressions for all your dependencies. You can also easily install all the tools in this section on Nix usingeasy-purescript-nix
. -
purty
is a code formatting tool for PureScript. Itās excellent, and you can install it the same way as everything else. -
zephyr
is a dead code elimination tool for PureScript. If you have a lot of dependencies with unused modules (purescript-web-*
is a common culprit) then it can nicely trim down your bundle size. You may have to download the binary directly, or usenix
to install it. -
psc-ide
provides really good editor support for PureScript, usable in VSCode, Emacs, Vim, etc. and is provided as part of the compiler by default (you donāt need to install anything). -
pscid
is a really fast file watcher which will report errors and provide suggestions in the shell. As mentioned by @Benjmhart this can be useful when the compiler + your bundler doesnāt provide a fast enough feedback cycle. -
psvm
is a version manager for PureScript; if you donāt use local PureScript installs for your projects and install rely on a global installation, thenpsvm
lets you easily switch between different versions of PureScript.nvm
is a similar tool for Node versions, which inspiredpsvm
.
Some other tooling from the PureScript ecosystem which I no longer use often, but which youāll see in the wild:
-
npm
is used for the same things asyarn
(install JavaScript libraries, install tooling from the NPM registry like the compiler and Spago, run scripts frompackage.json
) and was the only option before 2016. I and many others prefer Yarn, but it doesnāt really matter which one you use for a PureScript project. -
bower
was a package manager for JavaScript which happened to work well for PureScript, too. Itās now in maintenance mode, and most folks use Yarn for JavaScript dependencies and Spago for PureScript dependencies. Bower is still used as a registry for PureScript packages and you will still need to use it if you are publishing a library (though Spago will support this soon). -
pulp
was the de facto standard build tool for PureScript projects until Spago was released. Both tools will let you build your project using the PureScript compiler (purs
) and largely have similar commands. -
psc-package
was the first package manager built on the concept of package sets (a collection of packages which are all known to compile together, which you install without specifying a specific version). However, its main deficiency was that you had to either download the main PureScript package set manually and place it in your project, or write your own. You then had to manually add packages and maintain the package set, all in difficult-to-maintain JSON files. It is not a build tool, but is used by other build tools under the hood. Spago provides a much better workflow, and is also based on package sets. See this issue from the Real World project for problems I had withpsc-package
and this pull request which migrated the project from Bower to Spago. -
purp
is a tool for building projects usingpsc-package
. I havenāt used the tool myself, but as far as I can tell its functionality is matched (and exceeded) by Spago. -
spacchetti
was the first tool to manage package sets as Dhall files instead of JSON, unlocking significantly easier modification of the main package set + adding your own packages. Itās now used to power the main package set and other functionality has been rolled in to Spago instead.
As of today, if you are publishing a library then you will most likely want to use Pulp + Bower, and otherwise you will most likely want Spago. In the near future, Spago will also support library publishing.
Starting a New Project
Going back to the original list of tools, hereās a sequence of commands to set up a new project which relies on a few libraries from the PureScript and JavaScript. It assumes you already have Yarn installed, but will install the rest along the way.
mkdir my-project && cd my-project
yarn add --dev purescript spago parcel-bundler
At time of writing, Yarn has produced a package.json
file containing these dependencies:
{
"devDependencies": {
"parcel-bundler": "^1.12.3",
"purescript": "^0.13.3",
"spago": "^0.9.0"
}
}
We can now start using these locally-installed tools to get our project started. First, weāll initialize our PureScript project ā this will create a .gitignore
file, src
and test
directories, and sync us with the latest official package set.
# psc-0.13.3-20190831 is the package set at time of writing
yarn spago init
We can also compile and run the Main.purs
file which has been created in the src
directory:
$ yarn spago run
Installation complete.
Src Lib All
Warnings 0 0 0
Errors 0 0 0
Build succeeded.
š
Installing PureScript Dependencies
Letās say weāre going to use Halogen to build our UI ā we can now use Spago to install this PureScript library:
yarn spago install halogen
Now that itās installed, we can recompile our project so Halogen and its dependencies are built:
yarn spago build
And we could now start using Halogen in our project:
module MyApp.MyComponent where
import Halogen as H
import Halogen.HTML as HH
...
Installing JavaScript Libraries
Letās say we want to use `marked`, a JavaScript library for Markdown, via PureScriptās foreign-function interface. We can go back to Yarn to install the library:yarn add marked
And now our package.json
has the marked
library installed, too.
{
"devDependencies": {
"parcel-bundler": "^1.12.3",
"purescript": "^0.13.3",
"spago": "^0.9.0"
},
"dependencies": {
"marked": "^0.7.0"
}
}
Setting Up Bundling (Serving to the Browser)
Weāve compiled our PureScript project, which has produced JavaScript files in our `output` directory, but we havenāt yet got anything we can actually send to the browser.Note: this section is going to be rapid-fire just so youāre aware of the basics. Itās not a tutorial on using tools like Parcel and Webpack. See projects like Vidtracker or Real World Halogen for real world examples.
Letās use Spago to create a single app.js
file from our project, instead of just compiling a bunch of modules in the output
directory. That way we can actually import the resulting JavaScript file into an HTML file and view it in the browser.
mkdir dist
yarn spago bundle-app --main Main --to dist/app.js
Now, letās create a tiny HTML file which imports this JavaScript. Weāll then be able to open that file in the browser and use our app.
mkdir assets && touch assets/index.html
We can use a tiny HTML file like this one ā just enough to provide a DOM node for our application to run inside, and an import for our bundled app.js
file, which weāll create in a moment:
<!-- my-project/assets/index.html -->
<!DOCTYPE html>
<html>
<body>
<div id="app"></div>
<script src="../dist/app.js"></script>
</body>
</html>
If we had actually made a Halogen app, weād be able to see it now. But this isnāt quite enough ā we actually need to bundle in a JavaScript library, too, the marked
library we installed earlier.
Most single-page applications will use a bundler like Parcel or Webpack to bring all their JavaScript, CSS, and HTML together, minify them, and do other processing for efficiency. They also typically support other conveniences like dev servers and hot reloading.
Letās get Parcel working to bring our JavaScript and PureScript together in a single JavaScript file which can be shipped to the browser. Luckily for us, all we need to do is run parcel
on our index.html
file:
yarn parcel build assets/index.html
Running Scripts
Itās a bit tedious running all these commands manually. What if we wanted some automation? We donāt need to introduce any new tools.We can adjust the package.json
file to have a āscripts"
key where we put commands weād like to be able to run with Yarn. Hereās a small example:
{
"scripts": {
"postinstall": "spago install",
"build": "spago build",
"clean": "rm -rf node_modules output .spago dist/* *.lock",
"bundle": "spago bundle-app --main Main --to dist/app.js && parcel build assets/index.html",
},
"devDependencies": {
"parcel-bundler": "^1.12.3",
"purescript": "^0.13.3",
"spago": "^0.9.0"
},
"dependencies": {
"marked": "^0.7.0"
}
}
Now that we have these scripts in place, we can run commands like:
-
yarn build
to replaceyarn spago build
-
yarn bundle
to replaceyarn spago bundle-app --main Main --to dist/app.js && parcel build assets/index.html
-
yarn install
to replaceyarn install && yarn spago install