verz
Version:
[](https://www.npmjs.com/package/verz)
312 lines (303 loc) • 11 kB
JavaScript
var __defProp = Object.defineProperty;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/index.ts
import { readFileSync as readFileSync2, writeFileSync } from "fs";
import { join as join2 } from "path";
import { Command } from "commander";
import semver from "semver";
// src/lib/logger.ts
import { inspect } from "util";
var COLORS = {
reset: "\x1B[0m",
red: "\x1B[31m",
yellow: "\x1B[33m",
blue: "\x1B[34m",
green: "\x1B[32m",
magenta: "\x1B[35m",
gray: "\x1B[90m",
cyan: "\x1B[36m"
};
var Logger = class {
constructor(_level = "info") {
this._level = _level;
this.COLORS = COLORS;
}
debug(...args) {
if (this.shouldLog("debug"))
console.log(
`${COLORS.gray}[DEBUG] ${COLORS.reset}`,
...args.map((arg) => typeof arg === "object" ? inspect(arg) : arg),
COLORS.reset
);
}
info(...args) {
if (this.shouldLog("info"))
console.log(
`${COLORS.blue}[INFO] ${COLORS.reset}`,
...args.map((arg) => typeof arg === "object" ? inspect(arg) : arg),
COLORS.reset
);
}
warn(...args) {
if (this.shouldLog("warn"))
console.warn(
`${COLORS.yellow}[WARN] ${COLORS.reset}`,
...args.map((arg) => typeof arg === "object" ? inspect(arg) : arg),
COLORS.reset
);
}
error(...args) {
if (this.shouldLog("error"))
console.error(
`${COLORS.red}[ERROR] ${COLORS.reset}`,
...args.map((arg) => typeof arg === "object" ? inspect(arg) : arg),
COLORS.reset
);
}
level(level) {
this._level = level;
}
shouldLog(level) {
const levels = ["error", "warn", "info", "debug"];
return levels.indexOf(this._level) >= levels.indexOf(level);
}
};
var logger_default = new Logger();
// src/lib/exec.ts
import { spawnSync } from "child_process";
// src/lib/config.ts
import { join } from "path";
import { existsSync, readFileSync } from "fs";
// src/lib/utils.ts
var deepMerge = (target, source) => {
if (typeof source !== "object" || source === null) return source;
const output = __spreadValues({}, target);
for (const key in source) {
if (source[key] === void 0 || source[key] === null) continue;
if (typeof source[key] === "object" && !Array.isArray(source[key])) {
output[key] = deepMerge(output[key] || {}, source[key]);
} else {
output[key] = source[key];
}
}
return output;
};
// src/lib/config.ts
var defaultConfig = {
commit: {
message: "chore: version %v"
},
tag: {
name: "%v"
},
dryRun: false
};
var Config = class {
constructor() {
this.config = defaultConfig;
}
fileLoad() {
return __async(this, null, function* () {
const configFiles = ["verz.config.json", "verz.config.js", "verz.config.mjs"];
for (const file of configFiles) {
const configPath = join(process.cwd(), file);
if (existsSync(configPath)) {
logger_default.debug(`Loading config from ${file}`);
try {
if (file.endsWith(".json")) {
return JSON.parse(readFileSync(configPath, "utf-8"));
} else if (file.endsWith(".mjs")) {
const module = yield import(configPath);
return module.default;
} else {
return __require(configPath);
}
} catch (error) {
logger_default.warn(`Failed to load config from ${file}:`, error);
}
}
}
return {};
});
}
load() {
return __async(this, arguments, function* (override = {}) {
const fileConfig = yield this.fileLoad();
this.config = deepMerge(deepMerge(this.config, fileConfig), override);
return this.config;
});
}
get() {
return this.config;
}
};
var config_default = new Config();
// src/lib/exec.ts
var exec = (command, args, options) => {
var _a, _b;
const config = config_default.get();
logger_default.debug(`Running command${logger_default.COLORS.gray}`, command, args);
if (config.dryRun) return "";
const p = spawnSync(command, args.map(String), {
stdio: "pipe"
});
if (!(options == null ? void 0 : options.ignoreReturnCode)) {
if (p.error) {
throw p.error;
}
if (p.status !== 0) {
throw new Error(`Command failed with exit code ${p.status}`);
}
}
const stdout = (_b = (_a = p.stdout) == null ? void 0 : _a.toString()) != null ? _b : "";
if (stdout) {
}
return stdout;
};
var exec_default = exec;
// src/index.ts
function main() {
return __async(this, null, function* () {
const program = new Command();
program.name("verz").description("Version bumping tool").option("--patch", "bump patch version").option("--minor", "bump minor version").option("--major", "bump major version").option("--prerelease [preid]", "bump to prerelease version (optionally specify preid: alpha, beta, rc, etc.)").option("--version <version>", "set exact version (e.g., 1.2.3)").option("--commit.message <message>", "custom commit message").option("-v, --verbose", "enable verbose logging").option("--dry-run", "dry run").addHelpText(
"after",
"\nAt least one of --patch, --minor, --major, --prerelease, or --version must be specified."
).action((options) => __async(null, null, function* () {
var _a;
if (options.verbose) {
logger_default.level("debug");
}
try {
let type;
let preid;
let newVersion;
const versionBumpOptions = [
options.patch ? "patch" : null,
options.minor ? "minor" : null,
options.major ? "major" : null,
options.prerelease !== void 0 ? "prerelease" : null,
options.version ? "version" : null
].filter(Boolean);
if (versionBumpOptions.length === 0)
throw new Error("You must specify one of: --patch, --minor, --major, --prerelease, or --version");
if (versionBumpOptions.length > 1)
throw new Error(
`Only one version bump option can be specified. Found: ${versionBumpOptions.join(", ")}`
);
if (options.version) {
if (!semver.valid(options.version))
throw new Error(
`Invalid version specified: ${options.version}. Must be a valid semver version.`
);
newVersion = options.version;
} else if (options.prerelease !== void 0) {
type = "prerelease";
preid = typeof options.prerelease === "string" ? options.prerelease : "rc";
} else if (options.major) {
type = "major";
} else if (options.minor) {
type = "minor";
} else if (options.patch) {
type = "patch";
}
const cliConfig = {
commit: {
message: options["commit.message"]
},
dryRun: options["dryRun"]
};
yield config_default.load(cliConfig);
const config = config_default.get();
const packagePath = join2(process.cwd(), "package.json");
const packageContent = readFileSync2(packagePath, "utf-8");
const packageJson = JSON.parse(packageContent);
logger_default.info(`Current version${logger_default.COLORS.magenta}`, packageJson.version);
if (!newVersion) {
if (!type) throw new Error("No version bump type specified");
const calculatedVersion = semver.inc(packageJson.version, type, false, preid || "rc");
if (!calculatedVersion) throw new Error(`Failed to bump version: ${packageJson.version}`);
newVersion = calculatedVersion;
}
const packageJsonStatus = exec_default("git", ["status", "--porcelain", "package.json"], {
ignoreReturnCode: true
});
if (packageJsonStatus.length > 0) {
throw new Error("package.json has uncommitted changes. Please commit or stash them first.");
}
const hasChanges = exec_default("git", ["diff", "HEAD"], {
ignoreReturnCode: true
}).length > 0;
if (hasChanges) {
exec_default("git", ["stash", "push", "--keep-index"]);
}
packageJson.version = newVersion;
logger_default.debug(`Updating ${logger_default.COLORS.gray}package.json`);
const match = packageContent.match(/^(?:( +)|\t+)/m);
const indent = (_a = match == null ? void 0 : match[0]) != null ? _a : " ";
if (!config.dryRun) writeFileSync(packagePath, JSON.stringify(packageJson, null, indent) + "\n");
try {
exec_default("git", ["add", "package.json"]);
const commitMessage = config.commit.message.replace("%v", newVersion);
logger_default.info(`Committing changes with message${logger_default.COLORS.magenta}`, commitMessage);
exec_default("git", ["commit", "-m", commitMessage]);
const tagName = config.tag.name.replace("%v", newVersion);
logger_default.info(`Creating tag${logger_default.COLORS.magenta}`, tagName);
exec_default("git", ["tag", tagName]);
} catch (e) {
logger_default.error("Failed to commit and tag:", e);
} finally {
if (hasChanges) {
exec_default("git", ["stash", "pop"]);
}
}
logger_default.info(`Version bumped to${logger_default.COLORS.magenta}`, newVersion);
} catch (error) {
logger_default.error(error);
process.exit(1);
}
}));
program.parse();
});
}
main();