@gracefullight/validate-branch 1.1.0 → 1.1.2

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/index.js CHANGED
@@ -1,7 +1,50 @@
1
+ // src/load-config.ts
2
+ import { readFile } from "fs/promises";
3
+ import { resolve } from "path";
4
+ import { cwd } from "process";
5
+ import { pathToFileURL } from "url";
6
+ var CONFIG_FILES = [
7
+ "branch.config.ts",
8
+ "branch.config.mts",
9
+ "branch.config.js",
10
+ "branch.config.cjs",
11
+ "branch.config.mjs"
12
+ ];
13
+ async function loadConfig() {
14
+ for (const file of CONFIG_FILES) {
15
+ const configPath = resolve(cwd(), file);
16
+ try {
17
+ await readFile(configPath, "utf-8");
18
+ if (file.endsWith(".ts") || file.endsWith(".mts") || file.endsWith(".mjs")) {
19
+ const fileUrl = pathToFileURL(configPath);
20
+ fileUrl.searchParams.set("ts", Date.now().toString());
21
+ const { default: config } = await import(
22
+ /* @vite-ignore */
23
+ fileUrl.href
24
+ );
25
+ return config;
26
+ }
27
+ if (file.endsWith(".js") || file.endsWith(".cjs")) {
28
+ const fileUrl = pathToFileURL(configPath);
29
+ const { default: config } = await import(
30
+ /* @vite-ignore */
31
+ fileUrl.href
32
+ );
33
+ return config;
34
+ }
35
+ } catch (error) {
36
+ if (error.code !== "ENOENT") {
37
+ console.error(`Error loading config file ${file}:`, error);
38
+ }
39
+ }
40
+ }
41
+ return null;
42
+ }
43
+
1
44
  // src/validate-branch-name.ts
2
- import * as git from "isomorphic-git";
3
45
  import * as fs from "fs";
4
- import { cwd } from "process";
46
+ import { cwd as cwd2 } from "process";
47
+ import * as git from "isomorphic-git";
5
48
  var GITFLOW_PATTERN = /^(main|master|develop|stage|feature\/[A-Za-z0-9_-]+|fix\/[A-Za-z0-9_-]+|hotfix\/[A-Za-z0-9_-]+|release\/[A-Za-z0-9_.-]+)$/;
6
49
  var JIRA_PATTERN = /^(main|master|develop|stage|[A-Z]+-[0-9]+)$/;
7
50
  function validateBranchName(branchName, options) {
@@ -16,7 +59,7 @@ async function getCurrentBranchName() {
16
59
  try {
17
60
  const branch = await git.currentBranch({
18
61
  fs,
19
- dir: cwd(),
62
+ dir: cwd2(),
20
63
  fullname: false
21
64
  });
22
65
  return branch ?? null;
@@ -47,43 +90,6 @@ function validateWithDetails(branchName, options) {
47
90
  error: `Branch name "${branchName}" does not match pattern: ${pattern.source}`
48
91
  };
49
92
  }
50
-
51
- // src/load-config.ts
52
- import { readFile } from "fs/promises";
53
- import { resolve } from "path";
54
- import { cwd as cwd2 } from "process";
55
- var CONFIG_FILES = [
56
- "branch.config.ts",
57
- "branch.config.mts",
58
- "branch.config.js",
59
- "branch.config.cjs",
60
- "branch.config.mjs"
61
- ];
62
- async function loadConfig() {
63
- for (const file of CONFIG_FILES) {
64
- const configPath = resolve(cwd2(), file);
65
- try {
66
- await readFile(configPath, "utf-8");
67
- if (file.endsWith(".ts") || file.endsWith(".mts")) {
68
- const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
69
- return config;
70
- }
71
- if (file.endsWith(".mjs")) {
72
- const { default: config } = await import(`file://${configPath}?ts=${Date.now()}`);
73
- return config;
74
- }
75
- if (file.endsWith(".js") || file.endsWith(".cjs")) {
76
- const { default: config } = await import(configPath);
77
- return config;
78
- }
79
- } catch (error) {
80
- if (error.code !== "ENOENT") {
81
- console.error(`Error loading config file ${file}:`, error);
82
- }
83
- }
84
- }
85
- return null;
86
- }
87
93
  export {
88
94
  getCurrentBranchName,
89
95
  loadConfig,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gracefullight/validate-branch",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "Git branch name validation tool with custom regexp support",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -23,7 +23,16 @@
23
23
  "git",
24
24
  "branch",
25
25
  "validation",
26
- "cli"
26
+ "cli",
27
+ "git-hooks",
28
+ "lint",
29
+ "naming-convention",
30
+ "workflow",
31
+ "ci",
32
+ "gitflow",
33
+ "husky",
34
+ "standard",
35
+ "quality-control"
27
36
  ],
28
37
  "author": {
29
38
  "name": "Eunkwang Shin",
@@ -31,12 +40,12 @@
31
40
  },
32
41
  "repository": {
33
42
  "type": "git",
34
- "url": "https://github.com/gracefullight/ts-workspace.git",
43
+ "url": "https://github.com/gracefullight/pkgs.git",
35
44
  "directory": "packages/validate-branch"
36
45
  },
37
- "homepage": "https://github.com/gracefullight/ts-workspace/tree/main/packages/validate-branch#readme",
46
+ "homepage": "https://github.com/gracefullight/pkgs/tree/main/packages/validate-branch#readme",
38
47
  "bugs": {
39
- "url": "https://github.com/gracefullight/ts-workspace/issues"
48
+ "url": "https://github.com/gracefullight/pkgs/issues"
40
49
  },
41
50
  "license": "MIT",
42
51
  "funding": {
@@ -47,16 +56,16 @@
47
56
  "access": "public"
48
57
  },
49
58
  "devDependencies": {
50
- "@types/node": "^24.10.4",
59
+ "@types/node": "^24.10.9",
51
60
  "tsup": "^8.5.1",
52
- "tsx": "^4.19.2",
53
- "typescript": "^5.7.3",
54
- "vitest": "^4.0.7"
61
+ "tsx": "^4.21.0",
62
+ "typescript": "^5",
63
+ "vitest": "^4.0.17"
55
64
  },
56
65
  "dependencies": {
57
- "chalk": "^5.4.1",
58
- "commander": "^12.1.0",
59
- "isomorphic-git": "^1.30.1"
66
+ "chalk": "^5.6.2",
67
+ "commander": "^14.0.2",
68
+ "isomorphic-git": "^1.36.1"
60
69
  },
61
70
  "scripts": {
62
71
  "build": "tsup",
package/README.en.md DELETED
@@ -1,470 +0,0 @@
1
- # @gracefullight/validate-branch
2
-
3
- > Git branch name validation tool with custom regexp support
4
-
5
- [![npm version](https://img.shields.io/npm/v/@gracefullight/validate-branch.svg)](https://www.npmjs.com/package/@gracefullight/validate-branch)
6
- [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
-
8
- [한국어](./README.md) | **English**
9
-
10
- ## Features
11
-
12
- - **Flexible Regular Expression Pattern** - Use default pattern or custom regular expression
13
- - **Current Branch Detection** - Automatically detect current branch name from Git repository
14
- - **Config File Support** - Load patterns from `branch.config.{ts,js,mjs}` files
15
- - **CLI Tool** - Use directly from command line
16
- - **Detailed Error Messages** - Clear error descriptions on validation failure
17
- - **Full TypeScript Support** - Complete TypeScript type definitions
18
-
19
- ## Default Pattern
20
-
21
- By default, the following branch names are allowed:
22
-
23
- - `main` / `master` - Main branches
24
- - `develop` / `stage` - Development/staging branches
25
- - `feature/.*` - Feature branches (e.g., `feature/login`, `feature/user-auth`)
26
- - `fix/.*` - Bug fix branches (e.g., `fix/memory-leak`, `fix/typo`)
27
- - `hotfix/.*` - Hotfix branches (e.g., `hotfix/critical-bug`)
28
- - `release/.*` - Release branches (e.g., `release/v1.0.0`)
29
-
30
- ## Installation
31
-
32
- ```bash
33
- # Using pnpm
34
- pnpm add @gracefullight/validate-branch
35
-
36
- # Using npm
37
- npm install @gracefullight/validate-branch
38
-
39
- # Using yarn
40
- yarn add @gracefullight/validate-branch
41
- ```
42
-
43
- ## Usage
44
-
45
- ### CLI Usage
46
-
47
- ```bash
48
- # Validate current branch
49
- npx validate-branch
50
-
51
- # Validate specific branch
52
- npx validate-branch --test feature/new-feature
53
-
54
- # Use custom regular expression
55
- npx validate-branch --regexp "^(main|develop|feature/.+)$"
56
- ```
57
-
58
- ### Programmatic Usage
59
-
60
- #### Validate with Default Pattern
61
-
62
- ```typescript
63
- import { validateBranchName, validateWithDetails } from "@gracefullight/validate-branch";
64
-
65
- // Simple boolean return
66
- const isValid = validateBranchName("feature/new-component");
67
- console.log(isValid); // true
68
-
69
- // Detailed result return
70
- const result = validateWithDetails("feature/new-component");
71
- console.log(result);
72
- // { valid: true, branchName: "feature/new-component" }
73
- ```
74
-
75
- #### Validate with Custom Regexp
76
-
77
- ```typescript
78
- import { validateBranchName } from "@gracefullight/validate-branch";
79
-
80
- const customPattern = "^(main|develop|story/.+|bugfix/.+)$";
81
- const isValid = validateBranchName("story/US-123-add-user", { customRegexp: customPattern });
82
- console.log(isValid); // true
83
- ```
84
-
85
- #### Get Current Branch Name
86
-
87
- ```typescript
88
- import { getCurrentBranchName } from "@gracefullight/validate-branch";
89
-
90
- const currentBranch = await getCurrentBranchName();
91
- console.log(currentBranch); // "feature/new-component" or null
92
- ```
93
-
94
- #### Load Config File
95
-
96
- ```typescript
97
- import { loadConfig } from "@gracefullight/validate-branch";
98
-
99
- const config = await loadConfig();
100
- console.log(config);
101
- // {
102
- // pattern: "^(main|develop|feature/.+)$",
103
- // description: "Project branch naming rules"
104
- // }
105
- ```
106
-
107
- ## Configuration File
108
-
109
- Create a `branch.config.ts` or `branch.config.js` file in your project root to define custom patterns.
110
-
111
- ### TypeScript Config (`branch.config.ts`)
112
-
113
- ```typescript
114
- import type { Config } from "@gracefullight/validate-branch";
115
-
116
- const config: Config = {
117
- pattern: "^(main|develop|feature/.+|fix/.+|refactor/.+)$",
118
- description: "Project branch naming rules",
119
- };
120
-
121
- export default config;
122
- ```
123
-
124
- ### JavaScript Config (`branch.config.js`)
125
-
126
- ```javascript
127
- module.exports = {
128
- pattern: "^(main|develop|feature/.+|fix/.+)$",
129
- description: "Project branch naming rules",
130
- };
131
- ```
132
-
133
- ### Using Presets (`branch.config.ts`)
134
-
135
- You can use built-in presets instead of custom patterns.
136
-
137
- ```typescript
138
- import type { Config } from "@gracefullight/validate-branch";
139
-
140
- const config: Config = {
141
- preset: "jira", // "gitflow" or "jira"
142
- description: "JIRA ticket-based branch naming",
143
- };
144
-
145
- export default config;
146
- ```
147
-
148
- **Available Presets:**
149
-
150
- #### `gitflow` (default)
151
-
152
- Pattern following Git Flow branching strategy.
153
-
154
- | Status | Branch Examples |
155
- |--------|-----------------|
156
- | ✅ Allowed | `main`, `master`, `develop`, `stage` |
157
- | ✅ Allowed | `feature/login`, `feature/user-auth`, `feature/add-payment` |
158
- | ✅ Allowed | `fix/memory-leak`, `fix/typo`, `fix/null-pointer` |
159
- | ✅ Allowed | `hotfix/critical-bug`, `hotfix/security-patch` |
160
- | ✅ Allowed | `release/v1.0.0`, `release/2.3.1` |
161
- | ❌ Rejected | `login`, `bugfix`, `my-branch` (no prefix) |
162
- | ❌ Rejected | `feature/`, `fix/` (no name) |
163
- | ❌ Rejected | `Feature/Login`, `FIX/bug` (uppercase prefix) |
164
-
165
- #### `jira`
166
-
167
- JIRA ticket number-based branch pattern.
168
-
169
- | Status | Branch Examples |
170
- |--------|-----------------|
171
- | ✅ Allowed | `main`, `master`, `develop`, `stage` |
172
- | ✅ Allowed | `FEATURE-123`, `FEATURE-1`, `FEATURE-99999` |
173
- | ✅ Allowed | `BUG-456`, `STORY-789`, `TASK-101`, `HOTFIX-202` |
174
- | ❌ Rejected | `feature/login` (gitflow style) |
175
- | ❌ Rejected | `FEATURE-ABC` (not a number) |
176
- | ❌ Rejected | `feature-123` (lowercase) |
177
- | ❌ Rejected | `JIRA-123/description` (contains slash) |
178
-
179
- ## API Reference
180
-
181
- ### `validateBranchName(branchName, options?)`
182
-
183
- Check if a branch name is valid.
184
-
185
- ```typescript
186
- interface ValidateBranchNameOptions {
187
- customRegexp?: string;
188
- preset?: "gitflow" | "jira";
189
- }
190
-
191
- function validateBranchName(
192
- branchName: string,
193
- options?: ValidateBranchNameOptions
194
- ): boolean
195
- ```
196
-
197
- **Parameters:**
198
- - `branchName`: Branch name to validate
199
- - `options`: Optional, validation options object
200
- - `customRegexp`: Custom regular expression string
201
- - `preset`: Preset pattern (`gitflow` or `jira`, default: `gitflow`)
202
-
203
- **Returns:** Validity status
204
-
205
- ---
206
-
207
- ### `validateWithDetails(branchName, options?)`
208
-
209
- Return detailed validation result.
210
-
211
- ```typescript
212
- function validateWithDetails(
213
- branchName: string,
214
- options?: ValidateBranchNameOptions
215
- ): ValidationResult
216
- ```
217
-
218
- **Returns:**
219
- ```typescript
220
- {
221
- valid: boolean;
222
- branchName: string;
223
- error?: string; // Error message on failure
224
- }
225
- ```
226
-
227
- **Example:**
228
- ```typescript
229
- const result = validateWithDetails("invalid-branch");
230
- // {
231
- // valid: false,
232
- // branchName: "invalid-branch",
233
- // error: 'Branch name "invalid-branch" does not match pattern: /^(main|master|...)$/'
234
- // }
235
- ```
236
-
237
- ---
238
-
239
- ### `getCurrentBranchName()`
240
-
241
- Get current Git branch name.
242
-
243
- ```typescript
244
- function getCurrentBranchName(): Promise<string | null>
245
- ```
246
-
247
- **Returns:** Current branch name, `null` if not in a Git repository
248
-
249
- ---
250
-
251
- ### `loadConfig()`
252
-
253
- Load project configuration file.
254
-
255
- ```typescript
256
- function loadConfig(): Promise<Config | null>
257
- ```
258
-
259
- **Config file search order:**
260
- 1. `branch.config.ts`
261
- 2. `branch.config.mts`
262
- 3. `branch.config.js`
263
- 4. `branch.config.cjs`
264
- 5. `branch.config.mjs`
265
-
266
- **Returns:** Configuration object, `null` if no config file exists
267
-
268
- ---
269
-
270
- ## Type Definitions
271
-
272
- ### `Config`
273
-
274
- ```typescript
275
- interface Config {
276
- pattern?: string; // Single regex pattern
277
- patterns?: string[]; // Multiple regex patterns (planned)
278
- preset?: "gitflow" | "jira"; // Preset pattern (default: "gitflow")
279
- description?: string; // Config description
280
- }
281
- ```
282
-
283
- ### `ValidationResult`
284
-
285
- ```typescript
286
- interface ValidationResult {
287
- valid: boolean;
288
- branchName: string;
289
- error?: string;
290
- }
291
- ```
292
-
293
- ## Examples
294
-
295
- ### Use with Git Hooks
296
-
297
- #### With Husky in `package.json`
298
-
299
- ```json
300
- {
301
- "husky": {
302
- "hooks": {
303
- "pre-commit": "validate-branch"
304
- }
305
- }
306
- }
307
- ```
308
-
309
- #### Direct Git Hooks
310
-
311
- ```bash
312
- #!/bin/sh
313
- # .git/hooks/pre-commit
314
-
315
- npx validate-branch
316
- if [ $? -ne 0 ]; then
317
- echo "Branch name does not follow naming conventions."
318
- exit 1
319
- fi
320
- ```
321
-
322
- ### Use with GitHub Actions
323
-
324
- ```yaml
325
- name: Branch Validation
326
-
327
- on:
328
- pull_request:
329
- types: [opened, synchronize]
330
-
331
- jobs:
332
- validate-branch:
333
- runs-on: ubuntu-latest
334
- steps:
335
- - uses: actions/checkout@v4
336
-
337
- - name: Setup Node.js
338
- uses: actions/setup-node@v4
339
- with:
340
- node-version: "20"
341
-
342
- - name: Install validate-branch
343
- run: npm install -g @gracefullight/validate-branch
344
-
345
- - name: Validate branch name
346
- run: validate-branch --test ${{ github.head_ref }}
347
- ```
348
-
349
- ### Use in CI/CD Pipeline
350
-
351
- ```yaml
352
- # .github/workflows/ci.yml
353
-
354
- jobs:
355
- test:
356
- runs-on: ubuntu-latest
357
- steps:
358
- - name: Checkout
359
- uses: actions/checkout@v4
360
-
361
- - name: Validate branch name
362
- run: npx validate-branch
363
- ```
364
-
365
- ### Use with ESLint Plugin
366
-
367
- ```javascript
368
- // .eslintrc.js
369
- const { execSync } = require("child_process");
370
-
371
- module.exports = {
372
- // ... other config
373
- rules: {
374
- "custom/validate-branch": {
375
- meta: {
376
- type: "suggestion",
377
- docs: {
378
- description: "Validate git branch name",
379
- },
380
- },
381
- create(context) {
382
- return {
383
- Program() {
384
- const branch = execSync("git rev-parse --abbrev-ref HEAD", {
385
- encoding: "utf-8",
386
- }).trim();
387
-
388
- const { validateBranchName } = require("@gracefullight/validate-branch");
389
-
390
- if (!validateBranchName(branch)) {
391
- context.report({
392
- node: {},
393
- message: `Invalid branch name: ${branch}`,
394
- });
395
- }
396
- },
397
- };
398
- },
399
- },
400
- },
401
- };
402
- ```
403
-
404
- ## Development
405
-
406
- ### Setup
407
-
408
- ```bash
409
- # Clone repository
410
- git clone https://github.com/gracefullight/saju.git
411
- cd packages/validate-branch
412
-
413
- # Install dependencies
414
- pnpm install
415
-
416
- # Build
417
- pnpm build
418
-
419
- # Test
420
- pnpm test
421
-
422
- # Lint
423
- pnpm lint
424
-
425
- # Format
426
- pnpm lint:fix
427
- ```
428
-
429
- ### Test CLI Locally
430
-
431
- ```bash
432
- # After building, test CLI locally
433
- node bin/validate-branch.js --test feature/new-feature
434
-
435
- # Test with custom pattern
436
- node bin/validate-branch.js --test invalid-name --regexp "^(main|develop)$"
437
- ```
438
-
439
- ## Contributing
440
-
441
- Contributions are welcome! Please feel free to submit a Pull Request.
442
-
443
- 1. Fork repository
444
- 2. Create your feature branch (`git checkout -b feature/amazing-feature`)
445
- 3. Commit your changes (`git commit -m 'Add some amazing feature'`)
446
- 4. Push to branch (`git push origin feature/amazing-feature`)
447
- 5. Open a Pull Request
448
-
449
- ### Guidelines
450
-
451
- - Write tests for new features
452
- - Maintain or improve code coverage
453
- - Follow existing code style (enforced by Biome)
454
- - Update documentation as needed
455
-
456
- ## License
457
-
458
- MIT © [gracefullight](https://github.com/gracefullight)
459
-
460
- ## Related Projects
461
-
462
- - [isomorphic-git](https://isomorphic-git.org/) - Git implementation library
463
- - [Commander.js](https://commander.js/) - Node.js CLI framework
464
- - [Chalk](https://chalk.js.org/) - Terminal string styling
465
-
466
- ## Support
467
-
468
- - [Documentation](https://github.com/gracefullight/saju#readme)
469
- - [Issue Tracker](https://github.com/gracefullight/saju/issues)
470
- - [Discussions](https://github.com/gracefullight/saju/discussions)