better-commits 1.2.0 → 1.3.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.
- package/.better-commits.json +8 -0
- package/.github/workflows/publish.yml +0 -2
- package/dist/branch.js +331 -0
- package/dist/index.js +116 -78
- package/dist/init.js +31 -6
- package/dist/utils.js +160 -4
- package/dist/zod-state.js +28 -1
- package/package.json +5 -1
- package/readme.md +40 -1
- package/src/branch.ts +126 -0
- package/src/index.ts +3 -49
- package/src/init.ts +1 -1
- package/src/utils.ts +51 -0
- package/src/zod-state.ts +25 -1
package/.better-commits.json
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
{
|
|
2
|
+
"commit_type": {
|
|
3
|
+
"append_emoji_to_label": true,
|
|
4
|
+
"append_emoji_to_commit": true
|
|
5
|
+
},
|
|
2
6
|
"commit_scope": {
|
|
3
7
|
"enable": true,
|
|
4
8
|
"initial_value": "main",
|
|
@@ -7,6 +11,10 @@
|
|
|
7
11
|
"value": "main",
|
|
8
12
|
"label": "main"
|
|
9
13
|
},
|
|
14
|
+
{
|
|
15
|
+
"value": "branch",
|
|
16
|
+
"label": "branch"
|
|
17
|
+
},
|
|
10
18
|
{
|
|
11
19
|
"value": "init",
|
|
12
20
|
"label": "init"
|
package/dist/branch.js
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
#! /usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
25
|
+
|
|
26
|
+
// src/zod-state.ts
|
|
27
|
+
var import_zod2 = require("zod");
|
|
28
|
+
|
|
29
|
+
// src/utils.ts
|
|
30
|
+
var import_os = require("os");
|
|
31
|
+
var import_zod = require("zod");
|
|
32
|
+
var import_picocolors = __toESM(require("picocolors"));
|
|
33
|
+
var import_child_process = require("child_process");
|
|
34
|
+
var p = __toESM(require("@clack/prompts"));
|
|
35
|
+
var import_fs = __toESM(require("fs"));
|
|
36
|
+
var import_zod_validation_error = require("zod-validation-error");
|
|
37
|
+
var CONFIG_FILE_NAME = ".better-commits.json";
|
|
38
|
+
var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
|
|
39
|
+
var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
|
|
40
|
+
var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
|
|
41
|
+
var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
|
|
42
|
+
var REGEX_START_TAG = new RegExp(/^(\w+-\d+)/);
|
|
43
|
+
var REGEX_SLASH_NUM = new RegExp(/\/(\d+)/);
|
|
44
|
+
var REGEX_START_NUM = new RegExp(/^(\d+)/);
|
|
45
|
+
var DEFAULT_TYPE_OPTIONS = [
|
|
46
|
+
{ value: "feat", label: "feat", hint: "A new feature", emoji: "\u2728" },
|
|
47
|
+
{ value: "fix", label: "fix", hint: "A bug fix", emoji: "\u{1F41B}" },
|
|
48
|
+
{ value: "docs", label: "docs", hint: "Documentation only changes", emoji: "\u{1F4DA}" },
|
|
49
|
+
{ value: "refactor", label: "refactor", hint: "A code change that neither fixes a bug nor adds a feature", emoji: "\u{1F528}" },
|
|
50
|
+
{ value: "perf", label: "perf", hint: "A code change that improves performance", emoji: "\u{1F680}" },
|
|
51
|
+
{ value: "test", label: "test", hint: "Adding missing tests or correcting existing tests", emoji: "\u{1F6A8}" },
|
|
52
|
+
{ value: "build", label: "build", hint: "Changes that affect the build system or external dependencies", emoji: "\u{1F6A7}" },
|
|
53
|
+
{ value: "ci", label: "ci", hint: "Changes to our CI configuration files and scripts", emoji: "\u{1F916}" },
|
|
54
|
+
{ value: "chore", label: "chore", hint: "Other changes that do not modify src or test files", emoji: "\u{1F9F9}" },
|
|
55
|
+
{ value: "", label: "none" }
|
|
56
|
+
];
|
|
57
|
+
var DEFAULT_SCOPE_OPTIONS = [
|
|
58
|
+
{ value: "app", label: "app" },
|
|
59
|
+
{ value: "shared", label: "shared" },
|
|
60
|
+
{ value: "server", label: "server" },
|
|
61
|
+
{ value: "tools", label: "tools" },
|
|
62
|
+
{ value: "", label: "none" }
|
|
63
|
+
];
|
|
64
|
+
var CUSTOM_SCOPE_KEY = "custom";
|
|
65
|
+
var Z_FOOTER_OPTIONS = import_zod.z.enum(["closes", "breaking-change", "deprecated", "custom"]);
|
|
66
|
+
var FOOTER_OPTION_VALUES = ["closes", "breaking-change", "deprecated", "custom"];
|
|
67
|
+
function load_setup(cli_name = " better-commits ") {
|
|
68
|
+
console.clear();
|
|
69
|
+
p.intro(`${import_picocolors.default.bgCyan(import_picocolors.default.black(cli_name))}`);
|
|
70
|
+
const root = get_git_root();
|
|
71
|
+
const root_path = `${root}/${CONFIG_FILE_NAME}`;
|
|
72
|
+
if (import_fs.default.existsSync(root_path)) {
|
|
73
|
+
p.log.step("Found repository config");
|
|
74
|
+
return read_config_from_path(root_path);
|
|
75
|
+
}
|
|
76
|
+
const home_path = get_default_config_path();
|
|
77
|
+
if (import_fs.default.existsSync(home_path)) {
|
|
78
|
+
p.log.step("Found global config");
|
|
79
|
+
return read_config_from_path(home_path);
|
|
80
|
+
}
|
|
81
|
+
const default_config = Config.parse({});
|
|
82
|
+
p.log.step("Config not found. Generating default .better-commit.json at $HOME");
|
|
83
|
+
import_fs.default.writeFileSync(home_path, JSON.stringify(default_config, null, 4));
|
|
84
|
+
return default_config;
|
|
85
|
+
}
|
|
86
|
+
function read_config_from_path(config_path) {
|
|
87
|
+
let res = null;
|
|
88
|
+
try {
|
|
89
|
+
res = JSON.parse(import_fs.default.readFileSync(config_path, "utf8"));
|
|
90
|
+
} catch (err) {
|
|
91
|
+
p.log.error("Invalid JSON file. Exiting.\n" + err);
|
|
92
|
+
process.exit(0);
|
|
93
|
+
}
|
|
94
|
+
return validate_config(res);
|
|
95
|
+
}
|
|
96
|
+
function validate_config(config) {
|
|
97
|
+
try {
|
|
98
|
+
return Config.parse(config);
|
|
99
|
+
} catch (err) {
|
|
100
|
+
console.log((0, import_zod_validation_error.fromZodError)(err).message);
|
|
101
|
+
process.exit(0);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function get_git_root() {
|
|
105
|
+
return (0, import_child_process.execSync)("git rev-parse --show-toplevel").toString().trim();
|
|
106
|
+
}
|
|
107
|
+
function get_default_config_path() {
|
|
108
|
+
return (0, import_os.homedir)() + "/" + CONFIG_FILE_NAME;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// src/zod-state.ts
|
|
112
|
+
var Config = import_zod2.z.object({
|
|
113
|
+
check_status: import_zod2.z.boolean().default(true),
|
|
114
|
+
commit_type: import_zod2.z.object({
|
|
115
|
+
enable: import_zod2.z.boolean().default(true),
|
|
116
|
+
initial_value: import_zod2.z.string().default("feat"),
|
|
117
|
+
infer_type_from_branch: import_zod2.z.boolean().default(true),
|
|
118
|
+
append_emoji_to_label: import_zod2.z.boolean().default(false),
|
|
119
|
+
append_emoji_to_commit: import_zod2.z.boolean().default(false),
|
|
120
|
+
options: import_zod2.z.array(import_zod2.z.object({
|
|
121
|
+
value: import_zod2.z.string(),
|
|
122
|
+
label: import_zod2.z.string().optional(),
|
|
123
|
+
hint: import_zod2.z.string().optional(),
|
|
124
|
+
emoji: import_zod2.z.string().emoji().optional()
|
|
125
|
+
})).default(DEFAULT_TYPE_OPTIONS)
|
|
126
|
+
}).default({}).transform((val) => {
|
|
127
|
+
const options = val.options.map((v) => ({
|
|
128
|
+
...v,
|
|
129
|
+
label: v.emoji && val.append_emoji_to_label ? `${v.emoji} ${v.label}` : v.label
|
|
130
|
+
}));
|
|
131
|
+
return { ...val, options };
|
|
132
|
+
}).refine(
|
|
133
|
+
(val) => val.options.map((v) => v.value).includes(val.initial_value),
|
|
134
|
+
(val) => ({ message: `Type: initial_value "${val.initial_value}" must exist in options` })
|
|
135
|
+
),
|
|
136
|
+
commit_scope: import_zod2.z.object({
|
|
137
|
+
enable: import_zod2.z.boolean().default(true),
|
|
138
|
+
custom_scope: import_zod2.z.boolean().default(false),
|
|
139
|
+
initial_value: import_zod2.z.string().default("app"),
|
|
140
|
+
options: import_zod2.z.array(import_zod2.z.object({
|
|
141
|
+
value: import_zod2.z.string(),
|
|
142
|
+
label: import_zod2.z.string().optional(),
|
|
143
|
+
hint: import_zod2.z.string().optional()
|
|
144
|
+
})).default(DEFAULT_SCOPE_OPTIONS)
|
|
145
|
+
}).default({}).transform((val) => {
|
|
146
|
+
const options = val.options.map((v) => v.value);
|
|
147
|
+
if (val.custom_scope && !options.includes(CUSTOM_SCOPE_KEY)) {
|
|
148
|
+
return {
|
|
149
|
+
...val,
|
|
150
|
+
options: [...val.options, { label: CUSTOM_SCOPE_KEY, value: CUSTOM_SCOPE_KEY, hint: "Write a custom scope" }]
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
return val;
|
|
154
|
+
}).refine((val) => {
|
|
155
|
+
const options = val.options.map((v) => v.value);
|
|
156
|
+
return options.includes(val.initial_value);
|
|
157
|
+
}, (val) => ({ message: `Scope: initial_value "${val.initial_value}" must exist in options` })),
|
|
158
|
+
check_ticket: import_zod2.z.object({
|
|
159
|
+
infer_ticket: import_zod2.z.boolean().default(true),
|
|
160
|
+
confirm_ticket: import_zod2.z.boolean().default(true),
|
|
161
|
+
add_to_title: import_zod2.z.boolean().default(true),
|
|
162
|
+
append_hashtag: import_zod2.z.boolean().default(false)
|
|
163
|
+
}).default({}),
|
|
164
|
+
commit_title: import_zod2.z.object({
|
|
165
|
+
max_size: import_zod2.z.number().positive().default(70)
|
|
166
|
+
}).default({}),
|
|
167
|
+
commit_body: import_zod2.z.object({
|
|
168
|
+
enable: import_zod2.z.boolean().default(true),
|
|
169
|
+
required: import_zod2.z.boolean().default(false)
|
|
170
|
+
}).default({}),
|
|
171
|
+
commit_footer: import_zod2.z.object({
|
|
172
|
+
enable: import_zod2.z.boolean().default(true),
|
|
173
|
+
initial_value: import_zod2.z.array(Z_FOOTER_OPTIONS).default([]),
|
|
174
|
+
options: import_zod2.z.array(Z_FOOTER_OPTIONS).default(FOOTER_OPTION_VALUES)
|
|
175
|
+
}).default({}),
|
|
176
|
+
breaking_change: import_zod2.z.object({
|
|
177
|
+
add_exclamation_to_title: import_zod2.z.boolean().default(true)
|
|
178
|
+
}).default({}),
|
|
179
|
+
confirm_commit: import_zod2.z.boolean().default(true),
|
|
180
|
+
print_commit_output: import_zod2.z.boolean().default(true),
|
|
181
|
+
branch_pre_commands: import_zod2.z.array(import_zod2.z.string()).default([]),
|
|
182
|
+
branch_post_commands: import_zod2.z.array(import_zod2.z.string()).default([]),
|
|
183
|
+
branch_user: import_zod2.z.object({
|
|
184
|
+
required: import_zod2.z.boolean().default(false),
|
|
185
|
+
separator: import_zod2.z.enum(["/", "-", "_"]).default("/")
|
|
186
|
+
}).default({}),
|
|
187
|
+
branch_type: import_zod2.z.object({
|
|
188
|
+
separator: import_zod2.z.enum(["/", "-", "_"]).default("/")
|
|
189
|
+
}).default({}),
|
|
190
|
+
branch_ticket: import_zod2.z.object({
|
|
191
|
+
required: import_zod2.z.boolean().default(false),
|
|
192
|
+
separator: import_zod2.z.enum(["/", "-", "_"]).default("-")
|
|
193
|
+
}).default({}),
|
|
194
|
+
branch_description: import_zod2.z.object({
|
|
195
|
+
max_length: import_zod2.z.number().positive().default(70)
|
|
196
|
+
}).default({})
|
|
197
|
+
}).default({});
|
|
198
|
+
var CommitState = import_zod2.z.object({
|
|
199
|
+
type: import_zod2.z.string().default(""),
|
|
200
|
+
scope: import_zod2.z.string().default(""),
|
|
201
|
+
title: import_zod2.z.string().default(""),
|
|
202
|
+
body: import_zod2.z.string().default(""),
|
|
203
|
+
closes: import_zod2.z.string().default(""),
|
|
204
|
+
ticket: import_zod2.z.string().default(""),
|
|
205
|
+
breaking_title: import_zod2.z.string().default(""),
|
|
206
|
+
breaking_body: import_zod2.z.string().default(""),
|
|
207
|
+
deprecates: import_zod2.z.string().default(""),
|
|
208
|
+
deprecates_title: import_zod2.z.string().default(""),
|
|
209
|
+
deprecates_body: import_zod2.z.string().default(""),
|
|
210
|
+
custom_footer: import_zod2.z.string().default("")
|
|
211
|
+
}).default({});
|
|
212
|
+
var BranchState = import_zod2.z.object({
|
|
213
|
+
user: import_zod2.z.string().default(""),
|
|
214
|
+
type: import_zod2.z.string().default(""),
|
|
215
|
+
ticket: import_zod2.z.string().default(""),
|
|
216
|
+
description: import_zod2.z.string().default("")
|
|
217
|
+
}).default({});
|
|
218
|
+
|
|
219
|
+
// src/branch.ts
|
|
220
|
+
var import_simple_git = __toESM(require("simple-git"));
|
|
221
|
+
var p2 = __toESM(require("@clack/prompts"));
|
|
222
|
+
var import_configstore = __toESM(require("configstore"));
|
|
223
|
+
var import_child_process2 = require("child_process");
|
|
224
|
+
main(load_setup(" better-branch "));
|
|
225
|
+
async function main(config) {
|
|
226
|
+
const branch_state = BranchState.parse({});
|
|
227
|
+
const cache_user_name = get_user_from_cache();
|
|
228
|
+
const user_name_required = config.branch_user.required;
|
|
229
|
+
const user_name = await p2.text({
|
|
230
|
+
message: `Type your git username ${user_name_required ? "" : OPTIONAL_PROMPT} ${CACHE_PROMPT}`.trim(),
|
|
231
|
+
placeholder: "",
|
|
232
|
+
initialValue: cache_user_name,
|
|
233
|
+
validate: (val) => {
|
|
234
|
+
if (user_name_required && !val)
|
|
235
|
+
return "Please enter a username";
|
|
236
|
+
}
|
|
237
|
+
});
|
|
238
|
+
if (p2.isCancel(user_name))
|
|
239
|
+
process.exit(0);
|
|
240
|
+
branch_state.user = user_name?.replace(/\s+/g, "-")?.toLowerCase() ?? "";
|
|
241
|
+
set_user_cache(branch_state.user);
|
|
242
|
+
if (config.commit_type.enable) {
|
|
243
|
+
let initial_value = config.commit_type.initial_value;
|
|
244
|
+
const commit_type = await p2.select(
|
|
245
|
+
{
|
|
246
|
+
message: `Select a commit type`,
|
|
247
|
+
initialValue: initial_value,
|
|
248
|
+
options: config.commit_type.options
|
|
249
|
+
}
|
|
250
|
+
);
|
|
251
|
+
if (p2.isCancel(commit_type))
|
|
252
|
+
process.exit(0);
|
|
253
|
+
branch_state.type = commit_type;
|
|
254
|
+
}
|
|
255
|
+
const ticked_required = config.branch_ticket.required;
|
|
256
|
+
const ticket = await p2.text({
|
|
257
|
+
message: `Type ticket / issue number ${ticked_required ? "" : OPTIONAL_PROMPT}`.trim(),
|
|
258
|
+
placeholder: "",
|
|
259
|
+
validate: (val) => {
|
|
260
|
+
if (ticked_required && !val)
|
|
261
|
+
return "Please enter a ticket / issue";
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
if (p2.isCancel(ticket))
|
|
265
|
+
process.exit(0);
|
|
266
|
+
branch_state.ticket = ticket;
|
|
267
|
+
const description_max_length = config.branch_description.max_length;
|
|
268
|
+
const description = await p2.text({
|
|
269
|
+
message: "Type a short description",
|
|
270
|
+
placeholder: "",
|
|
271
|
+
validate: (value) => {
|
|
272
|
+
if (!value)
|
|
273
|
+
return "Please enter a description";
|
|
274
|
+
if (value.length > description_max_length)
|
|
275
|
+
return `Exceeded max length. Description max [${description_max_length}]`;
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
if (p2.isCancel(description))
|
|
279
|
+
process.exit(0);
|
|
280
|
+
branch_state.description = description?.replace(/\s+/g, "-")?.toLowerCase() ?? "";
|
|
281
|
+
config.branch_pre_commands.forEach((command) => {
|
|
282
|
+
try {
|
|
283
|
+
const output = (0, import_child_process2.execSync)(command).toString().trim();
|
|
284
|
+
p2.log.info(output);
|
|
285
|
+
} catch (err) {
|
|
286
|
+
p2.log.error("Something went wrong when executing pre-commands: " + err);
|
|
287
|
+
process.exit(0);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
const branch_name = build_branch(branch_state, config);
|
|
291
|
+
const simple_git = (0, import_simple_git.default)({ baseDir: get_git_root() });
|
|
292
|
+
await simple_git.checkoutLocalBranch(branch_name);
|
|
293
|
+
p2.log.info(`Switched to a new branch '${branch_name}'`);
|
|
294
|
+
config.branch_post_commands.forEach((command) => {
|
|
295
|
+
try {
|
|
296
|
+
const output = (0, import_child_process2.execSync)(command).toString().trim();
|
|
297
|
+
p2.log.info(output);
|
|
298
|
+
} catch (err) {
|
|
299
|
+
p2.log.error("Something went wrong when executing post-commands: " + err);
|
|
300
|
+
process.exit(0);
|
|
301
|
+
}
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
function build_branch(branch, config) {
|
|
305
|
+
let res = "";
|
|
306
|
+
if (branch.user)
|
|
307
|
+
res += branch.user + config.branch_user.separator;
|
|
308
|
+
if (branch.type)
|
|
309
|
+
res += branch.type + config.branch_type.separator;
|
|
310
|
+
if (branch.ticket)
|
|
311
|
+
res += branch.ticket + config.branch_ticket.separator;
|
|
312
|
+
if (branch.description)
|
|
313
|
+
res += branch.description;
|
|
314
|
+
return res;
|
|
315
|
+
}
|
|
316
|
+
function get_user_from_cache() {
|
|
317
|
+
try {
|
|
318
|
+
const config_store = new import_configstore.default("better-commits");
|
|
319
|
+
return config_store.get("username") ?? "";
|
|
320
|
+
} catch (err) {
|
|
321
|
+
p2.log.warn('There was an issue accessing username from cache. Check that the folder "~/.config" exists');
|
|
322
|
+
}
|
|
323
|
+
return "";
|
|
324
|
+
}
|
|
325
|
+
function set_user_cache(val) {
|
|
326
|
+
try {
|
|
327
|
+
const config_store = new import_configstore.default("better-commits");
|
|
328
|
+
config_store.set("username", val);
|
|
329
|
+
} catch (err) {
|
|
330
|
+
}
|
|
331
|
+
}
|