@hyperfrontend/versioning/commits/formatformat/
Pure formatter for commit drafts.
Overview
Renders a CommitDraft (the partial, in-progress shape of a
ConventionalCommit) into the exact message string that will land in
.git/COMMIT_EDITMSG. Used by the authoring session's preview step and by
the live 72-character countdown in the subject step.
Behavior
- Missing fields render empty. Drafts accumulate field-by-field, so the
formatter tolerates any combination of absent values rather than throwing.
formatHeader({ type: 'fix' })returns'fix: '. - Multi-scope joins with
,. Matches the parser format —scope: ['a', 'b']→(a,b). - Breaking marker precedes the colon.
breaking: trueadds!after the (optional) scope:feat(core)!: subject. BREAKING CHANGE:footer is synthesized whenbreakingandbreakingDescriptionare set and no existingBREAKING CHANGE/BREAKING-CHANGEfooter is present. The synthesized footer goes at the top of the footer block.- Footer separators are respected verbatim.
':'prints as:;' #'prints as#(no injected space).
Usage
Render a finished message
import { formatCommitMessage } from '@hyperfrontend/versioning/commits/format'
formatCommitMessage({
type: 'feat',
scope: ['versioning', 'questions'],
subject: 'add x',
breaking: true,
breakingDescription: 'removed Y',
footers: [{ key: 'Closes', value: '#1', separator: ':' }],
})
// => 'feat(versioning,questions)!: add x\n\nBREAKING CHANGE: removed Y\nCloses: #1'
Drive a live header countdown
import { countHeaderLength } from '@hyperfrontend/versioning/commits/format'
const remaining = 72 - countHeaderLength({ type: 'feat', scope: ['core'] }, userInput)
Edit an existing commit
import { parseConventionalCommit } from '@hyperfrontend/versioning'
import { toDraft, formatCommitMessage } from '@hyperfrontend/versioning/commits/format'
const draft = toDraft(parseConventionalCommit('feat: add login'))
formatCommitMessage({ ...draft, subject: 'add sso login' })
// => 'feat: add sso login'
See Also
- ../README.md — Parsing primitives that this formatter mirrors
- ../validate/README.md — Rule engine that consumes the same draft shape
API Reference
ƒ Functions
Computes the character length of the final header including the
The subject is passed separately (not read from
type(scope)!: prefix and the supplied subject. Powers the live 72-character countdown shown during the subject prompt. The subject is passed separately (not read from
draft.subject) because the caller typically has not yet committed the in-flight input to the draft.Parameters
Returns
numberCharacter length the final header will occupy
Example
Counting a header mid-type
countHeaderLength({ type: 'feat', scope: ['core'] }, 'add login')
// => 'feat(core): add login'.length === 21Renders a draft as the final commit message string — the exact text that would be written to
If
.git/COMMIT_EDITMSG. Layout: header, blank line, body, blank line, footers. If
draft.breaking and draft.breakingDescription are set and no BREAKING CHANGE:-family footer is already present, one is synthesized at the top of the footer block so the preview mirrors what a compliant parser will read back.Parameters
| Name | Type | Description |
|---|---|---|
§draft | CommitDraft | Commit draft to render |
Returns
stringFull commit message string with
\n separatorsExamples
Header only
formatCommitMessage({ type: 'feat', subject: 'add login' })
// => 'feat: add login'Multi-scope breaking change with closing footer
formatCommitMessage({
type: 'feat',
scope: ['versioning', 'questions'],
subject: 'add x',
breaking: true,
breakingDescription: 'removed Y',
footers: [{ key: 'Closes', value: '#1', separator: ':' }],
})
// => 'feat(versioning,questions)!: add x\n\nBREAKING CHANGE: removed Y\nCloses: #1'Builds the header line (
Missing fields render as empty — this is intentional so the header can be displayed incrementally during authoring (e.g. before the subject has been typed). Scopes are comma-joined to match the parser's multi-scope format.
type(scope)!: subject) from a draft. Missing fields render as empty — this is intentional so the header can be displayed incrementally during authoring (e.g. before the subject has been typed). Scopes are comma-joined to match the parser's multi-scope format.
Parameters
| Name | Type | Description |
|---|---|---|
§draft | CommitDraft | Draft to render (may be partial) |
Returns
stringHeader line; never contains a newline
Examples
Rendering a complete header
formatHeader({ type: 'feat', scope: ['core'], subject: 'add login' })
// => 'feat(core): add login'Rendering a multi-scope breaking-change header
formatHeader({ type: 'feat', scope: ['versioning', 'questions'], subject: 'add x', breaking: true })
// => 'feat(versioning,questions)!: add x'Rendering a partially-built draft mid-session
formatHeader({ type: 'fix' })
// => 'fix: 'Promotes any
ConventionalCommit to a CommitDraft. Useful for editing a parsed commit inside an authoring session.Parameters
| Name | Type | Description |
|---|---|---|
§commit | ConventionalCommit | Parsed conventional commit |
Returns
CommitDraftA draft carrying the same fields as the commit
Example
Promoting a parsed commit to an editable draft
toDraft(parseConventionalCommit('feat: add login'))
// => { type: 'feat', scope: [], subject: 'add login', footers: [], breaking: false }◈ Interfaces
In-progress version of a
ConventionalCommit accumulated field-by-field during an interactive authoring session. Every field is optional so the formatter can render intermediate states (e.g. the header as it exists after only type has been picked).Properties
§
readonly breakingDescription?:string— Breaking change description — surfaces as a
BREAKING CHANGE: footer when none is already present