@@ -2,8 +2,10 @@ import fs from "node:fs";
22import ansi from "#core/ansi" ;
33import Commit from "#core/api/git/commit" ;
44import Command from "#lib/command" ;
5+ import Changelog from "#lib/git/changelog" ;
56
67export 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}
0 commit comments