Spago

Intro

Spago is a new CLI that can replace your usage of Psc-Package, using Dhall to configure your packages and your project.

See the Spago repo for more: https://github.com/spacchetti/spago

What does all of this mean?

spago aims to tie together the UX of developing a PureScript project.In this Pursuit (see what I did there) it is heavily inspired by Rust’s Cargo and Haskell’s Stack, and builds on top of ideas from existing PureScript infrastructure and tooling, as psc-package, pulp and purp.

Installation

Right, so how can I get this thing?

The recommended installation methods on Linux and macOS are:

  • npm install -g purescript-spago (see the latest releases on npm here)
  • Download the binary from the latest GitHub release
  • Compile from source by cloning this repo and running stack install

Note #1: support for Windows is still basic, and we’re sorry for this - the reason is that no current maintainer runs it.Currently the only way to install on Windows is with stack - more info in #57.If you’d like to help with this that’s awesome! Get in touch by commenting there or opening another issue :)

Note #2: we assume you already installed the PureScript compiler. If not, get it with npm install -g purescript

Quickstart

Let’s set up a new project!

$ mkdir purescript-unicorns
$ cd purescript-unicorns
$ spago init

This last command will create a bunch of files:

.
├── packages.dhall
├── spago.dhall
├── src
│   └── Main.purs
└── test
    └── Main.purs

Convention note: spago expects your source files to be in src/ and your test files in test/.

Let’s take a look at the two Dhall configuration files that spago requires:

  • packages.dhall: this file is meant to contain the totality of the packages available to your project (that is, any package you might want to import).In practical terms, it pulls in a Spacchetti package-set as a base, and you are then able to add any package that might not be in the package set, or override esisting ones.
  • spago.dhall: this is your project configuration. It includes the above package-set, the list of your dependencies, and any other project-wide setting that spago will use for builds.

Configuration file format

It’s indeed useful to know what’s the format (or more precisely, the Dhall type) of the files that spago expects. Let’s define them in Dhall:

-- The basic building block is a Package:
let Package =
  { dependencies : List Text  -- the list of dependencies of the Package
  , repo = Text               -- the address of the git repo the Package is at
  , version = Text            -- git tag
  }

-- The type of `packages.dhall` is a Record from a PackageName to a Package
-- We're kind of stretching Dhall syntax here when defining this, but let's
-- say that its type is something like this:
let Packages =
  { console : Package
  , effect : Package
  ...                  -- and so on, for all the packages in the package-set
  }

-- The type of the `spago.dhall` configuration is then the following:
let Config =
  { name : Text               -- the name of our project
  , dependencies : List Text  -- the list of dependencies of our app
  , packages : Packages       -- this is the type we just defined above
  }

Commands

For an overview of the available commands, run:

$ spago --help

You will see several subcommands (e.g. build, test); you can ask for help about them by invoking the command with --help, e.g.:

$ spago build --help

This will give a detailed view of the command, and list any command-specific (vs global) flags.

Package management

We initialized a project and saw how to configure dependencies and packages, the next step is fetching its dependencies.

If we run:

$ spago install

..then spago will download all the dependencies listed in spago.dhall (and store them in the .spago folder).

Building, bundling and testing a project

We can then build the project and its dependencies by running:

$ spago build

This is just a thin layer above the PureScript compiler command purs compile.The build will produce very many JavaScript files in the output/ folder. These are CommonJS modules, and you can just require() them e.g. on Node.

It’s also possible to include custom source paths when building (src and test are always included):

$ spago build --path 'another_source/**/*.purs'

Note: the wrapper on the compiler is so thin that you can pass options to purs. E.g. if you wish to output your files in some other place than output/, you can run

spago build -- -o myOutput/

Anyways, the above will create a whole lot of files, but you might want to get just a single, executable file. You’d then use the following:

# You can specify the main module and the target file, or these defaults will be used
$ spago bundle --main Main --to index.js
Bundle succeeded and output file to index.js

# We can then run it with node:
$ node .

However, you might want to build a module that has been “dead code eliminated” if you plan to make a single module of your PS exports, which can then be required from JS.

Gotcha covered:

# You can specify the main module and the target file, or these defaults will be used
$ spago make-module --main Main --to index.js
Bundling first...
Bundle succeeded and output file to index.js
Make module succeeded and output file to index.js

> node -e "console.log(require('./index).main)"
[Function]

You can also test your project with spago:

# Test.Main is the default here, but you can override it as usual
$ spago test --main Test.Main
Build succeeded.
You should add some tests.
Tests succeeded.

Can I use this with psc-package?

Yes! Though the scope of the integration is limited to helping your psc-package-project to use the Spacchetti package-set.

Here’s what we can do about it:

psc-package-local-setup

This command creates a packages.dhall file in your project, that points to the most recent Spacchetti package-set, and lets you override and add arbitrary packages.See the Spacchetti docs about this here.

psc-package-insdhall

Do the Ins-Dhall-ation of the local project setup: that is, generates a local package-set for psc-package from your packages.dhall, and points your psc-package.json to it.

Functionally this is equivalent to running:

NAME='local'
TARGET=.psc-package/$NAME/.set/packages.json
mkdir -p .psc-package/$NAME/.set
dhall-to-json --pretty <<< './packages.dhall' > $TARGET
echo wrote packages.json to $TARGET

FAQ

Hey wait we have a perfectly functional pulp right?

Yees, however:

  • pulp is a build tool, so you’ll still have to use it with bower or psc-package.
  • If you go for bower, you’re missing out on package-sets (that is: packages versions that are known to be working together, saving you the headache of fitting package versions together all the time).
  • If you use psc-package, you have the problem of not having the ability of overriding packages versions when needed, leading everyone to make their own package-set, which then goes unmaintained, etc.Of course you can use Spacchetti to solve this issue, but this is exactly what we’re doing here: integrating all the workflow in a single tool, spago, instead of having to use pulp, psc-package, purp, etc.
So if I use spago make-module this thing will compile all my js deps in the file?

No. We only take care of PureScript land. In particular, make-module will do the most we can do on the PureScript side of things (dead code elimination), but will leave the requires still in.To fill them in you should use the proper js tool of the day, at the time of writing ParcelJS looks like a good option.