fork-version 4.1.10 → 5.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +20 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +54 -193
- package/dist/commands/inspect.d.ts +9 -0
- package/dist/commands/inspect.js +41 -0
- package/dist/commands/main.d.ts +16 -0
- package/dist/commands/main.js +30 -0
- package/dist/commands/validate-config.d.ts +6 -0
- package/dist/commands/validate-config.js +11 -0
- package/dist/commit-parser/commit-parser.d.ts +114 -0
- package/dist/commit-parser/commit-parser.js +327 -0
- package/dist/commit-parser/filter-reverted-commits.d.ts +17 -0
- package/dist/commit-parser/filter-reverted-commits.js +34 -0
- package/dist/commit-parser/options.d.ts +74 -0
- package/dist/commit-parser/options.js +70 -0
- package/dist/commit-parser/parser-error.js +14 -0
- package/dist/commit-parser/types.d.ts +53 -0
- package/dist/config/changelog-preset-config.js +41 -0
- package/dist/config/cli-arguments.d.ts +109 -0
- package/dist/config/cli-arguments.js +141 -0
- package/dist/config/defaults.js +38 -0
- package/dist/config/define-config.d.ts +9 -0
- package/dist/config/define-config.js +9 -0
- package/dist/config/load-config.js +45 -0
- package/dist/config/merge-files.js +12 -0
- package/dist/config/schema.d.ts +50 -0
- package/dist/config/schema.js +61 -0
- package/dist/config/types.d.ts +279 -0
- package/dist/config/user-config.d.ts +6 -0
- package/dist/config/user-config.js +50 -0
- package/dist/detect-git-host/detect-git-host.js +35 -0
- package/dist/detect-git-host/host-azure-devops.js +28 -0
- package/dist/detect-git-host/host-bitbucket.js +28 -0
- package/dist/detect-git-host/host-github.js +32 -0
- package/dist/detect-git-host/host-gitlab.js +48 -0
- package/dist/files/arm-bicep.js +38 -0
- package/dist/files/file-manager.d.ts +56 -0
- package/dist/files/file-manager.js +87 -0
- package/dist/files/install-shield-ism.js +55 -0
- package/dist/files/json-package.js +64 -0
- package/dist/files/ms-build-project.js +55 -0
- package/dist/files/plain-text.js +31 -0
- package/dist/files/yaml-package.js +57 -0
- package/dist/index.d.ts +21 -655
- package/dist/index.js +19 -10
- package/dist/process/changelog.d.ts +7 -0
- package/dist/process/changelog.js +69 -0
- package/dist/process/commit.d.ts +9 -0
- package/dist/process/commit.js +22 -0
- package/dist/process/get-commits.d.ts +14 -0
- package/dist/process/get-commits.js +25 -0
- package/dist/process/get-current-version.d.ts +13 -0
- package/dist/process/get-current-version.js +35 -0
- package/dist/process/get-next-version.d.ts +21 -0
- package/dist/process/get-next-version.js +72 -0
- package/dist/process/tag.d.ts +8 -0
- package/dist/process/tag.js +15 -0
- package/dist/services/git.d.ts +141 -0
- package/dist/services/git.js +236 -0
- package/dist/services/logger.d.ts +18 -0
- package/dist/services/logger.js +35 -0
- package/dist/utils/clean-tag.js +21 -0
- package/dist/utils/escape-regex.js +17 -0
- package/dist/utils/file-state.js +19 -0
- package/dist/utils/format-commit-message.js +13 -0
- package/dist/utils/parse-regexp-string.js +31 -0
- package/dist/utils/release-type.js +47 -0
- package/dist/utils/trim-string-array.js +20 -0
- package/package.json +11 -29
- package/dist/chunk-33WIJWQZ.cjs +0 -2306
- package/dist/chunk-33WIJWQZ.cjs.map +0 -1
- package/dist/chunk-L5UDUEHE.js +0 -2262
- package/dist/chunk-L5UDUEHE.js.map +0 -1
- package/dist/cli.cjs +0 -205
- package/dist/cli.cjs.map +0 -1
- package/dist/cli.d.cts +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/index.cjs +0 -80
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts +0 -655
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
import { createParserOptions } from "./options.js";
|
|
2
|
+
import { ParserError } from "./parser-error.js";
|
|
3
|
+
//#region src/commit-parser/commit-parser.ts
|
|
4
|
+
var CommitParser = class {
|
|
5
|
+
#options;
|
|
6
|
+
#logger;
|
|
7
|
+
constructor(userOptions) {
|
|
8
|
+
this.#options = createParserOptions(userOptions);
|
|
9
|
+
this.setLogger = this.setLogger.bind(this);
|
|
10
|
+
this.createCommit = this.createCommit.bind(this);
|
|
11
|
+
this.parseRawCommit = this.parseRawCommit.bind(this);
|
|
12
|
+
this.parseSubject = this.parseSubject.bind(this);
|
|
13
|
+
this.parseMerge = this.parseMerge.bind(this);
|
|
14
|
+
this.parseRevert = this.parseRevert.bind(this);
|
|
15
|
+
this.parseMentions = this.parseMentions.bind(this);
|
|
16
|
+
this.parseReferenceParts = this.parseReferenceParts.bind(this);
|
|
17
|
+
this.parseReferences = this.parseReferences.bind(this);
|
|
18
|
+
this.parseNotes = this.parseNotes.bind(this);
|
|
19
|
+
this.parseRawLines = this.parseRawLines.bind(this);
|
|
20
|
+
this.parse = this.parse.bind(this);
|
|
21
|
+
}
|
|
22
|
+
setLogger(logger) {
|
|
23
|
+
this.#logger = logger;
|
|
24
|
+
return this;
|
|
25
|
+
}
|
|
26
|
+
createCommit() {
|
|
27
|
+
return {
|
|
28
|
+
raw: "",
|
|
29
|
+
subject: "",
|
|
30
|
+
body: "",
|
|
31
|
+
hash: "",
|
|
32
|
+
refNames: "",
|
|
33
|
+
date: "",
|
|
34
|
+
name: "",
|
|
35
|
+
email: "",
|
|
36
|
+
type: "",
|
|
37
|
+
scope: "",
|
|
38
|
+
breakingChange: "",
|
|
39
|
+
title: "",
|
|
40
|
+
merge: null,
|
|
41
|
+
revert: null,
|
|
42
|
+
notes: [],
|
|
43
|
+
mentions: [],
|
|
44
|
+
references: [],
|
|
45
|
+
tags: []
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parse the raw commit message into its expected parts
|
|
50
|
+
* - subject
|
|
51
|
+
* - body
|
|
52
|
+
* - hash
|
|
53
|
+
* - date
|
|
54
|
+
* - name
|
|
55
|
+
* - email
|
|
56
|
+
*
|
|
57
|
+
* @throws {ParserError}
|
|
58
|
+
*/
|
|
59
|
+
parseRawCommit(rawCommit) {
|
|
60
|
+
const parsedCommit = this.createCommit();
|
|
61
|
+
const parts = rawCommit.split(/\r?\n/);
|
|
62
|
+
if (parts.length < 6) throw new ParserError("Commit doesn't contain enough parts", rawCommit);
|
|
63
|
+
const email = parts.pop();
|
|
64
|
+
const name = parts.pop();
|
|
65
|
+
const date = parts.pop();
|
|
66
|
+
const refNames = parts.pop();
|
|
67
|
+
const hash = parts.pop();
|
|
68
|
+
if (email) parsedCommit.email = email.trim();
|
|
69
|
+
if (name) parsedCommit.name = name.trim();
|
|
70
|
+
if (date) {
|
|
71
|
+
parsedCommit.date = date.trim();
|
|
72
|
+
if (Number.isNaN(Date.parse(parsedCommit.date))) throw new ParserError("Unable to parse commit date", rawCommit);
|
|
73
|
+
}
|
|
74
|
+
if (refNames) {
|
|
75
|
+
parsedCommit.refNames = refNames.trim();
|
|
76
|
+
const TAG_REGEX = /tag:\s*(?<tag>.+?)[,)]/gi;
|
|
77
|
+
let tagMatch = null;
|
|
78
|
+
while (tagMatch = TAG_REGEX.exec(refNames)) {
|
|
79
|
+
const { tag = "" } = tagMatch.groups ?? {};
|
|
80
|
+
if (tag) parsedCommit.tags.push(tag);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
if (hash) parsedCommit.hash = hash.trim();
|
|
84
|
+
const subject = parts.shift()?.trimStart();
|
|
85
|
+
if (subject) {
|
|
86
|
+
parsedCommit.subject = subject;
|
|
87
|
+
parsedCommit.raw = subject;
|
|
88
|
+
}
|
|
89
|
+
parsedCommit.body = parts.filter((line) => {
|
|
90
|
+
if (this.#options.commentPattern) return !this.#options.commentPattern.test(line.trim());
|
|
91
|
+
return true;
|
|
92
|
+
}).join("\n").trim();
|
|
93
|
+
const raw = parts.join("\n").trim();
|
|
94
|
+
if (raw) parsedCommit.raw += "\n" + raw;
|
|
95
|
+
return parsedCommit;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* Parse the commit subject into its expected parts
|
|
99
|
+
* - type
|
|
100
|
+
* - scope (optional)
|
|
101
|
+
* - breaking change (optional)
|
|
102
|
+
* - title
|
|
103
|
+
*
|
|
104
|
+
* @throws {ParserError}
|
|
105
|
+
*/
|
|
106
|
+
parseSubject(commit) {
|
|
107
|
+
if (!this.#options.subjectPattern) return false;
|
|
108
|
+
const subjectMatch = this.#options.subjectPattern.exec(commit.subject);
|
|
109
|
+
if (subjectMatch?.groups) {
|
|
110
|
+
const { type = "", scope = "", breakingChange = "", title = "" } = subjectMatch.groups;
|
|
111
|
+
if (!type || !title) throw new ParserError("Unable to parse commit subject", commit);
|
|
112
|
+
commit.type = type;
|
|
113
|
+
commit.scope = scope;
|
|
114
|
+
if (breakingChange) commit.breakingChange = breakingChange;
|
|
115
|
+
commit.title = title;
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
return false;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Parse merge information from the commit subject
|
|
122
|
+
* @example
|
|
123
|
+
* ```txt
|
|
124
|
+
* "Merge pull request #123 from fork-version/feature"
|
|
125
|
+
* ```
|
|
126
|
+
*/
|
|
127
|
+
parseMerge(commit) {
|
|
128
|
+
if (!this.#options.mergePattern) return false;
|
|
129
|
+
const mergeMatch = this.#options.mergePattern.exec(commit.subject);
|
|
130
|
+
if (mergeMatch?.groups) {
|
|
131
|
+
const { id = "", source = "" } = mergeMatch.groups;
|
|
132
|
+
commit.merge = {
|
|
133
|
+
id,
|
|
134
|
+
source
|
|
135
|
+
};
|
|
136
|
+
return true;
|
|
137
|
+
}
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Parse revert information from the commit body
|
|
142
|
+
* @example
|
|
143
|
+
* ```txt
|
|
144
|
+
* "Revert "feat: initial commit"
|
|
145
|
+
*
|
|
146
|
+
* This reverts commit 4a79e9e546b4020d2882b7810dc549fa71960f4f."
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
parseRevert(commit) {
|
|
150
|
+
if (!this.#options.revertPattern) return false;
|
|
151
|
+
const revertMatch = this.#options.revertPattern.exec(commit.raw);
|
|
152
|
+
if (revertMatch?.groups) {
|
|
153
|
+
const { hash = "", subject = "" } = revertMatch.groups;
|
|
154
|
+
commit.revert = {
|
|
155
|
+
hash,
|
|
156
|
+
subject
|
|
157
|
+
};
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Search for mentions from the commit line
|
|
164
|
+
* @example
|
|
165
|
+
* ```txt
|
|
166
|
+
* "@fork-version"
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
169
|
+
parseMentions(line, outMentions) {
|
|
170
|
+
if (!this.#options.mentionPattern) return false;
|
|
171
|
+
const mentionRegex = new RegExp(this.#options.mentionPattern, "g");
|
|
172
|
+
let foundMention = false;
|
|
173
|
+
let mentionMatch;
|
|
174
|
+
while (mentionMatch = mentionRegex.exec(line)) {
|
|
175
|
+
if (!mentionMatch) break;
|
|
176
|
+
const { username = "" } = mentionMatch.groups ?? {};
|
|
177
|
+
outMentions.add(username);
|
|
178
|
+
foundMention = true;
|
|
179
|
+
}
|
|
180
|
+
return foundMention;
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Search for references from the commit line
|
|
184
|
+
* @example
|
|
185
|
+
* ```txt
|
|
186
|
+
* "#1234"
|
|
187
|
+
* "owner/repo#1234"
|
|
188
|
+
* ```
|
|
189
|
+
*/
|
|
190
|
+
parseReferenceParts(referenceText, action) {
|
|
191
|
+
if (!this.#options.issuePattern) return void 0;
|
|
192
|
+
const references = [];
|
|
193
|
+
const issueRegex = new RegExp(this.#options.issuePattern, "gi");
|
|
194
|
+
let issueMatch;
|
|
195
|
+
while (issueMatch = issueRegex.exec(referenceText)) {
|
|
196
|
+
if (!issueMatch) break;
|
|
197
|
+
const { repository = "", prefix = "", issue = "" } = issueMatch.groups ?? {};
|
|
198
|
+
const reference = {
|
|
199
|
+
prefix,
|
|
200
|
+
issue,
|
|
201
|
+
action,
|
|
202
|
+
owner: null,
|
|
203
|
+
repository: null
|
|
204
|
+
};
|
|
205
|
+
if (repository) {
|
|
206
|
+
const slashIndex = repository.indexOf("/");
|
|
207
|
+
if (slashIndex !== -1) {
|
|
208
|
+
reference.owner = repository.slice(0, slashIndex);
|
|
209
|
+
reference.repository = repository.slice(slashIndex + 1);
|
|
210
|
+
} else reference.repository = repository;
|
|
211
|
+
}
|
|
212
|
+
references.push(reference);
|
|
213
|
+
}
|
|
214
|
+
if (references.length > 0) return references;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Search for actions and references from the commit line
|
|
218
|
+
* @example
|
|
219
|
+
* ```txt
|
|
220
|
+
* "Closes #1234"
|
|
221
|
+
* "fixes owner/repo#1234"
|
|
222
|
+
* ```
|
|
223
|
+
*/
|
|
224
|
+
parseReferences(line, outReferences) {
|
|
225
|
+
if (!this.#options.referenceActionPattern || !this.#options.issuePattern) return false;
|
|
226
|
+
const referenceActionRegex = new RegExp(this.#options.referenceActionPattern, "gi").test(line) ? new RegExp(this.#options.referenceActionPattern, "gi") : /(?<reference>.*)/g;
|
|
227
|
+
let foundReference = false;
|
|
228
|
+
let referenceActionMatch;
|
|
229
|
+
while (referenceActionMatch = referenceActionRegex.exec(line)) {
|
|
230
|
+
if (!referenceActionMatch) break;
|
|
231
|
+
const { action = "", reference = "" } = referenceActionMatch.groups ?? {};
|
|
232
|
+
const parsedReferences = this.parseReferenceParts(reference, action || null);
|
|
233
|
+
if (!parsedReferences) break;
|
|
234
|
+
for (const ref of parsedReferences) if (!outReferences.some((r) => r.prefix === ref.prefix && r.issue === ref.issue)) outReferences.push(ref);
|
|
235
|
+
foundReference = true;
|
|
236
|
+
}
|
|
237
|
+
return foundReference;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Search for notes from the commit line
|
|
241
|
+
* @example
|
|
242
|
+
* ```txt
|
|
243
|
+
* "BREAKING CHANGE: this is a breaking change"
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
246
|
+
parseNotes(line, outNotes) {
|
|
247
|
+
if (!this.#options.notePattern) return false;
|
|
248
|
+
const noteMatch = new RegExp(this.#options.notePattern, "ig").exec(line);
|
|
249
|
+
if (noteMatch?.groups) {
|
|
250
|
+
const { title = "", text = "" } = noteMatch.groups;
|
|
251
|
+
outNotes.push({
|
|
252
|
+
title,
|
|
253
|
+
text
|
|
254
|
+
});
|
|
255
|
+
return true;
|
|
256
|
+
}
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Parse the raw commit for mentions, references and notes
|
|
261
|
+
*/
|
|
262
|
+
parseRawLines(commit) {
|
|
263
|
+
const mentions = /* @__PURE__ */ new Set();
|
|
264
|
+
const references = [];
|
|
265
|
+
const notes = [];
|
|
266
|
+
let lastNoteLine = -1;
|
|
267
|
+
const splitMessage = commit.raw.split("\n");
|
|
268
|
+
for (let index = 0; index < splitMessage.length; index++) {
|
|
269
|
+
const line = splitMessage[index];
|
|
270
|
+
const trimmedLine = line.trim();
|
|
271
|
+
if (this.#options.commentPattern?.test(trimmedLine)) continue;
|
|
272
|
+
this.parseMentions(trimmedLine, mentions);
|
|
273
|
+
if (this.parseReferences(trimmedLine, references)) {
|
|
274
|
+
lastNoteLine = -1;
|
|
275
|
+
continue;
|
|
276
|
+
}
|
|
277
|
+
if (this.parseNotes(trimmedLine, notes)) lastNoteLine = index;
|
|
278
|
+
else if (lastNoteLine !== -1) {
|
|
279
|
+
notes[notes.length - 1].text += `\n${line}`;
|
|
280
|
+
lastNoteLine = index;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
if (mentions.size > 0) commit.mentions = Array.from(mentions);
|
|
284
|
+
if (references.length > 0) commit.references = references;
|
|
285
|
+
if (notes.length > 0) commit.notes = notes.map((note) => ({
|
|
286
|
+
...note,
|
|
287
|
+
text: note.text.trim()
|
|
288
|
+
}));
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Parse a commit log with the following format separated by new line characters:
|
|
292
|
+
* ```txt
|
|
293
|
+
* refactor: add test file
|
|
294
|
+
* Add a test file to the project
|
|
295
|
+
* 4ef2c86d393a9660aa9f753144256b1f200c16bd
|
|
296
|
+
* 2024-12-22T17:36:50Z
|
|
297
|
+
* Fork Version
|
|
298
|
+
* fork-version@example.com
|
|
299
|
+
* ```
|
|
300
|
+
*
|
|
301
|
+
* @example
|
|
302
|
+
* ```ts
|
|
303
|
+
* parse("refactor: add test file\nAdd a test file to the project\n4ef2c86d393a9660aa9f753144256b1f200c16bd\n2024-12-22T17:36:50Z\nFork Version\nfork-version@example.com");
|
|
304
|
+
* ```
|
|
305
|
+
*
|
|
306
|
+
* The expected input value can be generated by running the following command:
|
|
307
|
+
* ```sh
|
|
308
|
+
* git log --format="%s%n%b%n%H%n%cI%n%cN%n%cE%n"
|
|
309
|
+
* ```
|
|
310
|
+
* @see {@link https://git-scm.com/docs/pretty-formats|Git Pretty Format Documentation}
|
|
311
|
+
*/
|
|
312
|
+
parse(rawCommit) {
|
|
313
|
+
try {
|
|
314
|
+
const commit = this.parseRawCommit(rawCommit);
|
|
315
|
+
this.parseSubject(commit);
|
|
316
|
+
this.parseMerge(commit);
|
|
317
|
+
this.parseRevert(commit);
|
|
318
|
+
this.parseRawLines(commit);
|
|
319
|
+
return commit;
|
|
320
|
+
} catch (error) {
|
|
321
|
+
if (this.#logger) this.#logger.debug("[Commit Parser] Failed to parse commit", { error });
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
};
|
|
326
|
+
//#endregion
|
|
327
|
+
export { CommitParser };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Commit } from "./types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/commit-parser/filter-reverted-commits.d.ts
|
|
4
|
+
/**
|
|
5
|
+
* Filter out revert commits and their corresponding reverted commits,
|
|
6
|
+
* this function expects the input to be sorted by date in descending order
|
|
7
|
+
* from the most recent to the oldest commit.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```ts
|
|
11
|
+
* const commits: Commit[] = [...];
|
|
12
|
+
* const filteredCommits = filterRevertedCommits(commits);
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
declare function filterRevertedCommits(parsedCommits: Commit[]): Commit[];
|
|
16
|
+
//#endregion
|
|
17
|
+
export { filterRevertedCommits };
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
//#region src/commit-parser/filter-reverted-commits.ts
|
|
2
|
+
/**
|
|
3
|
+
* Filter out revert commits and their corresponding reverted commits,
|
|
4
|
+
* this function expects the input to be sorted by date in descending order
|
|
5
|
+
* from the most recent to the oldest commit.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```ts
|
|
9
|
+
* const commits: Commit[] = [...];
|
|
10
|
+
* const filteredCommits = filterRevertedCommits(commits);
|
|
11
|
+
* ```
|
|
12
|
+
*/
|
|
13
|
+
function filterRevertedCommits(parsedCommits) {
|
|
14
|
+
const revertedCommits = [];
|
|
15
|
+
for (const commit of parsedCommits) {
|
|
16
|
+
if (!commit.revert) continue;
|
|
17
|
+
if (revertedCommits.some((r) => r.revert?.hash === commit.hash || r.revert?.subject === commit.subject)) continue;
|
|
18
|
+
revertedCommits.push(commit);
|
|
19
|
+
}
|
|
20
|
+
if (revertedCommits.length === 0) return parsedCommits;
|
|
21
|
+
const commitsWithoutReverts = [];
|
|
22
|
+
for (const commit of parsedCommits) {
|
|
23
|
+
if (commit.revert) continue;
|
|
24
|
+
const revertedIndex = revertedCommits.findIndex((r) => r.revert?.hash === commit.hash || r.revert?.subject === commit.subject);
|
|
25
|
+
if (revertedIndex !== -1) {
|
|
26
|
+
revertedCommits.splice(revertedIndex, 1);
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
commitsWithoutReverts.push(commit);
|
|
30
|
+
}
|
|
31
|
+
return commitsWithoutReverts;
|
|
32
|
+
}
|
|
33
|
+
//#endregion
|
|
34
|
+
export { filterRevertedCommits };
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
//#region src/commit-parser/options.d.ts
|
|
2
|
+
interface ParserOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Pattern to match commit subjects
|
|
5
|
+
* - Expected capture groups: `type` `title`
|
|
6
|
+
* - Optional capture groups: `scope`, `breakingChange`
|
|
7
|
+
*/
|
|
8
|
+
subjectPattern: RegExp | undefined;
|
|
9
|
+
/**
|
|
10
|
+
* Pattern to match merge commits
|
|
11
|
+
* - Expected capture groups: `id`, `source`
|
|
12
|
+
*/
|
|
13
|
+
mergePattern: RegExp | undefined;
|
|
14
|
+
/**
|
|
15
|
+
* Pattern to match revert commits
|
|
16
|
+
* - Expected capture groups: `subject`, `hash`
|
|
17
|
+
*/
|
|
18
|
+
revertPattern: RegExp | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Pattern to match commented out lines which will be trimmed
|
|
21
|
+
*/
|
|
22
|
+
commentPattern: RegExp | undefined;
|
|
23
|
+
/**
|
|
24
|
+
* Pattern to match mentions
|
|
25
|
+
* - Expected capture groups: `username`
|
|
26
|
+
*/
|
|
27
|
+
mentionPattern: RegExp | undefined;
|
|
28
|
+
/**
|
|
29
|
+
* List of action labels to match reference sections
|
|
30
|
+
* @default
|
|
31
|
+
* ["close", "closes", "closed", "fix", "fixes", "fixed", "resolve", "resolves", "resolved"]
|
|
32
|
+
*/
|
|
33
|
+
referenceActions?: string[];
|
|
34
|
+
/**
|
|
35
|
+
* Pattern to match reference sections
|
|
36
|
+
* - Expected capture groups: `action`, `reference`
|
|
37
|
+
*/
|
|
38
|
+
referenceActionPattern: RegExp | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* List of issue prefixes to match issue ids
|
|
41
|
+
* @default
|
|
42
|
+
* ["#"]
|
|
43
|
+
*/
|
|
44
|
+
issuePrefixes?: string[];
|
|
45
|
+
/**
|
|
46
|
+
* Pattern to match issue references
|
|
47
|
+
* - Expected capture groups: `repository`, `prefix`, `issue`
|
|
48
|
+
*/
|
|
49
|
+
issuePattern: RegExp | undefined;
|
|
50
|
+
/**
|
|
51
|
+
* List of keywords to match note titles
|
|
52
|
+
* @default
|
|
53
|
+
* ["BREAKING CHANGE", "BREAKING-CHANGE"]
|
|
54
|
+
*/
|
|
55
|
+
noteKeywords?: string[];
|
|
56
|
+
/**
|
|
57
|
+
* Pattern to match note sections
|
|
58
|
+
* - Expected capture groups: `title`
|
|
59
|
+
* - Optional capture groups: `text`
|
|
60
|
+
*/
|
|
61
|
+
notePattern: RegExp | undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Creates parser options by merging user-provided options with default values.
|
|
65
|
+
*
|
|
66
|
+
* Additionally, if a user provides a string for a property that expects a RegExp value,
|
|
67
|
+
* the function will attempt to parse it into a RegExp object.
|
|
68
|
+
*
|
|
69
|
+
* @param userOptions Optional user-provided options to override the defaults.
|
|
70
|
+
* @return A complete set of parser options with defaults applied and user overrides where specified.
|
|
71
|
+
*/
|
|
72
|
+
declare function createParserOptions(userOptions?: Partial<ParserOptions>): ParserOptions;
|
|
73
|
+
//#endregion
|
|
74
|
+
export { ParserOptions, createParserOptions };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { escapeRegex } from "../utils/escape-regex.js";
|
|
2
|
+
import { parseRegExpString } from "../utils/parse-regexp-string.js";
|
|
3
|
+
import { trimStringArray } from "../utils/trim-string-array.js";
|
|
4
|
+
//#region src/commit-parser/options.ts
|
|
5
|
+
/**
|
|
6
|
+
* Creates default parser options with predefined patterns and lists.
|
|
7
|
+
*
|
|
8
|
+
* The default expects the user is using Github as their hosting service.
|
|
9
|
+
*
|
|
10
|
+
* @param userOptions Optional user-provided options to override the defaults.
|
|
11
|
+
* @returns A complete set of parser options with defaults applied and user overrides where specified.
|
|
12
|
+
*/
|
|
13
|
+
function createDefaultParserOptions(userOptions) {
|
|
14
|
+
const referenceActions = trimStringArray(userOptions?.referenceActions, escapeRegex) ?? [
|
|
15
|
+
"close",
|
|
16
|
+
"closes",
|
|
17
|
+
"closed",
|
|
18
|
+
"fix",
|
|
19
|
+
"fixes",
|
|
20
|
+
"fixed",
|
|
21
|
+
"resolve",
|
|
22
|
+
"resolves",
|
|
23
|
+
"resolved"
|
|
24
|
+
];
|
|
25
|
+
const joinedReferenceActions = referenceActions.join("|");
|
|
26
|
+
const issuePrefixes = trimStringArray(userOptions?.issuePrefixes, escapeRegex) ?? ["#"];
|
|
27
|
+
const joinedIssuePrefixes = issuePrefixes.join("|");
|
|
28
|
+
const noteKeywords = trimStringArray(userOptions?.noteKeywords, escapeRegex) ?? ["BREAKING CHANGE", "BREAKING-CHANGE"];
|
|
29
|
+
const joinedNoteKeywords = noteKeywords.join("|");
|
|
30
|
+
return {
|
|
31
|
+
subjectPattern: /^(?<type>\w+)(?:\((?<scope>.*)\))?(?<breakingChange>!)?:\s+(?<title>.*)/i,
|
|
32
|
+
mergePattern: /^Merge pull request #(?<id>\d*) from (?<source>.*)/i,
|
|
33
|
+
revertPattern: /^[Rr]evert "(?<subject>.*)"(\s*This reverts commit (?<hash>[a-zA-Z0-9]*)\.)?/,
|
|
34
|
+
commentPattern: /^#(?!\d+\s)/,
|
|
35
|
+
mentionPattern: /(?<!\w)@(?<username>[\w-]+)/,
|
|
36
|
+
referenceActions,
|
|
37
|
+
referenceActionPattern: joinedReferenceActions ? new RegExp(`(?<action>${joinedReferenceActions})(?:\\s+(?<reference>.*?))(?=(?:${joinedReferenceActions})|$)`) : void 0,
|
|
38
|
+
issuePrefixes,
|
|
39
|
+
issuePattern: joinedIssuePrefixes ? new RegExp(`(?:.*?)??\\s*(?<repository>[\\w-\\.\\/]*?)??(?<prefix>${joinedIssuePrefixes})(?<issue>[\\w-]*\\d+)`) : void 0,
|
|
40
|
+
noteKeywords,
|
|
41
|
+
notePattern: joinedNoteKeywords ? new RegExp(`^(?<title>${joinedNoteKeywords}):(\\s*(?<text>.*))`) : void 0
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Creates parser options by merging user-provided options with default values.
|
|
46
|
+
*
|
|
47
|
+
* Additionally, if a user provides a string for a property that expects a RegExp value,
|
|
48
|
+
* the function will attempt to parse it into a RegExp object.
|
|
49
|
+
*
|
|
50
|
+
* @param userOptions Optional user-provided options to override the defaults.
|
|
51
|
+
* @return A complete set of parser options with defaults applied and user overrides where specified.
|
|
52
|
+
*/
|
|
53
|
+
function createParserOptions(userOptions) {
|
|
54
|
+
const initialOptions = createDefaultParserOptions(userOptions);
|
|
55
|
+
if (userOptions) for (const key of Object.keys(userOptions)) {
|
|
56
|
+
const userValue = userOptions[key];
|
|
57
|
+
if (!(key in initialOptions)) continue;
|
|
58
|
+
if (Array.isArray(initialOptions[key])) continue;
|
|
59
|
+
if (initialOptions[key] instanceof RegExp) {
|
|
60
|
+
if (userValue instanceof RegExp) initialOptions[key] = userValue;
|
|
61
|
+
else if (typeof userValue === "string") {
|
|
62
|
+
const parsed = parseRegExpString(userValue);
|
|
63
|
+
if (parsed) initialOptions[key] = parsed;
|
|
64
|
+
} else if (userValue == void 0) initialOptions[key] = void 0;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return initialOptions;
|
|
68
|
+
}
|
|
69
|
+
//#endregion
|
|
70
|
+
export { createParserOptions };
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
//#region src/commit-parser/parser-error.ts
|
|
2
|
+
/**
|
|
3
|
+
* Error thrown when commit parser encounters an error.
|
|
4
|
+
*/
|
|
5
|
+
var ParserError = class extends Error {
|
|
6
|
+
detail;
|
|
7
|
+
constructor(message, detail) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.name = "ParserError";
|
|
10
|
+
this.detail = detail;
|
|
11
|
+
}
|
|
12
|
+
};
|
|
13
|
+
//#endregion
|
|
14
|
+
export { ParserError };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
//#region src/commit-parser/types.d.ts
|
|
2
|
+
interface CommitMerge {
|
|
3
|
+
id: string;
|
|
4
|
+
source: string;
|
|
5
|
+
}
|
|
6
|
+
interface CommitRevert {
|
|
7
|
+
hash: string;
|
|
8
|
+
subject: string;
|
|
9
|
+
}
|
|
10
|
+
interface CommitReference {
|
|
11
|
+
prefix: string;
|
|
12
|
+
issue: string;
|
|
13
|
+
action: string | null;
|
|
14
|
+
owner: string | null;
|
|
15
|
+
repository: string | null;
|
|
16
|
+
}
|
|
17
|
+
interface CommitNote {
|
|
18
|
+
title: string;
|
|
19
|
+
text: string;
|
|
20
|
+
}
|
|
21
|
+
interface Commit {
|
|
22
|
+
raw: string;
|
|
23
|
+
subject: string;
|
|
24
|
+
body: string;
|
|
25
|
+
hash: string;
|
|
26
|
+
refNames: string;
|
|
27
|
+
/**
|
|
28
|
+
* Committer date in ISO 8601 format
|
|
29
|
+
* @example
|
|
30
|
+
* "2024-12-22T17:36:50Z"
|
|
31
|
+
*/
|
|
32
|
+
date: string;
|
|
33
|
+
/**
|
|
34
|
+
* Committer name (respects .mailmap)
|
|
35
|
+
*/
|
|
36
|
+
name: string;
|
|
37
|
+
/**
|
|
38
|
+
* Committer email (respects .mailmap)
|
|
39
|
+
*/
|
|
40
|
+
email: string;
|
|
41
|
+
type: string;
|
|
42
|
+
scope: string;
|
|
43
|
+
breakingChange: string;
|
|
44
|
+
title: string;
|
|
45
|
+
merge: CommitMerge | null;
|
|
46
|
+
revert: CommitRevert | null;
|
|
47
|
+
mentions: string[];
|
|
48
|
+
references: CommitReference[];
|
|
49
|
+
notes: CommitNote[];
|
|
50
|
+
tags: string[];
|
|
51
|
+
}
|
|
52
|
+
//#endregion
|
|
53
|
+
export { Commit, CommitMerge, CommitNote, CommitReference, CommitRevert };
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ChangelogPresetConfigSchema, ChangelogPresetConfigTypeSchema } from "./schema.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import conventionalChangelogConfigSpec from "conventional-changelog-config-spec";
|
|
4
|
+
//#region src/config/changelog-preset-config.ts
|
|
5
|
+
function getChangelogPresetConfig(mergedConfig, cliArguments, detectedChangelogOptions) {
|
|
6
|
+
const preset = { name: "conventionalcommits" };
|
|
7
|
+
if (typeof conventionalChangelogConfigSpec.properties === "object") Object.entries(conventionalChangelogConfigSpec.properties).forEach(([key, value]) => {
|
|
8
|
+
if ("default" in value && value.default !== void 0) {
|
|
9
|
+
if (mergedConfig?.changelogAll && key === "types") {
|
|
10
|
+
const parsedTypes = z.array(ChangelogPresetConfigTypeSchema).safeParse(value.default);
|
|
11
|
+
if (parsedTypes.success) {
|
|
12
|
+
parsedTypes.data.forEach((type) => {
|
|
13
|
+
if (!type.section) {
|
|
14
|
+
delete type.hidden;
|
|
15
|
+
type.section = "Other Changes";
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
preset[key] = parsedTypes.data;
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
preset[key] = value.default;
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
if (detectedChangelogOptions) Object.entries(detectedChangelogOptions).forEach(([key, value]) => {
|
|
26
|
+
if (value !== void 0) preset[key] = value;
|
|
27
|
+
});
|
|
28
|
+
if (mergedConfig?.changelogPresetConfig && typeof mergedConfig.changelogPresetConfig === "object") Object.entries(mergedConfig.changelogPresetConfig).forEach(([key, value]) => {
|
|
29
|
+
if (value !== void 0) preset[key] = value;
|
|
30
|
+
});
|
|
31
|
+
if (mergedConfig?.releaseMessageSuffix && !cliArguments?.releaseMessageSuffix) preset.releaseCommitMessageFormat = `${preset.releaseCommitMessageFormat} ${mergedConfig.releaseMessageSuffix}`;
|
|
32
|
+
if (cliArguments?.commitUrlFormat) preset.commitUrlFormat = cliArguments.commitUrlFormat;
|
|
33
|
+
if (cliArguments?.compareUrlFormat) preset.compareUrlFormat = cliArguments.compareUrlFormat;
|
|
34
|
+
if (cliArguments?.issueUrlFormat) preset.issueUrlFormat = cliArguments.issueUrlFormat;
|
|
35
|
+
if (cliArguments?.userUrlFormat) preset.userUrlFormat = cliArguments.userUrlFormat;
|
|
36
|
+
if (cliArguments?.releaseCommitMessageFormat) preset.releaseCommitMessageFormat = cliArguments.releaseCommitMessageFormat;
|
|
37
|
+
if (cliArguments?.releaseMessageSuffix) preset.releaseCommitMessageFormat = `${preset.releaseCommitMessageFormat} ${cliArguments.releaseMessageSuffix}`;
|
|
38
|
+
return ChangelogPresetConfigSchema.passthrough().parse(preset);
|
|
39
|
+
}
|
|
40
|
+
//#endregion
|
|
41
|
+
export { getChangelogPresetConfig };
|