williamboman / mason.nvim

Portable package manager for Neovim that runs everywhere Neovim runs. Easily install and manage LSP servers, DAP servers, linters, and formatters.
Apache License 2.0
7.58k stars 270 forks source link

Configurable `npm` command #166

Open lxsymington opened 2 years ago

lxsymington commented 2 years ago

Is your feature request related to a problem? Please describe.

I am often working across a number of repositories that are on different versions of Node, including major versions. Typically npm modules are installed for the current version of node, but for almost all of the packages that are being used by neovim I am going to want to use a separate node version e.g. I am having to work on a repo using node v12 but for various language servers and formatters I want to use v16 as some of them don't even support v12. There are a number of ways of achieving this but I have found that volta.sh does this very nicely, the main caveat being that instead of doing a normal install:

npm install package_name -g

to get the separate version you use:

volta run --node 16 npm install package_name -g

see here That being said, from what I can tell from lua/mason-core/managers/npm/init.lua it doesn't look as if global modules are being used.

Describe the solution you'd like

I think I saw that for your nvim-lsp-installer you said that you weren't going to support yarn, I don't know if that still applies here? but one possible solution could be to allow users to configure the command used for npm installations. Being able to fix/lock the node version of the "package" where Mason installs npm dependencies.

Strictly speaking, the solution, rather than implementation, that I am interested in is being able to have the npm managed dependencies used in my neovim config to be independent of my node version.

Describe potential alternatives you've considered

Not using Mason to manage npm dependencies. Living with some tools not working when working on repos with old node versions.

Additional context

No response

williamboman commented 2 years ago

Hello! Yes this is a problem that I'd like to address somehow.

That being said, from what I can tell from lua/mason-core/managers/npm/init.lua it doesn't look as if global modules are being used.

Correct, packages are always installed locally inside the Mason installation location.

I think I saw that for your nvim-lsp-installer you said that you weren't going to support yarn, I don't know if that still applies here?

So the reason I'm not interested in adding Yarn support is that it doesn't solve any actual problems, the NPM vs Yarn debate in terms of the benefits they provide is largely moot in this project. At first glance, I wouldn't be opposed to considering Volta support!

I did some quick research and it seems like Volta only installs shim executables when you run a global installation. This is a bit problematic as global, externalized, installations are not in scope for Mason - do you know if one can achieve the same result with locally installed executables? I couldn't find much in their docs

lxsymington commented 2 years ago

The understanding volta page is probably the most useful one in this regard. Although the first section relates to toolchain I don't think that's the one that is relevant here as it seems predicated on a global installation. The second section on managing projects is the bit needed for this I think.

I don't really have a sufficient understanding of how Mason is setup yet, but I think that the npm dependencies managed by Mason are being installed into a local node package (set up by npm init) that exposes the relevant "bin" files and Mason adds them to the PATH? If this is the case, when they are run I think they might be being run in the context of that local node package? If that is the case then it might be possible to use volta to pin the node version for the local node package and therefore the exposed "bin" files?

dagadbm commented 2 years ago

just my 2 cents here.

I ended up moving away from nvm/volta/fnm and so on and now i just use asdf for everything with asdf-direnv to manage different node versions per folder.

I think this problem is really hard to solve because you might want also the opposite. Imagine you work in a project that has X package and this project is super old, you need X to run on a specific node version that is older than the one where you installed your original X version on. But if we go with this pinned solution you would not be able to use the local version of X which is what you would want to.

Replace X with prettier/eslint wathever you want.

williamboman commented 2 years ago

Executables installed with npm will execute using the first node executable in PATH. What Volta seems to do (and this seem to only apply to global package installations) is to override the executables to point to a volta-shim. This volta-shim will in turn execute the wrapped executable using the correct Node version. Due to how Volta only does this on global installations I think it'll be difficult to somehow integrate it into Mason (global, externally managed, installations is a non-goal).

My take on this is as follows: Due to how coupled Neovim is with the shell in which you're running it, you end up with situations where the editor will run with different versions of a runtime - often controlled by project-local environments that were activated in the parent shell session. This starts getting problematic when Neovim is also responsible for spawning external processes that rely on a runtime. For Node-based packages, in my experience it doesn't matter much which version you've activated, as long as it's >= 14.

I'm really not interested in solving the above issue in Mason directly, I feel like it's bigger than that (and it sounds like a nightmare to do correctly 🙈).

So, VSC*** works around this by running their extensions using the Node runtime bundled in the Electron instance itself. Maybe a very easy solution would be to add Node as a Mason package, allowing people to install it should they want to. This installation will take precedence in the Neovim session.

glyh commented 2 years ago

This would also allow user to use pnpm/yarn or other alternatives to npm. Sorry I don't see the author is not planning to support them.

mikedfunk commented 2 years ago

Maybe a very easy solution would be to add Node as a Mason package, allowing people to install it should they want to. This installation will take precedence in the Neovim session.

That would be great! I had this problem solved in nvim-lsp-installer via a custom installer pointing to an asdf node version, but sadly this same solution seems to be a lot more work to implement in mason.nvim.

-- call this in lsp setup
builtins.add_nvim_lsp_installers = function()
  local server = require 'nvim-lsp-installer.server'
  local npm = require 'nvim-lsp-installer.core.managers.npm'

  local flow_server = server.Server:new {
    name = 'flow',
    root_dir = server.get_server_root_path('flow'),
    async = true,
    installer = npm.packages { 'flow-bin', 'flow' },
    default_options = { cmd = {
      vim.fn.getenv('HOME') .. '/.asdf/installs/nodejs/17.8.0/bin/node',
      lsp_servers_dir .. '/flow/node_modules/.bin/flow',
      'lsp',
    } }
  }

  require 'nvim-lsp-installer.servers'.register(flow_server)
end
mikedfunk commented 2 years ago

I see that you have pip.install_args available in setup options.

Why not also add something like this as optional config?

{
  npm = {
    node_executable = 'node',
  }
}

That seems simpler than adding a new installer for node. The same could be done for other package managers.

hurricanehrndz commented 1 year ago

I have found vim.env.PATH a life saver.

ashishbinu commented 1 year ago

Is there a way to use pnpm instead of npm. npm seems to take more disk space.

xhyrom commented 1 year ago

Is there a way to use pnpm instead of npm. npm seems to take more disk space.

I could fork this, but adding more package managers is not very easy task (even when they're not compatible) so i did simple bash script: https://github.com/xHyroM/dotfiles/blob/main/.local/bin/npm removed npm from system, added ~/.local/bin to path

it works great for me, if you would have any issues, tell me :D