@soybeanjs/cli 0.4.1 → 0.5.0
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/index.cjs +587 -11
- package/dist/index.mjs +586 -13
- package/package.json +12 -7
package/dist/index.cjs
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
const cac = require('cac');
|
|
5
5
|
const enquirer = require('enquirer');
|
|
6
|
-
const execa = require('execa');
|
|
7
6
|
const actualFS = require('fs');
|
|
8
7
|
const kolorist = require('kolorist');
|
|
9
8
|
const rimraf = require('rimraf');
|
|
@@ -13,6 +12,11 @@ const promises = require('fs/promises');
|
|
|
13
12
|
const EE = require('events');
|
|
14
13
|
const Stream = require('stream');
|
|
15
14
|
const stringdecoder = require('string_decoder');
|
|
15
|
+
const cliProgress = require('cli-progress');
|
|
16
|
+
const ofetch = require('ofetch');
|
|
17
|
+
const dayjs = require('dayjs');
|
|
18
|
+
const convertGitmoji = require('convert-gitmoji');
|
|
19
|
+
const versionBumpp = require('bumpp');
|
|
16
20
|
|
|
17
21
|
function _interopDefaultCompat (e) { return e && typeof e === 'object' && 'default' in e ? e.default : e; }
|
|
18
22
|
|
|
@@ -34,8 +38,55 @@ const actualFS__namespace = /*#__PURE__*/_interopNamespaceCompat(actualFS);
|
|
|
34
38
|
const EE__default = /*#__PURE__*/_interopDefaultCompat(EE);
|
|
35
39
|
const Stream__default = /*#__PURE__*/_interopDefaultCompat(Stream);
|
|
36
40
|
const stringdecoder__default = /*#__PURE__*/_interopDefaultCompat(stringdecoder);
|
|
41
|
+
const cliProgress__default = /*#__PURE__*/_interopDefaultCompat(cliProgress);
|
|
42
|
+
const dayjs__default = /*#__PURE__*/_interopDefaultCompat(dayjs);
|
|
43
|
+
const versionBumpp__default = /*#__PURE__*/_interopDefaultCompat(versionBumpp);
|
|
37
44
|
|
|
38
|
-
const version = "0.
|
|
45
|
+
const version = "0.5.0";
|
|
46
|
+
|
|
47
|
+
async function execCommand(cmd, args, options) {
|
|
48
|
+
const { execa } = await import('execa');
|
|
49
|
+
const res = await execa(cmd, args, options);
|
|
50
|
+
return res?.stdout?.trim() || "";
|
|
51
|
+
}
|
|
52
|
+
function notNullish(v) {
|
|
53
|
+
return v !== null && v !== void 0;
|
|
54
|
+
}
|
|
55
|
+
function partition(array, ...filters) {
|
|
56
|
+
const result = new Array(filters.length + 1).fill(null).map(() => []);
|
|
57
|
+
array.forEach((e, idx, arr) => {
|
|
58
|
+
let i = 0;
|
|
59
|
+
for (const filter of filters) {
|
|
60
|
+
if (filter(e, idx, arr)) {
|
|
61
|
+
result[i].push(e);
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
i += 1;
|
|
65
|
+
}
|
|
66
|
+
result[i].push(e);
|
|
67
|
+
});
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
function groupBy(items, key, groups = {}) {
|
|
71
|
+
for (const item of items) {
|
|
72
|
+
const v = item[key];
|
|
73
|
+
groups[v] = groups[v] || [];
|
|
74
|
+
groups[v].push(item);
|
|
75
|
+
}
|
|
76
|
+
return groups;
|
|
77
|
+
}
|
|
78
|
+
function capitalize(str) {
|
|
79
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
80
|
+
}
|
|
81
|
+
function join(array, glue = ", ", finalGlue = " and ") {
|
|
82
|
+
if (!array || array.length === 0)
|
|
83
|
+
return "";
|
|
84
|
+
if (array.length === 1)
|
|
85
|
+
return array[0];
|
|
86
|
+
if (array.length === 2)
|
|
87
|
+
return array.join(finalGlue);
|
|
88
|
+
return `${array.slice(0, -1).join(glue)}${finalGlue}${array.slice(-1)}`;
|
|
89
|
+
}
|
|
39
90
|
|
|
40
91
|
const gitCommitTypes = [
|
|
41
92
|
{ value: "init", title: "init: \u9879\u76EE\u521D\u59CB\u5316" },
|
|
@@ -90,7 +141,7 @@ async function gitCommit() {
|
|
|
90
141
|
}
|
|
91
142
|
]);
|
|
92
143
|
const commitMsg = `${result.types}(${result.scopes}): ${result.description}`;
|
|
93
|
-
|
|
144
|
+
execCommand("git", ["commit", "-m", commitMsg], { stdio: "inherit" });
|
|
94
145
|
}
|
|
95
146
|
|
|
96
147
|
function gitCommitVerify() {
|
|
@@ -7429,14 +7480,14 @@ async function initSimpleGitHooks() {
|
|
|
7429
7480
|
const existHusky = actualFS.existsSync(huskyDir);
|
|
7430
7481
|
if (existHusky) {
|
|
7431
7482
|
await rimraf.rimraf(".husky");
|
|
7432
|
-
await
|
|
7483
|
+
await execCommand("git", ["config", "core.hooksPath", ".git/hooks/"], { stdio: "inherit" });
|
|
7433
7484
|
}
|
|
7434
7485
|
await rimraf.rimraf(".git/hooks");
|
|
7435
|
-
await
|
|
7486
|
+
await execCommand("npx", ["simple-git-hooks"], { stdio: "inherit" });
|
|
7436
7487
|
}
|
|
7437
7488
|
|
|
7438
7489
|
async function updatePkg() {
|
|
7439
|
-
|
|
7490
|
+
execCommand("npx", ["ncu", "--deep", "-u"], { stdio: "inherit" });
|
|
7440
7491
|
}
|
|
7441
7492
|
|
|
7442
7493
|
function prettierFormat() {
|
|
@@ -7456,26 +7507,541 @@ function prettierFormat() {
|
|
|
7456
7507
|
"!.github",
|
|
7457
7508
|
"!__snapshots__"
|
|
7458
7509
|
];
|
|
7459
|
-
|
|
7510
|
+
execCommand("npx", ["prettier", ".", "--write", ...formatFiles], {
|
|
7460
7511
|
stdio: "inherit"
|
|
7461
7512
|
});
|
|
7462
7513
|
}
|
|
7463
7514
|
|
|
7464
7515
|
async function eslintPretter() {
|
|
7465
|
-
await
|
|
7516
|
+
await execCommand("npx", ["eslint", ".", "--fix"], {
|
|
7466
7517
|
stdio: "inherit"
|
|
7467
7518
|
});
|
|
7468
|
-
await
|
|
7519
|
+
await execCommand("npx", ["soy", "prettier-format"], {
|
|
7469
7520
|
stdio: "inherit"
|
|
7470
7521
|
});
|
|
7471
7522
|
}
|
|
7472
7523
|
|
|
7473
7524
|
function lintStaged() {
|
|
7474
|
-
|
|
7525
|
+
execCommand("npx", ["lint-staged", "--config", "@soybeanjs/cli/lint-staged"], { stdio: "inherit" });
|
|
7526
|
+
}
|
|
7527
|
+
|
|
7528
|
+
async function getTotalGitTags() {
|
|
7529
|
+
const tagStr = await execCommand("git", ["--no-pager", "tag", "-l", "--sort=creatordate"]);
|
|
7530
|
+
const tags = tagStr.split("\n");
|
|
7531
|
+
return tags;
|
|
7532
|
+
}
|
|
7533
|
+
async function getTagDateMap() {
|
|
7534
|
+
const tagDateStr = await execCommand("git", [
|
|
7535
|
+
"--no-pager",
|
|
7536
|
+
"log",
|
|
7537
|
+
"--tags",
|
|
7538
|
+
"--simplify-by-decoration",
|
|
7539
|
+
"--pretty=format:%ci %d"
|
|
7540
|
+
]);
|
|
7541
|
+
const TAG_MARK = "(tag: ";
|
|
7542
|
+
const map = /* @__PURE__ */ new Map();
|
|
7543
|
+
const dates = tagDateStr.split("\n").filter((item) => item.includes(TAG_MARK));
|
|
7544
|
+
dates.forEach((item) => {
|
|
7545
|
+
const [dateStr, tagStr] = item.split(TAG_MARK);
|
|
7546
|
+
const date = dayjs__default(dateStr.trim()).format("YYYY-MM-DD");
|
|
7547
|
+
const VERSION_REG = /v\d+\.\d+\.\d+/;
|
|
7548
|
+
const tag = tagStr.match(VERSION_REG)?.[0];
|
|
7549
|
+
if (tag && date) {
|
|
7550
|
+
map.set(tag.trim(), date);
|
|
7551
|
+
}
|
|
7552
|
+
});
|
|
7553
|
+
return map;
|
|
7554
|
+
}
|
|
7555
|
+
function getFromToTags(tags) {
|
|
7556
|
+
const result = [];
|
|
7557
|
+
tags.forEach((tag, index) => {
|
|
7558
|
+
if (index < tags.length - 1) {
|
|
7559
|
+
result.push({ from: tag, to: tags[index + 1] });
|
|
7560
|
+
}
|
|
7561
|
+
});
|
|
7562
|
+
return result;
|
|
7563
|
+
}
|
|
7564
|
+
async function getLastGitTag(delta = 0) {
|
|
7565
|
+
const tags = await getTotalGitTags();
|
|
7566
|
+
return tags[tags.length + delta - 1];
|
|
7567
|
+
}
|
|
7568
|
+
async function getGitMainBranchName() {
|
|
7569
|
+
const main = await execCommand("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
7570
|
+
return main;
|
|
7571
|
+
}
|
|
7572
|
+
async function getCurrentGitBranch() {
|
|
7573
|
+
const tag = await execCommand("git", ["tag", "--points-at", "HEAD"]);
|
|
7574
|
+
const main = getGitMainBranchName();
|
|
7575
|
+
return tag || main;
|
|
7576
|
+
}
|
|
7577
|
+
async function getGitHubRepo() {
|
|
7578
|
+
const url = await execCommand("git", ["config", "--get", "remote.origin.url"]);
|
|
7579
|
+
const match = url.match(/github\.com[/:]([\w\d._-]+?)\/([\w\d._-]+?)(\.git)?$/i);
|
|
7580
|
+
if (!match) {
|
|
7581
|
+
throw new Error(`Can not parse GitHub repo from url ${url}`);
|
|
7582
|
+
}
|
|
7583
|
+
return `${match[1]}/${match[2]}`;
|
|
7584
|
+
}
|
|
7585
|
+
function isPrerelease(version) {
|
|
7586
|
+
const REG = /^[^.]*[\d.]+$/;
|
|
7587
|
+
return !REG.test(version);
|
|
7588
|
+
}
|
|
7589
|
+
function getFirstGitCommit() {
|
|
7590
|
+
return execCommand("git", ["rev-list", "--max-parents=0", "HEAD"]);
|
|
7591
|
+
}
|
|
7592
|
+
async function getGitDiff(from, to = "HEAD") {
|
|
7593
|
+
const rawGit = await execCommand("git", [
|
|
7594
|
+
"--no-pager",
|
|
7595
|
+
"log",
|
|
7596
|
+
`${from ? `${from}...` : ""}${to}`,
|
|
7597
|
+
'--pretty="----%n%s|%h|%an|%ae%n%b"',
|
|
7598
|
+
"--name-status"
|
|
7599
|
+
]);
|
|
7600
|
+
const rwaGitLines = rawGit.split("----\n").splice(1);
|
|
7601
|
+
const gitCommits = rwaGitLines.map((line) => {
|
|
7602
|
+
const [firstLine, ...body] = line.split("\n");
|
|
7603
|
+
const [message, shortHash, authorName, authorEmail] = firstLine.split("|");
|
|
7604
|
+
const gitCommmit = {
|
|
7605
|
+
message,
|
|
7606
|
+
shortHash,
|
|
7607
|
+
author: { name: authorName, email: authorEmail },
|
|
7608
|
+
body: body.join("\n")
|
|
7609
|
+
};
|
|
7610
|
+
return gitCommmit;
|
|
7611
|
+
});
|
|
7612
|
+
return gitCommits;
|
|
7613
|
+
}
|
|
7614
|
+
function parseGitCommit(commit, scopeMap) {
|
|
7615
|
+
const ConventionalCommitRegex = /(?<type>[a-z]+)(\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
|
|
7616
|
+
const CoAuthoredByRegex = /co-authored-by:\s*(?<name>.+)(<(?<email>.+)>)/gim;
|
|
7617
|
+
const PullRequestRE = /\([a-z]*(#\d+)\s*\)/gm;
|
|
7618
|
+
const IssueRE = /(#\d+)/gm;
|
|
7619
|
+
const match = commit.message.match(ConventionalCommitRegex);
|
|
7620
|
+
if (!match?.groups) {
|
|
7621
|
+
return null;
|
|
7622
|
+
}
|
|
7623
|
+
const type = match.groups.type;
|
|
7624
|
+
let scope = match.groups.scope || "";
|
|
7625
|
+
scope = scopeMap[scope] || scope;
|
|
7626
|
+
const isBreaking = Boolean(match.groups.breaking);
|
|
7627
|
+
let description = match.groups.description;
|
|
7628
|
+
const references = [];
|
|
7629
|
+
for (const m of description.matchAll(PullRequestRE)) {
|
|
7630
|
+
references.push({ type: "pull-request", value: m[1] });
|
|
7631
|
+
}
|
|
7632
|
+
for (const m of description.matchAll(IssueRE)) {
|
|
7633
|
+
if (!references.some((i) => i.value === m[1])) {
|
|
7634
|
+
references.push({ type: "issue", value: m[1] });
|
|
7635
|
+
}
|
|
7636
|
+
}
|
|
7637
|
+
references.push({ value: commit.shortHash, type: "hash" });
|
|
7638
|
+
description = description.replace(PullRequestRE, "").trim();
|
|
7639
|
+
const authors = [commit.author];
|
|
7640
|
+
const matchs = commit.body.matchAll(CoAuthoredByRegex);
|
|
7641
|
+
for (const $match of matchs) {
|
|
7642
|
+
const { name = "", email = "" } = $match.groups || {};
|
|
7643
|
+
const author = {
|
|
7644
|
+
name: name.trim(),
|
|
7645
|
+
email: email.trim()
|
|
7646
|
+
};
|
|
7647
|
+
authors.push(author);
|
|
7648
|
+
}
|
|
7649
|
+
return {
|
|
7650
|
+
...commit,
|
|
7651
|
+
authors,
|
|
7652
|
+
resolvedAuthors: [],
|
|
7653
|
+
description,
|
|
7654
|
+
type,
|
|
7655
|
+
scope,
|
|
7656
|
+
references,
|
|
7657
|
+
isBreaking
|
|
7658
|
+
};
|
|
7659
|
+
}
|
|
7660
|
+
async function getGitCommits(from, to = "HEAD", scopeMap = {}) {
|
|
7661
|
+
const rwaGitCommits = await getGitDiff(from, to);
|
|
7662
|
+
const commits = rwaGitCommits.map((commit) => parseGitCommit(commit, scopeMap)).filter(notNullish);
|
|
7663
|
+
return commits;
|
|
7664
|
+
}
|
|
7665
|
+
function getHeaders(githubToken) {
|
|
7666
|
+
return {
|
|
7667
|
+
accept: "application/vnd.github.v3+json",
|
|
7668
|
+
authorization: `token ${githubToken}`
|
|
7669
|
+
};
|
|
7670
|
+
}
|
|
7671
|
+
async function getResolvedAuthorLogin(params) {
|
|
7672
|
+
const { github, githubToken, commitHashes, email } = params;
|
|
7673
|
+
let login = "";
|
|
7674
|
+
if (!githubToken) {
|
|
7675
|
+
return login;
|
|
7676
|
+
}
|
|
7677
|
+
try {
|
|
7678
|
+
const data = await ofetch.ofetch(`https://api.github.com/search/users?q=${encodeURIComponent(email)}`);
|
|
7679
|
+
login = data.items[0].login;
|
|
7680
|
+
} catch {
|
|
7681
|
+
}
|
|
7682
|
+
if (login) {
|
|
7683
|
+
return login;
|
|
7684
|
+
}
|
|
7685
|
+
if (commitHashes.length) {
|
|
7686
|
+
try {
|
|
7687
|
+
const data = await ofetch.ofetch(`https://api.github.com/repos/${github}/commits/${commitHashes[0]}`, {
|
|
7688
|
+
headers: getHeaders(githubToken)
|
|
7689
|
+
});
|
|
7690
|
+
login = data?.author?.login || "";
|
|
7691
|
+
} catch (e) {
|
|
7692
|
+
}
|
|
7693
|
+
}
|
|
7694
|
+
return login;
|
|
7695
|
+
}
|
|
7696
|
+
async function getGitCommitsAndResolvedAuthors(commits, github, githubToken, resolvedLogins) {
|
|
7697
|
+
const resultCommits = [];
|
|
7698
|
+
const map = /* @__PURE__ */ new Map();
|
|
7699
|
+
for (const commit of commits) {
|
|
7700
|
+
const resolvedAuthors = [];
|
|
7701
|
+
for (const [index, author] of Object.entries(commit.authors)) {
|
|
7702
|
+
const { email, name } = author;
|
|
7703
|
+
if (email && name) {
|
|
7704
|
+
const commitHashes = [];
|
|
7705
|
+
if (index === "0") {
|
|
7706
|
+
commitHashes.push(commit.shortHash);
|
|
7707
|
+
}
|
|
7708
|
+
const resolvedAuthor = {
|
|
7709
|
+
name,
|
|
7710
|
+
email,
|
|
7711
|
+
commits: commitHashes,
|
|
7712
|
+
login: ""
|
|
7713
|
+
};
|
|
7714
|
+
if (!resolvedLogins?.has(email)) {
|
|
7715
|
+
const login = await getResolvedAuthorLogin({ github, githubToken, commitHashes, email });
|
|
7716
|
+
resolvedAuthor.login = login;
|
|
7717
|
+
resolvedLogins?.set(email, login);
|
|
7718
|
+
} else {
|
|
7719
|
+
const login = resolvedLogins?.get(email) || "";
|
|
7720
|
+
resolvedAuthor.login = login;
|
|
7721
|
+
}
|
|
7722
|
+
resolvedAuthors.push(resolvedAuthor);
|
|
7723
|
+
if (!map.has(email)) {
|
|
7724
|
+
map.set(email, resolvedAuthor);
|
|
7725
|
+
}
|
|
7726
|
+
}
|
|
7727
|
+
}
|
|
7728
|
+
const resultCommit = { ...commit, resolvedAuthors };
|
|
7729
|
+
resultCommits.push(resultCommit);
|
|
7730
|
+
}
|
|
7731
|
+
return {
|
|
7732
|
+
commits: resultCommits,
|
|
7733
|
+
contributors: Array.from(map.values())
|
|
7734
|
+
};
|
|
7735
|
+
}
|
|
7736
|
+
|
|
7737
|
+
function formatReferences(references, github, type) {
|
|
7738
|
+
const refs = references.filter((i) => {
|
|
7739
|
+
if (type === "issues")
|
|
7740
|
+
return i.type === "issue" || i.type === "pull-request";
|
|
7741
|
+
return i.type === "hash";
|
|
7742
|
+
}).map((ref) => {
|
|
7743
|
+
if (!github)
|
|
7744
|
+
return ref.value;
|
|
7745
|
+
if (ref.type === "pull-request" || ref.type === "issue")
|
|
7746
|
+
return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
|
|
7747
|
+
return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
|
|
7748
|
+
});
|
|
7749
|
+
const referencesString = join(refs).trim();
|
|
7750
|
+
if (type === "issues")
|
|
7751
|
+
return referencesString && `in ${referencesString}`;
|
|
7752
|
+
return referencesString;
|
|
7753
|
+
}
|
|
7754
|
+
function formatLine(commit, options) {
|
|
7755
|
+
const prRefs = formatReferences(commit.references, options.github, "issues");
|
|
7756
|
+
const hashRefs = formatReferences(commit.references, options.github, "hash");
|
|
7757
|
+
let authors = join([...new Set(commit.resolvedAuthors.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))]).trim();
|
|
7758
|
+
if (authors) {
|
|
7759
|
+
authors = `by ${authors}`;
|
|
7760
|
+
}
|
|
7761
|
+
let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
|
|
7762
|
+
if (refs) {
|
|
7763
|
+
refs = ` - ${refs}`;
|
|
7764
|
+
}
|
|
7765
|
+
const description = options.capitalize ? capitalize(commit.description) : commit.description;
|
|
7766
|
+
return [description, refs].filter((i) => i?.trim()).join(" ");
|
|
7767
|
+
}
|
|
7768
|
+
function formatTitle(name, options) {
|
|
7769
|
+
const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
|
|
7770
|
+
let formatName = name.trim();
|
|
7771
|
+
if (!options.emoji) {
|
|
7772
|
+
formatName = name.replace(emojisRE, "").trim();
|
|
7773
|
+
}
|
|
7774
|
+
return `### ${formatName}`;
|
|
7775
|
+
}
|
|
7776
|
+
function formatSection(commits, sectionName, options) {
|
|
7777
|
+
if (!commits.length)
|
|
7778
|
+
return [];
|
|
7779
|
+
const lines = ["", formatTitle(sectionName, options), ""];
|
|
7780
|
+
const scopes = groupBy(commits, "scope");
|
|
7781
|
+
let useScopeGroup = options.group;
|
|
7782
|
+
if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1))
|
|
7783
|
+
useScopeGroup = false;
|
|
7784
|
+
Object.keys(scopes).sort().forEach((scope) => {
|
|
7785
|
+
let padding = "";
|
|
7786
|
+
let prefix = "";
|
|
7787
|
+
const scopeText = `**${options.scopeMap[scope] || scope}**`;
|
|
7788
|
+
if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
|
|
7789
|
+
lines.push(`- ${scopeText}:`);
|
|
7790
|
+
padding = " ";
|
|
7791
|
+
} else if (scope) {
|
|
7792
|
+
prefix = `${scopeText}: `;
|
|
7793
|
+
}
|
|
7794
|
+
lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
|
|
7795
|
+
});
|
|
7796
|
+
return lines;
|
|
7797
|
+
}
|
|
7798
|
+
function getGitUserAvatar(userName) {
|
|
7799
|
+
const avatarUrl = `https://github.com/${userName}.png?size=48`;
|
|
7800
|
+
return avatarUrl;
|
|
7801
|
+
}
|
|
7802
|
+
function createContributorLine(contributors) {
|
|
7803
|
+
let loginLine = "";
|
|
7804
|
+
let unloginLine = "";
|
|
7805
|
+
contributors.forEach((contributor, index) => {
|
|
7806
|
+
const { name, email, login } = contributor;
|
|
7807
|
+
if (!login) {
|
|
7808
|
+
let line = `[${name}](mailto:${email})`;
|
|
7809
|
+
if (index < contributors.length - 1) {
|
|
7810
|
+
line += ", ";
|
|
7811
|
+
}
|
|
7812
|
+
unloginLine += line;
|
|
7813
|
+
} else {
|
|
7814
|
+
const avatar = getGitUserAvatar(login);
|
|
7815
|
+
loginLine += ` `;
|
|
7816
|
+
}
|
|
7817
|
+
});
|
|
7818
|
+
return `${loginLine}
|
|
7819
|
+
${unloginLine}`;
|
|
7820
|
+
}
|
|
7821
|
+
function generateMarkdown(params) {
|
|
7822
|
+
const VERSION_REG = /v\d+\.\d+\.\d+/;
|
|
7823
|
+
const { commits, options, showTitle, contributors } = params;
|
|
7824
|
+
const lines = [];
|
|
7825
|
+
const url = `https://github.com/${options.github}/compare/${options.from}...${options.to}`;
|
|
7826
|
+
if (showTitle) {
|
|
7827
|
+
const version = VERSION_REG.test(options.to) ? options.to : options.newVersion;
|
|
7828
|
+
const date = options.tagDateMap.get(options.to);
|
|
7829
|
+
let title = `## [${version}](${url})`;
|
|
7830
|
+
if (date) {
|
|
7831
|
+
title += ` (${date})`;
|
|
7832
|
+
}
|
|
7833
|
+
lines.push(title);
|
|
7834
|
+
}
|
|
7835
|
+
const [breaking, changes] = partition(commits, (c) => c.isBreaking);
|
|
7836
|
+
const group = groupBy(changes, "type");
|
|
7837
|
+
lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
|
|
7838
|
+
for (const type of Object.keys(options.types)) {
|
|
7839
|
+
const items = group[type] || [];
|
|
7840
|
+
lines.push(...formatSection(items, options.types[type].title, options));
|
|
7841
|
+
}
|
|
7842
|
+
if (!lines.length) {
|
|
7843
|
+
lines.push("*No significant changes*");
|
|
7844
|
+
}
|
|
7845
|
+
if (!showTitle) {
|
|
7846
|
+
lines.push("", `##### [View changes on GitHub](${url})`);
|
|
7847
|
+
}
|
|
7848
|
+
if (showTitle) {
|
|
7849
|
+
lines.push("", "### \u2764\uFE0F Contributors", "");
|
|
7850
|
+
const contributorLine = createContributorLine(contributors);
|
|
7851
|
+
lines.push(contributorLine);
|
|
7852
|
+
}
|
|
7853
|
+
const md = convertGitmoji.convert(lines.join("\n").trim(), true);
|
|
7854
|
+
const markdown = md.replace(/ /g, "");
|
|
7855
|
+
return markdown;
|
|
7856
|
+
}
|
|
7857
|
+
async function isVersionInMarkdown(version, mdPath) {
|
|
7858
|
+
const VERSION_REG_OF_MARKDOWN = /## \[v\d+\.\d+\.\d+\]/g;
|
|
7859
|
+
let isIn = false;
|
|
7860
|
+
const md = await promises.readFile(mdPath, "utf8");
|
|
7861
|
+
if (md) {
|
|
7862
|
+
const matches = md.match(VERSION_REG_OF_MARKDOWN);
|
|
7863
|
+
if (matches?.length) {
|
|
7864
|
+
const versionInMarkdown = `## [${version}]`;
|
|
7865
|
+
isIn = matches.includes(versionInMarkdown);
|
|
7866
|
+
}
|
|
7867
|
+
}
|
|
7868
|
+
return isIn;
|
|
7869
|
+
}
|
|
7870
|
+
async function writeMarkdown(md, mdPath, override = false) {
|
|
7871
|
+
let changelogMD;
|
|
7872
|
+
const changelogPrefix = "# Changelog";
|
|
7873
|
+
if (!override && actualFS.existsSync(mdPath)) {
|
|
7874
|
+
changelogMD = await promises.readFile(mdPath, "utf8");
|
|
7875
|
+
if (!changelogMD.startsWith(changelogPrefix)) {
|
|
7876
|
+
changelogMD = `${changelogPrefix}
|
|
7877
|
+
|
|
7878
|
+
${changelogMD}`;
|
|
7879
|
+
}
|
|
7880
|
+
} else {
|
|
7881
|
+
changelogMD = `${changelogPrefix}
|
|
7882
|
+
|
|
7883
|
+
`;
|
|
7884
|
+
}
|
|
7885
|
+
const lastEntry = changelogMD.match(/^###?\s+.*$/m);
|
|
7886
|
+
if (lastEntry) {
|
|
7887
|
+
changelogMD = `${changelogMD.slice(0, lastEntry.index) + md}
|
|
7888
|
+
|
|
7889
|
+
${changelogMD.slice(lastEntry.index)}`;
|
|
7890
|
+
} else {
|
|
7891
|
+
changelogMD += `
|
|
7892
|
+
${md}
|
|
7893
|
+
|
|
7894
|
+
`;
|
|
7895
|
+
}
|
|
7896
|
+
await promises.writeFile(mdPath, changelogMD);
|
|
7897
|
+
}
|
|
7898
|
+
|
|
7899
|
+
function createDefaultOptions() {
|
|
7900
|
+
const cwd = process.cwd();
|
|
7901
|
+
const options = {
|
|
7902
|
+
cwd,
|
|
7903
|
+
types: {
|
|
7904
|
+
feat: { title: "\u{1F680} Features" },
|
|
7905
|
+
fix: { title: "\u{1F41E} Bug Fixes" },
|
|
7906
|
+
perf: { title: "\u{1F525} Performance" },
|
|
7907
|
+
refactor: { title: "\u{1F485} Refactors" },
|
|
7908
|
+
docs: { title: "\u{1F4D6} Documentation" },
|
|
7909
|
+
build: { title: "\u{1F4E6} Build" },
|
|
7910
|
+
types: { title: "\u{1F30A} Types" },
|
|
7911
|
+
chore: { title: "\u{1F3E1} Chore" },
|
|
7912
|
+
examples: { title: "\u{1F3C0} Examples" },
|
|
7913
|
+
test: { title: "\u2705 Tests" },
|
|
7914
|
+
style: { title: "\u{1F3A8} Styles" },
|
|
7915
|
+
ci: { title: "\u{1F916} CI" }
|
|
7916
|
+
},
|
|
7917
|
+
scopeMap: {},
|
|
7918
|
+
github: "",
|
|
7919
|
+
githubToken: process.env.GITHUB_TOKEN || "",
|
|
7920
|
+
from: "",
|
|
7921
|
+
to: "",
|
|
7922
|
+
tags: [],
|
|
7923
|
+
tagDateMap: /* @__PURE__ */ new Map(),
|
|
7924
|
+
prerelease: false,
|
|
7925
|
+
capitalize: true,
|
|
7926
|
+
emoji: true,
|
|
7927
|
+
titles: {
|
|
7928
|
+
breakingChanges: "\u{1F6A8} Breaking Changes"
|
|
7929
|
+
},
|
|
7930
|
+
output: "CHANGELOG.md",
|
|
7931
|
+
overrideChangelog: false,
|
|
7932
|
+
newVersion: ""
|
|
7933
|
+
};
|
|
7934
|
+
return options;
|
|
7935
|
+
}
|
|
7936
|
+
async function getOptionsFromPkg(cwd) {
|
|
7937
|
+
let githubToken = "";
|
|
7938
|
+
let newVersion = "";
|
|
7939
|
+
try {
|
|
7940
|
+
const pkgJson = await promises.readFile(`${cwd}/package.json`, "utf-8");
|
|
7941
|
+
const pkg = JSON.parse(pkgJson);
|
|
7942
|
+
githubToken = pkg?.["github-token"] || "";
|
|
7943
|
+
newVersion = pkg?.version || "";
|
|
7944
|
+
} catch {
|
|
7945
|
+
}
|
|
7946
|
+
return {
|
|
7947
|
+
githubToken,
|
|
7948
|
+
newVersion
|
|
7949
|
+
};
|
|
7950
|
+
}
|
|
7951
|
+
async function initOptions() {
|
|
7952
|
+
const options = createDefaultOptions();
|
|
7953
|
+
const { githubToken, newVersion } = await getOptionsFromPkg(options.cwd);
|
|
7954
|
+
if (!options.githubToken) {
|
|
7955
|
+
options.githubToken = githubToken;
|
|
7956
|
+
}
|
|
7957
|
+
if (newVersion) {
|
|
7958
|
+
options.newVersion = `v${newVersion}`;
|
|
7959
|
+
}
|
|
7960
|
+
if (!options.from) {
|
|
7961
|
+
options.from = await getLastGitTag();
|
|
7962
|
+
}
|
|
7963
|
+
if (!options.to) {
|
|
7964
|
+
options.to = await getCurrentGitBranch();
|
|
7965
|
+
}
|
|
7966
|
+
if (options.to === options.from) {
|
|
7967
|
+
const lastTag = await getLastGitTag(-1);
|
|
7968
|
+
const firstCommit = await getFirstGitCommit();
|
|
7969
|
+
options.from = lastTag || firstCommit;
|
|
7970
|
+
}
|
|
7971
|
+
options.tags = await getTotalGitTags();
|
|
7972
|
+
options.tagDateMap = await getTagDateMap();
|
|
7973
|
+
options.github = await getGitHubRepo();
|
|
7974
|
+
options.prerelease = isPrerelease(options.to);
|
|
7975
|
+
return options;
|
|
7976
|
+
}
|
|
7977
|
+
async function generateChangelogByTag(options) {
|
|
7978
|
+
const gitCommits = await getGitCommits(options.from, options.to, options.scopeMap);
|
|
7979
|
+
const { commits, contributors } = await getGitCommitsAndResolvedAuthors(
|
|
7980
|
+
gitCommits,
|
|
7981
|
+
options.github,
|
|
7982
|
+
options.githubToken
|
|
7983
|
+
);
|
|
7984
|
+
const md = generateMarkdown({ commits, options, showTitle: true, contributors });
|
|
7985
|
+
return md;
|
|
7986
|
+
}
|
|
7987
|
+
async function generateChangelogByTags(options) {
|
|
7988
|
+
const tags = getFromToTags(options.tags);
|
|
7989
|
+
const bar = new cliProgress__default.SingleBar({}, cliProgress__default.Presets.shades_classic);
|
|
7990
|
+
bar.start(tags.length, 0);
|
|
7991
|
+
let md = "";
|
|
7992
|
+
const resolvedLogins = /* @__PURE__ */ new Map();
|
|
7993
|
+
for (let i = 0; i < tags.length; i += 1) {
|
|
7994
|
+
const { from, to } = tags[i];
|
|
7995
|
+
const gitCommits = await getGitCommits(from, to, options.scopeMap);
|
|
7996
|
+
const { commits, contributors } = await getGitCommitsAndResolvedAuthors(
|
|
7997
|
+
gitCommits,
|
|
7998
|
+
options.github,
|
|
7999
|
+
options.githubToken,
|
|
8000
|
+
resolvedLogins
|
|
8001
|
+
);
|
|
8002
|
+
const opts = { ...options, from, to };
|
|
8003
|
+
const nextMd = generateMarkdown({ commits, options: opts, showTitle: true, contributors });
|
|
8004
|
+
md = `${nextMd}
|
|
8005
|
+
|
|
8006
|
+
${md}`;
|
|
8007
|
+
bar.update(i + 1);
|
|
8008
|
+
}
|
|
8009
|
+
bar.stop();
|
|
8010
|
+
return md;
|
|
8011
|
+
}
|
|
8012
|
+
async function generateChangelog(total = false) {
|
|
8013
|
+
const options = await initOptions();
|
|
8014
|
+
let md = "";
|
|
8015
|
+
if (total) {
|
|
8016
|
+
md = await generateChangelogByTags(options);
|
|
8017
|
+
await writeMarkdown(md, options.output, true);
|
|
8018
|
+
return;
|
|
8019
|
+
}
|
|
8020
|
+
const isIn = await isVersionInMarkdown(options.to, options.output);
|
|
8021
|
+
if (!options.overrideChangelog && isIn) {
|
|
8022
|
+
return;
|
|
8023
|
+
}
|
|
8024
|
+
md = await generateChangelogByTag(options);
|
|
8025
|
+
await writeMarkdown(md, options.output, false);
|
|
8026
|
+
}
|
|
8027
|
+
|
|
8028
|
+
async function genChangelog(total = false) {
|
|
8029
|
+
await generateChangelog(total);
|
|
8030
|
+
}
|
|
8031
|
+
|
|
8032
|
+
async function release() {
|
|
8033
|
+
await versionBumpp__default({
|
|
8034
|
+
files: ["**/package.json", "!**/node_modules"],
|
|
8035
|
+
execute: "npx soy changelog",
|
|
8036
|
+
all: true,
|
|
8037
|
+
tag: true,
|
|
8038
|
+
commit: "chore(projects): release v%s",
|
|
8039
|
+
push: true
|
|
8040
|
+
});
|
|
7475
8041
|
}
|
|
7476
8042
|
|
|
7477
8043
|
const cli = cac__default("soybean");
|
|
7478
|
-
cli.version(version).help();
|
|
8044
|
+
cli.version(version).option("--total", "Generate changelog by total tags").help();
|
|
7479
8045
|
const commands = {
|
|
7480
8046
|
"git-commit": {
|
|
7481
8047
|
desc: "\u751F\u6210\u7B26\u5408 Angular \u89C4\u8303\u7684 git commit",
|
|
@@ -7508,6 +8074,16 @@ const commands = {
|
|
|
7508
8074
|
"lint-staged": {
|
|
7509
8075
|
desc: "\u6267\u884Clint-staged",
|
|
7510
8076
|
action: lintStaged
|
|
8077
|
+
},
|
|
8078
|
+
changelog: {
|
|
8079
|
+
desc: "\u751F\u6210changelog",
|
|
8080
|
+
action: async (args) => {
|
|
8081
|
+
await genChangelog(args?.total);
|
|
8082
|
+
}
|
|
8083
|
+
},
|
|
8084
|
+
release: {
|
|
8085
|
+
desc: "\u53D1\u5E03\uFF1A\u66F4\u65B0\u7248\u672C\u53F7\u3001\u751F\u6210changelog\u3001\u63D0\u4EA4\u4EE3\u7801",
|
|
8086
|
+
action: release
|
|
7511
8087
|
}
|
|
7512
8088
|
};
|
|
7513
8089
|
Object.entries(commands).forEach(([command, { desc, action }]) => {
|
package/dist/index.mjs
CHANGED
|
@@ -1,19 +1,67 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import cac from 'cac';
|
|
3
3
|
import enquirer from 'enquirer';
|
|
4
|
-
import { execa } from 'execa';
|
|
5
4
|
import * as actualFS from 'fs';
|
|
6
5
|
import { readFileSync, realpathSync as realpathSync$1, lstatSync, readdir, readdirSync, readlinkSync, existsSync } from 'fs';
|
|
7
6
|
import { bgRed, red, green } from 'kolorist';
|
|
8
7
|
import { rimraf } from 'rimraf';
|
|
9
8
|
import { win32, posix } from 'path';
|
|
10
9
|
import { fileURLToPath } from 'url';
|
|
11
|
-
import { lstat, readdir as readdir$1, readlink, realpath } from 'fs/promises';
|
|
10
|
+
import { lstat, readdir as readdir$1, readlink, realpath, readFile, writeFile } from 'fs/promises';
|
|
12
11
|
import EE from 'events';
|
|
13
12
|
import Stream from 'stream';
|
|
14
13
|
import stringdecoder from 'string_decoder';
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
import cliProgress from 'cli-progress';
|
|
15
|
+
import { ofetch } from 'ofetch';
|
|
16
|
+
import dayjs from 'dayjs';
|
|
17
|
+
import { convert } from 'convert-gitmoji';
|
|
18
|
+
import versionBumpp from 'bumpp';
|
|
19
|
+
|
|
20
|
+
const version = "0.5.0";
|
|
21
|
+
|
|
22
|
+
async function execCommand(cmd, args, options) {
|
|
23
|
+
const { execa } = await import('execa');
|
|
24
|
+
const res = await execa(cmd, args, options);
|
|
25
|
+
return res?.stdout?.trim() || "";
|
|
26
|
+
}
|
|
27
|
+
function notNullish(v) {
|
|
28
|
+
return v !== null && v !== void 0;
|
|
29
|
+
}
|
|
30
|
+
function partition(array, ...filters) {
|
|
31
|
+
const result = new Array(filters.length + 1).fill(null).map(() => []);
|
|
32
|
+
array.forEach((e, idx, arr) => {
|
|
33
|
+
let i = 0;
|
|
34
|
+
for (const filter of filters) {
|
|
35
|
+
if (filter(e, idx, arr)) {
|
|
36
|
+
result[i].push(e);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
i += 1;
|
|
40
|
+
}
|
|
41
|
+
result[i].push(e);
|
|
42
|
+
});
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
function groupBy(items, key, groups = {}) {
|
|
46
|
+
for (const item of items) {
|
|
47
|
+
const v = item[key];
|
|
48
|
+
groups[v] = groups[v] || [];
|
|
49
|
+
groups[v].push(item);
|
|
50
|
+
}
|
|
51
|
+
return groups;
|
|
52
|
+
}
|
|
53
|
+
function capitalize(str) {
|
|
54
|
+
return str.charAt(0).toUpperCase() + str.slice(1);
|
|
55
|
+
}
|
|
56
|
+
function join(array, glue = ", ", finalGlue = " and ") {
|
|
57
|
+
if (!array || array.length === 0)
|
|
58
|
+
return "";
|
|
59
|
+
if (array.length === 1)
|
|
60
|
+
return array[0];
|
|
61
|
+
if (array.length === 2)
|
|
62
|
+
return array.join(finalGlue);
|
|
63
|
+
return `${array.slice(0, -1).join(glue)}${finalGlue}${array.slice(-1)}`;
|
|
64
|
+
}
|
|
17
65
|
|
|
18
66
|
const gitCommitTypes = [
|
|
19
67
|
{ value: "init", title: "init: \u9879\u76EE\u521D\u59CB\u5316" },
|
|
@@ -68,7 +116,7 @@ async function gitCommit() {
|
|
|
68
116
|
}
|
|
69
117
|
]);
|
|
70
118
|
const commitMsg = `${result.types}(${result.scopes}): ${result.description}`;
|
|
71
|
-
|
|
119
|
+
execCommand("git", ["commit", "-m", commitMsg], { stdio: "inherit" });
|
|
72
120
|
}
|
|
73
121
|
|
|
74
122
|
function gitCommitVerify() {
|
|
@@ -7407,14 +7455,14 @@ async function initSimpleGitHooks() {
|
|
|
7407
7455
|
const existHusky = existsSync(huskyDir);
|
|
7408
7456
|
if (existHusky) {
|
|
7409
7457
|
await rimraf(".husky");
|
|
7410
|
-
await
|
|
7458
|
+
await execCommand("git", ["config", "core.hooksPath", ".git/hooks/"], { stdio: "inherit" });
|
|
7411
7459
|
}
|
|
7412
7460
|
await rimraf(".git/hooks");
|
|
7413
|
-
await
|
|
7461
|
+
await execCommand("npx", ["simple-git-hooks"], { stdio: "inherit" });
|
|
7414
7462
|
}
|
|
7415
7463
|
|
|
7416
7464
|
async function updatePkg() {
|
|
7417
|
-
|
|
7465
|
+
execCommand("npx", ["ncu", "--deep", "-u"], { stdio: "inherit" });
|
|
7418
7466
|
}
|
|
7419
7467
|
|
|
7420
7468
|
function prettierFormat() {
|
|
@@ -7434,26 +7482,541 @@ function prettierFormat() {
|
|
|
7434
7482
|
"!.github",
|
|
7435
7483
|
"!__snapshots__"
|
|
7436
7484
|
];
|
|
7437
|
-
|
|
7485
|
+
execCommand("npx", ["prettier", ".", "--write", ...formatFiles], {
|
|
7438
7486
|
stdio: "inherit"
|
|
7439
7487
|
});
|
|
7440
7488
|
}
|
|
7441
7489
|
|
|
7442
7490
|
async function eslintPretter() {
|
|
7443
|
-
await
|
|
7491
|
+
await execCommand("npx", ["eslint", ".", "--fix"], {
|
|
7444
7492
|
stdio: "inherit"
|
|
7445
7493
|
});
|
|
7446
|
-
await
|
|
7494
|
+
await execCommand("npx", ["soy", "prettier-format"], {
|
|
7447
7495
|
stdio: "inherit"
|
|
7448
7496
|
});
|
|
7449
7497
|
}
|
|
7450
7498
|
|
|
7451
7499
|
function lintStaged() {
|
|
7452
|
-
|
|
7500
|
+
execCommand("npx", ["lint-staged", "--config", "@soybeanjs/cli/lint-staged"], { stdio: "inherit" });
|
|
7501
|
+
}
|
|
7502
|
+
|
|
7503
|
+
async function getTotalGitTags() {
|
|
7504
|
+
const tagStr = await execCommand("git", ["--no-pager", "tag", "-l", "--sort=creatordate"]);
|
|
7505
|
+
const tags = tagStr.split("\n");
|
|
7506
|
+
return tags;
|
|
7507
|
+
}
|
|
7508
|
+
async function getTagDateMap() {
|
|
7509
|
+
const tagDateStr = await execCommand("git", [
|
|
7510
|
+
"--no-pager",
|
|
7511
|
+
"log",
|
|
7512
|
+
"--tags",
|
|
7513
|
+
"--simplify-by-decoration",
|
|
7514
|
+
"--pretty=format:%ci %d"
|
|
7515
|
+
]);
|
|
7516
|
+
const TAG_MARK = "(tag: ";
|
|
7517
|
+
const map = /* @__PURE__ */ new Map();
|
|
7518
|
+
const dates = tagDateStr.split("\n").filter((item) => item.includes(TAG_MARK));
|
|
7519
|
+
dates.forEach((item) => {
|
|
7520
|
+
const [dateStr, tagStr] = item.split(TAG_MARK);
|
|
7521
|
+
const date = dayjs(dateStr.trim()).format("YYYY-MM-DD");
|
|
7522
|
+
const VERSION_REG = /v\d+\.\d+\.\d+/;
|
|
7523
|
+
const tag = tagStr.match(VERSION_REG)?.[0];
|
|
7524
|
+
if (tag && date) {
|
|
7525
|
+
map.set(tag.trim(), date);
|
|
7526
|
+
}
|
|
7527
|
+
});
|
|
7528
|
+
return map;
|
|
7529
|
+
}
|
|
7530
|
+
function getFromToTags(tags) {
|
|
7531
|
+
const result = [];
|
|
7532
|
+
tags.forEach((tag, index) => {
|
|
7533
|
+
if (index < tags.length - 1) {
|
|
7534
|
+
result.push({ from: tag, to: tags[index + 1] });
|
|
7535
|
+
}
|
|
7536
|
+
});
|
|
7537
|
+
return result;
|
|
7538
|
+
}
|
|
7539
|
+
async function getLastGitTag(delta = 0) {
|
|
7540
|
+
const tags = await getTotalGitTags();
|
|
7541
|
+
return tags[tags.length + delta - 1];
|
|
7542
|
+
}
|
|
7543
|
+
async function getGitMainBranchName() {
|
|
7544
|
+
const main = await execCommand("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
|
|
7545
|
+
return main;
|
|
7546
|
+
}
|
|
7547
|
+
async function getCurrentGitBranch() {
|
|
7548
|
+
const tag = await execCommand("git", ["tag", "--points-at", "HEAD"]);
|
|
7549
|
+
const main = getGitMainBranchName();
|
|
7550
|
+
return tag || main;
|
|
7551
|
+
}
|
|
7552
|
+
async function getGitHubRepo() {
|
|
7553
|
+
const url = await execCommand("git", ["config", "--get", "remote.origin.url"]);
|
|
7554
|
+
const match = url.match(/github\.com[/:]([\w\d._-]+?)\/([\w\d._-]+?)(\.git)?$/i);
|
|
7555
|
+
if (!match) {
|
|
7556
|
+
throw new Error(`Can not parse GitHub repo from url ${url}`);
|
|
7557
|
+
}
|
|
7558
|
+
return `${match[1]}/${match[2]}`;
|
|
7559
|
+
}
|
|
7560
|
+
function isPrerelease(version) {
|
|
7561
|
+
const REG = /^[^.]*[\d.]+$/;
|
|
7562
|
+
return !REG.test(version);
|
|
7563
|
+
}
|
|
7564
|
+
function getFirstGitCommit() {
|
|
7565
|
+
return execCommand("git", ["rev-list", "--max-parents=0", "HEAD"]);
|
|
7566
|
+
}
|
|
7567
|
+
async function getGitDiff(from, to = "HEAD") {
|
|
7568
|
+
const rawGit = await execCommand("git", [
|
|
7569
|
+
"--no-pager",
|
|
7570
|
+
"log",
|
|
7571
|
+
`${from ? `${from}...` : ""}${to}`,
|
|
7572
|
+
'--pretty="----%n%s|%h|%an|%ae%n%b"',
|
|
7573
|
+
"--name-status"
|
|
7574
|
+
]);
|
|
7575
|
+
const rwaGitLines = rawGit.split("----\n").splice(1);
|
|
7576
|
+
const gitCommits = rwaGitLines.map((line) => {
|
|
7577
|
+
const [firstLine, ...body] = line.split("\n");
|
|
7578
|
+
const [message, shortHash, authorName, authorEmail] = firstLine.split("|");
|
|
7579
|
+
const gitCommmit = {
|
|
7580
|
+
message,
|
|
7581
|
+
shortHash,
|
|
7582
|
+
author: { name: authorName, email: authorEmail },
|
|
7583
|
+
body: body.join("\n")
|
|
7584
|
+
};
|
|
7585
|
+
return gitCommmit;
|
|
7586
|
+
});
|
|
7587
|
+
return gitCommits;
|
|
7588
|
+
}
|
|
7589
|
+
function parseGitCommit(commit, scopeMap) {
|
|
7590
|
+
const ConventionalCommitRegex = /(?<type>[a-z]+)(\((?<scope>.+)\))?(?<breaking>!)?: (?<description>.+)/i;
|
|
7591
|
+
const CoAuthoredByRegex = /co-authored-by:\s*(?<name>.+)(<(?<email>.+)>)/gim;
|
|
7592
|
+
const PullRequestRE = /\([a-z]*(#\d+)\s*\)/gm;
|
|
7593
|
+
const IssueRE = /(#\d+)/gm;
|
|
7594
|
+
const match = commit.message.match(ConventionalCommitRegex);
|
|
7595
|
+
if (!match?.groups) {
|
|
7596
|
+
return null;
|
|
7597
|
+
}
|
|
7598
|
+
const type = match.groups.type;
|
|
7599
|
+
let scope = match.groups.scope || "";
|
|
7600
|
+
scope = scopeMap[scope] || scope;
|
|
7601
|
+
const isBreaking = Boolean(match.groups.breaking);
|
|
7602
|
+
let description = match.groups.description;
|
|
7603
|
+
const references = [];
|
|
7604
|
+
for (const m of description.matchAll(PullRequestRE)) {
|
|
7605
|
+
references.push({ type: "pull-request", value: m[1] });
|
|
7606
|
+
}
|
|
7607
|
+
for (const m of description.matchAll(IssueRE)) {
|
|
7608
|
+
if (!references.some((i) => i.value === m[1])) {
|
|
7609
|
+
references.push({ type: "issue", value: m[1] });
|
|
7610
|
+
}
|
|
7611
|
+
}
|
|
7612
|
+
references.push({ value: commit.shortHash, type: "hash" });
|
|
7613
|
+
description = description.replace(PullRequestRE, "").trim();
|
|
7614
|
+
const authors = [commit.author];
|
|
7615
|
+
const matchs = commit.body.matchAll(CoAuthoredByRegex);
|
|
7616
|
+
for (const $match of matchs) {
|
|
7617
|
+
const { name = "", email = "" } = $match.groups || {};
|
|
7618
|
+
const author = {
|
|
7619
|
+
name: name.trim(),
|
|
7620
|
+
email: email.trim()
|
|
7621
|
+
};
|
|
7622
|
+
authors.push(author);
|
|
7623
|
+
}
|
|
7624
|
+
return {
|
|
7625
|
+
...commit,
|
|
7626
|
+
authors,
|
|
7627
|
+
resolvedAuthors: [],
|
|
7628
|
+
description,
|
|
7629
|
+
type,
|
|
7630
|
+
scope,
|
|
7631
|
+
references,
|
|
7632
|
+
isBreaking
|
|
7633
|
+
};
|
|
7634
|
+
}
|
|
7635
|
+
async function getGitCommits(from, to = "HEAD", scopeMap = {}) {
|
|
7636
|
+
const rwaGitCommits = await getGitDiff(from, to);
|
|
7637
|
+
const commits = rwaGitCommits.map((commit) => parseGitCommit(commit, scopeMap)).filter(notNullish);
|
|
7638
|
+
return commits;
|
|
7639
|
+
}
|
|
7640
|
+
function getHeaders(githubToken) {
|
|
7641
|
+
return {
|
|
7642
|
+
accept: "application/vnd.github.v3+json",
|
|
7643
|
+
authorization: `token ${githubToken}`
|
|
7644
|
+
};
|
|
7645
|
+
}
|
|
7646
|
+
async function getResolvedAuthorLogin(params) {
|
|
7647
|
+
const { github, githubToken, commitHashes, email } = params;
|
|
7648
|
+
let login = "";
|
|
7649
|
+
if (!githubToken) {
|
|
7650
|
+
return login;
|
|
7651
|
+
}
|
|
7652
|
+
try {
|
|
7653
|
+
const data = await ofetch(`https://api.github.com/search/users?q=${encodeURIComponent(email)}`);
|
|
7654
|
+
login = data.items[0].login;
|
|
7655
|
+
} catch {
|
|
7656
|
+
}
|
|
7657
|
+
if (login) {
|
|
7658
|
+
return login;
|
|
7659
|
+
}
|
|
7660
|
+
if (commitHashes.length) {
|
|
7661
|
+
try {
|
|
7662
|
+
const data = await ofetch(`https://api.github.com/repos/${github}/commits/${commitHashes[0]}`, {
|
|
7663
|
+
headers: getHeaders(githubToken)
|
|
7664
|
+
});
|
|
7665
|
+
login = data?.author?.login || "";
|
|
7666
|
+
} catch (e) {
|
|
7667
|
+
}
|
|
7668
|
+
}
|
|
7669
|
+
return login;
|
|
7670
|
+
}
|
|
7671
|
+
async function getGitCommitsAndResolvedAuthors(commits, github, githubToken, resolvedLogins) {
|
|
7672
|
+
const resultCommits = [];
|
|
7673
|
+
const map = /* @__PURE__ */ new Map();
|
|
7674
|
+
for (const commit of commits) {
|
|
7675
|
+
const resolvedAuthors = [];
|
|
7676
|
+
for (const [index, author] of Object.entries(commit.authors)) {
|
|
7677
|
+
const { email, name } = author;
|
|
7678
|
+
if (email && name) {
|
|
7679
|
+
const commitHashes = [];
|
|
7680
|
+
if (index === "0") {
|
|
7681
|
+
commitHashes.push(commit.shortHash);
|
|
7682
|
+
}
|
|
7683
|
+
const resolvedAuthor = {
|
|
7684
|
+
name,
|
|
7685
|
+
email,
|
|
7686
|
+
commits: commitHashes,
|
|
7687
|
+
login: ""
|
|
7688
|
+
};
|
|
7689
|
+
if (!resolvedLogins?.has(email)) {
|
|
7690
|
+
const login = await getResolvedAuthorLogin({ github, githubToken, commitHashes, email });
|
|
7691
|
+
resolvedAuthor.login = login;
|
|
7692
|
+
resolvedLogins?.set(email, login);
|
|
7693
|
+
} else {
|
|
7694
|
+
const login = resolvedLogins?.get(email) || "";
|
|
7695
|
+
resolvedAuthor.login = login;
|
|
7696
|
+
}
|
|
7697
|
+
resolvedAuthors.push(resolvedAuthor);
|
|
7698
|
+
if (!map.has(email)) {
|
|
7699
|
+
map.set(email, resolvedAuthor);
|
|
7700
|
+
}
|
|
7701
|
+
}
|
|
7702
|
+
}
|
|
7703
|
+
const resultCommit = { ...commit, resolvedAuthors };
|
|
7704
|
+
resultCommits.push(resultCommit);
|
|
7705
|
+
}
|
|
7706
|
+
return {
|
|
7707
|
+
commits: resultCommits,
|
|
7708
|
+
contributors: Array.from(map.values())
|
|
7709
|
+
};
|
|
7710
|
+
}
|
|
7711
|
+
|
|
7712
|
+
function formatReferences(references, github, type) {
|
|
7713
|
+
const refs = references.filter((i) => {
|
|
7714
|
+
if (type === "issues")
|
|
7715
|
+
return i.type === "issue" || i.type === "pull-request";
|
|
7716
|
+
return i.type === "hash";
|
|
7717
|
+
}).map((ref) => {
|
|
7718
|
+
if (!github)
|
|
7719
|
+
return ref.value;
|
|
7720
|
+
if (ref.type === "pull-request" || ref.type === "issue")
|
|
7721
|
+
return `https://github.com/${github}/issues/${ref.value.slice(1)}`;
|
|
7722
|
+
return `[<samp>(${ref.value.slice(0, 5)})</samp>](https://github.com/${github}/commit/${ref.value})`;
|
|
7723
|
+
});
|
|
7724
|
+
const referencesString = join(refs).trim();
|
|
7725
|
+
if (type === "issues")
|
|
7726
|
+
return referencesString && `in ${referencesString}`;
|
|
7727
|
+
return referencesString;
|
|
7728
|
+
}
|
|
7729
|
+
function formatLine(commit, options) {
|
|
7730
|
+
const prRefs = formatReferences(commit.references, options.github, "issues");
|
|
7731
|
+
const hashRefs = formatReferences(commit.references, options.github, "hash");
|
|
7732
|
+
let authors = join([...new Set(commit.resolvedAuthors.map((i) => i.login ? `@${i.login}` : `**${i.name}**`))]).trim();
|
|
7733
|
+
if (authors) {
|
|
7734
|
+
authors = `by ${authors}`;
|
|
7735
|
+
}
|
|
7736
|
+
let refs = [authors, prRefs, hashRefs].filter((i) => i?.trim()).join(" ");
|
|
7737
|
+
if (refs) {
|
|
7738
|
+
refs = ` - ${refs}`;
|
|
7739
|
+
}
|
|
7740
|
+
const description = options.capitalize ? capitalize(commit.description) : commit.description;
|
|
7741
|
+
return [description, refs].filter((i) => i?.trim()).join(" ");
|
|
7742
|
+
}
|
|
7743
|
+
function formatTitle(name, options) {
|
|
7744
|
+
const emojisRE = /([\u2700-\u27BF]|[\uE000-\uF8FF]|\uD83C[\uDC00-\uDFFF]|\uD83D[\uDC00-\uDFFF]|[\u2011-\u26FF]|\uD83E[\uDD10-\uDDFF])/g;
|
|
7745
|
+
let formatName = name.trim();
|
|
7746
|
+
if (!options.emoji) {
|
|
7747
|
+
formatName = name.replace(emojisRE, "").trim();
|
|
7748
|
+
}
|
|
7749
|
+
return `### ${formatName}`;
|
|
7750
|
+
}
|
|
7751
|
+
function formatSection(commits, sectionName, options) {
|
|
7752
|
+
if (!commits.length)
|
|
7753
|
+
return [];
|
|
7754
|
+
const lines = ["", formatTitle(sectionName, options), ""];
|
|
7755
|
+
const scopes = groupBy(commits, "scope");
|
|
7756
|
+
let useScopeGroup = options.group;
|
|
7757
|
+
if (!Object.entries(scopes).some(([k, v]) => k && v.length > 1))
|
|
7758
|
+
useScopeGroup = false;
|
|
7759
|
+
Object.keys(scopes).sort().forEach((scope) => {
|
|
7760
|
+
let padding = "";
|
|
7761
|
+
let prefix = "";
|
|
7762
|
+
const scopeText = `**${options.scopeMap[scope] || scope}**`;
|
|
7763
|
+
if (scope && (useScopeGroup === true || useScopeGroup === "multiple" && scopes[scope].length > 1)) {
|
|
7764
|
+
lines.push(`- ${scopeText}:`);
|
|
7765
|
+
padding = " ";
|
|
7766
|
+
} else if (scope) {
|
|
7767
|
+
prefix = `${scopeText}: `;
|
|
7768
|
+
}
|
|
7769
|
+
lines.push(...scopes[scope].reverse().map((commit) => `${padding}- ${prefix}${formatLine(commit, options)}`));
|
|
7770
|
+
});
|
|
7771
|
+
return lines;
|
|
7772
|
+
}
|
|
7773
|
+
function getGitUserAvatar(userName) {
|
|
7774
|
+
const avatarUrl = `https://github.com/${userName}.png?size=48`;
|
|
7775
|
+
return avatarUrl;
|
|
7776
|
+
}
|
|
7777
|
+
function createContributorLine(contributors) {
|
|
7778
|
+
let loginLine = "";
|
|
7779
|
+
let unloginLine = "";
|
|
7780
|
+
contributors.forEach((contributor, index) => {
|
|
7781
|
+
const { name, email, login } = contributor;
|
|
7782
|
+
if (!login) {
|
|
7783
|
+
let line = `[${name}](mailto:${email})`;
|
|
7784
|
+
if (index < contributors.length - 1) {
|
|
7785
|
+
line += ", ";
|
|
7786
|
+
}
|
|
7787
|
+
unloginLine += line;
|
|
7788
|
+
} else {
|
|
7789
|
+
const avatar = getGitUserAvatar(login);
|
|
7790
|
+
loginLine += ` `;
|
|
7791
|
+
}
|
|
7792
|
+
});
|
|
7793
|
+
return `${loginLine}
|
|
7794
|
+
${unloginLine}`;
|
|
7795
|
+
}
|
|
7796
|
+
function generateMarkdown(params) {
|
|
7797
|
+
const VERSION_REG = /v\d+\.\d+\.\d+/;
|
|
7798
|
+
const { commits, options, showTitle, contributors } = params;
|
|
7799
|
+
const lines = [];
|
|
7800
|
+
const url = `https://github.com/${options.github}/compare/${options.from}...${options.to}`;
|
|
7801
|
+
if (showTitle) {
|
|
7802
|
+
const version = VERSION_REG.test(options.to) ? options.to : options.newVersion;
|
|
7803
|
+
const date = options.tagDateMap.get(options.to);
|
|
7804
|
+
let title = `## [${version}](${url})`;
|
|
7805
|
+
if (date) {
|
|
7806
|
+
title += ` (${date})`;
|
|
7807
|
+
}
|
|
7808
|
+
lines.push(title);
|
|
7809
|
+
}
|
|
7810
|
+
const [breaking, changes] = partition(commits, (c) => c.isBreaking);
|
|
7811
|
+
const group = groupBy(changes, "type");
|
|
7812
|
+
lines.push(...formatSection(breaking, options.titles.breakingChanges, options));
|
|
7813
|
+
for (const type of Object.keys(options.types)) {
|
|
7814
|
+
const items = group[type] || [];
|
|
7815
|
+
lines.push(...formatSection(items, options.types[type].title, options));
|
|
7816
|
+
}
|
|
7817
|
+
if (!lines.length) {
|
|
7818
|
+
lines.push("*No significant changes*");
|
|
7819
|
+
}
|
|
7820
|
+
if (!showTitle) {
|
|
7821
|
+
lines.push("", `##### [View changes on GitHub](${url})`);
|
|
7822
|
+
}
|
|
7823
|
+
if (showTitle) {
|
|
7824
|
+
lines.push("", "### \u2764\uFE0F Contributors", "");
|
|
7825
|
+
const contributorLine = createContributorLine(contributors);
|
|
7826
|
+
lines.push(contributorLine);
|
|
7827
|
+
}
|
|
7828
|
+
const md = convert(lines.join("\n").trim(), true);
|
|
7829
|
+
const markdown = md.replace(/ /g, "");
|
|
7830
|
+
return markdown;
|
|
7831
|
+
}
|
|
7832
|
+
async function isVersionInMarkdown(version, mdPath) {
|
|
7833
|
+
const VERSION_REG_OF_MARKDOWN = /## \[v\d+\.\d+\.\d+\]/g;
|
|
7834
|
+
let isIn = false;
|
|
7835
|
+
const md = await readFile(mdPath, "utf8");
|
|
7836
|
+
if (md) {
|
|
7837
|
+
const matches = md.match(VERSION_REG_OF_MARKDOWN);
|
|
7838
|
+
if (matches?.length) {
|
|
7839
|
+
const versionInMarkdown = `## [${version}]`;
|
|
7840
|
+
isIn = matches.includes(versionInMarkdown);
|
|
7841
|
+
}
|
|
7842
|
+
}
|
|
7843
|
+
return isIn;
|
|
7844
|
+
}
|
|
7845
|
+
async function writeMarkdown(md, mdPath, override = false) {
|
|
7846
|
+
let changelogMD;
|
|
7847
|
+
const changelogPrefix = "# Changelog";
|
|
7848
|
+
if (!override && existsSync(mdPath)) {
|
|
7849
|
+
changelogMD = await readFile(mdPath, "utf8");
|
|
7850
|
+
if (!changelogMD.startsWith(changelogPrefix)) {
|
|
7851
|
+
changelogMD = `${changelogPrefix}
|
|
7852
|
+
|
|
7853
|
+
${changelogMD}`;
|
|
7854
|
+
}
|
|
7855
|
+
} else {
|
|
7856
|
+
changelogMD = `${changelogPrefix}
|
|
7857
|
+
|
|
7858
|
+
`;
|
|
7859
|
+
}
|
|
7860
|
+
const lastEntry = changelogMD.match(/^###?\s+.*$/m);
|
|
7861
|
+
if (lastEntry) {
|
|
7862
|
+
changelogMD = `${changelogMD.slice(0, lastEntry.index) + md}
|
|
7863
|
+
|
|
7864
|
+
${changelogMD.slice(lastEntry.index)}`;
|
|
7865
|
+
} else {
|
|
7866
|
+
changelogMD += `
|
|
7867
|
+
${md}
|
|
7868
|
+
|
|
7869
|
+
`;
|
|
7870
|
+
}
|
|
7871
|
+
await writeFile(mdPath, changelogMD);
|
|
7872
|
+
}
|
|
7873
|
+
|
|
7874
|
+
function createDefaultOptions() {
|
|
7875
|
+
const cwd = process.cwd();
|
|
7876
|
+
const options = {
|
|
7877
|
+
cwd,
|
|
7878
|
+
types: {
|
|
7879
|
+
feat: { title: "\u{1F680} Features" },
|
|
7880
|
+
fix: { title: "\u{1F41E} Bug Fixes" },
|
|
7881
|
+
perf: { title: "\u{1F525} Performance" },
|
|
7882
|
+
refactor: { title: "\u{1F485} Refactors" },
|
|
7883
|
+
docs: { title: "\u{1F4D6} Documentation" },
|
|
7884
|
+
build: { title: "\u{1F4E6} Build" },
|
|
7885
|
+
types: { title: "\u{1F30A} Types" },
|
|
7886
|
+
chore: { title: "\u{1F3E1} Chore" },
|
|
7887
|
+
examples: { title: "\u{1F3C0} Examples" },
|
|
7888
|
+
test: { title: "\u2705 Tests" },
|
|
7889
|
+
style: { title: "\u{1F3A8} Styles" },
|
|
7890
|
+
ci: { title: "\u{1F916} CI" }
|
|
7891
|
+
},
|
|
7892
|
+
scopeMap: {},
|
|
7893
|
+
github: "",
|
|
7894
|
+
githubToken: process.env.GITHUB_TOKEN || "",
|
|
7895
|
+
from: "",
|
|
7896
|
+
to: "",
|
|
7897
|
+
tags: [],
|
|
7898
|
+
tagDateMap: /* @__PURE__ */ new Map(),
|
|
7899
|
+
prerelease: false,
|
|
7900
|
+
capitalize: true,
|
|
7901
|
+
emoji: true,
|
|
7902
|
+
titles: {
|
|
7903
|
+
breakingChanges: "\u{1F6A8} Breaking Changes"
|
|
7904
|
+
},
|
|
7905
|
+
output: "CHANGELOG.md",
|
|
7906
|
+
overrideChangelog: false,
|
|
7907
|
+
newVersion: ""
|
|
7908
|
+
};
|
|
7909
|
+
return options;
|
|
7910
|
+
}
|
|
7911
|
+
async function getOptionsFromPkg(cwd) {
|
|
7912
|
+
let githubToken = "";
|
|
7913
|
+
let newVersion = "";
|
|
7914
|
+
try {
|
|
7915
|
+
const pkgJson = await readFile(`${cwd}/package.json`, "utf-8");
|
|
7916
|
+
const pkg = JSON.parse(pkgJson);
|
|
7917
|
+
githubToken = pkg?.["github-token"] || "";
|
|
7918
|
+
newVersion = pkg?.version || "";
|
|
7919
|
+
} catch {
|
|
7920
|
+
}
|
|
7921
|
+
return {
|
|
7922
|
+
githubToken,
|
|
7923
|
+
newVersion
|
|
7924
|
+
};
|
|
7925
|
+
}
|
|
7926
|
+
async function initOptions() {
|
|
7927
|
+
const options = createDefaultOptions();
|
|
7928
|
+
const { githubToken, newVersion } = await getOptionsFromPkg(options.cwd);
|
|
7929
|
+
if (!options.githubToken) {
|
|
7930
|
+
options.githubToken = githubToken;
|
|
7931
|
+
}
|
|
7932
|
+
if (newVersion) {
|
|
7933
|
+
options.newVersion = `v${newVersion}`;
|
|
7934
|
+
}
|
|
7935
|
+
if (!options.from) {
|
|
7936
|
+
options.from = await getLastGitTag();
|
|
7937
|
+
}
|
|
7938
|
+
if (!options.to) {
|
|
7939
|
+
options.to = await getCurrentGitBranch();
|
|
7940
|
+
}
|
|
7941
|
+
if (options.to === options.from) {
|
|
7942
|
+
const lastTag = await getLastGitTag(-1);
|
|
7943
|
+
const firstCommit = await getFirstGitCommit();
|
|
7944
|
+
options.from = lastTag || firstCommit;
|
|
7945
|
+
}
|
|
7946
|
+
options.tags = await getTotalGitTags();
|
|
7947
|
+
options.tagDateMap = await getTagDateMap();
|
|
7948
|
+
options.github = await getGitHubRepo();
|
|
7949
|
+
options.prerelease = isPrerelease(options.to);
|
|
7950
|
+
return options;
|
|
7951
|
+
}
|
|
7952
|
+
async function generateChangelogByTag(options) {
|
|
7953
|
+
const gitCommits = await getGitCommits(options.from, options.to, options.scopeMap);
|
|
7954
|
+
const { commits, contributors } = await getGitCommitsAndResolvedAuthors(
|
|
7955
|
+
gitCommits,
|
|
7956
|
+
options.github,
|
|
7957
|
+
options.githubToken
|
|
7958
|
+
);
|
|
7959
|
+
const md = generateMarkdown({ commits, options, showTitle: true, contributors });
|
|
7960
|
+
return md;
|
|
7961
|
+
}
|
|
7962
|
+
async function generateChangelogByTags(options) {
|
|
7963
|
+
const tags = getFromToTags(options.tags);
|
|
7964
|
+
const bar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
|
|
7965
|
+
bar.start(tags.length, 0);
|
|
7966
|
+
let md = "";
|
|
7967
|
+
const resolvedLogins = /* @__PURE__ */ new Map();
|
|
7968
|
+
for (let i = 0; i < tags.length; i += 1) {
|
|
7969
|
+
const { from, to } = tags[i];
|
|
7970
|
+
const gitCommits = await getGitCommits(from, to, options.scopeMap);
|
|
7971
|
+
const { commits, contributors } = await getGitCommitsAndResolvedAuthors(
|
|
7972
|
+
gitCommits,
|
|
7973
|
+
options.github,
|
|
7974
|
+
options.githubToken,
|
|
7975
|
+
resolvedLogins
|
|
7976
|
+
);
|
|
7977
|
+
const opts = { ...options, from, to };
|
|
7978
|
+
const nextMd = generateMarkdown({ commits, options: opts, showTitle: true, contributors });
|
|
7979
|
+
md = `${nextMd}
|
|
7980
|
+
|
|
7981
|
+
${md}`;
|
|
7982
|
+
bar.update(i + 1);
|
|
7983
|
+
}
|
|
7984
|
+
bar.stop();
|
|
7985
|
+
return md;
|
|
7986
|
+
}
|
|
7987
|
+
async function generateChangelog(total = false) {
|
|
7988
|
+
const options = await initOptions();
|
|
7989
|
+
let md = "";
|
|
7990
|
+
if (total) {
|
|
7991
|
+
md = await generateChangelogByTags(options);
|
|
7992
|
+
await writeMarkdown(md, options.output, true);
|
|
7993
|
+
return;
|
|
7994
|
+
}
|
|
7995
|
+
const isIn = await isVersionInMarkdown(options.to, options.output);
|
|
7996
|
+
if (!options.overrideChangelog && isIn) {
|
|
7997
|
+
return;
|
|
7998
|
+
}
|
|
7999
|
+
md = await generateChangelogByTag(options);
|
|
8000
|
+
await writeMarkdown(md, options.output, false);
|
|
8001
|
+
}
|
|
8002
|
+
|
|
8003
|
+
async function genChangelog(total = false) {
|
|
8004
|
+
await generateChangelog(total);
|
|
8005
|
+
}
|
|
8006
|
+
|
|
8007
|
+
async function release() {
|
|
8008
|
+
await versionBumpp({
|
|
8009
|
+
files: ["**/package.json", "!**/node_modules"],
|
|
8010
|
+
execute: "npx soy changelog",
|
|
8011
|
+
all: true,
|
|
8012
|
+
tag: true,
|
|
8013
|
+
commit: "chore(projects): release v%s",
|
|
8014
|
+
push: true
|
|
8015
|
+
});
|
|
7453
8016
|
}
|
|
7454
8017
|
|
|
7455
8018
|
const cli = cac("soybean");
|
|
7456
|
-
cli.version(version).help();
|
|
8019
|
+
cli.version(version).option("--total", "Generate changelog by total tags").help();
|
|
7457
8020
|
const commands = {
|
|
7458
8021
|
"git-commit": {
|
|
7459
8022
|
desc: "\u751F\u6210\u7B26\u5408 Angular \u89C4\u8303\u7684 git commit",
|
|
@@ -7486,6 +8049,16 @@ const commands = {
|
|
|
7486
8049
|
"lint-staged": {
|
|
7487
8050
|
desc: "\u6267\u884Clint-staged",
|
|
7488
8051
|
action: lintStaged
|
|
8052
|
+
},
|
|
8053
|
+
changelog: {
|
|
8054
|
+
desc: "\u751F\u6210changelog",
|
|
8055
|
+
action: async (args) => {
|
|
8056
|
+
await genChangelog(args?.total);
|
|
8057
|
+
}
|
|
8058
|
+
},
|
|
8059
|
+
release: {
|
|
8060
|
+
desc: "\u53D1\u5E03\uFF1A\u66F4\u65B0\u7248\u672C\u53F7\u3001\u751F\u6210changelog\u3001\u63D0\u4EA4\u4EE3\u7801",
|
|
8061
|
+
action: release
|
|
7489
8062
|
}
|
|
7490
8063
|
};
|
|
7491
8064
|
Object.entries(commands).forEach(([command, { desc, action }]) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soybeanjs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "SoybeanJS's command line tools",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Soybean",
|
|
@@ -25,26 +25,31 @@
|
|
|
25
25
|
}
|
|
26
26
|
},
|
|
27
27
|
"bin": {
|
|
28
|
-
"soybean": "dist/index.
|
|
29
|
-
"soy": "dist/index.
|
|
28
|
+
"soybean": "dist/index.cjs",
|
|
29
|
+
"soy": "dist/index.cjs"
|
|
30
30
|
},
|
|
31
31
|
"files": [
|
|
32
32
|
"dist"
|
|
33
33
|
],
|
|
34
34
|
"dependencies": {
|
|
35
|
-
"
|
|
35
|
+
"bumpp": "9.1.0",
|
|
36
|
+
"cac": "6.7.14",
|
|
37
|
+
"cli-progress": "^3.12.0",
|
|
38
|
+
"convert-gitmoji": "0.1.3",
|
|
39
|
+
"dayjs": "1.11.8",
|
|
36
40
|
"enquirer": "2.3.6",
|
|
37
41
|
"execa": "7.1.1",
|
|
38
42
|
"kolorist": "1.8.0",
|
|
39
43
|
"lint-staged": "13.2.2",
|
|
40
44
|
"minimist": "1.2.8",
|
|
41
45
|
"npm-check-updates": "16.10.12",
|
|
46
|
+
"ofetch": "1.0.1",
|
|
42
47
|
"rimraf": "5.0.1"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
45
50
|
"@soybeanjs/cli": "link:",
|
|
51
|
+
"@types/cli-progress": "^3.11.0",
|
|
46
52
|
"@types/node": "20.2.5",
|
|
47
|
-
"bumpp": "9.1.0",
|
|
48
53
|
"eslint": "8.41.0",
|
|
49
54
|
"eslint-config-soybeanjs": "0.4.7",
|
|
50
55
|
"simple-git-hooks": "2.8.1",
|
|
@@ -56,6 +61,7 @@
|
|
|
56
61
|
"commit-msg": "pnpm soy git-commit-verify",
|
|
57
62
|
"pre-commit": "pnpm typecheck && pnpm soy lint-staged"
|
|
58
63
|
},
|
|
64
|
+
"github-token": "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",
|
|
59
65
|
"scripts": {
|
|
60
66
|
"build": "pnpm typecheck && unbuild",
|
|
61
67
|
"lint": "eslint . --fix",
|
|
@@ -63,9 +69,8 @@
|
|
|
63
69
|
"commit": "soy git-commit",
|
|
64
70
|
"cleanup": "soy cleanup",
|
|
65
71
|
"update-pkg": "soy update-pkg",
|
|
66
|
-
"update-version": "bumpp --commit --push --tag",
|
|
67
72
|
"publish-pkg": "pnpm -r publish --access public",
|
|
68
73
|
"typecheck": "tsc --noEmit",
|
|
69
|
-
"release": "
|
|
74
|
+
"release": "soy release && pnpm build && pnpm publish-pkg"
|
|
70
75
|
}
|
|
71
76
|
}
|