moon v1.40 - JavaScript ecosystem WASM toolchains and more
It's been a while since our last release, as we've been busy working on new JavaScript ecosystem WASM toolchains, which are now available!
New JavaScript ecosystem toolchains powered by WASM
Porting the legacy Bun and Node.js toolchains to WASM has been a non-trivial amount of work, as the JavaScript ecosystem is quite convoluted compared to other languages. The paradigms required by JavaScript simply don't exist in other languages, and as such, we've had to build custom functionality into our toolchain plugin system to support it properly.
On top of that, Bun and Node.js share a lot of functionality, and if you've been using both legacy
toolchains in parallel, you may have noticed a handful of issues because of this, such as
overlapping dependency installs, conflicting alias/task extraction, or over-reading of
package.json
files. Additionally, this doesn't even take Deno into account, which has its own set
of problems to solve for interoperability.
To solve these problems, we've reimagined how JavaScript toolchains work in moon with the following goals in mind:
- Share as much functionality across Node, Bun, and Deno without duplication.
- Support any number of runtimes and package managers with clean interoperability.
- Allow each runtime and package manager to implement their own tier 1-3 features.
- Minimize the complexity of the toolchain configurations.
- Allow users to depend on these toolchains if necessary.
And the result of this rework is 6 new toolchains! Continue reading for more details.
Shared core: unstable_javascript
A new JavaScript toolchain has been introduced, called unstable_javascript
. This toolchain serves
as the foundation for all other JavaScript-related toolchains, providing a shared core of
functionality and settings. It implements tier 1 and tier 2 features, and is in charge of
the following:
- Defines which package manager (and runtime) to use.
- Extends projects and tasks with
package.json
andnode_modules
information. - Locates the dependencies root (
package.json
workspaces). - Installs and dedupes dependencies for the defined package manager.
- Parses manifests and lockfiles for relevant information.
- Handles project and workspace syncing operations.
- And anything else that is shared across the JavaScript ecosystem.
unstable_javascript:
packageManager: 'yarn'
inferTasksFromScripts: false
syncPackageManagerField: true
syncProjectWorkspaceDependencies: true
If you're familar with the legacy Bun or Node.js toolchains, this should feel very familiar, as this is a combination of their functionality. Learn more about its settings:
Runtimes: unstable_bun
and unstable_node
JavaScript is a unique language in that it has multiple runtimes, primarily Node.js, Bun, and Deno.
We support Node.js through the new unstable_node
toolchain, and Bun through the unstable_bun
toolchain, with Deno support coming soon. These toolchains only implement tier 1 and tier 3
features, as tier 2 is handled by the core unstable_javascript
toolchain.
Their primary role is to provide settings for runtime execution (task child processes), and for
downloading and installing the runtime tool into the proto toolchain (when the version
setting is
defined).
The runtime that will be utilized in the action graph is defined by the new
unstable_javascript.packageManager
setting (bun = bun, npm/pnpm/yarn = node), but that doesn't
stop you from using multiple runtimes in parallel.
unstable_node:
version: '24.0.0'
executeArgs: ['--preserve-symlinks']
Learn more about their settings:
Package managers: unstable_npm
, unstable_pnpm
, and unstable_yarn
All JavaScript package managers (including Bun) are now their own toolchain, with their own
configuration, and are no longer nested within the Node.js toolchain. These toolchains only
implement tier 1 and tier 3 features, as tier 2 is handled by the core
unstable_javascript
toolchain.
Their primary role is to provide settings for the unstable_javascript
toolchain when installing
and syncing dependencies, and for downloading and installing the package manager tool into the proto
toolchain (when the version
setting is defined).
Since there are multiple package managers, which do you need to configure? Only the one associated
with the new unstable_javascript.packageManager
setting!
unstable_pnpm:
version: '10.15.0'
installArgs: ['--frozen-lockfile']
Learn more about their settings:
Migrating from legacy toolchains
Migrating from the legacy toolchains to these new modern WASM toolchains is rather straightforward, as most of the existing settings have been ported over. Follow these steps:
- Move the
node.npm
,node.pnpm
, andnode.yarn
configuration to its own top-levelunstable_
toolchain. - Move the
bun.version
ornode.version
setting to anunstable_bun
orunstable_node
toolchain respectively. If not usingversion
, set an empty object. - Remove the
node.addEnginesConstraint
setting. - Rename the
node
orbun
toolchain tounstable_javascript
. - Rename the
node.binExecArgs
setting tounstable_node.executeArgs
. - Rename the
node.rootPackageOnly
setting tounstable_javascript.rootPackageDependenciesOnly
.
As an example, here's a before and after of our repository.
# Before
node:
version: '22.14.0'
packageManager: 'yarn'
yarn:
version: '4.8.0'
inferTasksFromScripts: false
syncPackageManagerField: true
syncProjectWorkspaceDependencies: true
# After
unstable_javascript:
packageManager: 'yarn'
inferTasksFromScripts: false
syncPackageManagerField: true
syncProjectWorkspaceDependencies: true
unstable_node:
version: '22.14.0'
unstable_yarn:
version: '4.8.0'
Backwards incompatibility and caveats
Because these new toolchains are built around a plugin system, and not hard-coded into core like the legacy toolchains, there are some backwards incompatibilities, changes, and caveats to be aware of:
- Because the old
node
/bun
toolchains are now spread across multiple new toolchains, instead of 1 toolchain, any configuration of the tasktoolchain
setting may now be inaccurate, as this setting overrides all detected/inherited toolchains. We suggest omitting this field unless you want full control and understand what you are doing.
# Invalid
tasks:
build:
# ...
toolchain: 'node'
# Valid
tasks:
build:
# ...
toolchain: ['unstable_javascript', 'unstable_node', 'unstable_npm']
# Or simply
toolchain: ['javascript', 'node', 'npm']
- Additionally, task inheritance may not function the same, based on what toolchains are now
automatically detected. Ensure that projects and tasks inherit the correct toolchains by
utilizing
moon project
andmoon task
commands.
If either of these issues, or other unexpected issues arise, please report it so we can fix it, or provide a work around!
New task caching options
Tasks have always supported a cache
option for toggling caching on
and off. With the introduction of remote cache, we're expanding these options to provide more
flexibility and control. Instead of supporting only a simple boolean flag, we're introducing new
local
and remote
values, which will only cache locally or remotely, respectively.
This is useful for tasks that need caching, but should not persist in the remote cache, and vice versa.
tasks:
build:
# ...
options:
cache: 'local'
New local read-only mode for remote cache
Adoption of our new remote cache solution has been going great, and we've heard positive feedback from users about its performance and reliability. However, it's not perfect and can always be improved!
And as such, we're introducing a new
unstable_remote.cache.localReadOnly
setting, which will
only read (download) outputs from the remote cache when in local development, but will not write
(upload) outputs. This is useful for teams that want to share cache between CI and local, but don't
want the overhead of uploading in-development outputs.
unstable_remote:
cache:
localReadOnly: true
Other changes
View the official release for a full list of changes.
- Updated
moon query touched-files
to default to comparing against remote branches when in CI, and local when not in CI. This aligns with the othermoon query
commands. - Updated task commands (child processes) to utilize toolchain executables directly, instead of
relying entirely on proto shims. It achieves this by locating the executables, and prepending
their directory onto
PATH
. - When running a task, we now set a
MOON_TASK_HASH
environment variable for the current hash, which can be read from child processes. - Published the moon VSCode extension to Open VSX: https://open-vsx.org/extension/moonrepo/moon-console
What's next?
With toolchains plugins being stabilized more, we'd like to focus on some other areas.
- Implement more of the task inputs RFC: https://github.com/moonrepo/moon/issues/1985
- Investigate a new
unstable_deno
toolchain - Investigate better task inheritance: https://github.com/moonrepo/moon/issues/2023