@@ -2,7 +2,7 @@ const path = require('path')
22const fs = require ( 'fs' )
33const _ = require ( 'lodash' )
44const execSync = require ( 'child_process' ) . execSync
5- const prompt = require ( 'prompt-sync' ) ( )
5+ const inquirer = require ( 'inquirer' ) ;
66const spellcheck = require ( 'nodehun-sentences' )
77const nodehun = require ( 'nodehun' )
88const emoji = require ( 'node-emoji' )
@@ -12,91 +12,69 @@ const colors = require('./colors')
1212//
1313// Functions
1414//
15- const formatChoices = ( choices ) => {
16- const maxNameLength = _ ( choices ) . map ( 'name' ) . map ( ( n ) => { return n . length } ) . max ( )
17- const maxEmojiLength = _ ( choices ) . map ( 'emoji' ) . map ( ( n ) => { return n . length } ) . max ( )
18-
19- return _ . map ( choices , ( choice ) => {
20- const n = _ . padEnd ( choice . name , maxNameLength )
21- const e = maxEmojiLength === 0 ? '' : _ . padEnd ( choice . emoji , 3 )
22- return [ n , e , choice . description ] . join ( " " )
23- } ) . join ( "\n" )
15+ const getCurrentBranchName = ( ) => {
16+ return execSync ( "git rev-parse --abbrev-ref HEAD" ) . toString ( ) . trim ( )
2417}
2518
26- const select = ( term , definition ) => {
27- const none = "(none)"
28- const header = `Select ${ term } : `
29- const choices = _ . map ( definition . values , function ( value ) {
30- const name = value . name
31- const description = value . description
32- let emojiStr = ''
19+ const branchText = ( definition ) => {
20+ const currentBranchName = getCurrentBranchName ( )
21+ const regExp = new RegExp ( definition . regExp , definition . regExpFlag )
22+ const matchNum = definition . regExpMatchNum || 1
23+ const match = currentBranchName . match ( regExp )
24+ const value = match ? match [ matchNum ] : ''
25+ return value
26+ }
3327
34- if ( / ^ : . + : $ / . test ( name ) && emoji . hasEmoji ( name ) ) emojiStr = emoji . get ( name )
35- return { name : name , emoji : emojiStr , description : description }
36- } )
37- if ( ! definition . required ) choices . unshift ( { name : '' , emoji : '' , description : none } )
28+ const checkAddedFile = ( ) => {
29+ const addedFiles = execSync ( `git diff -- name-only --cached 2> /dev/null` ) . toString ( ) . trim ( )
30+ if ( _ . isEmpty ( addedFiles ) ) throw new Error ( `no changes added to commit.` )
31+ }
3832
39- const command = `echo "${ formatChoices ( choices ) . replace ( / " / g, '\\"' ) } " | fzf --reverse --cycle --header="${ header } "`
33+ const checkSpell = ( message ) => {
34+ const dictionaryBase = path . dirname ( require . resolve ( 'dictionary-en-us' ) )
35+ const hunspell = new nodehun (
36+ fs . readFileSync ( path . join ( dictionaryBase , 'index.aff' ) ) ,
37+ fs . readFileSync ( path . join ( dictionaryBase , 'index.dic' ) )
38+ )
4039
41- const selectedValue = execSync ( command , { stdio : [ 'inherit' , 'pipe' , 'inherit' ] , shell : true } ) . toString ( ) . trim ( )
42- const value = selectedValue . split ( " " ) [ 0 ]
43- const v = value === none ? '' : value
44- console . log ( `${ header } ${ value } ` )
45- return v
46- }
40+ spellcheck ( hunspell , message , ( e , typos ) => {
41+ if ( e ) throw e
4742
48- const inputString = ( term , _definition ) => {
49- return prompt ( `${ colors . message } Enter ${ term } : ${ colors . reset } ` )
43+ _ . forEach ( typos , ( typo ) => {
44+ process . stdout . write ( `${ colors . warning } Is '${ typo . word } ' misspelled? ${ colors . reset } ` )
45+ console . log ( `${ colors . warning } Did you mean that? ${ typo . suggestions . map ( ( s ) => { return `'${ s } '` } ) . join ( ', ' ) } ${ colors . reset } ` )
46+ } )
47+ } )
5048}
5149
52- const inputText = ( term , _definition ) => {
53- console . log ( `${ colors . message } Enter ${ term } multiline:${ colors . reset } ` )
54- let values = [ ]
55- let value = ""
50+ const createChoicesNames = ( values ) => {
51+ const maxLength = _ ( values ) . map ( 'name' ) . map ( ( n ) => { return n . length } ) . max ( )
5652
57- do {
58- value = prompt ( )
59- values . push ( value )
60- } while ( ! _ . isEmpty ( value ) )
53+ const result = { }
54+ _ . forEach ( values , ( value ) => {
55+ const temp = [ ]
56+ temp . push ( _ . padEnd ( value . name , maxLength ) )
57+ if ( / ^ : .+ : $ / . test ( value . name ) && emoji . hasEmoji ( value . name ) ) temp . push ( emoji . get ( value . name ) + ' ' )
58+ temp . push ( value . description )
6159
62- return values . join ( "\n" ) . trim ( )
60+ result [ value . name ] = temp . join ( ' ' )
61+ } )
62+ return result
6363}
6464
65- const getCurrentBranchName = ( ) => {
66- return execSync ( "git rev-parse --abbrev-ref HEAD" ) . toString ( ) . trim ( )
67- }
65+ const createChoices = ( definition ) => {
66+ const names = createChoicesNames ( definition . values )
6867
69- const branchText = ( _term , definition ) => {
70- const currentBranchName = getCurrentBranchName ( )
71- const regExp = new RegExp ( definition . regExp , definition . regExpFlag )
72- const matchNum = definition . regExpMatchNum || 1
73- const match = currentBranchName . match ( regExp )
74- const value = match ? match [ matchNum ] : ''
75- return value
76- }
68+ const choices = _ . map ( definition . values , ( value ) => {
69+ return {
70+ name : names [ value . name ] ,
71+ value : value . name ,
72+ short : value . name ,
73+ }
74+ } )
75+ if ( ! definition . required ) choices . unshift ( { name : 'none' , value : '' , short : 'none' } )
7776
78- const input = ( term , definition ) => {
79- const type = definition . type
80- let inputValue
81- switch ( type ) {
82- case 'enum' :
83- inputValue = select ( term , definition )
84- break
85- case 'string' :
86- inputValue = inputString ( term , definition )
87- break
88- case 'text' :
89- inputValue = inputText ( term , definition )
90- break
91- case 'branch' :
92- inputValue = branchText ( term , definition )
93- break
94- case 'variable' :
95- break
96- default :
97- throw new Error ( `${ type } is not defined.` )
98- }
99- return inputValue
77+ return choices
10078}
10179
10280const checkValue = ( term , value , definition ) => {
@@ -147,74 +125,97 @@ const checkValue = (term, value, definition) => {
147125 return errorMessages . join ( "\n" )
148126}
149127
150- const replaceTerms = ( program , template , definitions , terms ) => {
151- if ( _ . isEmpty ( terms ) ) {
152- return template
153- } else {
154- const term = terms [ 0 ]
155- const definition = definitions [ term ]
156- template = replaceTerm ( program , template , definition , term )
157- return replaceTerms ( program , template , definitions , _ . tail ( terms ) )
158- }
159- }
160-
161- const replaceTerm = ( program , template , definition , term ) => {
162- let value = definition . type === 'variable' ? program [ definition . origin ] : program [ term ]
163-
164- if ( program . interactive ) {
165- if ( _ . isUndefined ( value ) || _ . isNull ( value ) ) {
166- if ( definition . required || ! program . skipOptions ) {
167- value = input ( term , definition )
168- } else {
169- value = ''
128+ const createQuestion = ( definition , term ) => {
129+ let result = { }
130+ switch ( definition . type ) {
131+ case 'enum' :
132+ result = {
133+ type : 'list' ,
134+ name : term ,
135+ message : `Select ${ term } :` ,
136+ choices : createChoices ( definition )
170137 }
171- let errorMessages = checkValue ( term , value , definition )
172- while ( ! _ . isEmpty ( errorMessages ) ) {
173- console . log ( `${ colors . warning } ${ errorMessages } ${ colors . reset } ` )
174- value = input ( term , definition )
175- errorMessages = checkValue ( term , value , definition )
138+ if ( definition . default ) result . default = definition . default
139+ return result
140+ case 'string' :
141+ result = {
142+ type : 'input' ,
143+ name : term ,
144+ message : `Write ${ term } :` ,
176145 }
177- }
178- } else {
179- if ( _ . isEmpty ( value ) && definition . required ) throw new Error ( `${ term } is required.` )
146+ if ( definition . default ) result . default = definition . default
147+ return result
148+ case 'text' :
149+ result = {
150+ type : 'editor' ,
151+ name : term ,
152+ message : `Write ${ term } :` ,
153+ }
154+ if ( definition . default ) result . default = definition . default
155+ return result
156+ case 'variable' :
157+ case 'branch' :
158+ return
159+ default :
160+ throw new Error ( `${ ruleName } is not defined.` )
180161 }
162+ }
181163
182- const errorMessages = checkValue ( term , value , definition )
183- if ( ! _ . isEmpty ( errorMessages ) ) throw new Error ( errorMessages )
184-
185- program [ term ] = value
164+ const createQuestions = ( program , definitions , terms ) => {
165+ return _ ( terms ) . map ( ( term ) => {
166+ const definition = definitions [ term ]
167+ if ( ! _ . isUndefined ( program [ term ] ) || ! _ . isUndefined ( definition . default ) ) return
168+ return createQuestion ( definitions [ term ] , term )
169+ } ) . compact ( ) . value ( )
170+ }
186171
187- let decoratedValue = value
188- if ( ! _ . isEmpty ( value ) ) {
189- if ( definition . prefix ) decoratedValue = definition . prefix + decoratedValue
190- if ( definition . suffix ) decoratedValue = decoratedValue + definition . suffix
172+ const getValue = ( program , definition , answer , term ) => {
173+ switch ( definition . type ) {
174+ case 'enum' :
175+ case 'string' :
176+ case 'text' :
177+ if ( ! _ . isUndefined ( program [ term ] ) ) return program [ term ]
178+ if ( ! _ . isUndefined ( answer ) ) return answer
179+ if ( ! _ . isUndefined ( definition . default ) ) return definition . default
180+ return ''
181+ case 'variable' :
182+ if ( ! _ . isUndefined ( program [ definition . origin ] ) ) return program [ definition . origin ]
183+ return ''
184+ case 'branch' :
185+ const val = branchText ( definition )
186+ if ( ! _ . isUndefined ( val ) ) return val
187+ return ''
188+ default :
189+ throw new Error ( `${ definition . type } is not defined.` )
191190 }
192-
193- return _ . replace ( template , `<${ term } >` , decoratedValue )
194191}
195192
196- const checkSpell = ( message ) => {
197- const dictionaryBase = path . dirname ( require . resolve ( 'dictionary-en-us' ) )
198- const hunspell = new nodehun (
199- fs . readFileSync ( path . join ( dictionaryBase , 'index.aff' ) ) ,
200- fs . readFileSync ( path . join ( dictionaryBase , 'index.dic' ) )
201- )
193+ const replaceTerms = ( program , template , definitions , terms , callback ) => {
194+ inquirer . prompt ( createQuestions ( program , definitions , terms ) ) . then ( ( answers ) => {
195+ let result = template
196+ _ . forEach ( terms , ( term ) => {
197+ const definition = definitions [ term ]
198+ const answer = answers [ term ]
199+ const value = getValue ( program , definition , answer , term )
202200
203- spellcheck ( hunspell , message , ( e , typos ) => {
204- if ( e ) throw e
201+ program [ term ] = value
205202
206- _ . forEach ( typos , ( typo ) => {
207- process . stdout . write ( `${ colors . warning } Is '${ typo . word } ' misspelled? ${ colors . reset } ` )
208- console . log ( `${ colors . warning } Did you mean that? ${ typo . suggestions . map ( ( s ) => { return `'${ s } '` } ) . join ( ', ' ) } ${ colors . reset } ` )
203+ const errorMessages = checkValue ( term , value , definition )
204+ if ( ! _ . isEmpty ( errorMessages ) ) throw new Error ( errorMessages )
205+
206+ let decoratedValue = value
207+ if ( ! _ . isEmpty ( value ) ) {
208+ if ( definition . prefix ) decoratedValue = definition . prefix + decoratedValue
209+ if ( definition . suffix ) decoratedValue = decoratedValue + definition . suffix
210+ }
211+ result = _ . replace ( result , `<${ term } >` , decoratedValue )
209212 } )
213+ callback ( result )
214+ } ) . catch ( ( e ) => {
215+ console . error ( `${ colors . error } ${ e . message } ${ colors . reset } ` )
210216 } )
211217}
212218
213- const checkAddedFile = ( ) => {
214- const addedFiles = execSync ( `git diff --name-only --cached 2> /dev/null` ) . toString ( ) . trim ( )
215- if ( _ . isEmpty ( addedFiles ) ) throw new Error ( `no changes added to commit.` )
216- }
217-
218219const gitCommit = ( commitMessage , duet = false , silent = false , dryRun = false ) => {
219220 const escapedCommitMessage = commitMessage . replace ( / " / g, '\\"' )
220221
@@ -242,9 +243,9 @@ const gitCommit = (commitMessage, duet = false, silent = false, dryRun = false)
242243module . exports = ( program , template , definitions , terms ) => {
243244 if ( ! program . dryRun ) checkAddedFile ( )
244245
245- const commitMessage = replaceTerms ( program , template , definitions , terms )
246+ replaceTerms ( program , template , definitions , terms , ( commitMessage ) => {
247+ if ( program . typoCheck ) checkSpell ( commitMessage )
246248
247- if ( program . typoCheck ) checkSpell ( commitMessage )
248-
249- gitCommit ( commitMessage . trim ( ) , program . duet , program . silent , program . dryRun )
249+ gitCommit ( commitMessage . trim ( ) , program . duet , program . silent , program . dryRun )
250+ } )
250251}
0 commit comments