@releasekit/version 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,2442 @@
1
+ import {
2
+ BaseVersionError,
3
+ ReleaseKitError
4
+ } from "./chunk-2MN2VLZF.js";
5
+ import {
6
+ execAsync,
7
+ execSync
8
+ } from "./chunk-LMPZV35Z.js";
9
+
10
+ // ../config/dist/index.js
11
+ import * as fs from "fs";
12
+ import * as path from "path";
13
+ import * as TOML from "smol-toml";
14
+ import * as fs3 from "fs";
15
+ import * as path3 from "path";
16
+ import { z as z2 } from "zod";
17
+ import { z } from "zod";
18
+ import * as fs2 from "fs";
19
+ import * as os from "os";
20
+ import * as path2 from "path";
21
+ function parseCargoToml(cargoPath) {
22
+ const content = fs.readFileSync(cargoPath, "utf-8");
23
+ return TOML.parse(content);
24
+ }
25
+ function isCargoToml(filePath) {
26
+ return path.basename(filePath) === "Cargo.toml";
27
+ }
28
+ var ConfigError = class extends ReleaseKitError {
29
+ code = "CONFIG_ERROR";
30
+ suggestions;
31
+ constructor(message, suggestions) {
32
+ super(message);
33
+ this.suggestions = suggestions ?? [
34
+ "Check that releasekit.config.json exists and is valid JSON",
35
+ "Run with --verbose for more details"
36
+ ];
37
+ }
38
+ };
39
+ var MAX_JSONC_LENGTH = 1e5;
40
+ function parseJsonc(content) {
41
+ if (content.length > MAX_JSONC_LENGTH) {
42
+ throw new Error(`JSONC content too long: ${content.length} characters (max ${MAX_JSONC_LENGTH})`);
43
+ }
44
+ try {
45
+ return JSON.parse(content);
46
+ } catch {
47
+ const cleaned = content.replace(/\/\/[^\r\n]{0,10000}$/gm, "").replace(/\/\*[\s\S]{0,50000}?\*\//g, "").trim();
48
+ return JSON.parse(cleaned);
49
+ }
50
+ }
51
+ var GitConfigSchema = z.object({
52
+ remote: z.string().default("origin"),
53
+ branch: z.string().default("main"),
54
+ pushMethod: z.enum(["auto", "ssh", "https"]).default("auto"),
55
+ /**
56
+ * Optional env var name containing a GitHub token for HTTPS pushes.
57
+ * When set, publish steps can use this token without mutating git remotes.
58
+ */
59
+ httpsTokenEnv: z.string().optional(),
60
+ push: z.boolean().optional(),
61
+ skipHooks: z.boolean().optional()
62
+ });
63
+ var MonorepoConfigSchema = z.object({
64
+ mode: z.enum(["root", "packages", "both"]).optional(),
65
+ rootPath: z.string().optional(),
66
+ packagesPath: z.string().optional(),
67
+ mainPackage: z.string().optional()
68
+ });
69
+ var BranchPatternSchema = z.object({
70
+ pattern: z.string(),
71
+ releaseType: z.enum(["major", "minor", "patch", "prerelease"])
72
+ });
73
+ var VersionCargoConfigSchema = z.object({
74
+ enabled: z.boolean().default(true),
75
+ paths: z.array(z.string()).optional()
76
+ });
77
+ var VersionConfigSchema = z.object({
78
+ tagTemplate: z.string().default("v{version}"),
79
+ packageSpecificTags: z.boolean().default(false),
80
+ preset: z.string().default("conventional"),
81
+ sync: z.boolean().default(true),
82
+ packages: z.array(z.string()).default([]),
83
+ mainPackage: z.string().optional(),
84
+ updateInternalDependencies: z.enum(["major", "minor", "patch", "no-internal-update"]).default("minor"),
85
+ skip: z.array(z.string()).optional(),
86
+ commitMessage: z.string().optional(),
87
+ versionStrategy: z.enum(["branchPattern", "commitMessage"]).default("commitMessage"),
88
+ branchPatterns: z.array(BranchPatternSchema).optional(),
89
+ defaultReleaseType: z.enum(["major", "minor", "patch", "prerelease"]).optional(),
90
+ mismatchStrategy: z.enum(["error", "warn", "ignore", "prefer-package", "prefer-git"]).default("warn"),
91
+ versionPrefix: z.string().default(""),
92
+ prereleaseIdentifier: z.string().optional(),
93
+ strictReachable: z.boolean().default(false),
94
+ cargo: VersionCargoConfigSchema.optional()
95
+ });
96
+ var NpmConfigSchema = z.object({
97
+ enabled: z.boolean().default(true),
98
+ auth: z.enum(["auto", "oidc", "token"]).default("auto"),
99
+ provenance: z.boolean().default(true),
100
+ access: z.enum(["public", "restricted"]).default("public"),
101
+ registry: z.string().default("https://registry.npmjs.org"),
102
+ copyFiles: z.array(z.string()).default(["LICENSE"]),
103
+ tag: z.string().default("latest")
104
+ });
105
+ var CargoPublishConfigSchema = z.object({
106
+ enabled: z.boolean().default(false),
107
+ noVerify: z.boolean().default(false),
108
+ publishOrder: z.array(z.string()).default([]),
109
+ clean: z.boolean().default(false)
110
+ });
111
+ var PublishGitConfigSchema = z.object({
112
+ push: z.boolean().default(true),
113
+ pushMethod: z.enum(["auto", "ssh", "https"]).optional(),
114
+ remote: z.string().optional(),
115
+ branch: z.string().optional(),
116
+ httpsTokenEnv: z.string().optional(),
117
+ skipHooks: z.boolean().optional()
118
+ });
119
+ var GitHubReleaseConfigSchema = z.object({
120
+ enabled: z.boolean().default(true),
121
+ draft: z.boolean().default(true),
122
+ perPackage: z.boolean().default(true),
123
+ prerelease: z.union([z.literal("auto"), z.boolean()]).default("auto"),
124
+ /**
125
+ * Controls the source for the GitHub release body.
126
+ * - 'auto': Use release notes if enabled, else changelog, else GitHub auto-generated.
127
+ * - 'releaseNotes': Use LLM-generated release notes (requires notes.releaseNotes.enabled: true).
128
+ * - 'changelog': Use formatted changelog entries.
129
+ * - 'generated': Use GitHub's auto-generated notes.
130
+ * - 'none': No body.
131
+ */
132
+ body: z.enum(["auto", "releaseNotes", "changelog", "generated", "none"]).default("auto")
133
+ });
134
+ var VerifyRegistryConfigSchema = z.object({
135
+ enabled: z.boolean().default(true),
136
+ maxAttempts: z.number().int().positive().default(5),
137
+ initialDelay: z.number().int().positive().default(15e3),
138
+ backoffMultiplier: z.number().positive().default(2)
139
+ });
140
+ var VerifyConfigSchema = z.object({
141
+ npm: VerifyRegistryConfigSchema.default({
142
+ enabled: true,
143
+ maxAttempts: 5,
144
+ initialDelay: 15e3,
145
+ backoffMultiplier: 2
146
+ }),
147
+ cargo: VerifyRegistryConfigSchema.default({
148
+ enabled: true,
149
+ maxAttempts: 10,
150
+ initialDelay: 3e4,
151
+ backoffMultiplier: 2
152
+ })
153
+ });
154
+ var PublishConfigSchema = z.object({
155
+ git: PublishGitConfigSchema.optional(),
156
+ npm: NpmConfigSchema.default({
157
+ enabled: true,
158
+ auth: "auto",
159
+ provenance: true,
160
+ access: "public",
161
+ registry: "https://registry.npmjs.org",
162
+ copyFiles: ["LICENSE"],
163
+ tag: "latest"
164
+ }),
165
+ cargo: CargoPublishConfigSchema.default({
166
+ enabled: false,
167
+ noVerify: false,
168
+ publishOrder: [],
169
+ clean: false
170
+ }),
171
+ githubRelease: GitHubReleaseConfigSchema.default({
172
+ enabled: true,
173
+ draft: true,
174
+ perPackage: true,
175
+ prerelease: "auto",
176
+ body: "auto"
177
+ }),
178
+ verify: VerifyConfigSchema.default({
179
+ npm: {
180
+ enabled: true,
181
+ maxAttempts: 5,
182
+ initialDelay: 15e3,
183
+ backoffMultiplier: 2
184
+ },
185
+ cargo: {
186
+ enabled: true,
187
+ maxAttempts: 10,
188
+ initialDelay: 3e4,
189
+ backoffMultiplier: 2
190
+ }
191
+ })
192
+ });
193
+ var TemplateConfigSchema = z.object({
194
+ path: z.string().optional(),
195
+ engine: z.enum(["handlebars", "liquid", "ejs"]).optional()
196
+ });
197
+ var LocationModeSchema = z.enum(["root", "packages", "both"]);
198
+ var ChangelogConfigSchema = z.object({
199
+ mode: LocationModeSchema.optional(),
200
+ file: z.string().optional(),
201
+ templates: TemplateConfigSchema.optional()
202
+ });
203
+ var LLMOptionsSchema = z.object({
204
+ timeout: z.number().optional(),
205
+ maxTokens: z.number().optional(),
206
+ temperature: z.number().optional()
207
+ });
208
+ var LLMRetryConfigSchema = z.object({
209
+ maxAttempts: z.number().int().positive().optional(),
210
+ initialDelay: z.number().nonnegative().optional(),
211
+ maxDelay: z.number().positive().optional(),
212
+ backoffFactor: z.number().positive().optional()
213
+ });
214
+ var LLMTasksConfigSchema = z.object({
215
+ summarize: z.boolean().optional(),
216
+ enhance: z.boolean().optional(),
217
+ categorize: z.boolean().optional(),
218
+ releaseNotes: z.boolean().optional()
219
+ });
220
+ var LLMCategorySchema = z.object({
221
+ name: z.string(),
222
+ description: z.string(),
223
+ scopes: z.array(z.string()).optional()
224
+ });
225
+ var ScopeRulesSchema = z.object({
226
+ allowed: z.array(z.string()).optional(),
227
+ caseSensitive: z.boolean().default(false),
228
+ invalidScopeAction: z.enum(["remove", "keep", "fallback"]).default("remove"),
229
+ fallbackScope: z.string().optional()
230
+ });
231
+ var ScopeConfigSchema = z.object({
232
+ mode: z.enum(["restricted", "packages", "none", "unrestricted"]).default("unrestricted"),
233
+ rules: ScopeRulesSchema.optional()
234
+ });
235
+ var LLMPromptOverridesSchema = z.object({
236
+ enhance: z.string().optional(),
237
+ categorize: z.string().optional(),
238
+ enhanceAndCategorize: z.string().optional(),
239
+ summarize: z.string().optional(),
240
+ releaseNotes: z.string().optional()
241
+ });
242
+ var LLMPromptsConfigSchema = z.object({
243
+ instructions: LLMPromptOverridesSchema.optional(),
244
+ templates: LLMPromptOverridesSchema.optional()
245
+ });
246
+ var LLMConfigSchema = z.object({
247
+ provider: z.string(),
248
+ model: z.string(),
249
+ baseURL: z.string().optional(),
250
+ apiKey: z.string().optional(),
251
+ options: LLMOptionsSchema.optional(),
252
+ concurrency: z.number().int().positive().optional(),
253
+ retry: LLMRetryConfigSchema.optional(),
254
+ tasks: LLMTasksConfigSchema.optional(),
255
+ categories: z.array(LLMCategorySchema).optional(),
256
+ style: z.string().optional(),
257
+ scopes: ScopeConfigSchema.optional(),
258
+ prompts: LLMPromptsConfigSchema.optional()
259
+ });
260
+ var ReleaseNotesConfigSchema = z.object({
261
+ mode: LocationModeSchema.optional(),
262
+ file: z.string().optional(),
263
+ templates: TemplateConfigSchema.optional(),
264
+ llm: LLMConfigSchema.optional()
265
+ });
266
+ var NotesInputConfigSchema = z.object({
267
+ source: z.string().optional(),
268
+ file: z.string().optional()
269
+ });
270
+ var NotesConfigSchema = z.object({
271
+ changelog: z.union([z.literal(false), ChangelogConfigSchema]).optional(),
272
+ releaseNotes: z.union([z.literal(false), ReleaseNotesConfigSchema]).optional(),
273
+ updateStrategy: z.enum(["prepend", "regenerate"]).optional()
274
+ });
275
+ var CILabelsConfigSchema = z.object({
276
+ stable: z.string().default("release:stable"),
277
+ prerelease: z.string().default("release:prerelease"),
278
+ skip: z.string().default("release:skip"),
279
+ major: z.string().default("release:major"),
280
+ minor: z.string().default("release:minor"),
281
+ patch: z.string().default("release:patch")
282
+ });
283
+ var CIConfigSchema = z.object({
284
+ releaseStrategy: z.enum(["manual", "direct", "standing-pr", "scheduled"]).default("direct"),
285
+ releaseTrigger: z.enum(["commit", "label"]).default("label"),
286
+ prPreview: z.boolean().default(true),
287
+ autoRelease: z.boolean().default(false),
288
+ /**
289
+ * Commit message prefixes that should not trigger a release.
290
+ * Defaults to `['chore: release ']` to match the release commit template
291
+ * (`chore: release ${packageName} v${version}`) and provide a
292
+ * secondary loop-prevention guard alongside `[skip ci]`.
293
+ */
294
+ skipPatterns: z.array(z.string()).default(["chore: release "]),
295
+ minChanges: z.number().int().positive().default(1),
296
+ labels: CILabelsConfigSchema.default({
297
+ stable: "release:stable",
298
+ prerelease: "release:prerelease",
299
+ skip: "release:skip",
300
+ major: "release:major",
301
+ minor: "release:minor",
302
+ patch: "release:patch"
303
+ })
304
+ });
305
+ var ReleaseCIConfigSchema = z.object({
306
+ skipPatterns: z.array(z.string().min(1)).optional(),
307
+ minChanges: z.number().int().positive().optional(),
308
+ /** Set to `false` to disable GitHub release creation in CI. */
309
+ githubRelease: z.literal(false).optional(),
310
+ /** Set to `false` to disable changelog generation in CI. */
311
+ notes: z.literal(false).optional()
312
+ });
313
+ var ReleaseConfigSchema = z.object({
314
+ /**
315
+ * Optional steps to enable. The version step always runs; only 'notes' and
316
+ * 'publish' can be opted out. Omitting a step is equivalent to --skip-<step>.
317
+ */
318
+ steps: z.array(z.enum(["notes", "publish"])).min(1).optional(),
319
+ ci: ReleaseCIConfigSchema.optional()
320
+ });
321
+ var ReleaseKitConfigSchema = z.object({
322
+ git: GitConfigSchema.optional(),
323
+ monorepo: MonorepoConfigSchema.optional(),
324
+ version: VersionConfigSchema.optional(),
325
+ publish: PublishConfigSchema.optional(),
326
+ notes: NotesConfigSchema.optional(),
327
+ ci: CIConfigSchema.optional(),
328
+ release: ReleaseConfigSchema.optional()
329
+ });
330
+ var MAX_INPUT_LENGTH = 1e4;
331
+ function substituteVariables(value) {
332
+ if (value.length > MAX_INPUT_LENGTH) {
333
+ throw new Error(`Input too long: ${value.length} characters (max ${MAX_INPUT_LENGTH})`);
334
+ }
335
+ const envPattern = /\{env:([^}]{1,1000})\}/g;
336
+ const filePattern = /\{file:([^}]{1,1000})\}/g;
337
+ let result = value;
338
+ result = result.replace(envPattern, (_, varName) => {
339
+ return process.env[varName] ?? "";
340
+ });
341
+ result = result.replace(filePattern, (_, filePath) => {
342
+ const expandedPath = filePath.startsWith("~") ? path2.join(os.homedir(), filePath.slice(1)) : filePath;
343
+ try {
344
+ return fs2.readFileSync(expandedPath, "utf-8").trim();
345
+ } catch {
346
+ return "";
347
+ }
348
+ });
349
+ return result;
350
+ }
351
+ var SOLE_REFERENCE_PATTERN = /^\{(?:env|file):[^}]+\}$/;
352
+ function substituteInObject(obj) {
353
+ if (typeof obj === "string") {
354
+ const result = substituteVariables(obj);
355
+ if (result === "" && SOLE_REFERENCE_PATTERN.test(obj)) {
356
+ return void 0;
357
+ }
358
+ return result;
359
+ }
360
+ if (Array.isArray(obj)) {
361
+ return obj.map((item) => substituteInObject(item));
362
+ }
363
+ if (obj && typeof obj === "object") {
364
+ const result = {};
365
+ for (const [key, value] of Object.entries(obj)) {
366
+ result[key] = substituteInObject(value);
367
+ }
368
+ return result;
369
+ }
370
+ return obj;
371
+ }
372
+ var AUTH_DIR = path2.join(os.homedir(), ".config", "releasekit");
373
+ var AUTH_FILE = path2.join(AUTH_DIR, "auth.json");
374
+ var CONFIG_FILE = "releasekit.config.json";
375
+ function loadConfigFile(configPath) {
376
+ if (!fs3.existsSync(configPath)) {
377
+ return {};
378
+ }
379
+ try {
380
+ const content = fs3.readFileSync(configPath, "utf-8");
381
+ const parsed = parseJsonc(content);
382
+ const substituted = substituteInObject(parsed);
383
+ return ReleaseKitConfigSchema.parse(substituted);
384
+ } catch (error) {
385
+ if (error instanceof z2.ZodError) {
386
+ const issues = error.issues.map((i) => ` ${i.path.join(".")}: ${i.message}`).join("\n");
387
+ throw new ConfigError(`Config validation errors:
388
+ ${issues}`);
389
+ }
390
+ if (error instanceof SyntaxError) {
391
+ throw new ConfigError(`Invalid JSON in config file: ${error.message}`);
392
+ }
393
+ throw error;
394
+ }
395
+ }
396
+ function loadConfig(options) {
397
+ const cwd3 = options?.cwd ?? process.cwd();
398
+ const configPath = options?.configPath ?? path3.join(cwd3, CONFIG_FILE);
399
+ return loadConfigFile(configPath);
400
+ }
401
+
402
+ // src/types.ts
403
+ function toVersionConfig(config, gitConfig) {
404
+ if (!config) {
405
+ return {
406
+ tagTemplate: "v{version}",
407
+ packageSpecificTags: false,
408
+ preset: "conventional",
409
+ sync: true,
410
+ packages: [],
411
+ updateInternalDependencies: "minor",
412
+ versionPrefix: "",
413
+ baseBranch: gitConfig?.branch
414
+ };
415
+ }
416
+ return {
417
+ tagTemplate: config.tagTemplate ?? "v{version}",
418
+ packageSpecificTags: config.packageSpecificTags,
419
+ preset: config.preset ?? "conventional",
420
+ sync: config.sync ?? true,
421
+ packages: config.packages ?? [],
422
+ mainPackage: config.mainPackage,
423
+ updateInternalDependencies: config.updateInternalDependencies ?? "minor",
424
+ skip: config.skip,
425
+ commitMessage: config.commitMessage,
426
+ versionStrategy: config.versionStrategy,
427
+ branchPatterns: config.branchPatterns?.map((bp) => ({
428
+ pattern: bp.pattern,
429
+ releaseType: bp.releaseType
430
+ })),
431
+ defaultReleaseType: config.defaultReleaseType,
432
+ mismatchStrategy: config.mismatchStrategy,
433
+ versionPrefix: config.versionPrefix ?? "",
434
+ prereleaseIdentifier: config.prereleaseIdentifier,
435
+ baseBranch: gitConfig?.branch,
436
+ cargo: config.cargo
437
+ };
438
+ }
439
+
440
+ // src/config.ts
441
+ function loadConfig2(options) {
442
+ const fullConfig = loadConfig(options);
443
+ return toVersionConfig(fullConfig.version, fullConfig.git);
444
+ }
445
+
446
+ // src/errors/versionError.ts
447
+ var VersionError = class extends BaseVersionError {
448
+ };
449
+ var VersionErrorCode = /* @__PURE__ */ ((VersionErrorCode2) => {
450
+ VersionErrorCode2["CONFIG_REQUIRED"] = "CONFIG_REQUIRED";
451
+ VersionErrorCode2["PACKAGES_NOT_FOUND"] = "PACKAGES_NOT_FOUND";
452
+ VersionErrorCode2["WORKSPACE_ERROR"] = "WORKSPACE_ERROR";
453
+ VersionErrorCode2["INVALID_CONFIG"] = "INVALID_CONFIG";
454
+ VersionErrorCode2["PACKAGE_NOT_FOUND"] = "PACKAGE_NOT_FOUND";
455
+ VersionErrorCode2["VERSION_CALCULATION_ERROR"] = "VERSION_CALCULATION_ERROR";
456
+ return VersionErrorCode2;
457
+ })(VersionErrorCode || {});
458
+ function createVersionError(code, details) {
459
+ const messages = {
460
+ ["CONFIG_REQUIRED" /* CONFIG_REQUIRED */]: "Configuration is required",
461
+ ["PACKAGES_NOT_FOUND" /* PACKAGES_NOT_FOUND */]: "Failed to get packages information",
462
+ ["WORKSPACE_ERROR" /* WORKSPACE_ERROR */]: "Failed to get workspace packages",
463
+ ["INVALID_CONFIG" /* INVALID_CONFIG */]: "Invalid configuration",
464
+ ["PACKAGE_NOT_FOUND" /* PACKAGE_NOT_FOUND */]: "Package not found",
465
+ ["VERSION_CALCULATION_ERROR" /* VERSION_CALCULATION_ERROR */]: "Failed to calculate version"
466
+ };
467
+ const suggestions = {
468
+ ["CONFIG_REQUIRED" /* CONFIG_REQUIRED */]: [
469
+ "Create a version.config.json file in your project root",
470
+ "Check the documentation for configuration examples"
471
+ ],
472
+ ["PACKAGES_NOT_FOUND" /* PACKAGES_NOT_FOUND */]: [
473
+ "Ensure package.json or Cargo.toml files exist in your project",
474
+ "Check workspace configuration (pnpm-workspace.yaml, etc.)",
475
+ "Verify file permissions and paths"
476
+ ],
477
+ ["WORKSPACE_ERROR" /* WORKSPACE_ERROR */]: [
478
+ "Verify workspace configuration files are valid",
479
+ "Check that workspace packages are accessible",
480
+ "Ensure proper monorepo structure"
481
+ ],
482
+ ["INVALID_CONFIG" /* INVALID_CONFIG */]: [
483
+ "Validate version.config.json syntax",
484
+ "Check configuration against schema",
485
+ "Review documentation for valid configuration options"
486
+ ],
487
+ ["PACKAGE_NOT_FOUND" /* PACKAGE_NOT_FOUND */]: [
488
+ "Verify package name spelling and case",
489
+ "Check if package exists in workspace",
490
+ "Review packages configuration in version.config.json"
491
+ ],
492
+ ["VERSION_CALCULATION_ERROR" /* VERSION_CALCULATION_ERROR */]: [
493
+ "Ensure git repository has commits",
494
+ "Check conventional commit message format",
495
+ "Verify git tags are properly formatted"
496
+ ]
497
+ };
498
+ const baseMessage = messages[code];
499
+ const fullMessage = details ? `${baseMessage}: ${details}` : baseMessage;
500
+ return new VersionError(fullMessage, code, suggestions[code]);
501
+ }
502
+
503
+ // src/utils/jsonOutput.ts
504
+ import fs4 from "fs";
505
+ var _jsonOutputMode = false;
506
+ var _pendingWrites = [];
507
+ var _jsonData = {
508
+ dryRun: false,
509
+ updates: [],
510
+ changelogs: [],
511
+ sharedEntries: void 0,
512
+ tags: []
513
+ };
514
+ function enableJsonOutput(dryRun = false) {
515
+ _jsonOutputMode = true;
516
+ _jsonData.dryRun = dryRun;
517
+ _jsonData.updates = [];
518
+ _jsonData.changelogs = [];
519
+ _jsonData.sharedEntries = void 0;
520
+ _jsonData.tags = [];
521
+ _jsonData.commitMessage = void 0;
522
+ _pendingWrites.length = 0;
523
+ }
524
+ function recordPendingWrite(path10, content) {
525
+ if (!_jsonOutputMode) return;
526
+ _pendingWrites.push({ path: path10, content });
527
+ }
528
+ function flushPendingWrites() {
529
+ try {
530
+ for (const { path: path10, content } of _pendingWrites) {
531
+ fs4.writeFileSync(path10, content);
532
+ }
533
+ } finally {
534
+ _pendingWrites.length = 0;
535
+ }
536
+ }
537
+ function isJsonOutputMode() {
538
+ return _jsonOutputMode;
539
+ }
540
+ function addPackageUpdate(packageName, newVersion, filePath) {
541
+ if (!_jsonOutputMode) return;
542
+ _jsonData.updates.push({
543
+ packageName,
544
+ newVersion,
545
+ filePath
546
+ });
547
+ }
548
+ function addChangelogData(data) {
549
+ if (!_jsonOutputMode) return;
550
+ _jsonData.changelogs.push(data);
551
+ }
552
+ function setSharedEntries(entries) {
553
+ if (!_jsonOutputMode) return;
554
+ _jsonData.sharedEntries = entries.length > 0 ? entries : void 0;
555
+ }
556
+ function addTag(tag) {
557
+ if (!_jsonOutputMode) return;
558
+ _jsonData.tags.push(tag);
559
+ }
560
+ function setCommitMessage(message) {
561
+ if (!_jsonOutputMode) return;
562
+ _jsonData.commitMessage = message;
563
+ }
564
+ function getJsonData() {
565
+ return { ..._jsonData };
566
+ }
567
+ function printJsonOutput() {
568
+ if (_jsonOutputMode) {
569
+ console.log(JSON.stringify(_jsonData, null, 2));
570
+ }
571
+ }
572
+
573
+ // src/utils/logging.ts
574
+ import chalk from "chalk";
575
+ import figlet from "figlet";
576
+ function log(message, level = "info") {
577
+ const showDebug = process.env.DEBUG === "true" || process.env.DEBUG === "1";
578
+ if (level === "debug" && !showDebug) {
579
+ return;
580
+ }
581
+ let chalkFn;
582
+ switch (level) {
583
+ case "success":
584
+ chalkFn = chalk.green;
585
+ break;
586
+ case "warning":
587
+ chalkFn = chalk.yellow;
588
+ break;
589
+ case "error":
590
+ chalkFn = chalk.red;
591
+ break;
592
+ case "debug":
593
+ chalkFn = chalk.gray;
594
+ break;
595
+ default:
596
+ chalkFn = chalk.blue;
597
+ }
598
+ const formattedMessage = level === "debug" ? `[DEBUG] ${message}` : message;
599
+ if (isJsonOutputMode()) {
600
+ console.error(chalkFn(formattedMessage));
601
+ return;
602
+ }
603
+ if (level === "error") {
604
+ console.error(chalkFn(formattedMessage));
605
+ } else {
606
+ console.log(chalkFn(formattedMessage));
607
+ }
608
+ }
609
+
610
+ // src/core/versionCalculator.ts
611
+ import { cwd } from "process";
612
+ import { Bumper } from "conventional-recommended-bump";
613
+ import semver3 from "semver";
614
+
615
+ // src/git/repository.ts
616
+ import { existsSync as existsSync3, statSync } from "fs";
617
+ import { join as join3 } from "path";
618
+ function getCurrentBranch() {
619
+ const result = execSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
620
+ return result.toString().trim();
621
+ }
622
+
623
+ // src/git/tagsAndBranches.ts
624
+ import { getSemverTags } from "git-semver-tags";
625
+ import semver from "semver";
626
+
627
+ // src/utils/formatting.ts
628
+ function escapeRegExp(string) {
629
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
630
+ }
631
+ function formatVersionPrefix(prefix) {
632
+ return prefix.endsWith("/") ? prefix.slice(0, -1) : prefix;
633
+ }
634
+ function formatTag(version, prefix, packageName, template, packageSpecificTags) {
635
+ const sanitizedPackageName = packageName?.startsWith("@") ? packageName.slice(1).replace(/\//g, "-") : packageName;
636
+ if (template?.includes("${packageName}") && !packageName) {
637
+ log(
638
+ `Warning: Your tagTemplate contains \${packageName} but no package name is available.
639
+ This will result in an empty package name in the tag (e.g., "@v1.0.0" instead of "my-package@v1.0.0").
640
+
641
+ To fix this:
642
+ \u2022 If using sync mode: Set "packageSpecificTags": true in your config to enable package names in tags
643
+ \u2022 If you want global tags: Remove \${packageName} from your tagTemplate (e.g., use "\${prefix}\${version}")
644
+ \u2022 If using single/async mode: Ensure your package.json has a valid "name" field`,
645
+ "warning"
646
+ );
647
+ }
648
+ if (template) {
649
+ return template.replace(/\$\{version\}/g, version).replace(/\$\{prefix\}/g, prefix).replace(/\$\{packageName\}/g, sanitizedPackageName || "");
650
+ }
651
+ if (packageSpecificTags && sanitizedPackageName) {
652
+ return `${sanitizedPackageName}@${prefix}${version}`;
653
+ }
654
+ return `${prefix}${version}`;
655
+ }
656
+ function formatCommitMessage(template, version, packageName, additionalContext) {
657
+ if (template.includes("${packageName}") && !packageName) {
658
+ log(
659
+ `Warning: Your commitMessage template contains \${packageName} but no package name is available.
660
+ This will result in an empty package name in the commit message (e.g., "Release @v1.0.0").
661
+
662
+ To fix this:
663
+ \u2022 If using sync mode: Set "packageSpecificTags": true to enable package names in commits
664
+ \u2022 If you want generic commit messages: Remove \${packageName} from your commitMessage template
665
+ \u2022 If using single/async mode: Ensure your package.json has a valid "name" field`,
666
+ "warning"
667
+ );
668
+ }
669
+ let result = template.replace(/\$\{version\}/g, version).replace(/\$\{packageName\}/g, packageName || "");
670
+ if (additionalContext) {
671
+ for (const [key, value] of Object.entries(additionalContext)) {
672
+ const placeholder = `${key ? `\${${key}}` : ""}`;
673
+ result = result.replace(new RegExp(escapeRegExp(placeholder), "g"), value);
674
+ }
675
+ }
676
+ return result;
677
+ }
678
+
679
+ // src/git/tagsAndBranches.ts
680
+ function getCommitsLength(pkgRoot, sinceTag) {
681
+ try {
682
+ let amount;
683
+ if (sinceTag && sinceTag.trim() !== "") {
684
+ amount = execSync("git", ["rev-list", "--count", `${sinceTag}..HEAD`, pkgRoot]).toString().trim();
685
+ } else {
686
+ const latestTag = execSync("git", ["describe", "--tags", "--abbrev=0"]).toString().trim();
687
+ amount = execSync("git", ["rev-list", "--count", "HEAD", `^${latestTag}`, pkgRoot]).toString().trim();
688
+ }
689
+ return Number(amount);
690
+ } catch (error) {
691
+ const errorMessage = error instanceof Error ? error.message : String(error);
692
+ log(`Failed to get number of commits since last tag: ${errorMessage}`, "error");
693
+ return 0;
694
+ }
695
+ }
696
+ async function getLatestTag(versionPrefix) {
697
+ try {
698
+ const tags = await getSemverTags({
699
+ tagPrefix: versionPrefix
700
+ });
701
+ if (tags.length === 0) {
702
+ return "";
703
+ }
704
+ const chronologicalLatest = tags[0];
705
+ const sortedTags = [...tags].sort((a, b) => {
706
+ const versionA = semver.clean(a) || "0.0.0";
707
+ const versionB = semver.clean(b) || "0.0.0";
708
+ return semver.rcompare(versionA, versionB);
709
+ });
710
+ const semanticLatest = sortedTags[0];
711
+ if (semanticLatest !== chronologicalLatest) {
712
+ log(
713
+ `Tag ordering differs: chronological latest is ${chronologicalLatest}, semantic latest is ${semanticLatest}`,
714
+ "debug"
715
+ );
716
+ log(`Using semantic latest (${semanticLatest}) to handle out-of-order tag creation`, "info");
717
+ }
718
+ return semanticLatest;
719
+ } catch (error) {
720
+ const errorMessage = error instanceof Error ? error.message : String(error);
721
+ log(`Failed to get latest tag: ${errorMessage}`, "error");
722
+ if (error instanceof Error && error.message.includes("No names found")) {
723
+ log("No tags found in the repository.", "info");
724
+ }
725
+ return "";
726
+ }
727
+ }
728
+ async function lastMergeBranchName(branches, baseBranch) {
729
+ try {
730
+ const escapedBranches = branches.map((branch) => escapeRegExp(branch));
731
+ const branchesRegex = `${escapedBranches.join("/(.*)|")}/(.*)`;
732
+ const { stdout } = await execAsync("git", [
733
+ "for-each-ref",
734
+ "--sort=-committerdate",
735
+ "--format=%(refname:short)",
736
+ "refs/heads",
737
+ `--merged=${baseBranch}`
738
+ ]);
739
+ const regex = new RegExp(branchesRegex, "i");
740
+ const matched = stdout.split("\n").map((l) => l.trim()).filter(Boolean).find((b) => regex.test(b));
741
+ return matched ?? null;
742
+ } catch (error) {
743
+ console.error("Error while getting the last branch name:", error instanceof Error ? error.message : String(error));
744
+ return null;
745
+ }
746
+ }
747
+ async function getLatestTagForPackage(packageName, versionPrefix, options) {
748
+ try {
749
+ const packageSpecificTags = options?.packageSpecificTags ?? false;
750
+ const tagTemplate = options?.tagTemplate || (packageSpecificTags ? `\${packageName}@\${prefix}\${version}` : `\${prefix}\${version}`);
751
+ const sanitizedPackageName = packageName.startsWith("@") ? packageName.slice(1).replace(/\//g, "-") : packageName;
752
+ const escapedPackageName = escapeRegExp(sanitizedPackageName);
753
+ const escapedPrefix = versionPrefix ? escapeRegExp(versionPrefix) : "";
754
+ log(
755
+ `Looking for tags for package ${packageName} with prefix ${versionPrefix || "none"}, packageSpecificTags: ${packageSpecificTags}`,
756
+ "debug"
757
+ );
758
+ let allTags = [];
759
+ try {
760
+ const { execSync: execSync2 } = await import("./commandExecutor-E44ID5U4.js");
761
+ const tagsOutput = execSync2("git", ["tag", "--sort=-creatordate"], { cwd: process.cwd() });
762
+ allTags = tagsOutput.toString().trim().split("\n").filter((tag) => tag.length > 0);
763
+ } catch (err) {
764
+ log(`Error getting tags: ${err instanceof Error ? err.message : String(err)}`, "error");
765
+ }
766
+ log(`Retrieved ${allTags.length} tags`, "debug");
767
+ if (packageSpecificTags) {
768
+ const packageTagPattern = escapeRegExp(tagTemplate).replace(/\\\$\\\{packageName\\\}/g, `(?:${escapedPackageName})`).replace(/\\\$\\\{prefix\\\}/g, `(?:${escapedPrefix})`).replace(/\\\$\\\{version\\\}/g, "(?:[0-9]+\\.[0-9]+\\.[0-9]+(?:-[a-zA-Z0-9.-]+)?)");
769
+ log(`Using package tag pattern: ${packageTagPattern}`, "debug");
770
+ const packageTagRegex = new RegExp(`^${packageTagPattern}$`);
771
+ const packageTags = allTags.filter((tag) => packageTagRegex.test(tag));
772
+ log(`Found ${packageTags.length} matching tags for ${packageName}`, "debug");
773
+ if (packageTags.length > 0) {
774
+ log(`Found ${packageTags.length} package tags using configured pattern`, "debug");
775
+ log(`Using most recently created tag: ${packageTags[0]}`, "debug");
776
+ return packageTags[0];
777
+ }
778
+ log("No matching tags found for configured tag pattern", "debug");
779
+ if (allTags.length > 0) {
780
+ log(`Available tags: ${allTags.join(", ")}`, "debug");
781
+ } else {
782
+ log("No tags available in the repository", "debug");
783
+ }
784
+ return "";
785
+ }
786
+ log(`Package-specific tags disabled for ${packageName}, falling back to global tags`, "debug");
787
+ return "";
788
+ } catch (error) {
789
+ const errorMessage = error instanceof Error ? error.message : String(error);
790
+ log(`Failed to get latest tag for package ${packageName}: ${errorMessage}`, "error");
791
+ if (error instanceof Error && error.message.includes("No names found")) {
792
+ log(`No tags found for package ${packageName}.`, "info");
793
+ }
794
+ return "";
795
+ }
796
+ }
797
+
798
+ // src/utils/manifestHelpers.ts
799
+ import fs6 from "fs";
800
+ import path5 from "path";
801
+
802
+ // src/cargo/cargoHandler.ts
803
+ import fs5 from "fs";
804
+ import path4 from "path";
805
+ import * as TOML2 from "smol-toml";
806
+ function getCargoInfo(cargoPath) {
807
+ if (!fs5.existsSync(cargoPath)) {
808
+ log(`Cargo.toml file not found at: ${cargoPath}`, "error");
809
+ throw new Error(`Cargo.toml file not found at: ${cargoPath}`);
810
+ }
811
+ try {
812
+ const cargo = parseCargoToml(cargoPath);
813
+ if (!cargo.package?.name) {
814
+ log(`Package name not found in: ${cargoPath}`, "error");
815
+ throw new Error(`Package name not found in: ${cargoPath}`);
816
+ }
817
+ return {
818
+ name: cargo.package.name,
819
+ version: cargo.package.version || "0.0.0",
820
+ path: cargoPath,
821
+ dir: path4.dirname(cargoPath),
822
+ content: cargo
823
+ };
824
+ } catch (error) {
825
+ log(`Error reading Cargo.toml: ${cargoPath}`, "error");
826
+ if (error instanceof Error) {
827
+ log(error.message, "error");
828
+ throw error;
829
+ }
830
+ throw new Error(`Failed to process Cargo.toml at ${cargoPath}`);
831
+ }
832
+ }
833
+ function updateCargoVersion(cargoPath, version, dryRun = false) {
834
+ try {
835
+ const cargo = parseCargoToml(cargoPath);
836
+ const packageName = cargo.package?.name;
837
+ if (!packageName) {
838
+ throw new Error(`No package name found in ${cargoPath}`);
839
+ }
840
+ if (!cargo.package) {
841
+ cargo.package = { name: packageName, version };
842
+ } else {
843
+ cargo.package.version = version;
844
+ }
845
+ const updatedContent = TOML2.stringify(cargo);
846
+ if (dryRun) {
847
+ recordPendingWrite(cargoPath, updatedContent);
848
+ } else {
849
+ fs5.writeFileSync(cargoPath, updatedContent);
850
+ }
851
+ addPackageUpdate(packageName, version, cargoPath);
852
+ log(`${dryRun ? "[DRY RUN] Would update" : "Updated"} Cargo.toml at ${cargoPath} to version ${version}`, "success");
853
+ } catch (error) {
854
+ log(`Failed to update Cargo.toml at ${cargoPath}`, "error");
855
+ if (error instanceof Error) {
856
+ log(error.message, "error");
857
+ }
858
+ throw error;
859
+ }
860
+ }
861
+
862
+ // src/utils/manifestHelpers.ts
863
+ function getVersionFromManifests(packageDir) {
864
+ const packageJsonPath = path5.join(packageDir, "package.json");
865
+ const cargoTomlPath = path5.join(packageDir, "Cargo.toml");
866
+ if (fs6.existsSync(packageJsonPath)) {
867
+ try {
868
+ const packageJson = JSON.parse(fs6.readFileSync(packageJsonPath, "utf-8"));
869
+ if (packageJson.version) {
870
+ log(`Found version ${packageJson.version} in package.json`, "debug");
871
+ return {
872
+ version: packageJson.version,
873
+ manifestFound: true,
874
+ manifestPath: packageJsonPath,
875
+ manifestType: "package.json"
876
+ };
877
+ }
878
+ log("No version field found in package.json", "debug");
879
+ } catch (packageJsonError) {
880
+ const errMsg = packageJsonError instanceof Error ? packageJsonError.message : String(packageJsonError);
881
+ log(`Error reading package.json: ${errMsg}`, "warning");
882
+ }
883
+ }
884
+ if (fs6.existsSync(cargoTomlPath)) {
885
+ try {
886
+ const cargoInfo = getCargoInfo(cargoTomlPath);
887
+ if (cargoInfo.version) {
888
+ log(`Found version ${cargoInfo.version} in Cargo.toml`, "debug");
889
+ return {
890
+ version: cargoInfo.version,
891
+ manifestFound: true,
892
+ manifestPath: cargoTomlPath,
893
+ manifestType: "Cargo.toml"
894
+ };
895
+ }
896
+ log("No version field found in Cargo.toml", "debug");
897
+ } catch (cargoTomlError) {
898
+ const errMsg = cargoTomlError instanceof Error ? cargoTomlError.message : String(cargoTomlError);
899
+ log(`Error reading Cargo.toml: ${errMsg}`, "warning");
900
+ }
901
+ }
902
+ return {
903
+ version: null,
904
+ manifestFound: false,
905
+ manifestPath: "",
906
+ manifestType: null
907
+ };
908
+ }
909
+
910
+ // src/utils/versionUtils.ts
911
+ import fs7 from "fs";
912
+ import semver2 from "semver";
913
+
914
+ // src/git/tagVerification.ts
915
+ function verifyTag(tagName, cwd3) {
916
+ if (!tagName || tagName.trim() === "") {
917
+ return { exists: false, reachable: false, error: "Empty tag name" };
918
+ }
919
+ try {
920
+ execSync("git", ["rev-parse", "--verify", tagName], {
921
+ cwd: cwd3,
922
+ stdio: "ignore"
923
+ });
924
+ return { exists: true, reachable: true };
925
+ } catch (error) {
926
+ const errorMessage = error instanceof Error ? error.message : String(error);
927
+ if (errorMessage.includes("unknown revision") || errorMessage.includes("bad revision") || errorMessage.includes("No such ref")) {
928
+ return {
929
+ exists: false,
930
+ reachable: false,
931
+ error: `Tag '${tagName}' not found in repository`
932
+ };
933
+ }
934
+ return {
935
+ exists: false,
936
+ reachable: false,
937
+ error: `Git error: ${errorMessage}`
938
+ };
939
+ }
940
+ }
941
+
942
+ // src/utils/versionUtils.ts
943
+ var STANDARD_BUMP_TYPES = ["major", "minor", "patch"];
944
+ function normalizePrereleaseIdentifier(prereleaseIdentifier, config) {
945
+ if (prereleaseIdentifier === true) {
946
+ return config?.prereleaseIdentifier || "next";
947
+ }
948
+ if (typeof prereleaseIdentifier === "string") {
949
+ return prereleaseIdentifier;
950
+ }
951
+ return void 0;
952
+ }
953
+ function bumpVersion(currentVersion, bumpType, prereleaseIdentifier) {
954
+ if (prereleaseIdentifier && STANDARD_BUMP_TYPES.includes(bumpType) && !semver2.prerelease(currentVersion)) {
955
+ const preBumpType = `pre${bumpType}`;
956
+ log(`Creating prerelease version with identifier '${prereleaseIdentifier}' using ${preBumpType}`, "debug");
957
+ return semver2.inc(currentVersion, preBumpType, prereleaseIdentifier) || "";
958
+ }
959
+ if (semver2.prerelease(currentVersion) && STANDARD_BUMP_TYPES.includes(bumpType)) {
960
+ const parsed = semver2.parse(currentVersion);
961
+ if (!parsed) {
962
+ return semver2.inc(currentVersion, bumpType) || "";
963
+ }
964
+ if (bumpType === "major" && parsed.minor === 0 && parsed.patch === 0 || bumpType === "minor" && parsed.patch === 0 || bumpType === "patch") {
965
+ log(`Cleaning prerelease identifier from ${currentVersion} for ${bumpType} bump`, "debug");
966
+ return `${parsed.major}.${parsed.minor}.${parsed.patch}`;
967
+ }
968
+ log(`Standard increment for ${currentVersion} with ${bumpType} bump`, "debug");
969
+ return semver2.inc(currentVersion, bumpType) || "";
970
+ }
971
+ if (prereleaseIdentifier) {
972
+ return semver2.inc(currentVersion, bumpType, prereleaseIdentifier) || "";
973
+ }
974
+ return semver2.inc(currentVersion, bumpType) || "";
975
+ }
976
+ function detectVersionMismatch(tagVersion, packageVersion) {
977
+ const tagIsPrerelease = semver2.prerelease(tagVersion) !== null;
978
+ const packageIsPrerelease = semver2.prerelease(packageVersion) !== null;
979
+ const tagParsed = semver2.parse(tagVersion);
980
+ const packageParsed = semver2.parse(packageVersion);
981
+ if (!tagParsed || !packageParsed) {
982
+ return { isMismatch: false, severity: "minor", message: "" };
983
+ }
984
+ if (!tagIsPrerelease && packageIsPrerelease && tagParsed.major === packageParsed.major) {
985
+ return {
986
+ isMismatch: true,
987
+ severity: "major",
988
+ message: `Git tag ${tagVersion} (stable) is ahead of package ${packageVersion} (prerelease). This may indicate a reverted release. Consider deleting tag ${tagVersion} or updating package.json.`
989
+ };
990
+ }
991
+ const tagHigher = semver2.gt(tagVersion, packageVersion);
992
+ if (tagHigher) {
993
+ const diff = semver2.diff(packageVersion, tagVersion);
994
+ if (diff === "major" || diff === "minor") {
995
+ return {
996
+ isMismatch: true,
997
+ severity: "major",
998
+ message: `Git tag ${tagVersion} is significantly ahead (${diff}) of package ${packageVersion}. This may cause unexpected version bumps.`
999
+ };
1000
+ }
1001
+ }
1002
+ if (tagIsPrerelease && !packageIsPrerelease) {
1003
+ return {
1004
+ isMismatch: true,
1005
+ severity: "minor",
1006
+ message: `Git tag ${tagVersion} is a prerelease but package ${packageVersion} is stable. Consider aligning your versioning.`
1007
+ };
1008
+ }
1009
+ return { isMismatch: false, severity: "minor", message: "" };
1010
+ }
1011
+ var VersionMismatchError = class extends Error {
1012
+ constructor(message, severity) {
1013
+ super(message);
1014
+ this.severity = severity;
1015
+ this.name = "VersionMismatchError";
1016
+ }
1017
+ };
1018
+ async function getBestVersionSource(tagName, packageVersion, cwd3, mismatchStrategy = "error", strictReachable = false) {
1019
+ if (!tagName?.trim()) {
1020
+ return packageVersion ? { source: "package", version: packageVersion, reason: "No git tag provided" } : { source: "initial", version: "0.1.0", reason: "No git tag or package version available" };
1021
+ }
1022
+ const verification = verifyTag(tagName, cwd3);
1023
+ if (!verification.exists || !verification.reachable) {
1024
+ if (strictReachable) {
1025
+ throw new Error(
1026
+ `Git tag '${tagName}' is not reachable from the current commit. The tag exists but cannot be reached from HEAD, which usually means you're on a different branch or the tag is orphaned. To allow fallback to package version, set strictReachable to false in your configuration.`
1027
+ );
1028
+ }
1029
+ if (packageVersion) {
1030
+ log(
1031
+ `Git tag '${tagName}' unreachable (${verification.error}), using package version: ${packageVersion}`,
1032
+ "warning"
1033
+ );
1034
+ return { source: "package", version: packageVersion, reason: "Git tag unreachable" };
1035
+ }
1036
+ log(`Git tag '${tagName}' unreachable and no package version available, using initial version`, "warning");
1037
+ return {
1038
+ source: "initial",
1039
+ version: "0.1.0",
1040
+ reason: "Git tag unreachable, no package version"
1041
+ };
1042
+ }
1043
+ if (!packageVersion) {
1044
+ return {
1045
+ source: "git",
1046
+ version: tagName,
1047
+ reason: "Git tag exists, no package version to compare"
1048
+ };
1049
+ }
1050
+ try {
1051
+ const cleanTagVersion = tagName.replace(/^.*?([0-9])/, "$1");
1052
+ const cleanPackageVersion = packageVersion;
1053
+ const mismatch = detectVersionMismatch(cleanTagVersion, cleanPackageVersion);
1054
+ const mismatchInfo = mismatch.isMismatch ? { detected: true, severity: mismatch.severity, message: mismatch.message } : void 0;
1055
+ if (mismatch.isMismatch) {
1056
+ switch (mismatchStrategy) {
1057
+ case "error":
1058
+ throw new VersionMismatchError(
1059
+ `Version mismatch detected: ${mismatch.message}
1060
+ To resolve: delete the conflicting tag, update package.json, or change mismatchStrategy to 'warn' or 'ignore'`,
1061
+ mismatch.severity
1062
+ );
1063
+ case "warn":
1064
+ log(mismatch.message, "warning");
1065
+ log(
1066
+ `Continuing with git tag ${tagName}. To use package version instead, set mismatchStrategy to 'prefer-package'`,
1067
+ "warning"
1068
+ );
1069
+ break;
1070
+ case "ignore":
1071
+ break;
1072
+ case "prefer-package":
1073
+ log(mismatch.message, "warning");
1074
+ log(`Using package version ${packageVersion} due to mismatchStrategy='prefer-package'`, "info");
1075
+ return {
1076
+ source: "package",
1077
+ version: packageVersion,
1078
+ reason: "Mismatch detected, using package version per strategy",
1079
+ mismatch: mismatchInfo
1080
+ };
1081
+ case "prefer-git":
1082
+ log(mismatch.message, "warning");
1083
+ log(`Using git tag ${tagName} due to mismatchStrategy='prefer-git'`, "info");
1084
+ return {
1085
+ source: "git",
1086
+ version: tagName,
1087
+ reason: "Mismatch detected, using git tag per strategy",
1088
+ mismatch: mismatchInfo
1089
+ };
1090
+ }
1091
+ }
1092
+ if (semver2.gt(cleanPackageVersion, cleanTagVersion)) {
1093
+ log(`Package version ${packageVersion} is newer than git tag ${tagName}, using package version`, "info");
1094
+ return {
1095
+ source: "package",
1096
+ version: packageVersion,
1097
+ reason: "Package version is newer",
1098
+ mismatch: mismatchInfo
1099
+ };
1100
+ }
1101
+ if (semver2.gt(cleanTagVersion, cleanPackageVersion)) {
1102
+ log(`Git tag ${tagName} is newer than package version ${packageVersion}, using git tag`, "info");
1103
+ return {
1104
+ source: "git",
1105
+ version: tagName,
1106
+ reason: "Git tag is newer",
1107
+ mismatch: mismatchInfo
1108
+ };
1109
+ }
1110
+ return {
1111
+ source: "git",
1112
+ version: tagName,
1113
+ reason: "Versions equal, using git tag",
1114
+ mismatch: mismatchInfo
1115
+ };
1116
+ } catch (error) {
1117
+ if (error instanceof VersionMismatchError) {
1118
+ throw error;
1119
+ }
1120
+ log(`Failed to compare versions, defaulting to git tag: ${error}`, "warning");
1121
+ return { source: "git", version: tagName, reason: "Version comparison failed" };
1122
+ }
1123
+ }
1124
+
1125
+ // src/core/versionCalculator.ts
1126
+ async function calculateVersion(config, options) {
1127
+ const {
1128
+ type: configType,
1129
+ preset = "angular",
1130
+ versionPrefix,
1131
+ prereleaseIdentifier: configPrereleaseIdentifier,
1132
+ branchPattern,
1133
+ baseBranch,
1134
+ mismatchStrategy,
1135
+ strictReachable
1136
+ } = config;
1137
+ const {
1138
+ latestTag,
1139
+ name,
1140
+ path: pkgPath,
1141
+ type: optionsType,
1142
+ prereleaseIdentifier: optionsPrereleaseIdentifier
1143
+ } = options;
1144
+ const type = optionsType || configType;
1145
+ const prereleaseIdentifier = optionsPrereleaseIdentifier || configPrereleaseIdentifier;
1146
+ const initialVersion = "0.1.0";
1147
+ const hasNoTags = !latestTag || latestTag.trim() === "";
1148
+ const normalizedPrereleaseId = normalizePrereleaseIdentifier(prereleaseIdentifier, config);
1149
+ try {
1150
+ let determineTagSearchPattern2 = function(packageName, prefix) {
1151
+ if (!packageName) {
1152
+ return prefix;
1153
+ }
1154
+ return `${packageName}@${prefix}`;
1155
+ }, escapeRegExp3 = function(string) {
1156
+ return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1157
+ }, getCurrentVersionFromSource2 = function() {
1158
+ if (!versionSource) {
1159
+ if (hasNoTags) {
1160
+ return initialVersion;
1161
+ }
1162
+ const cleanedTag = semver3.clean(latestTag) || latestTag;
1163
+ return semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1164
+ }
1165
+ if (versionSource.source === "git") {
1166
+ const cleanedTag = semver3.clean(versionSource.version) || versionSource.version;
1167
+ return semver3.clean(cleanedTag.replace(new RegExp(`^${escapedTagPattern}`), "")) || "0.0.0";
1168
+ }
1169
+ return versionSource.version;
1170
+ };
1171
+ var determineTagSearchPattern = determineTagSearchPattern2, escapeRegExp2 = escapeRegExp3, getCurrentVersionFromSource = getCurrentVersionFromSource2;
1172
+ const originalPrefix = versionPrefix || "";
1173
+ const tagSearchPattern = determineTagSearchPattern2(name, originalPrefix);
1174
+ const escapedTagPattern = escapeRegExp3(tagSearchPattern);
1175
+ let versionSource;
1176
+ if (pkgPath) {
1177
+ const packageDir = pkgPath || cwd();
1178
+ const manifestResult = getVersionFromManifests(packageDir);
1179
+ const packageVersion = manifestResult.manifestFound && manifestResult.version ? manifestResult.version : void 0;
1180
+ versionSource = await getBestVersionSource(
1181
+ latestTag,
1182
+ packageVersion,
1183
+ packageDir,
1184
+ mismatchStrategy,
1185
+ strictReachable
1186
+ );
1187
+ log(`Using version source: ${versionSource.source} (${versionSource.reason})`, "info");
1188
+ }
1189
+ const specifiedType = type;
1190
+ if (specifiedType) {
1191
+ const currentVersion = getCurrentVersionFromSource2();
1192
+ const isCurrentPrerelease = semver3.prerelease(currentVersion);
1193
+ const explicitlyRequestedPrerelease = config.isPrerelease;
1194
+ if (STANDARD_BUMP_TYPES.includes(specifiedType) && (isCurrentPrerelease || explicitlyRequestedPrerelease)) {
1195
+ const prereleaseId2 = explicitlyRequestedPrerelease || isCurrentPrerelease ? normalizedPrereleaseId : void 0;
1196
+ log(
1197
+ explicitlyRequestedPrerelease ? `Creating prerelease version with identifier '${prereleaseId2}' using ${specifiedType}` : `Cleaning prerelease identifier from ${currentVersion} for ${specifiedType} bump`,
1198
+ "debug"
1199
+ );
1200
+ return bumpVersion(currentVersion, specifiedType, prereleaseId2);
1201
+ }
1202
+ const isPrereleaseBumpType = ["prerelease", "premajor", "preminor", "prepatch"].includes(specifiedType);
1203
+ const prereleaseId = config.isPrerelease || isPrereleaseBumpType ? normalizedPrereleaseId : void 0;
1204
+ return bumpVersion(currentVersion, specifiedType, prereleaseId);
1205
+ }
1206
+ if (branchPattern && branchPattern.length > 0) {
1207
+ const currentBranch = getCurrentBranch();
1208
+ if (baseBranch) {
1209
+ lastMergeBranchName(branchPattern, baseBranch);
1210
+ }
1211
+ const branchToCheck = currentBranch;
1212
+ let branchVersionType;
1213
+ for (const pattern of branchPattern) {
1214
+ if (!pattern.includes(":")) {
1215
+ log(`Invalid branch pattern "${pattern}" - missing colon. Skipping.`, "warning");
1216
+ continue;
1217
+ }
1218
+ const [patternRegex, releaseType] = pattern.split(":");
1219
+ if (new RegExp(patternRegex).test(branchToCheck)) {
1220
+ branchVersionType = releaseType;
1221
+ log(`Using branch pattern ${patternRegex} for version type ${releaseType}`, "debug");
1222
+ break;
1223
+ }
1224
+ }
1225
+ if (branchVersionType) {
1226
+ const currentVersion = getCurrentVersionFromSource2();
1227
+ log(`Applying ${branchVersionType} bump based on branch pattern`, "debug");
1228
+ const isPrereleaseBumpType = ["prerelease", "premajor", "preminor", "prepatch"].includes(branchVersionType);
1229
+ const prereleaseId = config.isPrerelease || isPrereleaseBumpType ? normalizedPrereleaseId : void 0;
1230
+ return bumpVersion(currentVersion, branchVersionType, prereleaseId);
1231
+ }
1232
+ }
1233
+ try {
1234
+ const bumper = new Bumper();
1235
+ bumper.loadPreset(preset);
1236
+ const recommendedBump = await bumper.bump();
1237
+ const releaseTypeFromCommits = recommendedBump && "releaseType" in recommendedBump ? recommendedBump.releaseType : void 0;
1238
+ const currentVersion = getCurrentVersionFromSource2();
1239
+ if (versionSource && versionSource.source === "git") {
1240
+ const checkPath = pkgPath || cwd();
1241
+ const commitsLength = getCommitsLength(checkPath, versionSource.version);
1242
+ if (commitsLength === 0) {
1243
+ log(
1244
+ `No new commits found for ${name || "project"} since ${versionSource.version}, skipping version bump`,
1245
+ "info"
1246
+ );
1247
+ return "";
1248
+ }
1249
+ } else if (versionSource && versionSource.source === "package") {
1250
+ log(
1251
+ `Using package version ${versionSource.version} as base, letting conventional commits determine bump necessity`,
1252
+ "debug"
1253
+ );
1254
+ }
1255
+ if (!releaseTypeFromCommits) {
1256
+ if (latestTag && latestTag.trim() !== "") {
1257
+ log(`No relevant commits found for ${name || "project"} since ${latestTag}, skipping version bump`, "info");
1258
+ } else {
1259
+ log(`No relevant commits found for ${name || "project"}, skipping version bump`, "info");
1260
+ }
1261
+ return "";
1262
+ }
1263
+ const isPrereleaseBumpType = ["prerelease", "premajor", "preminor", "prepatch"].includes(releaseTypeFromCommits);
1264
+ const prereleaseId = config.isPrerelease || isPrereleaseBumpType ? normalizedPrereleaseId : void 0;
1265
+ return bumpVersion(currentVersion, releaseTypeFromCommits, prereleaseId);
1266
+ } catch (error) {
1267
+ log(`Failed to calculate version for ${name || "project"}`, "error");
1268
+ console.error(error);
1269
+ if (error instanceof Error && error.message.includes("No names found")) {
1270
+ log("No tags found, proceeding with initial version calculation (if applicable).", "info");
1271
+ return initialVersion;
1272
+ }
1273
+ throw error;
1274
+ }
1275
+ } catch (error) {
1276
+ log(`Failed to calculate version for ${name || "project"}`, "error");
1277
+ console.error(error);
1278
+ if (error instanceof Error && error.message.includes("No names found")) {
1279
+ log("No tags found, proceeding with initial version calculation (if applicable).", "info");
1280
+ return initialVersion;
1281
+ }
1282
+ throw error;
1283
+ }
1284
+ }
1285
+
1286
+ // src/package/packageProcessor.ts
1287
+ import * as fs9 from "fs";
1288
+ import path7 from "path";
1289
+
1290
+ // src/changelog/commitParser.ts
1291
+ var CONVENTIONAL_COMMIT_REGEX = /^(\w+)(?:\(([^)]+)\))?(!)?: (.+)(?:\n\n([\s\S]*))?/;
1292
+ var BREAKING_CHANGE_REGEX = /BREAKING CHANGE: ([\s\S]+?)(?:\n\n|$)/;
1293
+ function extractAllChangelogEntriesWithHash(projectDir, revisionRange) {
1294
+ try {
1295
+ const args = ["log", revisionRange, "--pretty=format:%H|||%B---COMMIT_DELIMITER---", "--no-merges"];
1296
+ const output = execSync("git", args, { cwd: projectDir, encoding: "utf8" }).toString();
1297
+ const commits = output.split("---COMMIT_DELIMITER---").filter((commit) => commit.trim() !== "");
1298
+ return commits.map((commit) => {
1299
+ const [hash, ...messageParts] = commit.split("|||");
1300
+ const message = messageParts.join("|||").trim();
1301
+ const entry = parseCommitMessage(message);
1302
+ if (entry && hash) {
1303
+ return { hash: hash.trim(), entry };
1304
+ }
1305
+ return null;
1306
+ }).filter((item) => item !== null);
1307
+ } catch (error) {
1308
+ const errorMessage = error instanceof Error ? error.message : String(error);
1309
+ log(`Error extracting all commits with hash: ${errorMessage}`, "error");
1310
+ return [];
1311
+ }
1312
+ }
1313
+ function commitTouchesAnyPackage(projectDir, commitHash, packageDirs, sharedPackageDirs = []) {
1314
+ try {
1315
+ const output = execSync("git", ["diff-tree", "--no-commit-id", "--name-only", "-r", commitHash], {
1316
+ cwd: projectDir,
1317
+ encoding: "utf8"
1318
+ }).toString().trim();
1319
+ if (!output) {
1320
+ return false;
1321
+ }
1322
+ const changedFiles = output.split("\n");
1323
+ return changedFiles.some((file) => {
1324
+ return packageDirs.some((pkgDir) => {
1325
+ if (sharedPackageDirs.some((sharedDir) => pkgDir.includes(sharedDir))) {
1326
+ return false;
1327
+ }
1328
+ const normalizedFile = file.replace(/\\/g, "/");
1329
+ const normalizedPkgDir = pkgDir.replace(/\\/g, "/").replace(/^\.\//, "");
1330
+ return normalizedFile.startsWith(normalizedPkgDir);
1331
+ });
1332
+ });
1333
+ } catch (error) {
1334
+ log(
1335
+ `Error checking if commit ${commitHash} touches packages: ${error instanceof Error ? error.message : String(error)}`,
1336
+ "debug"
1337
+ );
1338
+ return false;
1339
+ }
1340
+ }
1341
+ function extractRepoLevelChangelogEntries(projectDir, revisionRange, packageDirs, sharedPackageDirs = []) {
1342
+ try {
1343
+ const allCommits = extractAllChangelogEntriesWithHash(projectDir, revisionRange);
1344
+ const repoLevelCommits = allCommits.filter((commit) => {
1345
+ const touchesPackage = commitTouchesAnyPackage(projectDir, commit.hash, packageDirs, sharedPackageDirs);
1346
+ return !touchesPackage;
1347
+ });
1348
+ if (repoLevelCommits.length > 0) {
1349
+ log(
1350
+ `Found ${repoLevelCommits.length} repo-level commit(s) (including shared packages: ${sharedPackageDirs.join(", ")})`,
1351
+ "debug"
1352
+ );
1353
+ }
1354
+ return repoLevelCommits.map((c) => c.entry);
1355
+ } catch (error) {
1356
+ log(`Error extracting repo-level commits: ${error instanceof Error ? error.message : String(error)}`, "warning");
1357
+ return [];
1358
+ }
1359
+ }
1360
+ function extractChangelogEntriesFromCommits(projectDir, revisionRange) {
1361
+ return extractCommitsFromGitLog(projectDir, revisionRange, true);
1362
+ }
1363
+ function extractCommitsFromGitLog(projectDir, revisionRange, filterToPath) {
1364
+ try {
1365
+ const args = ["log", revisionRange, "--pretty=format:%B---COMMIT_DELIMITER---", "--no-merges"];
1366
+ if (filterToPath) {
1367
+ args.push("--", ".");
1368
+ }
1369
+ const output = execSync("git", args, { cwd: projectDir, encoding: "utf8" }).toString();
1370
+ const commits = output.split("---COMMIT_DELIMITER---").filter((commit) => commit.trim() !== "");
1371
+ return commits.map((commit) => parseCommitMessage(commit)).filter((entry) => entry !== null);
1372
+ } catch (error) {
1373
+ const errorMessage = error instanceof Error ? error.message : String(error);
1374
+ if (errorMessage.includes("ambiguous argument") && errorMessage.includes("unknown revision")) {
1375
+ const tagName = revisionRange.split("..")[0] || revisionRange;
1376
+ if (tagName.startsWith("v") && !tagName.includes("@")) {
1377
+ log(
1378
+ `Error: Tag "${tagName}" not found. If you're using package-specific tags (like "package-name@v1.0.0"), you may need to configure "tagTemplate" in your version.config.json to use: \${packageName}@\${prefix}\${version}`,
1379
+ "error"
1380
+ );
1381
+ } else {
1382
+ log(
1383
+ `Error: Tag or revision "${tagName}" not found in the repository. Please check if this tag exists or if you need to fetch it from the remote.`,
1384
+ "error"
1385
+ );
1386
+ }
1387
+ } else {
1388
+ log(`Error extracting commits: ${errorMessage}`, "error");
1389
+ }
1390
+ return [];
1391
+ }
1392
+ }
1393
+ function parseCommitMessage(message) {
1394
+ const trimmedMessage = message.trim();
1395
+ const match = trimmedMessage.match(CONVENTIONAL_COMMIT_REGEX);
1396
+ if (match) {
1397
+ const [, type, scope, breakingMark, subject, body = ""] = match;
1398
+ const breakingFromMark = breakingMark === "!";
1399
+ const breakingChangeMatch = body.match(BREAKING_CHANGE_REGEX);
1400
+ const hasBreakingChange = breakingFromMark || breakingChangeMatch !== null;
1401
+ const changelogType = mapCommitTypeToChangelogType(type);
1402
+ if (!changelogType) {
1403
+ return null;
1404
+ }
1405
+ const issueIds = extractIssueIds(body);
1406
+ let description = subject;
1407
+ if (hasBreakingChange) {
1408
+ description = `**BREAKING** ${description}`;
1409
+ }
1410
+ return {
1411
+ type: changelogType,
1412
+ description,
1413
+ scope: scope || void 0,
1414
+ issueIds: issueIds.length > 0 ? issueIds : void 0,
1415
+ originalType: type
1416
+ // Store original type for custom formatting
1417
+ };
1418
+ }
1419
+ if (!trimmedMessage.startsWith("Merge") && !trimmedMessage.match(/^v?\d+\.\d+\.\d+/)) {
1420
+ const firstLine = trimmedMessage.split("\n")[0].trim();
1421
+ return {
1422
+ type: "changed",
1423
+ description: firstLine
1424
+ };
1425
+ }
1426
+ return null;
1427
+ }
1428
+ function mapCommitTypeToChangelogType(type) {
1429
+ switch (type) {
1430
+ case "feat":
1431
+ return "added";
1432
+ case "fix":
1433
+ return "fixed";
1434
+ case "docs":
1435
+ case "style":
1436
+ case "refactor":
1437
+ case "perf":
1438
+ case "build":
1439
+ case "ci":
1440
+ return "changed";
1441
+ case "revert":
1442
+ return "removed";
1443
+ case "chore":
1444
+ return "changed";
1445
+ case "test":
1446
+ return null;
1447
+ default:
1448
+ return "changed";
1449
+ }
1450
+ }
1451
+ function extractIssueIds(body) {
1452
+ const issueRegex = /(?:fix|fixes|close|closes|resolve|resolves)\s+#(\d+)/gi;
1453
+ const issueIds = [];
1454
+ let match = issueRegex.exec(body);
1455
+ while (match !== null) {
1456
+ issueIds.push(`#${match[1]}`);
1457
+ match = issueRegex.exec(body);
1458
+ }
1459
+ return issueIds;
1460
+ }
1461
+
1462
+ // src/utils/packageMatching.ts
1463
+ import micromatch from "micromatch";
1464
+ function matchesPackageTarget(packageName, target) {
1465
+ if (packageName === target) {
1466
+ return true;
1467
+ }
1468
+ if (target.startsWith("@") && target.endsWith("/*") && !target.includes("**")) {
1469
+ const scope = target.slice(0, -2);
1470
+ return packageName.startsWith(`${scope}/`);
1471
+ }
1472
+ try {
1473
+ return micromatch.isMatch(packageName, target, {
1474
+ dot: true,
1475
+ contains: false,
1476
+ // Changed to false to ensure full pattern matching
1477
+ noglobstar: false,
1478
+ bash: true
1479
+ });
1480
+ } catch (error) {
1481
+ log(`Invalid pattern "${target}": ${error instanceof Error ? error.message : String(error)}`, "warning");
1482
+ return false;
1483
+ }
1484
+ }
1485
+ function shouldMatchPackageTargets(packageName, targets) {
1486
+ return targets.some((target) => matchesPackageTarget(packageName, target));
1487
+ }
1488
+ function shouldProcessPackage(packageName, skip = []) {
1489
+ if (skip.length === 0) {
1490
+ return true;
1491
+ }
1492
+ return !shouldMatchPackageTargets(packageName, skip);
1493
+ }
1494
+
1495
+ // src/package/packageManagement.ts
1496
+ import fs8 from "fs";
1497
+ import path6 from "path";
1498
+ function updatePackageVersion(packagePath, version, dryRun = false) {
1499
+ if (isCargoToml(packagePath)) {
1500
+ updateCargoVersion(packagePath, version, dryRun);
1501
+ return;
1502
+ }
1503
+ try {
1504
+ const packageContent = fs8.readFileSync(packagePath, "utf8");
1505
+ const packageJson = JSON.parse(packageContent);
1506
+ const packageName = packageJson.name;
1507
+ const updatedContent = `${JSON.stringify({ ...packageJson, version }, null, 2)}
1508
+ `;
1509
+ if (dryRun) {
1510
+ recordPendingWrite(packagePath, updatedContent);
1511
+ } else {
1512
+ fs8.writeFileSync(packagePath, updatedContent);
1513
+ }
1514
+ addPackageUpdate(packageName, version, packagePath);
1515
+ log(
1516
+ `${dryRun ? "[DRY RUN] Would update" : "Updated"} package.json at ${packagePath} to version ${version}`,
1517
+ "success"
1518
+ );
1519
+ } catch (error) {
1520
+ log(`Failed to update package.json at ${packagePath}`, "error");
1521
+ if (error instanceof Error) {
1522
+ log(error.message, "error");
1523
+ }
1524
+ throw error;
1525
+ }
1526
+ }
1527
+
1528
+ // src/package/packageProcessor.ts
1529
+ var PackageProcessor = class {
1530
+ skip;
1531
+ versionPrefix;
1532
+ tagTemplate;
1533
+ commitMessageTemplate;
1534
+ dryRun;
1535
+ getLatestTag;
1536
+ config;
1537
+ // Config for version calculation
1538
+ fullConfig;
1539
+ constructor(options) {
1540
+ this.skip = options.skip || [];
1541
+ this.versionPrefix = options.versionPrefix || "v";
1542
+ this.tagTemplate = options.tagTemplate;
1543
+ this.commitMessageTemplate = options.commitMessageTemplate || "";
1544
+ this.dryRun = options.dryRun || false;
1545
+ this.getLatestTag = options.getLatestTag;
1546
+ this.config = options.config;
1547
+ this.fullConfig = options.fullConfig;
1548
+ }
1549
+ /**
1550
+ * Process packages based on skip list only (targeting handled at discovery time)
1551
+ */
1552
+ async processPackages(packages) {
1553
+ const tags = [];
1554
+ const updatedPackagesInfo = [];
1555
+ if (!packages || !Array.isArray(packages)) {
1556
+ log("Invalid packages data provided. Expected array of packages.", "error");
1557
+ return { updatedPackages: [], tags: [] };
1558
+ }
1559
+ const pkgsToConsider = packages.filter((pkg) => {
1560
+ const pkgName = pkg.packageJson.name;
1561
+ const shouldProcess = shouldProcessPackage(pkgName, this.skip);
1562
+ if (!shouldProcess) {
1563
+ log(`Skipping package ${pkgName} as it's in the skip list.`, "info");
1564
+ }
1565
+ return shouldProcess;
1566
+ });
1567
+ log(`Found ${pkgsToConsider.length} package(s) to process after filtering.`, "info");
1568
+ if (pkgsToConsider.length === 0) {
1569
+ log("No packages found to process.", "info");
1570
+ return { updatedPackages: [], tags: [] };
1571
+ }
1572
+ const sharedEntriesMap = /* @__PURE__ */ new Map();
1573
+ for (const pkg of pkgsToConsider) {
1574
+ const name = pkg.packageJson.name;
1575
+ const pkgPath = pkg.dir;
1576
+ log(`Processing package ${name} at path: ${pkgPath}`, "info");
1577
+ const formattedPrefix = formatVersionPrefix(this.versionPrefix);
1578
+ let latestTagResult = "";
1579
+ try {
1580
+ latestTagResult = await getLatestTagForPackage(name, this.versionPrefix, {
1581
+ tagTemplate: this.tagTemplate,
1582
+ packageSpecificTags: this.fullConfig.packageSpecificTags
1583
+ });
1584
+ } catch (error) {
1585
+ const errorMessage = error instanceof Error ? error.message : String(error);
1586
+ log(`Error getting package-specific tag for ${name}, falling back to global tag: ${errorMessage}`, "warning");
1587
+ }
1588
+ if (!latestTagResult) {
1589
+ try {
1590
+ const packageDir = pkgPath;
1591
+ let manifestFallbackUsed = false;
1592
+ const manifestResult = getVersionFromManifests(packageDir);
1593
+ if (manifestResult.manifestFound && manifestResult.version) {
1594
+ log(
1595
+ `Using ${manifestResult.manifestType} version ${manifestResult.version} for ${name} as no package-specific tags found`,
1596
+ "info"
1597
+ );
1598
+ log(`FALLBACK: Using package version from ${manifestResult.manifestType} instead of global tag`, "debug");
1599
+ latestTagResult = `${this.versionPrefix || ""}${manifestResult.version}`;
1600
+ manifestFallbackUsed = true;
1601
+ }
1602
+ if (!manifestFallbackUsed) {
1603
+ const globalTagResult = await this.getLatestTag();
1604
+ if (globalTagResult) {
1605
+ latestTagResult = globalTagResult;
1606
+ log(`Using global tag ${globalTagResult} as fallback for package ${name}`, "info");
1607
+ }
1608
+ }
1609
+ } catch (error) {
1610
+ const errorMessage = error instanceof Error ? error.message : String(error);
1611
+ log(`Error getting fallback version, using empty tag value: ${errorMessage}`, "warning");
1612
+ }
1613
+ }
1614
+ const latestTag = latestTagResult;
1615
+ const nextVersion = await calculateVersion(this.fullConfig, {
1616
+ latestTag,
1617
+ versionPrefix: formattedPrefix,
1618
+ path: pkgPath,
1619
+ name,
1620
+ branchPattern: this.config.branchPattern,
1621
+ baseBranch: this.config.baseBranch,
1622
+ prereleaseIdentifier: this.config.prereleaseIdentifier,
1623
+ type: this.config.type
1624
+ });
1625
+ if (!nextVersion) {
1626
+ continue;
1627
+ }
1628
+ let changelogEntries = [];
1629
+ let revisionRange = "HEAD";
1630
+ try {
1631
+ if (latestTag) {
1632
+ const verification = verifyTag(latestTag, pkgPath);
1633
+ if (verification.exists && verification.reachable) {
1634
+ revisionRange = `${latestTag}..HEAD`;
1635
+ } else {
1636
+ if (this.config.strictReachable) {
1637
+ throw new Error(
1638
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1639
+ );
1640
+ }
1641
+ log(`Tag ${latestTag} is unreachable (${verification.error}), using all commits for changelog`, "debug");
1642
+ revisionRange = "HEAD";
1643
+ }
1644
+ } else {
1645
+ revisionRange = "HEAD";
1646
+ }
1647
+ changelogEntries = extractChangelogEntriesFromCommits(pkgPath, revisionRange);
1648
+ const allPackageDirs = packages.map((p) => p.dir);
1649
+ const sharedPackageNames = ["config", "core", "@releasekit/config", "@releasekit/core"];
1650
+ const sharedPackageDirs = packages.filter((p) => sharedPackageNames.includes(p.packageJson.name)).map((p) => p.dir);
1651
+ const repoLevelEntries = extractRepoLevelChangelogEntries(
1652
+ pkgPath,
1653
+ revisionRange,
1654
+ allPackageDirs,
1655
+ sharedPackageDirs
1656
+ );
1657
+ if (repoLevelEntries.length > 0) {
1658
+ log(`Found ${repoLevelEntries.length} repo-level commit(s) for ${name}`, "debug");
1659
+ for (const entry of repoLevelEntries) {
1660
+ sharedEntriesMap.set(`${entry.type}:${entry.description}`, entry);
1661
+ }
1662
+ }
1663
+ if (changelogEntries.length === 0) {
1664
+ changelogEntries = [
1665
+ {
1666
+ type: "changed",
1667
+ description: `Update version to ${nextVersion}`
1668
+ }
1669
+ ];
1670
+ }
1671
+ } catch (error) {
1672
+ log(`Error extracting changelog entries: ${error instanceof Error ? error.message : String(error)}`, "warning");
1673
+ changelogEntries = [
1674
+ {
1675
+ type: "changed",
1676
+ description: `Update version to ${nextVersion}`
1677
+ }
1678
+ ];
1679
+ }
1680
+ let repoUrl;
1681
+ try {
1682
+ const packageJsonPath2 = path7.join(pkgPath, "package.json");
1683
+ if (fs9.existsSync(packageJsonPath2)) {
1684
+ const packageJson = JSON.parse(fs9.readFileSync(packageJsonPath2, "utf8"));
1685
+ if (packageJson.repository) {
1686
+ if (typeof packageJson.repository === "string") {
1687
+ repoUrl = packageJson.repository;
1688
+ } else if (packageJson.repository.url) {
1689
+ repoUrl = packageJson.repository.url;
1690
+ }
1691
+ if (repoUrl?.startsWith("git+") && repoUrl?.endsWith(".git")) {
1692
+ repoUrl = repoUrl.substring(4, repoUrl.length - 4);
1693
+ }
1694
+ }
1695
+ }
1696
+ } catch (error) {
1697
+ log(
1698
+ `Could not determine repository URL for changelog links: ${error instanceof Error ? error.message : String(error)}`,
1699
+ "warning"
1700
+ );
1701
+ }
1702
+ addChangelogData({
1703
+ packageName: name,
1704
+ version: nextVersion,
1705
+ previousVersion: latestTag || null,
1706
+ revisionRange,
1707
+ repoUrl: repoUrl || null,
1708
+ entries: changelogEntries
1709
+ });
1710
+ const packageJsonPath = path7.join(pkgPath, "package.json");
1711
+ if (fs9.existsSync(packageJsonPath)) {
1712
+ updatePackageVersion(packageJsonPath, nextVersion, this.dryRun);
1713
+ }
1714
+ const cargoEnabled = this.fullConfig.cargo?.enabled !== false;
1715
+ log(`Cargo enabled for ${name}: ${cargoEnabled}, config: ${JSON.stringify(this.fullConfig.cargo)}`, "debug");
1716
+ if (cargoEnabled) {
1717
+ const cargoPaths = this.fullConfig.cargo?.paths;
1718
+ log(`Cargo paths config for ${name}: ${JSON.stringify(cargoPaths)}`, "debug");
1719
+ if (cargoPaths && cargoPaths.length > 0) {
1720
+ for (const cargoPath of cargoPaths) {
1721
+ const resolvedCargoPath = path7.resolve(pkgPath, cargoPath, "Cargo.toml");
1722
+ log(`Checking cargo path for ${name}: ${resolvedCargoPath}`, "debug");
1723
+ if (fs9.existsSync(resolvedCargoPath)) {
1724
+ log(`Found Cargo.toml for ${name} at ${resolvedCargoPath}, updating...`, "debug");
1725
+ updatePackageVersion(resolvedCargoPath, nextVersion, this.dryRun);
1726
+ } else {
1727
+ log(`Cargo.toml not found at ${resolvedCargoPath}`, "debug");
1728
+ }
1729
+ }
1730
+ } else {
1731
+ const cargoTomlPath = path7.join(pkgPath, "Cargo.toml");
1732
+ log(`Checking default cargo path for ${name}: ${cargoTomlPath}`, "debug");
1733
+ if (fs9.existsSync(cargoTomlPath)) {
1734
+ log(`Found Cargo.toml for ${name} at ${cargoTomlPath}, updating...`, "debug");
1735
+ updatePackageVersion(cargoTomlPath, nextVersion, this.dryRun);
1736
+ } else {
1737
+ log(`Cargo.toml not found for ${name} at ${cargoTomlPath}`, "debug");
1738
+ }
1739
+ }
1740
+ } else {
1741
+ log(`Cargo disabled for ${name}`, "debug");
1742
+ }
1743
+ const packageTag = formatTag(
1744
+ nextVersion,
1745
+ this.versionPrefix,
1746
+ name,
1747
+ this.tagTemplate,
1748
+ this.fullConfig.packageSpecificTags
1749
+ );
1750
+ addTag(packageTag);
1751
+ tags.push(packageTag);
1752
+ if (this.dryRun) {
1753
+ log(`[DRY RUN] Would create tag: ${packageTag}`, "info");
1754
+ } else {
1755
+ log(`Version ${nextVersion} prepared (tag: ${packageTag})`, "success");
1756
+ }
1757
+ updatedPackagesInfo.push({ name, version: nextVersion, path: pkgPath });
1758
+ }
1759
+ setSharedEntries([...sharedEntriesMap.values()]);
1760
+ if (updatedPackagesInfo.length === 0) {
1761
+ log("No packages required a version update.", "info");
1762
+ return { updatedPackages: [], tags };
1763
+ }
1764
+ const packageNames = updatedPackagesInfo.map((p) => p.name).join(", ");
1765
+ const representativeVersion = updatedPackagesInfo[0]?.version || "multiple";
1766
+ const versionsMatch = updatedPackagesInfo.length <= 1 || updatedPackagesInfo.every((p) => p.version === representativeVersion);
1767
+ let commitMessage = this.commitMessageTemplate || "chore: release";
1768
+ const MAX_COMMIT_MSG_LENGTH = 1e4;
1769
+ if (commitMessage.length > MAX_COMMIT_MSG_LENGTH) {
1770
+ log("Commit message template too long, truncating", "warning");
1771
+ commitMessage = commitMessage.slice(0, MAX_COMMIT_MSG_LENGTH);
1772
+ }
1773
+ const placeholderRegex = /\$\{[^{}$]{1,1000}\}/;
1774
+ if (placeholderRegex.test(commitMessage)) {
1775
+ const packageName = updatedPackagesInfo.length === 1 ? updatedPackagesInfo[0].name : packageNames;
1776
+ commitMessage = formatCommitMessage(commitMessage, representativeVersion, packageName);
1777
+ } else {
1778
+ if (versionsMatch) {
1779
+ const formattedVersion = `${formatVersionPrefix(this.versionPrefix)}${representativeVersion}`;
1780
+ commitMessage = `${commitMessage} ${packageNames} ${formattedVersion}`;
1781
+ } else {
1782
+ const packageVersionList = updatedPackagesInfo.map((p) => `${p.name}@${p.version}`).join(", ");
1783
+ commitMessage = `${commitMessage} ${packageVersionList}`;
1784
+ }
1785
+ }
1786
+ setCommitMessage(commitMessage);
1787
+ if (this.dryRun) {
1788
+ log(`[DRY RUN] Would commit with message: "${commitMessage}"`, "info");
1789
+ }
1790
+ return {
1791
+ updatedPackages: updatedPackagesInfo,
1792
+ commitMessage,
1793
+ tags
1794
+ };
1795
+ }
1796
+ };
1797
+
1798
+ // src/core/versionStrategies.ts
1799
+ import fs10 from "fs";
1800
+ import * as path8 from "path";
1801
+ function shouldProcessPackage2(pkg, config) {
1802
+ const pkgName = pkg.packageJson.name;
1803
+ return shouldProcessPackage(pkgName, config.skip);
1804
+ }
1805
+ function updateCargoFiles(packageDir, version, cargoConfig, dryRun = false) {
1806
+ const updatedFiles = [];
1807
+ const cargoEnabled = cargoConfig?.enabled !== false;
1808
+ if (!cargoEnabled) {
1809
+ return updatedFiles;
1810
+ }
1811
+ const cargoPaths = cargoConfig?.paths;
1812
+ if (cargoPaths && cargoPaths.length > 0) {
1813
+ for (const cargoPath of cargoPaths) {
1814
+ const resolvedCargoPath = path8.resolve(packageDir, cargoPath, "Cargo.toml");
1815
+ if (fs10.existsSync(resolvedCargoPath)) {
1816
+ updatePackageVersion(resolvedCargoPath, version, dryRun);
1817
+ updatedFiles.push(resolvedCargoPath);
1818
+ }
1819
+ }
1820
+ } else {
1821
+ const cargoTomlPath = path8.join(packageDir, "Cargo.toml");
1822
+ if (fs10.existsSync(cargoTomlPath)) {
1823
+ updatePackageVersion(cargoTomlPath, version, dryRun);
1824
+ updatedFiles.push(cargoTomlPath);
1825
+ }
1826
+ }
1827
+ return updatedFiles;
1828
+ }
1829
+ function createSyncStrategy(config) {
1830
+ return async (packages) => {
1831
+ try {
1832
+ const {
1833
+ versionPrefix,
1834
+ tagTemplate,
1835
+ baseBranch,
1836
+ branchPattern,
1837
+ commitMessage = `chore: release \${packageName} v\${version}`,
1838
+ prereleaseIdentifier,
1839
+ dryRun,
1840
+ mainPackage
1841
+ } = config;
1842
+ const formattedPrefix = formatVersionPrefix(versionPrefix || "v");
1843
+ let latestTag = await getLatestTag();
1844
+ let mainPkgPath = packages.root;
1845
+ let mainPkgName;
1846
+ let versionSourcePath = mainPkgPath;
1847
+ let versionSourceName;
1848
+ if (mainPackage) {
1849
+ const mainPkg = packages.packages.find((p) => p.packageJson.name === mainPackage);
1850
+ if (mainPkg) {
1851
+ mainPkgPath = mainPkg.dir;
1852
+ mainPkgName = mainPkg.packageJson.name;
1853
+ versionSourcePath = mainPkgPath;
1854
+ versionSourceName = mainPkgName;
1855
+ log(`Using ${mainPkgName} as primary package for version determination`, "info");
1856
+ } else {
1857
+ log(`Main package '${mainPackage}' not found. Using root package for version determination.`, "warning");
1858
+ }
1859
+ } else if (packages.packages.length > 0) {
1860
+ versionSourcePath = packages.packages[0].dir;
1861
+ versionSourceName = packages.packages[0].packageJson.name;
1862
+ log(`No mainPackage specified; using ${versionSourceName} as sync version source`, "info");
1863
+ }
1864
+ if (!mainPkgPath) {
1865
+ mainPkgPath = process.cwd();
1866
+ log(`No valid package path found, using current working directory: ${mainPkgPath}`, "warning");
1867
+ }
1868
+ if (versionSourceName) {
1869
+ const packageSpecificTag = await getLatestTagForPackage(versionSourceName, formattedPrefix, {
1870
+ tagTemplate,
1871
+ packageSpecificTags: config.packageSpecificTags
1872
+ });
1873
+ if (packageSpecificTag) {
1874
+ latestTag = packageSpecificTag;
1875
+ log(`Using package-specific tag for ${versionSourceName}: ${latestTag}`, "debug");
1876
+ } else {
1877
+ log(`No package-specific tag found for ${versionSourceName}, using global tag: ${latestTag}`, "debug");
1878
+ }
1879
+ }
1880
+ const nextVersion = await calculateVersion(config, {
1881
+ latestTag,
1882
+ versionPrefix: formattedPrefix,
1883
+ branchPattern,
1884
+ baseBranch,
1885
+ prereleaseIdentifier,
1886
+ path: versionSourcePath,
1887
+ name: versionSourceName,
1888
+ type: config.type
1889
+ });
1890
+ if (!nextVersion) {
1891
+ const msg = mainPkgName ? `No version change needed for ${mainPkgName}` : "No version change needed";
1892
+ log(msg, "info");
1893
+ return;
1894
+ }
1895
+ const files = [];
1896
+ const updatedPackages = [];
1897
+ const processedPaths = /* @__PURE__ */ new Set();
1898
+ try {
1899
+ if (packages.root) {
1900
+ const rootPkgPath = path8.join(packages.root, "package.json");
1901
+ if (fs10.existsSync(rootPkgPath)) {
1902
+ updatePackageVersion(rootPkgPath, nextVersion, dryRun);
1903
+ files.push(rootPkgPath);
1904
+ updatedPackages.push("root");
1905
+ processedPaths.add(rootPkgPath);
1906
+ const rootCargoFiles = updateCargoFiles(packages.root, nextVersion, config.cargo, dryRun);
1907
+ files.push(...rootCargoFiles);
1908
+ }
1909
+ } else {
1910
+ log("Root package path is undefined, skipping root package.json update", "warning");
1911
+ }
1912
+ } catch (error) {
1913
+ const errMessage = error instanceof Error ? error.message : String(error);
1914
+ log(`Failed to update root package.json: ${errMessage}`, "error");
1915
+ }
1916
+ for (const pkg of packages.packages) {
1917
+ if (!shouldProcessPackage2(pkg, config)) {
1918
+ continue;
1919
+ }
1920
+ const packageJsonPath = path8.join(pkg.dir, "package.json");
1921
+ if (processedPaths.has(packageJsonPath)) {
1922
+ continue;
1923
+ }
1924
+ updatePackageVersion(packageJsonPath, nextVersion, dryRun);
1925
+ files.push(packageJsonPath);
1926
+ updatedPackages.push(pkg.packageJson.name);
1927
+ processedPaths.add(packageJsonPath);
1928
+ const pkgCargoFiles = updateCargoFiles(pkg.dir, nextVersion, config.cargo, dryRun);
1929
+ files.push(...pkgCargoFiles);
1930
+ }
1931
+ if (updatedPackages.length > 0) {
1932
+ log(`Updated ${updatedPackages.length} package(s) to version ${nextVersion}`, "success");
1933
+ } else {
1934
+ log("No packages were updated", "warning");
1935
+ return;
1936
+ }
1937
+ let changelogEntries = [];
1938
+ let revisionRange = "HEAD";
1939
+ try {
1940
+ if (latestTag) {
1941
+ try {
1942
+ execSync("git", ["rev-parse", "--verify", latestTag], {
1943
+ cwd: mainPkgPath,
1944
+ stdio: "ignore"
1945
+ });
1946
+ revisionRange = `${latestTag}..HEAD`;
1947
+ } catch {
1948
+ if (config.strictReachable) {
1949
+ throw new Error(
1950
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
1951
+ );
1952
+ }
1953
+ log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
1954
+ revisionRange = "HEAD";
1955
+ }
1956
+ }
1957
+ changelogEntries = extractChangelogEntriesFromCommits(mainPkgPath, revisionRange);
1958
+ if (changelogEntries.length === 0) {
1959
+ changelogEntries = [
1960
+ {
1961
+ type: "changed",
1962
+ description: `Update version to ${nextVersion}`
1963
+ }
1964
+ ];
1965
+ }
1966
+ } catch (error) {
1967
+ log(`Error extracting changelog entries: ${error instanceof Error ? error.message : String(error)}`, "warning");
1968
+ changelogEntries = [
1969
+ {
1970
+ type: "changed",
1971
+ description: `Update version to ${nextVersion}`
1972
+ }
1973
+ ];
1974
+ }
1975
+ addChangelogData({
1976
+ packageName: mainPkgName || "monorepo",
1977
+ version: nextVersion,
1978
+ previousVersion: latestTag || null,
1979
+ revisionRange,
1980
+ repoUrl: null,
1981
+ entries: changelogEntries
1982
+ });
1983
+ let tagPackageName = null;
1984
+ if (config.packageSpecificTags && packages.packages.length === 1) {
1985
+ tagPackageName = packages.packages[0].packageJson.name;
1986
+ }
1987
+ const workspaceNames = updatedPackages.filter((n) => n !== "root");
1988
+ const commitPackageName = workspaceNames.length > 0 ? workspaceNames.join(", ") : void 0;
1989
+ const nextTag = formatTag(
1990
+ nextVersion,
1991
+ formattedPrefix,
1992
+ tagPackageName,
1993
+ // Only pass tagTemplate when we have a package name to substitute into it.
1994
+ // In multi-package sync mode tagPackageName is null, so omit the template to
1995
+ // avoid a spurious ${packageName} warning and a malformed tag like "-v1.0.0".
1996
+ tagPackageName ? tagTemplate : void 0,
1997
+ config.packageSpecificTags || false
1998
+ );
1999
+ let formattedCommitMessage;
2000
+ const hasPackageNamePlaceholder = commitMessage.includes("${packageName}");
2001
+ if (commitPackageName === void 0 && !hasPackageNamePlaceholder) {
2002
+ formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion, void 0, void 0);
2003
+ } else if (commitPackageName === void 0) {
2004
+ formattedCommitMessage = commitMessage.replace(/\$\{version\}/g, nextVersion).replace(/\$\{packageName\}/g, "").replace(/\$\{scope\}/g, "");
2005
+ } else {
2006
+ formattedCommitMessage = formatCommitMessage(commitMessage, nextVersion, commitPackageName, void 0);
2007
+ }
2008
+ formattedCommitMessage = formattedCommitMessage.replace(/\s{2,}/g, " ").trim();
2009
+ addTag(nextTag);
2010
+ setCommitMessage(formattedCommitMessage);
2011
+ if (!dryRun) {
2012
+ log(`Version ${nextVersion} prepared (tag: ${nextTag})`, "success");
2013
+ } else {
2014
+ log(`Would create tag: ${nextTag}`, "info");
2015
+ }
2016
+ } catch (error) {
2017
+ if (BaseVersionError.isVersionError(error)) {
2018
+ log(`Synced Strategy failed: ${error.message} (${error.code})`, "error");
2019
+ } else {
2020
+ const errorMessage = error instanceof Error ? error.message : String(error);
2021
+ log(`Synced Strategy failed: ${errorMessage}`, "error");
2022
+ }
2023
+ throw error;
2024
+ }
2025
+ };
2026
+ }
2027
+ function createSingleStrategy(config) {
2028
+ return async (packages) => {
2029
+ try {
2030
+ const {
2031
+ mainPackage,
2032
+ versionPrefix,
2033
+ tagTemplate,
2034
+ commitMessage = `chore: release \${packageName} v\${version}`,
2035
+ dryRun
2036
+ } = config;
2037
+ let packageName;
2038
+ if (mainPackage) {
2039
+ packageName = mainPackage;
2040
+ } else if (packages.packages.length === 1) {
2041
+ packageName = packages.packages[0].packageJson.name;
2042
+ } else {
2043
+ throw createVersionError(
2044
+ "INVALID_CONFIG" /* INVALID_CONFIG */,
2045
+ "Single mode requires either mainPackage or exactly one resolved package"
2046
+ );
2047
+ }
2048
+ const pkg = packages.packages.find((p) => p.packageJson.name === packageName);
2049
+ if (!pkg) {
2050
+ throw createVersionError("PACKAGE_NOT_FOUND" /* PACKAGE_NOT_FOUND */, packageName);
2051
+ }
2052
+ const pkgPath = pkg.dir;
2053
+ const formattedPrefix = formatVersionPrefix(versionPrefix || "v");
2054
+ let latestTagResult = await getLatestTagForPackage(packageName, formattedPrefix, {
2055
+ tagTemplate,
2056
+ packageSpecificTags: config.packageSpecificTags
2057
+ });
2058
+ if (!latestTagResult) {
2059
+ const globalTagResult = await getLatestTag();
2060
+ latestTagResult = globalTagResult || "";
2061
+ }
2062
+ const latestTag = latestTagResult;
2063
+ let nextVersion;
2064
+ nextVersion = await calculateVersion(config, {
2065
+ latestTag,
2066
+ versionPrefix: formattedPrefix,
2067
+ branchPattern: config.branchPattern,
2068
+ baseBranch: config.baseBranch,
2069
+ prereleaseIdentifier: config.prereleaseIdentifier,
2070
+ path: pkgPath,
2071
+ name: packageName,
2072
+ type: config.type
2073
+ });
2074
+ if (!nextVersion) {
2075
+ log(`No version change needed for ${packageName}`, "info");
2076
+ return;
2077
+ }
2078
+ let changelogEntries = [];
2079
+ let revisionRange = "HEAD";
2080
+ try {
2081
+ if (latestTag) {
2082
+ try {
2083
+ execSync("git", ["rev-parse", "--verify", latestTag], {
2084
+ cwd: pkgPath,
2085
+ stdio: "ignore"
2086
+ });
2087
+ revisionRange = `${latestTag}..HEAD`;
2088
+ } catch {
2089
+ if (config.strictReachable) {
2090
+ throw new Error(
2091
+ `Cannot generate changelog: tag '${latestTag}' is not reachable from the current commit. When strictReachable is enabled, all tags must be reachable. To allow fallback to all commits, set strictReachable to false.`
2092
+ );
2093
+ }
2094
+ log(`Tag ${latestTag} doesn't exist, using all commits for changelog`, "debug");
2095
+ revisionRange = "HEAD";
2096
+ }
2097
+ } else {
2098
+ revisionRange = "HEAD";
2099
+ }
2100
+ changelogEntries = extractChangelogEntriesFromCommits(pkgPath, revisionRange);
2101
+ if (changelogEntries.length === 0) {
2102
+ changelogEntries = [
2103
+ {
2104
+ type: "changed",
2105
+ description: `Update version to ${nextVersion}`
2106
+ }
2107
+ ];
2108
+ }
2109
+ } catch (error) {
2110
+ log(`Error extracting changelog entries: ${error instanceof Error ? error.message : String(error)}`, "warning");
2111
+ changelogEntries = [
2112
+ {
2113
+ type: "changed",
2114
+ description: `Update version to ${nextVersion}`
2115
+ }
2116
+ ];
2117
+ }
2118
+ let repoUrl;
2119
+ try {
2120
+ const packageJsonPath2 = path8.join(pkgPath, "package.json");
2121
+ if (fs10.existsSync(packageJsonPath2)) {
2122
+ const packageJson = JSON.parse(fs10.readFileSync(packageJsonPath2, "utf8"));
2123
+ if (packageJson.repository) {
2124
+ if (typeof packageJson.repository === "string") {
2125
+ repoUrl = packageJson.repository;
2126
+ } else if (packageJson.repository.url) {
2127
+ repoUrl = packageJson.repository.url;
2128
+ }
2129
+ if (repoUrl?.startsWith("git+") && repoUrl?.endsWith(".git")) {
2130
+ repoUrl = repoUrl.substring(4, repoUrl.length - 4);
2131
+ }
2132
+ }
2133
+ }
2134
+ } catch (error) {
2135
+ log(
2136
+ `Could not determine repository URL for changelog links: ${error instanceof Error ? error.message : String(error)}`,
2137
+ "warning"
2138
+ );
2139
+ }
2140
+ addChangelogData({
2141
+ packageName,
2142
+ version: nextVersion,
2143
+ previousVersion: latestTag || null,
2144
+ revisionRange,
2145
+ repoUrl: repoUrl || null,
2146
+ entries: changelogEntries
2147
+ });
2148
+ const packageJsonPath = path8.join(pkgPath, "package.json");
2149
+ updatePackageVersion(packageJsonPath, nextVersion, dryRun);
2150
+ const filesToCommit = [packageJsonPath];
2151
+ const cargoFiles = updateCargoFiles(pkgPath, nextVersion, config.cargo, dryRun);
2152
+ filesToCommit.push(...cargoFiles);
2153
+ log(`Updated package ${packageName} to version ${nextVersion}`, "success");
2154
+ const tagName = formatTag(nextVersion, formattedPrefix, packageName, tagTemplate, config.packageSpecificTags);
2155
+ const commitMsg = formatCommitMessage(commitMessage, nextVersion, packageName);
2156
+ addTag(tagName);
2157
+ setCommitMessage(commitMsg);
2158
+ if (!dryRun) {
2159
+ log(`Version ${nextVersion} prepared (tag: ${tagName})`, "success");
2160
+ } else {
2161
+ log(`Would create tag: ${tagName}`, "info");
2162
+ }
2163
+ } catch (error) {
2164
+ if (BaseVersionError.isVersionError(error)) {
2165
+ log(`Single Strategy failed: ${error.message} (${error.code})`, "error");
2166
+ } else {
2167
+ const errorMessage = error instanceof Error ? error.message : String(error);
2168
+ log(`Single Strategy failed: ${errorMessage}`, "error");
2169
+ }
2170
+ throw error;
2171
+ }
2172
+ };
2173
+ }
2174
+ function createAsyncStrategy(config) {
2175
+ const dependencies = {
2176
+ getLatestTag
2177
+ };
2178
+ const processorOptions = {
2179
+ skip: config.skip || [],
2180
+ versionPrefix: config.versionPrefix || "v",
2181
+ tagTemplate: config.tagTemplate,
2182
+ commitMessageTemplate: config.commitMessage || "",
2183
+ dryRun: config.dryRun || false,
2184
+ getLatestTag: dependencies.getLatestTag,
2185
+ fullConfig: config,
2186
+ // Extract common version configuration properties
2187
+ config: {
2188
+ branchPattern: config.branchPattern || [],
2189
+ baseBranch: config.baseBranch || "main",
2190
+ prereleaseIdentifier: config.prereleaseIdentifier,
2191
+ type: config.type
2192
+ }
2193
+ };
2194
+ const packageProcessor = new PackageProcessor(processorOptions);
2195
+ return async (packages, targets = []) => {
2196
+ try {
2197
+ let packagesToProcess = packages.packages;
2198
+ if (targets.length > 0) {
2199
+ const beforeCount = packagesToProcess.length;
2200
+ packagesToProcess = packagesToProcess.filter((pkg) => targets.includes(pkg.packageJson.name));
2201
+ log(
2202
+ `Runtime targets filter: ${beforeCount} \u2192 ${packagesToProcess.length} packages (${targets.join(", ")})`,
2203
+ "info"
2204
+ );
2205
+ }
2206
+ log(`Processing ${packagesToProcess.length} packages`, "info");
2207
+ const result = await packageProcessor.processPackages(packagesToProcess);
2208
+ if (result.updatedPackages.length === 0) {
2209
+ log("No packages required a version update.", "info");
2210
+ } else {
2211
+ const packageNames = result.updatedPackages.map((p) => p.name).join(", ");
2212
+ log(`Updated ${result.updatedPackages.length} package(s): ${packageNames}`, "success");
2213
+ if (result.tags.length > 0) {
2214
+ log(`Created ${result.tags.length} tag(s): ${result.tags.join(", ")}`, "success");
2215
+ }
2216
+ if (result.commitMessage) {
2217
+ log(`Created commit with message: "${result.commitMessage}"`, "success");
2218
+ }
2219
+ }
2220
+ } catch (error) {
2221
+ if (BaseVersionError.isVersionError(error)) {
2222
+ log(`Async Strategy failed: ${error.message} (${error.code})`, "error");
2223
+ } else {
2224
+ const errorMessage = error instanceof Error ? error.message : String(error);
2225
+ log(`Async Strategy failed: ${errorMessage}`, "error");
2226
+ }
2227
+ throw error;
2228
+ }
2229
+ };
2230
+ }
2231
+ function createStrategy(config) {
2232
+ if (config.sync) {
2233
+ return createSyncStrategy(config);
2234
+ }
2235
+ return createAsyncStrategy(config);
2236
+ }
2237
+ function createStrategyMap(config) {
2238
+ return {
2239
+ sync: createSyncStrategy(config),
2240
+ single: createSingleStrategy(config),
2241
+ async: createAsyncStrategy(config)
2242
+ };
2243
+ }
2244
+
2245
+ // src/core/versionEngine.ts
2246
+ import { cwd as cwd2 } from "process";
2247
+ import { getPackagesSync } from "@manypkg/get-packages";
2248
+
2249
+ // src/errors/gitError.ts
2250
+ var GitError = class extends BaseVersionError {
2251
+ };
2252
+
2253
+ // src/utils/packageFiltering.ts
2254
+ import path9 from "path";
2255
+ import micromatch2 from "micromatch";
2256
+ function filterPackagesByConfig(packages, configTargets, workspaceRoot) {
2257
+ if (configTargets.length === 0) {
2258
+ log("No config targets specified, returning all packages", "debug");
2259
+ return packages;
2260
+ }
2261
+ const matchedPackages = /* @__PURE__ */ new Set();
2262
+ for (const target of configTargets) {
2263
+ const dirMatches = filterByDirectoryPattern(packages, target, workspaceRoot);
2264
+ const nameMatches = filterByPackageNamePattern(packages, target);
2265
+ for (const pkg of dirMatches) {
2266
+ matchedPackages.add(pkg);
2267
+ }
2268
+ for (const pkg of nameMatches) {
2269
+ matchedPackages.add(pkg);
2270
+ }
2271
+ }
2272
+ return Array.from(matchedPackages);
2273
+ }
2274
+ function filterByDirectoryPattern(packages, pattern, workspaceRoot) {
2275
+ if (pattern === "./" || pattern === ".") {
2276
+ return packages.filter((pkg) => pkg.dir === workspaceRoot);
2277
+ }
2278
+ const normalizedPattern = pattern.replace(/\\/g, "/");
2279
+ return packages.filter((pkg) => {
2280
+ const relativePath = path9.relative(workspaceRoot, pkg.dir);
2281
+ const normalizedRelativePath = relativePath.replace(/\\/g, "/");
2282
+ if (normalizedPattern === normalizedRelativePath) {
2283
+ return true;
2284
+ }
2285
+ try {
2286
+ return micromatch2.isMatch(normalizedRelativePath, normalizedPattern, {
2287
+ dot: true,
2288
+ noglobstar: false,
2289
+ bash: true
2290
+ });
2291
+ } catch (error) {
2292
+ log(
2293
+ `Invalid directory pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`,
2294
+ "warning"
2295
+ );
2296
+ return false;
2297
+ }
2298
+ });
2299
+ }
2300
+ function filterByPackageNamePattern(packages, pattern) {
2301
+ return packages.filter((pkg) => {
2302
+ if (!pkg.packageJson?.name || typeof pkg.packageJson.name !== "string") {
2303
+ return false;
2304
+ }
2305
+ return matchesPackageNamePattern(pkg.packageJson.name, pattern);
2306
+ });
2307
+ }
2308
+ function matchesPackageNamePattern(packageName, pattern) {
2309
+ if (packageName === pattern) {
2310
+ return true;
2311
+ }
2312
+ if (pattern.startsWith("@") && pattern.endsWith("/*") && !pattern.includes("**")) {
2313
+ const scope = pattern.slice(0, -2);
2314
+ return packageName.startsWith(`${scope}/`);
2315
+ }
2316
+ try {
2317
+ return micromatch2.isMatch(packageName, pattern, {
2318
+ dot: true,
2319
+ contains: false,
2320
+ noglobstar: false,
2321
+ bash: true
2322
+ });
2323
+ } catch (error) {
2324
+ log(
2325
+ `Invalid package name pattern "${pattern}": ${error instanceof Error ? error.message : String(error)}`,
2326
+ "warning"
2327
+ );
2328
+ return false;
2329
+ }
2330
+ }
2331
+
2332
+ // src/core/versionEngine.ts
2333
+ var VersionEngine = class {
2334
+ config;
2335
+ workspaceCache = null;
2336
+ strategies;
2337
+ currentStrategy;
2338
+ constructor(config, _jsonMode = false) {
2339
+ if (!config) {
2340
+ throw createVersionError("CONFIG_REQUIRED" /* CONFIG_REQUIRED */);
2341
+ }
2342
+ if (!config.preset) {
2343
+ config.preset = "conventional-commits";
2344
+ log("No preset specified, using default: conventional-commits", "warning");
2345
+ }
2346
+ this.config = config;
2347
+ this.strategies = createStrategyMap(config);
2348
+ this.currentStrategy = createStrategy(config);
2349
+ }
2350
+ /**
2351
+ * Get workspace packages information - with caching for performance
2352
+ */
2353
+ async getWorkspacePackages() {
2354
+ try {
2355
+ if (this.workspaceCache) {
2356
+ return this.workspaceCache;
2357
+ }
2358
+ const pkgsResult = getPackagesSync(cwd2());
2359
+ if (!pkgsResult || !pkgsResult.packages) {
2360
+ throw createVersionError("PACKAGES_NOT_FOUND" /* PACKAGES_NOT_FOUND */);
2361
+ }
2362
+ if (!pkgsResult.root) {
2363
+ log("Root path is undefined in packages result, setting to current working directory", "warning");
2364
+ pkgsResult.root = cwd2();
2365
+ }
2366
+ if (this.config.packages && this.config.packages.length > 0) {
2367
+ const originalCount = pkgsResult.packages.length;
2368
+ const filteredPackages = filterPackagesByConfig(pkgsResult.packages, this.config.packages, pkgsResult.root);
2369
+ pkgsResult.packages = filteredPackages;
2370
+ log(
2371
+ `Filtered ${originalCount} workspace packages to ${filteredPackages.length} based on packages config`,
2372
+ "info"
2373
+ );
2374
+ if (filteredPackages.length === 0) {
2375
+ log("Warning: No packages matched the specified patterns in config.packages", "warning");
2376
+ }
2377
+ }
2378
+ this.workspaceCache = pkgsResult;
2379
+ return pkgsResult;
2380
+ } catch (error) {
2381
+ const errorMessage = error instanceof Error ? error.message : String(error);
2382
+ log(`Failed to get packages information: ${errorMessage}`, "error");
2383
+ console.error(error);
2384
+ throw createVersionError("WORKSPACE_ERROR" /* WORKSPACE_ERROR */, errorMessage);
2385
+ }
2386
+ }
2387
+ /**
2388
+ * Run the current strategy
2389
+ * @param packages Workspace packages to process
2390
+ * @param targets Optional package targets to process (only used by async strategy)
2391
+ */
2392
+ async run(packages, targets = []) {
2393
+ try {
2394
+ return this.currentStrategy(packages, targets);
2395
+ } catch (error) {
2396
+ if (error instanceof VersionError || error instanceof GitError) {
2397
+ log(`Version engine failed: ${error.message} (${error.code || "UNKNOWN"})`, "error");
2398
+ if (error instanceof GitError) {
2399
+ console.error("Git error details:");
2400
+ if (error.message.includes("Command failed:")) {
2401
+ const cmdOutput = error.message.split("Command failed:")[1];
2402
+ if (cmdOutput) {
2403
+ console.error("Command output:", cmdOutput.trim());
2404
+ }
2405
+ }
2406
+ }
2407
+ } else {
2408
+ const errorMessage = error instanceof Error ? error.message : String(error);
2409
+ log(`Version engine failed: ${errorMessage}`, "error");
2410
+ if (error instanceof Error && error.stack) {
2411
+ console.error("Error stack trace:");
2412
+ console.error(error.stack);
2413
+ }
2414
+ }
2415
+ throw error;
2416
+ }
2417
+ }
2418
+ /**
2419
+ * Change the current strategy
2420
+ * @param strategyType The strategy type to use: 'sync', 'single', or 'async'
2421
+ */
2422
+ setStrategy(strategyType) {
2423
+ this.currentStrategy = this.strategies[strategyType];
2424
+ }
2425
+ };
2426
+
2427
+ export {
2428
+ loadConfig2 as loadConfig,
2429
+ VersionErrorCode,
2430
+ createVersionError,
2431
+ enableJsonOutput,
2432
+ flushPendingWrites,
2433
+ getJsonData,
2434
+ printJsonOutput,
2435
+ log,
2436
+ calculateVersion,
2437
+ PackageProcessor,
2438
+ createSyncStrategy,
2439
+ createSingleStrategy,
2440
+ createAsyncStrategy,
2441
+ VersionEngine
2442
+ };