Setup toolchain
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
.
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.
- npm
- pnpm
- Yarn
- Yarn (classic)
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
# ...
nodeLinker: 'node-modules'
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
{
// ...
"workspaces": ["apps/*", "packages/*"]
}
packages:
- 'apps/*'
- 'packages/*'
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.
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
.
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?
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
.
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.
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.
{
// ...
"scripts": {
// ...
"moon": "moon",
// For Yarn 2+
"moon": "$(yarn bin moon)"
}
}
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
).