@hyperfrontend/versioning/commits/validatevalidate/
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:
- Inline step validators during interactive authoring (
commits/author/). - Preview-step warnings that render before the user confirms a commit.
- The out-of-band
clbin 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
- Create
rules/<name>.tsexporting aRule<YourOptions>whosecheck()returns a list of violation strings. - Register it in
rules/index.tsand inBUILT_IN_RULESinside engine.ts. - Write a colocated
.spec.tscovering: happy path, each failure branch, theoff/empty-options pass-through paths. - 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
- ../README.md: Parsing primitives this engine consumes
API Reference§
ƒ Functions
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
| Name | Type | Description |
|---|---|---|
§commit | ConventionalCommit | Parsed conventional commit to validate |
§ruleset | Ruleset | Configuration map from rule name to [level, options?] |
Returns
ValidationResultExample
Validating a commit against a preset ruleset
validateCommit(parseConventionalCommit('feat: add login'), conventionalPreset)
// => { valid: true, warnings: [], errors: [] }cl bin and anywhere else validation must start from a string rather than a parsed commit.Parameters
Returns
ValidationResultExample
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: ... }]validateCommitWithRules( commit: ConventionalCommit, ruleset: Ruleset, rules: Readonly< Record< string, Rule< never>>>): ValidationResult
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
Returns
ValidationResultExample
Extending the built-in rules with a custom rule
const customRules = { ...BUILT_IN_RULES, 'no-wip': noWipRule }
validateCommitWithRules(commit, { 'no-wip': ['error'] }, customRules)◈ Interfaces
Properties
Properties
Properties
ConventionalCommit and return zero or more violation messages. The engine applies severity from the ruleset config and aggregates RuleMessages across all rules.Properties
Methods
check( commit: ConventionalCommit, context: RuleContext< TOptions>): unknownParameters
| Name | Type | Description |
|---|---|---|
§commit | ConventionalCommit | Parsed conventional commit to validate |
§context | RuleContext< | Resolved severity and options for this invocation |
Returns
unknownProperties
Properties
Properties
Properties
readonly cases:unknownOne or more accepted case forms. A subject passing any accepted form is valid.Properties
◆ Types
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 | unknowntype RuleLevel = "off" | "warn" | "error"type RuleResult = unknown'off' by the engine.type Ruleset = Readonly< Record< string, RuleConfig>>type SubjectCase = "sentence-case" | "lower-case" | "upper-case" | "kebab-case" | "snake-case" | "start-case"● Variables
@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(scope)!: subject) exceeds the configured maximum length.Example
Enforcing a 72-character header limit
headerMaxLengthRule.check(
parseConventionalCommit('feat(core): short subject'),
{ level: 'warn', options: { maxLength: 72 } }
)
// => []Example
Hinting on past-tense verbs
imperativeMoodRule.check(
parseConventionalCommit('feat: added login'),
{ level: 'warn' }
)
// => ['subject should use imperative mood: use "add" instead of "added"']Example
Enforcing a scope enum
scopeEnumRule.check(
parseConventionalCommit('feat(auth): add login'),
{ level: 'error', options: { scopes: ['auth', 'api'] } }
)
// => []Example
Enforcing lower-case subjects
subjectCaseRule.check(
parseConventionalCommit('feat: Add login'),
{ level: 'error', options: { cases: ['lower-case'] } }
)
// => ['subject must be one of [lower-case] but was "Add login"']Example
Enforcing a non-empty subject
subjectEmptyRule.check(
parseConventionalCommit('feat: '),
{ level: 'error' }
)
// => ['subject must not be empty']Example
Enforcing a conventional type enum
typeEnumRule.check(
parseConventionalCommit('feat: add x'),
{ level: 'error', options: { types: ['feat', 'fix'] } }
)
// => []