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:

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

yarn to install all the other tools (including the PureScript compiler), install JavaScript libraries, and run scripts from a package.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 or parcel to perform bundling and optimization steps on the JavaScript compiled by PureScript and any JavaScript dependencies, to ultimately produce a single app.js file.

If you are starting a new application in PureScript today, I recommend the following tools:

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 use psa 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 of spago build . Note: @cprussin mentioned that Yarn supports the same functionality without using npx: yarn spago build will look for spago in your scripts and then in your local installed modules.

If you use nix , then you can just use nix to install all these tools instead, skipping npx . In fact, with spago2nix and yarn2nix you can generate Nix expressions for all your dependencies. You can also easily install all the tools in this section on Nix using easy-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 use nix 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, then psvm lets you easily switch between different versions of PureScript. nvm is a similar tool for Node versions, which inspired psvm .

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 as yarn (install JavaScript libraries, install tooling from the NPM registry like the compiler and Spago, run scripts from package.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 with psc-package and this pull request which migrated the project from Bower to Spago.

purp is a tool for building projects using psc-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

yarn add marked

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:

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)

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.

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.

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: