@quiteer/scripts 0.0.1

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2021 Soybean
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,4 @@
1
+ //#region src/index.d.ts
2
+ declare function setupCli(): Promise<void>;
3
+ //#endregion
4
+ export { setupCli };
package/dist/index.mjs ADDED
@@ -0,0 +1,327 @@
1
+ import cac from "cac";
2
+ import { bgRed, blue, gray, green, lightGreen, red, yellow } from "kolorist";
3
+ import { rimraf } from "rimraf";
4
+ import { access, writeFile } from "node:fs/promises";
5
+ import path from "node:path";
6
+ import process from "node:process";
7
+ import { prompt } from "enquirer";
8
+ import { loadConfig } from "c12";
9
+ import { readFileSync } from "node:fs";
10
+ import { versionBump } from "bumpp";
11
+
12
+ //#region package.json
13
+ var version = "0.0.1";
14
+
15
+ //#endregion
16
+ //#region src/commands/cleanup.ts
17
+ async function cleanup(paths) {
18
+ await rimraf(paths, { glob: true });
19
+ }
20
+
21
+ //#endregion
22
+ //#region src/config/index.ts
23
+ const defaultOptions = {
24
+ cwd: process.cwd(),
25
+ cleanupDirs: [
26
+ "**/dist",
27
+ "**/node_modules",
28
+ "!node_modules/**"
29
+ ],
30
+ lang: "zh-cn",
31
+ ncuCommandArgs: ["--deep", "-u"],
32
+ gitCommitVerifyIgnores: [
33
+ /^((Merge pull request)|(Merge (.*?) into (.*)|(Merge branch (.*)))(?:\r?\n)*$)/m,
34
+ /^(Merge tag (.*))(?:\r?\n)*$/m,
35
+ /^(R|r)evert (.*)/,
36
+ /^(amend|fixup|squash)!/,
37
+ /^(Merged (.*?)(in|into) (.*)|Merged PR (.*): (.*))/,
38
+ /^Merge remote-tracking branch(\s*)(.*)/,
39
+ /^Automatic merge(.*)/,
40
+ /^Auto-merged (.*?) into (.*)/
41
+ ]
42
+ };
43
+ async function loadCliOptions(overrides, cwd = process.cwd()) {
44
+ const { config } = await loadConfig({
45
+ name: "quiteer",
46
+ defaults: defaultOptions,
47
+ overrides,
48
+ cwd,
49
+ packageJson: true
50
+ });
51
+ return config;
52
+ }
53
+
54
+ //#endregion
55
+ //#region src/commands/generate-cfg.ts
56
+ /**
57
+ * 函数:在项目根目录生成 quiteer 配置文件
58
+ * 作用:若已有配置则询问是否覆盖;按默认配置生成 `quiteer.config.ts`
59
+ */
60
+ async function generateConfig() {
61
+ const cwd = process.cwd();
62
+ const configPath = path.join(cwd, "quiteer.config.ts");
63
+ let exists = true;
64
+ try {
65
+ await access(configPath);
66
+ } catch {
67
+ exists = false;
68
+ }
69
+ if (exists) {
70
+ const { overwrite } = await prompt([{
71
+ name: "overwrite",
72
+ type: "confirm",
73
+ message: `检测到已存在配置文件 ${path.basename(configPath)},是否覆盖?`
74
+ }]);
75
+ if (!overwrite) return;
76
+ }
77
+ const regexList = defaultOptions.gitCommitVerifyIgnores.map((r) => r.toString());
78
+ const indentJson = (val) => {
79
+ return JSON.stringify(val, null, 2).split("\n").map((line, i) => i === 0 ? line : ` ${line}`).join("\n");
80
+ };
81
+ await writeFile(configPath, [
82
+ "// 自动生成的 quiteer 配置文件",
83
+ "",
84
+ "export default {",
85
+ ` cleanupDirs: ${indentJson(defaultOptions.cleanupDirs)},`,
86
+ ` lang: ${JSON.stringify(defaultOptions.lang)},`,
87
+ ` ncuCommandArgs: ${indentJson(defaultOptions.ncuCommandArgs)},`,
88
+ " gitCommitVerifyIgnores: [",
89
+ ...regexList.map((r, idx, arr) => ` ${r}${idx < arr.length - 1 ? "," : ""}`),
90
+ " ]",
91
+ "}",
92
+ ""
93
+ ].join("\n"), "utf8");
94
+ }
95
+
96
+ //#endregion
97
+ //#region src/locales/index.ts
98
+ const locales = {
99
+ "zh-cn": {
100
+ gitCommitMessages: {
101
+ types: "请选择提交类型",
102
+ scopes: "请选择提交范围",
103
+ description: `请输入描述信息(${yellow("!")}开头表示破坏性改动`
104
+ },
105
+ gitCommitTypes: [
106
+ ["feat", "新功能"],
107
+ ["feat-wip", "开发中的功能,比如某功能的部分代码"],
108
+ ["fix", "修复Bug"],
109
+ ["docs", "只涉及文档更新"],
110
+ ["typo", "代码或文档勘误,比如错误拼写"],
111
+ ["style", "修改代码风格,不影响代码含义的变更"],
112
+ ["refactor", "代码重构,既不修复 bug 也不添加功能的代码变更"],
113
+ ["perf", "可提高性能的代码更改"],
114
+ ["optimize", "优化代码质量的代码更改"],
115
+ ["test", "添加缺失的测试或更正现有测试"],
116
+ ["build", "影响构建系统或外部依赖项的更改"],
117
+ ["ci", "对 CI 配置文件和脚本的更改"],
118
+ ["chore", "没有修改src或测试文件的其他变更"],
119
+ ["revert", "还原先前的提交"]
120
+ ],
121
+ gitCommitScopes: [
122
+ ["projects", "项目"],
123
+ ["packages", "包"],
124
+ ["components", "组件"],
125
+ ["hooks", "钩子函数"],
126
+ ["utils", "工具函数"],
127
+ ["types", "TS类型声明"],
128
+ ["styles", "代码风格"],
129
+ ["deps", "项目依赖"],
130
+ ["release", "发布项目新版本"],
131
+ ["other", "其他的变更"]
132
+ ],
133
+ gitCommitVerify: `${bgRed(" 错误 ")} ${red("git 提交信息必须符合 Conventional Commits 标准!")}\n\n${green("推荐使用命令 `pnpm commit` 生成符合 Conventional Commits 标准的提交信息。\n获取有关 Conventional Commits 的更多信息,请访问此链接: https://conventionalcommits.org")}`
134
+ },
135
+ "en-us": {
136
+ gitCommitMessages: {
137
+ types: "Please select a type",
138
+ scopes: "Please select a scope",
139
+ description: `Please enter a description (add prefix ${yellow("!")} to indicate breaking change)`
140
+ },
141
+ gitCommitTypes: [
142
+ ["feat", "A new feature"],
143
+ ["feat-wip", "Features in development, such as partial code for a certain feature"],
144
+ ["fix", "A bug fix"],
145
+ ["docs", "Documentation only changes"],
146
+ ["typo", "Code or document corrections, such as spelling errors"],
147
+ ["style", "Changes that do not affect the meaning of the code"],
148
+ ["refactor", "A code change that neither fixes a bug nor adds a feature"],
149
+ ["perf", "A code change that improves performance"],
150
+ ["optimize", "A code change that optimizes code quality"],
151
+ ["test", "Adding missing tests or correcting existing tests"],
152
+ ["build", "Changes that affect the build system or external dependencies"],
153
+ ["ci", "Changes to our CI configuration files and scripts"],
154
+ ["chore", "Other changes that don't modify src or test files"],
155
+ ["revert", "Reverts a previous commit"]
156
+ ],
157
+ gitCommitScopes: [
158
+ ["projects", "project"],
159
+ ["packages", "packages"],
160
+ ["components", "components"],
161
+ ["hooks", "hook functions"],
162
+ ["utils", "utils functions"],
163
+ ["types", "TS declaration"],
164
+ ["styles", "style"],
165
+ ["deps", "project dependencies"],
166
+ ["release", "release project"],
167
+ ["other", "other changes"]
168
+ ],
169
+ gitCommitVerify: `${bgRed(" ERROR ")} ${red("git commit message must match the Conventional Commits standard!")}\n\n${green("Recommended to use the command `pnpm commit` to generate Conventional Commits compliant commit information.\nGet more info about Conventional Commits, follow this link: https://conventionalcommits.org")}`
170
+ }
171
+ };
172
+
173
+ //#endregion
174
+ //#region src/shared/index.ts
175
+ async function execCommand(cmd, args, options) {
176
+ const { execa } = await import("execa");
177
+ return ((await execa(cmd, args, options))?.stdout)?.trim() || "";
178
+ }
179
+
180
+ //#endregion
181
+ //#region src/commands/git-commit.ts
182
+ /**
183
+ * 函数:交互式添加变更文件到暂存区
184
+ * 作用:先询问是否添加全部;若否,列出变更文件供多选后执行 git add
185
+ */
186
+ async function gitCommitAdd() {
187
+ const { confirm } = await prompt([{
188
+ name: "confirm",
189
+ type: "confirm",
190
+ message: "是否添加所有变更文件到暂存区?"
191
+ }]);
192
+ if (!confirm) {
193
+ const files = (await execCommand("git", ["diff", "--name-only"])).split("\n").filter(Boolean);
194
+ if (files.length === 0) return;
195
+ const { selected } = await prompt([{
196
+ name: "selected",
197
+ type: "multiselect",
198
+ message: "选择需要添加到暂存区的文件(空格选择,回车确认)",
199
+ choices: files.map((f) => ({
200
+ name: f,
201
+ value: f
202
+ }))
203
+ }]);
204
+ if (!selected?.length) return;
205
+ await execCommand("git", ["add", ...selected], { stdio: "inherit" });
206
+ return;
207
+ }
208
+ await execCommand("git", ["add", "."], { stdio: "inherit" });
209
+ }
210
+ /**
211
+ * Git commit with Conventional Commits standard
212
+ *
213
+ * @param lang
214
+ */
215
+ async function gitCommit(lang = "en-us") {
216
+ const { gitCommitMessages, gitCommitTypes, gitCommitScopes } = locales[lang];
217
+ const typesChoices = gitCommitTypes.map(([value, msg]) => {
218
+ return {
219
+ name: value,
220
+ message: `${`${value}:`.padEnd(12)}${msg}`
221
+ };
222
+ });
223
+ const scopesChoices = gitCommitScopes.map(([value, msg]) => ({
224
+ name: value,
225
+ message: `${value.padEnd(30)} (${msg})`
226
+ }));
227
+ const result = await prompt([
228
+ {
229
+ name: "types",
230
+ type: "select",
231
+ message: gitCommitMessages.types,
232
+ choices: typesChoices
233
+ },
234
+ {
235
+ name: "scopes",
236
+ type: "select",
237
+ message: gitCommitMessages.scopes,
238
+ choices: scopesChoices
239
+ },
240
+ {
241
+ name: "description",
242
+ type: "text",
243
+ message: gitCommitMessages.description
244
+ }
245
+ ]);
246
+ const breaking = result.description.startsWith("!") ? "!" : "";
247
+ const description = result.description.replace(/^!/, "").trim();
248
+ await execCommand("git", [
249
+ "commit",
250
+ "-m",
251
+ `${result.types}(${result.scopes})${breaking}: ${description}`
252
+ ], { stdio: "inherit" });
253
+ }
254
+ /** Git commit message verify */
255
+ async function gitCommitVerify(lang = "zh-cn", ignores = []) {
256
+ const gitPath = await execCommand("git", ["rev-parse", "--show-toplevel"]);
257
+ const commitMsg = readFileSync(path.join(gitPath, ".git", "COMMIT_EDITMSG"), "utf8").trim();
258
+ if (ignores.some((regExp) => regExp.test(commitMsg))) return;
259
+ if (!/(?<type>[a-z]+)(?:\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i.test(commitMsg)) {
260
+ const errorMsg = locales[lang].gitCommitVerify;
261
+ throw new Error(errorMsg);
262
+ }
263
+ }
264
+
265
+ //#endregion
266
+ //#region src/commands/release.ts
267
+ async function release(execute = "", push = true) {
268
+ await versionBump({
269
+ files: ["**/package.json", "!**/node_modules"],
270
+ execute,
271
+ all: true,
272
+ tag: true,
273
+ commit: "chore(projects): release v%s",
274
+ push
275
+ });
276
+ }
277
+
278
+ //#endregion
279
+ //#region src/commands/update-pkg.ts
280
+ async function updatePkg(args = ["--deep", "-u"]) {
281
+ execCommand("npx", ["npm-check-updates", ...args], { stdio: "inherit" });
282
+ }
283
+
284
+ //#endregion
285
+ //#region src/index.ts
286
+ async function setupCli() {
287
+ /**
288
+ * 初始化并注册 CLI
289
+ * - 加载配置,注册命令与选项
290
+ * - 设置中文提示与帮助文案
291
+ * @returns Promise<void>
292
+ */
293
+ const cliOptions = await loadCliOptions();
294
+ const cli = cac(blue("quiteer-scripts"));
295
+ cli.version(lightGreen(version)).help();
296
+ cli.command("generate-config", "在项目根目录下生成配置文件").alias("g").action(async () => {
297
+ await generateConfig();
298
+ });
299
+ cli.command("remove [path]", "删除单个或者多个文件,多个值用逗号分隔,递归删除").alias("rm").action(async (paths) => {
300
+ if (paths && paths.includes(",")) await cleanup(paths.split(","));
301
+ else if (paths) await cleanup([paths]);
302
+ else console.info("quiteer-script :>> ", gray("无事发生"));
303
+ });
304
+ cli.command("cleanup [path]", "清除目录 node_modules、dist 等").alias("c").action(async (paths) => {
305
+ if (paths && paths.includes(",")) await cleanup(paths.split(","));
306
+ else if (paths) await cleanup([paths]);
307
+ else await cleanup(cliOptions.cleanupDirs);
308
+ });
309
+ cli.command("update-pkg", "更新 package.json 依赖版本").alias("u").action(async () => {
310
+ await updatePkg(cliOptions.ncuCommandArgs);
311
+ });
312
+ cli.command("git-commit", "git 提交前后的操作和规范等").alias("git-c").option("--add", "添加所有变更文件到暂存区", { default: true }).option("-l ,--lang", "校验提交信息的语言", { default: cliOptions.lang }).action(async (args) => {
313
+ if (args?.add) await gitCommitAdd();
314
+ await gitCommit(args?.lang);
315
+ });
316
+ cli.command("git-commit-verify", "校验提交信息是否符合 Conventional Commits 标准").alias("git-v").option("-l --lang", "校验提交信息的语言", { default: cliOptions.lang }).action(async (args) => {
317
+ await gitCommitVerify(args?.lang, cliOptions.gitCommitVerifyIgnores);
318
+ });
319
+ cli.command("release", "发布:更新版本、生成变更日志、提交代码").alias("r").option("--execute", "执行发布的命令").option("--push", "是否推送代码", { default: true }).action(async (args) => {
320
+ await release(args?.execute, args?.push);
321
+ });
322
+ cli.parse();
323
+ }
324
+ setupCli();
325
+
326
+ //#endregion
327
+ export { setupCli };
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@quiteer/scripts",
3
+ "type": "module",
4
+ "version": "0.0.1",
5
+ "homepage": "https://github.com/TaiAiAc/web",
6
+ "publishConfig": {
7
+ "access": "public",
8
+ "registry": "https://registry.npmjs.org/"
9
+ },
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.mts",
13
+ "import": "./dist/index.mjs",
14
+ "require": "./dist/index.mjs"
15
+ }
16
+ },
17
+ "main": "./dist/index.mjs",
18
+ "module": "./dist/index.mjs",
19
+ "types": "./dist/index.d.mts",
20
+ "typesVersions": {
21
+ "*": {
22
+ "*": [
23
+ "./src/*"
24
+ ]
25
+ }
26
+ },
27
+ "bin": {
28
+ "quiteer": "./dist/index.mjs",
29
+ "qui": "./dist/index.mjs"
30
+ },
31
+ "files": [
32
+ "dist"
33
+ ],
34
+ "dependencies": {
35
+ "bumpp": "10.3.1",
36
+ "c12": "3.3.2",
37
+ "cac": "6.7.14",
38
+ "consola": "3.4.2",
39
+ "enquirer": "2.4.1",
40
+ "execa": "9.6.0",
41
+ "kolorist": "1.8.0",
42
+ "npm-check-updates": "19.1.2",
43
+ "rimraf": "6.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "tsdown": "^0.16.1",
47
+ "tsx": "^4.20.6"
48
+ },
49
+ "scripts": {
50
+ "dev": "tsdown -w",
51
+ "build": "tsdown",
52
+ "release": "pnpm publish"
53
+ }
54
+ }