https://github.com/saibotsivad/symlink-monorepo
CLI tool to setup a monorepo using symlinks.
https://github.com/saibotsivad/symlink-monorepo
monorepo symlink
Last synced: about 2 months ago
JSON representation
CLI tool to setup a monorepo using symlinks.
- Host: GitHub
- URL: https://github.com/saibotsivad/symlink-monorepo
- Owner: saibotsivad
- Created: 2021-08-28T16:24:02.000Z (about 4 years ago)
- Default Branch: main
- Last Pushed: 2021-08-30T03:14:54.000Z (about 4 years ago)
- Last Synced: 2025-02-15T10:04:05.711Z (8 months ago)
- Topics: monorepo, symlink
- Language: JavaScript
- Homepage:
- Size: 11.7 KB
- Stars: 1
- Watchers: 3
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
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.