@peiyanlu/cli-utils 0.0.2 → 0.0.4
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/dist/cjs/index.cjs +115 -32
- package/dist/cjs/index.d.cts +45 -12
- package/dist/esm/index.d.mts +45 -12
- package/dist/esm/index.mjs +105 -25
- package/package.json +1 -1
package/dist/cjs/index.cjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
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
|
+
let node_os = require("node:os");
|
|
4
5
|
let node_path = require("node:path");
|
|
5
6
|
let node_util = require("node:util");
|
|
6
7
|
|
|
@@ -125,31 +126,37 @@ const isTestFile = (name) => {
|
|
|
125
126
|
/^vitest([-.])(.*)\.m?(ts|js)$/
|
|
126
127
|
].some((reg) => reg.test(name));
|
|
127
128
|
};
|
|
129
|
+
/** 解析 Github 链接获取 owner 和 repo */
|
|
128
130
|
const parseGitHubRepo = (url) => {
|
|
129
131
|
const match = url.trim().match(/github(?:\.com)?[:/](.+?)\/(.+?)(?:[#/?].+?)?(?:\.git)?$/);
|
|
130
132
|
return match ? match.slice(1, 3) : [];
|
|
131
133
|
};
|
|
134
|
+
/** 基于 EOL 的可多换行函数 */
|
|
135
|
+
const eol = (n = 1) => node_os.EOL.repeat(n);
|
|
132
136
|
|
|
133
137
|
//#endregion
|
|
134
|
-
//#region src/
|
|
138
|
+
//#region src/styleText.ts
|
|
135
139
|
const dim = (text) => (0, node_util.styleText)(["dim"], text);
|
|
136
140
|
const red = (text) => (0, node_util.styleText)(["red"], text);
|
|
137
|
-
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/shell.ts
|
|
144
|
+
/** 异步执行 `spawn` 获取字符串类型的结果 */
|
|
138
145
|
const spawnAsync = (cmd, args, options) => {
|
|
139
|
-
return new Promise((resolve$
|
|
146
|
+
return new Promise((resolve$2) => {
|
|
140
147
|
const { trim, error, dryRun, ...others } = options ?? {};
|
|
141
|
-
const fullCmd = [cmd, ...args]
|
|
148
|
+
const fullCmd = stringifyArgs([cmd, ...args]);
|
|
142
149
|
if (dryRun) {
|
|
143
150
|
console.log(`${dim("[dry-run]")} ${fullCmd}`);
|
|
144
|
-
return resolve$
|
|
151
|
+
return resolve$2(void 0);
|
|
145
152
|
}
|
|
146
153
|
const child = (0, node_child_process.spawn)(cmd, args, { ...others });
|
|
147
154
|
let stdout = "";
|
|
148
|
-
child.stdout
|
|
155
|
+
child.stdout.setEncoding("utf-8");
|
|
149
156
|
child.stdout?.on("data", (data) => stdout += trim ? data.trim() : data);
|
|
150
157
|
let stderr = "";
|
|
151
|
-
child.stderr
|
|
152
|
-
child.stderr
|
|
158
|
+
child.stderr.setEncoding("utf-8");
|
|
159
|
+
child.stderr.on("data", (data) => stderr += trim ? data.trim() : data);
|
|
153
160
|
child.on("close", (code) => {
|
|
154
161
|
if (stderr) {
|
|
155
162
|
const err = `${red("spawnAsync")} ${dim(fullCmd)} ${stderr}`;
|
|
@@ -160,45 +167,116 @@ const spawnAsync = (cmd, args, options) => {
|
|
|
160
167
|
case "throw": throw new Error(err);
|
|
161
168
|
}
|
|
162
169
|
}
|
|
163
|
-
resolve$
|
|
170
|
+
resolve$2(0 === code ? stdout : void 0);
|
|
164
171
|
});
|
|
165
172
|
});
|
|
166
173
|
};
|
|
167
|
-
|
|
168
|
-
|
|
174
|
+
/** 异步执行 `exec` 获取字符串类型的结果 */
|
|
175
|
+
const execAsync = (cmd, argsOrOptions, maybeOptions) => {
|
|
176
|
+
return new Promise((resolve$2) => {
|
|
177
|
+
let command;
|
|
178
|
+
let options;
|
|
179
|
+
if (Array.isArray(argsOrOptions)) {
|
|
180
|
+
command = stringifyArgs([cmd, ...argsOrOptions]);
|
|
181
|
+
options = maybeOptions;
|
|
182
|
+
} else {
|
|
183
|
+
command = cmd;
|
|
184
|
+
options = argsOrOptions;
|
|
185
|
+
}
|
|
169
186
|
const { trim, dryRun, error, ...others } = options ?? {};
|
|
170
187
|
if (dryRun) {
|
|
171
|
-
console.log(`${dim("[dry-run]")} ${
|
|
172
|
-
return resolve$
|
|
188
|
+
console.log(`${dim("[dry-run]")} ${command}`);
|
|
189
|
+
return resolve$2(void 0);
|
|
173
190
|
}
|
|
174
|
-
(0, node_child_process.exec)(
|
|
175
|
-
if (
|
|
176
|
-
const
|
|
191
|
+
(0, node_child_process.exec)(command, { ...others }, (stderr, stdout) => {
|
|
192
|
+
if (stderr) {
|
|
193
|
+
const err = `${red("execAsync")} ${dim(command)} ${stderr.message}`;
|
|
177
194
|
switch (error) {
|
|
178
195
|
case "log":
|
|
179
|
-
console.error(
|
|
196
|
+
console.error(err);
|
|
180
197
|
break;
|
|
181
|
-
case "throw": throw new Error(
|
|
198
|
+
case "throw": throw new Error(err);
|
|
182
199
|
}
|
|
183
200
|
}
|
|
184
|
-
resolve$
|
|
201
|
+
resolve$2(stderr ? void 0 : trim ? stdout.trim() : stdout);
|
|
185
202
|
});
|
|
186
203
|
});
|
|
187
204
|
};
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
/** 基于 {@link spawnAsync} 实现 */
|
|
206
|
+
const runGit = async (args, options = { trim: true }) => {
|
|
207
|
+
return spawnAsync("git", args, options);
|
|
190
208
|
};
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
/** 基于 {@link execAsync} 实现 */
|
|
210
|
+
const runNpm = (args, options = { trim: true }) => {
|
|
211
|
+
return execAsync("npm", args, options);
|
|
212
|
+
};
|
|
213
|
+
/** 基于 {@link spawnSync} 实现 */
|
|
214
|
+
const runGitSync = (args, options) => {
|
|
215
|
+
const { stdout } = (0, node_child_process.spawnSync)("git", args, {
|
|
216
|
+
encoding: "utf-8",
|
|
217
|
+
...options
|
|
218
|
+
});
|
|
219
|
+
return stdout.toString().trim();
|
|
220
|
+
};
|
|
221
|
+
/** 基于 {@link execSync} 实现 */
|
|
222
|
+
const runNpmSync = (args, options) => {
|
|
223
|
+
return (0, node_child_process.execSync)(stringifyArgs(["npm", ...args]), {
|
|
224
|
+
encoding: "utf-8",
|
|
225
|
+
...options
|
|
226
|
+
}).toString().trim();
|
|
193
227
|
};
|
|
228
|
+
/** 将字符串以空格分割为数组 */
|
|
229
|
+
const parseArgs = (args) => args.trim() ? args.trim().split(" ") : [];
|
|
230
|
+
/** 将数组以空格拼接为字符串 */
|
|
231
|
+
const stringifyArgs = (args) => args.length ? args.join(" ") : "";
|
|
232
|
+
/** 支持所有支持 `--version` 命令的脚本查看版本 */
|
|
233
|
+
const checkVersion = async (cmd) => {
|
|
234
|
+
return execAsync(`${cmd} --version`);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/joinUrl.ts
|
|
239
|
+
function joinUrl(input) {
|
|
240
|
+
const temps = Array.isArray(input) ? input : [...arguments];
|
|
241
|
+
if (temps.length === 0) return "";
|
|
242
|
+
const result = [];
|
|
243
|
+
const parts = [...temps];
|
|
244
|
+
/** 协议正则 */
|
|
245
|
+
const PROTOCOL_RE = /^[^/:]+:\/*$/;
|
|
246
|
+
const FILE_PROTOCOL_RE = /^file:\/\/\//;
|
|
247
|
+
if (PROTOCOL_RE.test(parts[0]) && parts.length > 1) {
|
|
248
|
+
parts[1] = parts[0] + parts[1];
|
|
249
|
+
parts.shift();
|
|
250
|
+
}
|
|
251
|
+
if (FILE_PROTOCOL_RE.test(parts[0])) parts[0] = parts[0].replace(/^([^/:]+):\/*/, "$1:///");
|
|
252
|
+
else parts[0] = parts[0].replace(/^([^/:]+):\/*/, "$1://");
|
|
253
|
+
parts.forEach((part, index) => {
|
|
254
|
+
if (!part) return;
|
|
255
|
+
let segment = part;
|
|
256
|
+
if (index > 0) segment = segment.replace(/^\/+/, "");
|
|
257
|
+
if (index < parts.length - 1) segment = segment.replace(/\/+$/, "");
|
|
258
|
+
else segment = segment.replace(/\/+$/, "/");
|
|
259
|
+
result.push(segment);
|
|
260
|
+
});
|
|
261
|
+
let url = result.join("/");
|
|
262
|
+
url = url.replace(/\/(\?|&|#[^!])/g, "$1");
|
|
263
|
+
const [base, ...queryParts] = url.split("?");
|
|
264
|
+
url = base + (queryParts.length ? "?" + queryParts.join("&") : "");
|
|
265
|
+
return url;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/git.ts
|
|
270
|
+
/** 判断指定目录是否是 git 仓库 */
|
|
194
271
|
const isGitRepo = async (dir) => {
|
|
195
|
-
return
|
|
272
|
+
return "true" === await runGit([
|
|
196
273
|
"-C",
|
|
197
|
-
|
|
274
|
+
(0, node_path.resolve)(process.cwd(), dir || "."),
|
|
198
275
|
"rev-parse",
|
|
199
276
|
"--is-inside-work-tree"
|
|
200
277
|
]);
|
|
201
278
|
};
|
|
279
|
+
/** 获取指定的 git 配置 */
|
|
202
280
|
const getGitConfig = (key, global = true) => {
|
|
203
281
|
return runGit([
|
|
204
282
|
"config",
|
|
@@ -206,6 +284,7 @@ const getGitConfig = (key, global = true) => {
|
|
|
206
284
|
key
|
|
207
285
|
]);
|
|
208
286
|
};
|
|
287
|
+
/** 获取 git 远程地址 */
|
|
209
288
|
const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
210
289
|
return runGit([
|
|
211
290
|
"remote",
|
|
@@ -217,6 +296,10 @@ const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
|
217
296
|
`remote.${remoteName}.url`
|
|
218
297
|
]));
|
|
219
298
|
};
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/npm.ts
|
|
302
|
+
/** 获取指定包的版本 */
|
|
220
303
|
const pkgVersion = (pkg) => {
|
|
221
304
|
return runNpm([
|
|
222
305
|
"view",
|
|
@@ -224,9 +307,6 @@ const pkgVersion = (pkg) => {
|
|
|
224
307
|
"version"
|
|
225
308
|
]);
|
|
226
309
|
};
|
|
227
|
-
const checkVersion = async (cmd) => {
|
|
228
|
-
return execAsync(`${cmd} --version`);
|
|
229
|
-
};
|
|
230
310
|
|
|
231
311
|
//#endregion
|
|
232
312
|
exports.ConfirmResult = ConfirmResult;
|
|
@@ -235,10 +315,10 @@ exports.PkgManager = PkgManager;
|
|
|
235
315
|
exports.YesOrNo = YesOrNo;
|
|
236
316
|
exports.checkVersion = checkVersion;
|
|
237
317
|
exports.copyDirAsync = copyDirAsync;
|
|
238
|
-
exports.dim = dim;
|
|
239
318
|
exports.editFile = editFile;
|
|
240
319
|
exports.editJsonFile = editJsonFile;
|
|
241
320
|
exports.emptyDir = emptyDir;
|
|
321
|
+
exports.eol = eol;
|
|
242
322
|
exports.execAsync = execAsync;
|
|
243
323
|
exports.getGitConfig = getGitConfig;
|
|
244
324
|
exports.getGitRemoteUrl = getGitRemoteUrl;
|
|
@@ -246,16 +326,19 @@ exports.isEmpty = isEmpty;
|
|
|
246
326
|
exports.isGitRepo = isGitRepo;
|
|
247
327
|
exports.isTestFile = isTestFile;
|
|
248
328
|
exports.isValidPackageName = isValidPackageName;
|
|
329
|
+
exports.joinUrl = joinUrl;
|
|
330
|
+
exports.parseArgs = parseArgs;
|
|
249
331
|
exports.parseGitHubRepo = parseGitHubRepo;
|
|
250
332
|
exports.pkgFromUserAgent = pkgFromUserAgent;
|
|
251
333
|
exports.pkgVersion = pkgVersion;
|
|
252
334
|
exports.readJsonFile = readJsonFile;
|
|
253
335
|
exports.readSubDirs = readSubDirs;
|
|
254
|
-
exports.red = red;
|
|
255
336
|
exports.runCliForTest = runCliForTest;
|
|
256
337
|
exports.runGit = runGit;
|
|
338
|
+
exports.runGitSync = runGitSync;
|
|
257
339
|
exports.runNpm = runNpm;
|
|
340
|
+
exports.runNpmSync = runNpmSync;
|
|
258
341
|
exports.spawnAsync = spawnAsync;
|
|
342
|
+
exports.stringifyArgs = stringifyArgs;
|
|
259
343
|
exports.toValidPackageName = toValidPackageName;
|
|
260
|
-
exports.toValidProjectName = toValidProjectName;
|
|
261
|
-
exports.yellow = yellow;
|
|
344
|
+
exports.toValidProjectName = toValidProjectName;
|
package/dist/cjs/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ExecOptionsWithStringEncoding, SpawnOptionsWithoutStdio } from "node:child_process";
|
|
1
2
|
import * as child_process0 from "child_process";
|
|
2
|
-
import {
|
|
3
|
+
import { ExecSyncOptionsWithStringEncoding, SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
3
4
|
|
|
4
5
|
//#region src/enums.d.ts
|
|
5
6
|
declare enum PkgManager {
|
|
@@ -38,12 +39,15 @@ interface PkgInfo {
|
|
|
38
39
|
version: string;
|
|
39
40
|
}
|
|
40
41
|
interface ExecResultOptions {
|
|
42
|
+
/** 去掉结果的首尾空格 */
|
|
41
43
|
trim?: boolean;
|
|
44
|
+
/** 仅打印命令,不实际执行命令 */
|
|
42
45
|
dryRun?: boolean;
|
|
46
|
+
/** log: 打印错误信息,返回 undefined; throw: 抛出错误; ignore: 返回 undefined */
|
|
43
47
|
error?: 'log' | 'throw' | 'ignore';
|
|
44
48
|
}
|
|
45
|
-
type SpawnAsyncOptions
|
|
46
|
-
type ExecAsyncOptions
|
|
49
|
+
type SpawnAsyncOptions = SpawnOptionsWithoutStdio & ExecResultOptions;
|
|
50
|
+
type ExecAsyncOptions = ExecOptionsWithStringEncoding & ExecResultOptions;
|
|
47
51
|
//#endregion
|
|
48
52
|
//#region src/utils.d.ts
|
|
49
53
|
declare const isValidPackageName: (packageName: string) => boolean;
|
|
@@ -62,20 +66,49 @@ declare const pkgFromUserAgent: (userAgent: string | undefined) => PkgInfo | und
|
|
|
62
66
|
declare const runCliForTest: (path: string, args: string[], options?: SpawnSyncOptionsWithStringEncoding) => child_process0.SpawnSyncReturns<string>;
|
|
63
67
|
/** 判断测试文件(夹) */
|
|
64
68
|
declare const isTestFile: (name: string) => boolean;
|
|
69
|
+
/** 解析 Github 链接获取 owner 和 repo */
|
|
65
70
|
declare const parseGitHubRepo: (url: string) => string[];
|
|
71
|
+
/** 基于 EOL 的可多换行函数 */
|
|
72
|
+
declare const eol: (n?: number) => string;
|
|
66
73
|
//#endregion
|
|
67
74
|
//#region src/shell.d.ts
|
|
68
|
-
|
|
69
|
-
declare const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
/** 异步执行 `spawn` 获取字符串类型的结果 */
|
|
76
|
+
declare const spawnAsync: (cmd: string, args: string[], options?: SpawnAsyncOptions) => Promise<string | undefined>;
|
|
77
|
+
type ExecAsync = {
|
|
78
|
+
(cmd: string, options?: ExecAsyncOptions): Promise<string | undefined>;
|
|
79
|
+
(cmd: string, args: string[], options?: ExecAsyncOptions): Promise<string | undefined>;
|
|
80
|
+
};
|
|
81
|
+
/** 异步执行 `exec` 获取字符串类型的结果 */
|
|
82
|
+
declare const execAsync: ExecAsync;
|
|
83
|
+
/** 基于 {@link spawnAsync} 实现 */
|
|
84
|
+
declare const runGit: (args: string[], options?: SpawnAsyncOptions) => Promise<string | undefined>;
|
|
85
|
+
/** 基于 {@link execAsync} 实现 */
|
|
86
|
+
declare const runNpm: (args: string[], options?: ExecAsyncOptions) => Promise<string | undefined>;
|
|
87
|
+
/** 基于 {@link spawnSync} 实现 */
|
|
88
|
+
declare const runGitSync: (args: string[], options?: SpawnSyncOptionsWithStringEncoding) => string;
|
|
89
|
+
/** 基于 {@link execSync} 实现 */
|
|
90
|
+
declare const runNpmSync: (args: string[], options?: ExecSyncOptionsWithStringEncoding) => string;
|
|
91
|
+
/** 将字符串以空格分割为数组 */
|
|
92
|
+
declare const parseArgs: (args: string) => string[];
|
|
93
|
+
/** 将数组以空格拼接为字符串 */
|
|
94
|
+
declare const stringifyArgs: (args: string[]) => string;
|
|
95
|
+
/** 支持所有支持 `--version` 命令的脚本查看版本 */
|
|
96
|
+
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/joinUrl.d.ts
|
|
99
|
+
declare function joinUrl(...args: string[]): string;
|
|
100
|
+
declare function joinUrl(input: readonly string[]): string;
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/git.d.ts
|
|
103
|
+
/** 判断指定目录是否是 git 仓库 */
|
|
75
104
|
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
105
|
+
/** 获取指定的 git 配置 */
|
|
76
106
|
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
107
|
+
/** 获取 git 远程地址 */
|
|
77
108
|
declare const getGitRemoteUrl: (remoteName?: string) => Promise<string | undefined>;
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/npm.d.ts
|
|
111
|
+
/** 获取指定包的版本 */
|
|
78
112
|
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
79
|
-
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
80
113
|
//#endregion
|
|
81
|
-
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync,
|
|
114
|
+
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, eol, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, joinUrl, parseArgs, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, runCliForTest, runGit, runGitSync, runNpm, runNpmSync, spawnAsync, stringifyArgs, toValidPackageName, toValidProjectName };
|
package/dist/esm/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { ExecOptionsWithStringEncoding, SpawnOptionsWithoutStdio } from "node:child_process";
|
|
1
2
|
import * as child_process0 from "child_process";
|
|
2
|
-
import {
|
|
3
|
+
import { ExecSyncOptionsWithStringEncoding, SpawnSyncOptionsWithStringEncoding } from "child_process";
|
|
3
4
|
|
|
4
5
|
//#region src/enums.d.ts
|
|
5
6
|
declare enum PkgManager {
|
|
@@ -38,12 +39,15 @@ interface PkgInfo {
|
|
|
38
39
|
version: string;
|
|
39
40
|
}
|
|
40
41
|
interface ExecResultOptions {
|
|
42
|
+
/** 去掉结果的首尾空格 */
|
|
41
43
|
trim?: boolean;
|
|
44
|
+
/** 仅打印命令,不实际执行命令 */
|
|
42
45
|
dryRun?: boolean;
|
|
46
|
+
/** log: 打印错误信息,返回 undefined; throw: 抛出错误; ignore: 返回 undefined */
|
|
43
47
|
error?: 'log' | 'throw' | 'ignore';
|
|
44
48
|
}
|
|
45
|
-
type SpawnAsyncOptions
|
|
46
|
-
type ExecAsyncOptions
|
|
49
|
+
type SpawnAsyncOptions = SpawnOptionsWithoutStdio & ExecResultOptions;
|
|
50
|
+
type ExecAsyncOptions = ExecOptionsWithStringEncoding & ExecResultOptions;
|
|
47
51
|
//#endregion
|
|
48
52
|
//#region src/utils.d.ts
|
|
49
53
|
declare const isValidPackageName: (packageName: string) => boolean;
|
|
@@ -62,20 +66,49 @@ declare const pkgFromUserAgent: (userAgent: string | undefined) => PkgInfo | und
|
|
|
62
66
|
declare const runCliForTest: (path: string, args: string[], options?: SpawnSyncOptionsWithStringEncoding) => child_process0.SpawnSyncReturns<string>;
|
|
63
67
|
/** 判断测试文件(夹) */
|
|
64
68
|
declare const isTestFile: (name: string) => boolean;
|
|
69
|
+
/** 解析 Github 链接获取 owner 和 repo */
|
|
65
70
|
declare const parseGitHubRepo: (url: string) => string[];
|
|
71
|
+
/** 基于 EOL 的可多换行函数 */
|
|
72
|
+
declare const eol: (n?: number) => string;
|
|
66
73
|
//#endregion
|
|
67
74
|
//#region src/shell.d.ts
|
|
68
|
-
|
|
69
|
-
declare const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
+
/** 异步执行 `spawn` 获取字符串类型的结果 */
|
|
76
|
+
declare const spawnAsync: (cmd: string, args: string[], options?: SpawnAsyncOptions) => Promise<string | undefined>;
|
|
77
|
+
type ExecAsync = {
|
|
78
|
+
(cmd: string, options?: ExecAsyncOptions): Promise<string | undefined>;
|
|
79
|
+
(cmd: string, args: string[], options?: ExecAsyncOptions): Promise<string | undefined>;
|
|
80
|
+
};
|
|
81
|
+
/** 异步执行 `exec` 获取字符串类型的结果 */
|
|
82
|
+
declare const execAsync: ExecAsync;
|
|
83
|
+
/** 基于 {@link spawnAsync} 实现 */
|
|
84
|
+
declare const runGit: (args: string[], options?: SpawnAsyncOptions) => Promise<string | undefined>;
|
|
85
|
+
/** 基于 {@link execAsync} 实现 */
|
|
86
|
+
declare const runNpm: (args: string[], options?: ExecAsyncOptions) => Promise<string | undefined>;
|
|
87
|
+
/** 基于 {@link spawnSync} 实现 */
|
|
88
|
+
declare const runGitSync: (args: string[], options?: SpawnSyncOptionsWithStringEncoding) => string;
|
|
89
|
+
/** 基于 {@link execSync} 实现 */
|
|
90
|
+
declare const runNpmSync: (args: string[], options?: ExecSyncOptionsWithStringEncoding) => string;
|
|
91
|
+
/** 将字符串以空格分割为数组 */
|
|
92
|
+
declare const parseArgs: (args: string) => string[];
|
|
93
|
+
/** 将数组以空格拼接为字符串 */
|
|
94
|
+
declare const stringifyArgs: (args: string[]) => string;
|
|
95
|
+
/** 支持所有支持 `--version` 命令的脚本查看版本 */
|
|
96
|
+
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
97
|
+
//#endregion
|
|
98
|
+
//#region src/joinUrl.d.ts
|
|
99
|
+
declare function joinUrl(...args: string[]): string;
|
|
100
|
+
declare function joinUrl(input: readonly string[]): string;
|
|
101
|
+
//#endregion
|
|
102
|
+
//#region src/git.d.ts
|
|
103
|
+
/** 判断指定目录是否是 git 仓库 */
|
|
75
104
|
declare const isGitRepo: (dir?: string) => Promise<boolean>;
|
|
105
|
+
/** 获取指定的 git 配置 */
|
|
76
106
|
declare const getGitConfig: (key: string, global?: boolean) => Promise<string | undefined>;
|
|
107
|
+
/** 获取 git 远程地址 */
|
|
77
108
|
declare const getGitRemoteUrl: (remoteName?: string) => Promise<string | undefined>;
|
|
109
|
+
//#endregion
|
|
110
|
+
//#region src/npm.d.ts
|
|
111
|
+
/** 获取指定包的版本 */
|
|
78
112
|
declare const pkgVersion: (pkg: string) => Promise<string | undefined>;
|
|
79
|
-
declare const checkVersion: (cmd: string) => Promise<string | undefined>;
|
|
80
113
|
//#endregion
|
|
81
|
-
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync,
|
|
114
|
+
export { CliOptions, ConfirmResult, CopyOptions, ExecAsyncOptions, ExecResultOptions, HttpLibrary, PkgInfo, PkgManager, SpawnAsyncOptions, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, eol, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, joinUrl, parseArgs, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, runCliForTest, runGit, runGitSync, runNpm, runNpmSync, spawnAsync, stringifyArgs, toValidPackageName, toValidProjectName };
|
package/dist/esm/index.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { exec, spawn, spawnSync } from "node:child_process";
|
|
1
|
+
import { exec, execSync, 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
|
+
import { EOL } from "node:os";
|
|
4
5
|
import { join, resolve } from "node:path";
|
|
5
6
|
import { styleText } from "node:util";
|
|
6
7
|
|
|
@@ -125,31 +126,37 @@ const isTestFile = (name) => {
|
|
|
125
126
|
/^vitest([-.])(.*)\.m?(ts|js)$/
|
|
126
127
|
].some((reg) => reg.test(name));
|
|
127
128
|
};
|
|
129
|
+
/** 解析 Github 链接获取 owner 和 repo */
|
|
128
130
|
const parseGitHubRepo = (url) => {
|
|
129
131
|
const match = url.trim().match(/github(?:\.com)?[:/](.+?)\/(.+?)(?:[#/?].+?)?(?:\.git)?$/);
|
|
130
132
|
return match ? match.slice(1, 3) : [];
|
|
131
133
|
};
|
|
134
|
+
/** 基于 EOL 的可多换行函数 */
|
|
135
|
+
const eol = (n = 1) => EOL.repeat(n);
|
|
132
136
|
|
|
133
137
|
//#endregion
|
|
134
|
-
//#region src/
|
|
138
|
+
//#region src/styleText.ts
|
|
135
139
|
const dim = (text) => styleText(["dim"], text);
|
|
136
140
|
const red = (text) => styleText(["red"], text);
|
|
137
|
-
|
|
141
|
+
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/shell.ts
|
|
144
|
+
/** 异步执行 `spawn` 获取字符串类型的结果 */
|
|
138
145
|
const spawnAsync = (cmd, args, options) => {
|
|
139
146
|
return new Promise((resolve$1) => {
|
|
140
147
|
const { trim, error, dryRun, ...others } = options ?? {};
|
|
141
|
-
const fullCmd = [cmd, ...args]
|
|
148
|
+
const fullCmd = stringifyArgs([cmd, ...args]);
|
|
142
149
|
if (dryRun) {
|
|
143
150
|
console.log(`${dim("[dry-run]")} ${fullCmd}`);
|
|
144
151
|
return resolve$1(void 0);
|
|
145
152
|
}
|
|
146
153
|
const child = spawn(cmd, args, { ...others });
|
|
147
154
|
let stdout = "";
|
|
148
|
-
child.stdout
|
|
155
|
+
child.stdout.setEncoding("utf-8");
|
|
149
156
|
child.stdout?.on("data", (data) => stdout += trim ? data.trim() : data);
|
|
150
157
|
let stderr = "";
|
|
151
|
-
child.stderr
|
|
152
|
-
child.stderr
|
|
158
|
+
child.stderr.setEncoding("utf-8");
|
|
159
|
+
child.stderr.on("data", (data) => stderr += trim ? data.trim() : data);
|
|
153
160
|
child.on("close", (code) => {
|
|
154
161
|
if (stderr) {
|
|
155
162
|
const err = `${red("spawnAsync")} ${dim(fullCmd)} ${stderr}`;
|
|
@@ -164,41 +171,112 @@ const spawnAsync = (cmd, args, options) => {
|
|
|
164
171
|
});
|
|
165
172
|
});
|
|
166
173
|
};
|
|
167
|
-
|
|
174
|
+
/** 异步执行 `exec` 获取字符串类型的结果 */
|
|
175
|
+
const execAsync = (cmd, argsOrOptions, maybeOptions) => {
|
|
168
176
|
return new Promise((resolve$1) => {
|
|
177
|
+
let command;
|
|
178
|
+
let options;
|
|
179
|
+
if (Array.isArray(argsOrOptions)) {
|
|
180
|
+
command = stringifyArgs([cmd, ...argsOrOptions]);
|
|
181
|
+
options = maybeOptions;
|
|
182
|
+
} else {
|
|
183
|
+
command = cmd;
|
|
184
|
+
options = argsOrOptions;
|
|
185
|
+
}
|
|
169
186
|
const { trim, dryRun, error, ...others } = options ?? {};
|
|
170
187
|
if (dryRun) {
|
|
171
|
-
console.log(`${dim("[dry-run]")} ${
|
|
188
|
+
console.log(`${dim("[dry-run]")} ${command}`);
|
|
172
189
|
return resolve$1(void 0);
|
|
173
190
|
}
|
|
174
|
-
exec(
|
|
175
|
-
if (
|
|
176
|
-
const
|
|
191
|
+
exec(command, { ...others }, (stderr, stdout) => {
|
|
192
|
+
if (stderr) {
|
|
193
|
+
const err = `${red("execAsync")} ${dim(command)} ${stderr.message}`;
|
|
177
194
|
switch (error) {
|
|
178
195
|
case "log":
|
|
179
|
-
console.error(
|
|
196
|
+
console.error(err);
|
|
180
197
|
break;
|
|
181
|
-
case "throw": throw new Error(
|
|
198
|
+
case "throw": throw new Error(err);
|
|
182
199
|
}
|
|
183
200
|
}
|
|
184
|
-
resolve$1(
|
|
201
|
+
resolve$1(stderr ? void 0 : trim ? stdout.trim() : stdout);
|
|
185
202
|
});
|
|
186
203
|
});
|
|
187
204
|
};
|
|
188
|
-
|
|
189
|
-
|
|
205
|
+
/** 基于 {@link spawnAsync} 实现 */
|
|
206
|
+
const runGit = async (args, options = { trim: true }) => {
|
|
207
|
+
return spawnAsync("git", args, options);
|
|
190
208
|
};
|
|
191
|
-
|
|
192
|
-
|
|
209
|
+
/** 基于 {@link execAsync} 实现 */
|
|
210
|
+
const runNpm = (args, options = { trim: true }) => {
|
|
211
|
+
return execAsync("npm", args, options);
|
|
212
|
+
};
|
|
213
|
+
/** 基于 {@link spawnSync} 实现 */
|
|
214
|
+
const runGitSync = (args, options) => {
|
|
215
|
+
const { stdout } = spawnSync("git", args, {
|
|
216
|
+
encoding: "utf-8",
|
|
217
|
+
...options
|
|
218
|
+
});
|
|
219
|
+
return stdout.toString().trim();
|
|
220
|
+
};
|
|
221
|
+
/** 基于 {@link execSync} 实现 */
|
|
222
|
+
const runNpmSync = (args, options) => {
|
|
223
|
+
return execSync(stringifyArgs(["npm", ...args]), {
|
|
224
|
+
encoding: "utf-8",
|
|
225
|
+
...options
|
|
226
|
+
}).toString().trim();
|
|
193
227
|
};
|
|
228
|
+
/** 将字符串以空格分割为数组 */
|
|
229
|
+
const parseArgs = (args) => args.trim() ? args.trim().split(" ") : [];
|
|
230
|
+
/** 将数组以空格拼接为字符串 */
|
|
231
|
+
const stringifyArgs = (args) => args.length ? args.join(" ") : "";
|
|
232
|
+
/** 支持所有支持 `--version` 命令的脚本查看版本 */
|
|
233
|
+
const checkVersion = async (cmd) => {
|
|
234
|
+
return execAsync(`${cmd} --version`);
|
|
235
|
+
};
|
|
236
|
+
|
|
237
|
+
//#endregion
|
|
238
|
+
//#region src/joinUrl.ts
|
|
239
|
+
function joinUrl(input) {
|
|
240
|
+
const temps = Array.isArray(input) ? input : [...arguments];
|
|
241
|
+
if (temps.length === 0) return "";
|
|
242
|
+
const result = [];
|
|
243
|
+
const parts = [...temps];
|
|
244
|
+
/** 协议正则 */
|
|
245
|
+
const PROTOCOL_RE = /^[^/:]+:\/*$/;
|
|
246
|
+
const FILE_PROTOCOL_RE = /^file:\/\/\//;
|
|
247
|
+
if (PROTOCOL_RE.test(parts[0]) && parts.length > 1) {
|
|
248
|
+
parts[1] = parts[0] + parts[1];
|
|
249
|
+
parts.shift();
|
|
250
|
+
}
|
|
251
|
+
if (FILE_PROTOCOL_RE.test(parts[0])) parts[0] = parts[0].replace(/^([^/:]+):\/*/, "$1:///");
|
|
252
|
+
else parts[0] = parts[0].replace(/^([^/:]+):\/*/, "$1://");
|
|
253
|
+
parts.forEach((part, index) => {
|
|
254
|
+
if (!part) return;
|
|
255
|
+
let segment = part;
|
|
256
|
+
if (index > 0) segment = segment.replace(/^\/+/, "");
|
|
257
|
+
if (index < parts.length - 1) segment = segment.replace(/\/+$/, "");
|
|
258
|
+
else segment = segment.replace(/\/+$/, "/");
|
|
259
|
+
result.push(segment);
|
|
260
|
+
});
|
|
261
|
+
let url = result.join("/");
|
|
262
|
+
url = url.replace(/\/(\?|&|#[^!])/g, "$1");
|
|
263
|
+
const [base, ...queryParts] = url.split("?");
|
|
264
|
+
url = base + (queryParts.length ? "?" + queryParts.join("&") : "");
|
|
265
|
+
return url;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/git.ts
|
|
270
|
+
/** 判断指定目录是否是 git 仓库 */
|
|
194
271
|
const isGitRepo = async (dir) => {
|
|
195
|
-
return
|
|
272
|
+
return "true" === await runGit([
|
|
196
273
|
"-C",
|
|
197
|
-
|
|
274
|
+
resolve(process.cwd(), dir || "."),
|
|
198
275
|
"rev-parse",
|
|
199
276
|
"--is-inside-work-tree"
|
|
200
277
|
]);
|
|
201
278
|
};
|
|
279
|
+
/** 获取指定的 git 配置 */
|
|
202
280
|
const getGitConfig = (key, global = true) => {
|
|
203
281
|
return runGit([
|
|
204
282
|
"config",
|
|
@@ -206,6 +284,7 @@ const getGitConfig = (key, global = true) => {
|
|
|
206
284
|
key
|
|
207
285
|
]);
|
|
208
286
|
};
|
|
287
|
+
/** 获取 git 远程地址 */
|
|
209
288
|
const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
210
289
|
return runGit([
|
|
211
290
|
"remote",
|
|
@@ -217,6 +296,10 @@ const getGitRemoteUrl = async (remoteName = "origin") => {
|
|
|
217
296
|
`remote.${remoteName}.url`
|
|
218
297
|
]));
|
|
219
298
|
};
|
|
299
|
+
|
|
300
|
+
//#endregion
|
|
301
|
+
//#region src/npm.ts
|
|
302
|
+
/** 获取指定包的版本 */
|
|
220
303
|
const pkgVersion = (pkg) => {
|
|
221
304
|
return runNpm([
|
|
222
305
|
"view",
|
|
@@ -224,9 +307,6 @@ const pkgVersion = (pkg) => {
|
|
|
224
307
|
"version"
|
|
225
308
|
]);
|
|
226
309
|
};
|
|
227
|
-
const checkVersion = async (cmd) => {
|
|
228
|
-
return execAsync(`${cmd} --version`);
|
|
229
|
-
};
|
|
230
310
|
|
|
231
311
|
//#endregion
|
|
232
|
-
export { ConfirmResult, HttpLibrary, PkgManager, YesOrNo, checkVersion, copyDirAsync,
|
|
312
|
+
export { ConfirmResult, HttpLibrary, PkgManager, YesOrNo, checkVersion, copyDirAsync, editFile, editJsonFile, emptyDir, eol, execAsync, getGitConfig, getGitRemoteUrl, isEmpty, isGitRepo, isTestFile, isValidPackageName, joinUrl, parseArgs, parseGitHubRepo, pkgFromUserAgent, pkgVersion, readJsonFile, readSubDirs, runCliForTest, runGit, runGitSync, runNpm, runNpmSync, spawnAsync, stringifyArgs, toValidPackageName, toValidProjectName };
|