@adriencaccia/gitmoji-cli
Version:
A gitmoji client for using emojis on commit messages.
258 lines (227 loc) • 8.5 kB
JavaScript
const chalk = require('chalk')
const execa = require('execa')
const fs = require('fs')
const inquirer = require('inquirer')
const parentDirs = require('parent-dirs')
const path = require('path')
const pathExists = require('path-exists')
const config = require('./config')
const prompts = require('./prompts')
const constants = require('./constants')
inquirer.registerPrompt(
'autocomplete', require('inquirer-autocomplete-prompt')
)
class GitmojiCli {
constructor (gitmojiApiClient, trelloApiClient, gitmojis) {
this._gitmojiApiClient = gitmojiApiClient
this._trelloApiClient = trelloApiClient
this._gitmojis = gitmojis
if (config.getAutoAdd() === undefined) config.setAutoAdd(false)
if (!config.getEmojiFormat()) config.setEmojiFormat(constants.EMOJI)
if (config.getSignedCommit() === undefined) config.setSignedCommit(false)
if (config.getScopePrompt() === undefined) config.setScopePrompt(false)
if (config.getTrelloTicketNumberPrompt() === undefined) config.setTrelloTicketNumberPrompt(true)
}
config () {
inquirer.prompt(prompts.config).then(answers => {
config.setAutoAdd(answers[constants.AUTO_ADD])
config.setEmojiFormat(answers[constants.EMOJI_FORMAT])
config.setSignedCommit(answers[constants.SIGNED_COMMIT])
config.setScopePrompt(answers[constants.SCOPE_PROMPT])
config.setTrelloTicketNumberPrompt(answers[constants.TRELLO_TICKET_NUMBER_PROMPT])
inquirer.prompt(this._trelloApiClient.trelloApiPrompt()).then(answer => {
config.setTrelloApiToken(answer[constants.TRELLO_API_TOKEN])
})
})
}
init () {
if (!this._isAGitRepo()) {
return this._errorMessage('Not a git repository - @init')
}
execa('git', ['rev-parse', '--absolute-git-dir'])
.then(result => {
fs.writeFile(
result.stdout.trim() + constants.HOOK_PATH,
constants.HOOK_FILE_CONTENTS,
{ mode: constants.HOOK_PERMISSIONS },
(err) => {
if (err) this._errorMessage(err)
console.log(
`${chalk.yellow('gitmoji')} commit hook created successfully.`
)
}
)
})
.catch(err => {
return this._errorMessage(err)
})
}
remove () {
if (!this._isAGitRepo()) {
return this._errorMessage('Couldn\'t remove hook, not a git repository')
}
execa('git', ['rev-parse', '--absolute-git-dir'])
.then(result => {
fs.unlink(result.stdout.trim() + constants.HOOK_PATH, (err) => {
if (err) return this._errorMessage(err)
return console.log(
`${chalk.yellow('gitmoji')} commit hook unlinked successfully.`
)
})
})
.catch(err => {
return this._errorMessage(err)
})
}
list () {
return this._fetchEmojis()
.then(gitmojis => this._parseGitmojis(gitmojis))
.catch(err => this._errorMessage(`gitmoji list not found - ${err.code}`))
}
search (query) {
return this._fetchEmojis()
.then((gitmojis) => gitmojis.filter((gitmoji) => {
const emoji = gitmoji.name.concat(gitmoji.description).toLowerCase()
return (emoji.indexOf(query.toLowerCase()) !== -1)
}))
.then((gitmojisFiltered) => this._parseGitmojis(gitmojisFiltered))
.catch((err) => this._errorMessage(err.code))
}
ask (mode) {
if (!this._isAGitRepo()) {
return this._errorMessage('This directory is not a git repository.')
}
if (config.getTrelloBoardByPwd()) {
return this._ask(mode)
}
return inquirer.prompt(this._trelloApiClient.trelloBoardsPrompt()).then(answer => {
config.setTrelloBoardByPwd(answer.boardId)
return this._ask(mode)
})
}
_ask (mode) {
return this._fetchEmojis()
.then((gitmojis) => prompts.gitmoji(gitmojis))
.then((questions) => {
return inquirer.prompt(questions).then((answers) => {
if (answers.trelloTicketNumber) {
return this._trelloApiClient.fetchTicketInfo(answers.trelloTicketNumber)
.then(res => {
if (mode === constants.HOOK_MODE) this._hook(answers, res.data)
return this._commit(answers)
})
.catch(_ => {
console.log(chalk.red('Trello card not found !'))
if (mode === constants.HOOK_MODE) this._hook(answers)
return this._commit(answers)
})
} else {
if (mode === constants.HOOK_MODE) this._hook(answers)
return this._commit(answers)
}
})
})
.catch(err => this._errorMessage(err.code))
}
updateCache () {
this._fetchRemoteEmojis()
.then(emojis => this._createCache(this._getCachePath(), emojis))
}
_errorMessage (message) {
console.error(chalk.red(`ERROR: ${message}`))
}
_hook (answers, card = {}) {
const { name: cardName, shortUrl: cardUrl } = card
const trelloTicketNumberString = answers.trelloTicketNumber ? `[${answers.trelloTicketNumber}] ` : ''
const scopeString = answers.scope ? `(${answers.scope}): ` : ''
const title = `${trelloTicketNumberString}${answers.gitmoji} ${scopeString}${answers.title}`
const body = `${answers.message}\n${cardName || ''}\n${cardUrl || ''}`
try {
const commitFilePath = process.argv[3]
const commitFileContent = fs.readFileSync(commitFilePath).toString().split('\n')
const commitTitleAndMessage = [ title, '', body ]
commitFileContent.splice(0, 3, ...commitTitleAndMessage)
fs.writeFileSync(commitFilePath, commitFileContent.join('\n'))
} catch (error) {
return this._errorMessage(error)
}
process.exit(0)
}
_commit (answers) {
const trelloTicketNumberString = answers.trelloTicketNumber ? `[${answers.trelloTicketNumber}] ` : ''
const scopeString = answers.scope ? `(${answers.scope}): ` : ''
const title = `${trelloTicketNumberString}${answers.gitmoji} ${scopeString}${answers.title}`
const signed = config.getSignedCommit() ? '-S' : ''
const body = `${answers.message}`
const commit = `git commit ${signed} -m "${title}" -m "${body}"`
if (!this._isAGitRepo()) {
return this._errorMessage('Not a git repository')
}
if (config.getAutoAdd()) {
execa.stdout('git', ['add', '.'])
.then((res) => execa.shell(commit))
.then((res) => console.log(chalk.blue(res.stdout)))
.catch((err) => this._errorMessage(err.stderr ? err.stderr : err.stdout))
} else {
execa.shell(commit)
.then((res) => console.log(chalk.blue(res.stdout)))
.catch((err) => this._errorMessage(err.stderr ? err.stderr : err.stdout))
}
return commit
}
_parseGitmojis (gitmojis) {
return gitmojis.map(gitmoji => {
const emoji = gitmoji.emoji
const code = gitmoji.code
const description = gitmoji.description
return console.log(`${emoji} - ${chalk.blue(code)} - ${description}`)
})
}
_isAGitRepo () {
return parentDirs(process.cwd())
.some((directory) => pathExists.sync(path.resolve(directory, '.git')))
}
_getCachePath () {
const home = process.env.HOME || process.env.USERPROFILE
return path.join(home, '.gitmoji', 'gitmojis.json')
}
_cacheAvailable (cachePath) {
return pathExists.sync(cachePath)
}
_createCache (cachePath, emojis) {
const cacheDir = path.dirname(cachePath)
if (emojis !== undefined) {
if (!pathExists.sync(cacheDir)) {
fs.mkdirSync(cacheDir)
}
fs.writeFileSync(cachePath, JSON.stringify(emojis))
}
}
_fetchRemoteEmojis () {
console.log(`👋 Hey! We're fetching the emoji list...`)
return this._gitmojiApiClient.request({
method: 'GET',
url: '/src/data/gitmojis.json'
}).then((res) => {
console.log(`${chalk.yellow('Gitmojis')} updated successfully!`)
return res.data.gitmojis
})
.catch((error) =>
this._errorMessage(`Network connection not found - ${error.message}`)
)
}
_fetchCachedEmojis (cachePath) {
return Promise.resolve(JSON.parse(fs.readFileSync(cachePath)))
}
_fetchEmojis () {
const cachePath = this._getCachePath()
if (this._cacheAvailable(cachePath)) {
return this._fetchCachedEmojis(cachePath)
}
return this._fetchRemoteEmojis().then((emojis) => {
this._createCache(cachePath, emojis)
return emojis
})
}
}
module.exports = GitmojiCli