better-commits 1.1.0 โ†’ 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,31 @@
1
+ name: Publish to NPM
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - "src/**.ts"
9
+
10
+ jobs:
11
+ publish:
12
+ name: Publish
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - name: Checkout
16
+ uses: actions/checkout@v3
17
+ with:
18
+ fetch-depth: 0
19
+ - name: Setup Node.js
20
+ uses: actions/setup-node@v3
21
+ with:
22
+ node-version: "lts/*"
23
+ - name: Install dependencies
24
+ run: npm ci
25
+ - name: Build package
26
+ run: npm run build
27
+ - name: Release
28
+ env:
29
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
30
+ NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
31
+ run: npx semantic-release
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2023 Erik Verduin
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/dist/index.js CHANGED
@@ -47,15 +47,15 @@ var REGEX_START_TAG = new RegExp(/^(\w+-\d+)/);
47
47
  var REGEX_SLASH_NUM = new RegExp(/\/(\d+)/);
48
48
  var REGEX_START_NUM = new RegExp(/^(\d+)/);
49
49
  var DEFAULT_TYPE_OPTIONS = [
50
- { value: "feat", label: "feat", hint: "A new feature" },
51
- { value: "fix", label: "fix", hint: "A bug fix" },
52
- { value: "docs", label: "docs", hint: "Documentation only changes" },
53
- { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature" },
54
- { value: "perf", label: "perf", hint: "A code change that improves performance" },
55
- { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests" },
56
- { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies" },
57
- { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts" },
58
- { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files" },
50
+ { value: "feat", label: "feat", hint: "A new feature", emoji: "\u2728" },
51
+ { value: "fix", label: "fix", hint: "A bug fix", emoji: "\u{1F41B}" },
52
+ { value: "docs", label: "docs", hint: "Documentation only changes", emoji: "\u{1F4DA}" },
53
+ { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature", emoji: "\u{1F528}" },
54
+ { value: "perf", label: "perf", hint: "A code change that improves performance", emoji: "\u{1F680}" },
55
+ { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests", emoji: "\u{1F6A8}" },
56
+ { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies", emoji: "\u{1F6A7}" },
57
+ { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts", emoji: "\u{1F916}" },
58
+ { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files", emoji: "\u{1F9F9}" },
59
59
  { value: "", label: "none" }
60
60
  ];
61
61
  var DEFAULT_SCOPE_OPTIONS = [
@@ -90,6 +90,9 @@ function infer_type_from_branch(types) {
90
90
  });
91
91
  return found ?? "";
92
92
  }
93
+ function get_git_root() {
94
+ return (0, import_child_process.execSync)("git rev-parse --show-toplevel").toString().trim();
95
+ }
93
96
  function get_default_config_path() {
94
97
  return (0, import_os.homedir)() + "/" + CONFIG_FILE_NAME;
95
98
  }
@@ -115,15 +118,24 @@ var Config = import_zod2.z.object({
115
118
  enable: import_zod2.z.boolean().default(true),
116
119
  initial_value: import_zod2.z.string().default("feat"),
117
120
  infer_type_from_branch: import_zod2.z.boolean().default(true),
121
+ append_emoji_to_label: import_zod2.z.boolean().default(false),
122
+ append_emoji_to_commit: import_zod2.z.boolean().default(false),
118
123
  options: import_zod2.z.array(import_zod2.z.object({
119
124
  value: import_zod2.z.string(),
120
125
  label: import_zod2.z.string().optional(),
121
- hint: import_zod2.z.string().optional()
126
+ hint: import_zod2.z.string().optional(),
127
+ emoji: import_zod2.z.string().emoji().optional()
122
128
  })).default(DEFAULT_TYPE_OPTIONS)
123
- }).default({}).refine((val) => {
124
- const options = val.options.map((v) => v.value);
125
- return options.includes(val.initial_value);
126
- }, (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })),
129
+ }).default({}).transform((val) => {
130
+ const options = val.options.map((v) => ({
131
+ ...v,
132
+ label: v.emoji && val.append_emoji_to_label ? `${v.emoji} ${v.label}` : v.label
133
+ }));
134
+ return { ...val, options };
135
+ }).refine(
136
+ (val) => val.options.map((v) => v.value).includes(val.initial_value),
137
+ (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })
138
+ ),
127
139
  commit_scope: import_zod2.z.object({
128
140
  enable: import_zod2.z.boolean().default(true),
129
141
  custom_scope: import_zod2.z.boolean().default(false),
@@ -190,7 +202,7 @@ main(load_setup());
190
202
  function load_setup() {
191
203
  console.clear();
192
204
  p.intro(`${import_picocolors2.default.bgCyan(import_picocolors2.default.black(" better-commits "))}`);
193
- const root = (0, import_child_process2.execSync)("git rev-parse --show-toplevel").toString().trim();
205
+ const root = get_git_root();
194
206
  const root_path = `${root}/${CONFIG_FILE_NAME}`;
195
207
  if (import_fs.default.existsSync(root_path)) {
196
208
  p.log.step("Found repository config");
@@ -203,7 +215,7 @@ function load_setup() {
203
215
  }
204
216
  const default_config = Config.parse({});
205
217
  p.log.step("Config not found. Generating default .better-commit.json at $HOME");
206
- import_fs.default.writeFileSync(home_path, JSON.stringify(default_config, null, " "));
218
+ import_fs.default.writeFileSync(home_path, JSON.stringify(default_config, null, 4));
207
219
  return default_config;
208
220
  }
209
221
  function read_config_from_path(config_path) {
@@ -226,7 +238,8 @@ function validate_config(config) {
226
238
  }
227
239
  async function main(config) {
228
240
  let commit_state = CommitState.parse({});
229
- let git_status = await (0, import_simple_git.simpleGit)().status();
241
+ const simple_git = (0, import_simple_git.simpleGit)({ baseDir: get_git_root() });
242
+ let git_status = await simple_git.status();
230
243
  if (config.check_status) {
231
244
  p.log.step(import_picocolors2.default.black(import_picocolors2.default.bgGreen(" Checking Git Status ")));
232
245
  const missing_files = check_missing_stage(git_status);
@@ -242,8 +255,8 @@ async function main(config) {
242
255
  });
243
256
  if (p.isCancel(selected_for_staging))
244
257
  process.exit(0);
245
- await (0, import_simple_git.simpleGit)().add(selected_for_staging);
246
- git_status = await (0, import_simple_git.simpleGit)().status();
258
+ await simple_git.add(selected_for_staging);
259
+ git_status = await simple_git.status();
247
260
  if (selected_for_staging?.length) {
248
261
  p.log.success(import_picocolors2.default.green("Changes successfully staged"));
249
262
  }
@@ -261,6 +274,10 @@ async function main(config) {
261
274
  if (type_from_branch)
262
275
  initial_value = type_from_branch;
263
276
  }
277
+ const value_to_emoji = config.commit_type.options.reduce(
278
+ (acc, curr) => ({ ...acc, [curr.value]: curr.emoji ?? "" }),
279
+ {}
280
+ );
264
281
  const commit_type = await p.select(
265
282
  {
266
283
  message: `Select a commit type`,
@@ -270,7 +287,7 @@ async function main(config) {
270
287
  );
271
288
  if (p.isCancel(commit_type))
272
289
  process.exit(0);
273
- commit_state.type = commit_type;
290
+ commit_state.type = config.commit_type.append_emoji_to_commit ? `${value_to_emoji[commit_type]} ${commit_type}`.trim() : commit_type;
274
291
  }
275
292
  if (config.commit_scope.enable) {
276
293
  let commit_scope = await p.select({
package/dist/init.js CHANGED
@@ -29,6 +29,7 @@ var import_zod2 = require("zod");
29
29
  // src/utils.ts
30
30
  var import_zod = require("zod");
31
31
  var import_picocolors = __toESM(require("picocolors"));
32
+ var import_child_process = require("child_process");
32
33
  var CONFIG_FILE_NAME = ".better-commits.json";
33
34
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
34
35
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
@@ -37,15 +38,15 @@ var REGEX_START_TAG = new RegExp(/^(\w+-\d+)/);
37
38
  var REGEX_SLASH_NUM = new RegExp(/\/(\d+)/);
38
39
  var REGEX_START_NUM = new RegExp(/^(\d+)/);
39
40
  var DEFAULT_TYPE_OPTIONS = [
40
- { value: "feat", label: "feat", hint: "A new feature" },
41
- { value: "fix", label: "fix", hint: "A bug fix" },
42
- { value: "docs", label: "docs", hint: "Documentation only changes" },
43
- { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature" },
44
- { value: "perf", label: "perf", hint: "A code change that improves performance" },
45
- { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests" },
46
- { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies" },
47
- { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts" },
48
- { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files" },
41
+ { value: "feat", label: "feat", hint: "A new feature", emoji: "\u2728" },
42
+ { value: "fix", label: "fix", hint: "A bug fix", emoji: "\u{1F41B}" },
43
+ { value: "docs", label: "docs", hint: "Documentation only changes", emoji: "\u{1F4DA}" },
44
+ { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature", emoji: "\u{1F528}" },
45
+ { value: "perf", label: "perf", hint: "A code change that improves performance", emoji: "\u{1F680}" },
46
+ { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests", emoji: "\u{1F6A8}" },
47
+ { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies", emoji: "\u{1F6A7}" },
48
+ { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts", emoji: "\u{1F916}" },
49
+ { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files", emoji: "\u{1F9F9}" },
49
50
  { value: "", label: "none" }
50
51
  ];
51
52
  var DEFAULT_SCOPE_OPTIONS = [
@@ -58,6 +59,9 @@ var DEFAULT_SCOPE_OPTIONS = [
58
59
  var CUSTOM_SCOPE_KEY = "custom";
59
60
  var Z_FOOTER_OPTIONS = import_zod.z.enum(["closes", "breaking-change", "deprecated", "custom"]);
60
61
  var FOOTER_OPTION_VALUES = ["closes", "breaking-change", "deprecated", "custom"];
62
+ function get_git_root() {
63
+ return (0, import_child_process.execSync)("git rev-parse --show-toplevel").toString().trim();
64
+ }
61
65
 
62
66
  // src/zod-state.ts
63
67
  var Config = import_zod2.z.object({
@@ -66,15 +70,24 @@ var Config = import_zod2.z.object({
66
70
  enable: import_zod2.z.boolean().default(true),
67
71
  initial_value: import_zod2.z.string().default("feat"),
68
72
  infer_type_from_branch: import_zod2.z.boolean().default(true),
73
+ append_emoji_to_label: import_zod2.z.boolean().default(false),
74
+ append_emoji_to_commit: import_zod2.z.boolean().default(false),
69
75
  options: import_zod2.z.array(import_zod2.z.object({
70
76
  value: import_zod2.z.string(),
71
77
  label: import_zod2.z.string().optional(),
72
- hint: import_zod2.z.string().optional()
78
+ hint: import_zod2.z.string().optional(),
79
+ emoji: import_zod2.z.string().emoji().optional()
73
80
  })).default(DEFAULT_TYPE_OPTIONS)
74
- }).default({}).refine((val) => {
75
- const options = val.options.map((v) => v.value);
76
- return options.includes(val.initial_value);
77
- }, (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })),
81
+ }).default({}).transform((val) => {
82
+ const options = val.options.map((v) => ({
83
+ ...v,
84
+ label: v.emoji && val.append_emoji_to_label ? `${v.emoji} ${v.label}` : v.label
85
+ }));
86
+ return { ...val, options };
87
+ }).refine(
88
+ (val) => val.options.map((v) => v.value).includes(val.initial_value),
89
+ (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })
90
+ ),
78
91
  commit_scope: import_zod2.z.object({
79
92
  enable: import_zod2.z.boolean().default(true),
80
93
  custom_scope: import_zod2.z.boolean().default(false),
@@ -139,15 +152,14 @@ var CommitState = import_zod2.z.object({
139
152
  // src/init.ts
140
153
  var import_picocolors2 = __toESM(require("picocolors"));
141
154
  var import_fs = __toESM(require("fs"));
142
- var import_child_process = require("child_process");
143
155
  var p = __toESM(require("@clack/prompts"));
144
156
  try {
145
157
  console.clear();
146
158
  p.intro(`${import_picocolors2.default.bgCyan(import_picocolors2.default.black(" better-commits-init "))}`);
147
- const root = (0, import_child_process.execSync)("git rev-parse --show-toplevel").toString().trim();
159
+ const root = get_git_root();
148
160
  const root_path = `${root}/${CONFIG_FILE_NAME}`;
149
161
  const default_config = Config.parse({});
150
- import_fs.default.writeFileSync(root_path, JSON.stringify(default_config, null, " "));
162
+ import_fs.default.writeFileSync(root_path, JSON.stringify(default_config, null, 4));
151
163
  p.log.success(`${import_picocolors2.default.green("Successfully created .better-commits.json")}`);
152
164
  p.outro(`Run ${import_picocolors2.default.bgBlack(import_picocolors2.default.white("better-commits"))} to start the CLI`);
153
165
  } catch (err) {
package/dist/utils.js CHANGED
@@ -47,6 +47,7 @@ __export(utils_exports, {
47
47
  check_missing_stage: () => check_missing_stage,
48
48
  clean_commit_title: () => clean_commit_title,
49
49
  get_default_config_path: () => get_default_config_path,
50
+ get_git_root: () => get_git_root,
50
51
  infer_type_from_branch: () => infer_type_from_branch
51
52
  });
52
53
  module.exports = __toCommonJS(utils_exports);
@@ -62,15 +63,15 @@ var REGEX_START_TAG = new RegExp(/^(\w+-\d+)/);
62
63
  var REGEX_SLASH_NUM = new RegExp(/\/(\d+)/);
63
64
  var REGEX_START_NUM = new RegExp(/^(\d+)/);
64
65
  var DEFAULT_TYPE_OPTIONS = [
65
- { value: "feat", label: "feat", hint: "A new feature" },
66
- { value: "fix", label: "fix", hint: "A bug fix" },
67
- { value: "docs", label: "docs", hint: "Documentation only changes" },
68
- { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature" },
69
- { value: "perf", label: "perf", hint: "A code change that improves performance" },
70
- { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests" },
71
- { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies" },
72
- { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts" },
73
- { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files" },
66
+ { value: "feat", label: "feat", hint: "A new feature", emoji: "\u2728" },
67
+ { value: "fix", label: "fix", hint: "A bug fix", emoji: "\u{1F41B}" },
68
+ { value: "docs", label: "docs", hint: "Documentation only changes", emoji: "\u{1F4DA}" },
69
+ { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature", emoji: "\u{1F528}" },
70
+ { value: "perf", label: "perf", hint: "A code change that improves performance", emoji: "\u{1F680}" },
71
+ { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests", emoji: "\u{1F6A8}" },
72
+ { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies", emoji: "\u{1F6A7}" },
73
+ { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts", emoji: "\u{1F916}" },
74
+ { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files", emoji: "\u{1F9F9}" },
74
75
  { value: "", label: "none" }
75
76
  ];
76
77
  var DEFAULT_SCOPE_OPTIONS = [
@@ -105,6 +106,9 @@ function infer_type_from_branch(types) {
105
106
  });
106
107
  return found ?? "";
107
108
  }
109
+ function get_git_root() {
110
+ return (0, import_child_process.execSync)("git rev-parse --show-toplevel").toString().trim();
111
+ }
108
112
  function get_default_config_path() {
109
113
  return (0, import_os.homedir)() + "/" + CONFIG_FILE_NAME;
110
114
  }
@@ -141,5 +145,6 @@ function clean_commit_title(title) {
141
145
  check_missing_stage,
142
146
  clean_commit_title,
143
147
  get_default_config_path,
148
+ get_git_root,
144
149
  infer_type_from_branch
145
150
  });
package/dist/zod-state.js CHANGED
@@ -46,15 +46,15 @@ var REGEX_START_TAG = new RegExp(/^(\w+-\d+)/);
46
46
  var REGEX_SLASH_NUM = new RegExp(/\/(\d+)/);
47
47
  var REGEX_START_NUM = new RegExp(/^(\d+)/);
48
48
  var DEFAULT_TYPE_OPTIONS = [
49
- { value: "feat", label: "feat", hint: "A new feature" },
50
- { value: "fix", label: "fix", hint: "A bug fix" },
51
- { value: "docs", label: "docs", hint: "Documentation only changes" },
52
- { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature" },
53
- { value: "perf", label: "perf", hint: "A code change that improves performance" },
54
- { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests" },
55
- { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies" },
56
- { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts" },
57
- { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files" },
49
+ { value: "feat", label: "feat", hint: "A new feature", emoji: "\u2728" },
50
+ { value: "fix", label: "fix", hint: "A bug fix", emoji: "\u{1F41B}" },
51
+ { value: "docs", label: "docs", hint: "Documentation only changes", emoji: "\u{1F4DA}" },
52
+ { value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature", emoji: "\u{1F528}" },
53
+ { value: "perf", label: "perf", hint: "A code change that improves performance", emoji: "\u{1F680}" },
54
+ { value: "test", label: "test", hint: "Adding missing tests or correcting existing tests", emoji: "\u{1F6A8}" },
55
+ { value: "build", label: "build", hint: "Changes that affect the build system or external dependencies", emoji: "\u{1F6A7}" },
56
+ { value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts", emoji: "\u{1F916}" },
57
+ { value: "chore", label: "chore", hint: "Other changes that do not modify src or test files", emoji: "\u{1F9F9}" },
58
58
  { value: "", label: "none" }
59
59
  ];
60
60
  var DEFAULT_SCOPE_OPTIONS = [
@@ -75,15 +75,24 @@ var Config = import_zod2.z.object({
75
75
  enable: import_zod2.z.boolean().default(true),
76
76
  initial_value: import_zod2.z.string().default("feat"),
77
77
  infer_type_from_branch: import_zod2.z.boolean().default(true),
78
+ append_emoji_to_label: import_zod2.z.boolean().default(false),
79
+ append_emoji_to_commit: import_zod2.z.boolean().default(false),
78
80
  options: import_zod2.z.array(import_zod2.z.object({
79
81
  value: import_zod2.z.string(),
80
82
  label: import_zod2.z.string().optional(),
81
- hint: import_zod2.z.string().optional()
83
+ hint: import_zod2.z.string().optional(),
84
+ emoji: import_zod2.z.string().emoji().optional()
82
85
  })).default(DEFAULT_TYPE_OPTIONS)
83
- }).default({}).refine((val) => {
84
- const options = val.options.map((v) => v.value);
85
- return options.includes(val.initial_value);
86
- }, (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })),
86
+ }).default({}).transform((val) => {
87
+ const options = val.options.map((v) => ({
88
+ ...v,
89
+ label: v.emoji && val.append_emoji_to_label ? `${v.emoji} ${v.label}` : v.label
90
+ }));
91
+ return { ...val, options };
92
+ }).refine(
93
+ (val) => val.options.map((v) => v.value).includes(val.initial_value),
94
+ (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })
95
+ ),
87
96
  commit_scope: import_zod2.z.object({
88
97
  enable: import_zod2.z.boolean().default(true),
89
98
  custom_scope: import_zod2.z.boolean().default(false),
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "better-commits",
3
3
  "private": false,
4
- "version": "1.1.0",
4
+ "version": "1.2.0",
5
5
  "description": "A CLI for creating better commits following the conventional commit guidelines",
6
6
  "author": "Erik Verduin (https://github.com/everduin94)",
7
7
  "keywords": [
@@ -33,9 +33,31 @@
33
33
  "commit": "jiti ./src/index.ts"
34
34
  },
35
35
  "devDependencies": {
36
+ "@semantic-release/git": "^10.0.1",
36
37
  "@types/node": "^18.14.5",
37
38
  "jiti": "^1.17.0",
39
+ "semantic-release": "^21.0.1",
38
40
  "tsup": "^6.6.3",
39
41
  "tsx": "^3.12.3"
42
+ },
43
+ "release": {
44
+ "branches": [
45
+ "main"
46
+ ],
47
+ "plugins": [
48
+ "@semantic-release/commit-analyzer",
49
+ "@semantic-release/release-notes-generator",
50
+ "@semantic-release/npm",
51
+ "@semantic-release/github",
52
+ [
53
+ "@semantic-release/git",
54
+ {
55
+ "assets": [
56
+ "package.json"
57
+ ],
58
+ "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}"
59
+ }
60
+ ]
61
+ ]
40
62
  }
41
63
  }
package/readme.md CHANGED
@@ -61,136 +61,154 @@ All properties are optional, they can be removed from your configuration and wil
61
61
 
62
62
  ```json
63
63
  {
64
- "check_status": true,
65
- "commit_type": {
66
- "enable": true,
67
- "initial_value": "feat",
68
- "infer_type_from_branch": true,
69
- "options": [
70
- {
71
- "value": "feat",
72
- "label": "feat",
73
- "hint": "A new feature"
74
- },
75
- {
76
- "value": "fix",
77
- "label": "fix",
78
- "hint": "A bug fix"
79
- },
80
- {
81
- "value": "docs",
82
- "label": "docs",
83
- "hint": "Documentation only changes"
84
- },
85
- {
86
- "value": "refactor",
87
- "label": "refactor",
88
- "hint": "A code change that neither fixes a bug nor adds a feature"
89
- },
90
- {
91
- "value": "perf",
92
- "label": "perf",
93
- "hint": "A code change that improves performance"
94
- },
95
- {
96
- "value": "test",
97
- "label": "test",
98
- "hint": "Adding missing tests or correcting existing tests"
99
- },
100
- {
101
- "value": "build",
102
- "label": "build",
103
- "hint": "Changes that affect the build system or external dependencies"
104
- },
105
- {
106
- "value": "ci",
107
- "label": "ci",
108
- "hint": "Changes to our CI configuration files and scripts"
109
- },
110
- {
111
- "value": "chore",
112
- "label": "chore",
113
- "hint": "Other changes that do not modify src or test files"
114
- },
115
- {
116
- "value": "",
117
- "label": "none"
118
- }
119
- ]
120
- },
121
- "commit_scope": {
122
- "enable": true,
123
- "custom_scope": false,
124
- "initial_value": "app",
125
- "options": [
126
- {
127
- "value": "app",
128
- "label": "app"
129
- },
130
- {
131
- "value": "shared",
132
- "label": "shared"
133
- },
134
- {
135
- "value": "server",
136
- "label": "server"
137
- },
138
- {
139
- "value": "tools",
140
- "label": "tools"
141
- },
142
- {
143
- "value": "",
144
- "label": "none"
145
- }
146
- ]
147
- },
148
- "check_ticket": {
149
- "infer_ticket": true,
150
- "confirm_ticket": true,
151
- "add_to_title": true,
152
- "append_hashtag": false
153
- },
154
- "commit_title": {
155
- "max_size": 70
156
- },
157
- "commit_body": {
158
- "enable": true,
159
- "required": false
160
- },
161
- "commit_footer": {
162
- "enable": true,
163
- "initial_value": [],
164
- "options": [
165
- "closes",
166
- "breaking-change",
167
- "deprecated",
168
- "custom"
169
- ]
170
- },
171
- "breaking_change": {
172
- "add_exclamation_to_title": true
173
- },
174
- "confirm_commit": true,
175
- "print_commit_output": true
64
+ "check_status": true,
65
+ "commit_type": {
66
+ "enable": true,
67
+ "initial_value": "feat",
68
+ "infer_type_from_branch": true,
69
+ "append_emoji_to_label": false,
70
+ "append_emoji_to_commit": false,
71
+ "options": [
72
+ {
73
+ "value": "feat",
74
+ "label": "feat",
75
+ "hint": "A new feature",
76
+ "emoji": "โœจ"
77
+ },
78
+ {
79
+ "value": "fix",
80
+ "label": "fix",
81
+ "hint": "A bug fix",
82
+ "emoji": "๐Ÿ›"
83
+ },
84
+ {
85
+ "value": "docs",
86
+ "label": "docs",
87
+ "hint": "Documentation only changes",
88
+ "emoji": "๐Ÿ“š"
89
+ },
90
+ {
91
+ "value": "refactor",
92
+ "label": "refactor",
93
+ "hint": "A code change that neither fixes a bug nor adds a feature",
94
+ "emoji": "๐Ÿ”จ"
95
+ },
96
+ {
97
+ "value": "perf",
98
+ "label": "perf",
99
+ "hint": "A code change that improves performance",
100
+ "emoji": "๐Ÿš€"
101
+ },
102
+ {
103
+ "value": "test",
104
+ "label": "test",
105
+ "hint": "Adding missing tests or correcting existing tests",
106
+ "emoji": "๐Ÿšจ"
107
+ },
108
+ {
109
+ "value": "build",
110
+ "label": "build",
111
+ "hint": "Changes that affect the build system or external dependencies",
112
+ "emoji": "๐Ÿšง"
113
+ },
114
+ {
115
+ "value": "ci",
116
+ "label": "ci",
117
+ "hint": "Changes to our CI configuration files and scripts",
118
+ "emoji": "๐Ÿค–"
119
+ },
120
+ {
121
+ "value": "chore",
122
+ "label": "chore",
123
+ "hint": "Other changes that do not modify src or test files",
124
+ "emoji": "๐Ÿงน"
125
+ },
126
+ {
127
+ "value": "",
128
+ "label": "none"
129
+ }
130
+ ]
131
+ },
132
+ "commit_scope": {
133
+ "enable": true,
134
+ "custom_scope": false,
135
+ "initial_value": "app",
136
+ "options": [
137
+ {
138
+ "value": "app",
139
+ "label": "app"
140
+ },
141
+ {
142
+ "value": "shared",
143
+ "label": "shared"
144
+ },
145
+ {
146
+ "value": "server",
147
+ "label": "server"
148
+ },
149
+ {
150
+ "value": "tools",
151
+ "label": "tools"
152
+ },
153
+ {
154
+ "value": "",
155
+ "label": "none"
156
+ }
157
+ ]
158
+ },
159
+ "check_ticket": {
160
+ "infer_ticket": true,
161
+ "confirm_ticket": true,
162
+ "add_to_title": true,
163
+ "append_hashtag": false
164
+ },
165
+ "commit_title": {
166
+ "max_size": 70
167
+ },
168
+ "commit_body": {
169
+ "enable": true,
170
+ "required": false
171
+ },
172
+ "commit_footer": {
173
+ "enable": true,
174
+ "initial_value": [],
175
+ "options": [
176
+ "closes",
177
+ "breaking-change",
178
+ "deprecated",
179
+ "custom"
180
+ ]
181
+ },
182
+ "breaking_change": {
183
+ "add_exclamation_to_title": true
184
+ },
185
+ "confirm_commit": true,
186
+ "print_commit_output": true
176
187
  }
177
188
  ```
178
189
 
179
- #### โœ… Config Validation
180
- To simplify the CLI, some rules are enforced at runtime to make sure the program runs properly.
181
- - any property can be removed from the config, it will be replaced by the default at run-time
182
- - if a property is a string/number/boolean in the default, it must stay that type
183
- - the `initial_value` must be a valid value in the corresponding `options`
184
- - `commit_scope` and `commit_type` can be populated with as many or whatever options
185
- - must maintain the shape `{value: string, label?: string, hint?: string}`
186
- - `hint` and `label` are optional
187
- - to force scope or type to be required, remove `None`
188
- - `commit_footer` options are supplied from a fixed list, because they have specific functionality
189
- - thus, you can remove from that list, but you can't add custom values to it
190
+ ### โœ… Config Validation
191
+
192
+ Any property can be removed from the config, it will be replaced by the default at run-time
193
+ - See `.better-commits.json` in this repository as an example
194
+
195
+ #### Types
196
+ if a property is a string/number/boolean in the default, it must stay that type
197
+
198
+ #### Scope & Type
199
+ The `initial_value` must be a `value` in the corresponding `options`
200
+
201
+ `commit_scope` and `commit_type` can be populated with as many or whatever options you like
202
+ - `hint` and `label` are optional
203
+ - to make scope or type required, remove `None`
204
+
205
+ #### Footer
206
+ `commit_footer` options are supplied from a fixed list, because they have specific functionality
207
+ - You can remove from that list, but you can't add custom values to it
190
208
 
191
209
  TODO: Add table explaining properties
192
210
 
193
- #### ๐Ÿ”Ž Inference
211
+ ### ๐Ÿ”Ž Inference
194
212
 
195
213
  `better-commits` will attempt to infer the ticket/issue and the type from your branch name. It will auto populate the corresponding field if found.
196
214
 
@@ -208,23 +226,30 @@ TODO: Add table explaining properties
208
226
  - `-TYPE-` -- If a type is between two dashes
209
227
 
210
228
  ## ๐Ÿ˜ฎ Mildly Interesting
211
- - `better-commits` works with [Semantic Release](https://github.com/semantic-release/semantic-release)
212
- - if you use `better-commits` to create your *first* commit on a new branch, when you open a PR for that branch, it will properly **auto-populate the title and body**.
213
- - when you squash / merge with github, if `better-commits` is your *first* commit, all later commits like "addressing comments", "fixing mistake". Will be prefixed with an asterisk for easy deletion. This way you **maintain your pretty commit even when squashing**.
214
- - if you use a branch name like the ones below, better-commits will be able to infer your ticket/issue and type
215
- - `TYPE/TICKET-description`
216
- - `USER/TYPE/TICKET-description`
217
- - if you're using Github issues to track your work, and select the `closes` footer option when writing your commit. Github will **automatically link and close** that issue when your **pr is merged**
218
- - [better-commits](https://packagephobia.com/result?p=better-commits) is much smaller than its alternative [commitizen](https://packagephobia.com/result?p=commitizen)
219
- - `better-commits` uses native `git` commands under the hood. So any hooks, tools, or staging should work as if it was a normal commit.
220
- - You can add this badge to your repository to display that you're using a better-commits repository config
229
+
230
+ ### Building / Versioning
231
+ `better-commits` works with [Semantic Release](https://github.com/semantic-release/semantic-release)
232
+
233
+ ### Github
234
+ if you use `better-commits` to create your *first* commit on a new branch
235
+ - when you open a PR for that branch, it will properly **auto-populate the title and body**.
236
+ - when you squash/merge, all later commits like "addressing comments" or "fixing mistake". Will be prefixed with an asterisk for easy deletion. This way, you **maintain your pretty commit even when squashing**.
237
+
238
+ if you're using Github issues to track your work, and select the `closes` footer option when writing your commit. Github will **automatically link and close** that issue when your **pr is merged**
239
+
240
+ ### Fun Facts
241
+ [better-commits](https://packagephobia.com/result?p=better-commits) is much smaller than its alternative [commitizen](https://packagephobia.com/result?p=commitizen)
242
+
243
+ `better-commits` uses native `git` commands under the hood. So any hooks, tools, or staging should work as if it was a normal commit.
244
+
245
+ You can add this badge to your repository to display that you're using a better-commits repository config
221
246
  ```
222
247
  [![better commits is enabled](https://img.shields.io/badge/better--commits-enabled?style=for-the-badge&logo=git&color=a6e3a1&logoColor=D9E0EE&labelColor=302D41)](https://github.com/Everduin94/better-commits)
223
248
  ```
224
249
 
225
250
  [![better commits is enabled](https://img.shields.io/badge/better--commits-enabled?style=for-the-badge&logo=git&color=a6e3a1&logoColor=D9E0EE&labelColor=302D41)](https://github.com/Everduin94/better-commits)
226
251
 
227
-
252
+ ---
228
253
 
229
254
  ## โ“ Troubleshooting
230
255
 
package/src/index.ts CHANGED
@@ -2,13 +2,13 @@
2
2
 
3
3
  import * as p from '@clack/prompts';
4
4
  import color from 'picocolors';
5
- import { simpleGit } from "simple-git"
5
+ import { SimpleGit, simpleGit } from "simple-git"
6
6
  import fs from 'fs'
7
7
  import { execSync } from 'child_process';
8
8
  import { z } from "zod";
9
9
  import { fromZodError } from 'zod-validation-error';
10
10
  import { CommitState, Config } from './zod-state';
11
- import { CONFIG_FILE_NAME, get_default_config_path, check_missing_stage, addNewLine, SPACE_TO_SELECT, REGEX_SLASH_TAG, REGEX_SLASH_NUM, REGEX_START_TAG, REGEX_START_NUM, OPTIONAL_PROMPT, clean_commit_title, COMMIT_FOOTER_OPTIONS, infer_type_from_branch, Z_FOOTER_OPTIONS, CUSTOM_SCOPE_KEY } from './utils';
11
+ import { CONFIG_FILE_NAME, get_default_config_path, check_missing_stage, addNewLine, SPACE_TO_SELECT, REGEX_SLASH_TAG, REGEX_SLASH_NUM, REGEX_START_TAG, REGEX_START_NUM, OPTIONAL_PROMPT, clean_commit_title, COMMIT_FOOTER_OPTIONS, infer_type_from_branch, Z_FOOTER_OPTIONS, CUSTOM_SCOPE_KEY, get_git_root } from './utils';
12
12
 
13
13
  main(load_setup());
14
14
 
@@ -16,7 +16,7 @@ function load_setup(): z.infer<typeof Config> {
16
16
  console.clear();
17
17
  p.intro(`${color.bgCyan(color.black(' better-commits '))}`);
18
18
 
19
- const root = execSync('git rev-parse --show-toplevel').toString().trim();
19
+ const root = get_git_root();
20
20
  const root_path = `${root}/${CONFIG_FILE_NAME}`
21
21
  if (fs.existsSync(root_path)) {
22
22
  p.log.step('Found repository config')
@@ -31,7 +31,7 @@ function load_setup(): z.infer<typeof Config> {
31
31
 
32
32
  const default_config = Config.parse({})
33
33
  p.log.step('Config not found. Generating default .better-commit.json at $HOME')
34
- fs.writeFileSync(home_path, JSON.stringify(default_config, null, '\t'));
34
+ fs.writeFileSync(home_path, JSON.stringify(default_config, null, 4));
35
35
  return default_config;
36
36
  }
37
37
 
@@ -58,7 +58,8 @@ function validate_config(config: z.infer<typeof Config>): z.infer<typeof Config>
58
58
 
59
59
  async function main(config: z.infer<typeof Config>) {
60
60
  let commit_state = CommitState.parse({})
61
- let git_status = await simpleGit().status();
61
+ const simple_git = simpleGit({ baseDir: get_git_root() })
62
+ let git_status = await simple_git.status();
62
63
  if (config.check_status) {
63
64
  p.log.step(color.black(color.bgGreen(' Checking Git Status ')))
64
65
  const missing_files = check_missing_stage(git_status);
@@ -74,8 +75,8 @@ async function main(config: z.infer<typeof Config>) {
74
75
  }) as string[]
75
76
  if (p.isCancel(selected_for_staging)) process.exit(0)
76
77
 
77
- await simpleGit().add(selected_for_staging)
78
- git_status = await simpleGit().status();
78
+ await simple_git.add(selected_for_staging)
79
+ git_status = await simple_git.status();
79
80
  if (selected_for_staging?.length){
80
81
  p.log.success(color.green('Changes successfully staged'))
81
82
  }
@@ -94,6 +95,9 @@ async function main(config: z.infer<typeof Config>) {
94
95
  const type_from_branch = infer_type_from_branch(options)
95
96
  if (type_from_branch) initial_value = type_from_branch
96
97
  }
98
+ const value_to_emoji: Record<string,string> = config.commit_type.options.reduce(
99
+ (acc, curr) => ({ ...acc, [curr.value]: curr.emoji ?? '' }), {}
100
+ )
97
101
  const commit_type = await p.select(
98
102
  {
99
103
  message: `Select a commit type`,
@@ -102,7 +106,9 @@ async function main(config: z.infer<typeof Config>) {
102
106
  }
103
107
  )
104
108
  if (p.isCancel(commit_type)) process.exit(0)
105
- commit_state.type = commit_type;
109
+ commit_state.type = config.commit_type.append_emoji_to_commit ?
110
+ `${value_to_emoji[commit_type]} ${commit_type}`.trim()
111
+ : commit_type;
106
112
  }
107
113
 
108
114
  if (config.commit_scope.enable) {
package/src/init.ts CHANGED
@@ -3,17 +3,16 @@
3
3
  import { Config } from "./zod-state";
4
4
  import color from 'picocolors';
5
5
  import fs from 'fs'
6
- import { execSync } from "child_process";
7
6
  import * as p from '@clack/prompts';
8
- import { CONFIG_FILE_NAME } from "./utils";
7
+ import { CONFIG_FILE_NAME, get_git_root } from "./utils";
9
8
 
10
9
  try {
11
10
  console.clear();
12
11
  p.intro(`${color.bgCyan(color.black(' better-commits-init '))}`)
13
- const root = execSync('git rev-parse --show-toplevel').toString().trim();
12
+ const root = get_git_root();
14
13
  const root_path = `${root}/${CONFIG_FILE_NAME}`
15
14
  const default_config = Config.parse({})
16
- fs.writeFileSync(root_path, JSON.stringify(default_config, null, '\t'));
15
+ fs.writeFileSync(root_path, JSON.stringify(default_config, null, 4));
17
16
  p.log.success(`${color.green('Successfully created .better-commits.json')}`)
18
17
  p.outro(`Run ${color.bgBlack(color.white('better-commits'))} to start the CLI`)
19
18
  } catch (err: any) {
package/src/utils.ts CHANGED
@@ -12,15 +12,15 @@ export const REGEX_START_TAG = new RegExp(/^(\w+-\d+)/)
12
12
  export const REGEX_SLASH_NUM = new RegExp(/\/(\d+)/)
13
13
  export const REGEX_START_NUM = new RegExp(/^(\d+)/)
14
14
  export const DEFAULT_TYPE_OPTIONS = [
15
- { value: 'feat', label: 'feat' , hint: 'A new feature'},
16
- { value: 'fix', label: 'fix' , hint: 'A bug fix'},
17
- { value: 'docs', label: 'docs', hint: 'Documentation only changes'},
18
- { value: 'refactor', label: 'refactor', hint: 'A code change that neither fixes a bug nor adds a feature'},
19
- { value: 'perf', label: 'perf', hint: 'A code change that improves performance'},
20
- { value: 'test', label: 'test', hint: 'Adding missing tests or correcting existing tests'},
21
- { value: 'build', label: 'build', hint: 'Changes that affect the build system or external dependencies'},
22
- { value: 'ci', label: 'ci', hint: 'Changes to our CI configuration files and scripts'},
23
- { value: 'chore', label: 'chore', hint: 'Other changes that do not modify src or test files'},
15
+ { value: 'feat', label: 'feat' , hint: 'A new feature', emoji: 'โœจ'},
16
+ { value: 'fix', label: 'fix' , hint: 'A bug fix', emoji: '๐Ÿ›'},
17
+ { value: 'docs', label: 'docs', hint: 'Documentation only changes', emoji: '๐Ÿ“š'},
18
+ { value: 'refactor', label: 'refactor', hint: 'A code change that neither fixes a bug nor adds a feature', emoji: '๐Ÿ”จ'},
19
+ { value: 'perf', label: 'perf', hint: 'A code change that improves performance', emoji: '๐Ÿš€'},
20
+ { value: 'test', label: 'test', hint: 'Adding missing tests or correcting existing tests', emoji: '๐Ÿšจ'},
21
+ { value: 'build', label: 'build', hint: 'Changes that affect the build system or external dependencies', emoji: '๐Ÿšง'},
22
+ { value: 'ci', label: 'ci', hint: 'Changes to our CI configuration files and scripts', emoji: '๐Ÿค–'},
23
+ { value: 'chore', label: 'chore', hint: 'Other changes that do not modify src or test files', emoji: '๐Ÿงน'},
24
24
  { value: '', label: 'none'},
25
25
  ]
26
26
  export const DEFAULT_SCOPE_OPTIONS = [
@@ -60,6 +60,10 @@ export function infer_type_from_branch(types: string[]): string {
60
60
  return found ?? ''
61
61
  }
62
62
 
63
+ export function get_git_root(): string {
64
+ return execSync('git rev-parse --show-toplevel').toString().trim();
65
+ }
66
+
63
67
  export function get_default_config_path(): string {
64
68
  return homedir()+'/'+CONFIG_FILE_NAME
65
69
  }
package/src/zod-state.ts CHANGED
@@ -1,22 +1,30 @@
1
1
  import { z } from "zod"
2
2
  import { CUSTOM_SCOPE_KEY, DEFAULT_SCOPE_OPTIONS, DEFAULT_TYPE_OPTIONS, FOOTER_OPTION_VALUES, Z_FOOTER_OPTIONS } from "./utils"
3
3
 
4
- // TODO: add "Ref", "Fixes", ability to change phrase "Closes/closes/closes:"
4
+ // test semantic release
5
5
  export const Config = z.object({
6
6
  check_status: z.boolean().default(true),
7
7
  commit_type: z.object({
8
8
  enable: z.boolean().default(true),
9
9
  initial_value: z.string().default('feat'),
10
10
  infer_type_from_branch: z.boolean().default(true),
11
+ append_emoji_to_label: z.boolean().default(false),
12
+ append_emoji_to_commit: z.boolean().default(false),
11
13
  options: z.array(z.object({
12
14
  value: z.string(),
13
15
  label: z.string().optional(),
14
16
  hint: z.string().optional(),
17
+ emoji: z.string().emoji().optional(),
15
18
  })).default(DEFAULT_TYPE_OPTIONS)
16
- }).default({}).refine(val => {
17
- const options = val.options.map(v => v.value)
18
- return options.includes(val.initial_value)
19
- }, (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })),
19
+ }).default({}).transform(val => {
20
+ const options = val.options.map(v => ({
21
+ ...v,
22
+ label: v.emoji && val.append_emoji_to_label ? `${v.emoji} ${v.label}` : v.label,
23
+ }))
24
+ return { ...val, options }
25
+ })
26
+ .refine(val => val.options.map(v => v.value).includes(val.initial_value)
27
+ , (val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })),
20
28
  commit_scope: z.object({
21
29
  enable: z.boolean().default(true),
22
30
  custom_scope: z.boolean().default(false),
@@ -63,7 +71,7 @@ export const Config = z.object({
63
71
  add_exclamation_to_title: z.boolean().default(true)
64
72
  }).default({}),
65
73
  confirm_commit: z.boolean().default(true),
66
- print_commit_output: z.boolean().default(true)
74
+ print_commit_output: z.boolean().default(true),
67
75
  }).default({})
68
76
 
69
77
  export const CommitState = z.object({