-
Notifications
You must be signed in to change notification settings - Fork 147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
nohoist RFC #86
nohoist RFC #86
Conversation
Conceptually seems legit and reasonable. |
@bestander I just updated the RFC to callout nohoist exceptions for linked packages (including workspace itself): basically linked packages will always be hoisted. Please let me know if you see any issue with that. Not sure what is the expected end result for an RFC... do we need explicit approval before submitting the code change PR? I assume the state of the RFC is still pending on @BYK and @arcanis review? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@connectdotz that is some rad RFC you've sent!
Great work and a pleasure to read.
Go ahead and submit a tentative implementation PR.
I think we can merge this as is but I'll give a few days for @BYK and @arcanis to veto if they have any other plans
accepted/0000-nohoist.md
Outdated
nohoist config is inherited, i.e. if nohoist is specified in the root package.json, all workspaces will inherit the nohoist list, likewise all the dependencies of the workspace will inherit the nohoist list. This concept is simple and useful not only in reasoning nohoist from root to workspaces, it can be extended downwards to allow packages provide better monorepo support as well as easily handling deep-dependency as direct-dependency. | ||
|
||
### nohoist list naming | ||
lerna uses glob pattern to specify nohoist package, however based on the design principle, I argue it is probably better not to since nohoist should be "rare", therefore probably doesn't justify for extra matching complexity and risk. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nice approach!
Simplicity is important
accepted/0000-nohoist.md
Outdated
### nohoist list naming | ||
lerna uses glob pattern to specify nohoist package, however based on the design principle, I argue it is probably better not to since nohoist should be "rare", therefore probably doesn't justify for extra matching complexity and risk. | ||
|
||
However, it is not feasible to ask developers mark off every package if they just want to opt-out hoisting all together, therefore a simple keyword like "\_all\_" should be introduced to handle use-case-3 easily. For example the following config will basically turn off nohoist for the whole monorepo project: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not a huge fan of a new keyword.
Would it really be simpler than a glob?
Considering that we use globs already I think it would be expected by final users that nohoist fields is a glob too
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bestander I see your point, yes we can indeed use glob pattern to make it consistent with the packages patterns.
BTW, I loved the "why" feature of yarn, however, while using it to diagnose nohoist, there are a few bugs surfaced, mainly the current implementation assumed a given package only appeared once, which is not always true: 1. when version conflict 2. when it is marked nohoist. I will address them also in this PR so people can easily find out why a package appeared in multiple places.
(There is another why.js bug that it didn't look at peerDependency, but I will not include it in this PR, as it is independent of nohoist, trying to keep the scope tight.)
accepted/0000-nohoist.md
Outdated
|A uses yarn workspaces but excluded \_all\_|no impact, none will be hoisted|no impact, none will be hoisted| | ||
|A uses yarn workspaces but do not want to exclude react-native from hoisting (why?)|conflict! but the public package B is right and react-native should be excluded.|no conflict but B will most likely failed| | ||
|
||
In short, having unnecessary nohoist packages might cause inefficiency, but missing a necessary nohoist will lead to compile/execution error. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a good argument.
The counter argument is if we release it now and it goes to public packages then we won't have a chance to change our minds.
Especially if other package managers will implement workspaces.
How about this - we implement it the safer way, nohoist
only in root but we can implement the nohoist in public packages behind an experimental flag name nohoist-yarn-experimental
.
Give it some time and then decide how to proceed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, in that case, we can just use the existing workspaces-experimental
flag, since nohoist is within the workspaces config anyway, probably don't need another flag for nohoist explicitly.
Basically, both package producers and consumers need to have workspaces-experimental
flag on in order to access any workspaces config/functionality.
accepted/0000-nohoist.md
Outdated
IMHO, option-2 is more confusing than option-1. We can reason the nohoist inheritance is based on the dependency tree, once the tree is broken (by hoisting), so should the nohoist list inheritance. But I am open for better arguments. | ||
|
||
## unit tests failed on mac | ||
this is not related to nohoist issue specifically, but made submitting PR much more difficult and time consuming. Many async integration tests failed on my laptop (mac) for even a fresh clone. Is this a known issue? any suggestion or workaround? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope, unit tests should all pass.
Submit a PR and let's see what CI says
accepted/0000-nohoist.md
Outdated
workspace-a | ||
A -> B | ||
``` | ||
IMHO, option-2 is more confusing than option-1. We can reason the nohoist inheritance is based on the dependency tree, once the tree is broken (by hoisting), so should the nohoist list inheritance. But I am open for better arguments. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that nohoist
only works for workspace boundary, regular packages should not inherit workspaces' logic for hoisting.
So yeah, option-1 looks the right one
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
regular packages should not inherit workspaces' logic for hoisting
If by regular packages
you mean the project without turning on workspaces-experimental
, then yes nohoist only impacts the hoisting logic via workspaces.nohoist
in package.json.
The more I think about the original question raised in the RFC, the more I realize that it has to be option-1. option-2 is not a valid alternative actually. Because a hoisted package can be "originated" from multiple sources that might have conflicting nohoist rules, therefore, inheritance-from-source is problematic.
This also helps us simplify nohoist-inheritance concept, as it simply traverses the final node_module graph, just like package resolution.
I will update the RFC to reflect this.
Is this available as a beta or something like that? |
quick update: last week was a busy one but I managed to spend the weekend on this. Finally able to use the new nohoist for a fresh react-native run in my sandbox workspaces... Bumped into quite a few incompatible hidden assumptions in the non-hoisting part of the system and discovered that we do need deep-nohoist for react-native (Metro bundler etc)... anyway, I should be able to submit the code PR this week so we can have more concrete discussions and more people testing it... had to change the design a bit to provide a more powerful/flexible deep-nohoist mechanism. Fortunately, @bestander suggestion of using glob pattern matching provided an intuitive way to do this. I will need to update this RFC as well... |
- nohoist implementation - 'why' command fixes - 'add' command fixes - tests and test fixtures see [RFC yarnpkg#86](yarnpkg/rfcs#86) for detail
accepted/0000-nohoist.md
Outdated
|
||
# Unresolved questions | ||
|
||
## discussion-1 | ||
**is allowing nohoist in public package bad?** | ||
## dependency tree representation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bestander @BYK @arcanis would love to hear your thought on this issue...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, I'll check this later today or tomorrow
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a very thorough analysis, @connectdotz!
I agree with the proposal, let's get it in.
I'll ping the team to comment before merging in a couple of days.
@connectdotz Great job on this RFC! My feedback:
|
@bestander @arcanis thx for the feedback, addressing @arcanis comments:
I see, then it does make sense to have a new one. I will add
I wasn't quite sure what you refer to as "Doing this would remove users the control over the configuration of their package managers." Each package, public or private, is indeed giving package manager instruction on what to load. It is true that they usually don't care where it should be loaded, but for some cases that the position of the module is critical like the react-native module, somebody has to specify the exception. We can ask each consumer (i.e. our projects) to mark the exception, or the package owner can mark the exception itself. I think the later is more efficient and scalable. If you are concerning malicious "take over", I should have documented it better that nohoist rule is always prefixed with the "dependency path" at runtime, so they can really only influence their own packages tree down. For example: in a A/B/C tree, even if B has a [**"] nohoist rule, it will only impact any package match If for whatever reason we need to turn off a public package's nohoist rule, the workspace can always use glob's negation operation to override package's. (I didn't implement this in the initial PR as it can make the nohoist syntax a bit messy, but we can easily enable it later, if need to.) Can a public package do stupid thing? Yes, they could do many stupid things like adding unnecessary dependencies or adding unnecessary nohoist rules. They can cause our workspace less efficient but will most likely not break it. However, if the package didn't specify a needed nohoist rule, our workspace will not work. So my thinking goes like this: 1) there are reasonable protections to sandbox the nohoist rules to its originating package or override it; 2) abusing the rule causing less harm than not being able to specify it... therefore, nohoist for public package should be considered and even encouraged if the package requires it to work...;-)
Just to clarify... you are saying 'yes we should use path-like syntax to express dependency tree', right? I think we all agree that for configuration, glob pattern is the one to use, such as in workspaces.packages, version-selection and now nohoist. Sorry for not being clear, what I meant to ask is specifically regarding reporting progress and state, such as during install or why. For example when asking
You can see there are mixed dependency tree expressions. The new ones use standard path-like format('/'), the rest use yarn custom format ('#'). We should be consistent, if possible. IMHO, if there is no particular reason in choosing '#', we should probably consider standardizing on the path-like format instead... |
Yes, but they don't give the package manager instructions on how to store it on the filesystem. Two actual examples:
I see your point, but they're other factors into play:
I'm not so concerned about malicious packages, however I'm concerned about the ecosystem, and I prefer not to encourage a pattern that effectively decreases the user experience by making the node_modules larger and larger, and installs slower and slower.
Oh, I see. Yes, I agree it would be better to be consistent. However, I think this is worth being discussed in a separate PR - I'd suggest to use |
@arcanis I think we might have misaligned on a very important assumption: nohoist is not a stand-alone 'feature' for packages to take-over location decision from PM, it is merely a 'workaround' to make an otherwise 'working' package function within the monorepo project. nohoist is an no-op outside of workspaces. The developers have to make their packages work without nohoist (most of their consumers will not in monorepo env), and only add nohoist if some of its dependency hasn't been updated to work in monorepo env (we can easily add a warning to make it clear that such package should be fixed soon). In other words, There is no incentive for developers to add unnecessary nohoist. If this is still not enough to make you feel comfortable, then let's just make it private only as you and @bestander suggested😉 . Since many workspaces are also public packages, this change will mean that only the root package.json can specify nohoist. I think it is probably all right for now. We are just debating on some hypothetical situations that we might not yet fully understand anyway... If we ever change our mind, it is literally a 1-line code change...
hmmm... ok |
Eh eh, I'm afraid if it worked like that no package would have ever relied on the hoisting mechanism in the first place :p Ok, let's do the private thing, and I'll go review your implementation PRs! |
@bestander @arcanis code changes and RFC all updated per our discussion |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks!
* nohoist impl check point - nohoist implementation - 'why' command fixes - 'add' command fixes - tests and test fixtures see [RFC #86](yarnpkg/rfcs#86) for detail * fix not adding devDependencies * add nohoist flag and eligibility check 1. added a new flags 'workspaces-nohoist-experimental' to disable nohoist. 2. added eligibility validation in Config.getWorkspaces, violation will be reported and config be ignored. 3. update test fixtures to add private flag for nohoist tests * revert path separator to '#' for display * pass through private flag in root manifest * fix lint error * addressing @bestander review comments on 1/8 * fix merge conflict * fix merge lint issues * address @arcanis comments * update snapshot after merge * one more snapshot update
addressing #3882