简短回答:不幸的是,npm 没有提供任何内置功能来满足您的要求。
生命周期钩子/脚本等postinstall
仅在运行通用命令时调用,而不是在项目开发阶段npm install
有人运行时调用。npm install --save <pkg_name>
解决方法:考虑通过在 shell 级别npm install --save
覆盖命令来自定义复合命令的逻辑。npm
以下解决方案(尽管是 Bash 解决方案)描述了如何为特定项目实现此自定义逻辑。但是,此解决方案取决于以下条件:
- 开发您的项目的开发人员在运行复合命令时必须将其 shell 设置为Bash 。
npm install --save
- 为您的项目工作的开发人员将需要自定义他们的 Bash启动文件,也
~/.bashrc
可能是~/.bash_profile
.
- 项目目录,即您希望自定义逻辑对其生效的项目目录,必须包含自定义
.bashrc
文件。
重击解决方案:
以下三个步骤是配置您的项目和操作系统所必需的,以便在开发人员运行npm install --save <pkg_name>
(或它的变体)时npx snowpack
随后调用该命令。
注意:第 2 点和第 3 点(下文)是开发人员需要执行(一次)以自定义 Bash 启动文件的任务。
项目特定.bashrc
文件:
首先在项目目录的根目录中创建以下“项目特定” 文件,即将其保存在与项目文件所在.bashrc
位置相同的级别:package.json
/some/path/to/my-project/.bashrc
npm() {
local name_badge="\x1b[37;40mpostinstall\x1b[0m"
array_includes() {
local word=$1
shift
for el in "$@"; do [[ "$el" == "$word" ]] && return 0; done
}
log_warn_message() {
local cmd_name=$1 warn_badge warn_mssg
warn_badge="\x1b[30;43mWARN!\x1b[0m"
warn_mssg="${cmd_name} command not found. Cannot run npx snowpack."
echo -e "\n${name_badge} ${warn_badge} ${warn_mssg}" >&2
}
log_run_message() {
echo -e "\n${name_badge} Running pseudo postinstall hook."
}
if [[ $* == "install "* || $* == "i "* ]] && array_includes --save "$@"; then
# 1. Run the given `npm install --save ...` command.
command npm "$@"
# 2. Check whether the `npx` command exists globally.
command -v npx >/dev/null 2>&1 || {
log_warn_message npx
return 1
}
log_run_message
# 3. Run the pseudo "postinstall" command.
command npx snowpack
else
# Run all other `npm` commands as per normal.
command npm "$@"
fi
}
注意:为了更好地理解此文件的作用,请参阅下面的“说明”部分。
~/.bashrc
文件:
为了使自定义逻辑,即npm
上述.bashrc
文件中的函数生效,需要配置 Bash 以读取上述“项目特定” .bashrc
文件。要配置它,请将以下代码行添加到~/.bashrc
:
PROMPT_COMMAND='if [[ "$bashrc" != "$PWD" && "$PWD" != "$HOME" && -e .bashrc ]]; then bashrc="$PWD"; . .bashrc; fi'
注意:为了更好地理解这行代码的作用,请参阅下面的“说明”部分。
~/.bash_profile
文件:
通常,您~/.bash_profile
包含以下代码行来加载~/.bashrc
文件(或它的某些变体):
if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
如果不存在,则必须将其添加到~/.bash_profile
.
附加信息。
设置/配置助手:
考虑您的开发人员使用以下两个命令来帮助配置他们的 Bash 启动文件,按照上述步骤 2 和 3。
对于第二步,运行以下命令:
echo $'\n'"PROMPT_COMMAND='if [[ \"\$bashrc\" != \"\$PWD\" && \"\$PWD\" != \"\$HOME\" && -e .bashrc ]]; then bashrc=\"\$PWD\"; . .bashrc; fi'" >> ~/.bashrc
这会将PROMPT_COMMAND=...
代码行添加到现有~/.bashrc
文件中,或者如果它不存在则创建一个新文件:
对于第三步,运行以下命令以在~/.bash_profile
加载~/.bashrc
文件中附加所需的代码行:
echo $'\n'"if [ -f ~/.bashrc ]; then . ~/.bashrc; fi" >> ~/.bash_profile
我的 shell 是否配置为 Bash?
要检查 shell 是否配置为 Bash,您可以创建一个新会话,即创建一个新的终端窗口并运行:
echo $0
如果它打印-bash
,那么它正在使用 Bash。
如何将我的 shell 配置为 Bash?
如果echo $0
不打印-bash
,则需要更改外壳。要将其更改为 Bash 运行:
chsh -s /bin/bash
注意:您需要创建一个新会话才能使此更改生效。
解释
项目特定.bashrc
文件:
该.bashrc
文件包含一个名为npm
. 此函数的主体包含覆盖默认npm install|i --save
命令所需的逻辑。
语句中指定的条件if
,即读取的部分;
if [[ $* == "install "* || $* == "i "* ]] && array_includes --save "$@"; then
...
fi
本质上读取$*
特殊参数以检查传递给npm
函数的参数是否以任何一个开头;install
,或者它的简写等价物i
,以及--save
选项/参数是否也已通过。
为了检查参数是否存在,--save
我们将$@
特殊参数传递给array_includes
函数。我们处理这个参数的方式不同,因为--save
复合命令中选项的位置可能不同。例如,用户可以通过运行这个来安装一个包;
# Example showing `--save` option at the end
npm install <pkg_name> --save
或者这个(或其他一些变体):
# Example showing `--save` option in the middle
npm i --save <pkg_name>
当if
满足语句中指定的条件时,即它们是true
,我们在其主体中执行以下任务:
npm install --save ...
通过以下行按原样运行给定的命令:
command npm "$@"
通过以下部分检查npx
命令是否全局存在:
command -v npx >/dev/null 2>&1 || {
log_warn_message npx
return 1
}
如果该npx
命令(全局)不可用,我们会警告用户该npx snowpack
命令无法运行,并且return
从函数中提早退出状态为1
.
注意:我在此检查中的逻辑假设您将在npx
全局范围内安装。但是,如果您在npm
项目中本地安装,则需要更改此逻辑。也许通过检查是否./node_modules/.bin/npx
存在来代替。或者,您可能确信npx
command 将始终存在,因此得出结论认为此检查是不必要的。
如果该npx
命令全局存在,我们将运行伪“postinstall”命令,即
command npx snowpack
当if
语句中指定的条件不满足时,即它们是false
,用户实际上正在运行任何其他不满足的 npm 命令npm install --save <pkg_name>
。因此,在else
分支中,我们按原样运行命令:
command npm "$@"
~/.bashrc 文件:
在“Bash 参考手册”的第5.2 节 Bash 变量中,变量描述如下:PROMPT_COMMAND
PROMPT_COMMAND
如果设置,该值将被解释为在打印每个主要提示 ( ) 之前要执行的命令$PS1
。
所以,这行代码(这里又是):
PROMPT_COMMAND='if [[ "$bashrc" != "$PWD" && "$PWD" != "$HOME" && -e .bashrc ]]; then bashrc="$PWD"; . .bashrc; fi'
加载“项目特定” .bashrc
(如果存在),这反过来用函数覆盖npm
命令。npm
这基本上提供了一种机制来覆盖npm install --save
特定项目的复合命令。
有关进一步的解释,请参阅此答案。@Cyrus