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