hugo-extended 0.154.3 → 0.154.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,240 @@
1
+ import { HugoCommand, HugoOptionsFor } from "./generated/types.mjs";
2
+ import { ENV_VAR_DOCS, HugoEnvConfig, getEnvConfig } from "./lib/env.mjs";
3
+
4
+ //#region src/hugo.d.ts
5
+
6
+ /**
7
+ * Gets the path to the Hugo binary, automatically installing it if it's missing.
8
+ *
9
+ * This is the main entry point for the hugo-extended package. It checks if Hugo
10
+ * is already installed and available, and if not, triggers an automatic installation
11
+ * before returning the binary path.
12
+ *
13
+ * This handles the case where Hugo may mysteriously disappear (see issue #81),
14
+ * ensuring the binary is always available when this function is called.
15
+ *
16
+ * Environment variables that affect behavior:
17
+ * - HUGO_BIN_PATH: Use a custom binary path (skips auto-install if missing)
18
+ *
19
+ * @returns A promise that resolves with the absolute path to the Hugo binary
20
+ * @throws {Error} If installation fails, the platform is unsupported, or custom binary is missing
21
+ *
22
+ * @example
23
+ * ```typescript
24
+ * import hugo from 'hugo-extended';
25
+ *
26
+ * const hugoPath = await hugo();
27
+ * console.log(hugoPath); // "/usr/local/bin/hugo" or "./bin/hugo"
28
+ * ```
29
+ */
30
+ declare const getHugoBinary: () => Promise<string>;
31
+ /**
32
+ * Execute a Hugo command with type-safe options.
33
+ *
34
+ * This function runs Hugo with the specified command and options, inheriting stdio
35
+ * so output goes directly to the console. It's perfect for interactive commands
36
+ * like `hugo server` or build commands where you want to see live output.
37
+ *
38
+ * @param command - Hugo command to execute (e.g., "server", "build", "mod clean")
39
+ * @param positionalArgsOrOptions - Either positional arguments array or options object
40
+ * @param options - Type-safe options object (if first param is positional args)
41
+ * @returns A promise that resolves when the command completes successfully
42
+ * @throws {Error} If the command fails or Hugo is not available
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * import { exec } from 'hugo-extended';
47
+ *
48
+ * // Start development server
49
+ * await exec("server", {
50
+ * port: 1313,
51
+ * buildDrafts: true,
52
+ * baseURL: "http://localhost:1313"
53
+ * });
54
+ *
55
+ * // Create a new site
56
+ * await exec("new site", ["my-site"], { format: "yaml" });
57
+ *
58
+ * // Build site for production
59
+ * await exec("build", {
60
+ * minify: true,
61
+ * cleanDestinationDir: true
62
+ * });
63
+ * ```
64
+ */
65
+ declare function exec<C extends HugoCommand>(command: C, positionalArgsOrOptions?: string[] | HugoOptionsFor<C>, options?: HugoOptionsFor<C>): Promise<void>;
66
+ /**
67
+ * Execute a Hugo command and capture its output.
68
+ *
69
+ * This function runs Hugo with the specified command and options, capturing
70
+ * stdout and stderr. It's useful for commands where you need to process the
71
+ * output programmatically, like `hugo version` or `hugo list all`.
72
+ *
73
+ * @param command - Hugo command to execute (e.g., "version", "list all")
74
+ * @param positionalArgsOrOptions - Either positional arguments array or options object
75
+ * @param options - Type-safe options object (if first param is positional args)
76
+ * @returns A promise that resolves with stdout and stderr strings
77
+ * @throws {Error} If the command fails or Hugo is not available
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * import { execWithOutput } from 'hugo-extended';
82
+ *
83
+ * // Get Hugo version
84
+ * const { stdout } = await execWithOutput("version");
85
+ * console.log(stdout); // "hugo v0.154.3+extended ..."
86
+ *
87
+ * // List all content
88
+ * const { stdout: content } = await execWithOutput("list all");
89
+ * const pages = content.split('\n');
90
+ * ```
91
+ */
92
+ declare function execWithOutput<C extends HugoCommand>(command: C, positionalArgsOrOptions?: string[] | HugoOptionsFor<C>, options?: HugoOptionsFor<C>): Promise<{
93
+ stdout: string;
94
+ stderr: string;
95
+ }>;
96
+ /**
97
+ * Builder-style API for executing Hugo commands.
98
+ *
99
+ * Provides a fluent interface where each Hugo command is a method on the
100
+ * builder object. All methods are type-safe with autocomplete for options.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * import { hugo } from 'hugo-extended';
105
+ *
106
+ * // Start server
107
+ * await hugo.server({ port: 1313, buildDrafts: true });
108
+ *
109
+ * // Build site
110
+ * await hugo.build({ minify: true });
111
+ *
112
+ * // Module operations
113
+ * await hugo.mod.clean({ all: true });
114
+ * await hugo.mod.get();
115
+ * ```
116
+ */
117
+ declare const hugo: {
118
+ /** Build your site */
119
+ build: (options?: HugoOptionsFor<"build">) => Promise<void>;
120
+ /** Generate shell completion scripts */
121
+ completion: {
122
+ bash: (options?: HugoOptionsFor<"completion bash">) => Promise<void>;
123
+ fish: (options?: HugoOptionsFor<"completion fish">) => Promise<void>;
124
+ powershell: (options?: HugoOptionsFor<"completion powershell">) => Promise<void>;
125
+ zsh: (options?: HugoOptionsFor<"completion zsh">) => Promise<void>;
126
+ };
127
+ /** Print Hugo configuration */
128
+ config: (options?: HugoOptionsFor<"config">) => Promise<void>;
129
+ /** Convert content to different formats */
130
+ convert: {
131
+ toJSON: (options?: HugoOptionsFor<"convert toJSON">) => Promise<void>;
132
+ toTOML: (options?: HugoOptionsFor<"convert toTOML">) => Promise<void>;
133
+ toYAML: (options?: HugoOptionsFor<"convert toYAML">) => Promise<void>;
134
+ };
135
+ /** Print Hugo environment info */
136
+ env: (options?: HugoOptionsFor<"env">) => Promise<void>;
137
+ /** Generate documentation */
138
+ gen: {
139
+ doc: (options?: HugoOptionsFor<"gen doc">) => Promise<void>;
140
+ man: (options?: HugoOptionsFor<"gen man">) => Promise<void>;
141
+ };
142
+ /** Import your site from others */
143
+ import: {
144
+ jekyll: (options?: HugoOptionsFor<"import jekyll">) => Promise<void>;
145
+ };
146
+ /** List various types of content */
147
+ list: {
148
+ all: (options?: HugoOptionsFor<"list all">) => Promise<void>;
149
+ drafts: (options?: HugoOptionsFor<"list drafts">) => Promise<void>;
150
+ expired: (options?: HugoOptionsFor<"list expired">) => Promise<void>;
151
+ future: (options?: HugoOptionsFor<"list future">) => Promise<void>;
152
+ published: (options?: HugoOptionsFor<"list published">) => Promise<void>;
153
+ };
154
+ /** Module operations */
155
+ mod: {
156
+ clean: (options?: HugoOptionsFor<"mod clean">) => Promise<void>;
157
+ get: (options?: HugoOptionsFor<"mod get">) => Promise<void>;
158
+ graph: (options?: HugoOptionsFor<"mod graph">) => Promise<void>;
159
+ init: (options?: HugoOptionsFor<"mod init">) => Promise<void>;
160
+ npm: {
161
+ pack: (options?: HugoOptionsFor<"mod npm pack">) => Promise<void>;
162
+ };
163
+ tidy: (options?: HugoOptionsFor<"mod tidy">) => Promise<void>;
164
+ vendor: (options?: HugoOptionsFor<"mod vendor">) => Promise<void>;
165
+ verify: (options?: HugoOptionsFor<"mod verify">) => Promise<void>;
166
+ };
167
+ /** Create new content */
168
+ new: ((pathOrOptions?: string | HugoOptionsFor<"new">, options?: HugoOptionsFor<"new">) => Promise<void>) & {
169
+ content: (pathOrOptions?: string | HugoOptionsFor<"new content">, options?: HugoOptionsFor<"new content">) => Promise<void>;
170
+ site: (pathOrOptions?: string | HugoOptionsFor<"new site">, options?: HugoOptionsFor<"new site">) => Promise<void>;
171
+ theme: (nameOrOptions?: string | HugoOptionsFor<"new theme">, options?: HugoOptionsFor<"new theme">) => Promise<void>;
172
+ };
173
+ /** Start the Hugo development server */
174
+ server: (options?: HugoOptionsFor<"server">) => Promise<void>;
175
+ /** Print the Hugo version */
176
+ version: (options?: HugoOptionsFor<"version">) => Promise<void>;
177
+ };
178
+ declare const _default: (() => Promise<string>) & {
179
+ /** Build your site */
180
+ build: (options?: HugoOptionsFor<"build">) => Promise<void>;
181
+ /** Generate shell completion scripts */
182
+ completion: {
183
+ bash: (options?: HugoOptionsFor<"completion bash">) => Promise<void>;
184
+ fish: (options?: HugoOptionsFor<"completion fish">) => Promise<void>;
185
+ powershell: (options?: HugoOptionsFor<"completion powershell">) => Promise<void>;
186
+ zsh: (options?: HugoOptionsFor<"completion zsh">) => Promise<void>;
187
+ };
188
+ /** Print Hugo configuration */
189
+ config: (options?: HugoOptionsFor<"config">) => Promise<void>;
190
+ /** Convert content to different formats */
191
+ convert: {
192
+ toJSON: (options?: HugoOptionsFor<"convert toJSON">) => Promise<void>;
193
+ toTOML: (options?: HugoOptionsFor<"convert toTOML">) => Promise<void>;
194
+ toYAML: (options?: HugoOptionsFor<"convert toYAML">) => Promise<void>;
195
+ };
196
+ /** Print Hugo environment info */
197
+ env: (options?: HugoOptionsFor<"env">) => Promise<void>;
198
+ /** Generate documentation */
199
+ gen: {
200
+ doc: (options?: HugoOptionsFor<"gen doc">) => Promise<void>;
201
+ man: (options?: HugoOptionsFor<"gen man">) => Promise<void>;
202
+ };
203
+ /** Import your site from others */
204
+ import: {
205
+ jekyll: (options?: HugoOptionsFor<"import jekyll">) => Promise<void>;
206
+ };
207
+ /** List various types of content */
208
+ list: {
209
+ all: (options?: HugoOptionsFor<"list all">) => Promise<void>;
210
+ drafts: (options?: HugoOptionsFor<"list drafts">) => Promise<void>;
211
+ expired: (options?: HugoOptionsFor<"list expired">) => Promise<void>;
212
+ future: (options?: HugoOptionsFor<"list future">) => Promise<void>;
213
+ published: (options?: HugoOptionsFor<"list published">) => Promise<void>;
214
+ };
215
+ /** Module operations */
216
+ mod: {
217
+ clean: (options?: HugoOptionsFor<"mod clean">) => Promise<void>;
218
+ get: (options?: HugoOptionsFor<"mod get">) => Promise<void>;
219
+ graph: (options?: HugoOptionsFor<"mod graph">) => Promise<void>;
220
+ init: (options?: HugoOptionsFor<"mod init">) => Promise<void>;
221
+ npm: {
222
+ pack: (options?: HugoOptionsFor<"mod npm pack">) => Promise<void>;
223
+ };
224
+ tidy: (options?: HugoOptionsFor<"mod tidy">) => Promise<void>;
225
+ vendor: (options?: HugoOptionsFor<"mod vendor">) => Promise<void>;
226
+ verify: (options?: HugoOptionsFor<"mod verify">) => Promise<void>;
227
+ };
228
+ /** Create new content */
229
+ new: ((pathOrOptions?: string | HugoOptionsFor<"new">, options?: HugoOptionsFor<"new">) => Promise<void>) & {
230
+ content: (pathOrOptions?: string | HugoOptionsFor<"new content">, options?: HugoOptionsFor<"new content">) => Promise<void>;
231
+ site: (pathOrOptions?: string | HugoOptionsFor<"new site">, options?: HugoOptionsFor<"new site">) => Promise<void>;
232
+ theme: (nameOrOptions?: string | HugoOptionsFor<"new theme">, options?: HugoOptionsFor<"new theme">) => Promise<void>;
233
+ };
234
+ /** Start the Hugo development server */
235
+ server: (options?: HugoOptionsFor<"server">) => Promise<void>;
236
+ /** Print the Hugo version */
237
+ version: (options?: HugoOptionsFor<"version">) => Promise<void>;
238
+ };
239
+ //#endregion
240
+ export { ENV_VAR_DOCS, type HugoCommand, type HugoEnvConfig, type HugoOptionsFor, _default as default, exec, execWithOutput, getEnvConfig, getHugoBinary, hugo };
package/dist/hugo.mjs ADDED
@@ -0,0 +1,246 @@
1
+ import { buildArgs } from "./lib/args.mjs";
2
+ import { ENV_VAR_DOCS, getEnvConfig } from "./lib/env.mjs";
3
+ import { doesBinExist, getBinPath, logger } from "./lib/utils.mjs";
4
+ import install_default from "./lib/install.mjs";
5
+ import { spawn } from "node:child_process";
6
+
7
+ //#region src/hugo.ts
8
+ /**
9
+ * Gets the path to the Hugo binary, automatically installing it if it's missing.
10
+ *
11
+ * This is the main entry point for the hugo-extended package. It checks if Hugo
12
+ * is already installed and available, and if not, triggers an automatic installation
13
+ * before returning the binary path.
14
+ *
15
+ * This handles the case where Hugo may mysteriously disappear (see issue #81),
16
+ * ensuring the binary is always available when this function is called.
17
+ *
18
+ * Environment variables that affect behavior:
19
+ * - HUGO_BIN_PATH: Use a custom binary path (skips auto-install if missing)
20
+ *
21
+ * @returns A promise that resolves with the absolute path to the Hugo binary
22
+ * @throws {Error} If installation fails, the platform is unsupported, or custom binary is missing
23
+ *
24
+ * @example
25
+ * ```typescript
26
+ * import hugo from 'hugo-extended';
27
+ *
28
+ * const hugoPath = await hugo();
29
+ * console.log(hugoPath); // "/usr/local/bin/hugo" or "./bin/hugo"
30
+ * ```
31
+ */
32
+ const getHugoBinary = async () => {
33
+ const envConfig = getEnvConfig();
34
+ const bin = getBinPath();
35
+ if (envConfig.binPath) {
36
+ if (!doesBinExist(bin)) throw new Error(`Custom Hugo binary not found at HUGO_BIN_PATH: ${bin}`);
37
+ return bin;
38
+ }
39
+ if (!doesBinExist(bin)) {
40
+ logger.warn("Hugo is missing, reinstalling now...");
41
+ await install_default();
42
+ }
43
+ return bin;
44
+ };
45
+ /**
46
+ * Execute a Hugo command with type-safe options.
47
+ *
48
+ * This function runs Hugo with the specified command and options, inheriting stdio
49
+ * so output goes directly to the console. It's perfect for interactive commands
50
+ * like `hugo server` or build commands where you want to see live output.
51
+ *
52
+ * @param command - Hugo command to execute (e.g., "server", "build", "mod clean")
53
+ * @param positionalArgsOrOptions - Either positional arguments array or options object
54
+ * @param options - Type-safe options object (if first param is positional args)
55
+ * @returns A promise that resolves when the command completes successfully
56
+ * @throws {Error} If the command fails or Hugo is not available
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * import { exec } from 'hugo-extended';
61
+ *
62
+ * // Start development server
63
+ * await exec("server", {
64
+ * port: 1313,
65
+ * buildDrafts: true,
66
+ * baseURL: "http://localhost:1313"
67
+ * });
68
+ *
69
+ * // Create a new site
70
+ * await exec("new site", ["my-site"], { format: "yaml" });
71
+ *
72
+ * // Build site for production
73
+ * await exec("build", {
74
+ * minify: true,
75
+ * cleanDestinationDir: true
76
+ * });
77
+ * ```
78
+ */
79
+ async function exec(command, positionalArgsOrOptions, options) {
80
+ const bin = await getHugoBinary();
81
+ let positionalArgs;
82
+ let opts;
83
+ if (Array.isArray(positionalArgsOrOptions)) {
84
+ positionalArgs = positionalArgsOrOptions;
85
+ opts = options;
86
+ } else {
87
+ positionalArgs = void 0;
88
+ opts = positionalArgsOrOptions;
89
+ }
90
+ const args = buildArgs(command, positionalArgs, opts);
91
+ return new Promise((resolve, reject) => {
92
+ const child = spawn(bin, args, { stdio: "inherit" });
93
+ child.on("exit", (code) => {
94
+ if (code === 0 || code === null) resolve();
95
+ else reject(/* @__PURE__ */ new Error(`Hugo command failed with exit code ${code}`));
96
+ });
97
+ child.on("error", (err) => {
98
+ reject(err);
99
+ });
100
+ });
101
+ }
102
+ /**
103
+ * Execute a Hugo command and capture its output.
104
+ *
105
+ * This function runs Hugo with the specified command and options, capturing
106
+ * stdout and stderr. It's useful for commands where you need to process the
107
+ * output programmatically, like `hugo version` or `hugo list all`.
108
+ *
109
+ * @param command - Hugo command to execute (e.g., "version", "list all")
110
+ * @param positionalArgsOrOptions - Either positional arguments array or options object
111
+ * @param options - Type-safe options object (if first param is positional args)
112
+ * @returns A promise that resolves with stdout and stderr strings
113
+ * @throws {Error} If the command fails or Hugo is not available
114
+ *
115
+ * @example
116
+ * ```typescript
117
+ * import { execWithOutput } from 'hugo-extended';
118
+ *
119
+ * // Get Hugo version
120
+ * const { stdout } = await execWithOutput("version");
121
+ * console.log(stdout); // "hugo v0.154.3+extended ..."
122
+ *
123
+ * // List all content
124
+ * const { stdout: content } = await execWithOutput("list all");
125
+ * const pages = content.split('\n');
126
+ * ```
127
+ */
128
+ async function execWithOutput(command, positionalArgsOrOptions, options) {
129
+ const bin = await getHugoBinary();
130
+ let positionalArgs;
131
+ let opts;
132
+ if (Array.isArray(positionalArgsOrOptions)) {
133
+ positionalArgs = positionalArgsOrOptions;
134
+ opts = options;
135
+ } else {
136
+ positionalArgs = void 0;
137
+ opts = positionalArgsOrOptions;
138
+ }
139
+ const args = buildArgs(command, positionalArgs, opts);
140
+ return new Promise((resolve, reject) => {
141
+ const stdoutChunks = [];
142
+ const stderrChunks = [];
143
+ const child = spawn(bin, args);
144
+ if (child.stdout) child.stdout.on("data", (chunk) => {
145
+ stdoutChunks.push(chunk);
146
+ });
147
+ if (child.stderr) child.stderr.on("data", (chunk) => {
148
+ stderrChunks.push(chunk);
149
+ });
150
+ child.on("exit", (code) => {
151
+ const stdout = Buffer.concat(stdoutChunks).toString("utf8");
152
+ const stderr = Buffer.concat(stderrChunks).toString("utf8");
153
+ if (code === 0 || code === null) resolve({
154
+ stdout,
155
+ stderr
156
+ });
157
+ else reject(/* @__PURE__ */ new Error(`Hugo command failed with exit code ${code}${stderr ? `\n${stderr}` : ""}`));
158
+ });
159
+ child.on("error", (err) => {
160
+ reject(err);
161
+ });
162
+ });
163
+ }
164
+ /**
165
+ * Builder-style API for executing Hugo commands.
166
+ *
167
+ * Provides a fluent interface where each Hugo command is a method on the
168
+ * builder object. All methods are type-safe with autocomplete for options.
169
+ *
170
+ * @example
171
+ * ```typescript
172
+ * import { hugo } from 'hugo-extended';
173
+ *
174
+ * // Start server
175
+ * await hugo.server({ port: 1313, buildDrafts: true });
176
+ *
177
+ * // Build site
178
+ * await hugo.build({ minify: true });
179
+ *
180
+ * // Module operations
181
+ * await hugo.mod.clean({ all: true });
182
+ * await hugo.mod.get();
183
+ * ```
184
+ */
185
+ const hugo = {
186
+ build: (options) => exec("build", options),
187
+ completion: {
188
+ bash: (options) => exec("completion bash", options),
189
+ fish: (options) => exec("completion fish", options),
190
+ powershell: (options) => exec("completion powershell", options),
191
+ zsh: (options) => exec("completion zsh", options)
192
+ },
193
+ config: (options) => exec("config", options),
194
+ convert: {
195
+ toJSON: (options) => exec("convert toJSON", options),
196
+ toTOML: (options) => exec("convert toTOML", options),
197
+ toYAML: (options) => exec("convert toYAML", options)
198
+ },
199
+ env: (options) => exec("env", options),
200
+ gen: {
201
+ doc: (options) => exec("gen doc", options),
202
+ man: (options) => exec("gen man", options)
203
+ },
204
+ import: { jekyll: (options) => exec("import jekyll", options) },
205
+ list: {
206
+ all: (options) => exec("list all", options),
207
+ drafts: (options) => exec("list drafts", options),
208
+ expired: (options) => exec("list expired", options),
209
+ future: (options) => exec("list future", options),
210
+ published: (options) => exec("list published", options)
211
+ },
212
+ mod: {
213
+ clean: (options) => exec("mod clean", options),
214
+ get: (options) => exec("mod get", options),
215
+ graph: (options) => exec("mod graph", options),
216
+ init: (options) => exec("mod init", options),
217
+ npm: { pack: (options) => exec("mod npm pack", options) },
218
+ tidy: (options) => exec("mod tidy", options),
219
+ vendor: (options) => exec("mod vendor", options),
220
+ verify: (options) => exec("mod verify", options)
221
+ },
222
+ new: Object.assign((pathOrOptions, options) => {
223
+ if (typeof pathOrOptions === "string") return exec("new", [pathOrOptions], options);
224
+ return exec("new", pathOrOptions);
225
+ }, {
226
+ content: (pathOrOptions, options) => {
227
+ if (typeof pathOrOptions === "string") return exec("new content", [pathOrOptions], options);
228
+ return exec("new content", pathOrOptions);
229
+ },
230
+ site: (pathOrOptions, options) => {
231
+ if (typeof pathOrOptions === "string") return exec("new site", [pathOrOptions], options);
232
+ return exec("new site", pathOrOptions);
233
+ },
234
+ theme: (nameOrOptions, options) => {
235
+ if (typeof nameOrOptions === "string") return exec("new theme", [nameOrOptions], options);
236
+ return exec("new theme", nameOrOptions);
237
+ }
238
+ }),
239
+ server: (options) => exec("server", options),
240
+ version: (options) => exec("version", options)
241
+ };
242
+ const hugoCompat = getHugoBinary;
243
+ var hugo_default = Object.assign(hugoCompat, hugo);
244
+
245
+ //#endregion
246
+ export { ENV_VAR_DOCS, hugo_default as default, exec, execWithOutput, getEnvConfig, getHugoBinary, hugo };
@@ -0,0 +1,30 @@
1
+ //#region src/lib/args.d.ts
2
+ /**
3
+ * Build command-line arguments from a command and options object.
4
+ *
5
+ * This function:
6
+ * 1. Loads the Hugo spec to understand flag types
7
+ * 2. Converts camelCase property names to kebab-case flags
8
+ * 3. Formats values according to their types (boolean, string, number, arrays)
9
+ * 4. Returns an argv array ready to pass to child_process
10
+ *
11
+ * @param command - Hugo command string (e.g., "server", "build", "mod clean").
12
+ * @param positionalArgs - Optional array of positional arguments (e.g., paths, names).
13
+ * @param options - Options object with camelCase property names.
14
+ * @returns Array of command-line arguments.
15
+ *
16
+ * @example
17
+ * buildArgs("server", undefined, { port: 1313, buildDrafts: true })
18
+ * // Returns: ["server", "--port", "1313", "--build-drafts"]
19
+ *
20
+ * @example
21
+ * buildArgs("new site", ["my-site"], { format: "yaml" })
22
+ * // Returns: ["new", "site", "my-site", "--format", "yaml"]
23
+ *
24
+ * @example
25
+ * buildArgs("build", undefined, { theme: ["a", "b"], minify: true })
26
+ * // Returns: ["build", "--theme", "a", "--theme", "b", "--minify"]
27
+ */
28
+ declare function buildArgs(command: string, positionalArgs?: string[], options?: Record<string, unknown>): string[];
29
+ //#endregion
30
+ export { buildArgs };
@@ -0,0 +1,126 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ //#region src/lib/args.ts
6
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
7
+ let cachedSpec = null;
8
+ /**
9
+ * Load the Hugo spec from the generated json file (cached after first load).
10
+ *
11
+ * @returns The parsed Hugo spec containing global flags and command-specific flags.
12
+ */
13
+ function loadSpec() {
14
+ if (cachedSpec) return cachedSpec;
15
+ const specPath = path.join(__dirname, "..", "generated", "flags.json");
16
+ try {
17
+ const specText = fs.readFileSync(specPath, "utf8");
18
+ cachedSpec = JSON.parse(specText);
19
+ } catch (error) {
20
+ throw new Error(`Failed to load Hugo spec from ${specPath}. Ensure the project is built (npm run build) before use.`, { cause: error });
21
+ }
22
+ return cachedSpec;
23
+ }
24
+ /**
25
+ * Convert a camelCase property name to kebab-case flag name.
26
+ *
27
+ * @param name - Property name in camelCase (e.g., "buildDrafts").
28
+ * @returns Kebab-case flag name (e.g., "build-drafts").
29
+ *
30
+ * @example
31
+ * camelToKebab("baseURL") // "base-u-r-l"
32
+ * camelToKebab("buildDrafts") // "build-drafts"
33
+ */
34
+ function camelToKebab(name) {
35
+ return name.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
36
+ }
37
+ /**
38
+ * Find a flag spec by its camelCase property name.
39
+ *
40
+ * @param flags - Array of flag specs to search.
41
+ * @param propName - Property name in camelCase.
42
+ * @returns The matching flag spec, or undefined if not found.
43
+ */
44
+ function findFlag(flags, propName) {
45
+ const kebab = camelToKebab(propName);
46
+ return flags.find((f) => {
47
+ const flagName = f.long.startsWith("--") ? f.long.slice(2) : f.long;
48
+ return flagName === kebab || flagName === propName;
49
+ });
50
+ }
51
+ /**
52
+ * Build command-line arguments from a command and options object.
53
+ *
54
+ * This function:
55
+ * 1. Loads the Hugo spec to understand flag types
56
+ * 2. Converts camelCase property names to kebab-case flags
57
+ * 3. Formats values according to their types (boolean, string, number, arrays)
58
+ * 4. Returns an argv array ready to pass to child_process
59
+ *
60
+ * @param command - Hugo command string (e.g., "server", "build", "mod clean").
61
+ * @param positionalArgs - Optional array of positional arguments (e.g., paths, names).
62
+ * @param options - Options object with camelCase property names.
63
+ * @returns Array of command-line arguments.
64
+ *
65
+ * @example
66
+ * buildArgs("server", undefined, { port: 1313, buildDrafts: true })
67
+ * // Returns: ["server", "--port", "1313", "--build-drafts"]
68
+ *
69
+ * @example
70
+ * buildArgs("new site", ["my-site"], { format: "yaml" })
71
+ * // Returns: ["new", "site", "my-site", "--format", "yaml"]
72
+ *
73
+ * @example
74
+ * buildArgs("build", undefined, { theme: ["a", "b"], minify: true })
75
+ * // Returns: ["build", "--theme", "a", "--theme", "b", "--minify"]
76
+ */
77
+ function buildArgs(command, positionalArgs, options) {
78
+ const spec = loadSpec();
79
+ const args = [];
80
+ args.push(...command.split(" "));
81
+ if (positionalArgs && positionalArgs.length > 0) args.push(...positionalArgs);
82
+ if (!options || Object.keys(options).length === 0) return args;
83
+ const cmdSpec = spec.commands.find((c) => c.command === command);
84
+ const allFlags = [...spec.globalFlags, ...cmdSpec?.flags ?? []];
85
+ for (const [key, value] of Object.entries(options)) {
86
+ if (value === void 0 || value === null) continue;
87
+ const flagSpec = findFlag(allFlags, key);
88
+ const flagName = flagSpec ? flagSpec.long.startsWith("--") ? flagSpec.long : `--${flagSpec.long}` : `--${camelToKebab(key)}`;
89
+ switch (flagSpec?.kind ?? inferKind(value)) {
90
+ case "boolean":
91
+ if (value === true) args.push(flagName);
92
+ break;
93
+ case "string":
94
+ args.push(flagName, String(value));
95
+ break;
96
+ case "number":
97
+ args.push(flagName, String(value));
98
+ break;
99
+ case "string[]":
100
+ if (Array.isArray(value)) for (const item of value) args.push(flagName, String(item));
101
+ break;
102
+ case "number[]":
103
+ if (Array.isArray(value)) for (const item of value) args.push(flagName, String(item));
104
+ break;
105
+ }
106
+ }
107
+ return args;
108
+ }
109
+ /**
110
+ * Infer the kind of a value when we don't have spec information.
111
+ *
112
+ * @param value - The value to inspect.
113
+ * @returns The inferred flag kind.
114
+ */
115
+ function inferKind(value) {
116
+ if (typeof value === "boolean") return "boolean";
117
+ if (typeof value === "number") return "number";
118
+ if (Array.isArray(value)) {
119
+ if (value.length > 0 && typeof value[0] === "number") return "number[]";
120
+ return "string[]";
121
+ }
122
+ return "string";
123
+ }
124
+
125
+ //#endregion
126
+ export { buildArgs };