34

I'm trying to setup a monorepo with 3 services sharing some library code.

This is the current situation:

repo: web
pdf/
  package.json
    reference to shared-ts using github url
  tsconfig.json
frontend/
  package.json
    reference to shared-ts using github url
  tsconfig.json
repo: mobile (react-native)
  package.json
    reference to shared-ts using github url
  tsconfig.json
repo: shared-ts
  package.json
  tsconfig.json

This works but it's a pain to commit to shared-ts, build, change the hash in package.json and commit again.

This is what I'd like to achieve:

repo: monorepo
pdf/
  package.json
    reference to ../shared-ts
  tsconfig.json
frontend/
  package.json
    reference to ../shared-ts
  tsconfig.json
mobile/
  package.json
    reference to ../shared-ts
  tsconfig.json
shared-ts/
  package.json
  tsconfig.json

So far I've tried:

  • TypeScript project references, but it seems like there is no way to have dependencies in the shared-ts project
  • "shared-ts": "../shared-ts" in package.json but it copies shared-ts into the node_modules of each package so I have to re-run yarn everytime I make a change
  • yarn link in postinstall: error TS2307: Cannot find module 'shared-ts' or its corresponding type declarations.
  • creating a symlink directly in postinstall with ln -s ../shared-ts/ node_modules/shared-ts/ but it seems TypeScript fails to find the module
  • npm link in postinstall seems like the most promising but it's really slow and I'm having trouble running it in CI because of some permissions issues.

Is there a good way of doing this? Any ideas on other things I could try?

2
  • are you using Linux ? maybe a shell script can help
    – pfndesign
    Commented Nov 19, 2021 at 10:02
  • 2
    This is what workspaces and lerna are for. Very common.
    – morganney
    Commented Nov 21, 2021 at 14:10

5 Answers 5

8
+250

Solution 1:

with Lerna

you can use workspace and Lerna

yarn workspace & lerna

├── README.md
├── lerna.json
├── package.json
├── packages
│   ├── pdf
│   │   ├── package.json   /*  "shared-ts": "^1.0.0" */
│   │   └──  src
│   ├── frontend
│   │   ├── package.json
│   │   └── src
│   ├── mobile
│   │   ├── package.json
│   │   └── src
│   ├── shared-ts
│   │   ├── package.json
│   │   └──  src
├── tsconfig.json
└── yarn.lock

here is an example repo

here you can see x-cli is getting shared x-core

Solution 2:

without Lerna

you can use mtsl package which enables us to make tangible symlinks. you can install this package globally

npm install -g mtsl

then you just need to start to separate these three commands in terminal.

mtsl startwithoutadd -s path_of_project/packages/shared-ts -d path_of_project/packages/pdf/node_modules/shared-ts

mtsl startwithoutadd -s path_of_project/packages/shared-ts -d path_of_project/packages/frontend/node_modules/shared-ts

mtsl startwithoutadd -s path_of_project/packages/shared-ts -d path_of_project/packages/mobile/node_modules/shared-ts

Note don't stop this three watcher. after testing, you can make single command from the package.json script

7

Your use-case can be handled using the npm7 workspaces. In short your new monorepo structure should look like below:

repo: monorepo
package.json // <- here you define the workspaces
pdf/
  package.json
    reference to shared-ts
  tsconfig.json
frontend/
  package.json
    reference to shared-ts
  tsconfig.json
mobile/
  package.json
    reference to shared-ts
  tsconfig.json
shared-ts/
  package.json
  tsconfig.json

You need to list the workspaces in the root package.json which might look something like below:

{
  "name": "awesome-monorepo",
  "workspaces": [
    "pdf",
    "frontend",
    "mobile",
    "shared-ts"
  ]
}

After doing that, wherever in the monorepo you decide to use the shared-ts you can add that to dependencies or devDependencies simply referring by the version number instead of relative path.

All the node modules inclusive the workspaces gets hoisted to the root node_modules which is why the module resolution should work without friction.

-1

I have done the things you are curious about in a recent project. Desired results can be achieved by monorepo using lerna and yarn workspace. For details, please go to this link

With the above, we will be creating a package of types. In other packages, we will be just importing types from packages like below:

import { Post } from "@types";

Things are much easier this way than linking packages ourslef.

-1

You can use NX to maintain your repos, wherein your web and mobile repos will be your apps, and the shared-ts will be a lib such that, web and mobile depends on shared-ts lib.

You have a common package.json, or a separate package.json for each repo individually. NX provides dependencyGraph and affected features, wherein if you change the common libs, it figures out which modules/app to build without having to build the complete thing.

Your code structure would look like:

apps:
  web:
    src
    package.json

  mobile:
    src
    package.json

libs:
   shared-ts:
     src
     package.json

workspace.json

It'll probably be best to lookup the official docs for the best setup and options, but I believe it provides what you're looking for.

-2

using yarn workspace & lerna

here is an example monorepo-template

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.