【问题标题】:Run a script (like postinstall) after npm installing a single package?在 npm 安装单个包后运行脚本(如 postinstall)?
【发布时间】:2023-03-31 23:55:01
【问题描述】:

我开始玩 Snowpack。它采用与 Webpack 不同的方法,在安装后立即捆绑各个包。

“问题”是,当我安装一个包时,我必须先运行npm install --save my-package,然后我必须手动使用npx snowpack 打包它。 Snowpack docs 提到我可以包含一个 prepare 脚本,该脚本将在运行 snowpack 之后执行 npm install 的所有内容,但这不适用于单个包,仅适用于我的 package.json 中所有依赖项的通用 npm install .据我所知,npm docs 中提到的所有 npm 钩子都是这种情况。

有什么方法可以在我安装单个软件包时自动运行脚本?我能想到的唯一方法是覆盖安装脚本并添加一些东西。 GitHub 或其他地方有这方面的例子吗?

更新:为了澄清,我想在每次安装带有--save 的新软件包时运行npx snowpack,但最好不要使用--save-dev 或不使用--save。对于任何包,这永远不会有所不同。这将特定于某个 repo/项目,而不是我系统上的全局。

在简单地运行npm install 之后运行snowpack 是不够的,因为您可以通过挂钩postinstallrelease 获得。此外,我想确保从事我项目的开发人员可以像往常一样使用npm install --save newdep,然后snowpack 将运行。我不想要求开发人员使用自定义 named 脚本。

【问题讨论】:

  • @RobC 好主意,虽然我不想链接命令。当我有一个适用于此的脚本时,我会在这里发布。

标签: npm snowpack


【解决方案1】:

简短回答:很遗憾,npm 不提供任何内置功能来满足您的要求。

诸如postinstall 之类的生命周期挂钩/脚本仅在运行通用npm install 命令时调用,而不是在项目开发阶段有人运行npm install --save <pkg_name> 时调用。


解决方法:考虑通过在 shell 级别覆盖 npm 命令来自定义 npm install --save 复合命令的逻辑。

以下解决方案(尽管是 Bash 解决方案)描述了如何为特定项目实现此自定义逻辑。但是,此解决方案取决于以下条件:

  • 在运行npm install --save 复合命令时,从事您项目的开发人员必须将其shell 设置为Bash
  • 从事您项目的开发人员将需要自定义他们的 Bash startup files,即 ~/.bashrc 和可能的 ~/.bash_profile
  • 项目目录,即您希望自定义逻辑对其生效的项目目录,必须包含自定义.bashrc文件。

Bash 解决方案:

以下三个步骤对于配置您的项目和操作系统是必要的,这样当开发人员运行npm install --save <pkg_name>(或其变体)时,随后会调用npx snowpack 命令。

注意:第 2 点和第 3 点(下文)是开发人员需要执行(一次)以自定义其 Bash 启动文件的任务。

  1. 项目特定的.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
    }
    

    注意:要更好地了解此文件的作用,请参阅下面的“说明”部分。

  2. ~/.bashrc 文件:

    要使自定义逻辑,即上述.bashrc 文件中的npm 函数生效,需要配置Bash 以读取上述“项目特定” .bashrc 文件。要进行配置,请将以下代码行添加到~/.bashrc

    PROMPT_COMMAND='if [[ "$bashrc" != "$PWD" && "$PWD" != "$HOME" && -e .bashrc ]]; then bashrc="$PWD"; . .bashrc; fi'
    

    注意:要更好地理解这行代码的作用,请参阅下面的“说明”部分。

  3. ~/.bash_profile 文件:

    通常,您的 ~/.bash_profile 包含以下代码行来加载 ~/.bashrc 文件(或它的某些变体):

    if [ -f ~/.bashrc ]; then . ~/.bashrc; fi
    

    如果不存在,则必须将其添加到~/.bash_profile


其他信息。

设置/配置助手:

考虑您的开发人员使用以下两个命令来帮助配置他们的 Bash 启动文件,按照上述步骤 2 和 3。

  1. 对于第二步,运行以下命令:

    echo $'\n'"PROMPT_COMMAND='if [[ \"\$bashrc\" != \"\$PWD\" && \"\$PWD\" != \"\$HOME\" && -e .bashrc ]]; then bashrc=\"\$PWD\"; . .bashrc; fi'" >> ~/.bashrc
    

    这会将PROMPT_COMMAND=... 代码行添加到现有的~/.bashrc 文件中,如果不存在则创建一个新文件:

  2. 对于第三步,运行以下命令以在 ~/.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

注意:您需要创建一个新会话才能使此更改生效。


说明

  1. 项目特定的.bashrc 文件:

    .bashrc 文件包含一个名为npmshell function。此函数的主体包含覆盖默认npm install|i --save 命令所需的逻辑。

    • if语句中指定的条件,即读取的部分;

      if [[ $* == "install "* || $* == "i "* ]] && array_includes --save "$@"; then
        ...
      fi
      

      基本上读取$* special parameter 以检查传递给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,我们在其主体中执行以下任务:

      1. 通过以下行按原样运行给定的npm install --save ... 命令:

        command npm "$@"
        
      2. 通过以下部分检查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 命令将始终存在,因此得出结论认为此检查是不必要的。

      3. 如果npx 命令全局存在,我们将运行伪“postinstall”命令,即

        command npx snowpack
        
    • if 语句中指定的条件不满足时,即它们是false,用户实际上是在运行任何其他不是npm install --save &lt;pkg_name&gt; 的npm 命令。因此,在else 分支中,我们按原样运行命令:

      command npm "$@"
      
  2. ~/.bashrc 文件:

    “Bash 参考手册”5.2 Bash Variables 部分中,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 复合命令。

    请参阅@Cyrusthis answer 以获得进一步的解释。

【讨论】:

  • 这个答案花了很多时间和精力,但显然太过分了。我不是质疑您回答的合法性,它会产生 OP 所追求的效果。想想那些继续他的项目但没有参与的人。此外,这不是跨平台支持,但它看起来真的是一个很好的概念证明,再次证明一切皆有可能。
【解决方案2】:

使用较新版本的 Snowpack (>=2),您可以运行 snowpack dev,它会监视您的 npm_modules 文件夹以构建新模块。

【讨论】:

    【解决方案3】:

    我认为最好的办法是创建一个执行所需操作的新脚本。 package.json 中的内容如下:

    {
      "scripts": {
        "snowpack-install" : "npm install --save && npx snowpack"
      }
    }
    

    更正

    您实际上可以在 package.json 中使用 postinstall option。 postinstall 将在“安装包后”运行。这将如下所示:

    {
      "scripts": {
        "postinstall" : "npx snowpack"
      }
    }
    

    【讨论】:

    • 我相信postinstall 仅在运行npm install 的通用形式之后运行,所以这不会满足我的要求。此外,我想避免运行任何自定义命名脚本,因为这会使队友更难拾取项目和安装依赖项。我会在问题中澄清。
    • 使用 NPM 钩子怎么样? blog.npmjs.org/post/145260155635/…
    • 在这种情况下调用外部 http 服务似乎有点矫枉过正(钩子是否曾经退出测试版?)
    猜你喜欢
    • 1970-01-01
    • 2017-02-01
    • 2021-11-22
    • 2017-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-23
    • 2021-07-20
    相关资源
    最近更新 更多