@icebreakers/changelog-github 0.2.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1,311 +1,248 @@
1
- "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/index.ts
2
- var _getgithubinfo = require('@changesets/get-github-info');
3
- var _dotenv = require('dotenv');
4
- _dotenv.config.call(void 0, );
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" }
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
- if (type && type in releaseTypeMap) {
13
- return type;
14
- }
15
- return "none";
40
+ if (type && type in releaseTypeMap) return type;
41
+ return "none";
16
42
  }
17
43
  function assertRepo(options) {
18
- if (!options || typeof options["repo"] !== "string" || options["repo"].trim().length === 0) {
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
- const references = await Promise.all(
26
- changesets.map(async (cs) => {
27
- if (!cs.commit) {
28
- return null;
29
- }
30
- const { links } = await _getgithubinfo.getInfo.call(void 0, {
31
- repo,
32
- commit: cs.commit
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
- 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}`;
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
- const match = raw.match(
67
- /^(\w+)(?:\(([^)]*)\))?(!?):\s(.+)$/
68
- );
69
- if (!match) {
70
- return { breaking: false, description: raw };
71
- }
72
- const type = match[1];
73
- const scope = match[2] || void 0;
74
- const description = (_nullishCoalesce(match[4], () => ( raw))).trim();
75
- return {
76
- type,
77
- scope,
78
- breaking: match[3] === "!",
79
- description
80
- };
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
+ };
81
86
  }
82
87
  function formatHeadline(raw) {
83
- const parsed = parseConventionalHeadline(raw);
84
- if (!parsed.type) {
85
- return { text: raw, breaking: parsed.breaking };
86
- }
87
- const scopeTag = parsed.scope ? `${parsed.scope}: ` : "";
88
- return {
89
- text: `${scopeTag}${parsed.description}`,
90
- breaking: parsed.breaking
91
- };
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
+ };
92
97
  }
93
98
  function normalizeUserMentions(users) {
94
- const normalized = users.map((user) => user.trim()).filter(Boolean).map((user) => user.startsWith("@") ? user : `@${user}`);
95
- if (normalized.length === 0) {
96
- return "";
97
- }
98
- if (normalized.length === 1) {
99
- return _nullishCoalesce(normalized[0], () => ( ""));
100
- }
101
- if (normalized.length === 2) {
102
- return `${_nullishCoalesce(normalized[0], () => ( ""))} and ${_nullishCoalesce(normalized[1], () => ( ""))}`;
103
- }
104
- return `${normalized.slice(0, -1).join(", ")}, and ${_nullishCoalesce(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) ?? ""}`;
105
104
  }
106
105
  function extractSummaryMeta(summary) {
107
- let prNumber;
108
- let commitRef;
109
- let breaking = false;
110
- const users = [];
111
- const contentLines = summary.split(/\r?\n/).filter((line) => {
112
- const trimmed = line.trim();
113
- const withoutListMarker = trimmed.replace(/^[-*+]\s+/, "");
114
- const prMatch = withoutListMarker.match(
115
- /^(?:pr|pull|pull\s*request):\s*#?(\d+)\s*$/i
116
- );
117
- if (prMatch) {
118
- const num = Number(_nullishCoalesce(prMatch[1], () => ( Number.NaN)));
119
- if (!Number.isNaN(num)) {
120
- prNumber = num;
121
- }
122
- return false;
123
- }
124
- const commitMatch = withoutListMarker.match(/^commit:\s*(\S+)\s*$/i);
125
- if (commitMatch) {
126
- commitRef = commitMatch[1];
127
- return false;
128
- }
129
- const userMatch = withoutListMarker.match(/^(?:author|user):\s*(\S+)\s*$/i);
130
- if (userMatch) {
131
- const user = userMatch[1];
132
- if (user) {
133
- users.push(user.replace(/^@/, ""));
134
- }
135
- return false;
136
- }
137
- if (/^BREAKING[\s-]CHANGE:/i.test(withoutListMarker)) {
138
- breaking = true;
139
- }
140
- return true;
141
- }).map((line) => line.trimEnd());
142
- return {
143
- ...prNumber !== void 0 ? { prNumber } : {},
144
- ...commitRef !== void 0 ? { commitRef } : {},
145
- users,
146
- contentLines,
147
- breaking
148
- };
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
+ };
149
139
  }
150
140
  function parseSummary(summary) {
151
- const {
152
- prNumber,
153
- commitRef,
154
- users,
155
- contentLines,
156
- breaking: bodyBreaking
157
- } = extractSummaryMeta(summary);
158
- const nonEmptyIndex = contentLines.findIndex((line) => line.trim().length > 0);
159
- if (nonEmptyIndex === -1) {
160
- return {
161
- headline: "",
162
- detailLines: [],
163
- ...prNumber !== void 0 ? { prNumber } : {},
164
- ...commitRef !== void 0 ? { commitRef } : {},
165
- users,
166
- breaking: bodyBreaking
167
- };
168
- }
169
- const firstContentLine = _nullishCoalesce(_optionalChain([contentLines, 'access', _ => _[nonEmptyIndex], 'optionalAccess', _2 => _2.trim, 'call', _3 => _3()]), () => ( ""));
170
- const startsWithList = /^(?:[-*+]\s+|\d+[.)]\s+)/.test(firstContentLine);
171
- const headline = startsWithList ? "" : firstContentLine;
172
- const restLines = startsWithList ? contentLines.slice(nonEmptyIndex) : contentLines.slice(nonEmptyIndex + 1);
173
- const detailLines = restLines;
174
- const headlineParsed = headline ? parseConventionalHeadline(headline) : null;
175
- const breaking = bodyBreaking || (_nullishCoalesce(_optionalChain([headlineParsed, 'optionalAccess', _4 => _4.breaking]), () => ( false)));
176
- return {
177
- headline,
178
- detailLines,
179
- ...prNumber !== void 0 ? { prNumber } : {},
180
- ...commitRef !== void 0 ? { commitRef } : {},
181
- users,
182
- breaking
183
- };
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
+ };
184
165
  }
185
166
  async function resolveLinks(repo, parsed, changesetCommit) {
186
- if (parsed.prNumber !== void 0) {
187
- const { links } = await _getgithubinfo.getInfoFromPullRequest.call(void 0, {
188
- repo,
189
- pull: parsed.prNumber
190
- });
191
- if (parsed.commitRef) {
192
- const shortCommitId = parsed.commitRef.slice(0, 7);
193
- links.commit = `[\`${shortCommitId}\`](https://github.com/${repo}/commit/${parsed.commitRef})`;
194
- }
195
- return links;
196
- }
197
- const commitToUse = _nullishCoalesce(parsed.commitRef, () => ( changesetCommit));
198
- if (commitToUse) {
199
- const { links } = await _getgithubinfo.getInfo.call(void 0, {
200
- repo,
201
- commit: commitToUse
202
- });
203
- return links;
204
- }
205
- return {
206
- commit: null,
207
- pull: null,
208
- user: null
209
- };
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
+ };
210
188
  }
211
189
  function buildUserMentions(users, fallbackUser) {
212
- if (users.length > 0) {
213
- return normalizeUserMentions(users);
214
- }
215
- if (fallbackUser) {
216
- const match = fallbackUser.match(/@([^\]]+)/);
217
- const matchedUser = _optionalChain([match, 'optionalAccess', _5 => _5[1]]);
218
- return matchedUser ? normalizeUserMentions([matchedUser]) : "";
219
- }
220
- 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 "";
221
196
  }
222
197
  function indentMarkdownBlock(lines, spaces) {
223
- const prefix = " ".repeat(spaces);
224
- return lines.map((line) => {
225
- if (line.trim().length === 0) {
226
- return "";
227
- }
228
- return `${prefix}${line}`;
229
- }).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");
230
203
  }
231
204
  function formatDetailBlock(detailLines) {
232
- const trimmed = detailLines.join("\n").trim().split("\n").map((line) => line.trimEnd());
233
- const hasFence = trimmed.some((line) => /^\s*```/.test(line));
234
- const isListLike = trimmed.some((line) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(line));
235
- if (trimmed.length === 0) {
236
- return "";
237
- }
238
- if (hasFence || isListLike) {
239
- return indentMarkdownBlock(trimmed, 2);
240
- }
241
- const paragraphs = trimmed.join("\n").split(/\n\s*\n/).filter((chunk) => chunk.trim().length > 0);
242
- const lines = paragraphs.flatMap(
243
- (chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)
244
- );
245
- 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");
246
211
  }
247
- var changelogFunctions = {
248
- async getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
249
- assertRepo(options);
250
- const repo = options.repo;
251
- if (dependenciesUpdated.length === 0) {
252
- return "";
253
- }
254
- const references = await collectDependencyReferences(
255
- changesets,
256
- repo
257
- );
258
- return formatDependencyLine(references, dependenciesUpdated);
259
- },
260
- async getReleaseLine(changeset, type, options) {
261
- assertRepo(options);
262
- const repo = options.repo;
263
- const parsedSummary = parseSummary(changeset.summary);
264
- const resolvedType = resolveReleaseType(type);
265
- const links = await resolveLinks(
266
- repo,
267
- parsedSummary,
268
- changeset.commit
269
- );
270
- const userMentions = buildUserMentions(
271
- parsedSummary.users,
272
- links.user
273
- );
274
- const { text: headlineText, breaking: headlineBreaking } = parsedSummary.headline ? formatHeadline(parsedSummary.headline) : { text: "Miscellaneous improvements", breaking: false };
275
- const isBreaking = parsedSummary.breaking || headlineBreaking;
276
- const breakingPrefix = isBreaking ? "\u26A0\uFE0F " : "";
277
- const icon = isBreaking ? "\u{1F4A5}" : releaseTypeMap[resolvedType].icon;
278
- const mainLineParts = [
279
- `- ${icon}`,
280
- `${breakingPrefix}**${headlineText}**`
281
- ];
282
- if (links.pull) {
283
- mainLineParts.push(links.pull);
284
- } else if (links.commit) {
285
- mainLineParts.push(links.commit);
286
- }
287
- if (links.pull && parsedSummary.commitRef && links.commit) {
288
- mainLineParts.push(links.commit);
289
- }
290
- if (userMentions) {
291
- mainLineParts.push(`by ${userMentions}`);
292
- }
293
- const mainLine = mainLineParts.join(" ");
294
- const detailBlock = formatDetailBlock(parsedSummary.detailLines);
295
- return detailBlock.length > 0 ? `
296
-
297
- ${mainLine}
298
- ${detailBlock}` : `
299
-
300
- ${mainLine}`;
301
- }
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
+ }
302
241
  };
303
- var index_default = changelogFunctions;
304
- var { getDependencyReleaseLine, getReleaseLine } = changelogFunctions;
305
-
306
-
307
-
308
-
309
-
310
-
311
- 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 _changesets_types from '@changesets/types';
2
- import { ChangelogFunctions } from '@changesets/types';
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
- type?: string | undefined;
6
- scope?: string | undefined;
7
- breaking: boolean;
8
- description: string;
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
- text: string;
13
- breaking: boolean;
13
+ text: string;
14
+ breaking: boolean;
14
15
  };
15
16
  declare const changelogFunctions: ChangelogFunctions;
16
-
17
- declare const getDependencyReleaseLine: _changesets_types.GetDependencyReleaseLine;
18
- declare const getReleaseLine: _changesets_types.GetReleaseLine;
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 _changesets_types from '@changesets/types';
2
- import { ChangelogFunctions } from '@changesets/types';
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
- type?: string | undefined;
6
- scope?: string | undefined;
7
- breaking: boolean;
8
- description: string;
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
- text: string;
13
- breaking: boolean;
13
+ text: string;
14
+ breaking: boolean;
14
15
  };
15
16
  declare const changelogFunctions: ChangelogFunctions;
16
-
17
- declare const getDependencyReleaseLine: _changesets_types.GetDependencyReleaseLine;
18
- declare const getReleaseLine: _changesets_types.GetReleaseLine;
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.1",
4
+ "version": "1.0.0",
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,29 +23,38 @@
23
23
  "exports": {
24
24
  ".": {
25
25
  "types": "./dist/index.d.ts",
26
- "import": "./dist/index.mjs",
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.mjs",
31
+ "module": "./dist/index.js",
32
32
  "types": "./dist/index.d.ts",
33
33
  "files": [
34
34
  "dist"
35
35
  ],
36
+ "engines": {
37
+ "node": "^20.19.0 || >=22.12.0"
38
+ },
39
+ "tsd": {
40
+ "directory": "test-d",
41
+ "compilerOptions": {
42
+ "skipLibCheck": true
43
+ }
44
+ },
36
45
  "dependencies": {
37
46
  "@changesets/get-github-info": "^0.8.0",
38
47
  "@changesets/types": "^6.1.0",
39
- "dotenv": "^17.3.1"
48
+ "dotenv": "^17.4.0"
40
49
  },
41
50
  "devDependencies": {
42
51
  "@changesets/parse": "^0.4.3"
43
52
  },
44
53
  "scripts": {
45
- "stub": "unbuild --stub",
46
- "dev": "tsup --watch --sourcemap",
47
- "build": "tsup",
54
+ "dev": "tsdown --watch --sourcemap",
55
+ "build": "tsdown",
48
56
  "test": "vitest run",
57
+ "test:types": "tsd",
49
58
  "test:dev": "vitest",
50
59
  "release": "pnpm publish",
51
60
  "lint": "eslint .",
package/dist/index.mjs DELETED
@@ -1,311 +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 || typeof options["repo"] !== "string" || options["repo"].trim().length === 0) {
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
- const type = match[1];
73
- const scope = match[2] || void 0;
74
- const description = (match[4] ?? raw).trim();
75
- return {
76
- type,
77
- scope,
78
- breaking: match[3] === "!",
79
- description
80
- };
81
- }
82
- function formatHeadline(raw) {
83
- const parsed = parseConventionalHeadline(raw);
84
- if (!parsed.type) {
85
- return { text: raw, breaking: parsed.breaking };
86
- }
87
- const scopeTag = parsed.scope ? `${parsed.scope}: ` : "";
88
- return {
89
- text: `${scopeTag}${parsed.description}`,
90
- breaking: parsed.breaking
91
- };
92
- }
93
- function normalizeUserMentions(users) {
94
- const normalized = users.map((user) => user.trim()).filter(Boolean).map((user) => user.startsWith("@") ? user : `@${user}`);
95
- if (normalized.length === 0) {
96
- return "";
97
- }
98
- if (normalized.length === 1) {
99
- return normalized[0] ?? "";
100
- }
101
- if (normalized.length === 2) {
102
- return `${normalized[0] ?? ""} and ${normalized[1] ?? ""}`;
103
- }
104
- return `${normalized.slice(0, -1).join(", ")}, and ${normalized.at(-1) ?? ""}`;
105
- }
106
- function extractSummaryMeta(summary) {
107
- let prNumber;
108
- let commitRef;
109
- let breaking = false;
110
- const users = [];
111
- const contentLines = summary.split(/\r?\n/).filter((line) => {
112
- const trimmed = line.trim();
113
- const withoutListMarker = trimmed.replace(/^[-*+]\s+/, "");
114
- const prMatch = withoutListMarker.match(
115
- /^(?:pr|pull|pull\s*request):\s*#?(\d+)\s*$/i
116
- );
117
- if (prMatch) {
118
- const num = Number(prMatch[1] ?? Number.NaN);
119
- if (!Number.isNaN(num)) {
120
- prNumber = num;
121
- }
122
- return false;
123
- }
124
- const commitMatch = withoutListMarker.match(/^commit:\s*(\S+)\s*$/i);
125
- if (commitMatch) {
126
- commitRef = commitMatch[1];
127
- return false;
128
- }
129
- const userMatch = withoutListMarker.match(/^(?:author|user):\s*(\S+)\s*$/i);
130
- if (userMatch) {
131
- const user = userMatch[1];
132
- if (user) {
133
- users.push(user.replace(/^@/, ""));
134
- }
135
- return false;
136
- }
137
- if (/^BREAKING[\s-]CHANGE:/i.test(withoutListMarker)) {
138
- breaking = true;
139
- }
140
- return true;
141
- }).map((line) => line.trimEnd());
142
- return {
143
- ...prNumber !== void 0 ? { prNumber } : {},
144
- ...commitRef !== void 0 ? { commitRef } : {},
145
- users,
146
- contentLines,
147
- breaking
148
- };
149
- }
150
- function parseSummary(summary) {
151
- const {
152
- prNumber,
153
- commitRef,
154
- users,
155
- contentLines,
156
- breaking: bodyBreaking
157
- } = extractSummaryMeta(summary);
158
- const nonEmptyIndex = contentLines.findIndex((line) => line.trim().length > 0);
159
- if (nonEmptyIndex === -1) {
160
- return {
161
- headline: "",
162
- detailLines: [],
163
- ...prNumber !== void 0 ? { prNumber } : {},
164
- ...commitRef !== void 0 ? { commitRef } : {},
165
- users,
166
- breaking: bodyBreaking
167
- };
168
- }
169
- const firstContentLine = contentLines[nonEmptyIndex]?.trim() ?? "";
170
- const startsWithList = /^(?:[-*+]\s+|\d+[.)]\s+)/.test(firstContentLine);
171
- const headline = startsWithList ? "" : firstContentLine;
172
- const restLines = startsWithList ? contentLines.slice(nonEmptyIndex) : contentLines.slice(nonEmptyIndex + 1);
173
- const detailLines = restLines;
174
- const headlineParsed = headline ? parseConventionalHeadline(headline) : null;
175
- const breaking = bodyBreaking || (headlineParsed?.breaking ?? false);
176
- return {
177
- headline,
178
- detailLines,
179
- ...prNumber !== void 0 ? { prNumber } : {},
180
- ...commitRef !== void 0 ? { commitRef } : {},
181
- users,
182
- breaking
183
- };
184
- }
185
- async function resolveLinks(repo, parsed, changesetCommit) {
186
- if (parsed.prNumber !== void 0) {
187
- const { links } = await getInfoFromPullRequest({
188
- repo,
189
- pull: parsed.prNumber
190
- });
191
- if (parsed.commitRef) {
192
- const shortCommitId = parsed.commitRef.slice(0, 7);
193
- links.commit = `[\`${shortCommitId}\`](https://github.com/${repo}/commit/${parsed.commitRef})`;
194
- }
195
- return links;
196
- }
197
- const commitToUse = parsed.commitRef ?? changesetCommit;
198
- if (commitToUse) {
199
- const { links } = await getInfo({
200
- repo,
201
- commit: commitToUse
202
- });
203
- return links;
204
- }
205
- return {
206
- commit: null,
207
- pull: null,
208
- user: null
209
- };
210
- }
211
- function buildUserMentions(users, fallbackUser) {
212
- if (users.length > 0) {
213
- return normalizeUserMentions(users);
214
- }
215
- if (fallbackUser) {
216
- const match = fallbackUser.match(/@([^\]]+)/);
217
- const matchedUser = match?.[1];
218
- return matchedUser ? normalizeUserMentions([matchedUser]) : "";
219
- }
220
- return "";
221
- }
222
- function indentMarkdownBlock(lines, spaces) {
223
- const prefix = " ".repeat(spaces);
224
- return lines.map((line) => {
225
- if (line.trim().length === 0) {
226
- return "";
227
- }
228
- return `${prefix}${line}`;
229
- }).join("\n");
230
- }
231
- function formatDetailBlock(detailLines) {
232
- const trimmed = detailLines.join("\n").trim().split("\n").map((line) => line.trimEnd());
233
- const hasFence = trimmed.some((line) => /^\s*```/.test(line));
234
- const isListLike = trimmed.some((line) => /^\s*(?:[-*+]\s+|\d+[.)]\s+)/.test(line));
235
- if (trimmed.length === 0) {
236
- return "";
237
- }
238
- if (hasFence || isListLike) {
239
- return indentMarkdownBlock(trimmed, 2);
240
- }
241
- const paragraphs = trimmed.join("\n").split(/\n\s*\n/).filter((chunk) => chunk.trim().length > 0);
242
- const lines = paragraphs.flatMap(
243
- (chunk) => chunk.split("\n").map((line) => line.trim()).filter(Boolean)
244
- );
245
- return lines.map((text) => ` - ${text}`).join("\n");
246
- }
247
- var changelogFunctions = {
248
- async getDependencyReleaseLine(changesets, dependenciesUpdated, options) {
249
- assertRepo(options);
250
- const repo = options.repo;
251
- if (dependenciesUpdated.length === 0) {
252
- return "";
253
- }
254
- const references = await collectDependencyReferences(
255
- changesets,
256
- repo
257
- );
258
- return formatDependencyLine(references, dependenciesUpdated);
259
- },
260
- async getReleaseLine(changeset, type, options) {
261
- assertRepo(options);
262
- const repo = options.repo;
263
- const parsedSummary = parseSummary(changeset.summary);
264
- const resolvedType = resolveReleaseType(type);
265
- const links = await resolveLinks(
266
- repo,
267
- parsedSummary,
268
- changeset.commit
269
- );
270
- const userMentions = buildUserMentions(
271
- parsedSummary.users,
272
- links.user
273
- );
274
- const { text: headlineText, breaking: headlineBreaking } = parsedSummary.headline ? formatHeadline(parsedSummary.headline) : { text: "Miscellaneous improvements", breaking: false };
275
- const isBreaking = parsedSummary.breaking || headlineBreaking;
276
- const breakingPrefix = isBreaking ? "\u26A0\uFE0F " : "";
277
- const icon = isBreaking ? "\u{1F4A5}" : releaseTypeMap[resolvedType].icon;
278
- const mainLineParts = [
279
- `- ${icon}`,
280
- `${breakingPrefix}**${headlineText}**`
281
- ];
282
- if (links.pull) {
283
- mainLineParts.push(links.pull);
284
- } else if (links.commit) {
285
- mainLineParts.push(links.commit);
286
- }
287
- if (links.pull && parsedSummary.commitRef && links.commit) {
288
- mainLineParts.push(links.commit);
289
- }
290
- if (userMentions) {
291
- mainLineParts.push(`by ${userMentions}`);
292
- }
293
- const mainLine = mainLineParts.join(" ");
294
- const detailBlock = formatDetailBlock(parsedSummary.detailLines);
295
- return detailBlock.length > 0 ? `
296
-
297
- ${mainLine}
298
- ${detailBlock}` : `
299
-
300
- ${mainLine}`;
301
- }
302
- };
303
- var index_default = changelogFunctions;
304
- var { getDependencyReleaseLine, getReleaseLine } = changelogFunctions;
305
- export {
306
- index_default as default,
307
- formatHeadline,
308
- getDependencyReleaseLine,
309
- getReleaseLine,
310
- parseConventionalHeadline
311
- };