new-branch 0.1.1 → 0.2.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/README.md +25 -28
- package/dist/cli.js +10 -2
- package/dist/config/loadProjectConfig.js +19 -0
- package/dist/config/loadProjectConfig.test.js +34 -0
- package/dist/runtime/enums.js +11 -0
- package/dist/runtime/resolveMissingValues.js +13 -2
- package/package.json +6 -3
package/README.md
CHANGED
|
@@ -119,13 +119,11 @@ Example AST representation:
|
|
|
119
119
|
|
|
120
120
|
## 6. Built-in Variables
|
|
121
121
|
|
|
122
|
-
Variable Description
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
title Human-readable task title
|
|
128
|
-
id Task identifier (e.g., STK-123)
|
|
122
|
+
| Variable | Description |
|
|
123
|
+
| -------- | ------------------------------- |
|
|
124
|
+
| type | Branch type (feat, fix, etc.) |
|
|
125
|
+
| title | Human-readable task title |
|
|
126
|
+
| id | Task identifier (e.g., STK-123) |
|
|
129
127
|
|
|
130
128
|
---
|
|
131
129
|
|
|
@@ -135,28 +133,29 @@ All transforms must be pure functions.
|
|
|
135
133
|
|
|
136
134
|
### 7.1 String Transforms
|
|
137
135
|
|
|
138
|
-
| Transform
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
uppercase
|
|
143
|
-
|
|
136
|
+
| Transform | Description |
|
|
137
|
+
| --------- | ------------------------- |
|
|
138
|
+
| slugify | Converts to URL-safe slug |
|
|
139
|
+
| lowercase | Converts to lowercase |
|
|
140
|
+
| uppercase | Converts to uppercase |
|
|
141
|
+
| trim | Trims whitespace |
|
|
142
|
+
| titlecase | Capitalizes words |
|
|
144
143
|
|
|
145
144
|
### 7.2 Argument-based Transforms
|
|
146
145
|
|
|
147
|
-
| Transform
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
| Transform | Description | Example |
|
|
147
|
+
| --------- | ------------------------------ | ------- |
|
|
148
|
+
| max | Truncates string to max length | max:25 |
|
|
149
|
+
| pad | Pads string to length | pad:10 |
|
|
151
150
|
|
|
152
151
|
### 7.3 Validation Transforms
|
|
153
152
|
|
|
154
|
-
Validation transforms do not modify value but throw errors if invalid.
|
|
153
|
+
Validation transforms do not modify a value but throw errors if invalid.
|
|
155
154
|
|
|
156
|
-
| Transform
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
155
|
+
| Transform | Description |
|
|
156
|
+
| --------- | -------------------------- |
|
|
157
|
+
| required | Ensures value is not empty |
|
|
158
|
+
| match | Validates value via regex |
|
|
160
159
|
|
|
161
160
|
---
|
|
162
161
|
|
|
@@ -237,12 +236,10 @@ Validation must occur immediately after input.
|
|
|
237
236
|
|
|
238
237
|
## 12. Optional Flags
|
|
239
238
|
|
|
240
|
-
Flag Description
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
--create Creates branch using `git switch -c`
|
|
245
|
-
--print Prints branch name only (default behavior)
|
|
239
|
+
| Flag | Description |
|
|
240
|
+
| ---------- | ------------------------------------------ |
|
|
241
|
+
| `--create` | Creates branch using `git switch -c` |
|
|
242
|
+
| `--print` | Prints branch name only (default behavior) |
|
|
246
243
|
|
|
247
244
|
---
|
|
248
245
|
|
package/dist/cli.js
CHANGED
|
@@ -4,6 +4,7 @@ import { parsePattern } from "./pattern/parsePattern.js";
|
|
|
4
4
|
import { defaultTransforms } from "./pattern/transforms/index.js";
|
|
5
5
|
import { renderPattern } from "./pattern/transforms/renderPattern.js";
|
|
6
6
|
import { resolveMissingValues } from "./runtime/resolveMissingValues.js";
|
|
7
|
+
import { loadProjectConfig } from "./config/loadProjectConfig.js";
|
|
7
8
|
import { sanitizeGitRef } from "./git/sanitizeGitRef.js";
|
|
8
9
|
import { validateBranchName } from "./git/validateBranchName.js";
|
|
9
10
|
import { createBranch } from "./git/createBranch.js";
|
|
@@ -47,15 +48,22 @@ function toInitialValues(args) {
|
|
|
47
48
|
}
|
|
48
49
|
async function run() {
|
|
49
50
|
// Step 0: args/options
|
|
51
|
+
// Note: CAC prints help, but depending on our parseArgs wrapper we might not
|
|
52
|
+
// expose `help` in `args.options`. We still want to exit early and never
|
|
53
|
+
// require a pattern when the user just asked for help.
|
|
54
|
+
const argv = process.argv.slice(2);
|
|
55
|
+
const wantsHelp = argv.includes("--help") || argv.includes("-h");
|
|
50
56
|
const args = parseArgs(process.argv);
|
|
51
|
-
if (
|
|
57
|
+
if (wantsHelp) {
|
|
52
58
|
return;
|
|
53
59
|
}
|
|
54
60
|
const quiet = args.options.quiet === true;
|
|
55
61
|
const create = args.options.create === true;
|
|
56
62
|
const prompt = args.options.prompt !== false;
|
|
57
63
|
// Pipeline: pattern -> AST -> resolve values -> render -> sanitize -> validate -> (optional) git -> output
|
|
58
|
-
const
|
|
64
|
+
const projectConfig = await loadProjectConfig();
|
|
65
|
+
const resolvedPattern = args.options.pattern ?? projectConfig.pattern;
|
|
66
|
+
const patternRes = requirePattern(resolvedPattern);
|
|
59
67
|
if (!isOk(patternRes))
|
|
60
68
|
fail("Invalid CLI arguments.", patternRes.error);
|
|
61
69
|
const astRes = safe(() => parsePattern(patternRes.value));
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
export async function loadProjectConfig() {
|
|
4
|
+
try {
|
|
5
|
+
const path = join(process.cwd(), "package.json");
|
|
6
|
+
const raw = await readFile(path, "utf8");
|
|
7
|
+
const pkg = JSON.parse(raw);
|
|
8
|
+
const config = pkg["new-branch"];
|
|
9
|
+
if (!config || typeof config !== "object")
|
|
10
|
+
return {};
|
|
11
|
+
const cfg = config;
|
|
12
|
+
return {
|
|
13
|
+
pattern: typeof cfg.pattern === "string" ? cfg.pattern : undefined,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { vi, describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
vi.mock("node:fs/promises", () => ({
|
|
3
|
+
readFile: vi.fn(),
|
|
4
|
+
}));
|
|
5
|
+
import { readFile } from "node:fs/promises";
|
|
6
|
+
describe("loadProjectConfig", () => {
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
vi.resetAllMocks();
|
|
9
|
+
});
|
|
10
|
+
it("returns pattern when package.json contains new-branch.pattern as string", async () => {
|
|
11
|
+
readFile.mockResolvedValueOnce(JSON.stringify({ "new-branch": { pattern: "{type}/{title}-{id}" } }));
|
|
12
|
+
const { loadProjectConfig } = await import("./loadProjectConfig.js");
|
|
13
|
+
const cfg = await loadProjectConfig();
|
|
14
|
+
expect(cfg).toEqual({ pattern: "{type}/{title}-{id}" });
|
|
15
|
+
});
|
|
16
|
+
it("returns empty object when package.json has no new-branch key", async () => {
|
|
17
|
+
readFile.mockResolvedValueOnce(JSON.stringify({ name: "pkg" }));
|
|
18
|
+
const { loadProjectConfig } = await import("./loadProjectConfig.js");
|
|
19
|
+
const cfg = await loadProjectConfig();
|
|
20
|
+
expect(cfg).toEqual({});
|
|
21
|
+
});
|
|
22
|
+
it("ignores non-string pattern values", async () => {
|
|
23
|
+
readFile.mockResolvedValueOnce(JSON.stringify({ "new-branch": { pattern: 123 } }));
|
|
24
|
+
const { loadProjectConfig } = await import("./loadProjectConfig.js");
|
|
25
|
+
const cfg = await loadProjectConfig();
|
|
26
|
+
expect(cfg).toEqual({});
|
|
27
|
+
});
|
|
28
|
+
it("returns empty object if reading package.json throws", async () => {
|
|
29
|
+
readFile.mockRejectedValueOnce(new Error("enoent"));
|
|
30
|
+
const { loadProjectConfig } = await import("./loadProjectConfig.js");
|
|
31
|
+
const cfg = await loadProjectConfig();
|
|
32
|
+
expect(cfg).toEqual({});
|
|
33
|
+
});
|
|
34
|
+
});
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export const TYPE_CHOICES = [
|
|
2
|
+
{ name: "Feature", value: "feat" },
|
|
3
|
+
{ name: "Fix", value: "fix" },
|
|
4
|
+
{ name: "Documentation", value: "docs" },
|
|
5
|
+
{ name: "Chore", value: "chore" },
|
|
6
|
+
{ name: "Refactor", value: "refactor" },
|
|
7
|
+
{ name: "Test", value: "test" },
|
|
8
|
+
{ name: "Performance", value: "perf" },
|
|
9
|
+
{ name: "Build", value: "build" },
|
|
10
|
+
{ name: "CI", value: "ci" },
|
|
11
|
+
];
|
|
@@ -1,19 +1,30 @@
|
|
|
1
|
-
import { input } from "@inquirer/prompts";
|
|
1
|
+
import { input, select } from "@inquirer/prompts";
|
|
2
|
+
import { TYPE_CHOICES } from "../runtime/enums.js";
|
|
2
3
|
/**
|
|
3
4
|
* Resolves missing variable values required by the pattern.
|
|
4
5
|
*
|
|
5
6
|
* Rule (v1):
|
|
6
7
|
* - Any variable used in the pattern is considered required.
|
|
8
|
+
* - The "type" variable is resolved using a select menu with predefined choices.
|
|
7
9
|
*/
|
|
8
10
|
export async function resolveMissingValues(parsed, initialValues, opts) {
|
|
9
11
|
const requiredVars = parsed.variablesUsed;
|
|
10
12
|
const values = { ...initialValues };
|
|
11
13
|
for (const name of requiredVars) {
|
|
12
|
-
|
|
14
|
+
const current = values[name];
|
|
15
|
+
if (current && current.trim() !== "")
|
|
13
16
|
continue;
|
|
14
17
|
if (!opts.prompt) {
|
|
15
18
|
throw new Error(`Missing required value: "${name}"`);
|
|
16
19
|
}
|
|
20
|
+
if (name === "type") {
|
|
21
|
+
const selected = await select({
|
|
22
|
+
message: "Select branch type:",
|
|
23
|
+
choices: TYPE_CHOICES,
|
|
24
|
+
});
|
|
25
|
+
values[name] = selected;
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
17
28
|
const answer = await input({
|
|
18
29
|
message: `Enter ${name}:`,
|
|
19
30
|
validate: (v) => (v.trim() ? true : `${name} cannot be empty`),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "new-branch",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Generate and create standardized git branch names from a pattern.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"start": "node dist/cli.js",
|
|
19
19
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
20
20
|
"prepack": "pnpm build && pnpm test:run",
|
|
21
|
-
"test": "vitest",
|
|
22
|
-
"test:run": "vitest run",
|
|
21
|
+
"test": "vitest --dir src --exclude **/dist/**",
|
|
22
|
+
"test:run": "vitest run --dir src --exclude **/dist/**",
|
|
23
23
|
"format": "prettier . --write",
|
|
24
24
|
"format:check": "prettier . --check",
|
|
25
25
|
"lint": "eslint src --ext .ts"
|
|
@@ -40,6 +40,9 @@
|
|
|
40
40
|
"bugs": {
|
|
41
41
|
"url": "https://github.com/teles/new-branch/issues"
|
|
42
42
|
},
|
|
43
|
+
"new-branch": {
|
|
44
|
+
"pattern": "{type}/{title}-{id}"
|
|
45
|
+
},
|
|
43
46
|
"homepage": "https://github.com/teles/new-branch#readme",
|
|
44
47
|
"packageManager": "pnpm@10.22.0",
|
|
45
48
|
"devDependencies": {
|