Skip to content

Commit f6c8b7d

Browse files
committed
fix(prompt): small code refactor
1 parent 85fca63 commit f6c8b7d

File tree

5 files changed

+47
-72
lines changed

5 files changed

+47
-72
lines changed

.eslintrc.js

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ module.exports = {
5151
'@typescript-eslint/no-var-requires': 'off',
5252
'@typescript-eslint/no-inferrable-types': 'off',
5353
'@typescript-eslint/no-non-null-assertion': 'off',
54+
'@typescript-eslint/triple-slash-reference': 'off',
5455

5556
// TODO: enable those rules?
5657
'no-empty': 'off',

@commitlint/prompt/src/input.test.ts

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import {Answers, PromptModule, QuestionCollection} from 'inquirer';
2+
/// <reference path="./inquirer/inquirer.d.ts" />
23
import input from './input';
3-
import InputCustomPrompt from './inquirer/InputCustomPrompt';
44
import chalk from 'chalk';
55

66
jest.mock(
@@ -75,15 +75,7 @@ function stub(config: Record<string, Record<string, unknown>>): PromptModule {
7575
if (answer == null) {
7676
throw new Error(`Unexpected config name: ${promptConfig.name}`);
7777
}
78-
let validate = promptConfig.validate;
79-
if (promptConfig.type === 'input-custom') {
80-
const customInput = new InputCustomPrompt(
81-
promptConfig,
82-
{write: () => true} as any,
83-
result
84-
) as any;
85-
validate = customInput.opt.validate.bind(customInput);
86-
}
78+
const validate = promptConfig.validate;
8779
if (validate) {
8880
const validationResult = validate(answer, result);
8981
if (validationResult !== true) {

@commitlint/prompt/src/inquirer/InputCustomPrompt.ts

-31
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
// eslint-disable-next-line @typescript-eslint/triple-slash-reference
21
/// <reference path="./inquirer.d.ts" />
32
import {Interface as ReadlineInterface, Key} from 'readline';
43

@@ -10,7 +9,6 @@ import type {Subscription} from 'rxjs/internal/Subscription';
109

1110
import Answers = inquirer.Answers;
1211
import InputCustomOptions = inquirer.InputCustomOptions;
13-
import Validator = inquirer.Validator;
1412
import SuccessfulPromptStateData = inquirer.prompts.SuccessfulPromptStateData;
1513

1614
interface KeyDescriptor {
@@ -46,42 +44,13 @@ export default class InputCustomPrompt<
4644
this.tabCompletion = (this.opt.tabCompletion || [])
4745
.map((item) => item.value)
4846
.sort((a, b) => a.localeCompare(b));
49-
50-
this.opt.validate = this.extendedValidate(this.opt.validate);
5147
}
5248

5349
onEnd(state: SuccessfulPromptStateData): void {
5450
this.lineSubscription.unsubscribe();
5551
super.onEnd(state);
5652
}
5753

58-
extendedValidate(validate?: Validator<TQuestion>): Validator<TQuestion> {
59-
return (input, answers) => {
60-
if (input.length > this.opt.maxLength(answers)) {
61-
return 'Input contains too many characters!';
62-
}
63-
if (this.opt.required && input.trim().length === 0) {
64-
// Show help if enum is defined and input may not be empty
65-
return `⚠ ${chalk.bold(this.opt.name)} may not be empty.`;
66-
}
67-
68-
if (
69-
input.length > 0 &&
70-
this.tabCompletion.length > 0 &&
71-
!this.tabCompletion.includes(input)
72-
) {
73-
return `⚠ ${chalk.bold(
74-
this.opt.name
75-
)} must be one of ${this.tabCompletion.join(', ')}.`;
76-
}
77-
78-
if (validate) {
79-
return validate(input, answers);
80-
}
81-
return true;
82-
};
83-
}
84-
8554
/**
8655
* @see https://nodejs.org/api/readline.html#readline_rl_write_data_key
8756
* @see https://nodejs.org/api/readline.html#readline_rl_line

@commitlint/prompt/src/inquirer/inquirer.d.ts

-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ declare module 'inquirer' {
1212
* @inheritdoc
1313
*/
1414
type?: 'input-custom';
15-
required?: boolean;
1615
log?(answers?: T): string;
1716
tabCompletion?: InputCustomCompletionOption[];
1817
maxLength(answers?: T): number;

@commitlint/prompt/src/library/get-prompt.ts

+44-30
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import chalk from 'chalk';
2-
3-
import {InputCustomOptions, InputQuestion, ListQuestion} from 'inquirer';
2+
import {InputCustomOptions} from 'inquirer';
43

54
import type {InputSetting, RuleEntry, Result, ResultPart} from './types';
65

@@ -30,11 +29,7 @@ export default function getPrompt(
3029
type: ResultPart,
3130
rules: RuleEntry[] = [],
3231
settings: InputSetting = {}
33-
):
34-
| InputQuestion<Result>
35-
| ListQuestion<Result>
36-
| InputCustomOptions<Result>
37-
| null {
32+
): InputCustomOptions<Result> | null {
3833
const emptyRule = rules.filter(getHasName('empty')).find(ruleIsActive);
3934

4035
const mustBeEmpty = emptyRule ? ruleIsApplicable(emptyRule) : false;
@@ -55,23 +50,53 @@ export default function getPrompt(
5550

5651
const enumRule = rules.filter(getHasName('enum')).find(enumRuleIsActive);
5752

53+
const tabCompletion = enumRule
54+
? enumRule[1][2].map((enumerable) => {
55+
const enumSettings = (settings.enumerables || {})[enumerable] || {};
56+
return {
57+
value: forceLeadingBlankFn(forceCaseFn(enumerable)),
58+
description: enumSettings.description || '',
59+
};
60+
})
61+
: [];
62+
63+
const maxLength = (res: Result) => {
64+
let remainingHeaderLength = Infinity;
65+
if (settings.header && settings.header.length) {
66+
const header = format({
67+
type: res.type,
68+
scope: res.scope,
69+
subject: res.subject,
70+
});
71+
remainingHeaderLength = settings.header.length - header.length;
72+
}
73+
return Math.min(inputMaxLength, remainingHeaderLength);
74+
};
75+
5876
return {
5977
type: 'input-custom',
6078
name: type,
6179
message: `${type}:`,
62-
validate(): boolean | string {
80+
validate(input, answers) {
81+
if (input.length > maxLength(answers || {})) {
82+
return 'Input contains too many characters!';
83+
}
84+
if (required && input.trim().length === 0) {
85+
// Show help if enum is defined and input may not be empty
86+
return `⚠ ${chalk.bold(type)} may not be empty.`;
87+
}
88+
89+
const tabValues = tabCompletion.map((item) => item.value);
90+
if (
91+
input.length > 0 &&
92+
tabValues.length > 0 &&
93+
!tabValues.includes(input)
94+
) {
95+
return `⚠ ${chalk.bold(type)} must be one of ${tabValues.join(', ')}.`;
96+
}
6397
return true;
6498
},
65-
tabCompletion: enumRule
66-
? enumRule[1][2].map((enumerable) => {
67-
const enumSettings = (settings.enumerables || {})[enumerable] || {};
68-
return {
69-
value: forceLeadingBlankFn(forceCaseFn(enumerable)),
70-
description: enumSettings.description || '',
71-
};
72-
})
73-
: [],
74-
required,
99+
tabCompletion,
75100
log(answers?: Result) {
76101
let prefix =
77102
`${chalk.white('Please enter a')} ${chalk.bold(type)}: ${meta({
@@ -90,18 +115,7 @@ export default function getPrompt(
90115
}
91116
return prefix + EOL;
92117
},
93-
maxLength(res: Result) {
94-
let remainingHeaderLength = Infinity;
95-
if (settings.header && settings.header.length) {
96-
const header = format({
97-
type: res.type,
98-
scope: res.scope,
99-
subject: res.subject,
100-
});
101-
remainingHeaderLength = settings.header.length - header.length;
102-
}
103-
return Math.min(inputMaxLength, remainingHeaderLength);
104-
},
118+
maxLength,
105119
transformer(value: string) {
106120
return forceCaseFn(value);
107121
},

0 commit comments

Comments
 (0)