format-commit 1.0.0 → 1.1.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 +101 -33
- package/lib/ai-service.js +124 -103
- package/lib/commit.js +75 -106
- package/lib/create-branch.js +35 -5
- package/lib/index.js +6 -5
- package/lib/options.json +8 -8
- package/lib/setup.js +33 -6
- package/lib/utils.js +397 -13
- package/package.json +1 -1
package/lib/commit.js
CHANGED
|
@@ -15,90 +15,6 @@ const options = JSON.parse(
|
|
|
15
15
|
readFileSync(join(__dirname, 'options.json'), 'utf-8')
|
|
16
16
|
);
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
/** Parse and validate commit title format, auto-correct case */
|
|
20
|
-
const parseAndNormalizeCommitTitle = (title, config) => {
|
|
21
|
-
let type, scope, message, detectedFormatGroup;
|
|
22
|
-
|
|
23
|
-
// Try different format patterns
|
|
24
|
-
const format7_8 = /^([^(]+)\(([^)]+)\):\s*(.+)$/; // type(scope): message
|
|
25
|
-
const format5_6 = /^([^(]+)\(([^)]+)\)\s+(.+)$/; // type(scope) message
|
|
26
|
-
const format3_4 = /^([^:]+):\s*(.+)$/; // type: message
|
|
27
|
-
const format1_2 = /^\(([^)]+)\)\s+(.+)$/; // (type) message
|
|
28
|
-
|
|
29
|
-
let match;
|
|
30
|
-
|
|
31
|
-
if ((match = title.match(format7_8))) {
|
|
32
|
-
[, type, scope, message] = match;
|
|
33
|
-
detectedFormatGroup = 'type(scope):';
|
|
34
|
-
} else if ((match = title.match(format5_6))) {
|
|
35
|
-
[, type, scope, message] = match;
|
|
36
|
-
detectedFormatGroup = 'type(scope)';
|
|
37
|
-
} else if ((match = title.match(format3_4))) {
|
|
38
|
-
[, type, message] = match;
|
|
39
|
-
detectedFormatGroup = 'type:';
|
|
40
|
-
} else if ((match = title.match(format1_2))) {
|
|
41
|
-
[, type, message] = match;
|
|
42
|
-
detectedFormatGroup = '(type)';
|
|
43
|
-
} else {
|
|
44
|
-
return { error: 'Invalid commit format. Expected format with type prefix.' };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Verify format matches config
|
|
48
|
-
let expectedFormatGroup;
|
|
49
|
-
if (config.format >= 7) {
|
|
50
|
-
expectedFormatGroup = 'type(scope):';
|
|
51
|
-
} else if (config.format >= 5) {
|
|
52
|
-
expectedFormatGroup = 'type(scope)';
|
|
53
|
-
} else if (config.format >= 3) {
|
|
54
|
-
expectedFormatGroup = 'type:';
|
|
55
|
-
} else {
|
|
56
|
-
expectedFormatGroup = '(type)';
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (detectedFormatGroup !== expectedFormatGroup) {
|
|
60
|
-
const exampleTitle = utils.formatCommitTitle(
|
|
61
|
-
config.types[0].value,
|
|
62
|
-
'name',
|
|
63
|
-
config.format,
|
|
64
|
-
config.scopes?.[0]?.value
|
|
65
|
-
);
|
|
66
|
-
return { error: `Wrong format. Expected: "${exampleTitle}"` };
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
type = type.trim();
|
|
70
|
-
message = message.trim();
|
|
71
|
-
if (scope) {
|
|
72
|
-
scope = scope.trim();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Validate type exists (case-insensitive)
|
|
76
|
-
const validType = config.types.find(t => t.value.toLowerCase() === type.toLowerCase());
|
|
77
|
-
if (!validType) {
|
|
78
|
-
const validTypes = config.types.map(t => t.value).join(', ');
|
|
79
|
-
return { error: `Invalid type "${type}". Valid types: ${validTypes}` };
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// Validate scope if present (case-insensitive)
|
|
83
|
-
let validScope = scope;
|
|
84
|
-
if (scope) {
|
|
85
|
-
if (!config.scopes || config.scopes.length === 0) {
|
|
86
|
-
return { error: 'Scope not allowed in current format configuration' };
|
|
87
|
-
}
|
|
88
|
-
const foundScope = config.scopes.find(s => s.value.toLowerCase() === scope.toLowerCase());
|
|
89
|
-
if (!foundScope) {
|
|
90
|
-
const validScopes = config.scopes.map(s => s.value).join(', ');
|
|
91
|
-
return { error: `Invalid scope "${scope}". Valid scopes: ${validScopes}` };
|
|
92
|
-
}
|
|
93
|
-
validScope = foundScope.value;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Re-format with correct case
|
|
97
|
-
const normalized = utils.formatCommitTitle(validType.value, message, config.format, validScope);
|
|
98
|
-
|
|
99
|
-
return { normalized };
|
|
100
|
-
};
|
|
101
|
-
|
|
102
18
|
/** Prompt with pre-filled text (editable) using readline */
|
|
103
19
|
const promptWithPrefill = (message, prefill, validate) => {
|
|
104
20
|
return new Promise((resolve, reject) => {
|
|
@@ -108,7 +24,7 @@ const promptWithPrefill = (message, prefill, validate) => {
|
|
|
108
24
|
output: process.stdout
|
|
109
25
|
});
|
|
110
26
|
|
|
111
|
-
const questionText = `${kleur.bold(message)}\n
|
|
27
|
+
const questionText = `${kleur.bold(message)}\n> `;
|
|
112
28
|
|
|
113
29
|
// Handle Ctrl+C to cancel
|
|
114
30
|
rl.on('SIGINT', () => {
|
|
@@ -186,19 +102,23 @@ const finalizeCommit = async (title, description, commitData, currentBranch, tes
|
|
|
186
102
|
};
|
|
187
103
|
|
|
188
104
|
|
|
189
|
-
export default async function commit(config, testMode) {
|
|
105
|
+
export default async function commit(config, testMode, debugMode) {
|
|
190
106
|
if (!config) {
|
|
191
107
|
return;
|
|
192
108
|
}
|
|
193
109
|
utils.log('new commit');
|
|
194
110
|
|
|
195
|
-
if (!
|
|
111
|
+
if (!utils.hasStagedChanges()) {
|
|
196
112
|
utils.log('No staged changes found - stage your changes with git add', 'error');
|
|
197
113
|
return;
|
|
198
114
|
}
|
|
199
115
|
|
|
200
116
|
if (testMode) {
|
|
201
|
-
utils.log('test mode enabled
|
|
117
|
+
utils.log('test mode enabled (commit will not be performed)', 'warning');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (debugMode) {
|
|
121
|
+
utils.log('debug mode enabled (additional visible logs)', 'debug');
|
|
202
122
|
}
|
|
203
123
|
|
|
204
124
|
// Get current git branch for version change option "only on release branch"
|
|
@@ -212,22 +132,71 @@ export default async function commit(config, testMode) {
|
|
|
212
132
|
return;
|
|
213
133
|
}
|
|
214
134
|
|
|
135
|
+
if (config.format === 'custom') {
|
|
136
|
+
const formatValid = utils.validateCustomFormatPattern(config.customFormat);
|
|
137
|
+
if (formatValid !== true) {
|
|
138
|
+
utils.log(`Invalid custom format - ${formatValid}`, 'error');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const formatNeedsScope = config.format === 'custom'
|
|
144
|
+
? utils.customFormatHasScope(config.customFormat)
|
|
145
|
+
: config.format >= 5;
|
|
146
|
+
|
|
215
147
|
const noScope = !config.scopes || (config.scopes && config.scopes.length === 0);
|
|
216
|
-
if (
|
|
148
|
+
if (formatNeedsScope && noScope) {
|
|
217
149
|
utils.log('no scopes defined - update config or format option', 'error');
|
|
218
150
|
return;
|
|
219
151
|
}
|
|
220
152
|
|
|
153
|
+
// Warn if using default example scope and skip AI suggestions
|
|
154
|
+
const hasDefaultScope = config.scopes && config.scopes.length === 1 && config.scopes[0].value === 'example';
|
|
155
|
+
if (hasDefaultScope) {
|
|
156
|
+
utils.log('You are using the default scope "example" - customize your scopes in commit-config.json or run `format-commit --config`', 'warning');
|
|
157
|
+
utils.log('AI suggestions skipped - configure your scopes', 'warning');
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Collect custom field values early (needed before AI and classic flows)
|
|
161
|
+
let cancelled = false;
|
|
162
|
+
let customFieldValues = {};
|
|
163
|
+
if (config.format === 'custom') {
|
|
164
|
+
const fields = utils.getCustomFields(config.customFormat);
|
|
165
|
+
for (const label of fields) {
|
|
166
|
+
const resp = await prompts({
|
|
167
|
+
type: 'text',
|
|
168
|
+
name: 'value',
|
|
169
|
+
message: `${label}?`,
|
|
170
|
+
validate: v => (!v || !v.trim()) ? `${label} cannot be empty` : true,
|
|
171
|
+
}, {
|
|
172
|
+
onCancel: () => {
|
|
173
|
+
cancelled = true;
|
|
174
|
+
return false;
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
if (cancelled) {
|
|
178
|
+
utils.log('commit cancelled', 'error');
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
customFieldValues[label] = resp.value;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
221
185
|
let commitTitle = null;
|
|
222
186
|
let useAISuggestion = false;
|
|
223
187
|
|
|
224
|
-
|
|
225
|
-
if (config.ai?.enabled) {
|
|
188
|
+
if (config.ai?.enabled && !hasDefaultScope) {
|
|
226
189
|
utils.log('generating suggestions...');
|
|
227
190
|
const aiService = await import('./ai-service.js');
|
|
228
|
-
|
|
191
|
+
let suggestions = [];
|
|
192
|
+
|
|
193
|
+
try {
|
|
194
|
+
suggestions = await aiService.generateCommitSuggestions(config, debugMode, customFieldValues);
|
|
195
|
+
} catch (err) {
|
|
196
|
+
utils.log(`AI suggestions failed (${err.message})`, 'warning');
|
|
197
|
+
}
|
|
229
198
|
|
|
230
|
-
if (suggestions
|
|
199
|
+
if (suggestions?.length) {
|
|
231
200
|
let aiCancelled = false;
|
|
232
201
|
const aiChoice = await prompts({
|
|
233
202
|
type: 'select',
|
|
@@ -260,7 +229,7 @@ export default async function commit(config, testMode) {
|
|
|
260
229
|
}
|
|
261
230
|
|
|
262
231
|
// Parse and validate format, type, and scope
|
|
263
|
-
const result = parseAndNormalizeCommitTitle(val, config);
|
|
232
|
+
const result = utils.parseAndNormalizeCommitTitle(val, config, customFieldValues);
|
|
264
233
|
if (result.error) {
|
|
265
234
|
return result.error;
|
|
266
235
|
}
|
|
@@ -276,7 +245,7 @@ export default async function commit(config, testMode) {
|
|
|
276
245
|
);
|
|
277
246
|
|
|
278
247
|
// Normalize the final title (correct case)
|
|
279
|
-
const result = parseAndNormalizeCommitTitle(rawTitle, config);
|
|
248
|
+
const result = utils.parseAndNormalizeCommitTitle(rawTitle, config, customFieldValues);
|
|
280
249
|
commitTitle = result.normalized;
|
|
281
250
|
useAISuggestion = true;
|
|
282
251
|
} catch (err) {
|
|
@@ -286,13 +255,9 @@ export default async function commit(config, testMode) {
|
|
|
286
255
|
return;
|
|
287
256
|
}
|
|
288
257
|
}
|
|
289
|
-
} else {
|
|
290
|
-
utils.log('AI suggestions failed, using manual input', 'warning');
|
|
291
258
|
}
|
|
292
259
|
}
|
|
293
260
|
|
|
294
|
-
let cancelled = false;
|
|
295
|
-
|
|
296
261
|
// If AI suggestion was accepted, only ask for description, version, and push
|
|
297
262
|
if (useAISuggestion) {
|
|
298
263
|
const commit = await prompts([
|
|
@@ -323,7 +288,7 @@ export default async function commit(config, testMode) {
|
|
|
323
288
|
validate: val => utils.validVersion(val),
|
|
324
289
|
},
|
|
325
290
|
{
|
|
326
|
-
type: 'confirm',
|
|
291
|
+
type: testMode ? null : 'confirm',
|
|
327
292
|
name: 'pushAfterCommit',
|
|
328
293
|
message: 'Push changes?',
|
|
329
294
|
initial: false,
|
|
@@ -344,7 +309,7 @@ export default async function commit(config, testMode) {
|
|
|
344
309
|
return;
|
|
345
310
|
}
|
|
346
311
|
|
|
347
|
-
// Classic flow: ask for type and scope
|
|
312
|
+
// Classic flow: ask for type and scope
|
|
348
313
|
const typeScope = await prompts([
|
|
349
314
|
{
|
|
350
315
|
type: 'select',
|
|
@@ -353,7 +318,7 @@ export default async function commit(config, testMode) {
|
|
|
353
318
|
choices: config.types,
|
|
354
319
|
},
|
|
355
320
|
{
|
|
356
|
-
type:
|
|
321
|
+
type: formatNeedsScope ? 'select' : null,
|
|
357
322
|
name: 'scope',
|
|
358
323
|
message: 'Scope',
|
|
359
324
|
choices: config.scopes,
|
|
@@ -370,7 +335,7 @@ export default async function commit(config, testMode) {
|
|
|
370
335
|
return;
|
|
371
336
|
}
|
|
372
337
|
|
|
373
|
-
// Ask for title with full formatted length validation, then description,
|
|
338
|
+
// Ask for title with full formatted length validation, then description, and options
|
|
374
339
|
const commit = await prompts([
|
|
375
340
|
{
|
|
376
341
|
type: 'text',
|
|
@@ -380,7 +345,10 @@ export default async function commit(config, testMode) {
|
|
|
380
345
|
if (!val || val.trim().length === 0) {
|
|
381
346
|
return 'Commit title cannot be empty';
|
|
382
347
|
}
|
|
383
|
-
const fullTitle = utils.formatCommitTitle(
|
|
348
|
+
const fullTitle = utils.formatCommitTitle(
|
|
349
|
+
typeScope.type, val, config.format, typeScope.scope,
|
|
350
|
+
config.customFormat, customFieldValues
|
|
351
|
+
);
|
|
384
352
|
return utils.validCommitTitle(fullTitle, config.minLength, config.maxLength);
|
|
385
353
|
},
|
|
386
354
|
},
|
|
@@ -429,12 +397,13 @@ export default async function commit(config, testMode) {
|
|
|
429
397
|
return;
|
|
430
398
|
}
|
|
431
399
|
|
|
432
|
-
// Format changes message and commit it
|
|
433
400
|
commitTitle = utils.formatCommitTitle(
|
|
434
401
|
typeScope.type,
|
|
435
402
|
commit.title,
|
|
436
403
|
config.format,
|
|
437
|
-
typeScope.scope
|
|
404
|
+
typeScope.scope,
|
|
405
|
+
config.customFormat,
|
|
406
|
+
customFieldValues
|
|
438
407
|
);
|
|
439
408
|
|
|
440
409
|
await finalizeCommit(commitTitle, commit.description, commit, currentBranch, testMode);
|
package/lib/create-branch.js
CHANGED
|
@@ -23,14 +23,36 @@ export default async function createBranch(config, testMode) {
|
|
|
23
23
|
return;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
const isCustom = config.branchFormat === 'custom';
|
|
27
|
+
const customHasScope = isCustom && config.customBranchFormat && utils.customBranchFormatHasScope(config.customBranchFormat);
|
|
28
|
+
const needsScope = config.branchFormat === 2 || customHasScope;
|
|
29
|
+
|
|
26
30
|
const noScope = !config.scopes || (config.scopes && config.scopes.length === 0);
|
|
27
|
-
if (
|
|
31
|
+
if (needsScope && noScope) {
|
|
28
32
|
utils.log('no scopes defined - update config or branch format option', 'error');
|
|
29
33
|
return;
|
|
30
34
|
}
|
|
31
35
|
|
|
36
|
+
if (isCustom) {
|
|
37
|
+
const formatValid = utils.validateCustomBranchFormatPattern(config.customBranchFormat);
|
|
38
|
+
if (formatValid !== true) {
|
|
39
|
+
utils.log(`Invalid custom branch format - ${formatValid}`, 'error');
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Collect custom fields if custom format
|
|
45
|
+
const customFields = isCustom ? utils.getCustomBranchFields(config.customBranchFormat) : [];
|
|
46
|
+
const customFieldPrompts = customFields.map(field => ({
|
|
47
|
+
type: 'text',
|
|
48
|
+
name: `custom_${field}`,
|
|
49
|
+
message: `${field}?`,
|
|
50
|
+
validate: val => utils.validBranchCustomField(val, field),
|
|
51
|
+
}));
|
|
52
|
+
|
|
32
53
|
let cancelled = false;
|
|
33
54
|
const branch = await prompts([
|
|
55
|
+
...customFieldPrompts,
|
|
34
56
|
{
|
|
35
57
|
type: 'select',
|
|
36
58
|
name: 'type',
|
|
@@ -38,7 +60,7 @@ export default async function createBranch(config, testMode) {
|
|
|
38
60
|
choices: config.types,
|
|
39
61
|
},
|
|
40
62
|
{
|
|
41
|
-
type:
|
|
63
|
+
type: needsScope ? 'select' : null,
|
|
42
64
|
name: 'scope',
|
|
43
65
|
message: 'Scope',
|
|
44
66
|
choices: config.scopes,
|
|
@@ -50,7 +72,7 @@ export default async function createBranch(config, testMode) {
|
|
|
50
72
|
validate: val => utils.validBranchDescription(val, config.maxLength),
|
|
51
73
|
},
|
|
52
74
|
{
|
|
53
|
-
type: 'confirm',
|
|
75
|
+
type: testMode ? null : 'confirm',
|
|
54
76
|
name: 'checkoutAfterCreate',
|
|
55
77
|
message: 'Switch to the new branch after creation?',
|
|
56
78
|
initial: true,
|
|
@@ -67,17 +89,25 @@ export default async function createBranch(config, testMode) {
|
|
|
67
89
|
return;
|
|
68
90
|
}
|
|
69
91
|
|
|
92
|
+
// Build custom field values from prompt answers
|
|
93
|
+
const customFieldValues = {};
|
|
94
|
+
for (const field of customFields) {
|
|
95
|
+
customFieldValues[field] = branch[`custom_${field}`];
|
|
96
|
+
}
|
|
97
|
+
|
|
70
98
|
// Format branch name and create it
|
|
71
99
|
utils.log('create branch...');
|
|
72
100
|
const branchName = utils.formatBranchName(
|
|
73
101
|
branch.type,
|
|
74
102
|
branch.description,
|
|
75
103
|
config.branchFormat,
|
|
76
|
-
branch.scope
|
|
104
|
+
branch.scope,
|
|
105
|
+
config.customBranchFormat,
|
|
106
|
+
customFieldValues
|
|
77
107
|
);
|
|
78
108
|
|
|
79
109
|
if (testMode) {
|
|
80
|
-
utils.log(`
|
|
110
|
+
utils.log(`branch name: ${branchName}`, 'warning');
|
|
81
111
|
return;
|
|
82
112
|
}
|
|
83
113
|
|
package/lib/index.js
CHANGED
|
@@ -26,12 +26,13 @@ program
|
|
|
26
26
|
.version('0.3.1')
|
|
27
27
|
.option('-b, --branch', 'create a new branch with standardized naming')
|
|
28
28
|
.option('-c, --config', 'generate a configuration file on your project for format-commit')
|
|
29
|
-
.option('-t, --test', 'start without finalize commit (for tests)')
|
|
29
|
+
.option('-t, --test', 'start without finalize commit (for tests)')
|
|
30
|
+
.option('-d, --debug', 'display additional logs');
|
|
30
31
|
|
|
31
32
|
try {
|
|
32
33
|
program.parse(process.argv);
|
|
33
|
-
} catch (
|
|
34
|
-
console.error('Error parsing arguments:',
|
|
34
|
+
} catch (err) {
|
|
35
|
+
console.error('Error parsing arguments:', err.message);
|
|
35
36
|
process.exit(1);
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -49,7 +50,7 @@ try {
|
|
|
49
50
|
utils.log('no configuration found', 'warning');
|
|
50
51
|
const setupResult = await setup(true);
|
|
51
52
|
if (setupResult && setupResult.commitAfter) {
|
|
52
|
-
commit(setupResult.config, opts.test);
|
|
53
|
+
commit(setupResult.config, opts.test, opts.debug);
|
|
53
54
|
}
|
|
54
55
|
} else {
|
|
55
56
|
if (opts.branch) {
|
|
@@ -57,7 +58,7 @@ try {
|
|
|
57
58
|
createBranch(JSON.parse(data), opts.test);
|
|
58
59
|
return;
|
|
59
60
|
}
|
|
60
|
-
commit(JSON.parse(data), opts.test);
|
|
61
|
+
commit(JSON.parse(data), opts.test, opts.debug);
|
|
61
62
|
}
|
|
62
63
|
});
|
|
63
64
|
})();
|
package/lib/options.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"configFile": "commit-config",
|
|
3
3
|
"commitFormats": [
|
|
4
|
-
{ "value": 1, "title": "(type)
|
|
5
|
-
{ "value": 2, "title": "(type)
|
|
6
|
-
{ "value": 3, "title": "type:
|
|
7
|
-
{ "value": 4, "title": "type:
|
|
8
|
-
{ "value": 5, "title": "type(scope)
|
|
9
|
-
{ "value": 6, "title": "type(scope)
|
|
10
|
-
{ "value": 7, "title": "type(scope):
|
|
11
|
-
{ "value": 8, "title": "type(scope):
|
|
4
|
+
{ "value": 1, "title": "(type) Description" },
|
|
5
|
+
{ "value": 2, "title": "(type) description" },
|
|
6
|
+
{ "value": 3, "title": "type: Description" },
|
|
7
|
+
{ "value": 4, "title": "type: description" },
|
|
8
|
+
{ "value": 5, "title": "type(scope) Description" },
|
|
9
|
+
{ "value": 6, "title": "type(scope) description" },
|
|
10
|
+
{ "value": 7, "title": "type(scope): Description" },
|
|
11
|
+
{ "value": 8, "title": "type(scope): description" }
|
|
12
12
|
],
|
|
13
13
|
"branchFormats": [
|
|
14
14
|
{ "value": 1, "title": "type/description" },
|
package/lib/setup.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import prompts from 'prompts';
|
|
2
|
+
import kleur from 'kleur';
|
|
2
3
|
import fs, { readFileSync } from 'fs';
|
|
3
4
|
import * as utils from './utils.js';
|
|
4
5
|
import * as envUtils from './env-utils.js';
|
|
@@ -29,13 +30,31 @@ export default async function setup(askForCommitAfter) {
|
|
|
29
30
|
type: 'select',
|
|
30
31
|
name: 'format',
|
|
31
32
|
message: 'Commit messages nomenclature',
|
|
32
|
-
choices:
|
|
33
|
+
choices: [
|
|
34
|
+
...options.commitFormats,
|
|
35
|
+
{ value: 'custom', title: kleur.gray('Custom - define your own pattern') },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
type: prev => prev === 'custom' ? 'text' : null,
|
|
40
|
+
name: 'customFormat',
|
|
41
|
+
message: 'Custom format pattern (e.g. `{Issue ID} - type - scope - Description`)',
|
|
42
|
+
validate: val => utils.validateCustomFormatPattern(val),
|
|
33
43
|
},
|
|
34
44
|
{
|
|
35
45
|
type: 'select',
|
|
36
46
|
name: 'branchFormat',
|
|
37
47
|
message: 'Branch names nomenclature',
|
|
38
|
-
choices:
|
|
48
|
+
choices: [
|
|
49
|
+
...options.branchFormats,
|
|
50
|
+
{ value: 'custom', title: kleur.gray('Custom - define your own pattern') },
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
type: prev => prev === 'custom' ? 'text' : null,
|
|
55
|
+
name: 'customBranchFormat',
|
|
56
|
+
message: 'Custom branch format pattern (e.g. `{Issue ID}-type/description`)',
|
|
57
|
+
validate: val => utils.validateCustomBranchFormatPattern(val),
|
|
39
58
|
},
|
|
40
59
|
{
|
|
41
60
|
type: 'number',
|
|
@@ -196,13 +215,20 @@ export default async function setup(askForCommitAfter) {
|
|
|
196
215
|
}
|
|
197
216
|
|
|
198
217
|
// Parse prompt data and write config file
|
|
218
|
+
const needsScope = (
|
|
219
|
+
(configChoices.format !== 'custom' && configChoices.format >= 5) ||
|
|
220
|
+
(configChoices.format === 'custom' && utils.customFormatHasScope(configChoices.customFormat)) ||
|
|
221
|
+
configChoices.branchFormat === 2 ||
|
|
222
|
+
(configChoices.branchFormat === 'custom' && utils.customBranchFormatHasScope(configChoices.customBranchFormat))
|
|
223
|
+
);
|
|
224
|
+
|
|
199
225
|
const config = {
|
|
200
226
|
format: configChoices.format,
|
|
227
|
+
customFormat: configChoices.format === 'custom' ? configChoices.customFormat : undefined,
|
|
201
228
|
branchFormat: configChoices.branchFormat,
|
|
229
|
+
customBranchFormat: configChoices.branchFormat === 'custom' ? configChoices.customBranchFormat : undefined,
|
|
202
230
|
types: defaultConfig.types,
|
|
203
|
-
scopes:
|
|
204
|
-
? defaultConfig.scopes
|
|
205
|
-
: undefined,
|
|
231
|
+
scopes: needsScope ? defaultConfig.scopes : undefined,
|
|
206
232
|
minLength: configChoices.minLength,
|
|
207
233
|
maxLength: configChoices.maxLength,
|
|
208
234
|
changeVersion: configChoices.changeVersion,
|
|
@@ -255,8 +281,9 @@ export default async function setup(askForCommitAfter) {
|
|
|
255
281
|
try {
|
|
256
282
|
fs.writeFileSync(`./${options.configFile}.json`, parsedConfig);
|
|
257
283
|
utils.log('config file successfully created', 'success');
|
|
284
|
+
utils.log(`Customize default types and scopes in ${options.configFile}.json`);
|
|
258
285
|
} catch (err) {
|
|
259
|
-
utils.log(`
|
|
286
|
+
utils.log(`Unable to save config file: ${err.message}`, 'error');
|
|
260
287
|
return;
|
|
261
288
|
}
|
|
262
289
|
|