Skip to content
Permalink
main
Go to file
8 contributors

Users who have contributed to this file

@evocateur @tdmalone @ricky @pcowgill @jamiebuilds @kylecordes @joeytwiddle @0xflotus
76 lines (60 sloc) 3.62 KB

Lerna Hoisting

Use caution when enabling this feature, as certain configurations can cause problems.

When an overall project is divided into more than one NPM package, this organizational improvement generally comes with a cost: the various packages often have many duplicate dependencies in their package.json files, and as a result hundreds or thousands of duplicated files in various node_modules directories. By making it easier to manage a project comprised of many NPM packages, Lerna can inadvertently exacerbate this problem.

Fortunately, Lerna also offers a feature to improve the situation - Lerna can reduce the time and space requirements for numerous copies of packages in development and build environments, by "hoisting" dependencies up to the topmost, Lerna-project-level node_modules directory instead.

--hoist is intended to be transparent in use, a runtime optimization that ideally does not require any other modifications to a project. When the --hoist flag is used:

  • Common dependencies will be installed only to the top-level node_modules, and omitted from individual package node_modules.
  • Mostly-common dependencies are still hoisted, but outlier packages with different versions will get a normal, local node_modules installation of the necessary dependencies.
    • In this instance, lerna bootstrap will always use npm install with the --global-style flag, regardless of client configuration.
  • Binaries from those common packages are symlinked to individual package node_modules/.bin directories, so that package.json scripts continue to work unmodified.
  • Well-behaved Node-based software should continue to work unmodified.

Disadvantages with hoisting

Module resolution

The Node module resolution algorithm is recursive: When looking for package A, it looks in a local node_modules/A directory, then in ../node_modules/A, ../../node_modules/A, ../../../node_modules/A, etc.

Tooling that follows this specification can transparently find dependencies which have been hoisted.

Unfortunately, some tooling does not follow the module resolution spec closely, and instead assumes or requires that dependencies are present specifically in the local node_modules directory. To work around this, it is possible to symlink packages from their hoisted top-level location, to individual package node_modules directory. Lerna does not yet do this automatically, and it is recommended instead to work with tool maintainers to migrate to more compatible patterns.

Forgetting dependencies

Lerna will hoist dependencies which are used in multiple projects, even if they are not used in all projects.

As a result, your packages will be able to import or require any of the dependencies that have been hoisted, even if you forgot to specify that dependency in your package's package.json file.

Tests will pass fine, and you may not realise until later, when you try to use this package outside of the monorepo, that some of its dependencies are missing.

(This problem is not specific to lerna. It can also occur as a result of npm's flattening.)

To avoid this problem, we can use the eslint-plugin-import package, which has a rule no-extraneous-dependencies that can warn when an import is made from an unspecified package. It is enabled by default in the 'recommended' config. Otherwise, we should check by hand that all new imports come from packages specified in package.json.