本文将介绍如何解决 Linux 下,JetBrains 全家桶无法识别使用 Node 版本管理器安装的 Node 的问题。

现有问题

通常来说,我们会使用如 nvm, fnm, volta 等 Node 版本管理器来安装 Node。安装后,我们可以正常地在终端中使用 Node 版本管理器所安装的 Node。

但目前(2022年)为止,JetBrains 全家桶只兼容 nvm

nvm 有许多替代品,实际上开发者可能并不使用 nvm 而是使用如 fnm 等替代品。可 JetBrains 全家桶并不兼容它们,不能识别它们所安装的 Node。

分析问题

这里,让我们简单分析一下 nvm安装脚本,了解 nvm 是如何运作的。

当我们执行官方的安装脚本后,脚本首先将执行 nvm_do_install() 函数。我们需要关注的主要是这一部分:

local NVM_PROFILE
NVM_PROFILE="$(nvm_detect_profile)"
local PROFILE_INSTALL_DIR
PROFILE_INSTALL_DIR="$(nvm_install_dir | command sed "s:^$HOME:\$HOME:")"
SOURCE_STR="\\nexport NVM_DIR=\"${PROFILE_INSTALL_DIR}\"\\n[ -s \"\$NVM_DIR/nvm.sh\" ] && \\. \"\$NVM_DIR/nvm.sh\"  # This loads nvm\\n"
COMPLETION_STR='[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion\n'
BASH_OR_ZSH=false
if [ -z "${NVM_PROFILE-}" ] ; then
  local TRIED_PROFILE
  if [ -n "${PROFILE}" ]; then
    TRIED_PROFILE="${NVM_PROFILE} (as defined in \$PROFILE), "
  fi
  nvm_echo "=> Profile not found. Tried ${TRIED_PROFILE-}~/.bashrc, ~/.bash_profile, ~/.zshrc, and ~/.profile."
  nvm_echo "=> Create one of them and run this script again"
  nvm_echo "   OR"
  nvm_echo "=> Append the following lines to the correct file yourself:"
  command printf "${SOURCE_STR}"
  nvm_echo
else
  if nvm_profile_is_bash_or_zsh "${NVM_PROFILE-}"; then
    BASH_OR_ZSH=true
  fi
  if ! command grep -qc '/nvm.sh' "$NVM_PROFILE"; then
    nvm_echo "=> Appending nvm source string to $NVM_PROFILE"
    command printf "${SOURCE_STR}" >> "$NVM_PROFILE"
  else
    nvm_echo "=> nvm source string already in ${NVM_PROFILE}"
  fi
  if ${BASH_OR_ZSH} && ! command grep -qc '$NVM_DIR/bash_completion' "$NVM_PROFILE"; then
    nvm_echo "=> Appending bash_completion source string to $NVM_PROFILE"
    command printf "$COMPLETION_STR" >> "$NVM_PROFILE"
  else
    nvm_echo "=> bash_completion source string already in ${NVM_PROFILE}"
  fi
fi
if ${BASH_OR_ZSH} && [ -z "${NVM_PROFILE-}" ] ; then
  nvm_echo "=> Please also append the following lines to the if you are using bash/zsh shell:"
  command printf "${COMPLETION_STR}"
fi

安装脚本将检测 ~/.bashrc~/.zshrc~/.profile 等文件是否存在。如果存在,就向其写入如下内容:

export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"  # This loads nvm
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion"  # This loads nvm bash_completion

根据以上内容,当我们启动终端后,将自动执行 $HOME/.nvm/nvm.sh。这个脚本的主要工作之一就是向环境变量中写入其所安装并被用户启用的 Node 的路径。

类似的,fnm安装脚本也做了如 nvm 般的工作。它写入如下内容:

export PATH=/home/kubuntu/.fnm:$PATH
eval "`fnm env`"

显然,JetBrains 全家桶启动时并没有执行以上命令。

解决方案

了解问题所在后,解决它其实并不复杂。我们只需要让 JetBrains 全家桶(如 WebStorm)启动时,能够自动执行 ~/.bashrc~/.zshrc~/.profile 等文件即可。

在 KDE 桌面环境下,点击图标后所执行的动作由其中的 Exec 字段所定义。

Exec=env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/webstorm_webstorm.desktop /snap/bin/webstorm %f

在不同环境、不同安装方式下,你所看到的 Exec 字段内容可能与此处的大不相同,但这并不要紧。

为修复 JetBrains 全家桶无法识别使用 Node 版本管理器安装的 Node 的问题,我们只需要在原有命令前加上 bash -iczsh -ic 即可。例如:

Exec=bash -ic "env BAMF_DESKTOP_FILE_HINT=/var/lib/snapd/desktop/applications/webstorm_webstorm.desktop /snap/bin/webstorm %f"

这是使得我们以 Interactive Shell 的形式启动 Bash 并执行后续启动 JetBrains 全家桶的命令。

Zsh 的运作机制与 Bash 类似。

When an interactive shell that is not a login shell is started, bash reads and executes commands from ~/.bashrc, if that file exists. This may be inhibited by using the –norc option. The –rcfile file option will force bash to read and execute commands from file instead of ~/.bashrc.

当以 Interactive Shell 启动 Bash 时,Bash 将自动读取并执行 ~/.bashrc,这样 Node 版本管理器所想要设置的环境变量就得以传入 JetBrains 全家桶的运行环境。