Skip to content

Commit eaebda3

Browse files
committed
Initial commit
0 parents  commit eaebda3

File tree

11 files changed

+960
-0
lines changed

11 files changed

+960
-0
lines changed

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Node
2+
node_modules/
3+
4+
# MacOS
5+
.DS_Store

README.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<p align="center">
2+
<img width="580" src="assets/logo.png" alt="Script Palette">
3+
</p>
4+
5+
# CommitPal
6+
7+
A delightful CLI tool for building commit messages which conform to your project's specific commit message format.
8+
9+
<p align="center">
10+
<img width="580" src="assets/demo.gif" alt="Demo">
11+
</p>
12+
13+
## Install ⬇️
14+
15+
Install globally
16+
17+
```bash
18+
npm install -g commitpal
19+
```
20+
21+
## Get started 🏁
22+
23+
```bash
24+
commitpal
25+
```
26+
27+
Usage with npx
28+
29+
```bash
30+
npx commitpal
31+
```
32+
33+
## Configuration
34+
35+
If you're not using one of the predefined commit message formats, CommitPal will attempt to search for a `commitpal.config.json`.
36+
37+
Which can be configured like so:
38+
39+
```json
40+
{
41+
"name": "Angular commit message format",
42+
"steps": [
43+
{
44+
"type": "option",
45+
"message": "What type of change?",
46+
"options": [
47+
{ "value": "feat", "description": "feature" },
48+
{ "value": "fix", "description": "bug fix" },
49+
{ "value": "docs", "description": "documentation" },
50+
{
51+
"value": "style",
52+
"description": "formatting, missing semi colons, …"
53+
},
54+
{ "value": "test", "description": "tests" },
55+
{ "value": "chore", "description": "adhoc maintenance" }
56+
]
57+
},
58+
{
59+
"type": "option",
60+
"message": "What scope of the project is affected?",
61+
"before": "(",
62+
"after": "):",
63+
"options": [
64+
{ "value": "authentication", "description": "Authentication modules" },
65+
{ "value": "users", "description": "User profiles" },
66+
{ "value": "settings", "description": "Settings" },
67+
{ "value": "payments", "description": "Payments modules" },
68+
{ "value": "shipping", "description": "Shipping modules" },
69+
{ "value": "build system", "description": "Build system" },
70+
{ "value": "CI", "description": "Continuous integration" }
71+
]
72+
},
73+
{
74+
"type": "text",
75+
"message": "Summarise this change..."
76+
},
77+
{
78+
"type": "text",
79+
"before": "\n\n",
80+
"message": "Describe this change..."
81+
},
82+
{
83+
"type": "text",
84+
"before": "\n\n",
85+
"message": "Describe any breaking changes..."
86+
}
87+
]
88+
}
89+
```

assets/demo.gif

1.84 MB
Loading

assets/logo.png

241 KB
Loading

bin/file-manager.js

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
const fs = require("fs");
5+
const chalk = require("chalk");
6+
const { promisify } = require("util");
7+
8+
const readFile = promisify(fs.readFile);
9+
10+
function getConfig(path) {
11+
if (!hasFile(path)) {
12+
throw `${chalk.red("Error:")} ${chalk.bgRed(path)} ${chalk.red(
13+
"not found"
14+
)}`;
15+
}
16+
17+
return readFile(path).then(rawdata => JSON.parse(rawdata));
18+
}
19+
20+
function hasFile(path) {
21+
try {
22+
return fs.existsSync(path);
23+
} catch (error) {
24+
return false;
25+
}
26+
}
27+
28+
module.exports = {
29+
getConfig,
30+
hasFile
31+
};

bin/index.js

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
#!/usr/bin/env node
2+
"use strict";
3+
4+
const { prompt, Input, AutoComplete } = require("enquirer");
5+
const meow = require("meow");
6+
const findConfig = require("find-config");
7+
8+
const welcome = require("./welcome");
9+
const { getConfig, hasFile } = require("./file-manager");
10+
11+
function getStepPrompt(step) {
12+
switch (step.type) {
13+
case "option":
14+
return new AutoComplete({
15+
name: "step",
16+
message: step.message,
17+
choices: step.options.map(option => option.description)
18+
})
19+
.run()
20+
.then(choice => {
21+
const result = step.options.find(
22+
option => option.description === choice
23+
);
24+
25+
return result.value;
26+
});
27+
case "text":
28+
return new Input({
29+
message: step.message,
30+
initial: step.initial
31+
})
32+
.run()
33+
.then(choice => choice || "");
34+
default:
35+
throw `${chalk.red("Error: option type:")} ${chalk.bgRed(
36+
step.type
37+
)} ${chalk.red("not found")}`;
38+
}
39+
}
40+
41+
function getPreset(preset) {
42+
switch (preset) {
43+
case "angular":
44+
return `${__dirname}/presets/angular.json`;
45+
case "emoji":
46+
return `${__dirname}/presets/emoji.json`;
47+
default:
48+
throw `${chalk.red("Error: ")} ${chalk.bgRed(preset)} ${chalk.red(
49+
"is not a preset"
50+
)}`;
51+
break;
52+
}
53+
}
54+
55+
async function main(input, flags) {
56+
if (!flags.nowelcome) {
57+
welcome();
58+
}
59+
60+
try {
61+
const configPath = flags.config
62+
? flags.config
63+
: findConfig(`commitpal.config.json`);
64+
const presetPath = flags.preset ? getPreset(flags.preset) : undefined;
65+
66+
const config = await getConfig(presetPath || configPath);
67+
68+
const commitMessage = await config.steps.reduce(async (accum, step) => {
69+
const message = await accum;
70+
const prompt = getStepPrompt(step);
71+
const result = await prompt;
72+
73+
return `${message}${step.before || ""}${result}${step.after || ""}`;
74+
}, "");
75+
76+
// execute git commit. Maybe using: https://www.npmjs.com/package/simple-git
77+
78+
console.log("OUTPUT:", commitMessage);
79+
} catch (error) {
80+
if (error) {
81+
console.error(error, "🙅‍♂️");
82+
return 1;
83+
}
84+
85+
return 0;
86+
}
87+
}
88+
89+
const cli = meow(
90+
`
91+
Usage
92+
$ commitpal
93+
94+
Options
95+
--config, -c Custom configuration file
96+
--preset, -p Select an inbuilt preset. Options: 'angular', 'emoji'
97+
--nowelcome, -n Omit welcome message
98+
--help Help me
99+
--version, -v Version number
100+
101+
Examples
102+
$ commitpal --config ../commitpal.config.json
103+
$ commitpal --nowelcome
104+
$ commitpal --preset emoji
105+
$ npx commitpal
106+
`,
107+
{
108+
flags: {
109+
nowelcome: {
110+
type: "boolean",
111+
alias: "n"
112+
},
113+
config: {
114+
type: "string",
115+
alias: "c"
116+
},
117+
preset: {
118+
type: "string",
119+
alias: "p"
120+
}
121+
}
122+
}
123+
);
124+
125+
main(cli.input[0], cli.flags);

bin/presets/angular.json

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
{
2+
"name": "My project's commit message format",
3+
"steps": [
4+
{
5+
"type": "option",
6+
"message": "What type of change?",
7+
"options": [
8+
{ "value": "feat", "description": "feature" },
9+
{ "value": "fix", "description": "bug fix" },
10+
{ "value": "docs", "description": "documentation" },
11+
{
12+
"value": "style",
13+
"description": "formatting, missing semi colons, …"
14+
},
15+
{ "value": "test", "description": "tests" },
16+
{ "value": "chore", "description": "adhoc maintenance" }
17+
]
18+
},
19+
{
20+
"type": "option",
21+
"message": "What scope of the project is affected?",
22+
"before": "(",
23+
"after": "):",
24+
"options": [
25+
{ "value": "authentication", "description": "Authentication modules" },
26+
{ "value": "users", "description": "User profiles" },
27+
{ "value": "settings", "description": "Settings" },
28+
{ "value": "payments", "description": "Payments modules" },
29+
{ "value": "shipping", "description": "Shipping modules" },
30+
{ "value": "build system", "description": "Build system" },
31+
{ "value": "CI", "description": "Continuous integration" }
32+
]
33+
},
34+
{
35+
"type": "text",
36+
"message": "Summarise this change..."
37+
},
38+
{
39+
"type": "text",
40+
"before": "\n\n",
41+
"message": "Describe this change..."
42+
},
43+
{
44+
"type": "text",
45+
"before": "\n\n",
46+
"message": "Describe any breaking changes..."
47+
}
48+
]
49+
}

bin/presets/emoji.json

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "Emoji",
3+
"steps": [
4+
{
5+
"type": "option",
6+
"message": "What type of change?",
7+
"after": " ",
8+
"options": [
9+
{ "value": "🎉", "description": "🎉 Initial commit" },
10+
{ "value": "", "description": "✨ New feature" },
11+
{ "value": "🐛", "description": "🐛 Bugfix" },
12+
{ "value": "📚", "description": "📚 Documentation" },
13+
{ "value": "🐎", "description": "🐎 Performance" },
14+
{ "value": "💄", "description": "💄 Cosmetic" },
15+
{ "value": "🚨", "description": "🚨 Tests" },
16+
{ "value": "🎨", "description": "🎨 Improve format/structure" },
17+
{ "value": "🔨", "description": "🔨 Refactor code" },
18+
{ "value": "🚑", "description": "🚑 Critical hotfix" },
19+
{ "value": "🚧", "description": "🚧 Work in progress" },
20+
{ "value": "🔧", "description": "🔧 Configuration files" },
21+
{ "value": "💩", "description": "💩 Bad code" },
22+
{ "value": "", "description": "⏪ Reverting changes" },
23+
{ "value": "💥", "description": "💥 Breaking changes" }
24+
]
25+
},
26+
{
27+
"type": "text",
28+
"max": 50,
29+
"message": "Summarise this change..."
30+
}
31+
]
32+
}

bin/welcome.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
const gradient = require("gradient-string");
2+
3+
function welcome() {
4+
const logo = gradient.pastel("CommitPal ✨\n");
5+
6+
console.log(logo);
7+
}
8+
9+
module.exports = welcome;

0 commit comments

Comments
 (0)