使用npm安装yarn_如何使用npm yarn node js包json脚本作为构建工具

使用npm安装yarn

Bundled with the Node.js platform is an under-appreciated feature of the package management tool, npm. The primary purpose for npm is accessing a very large library of packages that run on Node.js. But it includes a feature, the scripts tag, that is itself very powerful. With that tag, it is possible to construct complex data manipulation systems using any command line utility.

与Node.js平台捆绑在一起的是包管理工具npm的一个未被重视的功能。 npm的主要目的是访问在Node.js上运行的非常大的软件包库。 但是它包括一个功能,即scripts标签,该功能本身非常强大。 使用该标记,可以使用任何命令行实用程序来构建复杂的数据处理系统。

With the scripts tag, we can record named commands in a package.json file. For example a deploy script might using rsync to upload files to a server. By itself this tag isn't terribly powerful, even though with it we can run any command-line tool. But, some available tools in the npm repository can be used to build complex processing architectures in the scripts section. Add to that the vast library of available Node.js packages, and we have a powerful environment with which to implement build systems, web publishing systems, or even systems as far afield as training machine learning models.

使用scripts标签,我们可以将命名命令记录在package.json文件中。 例如, deploy脚本可能使用rsync将文件上传到服务器。 即使使用我们可以运行任何命令行工具,该标签本身也并不强大。 但是,npm存储库中的一些可用工具可用于在scripts部分中构建复杂的处理体系结构。 再加上庞大的可用Node.js程序包库,我们拥有一个功能强大的环境,可用来构建构建系统,Web发布系统,甚至是用于训练机器学习模型的系统。

The scripts tag in package.json was introduced by the npm project. Its primary use is supporting package installation, other package management tasks, and simple project scripts like the method for starting a server. The primary target was Node.js package or application authors.

package.jsonscripts标签是npm项目引入的。 它的主要用途是支持程序包安装,其他程序包管理任务以及简单的项目脚本(如启动服务器的方法)。 主要目标是Node.js程序包或应用程序作者。

Just because that was the design goal doesn’t mean we can’t use the scripts tag for other purposes. The ability to run any command-line tool, and to use add-ons letting you build complex processing hierarchies, gives us potent combination.

仅仅因为那是设计目标,并不意味着我们不能将scripts标记用于其他目的。 运行任何命令行工具以及使用附加组件的功能使您可以构建复杂的处理层次结构,这为我们提供了强大的组合。

Instead of using package.json to develop a Node.js package, or application, or server, let's consider a different kind of project. For example, you've built a Docker container, and you want to automate building it and publishing it to a Docker repository. You could write a shell script, a Makefile, or you could implement the publishing process with npm and package.json. That requires recording docker login, docker build, and docker push commands into the scripts section of a package.json. You may need to support running the build on Windows, Linux and macOS machines, and there is a Node.js tool available to help run the commands across platforms.

让我们考虑使用另一种项目,而不是使用package.json开发Node.js包,应用程序或服务器。 例如,您已经构建了一个Docker容器,并且想要自动构建它并将其发布到Docker存储库。 您可以编写Shell脚本,Makefile,也可以使用npm和package.json实现发布过程。 这需要将docker logindocker builddocker push命令记录到package.jsonscripts部分中。 您可能需要支持在Windows,Linux和macOS计算机上运行构建,并且有一个Node.js工具可用来帮助跨平台运行命令。

It’s possible to automate this task, and many others, with package.json, or with many other tools. The choice is up to you. This article will introduce you to using package.json for purposes other than it's primary design goals.

可以使用package.json或其他许多工具来自动化此任务以及其他许多任务。 这个选择由你。 本文将向您介绍将package.json用于其主要设计目标以外的目的。

Where this makes the most sense is for those already using the Node.js platform, either for writing Node.js packages or applications, or else using tools that are written in Node.js. Such people are probably already using package.json files, and can easily extend that file into use as a build tool.

对于已经使用Node.js平台的用户(编写Node.js程序包或应用程序,或使用以Node.js编写的工具),这是最有意义的。 这样的人可能已经在使用package.json文件,并且可以轻松地将该文件扩展为构建工具。

TL;DR if you already know how to use package.json and npm, you will probably find the next few sections boring. This article starts the reader from zero in case they've never seen this material.

TL; DR,如果您已经知道如何使用package.json和npm,则可能会发现接下来的几节很无聊。 如果读者从未看过本文,那么本文将从零开始。

什么是package.json和npm / Yarn生态系统 (What is package.json and the npm/Yarn ecosystem)

A big attraction to using package.json is that it is natively supported by Node.js, npm, and Yarn. At our fingertips is not only a powerful programming platform, but a large ecosystem of Node.js packages.

使用package.json一个很大的吸引力是Node.js,npm和Yarn本身都支持它。 触手可及的不仅是强大的编程平台,而且是大型的Node.js软件包生态系统。

As we just said, there are three realms in which package.json is important. First, it is used by Node.js for describing a package directory. Second, it is used in the npm/Yarn ecosystem for package management. Third, it is used by package or application authors for scripts related to their project.

正如我们刚才所说, package.json在三个领域都很重要。 首先,Node.js使用它来描述软件包目录。 其次,它在npm / Yarn生态系统中用于软件包管理。 第三,程序包或应用程序作者将其用于与其项目相关的脚本。

In this section let’s quickly discuss the first two before doing a deep dive on the third.

在本节中,让我们快速讨论前两个,然后再深入研究第三个。

Node.js在package.json寻找什么? (What does Node.js look for in package.json?)

The package.json file was developed for Node.js, but Node.js uses only a small portion of the contents of a modern package.json. For example:

package.json文件是为Node.js开发的,但是Node.js仅使用了现代package.json内容的一小部分。 例如:

{
"name": "example-package-json",
"type": "commonjs",
"main": "./path/to/main.js"
}

That’s about it.

就是这样

The context is that Node.js treats individual JavaScript files as modules, plus it can treat a correctly constructed directory as a module. In the Node.js documentation look for Folders as Modules. One thing which makes a directory a module is the package.json file. The Node.js runtime looks for a very small number of fields in this file. In addition to the three fields shown here, there is exactly (as far as I can tell) one more field, exports, that is an alternate to the main field.

上下文是Node.js将单个JavaScript文件视为模块 ,并且可以将正确构造的目录视为模块 。 在Node.js文档中查找Folders as Modules 。 使目录成为模块的一件事是package.json文件。 Node.js运行时在此文件中查找非常少量的字段。 除了这里显示的三个字段外,确切地说(据我所知)还有一个字段exports ,它是main字段的替代字段。

npm或Yarn在package.json查找什么? (What does npm or Yarn look for in package.json?)

As we said, the package manager applications (npm, Yarn, etc) added a large number of fields to package.json. These fields cover package management, dependency management, and scripts for packaging or project administration.

如前所述,程序包管理器应用程序(npm,Yarn等)在package.json添加了大量字段。 这些字段涵盖程序包管理,依赖项管理以及用于程序包或项目管理的脚本。

Running the command npm help package.json shows a list of additional package.json fields.

运行命令npm help package.json显示其他package.json字段的列表。

The fields description, version, repository, keywords, author, license, bugs, and homepage are examples of data which is displayed on package listings on the npmjs. com website. These are examples of what we referred to earlier as package management.

字段descriptionversionrepositorykeywordsauthorlicensebugshomepage是数据的示例,这些数据显示在npmjs的软件包清单上。 com网站。 这些是我们先前称为包管理的示例。

The version field is also used in dependency management, as is the dependencies and devDependencies fields. These fields are used during while installing a package, with the package manager (npm/Yarn) reading entries in those fields for the packages to install. The version field advertises the version number for each instance of each package, and the dependencies fields specify the acceptable version numbers to use. This way the package manager can download the correct version of the required packages. The dependencies fields can also target packages outside the npm registry, such as from a Git repository or an arbitrary URL. These are examples of what we referred to earlier as dependency management.

version字段以及dependenciesdevDependencies字段也用于依赖项管理。 这些字段在安装软件包期间使用,软件包管理器(npm / Yarn)读取这些字段中的条目以安装软件包。 version字段会为每个程序包的每个实例发布版本号,而dependencies字段则指定要使用的可接受版本号。 这样,程序包管理器可以下载所需程序包的正确版本。 依赖项字段还可以定位到npm注册表外部的包,例如来自Git存储库或任意URL的包。 这些是我们先前称为依赖性管理的示例。

The primary mechanism for project management is the scripts field. We'll be exploring it in depth in the rest of this article. The scripts field was invented to serve package developers, or application developers. For a description of this field run npm help scripts. When reading that, especially pay attention to the predefined scripts.

项目管理的主要机制是scripts字段。 我们将在本文的其余部分中深入探讨它。 scripts字段是为服务包开发人员或应用程序开发人员而发明的。 有关此字段的描述,请运行npm help scripts 。 阅读时,尤其要注意预定义的脚本。

创建一个package.json文件 (Creating a package.json file)

To start let’s learn how to create a package.json using npm (or Yarn). It's very simple:

首先,让我们学习如何使用npm(或Yarn)创建package.json 。 很简单:

$ mkdir example1
$ cd example1
$ npm init -y
Wrote to /Volumes/Extra/nodejs/npm-build-scripts/example1/package.json:{
"name": "example1",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "David Herron <david@davidherron.com>",
"license": "ISC"
}

That’s the fastest way of building a package.json. Leave off the -y option and you'll be asked a few questions about the project, while using this option the default values for each option are taken.

这是构建package.json的最快方法。 取消-y选项,将询问有关项目的一些问题,而使用此选项时,将采用每个选项的默认值。

You can set some npm config settings to help auto-populate the generated package.json with the correct values:

您可以设置一些npm配置设置,以帮助使用正确的值自动填充生成的package.json

  • npm config set init.author.name "..." -- Sets the author name

    npm config set init.author.name "..." -设置作者名称

  • npm config set init.author.email ...@... -- Sets the author e-mail address

    npm config set init.author.email ...@...设置作者的电子邮件地址

  • npm config set init.author.url https://... -- Sets the author home page URL

    npm config set init.author.url https://...设置作者主页URL

  • npm config set init.license ... -- Sets the default license to use

    npm config set init.license ...设置要使用的默认许可证

To learn about config settings run npm help 7 config.

要了解配置设置,请运行npm help 7 config

Once you have the package.json in hand you can move forward.

一旦有了package.json ,您就可以继续前进。

熟悉package.jsonscripts标签 (Familiarizing ourselves with the scripts tag in package.json)

The scripts section of package.json contains a list of named scripts. When npm generates this file, as just shown above, it includes a test script. It is run as so:

package.json的脚本部分包含命名脚本的列表。 如上所示,npm生成此文件时,它包含一个test脚本。 它是这样运行的:

$ npm run test > example1@1.0.0 test /Users/David/nodejs/npm-build-scripts/example1 
> echo "Error: no test specified" && exit 1 Error: no test specified npm
ERR! code ELIFECYCLE
...

This script runs an echo command and then runs exit 1. The last is what makes npm believe an error occurred.

该脚本运行echo命令,然后运行exit 1 。 最后是使npm相信发生错误的原因。

We can also run this as npm test, leaving out run. This is because npm has several pre-defined script names that correspond to npm subcommands:

我们也可以将其作为npm test run ,而npm test run 。 这是因为npm具有几个与npm子命令相对应的预定义脚本名称:

  • npm test -- Runs a test script

    npm test运行测试脚本

  • npm start -- Runs a command to start a server

    npm start运行命令以启动服务器

  • npm restart -- Runs a command to restart a server

    npm restart运行命令以重新启动服务器

  • npm stop -- Runs a command to stop the running server

    npm stop运行命令以停止正在运行的服务器

For any other script it is executed as npm run script-name. Running npm help scripts gives more information about how this is interpreted. The pre-baked scripts are designed to handle lifecycle events meaning they are executed at certain phases of installing or using or developing Node.js packages in Node.js applications.

对于任何其他脚本,它都以npm run script-name身份执行。 运行npm help scripts可提供有关如何解释的更多信息。 预编写的脚本旨在处理生命周期事件,这意味着它们在Node.js应用程序中安装,使用或开发Node.js软件包的某些阶段执行。

使用前后脚本 (Using pre- and post- scripts)

The mechanism supports a rudimentary control flow. Consider:

该机制支持基本的控制流程。 考虑:

"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"prestart": "echo 'Pre-start runs before start'",
"start": "echo 'START'",
"poststart": "echo 'Post-start runs after start'"
},

As the script names suggest, for a script named foo, the prefoo script runs before foo, and the postfoo script runs after foo. It looks like this:

顾名思义,对于名为foo的脚本, prefoo脚本在foo之前运行,而postfoo脚本在foo之后运行。 看起来像这样:

$ npm start> example1@1.0.0 prestart /Users/David/nodejs/npm-build-scripts/example1
> echo 'Pre-start runs before start'Pre-start runs before start> example1@1.0.0 start /Users/David/nodejs/npm-build-scripts/example1
> echo 'START'START> example1@1.0.0 poststart /Users/David/nodejs/npm-build-scripts/example1
> echo 'Post-start runs after start'Post-start runs after start

If you want an infinite loop, change poststart to this:

如果需要无限循环, poststart更改为此:

"poststart": "echo 'Post-start runs after start' && npm run start"

Since the text inside the script is treated like a Unix shell script, and a command structure cmd && cmd runs the commands one after another, what this does is cause the poststart script to again run start. Which ... causes the whole prestart/ start/ poststart sequence to execute again.

由于脚本中的文本被视为Unix Shell脚本,并且命令结构cmd && cmd poststart运行命令,因此导致poststart脚本再次运行start 。 哪个...导致整个prestart / start / poststart序列再次执行。

将参数传递给脚本 (Passing arguments to a script)

It is possible to pass args from the npm command line to a script. Consider:

可以将args从npm命令行传递到脚本。 考虑:

"withargs": "echo ARGS"

Then we run this:

然后我们运行:

$ npm run withargs -- Hello World> example1@1.0.0 withargs /Users/David/nodejs/npm-build-scripts/example1
> echo ARGS "Hello" "World"ARGS Hello World

Everything after -- is passed on the command line of the executed script.

之后的一切--是通过执行脚本的命令行上。

使用外壳管道 (Using shell pipelines)

Since the scripts use Unix shell scripting syntax, that includes building pipelines.

由于脚本使用Unix Shell脚本语法,因此其中包括构建管道。

Consider:

考虑:

"pipeline": "echo one two three four | wc"

If you’ve made it this far, you probably know that the wc command prints the word count of whatever is on the standard input. It was the first trivial pipeline example I could come up with. Let's see how this runs:

如果到此为止,您可能知道wc命令会打印标准输入中任何内容的字数统计。 这是我可以想到的第一个琐碎的管道示例。 让我们看看它是如何运行的:

$ npm run pipeline > example1@ 1.0 .0 pipeline /Users/David/nodejs/npm-build-scripts/example1 
> echo one two three four | wc 1 4 19

Yes, the input is one line, with four words, and nineteen characters, just like the wc command says. This was a trivial example, but there are of course somewhere close to a zillion different possible pipelines you could build and use here.

是的,输入是一行,包含四个单词和十九个字符,就像wc命令说的那样。 这是一个简单的例子,但是您当然可以在这里建立和使用近千种不同的可能管道。

These trivial examples were meant to familiarize yourself with some of the possibilities. With this in mind let’s look at some more comprehensive examples.

这些琐碎的例子旨在使您熟悉一些可能性。 考虑到这一点,让我们看一些更全面的示例。

运行一系列脚本,一个接一个 (Running a series of scripts, one after the other)

In Unix command lines we can execute cmd && cmd && cmd to run a series of commands in a row. In package.json scripts we've already seen we can do the same to run a set of commands one after another. Think about how you could implement a multi-step process using && in a package.json script.

在Unix命令行中,我们可以执行cmd && cmd && cmd来连续运行一系列命令。 在package.json脚本中,我们已经看到我们可以执行相同的操作来依次运行一组命令。 考虑一下如何在package.json脚本中使用&&来实现多步骤过程。

In a new directory, called build-site, start a new project (running npm init -y). In that directory install these packages:

在名为build-site的新目录中,启动一个新项目(运行npm init -y )。 在该目录中安装以下软件包:

In the package.json add these script tags:

package.json添加以下script标记:

$ npm install lessc mustache html-minifer --save

Then consider that in the build-site directory is another directory, site, containing a file named style.less containing a stylesheet written in the LESS syntax, and a Mustache template meant to be rendered into a file named index.html.

然后,考虑到build-site目录中的另一个目录site ,其中包含一个名为style.less的文件,该文件包含以LESS语法编写的样式表,以及一个将要呈现为名为index.html的文件的Mustache模板。

In the package.json add these script tags:

package.json添加以下script标记:

"scripts": {
"build-site": "npm run less && npm run render && npm run deploy",
"less": "lessc site/style.less build/style.css",
"render": "mustache data.json site/index.mustache build/index.html",
"deploy": "cd build && rsync --archive --delete --verbose ./ user-name@remote-host.com:docroot-directory/"
},

We won’t take the space to show you the individual files, so use your imagine that those files exist and have reasonable contents. The build-site tag describes a multi-stage process, where each stage is another script command. The first stage uses lessc to compile the stylesheet, the second uses mustache to render the template and html-minifier to minify the HTML, and the last uses rsync to deploy to a server.

我们不会占用空间来显示各个文件,因此请想象一下这些文件是否存在并具有合理的内容。 build-site标记描述了一个多阶段的过程,其中每个阶段都是另一个脚本命令。 第一阶段使用lessc来编译样式表,第二阶段使用mustache来渲染模板,并使用html-minifier来缩小HTML,最后一个阶段使用rsync部署到服务器。

If we mock up some data so this runs:

如果我们模拟一些数据,那么它将运行:

$ mkdir site
$ touch site/style.less site/index.mustache
$ echo '{}' >data.json
$ mkdir build

Again, pretend that there are real files here. For example the data.json could be the result of a database query that we run in another stage of this process.

再次,假装这里有真实文件。 例如, data.json可能是我们在此过程的另一个阶段中运行的数据库查询的结果。

We can run the build like so:

我们可以这样运行构建:

$ npm run build-site> build-site@1.0.0 build-site /Users/David/nodejs/npm-build-scripts/build-site
> npm run less && npm run render && npm run deploy> build-site@1.0.0 less /Users/David/nodejs/npm-build-scripts/build-site
> lessc site/style.less build/style.css> build-site@1.0.0 render /Users/David/nodejs/npm-build-scripts/build-site
> mustache data.json site/index.mustache | html-minifier -o build/index.html> build-site@1.0.0 deploy /Users/David/nodejs/npm-build-scripts/build-site
> cd build && rsync --archive --delete --verbose ./ user-name@remote-host.com:docroot-directory/...

It is a good practice to separate each stage into its own scripts entry. That way we can run stages individually, if needed to debug one, while also making each script easier to read. Consider what the build-site script would be like if you wrote everything on one line. After you finish shuddering at the thought, praise the fact that npm (and Yarn) lets you use this technique.

将每个阶段分成自己的scripts条目是一个好习惯。 这样,如果需要调试一个脚本,我们可以分别运行各个阶段,同时还使每个脚本更易于阅读。 考虑一下如果您将所有内容写在一行上, build-site脚本将是什么样。 当您对这种想法不寒而栗后,赞美npm(和Yarn)可以让您使用此技术的事实。

使用npm-run-all改善顺序脚本执行 (Improving sequential script execution with npm-run-all)

The build-site example is interesting since it points towards the ability to construct a complex process using package.json. Obviously the number of stages can be expanded. For example we suggested the data.json could be generated from a database query, and we could easily write a custom Node.js script to do that query. There are other tools to consider, like using the TypeScript compiler to generate JavaScript files from TypeScript source. Or to use lint tools to ensure the resulting code is clean. And maybe some image processing steps are required. And of course the website could have multiple pages to render.

build-site示例很有趣,因为它指出了使用package.json构造复杂过程的能力。 显然,阶段数可以扩展。 例如,我们建议data.json可以从数据库查询中生成,并且我们可以轻松编写自定义Node.js脚本来执行该查询。 还需要考虑其他工具,例如使用TypeScript编译器从TypeScript源生成JavaScript文件。 或使用棉绒工具来确保生成的代码干净。 也许需要一些图像处理步骤。 当然,该网站可以呈现多个页面。

One issue is that the build-site script itself is a bit unwieldy. The more of those ideas we implement, this script becomes more and more unwieldy until it becomes downright unmanageable. Fortunately there is a tool, npm-run-all, that can simplify that setup.

一个问题是, build-site脚本本身有点笨拙。 我们实施的这些想法越多,这个脚本就变得越来越笨拙,直到完全无法管理为止。 幸运的是,有一个工具npm-run-all可以简化该设置。

For documentation see: https://www.npmjs.com/package/npm-run-all

有关文档,请参见: https : //www.npmjs.com/package/npm-run-all

To experiment with this, duplicate the build-site directory as build-site-2 and then run this command:

要对此进行试验,请将build-site目录复制为build-site-2 ,然后运行以下命令:

And then rewrite the scripts section to this:

然后将脚本部分重写为此:

$ cd build-site-2
$ npm install npm-run-all --save-dev

That’s a little cleaner. The build-site script is a little easier to read, and it has more room to grow since it's more compact. By itself those are small wins, but there is another feature of npm-run-all that will make it an even bigger win. Specifically: Wildcards

有点干净。 build-site脚本更易于阅读,并且由于更紧凑而具有更大的增长空间。 就其本身而言,这些都是小小的胜利,但是npm-run-all另一个功能将使它成为更大的胜利。 具体来说: 通配符

Change the script to this:

将脚本更改为此:

"scripts": {
"build-site": "npm-run-all build:less build:render build:deploy",
"build:less": "lessc site/style.less build/style.css",
"build:render": "mustache data.json site/index.mustache | html-minifier -o build/index.html",
"build:deploy": "cd build && rsync --archive --delete --verbose ./ user-name@remote-host.com:docroot-directory/"
},

That’s a little cleaner. The build-site script is a little easier to read, and it has more room to grow since it's more compact. By itself those are small wins, but there is another feature of npm-run-all that will make it an even bigger win. Specifically: Wildcards

有点干净。 build-site脚本更易于阅读,并且由于更紧凑而具有更大的增长空间。 就其本身而言,这些都是小小的胜利,但是npm-run-all另一个功能将使它成为更大的胜利。 具体来说: 通配符

Change the script to this:

将脚本更改为此:

"build-site": "npm-run-all build:*",

The build:* pattern matches, as you might expect, every script name starting with build.

如您所料, build:*模式匹配以build开头的每个脚本名称。

The way this works is that npm-run-all reads all the scripts from package.json and uses glob-style pattern matching to select the scripts to run. Using a single asterisk, it runs "sub scripts" of the patterns shown here, but it does not run "sub sub scripts" like build:render:index. To run sub-sub-script use the "globstar", or build:**.

这种工作方式是npm-run-allpackage.json读取所有脚本,并使用glob样式模式匹配来选择要运行的脚本。 它使用单个星号运行此处显示的模式的“子脚本”,但不运行build:render:index类的“子脚本”。 要运行子脚本,请使用“ globstar”或build:**

The resulting script execution looks like this:

结果脚本执行如下所示:

$ npm run build-site> build-site@1.0.0 build-site /Users/David/nodejs/npm-build-scripts/build-site-2
> npm-run-all build:*
> build-site@1.0.0 build:less /Users/David/nodejs/npm-build-scripts/build-site-2
> lessc site/style.less build/style.css
> build-site@1.0.0 build:render /Users/David/nodejs/npm-build-scripts/build-site-2
> mustache data.json site/index.mustache | html-minifier -o build/index.html
> build-site@1.0.0 build:deploy /Users/David/nodejs/npm-build-scripts/build-site-2
> cd build && rsync --archive --delete --verbose ./ user-name@remote-host.com:docroot-directory/

It’s about the same as before, but the build-site script is now much easier to manage. We can add or delete the scripts in the build group without having to then edit the build-site script.

它与以前几乎相同,但是现在更易于管理build-site脚本。 我们可以在build组中添加或删除脚本,而不必编辑build-site脚本。

并行脚本执行 (Parallel script execution)

With npm-run-all we're not limited to sequential execution, one step after another. It can also run the steps in parallel, which might be useful in some cases. To do that simply add the --parallel option to the command line.

使用npm-run-all我们不仅限于顺序执行,而是一步一步执行。 它还可以并行运行步骤,这在某些情况下可能很有用。 为此,只需在命令行中添加--parallel选项即可。

In our build-site example, the build steps are independent of one another and do not have to be run sequentially. That means we could add -parallel, like this:

在我们的build-site示例中,构建步骤彼此独立,不必顺序运行。 这意味着我们可以添加-parallel ,如下所示:

$ npm-run-all --parallel build:*

We’ll see in the next section a practical example of using this feature.

我们将在下一节中看到使用此功能的实际示例。

使用onchange更改文件时自动重建 (Automatically rebuilding when files change using onchange)

The simple “ build HTML page” scenario just described could go in several directions. One common direction to take is to automatically watch the source files, and rebuild any files that were changed. The coder would be editing the website code, save a change, the automated rebuild happens, and in a web browser they reload the screen. That’s the normal edit-build-test cycle of iterative development.

刚刚描述的简单的“ 构建HTML页面 ”场景可以从多个方向进行。 采取的一个常见方向是自动监视源文件,并重建所有已更改的文件。 编码人员将在编辑网站代码,保存更改,自动重建,然后在Web浏览器中重新加载屏幕。 这是迭代开发的正常编辑-构建-测试周期。

For example, if this were an actual project the HTML page might include a Vue.js or React client to a back-end service. Any edit in the files related to the application should trigger a rebuild.

例如,如果这是一个实际项目,则HTML页面可能包含Vue.js或后端服务的React客户端。 与应用程序相关的文件中的任何编辑都应触发重建。

Let’s stop pretending and make some real files, but lets keep it simple and concise.

让我们停止假装并制作一些真实的文件,但让它简单明了。

Create a directory to work in:

创建一个工作目录:

$ mkdir build-site-3 
$ cd build-site-3
$ cp ../build-site-2/package.json .

That gives us a known starting point from the previous section. In the package.json delete the build:deploy script since we don't actually need it. Then install these packages:

这为上一节提供了一个已知的起点。 在package.json删除build:deploy脚本,因为我们实际上不需要它。 然后安装以下软件包:

The onchange package is a tool for watching sets of files and running commands based on changes to those files. We’ll use this to trigger autorebuilds. For documentation, see: https://www.npmjs.com/package/onchange

onchange软件包是一个工具,用于监视文件集并根据这些文件的更改运行命令。 我们将使用它来触发自动重建。 有关文档,请参见: https : //www.npmjs.com/package/onchange

The live-server package is a simple web server that supports automatically reloading the browser page when files change. For documentation, see: https://www.npmjs.com/package/live-server

实时服务器软件包是一个简单的Web服务器,它支持在文件更改时自动重新加载浏览器页面。 有关文档,请参阅: https : //www.npmjs.com/package/live-server

Before we see how to use those packages, lets create a few simple source files to play with.

在我们看到如何使用这些软件包之前,让我们创建一些简单的源文件来使用。

Create a site directory, and in that directory create a file named index.mustache:

创建一个site目录,然后在该目录中创建一个名为index.mustache的文件:

<html>
<head>
<title>{{ title }}</title>
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>{{ title }}</h1>
<form>
<label for="labelFibonum" class="form-label">{{ fibolabel }}</label>
<input type="text" class="form-control" id="inputFibonum">
</form>
<div id="fiboVal"></div>
<script src="/fibocalc.js"></script>
</body>
</html>

What we have is a trivial HTML page that we can process with Mustache to produce an index.html. There are a few text values that we'll pull from the JSON file. We'll use the JSON file as a simplistic I18N implementation, letting us store text strings in the JSON and substitute different JSON files for different languages. In fibocalc.js we will write a small JavaScript application to calculate Fibonacci values. The user will enter a number in the input field, and the code will see that value and calculate the corresponding Fibonacci number.

我们拥有的是一个简单HTML页面,可以使用Mustache处理它以生成index.html 。 我们将从JSON文件中提取一些文本值。 我们将JSON文件用作简化的I18N实现,让我们将文本字符串存储在JSON中,并将不同的JSON文件替换为不同的语言。 在fibocalc.js我们将编写一个小JavaScript应用程序来计算Fibonacci值。 用户将在输入字段中输入数字,代码将看到该值并计算相应的斐波纳契数。

In the parent directory, next to package.json, create a file named labels.en.json:

在父目录中,在package.json旁边,创建一个名为labels.en.json的文件:

{
"title": "Fibo Calculator",
"fiboLabel": "Enter a number to calculate Fibonacci number"
}

We’ll use this file as a simplistic internationalization (I18N) method. The values for title and fibolabel becomes the title and fibolabel variables when processing the template. Hence, when we run the mustache command, these values will be picked up from this file, and inserted into the rendered output. We'll also need to change the build:render script to reference labels.en.json instead of data.json.

我们将此文件用作简化的国际化(I18N)方法。 处理模板时, titlefibolabel的值成为titlefibolabel变量。 因此,当我们运行mustache命令时,将从这些文件中选取这些值,并将其插入渲染的输出中。 我们还需要将build:render脚本更改为引用labels.en.json而不是data.json

In the site directory add a file named fibocalc.js containing:

site目录中,添加一个名为fibocalc.js的文件, fibocalc.js包含:

document.addEventListener("DOMContentLoaded", function() {let input = document.getElementById('inputFibonum');
let display = document.getElementById('fiboVal');input.oninput = function(e) {
let entered = e.target.value;
let fibonum = Number.parseInt(entered);
if (Number.isNaN(fibonum)) {
display.textContent = `ERROR: Non-number entered ${entered}`;
} else {
let fiboval = fibonacciLoop(fibonum);
display.textContent = `Fibonacci ${fibonum} = ${fiboval}`;
}
} function fibonacciLoop(n) {
let fibos = [];
fibos[0] = 0;
fibos[1] = 1;
fibos[2] = 1;
for (let i = 3; i <= n; i++) {
fibos[i] = fibos[i-2] + fibos[i-1];
}
return fibos[n];
}
});

We’re using pure JavaScript/DOM code here instead of a framework like jQuery. The outer wrapper for this is equivalent to the jQuery $(document).ready construct, where the purpose is to wait for the page to finish loading before running the JavaScript.

我们在这里使用的是纯JavaScript / DOM代码,而不是jQuery之类的框架。 外部包装器等效于jQuery $(document).ready构造,其目的是在运行JavaScript之前等待页面完成加载。

In the JavaScript we get the handles for the #inputFibonum and #fiboVal elements. For #inputFibonum we set up an oninput handler function causing this function to be called for any character typed into the input field.

在JavaScript中,我们获得了#inputFibonum#fiboVal元素的句柄。 对于#inputFibonum我们设置了oninput处理函数,使输入到输入字段中的任何字符均可调用此函数。

In the handler we try to parse the text as an integer. If that fails we output an error message into the #fiboVal display area. Otherwise we run a simple Fibonacci calculation, and display the result in the display area.

在处理程序中,我们尝试将文本解析为整数。 如果失败,则将错误消息输出到#fiboVal显示区域。 否则,我们将运行一个简单的斐波那契计算,并将结果显示在显示区域中。

To round it out, add a file named style.less in site containing:

要对其进行完善, style.less在以下site中添加一个名为style.less的文件:

#fiboVal {
border: 3px dashed red;
}

This will throw a little border around the display area.

这将在显示区域周围引发一些边框。

We now have a simple little browser-side application, and a build process to render files from the site to build directory. Since we started this section talking about automatic rebuilds, lets take care of that.

现在,我们有一个简单的浏览器端小应用程序,以及一个将site文件呈现到build目录的构建过程。 自从我们开始讨论自动重建这一节以来,让我们来解决这一问题。

The onchange command takes a list of file name patterns to watch, and a command to run. For example:

onchange命令获取要监视的文件名模式的列表以及要运行的命令。 例如:

$ onchange 'site/**.less' -- npm run build:less

This will watch for a change to any .less file in the site directory, and run the build:less script.

这将监视site目录中任何.less文件的更改,并运行build:less脚本。

With that in mind, let’s change the scripts section in package.json to this:

考虑到这一点,让我们将package.jsonscripts部分更改为:

"scripts": {
"build-site": "npm-run-all build:*",
"build:less": "lessc site/style.less build/style.css",
"build:render": "mustache labels.en.json site/index.mustache | html-minifier -o build/index.html",
"build:js": "cp site/fibocalc.js build",
"preview": "cd build && live-server --port=3333 ",
"watch": "npm-run-all --parallel watch:* preview",
"watch:less": "onchange 'site/**.less' -- npm run build:less",
"watch:js": "onchange 'site/**.js' -- npm run build:js",
"watch:html": "onchange 'labels.en.json' 'site/**.mustache' -- npm run build:render"
},

The build scripts are pretty much as we had them before. We've added a build:js script which simply copies the file to the build directory. That script could do a lot more, for example use Babel to convert modern ES2019 code into ES5 code that runs on any browser.

build脚本几乎和我们之前一样。 我们添加了一个build:js脚本,该脚本仅将文件复制到构建目录。 该脚本可以做更多的事情,例如使用Babel将现代的ES2019代码转换为可在任何浏览器上运行的ES5代码。

The preview script runs the live-server tool inside the build directory. This is a simple web server that includes automatic live reload. It watches the files it is serving to web browsers, and upon detecting a change will cause the browser to reload the page.

preview脚本在build目录中运行live-server工具。 这是一个简单的Web服务器,其中包括自动实时重新加载。 它监视向网络浏览器提供的文件,并且在检测到更改后将导致浏览器重新加载页面。

Then we have the watch scripts. The watch:* scripts follow the pattern just mentioned, where the onchange command is watching certain files and then running the corresponding build:* script.

然后,我们有了watch脚本。 watch:*脚本遵循刚才提到的模式,其中onchange命令正在监视某些文件,然后运行相应的build:*脚本。

For example, run npm run watch:js then edit fibocalc.js. On every edit this is printed:

例如,运行npm run watch:js然后编辑fibocalc.js 。 在每次编辑时都会打印:

> build-site@1.0.0 build:js /Users/David/nodejs/npm-build-scripts/build-site-3 
> site/fibocalc.js build

In other words, on every edit the build:js script is executed, just as the watch:js script says will happen.

换句话说,在每次编辑时都会执行build:js脚本,就像watch:js脚本所说的那样。

The watch script itself uses the npm-run-all --parallel option. What this does is to simultaneously run every watch:* script, plus the preview script. That way we have automatic rebuilds of the source files, plus automatic browser reloading.

watch脚本本身使用npm-run-all --parallel选项。 这是为了同时运行每个watch:*脚本和preview脚本。 这样,我们就可以自动重建源文件,以及自动重新加载浏览器。

Then, we can run this:

然后,我们可以运行以下命令:

$ npm run watch> build-site@1.0.0 watch /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> npm-run-all --parallel watch:* preview
> build-site@1.0.0 watch:less /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> onchange 'site/**.less' -- npm run build:less
> build-site@1.0.0 watch:html /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> onchange 'labels.en.json' 'site/**.mustache' -- npm run build:render
> build-site@1.0.0 watch:js /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> onchange 'site/**.js' -- npm run build:js
> build-site@1.0.0 preview /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> cd build && live-server --port=3333 Serving "/Volumes/Extra/nodejs/npm-build-scripts/build-site-3/build" at http://127.0.0.1:3333
Ready for changes

This starts the scripts running in parallel. The live-server process is what tells us it is ready for changes.

这将启动并行运行的脚本。 live-server过程告诉我们它已准备好进行更改。

The live-server also automatically opens a browser tab, and you'll see this:

live-server还会自动打开浏览器选项卡,您将看到以下内容:

Image for post

Well, you’ll see that after entering 55 in the input field.

好吧,在输入字段中输入55后,您会看到。

Now, edit site/fibocalc.js with this change:

现在,使用以下更改编辑site/fibocalc.js

display.textContent = `Fibonacci CHANGE ${fibonum} = ${fiboval}`;

As soon as you save the change, this prints in your terminal:

保存更改后,这将立即在您的终端中显示:

> build-site@1.0 .0 build:js /Volumes/Extra/nodejs/npm-build-scripts/build-site-3 
> cp site/fibocalc.js build Change detected /Volumes/Extra/nodejs/npm-build-scripts/build-site-3/build/fibocalc.js

First, onchange detected that the JavaScript file changed, and ran the build:js script. Second, the live-server detected that the site/fibocalc.js changed, and caused the browser to reload. Enter a new number to calculate, and the message changes to this:

首先, onchange检测到JavaScript文件已更改,然后运行build:js脚本。 其次, live-server检测到site/fibocalc.js更改,并导致浏览器重新加载。 输入一个新的数字进行计算,消息变为:

Fibonacci CHANGE 55 = 139583862445

Back out that change, the same automatic rebuild events occur, and the message changes back.

撤消该更改,将发生相同的自动重建事件,并且消息将更改回。

For example, edit labels.en.json with this change:

例如,使用以下更改编辑labels.en.json

"title": "Fibo Calculator - Hi Mom!",

The change is detected, and the following runs:

检测到更改,然后运行以下命令:

> build-site@1.0.0 build:render /Volumes/Extra/nodejs/npm-build-scripts/build-site-3
> mustache labels.en.json site/index.mustache | html-minifier -o build/index.htmlChange detected /Volumes/Extra/nodejs/npm-build-scripts/build-site-3/build/index.html

And then, as if by magic, the browser shows the message: Fibo Calculator — Hi Mom! Make another change, those commands run again, and the browser automatically updates to match.

然后,就像魔术一样,浏览器显示以下消息: Fibo Calculator-嗨,妈妈! 进行其他更改,这些命令将再次运行,浏览器将自动更新以匹配。

What we’ve demonstrated in this and the previous section is that npm-run-all lets us build a multidimensional hierarchy of script executions in a package.json file. Okay, maybe calling it multidimensional is silly, but we do have lots of freedom in two dimensions to construct complex command execution hierarchies. That's because we can handle both parallel and sequential task execution.

在本节和上一节中,我们已经证明了npm-run-all可以让我们在package.json文件中构建脚本执行的多维层次结构。 好的,也许称它为多维是愚蠢的,但是我们确实在二维上有很多自由来构建复杂的命令执行层次结构。 这是因为我们可以处理并行和顺序任务执行。

npm配置设置和环境变量 (npm configuration settings and environment variables)

Another feature of npm is the configuration settings. I don’t know if what I’m about to show works the same in Yarn. We already looked at a couple configuration settings, and saw it used for customizing npm’s behavior. An interesting detail is that the configuration values are available in package.json scripts as environment variables.

npm的另一个功能是配置设置。 我不知道我要显示的内容在Yarn中是否也一样。 我们已经查看了几个配置设置,并看到它用于自定义npm的行为。 一个有趣的细节是,配置值可在package.json脚本中用作环境变量。

Before starting this section, run the following commands:

在开始本节之前,请运行以下命令:

$ mkdir build-site-4 
$ -r build-site-3/site build-site-3/package.json build-site-3/.gitignore build-site-4
$ cd build-site-4
$ npm install

This gives us a clean working area in which to do the following experimentation.

这为我们提供了一个干净的工作区域,可以在其中进行以下实验。

When npm executes a script, it sets a lot of values in its environment. Every field in package.json is set as an environment variable beginning with npm_package_. We can explore this by adding the following to the scripts section:

当npm执行脚本时,它会在其环境中设置很多值。 package.json每个字段都设置为以npm_package_开头的环境变量。 我们可以通过在scripts部分添加以下内容来进行探索:

"env": "env"

The env script lets you see the variables that are available. Run this command and you'll see that many have the prefix npm_config_

使用env脚本,您可以查看可用的变量。 运行此命令,您将看到许多前缀为npm_config_

Let’s consider two things in our build scripts which we might want to override. The first is the file labels.en.json which we suggested was a simplistic internationalization implementation. For that purpose we would have parallel files named labels.ro.json (Romanian) or labels.fr.json (French). The second configuration value is the port number to use for the preview server.

让我们考虑一下构建脚本中可能要覆盖的两件事。 第一个是文件labels.en.json ,我们建议这是一个简化的国际化实现。 为此,我们将具有名为labels.ro.json (罗马尼亚语)或labels.fr.json (法语)的并行文件。 第二个配置值是用于预览服务器的端口号。

Let’s focus on the latter.

让我们专注于后者。

In package.json we can put values like this:

package.json我们可以输入如下值:

"config" : { 
"port" : "8080"
}

And we can see it in the environment:

我们可以在环境中看到它:

$ npm run env | grep port...
npm_package_config_port=8080
...

This means entries in the config section of package.json show up as environment variables with the prefix npm_package_config_.

这意味着package.jsonconfig部分中的条目显示为带有前缀npm_package_config_环境变量。

This value can be overridden by running this command:

可以通过运行以下命令来覆盖此值:

npm config set build-site:port 9090

The npm config command sets configuration values, and we can set our own values if desired. Any values we set become environment variables in executed scripts.

npm config命令设置配置值,如果需要,我们可以设置自己的值。 我们设置的任何值都将成为执行脚本中的环境变量。

$ npm config get build-site:port 
9090
$ npm run env | grep port
...
npm_config_build_site_port=9090
npm_package_config_port=9090
...

This actually sets two environment variables, which is interesting. But the important thing is that npm_package_config_port was overridden. Inside package.json the value is 8080 but we've now set it to 9090.

这实际上设置了两个环境变量,这很有趣。 但是重要的是npm_package_config_port已被覆盖。 在package.json内部,值为8080但现在将其设置为9090

The prefix build-site: is because that's the value of the name field in the package.json. If we leave off the prefix, in other words run npm config set port 9090, then a different environment variable ( npm_config_port) gets set instead. In order to override npm_package_config_port we must use the prefix.

前缀build-site:是因为这是package.json name字段的值。 如果我们npm_config_port前缀,换句话说,运行npm config set port 9090 ,那么将设​​置另一个环境变量( npm_config_port )。 为了覆盖npm_package_config_port我们必须使用前缀。

That means we can now make this change in the preview script:

这意味着我们现在可以在preview脚本中进行此更改:

"preview": "cd build && live-server --port=${npm_package_config_port} "

We changed this from the hard-coded value used earlier, to one referencing this environment variable. Then, we run this command:

我们将其从先前使用的硬编码值更改为引用此环境变量的值。 然后,我们运行以下命令:

$ npm run preview > build-site@ 1.0 .0 preview /Volumes/Extra/nodejs/npm-build-scripts/build-site-4 
> cd build && live-server --port=${npm_package_config_port} Serving "/Volumes/Extra/nodejs/npm-build-scripts/build-site-4/build" at http:
Ready for changes

And indeed, the preview server is listening on port 9090.

实际上,预览服务器正在侦听端口9090。

Then if we want to erase the override:

然后,如果我们要删除覆盖:

$ npm config delete build-site:port 
$ npm config get build-site:port
undefined
$ npm run env | grep port ...
npm_package_config_port=8080

As soon as we delete the configuration value, the environment variable reverts to the value set in the config tag of the package.json file. Run npm run preview again, and the preview server opens on port 8080.

删除配置值后,环境变量将恢复为package.json文件的config标记中设置的值。 再次运行npm run preview ,并且预览服务器在端口8080上打开。

用于package.json脚本的附加工具 (Additional tools for package.json scripts)

There are a large number of tools available for use with scripts in package.json files. We've seen so far that this can be very powerful, and these other tools add even more capabilities.

package.json文件中的脚本可使用大量工具。 到目前为止,我们已经看到它可以非常强大,并且这些其他工具添加了更多功能。

Cross platform scripts: In the previous section we added an environment variable reference to one of the scripts. While this is nice and easy, it does not work on Windows since cmd.exe has a different syntax for variable references. There are two packages that cover different aspects of erasing cross platform differences.

跨平台脚本 :在上一节中,我们向其中一个脚本添加了环境变量引用。 尽管这很方便,但由于cmd.exe对变量引用的语法不同,因此在Windows上不起作用。 有两个软件包涵盖了消除跨平台差异的不同方面。

Cross platform equivalents to Unix commands: It is a best practice for your package.json scripts to support execution on Windows as well as Unix-like systems. There are times you want to run normal commands like rm -rf build in a script, but of course Windows doesn't have that command.

跨平台等效于Unix命令 :最佳做法是package.json脚本支持在Windows以及类似Unix的系统上执行。 有时您想在脚本中运行rm -rf build类的普通命令,但是Windows当然没有该命令。

Integrate with Git hooks using Husky: The Husky package lets you add a section to package.json specifying commands that will execute for certain Git lifecycle hooks. A Git hook is a shell script conventionally stored in .git/hooks corresponding to different points of time in the lifecycle of a Git repository. For example you might use a pre-commit hook to run a few tests to guard against committing bad code.

使用Husky与Git挂钩集成 :使用Husky包,您可以在package.json添加一个部分,以指定将对某些Git生命周期挂钩执行的命令。 Git挂钩是通常存储在.git/hooks的Shell脚本,对应于Git存储库生命周期中的不同时间点。 例如,您可以使用pre-commit挂钩运行一些测试,以防止提交错误的代码。

Code development tools: There are a long list of software development tools available on the Node.js platform. We touched on a couple, like the Less compiler, but here’s a few more.

代码开发工具 :Node.js平台上有很多软件开发工具。 我们接触了一些,如Less编译器,但这里还有更多。

摘要 (Summary)

In this article we’ve seen that the package.json has a hidden superpower that can be used for any data processing task.

在本文中,我们已经看到package.json具有隐藏的超级能力,可以用于任何数据处理任务。

There are close to a zillion different possible projects involving input data, whether it be source code, HTML/CSS files, data files, datasets for machine learning projects, weather report data, or anything else. The input data would be processed using tools required for the project, producing an output, which could be a website, a software package, a machine learning model, or anything else.

涉及输入数据的项目有近千种,无论是源代码,HTML / CSS文件,数据文件,机器学习项目的数据集,天气报告数据还是其他任何项目。 输入数据将使用项目所需的工具进行处理,产生输出,该输出可以是网站,软件包,机器学习模型或其他任何东西。

While there are many kinds of build tools we could use for automating such projects, the package.json file is possibly already installed on your computer, and is up to the task.

虽然有许多种构建工具可用于使此类项目自动化,但package.json文件可能已经安装在您的计算机上,并且可以完成任务。

Originally published at https://techsparx.com.

最初在 https://techsparx.com上 发布

翻译自: https://levelup.gitconnected.com/how-to-use-npm-yarn-node-js-package-json-scripts-as-your-build-tool-f4bf87e85af5

使用npm安装yarn

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值