Library / SDK
purescript/spago avatar
purescript/spago

Spago: a package manager and build tool for PureScript projects

🍝 PureScript package manager and build tool

830 stars138 forksPureScriptBSD-3-Clause

At a glance

What is it?
Spago is the PureScript package manager and build tool, and it takes on the job that Dhall-based tooling used to handle. The interesting parts are the package set model, the split between a workspace section and a package section in spago.yaml, and the node version floor the tool now assumes.
Who is it for?
Spago is the right choice for teams already writing PureScript in 2026, especially those moving away from spago.dhall or bower, since it resolves dependencies, compiles and runs a project from one config file. It is the wrong choice if your pipeline cannot move to node 22.5.0 or newer, or if you need a package set version that the public registry does not carry.
Can I use it commercially?
Yes. BSD-3-Clause is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly PureScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Spago is a PureScript package manager, not a general purpose task runner

Spago describes itself in one line as a PureScript package manager and build tool, and the repository topics follow the same narrow line: build-tool, package-manager, package-sets, purescript. There is no plugin system, no shell script format and no attempt to replace make or a CI orchestrator. What Spago owns is the part of a PureScript project that is genuinely hard to hand-roll: deciding which version of which library a module may import, fetching that code, compiling the result, and emitting JavaScript that a runtime can execute.

The implementation is PureScript itself, which is a neat bit of self-hosting. The npm package is published under the name spago, at version 1.0.5, and its entry point is the bundled file bin/bundle.js. The source tree is split into a core/ directory, a src/ directory and a bin/ directory, and the project's own package.json builds those with a spago bundle command that targets a package named spago-bin. The engine field requires node at version 22.5.0 or above, which is the single most concrete constraint in the whole setup and the first thing to check in a CI image.

Installing Spago means a global npm package on top of a separately installed compiler

The installation path the documentation spells out is npm, and the same command works on Windows, Linux and macOS. Run this to put the spago binary on your PATH:

bash
npm install -g spago

There is a second option, a Nix overlay named purescript-overlay, but the npm route is the one described as recommended. The important catch is stated as an assumption rather than a step: Spago does not install the PureScript compiler for you. If you do not already have it, the documentation points you at the same global npm route.

bash
npm install -g purescript

So a working machine ends up with two global installs, and the order matters only in the sense that the compiler is the piece you can end up missing. Treat `purescript` being on the PATH as a precondition of every Spago command that follows, because every code path in the tool ends in a call to the compiler.

spago init writes a spago.yaml that separates workspace settings from package settings

The fastest way to see the shape of a Spago project is to generate one. Create a directory, move into it, and let the tool scaffold the tree:

console
$ mkdir purescript-pasta
$ cd purescript-pasta
$ spago init

The result is a small tree with a config file, a source directory and a test directory:

code
.
├── spago.yaml
├── src
│   └── Main.purs
└── test
    └── Test
        └── Main.purs

The config file carries two sections, and the division matters more than it first appears. The workspace section holds configuration for the dependencies of the project as a whole, and a workspace can be a monorepo holding more than one package, so it also carries general settings. In a freshly scaffolded project the only thing the workspace section needs is the version of the package set from which all dependencies are chosen. The package section describes the package in hand, its name and its dependencies.

The README points out that the workspace section is not a formality. Because it names a package set version, the entire dependency graph of every package in the workspace is pinned by one value.

spago run folds install, compile and execute into a single command

Once a project exists, one command covers the whole cycle:

console
$ spago run

The documentation spells out the three stages that happen under that one word. Dependencies are downloaded and compiled, which is what a separate spago install does. The project itself is compiled into the output/ directory, which is what a separate spago build does. The generated JavaScript is then executed, which the documentation compares to importing the built file and calling its main with Node:

console
$ node -e "import('./output/Main/index').then(m => m.main())"

The compiled module lands at output/Main/index.js, so you can open it and see what the compiler made of your Main.purs. That transparency is a real advantage over a bundler that hides intermediates, and it is also why a separate spago build step stays useful in scripts: the compile artifact exists on disk whether or not you intend to run it.

The README also documents a way to see which purs commands the tool issues, which is the place to look when a build behaves differently from what you expected.

spago bundle with --bundle-type app produces a file Node can run directly

For a command line program, compiling into a directory of modules is awkward. Spago can instead produce a single bundled file with an entry point:

console
$ spago bundle --bundle-type app --platform node

After that, the run command is simply:

console
$ node .

The choice of platform is explicit, and node is one of the documented values. The same section of the documentation covers source maps, with separate instructions for node and for browsers, so the two targets are treated as genuinely different environments rather than one default with a flag. Alternate backends are also listed as a supported route, alongside a way to skip the build step when a project does not need it and a section on generated build info and metadata.

For library authors, the publish flow is documented too, including publishing many packages together, and there is a section on authenticated commands with a specific case for transferring a package to a new owner. The dependency list in package.json includes ssh2, which is consistent with those authenticated operations needing a way to talk to a remote host.

The rewrite leaves spago.dhall and bower projects stranded on the old path

The single most important thing to understand about Spago today is that the current codebase is a rewrite. The documentation warns that it concerns the new PureScript implementation, and that anyone looking for the Haskell codebase should go to the spago-legacy repo, which covers versions up to 0.21.x. The current npm package is at 1.0.5, so the version numbers themselves do not tell you which lineage you are looking at.

The consequence is a hard break for existing projects. The table of contents lists a section for migrating from spago.dhall to spago.yaml, and another for migrating from bower, but those are migration paths with a cost, not a compatibility mode. A project with a spago.dhall file has to convert its configuration format, and a project that resolved dependencies through bower has to adopt a package set instead. The pure JavaScript ecosystem solved the same class of problem without a migration cliff, and PureScript teams paid for the rewrite in a single breaking change.

Two other gaps are worth knowing before you commit. The engine field demands node 22.5.0 or newer, which rules out many older CI images outright. And the documentation does not describe a rollback procedure for a package set version that turns out to be wrong, so treat the version you write into the workspace section as something you verify before it reaches a shared repository.

Licence, dependency surface and the pace of recent releases

Spago is released under the BSD-3-Clause licence, which is a permissive licence that allows reuse in closed and open projects alike, including commercial ones. The dependencies listed in package.json give a sense of the surface area: tar and semver for handling archives and versions, glob and picomatch for file matching, fs-extra and @nodelib/fs.walk for filesystem work, open for launching programs, markdown-it for rendering documentation, and spdx-expression-parse for reading licence expressions.

The release history is short and recent. Version 1.0.3 was published on 2026-02-01, 1.0.4 on 2026-03-30, and 1.0.5 on 2026-10-13, with the last push to the repository on 2026-10-18. The repository is not archived, and the recent push date means the project is not in the position of an abandoned tool that you would have to fork.

That recency also carries a cost. A tool at 1.0.5 whose configuration format is only a couple of releases old is likely to keep changing, and the CHANGELOG.md, CONTRIBUTING.md and flake.nix in the repository root are where to look for what moved between releases before you pin a version in your own build.

Editorial conclusion

Spago is the right choice for teams already writing PureScript in 2026, especially those moving away from spago.dhall or bower, since it resolves dependencies, compiles and runs a project from one config file. It is the wrong choice if your pipeline cannot move to node 22.5.0 or newer, or if you need a package set version that the public registry does not carry. Before adopting it, check that the package set version named in your spago.yaml still resolves, that your CI images carry a recent node, and that the compiler you install separately matches the version your code expects.

Frequently asked questions

How do I install Spago and the PureScript compiler it needs?

Install Spago globally with npm, which the documentation recommends for Windows, Linux and macOS, and install the PureScript compiler separately with npm if you do not already have it. The tool assumes a PureScript compiler is already present, and a Nix route through purescript-overlay is also listed.

What does spago run actually do when I run it?

It downloads and compiles the dependencies, compiles the project into the output/ directory, and then runs the generated JavaScript. The compiled entry module is written to output/Main/index.js.

How do I move an existing project from spago.dhall to spago.yaml?

The current Spago is a PureScript rewrite, and the documentation includes a dedicated section for migrating from spago.dhall to spago.yaml, with the older Haskell codebase kept in the spago-legacy repo for versions up to 0.21.x. The new config format splits settings into a workspace section and a package section.

Can one Spago project hold several packages?

Yes. The workspace section of spago.yaml holds configuration for the dependencies of the project as a whole, and a workspace can be a monorepo containing more than one package. The documentation also lists monorepo and polyrepo support in its table of contents.

Official sources

  1. Issues
  2. License: BSD-3-Clause
  3. purescript/spago on GitHub
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/purescript-spago.svg)](https://hysenlabs.com/projects/purescript-spago)

Was this useful?