Skip to content

Commit da91992

Browse files
committed
feat: update output formatting
BREAKING CHANGE: requires Node.js >= 22.13.0
1 parent 2cd3c4f commit da91992

6 files changed

Lines changed: 52 additions & 40 deletions

File tree

README.md

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Example output:
3333

3434
```bash
3535
$ git-merged-branches
36-
2 branches merged into 'master':
36+
2 branches merged into master:
3737
fix/crash-on-start
3838
feat/add-new-feature
3939
```
@@ -42,7 +42,7 @@ You can also delete these merged branches with the `--delete` option:
4242

4343
```bash
4444
$ git-merged-branches --delete
45-
2 branches merged into 'master':
45+
2 branches merged into master:
4646
fix/crash-on-start
4747
feature/add-new-feature
4848

@@ -82,12 +82,12 @@ With this setup, `git-merged-branches` will generate links for branches with suc
8282

8383
```bash
8484
$ git-merged-branches
85-
4 branches merged into 'master':
85+
4 branches merged into master:
8686
fix/EXTERNAL-391
87-
fix/TOKEN-123_some-fix <https://your-jira-instance.net/browse/TOKEN-123>
87+
fix/TOKEN-123_some-fix https://your-jira-instance.net/browse/TOKEN-123
8888
hotfix
89-
TOKEN-800_new-feature <https://your-jira-instance.net/browse/TOKEN-800>
90-
refactor/#55_cleanup <https://github.com/org/repo/issues/55>
89+
TOKEN-800_new-feature https://your-jira-instance.net/browse/TOKEN-800
90+
refactor/#55_cleanup https://github.com/org/repo/issues/55
9191
```
9292

9393
If the configuration is invalid, warnings will be shown and the utility will skip formatting URLs.

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
"type": "module",
1111
"version": "0.3.2",
1212
"license": "MIT",
13+
"engines": {
14+
"node": ">=22.13.0"
15+
},
1316
"author": {
1417
"name": "VChet",
1518
"url": "https://github.com/VChet"

src/helpers.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { styleText } from "node:util";
2+
13
export function logError(prefix: string, error: unknown): void {
24
if (error instanceof Error) {
35
console.warn(`${prefix}: ${error.message}`);
@@ -9,3 +11,10 @@ export function logError(prefix: string, error: unknown): void {
911
export function pluralize(count: number, words: [string, string]): string {
1012
return `${count} ${count === 1 ? words[0] : words[1]}`;
1113
}
14+
15+
export const red = (payload: string): string => styleText("red", payload);
16+
export const cyan = (payload: string): string => styleText("cyan", payload);
17+
export const yellow = (payload: string): string => styleText("yellow", payload);
18+
export const green = (payload: string): string => styleText("green", payload);
19+
export const bold = (payload: string): string => styleText("bold", payload);
20+
export const underline = (payload: string): string => styleText("underline", payload);

src/index.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
import process from "node:process";
22
import { getConfig, getDefaultTargetBranch, getMergedBranches, isDetachedHead, isGitRepo } from "./repo.js";
33
import { outputMergedBranches } from "./output.js";
4-
import { logError } from "./helpers.js";
4+
import { bold, logError, red } from "./helpers.js";
55

66
function main(): void {
77
if (!isGitRepo()) {
8-
console.error("Not a git repository.");
8+
console.error(red("Not a git repository"));
99
process.exit(1);
1010
}
1111
if (isDetachedHead()) {
12-
console.error("HEAD is detached (e.g., after checkout of a commit). Please switch to a branch.");
12+
console.error(red(`${bold("HEAD")} is detached (e.g., after checkout of a commit). Please switch to a branch`));
1313
process.exit(1);
1414
}
1515

1616
const targetBranch = getDefaultTargetBranch();
1717
if (!targetBranch) {
18-
console.error("No 'master' or 'main' branch found.");
18+
console.error(red(`No ${bold("master")} or ${bold("main")} branch found`));
1919
process.exit(1);
2020
}
2121
try {

src/output.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { pluralize } from "./helpers.js";
1+
import { bold, cyan, green, pluralize, underline, yellow } from "./helpers.js";
22
import { deleteBranches, fetchRemoteBranches } from "./repo.js";
33
import { isValidURL } from "./validate.js";
44
import type { GitMergedConfig, GitMergedOptions } from "./types.js";
@@ -16,7 +16,7 @@ function formatSingleBranch(
1616
if (!match) continue;
1717

1818
const url = issueUrlFormat.replace("{{prefix}}", prefix).replace("{{id}}", match[1]);
19-
return `${branch} <${url}>`;
19+
return `${branch} ${underline(url)}`;
2020
}
2121
return branch;
2222
}
@@ -25,12 +25,12 @@ export function formatTaskBranches(branches: string[], { issueUrlFormat, issueUr
2525
if (!issueUrlFormat || !issueUrlPrefix) { return branches; }
2626
// issueUrlFormat
2727
if (!isValidURL(issueUrlFormat)) {
28-
console.warn(`'${issueUrlFormat}' is not a valid URL. Skipped formatting.`);
28+
console.warn(`${yellow(issueUrlFormat)} is not a valid URL. Skipped formatting`);
2929
return branches;
3030
}
3131
// issueUrlPrefix
3232
if (!Array.isArray(issueUrlPrefix)) {
33-
console.warn(`'${issueUrlPrefix}' is not an array. Skipped formatting.`);
33+
console.warn(`${yellow(issueUrlPrefix)} is not an array. Skipped formatting`);
3434
return branches;
3535
}
3636
return branches.map((branch) => formatSingleBranch(branch, issueUrlFormat, issueUrlPrefix));
@@ -42,26 +42,26 @@ export function outputMergedBranches(
4242
config: GitMergedConfig,
4343
options: GitMergedOptions = {}
4444
): void {
45-
if (!branches.length) { return console.info(`No branches merged into '${targetBranch}'.`); }
45+
if (!branches.length) { return console.info(`No branches merged into ${bold(targetBranch)}`); }
4646

4747
const pluralized = pluralize(branches.length, ["branch", "branches"]);
4848

49-
console.info(`${pluralized} merged into '${targetBranch}':`);
49+
console.info(`${yellow(pluralized)} merged into ${bold(targetBranch)}:`);
5050
console.info(formatTaskBranches(branches, config).join("\n"));
5151

5252
const remoteBranches = fetchRemoteBranches("origin");
5353
const remoteMerged = branches.filter(branch => remoteBranches.includes(branch));
5454
if (options.deleteBranches) {
5555
deleteBranches(branches, remoteMerged);
56-
console.info("Branches deleted successfully.");
56+
console.info(green("Branches deleted successfully"));
5757
return;
5858
}
5959

60-
console.info(`\nUse --delete to delete ${pluralized} automatically.`);
60+
console.info(`\nUse ${cyan("--delete")} to delete ${yellow(pluralized)} automatically`);
6161
console.info("\nDelete locally:");
62-
console.info(` git branch --delete ${branches.join(" ")}`);
62+
console.info(cyan(` git branch --delete ${branches.join(" ")}`));
6363
if (remoteMerged.length) {
6464
console.info("\nDelete remotely:");
65-
console.info(` git push origin --delete ${remoteMerged.join(" ")}`);
65+
console.info(cyan(` git push origin --delete ${remoteMerged.join(" ")}`));
6666
}
6767
}

src/tests/output.test.ts

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ describe("formatTaskBranches", () => {
1717
const branches = ["feat/TOKEN-800_new-feature", "fix/TOKEN-123_some-fix"];
1818
const result = formatTaskBranches(branches, DEFAULT_CONFIG);
1919
expect(result).toEqual([
20-
"feat/TOKEN-800_new-feature <https://test-instance.org/browse/TOKEN-800>",
21-
"fix/TOKEN-123_some-fix <https://test-instance.org/browse/TOKEN-123>"
20+
"feat/TOKEN-800_new-feature https://test-instance.org/browse/TOKEN-800",
21+
"fix/TOKEN-123_some-fix https://test-instance.org/browse/TOKEN-123"
2222
]);
2323
});
2424

@@ -30,8 +30,8 @@ describe("formatTaskBranches", () => {
3030
};
3131
const result = formatTaskBranches(branches, config);
3232
expect(result).toEqual([
33-
"fix/TOKEN-123_fix <https://example.com/browse/TOKEN-123>",
34-
"feat/PROJECT-45_add-feature <https://example.com/browse/PROJECT-45>"
33+
"fix/TOKEN-123_fix https://example.com/browse/TOKEN-123",
34+
"feat/PROJECT-45_add-feature https://example.com/browse/PROJECT-45"
3535
]);
3636
});
3737

@@ -43,8 +43,8 @@ describe("formatTaskBranches", () => {
4343
};
4444
const result = formatTaskBranches(branches, config);
4545
expect(result).toEqual([
46-
"fix/#123_fix <https://github.com/org/repo/issues/123>",
47-
"feat/#45_add-feature <https://github.com/org/repo/issues/45>"
46+
"fix/#123_fix https://github.com/org/repo/issues/123",
47+
"feat/#45_add-feature https://github.com/org/repo/issues/45"
4848
]);
4949
});
5050

@@ -98,15 +98,15 @@ describe("outputMergedBranches", () => {
9898
const branches = ["feat/TOKEN-800_new-feature", "fix/TOKEN-123_some-fix"];
9999

100100
outputMergedBranches(branches, "master", DEFAULT_CONFIG);
101-
expect(infoSpy).toHaveBeenNthCalledWith(1, "2 branches merged into 'master':");
101+
expect(infoSpy).toHaveBeenNthCalledWith(1, "2 branches merged into master:");
102102
const branchOutput = [
103-
"feat/TOKEN-800_new-feature <https://test-instance.org/browse/TOKEN-800>",
104-
"fix/TOKEN-123_some-fix <https://test-instance.org/browse/TOKEN-123>"
103+
"feat/TOKEN-800_new-feature https://test-instance.org/browse/TOKEN-800",
104+
"fix/TOKEN-123_some-fix https://test-instance.org/browse/TOKEN-123"
105105
];
106106
expect(infoSpy).toHaveBeenNthCalledWith(2, branchOutput.join("\n"));
107107

108108
const localDelete = `git branch --delete ${branches.join(" ")}`;
109-
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 2 branches automatically.");
109+
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 2 branches automatically");
110110
expect(infoSpy).toHaveBeenNthCalledWith(4, "\nDelete locally:");
111111
expect(infoSpy).toHaveBeenNthCalledWith(5, ` ${localDelete}`);
112112
expect(infoSpy).toHaveBeenCalledTimes(5);
@@ -118,16 +118,16 @@ describe("outputMergedBranches", () => {
118118
const fetchRemoteMock = vi.spyOn(repoMethods, "fetchRemoteBranches").mockReturnValue(branches);
119119

120120
outputMergedBranches(branches, "master", DEFAULT_CONFIG);
121-
expect(infoSpy).toHaveBeenNthCalledWith(1, "2 branches merged into 'master':");
121+
expect(infoSpy).toHaveBeenNthCalledWith(1, "2 branches merged into master:");
122122
const branchOutput = [
123-
"feat/TOKEN-800_new-feature <https://test-instance.org/browse/TOKEN-800>",
124-
"fix/TOKEN-123_some-fix <https://test-instance.org/browse/TOKEN-123>"
123+
"feat/TOKEN-800_new-feature https://test-instance.org/browse/TOKEN-800",
124+
"fix/TOKEN-123_some-fix https://test-instance.org/browse/TOKEN-123"
125125
];
126126
expect(infoSpy).toHaveBeenNthCalledWith(2, branchOutput.join("\n"));
127127

128128
const localDelete = `git branch --delete ${branches.join(" ")}`;
129129
const remoteDelete = `git push origin --delete ${branches.join(" ")}`;
130-
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 2 branches automatically.");
130+
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 2 branches automatically");
131131
expect(infoSpy).toHaveBeenNthCalledWith(4, "\nDelete locally:");
132132
expect(infoSpy).toHaveBeenNthCalledWith(5, ` ${localDelete}`);
133133
expect(infoSpy).toHaveBeenNthCalledWith(6, "\nDelete remotely:");
@@ -142,11 +142,11 @@ describe("outputMergedBranches", () => {
142142
const branches = ["feat/TOKEN-800_new-feature"];
143143

144144
outputMergedBranches(branches, "master", DEFAULT_CONFIG);
145-
expect(infoSpy).toHaveBeenNthCalledWith(1, "1 branch merged into 'master':");
146-
expect(infoSpy).toHaveBeenNthCalledWith(2, "feat/TOKEN-800_new-feature <https://test-instance.org/browse/TOKEN-800>");
145+
expect(infoSpy).toHaveBeenNthCalledWith(1, "1 branch merged into master:");
146+
expect(infoSpy).toHaveBeenNthCalledWith(2, "feat/TOKEN-800_new-feature https://test-instance.org/browse/TOKEN-800");
147147

148148
const localDelete = `git branch --delete ${branches.join(" ")}`;
149-
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 1 branch automatically.");
149+
expect(infoSpy).toHaveBeenNthCalledWith(3, "\nUse --delete to delete 1 branch automatically");
150150
expect(infoSpy).toHaveBeenNthCalledWith(4, "\nDelete locally:");
151151
expect(infoSpy).toHaveBeenNthCalledWith(5, ` ${localDelete}`);
152152
expect(infoSpy).toHaveBeenCalledTimes(5);
@@ -155,7 +155,7 @@ describe("outputMergedBranches", () => {
155155

156156
it("should log a message when no branches are merged", () => {
157157
outputMergedBranches([], "master", DEFAULT_CONFIG);
158-
expect(infoSpy).toHaveBeenCalledWith("No branches merged into 'master'.");
158+
expect(infoSpy).toHaveBeenCalledWith("No branches merged into master");
159159
expect(infoSpy).toHaveBeenCalledTimes(1);
160160
expect(warnSpy).not.toHaveBeenCalled();
161161
});
@@ -166,7 +166,7 @@ describe("outputMergedBranches", () => {
166166

167167
outputMergedBranches(branches, "master", config);
168168
expect(infoSpy).toHaveBeenCalledTimes(5);
169-
expect(warnSpy).toHaveBeenCalledWith("'invalid-url' is not a valid URL. Skipped formatting.");
169+
expect(warnSpy).toHaveBeenCalledWith("invalid-url is not a valid URL. Skipped formatting");
170170
expect(warnSpy).toHaveBeenCalledTimes(1);
171171
});
172172

@@ -179,7 +179,7 @@ describe("outputMergedBranches", () => {
179179
outputMergedBranches(branches, "master", DEFAULT_CONFIG, { deleteBranches: true });
180180

181181
expect(deleteMock).toHaveBeenCalledWith(branches, branches);
182-
expect(infoSpy).toHaveBeenCalledWith("Branches deleted successfully.");
182+
expect(infoSpy).toHaveBeenCalledWith("Branches deleted successfully");
183183

184184
deleteMock.mockRestore();
185185
fetchRemoteMock.mockRestore();

0 commit comments

Comments
 (0)