@elsikora/git-branch-lint 1.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 ElsiKora
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/LICENSE.md ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2022 Bogdan Kolesnyk (bogdan.kolesnyk@gmail.com)
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
13
+ all 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
21
+ THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ <p align="center">
2
+ <img src="https://6jft62zmy9nx2oea.public.blob.vercel-storage.com/git-branch-lint-0rauIZprtXnHOrt840ZtTzrUK32erg.png" width="500" alt="project-logo">
3
+ </p>
4
+
5
+ <h1 align="center">Git Branch Lint 🌿</h1>
6
+ <p align="center"><em>Enforce consistent git branch naming conventions across your projects</em></p>
7
+
8
+ <p align="center">
9
+ <a aria-label="ElsiKora logo" href="https://elsikora.com">
10
+ <img src="https://img.shields.io/badge/MADE%20BY%20ElsiKora-333333.svg?style=for-the-badge" alt="ElsiKora">
11
+ </a> <img src="https://img.shields.io/badge/version-blue.svg?style=for-the-badge&logo=npm&logoColor=white" alt="version"> <img src="https://img.shields.io/badge/typescript-blue.svg?style=for-the-badge&logo=typescript&logoColor=white" alt="typescript"> <img src="https://img.shields.io/badge/license-green.svg?style=for-the-badge&logo=license&logoColor=white" alt="license"> <img src="https://img.shields.io/badge/node-green.svg?style=for-the-badge&logo=node.js&logoColor=white" alt="node">
12
+ </p>
13
+
14
+
15
+ ## 📚 Table of Contents
16
+ - [Description](#-description)
17
+ - [Features](#-features)
18
+ - [Installation](#-installation)
19
+ - [Usage](#-usage)
20
+ - [Roadmap](#-roadmap)
21
+ - [FAQ](#-faq)
22
+ - [License](#-license)
23
+
24
+
25
+ ## 📖 Description
26
+ Git Branch Lint is a powerful tool designed to maintain consistent git branch naming conventions in your development workflow. It helps teams enforce standardized branch naming patterns, prevent common naming mistakes, and maintain a clean git history. Built with TypeScript and following clean architecture principles, it provides flexible configuration options and helpful error messages to guide developers in following your team's branch naming conventions.
27
+
28
+ ## 🚀 Features
29
+ - ✨ **🔍 Validates branch names against customizable patterns**
30
+ - ✨ **⚡ Fast and lightweight with minimal dependencies**
31
+ - ✨ **🛠 Flexible configuration through multiple formats (JS, JSON, YAML)**
32
+ - ✨ **💡 Helpful error messages with suggestions for fixing invalid names**
33
+ - ✨ **🔒 Prevents usage of prohibited branch names**
34
+ - ✨ **📏 Configurable length restrictions for branch names**
35
+ - ✨ **🎨 Colored CLI output for better readability**
36
+ - ✨ **🔧 Easy integration with git hooks and CI/CD pipelines**
37
+
38
+ ## 🛠 Installation
39
+ ```bash
40
+ # Using npm
41
+ npm install --save-dev git-branch-lint
42
+
43
+ # Using yarn
44
+ yarn add -D git-branch-lint
45
+
46
+ # Using pnpm
47
+ pnpm add -D git-branch-lint
48
+ ```
49
+
50
+ ## 💡 Usage
51
+ ## Basic Usage
52
+
53
+ Run the linter directly:
54
+ ```bash
55
+ git-branch-lint
56
+ ```
57
+
58
+ ## Configuration
59
+
60
+ Create a configuration file `.elsikora/.git-branch-lintrc.json`:
61
+ ```json
62
+ {
63
+ "pattern": ":type/:name",
64
+ "params": {
65
+ "type": ["feature", "bugfix", "hotfix", "release"],
66
+ "name": ["[a-z0-9-]+"]
67
+ },
68
+ "prohibited": ["master", "main", "develop"],
69
+ "minLength": 5,
70
+ "maxLength": 50
71
+ }
72
+ ```
73
+
74
+ ## Git Hooks Integration
75
+
76
+ Add to your `package.json`:
77
+ ```json
78
+ {
79
+ "husky": {
80
+ "hooks": {
81
+ "pre-commit": "git-branch-lint"
82
+ }
83
+ }
84
+ }
85
+ ```
86
+
87
+ ## Advanced Usage
88
+
89
+ Custom configuration in JavaScript (`.elsikora/git-branch-lint.config.js`):
90
+ ```javascript
91
+ module.exports = {
92
+ pattern: ':type/:scope/:name',
93
+ params: {
94
+ type: ['feature', 'bugfix', 'hotfix'],
95
+ scope: ['api', 'ui', 'core'],
96
+ name: ['[a-z0-9-]+'],
97
+ },
98
+ prohibited: ['wip', 'temp'],
99
+ minLength: 10,
100
+ maxLength: 100
101
+ }
102
+ ```
103
+
104
+ ## CI/CD Integration
105
+
106
+ GitHub Actions example:
107
+ ```yaml
108
+ name: Lint Branch Names
109
+ on: [push]
110
+
111
+ jobs:
112
+ lint:
113
+ runs-on: ubuntu-latest
114
+ steps:
115
+ - uses: actions/checkout@v2
116
+ - uses: actions/setup-node@v2
117
+ - run: npm install
118
+ - run: npx git-branch-lint
119
+ ```
120
+
121
+ ## 🛣 Roadmap
122
+ | Task / Feature | Status |
123
+ |---------------|--------|
124
+ | - Add support for custom error messages | 🚧 In Progress |
125
+ | - Implement branch name suggestions when validation fails | 🚧 In Progress |
126
+ | - Add support for regex-based branch name validation | 🚧 In Progress |
127
+ | - Create GitHub Action for easier CI integration | 🚧 In Progress |
128
+ | - Add support for branch name templates | 🚧 In Progress |
129
+ | - Implement branch name statistics and reporting | 🚧 In Progress |
130
+ | (done) 🔍 Validates branch names against customizable patterns | 🚧 In Progress |
131
+ | (done) ⚡ Fast and lightweight with minimal dependencies | 🚧 In Progress |
132
+ | (done) 🛠 Flexible configuration through multiple formats (JS, JSON, YAML) | 🚧 In Progress |
133
+
134
+ ## ❓ FAQ
135
+ ### Why should I use Git Branch Lint?
136
+ Maintaining consistent branch naming conventions helps teams track work effectively and automate processes like deployment and versioning.
137
+
138
+ ### Can I use custom regular expressions?
139
+ Yes, you can define custom regex patterns in the configuration file's `params` section.
140
+
141
+ ### How do I skip validation for certain branches?
142
+ You can exclude branches by adding them to the `prohibited` list with a negative pattern.
143
+
144
+ ### Does it work with monorepos?
145
+ Yes, you can configure different patterns for different parts of your monorepo using workspace-specific configuration files.
146
+
147
+ ## 🔒 License
148
+ This project is licensed under **MIT License
149
+
150
+ Copyright (c) 2025 ElsiKora
151
+
152
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
153
+
154
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.**.
@@ -0,0 +1,19 @@
1
+ import type { IBranchConfig } from "../../domain/interfaces/branch-interfaces";
2
+ import type { IConfigRepository } from "../../domain/interfaces/repository-interfaces";
3
+ /**
4
+ * Use case for getting branch configuration
5
+ */
6
+ export declare class GetBranchConfigUseCase {
7
+ private readonly CONFIG_REPOSITORY;
8
+ /**
9
+ * Constructor
10
+ * @param configRepository The configuration repository
11
+ */
12
+ constructor(configRepository: IConfigRepository);
13
+ /**
14
+ * Execute the use case
15
+ * @param appName The application name
16
+ * @returns The branch configuration
17
+ */
18
+ execute(appName: string): Promise<IBranchConfig>;
19
+ }
@@ -0,0 +1,18 @@
1
+ import type { TBranchName } from "../../domain/interfaces/branch-interfaces";
2
+ import type { IBranchRepository } from "../../domain/interfaces/repository-interfaces";
3
+ /**
4
+ * Use case for getting the current branch name
5
+ */
6
+ export declare class GetCurrentBranchUseCase {
7
+ private readonly BRANCH_REPOSITORY;
8
+ /**
9
+ * Constructor
10
+ * @param branchRepository The branch repository
11
+ */
12
+ constructor(branchRepository: IBranchRepository);
13
+ /**
14
+ * Execute the use case
15
+ * @returns The current branch name
16
+ */
17
+ execute(): Promise<TBranchName>;
18
+ }
@@ -0,0 +1,23 @@
1
+ import type { IBranchConfig, TBranchName } from "../../domain/interfaces/branch-interfaces";
2
+ /**
3
+ * Use case for linting a branch name
4
+ */
5
+ export declare class LintBranchNameUseCase {
6
+ /**
7
+ * Lint a branch name against a configuration
8
+ * @param branchName The branch name to lint
9
+ * @param config The branch configuration
10
+ * @throws {ProhibitedBranchError} When branch name is prohibited
11
+ * @throws {PatternMatchError} When branch name doesn't match pattern
12
+ * @throws {BranchTooShortError} When branch name is shorter than the minimum length
13
+ * @throws {BranchTooLongError} When branch name is longer than the maximum length
14
+ */
15
+ execute(branchName: TBranchName, config: IBranchConfig): void;
16
+ /**
17
+ * Validate the branch name against the pattern
18
+ * @param branchName The branch name to validate
19
+ * @param config The branch configuration
20
+ * @throws {PatternMatchError} When branch name doesn't match pattern
21
+ */
22
+ private validatePattern;
23
+ }
@@ -0,0 +1,12 @@
1
+ import type { TBranchName } from "../interfaces/branch-interfaces";
2
+ /**
3
+ * Domain entity representing a Git branch
4
+ */
5
+ export declare class Branch {
6
+ private readonly VALUE;
7
+ constructor(name: TBranchName);
8
+ getName(): TBranchName;
9
+ isProhibited(prohibitedNames: ReadonlyArray<string>): boolean;
10
+ isTooLong(maxLength?: number): boolean;
11
+ isTooShort(minLength?: number): boolean;
12
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Base error class for branch linting errors
3
+ */
4
+ export declare class LintError extends Error {
5
+ constructor(message: string);
6
+ }
7
+ /**
8
+ * Error thrown when branch name is too long
9
+ */
10
+ export declare class BranchTooLongError extends LintError {
11
+ constructor(branchName: string, maxLength: number);
12
+ }
13
+ /**
14
+ * Error thrown when branch name is too short
15
+ */
16
+ export declare class BranchTooShortError extends LintError {
17
+ constructor(branchName: string, minLength: number);
18
+ }
19
+ /**
20
+ * Error thrown when branch name doesn't match the pattern
21
+ */
22
+ export declare class PatternMatchError extends LintError {
23
+ constructor(branchName: string);
24
+ }
25
+ /**
26
+ * Error thrown when branch name is prohibited
27
+ */
28
+ export declare class ProhibitedBranchError extends LintError {
29
+ constructor(branchName: string);
30
+ }
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Interface for branch configuration
3
+ */
4
+ export interface IBranchConfig {
5
+ readonly MAXLENGTH?: number;
6
+ readonly MINLENGTH?: number;
7
+ PARAMS: IBranchParameters;
8
+ readonly PATTERN: string;
9
+ readonly PROHIBITED: ReadonlyArray<string>;
10
+ }
11
+ /**
12
+ * Interface for branch parameters
13
+ */
14
+ export interface IBranchParameters {
15
+ readonly NAME: ReadonlyArray<string>;
16
+ readonly TYPE: ReadonlyArray<string>;
17
+ }
18
+ /**
19
+ * Type definition for a branch name
20
+ * We explicitly keep this type for semantic clarity
21
+ */
22
+ export type TBranchName = string;
@@ -0,0 +1,10 @@
1
+ import type { TBranchName } from "../branch-interfaces";
2
+ /**
3
+ * Repository interface for branch operations
4
+ */
5
+ export interface IBranchRepository {
6
+ /**
7
+ * Get the current branch name
8
+ */
9
+ getCurrentBranchName(): Promise<TBranchName>;
10
+ }
@@ -0,0 +1,11 @@
1
+ import type { IBranchConfig } from "../branch-interfaces";
2
+ /**
3
+ * Repository interface for configuration operations
4
+ */
5
+ export interface IConfigRepository {
6
+ /**
7
+ * Get the branch configuration
8
+ * @param appName The name of the application
9
+ */
10
+ getConfig(appName: string): Promise<IBranchConfig>;
11
+ }
@@ -0,0 +1,2 @@
1
+ export type { IBranchRepository } from "./repositories/ibranch-repository";
2
+ export type { IConfigRepository } from "./repositories/iconfig-repository";
@@ -0,0 +1,11 @@
1
+ import type { TBranchName } from "../interfaces/branch-interfaces";
2
+ /**
3
+ * Repository interface for branch operations
4
+ * @deprecated Use IBranchRepository instead
5
+ */
6
+ export interface BranchRepository {
7
+ /**
8
+ * Get the current branch name
9
+ */
10
+ getCurrentBranchName(): Promise<TBranchName>;
11
+ }
@@ -0,0 +1,12 @@
1
+ import type { IBranchConfig } from "../interfaces/branch-interfaces";
2
+ /**
3
+ * Repository interface for configuration operations
4
+ * @deprecated Use IConfigRepository instead
5
+ */
6
+ export interface ConfigRepository {
7
+ /**
8
+ * Get the branch configuration
9
+ * @param appName The name of the application
10
+ */
11
+ getConfig(appName: string): Promise<IBranchConfig>;
12
+ }
package/bin/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/bin/index.js ADDED
@@ -0,0 +1,446 @@
1
+ #!/usr/bin/env node
2
+ import { cosmiconfig } from 'cosmiconfig';
3
+ import { exec } from 'node:child_process';
4
+ import { promisify } from 'node:util';
5
+ import chalk from 'chalk';
6
+
7
+ /**
8
+ * Use case for getting branch configuration
9
+ */
10
+ class GetBranchConfigUseCase {
11
+ CONFIG_REPOSITORY;
12
+ /**
13
+ * Constructor
14
+ * @param configRepository The configuration repository
15
+ */
16
+ constructor(configRepository) {
17
+ this.CONFIG_REPOSITORY = configRepository;
18
+ }
19
+ /**
20
+ * Execute the use case
21
+ * @param appName The application name
22
+ * @returns The branch configuration
23
+ */
24
+ async execute(appName) {
25
+ return this.CONFIG_REPOSITORY.getConfig(appName);
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Use case for getting the current branch name
31
+ */
32
+ class GetCurrentBranchUseCase {
33
+ BRANCH_REPOSITORY;
34
+ /**
35
+ * Constructor
36
+ * @param branchRepository The branch repository
37
+ */
38
+ constructor(branchRepository) {
39
+ this.BRANCH_REPOSITORY = branchRepository;
40
+ }
41
+ /**
42
+ * Execute the use case
43
+ * @returns The current branch name
44
+ */
45
+ async execute() {
46
+ return this.BRANCH_REPOSITORY.getCurrentBranchName();
47
+ }
48
+ }
49
+
50
+ /**
51
+ * Domain entity representing a Git branch
52
+ */
53
+ class Branch {
54
+ VALUE;
55
+ constructor(name) {
56
+ this.VALUE = name;
57
+ }
58
+ getName() {
59
+ return this.VALUE;
60
+ }
61
+ isProhibited(prohibitedNames) {
62
+ return prohibitedNames.includes(this.VALUE);
63
+ }
64
+ isTooLong(maxLength) {
65
+ if (maxLength === undefined) {
66
+ return false;
67
+ }
68
+ return this.VALUE.length > maxLength;
69
+ }
70
+ isTooShort(minLength) {
71
+ if (minLength === undefined) {
72
+ return false;
73
+ }
74
+ return this.VALUE.length < minLength;
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Base error class for branch linting errors
80
+ */
81
+ class LintError extends Error {
82
+ constructor(message) {
83
+ super(message);
84
+ this.name = "LintError";
85
+ }
86
+ }
87
+ /**
88
+ * Error thrown when branch name is too long
89
+ */
90
+ class BranchTooLongError extends LintError {
91
+ constructor(branchName, maxLength) {
92
+ // eslint-disable-next-line @elsikora-typescript/restrict-template-expressions
93
+ super(`Branch name "${branchName}" is too long (maximum length: ${maxLength})`);
94
+ this.name = "BranchTooLongError";
95
+ }
96
+ }
97
+ /**
98
+ * Error thrown when branch name is too short
99
+ */
100
+ class BranchTooShortError extends LintError {
101
+ constructor(branchName, minLength) {
102
+ // eslint-disable-next-line @elsikora-typescript/restrict-template-expressions
103
+ super(`Branch name "${branchName}" is too short (minimum length: ${minLength})`);
104
+ this.name = "BranchTooShortError";
105
+ }
106
+ }
107
+ /**
108
+ * Error thrown when branch name doesn't match the pattern
109
+ */
110
+ class PatternMatchError extends LintError {
111
+ constructor(branchName) {
112
+ super(`Branch name "${branchName}" doesn't match pattern`);
113
+ this.name = "PatternMatchError";
114
+ }
115
+ }
116
+ /**
117
+ * Error thrown when branch name is prohibited
118
+ */
119
+ class ProhibitedBranchError extends LintError {
120
+ constructor(branchName) {
121
+ super(`Branch name "${branchName}" is prohibited`);
122
+ this.name = "ProhibitedBranchError";
123
+ }
124
+ }
125
+
126
+ /**
127
+ * Use case for linting a branch name
128
+ */
129
+ class LintBranchNameUseCase {
130
+ /**
131
+ * Lint a branch name against a configuration
132
+ * @param branchName The branch name to lint
133
+ * @param config The branch configuration
134
+ * @throws {ProhibitedBranchError} When branch name is prohibited
135
+ * @throws {PatternMatchError} When branch name doesn't match pattern
136
+ * @throws {BranchTooShortError} When branch name is shorter than the minimum length
137
+ * @throws {BranchTooLongError} When branch name is longer than the maximum length
138
+ */
139
+ execute(branchName, config) {
140
+ const branch = new Branch(branchName);
141
+ if (branch.isProhibited(config.PROHIBITED)) {
142
+ throw new ProhibitedBranchError(branchName);
143
+ }
144
+ // @ts-ignore
145
+ if (branch.isTooShort(config.MINLENGTH)) {
146
+ // @ts-ignore
147
+ throw new BranchTooShortError(branchName, config.MINLENGTH);
148
+ }
149
+ // @ts-ignore
150
+ if (branch.isTooLong(config.MAXLENGTH)) {
151
+ // @ts-ignore
152
+ throw new BranchTooLongError(branchName, config.MAXLENGTH);
153
+ }
154
+ this.validatePattern(branch.getName(), config);
155
+ }
156
+ /**
157
+ * Validate the branch name against the pattern
158
+ * @param branchName The branch name to validate
159
+ * @param config The branch configuration
160
+ * @throws {PatternMatchError} When branch name doesn't match pattern
161
+ */
162
+ validatePattern(branchName, config) {
163
+ // Start with original pattern
164
+ let pattern = config.PATTERN;
165
+ // Process each parameter in the configuration
166
+ for (const [key, values] of Object.entries(config.PARAMS)) {
167
+ // Create the placeholder - IMPORTANT: Convert to lowercase to match pattern
168
+ const placeholder = `:${key.toLowerCase()}`;
169
+ const parameterValues = values;
170
+ let replacement = "(";
171
+ for (let index = 0; index < parameterValues.length; index++) {
172
+ const value = parameterValues[index];
173
+ // Add the value to the replacement pattern
174
+ if (value.startsWith("[")) {
175
+ replacement += value;
176
+ }
177
+ else {
178
+ const escapedValue = value.replaceAll(/[-/\\^$*+?.()|[\]{}]/g, String.raw `\$&`);
179
+ replacement += escapedValue;
180
+ }
181
+ // Add OR separator if not the last value
182
+ if (index < parameterValues.length - 1) {
183
+ replacement += "|";
184
+ }
185
+ }
186
+ replacement += ")";
187
+ // Replace the placeholder in the pattern
188
+ pattern = pattern.replaceAll(new RegExp(placeholder, "g"), replacement);
189
+ }
190
+ // Create the regular expression
191
+ const regexp = new RegExp(`^${pattern}$`);
192
+ // Test the branch name against the pattern
193
+ if (!regexp.test(branchName)) {
194
+ throw new PatternMatchError(branchName);
195
+ }
196
+ }
197
+ }
198
+
199
+ /**
200
+ * Default configuration for branch linting
201
+ */
202
+ const DEFAULT_CONFIG = {
203
+ // eslint-disable-next-line @elsikora-typescript/no-magic-numbers
204
+ MAXLENGTH: 50,
205
+ // eslint-disable-next-line @elsikora-typescript/no-magic-numbers
206
+ MINLENGTH: 5,
207
+ PARAMS: {
208
+ NAME: ["[a-z0-9-]+"],
209
+ TYPE: ["feature", "hotfix", "support", "bugfix", "release"],
210
+ },
211
+ PATTERN: ":type/:name",
212
+ PROHIBITED: ["ci", "wip", "main", "test", "build", "master", "release", "dev", "develop"],
213
+ };
214
+ /**
215
+ * Cosmiconfig implementation of ConfigRepository
216
+ */
217
+ class CosmiconfigRepository {
218
+ /**
219
+ * Get the branch configuration
220
+ * @param appName The name of the application
221
+ * @returns A promise that resolves to the branch configuration
222
+ */
223
+ async getConfig(appName) {
224
+ const configExplorer = cosmiconfig(appName, {
225
+ packageProp: `elsikora.${appName}`,
226
+ searchPlaces: ["package.json", `.elsikora/.${appName}rc`, `.elsikora/.${appName}rc.json`, `.elsikora/.${appName}rc.yaml`, `.elsikora/.${appName}rc.yml`, `.elsikora/.${appName}rc.js`, `.elsikora/.${appName}rc.ts`, `.elsikora/.${appName}rc.mjs`, `.elsikora/.${appName}rc.cjs`, `.elsikora/${appName}.config.js`, `.elsikora/${appName}.config.ts`, `.elsikora/${appName}.config.mjs`, `.elsikora/${appName}.config.cjs`],
227
+ });
228
+ const result = await configExplorer.search();
229
+ if (!result || result.isEmpty) {
230
+ return DEFAULT_CONFIG;
231
+ }
232
+ // Convert the config to match our interfaces
233
+ const providedConfig = result.config;
234
+ const uppercasedConfig = {};
235
+ // Create a new object with uppercase keys instead of modifying the original
236
+ for (const key of Object.keys(providedConfig)) {
237
+ const uppercaseKey = key.toUpperCase();
238
+ const value = providedConfig[key];
239
+ if (Array.isArray(value)) {
240
+ // Preserve arrays
241
+ // eslint-disable-next-line @elsikora-typescript/no-unsafe-assignment
242
+ uppercasedConfig[uppercaseKey] = [...value];
243
+ }
244
+ else if (typeof value === "object" && value !== null) {
245
+ // Handle nested objects
246
+ const nestedObject = {};
247
+ for (const subKey of Object.keys(value)) {
248
+ const subValue = value[subKey];
249
+ if (Array.isArray(subValue)) {
250
+ // Preserve nested arrays
251
+ // eslint-disable-next-line @elsikora-typescript/no-unsafe-assignment
252
+ nestedObject[subKey.toUpperCase()] = [...subValue];
253
+ }
254
+ else {
255
+ nestedObject[subKey.toUpperCase()] = subValue;
256
+ }
257
+ }
258
+ uppercasedConfig[uppercaseKey] = nestedObject;
259
+ }
260
+ else {
261
+ // Handle primitive values
262
+ uppercasedConfig[uppercaseKey] = value;
263
+ }
264
+ }
265
+ const mergedConfig = {
266
+ ...DEFAULT_CONFIG,
267
+ ...uppercasedConfig,
268
+ };
269
+ if (uppercasedConfig.PARAMS && DEFAULT_CONFIG.PARAMS) {
270
+ mergedConfig.PARAMS = {
271
+ ...DEFAULT_CONFIG.PARAMS,
272
+ ...uppercasedConfig.PARAMS,
273
+ };
274
+ }
275
+ return mergedConfig;
276
+ }
277
+ }
278
+
279
+ const execAsync = promisify(exec);
280
+ /**
281
+ * Git implementation of BranchRepository
282
+ */
283
+ class GitBranchRepository {
284
+ /**
285
+ * Get the current branch name
286
+ * @returns A promise that resolves to the current branch name
287
+ */
288
+ async getCurrentBranchName() {
289
+ const command = "git rev-parse --abbrev-ref HEAD";
290
+ const { stdout } = await execAsync(command);
291
+ return stdout.trim();
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Format error messages for CLI output
297
+ */
298
+ class ErrorFormatter {
299
+ /**
300
+ * Format an error message
301
+ * @param message The error message
302
+ * @returns The formatted error message
303
+ */
304
+ format(message) {
305
+ return chalk.red(`✖ ${message}`);
306
+ }
307
+ }
308
+
309
+ /**
310
+ * Format hint messages for CLI output
311
+ */
312
+ class HintFormatter {
313
+ /**
314
+ * Format a hint message based on error type and config
315
+ * @param error The error that occurred
316
+ * @param config The branch configuration
317
+ * @returns The formatted hint message
318
+ */
319
+ format(error, config) {
320
+ let output = "";
321
+ if (error instanceof PatternMatchError) {
322
+ output += this.formatPatternMatchHint(config);
323
+ }
324
+ else if (error instanceof ProhibitedBranchError) {
325
+ output += this.formatProhibitedBranchHint(config);
326
+ }
327
+ return output;
328
+ }
329
+ /**
330
+ * Format a hint for pattern match errors
331
+ * @param config The branch configuration
332
+ * @returns The formatted hint
333
+ */
334
+ formatPatternMatchHint(config) {
335
+ let output = "";
336
+ output += `${chalk.blue("Expected pattern:")} ${chalk.yellow(config.PATTERN)}\n`;
337
+ // Format the parameters
338
+ for (const [parameterName, parameterValues] of Object.entries(config.PARAMS)) {
339
+ const valuesList = parameterValues.map((value) => chalk.yellow(value)).join(", ");
340
+ output += chalk.blue(`Valid ${parameterName} values:`) + " " + valuesList + "\n";
341
+ }
342
+ return output.trim();
343
+ }
344
+ /**
345
+ * Format a hint for prohibited branch errors
346
+ * @param config The branch configuration
347
+ * @returns The formatted hint
348
+ */
349
+ formatProhibitedBranchHint(config) {
350
+ const prohibitedList = config.PROHIBITED.map((name) => chalk.yellow(name)).join(", ");
351
+ return `${chalk.blue("Prohibited branch names:")} ${prohibitedList}`;
352
+ }
353
+ }
354
+
355
+ /**
356
+ * Controller for CLI operations
357
+ */
358
+ class CliController {
359
+ ERROR_FORMATTER;
360
+ GET_BRANCH_CONFIG_USE_CASE;
361
+ GET_CURRENT_BRANCH_USE_CASE;
362
+ HINT_FORMATTER;
363
+ LINT_BRANCH_NAME_USE_CASE;
364
+ /**
365
+ * Constructor
366
+ * @param getBranchConfigUseCase The use case for getting branch configuration
367
+ * @param getCurrentBranchUseCase The use case for getting the current branch
368
+ * @param lintBranchNameUseCase The use case for linting branch names
369
+ */
370
+ constructor(getBranchConfigUseCase, getCurrentBranchUseCase, lintBranchNameUseCase) {
371
+ this.GET_BRANCH_CONFIG_USE_CASE = getBranchConfigUseCase;
372
+ this.GET_CURRENT_BRANCH_USE_CASE = getCurrentBranchUseCase;
373
+ this.LINT_BRANCH_NAME_USE_CASE = lintBranchNameUseCase;
374
+ this.ERROR_FORMATTER = new ErrorFormatter();
375
+ this.HINT_FORMATTER = new HintFormatter();
376
+ }
377
+ /**
378
+ * Execute the CLI command
379
+ * @param appName The application name
380
+ */
381
+ async execute(appName) {
382
+ try {
383
+ const [config, branchName] = await Promise.all([this.GET_BRANCH_CONFIG_USE_CASE.execute(appName), this.GET_CURRENT_BRANCH_USE_CASE.execute()]);
384
+ this.LINT_BRANCH_NAME_USE_CASE.execute(branchName, config);
385
+ }
386
+ catch (error) {
387
+ await this.handleError(error);
388
+ }
389
+ }
390
+ /**
391
+ * Handle errors that occur during execution
392
+ * @param error The error that occurred
393
+ */
394
+ async handleError(error) {
395
+ if (!(error instanceof Error)) {
396
+ console.error(this.ERROR_FORMATTER.format("[LintBranchName] Unhandled error occurred"));
397
+ throw new Error("Unknown error occurred");
398
+ }
399
+ if (error instanceof LintError) {
400
+ try {
401
+ // Get the configuration using the service instead of hardcoded values
402
+ const config = await this.GET_BRANCH_CONFIG_USE_CASE.execute("git-branch-lint");
403
+ console.error(this.ERROR_FORMATTER.format(error.message));
404
+ console.log(this.HINT_FORMATTER.format(error, config));
405
+ // Since this is a CLI tool, it's appropriate to exit the process on validation errors
406
+ // ESLint wants us to throw instead, but this is a CLI application where exit is acceptable
407
+ // eslint-disable-next-line elsikora-node/no-process-exit, @elsikora-unicorn/no-process-exit
408
+ process.exit(1);
409
+ }
410
+ catch {
411
+ console.error(this.ERROR_FORMATTER.format("Failed to load configuration for error hint"));
412
+ console.error(this.ERROR_FORMATTER.format(error.message));
413
+ // eslint-disable-next-line elsikora-node/no-process-exit, @elsikora-unicorn/no-process-exit
414
+ process.exit(1);
415
+ }
416
+ }
417
+ throw error;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Application name used for configuration
423
+ */
424
+ const APP_NAME = "git-branch-lint";
425
+ /**
426
+ * Main function that bootstraps the application
427
+ */
428
+ const main = async () => {
429
+ // Infrastructure layer
430
+ const configRepository = new CosmiconfigRepository();
431
+ const branchRepository = new GitBranchRepository();
432
+ // Application layer
433
+ const getBranchConfigUseCase = new GetBranchConfigUseCase(configRepository);
434
+ const getCurrentBranchUseCase = new GetCurrentBranchUseCase(branchRepository);
435
+ const lintBranchNameUseCase = new LintBranchNameUseCase();
436
+ // Presentation layer
437
+ const cliController = new CliController(getBranchConfigUseCase, getCurrentBranchUseCase, lintBranchNameUseCase);
438
+ // Execute the application
439
+ await cliController.execute(APP_NAME);
440
+ };
441
+ // Bootstrap the application and handle errors
442
+ main().catch((error) => {
443
+ console.error("[LintBranchName] Unhandled error occurred");
444
+ throw error;
445
+ });
446
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/application/use-cases/get-branch-config-use-case.ts","../src/application/use-cases/get-current-branch-use-case.ts","../src/domain/entities/branch.ts","../src/domain/errors/lint-errors.ts","../src/application/use-cases/lint-branch-name-use-case.ts","../src/infrastructure/config/cosmiconfig-repository.ts","../src/infrastructure/git/git-branch-repository.ts","../src/presentation/cli/formatters/error-formatter.ts","../src/presentation/cli/formatters/hint-formatter.ts","../src/presentation/cli/cli-controller.ts","../src/index.ts"],"sourcesContent":[null,null,null,null,null,null,null,null,null,null,null],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { IBranchConfig } from "../../domain/interfaces/branch-interfaces";
2
+ import type { IConfigRepository } from "../../domain/interfaces/repository-interfaces";
3
+ /**
4
+ * Cosmiconfig implementation of ConfigRepository
5
+ */
6
+ export declare class CosmiconfigRepository implements IConfigRepository {
7
+ /**
8
+ * Get the branch configuration
9
+ * @param appName The name of the application
10
+ * @returns A promise that resolves to the branch configuration
11
+ */
12
+ getConfig(appName: string): Promise<IBranchConfig>;
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { TBranchName } from "../../domain/interfaces/branch-interfaces";
2
+ import type { IBranchRepository } from "../../domain/interfaces/repository-interfaces";
3
+ /**
4
+ * Git implementation of BranchRepository
5
+ */
6
+ export declare class GitBranchRepository implements IBranchRepository {
7
+ /**
8
+ * Get the current branch name
9
+ * @returns A promise that resolves to the current branch name
10
+ */
11
+ getCurrentBranchName(): Promise<TBranchName>;
12
+ }
@@ -0,0 +1,30 @@
1
+ import type { GetBranchConfigUseCase } from "../../application/use-cases/get-branch-config-use-case";
2
+ import type { GetCurrentBranchUseCase } from "../../application/use-cases/get-current-branch-use-case";
3
+ import type { LintBranchNameUseCase } from "../../application/use-cases/lint-branch-name-use-case";
4
+ /**
5
+ * Controller for CLI operations
6
+ */
7
+ export declare class CliController {
8
+ private readonly ERROR_FORMATTER;
9
+ private readonly GET_BRANCH_CONFIG_USE_CASE;
10
+ private readonly GET_CURRENT_BRANCH_USE_CASE;
11
+ private readonly HINT_FORMATTER;
12
+ private readonly LINT_BRANCH_NAME_USE_CASE;
13
+ /**
14
+ * Constructor
15
+ * @param getBranchConfigUseCase The use case for getting branch configuration
16
+ * @param getCurrentBranchUseCase The use case for getting the current branch
17
+ * @param lintBranchNameUseCase The use case for linting branch names
18
+ */
19
+ constructor(getBranchConfigUseCase: GetBranchConfigUseCase, getCurrentBranchUseCase: GetCurrentBranchUseCase, lintBranchNameUseCase: LintBranchNameUseCase);
20
+ /**
21
+ * Execute the CLI command
22
+ * @param appName The application name
23
+ */
24
+ execute(appName: string): Promise<void>;
25
+ /**
26
+ * Handle errors that occur during execution
27
+ * @param error The error that occurred
28
+ */
29
+ private handleError;
30
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Format error messages for CLI output
3
+ */
4
+ export declare class ErrorFormatter {
5
+ /**
6
+ * Format an error message
7
+ * @param message The error message
8
+ * @returns The formatted error message
9
+ */
10
+ format(message: string): string;
11
+ }
@@ -0,0 +1,25 @@
1
+ import type { IBranchConfig } from "../../../domain/interfaces/branch-interfaces";
2
+ /**
3
+ * Format hint messages for CLI output
4
+ */
5
+ export declare class HintFormatter {
6
+ /**
7
+ * Format a hint message based on error type and config
8
+ * @param error The error that occurred
9
+ * @param config The branch configuration
10
+ * @returns The formatted hint message
11
+ */
12
+ format(error: unknown, config: IBranchConfig): string;
13
+ /**
14
+ * Format a hint for pattern match errors
15
+ * @param config The branch configuration
16
+ * @returns The formatted hint
17
+ */
18
+ private formatPatternMatchHint;
19
+ /**
20
+ * Format a hint for prohibited branch errors
21
+ * @param config The branch configuration
22
+ * @returns The formatted hint
23
+ */
24
+ private formatProhibitedBranchHint;
25
+ }
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@elsikora/git-branch-lint",
3
+ "version": "1.0.0",
4
+ "description": "Lint your git branch names",
5
+ "keywords": [
6
+ "lint",
7
+ "validate",
8
+ "branch",
9
+ "name",
10
+ "git",
11
+ "git-branch",
12
+ "branch-name"
13
+ ],
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/ElsiKora/Git-Branch-Lint.git"
17
+ },
18
+ "license": "MIT",
19
+ "author": "ElsiKora",
20
+ "type": "module",
21
+ "main": "bin/index.js",
22
+ "bin": {
23
+ "branch-name-lint": "./bin/index.js"
24
+ },
25
+ "files": [
26
+ "bin"
27
+ ],
28
+ "scripts": {
29
+ "prebuild": "rimraf dist",
30
+ "build": "npm run prebuild && rollup -c",
31
+ "commit": "cz",
32
+ "format": "prettier --check .",
33
+ "format:fix": "prettier --write .",
34
+ "lint": "eslint ./",
35
+ "lint:all": "npm run lint && npm run lint:types",
36
+ "lint:all:fix": "npm run lint:fix && npm run lint:types:fix",
37
+ "lint:fix": "eslint --fix ./",
38
+ "lint:types": "tsc --noEmit",
39
+ "lint:types:fix": "tsc --noEmit --skipLibCheck",
40
+ "prepare": "husky",
41
+ "release": "semantic-release"
42
+ },
43
+ "config": {
44
+ "commitizen": {
45
+ "path": "@elsikora/commitizen-plugin-commitlint-ai"
46
+ }
47
+ },
48
+ "dependencies": {
49
+ "chalk": "^5.4.1",
50
+ "cosmiconfig": "^9.0.0",
51
+ "path-to-regexp": "^8.2.0"
52
+ },
53
+ "devDependencies": {
54
+ "@commitlint/cli": "^19.7.1",
55
+ "@commitlint/config-conventional": "^19.7.1",
56
+ "@elsikora/commitizen-plugin-commitlint-ai": "^1.0.0",
57
+ "@elsikora/eslint-config": "^3.3.4",
58
+ "@rollup/plugin-typescript": "^12.1.2",
59
+ "@saithodev/semantic-release-backmerge": "^4.0.1",
60
+ "@semantic-release/changelog": "^6.0.3",
61
+ "@semantic-release/commit-analyzer": "^13.0.1",
62
+ "@semantic-release/git": "^10.0.1",
63
+ "@semantic-release/github": "^11.0.1",
64
+ "@semantic-release/npm": "^12.0.1",
65
+ "@semantic-release/release-notes-generator": "^14.0.3",
66
+ "@types/node": "^22.13.8",
67
+ "commitizen": "^4.3.1",
68
+ "conventional-changelog-conventionalcommits": "^8.0.0",
69
+ "eslint": "^9.21.0",
70
+ "eslint-plugin-n": "^17.16.1",
71
+ "husky": "^9.1.7",
72
+ "lint-staged": "^15.4.3",
73
+ "prettier": "^3.5.3",
74
+ "rimraf": "^6.0.1",
75
+ "rollup": "^4.34.9",
76
+ "semantic-release": "^24.2.3",
77
+ "tslib": "^2.8.1",
78
+ "typescript": "^5.7.3"
79
+ },
80
+ "publishConfig": {
81
+ "access": "public"
82
+ }
83
+ }