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.
Files changed (81) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/cli.d.ts +1 -1
  3. package/dist/cli.js +54 -193
  4. package/dist/commands/inspect.d.ts +9 -0
  5. package/dist/commands/inspect.js +41 -0
  6. package/dist/commands/main.d.ts +16 -0
  7. package/dist/commands/main.js +30 -0
  8. package/dist/commands/validate-config.d.ts +6 -0
  9. package/dist/commands/validate-config.js +11 -0
  10. package/dist/commit-parser/commit-parser.d.ts +114 -0
  11. package/dist/commit-parser/commit-parser.js +327 -0
  12. package/dist/commit-parser/filter-reverted-commits.d.ts +17 -0
  13. package/dist/commit-parser/filter-reverted-commits.js +34 -0
  14. package/dist/commit-parser/options.d.ts +74 -0
  15. package/dist/commit-parser/options.js +70 -0
  16. package/dist/commit-parser/parser-error.js +14 -0
  17. package/dist/commit-parser/types.d.ts +53 -0
  18. package/dist/config/changelog-preset-config.js +41 -0
  19. package/dist/config/cli-arguments.d.ts +109 -0
  20. package/dist/config/cli-arguments.js +141 -0
  21. package/dist/config/defaults.js +38 -0
  22. package/dist/config/define-config.d.ts +9 -0
  23. package/dist/config/define-config.js +9 -0
  24. package/dist/config/load-config.js +45 -0
  25. package/dist/config/merge-files.js +12 -0
  26. package/dist/config/schema.d.ts +50 -0
  27. package/dist/config/schema.js +61 -0
  28. package/dist/config/types.d.ts +279 -0
  29. package/dist/config/user-config.d.ts +6 -0
  30. package/dist/config/user-config.js +50 -0
  31. package/dist/detect-git-host/detect-git-host.js +35 -0
  32. package/dist/detect-git-host/host-azure-devops.js +28 -0
  33. package/dist/detect-git-host/host-bitbucket.js +28 -0
  34. package/dist/detect-git-host/host-github.js +32 -0
  35. package/dist/detect-git-host/host-gitlab.js +48 -0
  36. package/dist/files/arm-bicep.js +38 -0
  37. package/dist/files/file-manager.d.ts +56 -0
  38. package/dist/files/file-manager.js +87 -0
  39. package/dist/files/install-shield-ism.js +55 -0
  40. package/dist/files/json-package.js +64 -0
  41. package/dist/files/ms-build-project.js +55 -0
  42. package/dist/files/plain-text.js +31 -0
  43. package/dist/files/yaml-package.js +57 -0
  44. package/dist/index.d.ts +21 -655
  45. package/dist/index.js +19 -10
  46. package/dist/process/changelog.d.ts +7 -0
  47. package/dist/process/changelog.js +69 -0
  48. package/dist/process/commit.d.ts +9 -0
  49. package/dist/process/commit.js +22 -0
  50. package/dist/process/get-commits.d.ts +14 -0
  51. package/dist/process/get-commits.js +25 -0
  52. package/dist/process/get-current-version.d.ts +13 -0
  53. package/dist/process/get-current-version.js +35 -0
  54. package/dist/process/get-next-version.d.ts +21 -0
  55. package/dist/process/get-next-version.js +72 -0
  56. package/dist/process/tag.d.ts +8 -0
  57. package/dist/process/tag.js +15 -0
  58. package/dist/services/git.d.ts +141 -0
  59. package/dist/services/git.js +236 -0
  60. package/dist/services/logger.d.ts +18 -0
  61. package/dist/services/logger.js +35 -0
  62. package/dist/utils/clean-tag.js +21 -0
  63. package/dist/utils/escape-regex.js +17 -0
  64. package/dist/utils/file-state.js +19 -0
  65. package/dist/utils/format-commit-message.js +13 -0
  66. package/dist/utils/parse-regexp-string.js +31 -0
  67. package/dist/utils/release-type.js +47 -0
  68. package/dist/utils/trim-string-array.js +20 -0
  69. package/package.json +11 -29
  70. package/dist/chunk-33WIJWQZ.cjs +0 -2306
  71. package/dist/chunk-33WIJWQZ.cjs.map +0 -1
  72. package/dist/chunk-L5UDUEHE.js +0 -2262
  73. package/dist/chunk-L5UDUEHE.js.map +0 -1
  74. package/dist/cli.cjs +0 -205
  75. package/dist/cli.cjs.map +0 -1
  76. package/dist/cli.d.cts +0 -1
  77. package/dist/cli.js.map +0 -1
  78. package/dist/index.cjs +0 -80
  79. package/dist/index.cjs.map +0 -1
  80. package/dist/index.d.cts +0 -655
  81. package/dist/index.js.map +0 -1
@@ -1,2306 +0,0 @@
1
- 'use strict';
2
-
3
- var zod = require('zod');
4
- var child_process = require('child_process');
5
- var semver5 = require('semver');
6
- var path = require('path');
7
- var promises = require('fs/promises');
8
- var conventionalChangelogConfigSpec = require('conventional-changelog-config-spec');
9
- var fs = require('fs');
10
- var JoyCon = require('joycon');
11
- var bundleRequire = require('bundle-require');
12
- var util = require('util');
13
- var jsoncParser = require('jsonc-parser');
14
- var yaml = require('yaml');
15
- var cheerio = require('cheerio/slim');
16
- var conventionalChangelog = require('conventional-changelog');
17
-
18
- function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
19
-
20
- function _interopNamespace(e) {
21
- if (e && e.__esModule) return e;
22
- var n = Object.create(null);
23
- if (e) {
24
- Object.keys(e).forEach(function (k) {
25
- if (k !== 'default') {
26
- var d = Object.getOwnPropertyDescriptor(e, k);
27
- Object.defineProperty(n, k, d.get ? d : {
28
- enumerable: true,
29
- get: function () { return e[k]; }
30
- });
31
- }
32
- });
33
- }
34
- n.default = e;
35
- return Object.freeze(n);
36
- }
37
-
38
- var semver5__default = /*#__PURE__*/_interopDefault(semver5);
39
- var conventionalChangelogConfigSpec__default = /*#__PURE__*/_interopDefault(conventionalChangelogConfigSpec);
40
- var JoyCon__default = /*#__PURE__*/_interopDefault(JoyCon);
41
- var cheerio__namespace = /*#__PURE__*/_interopNamespace(cheerio);
42
- var conventionalChangelog__default = /*#__PURE__*/_interopDefault(conventionalChangelog);
43
-
44
- // src/config/schema.js
45
- var ChangelogPresetConfigTypeSchema = zod.z.object({
46
- /**
47
- * The type of commit message.
48
- * @example "feat", "fix", "chore", etc..
49
- */
50
- type: zod.z.string().describe('The type of commit message, such as "feat", "fix", "chore".'),
51
- /**
52
- * The scope of the commit message.
53
- */
54
- scope: zod.z.string().optional().describe("The scope of the commit message."),
55
- /**
56
- * The section of the `CHANGELOG` the commit should show up in.
57
- */
58
- section: zod.z.string().optional().describe("The section of the `CHANGELOG` the commit should show up in."),
59
- /**
60
- * Should show in the generated changelog message?
61
- */
62
- hidden: zod.z.boolean().optional().describe("Should show in the generated changelog message?")
63
- });
64
- var ChangelogPresetConfigSchema = zod.z.object({
65
- /**
66
- * List of explicitly supported commit message types.
67
- */
68
- types: zod.z.array(ChangelogPresetConfigTypeSchema).describe("List of explicitly supported commit message types."),
69
- /**
70
- * A URL representing a specific commit at a hash.
71
- * @default "{{host}}/{{owner}}/{{repository}}/commit/{{hash}}"
72
- */
73
- commitUrlFormat: zod.z.string().describe("A URL representing a specific commit at a hash."),
74
- /**
75
- * A URL representing the comparison between two git SHAs.
76
- * @default "{{host}}/{{owner}}/{{repository}}/compare/{{previousTag}}...{{currentTag}}"
77
- */
78
- compareUrlFormat: zod.z.string().describe("A URL representing the comparison between two git SHAs."),
79
- /**
80
- * A URL representing the issue format (allowing a different URL format to be swapped in
81
- * for Gitlab, Bitbucket, etc).
82
- * @default "{{host}}/{{owner}}/{{repository}}/issues/{{id}}"
83
- */
84
- issueUrlFormat: zod.z.string().describe("A URL representing the issue format."),
85
- /**
86
- * A URL representing a user's profile on GitHub, Gitlab, etc. This URL is used
87
- * for substituting @eglavin with https://github.com/eglavin in commit messages.
88
- * @default "{{host}}/{{user}}"
89
- */
90
- userUrlFormat: zod.z.string().describe("A URL representing a user's profile on GitHub, Gitlab, etc."),
91
- /**
92
- * A string to be used to format the auto-generated release commit message.
93
- * @default "chore(release): {{currentTag}}"
94
- */
95
- releaseCommitMessageFormat: zod.z.string().describe("A string to be used to format the auto-generated release commit message."),
96
- /**
97
- * List of prefixes used to detect references to issues.
98
- * @default ["#"]
99
- */
100
- issuePrefixes: zod.z.array(zod.z.string()).describe("List of prefixes used to detect references to issues.")
101
- });
102
- var ForkConfigSchema = zod.z.object({
103
- // Commands
104
- //
105
- /**
106
- * The command to run, can be one of the following:
107
- *
108
- * - `main` - Bumps the version, update files, generate changelog, commit, and tag.
109
- * - `inspect-version` - Prints the current version and exits.
110
- * - `inspect-tag` - Prints the current git tag and exits.
111
- * - `inspect` - Prints the current version and git tag and exits.
112
- * - `validate-config` - Validates the configuration and exits.
113
- *
114
- * @default "main"
115
- */
116
- command: zod.z.literal(["main", "inspect", "inspect-version", "inspect-tag", "validate-config"]).describe(
117
- "The command to run. Can be one of: main, inspect, inspect-version, inspect-tag, validate-config. Defaults to main."
118
- ),
119
- /**
120
- * If set, Fork-Version will print the current version and exit.
121
- * @default false
122
- *
123
- * @deprecated Set the `inspect-version` command instead.
124
- */
125
- inspectVersion: zod.z.boolean().optional().describe("If set, Fork-Version will print the current version and exit."),
126
- // Options
127
- //
128
- /**
129
- * List of the files to be updated.
130
- * @default
131
- * ```js
132
- * ["bower.json", "deno.json", "deno.jsonc", "jsr.json", "jsr.jsonc", "manifest.json", "npm-shrinkwrap.json", "package-lock.json", "package.json"]
133
- * ```
134
- */
135
- files: zod.z.array(zod.z.string()).describe("List of the files to be updated."),
136
- /**
137
- * Glob pattern to match files to be updated.
138
- *
139
- * Internally we're using [glob](https://github.com/isaacs/node-glob) to match files.
140
- *
141
- * Read more about the pattern syntax [here](https://github.com/isaacs/node-glob/tree/v10.3.12?tab=readme-ov-file#glob-primer).
142
- *
143
- * @default undefined
144
- * @example "*.json"
145
- */
146
- glob: zod.z.string().optional().describe("Glob pattern to match files to be updated."),
147
- /**
148
- * The path Fork-Version will run from.
149
- * @default
150
- * ```js
151
- * process.cwd()
152
- * ```
153
- */
154
- path: zod.z.string().describe('The path Fork-Version will run from. Defaults to "process.cwd()".'),
155
- /**
156
- * Name of the changelog file.
157
- * @default "CHANGELOG.md"
158
- */
159
- changelog: zod.z.string().describe('Name of the changelog file. Defaults to "CHANGELOG.md".'),
160
- /**
161
- * The header text for the changelog.
162
- * @default
163
- * ```markdown
164
- * # Changelog
165
- *
166
- * All notable changes to this project will be documented in this file. See [fork-version](https://github.com/eglavin/fork-version) for commit guidelines.
167
- * ```
168
- */
169
- header: zod.z.string().describe("The header text for the changelog."),
170
- /**
171
- * Specify a prefix for the created tag.
172
- *
173
- * For instance if your version tag is prefixed by "version/" instead of "v" you have to specify
174
- * `tagPrefix: "version/"`.
175
- *
176
- * `tagPrefix` can also be used for a monorepo environment where you might want to deploy
177
- * multiple package from the same repository. In this case you can specify a prefix for
178
- * each package:
179
- *
180
- * | Example Value | Tag Created |
181
- * |:-------------------------|:------------------------------|
182
- * | "" | `1.2.3` |
183
- * | "version/" | `version/1.2.3` |
184
- * | "@eglavin/fork-version-" | `@eglavin/fork-version-1.2.3` |
185
- *
186
- * @example "", "version/", "@eglavin/fork-version-"
187
- * @default "v"
188
- */
189
- tagPrefix: zod.z.string().describe('Specify a prefix for the created tag. Defaults to "v".'),
190
- /**
191
- * Make a pre-release with optional label if given value is a string.
192
- *
193
- * | Example Value | Produced Version |
194
- * |:--------------|:-----------------|
195
- * | true | `1.2.3-0` |
196
- * | "alpha" | `1.2.3-alpha-0` |
197
- * | "beta" | `1.2.3-beta-0` |
198
- *
199
- * @example true, "alpha", "beta", "rc"
200
- * @default undefined
201
- */
202
- preRelease: zod.z.string().or(zod.z.boolean()).optional().describe("Make a pre-release with optional label if given value is a string."),
203
- /**
204
- * If set, Fork-Version will use this version instead of trying to determine one.
205
- * @example "1.0.0"
206
- * @default undefined
207
- */
208
- currentVersion: zod.z.string().optional().describe("If set, Fork-Version will use this version instead of trying to determine one."),
209
- /**
210
- * If set, Fork-Version will attempt to update to this version, instead of incrementing using "conventional-commit".
211
- * @example "2.0.0"
212
- * @default undefined
213
- */
214
- nextVersion: zod.z.string().optional().describe(
215
- 'If set, Fork-Version will attempt to update to this version, instead of incrementing using "conventional-commit".'
216
- ),
217
- /**
218
- * Release as increments the version by the specified level. Overrides the default behaviour of "conventional-commit".
219
- * @example "major", "minor", "patch"
220
- * @default undefined
221
- */
222
- releaseAs: zod.z.union([zod.z.literal("major"), zod.z.literal("minor"), zod.z.literal("patch")]).optional().describe(
223
- 'Release as increments the version by the specified level. Overrides the default behaviour of "conventional-commit".'
224
- ),
225
- // Flags
226
- //
227
- /**
228
- * Don't throw an error if multiple versions are found in the given files.
229
- * @default true
230
- */
231
- allowMultipleVersions: zod.z.boolean().describe("Don't throw an error if multiple versions are found in the given files."),
232
- /**
233
- * Commit all changes, not just files updated by Fork-Version.
234
- * @default false
235
- */
236
- commitAll: zod.z.boolean().describe("Commit all changes, not just files updated by Fork-Version."),
237
- /**
238
- * By default the conventional-changelog spec will only add commit types of `feat` and `fix` to the generated changelog.
239
- * If this flag is set, all [default commit types](https://github.com/conventional-changelog/conventional-changelog-config-spec/blob/238093090c14bd7d5151eb5316e635623ce633f9/versions/2.2.0/schema.json#L18)
240
- * will be added to the changelog.
241
- * @default false
242
- */
243
- changelogAll: zod.z.boolean().describe(
244
- "If this flag is set, all default commit types will be added to the changelog, not just `feat` and `fix`."
245
- ),
246
- /**
247
- * Output debug information.
248
- * @default false
249
- */
250
- debug: zod.z.boolean().describe("Output debug information."),
251
- /**
252
- * No output will be written to disk or committed.
253
- * @default false
254
- */
255
- dryRun: zod.z.boolean().describe("No output will be written to disk or committed."),
256
- /**
257
- * Run without logging to the terminal.
258
- * @default false
259
- */
260
- silent: zod.z.boolean().describe("Run without logging to the terminal."),
261
- /**
262
- * If unable to find a version in the given files, fallback and attempt to use the latest git tag.
263
- * @default true
264
- */
265
- gitTagFallback: zod.z.boolean().describe(
266
- "If unable to find a version in the given files, fallback and attempt to use the latest git tag. Defaults to true."
267
- ),
268
- /**
269
- * If true, git will sign the commit with the systems GPG key.
270
- * @see {@link https://git-scm.com/docs/git-commit#Documentation/git-commit.txt--Sltkeyidgt Git - GPG Sign Commits}
271
- * @default false
272
- */
273
- sign: zod.z.boolean().describe("If true, git will sign the commit with the systems GPG key."),
274
- /**
275
- * If true, git will run user defined git hooks before committing.
276
- * @see {@link https://git-scm.com/docs/githooks Git - Git Hooks}
277
- * @default false
278
- */
279
- verify: zod.z.boolean().describe("If true, git will run user defined git hooks before committing."),
280
- /**
281
- * Print inspected output as a parsable json string.
282
- * @default false
283
- */
284
- asJson: zod.z.boolean().describe("Print inspected output as a parsable json string."),
285
- // Skip Steps
286
- //
287
- /**
288
- * Skip the bump step.
289
- * @default false
290
- */
291
- skipBump: zod.z.boolean().describe("Skip the bump step."),
292
- /**
293
- * Skip the changelog step.
294
- * @default false
295
- */
296
- skipChangelog: zod.z.boolean().describe("Skip the changelog step."),
297
- /**
298
- * Skip the commit step.
299
- * @default false
300
- */
301
- skipCommit: zod.z.boolean().describe("Skip the commit step."),
302
- /**
303
- * Skip the tag step.
304
- * @default false
305
- */
306
- skipTag: zod.z.boolean().describe("Skip the tag step."),
307
- // Parser Options
308
- //
309
- /**
310
- * The detected git host:
311
- * - `GitHub`
312
- * - `GitLab`
313
- * - `Bitbucket`
314
- * - `Azure Devops`
315
- * - Or undefined if unknown or not detected.
316
- */
317
- detectedGitHost: zod.z.string().optional().describe(
318
- "The detected git host, such as GitHub, GitLab, Bitbucket, Azure Devops, or undefined if unknown or not detected."
319
- ),
320
- /**
321
- * Override the default "conventional-changelog-conventionalcommits" preset configuration.
322
- */
323
- changelogPresetConfig: ChangelogPresetConfigSchema.partial().optional().describe(
324
- 'Override the default "conventional-changelog-conventionalcommits" preset configuration.'
325
- ),
326
- /**
327
- * Add a suffix to the release commit message.
328
- * @example "[skip ci]"
329
- */
330
- releaseMessageSuffix: zod.z.string().optional().describe("Add a suffix to the release commit message."),
331
- /**
332
- * Options to pass to commits parser.
333
- */
334
- commitParserOptions: zod.z.looseObject().optional().describe("Options to pass to commits parser.")
335
- });
336
-
337
- // src/utils/escape-regex.ts
338
- function escapeRegex(input) {
339
- return input.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
340
- }
341
-
342
- // src/services/git.ts
343
- var Git = class {
344
- #path;
345
- #dryRun;
346
- constructor(config) {
347
- this.#path = config.path;
348
- this.#dryRun = config.dryRun ?? false;
349
- this.add = this.add.bind(this);
350
- this.commit = this.commit.bind(this);
351
- this.tag = this.tag.bind(this);
352
- this.log = this.log.bind(this);
353
- this.isIgnored = this.isIgnored.bind(this);
354
- this.getBranchName = this.getBranchName.bind(this);
355
- this.getRemoteUrl = this.getRemoteUrl.bind(this);
356
- this.getTags = this.getTags.bind(this);
357
- this.getMostRecentTag = this.getMostRecentTag.bind(this);
358
- this.getCommits = this.getCommits.bind(this);
359
- }
360
- async #execGit(command, args) {
361
- return new Promise((onResolve, onReject) => {
362
- child_process.execFile(
363
- "git",
364
- [command, ...args],
365
- {
366
- cwd: this.#path,
367
- maxBuffer: Infinity
368
- },
369
- (error, stdout, stderr) => {
370
- if (error) {
371
- onReject(error);
372
- } else {
373
- onResolve(stdout ? stdout : stderr);
374
- }
375
- }
376
- );
377
- });
378
- }
379
- /**
380
- * Add file contents to the index
381
- *
382
- * [git-add Documentation](https://git-scm.com/docs/git-add)
383
- *
384
- * @example
385
- * ```ts
386
- * await git.add("CHANGELOG.md");
387
- * ```
388
- */
389
- async add(...args) {
390
- if (this.#dryRun) {
391
- return "";
392
- }
393
- return this.#execGit("add", args.filter(Boolean));
394
- }
395
- /**
396
- * Record changes to the repository
397
- *
398
- * [git-commit Documentation](https://git-scm.com/docs/git-commit)
399
- *
400
- * @example
401
- * ```ts
402
- * await git.commit("--message", "chore(release): 1.2.3");
403
- * ```
404
- */
405
- async commit(...args) {
406
- if (this.#dryRun) {
407
- return "";
408
- }
409
- return this.#execGit("commit", args.filter(Boolean));
410
- }
411
- /**
412
- * Create, list, delete or verify a tag object
413
- *
414
- * [git-tag Documentation](https://git-scm.com/docs/git-tag)
415
- *
416
- * @example
417
- * ```ts
418
- * await git.tag("--annotate", "v1.2.3", "--message", "chore(release): 1.2.3");
419
- * ```
420
- */
421
- async tag(...args) {
422
- if (this.#dryRun) {
423
- return "";
424
- }
425
- return this.#execGit("tag", args.filter(Boolean));
426
- }
427
- /**
428
- * Show commit logs
429
- *
430
- * - [git-log Documentation](https://git-scm.com/docs/git-log)
431
- * - [pretty-formats Documentation](https://git-scm.com/docs/pretty-formats)
432
- *
433
- * @example
434
- * ```ts
435
- * await git.log("--oneline");
436
- * ```
437
- */
438
- async log(...args) {
439
- try {
440
- return await this.#execGit("log", args.filter(Boolean));
441
- } catch {
442
- return "";
443
- }
444
- }
445
- /**
446
- * Check if a file is ignored by git
447
- *
448
- * [git-check-ignore Documentation](https://git-scm.com/docs/git-check-ignore)
449
- *
450
- * @example
451
- * ```ts
452
- * await git.isIgnored("src/my-file.txt");
453
- * ```
454
- */
455
- async isIgnored(file) {
456
- try {
457
- await this.#execGit("check-ignore", ["--no-index", file]);
458
- return true;
459
- } catch (_error) {
460
- return false;
461
- }
462
- }
463
- /**
464
- * Get the name of the current branch
465
- *
466
- * [git-rev-parse Documentation](https://git-scm.com/docs/git-rev-parse)
467
- *
468
- * @example
469
- * ```ts
470
- * await git.getBranchName(); // "main"
471
- * ```
472
- */
473
- async getBranchName() {
474
- try {
475
- const branchName = await this.#execGit("rev-parse", ["--abbrev-ref", "HEAD"]);
476
- return branchName.trim();
477
- } catch {
478
- return "";
479
- }
480
- }
481
- /**
482
- * Get the URL of the remote repository
483
- *
484
- * [git-config Documentation](https://git-scm.com/docs/git-config)
485
- *
486
- * @example
487
- * ```ts
488
- * await git.getRemoteUrl(); // "https://github.com/eglavin/fork-version"
489
- * ```
490
- */
491
- async getRemoteUrl() {
492
- try {
493
- const remoteUrl = await this.#execGit("config", ["--get", "remote.origin.url"]);
494
- return remoteUrl.trim();
495
- } catch (_error) {
496
- return "";
497
- }
498
- }
499
- /**
500
- * `getTags` returns valid semver version tags in order of the commit history
501
- *
502
- * Using `git log` to get the commit history, we then parse the tags from the
503
- * commit details which is expected to be in the following format:
504
- * ```txt
505
- * commit 3841b1d05750d42197fe958e3d8e06df378a842d (HEAD -> main, tag: v1.0.2, tag: v1.0.1, tag: v1.0.0)
506
- * Author: Username <username@example.com>
507
- * Date: Sat Nov 9 15:00:00 2024 +0000
508
- *
509
- * chore(release): v1.0.0
510
- * ```
511
- *
512
- * - [Functionality extracted from the conventional-changelog - git-semver-tags project](https://github.com/conventional-changelog/conventional-changelog/blob/fac8045242099c016f5f3905e54e02b7d466bd7b/packages/git-semver-tags/index.js)
513
- * - [conventional-changelog git-semver-tags MIT Licence](https://github.com/conventional-changelog/conventional-changelog/blob/fac8045242099c016f5f3905e54e02b7d466bd7b/packages/git-semver-tags/LICENSE.md)
514
- *
515
- * @example
516
- * ```ts
517
- * await git.getTags("v"); // ["v1.0.2", "v1.0.1", "v1.0.0"]
518
- * ```
519
- */
520
- async getTags(tagPrefix) {
521
- const logOutput = await this.log("--decorate", "--no-color", "--date-order");
522
- const TAG_REGEX = /tag:\s*(?<tag>.+?)[,)]/gi;
523
- const tags = [];
524
- const escapedTagPrefix = tagPrefix ? escapeRegex(tagPrefix) : void 0;
525
- let tagMatch = null;
526
- while (tagMatch = TAG_REGEX.exec(logOutput)) {
527
- const { tag = "" } = tagMatch.groups ?? {};
528
- if (tagPrefix) {
529
- if (tag.startsWith(tagPrefix)) {
530
- const tagWithoutPrefix = tag.replace(new RegExp(`^${escapedTagPrefix}`), "");
531
- if (semver5__default.default.valid(tagWithoutPrefix)) {
532
- tags.push(tag);
533
- }
534
- }
535
- } else if (/^\d/.test(tag) && semver5__default.default.valid(tag)) {
536
- tags.push(tag);
537
- }
538
- }
539
- return tags;
540
- }
541
- /**
542
- * Returns the most recent tag from the commit history, or `undefined` if no valid semver tags are found
543
- *
544
- * @example
545
- * ```ts
546
- * await git.getMostRecentTag("v"); // "1.2.3"
547
- * ```
548
- */
549
- async getMostRecentTag(tagPrefix) {
550
- const tags = await this.getTags(tagPrefix);
551
- return tags[0] || void 0;
552
- }
553
- /**
554
- * Get commit history in a parsable format
555
- *
556
- * An array of strings with commit details is returned in the following format:
557
- * ```txt
558
- * subject
559
- * body
560
- * hash
561
- * committer date
562
- * committer name
563
- * committer email
564
- * ```
565
- *
566
- * @example
567
- * ```ts
568
- * await git.getCommits("v1.0.0", "HEAD", "src/utils");
569
- * ```
570
- */
571
- async getCommits(from = "", to = "HEAD", ...paths) {
572
- const SCISSOR = "^----------- FORK VERSION -----------^";
573
- const LOG_FORMAT = [
574
- "%s",
575
- // subject
576
- "%b",
577
- // body
578
- "%H",
579
- // hash
580
- "%d",
581
- // ref names
582
- "%cI",
583
- // committer date
584
- "%cN",
585
- // committer name
586
- "%cE",
587
- // committer email
588
- SCISSOR
589
- ].join("%n");
590
- const commits = await this.log(
591
- `--format=${LOG_FORMAT}`,
592
- [from, to].filter(Boolean).join(".."),
593
- paths.length ? "--" : "",
594
- ...paths
595
- );
596
- const splitCommits = commits.split(`
597
- ${SCISSOR}
598
- `);
599
- if (splitCommits.length === 0) {
600
- return splitCommits;
601
- }
602
- if (splitCommits[0] === SCISSOR) {
603
- splitCommits.shift();
604
- }
605
- if (splitCommits[splitCommits.length - 1] === "") {
606
- splitCommits.pop();
607
- }
608
- return splitCommits;
609
- }
610
- };
611
- function getChangelogPresetConfig(mergedConfig, cliArguments, detectedChangelogOptions) {
612
- const preset = {
613
- name: "conventionalcommits"
614
- };
615
- if (typeof conventionalChangelogConfigSpec__default.default.properties === "object") {
616
- Object.entries(conventionalChangelogConfigSpec__default.default.properties).forEach(([key, value]) => {
617
- if ("default" in value && value.default !== void 0) {
618
- if (mergedConfig?.changelogAll && key === "types") {
619
- const parsedTypes = zod.z.array(ChangelogPresetConfigTypeSchema).safeParse(value.default);
620
- if (parsedTypes.success) {
621
- parsedTypes.data.forEach((type) => {
622
- if (!type.section) {
623
- delete type.hidden;
624
- type.section = "Other Changes";
625
- }
626
- });
627
- preset[key] = parsedTypes.data;
628
- return;
629
- }
630
- }
631
- preset[key] = value.default;
632
- }
633
- });
634
- }
635
- if (detectedChangelogOptions) {
636
- Object.entries(detectedChangelogOptions).forEach(([key, value]) => {
637
- if (value !== void 0) {
638
- preset[key] = value;
639
- }
640
- });
641
- }
642
- if (mergedConfig?.changelogPresetConfig && typeof mergedConfig.changelogPresetConfig === "object") {
643
- Object.entries(mergedConfig.changelogPresetConfig).forEach(([key, value]) => {
644
- if (value !== void 0) {
645
- preset[key] = value;
646
- }
647
- });
648
- }
649
- if (mergedConfig?.releaseMessageSuffix && !cliArguments?.releaseMessageSuffix) {
650
- preset.releaseCommitMessageFormat = `${preset.releaseCommitMessageFormat} ${mergedConfig.releaseMessageSuffix}`;
651
- }
652
- if (cliArguments?.commitUrlFormat) {
653
- preset.commitUrlFormat = cliArguments.commitUrlFormat;
654
- }
655
- if (cliArguments?.compareUrlFormat) {
656
- preset.compareUrlFormat = cliArguments.compareUrlFormat;
657
- }
658
- if (cliArguments?.issueUrlFormat) {
659
- preset.issueUrlFormat = cliArguments.issueUrlFormat;
660
- }
661
- if (cliArguments?.userUrlFormat) {
662
- preset.userUrlFormat = cliArguments.userUrlFormat;
663
- }
664
- if (cliArguments?.releaseCommitMessageFormat) {
665
- preset.releaseCommitMessageFormat = cliArguments.releaseCommitMessageFormat;
666
- }
667
- if (cliArguments?.releaseMessageSuffix) {
668
- preset.releaseCommitMessageFormat = `${preset.releaseCommitMessageFormat} ${cliArguments.releaseMessageSuffix}`;
669
- }
670
- return ChangelogPresetConfigSchema.passthrough().parse(preset);
671
- }
672
-
673
- // src/config/defaults.ts
674
- var DEFAULT_CONFIG = {
675
- // Commands
676
- command: "main",
677
- // Options
678
- files: [
679
- "package.json",
680
- "package-lock.json",
681
- "npm-shrinkwrap.json",
682
- "jsr.json",
683
- "jsr.jsonc",
684
- "deno.json",
685
- "deno.jsonc",
686
- "manifest.json",
687
- // Chrome extensions
688
- "bower.json"
689
- ],
690
- path: process.cwd(),
691
- changelog: "CHANGELOG.md",
692
- header: `# Changelog
693
-
694
- All notable changes to this project will be documented in this file. See [fork-version](https://github.com/eglavin/fork-version) for commit guidelines.
695
- `,
696
- tagPrefix: "v",
697
- // Flags
698
- allowMultipleVersions: true,
699
- commitAll: false,
700
- changelogAll: false,
701
- debug: false,
702
- dryRun: false,
703
- silent: false,
704
- gitTagFallback: true,
705
- sign: false,
706
- verify: false,
707
- asJson: false,
708
- // Skip Steps
709
- skipBump: false,
710
- skipChangelog: false,
711
- skipCommit: false,
712
- skipTag: false
713
- };
714
-
715
- // src/detect-git-host/host-github.ts
716
- function detectGitHubOptions(remoteUrl) {
717
- let matches = null;
718
- if (/^https:\/\/(.*)?github\.com/.test(remoteUrl)) {
719
- matches = /^https:\/\/(.*)?github\.com\/(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
720
- remoteUrl
721
- );
722
- } else if (remoteUrl.startsWith("git@github.com:")) {
723
- matches = /^git@github\.com:(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
724
- remoteUrl
725
- );
726
- }
727
- if (matches?.groups) {
728
- const { organisation = "", repository = "" } = matches.groups;
729
- return {
730
- hostName: "GitHub",
731
- changelogOptions: {
732
- commitUrlFormat: `https://github.com/${organisation}/${repository}/commit/{{hash}}`,
733
- compareUrlFormat: `https://github.com/${organisation}/${repository}/compare/{{previousTag}}...{{currentTag}}`,
734
- issueUrlFormat: `https://github.com/${organisation}/${repository}/issues/{{id}}`,
735
- issuePrefixes: ["#", "gh-"]
736
- },
737
- commitParserOptions: {
738
- mergePattern: /^Merge pull request #(?<id>\d*) from (?<source>.*)/i,
739
- issuePrefixes: ["#", "gh-"]
740
- }
741
- };
742
- }
743
- return void 0;
744
- }
745
-
746
- // src/detect-git-host/host-gitlab.ts
747
- function detectGitlabOptions(remoteUrl) {
748
- let matches = null;
749
- if (/^https:\/\/(.*)?gitlab\.com/.test(remoteUrl)) {
750
- matches = /^https:\/\/(.*)?gitlab\.com\/(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
751
- remoteUrl
752
- );
753
- } else if (remoteUrl.startsWith("git@gitlab.com:")) {
754
- matches = /^git@gitlab\.com:(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
755
- remoteUrl
756
- );
757
- }
758
- if (matches?.groups) {
759
- const { organisation = "", repository = "" } = matches.groups;
760
- return {
761
- hostName: "GitLab",
762
- changelogOptions: {
763
- commitUrlFormat: `https://gitlab.com/${organisation}/${repository}/-/commit/{{hash}}`,
764
- compareUrlFormat: `https://gitlab.com/${organisation}/${repository}/-/compare/{{previousTag}}...{{currentTag}}`,
765
- issueUrlFormat: `https://gitlab.com/${organisation}/${repository}/-/issues/{{id}}`
766
- },
767
- commitParserOptions: {
768
- mergePattern: /^Merge branch '(?<source>.*)' into '(.*)'/i,
769
- // https://docs.gitlab.com/user/project/issues/managing_issues/#default-closing-pattern
770
- referenceActions: [
771
- "close",
772
- "closes",
773
- "closed",
774
- "closing",
775
- "fix",
776
- "fixes",
777
- "fixed",
778
- "fixing",
779
- "resolve",
780
- "resolves",
781
- "resolved",
782
- "resolving",
783
- "implement",
784
- "implements",
785
- "implemented",
786
- "implementing"
787
- ]
788
- }
789
- };
790
- }
791
- return void 0;
792
- }
793
-
794
- // src/detect-git-host/host-bitbucket.ts
795
- function detectBitbucketOptions(remoteUrl) {
796
- let matches = null;
797
- if (/^https:\/\/(.*)?bitbucket\.(org|com)/.test(remoteUrl)) {
798
- matches = /^https:\/\/(.*)?bitbucket\.(?<domain>org|com)\/(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
799
- remoteUrl
800
- );
801
- } else if (remoteUrl.startsWith("git@bitbucket.org:")) {
802
- matches = /^git@bitbucket\.(?<domain>org|com):(?<organisation>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
803
- remoteUrl
804
- );
805
- }
806
- if (matches?.groups) {
807
- const { domain = "", organisation = "", repository = "" } = matches.groups;
808
- return {
809
- hostName: "Bitbucket",
810
- changelogOptions: {
811
- commitUrlFormat: `https://bitbucket.${domain}/${organisation}/${repository}/commits/{{hash}}`,
812
- compareUrlFormat: `https://bitbucket.${domain}/${organisation}/${repository}/branches/compare/{{currentTag}}..{{previousTag}}`,
813
- // Bitbucket doesn't have a builtin issue tracker like GitHub or GitLab, this should be overridden by the user if they want to link to issues in their changelog.
814
- issueUrlFormat: `https://bitbucket.${domain}/${organisation}/${repository}/issues/{{id}}`
815
- },
816
- commitParserOptions: {
817
- mergePattern: /^Merged in (?<source>.*) \(pull request #(?<id>\d*)\)/i
818
- }
819
- };
820
- }
821
- return void 0;
822
- }
823
-
824
- // src/detect-git-host/host-azure-devops.ts
825
- function detectAzureDevopsOptions(remoteUrl) {
826
- let matches = null;
827
- if (/^https:\/\/(.*)?dev\.azure\.com/.test(remoteUrl)) {
828
- matches = /^https:\/\/(.*)?dev\.azure\.com\/(?<organisation>.*?)\/(?<project>.*?)\/_git\/(?<repository>.*?)(?:\.git)?$/.exec(
829
- remoteUrl
830
- );
831
- } else if (remoteUrl.startsWith("git@ssh.dev.azure.com:")) {
832
- matches = /^git@ssh\.dev\.azure\.com:v\d\/(?<organisation>.*?)\/(?<project>.*?)\/(?<repository>.*?)(?:\.git)?$/.exec(
833
- remoteUrl
834
- );
835
- }
836
- if (matches?.groups) {
837
- const { organisation = "", project = "", repository = "" } = matches.groups;
838
- return {
839
- hostName: "Azure Devops",
840
- changelogOptions: {
841
- commitUrlFormat: `https://dev.azure.com/${organisation}/${project}/_git/${repository}/commit/{{hash}}`,
842
- compareUrlFormat: `https://dev.azure.com/${organisation}/${project}/_git/${repository}/branchCompare?baseVersion=GT{{previousTag}}&targetVersion=GT{{currentTag}}`,
843
- issueUrlFormat: `https://dev.azure.com/${organisation}/${project}/_workitems/edit/{{id}}`
844
- },
845
- commitParserOptions: {
846
- mergePattern: /^Merged PR (?<id>\d*): (?<source>.*)/i
847
- }
848
- };
849
- }
850
- return void 0;
851
- }
852
-
853
- // src/detect-git-host/detect-git-host.ts
854
- async function detectGitHost(path) {
855
- const remoteUrl = await new Git({ path }).getRemoteUrl();
856
- if (remoteUrl.includes("github.com")) {
857
- const githubOptions = detectGitHubOptions(remoteUrl);
858
- if (githubOptions) {
859
- return githubOptions;
860
- }
861
- }
862
- if (remoteUrl.includes("gitlab.com")) {
863
- const gitlabOptions = detectGitlabOptions(remoteUrl);
864
- if (gitlabOptions) {
865
- return gitlabOptions;
866
- }
867
- }
868
- if (/bitbucket\.(org|com)/.test(remoteUrl)) {
869
- const bitbucketOptions = detectBitbucketOptions(remoteUrl);
870
- if (bitbucketOptions) {
871
- return bitbucketOptions;
872
- }
873
- }
874
- if (remoteUrl.includes("dev.azure.com")) {
875
- const azureDevopsOptions = detectAzureDevopsOptions(remoteUrl);
876
- if (azureDevopsOptions) {
877
- return azureDevopsOptions;
878
- }
879
- }
880
- return void 0;
881
- }
882
- var PACKAGE_JSON_CONFIG_KEY = "fork-version";
883
- async function loadConfigFile(cwd) {
884
- const joycon = new JoyCon__default.default({
885
- cwd,
886
- packageKey: PACKAGE_JSON_CONFIG_KEY,
887
- stopDir: path.parse(cwd).root
888
- });
889
- const configFilePath = await joycon.resolve([
890
- "fork.config.ts",
891
- "fork.config.js",
892
- "fork.config.cjs",
893
- "fork.config.mjs",
894
- "fork.config.json",
895
- "package.json"
896
- ]);
897
- if (!configFilePath) {
898
- return {};
899
- }
900
- if (configFilePath.endsWith("json")) {
901
- const fileContent2 = JSON.parse(fs.readFileSync(configFilePath).toString());
902
- if (configFilePath.endsWith("package.json")) {
903
- if (fileContent2[PACKAGE_JSON_CONFIG_KEY] && typeof fileContent2[PACKAGE_JSON_CONFIG_KEY] === "object") {
904
- const parsed3 = ForkConfigSchema.partial().safeParse(fileContent2[PACKAGE_JSON_CONFIG_KEY]);
905
- if (!parsed3.success) {
906
- throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed3.error });
907
- }
908
- return parsed3.data;
909
- }
910
- return {};
911
- }
912
- const parsed2 = ForkConfigSchema.partial().safeParse(fileContent2);
913
- if (!parsed2.success) {
914
- throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed2.error });
915
- }
916
- return parsed2.data;
917
- }
918
- const fileContent = await bundleRequire.bundleRequire({ filepath: configFilePath });
919
- const parsed = ForkConfigSchema.partial().safeParse(fileContent.mod.default || fileContent.mod);
920
- if (!parsed.success) {
921
- throw new Error(`Validation error in: ${configFilePath}`, { cause: parsed.error });
922
- }
923
- return parsed.data;
924
- }
925
-
926
- // src/config/merge-files.ts
927
- function mergeFiles(configFiles, cliFiles, globResults) {
928
- const listOfFiles = /* @__PURE__ */ new Set();
929
- if (Array.isArray(configFiles)) {
930
- configFiles.forEach((file) => listOfFiles.add(file));
931
- }
932
- if (Array.isArray(cliFiles)) {
933
- cliFiles.forEach((file) => listOfFiles.add(file));
934
- }
935
- globResults.forEach((file) => listOfFiles.add(file));
936
- if (listOfFiles.size) {
937
- return Array.from(listOfFiles);
938
- }
939
- return DEFAULT_CONFIG.files;
940
- }
941
-
942
- // src/config/user-config.ts
943
- async function getUserConfig(cliArguments) {
944
- const cwd = cliArguments.flags.path ? path.resolve(cliArguments.flags.path) : process.cwd();
945
- const configFile = await loadConfigFile(cwd);
946
- const mergedConfig = {
947
- ...DEFAULT_CONFIG,
948
- ...configFile,
949
- ...cliArguments.flags
950
- };
951
- const globResults = [];
952
- if (mergedConfig.glob) {
953
- const IGNORE_LIST = /* @__PURE__ */ new Set(["node_modules", ".git"]);
954
- const entries = promises.glob(mergedConfig.glob, {
955
- cwd,
956
- withFileTypes: true,
957
- exclude: (entry) => IGNORE_LIST.has(entry.name)
958
- });
959
- for await (const entry of entries) {
960
- if (entry.isFile()) {
961
- globResults.push(path.join(entry.parentPath, entry.name));
962
- }
963
- }
964
- }
965
- const files = mergeFiles(configFile?.files, cliArguments.flags.files, globResults);
966
- const detectedGitHost = await detectGitHost(cwd);
967
- let command = DEFAULT_CONFIG.command;
968
- if (cliArguments.input.length > 0 && cliArguments.input[0].trim()) {
969
- command = cliArguments.input[0].trim().toLowerCase();
970
- } else if (mergedConfig.command.trim()) {
971
- command = mergedConfig.command.trim().toLowerCase();
972
- }
973
- if (mergedConfig.inspectVersion) {
974
- command = "inspect-version";
975
- }
976
- const shouldBeSilent = ![DEFAULT_CONFIG.command].includes(command);
977
- return {
978
- ...mergedConfig,
979
- command,
980
- files,
981
- path: cwd,
982
- preRelease: (
983
- // Meow doesn't support multiple flags with the same name, so we need to check both.
984
- cliArguments.flags.preReleaseTag ?? cliArguments.flags.preRelease ?? configFile.preRelease
985
- ),
986
- silent: shouldBeSilent || mergedConfig.silent,
987
- detectedGitHost: detectedGitHost?.hostName,
988
- changelogPresetConfig: getChangelogPresetConfig(
989
- mergedConfig,
990
- cliArguments.flags,
991
- detectedGitHost?.changelogOptions
992
- ),
993
- commitParserOptions: {
994
- ...detectedGitHost?.commitParserOptions,
995
- ...mergedConfig.commitParserOptions
996
- }
997
- };
998
- }
999
- var Logger = class {
1000
- #silent;
1001
- #debug;
1002
- constructor(config) {
1003
- this.#silent = config.silent ?? false;
1004
- this.#debug = config.debug ?? false;
1005
- this.log = this.log.bind(this);
1006
- this.warn = this.warn.bind(this);
1007
- this.error = this.error.bind(this);
1008
- this.debug = this.debug.bind(this);
1009
- this.skipping = this.skipping.bind(this);
1010
- }
1011
- log(message) {
1012
- if (!this.#silent) {
1013
- console.log(message);
1014
- }
1015
- }
1016
- warn(message) {
1017
- if (!this.#silent) {
1018
- console.warn(util.styleText("yellowBright", message));
1019
- }
1020
- }
1021
- error(message) {
1022
- if (!this.#silent) {
1023
- console.error(util.styleText("redBright", message));
1024
- }
1025
- }
1026
- debug(message, ...optionalParams) {
1027
- if (!this.#silent && this.#debug) {
1028
- console.debug(util.styleText("cyanBright", message));
1029
- if (optionalParams.length > 0) {
1030
- console.debug(...optionalParams);
1031
- }
1032
- }
1033
- }
1034
- skipping(message) {
1035
- if (!this.#silent) {
1036
- console.log(util.styleText("magenta", message));
1037
- }
1038
- }
1039
- };
1040
- function fileExists(filePath) {
1041
- try {
1042
- return fs.lstatSync(filePath).isFile();
1043
- } catch (_error) {
1044
- return false;
1045
- }
1046
- }
1047
- var JSONPackage = class {
1048
- #logger;
1049
- constructor(logger) {
1050
- this.#logger = logger;
1051
- }
1052
- /** Options for parsing JSON and JSONC files. */
1053
- #jsoncOptions = {
1054
- allowEmptyContent: false,
1055
- allowTrailingComma: true,
1056
- disallowComments: false
1057
- };
1058
- /**
1059
- * Sets a new string value at the given path in a JSON or JSONC string.
1060
- * @param jsonc the JSON or JSONC string (the contents of a file)
1061
- * @param jsonPath path to the value to set, as an array of segments
1062
- * @param newString string to set the value to
1063
- * @returns the JSON or JSONC string with the value set
1064
- */
1065
- #setStringInJsonc(jsonc, jsonPath, newString) {
1066
- const edits = jsoncParser.modify(jsonc, jsonPath, newString, {});
1067
- return jsoncParser.applyEdits(jsonc, edits);
1068
- }
1069
- read(filePath) {
1070
- const fileName = path.basename(filePath);
1071
- const fileContents = fs.readFileSync(filePath, "utf8");
1072
- const parseErrors = [];
1073
- const parsedJson = jsoncParser.parse(fileContents, parseErrors, this.#jsoncOptions);
1074
- if (parsedJson?.version && parseErrors.length === 0) {
1075
- return {
1076
- name: fileName,
1077
- path: filePath,
1078
- version: parsedJson.version,
1079
- isPrivate: typeof parsedJson?.private === "boolean" ? parsedJson.private : true
1080
- };
1081
- }
1082
- this.#logger.warn(`[File Manager] Unable to determine json version: ${fileName}`);
1083
- }
1084
- write(fileState, newVersion) {
1085
- let fileContents = fs.readFileSync(fileState.path, "utf8");
1086
- const parseErrors = [];
1087
- const parsedJson = jsoncParser.parse(fileContents, parseErrors, this.#jsoncOptions);
1088
- fileContents = this.#setStringInJsonc(fileContents, ["version"], newVersion);
1089
- if (parsedJson?.packages?.[""]) {
1090
- fileContents = this.#setStringInJsonc(fileContents, ["packages", "", "version"], newVersion);
1091
- }
1092
- fs.writeFileSync(fileState.path, fileContents, "utf8");
1093
- }
1094
- isSupportedFile(fileName) {
1095
- return fileName.endsWith(".json") || fileName.endsWith(".jsonc");
1096
- }
1097
- };
1098
- var YAMLPackage = class {
1099
- #logger;
1100
- constructor(logger) {
1101
- this.#logger = logger;
1102
- }
1103
- /**
1104
- * If the version is returned with a "+" symbol in the value then the version might be from a
1105
- * flutter `pubspec.yaml` file, if so we want to retain the input builderNumber by splitting it
1106
- * and joining it again later.
1107
- */
1108
- #handleBuildNumber(fileVersion) {
1109
- const [version, builderNumber] = fileVersion.split("+");
1110
- if (/^\d+$/.test(builderNumber)) {
1111
- return {
1112
- version,
1113
- builderNumber
1114
- };
1115
- }
1116
- return {
1117
- version: fileVersion
1118
- };
1119
- }
1120
- read(filePath) {
1121
- const fileName = path.basename(filePath);
1122
- const fileContents = fs.readFileSync(filePath, "utf-8");
1123
- const fileVersion = yaml.parse(fileContents)?.version;
1124
- if (fileVersion) {
1125
- const parsedVersion = this.#handleBuildNumber(fileVersion);
1126
- return {
1127
- name: fileName,
1128
- path: filePath,
1129
- version: parsedVersion.version || "",
1130
- builderNumber: parsedVersion.builderNumber ?? void 0
1131
- };
1132
- }
1133
- this.#logger.warn(`[File Manager] Unable to determine yaml version: ${fileName}`);
1134
- }
1135
- write(fileState, newVersion) {
1136
- const fileContents = fs.readFileSync(fileState.path, "utf8");
1137
- const yamlDocument = yaml.parseDocument(fileContents);
1138
- let newFileVersion = newVersion;
1139
- if (fileState.builderNumber !== void 0) {
1140
- newFileVersion += `+${fileState.builderNumber}`;
1141
- }
1142
- yamlDocument.set("version", newFileVersion);
1143
- fs.writeFileSync(fileState.path, yamlDocument.toString(), "utf8");
1144
- }
1145
- isSupportedFile(fileName) {
1146
- return fileName.endsWith(".yaml") || fileName.endsWith(".yml");
1147
- }
1148
- };
1149
- var PlainText = class {
1150
- #logger;
1151
- constructor(logger) {
1152
- this.#logger = logger;
1153
- }
1154
- read(filePath) {
1155
- const fileName = path.basename(filePath);
1156
- const fileContents = fs.readFileSync(filePath, "utf8").trim();
1157
- if (fileContents) {
1158
- return {
1159
- name: fileName,
1160
- path: filePath,
1161
- version: fileContents
1162
- };
1163
- }
1164
- this.#logger.warn(`[File Manager] Unable to determine plain text version: ${fileName}`);
1165
- }
1166
- write(fileState, newVersion) {
1167
- fs.writeFileSync(fileState.path, newVersion, "utf8");
1168
- }
1169
- isSupportedFile(fileName) {
1170
- return fileName.endsWith("version.txt");
1171
- }
1172
- };
1173
- var MSBuildProject = class {
1174
- #logger;
1175
- constructor(logger) {
1176
- this.#logger = logger;
1177
- }
1178
- #cheerioOptions = {
1179
- xmlMode: true,
1180
- xml: { decodeEntities: false }
1181
- };
1182
- read(filePath) {
1183
- const fileName = path.basename(filePath);
1184
- const fileContents = fs.readFileSync(filePath, "utf8");
1185
- const $ = cheerio__namespace.load(fileContents, this.#cheerioOptions);
1186
- const version = $("Project > PropertyGroup > Version").text();
1187
- if (version) {
1188
- return {
1189
- name: fileName,
1190
- path: filePath,
1191
- version
1192
- };
1193
- }
1194
- this.#logger.warn(`[File Manager] Unable to determine ms-build version: ${fileName}`);
1195
- }
1196
- write(fileState, newVersion) {
1197
- const fileContents = fs.readFileSync(fileState.path, "utf8");
1198
- const $ = cheerio__namespace.load(fileContents, this.#cheerioOptions);
1199
- $("Project > PropertyGroup > Version").text(newVersion);
1200
- const updatedContent = $.xml().replaceAll('"/>', '" />');
1201
- fs.writeFileSync(fileState.path, updatedContent, "utf8");
1202
- }
1203
- isSupportedFile(fileName) {
1204
- return [".csproj", ".dbproj", ".esproj", ".fsproj", ".props", ".vbproj", ".vcxproj"].findIndex(
1205
- (ext) => fileName.endsWith(ext)
1206
- ) !== -1;
1207
- }
1208
- };
1209
- var ARMBicep = class {
1210
- #logger;
1211
- constructor(logger) {
1212
- this.#logger = logger;
1213
- }
1214
- /** https://regex101.com/r/Lriphb/2 */
1215
- #metadataRegex = /(metadata contentVersion *= *['"])(?<version>[^'"]+)(['"])/;
1216
- /** https://regex101.com/r/iKCTF9/1 */
1217
- #varRegex = /(var contentVersion(?: string)? *= *['"])(?<version>[^'"]+)(['"])/;
1218
- read(filePath) {
1219
- const fileName = path.basename(filePath);
1220
- const fileContents = fs.readFileSync(filePath, "utf8");
1221
- const metadataMatch = this.#metadataRegex.exec(fileContents);
1222
- const varMatch = this.#varRegex.exec(fileContents);
1223
- if (metadataMatch?.groups?.version && varMatch?.groups?.version) {
1224
- return {
1225
- name: fileName,
1226
- path: filePath,
1227
- version: metadataMatch.groups.version
1228
- };
1229
- }
1230
- if (!metadataMatch) {
1231
- this.#logger.warn(
1232
- `[File Manager] Missing 'metadata contentVersion' in bicep file: ${fileName}`
1233
- );
1234
- }
1235
- if (!varMatch) {
1236
- this.#logger.warn(`[File Manager] Missing 'var contentVersion' in bicep file: ${fileName}`);
1237
- }
1238
- }
1239
- write(fileState, newVersion) {
1240
- const fileContents = fs.readFileSync(fileState.path, "utf8");
1241
- const updatedContent = fileContents.replace(this.#metadataRegex, `$1${newVersion}$3`).replace(this.#varRegex, `$1${newVersion}$3`);
1242
- fs.writeFileSync(fileState.path, updatedContent, "utf8");
1243
- }
1244
- isSupportedFile(fileName) {
1245
- return fileName.endsWith(".bicep");
1246
- }
1247
- };
1248
- var InstallShieldISM = class {
1249
- #logger;
1250
- constructor(logger) {
1251
- this.#logger = logger;
1252
- }
1253
- #cheerioOptions = {
1254
- xmlMode: true,
1255
- xml: { decodeEntities: false }
1256
- };
1257
- read(filePath) {
1258
- const fileName = path.basename(filePath);
1259
- const fileContents = fs.readFileSync(filePath, "utf8");
1260
- const $ = cheerio__namespace.load(fileContents, this.#cheerioOptions);
1261
- const version = $('msi > table[name="Property"] > row > td:contains("ProductVersion")').next().text().trim();
1262
- if (version) {
1263
- return {
1264
- name: fileName,
1265
- path: filePath,
1266
- version
1267
- };
1268
- }
1269
- this.#logger.warn(`[File Manager] Unable to determine InstallShield ISM version: ${fileName}`);
1270
- }
1271
- write(fileState, newVersion) {
1272
- const fileContents = fs.readFileSync(fileState.path, "utf8");
1273
- const $ = cheerio__namespace.load(fileContents, this.#cheerioOptions);
1274
- const versionCell = $(
1275
- 'msi > table[name="Property"] > row > td:contains("ProductVersion")'
1276
- ).next();
1277
- if (versionCell.length > 0) {
1278
- versionCell.text(newVersion);
1279
- }
1280
- const updatedContent = $.xml().replaceAll('"/>', '" />');
1281
- fs.writeFileSync(fileState.path, updatedContent, "utf8");
1282
- }
1283
- isSupportedFile(fileName) {
1284
- return fileName.endsWith(".ism");
1285
- }
1286
- };
1287
-
1288
- // src/files/file-manager.ts
1289
- var FileManager = class {
1290
- #config;
1291
- #logger;
1292
- #fileManagers = [];
1293
- constructor(config, logger) {
1294
- this.#config = config;
1295
- this.#logger = logger;
1296
- this.#fileManagers = [
1297
- new JSONPackage(logger),
1298
- new YAMLPackage(logger),
1299
- new PlainText(logger),
1300
- new MSBuildProject(logger),
1301
- new ARMBicep(logger),
1302
- new InstallShieldISM(logger)
1303
- ];
1304
- }
1305
- /**
1306
- * Get the state from the given file name.
1307
- *
1308
- * @example
1309
- * ```ts
1310
- * fileManager.read("package.json");
1311
- * ```
1312
- *
1313
- * @returns
1314
- * ```json
1315
- * { "name": "package.json", "path": "/path/to/package.json", "version": "1.2.3", "isPrivate": true }
1316
- * ```
1317
- */
1318
- read(pathOrName) {
1319
- const _fileName = pathOrName.toLowerCase();
1320
- const filePath = path.isAbsolute(pathOrName) ? pathOrName : path.resolve(this.#config.path, pathOrName);
1321
- if (!fileExists(filePath)) return;
1322
- for (const fileManager of this.#fileManagers) {
1323
- if (fileManager.isSupportedFile(_fileName)) {
1324
- return fileManager.read(filePath);
1325
- }
1326
- }
1327
- this.#logger.error(`[File Manager] Unsupported file: ${pathOrName}`);
1328
- }
1329
- /**
1330
- * Write the new version to the given file.
1331
- *
1332
- * @example
1333
- * ```ts
1334
- * fileManager.write(
1335
- * { name: "package.json", path: "/path/to/package.json", version: "1.2.2" },
1336
- * "1.2.3"
1337
- * );
1338
- * ```
1339
- */
1340
- write(fileState, newVersion) {
1341
- if (this.#config.dryRun) {
1342
- return;
1343
- }
1344
- const _fileName = fileState.name.toLowerCase();
1345
- for (const fileManager of this.#fileManagers) {
1346
- if (fileManager.isSupportedFile(_fileName)) {
1347
- return fileManager.write(fileState, newVersion);
1348
- }
1349
- }
1350
- this.#logger.error(`[File Manager] Unsupported file: ${fileState.path}`);
1351
- }
1352
- };
1353
-
1354
- // src/commands/validate-config.ts
1355
- function validateConfig(config) {
1356
- console.log(`
1357
- \u2699\uFE0F Configuration:
1358
- ${JSON.stringify(config, null, 2)}
1359
-
1360
- \u2705 Configuration is valid.
1361
- `);
1362
- }
1363
-
1364
- // src/utils/parse-regexp-string.ts
1365
- function parseRegExpString(input) {
1366
- const literal = /^\/(.+)\/([a-z]*)$/i.exec(input);
1367
- if (literal) {
1368
- try {
1369
- return new RegExp(literal[1], literal[2]);
1370
- } catch {
1371
- return null;
1372
- }
1373
- }
1374
- try {
1375
- return new RegExp(input);
1376
- } catch {
1377
- return null;
1378
- }
1379
- }
1380
-
1381
- // src/utils/trim-string-array.ts
1382
- function trimStringArray(array, transformFn) {
1383
- const items = [];
1384
- if (Array.isArray(array)) {
1385
- for (const item of array) {
1386
- const _item = item.trim();
1387
- if (_item) {
1388
- items.push(transformFn ? transformFn(_item) : _item);
1389
- }
1390
- }
1391
- }
1392
- if (items.length === 0) {
1393
- return void 0;
1394
- }
1395
- return items;
1396
- }
1397
-
1398
- // src/commit-parser/options.ts
1399
- function createDefaultParserOptions(userOptions) {
1400
- const referenceActions = trimStringArray(userOptions?.referenceActions, escapeRegex) ?? [
1401
- "close",
1402
- "closes",
1403
- "closed",
1404
- "fix",
1405
- "fixes",
1406
- "fixed",
1407
- "resolve",
1408
- "resolves",
1409
- "resolved"
1410
- ];
1411
- const joinedReferenceActions = referenceActions.join("|");
1412
- const issuePrefixes = trimStringArray(userOptions?.issuePrefixes, escapeRegex) ?? ["#"];
1413
- const joinedIssuePrefixes = issuePrefixes.join("|");
1414
- const noteKeywords = trimStringArray(userOptions?.noteKeywords, escapeRegex) ?? [
1415
- "BREAKING CHANGE",
1416
- "BREAKING-CHANGE"
1417
- ];
1418
- const joinedNoteKeywords = noteKeywords.join("|");
1419
- return {
1420
- subjectPattern: /^(?<type>\w+)(?:\((?<scope>.*)\))?(?<breakingChange>!)?:\s+(?<title>.*)/i,
1421
- mergePattern: /^Merge pull request #(?<id>\d*) from (?<source>.*)/i,
1422
- revertPattern: /^[Rr]evert "(?<subject>.*)"(\s*This reverts commit (?<hash>[a-zA-Z0-9]*)\.)?/,
1423
- commentPattern: /^#(?!\d+\s)/,
1424
- mentionPattern: /(?<!\w)@(?<username>[\w-]+)/,
1425
- referenceActions,
1426
- referenceActionPattern: joinedReferenceActions ? new RegExp(
1427
- `(?<action>${joinedReferenceActions})(?:\\s+(?<reference>.*?))(?=(?:${joinedReferenceActions})|$)`
1428
- ) : void 0,
1429
- issuePrefixes,
1430
- issuePattern: joinedIssuePrefixes ? new RegExp(
1431
- `(?:.*?)??\\s*(?<repository>[\\w-\\.\\/]*?)??(?<prefix>${joinedIssuePrefixes})(?<issue>[\\w-]*\\d+)`
1432
- ) : void 0,
1433
- noteKeywords,
1434
- notePattern: joinedNoteKeywords ? new RegExp(`^(?<title>${joinedNoteKeywords}):(\\s*(?<text>.*))`) : void 0
1435
- };
1436
- }
1437
- function createParserOptions(userOptions) {
1438
- const initialOptions = createDefaultParserOptions(userOptions);
1439
- if (userOptions) {
1440
- for (const key of Object.keys(userOptions)) {
1441
- const userValue = userOptions[key];
1442
- if (!(key in initialOptions)) {
1443
- continue;
1444
- }
1445
- if (Array.isArray(initialOptions[key])) {
1446
- continue;
1447
- }
1448
- if (initialOptions[key] instanceof RegExp) {
1449
- if (userValue instanceof RegExp) {
1450
- initialOptions[key] = userValue;
1451
- } else if (typeof userValue === "string") {
1452
- const parsed = parseRegExpString(userValue);
1453
- if (parsed) {
1454
- initialOptions[key] = parsed;
1455
- }
1456
- } else if (userValue == void 0) {
1457
- initialOptions[key] = void 0;
1458
- }
1459
- }
1460
- }
1461
- }
1462
- return initialOptions;
1463
- }
1464
-
1465
- // src/commit-parser/parser-error.ts
1466
- var ParserError = class extends Error {
1467
- detail;
1468
- constructor(message, detail) {
1469
- super(message);
1470
- this.name = "ParserError";
1471
- this.detail = detail;
1472
- }
1473
- };
1474
-
1475
- // src/commit-parser/commit-parser.ts
1476
- var CommitParser = class {
1477
- #options;
1478
- #logger;
1479
- constructor(userOptions) {
1480
- this.#options = createParserOptions(userOptions);
1481
- this.setLogger = this.setLogger.bind(this);
1482
- this.createCommit = this.createCommit.bind(this);
1483
- this.parseRawCommit = this.parseRawCommit.bind(this);
1484
- this.parseSubject = this.parseSubject.bind(this);
1485
- this.parseMerge = this.parseMerge.bind(this);
1486
- this.parseRevert = this.parseRevert.bind(this);
1487
- this.parseMentions = this.parseMentions.bind(this);
1488
- this.parseReferenceParts = this.parseReferenceParts.bind(this);
1489
- this.parseReferences = this.parseReferences.bind(this);
1490
- this.parseNotes = this.parseNotes.bind(this);
1491
- this.parseRawLines = this.parseRawLines.bind(this);
1492
- this.parse = this.parse.bind(this);
1493
- }
1494
- setLogger(logger) {
1495
- this.#logger = logger;
1496
- return this;
1497
- }
1498
- createCommit() {
1499
- return {
1500
- raw: "",
1501
- subject: "",
1502
- body: "",
1503
- hash: "",
1504
- refNames: "",
1505
- date: "",
1506
- name: "",
1507
- email: "",
1508
- type: "",
1509
- scope: "",
1510
- breakingChange: "",
1511
- title: "",
1512
- merge: null,
1513
- revert: null,
1514
- notes: [],
1515
- mentions: [],
1516
- references: [],
1517
- tags: []
1518
- };
1519
- }
1520
- /**
1521
- * Parse the raw commit message into its expected parts
1522
- * - subject
1523
- * - body
1524
- * - hash
1525
- * - date
1526
- * - name
1527
- * - email
1528
- *
1529
- * @throws {ParserError}
1530
- */
1531
- parseRawCommit(rawCommit) {
1532
- const parsedCommit = this.createCommit();
1533
- const parts = rawCommit.split(/\r?\n/);
1534
- if (parts.length < 6) {
1535
- throw new ParserError("Commit doesn't contain enough parts", rawCommit);
1536
- }
1537
- const email = parts.pop();
1538
- const name = parts.pop();
1539
- const date = parts.pop();
1540
- const refNames = parts.pop();
1541
- const hash = parts.pop();
1542
- if (email) parsedCommit.email = email.trim();
1543
- if (name) parsedCommit.name = name.trim();
1544
- if (date) {
1545
- parsedCommit.date = date.trim();
1546
- if (Number.isNaN(Date.parse(parsedCommit.date))) {
1547
- throw new ParserError("Unable to parse commit date", rawCommit);
1548
- }
1549
- }
1550
- if (refNames) {
1551
- parsedCommit.refNames = refNames.trim();
1552
- const TAG_REGEX = /tag:\s*(?<tag>.+?)[,)]/gi;
1553
- let tagMatch = null;
1554
- while (tagMatch = TAG_REGEX.exec(refNames)) {
1555
- const { tag = "" } = tagMatch.groups ?? {};
1556
- if (tag) {
1557
- parsedCommit.tags.push(tag);
1558
- }
1559
- }
1560
- }
1561
- if (hash) parsedCommit.hash = hash.trim();
1562
- const subject = parts.shift()?.trimStart();
1563
- if (subject) {
1564
- parsedCommit.subject = subject;
1565
- parsedCommit.raw = subject;
1566
- }
1567
- parsedCommit.body = parts.filter((line) => {
1568
- if (this.#options.commentPattern) {
1569
- return !this.#options.commentPattern.test(line.trim());
1570
- }
1571
- return true;
1572
- }).join("\n").trim();
1573
- const raw = parts.join("\n").trim();
1574
- if (raw) parsedCommit.raw += "\n" + raw;
1575
- return parsedCommit;
1576
- }
1577
- /**
1578
- * Parse the commit subject into its expected parts
1579
- * - type
1580
- * - scope (optional)
1581
- * - breaking change (optional)
1582
- * - title
1583
- *
1584
- * @throws {ParserError}
1585
- */
1586
- parseSubject(commit) {
1587
- if (!this.#options.subjectPattern) return false;
1588
- const subjectMatch = this.#options.subjectPattern.exec(commit.subject);
1589
- if (subjectMatch?.groups) {
1590
- const { type = "", scope = "", breakingChange = "", title = "" } = subjectMatch.groups;
1591
- if (!type || !title) {
1592
- throw new ParserError("Unable to parse commit subject", commit);
1593
- }
1594
- commit.type = type;
1595
- commit.scope = scope;
1596
- if (breakingChange) commit.breakingChange = breakingChange;
1597
- commit.title = title;
1598
- return true;
1599
- }
1600
- return false;
1601
- }
1602
- /**
1603
- * Parse merge information from the commit subject
1604
- * @example
1605
- * ```txt
1606
- * "Merge pull request #123 from fork-version/feature"
1607
- * ```
1608
- */
1609
- parseMerge(commit) {
1610
- if (!this.#options.mergePattern) return false;
1611
- const mergeMatch = this.#options.mergePattern.exec(commit.subject);
1612
- if (mergeMatch?.groups) {
1613
- const { id = "", source = "" } = mergeMatch.groups;
1614
- commit.merge = {
1615
- id,
1616
- source
1617
- };
1618
- return true;
1619
- }
1620
- return false;
1621
- }
1622
- /**
1623
- * Parse revert information from the commit body
1624
- * @example
1625
- * ```txt
1626
- * "Revert "feat: initial commit"
1627
- *
1628
- * This reverts commit 4a79e9e546b4020d2882b7810dc549fa71960f4f."
1629
- * ```
1630
- */
1631
- parseRevert(commit) {
1632
- if (!this.#options.revertPattern) return false;
1633
- const revertMatch = this.#options.revertPattern.exec(commit.raw);
1634
- if (revertMatch?.groups) {
1635
- const { hash = "", subject = "" } = revertMatch.groups;
1636
- commit.revert = {
1637
- hash,
1638
- subject
1639
- };
1640
- return true;
1641
- }
1642
- return false;
1643
- }
1644
- /**
1645
- * Search for mentions from the commit line
1646
- * @example
1647
- * ```txt
1648
- * "@fork-version"
1649
- * ```
1650
- */
1651
- parseMentions(line, outMentions) {
1652
- if (!this.#options.mentionPattern) return false;
1653
- const mentionRegex = new RegExp(this.#options.mentionPattern, "g");
1654
- let foundMention = false;
1655
- let mentionMatch;
1656
- while (mentionMatch = mentionRegex.exec(line)) {
1657
- if (!mentionMatch) {
1658
- break;
1659
- }
1660
- const { username = "" } = mentionMatch.groups ?? {};
1661
- outMentions.add(username);
1662
- foundMention = true;
1663
- }
1664
- return foundMention;
1665
- }
1666
- /**
1667
- * Search for references from the commit line
1668
- * @example
1669
- * ```txt
1670
- * "#1234"
1671
- * "owner/repo#1234"
1672
- * ```
1673
- */
1674
- parseReferenceParts(referenceText, action) {
1675
- if (!this.#options.issuePattern) return void 0;
1676
- const references = [];
1677
- const issueRegex = new RegExp(this.#options.issuePattern, "gi");
1678
- let issueMatch;
1679
- while (issueMatch = issueRegex.exec(referenceText)) {
1680
- if (!issueMatch) {
1681
- break;
1682
- }
1683
- const { repository = "", prefix = "", issue = "" } = issueMatch.groups ?? {};
1684
- const reference = {
1685
- prefix,
1686
- issue,
1687
- action,
1688
- owner: null,
1689
- repository: null
1690
- };
1691
- if (repository) {
1692
- const slashIndex = repository.indexOf("/");
1693
- if (slashIndex !== -1) {
1694
- reference.owner = repository.slice(0, slashIndex);
1695
- reference.repository = repository.slice(slashIndex + 1);
1696
- } else {
1697
- reference.repository = repository;
1698
- }
1699
- }
1700
- references.push(reference);
1701
- }
1702
- if (references.length > 0) {
1703
- return references;
1704
- }
1705
- return void 0;
1706
- }
1707
- /**
1708
- * Search for actions and references from the commit line
1709
- * @example
1710
- * ```txt
1711
- * "Closes #1234"
1712
- * "fixes owner/repo#1234"
1713
- * ```
1714
- */
1715
- parseReferences(line, outReferences) {
1716
- if (!this.#options.referenceActionPattern || !this.#options.issuePattern) return false;
1717
- const referenceActionRegex = new RegExp(this.#options.referenceActionPattern, "gi").test(line) ? new RegExp(this.#options.referenceActionPattern, "gi") : /(?<reference>.*)/g;
1718
- let foundReference = false;
1719
- let referenceActionMatch;
1720
- while (referenceActionMatch = referenceActionRegex.exec(line)) {
1721
- if (!referenceActionMatch) {
1722
- break;
1723
- }
1724
- const { action = "", reference = "" } = referenceActionMatch.groups ?? {};
1725
- const parsedReferences = this.parseReferenceParts(reference, action || null);
1726
- if (!parsedReferences) {
1727
- break;
1728
- }
1729
- for (const ref of parsedReferences) {
1730
- if (!outReferences.some((r) => r.prefix === ref.prefix && r.issue === ref.issue)) {
1731
- outReferences.push(ref);
1732
- }
1733
- }
1734
- foundReference = true;
1735
- }
1736
- return foundReference;
1737
- }
1738
- /**
1739
- * Search for notes from the commit line
1740
- * @example
1741
- * ```txt
1742
- * "BREAKING CHANGE: this is a breaking change"
1743
- * ```
1744
- */
1745
- parseNotes(line, outNotes) {
1746
- if (!this.#options.notePattern) return false;
1747
- const noteMatch = new RegExp(this.#options.notePattern, "ig").exec(line);
1748
- if (noteMatch?.groups) {
1749
- const { title = "", text = "" } = noteMatch.groups;
1750
- outNotes.push({
1751
- title,
1752
- text
1753
- });
1754
- return true;
1755
- }
1756
- return false;
1757
- }
1758
- /**
1759
- * Parse the raw commit for mentions, references and notes
1760
- */
1761
- parseRawLines(commit) {
1762
- const mentions = /* @__PURE__ */ new Set();
1763
- const references = [];
1764
- const notes = [];
1765
- let lastNoteLine = -1;
1766
- const splitMessage = commit.raw.split("\n");
1767
- for (let index = 0; index < splitMessage.length; index++) {
1768
- const line = splitMessage[index];
1769
- const trimmedLine = line.trim();
1770
- if (this.#options.commentPattern?.test(trimmedLine)) {
1771
- continue;
1772
- }
1773
- this.parseMentions(trimmedLine, mentions);
1774
- const foundReference = this.parseReferences(trimmedLine, references);
1775
- if (foundReference) {
1776
- lastNoteLine = -1;
1777
- continue;
1778
- }
1779
- if (this.parseNotes(trimmedLine, notes)) {
1780
- lastNoteLine = index;
1781
- } else if (lastNoteLine !== -1) {
1782
- notes[notes.length - 1].text += `
1783
- ${line}`;
1784
- lastNoteLine = index;
1785
- }
1786
- }
1787
- if (mentions.size > 0) {
1788
- commit.mentions = Array.from(mentions);
1789
- }
1790
- if (references.length > 0) {
1791
- commit.references = references;
1792
- }
1793
- if (notes.length > 0) {
1794
- commit.notes = notes.map((note) => ({
1795
- ...note,
1796
- text: note.text.trim()
1797
- }));
1798
- }
1799
- }
1800
- /**
1801
- * Parse a commit log with the following format separated by new line characters:
1802
- * ```txt
1803
- * refactor: add test file
1804
- * Add a test file to the project
1805
- * 4ef2c86d393a9660aa9f753144256b1f200c16bd
1806
- * 2024-12-22T17:36:50Z
1807
- * Fork Version
1808
- * fork-version@example.com
1809
- * ```
1810
- *
1811
- * @example
1812
- * ```ts
1813
- * parse("refactor: add test file\nAdd a test file to the project\n4ef2c86d393a9660aa9f753144256b1f200c16bd\n2024-12-22T17:36:50Z\nFork Version\nfork-version@example.com");
1814
- * ```
1815
- *
1816
- * The expected input value can be generated by running the following command:
1817
- * ```sh
1818
- * git log --format="%s%n%b%n%H%n%cI%n%cN%n%cE%n"
1819
- * ```
1820
- * @see {@link https://git-scm.com/docs/pretty-formats|Git Pretty Format Documentation}
1821
- */
1822
- parse(rawCommit) {
1823
- try {
1824
- const commit = this.parseRawCommit(rawCommit);
1825
- this.parseSubject(commit);
1826
- this.parseMerge(commit);
1827
- this.parseRevert(commit);
1828
- this.parseRawLines(commit);
1829
- return commit;
1830
- } catch (error) {
1831
- if (this.#logger) {
1832
- this.#logger.debug("[Commit Parser] Failed to parse commit", { error });
1833
- }
1834
- return void 0;
1835
- }
1836
- }
1837
- };
1838
-
1839
- // src/commit-parser/filter-reverted-commits.ts
1840
- function filterRevertedCommits(parsedCommits) {
1841
- const revertedCommits = [];
1842
- for (const commit of parsedCommits) {
1843
- if (!commit.revert) continue;
1844
- if (revertedCommits.some(
1845
- (r) => r.revert?.hash === commit.hash || r.revert?.subject === commit.subject
1846
- )) {
1847
- continue;
1848
- }
1849
- revertedCommits.push(commit);
1850
- }
1851
- if (revertedCommits.length === 0) {
1852
- return parsedCommits;
1853
- }
1854
- const commitsWithoutReverts = [];
1855
- for (const commit of parsedCommits) {
1856
- if (commit.revert) continue;
1857
- const revertedIndex = revertedCommits.findIndex(
1858
- (r) => r.revert?.hash === commit.hash || r.revert?.subject === commit.subject
1859
- );
1860
- if (revertedIndex !== -1) {
1861
- revertedCommits.splice(revertedIndex, 1);
1862
- continue;
1863
- }
1864
- commitsWithoutReverts.push(commit);
1865
- }
1866
- return commitsWithoutReverts;
1867
- }
1868
- function cleanTag(tag, tagPrefix) {
1869
- if (!tag) return void 0;
1870
- const escapedTagPrefix = tagPrefix ? escapeRegex(tagPrefix) : void 0;
1871
- const tagWithoutPrefix = escapedTagPrefix ? tag.replace(new RegExp(`^${escapedTagPrefix}`), "") : tag;
1872
- return semver5__default.default.clean(tagWithoutPrefix) ?? void 0;
1873
- }
1874
-
1875
- // src/process/get-commits.ts
1876
- async function getCommitsSinceTag(config, logger, git) {
1877
- const commitParser = new CommitParser(config.commitParserOptions);
1878
- if (config.debug) commitParser.setLogger(logger);
1879
- const latestTag = await git.getMostRecentTag(config.tagPrefix);
1880
- if (!latestTag) {
1881
- logger.warn("No previous tag found, using all commits");
1882
- }
1883
- const foundCommits = await git.getCommits(latestTag, "HEAD");
1884
- const commits = foundCommits.reduce((acc, commit) => {
1885
- const parsed = commitParser.parse(commit);
1886
- if (parsed) {
1887
- acc.push(parsed);
1888
- }
1889
- return acc;
1890
- }, []);
1891
- const filteredCommits = filterRevertedCommits(commits);
1892
- logger.debug(
1893
- `Found ${foundCommits.length} commits since tag: ${latestTag ?? "none"} (${commits.length} parsed, ${filteredCommits.length} after filtering reverts)`
1894
- );
1895
- return {
1896
- latestTag,
1897
- latestTagVersion: cleanTag(latestTag, config.tagPrefix),
1898
- commits: filteredCommits
1899
- };
1900
- }
1901
- async function getCurrentVersion(config, logger, git, fileManager, filesToUpdate, latestTagVersion) {
1902
- const files = [];
1903
- const versions = /* @__PURE__ */ new Set();
1904
- for (const file of filesToUpdate) {
1905
- if (await git.isIgnored(file)) {
1906
- logger.debug(`[Git Ignored] ${file}`);
1907
- continue;
1908
- }
1909
- const fileState = fileManager.read(file);
1910
- if (fileState) {
1911
- files.push(fileState);
1912
- if (!config.currentVersion) {
1913
- versions.add(fileState.version);
1914
- }
1915
- }
1916
- }
1917
- if (config.currentVersion) {
1918
- versions.add(config.currentVersion);
1919
- }
1920
- if (versions.size === 0 && config.gitTagFallback && latestTagVersion) {
1921
- logger.warn(`Using latest git tag as fallback`);
1922
- versions.add(latestTagVersion);
1923
- }
1924
- if (versions.size === 0) {
1925
- throw new Error("Unable to find current version");
1926
- } else if (versions.size > 1) {
1927
- if (!config.allowMultipleVersions) {
1928
- throw new Error("Found multiple versions");
1929
- }
1930
- logger.warn(
1931
- `Found multiple versions (${Array.from(versions).join(", ")}), using the higher semver version`
1932
- );
1933
- }
1934
- const currentVersion = semver5__default.default.rsort(Array.from(versions))[0];
1935
- logger.log(`Current version: ${currentVersion}`);
1936
- return {
1937
- files,
1938
- version: currentVersion
1939
- };
1940
- }
1941
- async function inspect(config, logger, fileManager, git) {
1942
- let latestTag = "";
1943
- let latestVersion = "";
1944
- try {
1945
- const commits = await getCommitsSinceTag(config, logger, git);
1946
- if (commits.latestTag) {
1947
- latestTag = commits.latestTag;
1948
- latestVersion = commits.latestTagVersion ?? "";
1949
- }
1950
- const currentVersion = await getCurrentVersion(
1951
- config,
1952
- logger,
1953
- git,
1954
- fileManager,
1955
- config.files,
1956
- latestVersion
1957
- );
1958
- if (currentVersion.version) {
1959
- latestVersion = currentVersion.version;
1960
- }
1961
- } catch {
1962
- }
1963
- if (!latestVersion && !latestTag) {
1964
- console.error(
1965
- util.styleText(
1966
- "yellowBright",
1967
- "No version found. Make sure you have at least one tag in your repository."
1968
- )
1969
- );
1970
- process.exit(1);
1971
- return;
1972
- }
1973
- switch (config.command) {
1974
- case "inspect-version": {
1975
- console.log(
1976
- config.asJson ? JSON.stringify(
1977
- {
1978
- version: latestVersion
1979
- },
1980
- null,
1981
- 2
1982
- ) : latestVersion
1983
- );
1984
- return;
1985
- }
1986
- case "inspect-tag": {
1987
- console.log(
1988
- config.asJson ? JSON.stringify(
1989
- {
1990
- tag: latestTag
1991
- },
1992
- null,
1993
- 2
1994
- ) : latestTag
1995
- );
1996
- return;
1997
- }
1998
- default: {
1999
- console.log(
2000
- config.asJson ? JSON.stringify(
2001
- {
2002
- version: latestVersion,
2003
- tag: latestTag
2004
- },
2005
- null,
2006
- 2
2007
- ) : `
2008
- Version: ${latestVersion}
2009
- Tag: ${latestTag}
2010
- `.trim()
2011
- );
2012
- return;
2013
- }
2014
- }
2015
- }
2016
- function getPriority(type) {
2017
- return ["patch", "minor", "major"].indexOf(type ?? "");
2018
- }
2019
- function getVersionType(version) {
2020
- const parseVersion = semver5__default.default.parse(version);
2021
- if (parseVersion?.major) {
2022
- return "major";
2023
- } else if (parseVersion?.minor) {
2024
- return "minor";
2025
- } else if (parseVersion?.patch) {
2026
- return "patch";
2027
- }
2028
- return void 0;
2029
- }
2030
- function getReleaseType(releaseType, currentVersion, preReleaseTag) {
2031
- if (!preReleaseTag) {
2032
- return releaseType;
2033
- }
2034
- const currentVersionsIsPreRelease = Array.isArray(semver5__default.default.prerelease(currentVersion));
2035
- if (currentVersionsIsPreRelease) {
2036
- const currentReleaseType = getVersionType(currentVersion);
2037
- if (currentReleaseType === releaseType || getPriority(currentReleaseType) > getPriority(releaseType)) {
2038
- return "prerelease";
2039
- }
2040
- }
2041
- return `pre${releaseType}`;
2042
- }
2043
-
2044
- // src/process/get-next-version.ts
2045
- async function getNextVersion(config, logger, commits, currentVersion) {
2046
- if (config.skipBump) {
2047
- logger.skipping(`Skipping bump, using ${currentVersion} as the next version`);
2048
- return {
2049
- version: currentVersion
2050
- };
2051
- }
2052
- if (config.nextVersion) {
2053
- if (!semver5__default.default.valid(config.nextVersion)) {
2054
- throw new Error(`Invalid Version: ${config.nextVersion}`);
2055
- }
2056
- logger.log(`Next version: ${config.nextVersion}`);
2057
- return {
2058
- version: config.nextVersion
2059
- };
2060
- }
2061
- const isPreMajor = semver5__default.default.lt(currentVersion, "1.0.0");
2062
- let releaseType = "patch";
2063
- const changes = {
2064
- major: 0,
2065
- minor: 0,
2066
- patch: 0,
2067
- merges: 0,
2068
- reverts: 0
2069
- };
2070
- if (config.releaseAs) {
2071
- releaseType = config.releaseAs;
2072
- } else {
2073
- let level = 2;
2074
- const MINOR_TYPES = ["feat", "feature"];
2075
- for (const commit of commits) {
2076
- if (commit.merge) {
2077
- changes.merges += 1;
2078
- continue;
2079
- }
2080
- if (commit.revert) {
2081
- changes.reverts += 1;
2082
- continue;
2083
- }
2084
- if (commit.notes.length > 0 || commit.breakingChange) {
2085
- changes.major += commit.notes.length + (commit.breakingChange ? 1 : 0);
2086
- level = 0;
2087
- } else if (MINOR_TYPES.includes(commit.type.toLowerCase())) {
2088
- changes.minor += 1;
2089
- if (level === 2) {
2090
- level = 1;
2091
- }
2092
- } else {
2093
- changes.patch += 1;
2094
- }
2095
- }
2096
- if (isPreMajor && level < 2) {
2097
- level++;
2098
- changes.patch += changes.minor;
2099
- changes.minor = changes.major;
2100
- changes.major = 0;
2101
- }
2102
- if (level === 0) {
2103
- releaseType = "major";
2104
- } else if (level === 1) {
2105
- releaseType = "minor";
2106
- } else {
2107
- releaseType = "patch";
2108
- }
2109
- }
2110
- const releaseTypeOrPreRelease = getReleaseType(releaseType, currentVersion, config.preRelease);
2111
- const nextVersion = semver5__default.default.inc(
2112
- currentVersion,
2113
- releaseTypeOrPreRelease,
2114
- typeof config.preRelease === "string" ? config.preRelease : ""
2115
- ) ?? "";
2116
- logger.log(`Next version: ${nextVersion} (${releaseTypeOrPreRelease})`);
2117
- if (commits.length > 0) {
2118
- logger.log(
2119
- ` - Commits: ${commits.length}` + (changes.major > 0 ? `, Majors: ${changes.major}` : "") + (changes.minor > 0 ? `, Minors: ${changes.minor}` : "") + (changes.patch > 0 ? `, Patches: ${changes.patch}` : "") + (changes.reverts > 0 ? `, Reverts: ${changes.reverts}` : "") + (changes.merges > 0 ? `, Merges: ${changes.merges}` : "")
2120
- );
2121
- } else {
2122
- logger.log(" - No commits found.");
2123
- }
2124
- return {
2125
- version: nextVersion,
2126
- releaseType: releaseTypeOrPreRelease,
2127
- preMajor: isPreMajor,
2128
- changes
2129
- };
2130
- }
2131
- var RELEASE_PATTERN = /(^#+ \[?[0-9]+\.[0-9]+\.[0-9]+|<a name=)/m;
2132
- function getOldReleaseContent(filePath, exists) {
2133
- if (exists) {
2134
- const fileContents = fs.readFileSync(filePath, "utf-8");
2135
- const oldContentStart = fileContents.search(RELEASE_PATTERN);
2136
- if (oldContentStart !== -1) {
2137
- return fileContents.substring(oldContentStart);
2138
- }
2139
- }
2140
- return "";
2141
- }
2142
- function getNewReleaseContent(config, logger, nextVersion) {
2143
- return new Promise((onResolve) => {
2144
- let newContent = "";
2145
- conventionalChangelog__default.default(
2146
- {
2147
- preset: {
2148
- name: "conventionalcommits",
2149
- ...config.changelogPresetConfig
2150
- },
2151
- tagPrefix: config.tagPrefix,
2152
- warn: (...message) => logger.debug("[conventional-changelog] ", ...message),
2153
- cwd: config.path
2154
- },
2155
- {
2156
- version: nextVersion
2157
- },
2158
- {
2159
- merges: null,
2160
- path: config.path
2161
- }
2162
- ).on("error", (cause) => {
2163
- throw new Error("[conventional-changelog] Unable to parse changes", { cause });
2164
- }).on("data", (chunk) => {
2165
- newContent += chunk.toString();
2166
- }).on("end", () => {
2167
- onResolve(newContent);
2168
- });
2169
- });
2170
- }
2171
- async function updateChangelog(config, logger, nextVersion) {
2172
- if (config.skipChangelog) {
2173
- logger.skipping("Skipping changelog update");
2174
- return;
2175
- }
2176
- if (config.header.search(RELEASE_PATTERN) !== -1) {
2177
- throw new Error("Header cannot contain release pattern");
2178
- }
2179
- const changelogPath = path.resolve(config.path, config.changelog);
2180
- if (!config.dryRun && !fileExists(changelogPath)) {
2181
- logger.log(`Creating changelog: ${changelogPath}`);
2182
- fs.writeFileSync(changelogPath, "\n", "utf8");
2183
- } else {
2184
- logger.log(`Updating changelog: ${changelogPath}`);
2185
- }
2186
- const oldContent = getOldReleaseContent(changelogPath, fileExists(changelogPath));
2187
- const newContent = await getNewReleaseContent(config, logger, nextVersion);
2188
- if (!config.dryRun && newContent) {
2189
- fs.writeFileSync(
2190
- changelogPath,
2191
- `${config.header}
2192
- ${newContent}
2193
- ${oldContent}
2194
- `.trim(),
2195
- "utf8"
2196
- );
2197
- }
2198
- }
2199
-
2200
- // src/utils/format-commit-message.ts
2201
- function formatCommitMessage(message, version) {
2202
- if (!message) {
2203
- message = "chore(release): {{currentTag}}";
2204
- }
2205
- return message.replace(new RegExp("{{currentTag}}", "g"), version);
2206
- }
2207
-
2208
- // src/process/commit.ts
2209
- async function commitChanges(config, logger, git, files, nextVersion) {
2210
- if (config.skipCommit) {
2211
- logger.skipping("Skipping commit");
2212
- return;
2213
- }
2214
- logger.log("Committing changes");
2215
- const filesToCommit = [];
2216
- if (fileExists(path.resolve(config.path, config.changelog))) {
2217
- filesToCommit.push(path.resolve(config.path, config.changelog));
2218
- }
2219
- for (const file of files) {
2220
- filesToCommit.push(file.path);
2221
- }
2222
- if (filesToCommit.length === 0) {
2223
- return;
2224
- }
2225
- if (config.commitAll) {
2226
- await git.add("--all");
2227
- } else {
2228
- await git.add(...filesToCommit);
2229
- }
2230
- const shouldVerify = config.verify ? void 0 : "--no-verify";
2231
- const shouldSign = config.sign ? "--gpg-sign" : "--no-gpg-sign";
2232
- await git.commit(
2233
- shouldVerify,
2234
- shouldSign,
2235
- "--message",
2236
- formatCommitMessage(config.changelogPresetConfig?.releaseCommitMessageFormat, nextVersion)
2237
- );
2238
- }
2239
-
2240
- // src/process/tag.ts
2241
- async function tagChanges(config, logger, git, nextVersion) {
2242
- if (config.skipTag) {
2243
- logger.skipping("Skipping tag creation");
2244
- return;
2245
- }
2246
- const tag = `${config.tagPrefix}${nextVersion}`;
2247
- logger.log(`Creating tag: ${tag}`);
2248
- const shouldSign = config.sign ? "--sign" : "--no-sign";
2249
- await git.tag(
2250
- shouldSign,
2251
- "--annotate",
2252
- tag,
2253
- "--message",
2254
- formatCommitMessage(config.changelogPresetConfig?.releaseCommitMessageFormat, nextVersion)
2255
- );
2256
- }
2257
-
2258
- // src/commands/main.ts
2259
- async function main(config, logger, fileManager, git) {
2260
- logger.log(`Running fork-version - ${(/* @__PURE__ */ new Date()).toUTCString()}`);
2261
- logger.warn(config.dryRun ? "[Dry Run] No changes will be written to disk.\n" : "");
2262
- const commits = await getCommitsSinceTag(config, logger, git);
2263
- const current = await getCurrentVersion(
2264
- config,
2265
- logger,
2266
- git,
2267
- fileManager,
2268
- config.files,
2269
- commits.latestTagVersion
2270
- );
2271
- const next = await getNextVersion(config, logger, commits.commits, current.version);
2272
- logger.log("Updating files: ");
2273
- for (const outFile of current.files) {
2274
- logger.log(` - ${outFile.path}`);
2275
- fileManager.write(outFile, next.version);
2276
- }
2277
- await updateChangelog(config, logger, next.version);
2278
- await commitChanges(config, logger, git, current.files, next.version);
2279
- await tagChanges(config, logger, git, next.version);
2280
- return {
2281
- config,
2282
- commits,
2283
- current,
2284
- next
2285
- };
2286
- }
2287
-
2288
- exports.CommitParser = CommitParser;
2289
- exports.FileManager = FileManager;
2290
- exports.ForkConfigSchema = ForkConfigSchema;
2291
- exports.Git = Git;
2292
- exports.Logger = Logger;
2293
- exports.commitChanges = commitChanges;
2294
- exports.createParserOptions = createParserOptions;
2295
- exports.filterRevertedCommits = filterRevertedCommits;
2296
- exports.getCommitsSinceTag = getCommitsSinceTag;
2297
- exports.getCurrentVersion = getCurrentVersion;
2298
- exports.getNextVersion = getNextVersion;
2299
- exports.getUserConfig = getUserConfig;
2300
- exports.inspect = inspect;
2301
- exports.main = main;
2302
- exports.tagChanges = tagChanges;
2303
- exports.updateChangelog = updateChangelog;
2304
- exports.validateConfig = validateConfig;
2305
- //# sourceMappingURL=chunk-33WIJWQZ.cjs.map
2306
- //# sourceMappingURL=chunk-33WIJWQZ.cjs.map