better-commits 1.20.0-cli-flags → 1.20.1-temp.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/dist/branch.js +1 -1
- package/dist/{chunk-SIF4LZUS.js → chunk-LPU7O52G.js} +1 -1
- package/dist/chunk-QPUTIRGU.js +245 -0
- package/dist/index.js +2 -2
- package/dist/init.js +1 -1
- package/package.json +7 -1
- package/readme.md +141 -173
- package/.better-commits.json +0 -52
- package/.github/workflows/publish.yml +0 -34
- package/.github/workflows/test.yml +0 -27
- package/.opencode/package-lock.json +0 -115
- package/.opencode/plans/cli-args.md +0 -182
- package/.prettierignore +0 -5
- package/.prettierrc +0 -1
- package/.svelte-kit/ambient.d.ts +0 -289
- package/.svelte-kit/generated/client/app.js +0 -28
- package/.svelte-kit/generated/client/matchers.js +0 -1
- package/.svelte-kit/generated/client/nodes/0.js +0 -1
- package/.svelte-kit/generated/client/nodes/1.js +0 -1
- package/.svelte-kit/tsconfig.json +0 -49
- package/0001-feat-branch-124-update-worktrees-feature.patch +0 -316
- package/dist/chunk-OFJCRS3N.js +0 -4
- package/docs/ai-skills.yaml +0 -48
- package/docs/clack.md +0 -143
- package/docs/valibot.md +0 -228
- package/src/args.test.ts +0 -102
- package/src/args.ts +0 -106
- package/src/branch-args.test.ts +0 -72
- package/src/branch-args.ts +0 -106
- package/src/branch-help.ts +0 -114
- package/src/branch.ts +0 -95
- package/src/git.ts +0 -60
- package/src/help.ts +0 -131
- package/src/index.test.ts +0 -7
- package/src/index.ts +0 -100
- package/src/init.ts +0 -25
- package/src/prompts/branch-checkout.prompt.ts +0 -36
- package/src/prompts/branch-confirm.prompt.ts +0 -134
- package/src/prompts/branch-description.prompt.ts +0 -37
- package/src/prompts/branch-runnable.ts +0 -13
- package/src/prompts/branch-ticket.prompt.ts +0 -41
- package/src/prompts/branch-type.prompt.ts +0 -43
- package/src/prompts/branch-user.prompt.ts +0 -50
- package/src/prompts/branch-version.prompt.ts +0 -41
- package/src/prompts/commit-body.prompt.ts +0 -57
- package/src/prompts/commit-confirm.prompt.ts +0 -119
- package/src/prompts/commit-footer.prompt.ts +0 -195
- package/src/prompts/commit-scope.prompt.ts +0 -73
- package/src/prompts/commit-status.prompt.ts +0 -75
- package/src/prompts/commit-ticket.prompt.ts +0 -82
- package/src/prompts/commit-title.prompt.ts +0 -98
- package/src/prompts/commit-type.prompt.ts +0 -93
- package/src/prompts/runnable.ts +0 -13
- package/src/utils/build-branch.test.ts +0 -141
- package/src/utils/build-branch.ts +0 -46
- package/src/utils/build-commit-string.test.ts +0 -253
- package/src/utils/build-commit-string.ts +0 -158
- package/src/utils/commit-title-size.ts +0 -24
- package/src/utils/infer.test.ts +0 -83
- package/src/utils/infer.ts +0 -114
- package/src/utils/messages.ts +0 -25
- package/src/utils/no-interactive-branch-validation.test.ts +0 -170
- package/src/utils/no-interactive-validation.test.ts +0 -174
- package/src/utils/no-interactive-validation.ts +0 -190
- package/src/utils.ts +0 -219
- package/src/valibot-consts.ts +0 -114
- package/src/valibot-state.test.ts +0 -48
- package/src/valibot-state.ts +0 -265
- package/tsconfig.json +0 -15
- package/tsup.config.ts +0 -12
- package/vitest.config.ts +0 -8
package/docs/valibot.md
DELETED
|
@@ -1,228 +0,0 @@
|
|
|
1
|
-
# Valibot Usage
|
|
2
|
-
|
|
3
|
-
`better-commits` uses [`valibot`](https://valibot.dev) (`^0.30.0`) for all runtime schema validation, type inference, and config parsing. Valibot defines the shape of every configuration object, commit state, and branch state in the application.
|
|
4
|
-
|
|
5
|
-
## Files
|
|
6
|
-
|
|
7
|
-
| File | Role |
|
|
8
|
-
| ----------------------- | ------------------------------------------------------------------ |
|
|
9
|
-
| `src/valibot-consts.ts` | Shared picklist schemas and constant arrays |
|
|
10
|
-
| `src/valibot-state.ts` | Core schema definitions for `Config`, `CommitState`, `BranchState` |
|
|
11
|
-
| `src/utils.ts` | Config loading, `parse`, and `ValiError` handling |
|
|
12
|
-
| `src/index.ts` | Parses `CommitState`; uses `Output<>` types |
|
|
13
|
-
| `src/branch.ts` | Parses `BranchState`; uses `Output<>` types |
|
|
14
|
-
| `src/init.ts` | Calls `parse(Config, {})` to generate a default config file |
|
|
15
|
-
|
|
16
|
-
## Schema Overview
|
|
17
|
-
|
|
18
|
-
### `src/valibot-consts.ts` — Picklist Constants
|
|
19
|
-
|
|
20
|
-
Defines shared enum-like schemas used across the codebase:
|
|
21
|
-
|
|
22
|
-
```ts
|
|
23
|
-
export const V_BRANCH_ACTIONS = v.picklist(["branch", "worktree"]);
|
|
24
|
-
export const V_FOOTER_OPTIONS = v.picklist([
|
|
25
|
-
"closes",
|
|
26
|
-
"trailer",
|
|
27
|
-
"breaking-change",
|
|
28
|
-
"deprecated",
|
|
29
|
-
"custom",
|
|
30
|
-
]);
|
|
31
|
-
export const V_BRANCH_FIELDS = v.picklist([
|
|
32
|
-
"user",
|
|
33
|
-
"version",
|
|
34
|
-
"type",
|
|
35
|
-
"ticket",
|
|
36
|
-
"description",
|
|
37
|
-
]);
|
|
38
|
-
export const V_BRANCH_CONFIG_FIELDS = v.picklist([
|
|
39
|
-
"branch_user",
|
|
40
|
-
"branch_version",
|
|
41
|
-
"branch_type",
|
|
42
|
-
"branch_ticket",
|
|
43
|
-
"branch_description",
|
|
44
|
-
]);
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
Constant arrays are typed using `v.Output<>` to derive element types directly from the schemas:
|
|
48
|
-
|
|
49
|
-
```ts
|
|
50
|
-
export const FOOTER_OPTION_VALUES: v.Output<typeof V_FOOTER_OPTIONS>[] = [...];
|
|
51
|
-
export const BRANCH_ORDER_DEFAULTS: v.Output<typeof V_BRANCH_FIELDS>[] = [...];
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
---
|
|
55
|
-
|
|
56
|
-
### `src/valibot-state.ts` — Core Schemas
|
|
57
|
-
|
|
58
|
-
#### `Config`
|
|
59
|
-
|
|
60
|
-
The main application config schema. Validates `.better-commits.json`. Parsing an empty object `{}` produces a fully-defaulted config.
|
|
61
|
-
|
|
62
|
-
Key fields and their validators:
|
|
63
|
-
|
|
64
|
-
| Field | Schema | Default |
|
|
65
|
-
| ------------------------ | ------------------------------------------------------------- | ---------- |
|
|
66
|
-
| `check_status` | `v.optional(v.boolean(), true)` | `true` |
|
|
67
|
-
| `cache_last_value` | `v.optional(v.boolean(), true)` | `true` |
|
|
68
|
-
| `confirm_with_editor` | `v.optional(v.boolean(), false)` | `false` |
|
|
69
|
-
| `confirm_commit` | `v.optional(v.boolean(), true)` | `true` |
|
|
70
|
-
| `print_commit_output` | `v.optional(v.boolean(), true)` | `true` |
|
|
71
|
-
| `enable_worktrees` | `v.optional(v.boolean(), true)` | `true` |
|
|
72
|
-
| `branch_pre_commands` | `v.optional(v.array(v.string()), [])` | `[]` |
|
|
73
|
-
| `branch_post_commands` | `v.optional(v.array(v.string()), [])` | `[]` |
|
|
74
|
-
| `worktree_pre_commands` | `v.optional(v.array(v.string()), [])` | `[]` |
|
|
75
|
-
| `worktree_post_commands` | `v.optional(v.array(v.string()), [])` | `[]` |
|
|
76
|
-
| `branch_action_default` | `v.optional(V_BRANCH_ACTIONS, "branch")` | `"branch"` |
|
|
77
|
-
| `branch_order` | `v.optional(v.array(V_BRANCH_FIELDS), BRANCH_ORDER_DEFAULTS)` | all fields |
|
|
78
|
-
|
|
79
|
-
**Notable nested validators:**
|
|
80
|
-
|
|
81
|
-
- `commit_type.options[].emoji` — `v.string([v.emoji()])` validates the value is a real emoji character
|
|
82
|
-
- `commit_type.max_items` / `commit_scope.max_items` — `v.number([v.minValue(1)])` enforces minimum of 1
|
|
83
|
-
- `check_ticket.prepend_hashtag` — `v.picklist(["Never", "Always", "Prompt"])`
|
|
84
|
-
- `check_ticket.surround` — `v.picklist(["", "()", "[]", "{}"])`
|
|
85
|
-
- `check_ticket.title_position` — `v.picklist(["start", "end", "before-colon", "beginning"])`
|
|
86
|
-
- `branch_*.separator` — `v.picklist(["/", "-", "_"])` (branch_description also allows `""`)
|
|
87
|
-
|
|
88
|
-
**Cross-field validation with `v.custom`:**
|
|
89
|
-
|
|
90
|
-
Both `commit_type` and `commit_scope` use `v.custom` to ensure `initial_value` exists within their `options` array:
|
|
91
|
-
|
|
92
|
-
```ts
|
|
93
|
-
v.custom(
|
|
94
|
-
(val) =>
|
|
95
|
-
!val.initial_value ||
|
|
96
|
-
val.options.some((o) => o.value === val.initial_value),
|
|
97
|
-
(val) =>
|
|
98
|
-
`Type: initial_value "${val.input.initial_value}" must exist in options`,
|
|
99
|
-
);
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
**Post-parse transformation with `v.transform`:**
|
|
103
|
-
|
|
104
|
-
- `commit_type` — when `append_emoji_to_label` is `true`, prepends emoji to each option's label
|
|
105
|
-
- `commit_scope` — when `custom_scope` is `true`, injects `{ label: "custom", value: "custom", hint: "..." }` into options if not already present
|
|
106
|
-
|
|
107
|
-
---
|
|
108
|
-
|
|
109
|
-
#### `CommitState`
|
|
110
|
-
|
|
111
|
-
Models the mutable in-memory state of an in-progress commit. All fields are `v.optional(v.string(), "")`.
|
|
112
|
-
|
|
113
|
-
| Field | Purpose |
|
|
114
|
-
| ------------------ | --------------------------- |
|
|
115
|
-
| `type` | Commit type (e.g. `"feat"`) |
|
|
116
|
-
| `scope` | Commit scope (e.g. `"app"`) |
|
|
117
|
-
| `title` | Short commit title |
|
|
118
|
-
| `body` | Long commit body |
|
|
119
|
-
| `closes` | "Closes: " footer token |
|
|
120
|
-
| `ticket` | Issue/ticket number |
|
|
121
|
-
| `breaking_title` | Breaking change summary |
|
|
122
|
-
| `breaking_body` | Breaking change description |
|
|
123
|
-
| `deprecates` | Deprecation marker |
|
|
124
|
-
| `deprecates_title` | Deprecation summary |
|
|
125
|
-
| `deprecates_body` | Deprecation description |
|
|
126
|
-
| `custom_footer` | Custom footer text |
|
|
127
|
-
| `trailer` | Git trailer string |
|
|
128
|
-
|
|
129
|
-
---
|
|
130
|
-
|
|
131
|
-
#### `BranchState`
|
|
132
|
-
|
|
133
|
-
Models the mutable in-memory state for the `better-branch` CLI flow. All fields are `v.optional(v.string(), "")`.
|
|
134
|
-
|
|
135
|
-
| Field | Purpose |
|
|
136
|
-
| ------------- | ----------------------------------- |
|
|
137
|
-
| `user` | Git username portion of branch name |
|
|
138
|
-
| `type` | Branch type (mirrors commit type) |
|
|
139
|
-
| `ticket` | Ticket/issue number |
|
|
140
|
-
| `description` | Short branch description |
|
|
141
|
-
| `version` | Version number |
|
|
142
|
-
|
|
143
|
-
---
|
|
144
|
-
|
|
145
|
-
## Runtime Usage
|
|
146
|
-
|
|
147
|
-
### Parsing with defaults
|
|
148
|
-
|
|
149
|
-
All three state objects are initialized by parsing an empty object, which triggers all `v.optional` defaults:
|
|
150
|
-
|
|
151
|
-
```ts
|
|
152
|
-
// src/index.ts
|
|
153
|
-
const commit_state = parse(CommitState, {});
|
|
154
|
-
|
|
155
|
-
// src/branch.ts
|
|
156
|
-
const branch_state = parse(BranchState, {});
|
|
157
|
-
|
|
158
|
-
// src/init.ts
|
|
159
|
-
const default_config = parse(Config, {});
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Config validation in `src/utils.ts`
|
|
163
|
-
|
|
164
|
-
`validate_config` wraps `parse(Config, config)` to produce human-friendly errors:
|
|
165
|
-
|
|
166
|
-
```ts
|
|
167
|
-
function validate_config(config: unknown) {
|
|
168
|
-
try {
|
|
169
|
-
return parse(Config, config);
|
|
170
|
-
} catch (err) {
|
|
171
|
-
if (err instanceof ValiError) {
|
|
172
|
-
const path = err.issues[0].path?.map((p) => p.key).join(".");
|
|
173
|
-
p.log.error(`Invalid config at: ${path}`);
|
|
174
|
-
process.exit(1);
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
```
|
|
179
|
-
|
|
180
|
-
### TypeScript types via `Output<>`
|
|
181
|
-
|
|
182
|
-
`Output<typeof Schema>` is used throughout as the TypeScript type for parsed objects, avoiding manual interface duplication:
|
|
183
|
-
|
|
184
|
-
```ts
|
|
185
|
-
// Function signatures
|
|
186
|
-
function build_commit_string(
|
|
187
|
-
state: Output<typeof CommitState>,
|
|
188
|
-
config: Output<typeof Config>
|
|
189
|
-
): string { ... }
|
|
190
|
-
|
|
191
|
-
// Variable types
|
|
192
|
-
const checkout_type: Output<typeof V_BRANCH_ACTIONS> = ...;
|
|
193
|
-
```
|
|
194
|
-
|
|
195
|
-
---
|
|
196
|
-
|
|
197
|
-
## Valibot API Summary
|
|
198
|
-
|
|
199
|
-
| API | Purpose |
|
|
200
|
-
| ----------------------------- | ---------------------------------------------------------------------- |
|
|
201
|
-
| `v.object({...})` | Defines all three top-level schemas and all nested sub-objects |
|
|
202
|
-
| `v.optional(schema, default)` | Makes fields optional with a fallback default |
|
|
203
|
-
| `v.boolean()` | Validates boolean config flags |
|
|
204
|
-
| `v.string()` | Validates string fields |
|
|
205
|
-
| `v.number([...])` | Validates number fields with optional pipe validators |
|
|
206
|
-
| `v.array(schema)` | Validates array fields |
|
|
207
|
-
| `v.picklist([...])` | Validates enum-like string fields |
|
|
208
|
-
| `v.minValue(1)` | Pipe validator: enforces a minimum numeric value |
|
|
209
|
-
| `v.emoji()` | Pipe validator: ensures a string is a valid emoji character |
|
|
210
|
-
| `v.custom(fn, msgFn)` | Cross-field validation with a custom predicate |
|
|
211
|
-
| `v.transform(schema, fn)` | Post-parse transformation of validated data |
|
|
212
|
-
| `parse(schema, data)` | Parses and validates data, applying all defaults and transforms |
|
|
213
|
-
| `Output<typeof Schema>` | TypeScript utility to extract the inferred output type |
|
|
214
|
-
| `ValiError` | Error class caught to produce human-readable validation error messages |
|
|
215
|
-
|
|
216
|
-
---
|
|
217
|
-
|
|
218
|
-
## Known Quirk
|
|
219
|
-
|
|
220
|
-
`src/branch.ts` contains a bare no-op reference to `CommitState` at the bottom of the file:
|
|
221
|
-
|
|
222
|
-
```ts
|
|
223
|
-
// TODO: No idea what's happening here
|
|
224
|
-
// If you don't use CommitState, (even in unreachable code), parse fails on Config
|
|
225
|
-
CommitState;
|
|
226
|
-
```
|
|
227
|
-
|
|
228
|
-
This is a workaround for a bundler/tree-shaking issue: without this reference, `parse(Config, ...)` fails at runtime. The root cause is likely that `CommitState` imports something from `valibot-state.ts` that is needed for `Config` to work correctly at runtime, but the bundler eliminates it as dead code without this reference.
|
package/src/args.test.ts
DELETED
|
@@ -1,102 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { parse_runtime_flags } from "./args";
|
|
3
|
-
|
|
4
|
-
describe("parse_runtime_flags", () => {
|
|
5
|
-
it("uses interactive mode by default", () => {
|
|
6
|
-
const parsed = parse_runtime_flags([]);
|
|
7
|
-
|
|
8
|
-
expect(parsed.no_interactive).toBe(false);
|
|
9
|
-
expect(parsed.dry_run).toBe(false);
|
|
10
|
-
expect(parsed.git_args).toBe("");
|
|
11
|
-
expect(parsed.commit_state).toEqual({});
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it("parses --no-interactive and --dry-run", () => {
|
|
15
|
-
const parsed = parse_runtime_flags(["--no-interactive", "--dry-run"]);
|
|
16
|
-
|
|
17
|
-
expect(parsed.no_interactive).toBe(true);
|
|
18
|
-
expect(parsed.dry_run).toBe(true);
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it("parses --version and -v", () => {
|
|
22
|
-
const long_flag = parse_runtime_flags(["--version"]);
|
|
23
|
-
const short_flag = parse_runtime_flags(["-v"]);
|
|
24
|
-
|
|
25
|
-
expect(long_flag.version).toBe(true);
|
|
26
|
-
expect(short_flag.version).toBe(true);
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("maps commit flags into commit_state keys", () => {
|
|
30
|
-
const parsed = parse_runtime_flags([
|
|
31
|
-
"--type",
|
|
32
|
-
"feat",
|
|
33
|
-
"--title",
|
|
34
|
-
"ship feature",
|
|
35
|
-
"--breaking-title",
|
|
36
|
-
"api changed",
|
|
37
|
-
"--custom-footer",
|
|
38
|
-
"Reviewed-by: Jane",
|
|
39
|
-
]);
|
|
40
|
-
|
|
41
|
-
expect(parsed.commit_state).toEqual({
|
|
42
|
-
type: "feat",
|
|
43
|
-
title: "ship feature",
|
|
44
|
-
breaking_title: "api changed",
|
|
45
|
-
custom_footer: "Reviewed-by: Jane",
|
|
46
|
-
});
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
it("builds git args from --git-dir and --work-tree", () => {
|
|
50
|
-
const parsed = parse_runtime_flags([
|
|
51
|
-
"--git-dir",
|
|
52
|
-
"/tmp/repo/.git",
|
|
53
|
-
"--work-tree",
|
|
54
|
-
"/tmp/repo",
|
|
55
|
-
]);
|
|
56
|
-
|
|
57
|
-
expect(parsed.git_args).toBe(
|
|
58
|
-
"--git-dir=/tmp/repo/.git --work-tree=/tmp/repo",
|
|
59
|
-
);
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it("builds git args when only one git location flag is provided", () => {
|
|
63
|
-
const with_git_dir = parse_runtime_flags(["--git-dir", "/tmp/repo/.git"]);
|
|
64
|
-
const with_work_tree = parse_runtime_flags(["--work-tree", "/tmp/repo"]);
|
|
65
|
-
|
|
66
|
-
expect(with_git_dir.git_args).toBe("--git-dir=/tmp/repo/.git");
|
|
67
|
-
expect(with_work_tree.git_args).toBe("--work-tree=/tmp/repo");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it("maps dashed commit flags to snake_case commit_state keys", () => {
|
|
71
|
-
const parsed = parse_runtime_flags([
|
|
72
|
-
"--breaking-body",
|
|
73
|
-
"major impact",
|
|
74
|
-
"--deprecates-title",
|
|
75
|
-
"legacy endpoint",
|
|
76
|
-
"--deprecates-body",
|
|
77
|
-
"use v2 endpoint",
|
|
78
|
-
"--custom-footer",
|
|
79
|
-
"Reviewed-by: Alex",
|
|
80
|
-
"--breaking-title",
|
|
81
|
-
"v1 removed",
|
|
82
|
-
]);
|
|
83
|
-
|
|
84
|
-
expect(parsed.commit_state).toEqual({
|
|
85
|
-
breaking_body: "major impact",
|
|
86
|
-
deprecates_title: "legacy endpoint",
|
|
87
|
-
deprecates_body: "use v2 endpoint",
|
|
88
|
-
custom_footer: "Reviewed-by: Alex",
|
|
89
|
-
breaking_title: "v1 removed",
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
it("honors interactive flag semantics", () => {
|
|
94
|
-
const default_flags = parse_runtime_flags([]);
|
|
95
|
-
const explicit_interactive = parse_runtime_flags(["--interactive"]);
|
|
96
|
-
const no_interactive = parse_runtime_flags(["--no-interactive"]);
|
|
97
|
-
|
|
98
|
-
expect(default_flags.no_interactive).toBe(false);
|
|
99
|
-
expect(explicit_interactive.no_interactive).toBe(false);
|
|
100
|
-
expect(no_interactive.no_interactive).toBe(true);
|
|
101
|
-
});
|
|
102
|
-
});
|
package/src/args.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { parse } from "@bomb.sh/args";
|
|
2
|
-
import { InferOutput } from "valibot";
|
|
3
|
-
import { CommitState } from "./valibot-state";
|
|
4
|
-
|
|
5
|
-
type CommitStateRuntime = InferOutput<typeof CommitState>;
|
|
6
|
-
|
|
7
|
-
type ParsedRuntimeFlags = {
|
|
8
|
-
help: boolean;
|
|
9
|
-
version: boolean;
|
|
10
|
-
git_args: string;
|
|
11
|
-
no_interactive: boolean;
|
|
12
|
-
dry_run: boolean;
|
|
13
|
-
commit_state: Partial<CommitStateRuntime>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
export const COMMIT_OPTIONS = [
|
|
17
|
-
"type",
|
|
18
|
-
"scope",
|
|
19
|
-
"title",
|
|
20
|
-
"body",
|
|
21
|
-
"closes",
|
|
22
|
-
"ticket",
|
|
23
|
-
"trailer",
|
|
24
|
-
"deprecates",
|
|
25
|
-
"breaking-title",
|
|
26
|
-
"breaking-body",
|
|
27
|
-
"deprecates-title",
|
|
28
|
-
"deprecates-body",
|
|
29
|
-
"custom-footer",
|
|
30
|
-
] as const;
|
|
31
|
-
|
|
32
|
-
export const GIT_OPTIONS = ["git-dir", "work-tree"] as const;
|
|
33
|
-
|
|
34
|
-
export const BOOLEAN_FLAGS = [
|
|
35
|
-
"interactive",
|
|
36
|
-
"dry-run",
|
|
37
|
-
"help",
|
|
38
|
-
"version",
|
|
39
|
-
] as const;
|
|
40
|
-
|
|
41
|
-
class Flags {
|
|
42
|
-
#runtime: ParsedRuntimeFlags;
|
|
43
|
-
|
|
44
|
-
constructor(runtime: ParsedRuntimeFlags) {
|
|
45
|
-
this.#runtime = runtime;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
get git_args(): string {
|
|
49
|
-
return this.#runtime.git_args;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
get interactive(): boolean {
|
|
53
|
-
return !this.#runtime.no_interactive;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
get dry_run(): boolean {
|
|
57
|
-
return this.#runtime.dry_run;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
get help(): boolean {
|
|
61
|
-
return this.#runtime.help;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
get version(): boolean {
|
|
65
|
-
return this.#runtime.version;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
get commit_state(): Partial<CommitStateRuntime> {
|
|
69
|
-
return this.#runtime.commit_state;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
export const flags = new Flags(parse_runtime_flags(process.argv.slice(2)));
|
|
74
|
-
|
|
75
|
-
export function parse_runtime_flags(argv: string[]): ParsedRuntimeFlags {
|
|
76
|
-
const parsed = parse(argv, {
|
|
77
|
-
alias: { h: "help", v: "version" },
|
|
78
|
-
boolean: BOOLEAN_FLAGS,
|
|
79
|
-
string: [...COMMIT_OPTIONS, ...GIT_OPTIONS],
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
const commit_state: Partial<CommitStateRuntime> = {};
|
|
83
|
-
COMMIT_OPTIONS.forEach((value) => {
|
|
84
|
-
const cli_value = parsed[value];
|
|
85
|
-
if (cli_value) {
|
|
86
|
-
const str = value.replace("-", "_") as keyof CommitStateRuntime;
|
|
87
|
-
commit_state[str] = cli_value;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
help: parsed["help"] === true,
|
|
93
|
-
version: parsed["version"] === true,
|
|
94
|
-
git_args: get_git_args(parsed["git-dir"], parsed["work-tree"]),
|
|
95
|
-
no_interactive: parsed.interactive === false,
|
|
96
|
-
dry_run: parsed["dry-run"] === true,
|
|
97
|
-
commit_state,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function get_git_args(
|
|
102
|
-
git_dir: string | undefined,
|
|
103
|
-
work_tree: string | undefined,
|
|
104
|
-
): string {
|
|
105
|
-
return `${git_dir ? `--git-dir=${git_dir}` : ""} ${work_tree ? `--work-tree=${work_tree}` : ""}`.trim();
|
|
106
|
-
}
|
package/src/branch-args.test.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import { parse_branch_runtime_flags } from "./branch-args";
|
|
3
|
-
|
|
4
|
-
describe("parse_branch_runtime_flags", () => {
|
|
5
|
-
it("maps branch flags into branch_state keys", () => {
|
|
6
|
-
const parsed = parse_branch_runtime_flags([
|
|
7
|
-
"--user",
|
|
8
|
-
"erik",
|
|
9
|
-
"--type",
|
|
10
|
-
"feat",
|
|
11
|
-
"--ticket",
|
|
12
|
-
"ABC-123",
|
|
13
|
-
"--description",
|
|
14
|
-
"add-parser",
|
|
15
|
-
"--branch-version",
|
|
16
|
-
"1.2.0",
|
|
17
|
-
]);
|
|
18
|
-
|
|
19
|
-
expect(parsed.branch_state).toEqual({
|
|
20
|
-
user: "erik",
|
|
21
|
-
type: "feat",
|
|
22
|
-
ticket: "ABC-123",
|
|
23
|
-
description: "add-parser",
|
|
24
|
-
version: "1.2.0",
|
|
25
|
-
});
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
it("maps --checkout into checkout", () => {
|
|
29
|
-
const parsed = parse_branch_runtime_flags(["--checkout", "worktree"]);
|
|
30
|
-
|
|
31
|
-
expect(parsed.branch_state).toEqual({
|
|
32
|
-
checkout: "worktree",
|
|
33
|
-
});
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
it("does not set checkout when --checkout is omitted", () => {
|
|
37
|
-
const parsed = parse_branch_runtime_flags([
|
|
38
|
-
"--type",
|
|
39
|
-
"feat",
|
|
40
|
-
"--description",
|
|
41
|
-
"add-parser",
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
expect(parsed.branch_state).toEqual({
|
|
45
|
-
type: "feat",
|
|
46
|
-
description: "add-parser",
|
|
47
|
-
});
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it("honors interactive flag semantics", () => {
|
|
51
|
-
const default_flags = parse_branch_runtime_flags([]);
|
|
52
|
-
const explicit_interactive = parse_branch_runtime_flags(["--interactive"]);
|
|
53
|
-
const no_interactive = parse_branch_runtime_flags(["--no-interactive"]);
|
|
54
|
-
|
|
55
|
-
expect(default_flags.no_interactive).toBe(false);
|
|
56
|
-
expect(explicit_interactive.no_interactive).toBe(false);
|
|
57
|
-
expect(no_interactive.no_interactive).toBe(true);
|
|
58
|
-
});
|
|
59
|
-
|
|
60
|
-
it("parses git-dir and work-tree into git_args", () => {
|
|
61
|
-
const parsed = parse_branch_runtime_flags([
|
|
62
|
-
"--git-dir",
|
|
63
|
-
"/tmp/repo/.git",
|
|
64
|
-
"--work-tree",
|
|
65
|
-
"/tmp/repo",
|
|
66
|
-
]);
|
|
67
|
-
|
|
68
|
-
expect(parsed.git_args).toBe(
|
|
69
|
-
"--git-dir=/tmp/repo/.git --work-tree=/tmp/repo",
|
|
70
|
-
);
|
|
71
|
-
});
|
|
72
|
-
});
|
package/src/branch-args.ts
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
import { parse } from "@bomb.sh/args";
|
|
2
|
-
import { InferOutput } from "valibot";
|
|
3
|
-
import { BranchState } from "./valibot-state";
|
|
4
|
-
|
|
5
|
-
type BranchStateRuntime = InferOutput<typeof BranchState>;
|
|
6
|
-
|
|
7
|
-
type ParsedRuntimeFlags = {
|
|
8
|
-
help: boolean;
|
|
9
|
-
version: boolean;
|
|
10
|
-
git_args: string;
|
|
11
|
-
no_interactive: boolean;
|
|
12
|
-
dry_run: boolean;
|
|
13
|
-
branch_state: Partial<BranchStateRuntime>;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const BRANCH_OPTIONS = [
|
|
17
|
-
"user",
|
|
18
|
-
"type",
|
|
19
|
-
"description",
|
|
20
|
-
"ticket",
|
|
21
|
-
"branch-version",
|
|
22
|
-
"checkout",
|
|
23
|
-
] as const;
|
|
24
|
-
|
|
25
|
-
export const GIT_OPTIONS = ["git-dir", "work-tree"] as const;
|
|
26
|
-
|
|
27
|
-
export const BOOLEAN_FLAGS = [
|
|
28
|
-
"interactive",
|
|
29
|
-
"dry-run",
|
|
30
|
-
"help",
|
|
31
|
-
"version",
|
|
32
|
-
] as const;
|
|
33
|
-
|
|
34
|
-
class BranchFlags {
|
|
35
|
-
#runtime: ParsedRuntimeFlags;
|
|
36
|
-
|
|
37
|
-
constructor(runtime: ParsedRuntimeFlags) {
|
|
38
|
-
this.#runtime = runtime;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
get interactive(): boolean {
|
|
42
|
-
return !this.#runtime.no_interactive;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
get dry_run(): boolean {
|
|
46
|
-
return this.#runtime.dry_run;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
get help(): boolean {
|
|
50
|
-
return this.#runtime.help;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
get version(): boolean {
|
|
54
|
-
return this.#runtime.version;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
get git_args(): string {
|
|
58
|
-
return this.#runtime.git_args;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
get branch_state(): Partial<BranchStateRuntime> {
|
|
62
|
-
return this.#runtime.branch_state;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export const branch_flags = new BranchFlags(
|
|
67
|
-
parse_branch_runtime_flags(process.argv.slice(2)),
|
|
68
|
-
);
|
|
69
|
-
|
|
70
|
-
export function parse_branch_runtime_flags(argv: string[]): ParsedRuntimeFlags {
|
|
71
|
-
const parsed = parse(argv, {
|
|
72
|
-
alias: { h: "help", v: "version" },
|
|
73
|
-
boolean: BOOLEAN_FLAGS,
|
|
74
|
-
string: [...BRANCH_OPTIONS, ...GIT_OPTIONS],
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
const branch_state: Partial<BranchStateRuntime> = {};
|
|
78
|
-
BRANCH_OPTIONS.forEach((value) => {
|
|
79
|
-
const cli_value = parsed[value];
|
|
80
|
-
if (cli_value) {
|
|
81
|
-
const str = (value === "branch-version"
|
|
82
|
-
? "version"
|
|
83
|
-
: value.replace("-", "_")) as keyof BranchStateRuntime;
|
|
84
|
-
if (str === "checkout")
|
|
85
|
-
branch_state[str] =
|
|
86
|
-
(cli_value as "worktree" | "branch" | undefined) ?? "branch";
|
|
87
|
-
else branch_state[str] = cli_value;
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
help: parsed["help"] === true,
|
|
93
|
-
version: parsed["version"] === true,
|
|
94
|
-
git_args: get_git_args(parsed["git-dir"], parsed["work-tree"]),
|
|
95
|
-
no_interactive: parsed["interactive"] === false,
|
|
96
|
-
dry_run: parsed["dry-run"] === true,
|
|
97
|
-
branch_state: branch_state,
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function get_git_args(
|
|
102
|
-
git_dir: string | undefined,
|
|
103
|
-
work_tree: string | undefined,
|
|
104
|
-
): string {
|
|
105
|
-
return `${git_dir ? `--git-dir=${git_dir}` : ""} ${work_tree ? `--work-tree=${work_tree}` : ""}`.trim();
|
|
106
|
-
}
|