Skip to main content

Setup toolchain

4 min

One of moon's most powerful features is the toolchain, which manages and automatically installs Node.js and other tools. The toolchain is configured with .moon/toolchain.yml.

caution

This toolchain article is currently Node.js only. If you're using another language, feel free to skip to the next article, creating a project.

Choosing a package manager

Before we continue, we should briefly talk about Node.js package managers. To start, moon does not replace a package manager, nor does it apply any "magic" for them to work differently. Instead, moon builds upon a package manager to provide a robust task runner, assumes a standard node_modules layout and module resolution algorithm, and supports any of the following 3 package managers.

Enabling workspaces (optional)

moon was built with monorepos in mind, and as such, has first-class support for them through package workspaces (but is not a requirement). To utilize workspaces, enable them for your chosen package manager.

package.json
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
.yarnrc.yml
# ...
nodeLinker: 'node-modules'

Configuring Node.js

As Node.js is the backbone of a JavaScript based repository, we need to enable its functionality. This is done by defining node in .moon/toolchain.yml. Languages are like plugins, where the existence of a setting enables the plugin, hence why an empty object below is acceptable.

.moon/toolchain.yml
node: {}

By default, the language will execute tasks using the global node binary available on PATH. While this works, we suggest using our toolchain that will download, install, and execute tasks with the same version of Node.js across all machines. To enable the toolchain, simply define the version with node.version.

.moon/toolchain.yml
node:
version: '18.0.0'

This setting requires a fully qualified semantic version and does not support version ranges, so be sure to use an explicit version. We suggest always using an Active LTS version.

Let's now run moon setup --log debug to verify that Node.js is downloaded and installed correctly. Pretty cool right?

info

The Node.js version can also be customized per project using the toolchain.node.version setting in moon.yml. This setting exists to support legacy projects that are coupled to an old version and are unable to upgrade. We suggest keeping all projects on the same version whenever possible.

Configuring a package manager

Even though Node.js is now installed, we need a package manager to install dependencies. Earlier we talked about choosing a package manager, so let's use that choice here by defining node.packageManager.

.moon/toolchain.yml
node:
version: '18.0.0'
packageManager: 'yarn'

By default moon will install a stable version of the package manager, but this isn't consistently updated, so we suggest defining an explicit semantic version for the package manager as well, through the node.<packageManager>.version setting.

.moon/toolchain.yml
node:
version: '18.0.0'
packageManager: 'yarn'
yarn:
version: '3.2.0'

Once again, let's run moon setup --log debug to verify the package manager is installed, and also take note that Node.js is already installed. Based on the example configuration above, we'll be switching from npm (the default) to yarn.

Adding a package script

For scenarios like CI, moon can be ran through a root package.json script — but this comes with a cost — which is the overhead of launching Node.js and your chosen package manager to execute the Rust binary, instead of executing the Rust binary directly. If this is problematic, feel free to use the global moon binary.

package.json
{
// ...
"scripts": {
// ...
"moon": "moon",
// For Yarn 2+
"moon": "$(yarn bin moon)"
}
}
caution

Yarn 2+ does not support executing Rust binaries through package scripts (issue), so we must access the full binary path and execute on that.

With this script, moon can then be run with npm run moon ... (or yarn run, or pnpm run).

Next steps