npm scripts : 每个前端开发都应知道的一些使用提示

10年服务1亿前端开发工程师

npm 不仅是 JavaScript 的包管理工具,它还可以用于你代码库相关的配置工具,如 Linters(代码检查工具)、transpilers(代码转译工具)、testing(测试工具)和servers(本地服务器)等。这些都可以根据软件包的说明进行配置并运行。基本用法也很简单。

你可以在 package.json 主对象中的 scripts 属性中指定 scripts ,然后使用 npm run 运行 scripts。

例如:

{
  ...
  "scripts": {
    "build": "webpack --progress",
    "test": "karma start",
    "server": "webpack-dev-server"  
  }  
  ...
}

然而,在执行你的配置时,有些细节可能会很有用。

运行附带参数的 scripts

假设你想要运行 Karma ,默认情况下,你无需此功能即可监听所有文件是否已经更改。

为此,它们提供了一个可以与 script 一起使用的 --single-run 标志。

实现这一点的最简单方法是在我们的 scripts 中添加一个新条目。

例如:

"test:single-run": "karma start --single-run"

但是,如果我们要修改我们的默认 test 选项,我们必须在两个地方进行修改 testtest:single-run

或者我们可以直接从命令行中使用我们的标志来解决这个问题。

为了实现这一点,在我们的命令的末尾使用 --,这会告诉 npm ,在此之后的任何东西都应该直接附加到命令中。

$ npm run test -- --single-run

串行或并行运行多个命令

如果你想在一个 script 运行多个命令,假设说 CSS linter(检查),JS linter 和 HTML linter ,最好一次新运行它们来加快运行速度。(注:并行)

但是,如果你的命令存在相互依赖的话,比如说在运行 test 之前需要先运行 transpiler ,你会想要一个接一个地运行的执行流程,不是同时运行。(注:串行)

因为 npm scripts 在内部实际产生的是一个 shell 进程,所以我们可以使用 shell 语法来实现我们所需要的功能。 具体来说,使用 ; (和 &&,下一节会详细说明) 来串联运行,使用 & 来并行运行。

使用此语法的示例如下所示:

并行:

"lint": "eslint & csslint & htmllint"

串行:

"build": "babel; jest"

(我知道,Jest 测试运行器有一个内置的功能来预编译你的代码,这只是一个例子)。

它会工作很好,但是这种方法有一个相当大的问题。 语法会创建一个子进程,这会导致无法判断原始的 npm 进程是否已经完成。这可能是有问题的,特别是长时间运行 scripts 时。

为了使事情更加一致,我们可以使用一个名为 npm-run-all 的包。它提供了额外的命令,更具体地说就是,用 run-s 来运行串联任务, 用 run-p 来运行并行任务,它将正确处理所有的子进程。

并行:

"lint": "run-p eslint csslint htmllint"

串行:

"build": "run-s babel jest"

当命令失败时执行

在我们前面的例子中,在运行测试命令之前,我们运行转译器。

但是,如果一开始就发现了转译失败,那么运行测试的意义是什么?

; 语法会让下一个命令等待,直到上一个命令完成后。也就是说上一个命令完成后,再运行下一个命令,不管上一个命令的退出代码是什么,下一个命令始终会执行。

而我们想要的是,如果任何一个串联命令失败,则停止后续的所有命令执行。

要改成这样,我们只需使用 && 代替 ; 即可:

"build": "babel && jest"

现在,如果 babel 以除了 0 以外的代码退出,这意味着该进程会被中止,jest 将永远不会运行。

当然,我们可以按照我们想要的方式链调用这个语法:

"build": "eslint && babel && jest && deploy"

使用 HOOK SCRIPTS(钩子脚本)

npm 中的每条 script 在引擎内部都会运行三个单独的 script 。一个 pre scripts,一个 scripts 本身和一个 post scripts。
这两个额外运行的 scripts ,正如他们的名字所描述的那样,就是在该 scripts 运行前和该 scripts 运行后运行的脚本。

例如,在部署期间,它们可用来做一些设置和清理。

这两个 scripts 使用与之前 scripts 名称相同的 pre[scriptname]post[scriptname] 来表示。

假设我们要构建我们的项目,这将是一个非常简单的例子,只是为了展示这个概念。

我们会做的是这样的:

  • 创建一个新的一个 dist 目录,如果这个目录已经存在,那么从中删除所有内容
  • 创建 tmp 目录
  • 将项目构建到 tmp 目录
  • 将我们的包压缩到到 dist 目录
  • 删除 tmp 目录
"prebuild": "mkdir dist tmp; rm -rf dist/*",
"build": "browserify main.js -o tmp/bundle.js && uglifyjs -o dist/bundle.min.js -- tmp/bundle.js",
"postbuild": "rm -rf tmp"

请注意,你应该使用的 rimraf 包进行跨平台兼容性,因为上面的代码无法在 Windows 上运行。

现在,每当运行 npm run build 时,它会触发所有命令,并且保证他们以正确的顺序被执行。

运行命令组

npm 中的命名约定使用冒号对整组特定任务进行分组。在上面的代码示例之一中,我们使用 语法并行运行所有代码检查任务。

我经常想做的是将这些任务分成较小的块,并使用 script 本身的 npm run 命令将其作为命令组运行。

我们以前的例子看起来像这样:

"lint": "eslint & csslint & htmllint"

我们可以做的是将他们分隔成单独的任务(例如,如果我们需要添加一些 flags 标志来配置它们),并将它们组合在一起。

我们所能做的是将每一个单独的(例如,我们需要添加一些标记来配置它们)并将它们组合在一起。

"lint": "npm run lint:js & npm run lint:css & npm run lint:html",
"lint:js": "eslint --some-flag",
"lint:css": "csslint --that-will-change",
"lint:html": "htmllint --how-things-work"

在这个更改之后,它将以同样的方式工作,但是现在我们可以同时运行它们,或者在需要的时候也可以分别单独运行。

为了使其更加清洁,我们可以再次使用 npm-run-all ,并将我们的主要 lint 命令更改为 npm-run-all lint:* ,然后它将匹配 lint: 分组中的所有 scripts 。

npm completion

我最近刚学到的一件事是,npm 本身为我们提供了一种 baked-in 方式,即在终端中添加命令完成。更好的是它还将包含你所有的自定义 scripts !

根据你的环境(bashzsh) ,你只需将 npm completion 命令的结果直接传递给 ~/.bashrc~/.zshrc。记住要使用 source ~/.bashrc 重载这个文件!

$ npm completion >> ~/.bashrc
$ npm completion >> ~/.zshrc

为你的 NPM 编写自定义检查

由于实用了 && 语法,npm 能够识别如上所述的常规退出代码,我们可以编写非常简单的 node scripts,这将为我们做一些初步检查。

例如,确保用户指定了所有必需的 ENV 环境变量,或者在尝试运行命令名时不包含任何拼写错误。

我最近使用的是这个 node.js 脚本验证我已经设置了 NODE_ENV ,并且开发人员正在使用其中一个预定义的 env-specific(环境指定) scripts。

我的 scripts 看起来像这样:

"build": "node ./scripts/env-check.js && rimraf dist && webpack --bail --progress --profile --display-error-details",
"build:development": "NODE_ENV=development npm run build",
"build:staging": "NODE_ENV=staging npm run build",
"build:production": "NODE_ENV=production npm run build",

env-check.js 内容:

const task = process.env.npm_lifecycle_event
const packageJSON = require('../package.json')
const availableEnvironments = Object.keys(packageJSON.scripts)
  .filter(key => key.startsWith(task))
  .map(key => key.split(':')[1])
  .filter(key => key)

if (!process.env.NODE_ENV) {
  console.error(`[ Error ] NODE_ENV is required. Use ${task}:${availableEnvironments.join('/')} scripts instead.`)
  process.exit(1)
}

if (!availableEnvironments.includes(env)) {
  console.error(`[ Error ] ${env} is not valid NODE_ENV. Use ${task}:${availableEnvironments.join('/')} scripts instead.`)
  process.exit(1)
}

process.exit(0)

现在,每当开发人员输入 npm run build 时,该提示将显示:

[ Error ] NODE_ENV is required. Use build:development/staging/production scripts instead.

上面的代码有个缺点是,没有使用 pre script,这似乎是一个完美的地方来做这个检查:

"prebuild": "node ./scripts/env-check.js && rimraf dist",
"build": "webpack --bail --progress --profile --display-error-details",

要解决这个问题,我们必须更新 task 变量,因为 process.env.npm_lifecycle_event 不会返回 build 名称,而是返回 prebuild

const task = process.env.npm_lifecycle_event.startsWith('pre') ? process.env.npm_lifecycle_event.slice(3) : process.env.npm_lifecycle_event

现在,我们可以在任何 pre script 中放置 node ./scripts/env-check.js,它将为我们执行这些所有的初步检查。

我个人使用它来构建,服务和部署脚本,但它肯定可以在更多的地方有效地使用。

npm Scripts 相关热门文章

原文链接:http://corgibytes.com/blog/2017/04/18/npm-tips/

&nbsp

赞(0) 打赏
未经允许不得转载:WEB前端开发 » npm scripts : 每个前端开发都应知道的一些使用提示

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

前端开发相关广告投放 更专业 更精准

联系我们

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏