null-ls.nvim icon indicating copy to clipboard operation
null-ls.nvim copied to clipboard

Null-ls prefer_local and only_local are not resolving prettier executable

Open batusai513 opened this issue 3 years ago • 15 comments

FAQ

  • [X] I have checked the FAQ and it didn't resolve my problem.

Issues

  • [X] I have checked existing issues and there are no issues with the same problem.

Neovim Version

v0.8.0

Dev Version?

  • [X] I am using a stable Neovim release version, or if I am using a dev version of Neovim I have confirmed that my issue is reproducible on a stable version.

Operating System

macOS 12.5.1

Minimal Config

-- this template is borrowed from nvim-lspconfig
local on_windows = vim.loop.os_uname().version:match "Windows"

local function join_paths(...)
  local path_sep = on_windows and "\\" or "/"
  local result = table.concat({ ... }, path_sep)
  return result
end

vim.g.loaded_remote_plugins = ""
vim.cmd [[set runtimepath=$VIMRUNTIME]]

local temp_dir = vim.loop.os_getenv "TEMP" or "/tmp"

vim.cmd("set packpath=" .. join_paths(temp_dir, "nvim", "site"))

local package_root = join_paths(temp_dir, "nvim", "site", "pack")
local install_path = join_paths(package_root, "packer", "start", "packer.nvim")
local compile_path = join_paths(install_path, "plugin", "packer_compiled.lua")

local null_ls_config = function()
  local null_ls = require "null-ls"
  local formatting = null_ls.builtins.formatting
  -- add only what you need to reproduce your issue
  null_ls.setup {
    sources = {
      formatting.prettier.with {
        only_local = "node_modules/.bin",
      },
    },
    debug = true,
  }
end

local function load_plugins()
  -- only add other plugins if they are necessary to reproduce the issue
  require("packer").startup {
    {
      "wbthomason/packer.nvim",
      {
        "jose-elias-alvarez/null-ls.nvim",
        requires = { "nvim-lua/plenary.nvim" },
        config = null_ls_config,
      },
    },
    config = {
      package_root = package_root,
      compile_path = compile_path,
      profile = {
        enable = true,
        threshold = 0, -- the amount in ms that a plugins load time must be over for it to be included in the profile
      },
    },
  }
end

if vim.fn.isdirectory(install_path) == 0 then
  vim.fn.system { "git", "clone", "https://github.com/wbthomason/packer.nvim", install_path }
  load_plugins()
  require("packer").sync()
else
  load_plugins()
  require("packer").sync()
end

Steps to Reproduce

  • open project with nvim --clean -u minimal_init.lua
  • open ts file
  • run command lua vim.lsp.buf.format({ async = true })

Reproducibility Check

  • [X] I confirm that my minimal config is based on the minimal_init.lua template and that my issue is reproducible by running nvim --clean -u minimal_init.lua and following the steps above.

Expected Behavior

Format the document with prettier from node_modules

Actual Behavior

no format at all

Debug Log

[TRACE Fri Oct  7 13:16:54 2022] ...te/pack/packer/start/null-ls.nvim/lua/null-ls/client.lua:110: starting null-ls client
[TRACE Fri Oct  7 13:16:54 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:123: received LSP request for method initialize
[TRACE Fri Oct  7 13:16:54 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:148: received LSP notification for method initialized
[TRACE Fri Oct  7 13:16:54 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:148: received LSP notification for method textDocument/didOpen
[TRACE Fri Oct  7 13:16:54 2022] ...ack/packer/start/null-ls.nvim/lua/null-ls/generators.lua:21: running generators for method NULL_LS_DIAGNOSTICS_ON_OPEN
[DEBUG Fri Oct  7 13:16:54 2022] ...ack/packer/start/null-ls.nvim/lua/null-ls/generators.lua:24: no generators available
[TRACE Fri Oct  7 13:17:13 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:123: received LSP request for method textDocument/formatting
[TRACE Fri Oct  7 13:17:13 2022] ...ack/packer/start/null-ls.nvim/lua/null-ls/generators.lua:21: running generators for method NULL_LS_FORMATTING
[DEBUG Fri Oct  7 13:17:13 2022] ...t/null-ls.nvim/lua/null-ls/helpers/generator_factory.lua:284: unable to resolve command prettier; aborting
[TRACE Fri Oct  7 13:17:40 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:123: received LSP request for method shutdown
[TRACE Fri Oct  7 13:17:40 2022] .../site/pack/packer/start/null-ls.nvim/lua/null-ls/rpc.lua:148: received LSP notification for method exit

Help

Maybe

Implementation Help

Requirements

  • [x] I have read and followed the instructions above and understand that my issue will be closed if I did not provide the required information.

batusai513 avatar Oct 07 '22 11:10 batusai513

What is your project structure like? Are you able to run ./node_modules/.bin/prettier from the command line without issues?

jose-elias-alvarez avatar Oct 07 '22 15:10 jose-elias-alvarez

@jose-elias-alvarez the project structure is a regular monorepo with prettier installed at the root project, I can run the command from ./node_modules/.bin/prettier, the structure is quite big but it was working before. after more testing, this is happening when I open a project with nvim command, but when I open with nvim . it is working as expected.

batusai513 avatar Oct 07 '22 17:10 batusai513

Also, it seems to be happening as described above only in one of my monorepos, this is the structure:

.
├── Dockerfile
├── Jenkinsfile
├── Makefile
├── README.md
├── descriptor.yml
├── graphql.config.json
├── lerna-debug.log
├── lerna.json
├── nx.json
├── package.json
├── packages
│   ├── package-one
│   ├── package-two
│   ├── package-three
├── pnpm-lock.yaml
├── pnpm-workspace.yaml
├── tsconfig.json
├── turbo.json
└── yarn.lock

batusai513 avatar Oct 07 '22 17:10 batusai513

It sounds like this may be an issue with root directory resolution. Does :lua print(require("null-ls.client").get_client().config.root_dir) show what you expect? If not you may need to change your config.

jose-elias-alvarez avatar Oct 07 '22 19:10 jose-elias-alvarez

I run into the exact same behavior using eslint in a pnpm monorepo. I think the problem is that pnpm doesn't hoist out but only symlinks to dependencies (https://pnpm.io/motivation) and null-ls thus not being able to resolve them. :lua print(require("null-ls.client").get_client().config.root_dir) returned the expected working directory for me

Theo-Steiner avatar Oct 10 '22 10:10 Theo-Steiner

I run into the exact same behavior using eslint in a pnpm monorepo. I think the problem is that pnpm doesn't hoist out but only symlinks to dependencies (https://pnpm.io/motivation) and null-ls thus not being able to resolve them.

I am currently using null-ls in a pnpm project and have no issues, since (at least with this project's setup) the executables are shell script wrappers. This may be different for other versions, but I have no way of verifying that.

If anyone having this issue could put together a minimal sample project that I can use to reproduce the issue, I can investigate. Otherwise, there are too many factors involved in real-world projects to isolate this.

jose-elias-alvarez avatar Oct 10 '22 15:10 jose-elias-alvarez

I had the same issue, in my case it was because one of the directories contained a Makefile:

root_dir = u.root_pattern(".null-ls-root", "Makefile", ".git"),

IMO this shouldn't be used as a heuristic, as nested Makefiles are fairly common - even if some would argue it isn't proper.

Dkendal avatar Oct 20 '22 17:10 Dkendal

To fix it in your config you can change the root_dir property, as suggested above:

null_ls.setup({
	root_dir = require("null-ls.utils").root_pattern(".git", "package.json"),
	...
})

Dkendal avatar Oct 20 '22 17:10 Dkendal

IMO this shouldn't be used as a heuristic, as nested Makefiles are fairly common - even if some would argue it isn't proper.

I agree - if the practice is common enough that there are articles describing why it's wrong, then I don't think it's a good idea to rely on it. It would be a breaking change, but if multiple users are running into the issue, then I think it makes sense. Happy to review a quick PR if I haven't yet gotten around to it.

jose-elias-alvarez avatar Oct 20 '22 19:10 jose-elias-alvarez

I'm not sure if this is the exact issue that I am having (seems similar) but I am experiencing an issue with use_local and prefer_local finding the executable in the wrong location.

.
├── vendor
│  └── bin
│     └── phpcs
├── phpcs.xml
└── web
   └── app
      └── themes
         └── my-wp-theme
            ├── app
            │  └── my-file.php
            ├── phpcs.xml
            └── vendor
               └── bin
                   └── phpcs

I am editing my-file.php yet null-ls is using the root vendor/bin/phpcs even though my config has prefer_local = './vendor/bin. This causes the wrong config file phpcs.xml to be used (phpcs uses the file relative to where it is executed).

codepuncher avatar Oct 25 '22 06:10 codepuncher

Bit out of my depth here but does null-ls use npx or npm to execute Prettier 'under the hood'? I only ask as I have noticed with an exact version of Prettier in my package.json (2.5.0) from that project root, prettier -v gives me 2.7.0 whereas npx prettier -v returns the correct 2.5.0.

Could this have any bearing or is this likely unrelated?

benfrain avatar Dec 01 '22 11:12 benfrain

Bit out of my depth here but does null-ls use npx or npm to execute Prettier 'under the hood'? I only ask as I have noticed with an exact version of Prettier in my package.json (2.5.0) from that project root, prettier -v gives me 2.7.0 whereas npx prettier -v returns the correct 2.5.0.

Could this have any bearing or is this likely unrelated?

null-ls doesn't use npx or npm so this is likely unrelated.

jose-elias-alvarez avatar Dec 01 '22 17:12 jose-elias-alvarez

@jose-elias-alvarez I have been running into this too. The local resolver does work for me mostly, but there is something to say for also letting npx deal with it. Especially for monorepos and stuff, npx prettier will take care of finding prettier. Unfortunately, setting the command to npx prettier doesn't work (only allows a single command). I could set the command to npx and add prettier to the args, but that would break the dynamic args from the plugin. Would it be useful to have an option to use npx instead of the node_modules resolver?

dolfandringa avatar Mar 13 '23 13:03 dolfandringa

Ah, I have a work around to run prettier through npx

local null_ls = require("null-ls")
local null_ls_h = require("null-ls.helpers")
null_ls.setup({
  sources = {
    null_ls.builtins.formatting.prettier.with({
      command = "npx",
      args = null_ls_h.range_formatting_args_factory({
        "prettier",
        "--stdin-filepath",
        "$FILENAME",
      }, "--range-start", "--range-end", { row_offset = -3, col_offset = -1 }),
    }),
  },
})

dolfandringa avatar Mar 13 '23 14:03 dolfandringa

Yep, your workaround makes sense but is definitely not ideal, since as you noted it requires copying the default dynamic arguments. Happy to review a PR adding an npx command resolver.

jose-elias-alvarez avatar Mar 13 '23 14:03 jose-elias-alvarez