UNPKG

mkver

Version:

Node.js access to your app's version and release metadata

195 lines (193 loc) 7.41 kB
#!/usr/bin/env node "use strict"; 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