6

I'm trying to figure out correct approach for a javascript monorepo. Imagine monorepo containing packages / libraries:

root
  - node_modules
  - packages
      + lib-a
          * node_modules
      + lib-b
          * node_modules

Now let's say both lib-a and lib-b packages use webpack as their build tool.

I see two approaches

  1. Add wepback as dependency to root. Include "build" script in both packages: "build": "webpack -p --config webpack.config.js. webpack.config.js could include root webpack.config.js. Then I could use tool like lerna to run the build from root directory (which means webpack binary is recognized. However I will be unable to run the build in specific packages since webpack is not available there. I could probably change the build script to something like "build": "../../node_modules/.bin/webpack -p --config webpack.config.js

  2. Always include webpack in each package. This means that build script will succeed. This also means that each package will have the same dependency and I should probably watch that each package uses same webpack version.

Basically what I'm getting at is how should packages inside monorepo be structured? If any package is published, should it always be possible to build that package separately.

user3056783
  • 2,265
  • 1
  • 29
  • 55
  • 1
    The way our mono repo works is each packages has their own node-modules but you can install only the ones that interest you and you are working on. We then have a command Build at root which basically executes build in all packages – Mederic Sep 20 '19 at 08:50
  • So you are putting the responsibility of building onto the "root" package and run build commands from there. That seems like the correct approach. I'm thinking to do this for rest of the commands as well (such as testing, clean-up etc.). – user3056783 Sep 20 '19 at 11:23
  • You should look into **lerna**. It has tools to help you manage a monorepo and also automate certain tasks like linking the packages within your monorepo. I use lerna for my monorepo and it works beautifully. – hwkd Sep 26 '19 at 09:59
  • I think it would make sense to change the title of this question to something like "What is a correct approach to a javascript monorepo". This would help others to find this question/answer when searching through Stackoverflow/Google. – Jacek J Oct 04 '19 at 16:20

2 Answers2

4

Your approach #2 is right. You handle each package separately as it was an individual, self-contained package.

The advantage of a monorepo lays not in sharing files through the directory structure but in:

  1. Bootstrapping all dependencies to a single node_modules with flat structure, effectively deduplicating them.
  2. Making your packages available to your other packages through regular package import/require() as they were external dependencies. And, thanks to symlinks to node_modules, your "dependency" packages contain always the latest content without publishing.
  3. Enforcing consistent, always up-to-date, dependency structure in all your packages. As you said "This also means that each package will have the same dependency".
  4. Automation tools to perform different maintainance tasks (like build, publish) on all your packages with a single command.

I know it's not so easy at the beginning, but when you dig into Lerna documentation it's becoming more clear. Besides Lerna main page I recommend reading about hoisting, FAQ and individual commands like bootstrap and publish.

Jacek J
  • 965
  • 1
  • 8
  • 11
  • I second everything here. Each package should be able to build and test itself on its own. I recommend yarn workspaces + lerna, but it's not necessary. We created root-level tsconfig and jest config which are imported/extended by individual packages. We also let the root take care of linting for all packages, `husky` for pre-commit hooks, and `commitizen` for consistent commit messages. – Ryan Wheale Oct 07 '19 at 20:19
1

Our current configuration is same as you:

root
  - node_modules
  - packages
      + lib-a
          * node_modules
      + lib-b
          * node_modules

We use lerna to handle our project: https://github.com/lerna/lerna

You just need to specify your package folder in the lerna.json

{
  "lerna": "3.16.4",
  "packages": ["packages/*"],
  "version": "0.0.0",
  "npmClient": "yarn",
  "useWorkspaces": true
}

Then in your package.json scripts you can use the line:

"build": "lerna run build",

This will basically run a build in all packages. So as long as your build script in each package has the proper params and webpack installed it will automatically run the webpack build.

After that you can simply handle working in your designated packages.

Mederic
  • 1,949
  • 4
  • 19
  • 36