@hyperfrontend/versioning/commits/format

format/

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: true adds ! after the (optional) scope: feat(core)!: subject.
  • BREAKING CHANGE: footer is synthesized when breaking and breakingDescription are set and no existing BREAKING CHANGE / BREAKING-CHANGE footer 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

API Reference

ƒ Functions

§function

countHeaderLength(draft: CommitDraft, subject: string): number

Computes the character length of the final header including the 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

NameTypeDescription
§draft
CommitDraft
Draft supplying type, scope, and breaking marker
§subject
string
Subject text being typed

Returns

number
Character length the final header will occupy

Example

Counting a header mid-type

countHeaderLength({ type: 'feat', scope: ['core'] }, 'add login')
// => 'feat(core): add login'.length === 21
§function

formatCommitMessage(draft: CommitDraft): string

Renders a draft as the final commit message string — the exact text that would be written to .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

NameTypeDescription
§draft
CommitDraft
Commit draft to render

Returns

string
Full commit message string with \n separators

Examples

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'
§function

formatHeader(draft: CommitDraft): string

Builds the header line (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

NameTypeDescription
§draft
CommitDraft
Draft to render (may be partial)

Returns

string
Header 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: '
§function

toDraft(commit: ConventionalCommit): CommitDraft

Promotes any ConventionalCommit to a CommitDraft. Useful for editing a parsed commit inside an authoring session.

Parameters

NameTypeDescription
§commit
ConventionalCommit
Parsed conventional commit

Returns

CommitDraft
A 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

§interface

CommitDraft

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 body?:string
Full commit body
§readonly breaking?:boolean
Whether this is a breaking change — controls the ! subject marker
§readonly breakingDescription?:string
Breaking change description — surfaces as a BREAKING CHANGE: footer when none is already present
§readonly footers?:unknown
Footer trailers — caller may leave out BREAKING CHANGE:; the formatter will synthesize it when needed
§readonly scope?:unknown
Scopes in parentheses (empty or omitted = no scope)
§readonly subject?:string
Subject line
§readonly type?:string
Commit type (feat, fix, docs, etc.)