Skip to content

Commit

Permalink
feat(rules): add header-trim rule (#3199) (#3871)
Browse files Browse the repository at this point in the history
  • Loading branch information
marcalexiei authored Jan 25, 2024
1 parent 6d036d6 commit 331579a
Show file tree
Hide file tree
Showing 6 changed files with 112 additions and 0 deletions.
1 change: 1 addition & 0 deletions @commitlint/config-conventional/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ module.exports = {
'footer-leading-blank': [1, 'always'],
'footer-max-line-length': [2, 'always', 100],
'header-max-length': [2, 'always', 100],
'header-trim': [2, 'always'],
'subject-case': [
2,
'never',
Expand Down
77 changes: 77 additions & 0 deletions @commitlint/rules/src/header-trim.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import parse from '@commitlint/parse';
import {Commit} from '@commitlint/types';
import {headerTrim} from './header-trim';

const messages = {
correct: 'test: subject',

whitespaceStart: ' test: subject',
whitespaceEnd: 'test: subject ',
whitespaceSurround: ' test: subject ',

tabStart: '\t\ttest: subject',
tabEnd: 'test: subject\t\t',
tabSurround: '\t\ttest: subject\t',

mixStart: '\t\ttest: subject',
mixEnd: 'test: subject\t\t',
mixSurround: '\t \ttest: subject \t \t',
};

const parsed = Object.entries(messages).reduce((_parsed, [key, message]) => {
_parsed[key] = parse(message);
return _parsed;
}, {}) as Record<keyof typeof messages, Promise<Commit>>;

test('should succeed when header is not surrounded by whitespace', async () => {
const result = headerTrim(await parsed.correct);
expect(result).toEqual(expect.arrayContaining([true]));
});

(
[
['mixed whitespace', parsed.mixStart],
['whitespace', parsed.whitespaceStart],
['tab', parsed.tabStart],
] as const
).forEach(([desc, commit]) => {
test(`should fail with ${desc}`, async () => {
const result = headerTrim(await commit);
expect(result).toEqual(
expect.arrayContaining([false, 'header must not start with whitespace'])
);
});
});

(
[
['mixed whitespace', parsed.mixEnd],
['whitespace', parsed.whitespaceEnd],
['tab', parsed.tabEnd],
] as const
).forEach(([desc, commit]) => {
test(`should fail when ends with ${desc}`, async () => {
const result = headerTrim(await commit);
expect(result).toEqual(
expect.arrayContaining([false, 'header must not end with whitespace'])
);
});
});

(
[
['mixed whitespace', parsed.mixSurround],
['whitespace', parsed.whitespaceSurround],
['tab', parsed.tabSurround],
] as const
).forEach(([desc, commit]) => {
test(`should fail when surrounded by ${desc}`, async () => {
const result = headerTrim(await commit);
expect(result).toEqual(
expect.arrayContaining([
false,
'header must not be surrounded by whitespace',
])
);
});
});
26 changes: 26 additions & 0 deletions @commitlint/rules/src/header-trim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import message from '@commitlint/message';
import {SyncRule} from '@commitlint/types';

export const headerTrim: SyncRule = (parsed) => {
const {header} = parsed;

const startsWithWhiteSpace = header !== header.trimStart();
const endsWithWhiteSpace = header !== header.trimEnd();

switch (true) {
case startsWithWhiteSpace && endsWithWhiteSpace:
return [
false,
message(['header', 'must not be surrounded by whitespace']),
];

case startsWithWhiteSpace:
return [false, message(['header', 'must not start with whitespace'])];

case endsWithWhiteSpace:
return [false, message(['header', 'must not end with whitespace'])];

default:
return [true];
}
};
2 changes: 2 additions & 0 deletions @commitlint/rules/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {footerMaxLineLength} from './footer-max-line-length';
import {footerMinLength} from './footer-min-length';
import {headerCase} from './header-case';
import {headerFullStop} from './header-full-stop';
import {headerTrim} from './header-trim';
import {headerMaxLength} from './header-max-length';
import {headerMinLength} from './header-min-length';
import {referencesEmpty} from './references-empty';
Expand Down Expand Up @@ -51,6 +52,7 @@ export default {
'header-full-stop': headerFullStop,
'header-max-length': headerMaxLength,
'header-min-length': headerMinLength,
'header-trim': headerTrim,
'references-empty': referencesEmpty,
'scope-case': scopeCase,
'scope-empty': scopeEmpty,
Expand Down
1 change: 1 addition & 0 deletions @commitlint/types/src/rules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ export type RulesConfig<V = RuleConfigQuality.User> = {
'header-full-stop': RuleConfig<V, string>;
'header-max-length': LengthRuleConfig<V>;
'header-min-length': LengthRuleConfig<V>;
'header-trim': RuleConfig<V>;
'references-empty': RuleConfig<V>;
'scope-case': CaseRuleConfig<V>;
'scope-empty': RuleConfig<V>;
Expand Down
5 changes: 5 additions & 0 deletions docs/reference-rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,11 @@ Infinity
0
```

#### header-trim

- **condition**: `header` must not have initial and / or trailing whitespaces
- **rule**: `always`

#### references-empty

- **condition**: `references` has at least one entry
Expand Down

0 comments on commit 331579a

Please sign in to comment.