# symlink-monorepo

> Single command to setup a monorepo using symlinks.

Latest version **1.0.1** (published 2021-08-30) · 0 weekly downloads

## Install

```sh
npm install symlink-monorepo
pnpm add symlink-monorepo
yarn add symlink-monorepo
bun add symlink-monorepo
```

Provides the command `symlink-monorepo`.

## Health

**Score 20/100 (F)** — status: abandoned.

Positive: esm support; no vulnerabilities.

Warnings: low downloads; no types.

Negative: abandoned; low maintenance score.

## Facts

| | |
|---|---|
| Version | 1.0.1 |
| Published | 2021-08-30 |
| First published | 2021-08-30 |
| Weekly downloads | 0 |
| TypeScript types | none |
| Module format | ESM + CommonJS |
| Dependencies | 4 |
| Unpacked size | 10.4 KB |
| Known vulnerabilities | 0 |
| Install scripts | no |
| Author | saibotsivad |
| Maintainers | saibotsivad |

## Links

- npm: https://www.npmjs.com/package/symlink-monorepo
- npm.io page: https://npm.io/package/symlink-monorepo

## Dependencies (4)

- [mri](https://npm.io/package/mri.md) ^1.1.6
- [glob](https://npm.io/package/glob.md) ^7.1.7
- [mkdirp](https://npm.io/package/mkdirp.md) ^1.0.4
- [fs-symlink](https://npm.io/package/fs-symlink.md) ^1.2.1

## Recent versions

- 1.0.1 (latest) — 2021-08-30
- 1.0.0 — 2021-08-30

## README

# symlink-monorepo

Single command to setup a monorepo using symlinks.

## install

The usual way:

```shell
npm install symlink-monorepo
```

## use

It's a CLI tool (see below for details) so use it like:

```shell
symlink-monorepo --root=example-monorepo --folderPrefix="_" --npmPrefix="@"
```

* `root: String` *[optional]* - the monorepo root, typically leave this blank.
* `folderPrefix: String` *[default: "_"]* - the folder name prefix to use for symlinking
* `npmPrefix: String` *[default: "$"]* - the module scope, e.g. `_shared` maps to `import('$/shared')`

With that primer, let me tell you why this exists.

## the problem

Many monorepo projects are set up like this:

```
root
├┬ shared
│└─ util.js
└┬ apps
 ├┬ app1
 │└─ server.js
 └┬ app2
  └─ website.js
```

If you want to import `shared/util.js` from inside `apps/app1/server.js` you could use
relative paths:

```js
// apps/app1/server.js
import util from '../../shared/util.js'
```

But that definitely gets unwieldy pretty fast.

## partial solution

With the power of the NodeJS module resolution algorithm, you can embed a `node_modules` folder
within your project to use absolute paths:

```
root
└┬ apps
 └┬ node_modules
  ├┬ shared
  │└ util.js
  ├┬ app1
  │└─ server.js
  └┬ app2
   └─ website.js
```

Then, to import `apps/node_modules/shared/util.js` from inside `apps/node_modules/app1/server.js` you
can use non-relative paths:

```js
// apps/node_modules/app1/server.js
import util from 'shared/util.js'
```

The good news: this is fully supported with native NodeJS, so no bundler required.

The bad news: it feels a little clumsy, and some bundlers and tooling get really hung
up on the sub-folder named `node_modules`–they often filter by *anything* that has
`node_modules` in the path.

## better solution

Using symlinks, you can use symlinks to point to the appropriate folders, which looks
like this:

```
root
├┬ shared
│└─ util.js
└┬ apps
 ├┬ app1
 │├┬ node_modules
 ││└┬ prefix
 ││ └ shared ⇒ /shared
 │└─ server.js
 └┬ app2
  ├┬ node_modules
  │└┬ prefix
  │ └ shared ⇒ /shared
  └─ website.js
```

So now we have the flat file paths again, without the embedded `node_modules`, and to import
`shared/util.js` from inside `apps/app1/server.js` we can also use non-relative paths:

```js
// apps/app1/server.js
import util from 'prefix/shared/util.js'
```

## this tool

This is a CLI tool that assumes the following folder structure (more about prefixes later):

```
root
├┬ _shared1
│└─ # files and/or folders
├┬ _shared2
│└─ # files and/or folders
└┬ apps_folder
 └┬ app1
  ├┬ _lib1
  │└ # files and/or folders
  ├┬ _lib2
  │└ # files and/or folders
  ├┬ node_modules # generated by this CLI tool
  │└┬ @ # the prefix
  │ ├ shared1 ⇒ /_shared1
  │ ├ shared2 ⇒ /_shared2
  │ ├ lib1 ⇒ /apps_folder/app1/_lib1
  │ └ lib2 ⇒ /apps_folder/app1/_lib2
  └─ # files and/or folders
```

The `apps_folder/*/node_modules` folders in this case would be generated using:

```shell
symlink-monorepo --folderPrefix="_" --npmPrefix="@"
```

Now let's break down what this is all about:

### folder prefix

Folders that are at the repo root level, and at each app level, that are prefixed
with the `folderPrefix` property, are symlinked.

Most of the projects that I work with use the underscore (aka `_`) character, like the
earlier example, eg. `/_shared1` and `/apps_folder/app1/_lib1`.

Although it's common to have a single folder like `_shared` it is also common to have
multiple folders, e.g. one for services, one for controllers, etc.

### npm prefix

Each of these folders is symlinked to a single "scope" name, e.g. the [@angular/cli](https://www.npmjs.com/package/@angular/cli)
scope name is `@angular`, so if you set `--npmPrefix="@"` the import name would be e.g.
`@/shared` or `@/lib` etc.

Specifically, the `folderPrefix` gets stripped from the folder name as part of the symlink.

So if you set the `folderPrefix` to `_` and the `npmPrefix` to `$` than if you had a
file at `_controller/util.js` you would import it with `@/controller/util.js`. Or if you
had a file at `apps/app1/_lib/util.js` you would import with `@/lib/util.js`.

**Note:** because of this naming convention, you cannot have an app symlinked folder,
e.g. `apps/app1/_shared` that has the same name (in this case `_shared`) as a root folder.
To do so would not be possible, so it'll throw an error.

---
_Source: https://npm.io/package/symlink-monorepo · Machine-readable twin of the npm.io package page. Health data is recomputed on every publish._
