Skip to content

Commit e016978

Browse files
committed
refactor: update commit message hook error help
1 parent d5bd685 commit e016978

2 files changed

Lines changed: 87 additions & 27 deletions

File tree

lib/commands/git/commit-msg.js

Lines changed: 78 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import fs from "node:fs";
22
import ansi from "#core/ansi";
33
import Commit from "#core/api/git/commit";
44
import Command from "#lib/command";
5+
import Changelog from "#lib/git/changelog";
56

67
export default class extends Command {
8+
#commitsConfig;
79

810
// static
911
static cli () {
@@ -38,18 +40,10 @@ git commit -m"feat(core)!: commit description"
3840
const pkg = this._findGitPackage();
3941
if ( !pkg ) return;
4042

41-
// prepare types
42-
const types = {};
43-
for ( const [ type, config ] of Object.entries( pkg.cliConfig?.commits.types || {} ) ) {
44-
if ( !config ) continue;
43+
this.#commitsConfig = pkg.cliConfig?.commits;
4544

46-
types[ type ] = {
47-
...config,
48-
type,
49-
};
50-
}
51-
52-
const message = fs.readFileSync( process.cli.arguments.path, "utf8" ),
45+
const types = this.#getTypes(),
46+
message = fs.readFileSync( process.cli.arguments.path, "utf8" ),
5347
isRevert = fs.existsSync( ".git/REVERT_HEAD" );
5448

5549
if ( fs.existsSync( ".git/MERGE_HEAD" ) ) {
@@ -90,17 +84,15 @@ git commit -m"feat(core)!: commit description"
9084
return this.#throwError( `Commit message subject should not ends with ".".` );
9185
}
9286

93-
const type = types[ commit.type ],
94-
scopes = new Map( Object.entries( type?.productionChange
95-
? pkg.cliConfig?.commits.scopes || {}
96-
: type?.scopes || {} ).filter( ( [ key, value ] ) => value ) );
87+
const type = types.get( commit.type ),
88+
scopes = this.#getTypeScopes( type );
9789

9890
// check strict type
9991
if ( pkg.cliConfig?.commits.strictType ) {
10092

10193
// commit type is not valid
10294
if ( !type ) {
103-
return this.#throwError( `Commit type is not valid. Allowed types:\n${ this.#getAllowedTypes( types ) }` );
95+
return this.#throwError( "Commit type is not valid.", { "types": true } );
10496
}
10597
}
10698

@@ -113,7 +105,7 @@ git commit -m"feat(core)!: commit description"
113105
// commit scope is not valid
114106
if ( !scopes.has( commit.scope ) ) {
115107
if ( scopes.size ) {
116-
return this.#throwError( `Commit scope is not valid. Allowed scopes for this commit type are:\n${ this.#getAllowedScopes( scopes ) }` );
108+
return this.#throwError( "Commit scope is not valid.", { "types": true } );
117109
}
118110
else {
119111
return this.#throwError( `Commit scope is not allowed for this commit type.` );
@@ -124,7 +116,7 @@ git commit -m"feat(core)!: commit description"
124116

125117
// commit scope is required
126118
else if ( type.requireScope && scopes.size ) {
127-
return this.#throwError( `Commit scope is required. Allowed scopes for this commit type are:\n${ this.#getAllowedScopes( scopes ) }` );
119+
return this.#throwError( "Commit scope is required.", { "types": true } );
128120
}
129121

130122
// restrict breaking changes to the production changes only
@@ -142,22 +134,81 @@ git commit -m"feat(core)!: commit description"
142134

143135
// commit type is required
144136
else if ( pkg.cliConfig?.commits.requireType ) {
145-
return this.#throwError( `Commit type is required. Allowed types:\n${ this.#getAllowedTypes( types ) }` );
137+
return this.#throwError( "Commit type is required.", { "types": true } );
146138
}
147139
}
148140

149141
// private
150-
#getAllowedTypes ( types ) {
151-
return Object.values( types )
152-
.map( type => ` - "${ type.type }": ${ type.description }` )
153-
.join( "\n" );
142+
#getTypes () {
143+
const types = new Map();
144+
145+
if ( this.#commitsConfig ) {
146+
for ( const [ name, type ] of Object.entries( this.#commitsConfig.types ) ) {
147+
if ( !type ) continue;
148+
149+
type.name = name;
150+
151+
types.set( name, type );
152+
}
153+
}
154+
155+
return types;
154156
}
155157

156-
#getAllowedScopes ( scopes ) {
157-
return [ ...scopes.keys() ].map( scope => ` - "${ scope }": ${ scopes.get( scope ) }` ).join( "\n" );
158+
#getTypeScopes ( type ) {
159+
const scopes = new Map( Object.entries( type?.productionChange
160+
? this.#commitsConfig?.scopes || {}
161+
: type?.scopes || {} ).filter( ( [ key, value ] ) => value ) );
162+
163+
return scopes;
164+
}
165+
166+
#throwError ( message, { types } = {} ) {
167+
if ( types ) {
168+
const help = this.#createTypesHelp();
169+
170+
console.error( help );
171+
}
172+
173+
const statusText = `${ message }\nRefer to the documentation: ${ ansi.link( "https://www.conventionalcommits.org/en/" ) }.`;
174+
175+
throw result( [ 500, statusText ] );
158176
}
159177

160-
#throwError ( message ) {
161-
throw result( [ 500, `${ message }\nRefer to the documentation: ${ ansi.link( "https://www.conventionalcommits.org/en/" ) }.` ] );
178+
#createTypesHelp () {
179+
var markdown = "";
180+
181+
const types = this.#getTypes();
182+
183+
if ( types.size ) {
184+
markdown += `Commit types:\n`;
185+
186+
for ( const type of types.values() ) {
187+
markdown += `
188+
- \`${ type.name }\`: ${ type.title || Changelog.getTypeTitle( type.name ) }. ${ type.description }
189+
`;
190+
191+
const scopes = this.#getTypeScopes( type );
192+
193+
if ( scopes.size ) {
194+
if ( type.requireScope ) {
195+
markdown += `Commit scope is required. `;
196+
}
197+
198+
markdown += `Allowed scopes for this commit type:\n`;
199+
200+
for ( const [ name, description ] of scopes.entries() ) {
201+
markdown += `
202+
- \`${ name }\`: ${ description }`;
203+
}
204+
}
205+
}
206+
}
207+
208+
var text = Changelog.convertMarkdownToText( markdown, { "ansi": true } );
209+
210+
if ( text ) text += "\n";
211+
212+
return text;
162213
}
163214
}

lib/git/changelog.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,15 @@ export default class GitChangelog {
3737
this.#indexChanges( commitTypes || DEFAULT_COMMIT_TYPES );
3838
}
3939

40+
// static
41+
static getTypeTitle ( type ) {
42+
return TITLES[ type ];
43+
}
44+
45+
static convertMarkdownToText ( markdown, { ansi } = {} ) {
46+
return new Markdown( markdown ).toString( { ansi } ).trim();
47+
}
48+
4049
// properties
4150
get previousRelease () {
4251
return this.#previousRelease;

0 commit comments

Comments
 (0)