@hyperfrontend/versioning/commits/validate

validate/

Pure rule engine for commit-message validation.

Overview

Runs a configurable ruleset over a parsed ConventionalCommit and returns a structured ValidationResult containing warn-level and error-level messages. Mirrors the semantics of @commitlint/config-conventional's baseline but is bundled with @hyperfrontend/versioning and TTY-free.

The same engine powers three consumers:

  1. Inline step validators during interactive authoring (commits/author/).
  2. Preview-step warnings that render before the user confirms a commit.
  3. The out-of-band cl bin wired into .git/hooks/commit-msg.

Usage

Validate a parsed commit

import { parseConventionalCommit, validateCommit, conventionalPreset } from '@hyperfrontend/versioning'

const commit = parseConventionalCommit('feat: add login')
const result = validateCommit(commit, conventionalPreset)

result.valid // true
result.warnings // []
result.errors // []

Validate a raw message (for the cl bin)

import { validateCommitMessage, conventionalPreset } from '@hyperfrontend/versioning'

const result = validateCommitMessage('feat: added login', conventionalPreset)

result.valid // true — `imperative-mood` is a warning, not an error
result.warnings // [{ level: 'warn', ruleName: 'imperative-mood', message: '...' }]

Extend the preset

import type { Ruleset } from '@hyperfrontend/versioning'
import { conventionalPreset } from '@hyperfrontend/versioning'

const dogfoodRuleset: Ruleset = {
  ...conventionalPreset,
  'subject-case': ['off'],
  'header-max-length': ['warn', { maxLength: 72 }],
}

Authoring New Rules

  1. Create rules/<name>.ts exporting a Rule<YourOptions> whose check() returns a list of violation strings.
  2. Register it in rules/index.ts and in BUILT_IN_RULES inside engine.ts.
  3. Write a colocated .spec.ts covering: happy path, each failure branch, the off/empty-options pass-through paths.
  4. If it should ship in the default preset, add it to presets/conventional.ts.

The engine is intentionally dumb about rule semantics — rules own both the check and the message wording. Keep messages specific enough to act on ('type must be one of [feat, fix] but was "wip"', not 'invalid type').

See Also

API Reference

ƒ Functions

§function

validateCommit(commit: ConventionalCommit, ruleset: Ruleset): ValidationResult

Runs every rule configured in the ruleset against a parsed commit and aggregates the resulting warnings and errors.
Rules whose config is 'off' (or absent from the ruleset) are skipped. Unknown rule names in the ruleset are ignored — consumers extend the engine by calling validateCommitWithRules() and supplying additional rules.

Parameters

NameTypeDescription
§commit
ConventionalCommit
Parsed conventional commit to validate
§ruleset
Ruleset
Configuration map from rule name to [level, options?]

Returns

ValidationResult
Aggregated validation result

Example

Validating a commit against a preset ruleset

validateCommit(parseConventionalCommit('feat: add login'), conventionalPreset)
// => { valid: true, warnings: [], errors: [] }
§function

validateCommitMessage(raw: string, ruleset: Ruleset): ValidationResult

Parses a raw commit message and validates the resulting commit against the supplied ruleset. Used by the cl bin and anywhere else validation must start from a string rather than a parsed commit.

Parameters

NameTypeDescription
§raw
string
Raw commit message (as written to .git/COMMIT_EDITMSG)
§ruleset
Ruleset
Configuration map from rule name to [level, options?]

Returns

ValidationResult
Aggregated validation result

Example

Validating a raw commit message

validateCommitMessage('feat: add login', conventionalPreset)
// => { valid: true, warnings: [], errors: [] }

validateCommitMessage('feat: added login', conventionalPreset).warnings
// => [{ level: 'warn', ruleName: 'imperative-mood', message: ... }]
§function

validateCommitWithRules(commit: ConventionalCommit, ruleset: Ruleset, rules: Readonly<Record<string, Rule<never>>>): ValidationResult

Variant of validateCommit that accepts a caller-supplied rule registry. Useful for consumers that want to add project-specific rules without forking the built-in set.

Parameters

NameTypeDescription
§commit
ConventionalCommit
Parsed conventional commit to validate
§ruleset
Ruleset
Configuration map from rule name to [level, options?]
§rules
Readonly<Record<string, Rule<never>>>
Rule registry keyed by rule name

Returns

ValidationResult
Aggregated validation result

Example

Extending the built-in rules with a custom rule

const customRules = { ...BUILT_IN_RULES, 'no-wip': noWipRule }
validateCommitWithRules(commit, { 'no-wip': ['error'] }, customRules)

Interfaces

§interface

HeaderMaxLengthOptions

Options for the header-max-length rule.

Properties

§readonly maxLength:number
Maximum allowed header length. Use null to disable the check.
§interface

ImperativeMoodOptions

Options for the imperative-mood rule.

Properties

§readonly wordlist?:Readonly<Record<string, string>>
Map of past-tense words (lowercase) to their suggested imperative forms. Overrides DEFAULT_IMPERATIVE_WORDLIST when provided.
§interface

Rule

Pure rule definition. Implementations inspect a ConventionalCommit and return zero or more violation messages. The engine applies severity from the ruleset config and aggregates RuleMessages across all rules.

Properties

§readonly name:string
Stable identifier used to key the rule in a Ruleset
§interface

RuleContext

Runtime context passed to a rule invocation.

Properties

§readonly level:RuleLevel
Effective severity the engine will apply to any violations
§readonly options?:TOptions
Rule-specific options resolved from the ruleset config
§interface

RuleMessage

A single violation reported by the engine, tagged with its effective severity.

Properties

§readonly level:"error" | "warn"
Effective severity for this violation
§readonly message:string
Human-readable description of the violation
§readonly ruleName:string
Name of the rule that produced the violation
§interface

ScopeEnumOptions

Options for the scope-enum rule.

Properties

§readonly scopes:unknown
Allowed scope identifiers. Empty array disables the check.
§interface

SubjectCaseOptions

Options for the subject-case rule.

Properties

§readonly cases:unknown
One or more accepted case forms. A subject passing any accepted form is valid.
§interface

TypeEnumOptions

Options for the type-enum rule.

Properties

§readonly types:unknown
Allowed commit type identifiers. Empty array disables the check.
§interface

CommitValidationResult

Aggregated outcome of running a ruleset over a commit.

Properties

§readonly errors:unknown
Error-level violations (blocking)
§readonly valid:boolean
True when no error-level violations were recorded
§readonly warnings:unknown
Warn-level violations (non-blocking)

Types

§type

RuleConfig

Configuration tuple for a single rule in a ruleset.
A bare [level] entry disables the rule when level is 'off', or enables it with no options otherwise. The [level, options] form passes rule-specific options through to the rule's check() implementation.
type RuleConfig = unknown | unknown
§type

RuleLevel

Severity level for a rule in a ruleset.
type RuleLevel = "off" | "warn" | "error"
§type

RuleResult

Aggregated list of violations produced by the engine for a single ruleset run.
type RuleResult = unknown
§type

Ruleset

Mapping from rule name to its config tuple. Rules not present in the map are treated as 'off' by the engine.
type Ruleset = Readonly<Record<string, RuleConfig>>
§type

SubjectCase

Supported subject-case modes.
type SubjectCase = "sentence-case" | "lower-case" | "upper-case" | "kebab-case" | "snake-case" | "start-case"

Variables

§type

BUILT_IN_RULES

Registry of built-in rules the engine knows how to run, keyed by rule name.
§type

CONVENTIONAL_TYPES

Conventional Commits default type enum used by the preset.
§type

conventionalPreset

Default ruleset mirroring @commitlint/config-conventional's baseline.
| Rule | Level | Options | | ------------------ | ----- | ------------------------ | | type-enum | error | CONVENTIONAL_TYPES | | subject-empty | error | — | | scope-enum | off | — | | subject-case | off | — | | header-max-length | warn | { maxLength: 72 } | | imperative-mood | warn | — |
§type

DEFAULT_IMPERATIVE_WORDLIST

Default past-tense → imperative word map used when options are omitted.
§type

headerMaxLengthRule

Rule that fails when the reconstructed header (type(scope)!: subject) exceeds the configured maximum length.
§type

imperativeMoodRule

Rule that reports a warning when the first subject word (case-insensitive) matches a known past-tense form. Never blocks; intended as a nudge.
§type

scopeEnumRule

Rule that fails when any scope on the commit is not in the configured allow-list. A commit with no scopes (empty array) always passes; when scopes are required, pair this rule with a separate scope-empty check.
§type

subjectCaseRule

Rule that fails when the commit subject does not match any of the configured case forms. An empty subject always passes (handled by subject-empty).
§type

subjectEmptyRule

Rule that fails when the commit subject is empty or whitespace-only.
§type

typeEnumRule

Rule that fails when the commit type is not in the configured allow-list.