better-commits 1.7.2 → 1.8.1

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/dist/branch.js CHANGED
@@ -36,6 +36,9 @@ var import_fs = __toESM(require("fs"));
36
36
  var import_zod_validation_error = require("zod-validation-error");
37
37
  var CONFIG_FILE_NAME = ".better-commits.json";
38
38
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
39
+ var A_FOR_ALL = `${import_picocolors.default.dim(
40
+ "(<space> to select, <a> to select all)"
41
+ )}`;
39
42
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
40
43
  var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
41
44
  var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
package/dist/git.js ADDED
@@ -0,0 +1,89 @@
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 __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, { get: all[name], enumerable: true });
12
+ };
13
+ var __copyProps = (to, from, except, desc) => {
14
+ if (from && typeof from === "object" || typeof from === "function") {
15
+ for (let key of __getOwnPropNames(from))
16
+ if (!__hasOwnProp.call(to, key) && key !== except)
17
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
18
+ }
19
+ return to;
20
+ };
21
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
22
+ // If the importer is in node compatibility mode or this is not an ESM
23
+ // file that has been converted to a CommonJS file using a Babel-
24
+ // compatible transform (i.e. "__esModule" has not been set), then set
25
+ // "default" to the CommonJS "module.exports" for node compatibility.
26
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
27
+ mod
28
+ ));
29
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
30
+
31
+ // src/git.ts
32
+ var git_exports = {};
33
+ __export(git_exports, {
34
+ git_add: () => git_add,
35
+ git_status: () => git_status
36
+ });
37
+ module.exports = __toCommonJS(git_exports);
38
+ var import_child_process = require("child_process");
39
+ var p = __toESM(require("@clack/prompts"));
40
+ var import_picocolors = __toESM(require("picocolors"));
41
+ var porcelain_states = ["M", "T", "R", "D", "A", "C"];
42
+ function git_status() {
43
+ let status = "";
44
+ try {
45
+ status = (0, import_child_process.execSync)("git status --porcelain", { stdio: "pipe" }).toString();
46
+ } catch (err) {
47
+ p.log.error(import_picocolors.default.red("Failed to git status"));
48
+ return { index: [], work_tree: [] };
49
+ }
50
+ const lines = status.split("\n");
51
+ const work_tree = [];
52
+ const index = [];
53
+ lines.forEach((v) => {
54
+ const line = v.trimEnd();
55
+ if (!line)
56
+ return;
57
+ const path_plus_file = line.substring(2).trim();
58
+ const first_char = line.charAt(0).trim();
59
+ const second_char = line.charAt(1).trim();
60
+ if (first_char === "?" || second_char === "?") {
61
+ work_tree.push(path_plus_file);
62
+ }
63
+ if (porcelain_states.includes(first_char)) {
64
+ index.push(path_plus_file);
65
+ }
66
+ if (porcelain_states.includes(second_char)) {
67
+ work_tree.push(path_plus_file);
68
+ }
69
+ });
70
+ return { index, work_tree };
71
+ }
72
+ function git_add(files) {
73
+ const space_delimited_files = files.join(" ");
74
+ if (space_delimited_files) {
75
+ try {
76
+ (0, import_child_process.execSync)(`git add ${space_delimited_files}`, {
77
+ stdio: "pipe"
78
+ }).toString();
79
+ p.log.success(import_picocolors.default.green("Changes successfully staged"));
80
+ } catch (err) {
81
+ p.log.error(import_picocolors.default.red("Failed to stage changes"));
82
+ }
83
+ }
84
+ }
85
+ // Annotate the CommonJS export names for ESM import in node:
86
+ 0 && (module.exports = {
87
+ git_add,
88
+ git_status
89
+ });
package/dist/index.js CHANGED
@@ -34,10 +34,10 @@ __export(src_exports, {
34
34
  main: () => main
35
35
  });
36
36
  module.exports = __toCommonJS(src_exports);
37
- var p2 = __toESM(require("@clack/prompts"));
38
- var import_picocolors2 = __toESM(require("picocolors"));
39
- var import_simple_git = require("simple-git");
40
- var import_child_process2 = require("child_process");
37
+ var p3 = __toESM(require("@clack/prompts"));
38
+ var import_picocolors3 = __toESM(require("picocolors"));
39
+ var import_child_process3 = require("child_process");
40
+ var import_process = require("process");
41
41
 
42
42
  // src/zod-state.ts
43
43
  var import_zod2 = require("zod");
@@ -52,6 +52,9 @@ var import_fs = __toESM(require("fs"));
52
52
  var import_zod_validation_error = require("zod-validation-error");
53
53
  var CONFIG_FILE_NAME = ".better-commits.json";
54
54
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
55
+ var A_FOR_ALL = `${import_picocolors.default.dim(
56
+ "(<space> to select, <a> to select all)"
57
+ )}`;
55
58
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
56
59
  var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
57
60
  var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
@@ -217,9 +220,6 @@ function get_git_root() {
217
220
  function get_default_config_path() {
218
221
  return (0, import_os.homedir)() + "/" + CONFIG_FILE_NAME;
219
222
  }
220
- function check_missing_stage(stats) {
221
- return stats.files.filter((f) => f.index.trim() === "" || f.index === "?").map((f) => f.path);
222
- }
223
223
  function addNewLine(arr, i) {
224
224
  return i === arr.length - 1 ? "" : "\n";
225
225
  }
@@ -367,37 +367,82 @@ var BranchState = import_zod2.z.object({
367
367
  description: import_zod2.z.string().default("")
368
368
  }).default({});
369
369
 
370
+ // src/git.ts
371
+ var import_child_process2 = require("child_process");
372
+ var p2 = __toESM(require("@clack/prompts"));
373
+ var import_picocolors2 = __toESM(require("picocolors"));
374
+ var porcelain_states = ["M", "T", "R", "D", "A", "C"];
375
+ function git_status() {
376
+ let status = "";
377
+ try {
378
+ status = (0, import_child_process2.execSync)("git status --porcelain", { stdio: "pipe" }).toString();
379
+ } catch (err) {
380
+ p2.log.error(import_picocolors2.default.red("Failed to git status"));
381
+ return { index: [], work_tree: [] };
382
+ }
383
+ const lines = status.split("\n");
384
+ const work_tree = [];
385
+ const index = [];
386
+ lines.forEach((v) => {
387
+ const line = v.trimEnd();
388
+ if (!line)
389
+ return;
390
+ const path_plus_file = line.substring(2).trim();
391
+ const first_char = line.charAt(0).trim();
392
+ const second_char = line.charAt(1).trim();
393
+ if (first_char === "?" || second_char === "?") {
394
+ work_tree.push(path_plus_file);
395
+ }
396
+ if (porcelain_states.includes(first_char)) {
397
+ index.push(path_plus_file);
398
+ }
399
+ if (porcelain_states.includes(second_char)) {
400
+ work_tree.push(path_plus_file);
401
+ }
402
+ });
403
+ return { index, work_tree };
404
+ }
405
+ function git_add(files) {
406
+ const space_delimited_files = files.join(" ");
407
+ if (space_delimited_files) {
408
+ try {
409
+ (0, import_child_process2.execSync)(`git add ${space_delimited_files}`, {
410
+ stdio: "pipe"
411
+ }).toString();
412
+ p2.log.success(import_picocolors2.default.green("Changes successfully staged"));
413
+ } catch (err) {
414
+ p2.log.error(import_picocolors2.default.red("Failed to stage changes"));
415
+ }
416
+ }
417
+ }
418
+
370
419
  // src/index.ts
371
420
  main(load_setup());
372
421
  async function main(config) {
373
422
  let commit_state = CommitState.parse({});
374
- const simple_git = (0, import_simple_git.simpleGit)({ baseDir: get_git_root() });
375
- let git_status = await simple_git.status();
423
+ (0, import_process.chdir)(get_git_root());
376
424
  if (config.check_status) {
377
- p2.log.step(import_picocolors2.default.black(import_picocolors2.default.bgGreen(" Checking Git Status ")));
378
- const missing_files = check_missing_stage(git_status);
379
- const staged_files = git_status.staged.reduce((acc, curr, i) => import_picocolors2.default.green(acc + curr + addNewLine(git_status.staged, i)), "");
380
- p2.log.success("Changes to be committed:\n" + staged_files);
381
- if (missing_files.length) {
382
- const unstaged_files = missing_files.reduce((acc, curr, i) => import_picocolors2.default.red(acc + curr + addNewLine(missing_files, i)), "");
383
- p2.log.error("Changes not staged for commit:\n" + unstaged_files);
384
- const selected_for_staging = await p2.multiselect({
425
+ let { index, work_tree } = git_status();
426
+ p3.log.step(import_picocolors3.default.black(import_picocolors3.default.bgGreen(" Checking Git Status ")));
427
+ const staged_files = index.reduce((acc, curr, i) => import_picocolors3.default.green(acc + curr + addNewLine(index, i)), "");
428
+ p3.log.success("Changes to be committed:\n" + staged_files);
429
+ if (work_tree.length) {
430
+ const unstaged_files = work_tree.reduce((acc, curr, i) => import_picocolors3.default.red(acc + curr + addNewLine(work_tree, i)), "");
431
+ p3.log.error("Changes not staged for commit:\n" + unstaged_files);
432
+ const selected_for_staging = await p3.multiselect({
385
433
  message: `Some files have not been staged, would you like to add them now? ${SPACE_TO_SELECT}`,
386
- options: [{ value: ".", label: "." }, ...missing_files.map((v) => ({ value: v, label: v }))],
434
+ options: [{ value: ".", label: "." }, ...work_tree.map((v) => ({ value: v, label: v }))],
387
435
  required: false
388
436
  });
389
- if (p2.isCancel(selected_for_staging))
437
+ if (p3.isCancel(selected_for_staging))
390
438
  process.exit(0);
391
- await simple_git.add(selected_for_staging);
392
- git_status = await simple_git.status();
393
- if (selected_for_staging?.length) {
394
- p2.log.success(import_picocolors2.default.green("Changes successfully staged"));
395
- }
439
+ git_add(selected_for_staging);
440
+ }
441
+ let updated_status = git_status();
442
+ if (!updated_status.index.length) {
443
+ p3.log.error(import_picocolors3.default.red('no changes added to commit (use "git add" and/or "git commit -a")'));
444
+ process.exit(0);
396
445
  }
397
- }
398
- if (!git_status.staged.length) {
399
- p2.log.error(import_picocolors2.default.red('no changes added to commit (use "git add" and/or "git commit -a")'));
400
- process.exit(0);
401
446
  }
402
447
  if (config.commit_type.enable) {
403
448
  let message = "Select a commit type";
@@ -406,7 +451,7 @@ async function main(config) {
406
451
  const options = config.commit_type.options.map((o) => o.value);
407
452
  const type_from_branch = infer_type_from_branch(options);
408
453
  if (type_from_branch) {
409
- message = `Commit type inferred from branch ${import_picocolors2.default.dim("(confirm / edit)")}`;
454
+ message = `Commit type inferred from branch ${import_picocolors3.default.dim("(confirm / edit)")}`;
410
455
  initial_value = type_from_branch;
411
456
  }
412
457
  }
@@ -414,38 +459,38 @@ async function main(config) {
414
459
  (acc, curr) => ({ ...acc, [curr.value]: curr.emoji ?? "" }),
415
460
  {}
416
461
  );
417
- const commit_type = await p2.select(
462
+ const commit_type = await p3.select(
418
463
  {
419
464
  message,
420
465
  initialValue: initial_value,
421
466
  options: config.commit_type.options
422
467
  }
423
468
  );
424
- if (p2.isCancel(commit_type))
469
+ if (p3.isCancel(commit_type))
425
470
  process.exit(0);
426
471
  commit_state.type = config.commit_type.append_emoji_to_commit ? `${value_to_emoji[commit_type]} ${commit_type}`.trim() : commit_type;
427
472
  }
428
473
  if (config.commit_scope.enable) {
429
- let commit_scope = await p2.select({
474
+ let commit_scope = await p3.select({
430
475
  message: "Select a commit scope",
431
476
  initialValue: config.commit_scope.initial_value,
432
477
  options: config.commit_scope.options
433
478
  });
434
- if (p2.isCancel(commit_scope))
479
+ if (p3.isCancel(commit_scope))
435
480
  process.exit(0);
436
481
  if (commit_scope === CUSTOM_SCOPE_KEY && config.commit_scope.custom_scope) {
437
- commit_scope = await p2.text({
482
+ commit_scope = await p3.text({
438
483
  message: "Write a custom scope",
439
484
  placeholder: ""
440
485
  });
441
- if (p2.isCancel(commit_scope))
486
+ if (p3.isCancel(commit_scope))
442
487
  process.exit(0);
443
488
  }
444
489
  commit_state.scope = commit_scope;
445
490
  }
446
491
  if (config.check_ticket.infer_ticket) {
447
492
  try {
448
- const branch = (0, import_child_process2.execSync)("git branch --show-current", { stdio: "pipe" }).toString();
493
+ const branch = (0, import_child_process3.execSync)("git branch --show-current", { stdio: "pipe" }).toString();
449
494
  const found = [
450
495
  branch.match(REGEX_START_UND),
451
496
  branch.match(REGEX_SLASH_UND),
@@ -461,16 +506,16 @@ async function main(config) {
461
506
  }
462
507
  }
463
508
  if (config.check_ticket.confirm_ticket) {
464
- const user_commit_ticket = await p2.text({
465
- message: commit_state.ticket ? `Ticket / issue inferred from branch ${import_picocolors2.default.dim("(confirm / edit)")}` : `Add ticket / issue ${OPTIONAL_PROMPT}`,
509
+ const user_commit_ticket = await p3.text({
510
+ message: commit_state.ticket ? `Ticket / issue inferred from branch ${import_picocolors3.default.dim("(confirm / edit)")}` : `Add ticket / issue ${OPTIONAL_PROMPT}`,
466
511
  placeholder: "",
467
512
  initialValue: commit_state.ticket
468
513
  });
469
- if (p2.isCancel(user_commit_ticket))
514
+ if (p3.isCancel(user_commit_ticket))
470
515
  process.exit(0);
471
516
  commit_state.ticket = user_commit_ticket ?? "";
472
517
  }
473
- const commit_title = await p2.text(
518
+ const commit_title = await p3.text(
474
519
  {
475
520
  message: "Write a brief title describing the commit",
476
521
  placeholder: "",
@@ -485,11 +530,11 @@ async function main(config) {
485
530
  }
486
531
  }
487
532
  );
488
- if (p2.isCancel(commit_title))
533
+ if (p3.isCancel(commit_title))
489
534
  process.exit(0);
490
535
  commit_state.title = clean_commit_title(commit_title);
491
536
  if (config.commit_body.enable) {
492
- const commit_body = await p2.text({
537
+ const commit_body = await p3.text({
493
538
  message: `Write a detailed description of the changes ${OPTIONAL_PROMPT}`,
494
539
  placeholder: "",
495
540
  validate: (val) => {
@@ -497,21 +542,21 @@ async function main(config) {
497
542
  return "Please enter a description";
498
543
  }
499
544
  });
500
- if (p2.isCancel(commit_body))
545
+ if (p3.isCancel(commit_body))
501
546
  process.exit(0);
502
547
  commit_state.body = commit_body ?? "";
503
548
  }
504
549
  if (config.commit_footer.enable) {
505
- const commit_footer = await p2.multiselect({
550
+ const commit_footer = await p3.multiselect({
506
551
  message: `Select optional footers ${SPACE_TO_SELECT}`,
507
552
  initialValues: config.commit_footer.initial_value,
508
553
  options: COMMIT_FOOTER_OPTIONS,
509
554
  required: false
510
555
  });
511
- if (p2.isCancel(commit_footer))
556
+ if (p3.isCancel(commit_footer))
512
557
  process.exit(0);
513
558
  if (commit_footer.includes("breaking-change")) {
514
- const breaking_changes_title = await p2.text({
559
+ const breaking_changes_title = await p3.text({
515
560
  message: "Breaking changes: Write a short title / summary",
516
561
  placeholder: "",
517
562
  validate: (value) => {
@@ -519,19 +564,19 @@ async function main(config) {
519
564
  return "Please enter a title / summary";
520
565
  }
521
566
  });
522
- if (p2.isCancel(breaking_changes_title))
567
+ if (p3.isCancel(breaking_changes_title))
523
568
  process.exit(0);
524
- const breaking_changes_body = await p2.text({
569
+ const breaking_changes_body = await p3.text({
525
570
  message: `Breaking Changes: Write a description & migration instructions ${OPTIONAL_PROMPT}`,
526
571
  placeholder: ""
527
572
  });
528
- if (p2.isCancel(breaking_changes_body))
573
+ if (p3.isCancel(breaking_changes_body))
529
574
  process.exit(0);
530
575
  commit_state.breaking_title = breaking_changes_title;
531
576
  commit_state.breaking_body = breaking_changes_body;
532
577
  }
533
578
  if (commit_footer.includes("deprecated")) {
534
- const deprecated_title = await p2.text({
579
+ const deprecated_title = await p3.text({
535
580
  message: "Deprecated: Write a short title / summary",
536
581
  placeholder: "",
537
582
  validate: (value) => {
@@ -539,13 +584,13 @@ async function main(config) {
539
584
  return "Please enter a title / summary";
540
585
  }
541
586
  });
542
- if (p2.isCancel(deprecated_title))
587
+ if (p3.isCancel(deprecated_title))
543
588
  process.exit(0);
544
- const deprecated_body = await p2.text({
589
+ const deprecated_body = await p3.text({
545
590
  message: `Deprecated: Write a description ${OPTIONAL_PROMPT}`,
546
591
  placeholder: ""
547
592
  });
548
- if (p2.isCancel(deprecated_body))
593
+ if (p3.isCancel(deprecated_body))
549
594
  process.exit(0);
550
595
  commit_state.deprecates_body = deprecated_body;
551
596
  commit_state.deprecates_title = deprecated_title;
@@ -554,46 +599,46 @@ async function main(config) {
554
599
  commit_state.closes = "Closes:";
555
600
  }
556
601
  if (commit_footer.includes("custom")) {
557
- const custom_footer = await p2.text({
602
+ const custom_footer = await p3.text({
558
603
  message: "Write a custom footer",
559
604
  placeholder: ""
560
605
  });
561
- if (p2.isCancel(custom_footer))
606
+ if (p3.isCancel(custom_footer))
562
607
  process.exit(0);
563
608
  commit_state.custom_footer = custom_footer;
564
609
  }
565
610
  }
566
611
  let continue_commit = true;
567
- p2.note(build_commit_string(commit_state, config, true), "Commit Preview");
612
+ p3.note(build_commit_string(commit_state, config, true, false), "Commit Preview");
568
613
  if (config.confirm_commit) {
569
- continue_commit = await p2.confirm({ message: "Confirm Commit?" });
570
- if (p2.isCancel(continue_commit))
614
+ continue_commit = await p3.confirm({ message: "Confirm Commit?" });
615
+ if (p3.isCancel(continue_commit))
571
616
  process.exit(0);
572
617
  }
573
618
  if (!continue_commit) {
574
- p2.log.info("Exiting without commit");
619
+ p3.log.info("Exiting without commit");
575
620
  process.exit(0);
576
621
  }
577
622
  try {
578
623
  const options = config.overrides.shell ? { shell: config.overrides.shell } : {};
579
- const output = (0, import_child_process2.execSync)(`git commit -m "${build_commit_string(commit_state, config, false)}"`, options).toString().trim();
624
+ const output = (0, import_child_process3.execSync)(`git commit -m "${build_commit_string(commit_state, config, false, true)}"`, options).toString().trim();
580
625
  if (config.print_commit_output)
581
- p2.log.info(output);
626
+ p3.log.info(output);
582
627
  } catch (err) {
583
- p2.log.error("Something went wrong when committing: " + err);
628
+ p3.log.error("Something went wrong when committing: " + err);
584
629
  }
585
630
  }
586
- function build_commit_string(commit_state, config, colorize = false) {
631
+ function build_commit_string(commit_state, config, colorize = false, escape_quotes = false) {
587
632
  let commit_string = "";
588
633
  if (commit_state.type) {
589
- commit_string += colorize ? import_picocolors2.default.blue(commit_state.type) : commit_state.type;
634
+ commit_string += colorize ? import_picocolors3.default.blue(commit_state.type) : commit_state.type;
590
635
  }
591
636
  if (commit_state.scope) {
592
- const scope = colorize ? import_picocolors2.default.cyan(commit_state.scope) : commit_state.scope;
637
+ const scope = colorize ? import_picocolors3.default.cyan(commit_state.scope) : commit_state.scope;
593
638
  commit_string += `(${scope})`;
594
639
  }
595
640
  if (commit_state.breaking_title && config.breaking_change.add_exclamation_to_title) {
596
- commit_string += colorize ? import_picocolors2.default.red("!") : "!";
641
+ commit_string += colorize ? import_picocolors3.default.red("!") : "!";
597
642
  }
598
643
  if (commit_state.scope || commit_state.type) {
599
644
  commit_string += ": ";
@@ -601,17 +646,17 @@ function build_commit_string(commit_state, config, colorize = false) {
601
646
  const position_start = config.check_ticket.title_position === "start";
602
647
  const position_end = config.check_ticket.title_position === "end";
603
648
  if (commit_state.ticket && config.check_ticket.add_to_title && position_start) {
604
- commit_string += colorize ? import_picocolors2.default.magenta(commit_state.ticket) + " " : commit_state.ticket + " ";
649
+ commit_string += colorize ? import_picocolors3.default.magenta(commit_state.ticket) + " " : commit_state.ticket + " ";
605
650
  }
606
651
  if (commit_state.title) {
607
- commit_string += colorize ? import_picocolors2.default.reset(commit_state.title) : commit_state.title;
652
+ commit_string += colorize ? import_picocolors3.default.reset(commit_state.title) : commit_state.title;
608
653
  }
609
654
  if (commit_state.ticket && config.check_ticket.add_to_title && position_end) {
610
- commit_string += " " + (colorize ? import_picocolors2.default.magenta(commit_state.ticket) : commit_state.ticket);
655
+ commit_string += " " + (colorize ? import_picocolors3.default.magenta(commit_state.ticket) : commit_state.ticket);
611
656
  }
612
657
  if (commit_state.body) {
613
658
  const temp = commit_state.body.split("\\n");
614
- const res = temp.map((v) => colorize ? import_picocolors2.default.reset(v.trim()) : v.trim()).join("\n");
659
+ const res = temp.map((v) => colorize ? import_picocolors3.default.reset(v.trim()) : v.trim()).join("\n");
615
660
  commit_string += colorize ? `
616
661
 
617
662
  ${res}` : `
@@ -619,32 +664,32 @@ ${res}` : `
619
664
  ${res}`;
620
665
  }
621
666
  if (commit_state.breaking_title) {
622
- const title = colorize ? import_picocolors2.default.red(`BREAKING CHANGE: ${commit_state.breaking_title}`) : `BREAKING CHANGE: ${commit_state.breaking_title}`;
667
+ const title = colorize ? import_picocolors3.default.red(`BREAKING CHANGE: ${commit_state.breaking_title}`) : `BREAKING CHANGE: ${commit_state.breaking_title}`;
623
668
  commit_string += `
624
669
 
625
670
  ${title}`;
626
671
  }
627
672
  if (commit_state.breaking_body) {
628
- const body = colorize ? import_picocolors2.default.red(commit_state.breaking_body) : commit_state.breaking_body;
673
+ const body = colorize ? import_picocolors3.default.red(commit_state.breaking_body) : commit_state.breaking_body;
629
674
  commit_string += `
630
675
 
631
676
  ${body}`;
632
677
  }
633
678
  if (commit_state.deprecates_title) {
634
- const title = colorize ? import_picocolors2.default.yellow(`DEPRECATED: ${commit_state.deprecates_title}`) : `DEPRECATED: ${commit_state.deprecates_title}`;
679
+ const title = colorize ? import_picocolors3.default.yellow(`DEPRECATED: ${commit_state.deprecates_title}`) : `DEPRECATED: ${commit_state.deprecates_title}`;
635
680
  commit_string += `
636
681
 
637
682
  ${title}`;
638
683
  }
639
684
  if (commit_state.deprecates_body) {
640
- const body = colorize ? import_picocolors2.default.yellow(commit_state.deprecates_body) : commit_state.deprecates_body;
685
+ const body = colorize ? import_picocolors3.default.yellow(commit_state.deprecates_body) : commit_state.deprecates_body;
641
686
  commit_string += `
642
687
 
643
688
  ${body}`;
644
689
  }
645
690
  if (commit_state.custom_footer) {
646
691
  const temp = commit_state.custom_footer.split("\\n");
647
- const res = temp.map((v) => colorize ? import_picocolors2.default.reset(v.trim()) : v.trim()).join("\n");
692
+ const res = temp.map((v) => colorize ? import_picocolors3.default.reset(v.trim()) : v.trim()).join("\n");
648
693
  commit_string += colorize ? `
649
694
 
650
695
  ${res}` : `
@@ -654,10 +699,13 @@ ${res}`;
654
699
  if (commit_state.closes && commit_state.ticket) {
655
700
  commit_string += colorize ? `
656
701
 
657
- ${import_picocolors2.default.reset(commit_state.closes)} ${import_picocolors2.default.magenta(commit_state.ticket)}` : `
702
+ ${import_picocolors3.default.reset(commit_state.closes)} ${import_picocolors3.default.magenta(commit_state.ticket)}` : `
658
703
 
659
704
  ${commit_state.closes} ${commit_state.ticket}`;
660
705
  }
706
+ if (escape_quotes) {
707
+ commit_string = commit_string.replaceAll('"', '\\"');
708
+ }
661
709
  return commit_string;
662
710
  }
663
711
  // Annotate the CommonJS export names for ESM import in node:
package/dist/init.js CHANGED
@@ -34,6 +34,9 @@ var p = __toESM(require("@clack/prompts"));
34
34
  var import_zod_validation_error = require("zod-validation-error");
35
35
  var CONFIG_FILE_NAME = ".better-commits.json";
36
36
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
37
+ var A_FOR_ALL = `${import_picocolors.default.dim(
38
+ "(<space> to select, <a> to select all)"
39
+ )}`;
37
40
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
38
41
  var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
39
42
  var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
package/dist/utils.js CHANGED
@@ -30,6 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/utils.ts
31
31
  var utils_exports = {};
32
32
  __export(utils_exports, {
33
+ A_FOR_ALL: () => A_FOR_ALL,
33
34
  BRANCH_ACTION_OPTIONS: () => BRANCH_ACTION_OPTIONS,
34
35
  CACHE_PROMPT: () => CACHE_PROMPT,
35
36
  COMMIT_FOOTER_OPTIONS: () => COMMIT_FOOTER_OPTIONS,
@@ -49,7 +50,6 @@ __export(utils_exports, {
49
50
  Z_BRANCH_ACTIONS: () => Z_BRANCH_ACTIONS,
50
51
  Z_FOOTER_OPTIONS: () => Z_FOOTER_OPTIONS,
51
52
  addNewLine: () => addNewLine,
52
- check_missing_stage: () => check_missing_stage,
53
53
  clean_commit_title: () => clean_commit_title,
54
54
  get_default_config_path: () => get_default_config_path,
55
55
  get_git_root: () => get_git_root,
@@ -204,6 +204,9 @@ var BranchState = import_zod.z.object({
204
204
  // src/utils.ts
205
205
  var CONFIG_FILE_NAME = ".better-commits.json";
206
206
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
207
+ var A_FOR_ALL = `${import_picocolors.default.dim(
208
+ "(<space> to select, <a> to select all)"
209
+ )}`;
207
210
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
208
211
  var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
209
212
  var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
@@ -373,9 +376,6 @@ function get_git_root() {
373
376
  function get_default_config_path() {
374
377
  return (0, import_os.homedir)() + "/" + CONFIG_FILE_NAME;
375
378
  }
376
- function check_missing_stage(stats) {
377
- return stats.files.filter((f) => f.index.trim() === "" || f.index === "?").map((f) => f.path);
378
- }
379
379
  function addNewLine(arr, i) {
380
380
  return i === arr.length - 1 ? "" : "\n";
381
381
  }
@@ -389,6 +389,7 @@ function clean_commit_title(title) {
389
389
  }
390
390
  // Annotate the CommonJS export names for ESM import in node:
391
391
  0 && (module.exports = {
392
+ A_FOR_ALL,
392
393
  BRANCH_ACTION_OPTIONS,
393
394
  CACHE_PROMPT,
394
395
  COMMIT_FOOTER_OPTIONS,
@@ -408,7 +409,6 @@ function clean_commit_title(title) {
408
409
  Z_BRANCH_ACTIONS,
409
410
  Z_FOOTER_OPTIONS,
410
411
  addNewLine,
411
- check_missing_stage,
412
412
  clean_commit_title,
413
413
  get_default_config_path,
414
414
  get_git_root,
package/dist/zod-state.js CHANGED
@@ -43,6 +43,9 @@ var import_picocolors = __toESM(require("picocolors"));
43
43
  var p = __toESM(require("@clack/prompts"));
44
44
  var import_zod_validation_error = require("zod-validation-error");
45
45
  var SPACE_TO_SELECT = `${import_picocolors.default.dim("(<space> to select)")}`;
46
+ var A_FOR_ALL = `${import_picocolors.default.dim(
47
+ "(<space> to select, <a> to select all)"
48
+ )}`;
46
49
  var OPTIONAL_PROMPT = `${import_picocolors.default.dim("(optional)")}`;
47
50
  var CACHE_PROMPT = `${import_picocolors.default.dim("(value will be saved)")}`;
48
51
  var REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "better-commits",
3
3
  "private": false,
4
- "version": "1.7.2",
4
+ "version": "1.8.1",
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": [
@@ -24,7 +24,6 @@
24
24
  "@clack/prompts": "^0.6.2",
25
25
  "configstore": "^5.0.1",
26
26
  "picocolors": "^1.0.0",
27
- "simple-git": "^3.16.1",
28
27
  "zod": "^3.21.3",
29
28
  "zod-validation-error": "^1.0.1"
30
29
  },
package/src/git.ts ADDED
@@ -0,0 +1,57 @@
1
+ #! /usr/bin/env node
2
+ import { execSync } from "child_process";
3
+ import * as p from "@clack/prompts";
4
+ import color from "picocolors";
5
+
6
+ const porcelain_states = ["M", "T", "R", "D", "A", "C"];
7
+
8
+ export function git_status(): { index: string[]; work_tree: string[] } {
9
+ let status = "";
10
+ try {
11
+ status = execSync("git status --porcelain", { stdio: "pipe" }).toString();
12
+ } catch (err) {
13
+ p.log.error(color.red("Failed to git status"));
14
+ return { index: [], work_tree: [] };
15
+ }
16
+
17
+ const lines = status.split("\n");
18
+ const work_tree: string[] = [];
19
+ const index: string[] = [];
20
+ lines.forEach((v) => {
21
+ const line = v.trimEnd();
22
+ if (!line) return;
23
+
24
+ const path_plus_file = line.substring(2).trim();
25
+ const first_char = line.charAt(0).trim();
26
+ const second_char = line.charAt(1).trim();
27
+
28
+ // Untracked, always dirty
29
+ if (first_char === "?" || second_char === "?") {
30
+ work_tree.push(path_plus_file);
31
+ }
32
+
33
+ if (porcelain_states.includes(first_char)) {
34
+ index.push(path_plus_file);
35
+ }
36
+
37
+ if (porcelain_states.includes(second_char)) {
38
+ work_tree.push(path_plus_file);
39
+ }
40
+ });
41
+
42
+ return { index, work_tree };
43
+ }
44
+
45
+ export function git_add(files: string[]) {
46
+ const space_delimited_files = files.join(" ");
47
+ if (space_delimited_files) {
48
+ try {
49
+ execSync(`git add ${space_delimited_files}`, {
50
+ stdio: "pipe",
51
+ }).toString();
52
+ p.log.success(color.green("Changes successfully staged"));
53
+ } catch (err) {
54
+ p.log.error(color.red("Failed to stage changes"));
55
+ }
56
+ }
57
+ }
package/src/index.ts CHANGED
@@ -2,46 +2,43 @@
2
2
 
3
3
  import * as p from '@clack/prompts';
4
4
  import color from 'picocolors';
5
- import { simpleGit } from "simple-git"
6
5
  import { execSync } from 'child_process';
6
+ import { chdir } from "process";
7
7
  import { z } from "zod";
8
8
  import { CommitState, Config } from './zod-state';
9
- import { load_setup, 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, REGEX_SLASH_UND, REGEX_START_UND } from './utils';
9
+ import { load_setup, 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, REGEX_SLASH_UND, REGEX_START_UND } from './utils';
10
+ import { git_add, git_status } from './git';
10
11
 
11
12
  main(load_setup());
12
13
 
13
14
  export async function main(config: z.infer<typeof Config>) {
14
15
  let commit_state = CommitState.parse({})
15
- const simple_git = simpleGit({ baseDir: get_git_root() })
16
- let git_status = await simple_git.status();
16
+ chdir(get_git_root());
17
+
17
18
  if (config.check_status) {
19
+ let {index, work_tree} = git_status()
18
20
  p.log.step(color.black(color.bgGreen(' Checking Git Status ')))
19
- const missing_files = check_missing_stage(git_status);
20
- const staged_files = git_status.staged.reduce((acc,curr,i: number) => color.green(acc+curr+addNewLine(git_status.staged,i)), '');
21
+ const staged_files = index.reduce((acc,curr,i: number) => color.green(acc+curr+addNewLine(index,i)), '');
21
22
  p.log.success('Changes to be committed:\n'+staged_files)
22
- if (missing_files.length) {
23
- const unstaged_files = missing_files.reduce((acc,curr,i: number) => color.red(acc+curr+addNewLine(missing_files,i)), '');
23
+ if (work_tree.length) {
24
+ const unstaged_files = work_tree.reduce((acc,curr,i: number) => color.red(acc+curr+addNewLine(work_tree,i)), '');
24
25
  p.log.error('Changes not staged for commit:\n'+unstaged_files)
25
26
  const selected_for_staging = await p.multiselect({
26
27
  message: `Some files have not been staged, would you like to add them now? ${SPACE_TO_SELECT}`,
27
- options: [{value: '.', label: '.'}, ...missing_files.map(v => ({value: v, label: v}))],
28
+ options: [{value: '.', label: '.'}, ...work_tree.map(v => ({value: v, label: v}))],
28
29
  required: false,
29
30
  }) as string[]
30
31
  if (p.isCancel(selected_for_staging)) process.exit(0)
32
+ git_add(selected_for_staging);
33
+ }
31
34
 
32
- await simple_git.add(selected_for_staging)
33
- git_status = await simple_git.status();
34
- if (selected_for_staging?.length){
35
- p.log.success(color.green('Changes successfully staged'))
36
- }
35
+ let updated_status = git_status()
36
+ if (!updated_status.index.length) {
37
+ p.log.error(color.red('no changes added to commit (use "git add" and/or "git commit -a")'))
38
+ process.exit(0);
37
39
  }
38
40
  }
39
41
 
40
- if (!git_status.staged.length) {
41
- p.log.error(color.red('no changes added to commit (use "git add" and/or "git commit -a")'))
42
- process.exit(0);
43
- }
44
-
45
42
  if (config.commit_type.enable) {
46
43
  let message = 'Select a commit type';
47
44
  let initial_value = config.commit_type.initial_value
@@ -208,7 +205,7 @@ export async function main(config: z.infer<typeof Config>) {
208
205
 
209
206
 
210
207
  let continue_commit = true;
211
- p.note(build_commit_string(commit_state, config, true), 'Commit Preview')
208
+ p.note(build_commit_string(commit_state, config, true, false), 'Commit Preview')
212
209
  if (config.confirm_commit) {
213
210
  continue_commit = await p.confirm({message: 'Confirm Commit?'}) as boolean;
214
211
  if (p.isCancel(continue_commit)) process.exit(0)
@@ -221,14 +218,14 @@ export async function main(config: z.infer<typeof Config>) {
221
218
 
222
219
  try {
223
220
  const options = config.overrides.shell ? { shell: config.overrides.shell } : {}
224
- const output = execSync(`git commit -m "${build_commit_string(commit_state, config, false)}"`, options).toString().trim();
221
+ const output = execSync(`git commit -m "${build_commit_string(commit_state, config, false, true)}"`, options).toString().trim();
225
222
  if (config.print_commit_output) p.log.info(output)
226
223
  } catch(err) {
227
224
  p.log.error('Something went wrong when committing: ' + err)
228
225
  }
229
226
  }
230
227
 
231
- function build_commit_string(commit_state: z.infer<typeof CommitState>, config: z.infer<typeof Config>, colorize: boolean = false): string {
228
+ function build_commit_string(commit_state: z.infer<typeof CommitState>, config: z.infer<typeof Config>, colorize: boolean = false, escape_quotes: boolean = false): string {
232
229
  let commit_string = '';
233
230
  if (commit_state.type) {
234
231
  commit_string += colorize ? color.blue(commit_state.type) : commit_state.type
@@ -298,6 +295,10 @@ function build_commit_string(commit_state: z.infer<typeof CommitState>, config:
298
295
  commit_string += colorize ? `\n\n${color.reset(commit_state.closes)} ${color.magenta(commit_state.ticket)}` : `\n\n${commit_state.closes} ${commit_state.ticket}`;
299
296
  }
300
297
 
298
+ if (escape_quotes) {
299
+ commit_string = commit_string.replaceAll('"', '\\"')
300
+ }
301
+
301
302
  return commit_string;
302
303
  }
303
304
 
package/src/utils.ts CHANGED
@@ -1,5 +1,4 @@
1
1
  import { homedir } from "os";
2
- import { StatusResult } from "simple-git";
3
2
  import { z } from "zod";
4
3
  import color from "picocolors";
5
4
  import { execSync } from "child_process";
@@ -10,6 +9,9 @@ import { Config } from "./zod-state";
10
9
 
11
10
  export const CONFIG_FILE_NAME = ".better-commits.json";
12
11
  export const SPACE_TO_SELECT = `${color.dim("(<space> to select)")}`;
12
+ export const A_FOR_ALL = `${color.dim(
13
+ "(<space> to select, <a> to select all)"
14
+ )}`;
13
15
  export const OPTIONAL_PROMPT = `${color.dim("(optional)")}`;
14
16
  export const CACHE_PROMPT = `${color.dim("(value will be saved)")}`;
15
17
  export const REGEX_SLASH_TAG = new RegExp(/\/(\w+-\d+)/);
@@ -206,12 +208,6 @@ export function get_default_config_path(): string {
206
208
  return homedir() + "/" + CONFIG_FILE_NAME;
207
209
  }
208
210
 
209
- export function check_missing_stage(stats: StatusResult): string[] {
210
- return stats.files
211
- .filter((f) => f.index.trim() === "" || f.index === "?")
212
- .map((f) => f.path);
213
- }
214
-
215
211
  export function addNewLine(arr: string[], i: number) {
216
212
  return i === arr.length - 1 ? "" : "\n";
217
213
  }