@codluv/versionguard 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.
- package/dist/calver.d.ts +65 -22
- package/dist/calver.d.ts.map +1 -1
- package/dist/chunks/{index-BrZJDWya.js → index-B3R60bYJ.js} +451 -151
- package/dist/chunks/index-B3R60bYJ.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +248 -25
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/feedback/index.d.ts +1 -1
- package/dist/feedback/index.d.ts.map +1 -1
- package/dist/fix/index.d.ts +6 -1
- package/dist/fix/index.d.ts.map +1 -1
- package/dist/guard.d.ts +45 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -13
- package/dist/init-wizard.d.ts +49 -0
- package/dist/init-wizard.d.ts.map +1 -0
- package/dist/project.d.ts +23 -2
- package/dist/project.d.ts.map +1 -1
- package/dist/sources/git-tag.d.ts +29 -0
- package/dist/sources/git-tag.d.ts.map +1 -1
- package/dist/sources/json.d.ts +31 -0
- package/dist/sources/json.d.ts.map +1 -1
- package/dist/sources/regex.d.ts +30 -0
- package/dist/sources/regex.d.ts.map +1 -1
- package/dist/sources/resolve.d.ts +27 -8
- package/dist/sources/resolve.d.ts.map +1 -1
- package/dist/sources/toml.d.ts +36 -1
- package/dist/sources/toml.d.ts.map +1 -1
- package/dist/sources/utils.d.ts +73 -0
- package/dist/sources/utils.d.ts.map +1 -0
- package/dist/sources/version-file.d.ts +28 -1
- package/dist/sources/version-file.d.ts.map +1 -1
- package/dist/sources/yaml.d.ts +29 -0
- package/dist/sources/yaml.d.ts.map +1 -1
- package/dist/tag/index.d.ts.map +1 -1
- package/dist/types.d.ts +89 -4
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/dist/chunks/index-BrZJDWya.js.map +0 -1
|
@@ -6,38 +6,83 @@ import { parse as parse$2 } from "smol-toml";
|
|
|
6
6
|
import * as yaml from "js-yaml";
|
|
7
7
|
import { globSync } from "glob";
|
|
8
8
|
import { fileURLToPath } from "node:url";
|
|
9
|
+
const VALID_TOKENS = /* @__PURE__ */ new Set([
|
|
10
|
+
"YYYY",
|
|
11
|
+
"YY",
|
|
12
|
+
"0Y",
|
|
13
|
+
"MM",
|
|
14
|
+
"M",
|
|
15
|
+
"0M",
|
|
16
|
+
"WW",
|
|
17
|
+
"0W",
|
|
18
|
+
"DD",
|
|
19
|
+
"D",
|
|
20
|
+
"0D",
|
|
21
|
+
"MICRO",
|
|
22
|
+
"PATCH"
|
|
23
|
+
]);
|
|
24
|
+
const YEAR_TOKENS = /* @__PURE__ */ new Set(["YYYY", "YY", "0Y"]);
|
|
25
|
+
const MONTH_TOKENS = /* @__PURE__ */ new Set(["MM", "M", "0M"]);
|
|
26
|
+
const WEEK_TOKENS = /* @__PURE__ */ new Set(["WW", "0W"]);
|
|
27
|
+
const DAY_TOKENS = /* @__PURE__ */ new Set(["DD", "D", "0D"]);
|
|
28
|
+
const COUNTER_TOKENS = /* @__PURE__ */ new Set(["MICRO", "PATCH"]);
|
|
29
|
+
function isValidCalVerFormat(formatStr) {
|
|
30
|
+
const tokens = formatStr.split(".");
|
|
31
|
+
if (tokens.length < 2) return false;
|
|
32
|
+
if (!tokens.every((t) => VALID_TOKENS.has(t))) return false;
|
|
33
|
+
if (!YEAR_TOKENS.has(tokens[0])) return false;
|
|
34
|
+
const hasWeek = tokens.some((t) => WEEK_TOKENS.has(t));
|
|
35
|
+
const hasMonthOrDay = tokens.some((t) => MONTH_TOKENS.has(t) || DAY_TOKENS.has(t));
|
|
36
|
+
if (hasWeek && hasMonthOrDay) return false;
|
|
37
|
+
const counterIndex = tokens.findIndex((t) => COUNTER_TOKENS.has(t));
|
|
38
|
+
if (counterIndex !== -1 && counterIndex !== tokens.length - 1) return false;
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
9
41
|
function parseFormat(calverFormat) {
|
|
10
|
-
const
|
|
42
|
+
const tokens = calverFormat.split(".");
|
|
11
43
|
const result = {
|
|
12
|
-
year:
|
|
13
|
-
month: parts[1]
|
|
44
|
+
year: tokens[0]
|
|
14
45
|
};
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
46
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
47
|
+
const token = tokens[i];
|
|
48
|
+
if (MONTH_TOKENS.has(token)) {
|
|
49
|
+
result.month = token;
|
|
50
|
+
} else if (WEEK_TOKENS.has(token)) {
|
|
51
|
+
result.week = token;
|
|
52
|
+
} else if (DAY_TOKENS.has(token)) {
|
|
53
|
+
result.day = token;
|
|
54
|
+
} else if (COUNTER_TOKENS.has(token)) {
|
|
55
|
+
result.counter = token;
|
|
56
|
+
}
|
|
22
57
|
}
|
|
23
58
|
return result;
|
|
24
59
|
}
|
|
60
|
+
const MODIFIER_PATTERN = "(?:-([0-9A-Za-z-]+(?:\\.[0-9A-Za-z-]+)*))?";
|
|
25
61
|
function tokenPattern(token) {
|
|
26
62
|
switch (token) {
|
|
27
63
|
case "YYYY":
|
|
28
|
-
return "(\\d{
|
|
64
|
+
return "([1-9]\\d{3})";
|
|
29
65
|
case "YY":
|
|
30
|
-
return "(\\d{
|
|
31
|
-
case "
|
|
32
|
-
|
|
33
|
-
return "(\\d{2})";
|
|
66
|
+
return "(\\d{1,3})";
|
|
67
|
+
case "0Y":
|
|
68
|
+
return "(\\d{2,3})";
|
|
34
69
|
case "MM":
|
|
35
|
-
case "DD":
|
|
36
70
|
case "M":
|
|
71
|
+
return "([1-9]|1[0-2])";
|
|
72
|
+
case "0M":
|
|
73
|
+
return "(0[1-9]|1[0-2])";
|
|
74
|
+
case "WW":
|
|
75
|
+
return "([1-9]|[1-4]\\d|5[0-3])";
|
|
76
|
+
case "0W":
|
|
77
|
+
return "(0[1-9]|[1-4]\\d|5[0-3])";
|
|
78
|
+
case "DD":
|
|
37
79
|
case "D":
|
|
38
|
-
return "(\\d
|
|
80
|
+
return "([1-9]|[12]\\d|3[01])";
|
|
81
|
+
case "0D":
|
|
82
|
+
return "(0[1-9]|[12]\\d|3[01])";
|
|
83
|
+
case "MICRO":
|
|
39
84
|
case "PATCH":
|
|
40
|
-
return "(\\d
|
|
85
|
+
return "(0|[1-9]\\d*)";
|
|
41
86
|
default:
|
|
42
87
|
throw new Error(`Unsupported CalVer token: ${token}`);
|
|
43
88
|
}
|
|
@@ -45,7 +90,7 @@ function tokenPattern(token) {
|
|
|
45
90
|
function getRegexForFormat(calverFormat) {
|
|
46
91
|
const tokens = calverFormat.split(".");
|
|
47
92
|
const pattern = tokens.map(tokenPattern).join("\\.");
|
|
48
|
-
return new RegExp(`^${pattern}$`);
|
|
93
|
+
return new RegExp(`^${pattern}${MODIFIER_PATTERN}$`);
|
|
49
94
|
}
|
|
50
95
|
function parse$1(version, calverFormat) {
|
|
51
96
|
const match = version.match(getRegexForFormat(calverFormat));
|
|
@@ -53,28 +98,44 @@ function parse$1(version, calverFormat) {
|
|
|
53
98
|
return null;
|
|
54
99
|
}
|
|
55
100
|
const definition = parseFormat(calverFormat);
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
101
|
+
const yearToken = definition.year;
|
|
102
|
+
let year = Number.parseInt(match[1], 10);
|
|
103
|
+
if (yearToken === "YY" || yearToken === "0Y") {
|
|
104
|
+
year = 2e3 + year;
|
|
105
|
+
}
|
|
106
|
+
let cursor = 2;
|
|
107
|
+
let month;
|
|
59
108
|
let day;
|
|
60
109
|
let patch;
|
|
110
|
+
if (definition.month) {
|
|
111
|
+
month = Number.parseInt(match[cursor], 10);
|
|
112
|
+
cursor += 1;
|
|
113
|
+
}
|
|
114
|
+
if (definition.week) {
|
|
115
|
+
month = Number.parseInt(match[cursor], 10);
|
|
116
|
+
cursor += 1;
|
|
117
|
+
}
|
|
61
118
|
if (definition.day) {
|
|
62
119
|
day = Number.parseInt(match[cursor], 10);
|
|
63
120
|
cursor += 1;
|
|
64
121
|
}
|
|
65
|
-
if (definition.
|
|
122
|
+
if (definition.counter) {
|
|
66
123
|
patch = Number.parseInt(match[cursor], 10);
|
|
124
|
+
cursor += 1;
|
|
67
125
|
}
|
|
126
|
+
const modifierGroup = match[cursor];
|
|
127
|
+
const modifier = modifierGroup || void 0;
|
|
68
128
|
return {
|
|
69
129
|
year,
|
|
70
|
-
month,
|
|
130
|
+
month: month ?? 1,
|
|
71
131
|
day,
|
|
72
132
|
patch,
|
|
133
|
+
modifier,
|
|
73
134
|
format: calverFormat,
|
|
74
135
|
raw: version
|
|
75
136
|
};
|
|
76
137
|
}
|
|
77
|
-
function validate$2(version, calverFormat, preventFutureDates = true) {
|
|
138
|
+
function validate$2(version, calverFormat, preventFutureDates = true, schemeRules) {
|
|
78
139
|
const errors = [];
|
|
79
140
|
const parsed = parse$1(version, calverFormat);
|
|
80
141
|
if (!parsed) {
|
|
@@ -88,19 +149,26 @@ function validate$2(version, calverFormat, preventFutureDates = true) {
|
|
|
88
149
|
]
|
|
89
150
|
};
|
|
90
151
|
}
|
|
91
|
-
|
|
152
|
+
const definition = parseFormat(calverFormat);
|
|
153
|
+
if (definition.month && (parsed.month < 1 || parsed.month > 12)) {
|
|
92
154
|
errors.push({
|
|
93
155
|
message: `Invalid month: ${parsed.month}. Must be between 1 and 12.`,
|
|
94
156
|
severity: "error"
|
|
95
157
|
});
|
|
96
158
|
}
|
|
159
|
+
if (definition.week && (parsed.month < 1 || parsed.month > 53)) {
|
|
160
|
+
errors.push({
|
|
161
|
+
message: `Invalid week: ${parsed.month}. Must be between 1 and 53.`,
|
|
162
|
+
severity: "error"
|
|
163
|
+
});
|
|
164
|
+
}
|
|
97
165
|
if (parsed.day !== void 0) {
|
|
98
166
|
if (parsed.day < 1 || parsed.day > 31) {
|
|
99
167
|
errors.push({
|
|
100
168
|
message: `Invalid day: ${parsed.day}. Must be between 1 and 31.`,
|
|
101
169
|
severity: "error"
|
|
102
170
|
});
|
|
103
|
-
} else {
|
|
171
|
+
} else if (definition.month) {
|
|
104
172
|
const daysInMonth = new Date(parsed.year, parsed.month, 0).getDate();
|
|
105
173
|
if (parsed.day > daysInMonth) {
|
|
106
174
|
errors.push({
|
|
@@ -120,56 +188,83 @@ function validate$2(version, calverFormat, preventFutureDates = true) {
|
|
|
120
188
|
message: `Future year not allowed: ${parsed.year}. Current year is ${currentYear}.`,
|
|
121
189
|
severity: "error"
|
|
122
190
|
});
|
|
123
|
-
} else if (parsed.year === currentYear && parsed.month > currentMonth) {
|
|
191
|
+
} else if (definition.month && parsed.year === currentYear && parsed.month > currentMonth) {
|
|
124
192
|
errors.push({
|
|
125
193
|
message: `Future month not allowed: ${parsed.year}.${parsed.month}. Current month is ${currentMonth}.`,
|
|
126
194
|
severity: "error"
|
|
127
195
|
});
|
|
128
|
-
} else if (parsed.year === currentYear && parsed.month === currentMonth && parsed.day !== void 0 && parsed.day > currentDay) {
|
|
196
|
+
} else if (definition.month && parsed.year === currentYear && parsed.month === currentMonth && parsed.day !== void 0 && parsed.day > currentDay) {
|
|
129
197
|
errors.push({
|
|
130
198
|
message: `Future day not allowed: ${parsed.year}.${parsed.month}.${parsed.day}. Current day is ${currentDay}.`,
|
|
131
199
|
severity: "error"
|
|
132
200
|
});
|
|
133
201
|
}
|
|
134
202
|
}
|
|
203
|
+
if (parsed.modifier && schemeRules?.allowedModifiers) {
|
|
204
|
+
const baseModifier = parsed.modifier.replace(/[\d.]+$/, "") || parsed.modifier;
|
|
205
|
+
if (!schemeRules.allowedModifiers.includes(baseModifier)) {
|
|
206
|
+
errors.push({
|
|
207
|
+
message: `Modifier "${parsed.modifier}" is not allowed. Allowed: ${schemeRules.allowedModifiers.join(", ")}`,
|
|
208
|
+
severity: "error"
|
|
209
|
+
});
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (schemeRules?.maxNumericSegments) {
|
|
213
|
+
const segmentCount = calverFormat.split(".").length;
|
|
214
|
+
if (segmentCount > schemeRules.maxNumericSegments) {
|
|
215
|
+
errors.push({
|
|
216
|
+
message: `Format has ${segmentCount} segments, convention recommends ${schemeRules.maxNumericSegments} or fewer`,
|
|
217
|
+
severity: "warning"
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
135
221
|
return {
|
|
136
|
-
valid: errors.length === 0,
|
|
222
|
+
valid: errors.filter((e) => e.severity === "error").length === 0,
|
|
137
223
|
errors,
|
|
138
224
|
version: { type: "calver", version: parsed }
|
|
139
225
|
};
|
|
140
226
|
}
|
|
141
227
|
function formatToken(token, value) {
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
228
|
+
switch (token) {
|
|
229
|
+
case "0M":
|
|
230
|
+
case "0D":
|
|
231
|
+
case "0W":
|
|
232
|
+
case "0Y":
|
|
233
|
+
return String(token === "0Y" ? value % 100 : value).padStart(2, "0");
|
|
234
|
+
case "YY":
|
|
235
|
+
return String(value % 100).padStart(2, "0");
|
|
236
|
+
default:
|
|
237
|
+
return String(value);
|
|
147
238
|
}
|
|
148
|
-
return String(value);
|
|
149
239
|
}
|
|
150
240
|
function format$1(version) {
|
|
151
|
-
const
|
|
152
|
-
const
|
|
153
|
-
if (
|
|
154
|
-
|
|
241
|
+
const definition = parseFormat(version.format);
|
|
242
|
+
const parts = [formatToken(definition.year, version.year)];
|
|
243
|
+
if (definition.month) {
|
|
244
|
+
parts.push(formatToken(definition.month, version.month));
|
|
155
245
|
}
|
|
156
|
-
if (
|
|
157
|
-
|
|
246
|
+
if (definition.week) {
|
|
247
|
+
parts.push(formatToken(definition.week, version.month));
|
|
158
248
|
}
|
|
159
|
-
|
|
249
|
+
if (definition.day) {
|
|
250
|
+
parts.push(formatToken(definition.day, version.day ?? 1));
|
|
251
|
+
}
|
|
252
|
+
if (definition.counter) {
|
|
253
|
+
parts.push(formatToken(definition.counter, version.patch ?? 0));
|
|
254
|
+
}
|
|
255
|
+
const base = parts.join(".");
|
|
256
|
+
return version.modifier ? `${base}-${version.modifier}` : base;
|
|
160
257
|
}
|
|
161
258
|
function getCurrentVersion(calverFormat, now = /* @__PURE__ */ new Date()) {
|
|
162
259
|
const definition = parseFormat(calverFormat);
|
|
163
|
-
const currentDay = now.getDate();
|
|
164
260
|
const base = {
|
|
165
261
|
year: now.getFullYear(),
|
|
166
262
|
month: now.getMonth() + 1,
|
|
167
|
-
day: definition.day ?
|
|
168
|
-
patch: definition.
|
|
263
|
+
day: definition.day ? now.getDate() : void 0,
|
|
264
|
+
patch: definition.counter ? 0 : void 0,
|
|
265
|
+
format: calverFormat
|
|
169
266
|
};
|
|
170
|
-
|
|
171
|
-
const patch = base.patch ?? 0;
|
|
172
|
-
return formatToken(definition.year, base.year).concat(`.${formatToken(definition.month, base.month)}`).concat(definition.day ? `.${formatToken(definition.day, day)}` : "").concat(definition.patch ? `.${patch}` : "");
|
|
267
|
+
return format$1(base);
|
|
173
268
|
}
|
|
174
269
|
function compare$1(a, b, calverFormat) {
|
|
175
270
|
const left = parse$1(a, calverFormat);
|
|
@@ -195,12 +290,11 @@ function increment$1(version, calverFormat) {
|
|
|
195
290
|
const next = {
|
|
196
291
|
...parsed
|
|
197
292
|
};
|
|
198
|
-
if (definition.
|
|
199
|
-
|
|
200
|
-
next.patch = patch + 1;
|
|
293
|
+
if (definition.counter) {
|
|
294
|
+
next.patch = (parsed.patch ?? 0) + 1;
|
|
201
295
|
} else {
|
|
202
296
|
next.patch = 0;
|
|
203
|
-
next.format = `${calverFormat}.
|
|
297
|
+
next.format = `${calverFormat}.MICRO`;
|
|
204
298
|
}
|
|
205
299
|
return format$1(next);
|
|
206
300
|
}
|
|
@@ -215,6 +309,7 @@ const calver = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProper
|
|
|
215
309
|
getNextVersions,
|
|
216
310
|
getRegexForFormat,
|
|
217
311
|
increment: increment$1,
|
|
312
|
+
isValidCalVerFormat,
|
|
218
313
|
parse: parse$1,
|
|
219
314
|
parseFormat,
|
|
220
315
|
validate: validate$2
|
|
@@ -246,7 +341,7 @@ function validateChangelog(changelogPath, version, strict = true, requireEntry =
|
|
|
246
341
|
errors.push("Changelog should include compare links at the bottom");
|
|
247
342
|
}
|
|
248
343
|
const versionHeaderMatch = content.match(
|
|
249
|
-
new RegExp(`## \\[${escapeRegExp$
|
|
344
|
+
new RegExp(`## \\[${escapeRegExp$1(version)}\\] - ([^\r
|
|
250
345
|
]+)`)
|
|
251
346
|
);
|
|
252
347
|
if (requireEntry && hasEntryForVersion) {
|
|
@@ -286,7 +381,7 @@ function addVersionEntry(changelogPath, version, date = (/* @__PURE__ */ new Dat
|
|
|
286
381
|
const updated = `${content.slice(0, insertIndex)}${block}${content.slice(insertIndex)}`;
|
|
287
382
|
fs.writeFileSync(changelogPath, updated, "utf-8");
|
|
288
383
|
}
|
|
289
|
-
function escapeRegExp$
|
|
384
|
+
function escapeRegExp$1(value) {
|
|
290
385
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
291
386
|
}
|
|
292
387
|
const HOOK_NAMES$1 = ["pre-commit", "pre-push", "post-tag"];
|
|
@@ -356,8 +451,16 @@ fi
|
|
|
356
451
|
`;
|
|
357
452
|
}
|
|
358
453
|
class GitTagSource {
|
|
454
|
+
/** Human-readable provider name. */
|
|
359
455
|
name = "git-tag";
|
|
456
|
+
/** Empty string since git-tag has no manifest file. */
|
|
360
457
|
manifestFile = "";
|
|
458
|
+
/**
|
|
459
|
+
* Returns `true` when `cwd` is inside a Git repository.
|
|
460
|
+
*
|
|
461
|
+
* @param cwd - Project directory to check.
|
|
462
|
+
* @returns Whether a Git repository is found.
|
|
463
|
+
*/
|
|
361
464
|
exists(cwd) {
|
|
362
465
|
try {
|
|
363
466
|
execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
@@ -369,87 +472,167 @@ class GitTagSource {
|
|
|
369
472
|
return false;
|
|
370
473
|
}
|
|
371
474
|
}
|
|
475
|
+
/**
|
|
476
|
+
* Reads the version string from the latest Git tag.
|
|
477
|
+
*
|
|
478
|
+
* @param cwd - Project directory containing the Git repository.
|
|
479
|
+
* @returns The version string extracted from the latest version tag.
|
|
480
|
+
*/
|
|
372
481
|
getVersion(cwd) {
|
|
373
482
|
try {
|
|
374
|
-
const tag =
|
|
375
|
-
cwd,
|
|
376
|
-
encoding: "utf-8",
|
|
377
|
-
stdio: ["pipe", "pipe", "ignore"]
|
|
378
|
-
}).trim();
|
|
483
|
+
const tag = this.describeVersionTag(cwd);
|
|
379
484
|
return tag.replace(/^v/, "");
|
|
380
485
|
} catch {
|
|
381
|
-
throw new Error("No
|
|
486
|
+
throw new Error("No version tags found. Create a tag first (e.g., git tag v0.1.0)");
|
|
382
487
|
}
|
|
383
488
|
}
|
|
489
|
+
/**
|
|
490
|
+
* Creates a new annotated Git tag for the given version.
|
|
491
|
+
*
|
|
492
|
+
* @param version - Version string to tag.
|
|
493
|
+
* @param cwd - Project directory containing the Git repository.
|
|
494
|
+
*/
|
|
384
495
|
setVersion(version, cwd) {
|
|
385
|
-
const
|
|
496
|
+
const prefix = this.detectPrefix(cwd);
|
|
497
|
+
const tagName = `${prefix}${version}`;
|
|
386
498
|
execFileSync("git", ["tag", "-a", tagName, "-m", `Release ${version}`], {
|
|
387
499
|
cwd,
|
|
388
500
|
stdio: ["pipe", "pipe", "ignore"]
|
|
389
501
|
});
|
|
390
502
|
}
|
|
503
|
+
/** Try version-like tag patterns, fall back to any tag. */
|
|
504
|
+
describeVersionTag(cwd) {
|
|
505
|
+
try {
|
|
506
|
+
return execFileSync("git", ["describe", "--tags", "--abbrev=0", "--match", "v[0-9]*"], {
|
|
507
|
+
cwd,
|
|
508
|
+
encoding: "utf-8",
|
|
509
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
510
|
+
}).trim();
|
|
511
|
+
} catch {
|
|
512
|
+
}
|
|
513
|
+
try {
|
|
514
|
+
return execFileSync("git", ["describe", "--tags", "--abbrev=0", "--match", "[0-9]*"], {
|
|
515
|
+
cwd,
|
|
516
|
+
encoding: "utf-8",
|
|
517
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
518
|
+
}).trim();
|
|
519
|
+
} catch {
|
|
520
|
+
throw new Error("No version tags found");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
/** Detect whether existing tags use a `v` prefix or not. */
|
|
524
|
+
detectPrefix(cwd) {
|
|
525
|
+
try {
|
|
526
|
+
const tag = this.describeVersionTag(cwd);
|
|
527
|
+
return tag.startsWith("v") ? "v" : "";
|
|
528
|
+
} catch {
|
|
529
|
+
return "v";
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
function getNestedValue(obj, dotPath) {
|
|
534
|
+
let current = obj;
|
|
535
|
+
for (const key of dotPath.split(".")) {
|
|
536
|
+
if (current === null || typeof current !== "object") {
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
539
|
+
current = current[key];
|
|
540
|
+
}
|
|
541
|
+
return current;
|
|
542
|
+
}
|
|
543
|
+
function setNestedValue(obj, dotPath, value) {
|
|
544
|
+
const keys = dotPath.split(".");
|
|
545
|
+
let current = obj;
|
|
546
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
547
|
+
const next = current[keys[i]];
|
|
548
|
+
if (typeof next !== "object" || next === null) {
|
|
549
|
+
throw new Error(`Missing intermediate key '${keys.slice(0, i + 1).join(".")}' in manifest`);
|
|
550
|
+
}
|
|
551
|
+
current = next;
|
|
552
|
+
}
|
|
553
|
+
current[keys[keys.length - 1]] = value;
|
|
554
|
+
}
|
|
555
|
+
function escapeRegExp(value) {
|
|
556
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
391
557
|
}
|
|
392
558
|
class JsonVersionSource {
|
|
559
|
+
/** Human-readable provider name. */
|
|
393
560
|
name;
|
|
561
|
+
/** Filename of the JSON manifest (e.g. `'package.json'`). */
|
|
394
562
|
manifestFile;
|
|
563
|
+
/** Dotted key path to the version field within the JSON document. */
|
|
395
564
|
versionPath;
|
|
565
|
+
/**
|
|
566
|
+
* Creates a new JSON version source.
|
|
567
|
+
*
|
|
568
|
+
* @param manifestFile - JSON manifest filename.
|
|
569
|
+
* @param versionPath - Dotted key path to the version field.
|
|
570
|
+
*/
|
|
396
571
|
constructor(manifestFile = "package.json", versionPath = "version") {
|
|
397
572
|
this.name = manifestFile;
|
|
398
573
|
this.manifestFile = manifestFile;
|
|
399
574
|
this.versionPath = versionPath;
|
|
400
575
|
}
|
|
576
|
+
/**
|
|
577
|
+
* Returns `true` when the manifest file exists in `cwd`.
|
|
578
|
+
*
|
|
579
|
+
* @param cwd - Project directory to check.
|
|
580
|
+
* @returns Whether the manifest file exists.
|
|
581
|
+
*/
|
|
401
582
|
exists(cwd) {
|
|
402
583
|
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
403
584
|
}
|
|
585
|
+
/**
|
|
586
|
+
* Reads the version string from the JSON manifest.
|
|
587
|
+
*
|
|
588
|
+
* @param cwd - Project directory containing the manifest.
|
|
589
|
+
* @returns The version string extracted from the manifest.
|
|
590
|
+
*/
|
|
404
591
|
getVersion(cwd) {
|
|
405
592
|
const filePath = path.join(cwd, this.manifestFile);
|
|
406
593
|
if (!fs.existsSync(filePath)) {
|
|
407
594
|
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
408
595
|
}
|
|
409
596
|
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
410
|
-
const version = getNestedValue
|
|
597
|
+
const version = getNestedValue(content, this.versionPath);
|
|
411
598
|
if (typeof version !== "string" || version.length === 0) {
|
|
412
599
|
throw new Error(`No version field in ${this.manifestFile}`);
|
|
413
600
|
}
|
|
414
601
|
return version;
|
|
415
602
|
}
|
|
603
|
+
/**
|
|
604
|
+
* Writes a version string to the JSON manifest, preserving indentation.
|
|
605
|
+
*
|
|
606
|
+
* @param version - Version string to write.
|
|
607
|
+
* @param cwd - Project directory containing the manifest.
|
|
608
|
+
*/
|
|
416
609
|
setVersion(version, cwd) {
|
|
417
610
|
const filePath = path.join(cwd, this.manifestFile);
|
|
418
611
|
if (!fs.existsSync(filePath)) {
|
|
419
612
|
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
420
613
|
}
|
|
421
|
-
const
|
|
614
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
615
|
+
const indentMatch = raw.match(/^(\s+)"/m);
|
|
616
|
+
const indent = indentMatch?.[1]?.length ?? 2;
|
|
617
|
+
const content = JSON.parse(raw);
|
|
422
618
|
setNestedValue(content, this.versionPath, version);
|
|
423
|
-
fs.writeFileSync(filePath, `${JSON.stringify(content, null,
|
|
619
|
+
fs.writeFileSync(filePath, `${JSON.stringify(content, null, indent)}
|
|
424
620
|
`, "utf-8");
|
|
425
621
|
}
|
|
426
622
|
}
|
|
427
|
-
function getNestedValue$1(obj, dotPath) {
|
|
428
|
-
let current = obj;
|
|
429
|
-
for (const key of dotPath.split(".")) {
|
|
430
|
-
if (current === null || typeof current !== "object") {
|
|
431
|
-
return void 0;
|
|
432
|
-
}
|
|
433
|
-
current = current[key];
|
|
434
|
-
}
|
|
435
|
-
return current;
|
|
436
|
-
}
|
|
437
|
-
function setNestedValue(obj, dotPath, value) {
|
|
438
|
-
const keys = dotPath.split(".");
|
|
439
|
-
let current = obj;
|
|
440
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
441
|
-
const key = keys[i];
|
|
442
|
-
if (typeof current[key] !== "object" || current[key] === null) {
|
|
443
|
-
current[key] = {};
|
|
444
|
-
}
|
|
445
|
-
current = current[key];
|
|
446
|
-
}
|
|
447
|
-
current[keys[keys.length - 1]] = value;
|
|
448
|
-
}
|
|
449
623
|
class RegexVersionSource {
|
|
624
|
+
/** Human-readable provider name. */
|
|
450
625
|
name;
|
|
626
|
+
/** Filename of the source manifest (e.g. `'setup.py'`). */
|
|
451
627
|
manifestFile;
|
|
628
|
+
/** Compiled regex used to locate the version string. */
|
|
452
629
|
versionRegex;
|
|
630
|
+
/**
|
|
631
|
+
* Creates a new regex version source.
|
|
632
|
+
*
|
|
633
|
+
* @param manifestFile - Source manifest filename.
|
|
634
|
+
* @param versionRegex - Regex string with at least one capture group for the version.
|
|
635
|
+
*/
|
|
453
636
|
constructor(manifestFile, versionRegex) {
|
|
454
637
|
this.name = manifestFile;
|
|
455
638
|
this.manifestFile = manifestFile;
|
|
@@ -462,9 +645,21 @@ class RegexVersionSource {
|
|
|
462
645
|
throw new Error(`Version regex for ${manifestFile} must contain at least one capture group`);
|
|
463
646
|
}
|
|
464
647
|
}
|
|
648
|
+
/**
|
|
649
|
+
* Returns `true` when the manifest file exists in `cwd`.
|
|
650
|
+
*
|
|
651
|
+
* @param cwd - Project directory to check.
|
|
652
|
+
* @returns Whether the manifest file exists.
|
|
653
|
+
*/
|
|
465
654
|
exists(cwd) {
|
|
466
655
|
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
467
656
|
}
|
|
657
|
+
/**
|
|
658
|
+
* Reads the version string from the source manifest using regex extraction.
|
|
659
|
+
*
|
|
660
|
+
* @param cwd - Project directory containing the manifest.
|
|
661
|
+
* @returns The version string captured by group 1 of the regex.
|
|
662
|
+
*/
|
|
468
663
|
getVersion(cwd) {
|
|
469
664
|
const filePath = path.join(cwd, this.manifestFile);
|
|
470
665
|
if (!fs.existsSync(filePath)) {
|
|
@@ -477,6 +672,12 @@ class RegexVersionSource {
|
|
|
477
672
|
}
|
|
478
673
|
return match[1];
|
|
479
674
|
}
|
|
675
|
+
/**
|
|
676
|
+
* Writes a version string to the source manifest using position-based replacement.
|
|
677
|
+
*
|
|
678
|
+
* @param version - Version string to write.
|
|
679
|
+
* @param cwd - Project directory containing the manifest.
|
|
680
|
+
*/
|
|
480
681
|
setVersion(version, cwd) {
|
|
481
682
|
const filePath = path.join(cwd, this.manifestFile);
|
|
482
683
|
if (!fs.existsSync(filePath)) {
|
|
@@ -494,17 +695,38 @@ class RegexVersionSource {
|
|
|
494
695
|
}
|
|
495
696
|
}
|
|
496
697
|
class TomlVersionSource {
|
|
698
|
+
/** Human-readable provider name. */
|
|
497
699
|
name;
|
|
700
|
+
/** Filename of the TOML manifest (e.g. `'Cargo.toml'`). */
|
|
498
701
|
manifestFile;
|
|
702
|
+
/** Dotted key path to the version field within the TOML document. */
|
|
499
703
|
versionPath;
|
|
704
|
+
/**
|
|
705
|
+
* Creates a new TOML version source.
|
|
706
|
+
*
|
|
707
|
+
* @param manifestFile - TOML manifest filename.
|
|
708
|
+
* @param versionPath - Dotted key path to the version field.
|
|
709
|
+
*/
|
|
500
710
|
constructor(manifestFile = "Cargo.toml", versionPath = "package.version") {
|
|
501
711
|
this.name = manifestFile;
|
|
502
712
|
this.manifestFile = manifestFile;
|
|
503
713
|
this.versionPath = versionPath;
|
|
504
714
|
}
|
|
715
|
+
/**
|
|
716
|
+
* Returns `true` when the manifest file exists in `cwd`.
|
|
717
|
+
*
|
|
718
|
+
* @param cwd - Project directory to check.
|
|
719
|
+
* @returns Whether the manifest file exists.
|
|
720
|
+
*/
|
|
505
721
|
exists(cwd) {
|
|
506
722
|
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
507
723
|
}
|
|
724
|
+
/**
|
|
725
|
+
* Reads the version string from the TOML manifest.
|
|
726
|
+
*
|
|
727
|
+
* @param cwd - Project directory containing the manifest.
|
|
728
|
+
* @returns The version string extracted from the manifest.
|
|
729
|
+
*/
|
|
508
730
|
getVersion(cwd) {
|
|
509
731
|
const filePath = path.join(cwd, this.manifestFile);
|
|
510
732
|
if (!fs.existsSync(filePath)) {
|
|
@@ -518,6 +740,12 @@ class TomlVersionSource {
|
|
|
518
740
|
}
|
|
519
741
|
return version;
|
|
520
742
|
}
|
|
743
|
+
/**
|
|
744
|
+
* Writes a version string to the TOML manifest, preserving formatting.
|
|
745
|
+
*
|
|
746
|
+
* @param version - Version string to write.
|
|
747
|
+
* @param cwd - Project directory containing the manifest.
|
|
748
|
+
*/
|
|
521
749
|
setVersion(version, cwd) {
|
|
522
750
|
const filePath = path.join(cwd, this.manifestFile);
|
|
523
751
|
if (!fs.existsSync(filePath)) {
|
|
@@ -531,6 +759,11 @@ class TomlVersionSource {
|
|
|
531
759
|
}
|
|
532
760
|
fs.writeFileSync(filePath, updated, "utf-8");
|
|
533
761
|
}
|
|
762
|
+
/**
|
|
763
|
+
* Splits the dotted version path into a TOML section name and key name.
|
|
764
|
+
*
|
|
765
|
+
* @returns An object with `section` and `key` components.
|
|
766
|
+
*/
|
|
534
767
|
getSectionKey() {
|
|
535
768
|
const parts = this.versionPath.split(".");
|
|
536
769
|
if (parts.length === 1) {
|
|
@@ -542,21 +775,32 @@ class TomlVersionSource {
|
|
|
542
775
|
};
|
|
543
776
|
}
|
|
544
777
|
}
|
|
545
|
-
function
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
778
|
+
function replaceTomlVersion(content, target, newVersion) {
|
|
779
|
+
const result = replaceInSection(content, target, newVersion);
|
|
780
|
+
if (result !== content) return result;
|
|
781
|
+
if (target.section) {
|
|
782
|
+
const dottedRegex = new RegExp(
|
|
783
|
+
`^(\\s*${escapeRegExp(target.section)}\\.${escapeRegExp(target.key)}\\s*=\\s*)(["'])([^"']*)(\\2)`,
|
|
784
|
+
"m"
|
|
785
|
+
);
|
|
786
|
+
const dottedResult = content.replace(dottedRegex, `$1$2${newVersion}$4`);
|
|
787
|
+
if (dottedResult !== content) return dottedResult;
|
|
552
788
|
}
|
|
553
|
-
|
|
789
|
+
if (target.section) {
|
|
790
|
+
const inlineRegex = new RegExp(
|
|
791
|
+
`^(\\s*${escapeRegExp(target.section)}\\s*=\\s*\\{[^}]*${escapeRegExp(target.key)}\\s*=\\s*)(["'])([^"']*)(\\2)`,
|
|
792
|
+
"m"
|
|
793
|
+
);
|
|
794
|
+
const inlineResult = content.replace(inlineRegex, `$1$2${newVersion}$4`);
|
|
795
|
+
if (inlineResult !== content) return inlineResult;
|
|
796
|
+
}
|
|
797
|
+
return content;
|
|
554
798
|
}
|
|
555
|
-
function
|
|
799
|
+
function replaceInSection(content, target, newVersion) {
|
|
556
800
|
const lines = content.split("\n");
|
|
557
801
|
const sectionHeader = target.section ? `[${target.section}]` : null;
|
|
558
802
|
let inSection = sectionHeader === null;
|
|
559
|
-
const versionRegex = new RegExp(`^(\\s*${escapeRegExp
|
|
803
|
+
const versionRegex = new RegExp(`^(\\s*${escapeRegExp(target.key)}\\s*=\\s*)(["'])([^"']*)(\\2)`);
|
|
560
804
|
for (let i = 0; i < lines.length; i++) {
|
|
561
805
|
const trimmed = lines[i].trim();
|
|
562
806
|
if (sectionHeader !== null) {
|
|
@@ -579,30 +823,56 @@ function replaceTomlVersion(content, target, newVersion) {
|
|
|
579
823
|
}
|
|
580
824
|
return content;
|
|
581
825
|
}
|
|
582
|
-
function escapeRegExp$1(value) {
|
|
583
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
584
|
-
}
|
|
585
826
|
class VersionFileSource {
|
|
827
|
+
/** Human-readable provider name. */
|
|
586
828
|
name;
|
|
829
|
+
/** Filename of the version file (e.g. `'VERSION'`). */
|
|
587
830
|
manifestFile;
|
|
831
|
+
/**
|
|
832
|
+
* Creates a new plain text version file source.
|
|
833
|
+
*
|
|
834
|
+
* @param manifestFile - Version filename.
|
|
835
|
+
*/
|
|
588
836
|
constructor(manifestFile = "VERSION") {
|
|
589
837
|
this.name = manifestFile;
|
|
590
838
|
this.manifestFile = manifestFile;
|
|
591
839
|
}
|
|
840
|
+
/**
|
|
841
|
+
* Returns `true` when the version file exists in `cwd`.
|
|
842
|
+
*
|
|
843
|
+
* @param cwd - Project directory to check.
|
|
844
|
+
* @returns Whether the version file exists.
|
|
845
|
+
*/
|
|
592
846
|
exists(cwd) {
|
|
593
847
|
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
594
848
|
}
|
|
849
|
+
/**
|
|
850
|
+
* Reads the version string from the plain text version file.
|
|
851
|
+
*
|
|
852
|
+
* @param cwd - Project directory containing the version file.
|
|
853
|
+
* @returns The version string from the first line of the file.
|
|
854
|
+
*/
|
|
595
855
|
getVersion(cwd) {
|
|
596
856
|
const filePath = path.join(cwd, this.manifestFile);
|
|
597
857
|
if (!fs.existsSync(filePath)) {
|
|
598
858
|
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
599
859
|
}
|
|
600
|
-
const
|
|
860
|
+
const raw = fs.readFileSync(filePath, "utf-8");
|
|
861
|
+
if (raw.includes("\0")) {
|
|
862
|
+
throw new Error(`${this.manifestFile} appears to be a binary file`);
|
|
863
|
+
}
|
|
864
|
+
const version = raw.split("\n")[0].trim();
|
|
601
865
|
if (version.length === 0) {
|
|
602
866
|
throw new Error(`${this.manifestFile} is empty`);
|
|
603
867
|
}
|
|
604
868
|
return version;
|
|
605
869
|
}
|
|
870
|
+
/**
|
|
871
|
+
* Writes a version string to the plain text version file.
|
|
872
|
+
*
|
|
873
|
+
* @param version - Version string to write.
|
|
874
|
+
* @param cwd - Project directory containing the version file.
|
|
875
|
+
*/
|
|
606
876
|
setVersion(version, cwd) {
|
|
607
877
|
const filePath = path.join(cwd, this.manifestFile);
|
|
608
878
|
if (!fs.existsSync(filePath)) {
|
|
@@ -613,17 +883,38 @@ class VersionFileSource {
|
|
|
613
883
|
}
|
|
614
884
|
}
|
|
615
885
|
class YamlVersionSource {
|
|
886
|
+
/** Human-readable provider name. */
|
|
616
887
|
name;
|
|
888
|
+
/** Filename of the YAML manifest (e.g. `'pubspec.yaml'`). */
|
|
617
889
|
manifestFile;
|
|
890
|
+
/** Dotted key path to the version field within the YAML document. */
|
|
618
891
|
versionKey;
|
|
892
|
+
/**
|
|
893
|
+
* Creates a new YAML version source.
|
|
894
|
+
*
|
|
895
|
+
* @param manifestFile - YAML manifest filename.
|
|
896
|
+
* @param versionKey - Dotted key path to the version field.
|
|
897
|
+
*/
|
|
619
898
|
constructor(manifestFile = "pubspec.yaml", versionKey = "version") {
|
|
620
899
|
this.name = manifestFile;
|
|
621
900
|
this.manifestFile = manifestFile;
|
|
622
901
|
this.versionKey = versionKey;
|
|
623
902
|
}
|
|
903
|
+
/**
|
|
904
|
+
* Returns `true` when the manifest file exists in `cwd`.
|
|
905
|
+
*
|
|
906
|
+
* @param cwd - Project directory to check.
|
|
907
|
+
* @returns Whether the manifest file exists.
|
|
908
|
+
*/
|
|
624
909
|
exists(cwd) {
|
|
625
910
|
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
626
911
|
}
|
|
912
|
+
/**
|
|
913
|
+
* Reads the version string from the YAML manifest.
|
|
914
|
+
*
|
|
915
|
+
* @param cwd - Project directory containing the manifest.
|
|
916
|
+
* @returns The version string extracted from the manifest.
|
|
917
|
+
*/
|
|
627
918
|
getVersion(cwd) {
|
|
628
919
|
const filePath = path.join(cwd, this.manifestFile);
|
|
629
920
|
if (!fs.existsSync(filePath)) {
|
|
@@ -634,7 +925,7 @@ class YamlVersionSource {
|
|
|
634
925
|
if (!parsed || typeof parsed !== "object") {
|
|
635
926
|
throw new Error(`Failed to parse ${this.manifestFile}`);
|
|
636
927
|
}
|
|
637
|
-
const version = parsed
|
|
928
|
+
const version = getNestedValue(parsed, this.versionKey);
|
|
638
929
|
if (typeof version !== "string" || version.length === 0) {
|
|
639
930
|
if (typeof version === "number") {
|
|
640
931
|
return String(version);
|
|
@@ -643,13 +934,21 @@ class YamlVersionSource {
|
|
|
643
934
|
}
|
|
644
935
|
return version;
|
|
645
936
|
}
|
|
937
|
+
/**
|
|
938
|
+
* Writes a version string to the YAML manifest, preserving formatting.
|
|
939
|
+
*
|
|
940
|
+
* @param version - Version string to write.
|
|
941
|
+
* @param cwd - Project directory containing the manifest.
|
|
942
|
+
*/
|
|
646
943
|
setVersion(version, cwd) {
|
|
647
944
|
const filePath = path.join(cwd, this.manifestFile);
|
|
648
945
|
if (!fs.existsSync(filePath)) {
|
|
649
946
|
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
650
947
|
}
|
|
948
|
+
const keyParts = this.versionKey.split(".");
|
|
949
|
+
const leafKey = keyParts[keyParts.length - 1];
|
|
651
950
|
const content = fs.readFileSync(filePath, "utf-8");
|
|
652
|
-
const regex = new RegExp(`^(
|
|
951
|
+
const regex = new RegExp(`^(\\s*${escapeRegExp(leafKey)}:\\s*)(["']?)(.+?)\\2\\s*$`, "m");
|
|
653
952
|
const updated = content.replace(regex, `$1$2${version}$2`);
|
|
654
953
|
if (updated === content) {
|
|
655
954
|
throw new Error(`Could not find version field to update in ${this.manifestFile}`);
|
|
@@ -657,9 +956,6 @@ class YamlVersionSource {
|
|
|
657
956
|
fs.writeFileSync(filePath, updated, "utf-8");
|
|
658
957
|
}
|
|
659
958
|
}
|
|
660
|
-
function escapeRegExp(value) {
|
|
661
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
662
|
-
}
|
|
663
959
|
const VALID_SOURCES = /* @__PURE__ */ new Set([
|
|
664
960
|
"auto",
|
|
665
961
|
"package.json",
|
|
@@ -765,7 +1061,10 @@ function resolveVersionSource(config, cwd = process.cwd()) {
|
|
|
765
1061
|
return provider;
|
|
766
1062
|
}
|
|
767
1063
|
}
|
|
768
|
-
|
|
1064
|
+
const supported = DETECTION_TABLE.map((e) => e.file).join(", ");
|
|
1065
|
+
throw new Error(
|
|
1066
|
+
`No supported manifest file found in ${cwd}. Looked for: ${supported}. Set manifest.source explicitly in .versionguard.yml or create a supported manifest file.`
|
|
1067
|
+
);
|
|
769
1068
|
}
|
|
770
1069
|
function detectManifests(cwd = process.cwd()) {
|
|
771
1070
|
const detected = [];
|
|
@@ -1059,6 +1358,12 @@ function checkHardcodedVersions(expectedVersion, config, ignorePatterns, cwd = p
|
|
|
1059
1358
|
}
|
|
1060
1359
|
return mismatches;
|
|
1061
1360
|
}
|
|
1361
|
+
function getCalVerConfig(config) {
|
|
1362
|
+
if (!config.versioning.calver) {
|
|
1363
|
+
throw new Error('CalVer configuration is required when versioning.type is "calver"');
|
|
1364
|
+
}
|
|
1365
|
+
return config.versioning.calver;
|
|
1366
|
+
}
|
|
1062
1367
|
const CONFIG_FILE_NAMES = [
|
|
1063
1368
|
".versionguard.yml",
|
|
1064
1369
|
".versionguard.yaml",
|
|
@@ -1068,9 +1373,14 @@ const CONFIG_FILE_NAMES = [
|
|
|
1068
1373
|
const DEFAULT_CONFIG = {
|
|
1069
1374
|
versioning: {
|
|
1070
1375
|
type: "semver",
|
|
1376
|
+
schemeRules: {
|
|
1377
|
+
maxNumericSegments: 3,
|
|
1378
|
+
allowedModifiers: ["dev", "alpha", "beta", "rc"]
|
|
1379
|
+
},
|
|
1071
1380
|
calver: {
|
|
1072
1381
|
format: "YYYY.MM.PATCH",
|
|
1073
|
-
preventFutureDates: true
|
|
1382
|
+
preventFutureDates: true,
|
|
1383
|
+
strictMutualExclusion: true
|
|
1074
1384
|
}
|
|
1075
1385
|
},
|
|
1076
1386
|
manifest: {
|
|
@@ -1196,13 +1506,7 @@ function getVersionFeedback(version, config, previousVersion) {
|
|
|
1196
1506
|
if (config.versioning.type === "semver") {
|
|
1197
1507
|
return getSemVerFeedback(version, previousVersion);
|
|
1198
1508
|
}
|
|
1199
|
-
return getCalVerFeedback(version, getCalVerConfig
|
|
1200
|
-
}
|
|
1201
|
-
function getCalVerConfig$2(config) {
|
|
1202
|
-
if (!config.versioning.calver) {
|
|
1203
|
-
throw new Error('CalVer configuration is required when versioning.type is "calver"');
|
|
1204
|
-
}
|
|
1205
|
-
return config.versioning.calver;
|
|
1509
|
+
return getCalVerFeedback(version, getCalVerConfig(config), previousVersion);
|
|
1206
1510
|
}
|
|
1207
1511
|
function getSemVerFeedback(version, previousVersion) {
|
|
1208
1512
|
const errors = [];
|
|
@@ -1218,7 +1522,7 @@ function getSemVerFeedback(version, previousVersion) {
|
|
|
1218
1522
|
});
|
|
1219
1523
|
suggestions.push({
|
|
1220
1524
|
message: `Remove the 'v' prefix`,
|
|
1221
|
-
fix: `
|
|
1525
|
+
fix: `npx versionguard fix --version ${cleanVersion}`,
|
|
1222
1526
|
autoFixable: true
|
|
1223
1527
|
});
|
|
1224
1528
|
} else if (version.split(".").length === 2) {
|
|
@@ -1228,7 +1532,7 @@ function getSemVerFeedback(version, previousVersion) {
|
|
|
1228
1532
|
});
|
|
1229
1533
|
suggestions.push({
|
|
1230
1534
|
message: `Add patch number (e.g., ${version}.0)`,
|
|
1231
|
-
fix: `
|
|
1535
|
+
fix: `npx versionguard fix --version ${version}.0`,
|
|
1232
1536
|
autoFixable: true
|
|
1233
1537
|
});
|
|
1234
1538
|
} else if (/^\d+\.\d+\.\d+\.\d+$/.test(version)) {
|
|
@@ -1278,7 +1582,7 @@ function getSemVerFeedback(version, previousVersion) {
|
|
|
1278
1582
|
});
|
|
1279
1583
|
suggestions.push({
|
|
1280
1584
|
message: `Version must be greater than ${previousVersion}`,
|
|
1281
|
-
fix: `
|
|
1585
|
+
fix: `npx versionguard fix --version ${increment(previousVersion, "patch")}`,
|
|
1282
1586
|
autoFixable: true
|
|
1283
1587
|
});
|
|
1284
1588
|
} else if (comparison === 0) {
|
|
@@ -1288,7 +1592,7 @@ function getSemVerFeedback(version, previousVersion) {
|
|
|
1288
1592
|
});
|
|
1289
1593
|
suggestions.push({
|
|
1290
1594
|
message: `Bump the version`,
|
|
1291
|
-
fix: `
|
|
1595
|
+
fix: `npx versionguard fix --version ${increment(previousVersion, "patch")}`,
|
|
1292
1596
|
autoFixable: true
|
|
1293
1597
|
});
|
|
1294
1598
|
} else {
|
|
@@ -1358,21 +1662,21 @@ function getCalVerFeedback(version, calverConfig, previousVersion) {
|
|
|
1358
1662
|
if (preventFutureDates && parsed.year > now.getFullYear()) {
|
|
1359
1663
|
suggestions.push({
|
|
1360
1664
|
message: `Use current year (${now.getFullYear()}) or a past year`,
|
|
1361
|
-
fix: `
|
|
1665
|
+
fix: `npx versionguard fix --version ${formatCalVerVersion({ ...parsed, year: now.getFullYear() })}`,
|
|
1362
1666
|
autoFixable: true
|
|
1363
1667
|
});
|
|
1364
1668
|
}
|
|
1365
1669
|
if (preventFutureDates && parsed.year === now.getFullYear() && parsed.month > now.getMonth() + 1) {
|
|
1366
1670
|
suggestions.push({
|
|
1367
1671
|
message: `Current month is ${now.getMonth() + 1}`,
|
|
1368
|
-
fix: `
|
|
1672
|
+
fix: `npx versionguard fix --version ${formatCalVerVersion({ ...parsed, month: now.getMonth() + 1 })}`,
|
|
1369
1673
|
autoFixable: true
|
|
1370
1674
|
});
|
|
1371
1675
|
}
|
|
1372
1676
|
if (preventFutureDates && parsed.year === now.getFullYear() && parsed.month === now.getMonth() + 1 && parsed.day !== void 0 && parsed.day > now.getDate()) {
|
|
1373
1677
|
suggestions.push({
|
|
1374
1678
|
message: `Current day is ${now.getDate()}`,
|
|
1375
|
-
fix: `
|
|
1679
|
+
fix: `npx versionguard fix --version ${formatCalVerVersion({ ...parsed, day: now.getDate() })}`,
|
|
1376
1680
|
autoFixable: true
|
|
1377
1681
|
});
|
|
1378
1682
|
}
|
|
@@ -1386,7 +1690,7 @@ function getCalVerFeedback(version, calverConfig, previousVersion) {
|
|
|
1386
1690
|
});
|
|
1387
1691
|
suggestions.push({
|
|
1388
1692
|
message: `CalVer must increase over time`,
|
|
1389
|
-
fix: `
|
|
1693
|
+
fix: `npx versionguard fix --version ${increment$1(previousVersion, format2)}`,
|
|
1390
1694
|
autoFixable: true
|
|
1391
1695
|
});
|
|
1392
1696
|
}
|
|
@@ -1595,7 +1899,7 @@ function suggestNextVersion(currentVersion, config, changeType) {
|
|
|
1595
1899
|
});
|
|
1596
1900
|
}
|
|
1597
1901
|
} else {
|
|
1598
|
-
const format2 = getCalVerConfig
|
|
1902
|
+
const format2 = getCalVerConfig(config).format;
|
|
1599
1903
|
const currentCal = getCurrentVersion(format2);
|
|
1600
1904
|
suggestions.push({
|
|
1601
1905
|
version: currentCal,
|
|
@@ -1608,12 +1912,6 @@ function suggestNextVersion(currentVersion, config, changeType) {
|
|
|
1608
1912
|
}
|
|
1609
1913
|
return suggestions;
|
|
1610
1914
|
}
|
|
1611
|
-
function getCalVerConfig$1(config) {
|
|
1612
|
-
if (!config.versioning.calver) {
|
|
1613
|
-
throw new Error('CalVer configuration is required when versioning.type is "calver"');
|
|
1614
|
-
}
|
|
1615
|
-
return config.versioning.calver;
|
|
1616
|
-
}
|
|
1617
1915
|
const HOOK_NAMES = ["pre-commit", "pre-push", "post-tag"];
|
|
1618
1916
|
function checkHooksPathOverride(cwd) {
|
|
1619
1917
|
try {
|
|
@@ -1897,7 +2195,8 @@ function getTagPreflightError(config, cwd, expectedVersion, allowAutoFix = false
|
|
|
1897
2195
|
const versionResult = config.versioning.type === "semver" ? validate$1(version) : validate$2(
|
|
1898
2196
|
version,
|
|
1899
2197
|
config.versioning.calver?.format ?? "YYYY.MM.PATCH",
|
|
1900
|
-
config.versioning.calver?.preventFutureDates ?? true
|
|
2198
|
+
config.versioning.calver?.preventFutureDates ?? true,
|
|
2199
|
+
config.versioning.schemeRules
|
|
1901
2200
|
);
|
|
1902
2201
|
if (!versionResult.valid) {
|
|
1903
2202
|
return versionResult.errors[0]?.message ?? `Invalid version: ${version}`;
|
|
@@ -1981,7 +2280,12 @@ function validateVersion(version, config) {
|
|
|
1981
2280
|
return validate$1(version);
|
|
1982
2281
|
}
|
|
1983
2282
|
const calverConfig = getCalVerConfig(config);
|
|
1984
|
-
return validate$2(
|
|
2283
|
+
return validate$2(
|
|
2284
|
+
version,
|
|
2285
|
+
calverConfig.format,
|
|
2286
|
+
calverConfig.preventFutureDates,
|
|
2287
|
+
config.versioning.schemeRules
|
|
2288
|
+
);
|
|
1985
2289
|
}
|
|
1986
2290
|
function validate(config, cwd = process.cwd()) {
|
|
1987
2291
|
const errors = [];
|
|
@@ -2093,12 +2397,6 @@ function canBump(currentVersion, newVersion, config) {
|
|
|
2093
2397
|
}
|
|
2094
2398
|
return { canBump: true };
|
|
2095
2399
|
}
|
|
2096
|
-
function getCalVerConfig(config) {
|
|
2097
|
-
if (!config.versioning.calver) {
|
|
2098
|
-
throw new Error('CalVer configuration is required when versioning.type is "calver"');
|
|
2099
|
-
}
|
|
2100
|
-
return config.versioning.calver;
|
|
2101
|
-
}
|
|
2102
2400
|
function isWorktreeClean(cwd) {
|
|
2103
2401
|
try {
|
|
2104
2402
|
return execSync("git status --porcelain", { cwd, encoding: "utf-8" }).trim().length === 0;
|
|
@@ -2110,20 +2408,22 @@ export {
|
|
|
2110
2408
|
fixChangelog as A,
|
|
2111
2409
|
fixPackageVersion as B,
|
|
2112
2410
|
getAllTags as C,
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2411
|
+
getCalVerConfig as D,
|
|
2412
|
+
getLatestTag as E,
|
|
2413
|
+
getTagFeedback as F,
|
|
2116
2414
|
GitTagSource as G,
|
|
2117
|
-
|
|
2118
|
-
|
|
2415
|
+
getVersionSource as H,
|
|
2416
|
+
initConfig as I,
|
|
2119
2417
|
JsonVersionSource as J,
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2418
|
+
resolveVersionSource as K,
|
|
2419
|
+
semver as L,
|
|
2420
|
+
suggestTagMessage as M,
|
|
2421
|
+
sync as N,
|
|
2422
|
+
syncVersion as O,
|
|
2423
|
+
validateChangelog as P,
|
|
2424
|
+
validateTagForPush as Q,
|
|
2126
2425
|
RegexVersionSource as R,
|
|
2426
|
+
validateVersion as S,
|
|
2127
2427
|
TomlVersionSource as T,
|
|
2128
2428
|
VersionFileSource as V,
|
|
2129
2429
|
YamlVersionSource as Y,
|
|
@@ -2135,7 +2435,7 @@ export {
|
|
|
2135
2435
|
doctor as f,
|
|
2136
2436
|
getConfig as g,
|
|
2137
2437
|
handlePostTag as h,
|
|
2138
|
-
|
|
2438
|
+
isValidCalVerFormat as i,
|
|
2139
2439
|
fixAll as j,
|
|
2140
2440
|
fixSyncIssues as k,
|
|
2141
2441
|
setPackageVersion as l,
|
|
@@ -2154,4 +2454,4 @@ export {
|
|
|
2154
2454
|
checkHuskyBypass as y,
|
|
2155
2455
|
detectManifests as z
|
|
2156
2456
|
};
|
|
2157
|
-
//# sourceMappingURL=index-
|
|
2457
|
+
//# sourceMappingURL=index-B3R60bYJ.js.map
|