-
-
Notifications
You must be signed in to change notification settings - Fork 587
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
Declare plugins as peerDependencies #623
Comments
Could you explain it with an example? |
In the AMQP transporter, you require The same pattern happens for several other "pluggable" modules in the framework. This is problematic, because you don't know which version of the required library you will receive and you can't make guarantees regarding compatbilitiy. Thus, the accepted version should be declared in a peerDependencies section in the Additionally, there is really no guarantee that the software will be installed in a way that The real only proper way to resolve this is to have the dependent code pass their instance of the module into your code. Then they can decide which library, with which modifications they get to load and to pass into moleculer. If you want more control in moleculer, declare the dependency in moleculer. |
But if I add all available pluggable lib dependencies as peerDependency, but you don't use them, NPM will warn you that these many peer dependency is not installed. |
That is true and it should be seen a benefit IMHO. It makes it clear that certain features won't be available until the peer dependency is installed or that it will not work at all because of a version conflict. |
This seems to me it would be a better use case for optionalDependencies more than peerDependencies. The expectation of a peerDependency is that both the library's consumer and the library need the dependency (e.g. a library consuming react would also use react-dom, thus react-dom is a peerDependency of react). citation In the case of moleculer, you have a number of choices as to how you utilize it. For instance, you can choose which transporter to use, which is opt-in functionality. Declaring every transporter in However, optional dependencies might be a good way to express the expected version ranges that moleculer is compatible with. |
@shawnmcknight the problem with optional dependencies is that the simple |
@shawnmcknight I don't necessarily agree with the reasoning, but either way would achieve boths goals I proposed so I don't want to argue over the specifics :D |
As mentioned, npm v6 currently prints a warning if a peer dependency is unmet, which is more of an annoyance than a real problem in this case. That can be solved by marking the peer dependencies as optional in I'm going to ping @isaacs here to see if he has any input on the best way to handle this case. How can we avoid bloating dependent projects while still being a good citizen and declaring compatible opt-in dependencies? |
Yeahhh... don't add a zillion things as peer deps, that's going to be a nightmare. A peer dep is not a thing you might use, but don't load. A peer dep is a thing you do rely on, and need the caller to rely on as well. So, if I'm understanding the situation properly (which I very well may not be!), the plugins should be declaring moleculer as a peerDependency, not the other way around. Yes, npm v7 will almost certainly install peer deps by default. Even if not installed, it will fetch their metadata and arrange the tree so that they can be installed without conflicts, or fail if they cannot, so this will still impose a lot of cost if you declare a bunch of them unnecessarily. Of course the problem is that if moleculer is doing You could also just document that pnpm users will have to declare their plugins with the full path. Ie, instead of For situations like this, where you try to require() something, and then fall back if it's missing, an Bottom line, you're doing something clever here that leverages an implementation detail of the npm tree reification strategy, and it falls over when using a package manager with the same logical contract but a different implementation strategy. Looking more closely at what's going on here... Yeah, I'd probably just pick one of the following:
|
@isaacs Thanks for the input. It's very much appreciated. To clarify, the "plugins" we're talking about are simply third-party modules that don't know or care about moleculer. Moleculer has built-in adapters for different categories of modules (logger, transporter, serializer, tracer, etc.) and uses a configuration to load the desired pieces at runtime — essentially a factory pattern. It seems like you got that after the "Looking more closely at what's going on here..." bit though. 👍 Based on your knowledge of the ecosystem, do you think there's any room to build in better support for this use case in the package.json dependency structure? Perhaps a new @icebob and others: It seems like this option is the best way to solve this for now.
This echoes @oliversalzburg's earlier suggestion:
|
Given that the handling of peer dependencies in package managers seems to have varied in the past and seems to still be unclear, I definitely retrackt the idea of adding peerDeps to moleculer. Having the ability to pass in the requried code module yourself is always a great option to have. I see no downsides to it, that you don't already have by relying on a package manager. The worst possible solution is definitely a plugin architecture like babel, ESLint, karma, ... failed to implement properly. Before that is happening, please don't change anything at all :D |
This is really an increasing problem. We're now using yarn2 with PnP and it also checks for dependencies very strictly. Hoisting is not a valid approach to external code loading. Right now users will have to manually fix these missing dependency links. For example, for yarn2 that would be something like: packageExtensions:
"moleculer@0.14.*":
peerDependencies:
ioredis: "^4.17.1"
jaeger-client: "^3.18.0"
nats: "^1.4.8" Maybe consider utilizing |
As of {
"peerDependencies": {
"some-database-driver": "2.x",
"other-stuff": "1.2.3 - 2.3.4",
"etc": "...",
},
"peerDependenciesMeta": {
"some-database-driver": {
"optional": true
},
"other-stuff": {
"optional": true
},
"etc": {
"optional": true
}
}
} It's quite verbose, but I think it will do what you were hoping to accomplish by using |
Is your feature request related to a problem? Please describe.
We use a package manager that utilizes symlinks to build the dependency tree from a flat storage area of all installed packages. Because of that, packages can not
require()
other packages that live closer to the root of the dependency tree, because their on-disk location doesn't have the required dependency as a parent.Describe the solution you'd like
npm allows to declare
peerDependencies
to advertise the desire to a load a module that is not directly depended upon. Declaring a module as a peer dependency ensures that it will be made available to the depending module if it exists in the dependency tree.Thus, declaring all dependencies moleculer might want to require should be declared as
peerDependencies
. This will also ensure that it is clear which versions of the dependency moleculer is designed to run with.Describe alternatives you've considered
Peering can be manually configured in our package manager and that would work just fine for us. I would consider adding the peer dependencies more correct for the reasons given above.
Additional context
It seems some work in this area existed once: #37
The text was updated successfully, but these errors were encountered: