feat: 添加 execa 、semver 文档
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
export const nodejsNavItem = [
|
||||
{
|
||||
text: "nodejs",
|
||||
items: [
|
||||
{
|
||||
text: "prompt(命令行输入交互)",
|
||||
link: "/note/nodejs/prompts",
|
||||
},
|
||||
{
|
||||
text: "semver(版本号控制)",
|
||||
link: "/note/nodejs/semver",
|
||||
},
|
||||
{
|
||||
text: "execa(命令运行工具)",
|
||||
link: "/note/nodejs/execa",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
@@ -1,12 +1,3 @@
|
||||
// VitePress技术笔记左侧导航栏
|
||||
export const nodejs = [
|
||||
{
|
||||
text: "nodejs 相关库",
|
||||
items: [
|
||||
{
|
||||
text: "1. prompt 库",
|
||||
link: "/note/nodejs/prompts",
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
import { nodejsNavItem } from "../../common/nodejsNavItem.ts";
|
||||
export const nodejs = [...nodejsNavItem];
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { nodejsNavItem } from "../common/nodejsNavItem.ts";
|
||||
|
||||
export default [
|
||||
{
|
||||
text: "组件展示",
|
||||
@@ -16,10 +18,7 @@ export default [
|
||||
text: "技术笔记",
|
||||
items: [{ text: "VitePress", link: "/note/vitePress" }],
|
||||
},
|
||||
{
|
||||
text: "nodejs",
|
||||
items: [{ text: "prompt", link: "/note/nodejs/prompts" }],
|
||||
},
|
||||
...nodejsNavItem,
|
||||
{
|
||||
text: "关于我们",
|
||||
link: "/about/page",
|
||||
|
||||
@@ -0,0 +1,383 @@
|
||||
# [execa](https://github.com/sindresorhus/execa)
|
||||
|
||||
Execa是为脚本、应用和库运行命令的工具,与shell不同,它专为程序化使用进行了优化。基于Node.js的核心模块child_process构建。
|
||||
|
||||
## Install
|
||||
|
||||
```sh
|
||||
npm install execa
|
||||
```
|
||||
|
||||
## Documentation
|
||||
|
||||
Execution:
|
||||
|
||||
- ▶️ [Basic execution](docs/execution.md)
|
||||
- 💬 [Escaping/quoting](docs/escaping.md)
|
||||
- 💻 [Shell](docs/shell.md)
|
||||
- 📜 [Scripts](docs/scripts.md)
|
||||
- 🐢 [Node.js files](docs/node.md)
|
||||
- 🌐 [Environment](docs/environment.md)
|
||||
- ❌ [Errors](docs/errors.md)
|
||||
- 🏁 [Termination](docs/termination.md)
|
||||
|
||||
Input/output:
|
||||
|
||||
- 🎹 [Input](docs/input.md)
|
||||
- 📢 [Output](docs/output.md)
|
||||
- 📃 [Text lines](docs/lines.md)
|
||||
- 🤖 [Binary data](docs/binary.md)
|
||||
- 🧙 [Transforms](docs/transform.md)
|
||||
|
||||
Advanced usage:
|
||||
|
||||
- 🔀 [Piping multiple subprocesses](docs/pipe.md)
|
||||
- ⏳️ [Streams](docs/streams.md)
|
||||
- 📞 [Inter-process communication](docs/ipc.md)
|
||||
- 🐛 [Debugging](docs/debugging.md)
|
||||
- 📎 [Windows](docs/windows.md)
|
||||
- 🔍 [Difference with Bash and zx](docs/bash.md)
|
||||
- 🤓 [TypeScript](docs/typescript.md)
|
||||
- 📔 [API reference](docs/api.md)
|
||||
|
||||
## Examples
|
||||
|
||||
### Execution
|
||||
|
||||
#### Simple syntax
|
||||
|
||||
```js
|
||||
import {execa} from 'execa';
|
||||
|
||||
const {stdout} = await execa`npm run build`;
|
||||
// Print command's output
|
||||
console.log(stdout);
|
||||
```
|
||||
|
||||
#### Script
|
||||
|
||||
```js
|
||||
import {$} from 'execa';
|
||||
|
||||
const {stdout: name} = await $`cat package.json`.pipe`grep name`;
|
||||
console.log(name);
|
||||
|
||||
const branch = await $`git branch --show-current`;
|
||||
await $`dep deploy --branch=${branch}`;
|
||||
|
||||
await Promise.all([
|
||||
$`sleep 1`,
|
||||
$`sleep 2`,
|
||||
$`sleep 3`,
|
||||
]);
|
||||
|
||||
const directoryName = 'foo bar';
|
||||
await $`mkdir /tmp/${directoryName}`;
|
||||
```
|
||||
|
||||
#### Local binaries
|
||||
|
||||
```sh
|
||||
npm install -D eslint
|
||||
```
|
||||
|
||||
```js
|
||||
await execa({preferLocal: true})`eslint`;
|
||||
```
|
||||
|
||||
#### Pipe multiple subprocesses
|
||||
|
||||
```js
|
||||
const {stdout, pipedFrom} = await execa`npm run build`
|
||||
.pipe`sort`
|
||||
.pipe`head -n 2`;
|
||||
|
||||
// Output of `npm run build | sort | head -n 2`
|
||||
console.log(stdout);
|
||||
// Output of `npm run build | sort`
|
||||
console.log(pipedFrom[0].stdout);
|
||||
// Output of `npm run build`
|
||||
console.log(pipedFrom[0].pipedFrom[0].stdout);
|
||||
```
|
||||
|
||||
### Input/output
|
||||
|
||||
#### Interleaved output
|
||||
|
||||
```js
|
||||
const {all} = await execa({all: true})`npm run build`;
|
||||
// stdout + stderr, interleaved
|
||||
console.log(all);
|
||||
```
|
||||
|
||||
#### Programmatic + terminal output
|
||||
|
||||
```js
|
||||
const {stdout} = await execa({stdout: ['pipe', 'inherit']})`npm run build`;
|
||||
// stdout is also printed to the terminal
|
||||
console.log(stdout);
|
||||
```
|
||||
|
||||
#### Simple input
|
||||
|
||||
```js
|
||||
const getInputString = () => { /* ... */ };
|
||||
const {stdout} = await execa({input: getInputString()})`sort`;
|
||||
console.log(stdout);
|
||||
```
|
||||
|
||||
#### File input
|
||||
|
||||
```js
|
||||
// Similar to: npm run build < input.txt
|
||||
await execa({stdin: {file: 'input.txt'}})`npm run build`;
|
||||
```
|
||||
|
||||
#### File output
|
||||
|
||||
```js
|
||||
// Similar to: npm run build > output.txt
|
||||
await execa({stdout: {file: 'output.txt'}})`npm run build`;
|
||||
```
|
||||
|
||||
#### Split into text lines
|
||||
|
||||
```js
|
||||
const {stdout} = await execa({lines: true})`npm run build`;
|
||||
// Print first 10 lines
|
||||
console.log(stdout.slice(0, 10).join('\n'));
|
||||
```
|
||||
|
||||
### Streaming
|
||||
|
||||
#### Iterate over text lines
|
||||
|
||||
```js
|
||||
for await (const line of execa`npm run build`) {
|
||||
if (line.includes('WARN')) {
|
||||
console.warn(line);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Transform/filter output
|
||||
|
||||
```js
|
||||
let count = 0;
|
||||
|
||||
// Filter out secret lines, then prepend the line number
|
||||
const transform = function * (line) {
|
||||
if (!line.includes('secret')) {
|
||||
yield `[${count++}] ${line}`;
|
||||
}
|
||||
};
|
||||
|
||||
await execa({stdout: transform})`npm run build`;
|
||||
```
|
||||
|
||||
#### Web streams
|
||||
|
||||
```js
|
||||
const response = await fetch('https://example.com');
|
||||
await execa({stdin: response.body})`sort`;
|
||||
```
|
||||
|
||||
#### Convert to Duplex stream
|
||||
|
||||
```js
|
||||
import {execa} from 'execa';
|
||||
import {pipeline} from 'node:stream/promises';
|
||||
import {createReadStream, createWriteStream} from 'node:fs';
|
||||
|
||||
await pipeline(
|
||||
createReadStream('./input.txt'),
|
||||
execa`node ./transform.js`.duplex(),
|
||||
createWriteStream('./output.txt'),
|
||||
);
|
||||
```
|
||||
|
||||
### IPC
|
||||
|
||||
#### Exchange messages
|
||||
|
||||
```js
|
||||
// parent.js
|
||||
import {execaNode} from 'execa';
|
||||
|
||||
const subprocess = execaNode`child.js`;
|
||||
await subprocess.sendMessage('Hello from parent');
|
||||
const message = await subprocess.getOneMessage();
|
||||
console.log(message); // 'Hello from child'
|
||||
```
|
||||
|
||||
```js
|
||||
// child.js
|
||||
import {getOneMessage, sendMessage} from 'execa';
|
||||
|
||||
const message = await getOneMessage(); // 'Hello from parent'
|
||||
const newMessage = message.replace('parent', 'child'); // 'Hello from child'
|
||||
await sendMessage(newMessage);
|
||||
```
|
||||
|
||||
#### Any input type
|
||||
|
||||
```js
|
||||
// main.js
|
||||
import {execaNode} from 'execa';
|
||||
|
||||
const ipcInput = [
|
||||
{task: 'lint', ignore: /test\.js/},
|
||||
{task: 'copy', files: new Set(['main.js', 'index.js']),
|
||||
}];
|
||||
await execaNode({ipcInput})`build.js`;
|
||||
```
|
||||
|
||||
```js
|
||||
// build.js
|
||||
import {getOneMessage} from 'execa';
|
||||
|
||||
const ipcInput = await getOneMessage();
|
||||
```
|
||||
|
||||
#### Any output type
|
||||
|
||||
```js
|
||||
// main.js
|
||||
import {execaNode} from 'execa';
|
||||
|
||||
const {ipcOutput} = await execaNode`build.js`;
|
||||
console.log(ipcOutput[0]); // {kind: 'start', timestamp: date}
|
||||
console.log(ipcOutput[1]); // {kind: 'stop', timestamp: date}
|
||||
```
|
||||
|
||||
```js
|
||||
// build.js
|
||||
import {sendMessage} from 'execa';
|
||||
|
||||
const runBuild = () => { /* ... */ };
|
||||
|
||||
await sendMessage({kind: 'start', timestamp: new Date()});
|
||||
await runBuild();
|
||||
await sendMessage({kind: 'stop', timestamp: new Date()});
|
||||
```
|
||||
|
||||
#### Graceful termination
|
||||
|
||||
```js
|
||||
// main.js
|
||||
import {execaNode} from 'execa';
|
||||
|
||||
const controller = new AbortController();
|
||||
setTimeout(() => {
|
||||
controller.abort();
|
||||
}, 5000);
|
||||
|
||||
await execaNode({
|
||||
cancelSignal: controller.signal,
|
||||
gracefulCancel: true,
|
||||
})`build.js`;
|
||||
```
|
||||
|
||||
```js
|
||||
// build.js
|
||||
import {getCancelSignal} from 'execa';
|
||||
|
||||
const cancelSignal = await getCancelSignal();
|
||||
const url = 'https://example.com/build/info';
|
||||
const response = await fetch(url, {signal: cancelSignal});
|
||||
```
|
||||
|
||||
### Debugging
|
||||
|
||||
#### Detailed error
|
||||
|
||||
```js
|
||||
import {execa, ExecaError} from 'execa';
|
||||
|
||||
try {
|
||||
await execa`unknown command`;
|
||||
} catch (error) {
|
||||
if (error instanceof ExecaError) {
|
||||
console.log(error);
|
||||
}
|
||||
/*
|
||||
ExecaError: Command failed with ENOENT: unknown command
|
||||
spawn unknown ENOENT
|
||||
at ...
|
||||
at ... {
|
||||
shortMessage: 'Command failed with ENOENT: unknown command\nspawn unknown ENOENT',
|
||||
originalMessage: 'spawn unknown ENOENT',
|
||||
command: 'unknown command',
|
||||
escapedCommand: 'unknown command',
|
||||
cwd: '/path/to/cwd',
|
||||
durationMs: 28.217566,
|
||||
failed: true,
|
||||
timedOut: false,
|
||||
isCanceled: false,
|
||||
isTerminated: false,
|
||||
isMaxBuffer: false,
|
||||
code: 'ENOENT',
|
||||
stdout: '',
|
||||
stderr: '',
|
||||
stdio: [undefined, '', ''],
|
||||
pipedFrom: []
|
||||
[cause]: Error: spawn unknown ENOENT
|
||||
at ...
|
||||
at ... {
|
||||
errno: -2,
|
||||
code: 'ENOENT',
|
||||
syscall: 'spawn unknown',
|
||||
path: 'unknown',
|
||||
spawnargs: [ 'command' ]
|
||||
}
|
||||
}
|
||||
*/
|
||||
}
|
||||
```
|
||||
|
||||
#### Verbose mode
|
||||
|
||||
```js
|
||||
await execa`npm run build`;
|
||||
await execa`npm run test`;
|
||||
```
|
||||
|
||||
<!-- <img alt="execa verbose output" src="media/verbose.png" width="603"> -->
|
||||
|
||||
#### Custom logging
|
||||
|
||||
```js
|
||||
import {execa as execa_} from 'execa';
|
||||
import {createLogger, transports} from 'winston';
|
||||
|
||||
// Log to a file using Winston
|
||||
const transport = new transports.File({filename: 'logs.txt'});
|
||||
const logger = createLogger({transports: [transport]});
|
||||
const LOG_LEVELS = {
|
||||
command: 'info',
|
||||
output: 'verbose',
|
||||
ipc: 'verbose',
|
||||
error: 'error',
|
||||
duration: 'info',
|
||||
};
|
||||
|
||||
const execa = execa_({
|
||||
verbose(verboseLine, {message, ...verboseObject}) {
|
||||
const level = LOG_LEVELS[verboseObject.type];
|
||||
logger[level](message, verboseObject);
|
||||
},
|
||||
});
|
||||
|
||||
await execa`npm run build`;
|
||||
await execa`npm run test`;
|
||||
```
|
||||
|
||||
## Related
|
||||
|
||||
- [gulp-execa](https://github.com/ehmicky/gulp-execa) - Gulp plugin for Execa
|
||||
- [nvexeca](https://github.com/ehmicky/nvexeca) - Run Execa using any Node.js version
|
||||
|
||||
## Maintainers
|
||||
|
||||
- [Sindre Sorhus](https://github.com/sindresorhus)
|
||||
- [@ehmicky](https://github.com/ehmicky)
|
||||
@@ -0,0 +1,38 @@
|
||||
# [semver](https://www.npmjs.com/package/semver)
|
||||
|
||||
语义化版本控制库,[详细解读参考](https://www.wuzao.com/document/30-seconds-of-code/js/semantic-versioning/)
|
||||
|
||||
1. 安装
|
||||
|
||||
```bash
|
||||
npm i semver
|
||||
```
|
||||
|
||||
{major}.{minor}.{patch}
|
||||
|
||||
每个组件代表对软件所做的特定类型的更改。
|
||||
|
||||
- **主要版本**:重大更改,可能会**破坏兼容性**。开发人员在升级之前应仔细阅读文档并针对新版本测试其代码。
|
||||
- **次要版本**:向后兼容的**添加或改进**,不会破坏与之前版本的兼容性。用户通常可以升级到新的次要版本,而不必担心可能需要修改代码的重大更改。
|
||||
- **补丁版本**:向后兼容的**错误修复、补丁或维护**发布。补丁版本旨在安全,不应引入新功能或破坏性更改。
|
||||
|
||||
以下表格总结了每个组件所代表的不同类型的更改:
|
||||
|
||||
| 组件 | 更改类型 | 示例 |
|
||||
| --------- | ------------ | ------------------------------------------ |
|
||||
| 主要版本 | 不兼容 | 破坏性更改、重写、架构更改 |
|
||||
| 次要版本 | 兼容 | 新功能、功能增强 |
|
||||
| 补丁版本 | 兼容 | 错误修复、补丁、维护发布 |
|
||||
|
||||
## 发布和预发布版本
|
||||
|
||||
软件包的**第一个版本**通常被标记为`1.0.0`。这是因为软件包的初始发布被认为是一个主要版本,而主要版本的第一个版本总是`1.0.0`。以`0.x.x`开头的版本被认为是预发布版本,不适用于生产环境。
|
||||
|
||||
此外,SemVer允许在版本号后附加**预发布版本**。这些版本由连字符和一系列字母数字标识符组成,例如`1.0.0-alpha.1`或`1.0.0-beta.2`。预发布版本通常用于表示软件仍在积极开发中,可能还不适用于生产环境。
|
||||
|
||||
## 指定要使用的版本
|
||||
|
||||
在安装软件包时,您可以通过将版本号附加到软件包名称来指定要使用的版本,如下所示:
|
||||
|
||||
```shell
|
||||
npm install my-package@1.0.0
|
||||
@@ -1,43 +1,43 @@
|
||||
const prompts = require('prompts');
|
||||
|
||||
// 单个问题
|
||||
// (async () => {
|
||||
// const response = await prompts({
|
||||
// type: 'number',
|
||||
// name: 'value',
|
||||
// message: 'How old are you?',
|
||||
// validate: value => value < 18 ? `Nightclub is 18+ only` : true
|
||||
// });
|
||||
(async () => {
|
||||
const response = await prompts({
|
||||
type: 'number',
|
||||
name: 'value',
|
||||
message: 'How old are you?',
|
||||
validate: value => value < 18 ? `Nightclub is 18+ only` : true
|
||||
});
|
||||
|
||||
// console.log(response); // => { value: 24 }
|
||||
// })();
|
||||
console.log(response); // => { value: 24 }
|
||||
})();
|
||||
|
||||
// 问题链(Prompt Chain)
|
||||
|
||||
// const questions = [
|
||||
// {
|
||||
// type: 'text',
|
||||
// name: 'username',
|
||||
// message: 'What is your GitHub username?'
|
||||
// },
|
||||
// {
|
||||
// type: 'number',
|
||||
// name: 'age',
|
||||
// message: 'How old are you?'
|
||||
// },
|
||||
// {
|
||||
// type: 'text',
|
||||
// name: 'about',
|
||||
// message: 'Tell something about yourself',
|
||||
// initial: 'Why should I?'
|
||||
// }
|
||||
// ];
|
||||
const questions = [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'username',
|
||||
message: 'What is your GitHub username?'
|
||||
},
|
||||
{
|
||||
type: 'number',
|
||||
name: 'age',
|
||||
message: 'How old are you?'
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'about',
|
||||
message: 'Tell something about yourself',
|
||||
initial: 'Why should I?'
|
||||
}
|
||||
];
|
||||
|
||||
// (async () => {
|
||||
// const response = await prompts(questions);
|
||||
// console.log(response)
|
||||
// // => response => { username, age, about }
|
||||
// })();
|
||||
(async () => {
|
||||
const response = await prompts(questions);
|
||||
console.log(response)
|
||||
// => response => { username, age, about }
|
||||
})();
|
||||
|
||||
|
||||
const questions = [
|
||||
|
||||
Reference in New Issue
Block a user