mkver
Version:
Node.js access to your app's version and release metadata
195 lines (193 loc) • 7.41 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.mkver = exports.fmtYMDHMS = void 0;
const node_child_process_1 = require("node:child_process");
const promises_1 = require("node:fs/promises");
const node_path_1 = require("node:path");
const node_process_1 = require("node:process");
const node_util_1 = require("node:util");
const semver = require("semver");
const execFileP = (0, node_util_1.promisify)(node_child_process_1.execFile);
function notBlank(s) {
return s != null && String(s).trim().length > 0;
}
function findPackageVersion(dir) {
return __awaiter(this, void 0, void 0, function* () {
const path = (0, node_path_1.resolve)((0, node_path_1.join)(dir, "package.json"));
try {
const json = JSON.parse((yield (0, promises_1.readFile)(path)).toString());
if (json != null) {
if (notBlank(json.version)) {
return { version: json.version, dir };
}
else {
throw new Error("No `version` field was found in " + path);
}
}
}
catch (err) {
const parent = (0, node_path_1.resolve)((0, node_path_1.join)(dir, ".."));
if ((0, node_path_1.resolve)(dir) !== parent) {
return findPackageVersion(parent);
}
else {
throw err;
}
}
});
}
function headSha(cwd) {
return __awaiter(this, void 0, void 0, function* () {
const gitSha = (yield execFileP("git", ["rev-parse", "-q", "HEAD"], { cwd })).stdout
.toString()
.trim();
if (gitSha.length < 40) {
throw new Error("Unexpected git SHA: " + gitSha);
}
else {
return gitSha;
}
});
}
function headUnixtime(cwd) {
return __awaiter(this, void 0, void 0, function* () {
const unixtimeStr = (yield execFileP("git", ["log", "-1", "--pretty=format:%ct"], {
cwd,
})).stdout.toString();
const unixtime = parseInt(unixtimeStr);
const date = new Date(unixtime * 1000);
if (date > new Date() || date < new Date(2000, 0, 1)) {
throw new Error("Unexpected unixtime for commit: " + unixtime);
}
return date;
});
}
// NOT FOR GENERAL USE. Only works for positive values.
function pad2(i) {
const s = String(i);
return s.length >= 2 ? s : ("0" + s).slice(-2);
}
/**
* Appropriate for filenames: yMMddHHmmss
*/
function fmtYMDHMS(d) {
return (d.getFullYear() +
pad2(d.getMonth() + 1) +
pad2(d.getDate()) +
pad2(d.getHours()) +
pad2(d.getMinutes()) +
pad2(d.getSeconds()));
}
exports.fmtYMDHMS = fmtYMDHMS;
function renderVersionInfo(o) {
const msg = [];
const ext = o.path.ext.toLowerCase();
const cjs = ext === ".js";
const mjs = ext === ".mjs";
const ts = ext === ".ts";
if (!cjs && !mjs && !ts) {
throw new Error(`Unsupported file extension (expected output, ${JSON.stringify(o.path)}, to end in .ts, .js, or .mjs)`);
}
if (cjs) {
msg.push(`"use strict";`, `Object.defineProperty(exports, "__esModule", { value: true });`);
}
const parsed = semver.parse(o.version);
const fields = [];
for (const { field, value } of [
{ field: "version", value: o.version },
{ field: "versionMajor", value: parsed === null || parsed === void 0 ? void 0 : parsed.major },
{ field: "versionMinor", value: parsed === null || parsed === void 0 ? void 0 : parsed.minor },
{ field: "versionPatch", value: parsed === null || parsed === void 0 ? void 0 : parsed.patch },
{ field: "versionPrerelease", value: parsed === null || parsed === void 0 ? void 0 : parsed.prerelease },
{ field: "release", value: o.release },
{ field: "gitSha", value: o.gitSha },
{ field: "gitDate", value: o.gitDate },
]) {
if (value != null) {
fields.push(field);
const strVal = value instanceof Date
? `new Date(${value.getTime()})`
: JSON.stringify(value);
const ea = `${field} = ${strVal}`;
msg.push(cjs ? `exports.${ea};` : `export const ${ea};`);
}
}
if (ts || mjs) {
msg.push(`export default {${fields.join(",")}};`);
}
return msg.join("\n") + "\n";
}
/**
* Writes a file with version and release metadata to `output`
*
* @param output - The file to write to. Defaults to "./Version.ts". File format
* is determined by the file extension. Supported extensions are ".ts", ".js",
* and ".mjs".
* @returns The version and release metadata written to the file.
*/
function mkver(output) {
return __awaiter(this, void 0, void 0, function* () {
if (output == null || output.trim().length === 0) {
output = "./Version.ts";
}
const file = (0, node_path_1.resolve)((0, node_path_1.normalize)(output));
const parsed = (0, node_path_1.parse)(file);
const v = yield findPackageVersion(parsed.dir);
if (v == null) {
throw new Error("No package.json was found in " + parsed.dir + " or parent directories.");
}
const gitSha = yield headSha(v.dir);
const gitDate = yield headUnixtime(v.dir);
const versionInfo = {
path: parsed,
version: v.version,
release: `${v.version}+${fmtYMDHMS(gitDate)}`,
gitSha,
gitDate,
};
const buf = renderVersionInfo(versionInfo);
try {
yield (0, promises_1.mkdir)(parsed.dir, { recursive: true });
}
catch (err) {
if (err.code !== "EEXIST")
throw err;
}
yield (0, promises_1.writeFile)(file, buf);
return versionInfo;
});
}
exports.mkver = mkver;
function main() {
return __awaiter(this, void 0, void 0, function* () {
var _a;
const arg = (_a = node_process_1.argv[2]) !== null && _a !== void 0 ? _a : "";
if (["--help", "-h"].includes(arg)) {
// Show them usage instructions:
console.log(`Usage: mkver [FILE]
Provides Node.js access to your app's version and release metadata.
With no FILE, default output is "./Version.ts".
See <https://github.com/photostructure/mkver> for more information.`);
}
else {
return mkver(arg);
}
});
}
if (require.main === module) {
void main().catch((error) => {
console.error("Failed: " + error);
(0, node_process_1.exit)(1);
});
}
//# sourceMappingURL=mkver.js.map