@codluv/versionguard 0.2.0 → 0.3.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/chunks/{index-BwE_OaV3.js → index-BrZJDWya.js} +528 -53
- package/dist/chunks/index-BrZJDWya.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +24 -11
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/fix/index.d.ts +2 -2
- package/dist/fix/index.d.ts.map +1 -1
- package/dist/guard.d.ts.map +1 -1
- package/dist/hooks.d.ts.map +1 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +31 -22
- package/dist/project.d.ts +33 -10
- package/dist/project.d.ts.map +1 -1
- package/dist/sources/git-tag.d.ts +23 -0
- package/dist/sources/git-tag.d.ts.map +1 -0
- package/dist/sources/index.d.ts +15 -0
- package/dist/sources/index.d.ts.map +1 -0
- package/dist/sources/json.d.ts +22 -0
- package/dist/sources/json.d.ts.map +1 -0
- package/dist/sources/provider.d.ts +25 -0
- package/dist/sources/provider.d.ts.map +1 -0
- package/dist/sources/regex.d.ts +26 -0
- package/dist/sources/regex.d.ts.map +1 -0
- package/dist/sources/resolve.d.ts +35 -0
- package/dist/sources/resolve.d.ts.map +1 -0
- package/dist/sources/toml.d.ts +25 -0
- package/dist/sources/toml.d.ts.map +1 -0
- package/dist/sources/version-file.d.ts +24 -0
- package/dist/sources/version-file.d.ts.map +1 -0
- package/dist/sources/yaml.d.ts +24 -0
- package/dist/sources/yaml.d.ts.map +1 -0
- package/dist/types.d.ts +49 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -2
- package/dist/chunks/index-BwE_OaV3.js.map +0 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import * as childProcess from "node:child_process";
|
|
2
|
-
import { execSync } from "node:child_process";
|
|
2
|
+
import { execFileSync, execSync } from "node:child_process";
|
|
3
3
|
import * as path from "node:path";
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
|
+
import { parse as parse$2 } from "smol-toml";
|
|
6
|
+
import * as yaml from "js-yaml";
|
|
5
7
|
import { globSync } from "glob";
|
|
6
8
|
import { fileURLToPath } from "node:url";
|
|
7
|
-
import * as yaml from "js-yaml";
|
|
8
9
|
function parseFormat(calverFormat) {
|
|
9
10
|
const parts = calverFormat.split(".");
|
|
10
11
|
const result = {
|
|
@@ -245,7 +246,7 @@ function validateChangelog(changelogPath, version, strict = true, requireEntry =
|
|
|
245
246
|
errors.push("Changelog should include compare links at the bottom");
|
|
246
247
|
}
|
|
247
248
|
const versionHeaderMatch = content.match(
|
|
248
|
-
new RegExp(`## \\[${escapeRegExp(version)}\\] - ([^\r
|
|
249
|
+
new RegExp(`## \\[${escapeRegExp$2(version)}\\] - ([^\r
|
|
249
250
|
]+)`)
|
|
250
251
|
);
|
|
251
252
|
if (requireEntry && hasEntryForVersion) {
|
|
@@ -285,7 +286,7 @@ function addVersionEntry(changelogPath, version, date = (/* @__PURE__ */ new Dat
|
|
|
285
286
|
const updated = `${content.slice(0, insertIndex)}${block}${content.slice(insertIndex)}`;
|
|
286
287
|
fs.writeFileSync(changelogPath, updated, "utf-8");
|
|
287
288
|
}
|
|
288
|
-
function escapeRegExp(value) {
|
|
289
|
+
function escapeRegExp$2(value) {
|
|
289
290
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
290
291
|
}
|
|
291
292
|
const HOOK_NAMES$1 = ["pre-commit", "pre-push", "post-tag"];
|
|
@@ -342,6 +343,7 @@ function areHooksInstalled(cwd = process.cwd()) {
|
|
|
342
343
|
}
|
|
343
344
|
function generateHookScript(hookName) {
|
|
344
345
|
return `#!/bin/sh
|
|
346
|
+
# versionguard
|
|
345
347
|
# VersionGuard ${hookName} hook
|
|
346
348
|
# --no-install prevents accidentally downloading an unscoped package
|
|
347
349
|
# if @codluv/versionguard is not installed locally
|
|
@@ -353,6 +355,428 @@ if [ $status -ne 0 ]; then
|
|
|
353
355
|
fi
|
|
354
356
|
`;
|
|
355
357
|
}
|
|
358
|
+
class GitTagSource {
|
|
359
|
+
name = "git-tag";
|
|
360
|
+
manifestFile = "";
|
|
361
|
+
exists(cwd) {
|
|
362
|
+
try {
|
|
363
|
+
execFileSync("git", ["rev-parse", "--git-dir"], {
|
|
364
|
+
cwd,
|
|
365
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
366
|
+
});
|
|
367
|
+
return true;
|
|
368
|
+
} catch {
|
|
369
|
+
return false;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
getVersion(cwd) {
|
|
373
|
+
try {
|
|
374
|
+
const tag = execFileSync("git", ["describe", "--tags", "--abbrev=0"], {
|
|
375
|
+
cwd,
|
|
376
|
+
encoding: "utf-8",
|
|
377
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
378
|
+
}).trim();
|
|
379
|
+
return tag.replace(/^v/, "");
|
|
380
|
+
} catch {
|
|
381
|
+
throw new Error("No git tags found. Create a tag first (e.g., git tag v0.1.0)");
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
setVersion(version, cwd) {
|
|
385
|
+
const tagName = `v${version}`;
|
|
386
|
+
execFileSync("git", ["tag", "-a", tagName, "-m", `Release ${version}`], {
|
|
387
|
+
cwd,
|
|
388
|
+
stdio: ["pipe", "pipe", "ignore"]
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
class JsonVersionSource {
|
|
393
|
+
name;
|
|
394
|
+
manifestFile;
|
|
395
|
+
versionPath;
|
|
396
|
+
constructor(manifestFile = "package.json", versionPath = "version") {
|
|
397
|
+
this.name = manifestFile;
|
|
398
|
+
this.manifestFile = manifestFile;
|
|
399
|
+
this.versionPath = versionPath;
|
|
400
|
+
}
|
|
401
|
+
exists(cwd) {
|
|
402
|
+
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
403
|
+
}
|
|
404
|
+
getVersion(cwd) {
|
|
405
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
406
|
+
if (!fs.existsSync(filePath)) {
|
|
407
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
408
|
+
}
|
|
409
|
+
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
410
|
+
const version = getNestedValue$1(content, this.versionPath);
|
|
411
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
412
|
+
throw new Error(`No version field in ${this.manifestFile}`);
|
|
413
|
+
}
|
|
414
|
+
return version;
|
|
415
|
+
}
|
|
416
|
+
setVersion(version, cwd) {
|
|
417
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
418
|
+
if (!fs.existsSync(filePath)) {
|
|
419
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
420
|
+
}
|
|
421
|
+
const content = JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
422
|
+
setNestedValue(content, this.versionPath, version);
|
|
423
|
+
fs.writeFileSync(filePath, `${JSON.stringify(content, null, 2)}
|
|
424
|
+
`, "utf-8");
|
|
425
|
+
}
|
|
426
|
+
}
|
|
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
|
+
class RegexVersionSource {
|
|
450
|
+
name;
|
|
451
|
+
manifestFile;
|
|
452
|
+
versionRegex;
|
|
453
|
+
constructor(manifestFile, versionRegex) {
|
|
454
|
+
this.name = manifestFile;
|
|
455
|
+
this.manifestFile = manifestFile;
|
|
456
|
+
try {
|
|
457
|
+
this.versionRegex = new RegExp(versionRegex, "m");
|
|
458
|
+
} catch (err) {
|
|
459
|
+
throw new Error(`Invalid version regex for ${manifestFile}: ${err.message}`);
|
|
460
|
+
}
|
|
461
|
+
if (!/\((?!\?)/.test(versionRegex)) {
|
|
462
|
+
throw new Error(`Version regex for ${manifestFile} must contain at least one capture group`);
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
exists(cwd) {
|
|
466
|
+
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
467
|
+
}
|
|
468
|
+
getVersion(cwd) {
|
|
469
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
470
|
+
if (!fs.existsSync(filePath)) {
|
|
471
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
472
|
+
}
|
|
473
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
474
|
+
const match = content.match(this.versionRegex);
|
|
475
|
+
if (!match?.[1]) {
|
|
476
|
+
throw new Error(`No version match found in ${this.manifestFile}`);
|
|
477
|
+
}
|
|
478
|
+
return match[1];
|
|
479
|
+
}
|
|
480
|
+
setVersion(version, cwd) {
|
|
481
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
482
|
+
if (!fs.existsSync(filePath)) {
|
|
483
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
484
|
+
}
|
|
485
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
486
|
+
const match = this.versionRegex.exec(content);
|
|
487
|
+
if (!match || match.index === void 0) {
|
|
488
|
+
throw new Error(`No version match found in ${this.manifestFile}`);
|
|
489
|
+
}
|
|
490
|
+
const captureStart = match.index + match[0].indexOf(match[1]);
|
|
491
|
+
const captureEnd = captureStart + match[1].length;
|
|
492
|
+
const updated = content.slice(0, captureStart) + version + content.slice(captureEnd);
|
|
493
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
class TomlVersionSource {
|
|
497
|
+
name;
|
|
498
|
+
manifestFile;
|
|
499
|
+
versionPath;
|
|
500
|
+
constructor(manifestFile = "Cargo.toml", versionPath = "package.version") {
|
|
501
|
+
this.name = manifestFile;
|
|
502
|
+
this.manifestFile = manifestFile;
|
|
503
|
+
this.versionPath = versionPath;
|
|
504
|
+
}
|
|
505
|
+
exists(cwd) {
|
|
506
|
+
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
507
|
+
}
|
|
508
|
+
getVersion(cwd) {
|
|
509
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
510
|
+
if (!fs.existsSync(filePath)) {
|
|
511
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
512
|
+
}
|
|
513
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
514
|
+
const parsed = parse$2(content);
|
|
515
|
+
const version = getNestedValue(parsed, this.versionPath);
|
|
516
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
517
|
+
throw new Error(`No version field at '${this.versionPath}' in ${this.manifestFile}`);
|
|
518
|
+
}
|
|
519
|
+
return version;
|
|
520
|
+
}
|
|
521
|
+
setVersion(version, cwd) {
|
|
522
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
523
|
+
if (!fs.existsSync(filePath)) {
|
|
524
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
525
|
+
}
|
|
526
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
527
|
+
const sectionKey = this.getSectionKey();
|
|
528
|
+
const updated = replaceTomlVersion(content, sectionKey, version);
|
|
529
|
+
if (updated === content) {
|
|
530
|
+
throw new Error(`Could not find version field to update in ${this.manifestFile}`);
|
|
531
|
+
}
|
|
532
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
533
|
+
}
|
|
534
|
+
getSectionKey() {
|
|
535
|
+
const parts = this.versionPath.split(".");
|
|
536
|
+
if (parts.length === 1) {
|
|
537
|
+
return { section: "", key: parts[0] };
|
|
538
|
+
}
|
|
539
|
+
return {
|
|
540
|
+
section: parts.slice(0, -1).join("."),
|
|
541
|
+
key: parts[parts.length - 1]
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
function getNestedValue(obj, dotPath) {
|
|
546
|
+
let current = obj;
|
|
547
|
+
for (const key of dotPath.split(".")) {
|
|
548
|
+
if (current === null || typeof current !== "object") {
|
|
549
|
+
return void 0;
|
|
550
|
+
}
|
|
551
|
+
current = current[key];
|
|
552
|
+
}
|
|
553
|
+
return current;
|
|
554
|
+
}
|
|
555
|
+
function replaceTomlVersion(content, target, newVersion) {
|
|
556
|
+
const lines = content.split("\n");
|
|
557
|
+
const sectionHeader = target.section ? `[${target.section}]` : null;
|
|
558
|
+
let inSection = sectionHeader === null;
|
|
559
|
+
const versionRegex = new RegExp(`^(\\s*${escapeRegExp$1(target.key)}\\s*=\\s*)(["'])([^"']*)(\\2)`);
|
|
560
|
+
for (let i = 0; i < lines.length; i++) {
|
|
561
|
+
const trimmed = lines[i].trim();
|
|
562
|
+
if (sectionHeader !== null) {
|
|
563
|
+
if (trimmed === sectionHeader) {
|
|
564
|
+
inSection = true;
|
|
565
|
+
continue;
|
|
566
|
+
}
|
|
567
|
+
if (inSection && trimmed.startsWith("[") && trimmed !== sectionHeader) {
|
|
568
|
+
inSection = false;
|
|
569
|
+
continue;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
if (inSection) {
|
|
573
|
+
const match = lines[i].match(versionRegex);
|
|
574
|
+
if (match) {
|
|
575
|
+
lines[i] = lines[i].replace(versionRegex, `$1$2${newVersion}$4`);
|
|
576
|
+
return lines.join("\n");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return content;
|
|
581
|
+
}
|
|
582
|
+
function escapeRegExp$1(value) {
|
|
583
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
584
|
+
}
|
|
585
|
+
class VersionFileSource {
|
|
586
|
+
name;
|
|
587
|
+
manifestFile;
|
|
588
|
+
constructor(manifestFile = "VERSION") {
|
|
589
|
+
this.name = manifestFile;
|
|
590
|
+
this.manifestFile = manifestFile;
|
|
591
|
+
}
|
|
592
|
+
exists(cwd) {
|
|
593
|
+
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
594
|
+
}
|
|
595
|
+
getVersion(cwd) {
|
|
596
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
597
|
+
if (!fs.existsSync(filePath)) {
|
|
598
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
599
|
+
}
|
|
600
|
+
const version = fs.readFileSync(filePath, "utf-8").trim();
|
|
601
|
+
if (version.length === 0) {
|
|
602
|
+
throw new Error(`${this.manifestFile} is empty`);
|
|
603
|
+
}
|
|
604
|
+
return version;
|
|
605
|
+
}
|
|
606
|
+
setVersion(version, cwd) {
|
|
607
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
608
|
+
if (!fs.existsSync(filePath)) {
|
|
609
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
610
|
+
}
|
|
611
|
+
fs.writeFileSync(filePath, `${version}
|
|
612
|
+
`, "utf-8");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
class YamlVersionSource {
|
|
616
|
+
name;
|
|
617
|
+
manifestFile;
|
|
618
|
+
versionKey;
|
|
619
|
+
constructor(manifestFile = "pubspec.yaml", versionKey = "version") {
|
|
620
|
+
this.name = manifestFile;
|
|
621
|
+
this.manifestFile = manifestFile;
|
|
622
|
+
this.versionKey = versionKey;
|
|
623
|
+
}
|
|
624
|
+
exists(cwd) {
|
|
625
|
+
return fs.existsSync(path.join(cwd, this.manifestFile));
|
|
626
|
+
}
|
|
627
|
+
getVersion(cwd) {
|
|
628
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
629
|
+
if (!fs.existsSync(filePath)) {
|
|
630
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
631
|
+
}
|
|
632
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
633
|
+
const parsed = yaml.load(content);
|
|
634
|
+
if (!parsed || typeof parsed !== "object") {
|
|
635
|
+
throw new Error(`Failed to parse ${this.manifestFile}`);
|
|
636
|
+
}
|
|
637
|
+
const version = parsed[this.versionKey];
|
|
638
|
+
if (typeof version !== "string" || version.length === 0) {
|
|
639
|
+
if (typeof version === "number") {
|
|
640
|
+
return String(version);
|
|
641
|
+
}
|
|
642
|
+
throw new Error(`No version field in ${this.manifestFile}`);
|
|
643
|
+
}
|
|
644
|
+
return version;
|
|
645
|
+
}
|
|
646
|
+
setVersion(version, cwd) {
|
|
647
|
+
const filePath = path.join(cwd, this.manifestFile);
|
|
648
|
+
if (!fs.existsSync(filePath)) {
|
|
649
|
+
throw new Error(`${this.manifestFile} not found in ${cwd}`);
|
|
650
|
+
}
|
|
651
|
+
const content = fs.readFileSync(filePath, "utf-8");
|
|
652
|
+
const regex = new RegExp(`^(${escapeRegExp(this.versionKey)}:\\s*)(["']?)(.+?)\\2\\s*$`, "m");
|
|
653
|
+
const updated = content.replace(regex, `$1$2${version}$2`);
|
|
654
|
+
if (updated === content) {
|
|
655
|
+
throw new Error(`Could not find version field to update in ${this.manifestFile}`);
|
|
656
|
+
}
|
|
657
|
+
fs.writeFileSync(filePath, updated, "utf-8");
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
|
+
function escapeRegExp(value) {
|
|
661
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
662
|
+
}
|
|
663
|
+
const VALID_SOURCES = /* @__PURE__ */ new Set([
|
|
664
|
+
"auto",
|
|
665
|
+
"package.json",
|
|
666
|
+
"composer.json",
|
|
667
|
+
"Cargo.toml",
|
|
668
|
+
"pyproject.toml",
|
|
669
|
+
"pubspec.yaml",
|
|
670
|
+
"pom.xml",
|
|
671
|
+
"VERSION",
|
|
672
|
+
"git-tag",
|
|
673
|
+
"custom"
|
|
674
|
+
]);
|
|
675
|
+
const DETECTION_TABLE = [
|
|
676
|
+
{
|
|
677
|
+
file: "package.json",
|
|
678
|
+
source: "package.json",
|
|
679
|
+
factory: () => new JsonVersionSource("package.json", "version")
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
file: "Cargo.toml",
|
|
683
|
+
source: "Cargo.toml",
|
|
684
|
+
factory: () => new TomlVersionSource("Cargo.toml", "package.version")
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
file: "pyproject.toml",
|
|
688
|
+
source: "pyproject.toml",
|
|
689
|
+
factory: () => new TomlVersionSource("pyproject.toml", "project.version")
|
|
690
|
+
},
|
|
691
|
+
{
|
|
692
|
+
file: "pubspec.yaml",
|
|
693
|
+
source: "pubspec.yaml",
|
|
694
|
+
factory: () => new YamlVersionSource("pubspec.yaml", "version")
|
|
695
|
+
},
|
|
696
|
+
{
|
|
697
|
+
file: "composer.json",
|
|
698
|
+
source: "composer.json",
|
|
699
|
+
factory: () => new JsonVersionSource("composer.json", "version")
|
|
700
|
+
},
|
|
701
|
+
{
|
|
702
|
+
// H-002: Use regex that skips <parent> blocks for pom.xml
|
|
703
|
+
file: "pom.xml",
|
|
704
|
+
source: "pom.xml",
|
|
705
|
+
factory: () => new RegexVersionSource("pom.xml", "<project[^>]*>[\\s\\S]*?<version>([^<]+)</version>")
|
|
706
|
+
},
|
|
707
|
+
{ file: "VERSION", source: "VERSION", factory: () => new VersionFileSource("VERSION") }
|
|
708
|
+
];
|
|
709
|
+
function assertPathContained(manifestFile, cwd) {
|
|
710
|
+
const resolved = path.resolve(cwd, manifestFile);
|
|
711
|
+
const root = path.resolve(cwd);
|
|
712
|
+
if (!resolved.startsWith(`${root}${path.sep}`) && resolved !== root) {
|
|
713
|
+
throw new Error(`Manifest path "${manifestFile}" resolves outside the project directory`);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
function createProvider(source, config, cwd) {
|
|
717
|
+
if (!VALID_SOURCES.has(source)) {
|
|
718
|
+
throw new Error(
|
|
719
|
+
`Invalid manifest source "${source}". Valid sources: ${[...VALID_SOURCES].join(", ")}`
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
switch (source) {
|
|
723
|
+
case "package.json":
|
|
724
|
+
return new JsonVersionSource("package.json", config.path ?? "version");
|
|
725
|
+
case "composer.json":
|
|
726
|
+
return new JsonVersionSource("composer.json", config.path ?? "version");
|
|
727
|
+
case "Cargo.toml":
|
|
728
|
+
return new TomlVersionSource("Cargo.toml", config.path ?? "package.version");
|
|
729
|
+
case "pyproject.toml":
|
|
730
|
+
return new TomlVersionSource("pyproject.toml", config.path ?? "project.version");
|
|
731
|
+
case "pubspec.yaml":
|
|
732
|
+
return new YamlVersionSource("pubspec.yaml", config.path ?? "version");
|
|
733
|
+
case "pom.xml":
|
|
734
|
+
return new RegexVersionSource(
|
|
735
|
+
"pom.xml",
|
|
736
|
+
config.regex ?? "<project[^>]*>[\\s\\S]*?<version>([^<]+)</version>"
|
|
737
|
+
);
|
|
738
|
+
case "VERSION":
|
|
739
|
+
return new VersionFileSource(config.path ?? "VERSION");
|
|
740
|
+
case "git-tag":
|
|
741
|
+
return new GitTagSource();
|
|
742
|
+
case "custom": {
|
|
743
|
+
if (!config.regex) {
|
|
744
|
+
throw new Error("Custom manifest source requires a 'regex' field in manifest config");
|
|
745
|
+
}
|
|
746
|
+
if (!config.path) {
|
|
747
|
+
throw new Error(
|
|
748
|
+
"Custom manifest source requires a 'path' field (manifest filename) in manifest config"
|
|
749
|
+
);
|
|
750
|
+
}
|
|
751
|
+
assertPathContained(config.path, cwd);
|
|
752
|
+
return new RegexVersionSource(config.path, config.regex);
|
|
753
|
+
}
|
|
754
|
+
default:
|
|
755
|
+
throw new Error(`Unknown manifest source: ${source}`);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
function resolveVersionSource(config, cwd = process.cwd()) {
|
|
759
|
+
if (config.source !== "auto") {
|
|
760
|
+
return createProvider(config.source, config, cwd);
|
|
761
|
+
}
|
|
762
|
+
for (const entry of DETECTION_TABLE) {
|
|
763
|
+
const provider = entry.factory();
|
|
764
|
+
if (provider.exists(cwd)) {
|
|
765
|
+
return provider;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
return new JsonVersionSource("package.json", "version");
|
|
769
|
+
}
|
|
770
|
+
function detectManifests(cwd = process.cwd()) {
|
|
771
|
+
const detected = [];
|
|
772
|
+
for (const entry of DETECTION_TABLE) {
|
|
773
|
+
const provider = entry.factory();
|
|
774
|
+
if (provider.exists(cwd)) {
|
|
775
|
+
detected.push(entry.source);
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
return detected;
|
|
779
|
+
}
|
|
356
780
|
function getPackageJsonPath(cwd = process.cwd()) {
|
|
357
781
|
return path.join(cwd, "package.json");
|
|
358
782
|
}
|
|
@@ -367,18 +791,30 @@ function writePackageJson(pkg, cwd = process.cwd()) {
|
|
|
367
791
|
fs.writeFileSync(getPackageJsonPath(cwd), `${JSON.stringify(pkg, null, 2)}
|
|
368
792
|
`, "utf-8");
|
|
369
793
|
}
|
|
370
|
-
function getPackageVersion(cwd = process.cwd()) {
|
|
794
|
+
function getPackageVersion(cwd = process.cwd(), manifest) {
|
|
795
|
+
if (manifest) {
|
|
796
|
+
const provider = resolveVersionSource(manifest, cwd);
|
|
797
|
+
return provider.getVersion(cwd);
|
|
798
|
+
}
|
|
371
799
|
const pkg = readPackageJson(cwd);
|
|
372
800
|
if (typeof pkg.version !== "string" || pkg.version.length === 0) {
|
|
373
801
|
throw new Error("No version field in package.json");
|
|
374
802
|
}
|
|
375
803
|
return pkg.version;
|
|
376
804
|
}
|
|
377
|
-
function setPackageVersion(version, cwd = process.cwd()) {
|
|
805
|
+
function setPackageVersion(version, cwd = process.cwd(), manifest) {
|
|
806
|
+
if (manifest) {
|
|
807
|
+
const provider = resolveVersionSource(manifest, cwd);
|
|
808
|
+
provider.setVersion(version, cwd);
|
|
809
|
+
return;
|
|
810
|
+
}
|
|
378
811
|
const pkg = readPackageJson(cwd);
|
|
379
812
|
pkg.version = version;
|
|
380
813
|
writePackageJson(pkg, cwd);
|
|
381
814
|
}
|
|
815
|
+
function getVersionSource(manifest, cwd = process.cwd()) {
|
|
816
|
+
return resolveVersionSource(manifest, cwd);
|
|
817
|
+
}
|
|
382
818
|
const SEMVER_REGEX = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
|
|
383
819
|
function parse(version) {
|
|
384
820
|
const match = version.match(SEMVER_REGEX);
|
|
@@ -637,6 +1073,9 @@ const DEFAULT_CONFIG = {
|
|
|
637
1073
|
preventFutureDates: true
|
|
638
1074
|
}
|
|
639
1075
|
},
|
|
1076
|
+
manifest: {
|
|
1077
|
+
source: "auto"
|
|
1078
|
+
},
|
|
640
1079
|
sync: {
|
|
641
1080
|
files: ["README.md", "CHANGELOG.md"],
|
|
642
1081
|
patterns: [
|
|
@@ -896,7 +1335,7 @@ function getCalVerFeedback(version, calverConfig, previousVersion) {
|
|
|
896
1335
|
});
|
|
897
1336
|
suggestions.push({
|
|
898
1337
|
message: `Expected format: ${format2}`,
|
|
899
|
-
fix: `Update
|
|
1338
|
+
fix: `Update version to current date: "${getCurrentVersion(format2)}"`,
|
|
900
1339
|
autoFixable: true
|
|
901
1340
|
});
|
|
902
1341
|
return { valid: false, errors, suggestions, canAutoFix: true };
|
|
@@ -1003,7 +1442,7 @@ function getChangelogFeedback(hasEntry, version, latestChangelogVersion) {
|
|
|
1003
1442
|
}
|
|
1004
1443
|
if (latestChangelogVersion && latestChangelogVersion !== version) {
|
|
1005
1444
|
suggestions.push({
|
|
1006
|
-
message: `CHANGELOG.md latest entry is ${latestChangelogVersion}, but
|
|
1445
|
+
message: `CHANGELOG.md latest entry is ${latestChangelogVersion}, but manifest version is ${version}`,
|
|
1007
1446
|
fix: `Make sure versions are in sync`,
|
|
1008
1447
|
autoFixable: false
|
|
1009
1448
|
});
|
|
@@ -1014,7 +1453,7 @@ function getTagFeedback(tagVersion, packageVersion, hasUnsyncedFiles) {
|
|
|
1014
1453
|
const suggestions = [];
|
|
1015
1454
|
if (tagVersion !== packageVersion) {
|
|
1016
1455
|
suggestions.push({
|
|
1017
|
-
message: `Git tag "${tagVersion}" doesn't match
|
|
1456
|
+
message: `Git tag "${tagVersion}" doesn't match manifest version "${packageVersion}"`,
|
|
1018
1457
|
fix: `Delete tag and recreate: git tag -d ${tagVersion} && git tag ${packageVersion}`,
|
|
1019
1458
|
autoFixable: false
|
|
1020
1459
|
});
|
|
@@ -1028,25 +1467,43 @@ function getTagFeedback(tagVersion, packageVersion, hasUnsyncedFiles) {
|
|
|
1028
1467
|
}
|
|
1029
1468
|
return suggestions;
|
|
1030
1469
|
}
|
|
1031
|
-
function fixPackageVersion(targetVersion, cwd = process.cwd()) {
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1470
|
+
function fixPackageVersion(targetVersion, cwd = process.cwd(), manifest) {
|
|
1471
|
+
if (!manifest) {
|
|
1472
|
+
const packagePath = path.join(cwd, "package.json");
|
|
1473
|
+
if (!fs.existsSync(packagePath)) {
|
|
1474
|
+
return { fixed: false, message: "package.json not found" };
|
|
1475
|
+
}
|
|
1476
|
+
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
1477
|
+
const oldVersion2 = typeof pkg.version === "string" ? pkg.version : void 0;
|
|
1478
|
+
if (oldVersion2 === targetVersion) {
|
|
1479
|
+
return { fixed: false, message: `Already at version ${targetVersion}` };
|
|
1480
|
+
}
|
|
1481
|
+
setPackageVersion(targetVersion, cwd);
|
|
1482
|
+
return {
|
|
1483
|
+
fixed: true,
|
|
1484
|
+
message: `Updated package.json from ${oldVersion2} to ${targetVersion}`,
|
|
1485
|
+
file: packagePath
|
|
1486
|
+
};
|
|
1487
|
+
}
|
|
1488
|
+
const provider = getVersionSource(manifest, cwd);
|
|
1489
|
+
let oldVersion;
|
|
1490
|
+
try {
|
|
1491
|
+
oldVersion = provider.getVersion(cwd);
|
|
1492
|
+
} catch {
|
|
1493
|
+
return { fixed: false, message: "Version source not found" };
|
|
1035
1494
|
}
|
|
1036
|
-
const pkg = JSON.parse(fs.readFileSync(packagePath, "utf-8"));
|
|
1037
|
-
const oldVersion = typeof pkg.version === "string" ? pkg.version : void 0;
|
|
1038
1495
|
if (oldVersion === targetVersion) {
|
|
1039
1496
|
return { fixed: false, message: `Already at version ${targetVersion}` };
|
|
1040
1497
|
}
|
|
1041
|
-
|
|
1498
|
+
provider.setVersion(targetVersion, cwd);
|
|
1042
1499
|
return {
|
|
1043
1500
|
fixed: true,
|
|
1044
|
-
message: `Updated
|
|
1045
|
-
file:
|
|
1501
|
+
message: `Updated version from ${oldVersion} to ${targetVersion}`,
|
|
1502
|
+
file: provider.manifestFile ? path.join(cwd, provider.manifestFile) : void 0
|
|
1046
1503
|
};
|
|
1047
1504
|
}
|
|
1048
1505
|
function fixSyncIssues(config, cwd = process.cwd()) {
|
|
1049
|
-
const version = getPackageVersion(cwd);
|
|
1506
|
+
const version = getPackageVersion(cwd, config.manifest);
|
|
1050
1507
|
const results = syncVersion(version, config.sync, cwd).filter((result) => result.updated).map((result) => ({
|
|
1051
1508
|
fixed: true,
|
|
1052
1509
|
message: `Updated ${path.relative(cwd, result.file)} (${result.changes.length} changes)`,
|
|
@@ -1102,9 +1559,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
1102
1559
|
}
|
|
1103
1560
|
function fixAll(config, targetVersion, cwd = process.cwd()) {
|
|
1104
1561
|
const results = [];
|
|
1105
|
-
const version = targetVersion || getPackageVersion(cwd);
|
|
1106
|
-
if (targetVersion && targetVersion !== getPackageVersion(cwd)) {
|
|
1107
|
-
results.push(fixPackageVersion(targetVersion, cwd));
|
|
1562
|
+
const version = targetVersion || getPackageVersion(cwd, config.manifest);
|
|
1563
|
+
if (targetVersion && targetVersion !== getPackageVersion(cwd, config.manifest)) {
|
|
1564
|
+
results.push(fixPackageVersion(targetVersion, cwd, config.manifest));
|
|
1108
1565
|
}
|
|
1109
1566
|
const syncResults = fixSyncIssues(config, cwd);
|
|
1110
1567
|
results.push(...syncResults);
|
|
@@ -1165,6 +1622,15 @@ function checkHooksPathOverride(cwd) {
|
|
|
1165
1622
|
encoding: "utf-8"
|
|
1166
1623
|
}).trim();
|
|
1167
1624
|
if (hooksPath) {
|
|
1625
|
+
const resolved = path.resolve(cwd, hooksPath);
|
|
1626
|
+
const huskyDir = path.resolve(cwd, ".husky");
|
|
1627
|
+
if (resolved === huskyDir || resolved.startsWith(`${huskyDir}${path.sep}`)) {
|
|
1628
|
+
return {
|
|
1629
|
+
code: "HOOKS_PATH_HUSKY",
|
|
1630
|
+
severity: "warning",
|
|
1631
|
+
message: `Husky detected — core.hooksPath is set to "${hooksPath}". Hooks in .git/hooks/ are bypassed. Add versionguard validate to your .husky/pre-commit manually or use a tool like forge-ts that manages .husky/ hooks cooperatively.`
|
|
1632
|
+
};
|
|
1633
|
+
}
|
|
1168
1634
|
return {
|
|
1169
1635
|
code: "HOOKS_PATH_OVERRIDE",
|
|
1170
1636
|
severity: "error",
|
|
@@ -1315,7 +1781,7 @@ function createTag(version, message, autoFix = true, config, cwd = process.cwd()
|
|
|
1315
1781
|
actions
|
|
1316
1782
|
};
|
|
1317
1783
|
}
|
|
1318
|
-
const packageVersion = getPackageVersion(cwd);
|
|
1784
|
+
const packageVersion = getPackageVersion(cwd, config.manifest);
|
|
1319
1785
|
const shouldAutoFix = autoFix;
|
|
1320
1786
|
const preflightError = getTagPreflightError(config, cwd, version, shouldAutoFix);
|
|
1321
1787
|
if (preflightError) {
|
|
@@ -1328,7 +1794,7 @@ function createTag(version, message, autoFix = true, config, cwd = process.cwd()
|
|
|
1328
1794
|
if (version !== packageVersion && !autoFix) {
|
|
1329
1795
|
return {
|
|
1330
1796
|
success: false,
|
|
1331
|
-
message: `Version mismatch:
|
|
1797
|
+
message: `Version mismatch: manifest version is ${packageVersion}, tag is ${version}`,
|
|
1332
1798
|
actions: []
|
|
1333
1799
|
};
|
|
1334
1800
|
}
|
|
@@ -1388,15 +1854,15 @@ function handlePostTag(config, cwd = process.cwd()) {
|
|
|
1388
1854
|
actions
|
|
1389
1855
|
};
|
|
1390
1856
|
}
|
|
1391
|
-
const packageVersion = getPackageVersion(cwd);
|
|
1857
|
+
const packageVersion = getPackageVersion(cwd, config.manifest);
|
|
1392
1858
|
if (tag.version !== packageVersion) {
|
|
1393
1859
|
return {
|
|
1394
1860
|
success: false,
|
|
1395
|
-
message: `Tag version ${tag.version} doesn't match
|
|
1861
|
+
message: `Tag version ${tag.version} doesn't match manifest version ${packageVersion}`,
|
|
1396
1862
|
actions: [
|
|
1397
1863
|
"To fix: delete tag and recreate with correct version",
|
|
1398
1864
|
` git tag -d ${tag.name}`,
|
|
1399
|
-
`
|
|
1865
|
+
` Update manifest to ${tag.version}`,
|
|
1400
1866
|
` git tag ${tag.name}`
|
|
1401
1867
|
]
|
|
1402
1868
|
};
|
|
@@ -1427,7 +1893,7 @@ function getTagPreflightError(config, cwd, expectedVersion, allowAutoFix = false
|
|
|
1427
1893
|
if (hasDirtyWorktree(cwd)) {
|
|
1428
1894
|
return "Working tree must be clean before creating or validating release tags";
|
|
1429
1895
|
}
|
|
1430
|
-
const version = expectedVersion ?? getPackageVersion(cwd);
|
|
1896
|
+
const version = expectedVersion ?? getPackageVersion(cwd, config.manifest);
|
|
1431
1897
|
const versionResult = config.versioning.type === "semver" ? validate$1(version) : validate$2(
|
|
1432
1898
|
version,
|
|
1433
1899
|
config.versioning.calver?.format ?? "YYYY.MM.PATCH",
|
|
@@ -1521,7 +1987,7 @@ function validate(config, cwd = process.cwd()) {
|
|
|
1521
1987
|
const errors = [];
|
|
1522
1988
|
let version;
|
|
1523
1989
|
try {
|
|
1524
|
-
version = getPackageVersion(cwd);
|
|
1990
|
+
version = getPackageVersion(cwd, config.manifest);
|
|
1525
1991
|
} catch (err) {
|
|
1526
1992
|
return {
|
|
1527
1993
|
valid: false,
|
|
@@ -1592,7 +2058,7 @@ function doctor(config, cwd = process.cwd()) {
|
|
|
1592
2058
|
};
|
|
1593
2059
|
}
|
|
1594
2060
|
function sync(config, cwd = process.cwd()) {
|
|
1595
|
-
const version = getPackageVersion(cwd);
|
|
2061
|
+
const version = getPackageVersion(cwd, config.manifest);
|
|
1596
2062
|
syncVersion(version, config.sync, cwd);
|
|
1597
2063
|
}
|
|
1598
2064
|
function canBump(currentVersion, newVersion, config) {
|
|
@@ -1641,30 +2107,39 @@ function isWorktreeClean(cwd) {
|
|
|
1641
2107
|
}
|
|
1642
2108
|
}
|
|
1643
2109
|
export {
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
2110
|
+
fixChangelog as A,
|
|
2111
|
+
fixPackageVersion as B,
|
|
2112
|
+
getAllTags as C,
|
|
2113
|
+
getLatestTag as D,
|
|
2114
|
+
getTagFeedback as E,
|
|
2115
|
+
getVersionSource as F,
|
|
2116
|
+
GitTagSource as G,
|
|
2117
|
+
resolveVersionSource as H,
|
|
2118
|
+
semver as I,
|
|
2119
|
+
JsonVersionSource as J,
|
|
2120
|
+
suggestTagMessage as K,
|
|
2121
|
+
sync as L,
|
|
2122
|
+
syncVersion as M,
|
|
2123
|
+
validateChangelog as N,
|
|
2124
|
+
validateTagForPush as O,
|
|
2125
|
+
validateVersion as P,
|
|
2126
|
+
RegexVersionSource as R,
|
|
2127
|
+
TomlVersionSource as T,
|
|
2128
|
+
VersionFileSource as V,
|
|
2129
|
+
YamlVersionSource as Y,
|
|
2130
|
+
installHooks as a,
|
|
2131
|
+
getPackageVersion as b,
|
|
2132
|
+
getVersionFeedback as c,
|
|
2133
|
+
getSyncFeedback as d,
|
|
2134
|
+
getChangelogFeedback as e,
|
|
2135
|
+
doctor as f,
|
|
1661
2136
|
getConfig as g,
|
|
1662
2137
|
handlePostTag as h,
|
|
1663
2138
|
initConfig as i,
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
2139
|
+
fixAll as j,
|
|
2140
|
+
fixSyncIssues as k,
|
|
2141
|
+
setPackageVersion as l,
|
|
2142
|
+
createTag as m,
|
|
1668
2143
|
areHooksInstalled as n,
|
|
1669
2144
|
calver as o,
|
|
1670
2145
|
canBump as p,
|
|
@@ -1677,6 +2152,6 @@ export {
|
|
|
1677
2152
|
checkHookIntegrity as w,
|
|
1678
2153
|
checkHooksPathOverride as x,
|
|
1679
2154
|
checkHuskyBypass as y,
|
|
1680
|
-
|
|
2155
|
+
detectManifests as z
|
|
1681
2156
|
};
|
|
1682
|
-
//# sourceMappingURL=index-
|
|
2157
|
+
//# sourceMappingURL=index-BrZJDWya.js.map
|