Skip to content

[RRFC] Add nohoist option for workspaces #287

Open

Description

@socialwyze-franklin

Motivation ("The Why")

Yarn already supports this option. It makes it possible to work with tooling that expects certain modules to be located in inside the same package that consumes them.

Example

In order for a React Native app to build properly within a monorepo, some packages that react-native depends on must reside inside the node_modules folder within the app package. With yarn, I'm able to accomplish it with this configuration:

  "workspaces": {
    "packages": [
      "api",
      "app",
      "shared/*"
    ],
    "nohoist": [
      // my-app is the name of the package inside the app folder
      "my-app/**"
    ]
  }

How

See: https://classic.yarnpkg.com/blog/2018/02/15/nohoist/

References

Activity

  1. ljharb commented on Nov 11, 2020

    @ljharb
    Contributor

    What I'd prefer, rather than adding "nohoist" and having to manually configure it, is finding a way for nothing to hoist, so that each folder's node_modules directory only ever contained the dep graph that its own package.json would have produced.

  2. simllll commented on Nov 19, 2020

    @simllll

    What I'd prefer, rather than adding "nohoist" and having to manually configure it, is finding a way for nothing to hoist, so that each folder's node_modules directory only ever contained the dep graph that its own package.json would have produced.

    sounds very much likes "pnpm" approach of doing things.

  3. isaacs commented on Dec 2, 2020

    @isaacs
    Contributor

    Current status:

    • At the very least, the canonical option should be hoist: false rather than nohoist: true to avoid the "notfalse" config antipattern. (Whatever direction we go, we can support nohoist as well.)
    • Some discussion around whether this should be per-workspace, or for the project as a whole.
    • Main hazard: can easily result in workspace projects that install fine in dev, but cannot be correctly used together in production. Eg, packages/foo has a peerDep on react@16.8.0 and packages/bar has a peerDep on react@16.13.0. Without hoisting, this conflict is not evident in development. As @ljharb points out, this is desired behavior for projects that provide separate connectors for different react versions, but definitely something that should be an opt-in.
    • Discussed other pnpm-style tree generation approaches, and/or having separate "tree strategy" settings for different workspaces. Eg, packages/foo uses global-style, packages/bar uses legacy-peer-deps, etc. (This would be a pretty significant refactor for arborist, but not impossible.) Decided that this is orthogonal to the question of whether we hoist to workspace parent dir or not.
  4. ljharb commented on Dec 2, 2020

    @ljharb
    Contributor

    I'd amend this to say, "hoisting" is the improper term in every case - that what is actually desired is control around "sharing".

    I think by default, the root, and individual subpackages, should share all peer deps, and all subpackages themselves, but observably share nothing else (iow, it's fine if they actually share the same copy of lodash, but a workspace shouldn't be able to require lodash unless its own dep graph includes it).

  5. removed
    Agendawill be discussed at the Open RFC call
    on Dec 16, 2020
  6. zameschua commented on Jul 23, 2021

    @zameschua

    Hi guys is there any progress on this issue? I really want to use npm for workspaces but I can't because react-native projects require a nohoist option..

  7. BitForger commented on Oct 18, 2021

    @BitForger

    This would actually be very helpful for monorepos with Angular and certain dependencies that look for angular core and common as peer deps.

    I'm encountering a scenario where I have an angular theme library that looks for angular/core and angular/common as a peer dep but it's getting hoisted to <project_root>/node_modules. Angular isn't because I have other packages that depend on a newer version of rxjs then angular.

  8. rmuchall commented on Oct 22, 2021

    @rmuchall

    I would also like to add my support for a nohoist option. I've ran into problems with eslint configurations in a monorepo because it expects to be installed at the local package level.

  9. d3lm commented on Oct 22, 2021

    @d3lm

    I agree. A nohoist option would be very useful.

  10. will-fagerstrom commented on Nov 1, 2021

    @will-fagerstrom

    I'd like to add an additional use case for being able to disable hoisting in workspaces. I recently had to abandon workspaces in favour of Lerna to resolve this for myself.

    I am working on a very large project which has quite a few serevrless components to it. We have a bunch of queueing and lambada workers. The project is setup with the following folder structure

    /applications/admin-application
    /applications/queue-system/
    /applications/queue-system/lambda-1
    /applications/queue-system/lambda-2
    /applications/queue-system/lambda-etc
    /applications/queue-system/cdk-infrastructure
    /packages/shared-code-packages
    

    each of those folders is a npm package which maintains its own set of dependencies. Some shared some not, some packages that only exist in this project and wont be published to any registry.

    Part of publishing an aws lambda is the package needs to contain all of the dependencies in a package (ignoring layers for now). What we want to be able to do is, after an install be able to zip up each of the lambdas with all and only their dependencies so the cdk deployment can pick that up and push it out too aws.

    With hoisting everything is in the root which is many millions of unneeded files for each of the lambdas. Not only that, you must handle the folder structure of walking up the tree and pulling everything down to the lambda folder or you end up with a deeply nested folder structure in the lambda.

    I was very close to creating some sort of symlink system of constructing the node_modules folder in each of the lambdas at deploy time, then zipping that up. But Lerna more or less just works

  11. khitrenovich commented on Nov 1, 2021

    @khitrenovich

    Our use case is very similar to what @will-fagerstrom just described, with a twist of Lambda Layers.

    We pack all 3rd party deps to the shared layer (as described here) and nohost all internal dependencies to the lambdas themselves. Currently it works perfectly with Yarn 1. Our workspaces config looks similar to the following:

      "workspaces": {
        "packages": [
          "api/*",
          "services/*",
          "client/*"
        ],
        "nohoist": [
          "@octobat/bat-services-lambda-layer/**",
          "@octobat/bat-services-*/@octobat/*"
        ]
      },
    

    I would rather go back to NPM than to Yarn 2+.

  12. isaacs commented on Nov 5, 2021

    @isaacs
    Contributor

    If and when something like this lands, #375 is probably going to be the first step. If you have a moment, please go weigh in there about how this will affect your use case, if it will satisfy your needs, etc.

    I will update that PR now with my notes from our discussion in the open RFC call a few days ago.

  13. isaacs commented on Nov 5, 2021

    @isaacs
    Contributor
  14. khitrenovich commented on Nov 5, 2021

    @khitrenovich

    @isaacs -

    Thank you for pointing out the proposal in #375.
    Unfortunately, I don't think it will help in the case I described above.

    As you've mentioned in the proposal,

    1. The wording of "nohoist" is overly specific to a particular
      implementation. That is, the declaration describes the "what" of
      package folder tree implementation, instead of the "why" of dependency
      graph resolution semantics.

    This is exactly the way we use nohost option in Yarn. It has nothing to do with the version resolution per se. Instead, we are dealing purely with the deduplication aspect of hoisting. The primary reason for that is the need to package individual parts of the project tree to be used outside - either individually (AWS Lambdas, as described by @will-fagerstrom) or in conjunction with other dependency resolution mechanisms (AWS Lambda + AWS Lambda Layers use case above).

  15. 49 remaining items

  16. maahtiin commented on Jan 8, 2024

    @maahtiin

    Not fully tested, but if i specify typeRoots: {} with the needed types in my tsconfig.json of the specific package giving the errors, the compilation seems to succeed without errors. Might this be a way to circumvent conflicting @types dependencies?
    Couldn't test it fully as i'm still building the project, but will update this post as i progress.

  17. ranwawa commented on Jan 8, 2024

    @ranwawa
  18. raisiqueira commented on Apr 17, 2024

    @raisiqueira

    Hey folks, what's the status on this? Will it ever happen?

  19. ranwawa commented on Apr 17, 2024

    @ranwawa
  20. added a commit that references this issue on Apr 18, 2024
  21. sweetpalma commented on Aug 12, 2024

    @sweetpalma

    I have struggled with this issue for a while and eventually found a sort of workaround for a pretty specific case. This approach creates two separate node_modules folders containing dependencies ONLY for a particular sub-package, and then merges them, allowing you to deploy it - e.g. as a part of your CI pipeline.

    Example:

    cd my_monorepo_root
    rm -rf ./node_modules
    npm ci --install-links --ignore-scripts --workspace=azure-functions
    cp -R ./node_modules ./packages/azure-functions
  22. ranwawa commented on Aug 12, 2024

    @ranwawa
  23. zreecespieces commented on Jan 20, 2025

    @zreecespieces

    insane this still hasn't been added when so many people for four years now have been wanting it. Please!!

  24. mbret commented on Jun 1, 2025

    @mbret

    So, any reply from the team ? This is a requirement for many of us sadly

  25. gidich commented on Sep 29, 2025

    @gidich

    Bumping this issue - looking for using this as well.

  26. ranwawa commented on Sep 29, 2025

    @ranwawa
  27. joshcanhelp commented on Oct 17, 2025

    @joshcanhelp

    Totally support this option but found something that worked for me.

    I'm using a monorepo to manage severless functions like other folks in the thread here, Digital Ocean specifically. The deployment process using their doctl CLI runs npm run build after npm install so I added a second install command scoped to the current directory:

    {
      "scripts": {
        "build": "npm --prefix=. install && tsc"
      },
      "dependencies": {
        "slonik": "^47.3.2"
      }
    }
  28. ranwawa commented on Oct 17, 2025

    @ranwawa
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions