Skip to content

Commit 8b394c9

Browse files
authoredFeb 18, 2020
feat(config-conventional): footer/body-max-line (#436)
* feat(config-conventional): footer/body-max-line Co-authored-by: Mario Nebl <marionebl@users.noreply.github.com> BREAKING CHANGE Introduces new rules on `error` level that validate the line length of `footer` and `body`. This might create negative linting reports for commits that previously where passing.
1 parent 4443062 commit 8b394c9

File tree

5 files changed

+321
-0
lines changed

5 files changed

+321
-0
lines changed
 

‎@commitlint/config-conventional/README.md

+94
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ Consult [docs/rules](https://conventional-changelog.github.io/commitlint/#/refer
2424

2525
- **condition**: `type` is found in value
2626
- **rule**: `always`
27+
- **level**: `error`
2728
- **value**
2829

2930
```
@@ -51,6 +52,7 @@ echo "fix: some message" # passes
5152

5253
- **description**: `type` is in case `value`
5354
- **rule**: `always`
55+
- **level**: `error`
5456
- **value**
5557
```
5658
'lowerCase'
@@ -65,6 +67,7 @@ echo "fix: some message" # passes
6567

6668
- **condition**: `type` is empty
6769
- **rule**: `never`
70+
- **level**: `error`
6871

6972
```sh
7073
echo ": some message" # fails
@@ -75,6 +78,7 @@ echo "fix: some message" # passes
7578

7679
- **condition**: `scope` is in case `value`
7780
- **rule**: `always`
81+
- **level**: `error`
7882

7983
```
8084
'lowerCase'
@@ -89,6 +93,7 @@ echo "fix(scope): some message" # passes
8993

9094
- **condition**: `subject` is in one of the cases `['sentence-case', 'start-case', 'pascal-case', 'upper-case']`
9195
- **rule**: `never`
96+
- **level**: `error`
9297

9398
```sh
9499
echo "fix(SCOPE): Some message" # fails
@@ -103,6 +108,7 @@ echo "fix(scope): some Message" # passes
103108

104109
- **condition**: `subject` is empty
105110
- **rule**: `never`
111+
- **level**: `error`
106112

107113
```sh
108114
echo "fix:" # fails
@@ -113,6 +119,7 @@ echo "fix: some message" # passes
113119

114120
- **condition**: `subject` ends with `value`
115121
- **rule**: `never`
122+
- **level**: `error`
116123
- **value**
117124

118125
```
@@ -128,6 +135,7 @@ echo "fix: some message" # passes
128135

129136
- **condition**: `header` has `value` or less characters
130137
- **rule**: `always`
138+
- **level**: `error`
131139
- **value**
132140

133141
```
@@ -138,3 +146,89 @@ echo "fix: some message" # passes
138146
echo "fix: some message that is way too long and breaks the line max-length by several characters" # fails
139147
echo "fix: some message" # passes
140148
```
149+
150+
#### footer-leading-blank
151+
152+
- **condition**: `footer` should have a leading blank line
153+
- **rule**: `always`
154+
- level: `warning`
155+
- **value**
156+
157+
```
158+
100
159+
```
160+
161+
```sh
162+
echo "fix: some message
163+
BREAKING CHANGE: It will be significant" # warning
164+
165+
echo "fix: some message
166+
167+
BREAKING CHANGE: It will be significant" # passes
168+
```
169+
170+
#### footer-max-line-length
171+
172+
- **condition**: `footer` each line has `value` or less characters
173+
- **rule**: `always`
174+
- level: `error`
175+
- **value**
176+
177+
```
178+
100
179+
```
180+
181+
```sh
182+
echo "fix: some message
183+
184+
BREAKING CHANGE: footer with multiple lines
185+
has a message that is way too long and will break the line rule 'line-max-length' by several characters" # fails
186+
187+
echo "fix: some message
188+
189+
BREAKING CHANGE: footer with multiple lines
190+
but still no line is too long" # passes
191+
```
192+
193+
#### body-leading-blank
194+
195+
- **condition**: `body` should have a leading blank line
196+
- **rule**: `always`
197+
- level: `warning`
198+
- **value**
199+
200+
```js
201+
100;
202+
```
203+
204+
```sh
205+
echo "fix: some message
206+
body" # warning
207+
208+
echo "fix: some message
209+
210+
body" # passes
211+
```
212+
213+
#### body-max-line-length
214+
215+
- **condition**: `body` each line has `value` or less characters
216+
- **rule**: `always`
217+
- level: `error`
218+
- **value**
219+
220+
```js
221+
100;
222+
```
223+
224+
```sh
225+
echo "fix: some message
226+
227+
body with multiple lines
228+
has a message that is way too long and will break the line rule 'line-max-length' by several characters" # fails
229+
230+
echo "fix: some message
231+
232+
body with multiple lines
233+
but still no line is too long" # passes
234+
```

‎@commitlint/config-conventional/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@ module.exports = {
22
parserPreset: 'conventional-changelog-conventionalcommits',
33
rules: {
44
'body-leading-blank': [1, 'always'],
5+
'body-max-line-length': [2, 'always', 100],
56
'footer-leading-blank': [1, 'always'],
7+
'footer-max-line-length': [2, 'always', 100],
68
'header-max-length': [2, 'always', 100],
79
'scope-case': [2, 'always', 'lower-case'],
810
'subject-case': [
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
import lint from '@commitlint/lint';
2+
import {rules} from '.';
3+
4+
const messages = {
5+
invalidTypeEnum: 'foo: some message',
6+
invalidTypeCase: 'FIX: some message',
7+
invalidTypeEmpty: ': some message',
8+
invalidScopeCase: 'fix(SCOPE): some message',
9+
invalidSubjectCases: [
10+
'fix(scope): Some message',
11+
'fix(scope): Some Message',
12+
'fix(scope): SomeMessage',
13+
'fix(scope): SOMEMESSAGE'
14+
],
15+
invalidSubjectEmpty: 'fix:',
16+
invalidSubjectFullStop: 'fix: some message.',
17+
invalidHeaderMaxLength:
18+
'fix: some message that is way too long and breaks the line max-length by several characters since the max is 100',
19+
warningFooterLeadingBlank:
20+
'fix: some message\n\nbody\nBREAKING CHANGE: It will be significant',
21+
invalidFooterMaxLineLength:
22+
'fix: some message\n\nbody\n\nBREAKING CHANGE: footer with multiple lines\nhas a message that is way too long and will break the line rule "line-max-length" by several characters',
23+
warningBodyLeadingBlank: 'fix: some message\nbody',
24+
invalidBodyMaxLineLength:
25+
'fix: some message\n\nbody with multiple lines\nhas a message that is way too long and will break the line rule "line-max-length" by several characters',
26+
validMessages: [
27+
'fix: some message',
28+
'fix(scope): some message',
29+
'fix(scope): some Message',
30+
'fix(scope): some message\n\nBREAKING CHANGE: it will be significant!',
31+
'fix(scope): some message\n\nbody'
32+
]
33+
};
34+
35+
const errors = {
36+
typeEnum: {
37+
level: 2,
38+
message:
39+
'type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test]',
40+
name: 'type-enum',
41+
valid: false
42+
},
43+
typeCase: {
44+
level: 2,
45+
message: 'type must be lower-case',
46+
name: 'type-case',
47+
valid: false
48+
},
49+
typeEmpty: {
50+
level: 2,
51+
message: 'type may not be empty',
52+
name: 'type-empty',
53+
valid: false
54+
},
55+
scopeCase: {
56+
level: 2,
57+
message: 'scope must be lower-case',
58+
name: 'scope-case',
59+
valid: false
60+
},
61+
subjectCase: {
62+
level: 2,
63+
message:
64+
'subject must not be sentence-case, start-case, pascal-case, upper-case',
65+
name: 'subject-case',
66+
valid: false
67+
},
68+
subjectEmpty: {
69+
level: 2,
70+
message: 'subject may not be empty',
71+
name: 'subject-empty',
72+
valid: false
73+
},
74+
subjectFullStop: {
75+
level: 2,
76+
message: 'subject may not end with full stop',
77+
name: 'subject-full-stop',
78+
valid: false
79+
},
80+
headerMaxLength: {
81+
level: 2,
82+
message:
83+
'header must not be longer than 100 characters, current length is 112',
84+
name: 'header-max-length',
85+
valid: false
86+
},
87+
footerMaxLineLength: {
88+
level: 2,
89+
message: "footer's lines must not be longer than 100 characters",
90+
name: 'footer-max-line-length',
91+
valid: false
92+
},
93+
bodyMaxLineLength: {
94+
level: 2,
95+
message: "body's lines must not be longer than 100 characters",
96+
name: 'body-max-line-length',
97+
valid: false
98+
}
99+
};
100+
101+
const warnings = {
102+
footerLeadingBlank: {
103+
level: 1,
104+
message: 'footer must have leading blank line',
105+
name: 'footer-leading-blank',
106+
valid: false
107+
},
108+
bodyLeadingBlank: {
109+
level: 1,
110+
message: 'body must have leading blank line',
111+
name: 'body-leading-blank',
112+
valid: false
113+
}
114+
};
115+
116+
test('type-enum', async () => {
117+
const result = await lint(messages.invalidTypeEnum, rules);
118+
119+
expect(result.valid).toBe(false);
120+
expect(result.errors).toEqual([errors.typeEnum]);
121+
});
122+
123+
test('type-case', async () => {
124+
const result = await lint(messages.invalidTypeCase, rules);
125+
126+
expect(result.valid).toBe(false);
127+
expect(result.errors).toEqual([errors.typeCase, errors.typeEnum]);
128+
});
129+
130+
test('type-empty', async () => {
131+
const result = await lint(messages.invalidTypeEmpty, rules);
132+
133+
expect(result.valid).toBe(false);
134+
expect(result.errors).toEqual([errors.typeEmpty]);
135+
});
136+
137+
test('scope-case', async () => {
138+
const result = await lint(messages.invalidScopeCase, rules);
139+
140+
expect(result.valid).toBe(false);
141+
expect(result.errors).toEqual([errors.scopeCase]);
142+
});
143+
144+
test('subject-case', async () => {
145+
const invalidInputs = await Promise.all(
146+
messages.invalidSubjectCases.map(invalidInput => lint(invalidInput, rules))
147+
);
148+
149+
invalidInputs.forEach(result => {
150+
expect(result.valid).toBe(false);
151+
expect(result.errors).toEqual([errors.subjectCase]);
152+
});
153+
});
154+
155+
test('subject-empty', async () => {
156+
const result = await lint(messages.invalidSubjectEmpty, rules);
157+
158+
expect(result.valid).toBe(false);
159+
expect(result.errors).toEqual([errors.subjectEmpty, errors.typeEmpty]);
160+
});
161+
162+
test('subject-full-stop', async () => {
163+
const result = await lint(messages.invalidSubjectFullStop, rules);
164+
165+
expect(result.valid).toBe(false);
166+
expect(result.errors).toEqual([errors.subjectFullStop]);
167+
});
168+
169+
test('header-max-length', async () => {
170+
const result = await lint(messages.invalidHeaderMaxLength, rules);
171+
172+
expect(result.valid).toBe(false);
173+
expect(result.errors).toEqual([errors.headerMaxLength]);
174+
});
175+
176+
test('footer-leading-blank', async () => {
177+
const result = await lint(messages.warningFooterLeadingBlank, rules);
178+
179+
expect(result.valid).toBe(true);
180+
expect(result.warnings).toEqual([warnings.footerLeadingBlank]);
181+
});
182+
183+
test('footer-max-line-length', async () => {
184+
const result = await lint(messages.invalidFooterMaxLineLength, rules);
185+
186+
expect(result.valid).toBe(false);
187+
expect(result.errors).toEqual([errors.footerMaxLineLength]);
188+
});
189+
190+
test('body-leading-blank', async () => {
191+
const result = await lint(messages.warningBodyLeadingBlank, rules);
192+
193+
expect(result.valid).toBe(true);
194+
expect(result.warnings).toEqual([warnings.bodyLeadingBlank]);
195+
});
196+
197+
test('body-max-line-length', async () => {
198+
const result = await lint(messages.invalidBodyMaxLineLength, rules);
199+
200+
expect(result.valid).toBe(false);
201+
expect(result.errors).toEqual([errors.bodyMaxLineLength]);
202+
});
203+
204+
test('valid messages', async () => {
205+
const validInputs = await Promise.all(
206+
messages.validMessages.map(input => lint(input, rules))
207+
);
208+
209+
validInputs.forEach(result => {
210+
expect(result.valid).toBe(true);
211+
expect(result.errors).toEqual([]);
212+
expect(result.warnings).toEqual([]);
213+
});
214+
});

‎@commitlint/config-conventional/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"node": ">=8"
3333
},
3434
"devDependencies": {
35+
"@commitlint/lint": "^8.3.4",
3536
"@commitlint/utils": "^8.3.4"
3637
},
3738
"dependencies": {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
{
2+
"extends": "../../tsconfig.shared.json",
3+
"compilerOptions": {
4+
"composite": true,
5+
"rootDir": "./src",
6+
"outDir": "./lib"
7+
},
8+
"include": ["./**/*.ts"],
9+
"exclude": ["./**/*.test.ts"]
10+
}

1 commit comments

Comments
 (1)

khashayarnazari commented on Nov 19, 2021

@khashayarnazari
Please sign in to comment.