hugo-extended
Version:
✏️ Plug-and-play binary wrapper for Hugo Extended, the awesomest static-site generator.
191 lines (189 loc) • 7.48 kB
JavaScript
import { getEnvConfig } from "./env.mjs";
import { execFileSync } from "node:child_process";
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
//#region src/lib/utils.ts
const __dirname = path.dirname(fileURLToPath(import.meta.url));
/**
* The first Hugo version that uses .pkg installers for macOS.
* Versions before this use .tar.gz archives.
*
* @see https://github.com/gohugoio/hugo/issues/14135
*/
const MACOS_PKG_MIN_VERSION = "0.153.0";
/**
* Compares two semver version strings.
*
* @param a - First version string (e.g., "0.153.0")
* @param b - Second version string (e.g., "0.152.1")
* @returns -1 if a < b, 0 if a === b, 1 if a > b
*/
function compareVersions(a, b) {
const partsA = a.split(".").map((n) => Number.parseInt(n, 10));
const partsB = b.split(".").map((n) => Number.parseInt(n, 10));
const maxLen = Math.max(partsA.length, partsB.length);
for (let i = 0; i < maxLen; i++) {
const numA = partsA[i] ?? 0;
const numB = partsB[i] ?? 0;
if (numA < numB) return -1;
if (numA > numB) return 1;
}
return 0;
}
/**
* Checks if a version uses .pkg installers for macOS.
* Hugo v0.153.0+ uses .pkg, earlier versions use .tar.gz.
*
* @param version - The Hugo version to check
* @returns true if the version uses .pkg installers on macOS
*/
function usesMacOSPkg(version) {
return compareVersions(version, MACOS_PKG_MIN_VERSION) >= 0;
}
/**
* Gets the Hugo version to install.
*
* Resolution order:
* 1. HUGO_OVERRIDE_VERSION environment variable (if set)
* 2. `hugoVersion` field in package.json (for emergency overrides)
* 3. `version` field in package.json (should match Hugo release)
*
* @throws {Error} If package.json cannot be found and no override is set
* @returns The version string (e.g., "0.88.1")
*/
function getPkgVersion() {
const envConfig = getEnvConfig();
if (envConfig.overrideVersion) return envConfig.overrideVersion;
const packageJsonPath = path.join(__dirname, "..", "..", "package.json");
try {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
return packageJson.hugoVersion || packageJson.version;
} catch {
throw new Error(`Could not find or read package.json at ${packageJsonPath}`);
}
}
/**
* Generates the full URL to a Hugo release file.
*
* By default, downloads from GitHub releases. Can be overridden with
* HUGO_MIRROR_BASE_URL for mirrors or air-gapped environments.
*
* @param version - The Hugo version number (e.g., "0.88.1")
* @param filename - The release filename (e.g., "hugo_extended_0.88.1_darwin-universal.pkg")
* @returns The complete download URL for the release file
*/
function getReleaseUrl(version, filename) {
const envConfig = getEnvConfig();
if (envConfig.downloadBaseUrl) return `${envConfig.downloadBaseUrl.replace(/\/$/, "")}/${filename}`;
return `https://github.com/gohugoio/hugo/releases/download/v${version}/${filename}`;
}
/**
* Gets the Hugo binary filename for the current platform.
*
* @returns "hugo.exe" on Windows, "hugo" on all other platforms
*/
function getBinFilename() {
return process.platform === "win32" ? "hugo.exe" : "hugo";
}
/**
* Gets the absolute path to the Hugo binary.
*
* Resolution order:
* 1. HUGO_BIN_PATH environment variable (if set)
* 2. Local bin directory (./bin/hugo or ./bin/hugo.exe)
*
* @returns The absolute path to hugo binary
*/
function getBinPath() {
const envConfig = getEnvConfig();
if (envConfig.binPath) return envConfig.binPath;
return path.join(__dirname, "..", "..", "bin", getBinFilename());
}
/**
* Executes the Hugo binary and returns its version string.
*
* @param bin - The absolute path to the Hugo binary
* @returns The version output string (e.g., "hugo v0.88.1-5BC54738+extended darwin/arm64 BuildDate=...")
* @throws {Error} If the binary cannot be executed
*/
function getBinVersion(bin) {
return execFileSync(bin, ["version"]).toString().trim();
}
/**
* Checks if the Hugo binary exists at the specified path.
*
* @param bin - The absolute path to check for the Hugo binary
* @returns `true` if the file exists, `false` if it doesn't
* @throws {Error} If an unexpected error occurs (other than ENOENT)
*/
function doesBinExist(bin) {
try {
if (fs.existsSync(bin)) return true;
} catch (error) {
if (error && typeof error === "object" && "code" in error && error.code !== "ENOENT") throw error;
return false;
}
return false;
}
/**
* Determines the correct Hugo release filename for the current platform and architecture.
*
* Hugo Extended is available for:
* - macOS: x64 and ARM64 (universal binaries as of v0.102.0)
* - Linux: x64 and ARM64
* - Windows: x64 only
*
* Other platform/architecture combinations fall back to vanilla Hugo where available.
* Set HUGO_NO_EXTENDED=1 to force vanilla Hugo even on platforms that support Extended.
*
* Note: macOS uses .pkg installers starting from v0.153.0. Earlier versions use .tar.gz.
*
* @param version - The Hugo version number (e.g., "0.88.1")
* @returns The release filename if supported (e.g., "hugo_extended_0.88.1_darwin-universal.pkg"),
* or `null` if the platform/architecture combination is not supported
*/
function getReleaseFilename(version) {
const { platform, arch } = process;
const forceStandard = getEnvConfig().forceStandard;
const edition = (extended, standard) => forceStandard ? standard : extended;
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) {
if (usesMacOSPkg(version)) return edition(`hugo_extended_${version}_darwin-universal.pkg`, `hugo_${version}_darwin-universal.pkg`);
return edition(`hugo_extended_${version}_darwin-universal.tar.gz`, `hugo_${version}_darwin-universal.tar.gz`);
}
return platform === "win32" && arch === "x64" ? edition(`hugo_extended_${version}_windows-amd64.zip`, `hugo_${version}_windows-amd64.zip`) : platform === "win32" && arch === "arm64" ? `hugo_${version}_windows-arm64.zip` : platform === "linux" && arch === "x64" ? edition(`hugo_extended_${version}_linux-amd64.tar.gz`, `hugo_${version}_linux-amd64.tar.gz`) : platform === "linux" && arch === "arm64" ? edition(`hugo_extended_${version}_linux-arm64.tar.gz`, `hugo_${version}_linux-arm64.tar.gz`) : platform === "freebsd" && arch === "x64" ? `hugo_${version}_freebsd-amd64.tar.gz` : platform === "openbsd" && arch === "x64" ? `hugo_${version}_openbsd-amd64.tar.gz` : null;
}
/**
* Generates the checksums filename for a given Hugo version.
*
* @param version - The Hugo version number (e.g., "0.88.1")
* @returns The checksums filename (e.g., "hugo_0.88.1_checksums.txt")
*/
function getChecksumFilename(version) {
return `hugo_${version}_checksums.txt`;
}
/**
* Determines if a release filename corresponds to Hugo Extended or vanilla Hugo.
*
* @param releaseFile - The release filename to check (e.g., "hugo_extended_0.88.1_darwin-universal.pkg")
* @returns `true` if the release is Hugo Extended, `false` if it's vanilla Hugo
*/
function isExtended(releaseFile) {
return releaseFile.startsWith("hugo_extended_");
}
/**
* Logger utility that respects the HUGO_QUIET setting.
*/
const logger = {
info: (message) => {
if (!getEnvConfig().quiet) console.info(message);
},
warn: (message) => {
if (!getEnvConfig().quiet) console.warn(`⚠ ${message}`);
},
error: (message) => {
console.error(`✖ ${message}`);
}
};
//#endregion
export { compareVersions, doesBinExist, getBinFilename, getBinPath, getBinVersion, getChecksumFilename, getPkgVersion, getReleaseFilename, getReleaseUrl, isExtended, logger, usesMacOSPkg };