diff --git a/@commitlint/rules/src/body-full-stop.test.ts b/@commitlint/rules/src/body-full-stop.test.ts new file mode 100644 index 0000000000..7f1c1c6913 --- /dev/null +++ b/@commitlint/rules/src/body-full-stop.test.ts @@ -0,0 +1,50 @@ +import parse from '@commitlint/parse'; +import {bodyFullStop} from './body-full-stop'; + +const messages = { + empty: 'test:\n', + with: `test: subject\n\nbody.`, + without: `test: subject\n\nbody`, +}; + +const parsed = { + empty: parse(messages.empty), + with: parse(messages.with), + without: parse(messages.without), +}; + +test('empty against "always" should succeed', async () => { + const [actual] = bodyFullStop(await parsed.empty, 'always', '.'); + const expected = true; + expect(actual).toEqual(expected); +}); + +test('empty against "never ." should succeed', async () => { + const [actual] = bodyFullStop(await parsed.empty, 'never', '.'); + const expected = true; + expect(actual).toEqual(expected); +}); + +test('with against "always ." should succeed', async () => { + const [actual] = bodyFullStop(await parsed.with, 'always', '.'); + const expected = true; + expect(actual).toEqual(expected); +}); + +test('with against "never ." should fail', async () => { + const [actual] = bodyFullStop(await parsed.with, 'never', '.'); + const expected = false; + expect(actual).toEqual(expected); +}); + +test('without against "always ." should fail', async () => { + const [actual] = bodyFullStop(await parsed.without, 'always', '.'); + const expected = false; + expect(actual).toEqual(expected); +}); + +test('without against "never ." should succeed', async () => { + const [actual] = bodyFullStop(await parsed.without, 'never', '.'); + const expected = true; + expect(actual).toEqual(expected); +}); diff --git a/@commitlint/rules/src/body-full-stop.ts b/@commitlint/rules/src/body-full-stop.ts new file mode 100644 index 0000000000..61d79ec7c8 --- /dev/null +++ b/@commitlint/rules/src/body-full-stop.ts @@ -0,0 +1,22 @@ +import message from '@commitlint/message'; +import {SyncRule} from '@commitlint/types'; + +export const bodyFullStop: SyncRule = ( + parsed, + when = 'always', + value = '.' +) => { + const input = parsed.body; + + if (!input) { + return [true]; + } + + const negated = when === 'never'; + const hasStop = input[input.length - 1] === value; + + return [ + negated ? !hasStop : hasStop, + message(['body', negated ? 'may not' : 'must', 'end with full stop']), + ]; +}; diff --git a/@commitlint/rules/src/index.ts b/@commitlint/rules/src/index.ts index e26930908f..8053f721f3 100644 --- a/@commitlint/rules/src/index.ts +++ b/@commitlint/rules/src/index.ts @@ -1,5 +1,6 @@ import {bodyCase} from './body-case'; import {bodyEmpty} from './body-empty'; +import {bodyFullStop} from './body-full-stop'; import {bodyLeadingBlank} from './body-leading-blank'; import {bodyMaxLength} from './body-max-length'; import {bodyMaxLineLength} from './body-max-line-length'; @@ -34,6 +35,7 @@ import {typeMinLength} from './type-min-length'; export default { 'body-case': bodyCase, 'body-empty': bodyEmpty, + 'body-full-stop': bodyFullStop, 'body-leading-blank': bodyLeadingBlank, 'body-max-length': bodyMaxLength, 'body-max-line-length': bodyMaxLineLength, diff --git a/@commitlint/types/src/rules.ts b/@commitlint/types/src/rules.ts index 11c13e250a..efc69d0cc6 100644 --- a/@commitlint/types/src/rules.ts +++ b/@commitlint/types/src/rules.ts @@ -91,6 +91,7 @@ export type EnumRuleConfig = RuleConfig< export type RulesConfig = { 'body-case': CaseRuleConfig; 'body-empty': RuleConfig; + 'body-full-stop': RuleConfig; 'body-leading-blank': RuleConfig; 'body-max-length': LengthRuleConfig; 'body-max-line-length': LengthRuleConfig; diff --git a/docs/reference-rules.md b/docs/reference-rules.md index 6c418c0e0b..06454f1e3c 100644 --- a/docs/reference-rules.md +++ b/docs/reference-rules.md @@ -42,6 +42,16 @@ Rule configurations are either of type `array` residing on a key with the rule's ### Available rules +#### body-full-stop + +- **condition**: `body` ends with `value` +- **rule**: `never` +- **value** + +``` +'.' +``` + #### body-leading-blank - **condition**: `body` begins with blank line