---
title: Rush Monorepo
description: How to set up a monorepo with Piral and Rush.
audience: Architects, Developers
level: Proficient
section: Details
---

# Setting up a Monorepo with Piral and Rush

You might be wondering "what is this Rush"? Rush is a Node.js command line tool by Microsoft that allows you to manage monorepos. Classically, monorepos might be difficult to deal with as you might need to jump to different directories, patch multiple files at once, cannot really reference other packages in the monorepo, or have trouble with versioning of these packages. Rush helps you will with that - and more.

## Rush Setup

Before we get started, make sure you have the latest Rush release installed globally (right now this is tested using version 5 of Rush, it might work with a different version, too):

```sh
npm install -g @microsoft/rush
```

Let's start a new monorepo and add a Piral instance with some pilets. We begin with a new fresh directory:

```sh
git init
rush init
```

With these three commands we

1. Initialize a new git repository
2. Generate Rush's config files

At this point we should see four items in the directory:

1. *.git* (from the first command, the directory where git stores its information)
2. *rush.json* (from the second command, the file configuring Rush)
3. *.gitattributes* (from the second command, sets some git configurations)
4. *.gitignore* (from the second command, tells git which files to ignore)
5. *.travis.yml* (from the second command, configures the [Travis CI](https://www.travis-ci.com/) service)
6. *common* (from the second command, Rush stores all files it needs in there. Read more about this in Rush's [config file reference](https://rushjs.io/pages/advanced/config_files/))

Great! Now we need to configure rush by opening *rush.json* and performing the following steps:

- **Choose a package manager:** Search for `npmVersion` inside the *rush.json* file. You'll see that rush uses pnpm as the default package manager. Since we only tried this with npm we uncomment the `npmVersion` line and comment the `pnpmVersion` line. You should also check if you have the latest npm version installed globally and set this version as the `npmVersion` value. You can, of course, also use another package manager if you want to. Read more about the supported package managers [here](https://rushjs.io/pages/maintainer/package_managers/).
- **Custom npm registry:** If you use a custom npm registry, you can configure it in `common\config\rush\.npmrc`. Read more about this [here](https://rushjs.io/pages/maintainer/npm_registry_auth/).
- **Folder structure:** Since we don't want to mix Rush's files with our packages, we add a folder "packages" in the root directory by running `mkdir tools`. All pilets and the app shell will live in their own folders directly inside the packages folder. In this scenario, they are nested in a folder depth of 2 which is Rush's default. If you want to have a different folder structure you might need to configure `projectFolderMinDepth` and `projectFolderMaxDepth` inside *rush.json*. I'll just uncomment them being set to 2.

## Add a Piral Instance

Now let's add a Piral Instance. Make a new directory *app-shell* (or whatever you want to call it - we'll refer to it as *app-shell* from here on) in the *packages* directory:

```sh
mkdir packages/app-shell
npx piral new --no-install --target packages/app-shell --npm-client rush
```

Be sure it didn't install any dependencies. If it did just delete the `node_modules` folder and the `package-lock.json` file inside the app-shell folder. You need to delete those because all node modules as well as the shrinkwrap file (in our case that's the _package-lock.json_* file because we use npm) will be handled by Rush inside the `common` folder. Rush will show an error if it does find a shrinkwrap file in any Rush project.

To make rush aware of the app-shell package we need to add it to the `projects` setting inside the rush.json file like this (it should be automatically added, but make sure to check it and rename it properly):

```json
  "projects": [
    //...
    {
      "packageName": "app-shell",
      "projectFolder": "packages/app-shell"
    }
    //...
  ]
```

Here the `packageName` property value needs to match the actual name of your package defined inside `packages\app-shell\package.json`. `projectFolder`, of course, needs to point to the packages folder.

Now we are ready to install all package dependencies. Note that in a Rush monorepo you shouldn't use any yarn or npm commands like `npm install`. Those commands would create a per package shrinkwrap file which Rush doesn't want. Instead, Rush has its own commands to handle things.

To install the dependencies of all packages inside our monorepo you can run `rush update` from any folder inside the monorepo. This will create node_modules folders in all package folders which will actually be just symlinks to `common\temp\node_modules`. If you want to remove all of them you can always run `rush purge` which is a very good command to do a clean start if you messed something up.

Now we can run `rush build` to build all packages inside the monorepo. This command assumes that every package has a build script and runs this script.

To run the app-shell locally, `cd` into the app-shell folder and run `rushx start`. `rushx` is a way to run any of your scripts. So `rushx start` is basically the same as running `npm run start`, just shorter.

## Add a Pilet

When adding more items to the monorepo it may make sense to follow a certain naming convention. As an example, we could suffix the pilets with `-pilet`. The exact convention is up to you - we will refer to the `-pilet` for our convention in this tutorial. Just make sure to stay consistent.

The following command creates a new pilet called `foo-pilet`:

```sh
npx pilet new app-shell --no-install --target packages/foo-pilet --npm-client rush
```

Like beforehand, go and delete the `node_modules` folder and the shrinkwrap file if those have been created.

To make rush aware of the foo-pilet we need to add it to the `projects` setting inside the *rush.json* file like this (as beforehand, this should have been available by now - but better to be sure and check it):

```json
  "projects": [
    //...
    {
      "packageName": "foo-pilet",
      "projectFolder": "packages/foo-pilet"
    }
    //...
  ]
```

Now we need to run `rush update` again to install the dependencies of the new pilet.

You can now change into `packages\foo-pilet` and run `rushx start` to run the pilet locally.

::: tip: Package dependencies
In some cases you might need additional dependencies for such repositories to work. For instance, when using `piral-cli-webpack5` you will need to add `@babel/preset-env` and `@babel/preset-react` - otherwise, these dependencies cannot be resolved. Also, make sure to add `react` to the pilets `devDependencies`.

As an example you can run these 3 commands inside the pilets folder:

```sh
rush add -p @babel/preset-env --dev
rush add -p @babel/preset-react --dev
rush add -p react --dev
```

After adding new packages you sometimes need to run `rush update --purge --full` to force-update all cache files.
:::

## Add a Blazor Pilet

Since Blazor pilets need some more settings, we'll cover them here in more depth. Let's start by adding a `blazorwasm` project. To do so, run:

```sh
dotnet new blazorwasm -o .\packages\blazor-pilet
```

We should also add these two lines to the `.gitignore` file:

```
obj/
bin/
piral~/
```

We now have a standard `blazorwasm` project. To convert it into a pilet we need to go through the following steps (you can read more about this in the [Piral.Blazor README](https://github.com/smapiot/Piral.Blazor)):

- Open `packages\blazor-pilet\blazor-pilet.csproj` and add the following inside the `PropertyGroup` section at the top:
    ```xml
    <PiralInstance>../app-shell/dist/emulator/app-shell-1.0.0.tgz</PiralInstance>
    <Monorepo>enable</Monorepo>
    ```
- Install the `Piral.Blazor.Tools` and `Piral.Blazor.Utils` NuGet packages.
- Rename *Program.cs* to *Module.cs*, and make sure to make the `Main` method an empty method.

Now we need to prepare the Piral instance to be able to include blazor pilets with these steps:

- Add the `blazor` dependency by running `rush add -p blazor` inside `packages\app-shell`
- Add the `piral-blazor` dependency by running `rush add -p piral-blazor` inside `packages\app-shell`
- In `packages\app-shell\src\index.tsx` add the import `import { createBlazorApi } from 'piral-blazor';`
- In `packages\app-shell\src\index.tsx` add `plugins: [createBlazorApi()], ` inside the `renderInstance` call
- You might want to run `rush update --full --purge` to ensure that all dependencies are correctly installed
- Rebuild the app-shell via `rushx build`

Now it's time to build the pilet. Navigate to `packages\blazor-pilet` and run:

```sh
dotnet build
```

This will create a JavaScript package for the blazor pilet in the `packages\piral~\blazor-pilet` folder. To make Rush aware of the new pilet, we need to add it to the `projects` setting inside the *rush.json* file like this:

```json
  "projects": [
    //...
    {
      "packageName": "blazor-pilet",
      "projectFolder": "packages/piral~/blazor-pilet"
    }
    //...
  ]
```

Since the new pilet is now located inside a folder depth of 3, we need to set `projectFolderMaxDepth` inside `rush.json` to 3 like this:

```json
  "projectFolderMinDepth": 2,
  "projectFolderMaxDepth": 3,
```

Now run `rush update` to install the new pilet's dependencies.

Finally, navigate to `packages\piral~\blazor-pilet` and run the new pilet via:

```sh
rushx start
```

## Conclusion

As you can see Rush is a handy tool to manage your monorepo. If you want to learn more about it go [to rushjs.io](https://rushjs.io) and read the docs. To set up a monorepo you should start [at the setup new repo page](https://rushjs.io/pages/maintainer/setup_new_repo/). If you are a developer using Rush you might start [at the new developer page](https://rushjs.io/pages/developer/new_developer/).

In the next tutorial, we'll look at a set of useful patterns to implement agile frontends using extension components.
