Skip to content

Commit 1d27083

Browse files
committed
test(cz-commitlint): Add Util Function Tests
1 parent 5b60836 commit 1d27083

File tree

11 files changed

+347
-197
lines changed

11 files changed

+347
-197
lines changed

@commitlint/cz-commitlint/README.md

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
> Commitizen adapter using the commitlint.config.js
2+
3+
# @commitlint/cz-commitlint
4+
5+
This is a commitizen adapter, using this adapter, commitizen works based on commitlint.config.js.
6+
7+
Submit by commitizen, lint by commitlint, just need maintain one configuration file, Consistent and Scalable.
8+
9+
The interactive process is inspired by [cz-conventional-changelog](https://github.com/commitizen/cz-conventional-changelog).
10+
11+
## Getting started
12+
13+
### Using commitizen adapter
14+
15+
```bash
16+
npm install --save-dev @commitlint/cz-commitlint commitizen
17+
```
18+
19+
In package.json
20+
21+
```
22+
{
23+
"scripts": {
24+
"commit": "git-cz"
25+
},
26+
"config": {
27+
"commitizen": {
28+
"path": "@commitlint/cz-commitlint"
29+
}
30+
}
31+
}
32+
```
33+
34+
### Configure commitlint
35+
36+
```bash
37+
# Install commitlint cli and conventional config
38+
npm install --save-dev @commitlint/config-conventional @commitlint/cli
39+
40+
# Simple: config with conventional
41+
echo "module.exports = {extends: ['@commitlint/config-conventional']};" > commitlint.config.js
42+
43+
# commitlint configuration is shareable,
44+
# Install lerna-scopes
45+
npm install --save-dev @commitlint/config-lerna-scopes
46+
# Scalable: config with lerna-scopes in monorepo mode
47+
echo "module.exports = {extends: ['@commitlint/config-conventional', '@commitlint/config-lerna-scopes']};" > commitlint.config.js
48+
```
49+
50+
### Set Git Hooks by husky
51+
52+
```base
53+
54+
# ------- using npm ----------
55+
# Install Husky
56+
npm install husky --save-dev
57+
# Active hooks
58+
npx husky install
59+
# Add commitlint hook
60+
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit $1'
61+
# Add commitizen hook
62+
npx husky add .husky/prepare-commit-msg 'exec < /dev/tty && node_modules/.bin/cz --hook || true'
63+
64+
65+
# ------- using yarn ----------
66+
# Install Husky
67+
yarn add husky --dev
68+
# Active hooks
69+
yarn husky install
70+
# Add commitlint hook
71+
yarn husky add .husky/commit-msg 'yarn --no-install commitlint --edit $1'
72+
# Add commitizen hook
73+
yarn husky add .husky/prepare-commit-msg 'exec < /dev/tty && node_modules/.bin/cz --hook || true'
74+
75+
```
76+
77+
### Try it out
78+
79+
```bash
80+
git add .
81+
npm run commit
82+
# or
83+
yarn run commit
84+
```
85+
86+
## Related
87+
88+
- [Commitlint Shared Configuration](https://github.com/conventional-changelog/commitlint#shared-configuration) - You can find more shared configurations are available to install and use with commitlint

@commitlint/cz-commitlint/TODO

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
[] jest Test
2+
[] insert prompt settings to commitlint.config.js
3+
[] support emoji and title
4+
[] support multi line
5+
[] recognize "signed-off-by" and "references-empty" rules

@commitlint/cz-commitlint/package.json

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
2-
"name": "cz-conventional-changelog",
2+
"name": "cz-commitlint",
33
"version": "1.0.0",
44
"description": "Commitizen adapter using the commitlint.config.js",
55
"main": "./lib/index.js",
@@ -30,9 +30,6 @@
3030
"lodash": "^4.17.21",
3131
"word-wrap": "^1.2.3"
3232
},
33-
"devDependencies": {
34-
"@types/inquirer": "^7.3.1"
35-
},
3633
"peerDependencies": {
3734
"commitizen": "^4.0.3",
3835
"inquirer": "^8.0.0"

@commitlint/cz-commitlint/src/Prompter.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import {Answers, DistinctQuestion, Inquirer} from 'inquirer';
33
import wrap from 'word-wrap';
44
import Question, {QuestionConfig} from './Question';
55
import {PromptConfig, PromptName, Rule, RuleField} from './types';
6-
import getForcedCaseFn from './utils/case-fn';
6+
import getCaseFn from './utils/case-fn';
77
import getFullStopFn from './utils/full-stop-fn';
88
import getLeadingBlankFn from './utils/leading-blank-fn';
99
import {
@@ -96,60 +96,54 @@ export default class Prompter {
9696
.filter((name) => name in this.prompts.questions)
9797
.map((name) => {
9898
const {questions, messages} = this.prompts;
99-
const instance = new Question(name, {
99+
100+
const questionConfigs = {
100101
messages: {
101-
title: questions[name]?.description,
102+
title: questions[name]?.description ?? '',
102103
...messages,
103104
},
104105
maxLength: footerMaxLength,
105106
minLength: footerMinLength,
106-
});
107-
108-
const combineFooter = this.combineFooter.bind(this);
109-
instance.onBeforeAsk = function (answers) {
110-
const remainLength = footerMaxLength - combineFooter(answers).length;
111-
this.maxLength = Math.min(this.maxLength, remainLength);
112-
this.minLength = Math.max(this.minLength, footerMinLength);
113107
};
114108

115109
if (name === 'isBreaking') {
116-
instance.setQuestionProperty({
117-
default: false,
110+
Object.assign(questionConfigs, {
111+
defaultValue: false,
118112
});
119113
}
120114

121115
if (name === 'breaking') {
122-
instance.setQuestionProperty({
116+
Object.assign(questionConfigs, {
123117
when: (answers: Answers) => {
124118
return answers.isBreaking;
125119
},
126120
});
127121
}
128122

129123
if (name === 'breakingBody') {
130-
instance.setQuestionProperty({
124+
Object.assign(questionConfigs, {
131125
when: (answers: Answers) => {
132126
return answers.isBreaking && !answers.body;
133127
},
134128
});
135129
}
136130

137131
if (name === 'isIssueAffected') {
138-
instance.setQuestionProperty({
132+
Object.assign(questionConfigs, {
139133
default: false,
140134
});
141135
}
142136

143137
if (name === 'issues') {
144-
instance.setQuestionProperty({
138+
Object.assign(questionConfigs, {
145139
when: (answers: Answers) => {
146140
return answers.isIssueAffected;
147141
},
148142
});
149143
}
150144

151145
if (name === 'issuesBody') {
152-
instance.setQuestionProperty({
146+
Object.assign(questionConfigs, {
153147
when: (answers: Answers) => {
154148
return (
155149
answers.isIssueAffected &&
@@ -159,6 +153,13 @@ export default class Prompter {
159153
},
160154
});
161155
}
156+
const instance = new Question(name, questionConfigs);
157+
const combineFooter = this.combineFooter.bind(this);
158+
instance.onBeforeAsk = function (answers) {
159+
const remainLength = footerMaxLength - combineFooter(answers).length;
160+
this.maxLength = Math.min(this.maxLength, remainLength);
161+
this.minLength = Math.max(this.minLength, footerMinLength);
162+
};
162163

163164
return instance.getQuestion();
164165
});
@@ -219,12 +220,12 @@ export default class Prompter {
219220
return {
220221
skip: canBeSkip,
221222
enumList,
222-
caseFn: getForcedCaseFn(this.getRule(rulePrefix, 'case')),
223+
caseFn: getCaseFn(this.getRule(rulePrefix, 'case')),
223224
fullStopFn: getFullStopFn(this.getRule(rulePrefix, 'full-stop')),
224225
minLength: getMinLength(this.getRule(rulePrefix, 'min-length')),
225226
maxLength: getMaxLength(this.getRule(rulePrefix, 'max-length')),
226227
messages: {
227-
title: questionSettings?.['description'],
228+
title: questionSettings?.['description'] ?? '',
228229
...messages,
229230
},
230231
};

@commitlint/cz-commitlint/src/Question.ts

Lines changed: 20 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,32 @@
11
import chalk from 'chalk';
22
import inquirer, {
33
Answers,
4+
AsyncDynamicQuestionProperty,
45
ChoiceCollection,
56
DistinctQuestion,
6-
Question as InquirerQuestion,
77
} from 'inquirer';
88
import {PromptName} from './types';
99
import {CaseFn} from './utils/case-fn';
1010
import {FullStopFn} from './utils/full-stop-fn';
1111

12-
// TODO Require 'title'
13-
type Messages = Partial<
14-
Record<
15-
| 'skip'
16-
| 'title'
17-
| 'max'
18-
| 'min'
19-
| 'emptyWarning'
20-
| 'upperLimitWarning'
21-
| 'lowerLimitWarning',
22-
string
23-
>
24-
>;
12+
type Messages = Record<'title', string> &
13+
Partial<
14+
Record<
15+
| 'skip'
16+
| 'max'
17+
| 'min'
18+
| 'emptyWarning'
19+
| 'upperLimitWarning'
20+
| 'lowerLimitWarning',
21+
string
22+
>
23+
>;
2524
export type QuestionConfig = {
2625
messages: Messages;
2726
maxLength: number;
2827
minLength: number;
29-
defaultValue?: string | boolean;
28+
defaultValue?: string;
29+
when?: AsyncDynamicQuestionProperty<boolean, Answers>;
3030
skip?: boolean;
3131
enumList?: ChoiceCollection<{
3232
name: string;
@@ -48,10 +48,11 @@ export default class Question {
4848
constructor(
4949
name: PromptName,
5050
{
51-
defaultValue,
5251
enumList,
5352
messages,
54-
skip,
53+
defaultValue,
54+
when,
55+
skip = false,
5556
fullStopFn = (_: string) => _,
5657
caseFn = (_: string) => _,
5758
maxLength = Infinity,
@@ -80,23 +81,18 @@ export default class Question {
8081
},
8182
]
8283
: enumList,
83-
default: defaultValue,
8484
};
8585
} else {
8686
this.#data = {
8787
type: /^is[A-Z]/.test(name) ? 'confirm' : 'input',
8888
name: name,
8989
message: this.decorateMessage,
90-
default: defaultValue,
9190
transformer: this.transformer,
9291
};
9392
}
9493

95-
Object.assign(this.#data, {
96-
filter: this.filter,
97-
validate: this.validate,
98-
});
99-
94+
this.#data.default = defaultValue;
95+
this.#data.when = when;
10096
this.#data.filter = this.filter;
10197
this.#data.validate = this.validate;
10298
}
@@ -113,10 +109,6 @@ export default class Question {
113109
return this.#data.name;
114110
}
115111

116-
setQuestionProperty(property: InquirerQuestion): void {
117-
Object.assign(this.#data, property);
118-
}
119-
120112
validate: (input: string) => boolean | string = (input) => {
121113
const filterSubject = this.filter(input);
122114

0 commit comments

Comments
 (0)