.env
文件设置环境变量npm install
期间运行的生命周期脚本的输出node --eval
和 node --print
per-env
:根据 $NODE_ENV
在脚本之间切换package.json
具有属性 "scripts"
,它允许我们定义*包脚本*,这些小型 Shell 脚本执行与包相关的任务,例如编译工件或运行测试。本章解释了它们,以及我们如何编写它们,以便它们在 Windows 和 Unix(macOS、Linux 等)上都能工作。
*npm 包脚本*通过 package.json
的属性 "scripts"
定义
{
···
"scripts": {
"tsc": "tsc",
"tscwatch": "tsc --watch",
"tscclean": "shx rm -rf ./dist/*"
},
···
}
"scripts"
的值是一个对象,其中每个属性定义一个包脚本
如果我们输入
npm run <script-name>
那么 npm 会在 Shell 中执行名称为 script-name
的脚本。例如,我们可以使用
npm run tscwatch
在 Shell 中运行以下命令
tsc --watch
在本章中,我们偶尔会使用 npm
选项 -s
,它是 --silent
的缩写,它告诉 npm run
减少输出
npm -s run <script-name>
此选项在关于日志记录的部分中有更详细的介绍。
某些包脚本可以通过更短的 npm 命令运行
命令 | 等效 |
---|---|
npm test 、npm t |
npm run test |
npm start |
npm run start |
npm stop |
npm run stop |
npm restart |
npm run restart |
npm start
:如果没有包脚本 "start"
,npm 将运行 node server.js
。npm restart
:如果没有包脚本 "restart"
,npm 将运行 "prerestart"
、"stop"
、"start"
、"postrestart"
。默认情况下,npm 在 Windows 上通过 cmd.exe
运行包脚本,在 Unix 上通过 /bin/sh
运行包脚本。我们可以通过npm 配置设置 script-shell
更改它。
但是,这样做很少是一个好主意:许多现有的跨平台脚本都是为 sh
和 cmd.exe
编写的,并且会停止工作。
某些脚本名称是为*生命周期脚本*保留的,每当我们执行某些 npm 命令时,npm 都会运行这些脚本。
例如,每当我们执行 npm install
(不带参数)时,npm 都会运行脚本 "postinstall"
。生命周期脚本将在后面详细介绍。
如果配置设置 ignore-scripts
为 true
,则 npm 永远不会自动运行脚本,只有在我们直接调用它们时才会运行。
在 Unix 上,npm 通过npm completion
支持命令和包脚本名称的 Tab 自动补全。我们可以通过在 .profile
/ .zprofile
/ .bash_profile
/ 等文件中添加以下行来安装它。
. <(npm completion)
如果您需要非 Unix 平台的 Tab 自动补全,请执行网络搜索,例如“npm tab completion PowerShell”。
不带名称的 npm run
将列出可用的脚本。如果存在以下脚本
"scripts": {
"tsc": "tsc",
"tscwatch": "tsc --watch",
"serve": "serve ./site/"
}
那么它们将像这样列出
% npm run
Scripts available via `npm run-script`:
tsc
tsc
tscwatch
tsc --watch
serve
serve ./site/
如果有很多包脚本,我们可以将脚本名称误用作分隔符(脚本 "help"
将在下一小节中解释)
"scripts": {
"help": "scripts-help -w 40",
"\n========== Building ==========": "",
"tsc": "tsc",
"tscwatch": "tsc --watch",
"\n========== Serving ==========": "",
"serve": "serve ./site/"
},
现在脚本如下所示
% npm run
Scripts available via `npm run-script`:
help
scripts-help -w 40
========== Building ==========
tsc
tsc
tscwatch
tsc --watch
========== Serving ==========
serve
serve ./site/
请注意,预先添加换行符 (\n
) 的技巧在 Unix 和 Windows 上都有效。
包脚本 "help"
通过包 @rauschma/scripts-help
中的 bin 脚本 scripts-help
打印帮助信息。我们通过 package.json
属性 "scripts-help"
提供描述("tscwatch"
的值被缩写以适应一行)
"scripts-help": {
"tsc": "Compile the TypeScript to JavaScript.",
"tscwatch": "Watch the TypeScript source code [...]",
"serve": "Serve the generated website via a local server."
}
这就是帮助信息的样子
% npm -s run help
Package “demo”
╔══════╤══════════════════════════╗
║ help │ scripts-help -w 40 ║
╚══════╧══════════════════════════╝
Building
╔══════════╤══════════════════════════════════════════╗
║ tsc │ Compile the TypeScript to JavaScript. ║
╟──────────┼──────────────────────────────────────────╢
║ tscwatch │ Watch the TypeScript source code and ║
║ │ compile it incrementally when and if ║
║ │ there are changes. ║
╚══════════╧══════════════════════════════════════════╝
Serving
╔═══════╤══════════════════════════════════════════╗
║ serve │ Serve the generated website via a local ║
║ │ server. ║
╚═══════╧══════════════════════════════════════════╝
如果某些名称用于脚本,则它们会在某些情况下自动运行
npm install
等操作时运行。所有其他脚本都称为*直接运行脚本*。
每当 npm 运行包脚本 PS
时,它都会自动运行以下脚本(如果它们存在)
prePS
(*前置脚本*)postPS
(*后置脚本*)以下脚本包含前置脚本 prehello
和后置脚本 posthello
"scripts": {
"hello": "echo hello",
"prehello": "echo BEFORE",
"posthello": "echo AFTER"
},
如果我们运行 hello
,就会发生这种情况
% npm -s run hello
BEFORE
hello
AFTER
npm 在 npm 命令期间运行*生命周期脚本*,例如
npm publish
(将包上传到 npm 注册表)npm pack
(为注册表包、包目录等创建存档)npm install
(不带参数使用,用于安装从 npm 注册表以外的来源下载的包的依赖项)如果任何生命周期脚本失败,则整个命令将立即停止并报错。
生命周期脚本的用例是什么?
编译 TypeScript:如果一个包包含 TypeScript 代码,我们通常会在使用它之前将其编译成 JavaScript 代码。虽然后者代码通常不会签入版本控制,但必须将其上传到 npm 注册表,以便可以从 JavaScript 使用该包。生命周期脚本允许我们在 npm publish
上传包之前编译 TypeScript 代码。这确保了在 npm 注册表中,JavaScript 代码始终与我们的 TypeScript 代码同步。它还确保我们的 TypeScript 代码没有静态类型错误,因为在遇到这些错误时,编译(以及发布)会停止。
运行测试:我们还可以在发布包之前使用生命周期脚本运行测试。如果测试失败,则不会发布包。
这些是最重要的生命周期脚本(有关所有生命周期脚本的详细信息,请参阅npm 文档)
"prepare"
:
.tgz
文件)之前运行npm publish
期间npm pack
期间npm install
或全局安装包时运行。"prepack"
在创建包存档(.tgz
文件)之前运行npm publish
期间npm pack
期间"prepublishOnly"
仅在 npm publish
期间运行。"install"
在不带参数使用 npm install
或全局安装包时运行。"preinstall"
和/或一个后置脚本 "postinstall"
。它们的名称更清楚地表明了 npm 何时运行它们。下表总结了这些生命周期脚本的运行时间
prepublishOnly |
prepack |
prepare |
install |
|
---|---|---|---|---|
npm publish |
✔ |
✔ |
✔ |
|
npm pack |
✔ |
✔ |
||
npm install |
✔ |
✔ |
||
全局安装 | ✔ |
✔ |
||
通过 git、路径安装 | ✔ |
**注意:**自动执行操作总是有点棘手。我通常遵循以下规则
prepublishOnly
)。postinstall
)。在本节中,我们将偶尔使用
node -p <expr>
它运行 expr
中的 JavaScript 代码并将结果打印到终端 - 例如
% node -p "'hello everyone!'.toUpperCase()"
HELLO EVERYONE!
当包脚本运行时,当前目录始终是包目录,与我们在其根目录树中的位置无关。我们可以通过将以下脚本添加到 package.json
来确认这一点
"cwd": "node -p \"process.cwd()\""
让我们在 Unix 上试用 cwd
% cd /Users/robin/new-package/src/util
% npm -s run cwd
/Users/robin/new-package
以这种方式更改当前目录有助于编写包脚本,因为我们可以使用相对于包目录的路径。
当一个模块 M
从一个以包 P
的名称开头的说明符导入模块时,Node.js 会遍历 node_modules
目录,直到找到 P
的目录。
M
父目录中的 node_modules
(如果存在)。M
父目录的父目录中的 node_modules
(如果存在)。也就是说,M
继承了其祖先目录的 node_modules
目录。
类似的继承也发生在 bin 脚本上,当我们安装一个包时,这些脚本存储在 node_modules/.bin
中。npm run
会临时将条目添加到 shell PATH 变量中(Unix 上为 $PATH
,Windows 上为 %Path%
)。
node_modules/.bin
。node_modules/.bin
。要查看这些添加的内容,我们可以使用以下包脚本:
"bin-dirs": "node -p \"JS\""
JS
代表包含以下 JavaScript 代码的单行:
process.env.PATH ?? process.env.Path)
(.split(path.delimiter)
.filter(p => p.includes('.bin'))
在 Unix 上,如果我们运行 bin-dirs
,我们会得到以下输出:
% npm -s run bin-dirs
[
'/Users/robin/new-package/node_modules/.bin',
'/Users/robin/node_modules/.bin',
'/Users/node_modules/.bin',
'/node_modules/.bin'
]
在 Windows 上,我们会得到:
>npm -s run bin-dirs
[
'C:\\Users\\charlie\\new-package\\node_modules\\.bin',
'C:\\Users\\charlie\\node_modules\\.bin',
'C:\\Users\\node_modules\\.bin',
'C:\\node_modules\\.bin'
]
在 Make、Grunt 和 Gulp 等任务运行器中,变量很重要,因为它们有助于减少冗余。然而,虽然包脚本没有自己的变量,但我们可以使用_环境变量_(也称为_shell 变量_)来解决这个问题。
我们可以使用以下命令列出特定于平台的环境变量:
env
SET
node -p process.env
在 macOS 上,结果如下所示:
TERM_PROGRAM=Apple_Terminal
SHELL=/bin/zsh
TMPDIR=/var/folders/ph/sz0384m11vxf5byk12fzjms40000gn/T/
USER=robin
PATH=/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin
PWD=/Users/robin/new-package
HOME=/Users/robin
LOGNAME=robin
···
在 Windows 命令行中,结果如下所示:
Path=C:\Windows;C:\Users\charlie\AppData\Roaming\npm;···
PATHEXT=.COM;.EXE;.BAT;.CMD;.VBS;.VBE;.JS;.JSE;.WSF;.WSH;.MSC
PROMPT=$P$G
TEMP=C:\Users\charlie\AppData\Local\Temp
TMP=C:\Users\charlie\AppData\Local\Temp
USERNAME=charlie
USERPROFILE=C:\Users\charlie
···
此外,npm 会在运行包脚本之前临时添加更多环境变量。要查看最终结果,我们可以使用以下命令:
npm run env
此命令调用内置的包脚本。让我们用这个 package.json
来试试:
{
"name": "@my-scope/new-package",
"version": "1.0.0",
"bin": {
"hello": "./hello.mjs"
},
"config": {
"stringProp": "yes",
"arrayProp": ["a", "b", "c"],
"objectProp": {
"one": 1,
"two": 2
}
}
}
所有 npm 临时变量的名称都以 npm_
开头。让我们按字母顺序只打印这些变量:
npm run env | grep npm_ | sort
npm_
变量具有层次结构。在 npm_lifecycle_
下,我们可以找到当前正在运行的包脚本的名称和定义:
: 'env',
npm_lifecycle_event: 'env', npm_lifecycle_script
在 Windows 上,npm_lifecycle_script
在这种情况下将是 SET
。
在 npm_config_
前缀下,我们可以看到一些 npm 的配置设置(在 npm 文档中有描述)。以下是一些示例:
npm_config_cache: '/Users/robin/.npm',
npm_config_global_prefix: '/usr/local',
npm_config_globalconfig: '/usr/local/etc/npmrc',
npm_config_local_prefix: '/Users/robin/new-package',
npm_config_prefix: '/usr/local'
npm_config_user_agent: 'npm/8.15.0 node/v18.7.0 darwin arm64 workspaces/false',
npm_config_userconfig: '/Users/robin/.npmrc',
前缀 npm_package_
允许我们访问 package.json
的内容。它的顶层如下所示:
npm_package_json: '/Users/robin/new-package/package.json',
npm_package_name: '@my-scope/new-package',
npm_package_version: '1.0.0',
在 npm_package_bin_
下,我们可以找到 package.json
属性 "bin"
的属性:
npm_package_bin_hello: 'hello.mjs',
npm_package_config_
条目允许我们访问 "config"
的属性:
npm_package_config_arrayProp: 'a\n\nb\n\nc',
npm_package_config_objectProp_one: '1',
npm_package_config_objectProp_two: '2',
npm_package_config_stringProp: 'yes',
这意味着 "config"
允许我们设置可以在包脚本中使用的变量。下一小节将对此进行更深入的探讨。
请注意,对象已转换为“嵌套”条目(第 2 行和第 3 行),而数组(第 1 行)和数字(第 2 行和第 3 行)已转换为字符串。
以下是剩余的 npm_
环境变量:
npm_command: 'run-script',
npm_execpath: '/usr/local/lib/node_modules/npm/bin/npm-cli.js',
npm_node_execpath: '/usr/local/bin/node',
以下 package.json
演示了如何在包脚本中访问通过 "config"
定义的变量:
{
"scripts": {
"hi:unix": "echo $npm_package_config_hi",
"hi:windows": "echo %npm_package_config_hi%"
},
"config": {
"hi": "HELLO"
}
}
遗憾的是,没有内置的跨平台方法可以从包脚本访问环境变量。
但是,有一些带有 bin 脚本的包可以帮助我们。
包 env-var
允许我们获取环境变量。
"scripts": {
"hi": "env-var echo {{npm_package_config_hi}}"
}
包 cross-env
允许我们设置环境变量。
"scripts": {
"build": "cross-env FIRST=one SECOND=two node ./build.mjs"
}
.env
文件设置环境变量还有一些包允许我们通过 .env
文件设置环境变量。这些文件具有以下格式:
# Comment
SECRET_HOST="https://example.com"
SECRET_KEY="123456789" # another comment
使用与 package.json
分开的文 件可以让我们将这些数据保留在版本控制之外。
以下是一些支持 .env
文件的包:
包 dotenv
支持 JavaScript 模块的 .env
文件。我们可以预加载它:
node -r dotenv/config app.mjs
我们也可以导入它:
import dotenv from 'dotenv';
.config();
dotenvconsole.log(process.env);
包 node-env-run
允许我们通过 shell 命令使用 .env
文件:
# Loads `.env` and runs an arbitrary shell script.
# If there are CLI options, we need to use `--`.
nodenv --exec node -- -p process.env.SECRET
# Loads `.env` and uses `node` to run `script.mjs`.
nodenv script.mjs
包 env-cmd
是前一个包的替代方案。
# Loads `.env` and runs an arbitrary shell script
env-cmd node -p process.env.SECRET
该包具有更多功能:在多组变量之间切换、更多文件格式等。
让我们探讨如何将参数传递给通过包脚本调用的 shell 命令。我们将使用以下 package.json
:
{
···
"scripts": {
"args": "log-args"
},
"dependencies": {
"log-args": "^1.0.0"
}
}
bin 脚本 log-args
如下所示:
for (const [key,value] of Object.entries(process.env)) {
if (key.startsWith('npm_config_arg')) {
console.log(`${key}=${JSON.stringify(value)}`);
}
}console.log(process.argv.slice(2));
位置参数按预期工作:
% npm -s run args three positional arguments
[ 'three', 'positional', 'arguments' ]
npm run
会消耗选项并为其创建环境变量。它们不会添加到 process.argv
中。
% npm -s run args --arg1='first arg' --arg2='second arg'
npm_config_arg2="second arg"
npm_config_arg1="first arg"
[]
如果我们希望选项出现在 process.argv
中,则必须使用_选项终止符_ --
。该终止符通常插入在包脚本名称之后:
% npm -s run args -- --arg1='first arg' --arg2='second arg'
[ '--arg1=first arg', '--arg2=second arg' ]
但我们也可以将其插入在该名称之前:
% npm -s run -- args --arg1='first arg' --arg2='second arg'
[ '--arg1=first arg', '--arg2=second arg' ]
npm 支持以下日志级别:
日志级别 | npm 选项 |
别名 |
---|---|---|
silent | --loglevel silent |
-s --silent |
error | --loglevel error |
|
warn | --loglevel warn |
-q --quiet |
notice | --loglevel notice |
|
http | --loglevel http |
|
timing | --loglevel timing |
|
info | --loglevel info |
-d |
verbose | --loglevel verbose |
-dd --verbose |
silly | --loglevel silly |
-ddd |
日志记录指的是两种活动:
以下小节描述了:
日志级别如何影响这些活动。原则上,silent
记录最少,而 silly
记录最多。
如何配置日志记录。上表显示了如何通过命令行选项临时更改日志级别,但还有更多设置。我们可以临时或永久地更改它们。
默认情况下,包脚本在终端输出方面相对详细。以以下 package.json
文件为例:
{
"name": "@my-scope/new-package",
"version": "1.0.0",
"scripts": {
"hello": "echo Hello",
"err": "more does-not-exist.txt"
},
···
}
如果日志级别高于 silent
并且包脚本退出时没有错误,则会发生以下情况:
% npm run hello
> @my-scope/[email protected] hello
> echo Hello
Hello
如果日志级别高于 silent
并且包脚本失败,则会发生以下情况:
% npm run err
> @my-scope/[email protected] err
> more does-not-exist.txt
does-not-exist.txt: No such file or directory
使用日志级别 silent
,输出会变得不那么混乱:
% npm -s run hello
Hello
% npm -s run err
does-not-exist.txt: No such file or directory
某些错误会被 -s
吞没。
% npm -s run abc
%
我们需要至少 error
日志级别才能看到它们:
% npm --loglevel error run abc
npm ERR! Missing script: "abc"
npm ERR!
npm ERR! To see a list of scripts, run:
npm ERR! npm run
npm ERR! A complete log of this run can be found in:
npm ERR! /Users/robin/.npm/_logs/2072-08-30T14_59_40_474Z-debug-0.log
遗憾的是,日志级别 silent
也会抑制 npm run
(不带参数)的输出。
% npm -s run
%
默认情况下,日志会写入 npm 缓存目录,我们可以通过 npm config
获取其路径:
% npm config get cache
/Users/robin/.npm
日志目录的内容如下所示:
% ls -1 /Users/robin/.npm/_logs
2072-08-28T11_44_38_499Z-debug-0.log
2072-08-28T11_45_45_703Z-debug-0.log
2072-08-28T11_52_04_345Z-debug-0.log
日志中的每一行都以行索引和日志级别开头。这是一个使用日志级别 notice
编写的日志示例。有趣的是,即使是比 notice
“更详细”的日志级别(例如 silly
)也会出现在其中:
0 verbose cli /usr/local/bin/node /usr/local/bin/npm
1 info using [email protected]
···
33 silly logfile done cleaning log files
34 timing command:run Completed in 9ms
···
如果 npm run
返回错误,则相应的日志将如下所示结尾:
34 timing command:run Completed in 7ms
35 verbose exit 1
36 timing npm Completed in 28ms
37 verbose code 1
如果没有错误,则相应的日志将如下所示结尾:
34 timing command:run Completed in 7ms
35 verbose exit 0
36 timing npm Completed in 26ms
37 info ok
npm config list --long
会打印各种设置的默认值。以下是与日志记录相关的设置的默认值:
% npm config list --long | grep log
loglevel = "notice"
logs-dir = null
logs-max = 10
如果 logs-dir
的值为 null
,则 npm 将使用 npm 缓存目录中的 _logs
目录(如前所述)。
logs-dir
允许我们覆盖默认值,以便 npm 将其日志写入我们选择的目录。logs-max
允许我们配置在 npm 删除旧文件之前要写入日志目录的文件数量。如果我们将 logs-max
设置为 0,则永远不会写入日志。loglevel
允许我们配置 npm 的日志级别。要永久更改这些设置,我们也使用 npm config
,例如:
获取当前日志级别:
npm config get loglevel
永久设置当前日志级别:
npm config set loglevel silent
将日志级别永久重置为内置默认值:
npm config delete loglevel
我们也可以通过命令行选项临时更改设置,例如:
npm --loglevel silent run build
npm 文档 解释了更改设置的其他方法(例如使用环境变量)。
npm install
期间运行的生命周期脚本的输出在 npm install
(不带参数)期间运行的生命周期脚本的输出是隐藏的。我们可以通过(临时或永久)将 foreground-scripts
设置为 true
来更改此设置。
silent
才会在使用 npm run
时关闭额外输出。最常用于包脚本的两个 shell 是:
sh
。cmd.exe
。在本节中,我们将研究在两个 shell 中都有效的结构。
提示:
使用段之间用斜杠分隔的相对路径:即使您通常在 Windows 平台上使用反斜杠,Windows 也接受斜杠作为分隔符。
用双引号引起来参数:虽然 sh
支持单引号,但 Windows 命令行不支持。遗憾的是,当我们在包脚本定义中使用双引号时,我们必须对其进行转义。
"dir": "mkdir \"\my dir""
有两种方法可以链接在两个平台上都有效的命令:
&&
之后的命令仅在前一个命令成功(退出代码为 0)时执行。||
之后的命令仅在前一个命令失败(退出代码不为 0)时执行。忽略退出代码的链接在不同平台上有所不同:
;
&
以下交互演示了 &&
和 ||
在 Unix 上的工作方式(在 Windows 上,我们将使用 dir
而不是 ls
):
% ls unknown && echo "SUCCESS" || echo "FAILURE"
ls: unknown: No such file or directory
FAILURE
% ls package.json && echo "SUCCESS" || echo "FAILURE"
package.json
SUCCESS
可以通过 shell 变量访问退出代码:
$?
%errorlevel%
npm run
返回的退出代码与最后执行的 shell 脚本的退出代码相同:
{
···
"scripts": {
"hello": "echo Hello",
"err": "more does-not-exist.txt"
}
}
以下交互发生在 Unix 上:
% npm -s run hello ; echo $?
Hello
0
% npm -s run err ; echo $?
does-not-exist.txt: No such file or directory
1
|
cmd > stdout-saved-to-file.txt
cmd < stdin-from-file.txt
以下命令在两个平台上都存在(但在选项方面有所不同):
cd
echo
。Windows 上的注意事项:双引号会被打印出来,而不是被忽略。exit
mkdir
more
rmdir
sort
以下 package.json
演示了三种调用依赖项中的 bin 脚本的方法:
{
"scripts": {
"hi1": "./node_modules/.bin/cowsay Hello",
"hi2": "cowsay Hello",
"hi3": "npx cowsay Hello"
},
"dependencies": {
"cowsay": "^1.5.0"
}
}
说明:
hi1
:依赖项中的 bin 脚本安装在 node_modules/.bin
目录中。
hi2
:如我们所见,npm 在执行包脚本时会将 node_modules/.bin
添加到 shell PATH 中。这意味着我们可以像使用全局安装的 bin 脚本一样使用本地 bin 脚本。
hi3
:当 npx
运行脚本时,它也会将 node_modules/.bin
添加到 shell PATH 中。
在 Unix 上,我们可以直接调用包本地脚本,前提是它们具有 hashbang 并且是可执行的。但是,这在 Windows 上不起作用,这就是为什么最好通过 node
调用它们的原因。
"build": "node ./build.mjs"
node --eval
和 node --print
当包脚本的功能变得过于复杂时,通常最好通过 Node.js 模块来实现它,这使得编写跨平台代码变得容易。
但是,我们也可以使用 node
命令来运行小的 JavaScript 代码片段,这对于以跨平台的方式执行小任务非常有用。相关的选项有:
node --eval <expr>
计算 JavaScript 表达式 expr
。node -e
node --print <expr>
计算 JavaScript 表达式 expr
并将结果打印到终端。node -p
以下命令在 Unix 和 Windows 上均可使用(只有注释是特定于 Unix 的)
# Print a string to the terminal (cross-platform echo)
node -p "'How are you?'"
# Print the value of an environment variable
# (Alas, we can’t change variables via `process.env`)
node -p process.env.USER # only Unix
node -p process.env.USERNAME # only Windows
node -p "process.env.USER ?? process.env.USERNAME"
# Print all environment variables
node -p process.env
# Print the current working directory
node -p "process.cwd()"
# Print the path of the current home directory
node -p "os.homedir()"
# Print the path of the current temporary directory
node -p "os.tmpdir()"
# Print the contents of a text file
node -p "fs.readFileSync('package.json', 'utf-8')"
# Write a string to a file
node -e "fs.writeFileSync('file.txt', 'Text content', 'utf-8')"
如果我们需要特定于平台的行终止符,我们可以使用 os.EOL
——例如,我们可以将前面命令中的 'Text content'
替换为
`line 1${os.EOL}line2${os.EOL}`
注意事项
os
或 fs
的原因。fs
支持更多文件系统操作。这些操作记录在 §8 “在 Node.js 上使用文件系统” 中。npm-quick-run 提供了一个 bin 脚本 nr
,它允许我们使用缩写来运行包脚本——例如
nr m -w
执行 "npm run mocha -- -w"
(如果 "mocha"
是第一个名称以“m”开头的包脚本)。nr c:o
运行包脚本 "cypress:open"
。并发运行 shell 脚本
&
start
以下两个包为我们提供了跨平台的选择,用于实现该功能和相关功能
concurrently 并发运行多个 shell 命令——例如
concurrently "npm run clean" "npm run build"
npm-run-all 提供了几种功能——例如
一种更方便的顺序调用包脚本的方式。以下两个命令是等效的
npm-run-all clean lint build
npm run clean && npm run lint && npm run build
并发运行包脚本
npm-run-all --parallel lint build
使用通配符运行多个脚本——例如,watch:*
代表所有名称以 watch:
开头的包脚本(watch:html
、watch:js
等)
npm-run-all "watch:*"
npm-run-all --parallel "watch:*"
包 shx
允许我们使用“Unix 语法”来运行各种文件系统操作。它所做的一切都可以在 Unix 和 Windows 上运行。
创建目录
"create-asset-dir": "shx mkdir ./assets"
删除目录
"remove-asset-dir": "shx rm -rf ./assets"
清空目录(使用双引号以确保通配符 *
的安全)
"tscclean": "shx rm -rf \"./dist/*\""
复制文件
"copy-index": "shx cp ./html/index.html ./out/index.html"
删除文件
"remove-index": "shx rm ./out/index.html"
shx
基于 JavaScript 库 ShellJS,其存储库列出了 所有受支持的命令。除了我们已经看到的 Unix 命令之外,它还模拟:cat
、chmod
、echo
、find
、grep
、head
、ln
、ls
、mv
、pwd
、sed
、sort
、tail
、touch
、uniq
等。
包 trash-cli
适用于 macOS(10.12+)、Linux 和 Windows(8+)。它将文件和目录放入回收站,并支持路径和 glob 模式。以下是使用它的示例
trash tmp-file.txt
trash tmp-dir
trash "*.jpg"
包 copyfiles
允许我们复制文件树。
以下是 copyfiles
的一个用例:在 TypeScript 中,我们可以导入非代码资产,例如 CSS 和图像。TypeScript 编译器将代码编译到“dist”(输出)目录,但会忽略非代码资产。此跨平台 shell 命令将它们复制到 dist 目录
copyfiles --up 1 "./ts/**/*.{css,png,svg,gif}" ./dist
TypeScript 编译
my-pkg/ts/client/picker.ts -> my-pkg/dist/client/picker.js
copy-assets
复制
my-pkg/ts/client/picker.css -> my-pkg/dist/client/picker.css
my-pkg/ts/client/icon.svg -> my-pkg/dist/client/icon.svg
包 onchange
监视文件并在每次文件更改时运行 shell 命令——例如
onchange 'app/**/*.js' 'test/**/*.js' -- npm test
一个常见的替代方案(还有许多其他方案)
在开发过程中,拥有一个 HTTP 服务器通常很有用。以下软件包(以及许多其他软件包)可以提供帮助
per-env
:根据 $NODE_ENV
切换脚本bin 脚本 per-env
允许我们运行包脚本 SCRIPT
,并根据环境变量 NODE_ENV
的值自动在(例如)SCRIPT:development
、SCRIPT:staging
和 SCRIPT:production
之间切换
{
"scripts": {
// If NODE_ENV is missing, the default is "development"
"build": "per-env",
"build:development": "webpack -d --watch",
"build:staging": "webpack -p",
"build:production": "webpack -p"
},
// Processes spawned by `per-env` inherit environment-specific
// variables, if defined.
"per-env": {
"production": {
"DOCKER_USER": "my",
"DOCKER_REPO": "project"
}
}
}
bin 脚本 cross-os
根据当前操作系统在脚本之间切换。
{
"scripts": {
"user": "cross-os user"
},
"cross-os": {
"user": {
"darwin": "echo $USER",
"win32": "echo %USERNAME%",
"linux": "echo $USER"
}
},
···
}
支持的属性值有:darwin
、freebsd
、linux
、sunos
、win32
。