-
-
Notifications
You must be signed in to change notification settings - Fork 6.3k
[RFC] CheckHealth: Fix logic for yarn & npm when checking for neovim node package #8528
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
Conversation
runtime/autoload/provider/node.vim
Outdated
" `yarn global dir` returns the root directory of the global modules | ||
let yarn_global_modules = get(split(system('yarn global dir'), "\n"), 0, '') . '/node_modules' | ||
let is_node_modules_dir = isdirectory(npm_global_modules) || isdirectory(yarn_global_modules) | ||
if v:shell_error || !is_node_modules_dir | ||
return '' |
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.
v:shell_error must also be checked immediately after the npm command.
Also should skip yarn if it was found in npm, to save time.
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.
v:shell_error must also be checked immediately after the npm command.
Then the yarn logic needs to move inside the if statement so we don't return an empty string, right?
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.
sure
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.
is there not a more generic way in nodejs-land to check if a freakin' module is available? There are numerous package managers ...
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.
is there not a more generic way in nodejs-land to check if a freakin' module is available? There are numerous package managers ...
I don't think so or I'm not aware of any universal solution for this. But also the main package managers (covers 99%) are npm
& yarn
What if I do it like this instead? any drawbacks or issues?
diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim
index 7d4d5698a..8b8b0f901 100644
--- a/runtime/autoload/provider/node.vim
+++ b/runtime/autoload/provider/node.vim
@@ -22,6 +22,14 @@ function! s:is_minimum_version(version, min_major, min_minor) abort
\ && str2nr(v_list[1]) >= str2nr(a:min_minor)))
endfunction
+function! s:get_node_client_entry(global_modules_path, cli_bin) abort
+ let entry_path = glob(a:global_modules_path . a:cli_bin)
+ if v:shell_error || !isdirectory(a:global_modules_path) || empty(entry_path)
+ return ''
+ endif
+ return entry_path
+endfunction
+
" Support for --inspect-brk requires node 6.12+ or 7.6+ or 8+
" Return 1 if it is supported
" Return 0 otherwise
@@ -41,20 +49,13 @@ function! provider#node#Detect() abort
if exists('g:node_host_prog')
return g:node_host_prog
endif
- let npm_global_modules = get(split(system('npm root -g'), "\n"), 0, '')
- " `yarn global dir` returns the root directory of the global modules
- let yarn_global_modules = get(split(system('yarn global dir'), "\n"), 0, '') . '/node_modules'
- let is_node_modules_dir = isdirectory(npm_global_modules) || isdirectory(yarn_global_modules)
- if v:shell_error || !is_node_modules_dir
- return ''
- endif
if !s:is_minimum_version(v:null, 6, 0)
return ''
endif
let cli_bin = '/neovim/bin/cli.js'
- let entry_point = empty(glob(npm_global_modules . cli_bin))
- \ ? glob(yarn_global_modules . cli_bin)
- \ : glob(npm_global_modules . cli_bin)
+ let entry_point = s:get_node_client_entry(get(split(system('npm root -g'), "\n"), 0, ''), cli_bin)
+ " `yarn global dir` returns the root directory of the global modules
+ \ || s:get_node_client_entry(get(split(system('yarn global dir'), "\n"), 0, '') . '/node_modules', cli_bin)
if !filereadable(entry_point)
return ''
endif
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.
Also the check for
empty(glob(npm_global_modules . cli_bin))
is now missing, which changes the logic. Was that intentional?
That's already happening inside the s:get_node_client_entry
function here
let entry_path = glob(a:global_modules_path . a:cli_bin)
if v:shell_error || !isdirectory(a:global_modules_path) || empty(entry_path) " <--
return ''
endif
return entry_path
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.
Looks good, but pass the
'npm root -g'
command tos:get_node_client_entry
then call system from there, so that thev:shell_error
check is coupled with it more
The only reason I didn't do this was that for yarn
I need to concat with /node_modules
s:get_node_client_entry(get(split(system('npm root -g'), "\n"), 0, ''), cli_bin)
" vs
s:get_node_client_entry(get(split(system('yarn global dir'), "\n"), 0, '') . '/node_modules', cli_bin)
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.
@justinmk what do you think about this?
function! s:get_node_client_entry(package_manager_cmd) abort
let cli_bin = '/neovim/bin/cli.js'
" `yarn global dir` returns the root directory of the global modules
let yarn_extra_path = a:package_manager_cmd ==# 'yarn global dir' ? '/node_modules' : ''
let entry_path = glob(get(split(system(a:package_manager_cmd), "\n"), 0, '') . yarn_extra_path . cli_bin)
if v:shell_error || empty(entry_path) || !isdirectory(entry_path)
return ''
endif
return entry_path
endfunction
function! provider#node#Detect() abort
if exists('g:node_host_prog')
return g:node_host_prog
endif
if !s:is_minimum_version(v:null, 6, 0)
return ''
endif
let entry_point = s:get_node_client_entry('npm root -g')
let entry_point = !empty(entry_point) ? entry_point : s:get_node_client_entry('yarn global dir')
if !filereadable(entry_point)
return ''
endif
return entry_point
endfunction
Not a fan of this
let yarn_extra_path = a:package_manager_cmd ==# 'yarn global dir' ? '/node_modules' : ''
but not sure how to solve this.
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's fine.
Small nit: for such small scopes package_manager_cmd
name is too verbose, cmd
is enough.
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.
Will do the changes, rebase & push then 👍
runtime/autoload/health/provider.vim
Outdated
@@ -502,10 +502,10 @@ function! s:check_node() abort | |||
return | |||
endif | |||
|
|||
if !executable('node') || !executable('npm') | |||
if !executable('node') || !executable('npm') || !executable('yarn') |
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.
with this condition, checkhealth will warn if yarn isn't installed, even if npm is. how about:
if !executable('node') || (!executable('npm') && !executable('yarn'))
runtime/autoload/health/provider.vim
Outdated
@@ -522,8 +522,8 @@ function! s:check_node() abort | |||
let host = provider#node#Detect() | |||
if empty(host) | |||
call health#report_warn('Missing "neovim" npm package.', | |||
\ ['Run in shell: npm install -g neovim', | |||
\ 'Is the npm bin directory in $PATH?']) | |||
\ ['Run in shell: npm install -g neovim or yarn global add neovim', |
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.
use a separate advice line:
\ ['Run in shell (if you use yarn): yarn global add neovim',
a8d3ce9
to
096a99f
Compare
@justinmk made all the changes needed, let me know if there is anything more needs to be done |
runtime/autoload/health/provider.vim
Outdated
@@ -523,7 +523,8 @@ function! s:check_node() abort | |||
if empty(host) | |||
call health#report_warn('Missing "neovim" npm package.', | |||
\ ['Run in shell: npm install -g neovim', | |||
\ 'Is the npm bin directory in $PATH?']) | |||
\ 'Run in shell (if you use yarn): yarn global add neovim', | |||
\ 'Is the global node_modules/bin directory in $PATH?']) |
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.
What does this mean? Either node_modules/
or bin/
is in PATH
?
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.
Maybe that was not clear from my end (& also wrong). node_modules
stores the bin
executables inside node_modules/.bin
folder that's how you can run these binaries either globally or not.
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.
The point of npm root -g
when detecting the node.js host is that it doesn't use node_modules/.bin
because those are shims. Shims don't work on Windows for node.js host because you can't pass the debug flags to node binary.
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.
Actually I think maybe this message can be removed? 'Is the global node_modules/bin directory in $PATH?'
Because:
npm
will symlink the executables on mac or Linux in/usr/local/bin
(don't know about windows)yarn
you will need to add$(yarn global dir)/node_modules
in your$PATH
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'd remove it. In Windows, npm will copy (ie. npm link neovim
copies the node_modules/neovim/
and its dependencies).
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.
@justinmk are you also ok with removing this line?
'Is the global node_modules/bin directory in $PATH?'
or before my change 'Is the npm bin directory in $PATH?'
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.
sure
runtime/autoload/provider/node.vim
Outdated
return '' | ||
endif | ||
let entry_point = s:get_node_client_entry('npm root -g') | ||
let entry_point = !empty(entry_point) ? entry_point : s:get_node_client_entry('yarn global dir') |
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.
Pass the name of the package manager instead to determine the command for the directory for global node modules. You can then check if the package manager is in PATH
via executable()
.
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.
We do a check for executables here already https://github.com/neovim/neovim/pull/8528/files#diff-2d7f214e0c52c9cb5f1601bdfe0e46b6R505
but also if I pass only the name then I'll need to reconstruct the command inside the function too depends on that
let package_manager = executable(a:package_manager) ? a:package_manager : ''
let cmd = package_manager ==# 'npm' ? package_manager . ' root -g' : package_manager . ' global dir'
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's the point. It's internal to the function so that it's easier to use and modify.
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's the point. It's internal to the function so that it's easier to use and modify.
I understand, just thought passing the full command will make it more flexible. So if a new package manager X is released you pass X find-root
command instead of adding more logic. But I'm fine doing this 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.
It doesn't hurt to have "redundant" executable()
checks for autoloaded functions because the user can test each function if they work without running :checkhealth
.
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.
You need to check if
yarn global dir
returns a valid path on top of itsnode_modules/
subfolder otherwise it might use/node_modules/
, where/
is root, as its entry point ifv:shell_error
returns 0.
The check is already here, no? if v:shell_error || !isdirectory(global_modules_dir)
https://github.com/neovim/neovim/pull/8528/files/096a99f3dee634c11843baeb0fb3cc724d81fda4#diff-f9dd34ad99ab738d7adc7261f3fbb879R31
Fixed
Can you make the code more explicit? Only npm and yarn are supported.
let cmd = a:package_manager ==# 'yarn' ? 'yarn global dir' : 'npm root -g'
Ok will do that.
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.
@janlazo good now?
function! s:get_node_client_entry(package_manager) abort
if !executable(a:package_manager)
return ''
endif
let cli_bin = '/neovim/bin/cli.js'
let cmd = a:package_manager ==# 'yarn' ? 'yarn global dir' : 'npm root -g'
let cmd_output = system(cmd)
if v:shell_error || !isdirectory(cmd_output)
return ''
endif
" `yarn global dir` returns the root directory of the global modules
" so we need to append the `/node_modules` part
let yarn_extra_path = a:package_manager ==# 'yarn' ? '/node_modules' : ''
let global_modules_dir = get(split(cmd_output, "\n"), 0, '') . yarn_extra_path
if !isdirectory(global_modules_dir)
return ''
endif
let entry_path = glob(global_modules_dir . cli_bin)
if !filereadable(entry_path)
return ''
endif
return entry_path
endfunction
function! provider#node#Detect() abort
if exists('g:node_host_prog')
return g:node_host_prog
endif
if !s:is_minimum_version(v:null, 6, 0)
return ''
endif
let entry_point = s:get_node_client_entry('npm')
let entry_point = !empty(entry_point) ? entry_point : s:get_node_client_entry('yarn')
return entry_point
endfunction
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.
Why did you break up get(split(system(cmd), "\n"), 0, '')
? You need that to remove traililng <CR>
before passing the output to isdirectory
.
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 was a mistake, this is better now?
function! s:get_node_client_entry(package_manager) abort
if !executable(a:package_manager)
return ''
endif
let cli_bin = '/neovim/bin/cli.js'
let cmd = a:package_manager ==# 'yarn' ? 'yarn global dir' : 'npm root -g'
let modules_dir = get(split(system(cmd), "\n"), 0, '')
if v:shell_error || !isdirectory(modules_dir)
return ''
endif
" `yarn global dir` returns the root directory of the global modules
" so we need to append the `/node_modules` part
let global_modules_dir = a:package_manager ==# 'yarn' ? modules_dir . '/node_modules' : modules_dir
if !isdirectory(global_modules_dir)
return ''
endif
let entry_path = glob(global_modules_dir . cli_bin)
if !filereadable(entry_path)
return ''
endif
return entry_path
endfunction
function! provider#node#Detect() abort
if exists('g:node_host_prog')
return g:node_host_prog
endif
if !s:is_minimum_version(v:null, 6, 0)
return ''
endif
let entry_point = s:get_node_client_entry('npm')
let entry_point = !empty(entry_point) ? entry_point : s:get_node_client_entry('yarn')
return entry_point
endfunction
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.
@janlazo I pushed the latest changes have a look & let me know what you think.
d60a058
to
88e96ea
Compare
runtime/autoload/provider/node.vim
Outdated
if !isdirectory(global_modules_dir) | ||
return '' | ||
endif | ||
let entry_path = glob(global_modules_dir . cli_bin) |
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.
cli_bin
is used once so why not glob(global_modules_dir . '/neovim/bin/cli.js')
?
runtime/autoload/provider/node.vim
Outdated
endif | ||
" `yarn global dir` returns the root directory of the global modules | ||
" so we need to append the `/node_modules` part | ||
let global_modules_dir = is_yarn ? modules_dir . '/node_modules' : modules_dir |
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.
Misleading variable name because modules_dir
is used already and npm's global modules directory is also its root directory. I'd reuse modules_dir
variable here by appending /node_modules
for yarn or rename both variables.
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.
@janlazo check again, updated the PR.
88e96ea
to
e2e8a8d
Compare
It looks fine now. Good job. |
@ahmedelgabri Can you skip the let entry_path = global_modules_dir . '/neovim/bin/cli.js' |
The current checks assumes only that the node package is installed globally using `npm` only. This will make it work if it's installed globally using `npm` or `yarn`
e2e8a8d
to
d1fb25a
Compare
@janlazo done & thanks for that didn't know that |
- "neovim" package may be installed with yarn. Check yarn if npm fails. - Use filereadable() instead of glob(). closes #8552
Merged. Thanks @ahmedelgabri and @janlazo ! |
FEATURES: 07499a8 #8709 man.vim: C highlighting for EXAMPLES section 07f82ad #8699 TUI: urxvt: also send xterm focus-reporting seqs 40911e4 #8616 API: emit nvim_buf_lines_event from :terminal c46997a #8546 fillchars: Add "eob" flag FIXES: 74d19f6 #8576 startup: avoid blank stdin buffer if other files were opened 4874214 #8737 Only waitpid() for processes that we care about cd6e7e8 #8743 Check all child processes for exit in SIGCHLD handler c230ef2 #8746 channel.c: Prevent channel_destroy_early() from freeing uninitialized rpc stuff 0ed8b12 #8681 transstr_buf: fix length comparison d241f27 #8708 TUI: Fix standout mode 9afed40 #8698 man.vim: fix for mandoc e889640 #8682 provider/node: npm --loglevel silent 1cbc830 #8613 API: nvim_win_set_cursor: set curswant bf6048e #8628 checkhealth: Python: fix VIRTUAL_ENV check 3cc3506 #8528 checkhealth: node.js: also search yarn CHANGES: b751449 #8619 defaults: shortmess+=F 1248178 #8578 highlight: high-priority CursorLine if fg is set. 01570f1 #8726 terminal: handle &confirm and :confirm on unloading 56065bb #8721 screen: truncate showmode messages bf2460e #7551 buffer: fix copying :setlocal options c1c14fa #8520 Ex mode: always "improved" (gQ) 050f397 #7992 options: remove 'maxcombine` option (always 6) INTERNAL: 463da84 #7992 screen: use UTF-8 representation
FEATURES: 07499a8 neovim#8709 man.vim: C highlighting for EXAMPLES section 07f82ad neovim#8699 TUI: urxvt: also send xterm focus-reporting seqs 40911e4 neovim#8616 API: emit nvim_buf_lines_event from :terminal c46997a neovim#8546 fillchars: Add "eob" flag FIXES: 74d19f6 neovim#8576 startup: avoid blank stdin buffer if other files were opened 4874214 neovim#8737 Only waitpid() for processes that we care about cd6e7e8 neovim#8743 Check all child processes for exit in SIGCHLD handler c230ef2 neovim#8746 channel.c: Prevent channel_destroy_early() from freeing uninitialized rpc stuff 0ed8b12 neovim#8681 transstr_buf: fix length comparison d241f27 neovim#8708 TUI: Fix standout mode 9afed40 neovim#8698 man.vim: fix for mandoc e889640 neovim#8682 provider/node: npm --loglevel silent 1cbc830 neovim#8613 API: nvim_win_set_cursor: set curswant bf6048e neovim#8628 checkhealth: Python: fix VIRTUAL_ENV check 3cc3506 neovim#8528 checkhealth: node.js: also search yarn CHANGES: b751449 neovim#8619 defaults: shortmess+=F 1248178 neovim#8578 highlight: high-priority CursorLine if fg is set. 01570f1 neovim#8726 terminal: handle &confirm and :confirm on unloading 56065bb neovim#8721 screen: truncate showmode messages bf2460e neovim#7551 buffer: fix copying :setlocal options c1c14fa neovim#8520 Ex mode: always "improved" (gQ) 050f397 neovim#7992 options: remove 'maxcombine` option (always 6) INTERNAL: 463da84 neovim#7992 screen: use UTF-8 representation
The current checks assume only that the node package is installed globally using
npm
only. This PR will make it work if it's installed globally usingnpm
oryarn
.