Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 17 additions & 15 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ module.exports = {
"parserOptions": {
"sourceType": "module"
},
"globals": {
"test": true,
"expect": true,
"describe": true
},
"rules": {
"node/no-unsupported-features": ["error"],
"accessor-pairs": "error",
Expand Down Expand Up @@ -64,12 +69,9 @@ module.exports = {
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "error",
"func-style": [
"error",
"expression"
],
"func-style": "off",
"generator-star-spacing": "error",
"global-require": "error",
"global-require": "off",
"guard-for-in": "error",
"handle-callback-err": "error",
"id-blacklist": "error",
Expand All @@ -94,18 +96,18 @@ module.exports = {
"max-params": "error",
"max-statements": "error",
"max-statements-per-line": "error",
"multiline-ternary": "error",
"multiline-ternary": "off",
"new-cap": "error",
"new-parens": "error",
"newline-after-var": "off",
"newline-before-return": "error",
"newline-before-return": "off",
"newline-per-chained-call": "error",
"no-alert": "error",
"no-array-constructor": "error",
"no-bitwise": "error",
"no-caller": "error",
"no-catch-shadow": "error",
"no-confusing-arrow": "error",
"no-confusing-arrow": "off",
"no-console": "off",
"no-continue": "error",
"no-div-regex": "error",
Expand Down Expand Up @@ -169,11 +171,11 @@ module.exports = {
"no-sync": "off",
"no-tabs": "error",
"no-template-curly-in-string": "error",
"no-ternary": "error",
"no-ternary": "off",
"no-throw-literal": "error",
"no-trailing-spaces": "error",
"no-undef-init": "error",
"no-undefined": "error",
"no-undefined": "off",
"no-underscore-dangle": "error",
"no-unmodified-loop-condition": "error",
"no-unneeded-ternary": "error",
Expand All @@ -191,8 +193,8 @@ module.exports = {
"no-warning-comments": "error",
"no-whitespace-before-property": "error",
"no-with": "error",
"object-curly-newline": "error",
"object-curly-spacing": "error",
"object-curly-newline": "off",
"object-curly-spacing": ["error", "always"],
"object-property-newline": "error",
"object-shorthand": "error",
"one-var": "off",
Expand All @@ -213,15 +215,15 @@ module.exports = {
"single"
],
"radix": "error",
"require-jsdoc": "error",
"require-jsdoc": "off",
"rest-spread-spacing": "error",
"semi": "off",
"semi": ["error", "always"],
"semi-spacing": "error",
"sort-imports": "error",
"sort-keys": "off",
"sort-vars": "error",
"space-before-blocks": "error",
"space-before-function-paren": "error",
"space-before-function-paren": ["error", "never"],
"space-in-parens": [
"error",
"never"
Expand Down
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
node_modules
.vscode
mock_hooks/prepare-commit-msg
3 changes: 1 addition & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@ language: node_js

node_js:
- 6

cache: yarn
- node

notifications:
email: false
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,21 @@ $ gitmoji-commit-hook --init

![Demo](https://github.com/tjoskar/gitmoji-commit-hook/blob/master/demo.gif?raw=true)

## Config

You can put unwanted emojis in a blacklist section by adding the name in a blacklist array in your `package.json`:

```json
{
"gitmoji": {
"blacklist": [
"card-file-box",
"beers"
]
}
}
```

## Emoji Meanings

A list of available emojis and their associated meanings can be found at [gitmoji.carloscuesta.me](https://gitmoji.carloscuesta.me/)
Expand All @@ -35,6 +50,19 @@ to add an emoji from gitmojis list to your commit.
If you're looking for some other cool feature like search in gitmojis list,
please consider [gitmoji-cli](https://github.com/carloscuesta/gitmoji-cli)

## Develop

To run the linter: `npm run lint`

To run the unit test: `npm test`

To dry run the script:
```bash
node invoke.js --init # run the init setup

node invoke.js mock_hooks/COMMIT_EDITMSG # simulate a git commit
```

## License

The code is available under the [MIT](https://github.com/tjoskar/gitmoji-commit-hook/blob/master/LICENSE) license.
86 changes: 2 additions & 84 deletions bin/index.js
Original file line number Diff line number Diff line change
@@ -1,88 +1,6 @@
#!/usr/bin/env node
'use strict';

const fs = require('fs');
const inquirer = require('inquirer');
const axios = require('axios');
const chalk = require('chalk');
const pathExists = require('path-exists');
const fileExists = require('file-exists');
const { gitmojiCommitHook } = require('./lib');

const gitmojiData = require('gitmoji-data/data/gitmojis.json');
const GITMOJI_DATA_URL = 'https://raw.githubusercontent.com/gyran/gitmoji-data/master/data/gitmojis.json';

const errorHandler = error => {
console.error(chalk.red(`🚨 ERROR: ${error}`));
process.exit(1);
};

let questions = []

const getGitmojiList = () => {
return axios.get(GITMOJI_DATA_URL)
.then((res) => {
if (res && res.data && res.data.gitmojis) {
return res.data.gitmojis;
}

throw new Error('Could not find gitmojis at url');
})
.catch(() => {
return gitmojiData.gitmojis;
});
};

if (process.argv[2] === '--init') {
const path = `${process.env.PWD}/.git/hooks`;

if (!pathExists.sync('.git')) {
errorHandler('The directory is not a git repository.')
}

if (fileExists.sync(`${path}/prepare-commit-msg`)) {
errorHandler(`A prepare-commit hook already exists, please remove the hook (rm ${path}/prepare-commit-msg)
or install gitmoji-commit-hook manually by adding the following content info ${path}/prepare-commit-msg: \nexec < /dev/tty\ngitmoji-commit-hook $1`);
}

fs.writeFile(`${path}/prepare-commit-msg`, '#!/bin/sh\nexec < /dev/tty\ngitmoji-commit-hook $1', writeError => {
if (writeError) {
errorHandler(writeError);
} else {
fs.chmod(`${path}/prepare-commit-msg`, '755', chmodError => {
if (chmodError) {
errorHandler(chmodError);
} else {
console.log(`${chalk.green('🎉 SUCCESS 🎉')} gitmoji-commit-hook initialized with success.`);
}
});
}
});
} else {
getGitmojiList()
.then((gitmojis) => {
questions.push({
type: 'checkbox',
name: 'emoji',
message: 'Select emoji(s) for your commit',
choices: gitmojis.map(gitmoji => {
return {
name: gitmoji.emoji + ' ' + gitmoji.description,
value: gitmoji.emoji
};
})
});

if(/COMMIT_EDITMSG/g.test(process.argv[2])) {
return inquirer.prompt(questions).then((answers) => {
let commitMsg = fs.readFileSync(process.argv[2]);
commitMsg = `${answers.emoji.join(' ')} ${commitMsg}`;
fs.writeFileSync(process.argv[2], commitMsg);

process.exit(0);
});
}
})
.catch(err => {
errorHandler(err);
});
}
gitmojiCommitHook(`${process.env.PWD}/.git/hooks`, process.argv[2]);
149 changes: 149 additions & 0 deletions bin/lib.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
'use strict';

const fs = require('fs');
const promisify = require('es6-promisify');
const inquirer = require('inquirer');
const axios = require('axios');
const chalk = require('chalk');
const pathExists = require('path-exists');
const fileExists = require('file-exists');
const { map, path, test, join } = require('ramda');

const writeFile = promisify(fs.writeFile);
const readFile = promisify(fs.readFile);
const chmod = promisify(fs.chmod);

const gitmojiUrl = 'https://raw.githubusercontent.com/carloscuesta/gitmoji/master/src/data/gitmojis.json';
const prepareCommitMsgFileName = 'prepare-commit-msg';

const gitmojiCommitHookComand = `#!/bin/sh
exec < /dev/tty
gitmoji-commit-hook $1
`;

const errorMessage = {
notGit: 'The directory is not a git repository.',
commitHookExist: `A prepare-commit hook already exists, please remove the hook (rm .git/hooks/${prepareCommitMsgFileName}) or install gitmoji-commit-hook manually by adding the following content info .git/hooks/\n\n${prepareCommitMsgFileName}:${gitmojiCommitHookComand}`,
gitmojiParse: 'Could not find gitmojis at url'
};

function errorHandler(error) {
console.error(chalk.red(`🚨 ERROR: ${error}`));
process.exit(1);
}

function rejectIf(errorMsg) {
return val => val ? Promise.reject(new Error(errorMsg)) : val;
}

function rejectIfNot(errorMsg) {
return val => val ? val : Promise.reject(new Error(errorMsg));
}

function getGitmojiList() {
return axios.get(gitmojiUrl)
.then(path(['data', 'gitmojis']))
.then(rejectIfNot(errorMessage.gitmojiParse));
}

function assertGitRepository() {
return pathExists('.git')
.then(rejectIfNot(errorMessage.notGit));
}

function assertNoPrepareCommitHook(gitHookPath) {
return () => fileExists(`${gitHookPath}/${prepareCommitMsgFileName}`)
.then(rejectIf(errorMessage.commitHookExist));
}

function initProject(gitHookPath) {
return assertGitRepository()
.then(assertNoPrepareCommitHook(gitHookPath))
.then(() => writeFile(`${gitHookPath}/${prepareCommitMsgFileName}`, gitmojiCommitHookComand))
.then(() => chmod(`${gitHookPath}/${prepareCommitMsgFileName}`, '755'));
}

function prependMessage(getMessage, putMessage) {
return filepath => message => getMessage(filepath)
.then(fileContent => `${message} ${fileContent}`)
.then(fileContent => putMessage(filepath, fileContent));
}

const prependMessageToFile = prependMessage(readFile, writeFile);

function getGitmojiBlacklist() {
return fileExists(`${process.env.PWD}/package.json`)
.then(exist => exist ? require(`${process.env.PWD}/package.json`) : {})
.then(packageJson => packageJson.gitmoji || {})
.then(gitmoji => gitmoji.blacklist || []);
}

function seperateChoices(choices) {
return blacklist => {
if (blacklist.length === 0) {
return choices;
}
return [
...choices.filter(choice => !blacklist.includes(choice.type)),
new inquirer.Separator(),
...choices.filter(choice => blacklist.includes(choice.type)),
new inquirer.Separator()
];
};
}

function seperateBlacklistEmojis(choices) {
return getGitmojiBlacklist()
.then(seperateChoices(choices));
}

function printInitSuccess() {
console.log(`${chalk.green('🎉 SUCCESS 🎉')} gitmoji-commit-hook initialized with success.`);
}

function mapGitmojiItemToOption(gitmoji) {
return {
name: gitmoji.emoji + ' ' + gitmoji.description,
value: gitmoji.emoji,
type: gitmoji.name
};
}

function createInquirerQuestion(emojis) {
return [{
type: 'checkbox',
name: 'emoji',
message: 'Select emoji(s) for your commit',
choices: emojis
}];
}

const isCommitEditMsgFile = test(/COMMIT_EDITMSG/g);

function gitmojiCommitHook(gitHookPath, commitFile) {
if (commitFile === '--init') {
initProject(gitHookPath)
.then(printInitSuccess)
.catch(errorHandler);
} else if (isCommitEditMsgFile(commitFile)) {
getGitmojiList()
.then(map(mapGitmojiItemToOption))
.then(seperateBlacklistEmojis)
.then(createInquirerQuestion)
.then(inquirer.prompt)
.then(answers => answers.emoji)
.then(join(' '))
.then(prependMessageToFile(commitFile))
.catch(errorHandler);
}
}

module.exports = {
rejectIf,
rejectIfNot,
gitmojiCommitHook,
prependMessage,
mapGitmojiItemToOption,
createInquirerQuestion,
seperateChoices
};
4 changes: 4 additions & 0 deletions invoke.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

const { gitmojiCommitHook } = require('./bin/lib')

gitmojiCommitHook(`${process.env.PWD}/mock_hooks`, process.argv[2])
Empty file added mock_hooks/COMMIT_EDITMSG
Empty file.
Loading