Replies: 26 comments 14 replies
-
Wouldn't subpath imports address your issue? |
Beta Was this translation helpful? Give feedback.
-
@aduh95 Not really, as that would still require import rewriting. |
Beta Was this translation helpful? Give feedback.
-
cc @nodejs/modules |
Beta Was this translation helpful? Give feedback.
-
It’s not clear what the proposed solution is, or how it would work. I would note that the best way for this feature to happen is for you (or somebody else) to champion its implementation. We might also want to include the yarn folks as well (and maybe folks from the bundlers community) |
Beta Was this translation helpful? Give feedback.
-
I personally doubt avoiding symlinks would be enough (assuming it could even be done). The node_modules algorithm is deeply flawed in its very design, and symlinks (or not-symlinks, as suggested here) only try to workaround the problem - in that you can often make it work-ish, but you see light on the sides. Peer dependencies and workspaces, in particular, are extremely hard (and impossible in their current state) to make work together while being true to both of their contracts, symlinks or no symlinks. Similarly, good hoisting is unbelievably difficult to compute, for no good reason. Add on top of that that the algorithmic complexity is suboptimal, the I/O overhead, the leaky boundaries, etc etc... In my opinion, the best option at this stage would be to accept that node_modules will keep being problematic, and open the project to the possibility for having first-class support for an additional, modern, builtin module loading algorithm (PnP being of course the one I would suggest). Loaders are a good step in this direction, but as long as they aren't first-class they will face unwarranted resistance that will hurt their adoption and therefore make the user experience subpar. Fwiw I think we (at Yarn) would be happy to champion builtin PnP support, but we'd need a clear signal that this would be seriously considered: a co-champion would also have to manifest themselves amongst the Node core contributors. |
Beta Was this translation helpful? Give feedback.
-
I will state that doing this sounds like reimplementing symlinks in user land? If so, that seems very dangerous given that Symlink escapes etc. are a major reason some OS limit their usage. I think any such solution would require a major security review and need to consider actual usage to avoid introducing the same problems. If this is only scoped rewriting module locations is already possible using policies.
Unclear what this means. If the claim is needing to have an API that mutates the current module/loader space I don't think this is likely to occur due to a variety of issues the most obvious of which is that ESM links prior to any such API being able to be used. |
Beta Was this translation helpful? Give feedback.
-
An API would make it third-party. By first-party, I mean that Node itself, within its core, would add support for this new resolution mechanism. Its spec would then become a formal common that tooling authors could (and would) rely on in order to build portable tools. |
Beta Was this translation helpful? Give feedback.
-
So, some new mode of |
Beta Was this translation helpful? Give feedback.
-
Would this already be solved by export maps? Couldn't the |
Beta Was this translation helpful? Give feedback.
-
The problem with "large scale" package loaders isn't technical: there are more than enough prior works, the implementation is trivial. The really difficult part is to get everyone to agree that, yes, it's how we want things to work, no, it's not the work of crazy lunatics working from their garages. A |
Beta Was this translation helpful? Give feedback.
-
Export maps allow only paths that start with We would need a path the goes outside the package's directory. Something like: {
"exports": {
"./": "../../foo@1.0.0/node_modules/foo"
}
} |
Beta Was this translation helpful? Give feedback.
-
@zkochan would altering that check to allow absolute URL targets be enough? I would note that it likely wouldn't use |
Beta Was this translation helpful? Give feedback.
-
thanks, @bmeck. I can experiment with it. I think it would be enough. Instead of symlinks, pnpm would then just create these dummy package.json files for redirecting the resolver to the reallocation of the package. |
Beta Was this translation helpful? Give feedback.
-
@zkochan if there is a desire to avoid creating multiple files a policy would also work but also require a runtime flag |
Beta Was this translation helpful? Give feedback.
-
I'm in general positive of having something official as long as it has npm onboard, cc @MylesBorins. |
Beta Was this translation helpful? Give feedback.
-
@zkochan did you consider writing a custom loader here? |
Beta Was this translation helpful? Give feedback.
-
Am I wrong in thinking that implementing import maps would solve the problem? Using import maps, each package manager would create an import map when changing the This is a good way to distribute responsibility: the package manager implement the rules of what to load, and Node implements the loading itself. |
Beta Was this translation helpful? Give feedback.
-
Relevant discussions for import maps:
Now would be a good time as ever for starting experimenting with it in Node.js. |
Beta Was this translation helpful? Give feedback.
-
In the short term I think import maps support could be implemented via a loader; but if the feature lands in other platforms that seems to me to provide a strong case for including support in core. |
Beta Was this translation helpful? Give feedback.
-
The hoisting and peer dependencies is very easy and very simple with symlinks.
You can construct as flexible a dependency tree you want this way. As I described in #37465, however, mandating symlinks to do this is problematic. And like you say, the major package managers have chosen not to use symlinks. The "workspaces" problem -- is that a package being reused across multiple dependency tree contexts? like yarn portal? -- would require some significant rethinking.
Node.js module resolution should not have anything to do with OS symlinks. Look at the sheer number of issues Node.sj users vs Python or Java or Ruby or any other runtime. File systems, symlinks, hardlinks, etc. should be OS tools accessible to the user changing storage, with no impact on Node.js function. |
Beta Was this translation helpful? Give feedback.
-
To my knowledge symlinks have issues in all those issue trackers. Is there a specific kind of issue that is being avoided in them that we can focus upon?
I'm unclear on what this means. These all do affect filesystem operations which Node uses to load code is the suggestion we try to abstract away all raw FS behaviors? |
Beta Was this translation helpful? Give feedback.
-
You can argue about the particular decisions, but import maps certainly support anything you'd want to do, and are already implemented in Chrome, and Firefox has indicated support. Node.js supporting the same mechanism as browsers would be absolutely huge and absolutely beneficial to package managers and developers. From an adoption/standards perspective, import maps are the winner IMO. |
Beta Was this translation helpful? Give feedback.
-
Symlinks in code loading?
I expand on this in #37465 , but Node.js code loading should read links transparently, which is what Python, Java, Ruby, etc. runtimes do. A user should be able put symlinks however they want, and Node.js should work the same. (Yes I know --preserve-symlinks but the fact is, symlinks are actually required for not duplicating modules and preserving dependency expectations...example in dup issue.) |
Beta Was this translation helpful? Give feedback.
-
On this point, I think everyone agrees import maps support would benefit Node.js; if anyone wants to work on a PR, it would be warmly welcome. |
Beta Was this translation helpful? Give feedback.
-
One use case that often gets missed in these discussions is "I want to put a That said, I think import maps are the obvious solution to pursue here. Symlinks are challenging in many contexts. Import maps lack the ability to inspect with standard system tools, but we don't have to worry about Junctions or cross-device linking or WSL thinking it's linux when it's actually using a Windows filesystem. At least import maps are known standard that is relatively easy for package managers to programmatically generate. I look forward to seeing what pnpm comes up with in this space. It would certainly be exciting to see some of the benefits that pnpm's symlink approach provides (lower disk space, faster reification, etc.) without having to rely on symlinks as such. Adding a whole new loader to make the "what file is loaded when I On some of the side issues brought up:
|
Beta Was this translation helpful? Give feedback.
-
so what is the workaround for that, as you have mentioned that pnpm doesn't work with electron, since I am using pnpm as my package manager and the only issue I am facing is the electron package in my monorepo. So should I use yarn as package manager ? |
Beta Was this translation helpful? Give feedback.
-
Is your feature request related to a problem? Please describe.
pnpm is a mature package manager used by a significant amount of companies and it works well with almost all stacks in the ecosystem. However, because the node_modules structure created by pnpm heavily relies on symlinks, pnpm is unusable in environments that do not support symlinks:
These issues of symlinks are probably the main reason neither npm nor Yarn considered to create a properly nested node_modules using symlinks.
Describe the solution you'd like
Some way to link a package from a different location using a file, not a symlink. For instance,
node_modules/foo
can be a text file that contains a relative path to a different location. Whenrequire('foo')
is searching for the location of foo, the path from the text file is read and used to track down the reallocation of foo (in case of pnpm it will be something like.pnpm/foo@1.0.0/node_modules/foo
)Describe alternatives you've considered
I considered Yarn Plug'n'Play. pnpm supports Yarn PnP, there's an option that makes pnpm create and use a pnp generated via one of Yarn's packages. I like the concept of Plug'n'Play but it still has many issues. Even more issues than symlinks.
I think Plug'n'Play would be a good solution if Node.js would natively support providing some import maps. pnpm would then generate such import maps and node would read the files directly from pnpm's content-addressable store.
I also considered creating dummy redirect files. Instead of creating a symlink, we can create something like
node_modules/foo/index.js
:However, this would not cover cases like
require('foo/lib/something')
I also considered replacing the contents of all require/import statements during installation. So if a package is installed, which requires
foo
, pnpm replacesrequire('foo')
with the reallocation of foo (require('../../foo@1.0.0/node_modules/foo')
) in all files.This solution would probably work in most cases but it would require a lot of new logic to be added to pnpm.
Also, it would only work in copy mode, and that would destroy all the great disk saving benefits of pnpm.
cc @shellscape @vjpr @ExE-Boss
Beta Was this translation helpful? Give feedback.
All reactions