UNPKG

hugo-extended

Version:

✏️ Plug-and-play binary wrapper for Hugo Extended, the awesomest static-site generator.

126 lines (124 loc) 4.35 kB
import fs from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; //#region src/lib/args.ts const __dirname = path.dirname(fileURLToPath(import.meta.url)); let cachedSpec = null; /** * Load the Hugo spec from the generated json file (cached after first load). * * @returns The parsed Hugo spec containing global flags and command-specific flags. */ function loadSpec() { if (cachedSpec) return cachedSpec; const specPath = path.join(__dirname, "..", "generated", "flags.json"); try { const specText = fs.readFileSync(specPath, "utf8"); cachedSpec = JSON.parse(specText); } catch (error) { throw new Error(`Failed to load Hugo spec from ${specPath}. Ensure the project is built (npm run build) before use.`, { cause: error }); } return cachedSpec; } /** * Convert a camelCase property name to kebab-case flag name. * * @param name - Property name in camelCase (e.g., "buildDrafts"). * @returns Kebab-case flag name (e.g., "build-drafts"). * * @example * camelToKebab("baseURL") // "base-u-r-l" * camelToKebab("buildDrafts") // "build-drafts" */ function camelToKebab(name) { return name.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`); } /** * Find a flag spec by its camelCase property name. * * @param flags - Array of flag specs to search. * @param propName - Property name in camelCase. * @returns The matching flag spec, or undefined if not found. */ function findFlag(flags, propName) { const kebab = camelToKebab(propName); return flags.find((f) => { const flagName = f.long.startsWith("--") ? f.long.slice(2) : f.long; return flagName === kebab || flagName === propName; }); } /** * Build command-line arguments from a command and options object. * * This function: * 1. Loads the Hugo spec to understand flag types * 2. Converts camelCase property names to kebab-case flags * 3. Formats values according to their types (boolean, string, number, arrays) * 4. Returns an argv array ready to pass to child_process * * @param command - Hugo command string (e.g., "server", "build", "mod clean"). * @param positionalArgs - Optional array of positional arguments (e.g., paths, names). * @param options - Options object with camelCase property names. * @returns Array of command-line arguments. * * @example * buildArgs("server", undefined, { port: 1313, buildDrafts: true }) * // Returns: ["server", "--port", "1313", "--build-drafts"] * * @example * buildArgs("new site", ["my-site"], { format: "yaml" }) * // Returns: ["new", "site", "my-site", "--format", "yaml"] * * @example * buildArgs("build", undefined, { theme: ["a", "b"], minify: true }) * // Returns: ["build", "--theme", "a", "--theme", "b", "--minify"] */ function buildArgs(command, positionalArgs, options) { const spec = loadSpec(); const args = []; args.push(...command.split(" ")); if (positionalArgs && positionalArgs.length > 0) args.push(...positionalArgs); if (!options || Object.keys(options).length === 0) return args; const cmdSpec = spec.commands.find((c) => c.command === command); const allFlags = [...spec.globalFlags, ...cmdSpec?.flags ?? []]; for (const [key, value] of Object.entries(options)) { if (value === void 0 || value === null) continue; const flagSpec = findFlag(allFlags, key); const flagName = flagSpec ? flagSpec.long.startsWith("--") ? flagSpec.long : `--${flagSpec.long}` : `--${camelToKebab(key)}`; switch (flagSpec?.kind ?? inferKind(value)) { case "boolean": if (value === true) args.push(flagName); break; case "string": args.push(flagName, String(value)); break; case "number": args.push(flagName, String(value)); break; case "string[]": if (Array.isArray(value)) for (const item of value) args.push(flagName, String(item)); break; case "number[]": if (Array.isArray(value)) for (const item of value) args.push(flagName, String(item)); break; } } return args; } /** * Infer the kind of a value when we don't have spec information. * * @param value - The value to inspect. * @returns The inferred flag kind. */ function inferKind(value) { if (typeof value === "boolean") return "boolean"; if (typeof value === "number") return "number"; if (Array.isArray(value)) { if (value.length > 0 && typeof value[0] === "number") return "number[]"; return "string[]"; } return "string"; } //#endregion export { buildArgs };