在过去的几年中,我花了很大一部分时间在nodejs编写脚本作为我的默认脚本语言。我也有点涉足bash,但是它仍然很奇怪,很难测试,而且我是一个打字稿的风扇,我通常更喜欢尽可能多地走打字稿。
下面是使编写nodejs脚本变得有趣,快速且容易的技巧列表。
使用ES模块
是!您现在可以做到!但是,thar be dragons。
一般而言,如果要在节点中使用ES模块,则可以...
- 用
.mjs
在其末尾命名您的文件 - 在最接近
package.json
具有"type": "module"
的地方使用 - 使用
node --eval
并通过--input-type=module
.js
文件
类似的规则适用于保留文件使用commonjs
- 用
.cjs
命名您的文件7 - 在最接近的
package.json
具有"type": "commonjs"
的地方使用 - 使用
node --eval
并通过--input-type=commonjs
.js
文件
有几个条款,a,一对 quid pro pro
切换到节点中的ES模块时,必须:
- 始终将扩展名包括在您的导入中...
import { foo } from "./bar.js";
- 您必须使用
import()
而不是require
- 这不起作用...
__dirname
但是,这确实...
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
您可能还会遇到您导入的node_modules
的某些 问题,因此,请小心。但是,如果您通常坚持本地节点模块和实用程序,那么您应该很好。
使用顶级等待
这在14.8中可用。在最长的时间里,每次我准备编写一个node.js脚本...
时,我总是写一个包装器
const main = async () => {
// await some stuff in here
};
main().catch((err) => {
console.error(err);
process.exit(1);
});
我经常这样做,以至于我为此编写了Vscode摘要。嘿。
但是,很酷的事情是,您现在只能在JS文件中的任何地方await
。
注意:您只能在“模块”上下文中执行此操作,即请参见上面的“使用ES模块”
import fs from 'node:fs/promises';
const pkg = await fs.readFile('./package.json');
console.log(JSON.parse(pkg).name);
将基于新的承诺的API与节点前缀一起使用
从节点v14.18和v16开始,您现在可以要求 andation node模块,即path
,fs
,etc et in inde inde indimptim或需要声明。这将阻止剧本作家意外进口的实用程序第三方版本。
import fs from "node:fs/promises";
import stream from "node:stream/promises";
import dns from "node:dns/promises";
import timers from "node:timers/promises";
另外,对于采用标准节点(err, results) => {}
回调的所有其他所有内容,您始终可以回到promisify
方法上。
import fs from 'node:fs/promises';
import { exec as _exec } from 'node:child_process';
import { promisify } from 'node:util';
const exec = promisify(_exec);
const out = await exec('git status');
console.log(out.stdout);
有效地使用child_process
child_process
模块是脚本编写者背包中最有用的模块之一。您可以使用其spawn
或exec
方法在计算机上运行任何任意内容。但是,知道何时以及如何使用其中任何一个都是必不可少的。
spawn
和exec
之间的最根本区别是...
spawn
创建并返回一个子过程,您可以通过流轻松地与之交互
exec
创建A运行A子过程,以最大为200KB
因此,大多数时间exec
都会执行您需要的事情,但是如果您需要某种交互性或具有一堆输出,则可以使用spawn
。
上面的以前示例之一使用exec
调用git status
。另一个示例可能是对像find
这样的unix
方法的抽象。
import fs from 'node:fs/promises';
import { exec as _exec } from 'node:child_process';
import { promisify } from 'node:util';
const exec = promisify(_exec);
const [,, filename, directory] = process.argv;
const out = await exec(`find ${directory} -type f -name ${filename} `);
console.log(out.stdout);
// run with...
// node ./script.mjs filename ./directory
这是使用spawn
做某事的示例。
在这种情况下,我们将在我们的存储库中的另一个软件包中生成一个npm install
。
import { spawn } from 'node:child_process';
const install = () => {
return new Promise((resolve, reject) => {
const proc = spawn('npm', ['install'], {
stdio: ['inherit', 'pipe', 'pipe'],
env: {
...process.env,
}
});
proc.stdout.on('data', (data) => {
if (data.includes('npm WARN')) {
// Do something
}
});
proc.stderr.on('data', (data) => {
console.error(`stderr: ${data}`);
});
proc.on('close', (code) => {
if (code !== 0) {
return reject(code);
}
resolve();
});
});
};
try {
await install();
} catch(e) {
console.error("Something went wrong with the install.");
process.exit(1);
}
首先要注意的是,我们要创建一个函数,并在return
中使用Promise
构造函数。这样我们就可以在顶层进行await
。
让我们看一下我们传递给spawn
的选项。 (您可以看到Spawn here的整个API)第一个是顶级命令,在这种情况下为npm
。第二个选项是我们要传递给npm
的参数的数组,只是install
。但是,如果我们想将多个事物传递给npm
,我们需要将它们添加为每个元素,例如['install', '--save-dev', '--legacy-peer-deps']
。
第三个参数是options
对象。默认情况下,当您产生一个过程时,stdio
选项设置为pipe
。
pipe
选项意味着从spawn
返回的proc
将在其上具有proc.stdout
,proc.stdin
和proc.stderr
。这些是streams
,因此您可以使用on
和one
等方法来响应data
等事件。
stdin
参数可以采用pipe
,inherit
或与您要控制的过程的哪个部分相对应的数组...
如果您不需要需要使用stdin
或stderr
,则可以从pipe
切换到inherit
。
stdin: [
'inherit' /* stdin */,
'pipe' /* stdout */,
'inherit' /* stderr */
],
像那样传递stdin
ð会提供proc.stdout
,但不提供proc.stdin
或proc.stderr
。
最后,您可以注意要使用proc.on('close', () => {});
关闭的过程。
另外一个不错的选择是,您可以通过cwd
选项控制该过程的哪个工作目录。这使您可以使用process.cwd()
,或设置一些实际想要产生该过程的其他路径。
使用Typescript进行更酷的脚本写作体验
通常,我更喜欢在这些天(包括脚本)编写任何内容时使用Typescript。编写这样的脚本时,有几种不同的方式可以用来利用Typescript在代码中的额外的检查毯子。
首先,如果您要使用打字稿和节点,请确保将@types/node
软件包安装为"devDependency"
。这将确保所有本机模块都有正确的类型信息。
本身就是超级有用的。
真正值得注意的是,您不必将文件以.ts
扩展为止。您可以为tsconfig.json
添加几个选项。它们是"allowJs"
和"checkJs"
。将它们都设置为真,您就可以了。
typeScript与4.7 release一起使用,现在也通过读取可以添加到package.json
文件中的"type": "module"
来支持ES模块。您还可以通过将"module"
选项设置为node16
或nodenext
来手动告诉Typescript使用模块。所有这些记录的here。
如果您想使用.ts
而不是使用允许J和CheckJS,则可能还需要安装诸如@babel/register
或ts-node
之类的内容,甚至可能是esbuild-runner。我仍然个人喜欢Babel route。
结论
在Frontend和Devops(又名DivOps)中,编写脚本是不可避免的。在JavaScript / TypeScript中编写脚本可让您使用所具有的JS技能作为前端开发人员与计算机进行交互,以执行各种自动化,管道等和奖励点,因为您也可以编写单元测试。出去并在JavaScript中写下所有脚本!