Skip to content

Commit 331579a

Browse files
authoredJan 25, 2024
feat(rules): add header-trim rule (#3199) (#3871)
1 parent 6d036d6 commit 331579a

File tree

6 files changed

+112
-0
lines changed

6 files changed

+112
-0
lines changed
 

‎@commitlint/config-conventional/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ module.exports = {
66
'footer-leading-blank': [1, 'always'],
77
'footer-max-line-length': [2, 'always', 100],
88
'header-max-length': [2, 'always', 100],
9+
'header-trim': [2, 'always'],
910
'subject-case': [
1011
2,
1112
'never',
+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
import parse from '@commitlint/parse';
2+
import {Commit} from '@commitlint/types';
3+
import {headerTrim} from './header-trim';
4+
5+
const messages = {
6+
correct: 'test: subject',
7+
8+
whitespaceStart: ' test: subject',
9+
whitespaceEnd: 'test: subject ',
10+
whitespaceSurround: ' test: subject ',
11+
12+
tabStart: '\t\ttest: subject',
13+
tabEnd: 'test: subject\t\t',
14+
tabSurround: '\t\ttest: subject\t',
15+
16+
mixStart: '\t\ttest: subject',
17+
mixEnd: 'test: subject\t\t',
18+
mixSurround: '\t \ttest: subject \t \t',
19+
};
20+
21+
const parsed = Object.entries(messages).reduce((_parsed, [key, message]) => {
22+
_parsed[key] = parse(message);
23+
return _parsed;
24+
}, {}) as Record<keyof typeof messages, Promise<Commit>>;
25+
26+
test('should succeed when header is not surrounded by whitespace', async () => {
27+
const result = headerTrim(await parsed.correct);
28+
expect(result).toEqual(expect.arrayContaining([true]));
29+
});
30+
31+
(
32+
[
33+
['mixed whitespace', parsed.mixStart],
34+
['whitespace', parsed.whitespaceStart],
35+
['tab', parsed.tabStart],
36+
] as const
37+
).forEach(([desc, commit]) => {
38+
test(`should fail with ${desc}`, async () => {
39+
const result = headerTrim(await commit);
40+
expect(result).toEqual(
41+
expect.arrayContaining([false, 'header must not start with whitespace'])
42+
);
43+
});
44+
});
45+
46+
(
47+
[
48+
['mixed whitespace', parsed.mixEnd],
49+
['whitespace', parsed.whitespaceEnd],
50+
['tab', parsed.tabEnd],
51+
] as const
52+
).forEach(([desc, commit]) => {
53+
test(`should fail when ends with ${desc}`, async () => {
54+
const result = headerTrim(await commit);
55+
expect(result).toEqual(
56+
expect.arrayContaining([false, 'header must not end with whitespace'])
57+
);
58+
});
59+
});
60+
61+
(
62+
[
63+
['mixed whitespace', parsed.mixSurround],
64+
['whitespace', parsed.whitespaceSurround],
65+
['tab', parsed.tabSurround],
66+
] as const
67+
).forEach(([desc, commit]) => {
68+
test(`should fail when surrounded by ${desc}`, async () => {
69+
const result = headerTrim(await commit);
70+
expect(result).toEqual(
71+
expect.arrayContaining([
72+
false,
73+
'header must not be surrounded by whitespace',
74+
])
75+
);
76+
});
77+
});

‎@commitlint/rules/src/header-trim.ts

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import message from '@commitlint/message';
2+
import {SyncRule} from '@commitlint/types';
3+
4+
export const headerTrim: SyncRule = (parsed) => {
5+
const {header} = parsed;
6+
7+
const startsWithWhiteSpace = header !== header.trimStart();
8+
const endsWithWhiteSpace = header !== header.trimEnd();
9+
10+
switch (true) {
11+
case startsWithWhiteSpace && endsWithWhiteSpace:
12+
return [
13+
false,
14+
message(['header', 'must not be surrounded by whitespace']),
15+
];
16+
17+
case startsWithWhiteSpace:
18+
return [false, message(['header', 'must not start with whitespace'])];
19+
20+
case endsWithWhiteSpace:
21+
return [false, message(['header', 'must not end with whitespace'])];
22+
23+
default:
24+
return [true];
25+
}
26+
};

‎@commitlint/rules/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {footerMaxLineLength} from './footer-max-line-length';
1212
import {footerMinLength} from './footer-min-length';
1313
import {headerCase} from './header-case';
1414
import {headerFullStop} from './header-full-stop';
15+
import {headerTrim} from './header-trim';
1516
import {headerMaxLength} from './header-max-length';
1617
import {headerMinLength} from './header-min-length';
1718
import {referencesEmpty} from './references-empty';
@@ -51,6 +52,7 @@ export default {
5152
'header-full-stop': headerFullStop,
5253
'header-max-length': headerMaxLength,
5354
'header-min-length': headerMinLength,
55+
'header-trim': headerTrim,
5456
'references-empty': referencesEmpty,
5557
'scope-case': scopeCase,
5658
'scope-empty': scopeEmpty,

‎@commitlint/types/src/rules.ts

+1
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ export type RulesConfig<V = RuleConfigQuality.User> = {
105105
'header-full-stop': RuleConfig<V, string>;
106106
'header-max-length': LengthRuleConfig<V>;
107107
'header-min-length': LengthRuleConfig<V>;
108+
'header-trim': RuleConfig<V>;
108109
'references-empty': RuleConfig<V>;
109110
'scope-case': CaseRuleConfig<V>;
110111
'scope-empty': RuleConfig<V>;

‎docs/reference-rules.md

+5
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,11 @@ Infinity
212212
0
213213
```
214214

215+
#### header-trim
216+
217+
- **condition**: `header` must not have initial and / or trailing whitespaces
218+
- **rule**: `always`
219+
215220
#### references-empty
216221

217222
- **condition**: `references` has at least one entry

0 commit comments

Comments
 (0)
Please sign in to comment.