@icebreakers/changelog-github 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.zh.md +2 -2
- package/dist/index.cjs +220 -276
- package/dist/index.d.cts +12 -15
- package/dist/index.d.ts +12 -13
- package/dist/index.js +241 -0
- package/package.json +17 -10
- package/dist/index.mjs +0 -304
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ All other behaviours (tuple arguments, required `repo` option, rendered layout)
|
|
|
46
46
|
|
|
47
47
|
Leverage the summary tags to control the metadata the formatter displays:
|
|
48
48
|
|
|
49
|
-
```
|
|
49
|
+
```text
|
|
50
50
|
pr: #42
|
|
51
51
|
commit: 1234567890abcdef1234567890abcdef12345678
|
|
52
52
|
author: @octocat
|
|
@@ -59,7 +59,7 @@ Add optional `tailwind-config` flag to the lint preset
|
|
|
59
59
|
|
|
60
60
|
The rendered changelog entry becomes:
|
|
61
61
|
|
|
62
|
-
```
|
|
62
|
+
```md
|
|
63
63
|
- ✨ **Add optional `tailwind-config` flag** [#42] [`1234567`](https://github.com/org/repo/commit/1234567890abcdef1234567890abcdef12345678) by @octocat
|
|
64
64
|
- accepts a relative path to `tailwind.config.ts`
|
|
65
65
|
- falls back to the workspace root when unset
|
package/README.zh.md
CHANGED
|
@@ -44,7 +44,7 @@ pnpm add -D @changesets/cli @icebreakers/changelog-github
|
|
|
44
44
|
|
|
45
45
|
可以借助摘要指令精准控制展示的元数据:
|
|
46
46
|
|
|
47
|
-
```
|
|
47
|
+
```text
|
|
48
48
|
pr: #42
|
|
49
49
|
commit: 1234567890abcdef1234567890abcdef12345678
|
|
50
50
|
author: @octocat
|
|
@@ -57,7 +57,7 @@ author: @octocat
|
|
|
57
57
|
|
|
58
58
|
最终 changelog 会渲染为:
|
|
59
59
|
|
|
60
|
-
```
|
|
60
|
+
```md
|
|
61
61
|
- ✨ **为 lint preset 新增可选的 `tailwind-config` 配置项** [#42] [`1234567`](https://github.com/org/repo/commit/1234567890abcdef1234567890abcdef12345678) by @octocat
|
|
62
62
|
- 接受相对路径指向 `tailwind.config.ts`
|
|
63
63
|
- 未设置时默认读取工作区根目录
|
package/dist/index.cjs
CHANGED
|
@@ -1,304 +1,248 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
Object.defineProperties(exports, {
|
|
2
|
+
__esModule: { value: true },
|
|
3
|
+
[Symbol.toStringTag]: { value: "Module" }
|
|
4
|
+
});
|
|
5
|
+
let _changesets_get_github_info = require("@changesets/get-github-info");
|
|
6
|
+
//#region src/index.ts
|
|
7
|
+
(0, require("dotenv").config)();
|
|
8
|
+
const releaseTypeMap = {
|
|
9
|
+
major: {
|
|
10
|
+
icon: "🚀",
|
|
11
|
+
label: "Major"
|
|
12
|
+
},
|
|
13
|
+
minor: {
|
|
14
|
+
icon: "✨",
|
|
15
|
+
label: "Minor"
|
|
16
|
+
},
|
|
17
|
+
patch: {
|
|
18
|
+
icon: "🐛",
|
|
19
|
+
label: "Patch"
|
|
20
|
+
},
|
|
21
|
+
none: {
|
|
22
|
+
icon: "📝",
|
|
23
|
+
label: "Update"
|
|
24
|
+
}
|
|
10
25
|
};
|
|
26
|
+
const CONVENTIONAL_HEADLINE_RE = /^(\w+)(?:\(([^)]*)\))?(!?):\s(.+)$/;
|
|
27
|
+
const NEWLINE_RE = /\r?\n/;
|
|
28
|
+
const LIST_MARKER_RE = /^[-*+]\s+/;
|
|
29
|
+
const PR_META_RE = /^(?:pr|pull|pull\s*request):\s*#?(\d+)\s*$/i;
|
|
30
|
+
const COMMIT_META_RE = /^commit:\s*(\S+)\s*$/i;
|
|
31
|
+
const USER_META_RE = /^(?:author|user):\s*(\S+)\s*$/i;
|
|
32
|
+
const LEADING_AT_RE = /^@/;
|
|
33
|
+
const BREAKING_CHANGE_RE = /^BREAKING[\s-]CHANGE:/i;
|
|
34
|
+
const LIST_LINE_RE = /^(?:[-*+]\s+|\d+[.)]\s+)/;
|
|
35
|
+
const FALLBACK_USER_RE = /@([^\]]+)/;
|
|
36
|
+
const CODE_FENCE_RE = /^\s*```/;
|
|
37
|
+
const LIST_LIKE_RE = /^\s*(?:[-*+]\s+|\d+[.)]\s+)/;
|
|
38
|
+
const PARAGRAPH_BREAK_RE = /\n\s*\n/;
|
|
11
39
|
function resolveReleaseType(type) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
}
|
|
15
|
-
return "none";
|
|
40
|
+
if (type && type in releaseTypeMap) return type;
|
|
41
|
+
return "none";
|
|
16
42
|
}
|
|
17
43
|
function assertRepo(options) {
|
|
18
|
-
|
|
19
|
-
throw new Error(
|
|
20
|
-
'Please provide a repo to this changelog generator like this:\n"changelog": ["@icebreakers/changelog-github", { "repo": "org/repo" }]'
|
|
21
|
-
);
|
|
22
|
-
}
|
|
44
|
+
if (!options || typeof options["repo"] !== "string" || options["repo"].trim().length === 0) throw new Error("Please provide a repo to this changelog generator like this:\n\"changelog\": [\"@icebreakers/changelog-github\", { \"repo\": \"org/repo\" }]");
|
|
23
45
|
}
|
|
24
46
|
async function collectDependencyReferences(changesets, repo) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return links.commit;
|
|
35
|
-
})
|
|
36
|
-
);
|
|
37
|
-
return [...new Set(references.filter((link) => Boolean(link)))];
|
|
47
|
+
const references = await Promise.all(changesets.map(async (cs) => {
|
|
48
|
+
if (!cs.commit) return null;
|
|
49
|
+
const { links } = await (0, _changesets_get_github_info.getInfo)({
|
|
50
|
+
repo,
|
|
51
|
+
commit: cs.commit
|
|
52
|
+
});
|
|
53
|
+
return links.commit;
|
|
54
|
+
}));
|
|
55
|
+
return [...new Set(references.filter((link) => Boolean(link)))];
|
|
38
56
|
}
|
|
39
57
|
function formatDependencyLine(references, dependenciesUpdated) {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
${details}`;
|
|
53
|
-
}
|
|
54
|
-
const header = firstCommit ? `- \u{1F4E6} Updated ${count} dependencies ${firstCommit}` : `- \u{1F4E6} Updated ${count} dependencies`;
|
|
55
|
-
const detailBlock = [
|
|
56
|
-
" <details><summary>Details</summary>",
|
|
57
|
-
"",
|
|
58
|
-
` ${dependencySummaries.join(", ")}`,
|
|
59
|
-
"",
|
|
60
|
-
" </details>"
|
|
61
|
-
].join("\n");
|
|
62
|
-
return `${header}
|
|
63
|
-
${detailBlock}`;
|
|
58
|
+
const count = dependenciesUpdated.length;
|
|
59
|
+
const firstCommit = references[0] || "";
|
|
60
|
+
if (count === 0) return firstCommit ? `- 📦 **Dependencies** ${firstCommit}` : "- 📦 **Dependencies**";
|
|
61
|
+
const dependencySummaries = dependenciesUpdated.map((dependency) => `\`${dependency.name}@${dependency.newVersion}\``);
|
|
62
|
+
if (count <= 3) return `${firstCommit ? `- 📦 **Dependencies** ${firstCommit}` : "- 📦 **Dependencies**"}\n${` → ${dependencySummaries.join(", ")}`}`;
|
|
63
|
+
return `${firstCommit ? `- 📦 Updated ${count} dependencies ${firstCommit}` : `- 📦 Updated ${count} dependencies`}\n${[
|
|
64
|
+
" <details><summary>Details</summary>",
|
|
65
|
+
"",
|
|
66
|
+
` ${dependencySummaries.join(", ")}`,
|
|
67
|
+
"",
|
|
68
|
+
" </details>"
|
|
69
|
+
].join("\n")}`;
|
|
64
70
|
}
|
|
65
71
|
function parseConventionalHeadline(raw) {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
72
|
+
const match = raw.match(CONVENTIONAL_HEADLINE_RE);
|
|
73
|
+
if (!match) return {
|
|
74
|
+
breaking: false,
|
|
75
|
+
description: raw
|
|
76
|
+
};
|
|
77
|
+
const type = match[1];
|
|
78
|
+
const scope = match[2] || void 0;
|
|
79
|
+
const description = (match[4] ?? raw).trim();
|
|
80
|
+
return {
|
|
81
|
+
type,
|
|
82
|
+
scope,
|
|
83
|
+
breaking: match[3] === "!",
|
|
84
|
+
description
|
|
85
|
+
};
|
|
78
86
|
}
|
|
79
87
|
function formatHeadline(raw) {
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
const parsed = parseConventionalHeadline(raw);
|
|
89
|
+
if (!parsed.type) return {
|
|
90
|
+
text: raw,
|
|
91
|
+
breaking: parsed.breaking
|
|
92
|
+
};
|
|
93
|
+
return {
|
|
94
|
+
text: `${parsed.scope ? `${parsed.scope}: ` : ""}${parsed.description}`,
|
|
95
|
+
breaking: parsed.breaking
|
|
96
|
+
};
|
|
89
97
|
}
|
|
90
98
|
function normalizeUserMentions(users) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return normalized[0];
|
|
97
|
-
}
|
|
98
|
-
if (normalized.length === 2) {
|
|
99
|
-
return `${normalized[0]} and ${normalized[1]}`;
|
|
100
|
-
}
|
|
101
|
-
return `${normalized.slice(0, -1).join(", ")}, and ${normalized.at(-1)}`;
|
|
99
|
+
const normalized = users.map((user) => user.trim()).filter(Boolean).map((user) => user.startsWith("@") ? user : `@${user}`);
|
|
100
|
+
if (normalized.length === 0) return "";
|
|
101
|
+
if (normalized.length === 1) return normalized[0] ?? "";
|
|
102
|
+
if (normalized.length === 2) return `${normalized[0] ?? ""} and ${normalized[1] ?? ""}`;
|
|
103
|
+
return `${normalized.slice(0, -1).join(", ")}, and ${normalized.at(-1) ?? ""}`;
|
|
102
104
|
}
|
|
103
105
|
function extractSummaryMeta(summary) {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
prNumber,
|
|
138
|
-
commitRef,
|
|
139
|
-
users,
|
|
140
|
-
contentLines,
|
|
141
|
-
breaking
|
|
142
|
-
};
|
|
106
|
+
let prNumber;
|
|
107
|
+
let commitRef;
|
|
108
|
+
let breaking = false;
|
|
109
|
+
const users = [];
|
|
110
|
+
const contentLines = summary.split(NEWLINE_RE).filter((line) => {
|
|
111
|
+
const withoutListMarker = line.trim().replace(LIST_MARKER_RE, "");
|
|
112
|
+
const prMatch = withoutListMarker.match(PR_META_RE);
|
|
113
|
+
if (prMatch) {
|
|
114
|
+
const num = Number(prMatch[1] ?? NaN);
|
|
115
|
+
if (!Number.isNaN(num)) prNumber = num;
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const commitMatch = withoutListMarker.match(COMMIT_META_RE);
|
|
119
|
+
if (commitMatch) {
|
|
120
|
+
commitRef = commitMatch[1];
|
|
121
|
+
return false;
|
|
122
|
+
}
|
|
123
|
+
const userMatch = withoutListMarker.match(USER_META_RE);
|
|
124
|
+
if (userMatch) {
|
|
125
|
+
const user = userMatch[1];
|
|
126
|
+
if (user) users.push(user.replace(LEADING_AT_RE, ""));
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
if (BREAKING_CHANGE_RE.test(withoutListMarker)) breaking = true;
|
|
130
|
+
return true;
|
|
131
|
+
}).map((line) => line.trimEnd());
|
|
132
|
+
return {
|
|
133
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
134
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
135
|
+
users,
|
|
136
|
+
contentLines,
|
|
137
|
+
breaking
|
|
138
|
+
};
|
|
143
139
|
}
|
|
144
140
|
function parseSummary(summary) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const breaking = bodyBreaking || (_nullishCoalesce(_optionalChain([headlineParsed, 'optionalAccess', _ => _.breaking]), () => ( false)));
|
|
170
|
-
return {
|
|
171
|
-
headline,
|
|
172
|
-
detailLines,
|
|
173
|
-
prNumber,
|
|
174
|
-
commitRef,
|
|
175
|
-
users,
|
|
176
|
-
breaking
|
|
177
|
-
};
|
|
141
|
+
const { prNumber, commitRef, users, contentLines, breaking: bodyBreaking } = extractSummaryMeta(summary);
|
|
142
|
+
const nonEmptyIndex = contentLines.findIndex((line) => line.trim().length > 0);
|
|
143
|
+
if (nonEmptyIndex === -1) return {
|
|
144
|
+
headline: "",
|
|
145
|
+
detailLines: [],
|
|
146
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
147
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
148
|
+
users,
|
|
149
|
+
breaking: bodyBreaking
|
|
150
|
+
};
|
|
151
|
+
const firstContentLine = contentLines[nonEmptyIndex]?.trim() ?? "";
|
|
152
|
+
const startsWithList = LIST_LINE_RE.test(firstContentLine);
|
|
153
|
+
const headline = startsWithList ? "" : firstContentLine;
|
|
154
|
+
const detailLines = startsWithList ? contentLines.slice(nonEmptyIndex) : contentLines.slice(nonEmptyIndex + 1);
|
|
155
|
+
const headlineParsed = headline ? parseConventionalHeadline(headline) : null;
|
|
156
|
+
const breaking = bodyBreaking || (headlineParsed?.breaking ?? false);
|
|
157
|
+
return {
|
|
158
|
+
headline,
|
|
159
|
+
detailLines,
|
|
160
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
161
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
162
|
+
users,
|
|
163
|
+
breaking
|
|
164
|
+
};
|
|
178
165
|
}
|
|
179
166
|
async function resolveLinks(repo, parsed, changesetCommit) {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
pull: null,
|
|
202
|
-
user: null
|
|
203
|
-
};
|
|
167
|
+
if (parsed.prNumber !== void 0) {
|
|
168
|
+
const { links } = await (0, _changesets_get_github_info.getInfoFromPullRequest)({
|
|
169
|
+
repo,
|
|
170
|
+
pull: parsed.prNumber
|
|
171
|
+
});
|
|
172
|
+
if (parsed.commitRef) links.commit = `[\`${parsed.commitRef.slice(0, 7)}\`](https://github.com/${repo}/commit/${parsed.commitRef})`;
|
|
173
|
+
return links;
|
|
174
|
+
}
|
|
175
|
+
const commitToUse = parsed.commitRef ?? changesetCommit;
|
|
176
|
+
if (commitToUse) {
|
|
177
|
+
const { links } = await (0, _changesets_get_github_info.getInfo)({
|
|
178
|
+
repo,
|
|
179
|
+
commit: commitToUse
|
|
180
|
+
});
|
|
181
|
+
return links;
|
|
182
|
+
}
|
|
183
|
+
return {
|
|
184
|
+
commit: null,
|
|
185
|
+
pull: null,
|
|
186
|
+
user: null
|
|
187
|
+
};
|
|
204
188
|
}
|
|
205
189
|
function buildUserMentions(users, fallbackUser) {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
213
|
-
return "";
|
|
190
|
+
if (users.length > 0) return normalizeUserMentions(users);
|
|
191
|
+
if (fallbackUser) {
|
|
192
|
+
const matchedUser = fallbackUser.match(FALLBACK_USER_RE)?.[1];
|
|
193
|
+
return matchedUser ? normalizeUserMentions([matchedUser]) : "";
|
|
194
|
+
}
|
|
195
|
+
return "";
|
|
214
196
|
}
|
|
215
197
|
function indentMarkdownBlock(lines, spaces) {
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
return `${prefix}${line}`;
|
|
222
|
-
}).join("\n");
|
|
198
|
+
const prefix = " ".repeat(spaces);
|
|
199
|
+
return lines.map((line) => {
|
|
200
|
+
if (line.trim().length === 0) return "";
|
|
201
|
+
return `${prefix}${line}`;
|
|
202
|
+
}).join("\n");
|
|
223
203
|
}
|
|
224
204
|
function formatDetailBlock(detailLines) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
if (hasFence || isListLike) {
|
|
232
|
-
return indentMarkdownBlock(trimmed, 2);
|
|
233
|
-
}
|
|
234
|
-
const paragraphs = trimmed.join("\n").split(/\n\s*\n/).filter((chunk) => chunk.trim().length > 0);
|
|
235
|
-
const lines = paragraphs.flatMap(
|
|
236
|
-
(chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)
|
|
237
|
-
);
|
|
238
|
-
return lines.map((text) => ` - ${text}`).join("\n");
|
|
205
|
+
const trimmed = detailLines.join("\n").trim().split("\n").map((line) => line.trimEnd());
|
|
206
|
+
const hasFence = trimmed.some((line) => CODE_FENCE_RE.test(line));
|
|
207
|
+
const isListLike = trimmed.some((line) => LIST_LIKE_RE.test(line));
|
|
208
|
+
if (trimmed.length === 0) return "";
|
|
209
|
+
if (hasFence || isListLike) return indentMarkdownBlock(trimmed, 2);
|
|
210
|
+
return trimmed.join("\n").split(PARAGRAPH_BREAK_RE).filter((chunk) => chunk.trim().length > 0).flatMap((chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)).map((text) => ` - ${text}`).join("\n");
|
|
239
211
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
const breakingPrefix = isBreaking ? "\u26A0\uFE0F " : "";
|
|
270
|
-
const icon = isBreaking ? "\u{1F4A5}" : releaseTypeMap[resolvedType].icon;
|
|
271
|
-
const mainLineParts = [
|
|
272
|
-
`- ${icon}`,
|
|
273
|
-
`${breakingPrefix}**${headlineText}**`
|
|
274
|
-
];
|
|
275
|
-
if (links.pull) {
|
|
276
|
-
mainLineParts.push(links.pull);
|
|
277
|
-
} else if (links.commit) {
|
|
278
|
-
mainLineParts.push(links.commit);
|
|
279
|
-
}
|
|
280
|
-
if (links.pull && parsedSummary.commitRef && links.commit) {
|
|
281
|
-
mainLineParts.push(links.commit);
|
|
282
|
-
}
|
|
283
|
-
if (userMentions) {
|
|
284
|
-
mainLineParts.push(`by ${userMentions}`);
|
|
285
|
-
}
|
|
286
|
-
const mainLine = mainLineParts.join(" ");
|
|
287
|
-
const detailBlock = formatDetailBlock(parsedSummary.detailLines);
|
|
288
|
-
return detailBlock.length > 0 ? `
|
|
289
|
-
|
|
290
|
-
${mainLine}
|
|
291
|
-
${detailBlock}` : `
|
|
292
|
-
|
|
293
|
-
${mainLine}`;
|
|
294
|
-
}
|
|
212
|
+
const changelogFunctions = {
|
|
213
|
+
async getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
|
|
214
|
+
assertRepo(options);
|
|
215
|
+
const repo = options.repo;
|
|
216
|
+
if (dependenciesUpdated.length === 0) return "";
|
|
217
|
+
return formatDependencyLine(await collectDependencyReferences(changesets, repo), dependenciesUpdated);
|
|
218
|
+
},
|
|
219
|
+
async getReleaseLine(changeset, type, options) {
|
|
220
|
+
assertRepo(options);
|
|
221
|
+
const repo = options.repo;
|
|
222
|
+
const parsedSummary = parseSummary(changeset.summary);
|
|
223
|
+
const resolvedType = resolveReleaseType(type);
|
|
224
|
+
const links = await resolveLinks(repo, parsedSummary, changeset.commit);
|
|
225
|
+
const userMentions = buildUserMentions(parsedSummary.users, links.user);
|
|
226
|
+
const { text: headlineText, breaking: headlineBreaking } = parsedSummary.headline ? formatHeadline(parsedSummary.headline) : {
|
|
227
|
+
text: "Miscellaneous improvements",
|
|
228
|
+
breaking: false
|
|
229
|
+
};
|
|
230
|
+
const isBreaking = parsedSummary.breaking || headlineBreaking;
|
|
231
|
+
const breakingPrefix = isBreaking ? "⚠️ " : "";
|
|
232
|
+
const mainLineParts = [`- ${isBreaking ? "💥" : releaseTypeMap[resolvedType].icon}`, `${breakingPrefix}**${headlineText}**`];
|
|
233
|
+
if (links.pull) mainLineParts.push(links.pull);
|
|
234
|
+
else if (links.commit) mainLineParts.push(links.commit);
|
|
235
|
+
if (links.pull && parsedSummary.commitRef && links.commit) mainLineParts.push(links.commit);
|
|
236
|
+
if (userMentions) mainLineParts.push(`by ${userMentions}`);
|
|
237
|
+
const mainLine = mainLineParts.join(" ");
|
|
238
|
+
const detailBlock = formatDetailBlock(parsedSummary.detailLines);
|
|
239
|
+
return detailBlock.length > 0 ? `\n\n${mainLine}\n${detailBlock}` : `\n\n${mainLine}`;
|
|
240
|
+
}
|
|
295
241
|
};
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
exports.default = index_default; exports.formatHeadline = formatHeadline; exports.getDependencyReleaseLine = getDependencyReleaseLine; exports.getReleaseLine = getReleaseLine; exports.parseConventionalHeadline = parseConventionalHeadline;
|
|
242
|
+
const { getDependencyReleaseLine, getReleaseLine } = changelogFunctions;
|
|
243
|
+
//#endregion
|
|
244
|
+
exports.default = changelogFunctions;
|
|
245
|
+
exports.formatHeadline = formatHeadline;
|
|
246
|
+
exports.getDependencyReleaseLine = getDependencyReleaseLine;
|
|
247
|
+
exports.getReleaseLine = getReleaseLine;
|
|
248
|
+
exports.parseConventionalHeadline = parseConventionalHeadline;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,22 +1,19 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { ChangelogFunctions } from
|
|
1
|
+
import * as _changesets_types0 from "@changesets/types";
|
|
2
|
+
import { ChangelogFunctions } from "@changesets/types";
|
|
3
3
|
|
|
4
|
+
//#region src/index.d.ts
|
|
4
5
|
interface ConventionalHeadline {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
type?: string | undefined;
|
|
7
|
+
scope?: string | undefined;
|
|
8
|
+
breaking: boolean;
|
|
9
|
+
description: string;
|
|
9
10
|
}
|
|
10
11
|
declare function parseConventionalHeadline(raw: string): ConventionalHeadline;
|
|
11
12
|
declare function formatHeadline(raw: string): {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
text: string;
|
|
14
|
+
breaking: boolean;
|
|
14
15
|
};
|
|
15
16
|
declare const changelogFunctions: ChangelogFunctions;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
// @ts-ignore
|
|
21
|
-
export = changelogFunctions;
|
|
22
|
-
export { formatHeadline, getDependencyReleaseLine, getReleaseLine, parseConventionalHeadline };
|
|
17
|
+
declare const getDependencyReleaseLine: _changesets_types0.GetDependencyReleaseLine, getReleaseLine: _changesets_types0.GetReleaseLine;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { changelogFunctions as default, formatHeadline, getDependencyReleaseLine, getReleaseLine, parseConventionalHeadline };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,20 +1,19 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
import { ChangelogFunctions } from
|
|
1
|
+
import * as _changesets_types0 from "@changesets/types";
|
|
2
|
+
import { ChangelogFunctions } from "@changesets/types";
|
|
3
3
|
|
|
4
|
+
//#region src/index.d.ts
|
|
4
5
|
interface ConventionalHeadline {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
type?: string | undefined;
|
|
7
|
+
scope?: string | undefined;
|
|
8
|
+
breaking: boolean;
|
|
9
|
+
description: string;
|
|
9
10
|
}
|
|
10
11
|
declare function parseConventionalHeadline(raw: string): ConventionalHeadline;
|
|
11
12
|
declare function formatHeadline(raw: string): {
|
|
12
|
-
|
|
13
|
-
|
|
13
|
+
text: string;
|
|
14
|
+
breaking: boolean;
|
|
14
15
|
};
|
|
15
16
|
declare const changelogFunctions: ChangelogFunctions;
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
export { changelogFunctions as default, formatHeadline, getDependencyReleaseLine, getReleaseLine, parseConventionalHeadline };
|
|
17
|
+
declare const getDependencyReleaseLine: _changesets_types0.GetDependencyReleaseLine, getReleaseLine: _changesets_types0.GetReleaseLine;
|
|
18
|
+
//#endregion
|
|
19
|
+
export { changelogFunctions as default, formatHeadline, getDependencyReleaseLine, getReleaseLine, parseConventionalHeadline };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";
|
|
2
|
+
import { config } from "dotenv";
|
|
3
|
+
//#region src/index.ts
|
|
4
|
+
config();
|
|
5
|
+
const releaseTypeMap = {
|
|
6
|
+
major: {
|
|
7
|
+
icon: "🚀",
|
|
8
|
+
label: "Major"
|
|
9
|
+
},
|
|
10
|
+
minor: {
|
|
11
|
+
icon: "✨",
|
|
12
|
+
label: "Minor"
|
|
13
|
+
},
|
|
14
|
+
patch: {
|
|
15
|
+
icon: "🐛",
|
|
16
|
+
label: "Patch"
|
|
17
|
+
},
|
|
18
|
+
none: {
|
|
19
|
+
icon: "📝",
|
|
20
|
+
label: "Update"
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
const CONVENTIONAL_HEADLINE_RE = /^(\w+)(?:\(([^)]*)\))?(!?):\s(.+)$/;
|
|
24
|
+
const NEWLINE_RE = /\r?\n/;
|
|
25
|
+
const LIST_MARKER_RE = /^[-*+]\s+/;
|
|
26
|
+
const PR_META_RE = /^(?:pr|pull|pull\s*request):\s*#?(\d+)\s*$/i;
|
|
27
|
+
const COMMIT_META_RE = /^commit:\s*(\S+)\s*$/i;
|
|
28
|
+
const USER_META_RE = /^(?:author|user):\s*(\S+)\s*$/i;
|
|
29
|
+
const LEADING_AT_RE = /^@/;
|
|
30
|
+
const BREAKING_CHANGE_RE = /^BREAKING[\s-]CHANGE:/i;
|
|
31
|
+
const LIST_LINE_RE = /^(?:[-*+]\s+|\d+[.)]\s+)/;
|
|
32
|
+
const FALLBACK_USER_RE = /@([^\]]+)/;
|
|
33
|
+
const CODE_FENCE_RE = /^\s*```/;
|
|
34
|
+
const LIST_LIKE_RE = /^\s*(?:[-*+]\s+|\d+[.)]\s+)/;
|
|
35
|
+
const PARAGRAPH_BREAK_RE = /\n\s*\n/;
|
|
36
|
+
function resolveReleaseType(type) {
|
|
37
|
+
if (type && type in releaseTypeMap) return type;
|
|
38
|
+
return "none";
|
|
39
|
+
}
|
|
40
|
+
function assertRepo(options) {
|
|
41
|
+
if (!options || typeof options["repo"] !== "string" || options["repo"].trim().length === 0) throw new Error("Please provide a repo to this changelog generator like this:\n\"changelog\": [\"@icebreakers/changelog-github\", { \"repo\": \"org/repo\" }]");
|
|
42
|
+
}
|
|
43
|
+
async function collectDependencyReferences(changesets, repo) {
|
|
44
|
+
const references = await Promise.all(changesets.map(async (cs) => {
|
|
45
|
+
if (!cs.commit) return null;
|
|
46
|
+
const { links } = await getInfo({
|
|
47
|
+
repo,
|
|
48
|
+
commit: cs.commit
|
|
49
|
+
});
|
|
50
|
+
return links.commit;
|
|
51
|
+
}));
|
|
52
|
+
return [...new Set(references.filter((link) => Boolean(link)))];
|
|
53
|
+
}
|
|
54
|
+
function formatDependencyLine(references, dependenciesUpdated) {
|
|
55
|
+
const count = dependenciesUpdated.length;
|
|
56
|
+
const firstCommit = references[0] || "";
|
|
57
|
+
if (count === 0) return firstCommit ? `- 📦 **Dependencies** ${firstCommit}` : "- 📦 **Dependencies**";
|
|
58
|
+
const dependencySummaries = dependenciesUpdated.map((dependency) => `\`${dependency.name}@${dependency.newVersion}\``);
|
|
59
|
+
if (count <= 3) return `${firstCommit ? `- 📦 **Dependencies** ${firstCommit}` : "- 📦 **Dependencies**"}\n${` → ${dependencySummaries.join(", ")}`}`;
|
|
60
|
+
return `${firstCommit ? `- 📦 Updated ${count} dependencies ${firstCommit}` : `- 📦 Updated ${count} dependencies`}\n${[
|
|
61
|
+
" <details><summary>Details</summary>",
|
|
62
|
+
"",
|
|
63
|
+
` ${dependencySummaries.join(", ")}`,
|
|
64
|
+
"",
|
|
65
|
+
" </details>"
|
|
66
|
+
].join("\n")}`;
|
|
67
|
+
}
|
|
68
|
+
function parseConventionalHeadline(raw) {
|
|
69
|
+
const match = raw.match(CONVENTIONAL_HEADLINE_RE);
|
|
70
|
+
if (!match) return {
|
|
71
|
+
breaking: false,
|
|
72
|
+
description: raw
|
|
73
|
+
};
|
|
74
|
+
const type = match[1];
|
|
75
|
+
const scope = match[2] || void 0;
|
|
76
|
+
const description = (match[4] ?? raw).trim();
|
|
77
|
+
return {
|
|
78
|
+
type,
|
|
79
|
+
scope,
|
|
80
|
+
breaking: match[3] === "!",
|
|
81
|
+
description
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function formatHeadline(raw) {
|
|
85
|
+
const parsed = parseConventionalHeadline(raw);
|
|
86
|
+
if (!parsed.type) return {
|
|
87
|
+
text: raw,
|
|
88
|
+
breaking: parsed.breaking
|
|
89
|
+
};
|
|
90
|
+
return {
|
|
91
|
+
text: `${parsed.scope ? `${parsed.scope}: ` : ""}${parsed.description}`,
|
|
92
|
+
breaking: parsed.breaking
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
function normalizeUserMentions(users) {
|
|
96
|
+
const normalized = users.map((user) => user.trim()).filter(Boolean).map((user) => user.startsWith("@") ? user : `@${user}`);
|
|
97
|
+
if (normalized.length === 0) return "";
|
|
98
|
+
if (normalized.length === 1) return normalized[0] ?? "";
|
|
99
|
+
if (normalized.length === 2) return `${normalized[0] ?? ""} and ${normalized[1] ?? ""}`;
|
|
100
|
+
return `${normalized.slice(0, -1).join(", ")}, and ${normalized.at(-1) ?? ""}`;
|
|
101
|
+
}
|
|
102
|
+
function extractSummaryMeta(summary) {
|
|
103
|
+
let prNumber;
|
|
104
|
+
let commitRef;
|
|
105
|
+
let breaking = false;
|
|
106
|
+
const users = [];
|
|
107
|
+
const contentLines = summary.split(NEWLINE_RE).filter((line) => {
|
|
108
|
+
const withoutListMarker = line.trim().replace(LIST_MARKER_RE, "");
|
|
109
|
+
const prMatch = withoutListMarker.match(PR_META_RE);
|
|
110
|
+
if (prMatch) {
|
|
111
|
+
const num = Number(prMatch[1] ?? NaN);
|
|
112
|
+
if (!Number.isNaN(num)) prNumber = num;
|
|
113
|
+
return false;
|
|
114
|
+
}
|
|
115
|
+
const commitMatch = withoutListMarker.match(COMMIT_META_RE);
|
|
116
|
+
if (commitMatch) {
|
|
117
|
+
commitRef = commitMatch[1];
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
const userMatch = withoutListMarker.match(USER_META_RE);
|
|
121
|
+
if (userMatch) {
|
|
122
|
+
const user = userMatch[1];
|
|
123
|
+
if (user) users.push(user.replace(LEADING_AT_RE, ""));
|
|
124
|
+
return false;
|
|
125
|
+
}
|
|
126
|
+
if (BREAKING_CHANGE_RE.test(withoutListMarker)) breaking = true;
|
|
127
|
+
return true;
|
|
128
|
+
}).map((line) => line.trimEnd());
|
|
129
|
+
return {
|
|
130
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
131
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
132
|
+
users,
|
|
133
|
+
contentLines,
|
|
134
|
+
breaking
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
function parseSummary(summary) {
|
|
138
|
+
const { prNumber, commitRef, users, contentLines, breaking: bodyBreaking } = extractSummaryMeta(summary);
|
|
139
|
+
const nonEmptyIndex = contentLines.findIndex((line) => line.trim().length > 0);
|
|
140
|
+
if (nonEmptyIndex === -1) return {
|
|
141
|
+
headline: "",
|
|
142
|
+
detailLines: [],
|
|
143
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
144
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
145
|
+
users,
|
|
146
|
+
breaking: bodyBreaking
|
|
147
|
+
};
|
|
148
|
+
const firstContentLine = contentLines[nonEmptyIndex]?.trim() ?? "";
|
|
149
|
+
const startsWithList = LIST_LINE_RE.test(firstContentLine);
|
|
150
|
+
const headline = startsWithList ? "" : firstContentLine;
|
|
151
|
+
const detailLines = startsWithList ? contentLines.slice(nonEmptyIndex) : contentLines.slice(nonEmptyIndex + 1);
|
|
152
|
+
const headlineParsed = headline ? parseConventionalHeadline(headline) : null;
|
|
153
|
+
const breaking = bodyBreaking || (headlineParsed?.breaking ?? false);
|
|
154
|
+
return {
|
|
155
|
+
headline,
|
|
156
|
+
detailLines,
|
|
157
|
+
...prNumber !== void 0 ? { prNumber } : {},
|
|
158
|
+
...commitRef !== void 0 ? { commitRef } : {},
|
|
159
|
+
users,
|
|
160
|
+
breaking
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
async function resolveLinks(repo, parsed, changesetCommit) {
|
|
164
|
+
if (parsed.prNumber !== void 0) {
|
|
165
|
+
const { links } = await getInfoFromPullRequest({
|
|
166
|
+
repo,
|
|
167
|
+
pull: parsed.prNumber
|
|
168
|
+
});
|
|
169
|
+
if (parsed.commitRef) links.commit = `[\`${parsed.commitRef.slice(0, 7)}\`](https://github.com/${repo}/commit/${parsed.commitRef})`;
|
|
170
|
+
return links;
|
|
171
|
+
}
|
|
172
|
+
const commitToUse = parsed.commitRef ?? changesetCommit;
|
|
173
|
+
if (commitToUse) {
|
|
174
|
+
const { links } = await getInfo({
|
|
175
|
+
repo,
|
|
176
|
+
commit: commitToUse
|
|
177
|
+
});
|
|
178
|
+
return links;
|
|
179
|
+
}
|
|
180
|
+
return {
|
|
181
|
+
commit: null,
|
|
182
|
+
pull: null,
|
|
183
|
+
user: null
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function buildUserMentions(users, fallbackUser) {
|
|
187
|
+
if (users.length > 0) return normalizeUserMentions(users);
|
|
188
|
+
if (fallbackUser) {
|
|
189
|
+
const matchedUser = fallbackUser.match(FALLBACK_USER_RE)?.[1];
|
|
190
|
+
return matchedUser ? normalizeUserMentions([matchedUser]) : "";
|
|
191
|
+
}
|
|
192
|
+
return "";
|
|
193
|
+
}
|
|
194
|
+
function indentMarkdownBlock(lines, spaces) {
|
|
195
|
+
const prefix = " ".repeat(spaces);
|
|
196
|
+
return lines.map((line) => {
|
|
197
|
+
if (line.trim().length === 0) return "";
|
|
198
|
+
return `${prefix}${line}`;
|
|
199
|
+
}).join("\n");
|
|
200
|
+
}
|
|
201
|
+
function formatDetailBlock(detailLines) {
|
|
202
|
+
const trimmed = detailLines.join("\n").trim().split("\n").map((line) => line.trimEnd());
|
|
203
|
+
const hasFence = trimmed.some((line) => CODE_FENCE_RE.test(line));
|
|
204
|
+
const isListLike = trimmed.some((line) => LIST_LIKE_RE.test(line));
|
|
205
|
+
if (trimmed.length === 0) return "";
|
|
206
|
+
if (hasFence || isListLike) return indentMarkdownBlock(trimmed, 2);
|
|
207
|
+
return trimmed.join("\n").split(PARAGRAPH_BREAK_RE).filter((chunk) => chunk.trim().length > 0).flatMap((chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)).map((text) => ` - ${text}`).join("\n");
|
|
208
|
+
}
|
|
209
|
+
const changelogFunctions = {
|
|
210
|
+
async getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
|
|
211
|
+
assertRepo(options);
|
|
212
|
+
const repo = options.repo;
|
|
213
|
+
if (dependenciesUpdated.length === 0) return "";
|
|
214
|
+
return formatDependencyLine(await collectDependencyReferences(changesets, repo), dependenciesUpdated);
|
|
215
|
+
},
|
|
216
|
+
async getReleaseLine(changeset, type, options) {
|
|
217
|
+
assertRepo(options);
|
|
218
|
+
const repo = options.repo;
|
|
219
|
+
const parsedSummary = parseSummary(changeset.summary);
|
|
220
|
+
const resolvedType = resolveReleaseType(type);
|
|
221
|
+
const links = await resolveLinks(repo, parsedSummary, changeset.commit);
|
|
222
|
+
const userMentions = buildUserMentions(parsedSummary.users, links.user);
|
|
223
|
+
const { text: headlineText, breaking: headlineBreaking } = parsedSummary.headline ? formatHeadline(parsedSummary.headline) : {
|
|
224
|
+
text: "Miscellaneous improvements",
|
|
225
|
+
breaking: false
|
|
226
|
+
};
|
|
227
|
+
const isBreaking = parsedSummary.breaking || headlineBreaking;
|
|
228
|
+
const breakingPrefix = isBreaking ? "⚠️ " : "";
|
|
229
|
+
const mainLineParts = [`- ${isBreaking ? "💥" : releaseTypeMap[resolvedType].icon}`, `${breakingPrefix}**${headlineText}**`];
|
|
230
|
+
if (links.pull) mainLineParts.push(links.pull);
|
|
231
|
+
else if (links.commit) mainLineParts.push(links.commit);
|
|
232
|
+
if (links.pull && parsedSummary.commitRef && links.commit) mainLineParts.push(links.commit);
|
|
233
|
+
if (userMentions) mainLineParts.push(`by ${userMentions}`);
|
|
234
|
+
const mainLine = mainLineParts.join(" ");
|
|
235
|
+
const detailBlock = formatDetailBlock(parsedSummary.detailLines);
|
|
236
|
+
return detailBlock.length > 0 ? `\n\n${mainLine}\n${detailBlock}` : `\n\n${mainLine}`;
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
const { getDependencyReleaseLine, getReleaseLine } = changelogFunctions;
|
|
240
|
+
//#endregion
|
|
241
|
+
export { changelogFunctions as default, formatHeadline, getDependencyReleaseLine, getReleaseLine, parseConventionalHeadline };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@icebreakers/changelog-github",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.2.
|
|
4
|
+
"version": "0.2.2",
|
|
5
5
|
"description": "Changesets GitHub changelog formatter for Icebreaker's dev-configs",
|
|
6
6
|
"author": "ice breaker <1324318532@qq.com>",
|
|
7
7
|
"license": "MIT",
|
|
@@ -23,32 +23,39 @@
|
|
|
23
23
|
"exports": {
|
|
24
24
|
".": {
|
|
25
25
|
"types": "./dist/index.d.ts",
|
|
26
|
-
"import": "./dist/index.
|
|
26
|
+
"import": "./dist/index.js",
|
|
27
27
|
"require": "./dist/index.cjs"
|
|
28
28
|
}
|
|
29
29
|
},
|
|
30
30
|
"main": "./dist/index.cjs",
|
|
31
|
-
"module": "./dist/index.
|
|
31
|
+
"module": "./dist/index.js",
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
33
|
"files": [
|
|
34
34
|
"dist"
|
|
35
35
|
],
|
|
36
|
+
"tsd": {
|
|
37
|
+
"directory": "test-d",
|
|
38
|
+
"compilerOptions": {
|
|
39
|
+
"skipLibCheck": true
|
|
40
|
+
}
|
|
41
|
+
},
|
|
36
42
|
"dependencies": {
|
|
37
|
-
"@changesets/get-github-info": "^0.
|
|
43
|
+
"@changesets/get-github-info": "^0.8.0",
|
|
38
44
|
"@changesets/types": "^6.1.0",
|
|
39
|
-
"dotenv": "^17.
|
|
45
|
+
"dotenv": "^17.3.1"
|
|
40
46
|
},
|
|
41
47
|
"devDependencies": {
|
|
42
|
-
"@changesets/parse": "^0.4.
|
|
48
|
+
"@changesets/parse": "^0.4.3"
|
|
43
49
|
},
|
|
44
50
|
"scripts": {
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
"build": "tsup",
|
|
51
|
+
"dev": "tsdown --watch --sourcemap",
|
|
52
|
+
"build": "tsdown",
|
|
48
53
|
"test": "vitest run",
|
|
54
|
+
"test:types": "tsd",
|
|
49
55
|
"test:dev": "vitest",
|
|
50
56
|
"release": "pnpm publish",
|
|
51
57
|
"lint": "eslint .",
|
|
52
|
-
"lint:fix": "eslint . --fix"
|
|
58
|
+
"lint:fix": "eslint . --fix",
|
|
59
|
+
"typecheck": "tsc --noEmit"
|
|
53
60
|
}
|
|
54
61
|
}
|
package/dist/index.mjs
DELETED
|
@@ -1,304 +0,0 @@
|
|
|
1
|
-
// src/index.ts
|
|
2
|
-
import { getInfo, getInfoFromPullRequest } from "@changesets/get-github-info";
|
|
3
|
-
import { config as loadEnv } from "dotenv";
|
|
4
|
-
loadEnv();
|
|
5
|
-
var releaseTypeMap = {
|
|
6
|
-
major: { icon: "\u{1F680}", label: "Major" },
|
|
7
|
-
minor: { icon: "\u2728", label: "Minor" },
|
|
8
|
-
patch: { icon: "\u{1F41B}", label: "Patch" },
|
|
9
|
-
none: { icon: "\u{1F4DD}", label: "Update" }
|
|
10
|
-
};
|
|
11
|
-
function resolveReleaseType(type) {
|
|
12
|
-
if (type && type in releaseTypeMap) {
|
|
13
|
-
return type;
|
|
14
|
-
}
|
|
15
|
-
return "none";
|
|
16
|
-
}
|
|
17
|
-
function assertRepo(options) {
|
|
18
|
-
if (!options || !options.repo) {
|
|
19
|
-
throw new Error(
|
|
20
|
-
'Please provide a repo to this changelog generator like this:\n"changelog": ["@icebreakers/changelog-github", { "repo": "org/repo" }]'
|
|
21
|
-
);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
async function collectDependencyReferences(changesets, repo) {
|
|
25
|
-
const references = await Promise.all(
|
|
26
|
-
changesets.map(async (cs) => {
|
|
27
|
-
if (!cs.commit) {
|
|
28
|
-
return null;
|
|
29
|
-
}
|
|
30
|
-
const { links } = await getInfo({
|
|
31
|
-
repo,
|
|
32
|
-
commit: cs.commit
|
|
33
|
-
});
|
|
34
|
-
return links.commit;
|
|
35
|
-
})
|
|
36
|
-
);
|
|
37
|
-
return [...new Set(references.filter((link) => Boolean(link)))];
|
|
38
|
-
}
|
|
39
|
-
function formatDependencyLine(references, dependenciesUpdated) {
|
|
40
|
-
const count = dependenciesUpdated.length;
|
|
41
|
-
const firstCommit = references[0] || "";
|
|
42
|
-
if (count === 0) {
|
|
43
|
-
return firstCommit ? `- \u{1F4E6} **Dependencies** ${firstCommit}` : "- \u{1F4E6} **Dependencies**";
|
|
44
|
-
}
|
|
45
|
-
const dependencySummaries = dependenciesUpdated.map(
|
|
46
|
-
(dependency) => `\`${dependency.name}@${dependency.newVersion}\``
|
|
47
|
-
);
|
|
48
|
-
if (count <= 3) {
|
|
49
|
-
const header2 = firstCommit ? `- \u{1F4E6} **Dependencies** ${firstCommit}` : "- \u{1F4E6} **Dependencies**";
|
|
50
|
-
const details = ` \u2192 ${dependencySummaries.join(", ")}`;
|
|
51
|
-
return `${header2}
|
|
52
|
-
${details}`;
|
|
53
|
-
}
|
|
54
|
-
const header = firstCommit ? `- \u{1F4E6} Updated ${count} dependencies ${firstCommit}` : `- \u{1F4E6} Updated ${count} dependencies`;
|
|
55
|
-
const detailBlock = [
|
|
56
|
-
" <details><summary>Details</summary>",
|
|
57
|
-
"",
|
|
58
|
-
` ${dependencySummaries.join(", ")}`,
|
|
59
|
-
"",
|
|
60
|
-
" </details>"
|
|
61
|
-
].join("\n");
|
|
62
|
-
return `${header}
|
|
63
|
-
${detailBlock}`;
|
|
64
|
-
}
|
|
65
|
-
function parseConventionalHeadline(raw) {
|
|
66
|
-
const match = raw.match(
|
|
67
|
-
/^(\w+)(?:\(([^)]*)\))?(!?):\s(.+)$/
|
|
68
|
-
);
|
|
69
|
-
if (!match) {
|
|
70
|
-
return { breaking: false, description: raw };
|
|
71
|
-
}
|
|
72
|
-
return {
|
|
73
|
-
type: match[1],
|
|
74
|
-
scope: match[2] || void 0,
|
|
75
|
-
breaking: match[3] === "!",
|
|
76
|
-
description: match[4].trim()
|
|
77
|
-
};
|
|
78
|
-
}
|
|
79
|
-
function formatHeadline(raw) {
|
|
80
|
-
const parsed = parseConventionalHeadline(raw);
|
|
81
|
-
if (!parsed.type) {
|
|
82
|
-
return { text: raw, breaking: parsed.breaking };
|
|
83
|
-
}
|
|
84
|
-
const scopeTag = parsed.scope ? `${parsed.scope}: ` : "";
|
|
85
|
-
return {
|
|
86
|
-
text: `${scopeTag}${parsed.description}`,
|
|
87
|
-
breaking: parsed.breaking
|
|
88
|
-
};
|
|
89
|
-
}
|
|
90
|
-
function normalizeUserMentions(users) {
|
|
91
|
-
const normalized = users.map((user) => user.trim()).filter(Boolean).map((user) => user.startsWith("@") ? user : `@${user}`);
|
|
92
|
-
if (normalized.length === 0) {
|
|
93
|
-
return "";
|
|
94
|
-
}
|
|
95
|
-
if (normalized.length === 1) {
|
|
96
|
-
return normalized[0];
|
|
97
|
-
}
|
|
98
|
-
if (normalized.length === 2) {
|
|
99
|
-
return `${normalized[0]} and ${normalized[1]}`;
|
|
100
|
-
}
|
|
101
|
-
return `${normalized.slice(0, -1).join(", ")}, and ${normalized.at(-1)}`;
|
|
102
|
-
}
|
|
103
|
-
function extractSummaryMeta(summary) {
|
|
104
|
-
let prNumber;
|
|
105
|
-
let commitRef;
|
|
106
|
-
let breaking = false;
|
|
107
|
-
const users = [];
|
|
108
|
-
const contentLines = summary.split(/\r?\n/).filter((line) => {
|
|
109
|
-
const trimmed = line.trim();
|
|
110
|
-
const withoutListMarker = trimmed.replace(/^[-*+]\s+/, "");
|
|
111
|
-
const prMatch = withoutListMarker.match(
|
|
112
|
-
/^(?:pr|pull|pull\s*request):\s*#?(\d+)\s*$/i
|
|
113
|
-
);
|
|
114
|
-
if (prMatch) {
|
|
115
|
-
const num = Number(prMatch[1]);
|
|
116
|
-
if (!Number.isNaN(num)) {
|
|
117
|
-
prNumber = num;
|
|
118
|
-
}
|
|
119
|
-
return false;
|
|
120
|
-
}
|
|
121
|
-
const commitMatch = withoutListMarker.match(/^commit:\s*(\S+)\s*$/i);
|
|
122
|
-
if (commitMatch) {
|
|
123
|
-
commitRef = commitMatch[1];
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
const userMatch = withoutListMarker.match(/^(?:author|user):\s*(\S+)\s*$/i);
|
|
127
|
-
if (userMatch) {
|
|
128
|
-
users.push(userMatch[1].replace(/^@/, ""));
|
|
129
|
-
return false;
|
|
130
|
-
}
|
|
131
|
-
if (/^BREAKING[\s-]CHANGE:/i.test(withoutListMarker)) {
|
|
132
|
-
breaking = true;
|
|
133
|
-
}
|
|
134
|
-
return true;
|
|
135
|
-
}).map((line) => line.trimEnd());
|
|
136
|
-
return {
|
|
137
|
-
prNumber,
|
|
138
|
-
commitRef,
|
|
139
|
-
users,
|
|
140
|
-
contentLines,
|
|
141
|
-
breaking
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
function parseSummary(summary) {
|
|
145
|
-
const {
|
|
146
|
-
prNumber,
|
|
147
|
-
commitRef,
|
|
148
|
-
users,
|
|
149
|
-
contentLines,
|
|
150
|
-
breaking: bodyBreaking
|
|
151
|
-
} = extractSummaryMeta(summary);
|
|
152
|
-
const nonEmptyIndex = contentLines.findIndex((line) => line.trim().length > 0);
|
|
153
|
-
if (nonEmptyIndex === -1) {
|
|
154
|
-
return {
|
|
155
|
-
headline: "",
|
|
156
|
-
detailLines: [],
|
|
157
|
-
prNumber,
|
|
158
|
-
commitRef,
|
|
159
|
-
users,
|
|
160
|
-
breaking: bodyBreaking
|
|
161
|
-
};
|
|
162
|
-
}
|
|
163
|
-
const firstContentLine = contentLines[nonEmptyIndex].trim();
|
|
164
|
-
const startsWithList = /^(?:[-*+]\s+|\d+[.)]\s+)/.test(firstContentLine);
|
|
165
|
-
const headline = startsWithList ? "" : firstContentLine;
|
|
166
|
-
const restLines = startsWithList ? contentLines.slice(nonEmptyIndex) : contentLines.slice(nonEmptyIndex + 1);
|
|
167
|
-
const detailLines = restLines;
|
|
168
|
-
const headlineParsed = headline ? parseConventionalHeadline(headline) : null;
|
|
169
|
-
const breaking = bodyBreaking || (headlineParsed?.breaking ?? false);
|
|
170
|
-
return {
|
|
171
|
-
headline,
|
|
172
|
-
detailLines,
|
|
173
|
-
prNumber,
|
|
174
|
-
commitRef,
|
|
175
|
-
users,
|
|
176
|
-
breaking
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
async function resolveLinks(repo, parsed, changesetCommit) {
|
|
180
|
-
if (parsed.prNumber !== void 0) {
|
|
181
|
-
const { links } = await getInfoFromPullRequest({
|
|
182
|
-
repo,
|
|
183
|
-
pull: parsed.prNumber
|
|
184
|
-
});
|
|
185
|
-
if (parsed.commitRef) {
|
|
186
|
-
const shortCommitId = parsed.commitRef.slice(0, 7);
|
|
187
|
-
links.commit = `[\`${shortCommitId}\`](https://github.com/${repo}/commit/${parsed.commitRef})`;
|
|
188
|
-
}
|
|
189
|
-
return links;
|
|
190
|
-
}
|
|
191
|
-
const commitToUse = parsed.commitRef ?? changesetCommit;
|
|
192
|
-
if (commitToUse) {
|
|
193
|
-
const { links } = await getInfo({
|
|
194
|
-
repo,
|
|
195
|
-
commit: commitToUse
|
|
196
|
-
});
|
|
197
|
-
return links;
|
|
198
|
-
}
|
|
199
|
-
return {
|
|
200
|
-
commit: null,
|
|
201
|
-
pull: null,
|
|
202
|
-
user: null
|
|
203
|
-
};
|
|
204
|
-
}
|
|
205
|
-
function buildUserMentions(users, fallbackUser) {
|
|
206
|
-
if (users.length > 0) {
|
|
207
|
-
return normalizeUserMentions(users);
|
|
208
|
-
}
|
|
209
|
-
if (fallbackUser) {
|
|
210
|
-
const match = fallbackUser.match(/@([^\]]+)/);
|
|
211
|
-
return match ? normalizeUserMentions([match[1]]) : "";
|
|
212
|
-
}
|
|
213
|
-
return "";
|
|
214
|
-
}
|
|
215
|
-
function indentMarkdownBlock(lines, spaces) {
|
|
216
|
-
const prefix = " ".repeat(spaces);
|
|
217
|
-
return lines.map((line) => {
|
|
218
|
-
if (line.trim().length === 0) {
|
|
219
|
-
return "";
|
|
220
|
-
}
|
|
221
|
-
return `${prefix}${line}`;
|
|
222
|
-
}).join("\n");
|
|
223
|
-
}
|
|
224
|
-
function formatDetailBlock(detailLines) {
|
|
225
|
-
const trimmed = detailLines.join("\n").trim().split("\n").map((line) => line.trimEnd());
|
|
226
|
-
const hasFence = trimmed.some((line) => /^\s*```/.test(line));
|
|
227
|
-
const isListLike = trimmed.some((line) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(line));
|
|
228
|
-
if (trimmed.length === 0) {
|
|
229
|
-
return "";
|
|
230
|
-
}
|
|
231
|
-
if (hasFence || isListLike) {
|
|
232
|
-
return indentMarkdownBlock(trimmed, 2);
|
|
233
|
-
}
|
|
234
|
-
const paragraphs = trimmed.join("\n").split(/\n\s*\n/).filter((chunk) => chunk.trim().length > 0);
|
|
235
|
-
const lines = paragraphs.flatMap(
|
|
236
|
-
(chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)
|
|
237
|
-
);
|
|
238
|
-
return lines.map((text) => ` - ${text}`).join("\n");
|
|
239
|
-
}
|
|
240
|
-
var changelogFunctions = {
|
|
241
|
-
async getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
|
|
242
|
-
assertRepo(options);
|
|
243
|
-
const { repo } = options;
|
|
244
|
-
if (dependenciesUpdated.length === 0) {
|
|
245
|
-
return "";
|
|
246
|
-
}
|
|
247
|
-
const references = await collectDependencyReferences(
|
|
248
|
-
changesets,
|
|
249
|
-
repo
|
|
250
|
-
);
|
|
251
|
-
return formatDependencyLine(references, dependenciesUpdated);
|
|
252
|
-
},
|
|
253
|
-
async getReleaseLine(changeset, type, options) {
|
|
254
|
-
assertRepo(options);
|
|
255
|
-
const { repo } = options;
|
|
256
|
-
const parsedSummary = parseSummary(changeset.summary);
|
|
257
|
-
const resolvedType = resolveReleaseType(type);
|
|
258
|
-
const links = await resolveLinks(
|
|
259
|
-
repo,
|
|
260
|
-
parsedSummary,
|
|
261
|
-
changeset.commit
|
|
262
|
-
);
|
|
263
|
-
const userMentions = buildUserMentions(
|
|
264
|
-
parsedSummary.users,
|
|
265
|
-
links.user
|
|
266
|
-
);
|
|
267
|
-
const { text: headlineText, breaking: headlineBreaking } = parsedSummary.headline ? formatHeadline(parsedSummary.headline) : { text: "Miscellaneous improvements", breaking: false };
|
|
268
|
-
const isBreaking = parsedSummary.breaking || headlineBreaking;
|
|
269
|
-
const breakingPrefix = isBreaking ? "\u26A0\uFE0F " : "";
|
|
270
|
-
const icon = isBreaking ? "\u{1F4A5}" : releaseTypeMap[resolvedType].icon;
|
|
271
|
-
const mainLineParts = [
|
|
272
|
-
`- ${icon}`,
|
|
273
|
-
`${breakingPrefix}**${headlineText}**`
|
|
274
|
-
];
|
|
275
|
-
if (links.pull) {
|
|
276
|
-
mainLineParts.push(links.pull);
|
|
277
|
-
} else if (links.commit) {
|
|
278
|
-
mainLineParts.push(links.commit);
|
|
279
|
-
}
|
|
280
|
-
if (links.pull && parsedSummary.commitRef && links.commit) {
|
|
281
|
-
mainLineParts.push(links.commit);
|
|
282
|
-
}
|
|
283
|
-
if (userMentions) {
|
|
284
|
-
mainLineParts.push(`by ${userMentions}`);
|
|
285
|
-
}
|
|
286
|
-
const mainLine = mainLineParts.join(" ");
|
|
287
|
-
const detailBlock = formatDetailBlock(parsedSummary.detailLines);
|
|
288
|
-
return detailBlock.length > 0 ? `
|
|
289
|
-
|
|
290
|
-
${mainLine}
|
|
291
|
-
${detailBlock}` : `
|
|
292
|
-
|
|
293
|
-
${mainLine}`;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
var index_default = changelogFunctions;
|
|
297
|
-
var { getDependencyReleaseLine, getReleaseLine } = changelogFunctions;
|
|
298
|
-
export {
|
|
299
|
-
index_default as default,
|
|
300
|
-
formatHeadline,
|
|
301
|
-
getDependencyReleaseLine,
|
|
302
|
-
getReleaseLine,
|
|
303
|
-
parseConventionalHeadline
|
|
304
|
-
};
|