In my monorepo, I have 3 packages package1, package2, package3, each package contains a npm script named build.
However, these packages are not linked together. I.e. there are no require() in any of those packages linking to a sibling package.
From the root folder, I run lerna run build. It seems to run build of the packages in the alphabetically order.
Is there a way to specify the order to run the build commands of these packages?
--sort won't work because they are not linked.
You don't specify the order, you specify the topology by including a package as a dependency of another.
If package1 needs to be built before package2 you add package1 to the dependencies of package2 in the latter's package.json file. If you do not want package2 to directly depend on package1 (e.g. on production) you can still add it into devDependencies and Lerna will understand the dependency.
From lerna -h:
--sort Sort packages topologically (dependencies before dependents).
Pass --no-sort to disable. [boolean] [default: true]
Note Some commands can be ran ignoring this topology, for example from lerna exec's --parallel option documentation:
completely disregards concurrency and topological sorting
lerna run build --include-dependencies --stream
--include-dependencies this flag can help
You can first build your shared package and after that trigger another build.
Ex :
yarn workspace #shared run build && yarn lerna run build
Related
I have a yarn/lerna monorepo with multiple packages that depend on each other. If I add packageA as a dependency to packageB and execute yarn install I see that node_modules/packageA is actually a symlink to packages/packageA instead of the published version of that package.
This creates problems on CI if packageB is build before packageA - the build fails because node_modules/packageA just points to the bare sources, without the build products (because packageA has not yet been built).
How can I force yarn to always download the published version of packageA?
yarn --version: 1.22.10
sidenote: If I wanted to use a local version of packageA instead, I would use yarn link or a local path instead of a version in package.json. Why is yarn defaulting to this behaviour?
One options is: "focussed workspaces" - see the guide here.
In my case, I added a file packages/packageB/.yarnrc that specifies to always use the --focus argument for yarn install:
--install.focus true
This will make sure that packageB has a copy of the published packageA in it's own node_modules folder.
However: This only works for one package at a time.
You can just build packages in order of dependencies. So in your case it'd be something like this in your CI (assuming there is a script entry called "build" in package.json of the packages):
yarn workspace packageA run build
yarn workspace packageB run build
This way you control the order of builds,they complete successfully, and you don't have to force using published package.
When using Yarn workspaces, can I install every devDependency in the root workspace? Or should I keep them in each separate workspace?
For example:
packages
package1
package.json
package2
package.json
package3
package.json
package.json
And here are the needed devDependencies for each package.
package1 => external-package-A
package2 => external-package-A
package3 => external-package-A + external-package-B
Where should a install the external-package-A and external-package-B ?
Should a install external-package-A in the root workspace, since it's used by all of my packages?
Will there be any problem if I also install external-package-B in my root workspace?
Or should I install them in each of the packages? What I mean is they'd be listed in each respective package.json file for each package, instead of being listed in the root one.
Here is what I've found on Reddit.
https://www.reddit.com/r/javascript/comments/9t6yht/yarn_workspaces_why_is_adding_something_to_the/
Comment 1
It's not bad. It's just have to be explicit.
For example, you have package A. It relies on external dependency B. If you install B into root, you'll get package A working inside workspaces, but on install it will fail. All your dev dependencies, which is not called from workspace alone can be installed to root with no problems.
For example, we have babel in every workspace (different versions in different packages), but have eslint in root. We're working towards unified build process, so babel will also migrate to root deps.
Comment 2
Disclaimer: I'm working on implementing a company-wide monorepo at
Uber.
Two reasons.
It's "bad" if you are publishing each package separately. If you do
that, then when you install it elsewhere, a required dependency will
be missing from its package.json.
If you are in a large company-wide monorepo (or any monorepo large
enough that it has several separate teams working on separate
packages), then upgrading a top-level dep that everyone depends on can
be very difficult to land, since it may require code reviews from
dozens of people, and the diff may break people's packages in ways
that tests may not catch (and that's without counting things like
packages that no longer have active maintainers).
Having deps in the top level is a strategy known as locked
dependencies and it does have some theoretical benefits (e.g. only a
single version of every direct dep, and thus faster installs, CI,
etc), but in practice, it's very expensive to maintain, compared to
the unlocked dependencies strategy (no deps at top level).
Below is the description of the issue:-
Expected behaviour is to have a package-lock.json file generated for every package in packages folder.
Current Behaviour
My current project structure look like:-
packages/internal-package-1/package.json
packages/internal-package-2/package.json
packages/internal-package-3/package.json
lerna.json
package.json
package-lock.json
Right now as shown above there is only one package-lock.json file which is generated for . the entire project and it only contains the dependency which in top package.json file.
My expectation was that for every package.json file corresponding package-lock.json should be generated but that is not the case. Furthermore, the top package-lock.json file only contains the dependencies in the top package.json and not the all the dependencies which are declared in evey package.json file.
Now, if we try to consume for example internal-package-1 in a different project that as there is no lock file for this package , latest version of the dependencies gets downloaded which is not the expected behaviour.
Possible Solution
Possible solution or expectation is to have a lock file generated for every package.
lerna.json
{
"packages": [
"packages/*",
"packages/Foundation/src/SampleNestedModule"
],
"version": "0.0.0"
}
This issue is affecting us because as the lock file is not generated for every package and if i try to consume the internal-package-1 in a different project then locked dependency are not getting downloaded but the latest version of them gets downloaded.
We are hoisting the dependency hence we have modified our npm install script as below:-
"install": "lerna bootstrap --hoist" , this correctly hoists the dependency but does not generate the lock file for individual package.
Executable Version
lerna --version 3.17.0
npm --version 6.10.1
yarn --version Not using yarn
node --version 10.16.0
| OS | Version |
MACOS
| NAME | VERSION |
| macOS Catalina | 10.15.2 |
Below are some of the post regarding same which i have already looked into-
https://github.com/lerna/lerna/issues/1462
https://github.com/lerna/lerna/issues/2105
Thanks,
Vishesh.
I couldn't find a concrete solution to generate lock files for all packages. I mean there are ways but, everything is increasing the installing time to very high. Below are 2 ways to generate package-lock.json file for all packages:-
Directly use lerna bootstrap without --hoist flag ------- This does generate lock file but increases the install time way to high.
Use "lerna exec -- npm i" ------ This will generate the lock file but "install" times are way higher not a viable solution with 25 packages in my repository.
As above 2 solutions were taking way to much time hence i considered them as not a feasible solution for large repos hence, i came up with a third way or i would call it a workaround , this is also not the cleaneast solution but does the job with very slight increase in installation time.
Create a npm script in all your packages which would generate only package-lock file without installation which would be something like below:-
"genPackagelock": "npm i --package-lock-only"
In you root package.json file as part of postinstall call the above defined script for all the packages as below:-
"postinstall": "lerna run --parallel genPackagelock"
The above "postinstall" basically generates package-lock.json file for all the packages along with the internal dependencies.
With Lerna to add a package to all other package file I can use -
yarn run lerna add component-a
With this I see the item added to all package files in my overall repo.
If I use
yarn run lerna clean component-a
the output suggests its remove the component from the node_modules but it still exists inside the package files that were added via the add command.
So what will reverse the add command fully.
Let's say I have multiple packages in my yarn workspaces.
#mycompany/utils
#mycompany/app
#mycompany/serv
Let's say each of these packages has a dependency on lodash. I want to make sure that they all have the same lodash version.
Is there a way to do that in each of the package.json?
Use syncpack to force all sub-packages in a monorepo uses the same version of each dependency.
Install in the root package.json:
yarn add --dev -W syncpack
Run (Recommnded: Run on every commit using husky):
syncpack list-mismatches
More info: https://github.com/JamieMason/syncpack