How to create your own modern yarn cli bundle
Tags: yarn, node, javascript, npm
Published: 2022-02-20 18:40:12

Hello dev.to community! 
This is my first post so I'm happy to start to share my experience (hope this will be useful for somebody) with monorepos and yarn tool here.

In my current job I work with the big infrastructure of Node.js multiple repos such as single codebase or monorepos in different customer domains (Angular projects, pure JS, Node.js backends, etc) therefore is so critical to provide better developer and CI/CD experience when we need to work with hundreds projects by the one strategy.

As a package manager we chose yarn berry (I believe I’ll prepare another one post where I’ll compare monorepo tools) because:

* Pluggable system where we can extend existing functionality using JS.
* Efficient strategies to work with dependencies.
* New features from plugins such as modern workspaces like Lerna.
* Is easily to start to use yarn from any project without specific code base changes.
* We had experience with this tool.
* The way to control consistent client for all environments (CI, developers) with the one client version. 

And in this post I want to focus about latest point and how I enhanced it.

## The problem

As I said above Yarn allows to add many really useful plugins to any project, all its binaries and extensions are stored at repository, it provides benefit that each developer or CI/CD environment uses the one consistent environment but from the other had we can face to the problems:

* Yarn doesn’t provide any plugin version manager, I can’t verify that I have latest plugin version and therefore is more difficult that my plugin in specific project has needed version.
* If we have hundreds projects developer have to copy the one plugins set from one to one projects and is more pain to update them one by one.

And when I read yarn API documentation I found very interesting part of it: [yarn builder build](https://yarnpkg.com/builder/cli/build/bundle) bundle which helps to build own yarn cli clients having my own custom set of all required plugins with specific versions with my own version mark - this is solution!
When I use it I can organise my own plugins manifest in package.json and commit their versions using simple dependencies versions where plugins work as simple npm packages.

But this API has the one restriction - is internal building API. I didn’t find an appropriate way to use this builder in my clean project without additional artefacts but this is not a big problem and for now I can share a simple solution of it.

## How to create CLI environment

First of all you should initialise your project using yarn cli:

> yarn init

After you should to set yarn berry in order to work with appropriate API:

> yarn set version berry

In the next step you should think about what you want to archive into your CLI: what plugins, versions, etc. You can take as example official yarn berry cli: https://github.com/yarnpkg/berry/blob/master/packages/yarnpkg-cli/package.json
This `package.json` has several important sections:

* `@yarnpkg/builder` key where you can define profile with buildable plugins (it can be any local or custom plugins), if you don’t need to variate different plugins sets just define “standard” profile from example.
* In your dependencies (or dev) section set related from plugins list packages. It requires by builder to put sources into your cli.
* Add dev dependencies relying to your yarn version:
    * @yarnpkg/builder
    * @yarnpkg/cli
* `version` - this is your own version printed by “yarn --version”.
 
Additionally you can add build script for your CI/CD or just for simplifying:
> builder build bundle 

Looks easy? Ok, let’s try to execute:
> yarn builder build bundle

But for now this is the problem of internal APIs:

```
➤ YN0000: ┌ Building the CLI
➤ YN0001: │ Error: Build failed with 1 error:
error: Could not resolve “/sources/cli.ts" 
```

Yarn bundle builder is oriented to original berry repository and some internal scripts. I didn’t find any ways to solve this problems without fetching these files, therefore you should copy https://github.com/yarnpkg/berry/tree/master/packages/yarnpkg-cli/sources directory to your project/sources directory.

If you’ll retry build command you should see a new one `bundles/yarn.js` file - this is your bundle.

As another one example, you can look on my bundle: https://github.com/RuBAN-GT/yarn-ultimate-cli having my own plugin helping to work with workspaces and determine their changes.

## How to use your CLI

From 2-3.1.1 yarn versions yarn doesn’t support fetching external yarn clients from CDN but my simple enhancement was merged recently (https://github.com/yarnpkg/berry/pull/4089) and we’ll can simply set our versions using 
> yarn set version https://your_address/yarn.js 

But while yarn team didn’t published a new one minor release we can take the next workaround using availability to fetch yarn clients from file system:

Fetch yarn js bundle using curl like: 

>  curl -L 'https://raw.githubusercontent.com/RuBAN-GT/yarn-ultimate-cli/master/bundles/yarn.js' > '/tmp/yarn.js'

Set local fs version:
> yarn set version berry && yarn set version /tmp/yarn.js

If you’ll execute `yarn --version` you should to see your own version name. 
My congratulations!

## Summary

I hope my post can help to know more about awesome Node.js package manager - Yarn and its API and ways to enhance your infrastructure. If you’ll face to issues with configurations of your bundles, feel free to ask from comments.

Good code everyone!