Skip to content

Commit 6a24e28

Browse files
committed
feat(gen-config): implement gen-config
1 parent f953116 commit 6a24e28

7 files changed

Lines changed: 221 additions & 6 deletions

File tree

.git_consistent

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ scope:
5454
values:
5555
-
5656
name: "main"
57-
description: 'git-consistent'
57+
description: 'main.js'
58+
-
59+
name: "gen-config"
60+
description: 'gen-config.js'
5861
subject:
5962
type: string
6063
required: true
@@ -67,7 +70,7 @@ body:
6770
type: text
6871
default: ''
6972
required: false
70-
description: 'Body'
73+
description: 'The body contains details of the change'
7174
rules:
7275
firstLatter: upper
7376
dotAtEnd: true

git-consistent

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ try {
6363
if (process.argv.includes('--gen-config')) {
6464
program
6565
.parse(process.argv)
66+
genConfig(program, templateFileName, definitionsFileName)
6667
} else {
6768
const projectRoot = getProjectRoot()
6869
const rootPath = projectRoot === "" ? process.env.HOME : projectRoot
@@ -78,5 +79,6 @@ try {
7879
main(program, template, definitions, terms)
7980
}
8081
} catch (e) {
82+
throw e
8183
console.error(`${colors.error}${e.message}${colors.reset}`)
8284
}

lib/gen-config.js

Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
const path = require('path')
2+
const fs = require('fs')
3+
const _ = require('lodash')
4+
const execSync = require('child_process').execSync
5+
const prompt = require('prompt-sync')()
6+
const csvParse = require('csv-parse/lib/sync')
7+
const yaml = require('js-yaml')
8+
9+
const colors = require('./colors')
10+
11+
//
12+
// Functions
13+
//
14+
const getMaxLength = (types, headName) => {
15+
return _(types).map(headName).map((n) => { return n.length }).max()
16+
}
17+
18+
const createChoices = (types) => {
19+
const maxLengths = {}
20+
_(types[0]).keys().forEach((key) => {
21+
maxLengths[key] = getMaxLength(types, key)
22+
})
23+
24+
return _.map(types, (type) => {
25+
return _.map(type, (value, key) => {
26+
return _.padEnd(type[key], maxLengths[key])
27+
}).join(" ")
28+
}).join("\n")
29+
}
30+
31+
const selectTypes = (types) => {
32+
const header = "choice types by <TAB>"
33+
const choices = createChoices(types)
34+
const command = `echo "${choices.replace(/"/g, '\\"')}" | fzf -m --reverse --header="${header}"`
35+
const selectedValues = execSync(command, { stdio: ['inherit', 'pipe', 'inherit'], shell: true }).toString().trim().split("\n")
36+
37+
const selectedNames = selectedValues.map((v) => { return _.words(v)[0] })
38+
39+
return _.filter(types, (type) => {
40+
return selectedNames.includes(type.name)
41+
})
42+
}
43+
44+
const promptBool = (text, defaultValue = true) => {
45+
const defaultStr = defaultValue ? '(Y/n)' : '(y/N)'
46+
const input = prompt(`${text} ${defaultStr}: `)
47+
48+
if (defaultValue) {
49+
if ((/^n(o)?/i).test(input)) {
50+
return false
51+
} else {
52+
return true
53+
}
54+
} else {
55+
if ((/^y(es)?/i).test(input)) {
56+
return true
57+
} else {
58+
return false
59+
}
60+
}
61+
}
62+
63+
const generateTemplateFile = (templateFileName, templateDefines, dryRun = false) => {
64+
let typeName = ''
65+
if(templateDefines.type) {
66+
if(templateDefines.type.useEmoji) {
67+
typeName = '<emoji> '
68+
} else {
69+
typeName = '<type>: '
70+
}
71+
}
72+
73+
const file = `${typeName}<subject>
74+
75+
<body>`
76+
77+
writeFile(path.join('.', templateFileName), file, dryRun)
78+
}
79+
80+
const generateDefinitionsFile = (definitionsFileName, templateDefines, dryRun = false) => {
81+
const content = {}
82+
83+
if(templateDefines.type) {
84+
const name = templateDefines.type.useEmoji ? 'emoji' : 'type'
85+
86+
let values = []
87+
if(templateDefines.type.useEmoji) {
88+
values = _.map(templateDefines.type.types, (type) => {
89+
return {
90+
name: type.emojiSign,
91+
description: `${type.meaning}: ${type.description}`,
92+
}
93+
})
94+
} else {
95+
values = _.map(templateDefines.type.types, (type) => {
96+
return {
97+
name: type.name,
98+
description: `${type.meaning}: ${type.description}`,
99+
}
100+
})
101+
}
102+
103+
content[name] = {
104+
type: 'enum',
105+
required: true,
106+
description: 'commit type',
107+
values: values,
108+
}
109+
}
110+
111+
content.subject = {
112+
type: 'string',
113+
required: true,
114+
description: 'The subject contains succinct description of the change',
115+
rules: {
116+
firstLatter: templateDefines.subject.lowerCaseStart ? 'lower' : 'upper',
117+
dotAtEnd: templateDefines.subject.dotAtEnd,
118+
ascii: false,
119+
}
120+
}
121+
122+
content.body = {
123+
type: 'text',
124+
default: '',
125+
required: false,
126+
description: 'The body contains details of the change',
127+
rules: {
128+
firstLatter: 'upper',
129+
dotAtEnd: true,
130+
ascii: false,
131+
}
132+
}
133+
134+
writeFile(path.join('.', definitionsFileName), yaml.safeDump(content), dryRun)
135+
}
136+
137+
const writeFile = (path, text, dryRun = false) => {
138+
if (fs.existsSync(path)) {
139+
if(promptBool(`${colors.warning}'${path}' is already exists. over write it?${colors.reset}`, false)) {
140+
if (dryRun) {
141+
console.log("-------------------------")
142+
console.log(text)
143+
console.log("-------------------------")
144+
} else {
145+
fs.writeFileSync(path, text)
146+
}
147+
} else {
148+
console.log(`Canceled generate '${path}'.`)
149+
}
150+
} else {
151+
if (dryRun) {
152+
console.log("-------------------------")
153+
console.log(text)
154+
console.log("-------------------------")
155+
} else {
156+
fs.writeFileSync(path, text)
157+
}
158+
}
159+
}
160+
161+
//
162+
// Main
163+
//
164+
module.exports = (program, templateFileName, definitionsFileName, dryRun = false) => {
165+
const templateDefines = {}
166+
167+
if(promptBool(`${colors.message}Use Type?${colors.reset}`, true)) {
168+
const typesCsv = fs.readFileSync(path.join('lib', 'types.csv'))
169+
const types = csvParse(typesCsv, {columns: true})
170+
171+
templateDefines.type = {
172+
types: selectTypes(types),
173+
useEmoji: promptBool(`${colors.message}Use Emoji?${colors.reset}`, false),
174+
}
175+
}
176+
177+
templateDefines.subject = {
178+
lowerCaseStart: promptBool(`${colors.message}Does the subject start with lower case?${colors.reset}`, true),
179+
dotAtEnd: promptBool(`${colors.message}Does the subject put dot (.) at end?${colors.reset}`, false),
180+
}
181+
182+
generateTemplateFile(templateFileName, templateDefines, program.dryRun)
183+
generateDefinitionsFile(definitionsFileName, templateDefines, program.dryRun)
184+
185+
console.log(`Generated '${templateFileName}' and '${definitionsFileName}'.`)
186+
console.log(`You can edit them freely.`)
187+
console.log(`Enjoy!`)
188+
}

lib/main.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ const colors = require('./colors')
1313
// Functions
1414
//
1515
const formatChoices = (choices) => {
16-
const nameMaxLength = _(choices).map('name').map((n) => { return n.length }).max()
17-
const emojiMaxLength = _(choices).map('emoji').map((n) => { return n.length }).max()
16+
const maxNameLength = _(choices).map('name').map((n) => { return n.length }).max()
17+
const maxEmojiLength = _(choices).map('emoji').map((n) => { return n.length }).max()
1818

1919
return _.map(choices, (choice) => {
20-
const n = _.padEnd(choice.name, nameMaxLength)
21-
const e = emojiMaxLength === 0 ? '' : _.padEnd(choice.emoji, 3)
20+
const n = _.padEnd(choice.name, maxNameLength)
21+
const e = maxEmojiLength === 0 ? '' : _.padEnd(choice.emoji, 3)
2222
return [n, e, choice.description].join(" ")
2323
}).join("\n")
2424
}

lib/types.csv

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
name,meaning,emojiSign,isCommitizen,description
2+
feat,Features,:heavy_plus_sign:,commitizen,when implementing function
3+
fix,Bug Fixes,:sunny:,commitizen,when fixing a bug
4+
docs,Documentation,:pencil2:,commitizen,when writing docs
5+
style,Styles,:shirt:,commitizen,when change coding style
6+
refactor,Code Refactoring,:art:,commitizen,when refactoring
7+
perf,Performances,:rocket:,commitizen,when improving performance
8+
test,Tests,:white_check_mark:,commitizen,when adding tests
9+
build,Builds,:arrow_heading_up:,commitizen,when changes build system
10+
ci,Continuous Integrations,:green_heart:,commitizen,when change the CI build
11+
chore,Chores,:wrench:,commitizen,when change other that don't modify code or test files
12+
revert,Reverts,:arrow_heading_down:,commitizen,when reverts a previous commit
13+
remove,Removes,:heavy_minus_sign:,,when removing function
14+
sec,Securities,:lock:,,when dealing with security
15+
up,Update dependencies,:arrow_up:,,when upgrading dependencies
16+
down,Downgrade dependencies,:arrow_down:,,when downgrading dependencies
17+
tada,Celebrats,:tada:,,when celebrating

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
},
2121
"dependencies": {
2222
"commander": "^2.14.1",
23+
"csv-parse": "^2.0.4",
2324
"dictionary-en-us": "^2.0.0",
2425
"js-yaml": "^3.10.0",
2526
"lodash": "^4.17.5",

yarn.lock

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,10 @@ commander@^2.14.1:
2222
version "2.14.1"
2323
resolved "https://registry.yarnpkg.com/commander/-/commander-2.14.1.tgz#2235123e37af8ca3c65df45b026dbd357b01b9aa"
2424

25+
csv-parse@^2.0.4:
26+
version "2.0.4"
27+
resolved "https://registry.yarnpkg.com/csv-parse/-/csv-parse-2.0.4.tgz#5b469596766ee07f2cbd71aced7ae76b3c73c901"
28+
2529
dictionary-en-us@^2.0.0:
2630
version "2.0.0"
2731
resolved "https://registry.yarnpkg.com/dictionary-en-us/-/dictionary-en-us-2.0.0.tgz#b9135f403444bb5e4b3cf465fb7d3f2fd276a839"

0 commit comments

Comments
 (0)