docs: update AGENTS.md and README.md for clarity and accuracy

This commit is contained in:
2026-06-21 15:08:06 -04:00
parent 83abb6a370
commit f16b44ff87
2 changed files with 238 additions and 157 deletions
+1 -1
View File
@@ -85,7 +85,7 @@ npm run test:coverage # coverage via v8
- Example: `tests/unit/args.test.ts` covers argv building behavior driven by `flags.json`.
- Example: `tests/unit/types.test.ts` uses `expectTypeOf` to validate type surfaces.
- Example: `tests/unit/utils.test.ts` covers platform package detection and binary path resolution.
- Example: `tests/unit/binary-packages.test.ts` covers generated package manifests, checksum parsing, and archive type detection.
- Example: `tests/unit/platform.test.ts` covers generated package manifests, checksum parsing, and archive type detection.
- `tests/integration/*`
- Executes real Hugo commands and does real filesystem work in temp dirs.
+237 -156
View File
@@ -4,41 +4,39 @@
[![NPM Downloads](https://img.shields.io/npm/dw/hugo-extended?color=rebeccapurple)](https://www.npmjs.com/package/hugo-extended)
[![CI status](https://github.com/jakejarvis/hugo-extended/workflows/Run%20tests/badge.svg)](https://github.com/jakejarvis/hugo-extended/actions)
> Plug-and-play binary wrapper for [Hugo Extended](https://gohugo.io/), the awesomest static-site generator. Now with full TypeScript support and type-safe APIs!
A plug-and-play NPM wrapper for the [Hugo](https://gohugo.io/) static-site generator.
## Features
- 🚀 **Zero configuration** — Hugo binary is provided by a platform-specific optional package
- 📦 **Version-locked** — Package version matches Hugo version (e.g., `hugo-extended@0.140.0` = Hugo v0.140.0)
- 🔒 **Type-safe API** — Full TypeScript support with autocomplete for all Hugo commands and flags
- **Multiple APIs** — Use CLI, function-based, or builder-style APIs
- 🎯 **Extended by default** — Automatically uses Hugo Extended on supported platforms
- **CLI passthrough** Run Hugo with `hugo` (or `hugo-extended`) from `package.json` scripts.
- **Predictable versioning** `hugo-extended`'s version in `package.json` will always match the version of Hugo invoked by your package manager.
- **Type-safe API** Generated command and option types for the Hugo version in this package.
- **Builder API** Call common commands as methods such as `hugo.server()` and `hugo.new.project()` programmatically.
- **No install-time scripts** Runtime resolution uses optional platform and architecture-specific NPM packages.
## Requirements
- Node.js `>=22.12.0`
- A package manager that installs optional dependencies (unless you set `HUGO_BIN_PATH`; see [Binary Resolution](#binary-resolution) below)
## Installation
```sh
npm install hugo-extended --save-dev
# or
yarn add hugo-extended --dev
# or
# or...
pnpm add hugo-extended --save-dev
# or...
bun add hugo-extended --dev
# or...
yarn add hugo-extended --dev
```
### SCSS/PostCSS Support
> [!IMPORTANT]
> **Recommended:** Use `--save-exact` to ensure your team is developing against the same Hugo version.
If you're using Hugo's SCSS features, you'll also want:
## CLI Usage
```sh
npm install postcss postcss-cli autoprefixer --save-dev
```
These integrate seamlessly with Hugo's [built-in PostCSS pipes](https://gohugo.io/functions/css/postcss/).
## Usage
### CLI Usage
The simplest way — just run `hugo` commands directly:
Call the `hugo` binary from NPM scripts just like you would with a system-wide Hugo installation:
```jsonc
// package.json
@@ -55,184 +53,259 @@ The simplest way — just run `hugo` commands directly:
npm run dev
```
### Programmatic API
The wrapper resolves the binary, forwards all arguments to Hugo, and exits with Hugo's exit code.
#### Builder-style API
## TypeScript API
A fluent interface where each Hugo command is a method:
### Builder API
```typescript
The default export is callable and also has builder methods attached:
```ts
import hugo from "hugo-extended";
// Start server
await hugo.server({
port: 1313,
buildDrafts: true,
});
// Build site
await hugo.build({
minify: true,
environment: "production",
});
// Module commands
await hugo.mod.get();
await hugo.mod.tidy();
await hugo.mod.clean({ all: true });
// Generate shell completions
await hugo.completion.zsh();
```
#### Function-based API
Use `exec()` for commands that output to the console, or `execWithOutput()` to capture the output:
```typescript
import { exec, execWithOutput } from "hugo-extended";
// Start development server with full type safety
await exec("server", {
source: "site",
port: 1313,
buildDrafts: true,
navigateToChanged: true,
});
// Build for production
await exec("build", {
await hugo.build({
source: "site",
minify: true,
cleanDestinationDir: true,
baseURL: "https://example.com",
baseURL: "https://example.com/",
});
// Capture command output
const { stdout } = await execWithOutput("version");
console.log(stdout); // "hugo v0.140.0+extended darwin/arm64 ..."
// List all content pages
const { stdout: pages } = await execWithOutput("list all");
await hugo.new.project("site", { format: "yaml" });
await hugo.new.content("posts/hello.md", {
source: "site",
kind: "post",
});
await hugo.mod.tidy({ source: "site" });
```
#### Direct Binary Access
> [!NOTE]
> Builder methods inherit stdio, so Hugo output is printed directly to the current process. Use `execWithOutput()` when you need to capture output.
For advanced use cases, get the Hugo binary path directly:
### Function API
```typescript
import hugo from "hugo-extended";
import { spawn } from "child_process";
Use `exec()` when you want inherited stdio:
const binPath = await hugo();
console.log(binPath); // "/usr/local/bin/hugo" or similar
```ts
import { exec } from "hugo-extended";
// Use with spawn, exec, or any process library
spawn(binPath, ["version"], { stdio: "inherit" });
```
### Type Imports
Import Hugo types for use in your own code:
```typescript
import type { HugoCommand, HugoOptionsFor, HugoServerOptions } from "hugo-extended";
// Type-safe option objects
const serverOpts: HugoServerOptions = {
await exec("server", {
source: "site",
port: 1313,
buildDrafts: true,
disableLiveReload: false,
};
});
// Generic helper
function runHugo<C extends HugoCommand>(cmd: C, opts: HugoOptionsFor<C>) {
// ...
await exec("new project", ["site"], {
format: "yaml",
});
```
Use `execWithOutput()` when you need stdout and stderr:
```ts
import { execWithOutput } from "hugo-extended";
const { stdout } = await execWithOutput("version");
console.log(stdout);
const { stdout: pages } = await execWithOutput("list all", {
source: "site",
});
console.log(pages);
```
### Binary Path Access
Call the default export or `getHugoBinary()` to resolve the executable:
```ts
import { spawn } from "node:child_process";
import hugo, { getHugoBinary } from "hugo-extended";
const binFromDefault = await hugo();
const binFromNamedExport = await getHugoBinary();
spawn(binFromDefault, ["version"], { stdio: "inherit" });
console.log(binFromNamedExport);
```
### Types
The main entry exports the generic command helpers:
```ts
import type { HugoCommand, HugoOptionsFor } from "hugo-extended";
function run<C extends HugoCommand>(command: C, options: HugoOptionsFor<C>) {
return { command, options };
}
```
## API Reference
Generated command-specific interfaces are available from `hugo-extended/types`:
### `exec(command, options?)`
```ts
import type { HugoServerOptions } from "hugo-extended/types";
Execute a Hugo command with inherited stdio (output goes to console).
- **command** — Hugo command string (e.g., `"server"`, `"build"`, `"mod clean"`)
- **options** — Type-safe options object (optional)
- **Returns** — `Promise<void>`
### `execWithOutput(command, options?)`
Execute a Hugo command and capture output.
- **command** — Hugo command string
- **options** — Type-safe options object (optional)
- **Returns** — `Promise<{ stdout: string; stderr: string }>`
### `hugo` (default export)
The default export is both callable (returns binary path) and has builder methods:
```typescript
// Get binary path (backward compatible)
const binPath = await hugo();
// Builder methods
await hugo.build({ minify: true });
await hugo.server({ port: 3000 });
const options: HugoServerOptions = {
source: "site",
port: 1313,
buildDrafts: true,
};
```
### Available Commands
## Commands and Options
All Hugo commands are fully typed with autocomplete:
Command and option types are generated from `hugo help` output for the Hugo version locked to this package. Regenerate them when bumping the Hugo version.
| Command | Builder Method | Description |
| ------------- | -------------------- | ---------------------------------------------------------- |
| `build` | `hugo.build()` | Build your site |
| `server` | `hugo.server()` | Start dev server |
| `new` | `hugo.new()` | Create new content |
| `mod get` | `hugo.mod.get()` | Download modules |
| `mod tidy` | `hugo.mod.tidy()` | Clean go.mod/go.sum |
| `mod clean` | `hugo.mod.clean()` | Clean module cache |
| `mod vendor` | `hugo.mod.vendor()` | Vendor dependencies |
| `list all` | `hugo.list.all()` | List all content |
| `list drafts` | `hugo.list.drafts()` | List draft content |
| `config` | `hugo.config()` | Print configuration |
| `version` | `hugo.version()` | Print version |
| `env` | `hugo.env()` | Print environment |
| ... | ... | [All Hugo commands supported](https://gohugo.io/commands/) |
Options use camelCase property names:
## Platform Support
```ts
await hugo.build({
baseURL: "https://example.com/",
buildDrafts: true,
cleanDestinationDir: true,
});
```
Hugo Extended is automatically used on supported platforms:
When an option exists in the generated flag spec, the wrapper preserves Hugo's canonical long flag exactly, including mixed-case flags such as `--baseURL` and `--buildDrafts`. Unknown camelCase properties fall back to kebab-case.
| Platform | Architecture | Hugo Extended |
| -------- | ------------ | ----------------- |
| macOS | x64, ARM64 | ✅ |
| Linux | x64, ARM64 | ✅ |
| Windows | x64 | ✅ |
| Windows | ARM64 | ❌ (vanilla Hugo) |
Value handling:
- Boolean options are emitted only when `true`.
- String and number options are emitted as `--flag value`.
- Array options repeat the flag once per value.
- `undefined` and `null` options are skipped.
If a command is not exposed as a builder method, use `exec("command name", ...)` or `execWithOutput("command name", ...)`.
## Binary Resolution
At runtime, `hugo-extended` resolves the binary in this order:
1. `HUGO_BIN_PATH`, when set.
2. The platform-specific optional dependency for the current OS and CPU.
Runtime code never downloads, installs, or extracts Hugo.
| Platform | Architecture | Package | Edition |
| -------- | ------------ | -------------------------------------------- | ------------ |
| macOS | x64, arm64 | `@jakejarvis/hugo-extended-darwin-universal` | Extended |
| Linux | x64 | `@jakejarvis/hugo-extended-linux-amd64` | Extended |
| Linux | arm64 | `@jakejarvis/hugo-extended-linux-arm64` | Extended |
| Windows | x64 | `@jakejarvis/hugo-extended-windows-amd64` | Extended |
| Windows | arm64 | `@jakejarvis/hugo-windows-arm64` | Vanilla Hugo |
Unsupported platforms can still use this package by setting `HUGO_BIN_PATH` to a compatible Hugo executable.
## Environment Variables
Customize runtime binary resolution with this environment variable:
| Variable | Description |
| --------------- | ------------------------------------------------------------------------------------------------------------------- |
| `HUGO_BIN_PATH` | Use a pre-existing Hugo binary instead of the platform package binary. Example: `HUGO_BIN_PATH=/usr/local/bin/hugo` |
### Examples
| Variable | Type | Description |
| --------------- | ------ | -------------------------------------------- |
| `HUGO_BIN_PATH` | string | Absolute path to a pre-existing Hugo binary. |
```sh
# Use a system-installed Hugo binary
HUGO_BIN_PATH=/usr/local/bin/hugo npm run build
```
For programmatic access, import `getEnvConfig()` or `ENV_VAR_DOCS`:
```ts
import { ENV_VAR_DOCS, getEnvConfig } from "hugo-extended";
console.log(getEnvConfig());
console.log(ENV_VAR_DOCS);
```
## SCSS and PostCSS
Hugo's SCSS support is provided by Hugo Extended. If your site uses Hugo's PostCSS integration, install the Node tools your Hugo pipeline expects:
```sh
npm install postcss postcss-cli autoprefixer --save-dev
```
See Hugo's [PostCSS documentation](https://gohugo.io/functions/css/postcss/) for pipeline configuration.
## Development
Install dependencies:
```sh
npm install
```
Common commands:
```sh
npm run build
npm run check-types
npm run lint
npm run fmt:check
npm test
```
Test targets:
```sh
npm run test:unit
npm run test:integration
npm run test:e2e
npm run test:coverage
```
Integration and e2e tests run the resolved Hugo binary. In tests that operate on a site directory, prefer passing Hugo's global `source` option over changing `process.cwd()`.
### Regenerating Types
[`generate-types.ts`](scripts/generate-types.ts) traverses Hugo help output and writes:
- `src/generated/types.ts`
- `src/generated/flags.json`
Run it after bumping the package/Hugo version:
```sh
npm run generate-types
```
Expect generated type changes when Hugo adds, removes, renames, or recases commands and flags upstream.
### Generating Platform Packages
[`generate-packages.ts`](scripts/generate-packages.ts) downloads Hugo release assets, verifies SHA-256 checksums, extracts the binary, and writes publishable packages to `dist-platforms/`.
Generate the package for the current platform:
```sh
npm run generate-packages
```
Generate one package by name:
```sh
npm run generate-packages -- --package @jakejarvis/hugo-extended-linux-amd64
```
Generate all packages:
```sh
npm run generate-packages -- --all
```
macOS Hugo v0.153.0 and newer ships as a `.pkg`. Generating the macOS package requires macOS because extraction uses `pkgutil --expand-full`. `.tar.gz` and `.zip` assets can be generated on Linux or macOS.
## Troubleshooting
### Hugo binary not found
### Hugo binary package not found
`hugo-extended` depends on platform-specific optional packages such as `@jakejarvis/hugo-extended-linux-amd64`. If you install with optional dependencies omitted, the wrapper cannot find its bundled Hugo binary.
Reinstall with optional dependencies explicitly enabled. A plain install will not fix this if your environment or package-manager config is still omitting optional dependencies.
This usually means optional dependencies were not installed. Reinstall with optional dependencies enabled:
```sh
npm install --include=optional
@@ -240,11 +313,19 @@ pnpm install --config.optional=true
yarn config set ignore-optional false && yarn install
```
If your environment intentionally omits optional dependencies, set `HUGO_BIN_PATH` to a compatible Hugo binary instead.
If your environment intentionally omits optional dependencies, set `HUGO_BIN_PATH` instead.
### macOS installation
### Unsupported platform
As of [v0.153.0](https://github.com/gohugoio/hugo/releases/tag/v0.153.0), Hugo is distributed as a `.pkg` installer for macOS. The macOS binary package is built from that `.pkg` during release packaging, so **no sudo, global installation, or install-time script is required** for consumers.
Set `HUGO_BIN_PATH` to a Hugo binary built for your platform:
```sh
HUGO_BIN_PATH=/opt/bin/hugo npx hugo version
```
### macOS packaging
The macOS platform package is created during release packaging from Hugo's upstream `.pkg` asset. Consumers do not need sudo access, global installation, or install-time scripts.
## License