@peiyanlu/cli-utils 0.0.1 → 0.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -5
- package/dist/cjs/index.cjs +125 -27
- package/dist/cjs/index.d.cts +29 -15
- package/dist/esm/index.d.mts +29 -15
- package/dist/esm/index.mjs +116 -27
- package/package.json +1 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @peiyanlu/cli-utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Shared utils for building interactive Node.js CLI applications.
|
|
4
4
|
|
|
5
5
|
## Project setup
|
|
6
6
|
|
|
@@ -21,12 +21,9 @@ $ pnpm run build
|
|
|
21
21
|
## Run tests
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
#
|
|
24
|
+
# tests
|
|
25
25
|
$ pnpm run test
|
|
26
26
|
|
|
27
|
-
# e2e tests
|
|
28
|
-
$ pnpm run test:e2e
|
|
29
|
-
|
|
30
27
|
# test coverage
|
|
31
28
|
$ pnpm run test:cov
|
|
32
29
|
```
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -2,6 +2,7 @@ let node_child_process = require("node:child_process");
|
|
|
2
2
|
let node_fs = require("node:fs");
|
|
3
3
|
let node_fs_promises = require("node:fs/promises");
|
|
4
4
|
let node_path = require("node:path");
|
|
5
|
+
let node_util = require("node:util");
|
|
5
6
|
|
|
6
7
|
//#region src/enums.ts
|
|
7
8
|
let PkgManager = /* @__PURE__ */ function(PkgManager$1) {
|
|
@@ -35,17 +36,6 @@ let HttpLibrary = /* @__PURE__ */ function(HttpLibrary$1) {
|
|
|
35
36
|
|
|
36
37
|
//#endregion
|
|
37
38
|
//#region src/utils.ts
|
|
38
|
-
const execAsync = (cmd) => {
|
|
39
|
-
return new Promise((r) => {
|
|
40
|
-
(0, node_child_process.exec)(cmd, (err, stdout) => r(err ? void 0 : stdout.trim()));
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
const pkgVersion = (pkg) => {
|
|
44
|
-
return execAsync(`npm view ${pkg} version`);
|
|
45
|
-
};
|
|
46
|
-
const checkVersion = async (cmd) => {
|
|
47
|
-
return execAsync(`${cmd} --version`);
|
|
48
|
-
};
|
|
49
39
|
const isValidPackageName = (packageName) => {
|
|
50
40
|
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(packageName);
|
|
51
41
|
};
|
|
@@ -81,15 +71,9 @@ const editJsonFile = (file, callback) => {
|
|
|
81
71
|
}
|
|
82
72
|
});
|
|
83
73
|
};
|
|
84
|
-
const isGitRepo = async (dir) => {
|
|
85
|
-
return !!await execAsync(`git -C "${dir ? `./${dir}` : "."}" rev-parse --is-inside-work-tree`);
|
|
86
|
-
};
|
|
87
74
|
const readSubDirs = async (source, ignore = []) => {
|
|
88
75
|
return (await (0, node_fs_promises.readdir)(source, { withFileTypes: true })).filter((k) => k.isDirectory() && !ignore.includes(k.name)).map((dir) => dir.name);
|
|
89
76
|
};
|
|
90
|
-
const getGitConfig = (key, global = true) => {
|
|
91
|
-
return execAsync(`git config ${global ? "--global" : ""} ${key}`);
|
|
92
|
-
};
|
|
93
77
|
const copyDirAsync = async (src, dest, options) => {
|
|
94
78
|
await (0, node_fs_promises.mkdir)(dest, { recursive: true });
|
|
95
79
|
const entries = await (0, node_fs_promises.readdir)(src, { withFileTypes: true });
|
|
@@ -113,13 +97,7 @@ const readJsonFile = (file) => {
|
|
|
113
97
|
return {};
|
|
114
98
|
}
|
|
115
99
|
};
|
|
116
|
-
/**
|
|
117
|
-
* 通过包管理器执行脚本时生效
|
|
118
|
-
*
|
|
119
|
-
* UserAgent: `process.env.npm_config_user_agent`
|
|
120
|
-
* @param {string | undefined} userAgent
|
|
121
|
-
* @returns {PkgInfo | undefined}
|
|
122
|
-
*/
|
|
100
|
+
/** 通过包管理器执行脚本时生效 UserAgent: `process.env.npm_config_user_agent` */
|
|
123
101
|
const pkgFromUserAgent = (userAgent) => {
|
|
124
102
|
if (!userAgent) return void 0;
|
|
125
103
|
const [name, version] = userAgent.split(" ")[0].split("/");
|
|
@@ -128,7 +106,8 @@ const pkgFromUserAgent = (userAgent) => {
|
|
|
128
106
|
version
|
|
129
107
|
};
|
|
130
108
|
};
|
|
131
|
-
|
|
109
|
+
/** 同步执行 Node CLI(用于测试环境) */
|
|
110
|
+
const runCliForTest = (path, args, options) => {
|
|
132
111
|
return (0, node_child_process.spawnSync)("node", [path, ...args], {
|
|
133
112
|
env: {
|
|
134
113
|
...process.env,
|
|
@@ -138,6 +117,116 @@ const runCli = (path, args, options) => {
|
|
|
138
117
|
...options
|
|
139
118
|
});
|
|
140
119
|
};
|
|
120
|
+
/** 判断测试文件(夹) */
|
|
121
|
+
const isTestFile = (name) => {
|
|
122
|
+
return [
|
|
123
|
+
/(^|[\\/])(test(s?)|__test(s?)__)([\\/]|$)/,
|
|
124
|
+
/\.([a-zA-Z0-9]+-)?(test|spec)\.m?(ts|js)$/,
|
|
125
|
+
/^vitest([-.])(.*)\.m?(ts|js)$/
|
|
126
|
+
].some((reg) => reg.test(name));
|
|
127
|
+
};
|
|
128
|
+
const parseGitHubRepo = (url) => {
|
|
129
|
+
const match = url.trim().match(/github(?:\.com)?[:/](.+?)\/(.+?)(?:[#/?].+?)?(?:\.git)?$/);
|
|
130
|
+
return match ? match.slice(1, 3) : [];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/shell.ts
|
|
135
|
+
const dim = (text) => (0, node_util.styleText)(["dim"], text);
|
|
136
|
+
const red = (text) => (0, node_util.styleText)(["red"], text);
|
|
137
|
+
const yellow = (text) => (0, node_util.styleText)(["yellow"], text);
|
|
138
|
+
const spawnAsync = (cmd, args, options) => {
|
|
139
|
+
return new Promise((resolve$1) => {
|
|
140
|
+
const { trim, error, dryRun, ...others } = options ?? {};
|
|
141
|
+
const fullCmd = [cmd, ...args].join(" ");
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
console.log(`${dim("[dry-run]")} ${fullCmd}`);
|
|
144
|
+
return resolve$1(void 0);
|
|
145
|
+
}
|
|
146
|
+
const child = (0, node_child_process.spawn)(cmd, args, { ...others });
|
|
147
|
+
let stdout = "";
|
|
148
|
+
child.stdout?.setEncoding("utf-8");
|
|
149
|
+
child.stdout?.on("data", (data) => stdout += trim ? data.trim() : data);
|
|
150
|
+
let stderr = "";
|
|
151
|
+
child.stderr?.setEncoding("utf-8");
|
|
152
|
+
child.stderr?.on("data", (data) => stderr += trim ? data.trim() : data);
|
|
153
|
+
child.on("close", (code) => {
|
|
154
|
+
if (stderr) {
|
|
155
|
+
const err = `${red("spawnAsync")} ${dim(fullCmd)} ${stderr}`;
|
|
156
|
+
switch (error) {
|
|
157
|
+
case "log":
|
|
158
|
+
console.error(err);
|
|
159
|
+
break;
|
|
160
|
+
case "throw": throw new Error(err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
resolve$1(0 === code ? stdout : void 0);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const execAsync = (cmd, options) => {
|
|
168
|
+
return new Promise((resolve$1) => {
|
|
169
|
+
const { trim, dryRun, error, ...others } = options ?? {};
|
|
170
|
+
if (dryRun) {
|
|
171
|
+
console.log(`${dim("[dry-run]")} ${cmd}`);
|
|
172
|
+
return resolve$1(void 0);
|
|
173
|
+
}
|
|
174
|
+
(0, node_child_process.exec)(cmd, { ...others }, (err, stdout) => {
|
|
175
|
+
if (err) {
|
|
176
|
+
const msg = `${red("execAsync")} ${dim(cmd)} ${err.message}`;
|
|
177
|
+
switch (error) {
|
|
178
|
+
case "log":
|
|
179
|
+
console.error(msg);
|
|
180
|
+
break;
|
|
181
|
+
case "throw": throw new Error(msg);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
resolve$1(err ? void 0 : trim ? stdout.trim() : stdout);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const runGit = async (args, trim = true) => {
|
|
189
|
+
return spawnAsync("git", args, { trim });
|
|
190
|
+
};
|
|
191
|
+
const runNpm = (args) => {
|
|
192
|
+
return execAsync(["npm", ...args].join(" "));
|
|
193
|
+
};
|
|
194
|
+
const isGitRepo = async (dir) => {
|
|
195
|
+
return !!await runGit([
|
|
196
|
+
"-C",
|
|
197
|
+
dir ? `./${dir}` : ".",
|
|
198
|
+
"rev-parse",
|
|
199
|
+
"--is-inside-work-tree"
|
|
200
|
+
]);
|
|
201
|
+
};
|
|
202
|
+
const getGitConfig = (key, global = true) => {
|
|
203
|
+
return runGit([
|
|
204
|
+
"config",
|
|
205
|
+
...global ? ["--global"] : [],
|
|
206
|
+
key
|
|
207
|
+
]);
|
|
208
|
+
};
|
|
209
|
+
const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
210
|
+
return runGit([
|
|
211
|
+
"remote",
|
|
212
|
+
"get-url",
|
|
213
|
+
remoteName
|
|
214
|
+
]).catch((_) => runGit([
|
|
215
|
+
"config",
|
|
216
|
+
"--get",
|
|
217
|
+
`remote.${remoteName}.url`
|
|
218
|
+
]));
|
|
219
|
+
};
|
|
220
|
+
const pkgVersion = (pkg) => {
|
|
221
|
+
return runNpm([
|
|
222
|
+
"view",
|
|
223
|
+
pkg,
|
|
224
|
+
"version"
|
|
225
|
+
]);
|
|
226
|
+
};
|
|
227
|
+
const checkVersion = async (cmd) => {
|
|
228
|
+
return execAsync(`${cmd} --version`);
|
|
229
|
+
};
|
|
141
230
|
|
|
142
231
|
//#endregion
|
|
143
232
|
exports.ConfirmResult = ConfirmResult;
|
|
@@ -146,18 +235,27 @@ exports.PkgManager = PkgManager;
|
|
|
146
235
|
exports.YesOrNo = YesOrNo;
|
|
147
236
|
exports.checkVersion = checkVersion;
|
|
148
237
|
exports.copyDirAsync = copyDirAsync;
|
|
238
|
+
exports.dim = dim;
|
|
149
239
|
exports.editFile = editFile;
|
|
150
240
|
exports.editJsonFile = editJsonFile;
|
|
151
241
|
exports.emptyDir = emptyDir;
|
|
152
242
|
exports.execAsync = execAsync;
|
|
153
243
|
exports.getGitConfig = getGitConfig;
|
|
244
|
+
exports.getGitRemoteUrl = getGitRemoteUrl;
|
|
154
245
|
exports.isEmpty = isEmpty;
|
|
155
246
|
exports.isGitRepo = isGitRepo;
|
|
247
|
+
exports.isTestFile = isTestFile;
|
|
156
248
|
exports.isValidPackageName = isValidPackageName;
|
|
249
|
+
exports.parseGitHubRepo = parseGitHubRepo;
|
|
157
250
|
exports.pkgFromUserAgent = pkgFromUserAgent;
|
|
158
251
|
exports.pkgVersion = pkgVersion;
|
|
159
252
|
exports.readJsonFile = readJsonFile;
|
|
160
253
|
exports.readSubDirs = readSubDirs;
|
|
161
|
-
exports.
|
|
254
|
+
exports.red = red;
|
|
255
|
+
exports.runCliForTest = runCliForTest;
|
|
256
|
+
exports.runGit = runGit;
|
|
257
|
+
exports.runNpm = runNpm;
|
|
258
|
+
exports.spawnAsync = spawnAsync;
|
|
162
259
|
exports.toValidPackageName = toValidPackageName;
|
|
163
|
-
exports.toValidProjectName = toValidProjectName;
|
|
260
|
+
exports.toValidProjectName = toValidProjectName;
|
|
261
|
+
exports.yellow = yellow;
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as child_process0 from "child_process";
|
|
2
|
-
import { SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
2
|
+
import { ExecOptions, SpawnOptions, SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
3
3
|
|
|
4
4
|
//#region src/enums.d.ts
|
|
5
5
|
declare enum PkgManager {
|
|
@@ -37,11 +37,15 @@ interface PkgInfo {
|
|
|
37
37
|
name: string;
|
|
38
38
|
version: string;
|
|
39
39
|
}
|
|
40
|
+
interface ExecResultOptions {
|
|
41
|
+
trim?: boolean;
|
|
42
|
+
dryRun?: boolean;
|
|
43
|
+
error?: 'log' | 'throw' | 'ignore';
|
|
44
|
+
}
|
|
45
|
+
type SpawnAsyncOptions<T = SpawnOptions> = T & ExecResultOptions;
|
|
46
|
+
type ExecAsyncOptions<T = ExecOptions> = T & ExecResultOptions;
|
|
40
47
|
//#endregion
|
|
41
48
|
//#region src/utils.d.ts
|
|
42
|
-
declare const execAsync: (cmd: string) => Promise<string | undefined>;
|
|
43
|
-
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
44
|
-
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
45
49
|
declare const isValidPackageName: (packageName: string) => boolean;
|
|
46
50
|
declare const toValidPackageName: (packageName: string) => string;
|
|
47
51
|
declare const toValidProjectName: (projectName: string) => string;
|
|
@@ -49,19 +53,29 @@ declare const emptyDir: (dir: string, ignore?: string[]) => Promise<boolean>;
|
|
|
49
53
|
declare const isEmpty: (path: string, ignore?: string[]) => Promise<boolean>;
|
|
50
54
|
declare const editFile: (file: string, callback: (content: string) => string) => Promise<void>;
|
|
51
55
|
declare const editJsonFile: <T extends Record<string, any>>(file: string, callback: (json: T) => void) => Promise<void>;
|
|
52
|
-
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
53
56
|
declare const readSubDirs: (source: string, ignore?: string[]) => Promise<string[]>;
|
|
54
|
-
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
55
57
|
declare const copyDirAsync: (src: string, dest: string, options: CopyOptions) => Promise<void>;
|
|
56
58
|
declare const readJsonFile: <T extends Record<string, any>>(file: string) => T;
|
|
57
|
-
/**
|
|
58
|
-
* 通过包管理器执行脚本时生效
|
|
59
|
-
*
|
|
60
|
-
* UserAgent: `process.env.npm_config_user_agent`
|
|
61
|
-
* @param {string | undefined} userAgent
|
|
62
|
-
* @returns {PkgInfo | undefined}
|
|
63
|
-
*/
|
|
59
|
+
/** 通过包管理器执行脚本时生效 UserAgent: `process.env.npm_config_user_agent` */
|
|
64
60
|
declare const pkgFromUserAgent: (userAgent: string | undefined) => PkgInfo | undefined;
|
|
65
|
-
|
|
61
|
+
/** 同步执行 Node CLI(用于测试环境) */
|
|
62
|
+
declare const runCliForTest: (path: string, args: string[], options?: SpawnSyncOptionsWithStringEncoding) => child_process0.SpawnSyncReturns<string>;
|
|
63
|
+
/** 判断测试文件(夹) */
|
|
64
|
+
declare const isTestFile: (name: string) => boolean;
|
|
65
|
+
declare const parseGitHubRepo: (url: string) => string[];
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/shell.d.ts
|
|
68
|
+
declare const dim: (text: string) => string;
|
|
69
|
+
declare const red: (text: string) => string;
|
|
70
|
+
declare const yellow: (text: string) => string;
|
|
71
|
+
declare const spawnAsync: <T = SpawnOptions>(cmd: string, args: string[], options?: SpawnAsyncOptions<T>) => Promise<string | undefined>;
|
|
72
|
+
declare const execAsync: <T = ExecOptions>(cmd: string, options?: ExecAsyncOptions<T>) => Promise<string | undefined>;
|
|
73
|
+
declare const runGit: (args: string[], trim?: boolean) => Promise<string | undefined>;
|
|
74
|
+
declare const runNpm: (args: string[]) => Promise<string | undefined>;
|
|
75
|
+
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
76
|
+
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
77
|
+
declare const getGitRemoteUrl: (remoteName?: string) => Promise<string | undefined>;
|
|
78
|
+
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
79
|
+
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
66
80
|
//#endregion
|
|
67
|
-
export { CliOptions, ConfirmResult, CopyOptions, HttpLibrary, PkgInfo, PkgManager, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, isEmpty, isGitRepo, isValidPackageName, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs,
|
|
81
|
+
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync, dim, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, red, runCliForTest, runGit, runNpm, spawnAsync, toValidPackageName, toValidProjectName, yellow };
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as child_process0 from "child_process";
|
|
2
|
-
import { SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
2
|
+
import { ExecOptions, SpawnOptions, SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
3
3
|
|
|
4
4
|
//#region src/enums.d.ts
|
|
5
5
|
declare enum PkgManager {
|
|
@@ -37,11 +37,15 @@ interface PkgInfo {
|
|
|
37
37
|
name: string;
|
|
38
38
|
version: string;
|
|
39
39
|
}
|
|
40
|
+
interface ExecResultOptions {
|
|
41
|
+
trim?: boolean;
|
|
42
|
+
dryRun?: boolean;
|
|
43
|
+
error?: 'log' | 'throw' | 'ignore';
|
|
44
|
+
}
|
|
45
|
+
type SpawnAsyncOptions<T = SpawnOptions> = T & ExecResultOptions;
|
|
46
|
+
type ExecAsyncOptions<T = ExecOptions> = T & ExecResultOptions;
|
|
40
47
|
//#endregion
|
|
41
48
|
//#region src/utils.d.ts
|
|
42
|
-
declare const execAsync: (cmd: string) => Promise<string | undefined>;
|
|
43
|
-
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
44
|
-
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
45
49
|
declare const isValidPackageName: (packageName: string) => boolean;
|
|
46
50
|
declare const toValidPackageName: (packageName: string) => string;
|
|
47
51
|
declare const toValidProjectName: (projectName: string) => string;
|
|
@@ -49,19 +53,29 @@ declare const emptyDir: (dir: string, ignore?: string[]) => Promise<boolean>;
|
|
|
49
53
|
declare const isEmpty: (path: string, ignore?: string[]) => Promise<boolean>;
|
|
50
54
|
declare const editFile: (file: string, callback: (content: string) => string) => Promise<void>;
|
|
51
55
|
declare const editJsonFile: <T extends Record<string, any>>(file: string, callback: (json: T) => void) => Promise<void>;
|
|
52
|
-
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
53
56
|
declare const readSubDirs: (source: string, ignore?: string[]) => Promise<string[]>;
|
|
54
|
-
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
55
57
|
declare const copyDirAsync: (src: string, dest: string, options: CopyOptions) => Promise<void>;
|
|
56
58
|
declare const readJsonFile: <T extends Record<string, any>>(file: string) => T;
|
|
57
|
-
/**
|
|
58
|
-
* 通过包管理器执行脚本时生效
|
|
59
|
-
*
|
|
60
|
-
* UserAgent: `process.env.npm_config_user_agent`
|
|
61
|
-
* @param {string | undefined} userAgent
|
|
62
|
-
* @returns {PkgInfo | undefined}
|
|
63
|
-
*/
|
|
59
|
+
/** 通过包管理器执行脚本时生效 UserAgent: `process.env.npm_config_user_agent` */
|
|
64
60
|
declare const pkgFromUserAgent: (userAgent: string | undefined) => PkgInfo | undefined;
|
|
65
|
-
|
|
61
|
+
/** 同步执行 Node CLI(用于测试环境) */
|
|
62
|
+
declare const runCliForTest: (path: string, args: string[], options?: SpawnSyncOptionsWithStringEncoding) => child_process0.SpawnSyncReturns<string>;
|
|
63
|
+
/** 判断测试文件(夹) */
|
|
64
|
+
declare const isTestFile: (name: string) => boolean;
|
|
65
|
+
declare const parseGitHubRepo: (url: string) => string[];
|
|
66
|
+
//#endregion
|
|
67
|
+
//#region src/shell.d.ts
|
|
68
|
+
declare const dim: (text: string) => string;
|
|
69
|
+
declare const red: (text: string) => string;
|
|
70
|
+
declare const yellow: (text: string) => string;
|
|
71
|
+
declare const spawnAsync: <T = SpawnOptions>(cmd: string, args: string[], options?: SpawnAsyncOptions<T>) => Promise<string | undefined>;
|
|
72
|
+
declare const execAsync: <T = ExecOptions>(cmd: string, options?: ExecAsyncOptions<T>) => Promise<string | undefined>;
|
|
73
|
+
declare const runGit: (args: string[], trim?: boolean) => Promise<string | undefined>;
|
|
74
|
+
declare const runNpm: (args: string[]) => Promise<string | undefined>;
|
|
75
|
+
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
76
|
+
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
77
|
+
declare const getGitRemoteUrl: (remoteName?: string) => Promise<string | undefined>;
|
|
78
|
+
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
79
|
+
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
66
80
|
//#endregion
|
|
67
|
-
export { CliOptions, ConfirmResult, CopyOptions, HttpLibrary, PkgInfo, PkgManager, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, isEmpty, isGitRepo, isValidPackageName, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs,
|
|
81
|
+
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync, dim, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, red, runCliForTest, runGit, runNpm, spawnAsync, toValidPackageName, toValidProjectName, yellow };
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { exec, spawnSync } from "node:child_process";
|
|
1
|
+
import { exec, spawn, spawnSync } from "node:child_process";
|
|
2
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
3
3
|
import { copyFile, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { join, resolve } from "node:path";
|
|
5
|
+
import { styleText } from "node:util";
|
|
5
6
|
|
|
6
7
|
//#region src/enums.ts
|
|
7
8
|
let PkgManager = /* @__PURE__ */ function(PkgManager$1) {
|
|
@@ -35,17 +36,6 @@ let HttpLibrary = /* @__PURE__ */ function(HttpLibrary$1) {
|
|
|
35
36
|
|
|
36
37
|
//#endregion
|
|
37
38
|
//#region src/utils.ts
|
|
38
|
-
const execAsync = (cmd) => {
|
|
39
|
-
return new Promise((r) => {
|
|
40
|
-
exec(cmd, (err, stdout) => r(err ? void 0 : stdout.trim()));
|
|
41
|
-
});
|
|
42
|
-
};
|
|
43
|
-
const pkgVersion = (pkg) => {
|
|
44
|
-
return execAsync(`npm view ${pkg} version`);
|
|
45
|
-
};
|
|
46
|
-
const checkVersion = async (cmd) => {
|
|
47
|
-
return execAsync(`${cmd} --version`);
|
|
48
|
-
};
|
|
49
39
|
const isValidPackageName = (packageName) => {
|
|
50
40
|
return /^(?:@[a-z\d\-*~][a-z\d\-*._~]*\/)?[a-z\d\-~][a-z\d\-._~]*$/.test(packageName);
|
|
51
41
|
};
|
|
@@ -81,15 +71,9 @@ const editJsonFile = (file, callback) => {
|
|
|
81
71
|
}
|
|
82
72
|
});
|
|
83
73
|
};
|
|
84
|
-
const isGitRepo = async (dir) => {
|
|
85
|
-
return !!await execAsync(`git -C "${dir ? `./${dir}` : "."}" rev-parse --is-inside-work-tree`);
|
|
86
|
-
};
|
|
87
74
|
const readSubDirs = async (source, ignore = []) => {
|
|
88
75
|
return (await readdir(source, { withFileTypes: true })).filter((k) => k.isDirectory() && !ignore.includes(k.name)).map((dir) => dir.name);
|
|
89
76
|
};
|
|
90
|
-
const getGitConfig = (key, global = true) => {
|
|
91
|
-
return execAsync(`git config ${global ? "--global" : ""} ${key}`);
|
|
92
|
-
};
|
|
93
77
|
const copyDirAsync = async (src, dest, options) => {
|
|
94
78
|
await mkdir(dest, { recursive: true });
|
|
95
79
|
const entries = await readdir(src, { withFileTypes: true });
|
|
@@ -113,13 +97,7 @@ const readJsonFile = (file) => {
|
|
|
113
97
|
return {};
|
|
114
98
|
}
|
|
115
99
|
};
|
|
116
|
-
/**
|
|
117
|
-
* 通过包管理器执行脚本时生效
|
|
118
|
-
*
|
|
119
|
-
* UserAgent: `process.env.npm_config_user_agent`
|
|
120
|
-
* @param {string | undefined} userAgent
|
|
121
|
-
* @returns {PkgInfo | undefined}
|
|
122
|
-
*/
|
|
100
|
+
/** 通过包管理器执行脚本时生效 UserAgent: `process.env.npm_config_user_agent` */
|
|
123
101
|
const pkgFromUserAgent = (userAgent) => {
|
|
124
102
|
if (!userAgent) return void 0;
|
|
125
103
|
const [name, version] = userAgent.split(" ")[0].split("/");
|
|
@@ -128,7 +106,8 @@ const pkgFromUserAgent = (userAgent) => {
|
|
|
128
106
|
version
|
|
129
107
|
};
|
|
130
108
|
};
|
|
131
|
-
|
|
109
|
+
/** 同步执行 Node CLI(用于测试环境) */
|
|
110
|
+
const runCliForTest = (path, args, options) => {
|
|
132
111
|
return spawnSync("node", [path, ...args], {
|
|
133
112
|
env: {
|
|
134
113
|
...process.env,
|
|
@@ -138,6 +117,116 @@ const runCli = (path, args, options) => {
|
|
|
138
117
|
...options
|
|
139
118
|
});
|
|
140
119
|
};
|
|
120
|
+
/** 判断测试文件(夹) */
|
|
121
|
+
const isTestFile = (name) => {
|
|
122
|
+
return [
|
|
123
|
+
/(^|[\\/])(test(s?)|__test(s?)__)([\\/]|$)/,
|
|
124
|
+
/\.([a-zA-Z0-9]+-)?(test|spec)\.m?(ts|js)$/,
|
|
125
|
+
/^vitest([-.])(.*)\.m?(ts|js)$/
|
|
126
|
+
].some((reg) => reg.test(name));
|
|
127
|
+
};
|
|
128
|
+
const parseGitHubRepo = (url) => {
|
|
129
|
+
const match = url.trim().match(/github(?:\.com)?[:/](.+?)\/(.+?)(?:[#/?].+?)?(?:\.git)?$/);
|
|
130
|
+
return match ? match.slice(1, 3) : [];
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/shell.ts
|
|
135
|
+
const dim = (text) => styleText(["dim"], text);
|
|
136
|
+
const red = (text) => styleText(["red"], text);
|
|
137
|
+
const yellow = (text) => styleText(["yellow"], text);
|
|
138
|
+
const spawnAsync = (cmd, args, options) => {
|
|
139
|
+
return new Promise((resolve$1) => {
|
|
140
|
+
const { trim, error, dryRun, ...others } = options ?? {};
|
|
141
|
+
const fullCmd = [cmd, ...args].join(" ");
|
|
142
|
+
if (dryRun) {
|
|
143
|
+
console.log(`${dim("[dry-run]")} ${fullCmd}`);
|
|
144
|
+
return resolve$1(void 0);
|
|
145
|
+
}
|
|
146
|
+
const child = spawn(cmd, args, { ...others });
|
|
147
|
+
let stdout = "";
|
|
148
|
+
child.stdout?.setEncoding("utf-8");
|
|
149
|
+
child.stdout?.on("data", (data) => stdout += trim ? data.trim() : data);
|
|
150
|
+
let stderr = "";
|
|
151
|
+
child.stderr?.setEncoding("utf-8");
|
|
152
|
+
child.stderr?.on("data", (data) => stderr += trim ? data.trim() : data);
|
|
153
|
+
child.on("close", (code) => {
|
|
154
|
+
if (stderr) {
|
|
155
|
+
const err = `${red("spawnAsync")} ${dim(fullCmd)} ${stderr}`;
|
|
156
|
+
switch (error) {
|
|
157
|
+
case "log":
|
|
158
|
+
console.error(err);
|
|
159
|
+
break;
|
|
160
|
+
case "throw": throw new Error(err);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
resolve$1(0 === code ? stdout : void 0);
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
};
|
|
167
|
+
const execAsync = (cmd, options) => {
|
|
168
|
+
return new Promise((resolve$1) => {
|
|
169
|
+
const { trim, dryRun, error, ...others } = options ?? {};
|
|
170
|
+
if (dryRun) {
|
|
171
|
+
console.log(`${dim("[dry-run]")} ${cmd}`);
|
|
172
|
+
return resolve$1(void 0);
|
|
173
|
+
}
|
|
174
|
+
exec(cmd, { ...others }, (err, stdout) => {
|
|
175
|
+
if (err) {
|
|
176
|
+
const msg = `${red("execAsync")} ${dim(cmd)} ${err.message}`;
|
|
177
|
+
switch (error) {
|
|
178
|
+
case "log":
|
|
179
|
+
console.error(msg);
|
|
180
|
+
break;
|
|
181
|
+
case "throw": throw new Error(msg);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
resolve$1(err ? void 0 : trim ? stdout.trim() : stdout);
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
};
|
|
188
|
+
const runGit = async (args, trim = true) => {
|
|
189
|
+
return spawnAsync("git", args, { trim });
|
|
190
|
+
};
|
|
191
|
+
const runNpm = (args) => {
|
|
192
|
+
return execAsync(["npm", ...args].join(" "));
|
|
193
|
+
};
|
|
194
|
+
const isGitRepo = async (dir) => {
|
|
195
|
+
return !!await runGit([
|
|
196
|
+
"-C",
|
|
197
|
+
dir ? `./${dir}` : ".",
|
|
198
|
+
"rev-parse",
|
|
199
|
+
"--is-inside-work-tree"
|
|
200
|
+
]);
|
|
201
|
+
};
|
|
202
|
+
const getGitConfig = (key, global = true) => {
|
|
203
|
+
return runGit([
|
|
204
|
+
"config",
|
|
205
|
+
...global ? ["--global"] : [],
|
|
206
|
+
key
|
|
207
|
+
]);
|
|
208
|
+
};
|
|
209
|
+
const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
210
|
+
return runGit([
|
|
211
|
+
"remote",
|
|
212
|
+
"get-url",
|
|
213
|
+
remoteName
|
|
214
|
+
]).catch((_) => runGit([
|
|
215
|
+
"config",
|
|
216
|
+
"--get",
|
|
217
|
+
`remote.${remoteName}.url`
|
|
218
|
+
]));
|
|
219
|
+
};
|
|
220
|
+
const pkgVersion = (pkg) => {
|
|
221
|
+
return runNpm([
|
|
222
|
+
"view",
|
|
223
|
+
pkg,
|
|
224
|
+
"version"
|
|
225
|
+
]);
|
|
226
|
+
};
|
|
227
|
+
const checkVersion = async (cmd) => {
|
|
228
|
+
return execAsync(`${cmd} --version`);
|
|
229
|
+
};
|
|
141
230
|
|
|
142
231
|
//#endregion
|
|
143
|
-
export { ConfirmResult, HttpLibrary, PkgManager, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, isEmpty, isGitRepo, isValidPackageName, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs,
|
|
232
|
+
export { ConfirmResult, HttpLibrary, PkgManager, YesOrNo, checkVersion, copyDirAsync, dim, editFile, editJsonFile, emptyDir, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, red, runCliForTest, runGit, runNpm, spawnAsync, toValidPackageName, toValidProjectName, yellow };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@peiyanlu/cli-utils",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.2",
|
|
4
4
|
"description": "Shared utils for building interactive Node.js CLI applications.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -56,7 +56,6 @@
|
|
|
56
56
|
"prepublish": "tsdown",
|
|
57
57
|
"release": "release-it",
|
|
58
58
|
"test": "vitest run",
|
|
59
|
-
"test:e2e": "vitest run -c vitest.config.e2e.mts",
|
|
60
59
|
"test:cov": "vitest run --coverage"
|
|
61
60
|
}
|
|
62
61
|
}
|