-
Notifications
You must be signed in to change notification settings - Fork 912
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add subject-exclamation-mark rule to improve error messages (#2593
) When using the conventional commit feature of an `!` in the commit subject the angular config get really confused and gives some error messages that do not relate to the issue due to the message failing at the parse stage. This overrides the angular parser preset to add in the exclamation mark then uses the new rule to give a better error message. The result is with the message "fix!: the fix" previously the error message would be "subject may not be empty" now the error message is "subject must not have an exclamation mark in the subject to identify a breaking change". This message it more descriptive and will give the user info they need to resolve the issue. This also updates the docs to highlight the difference in angular and conventional configs, and point them in the direction of the conventional config if they want to use the `!` in the commit messages
- Loading branch information
1 parent
3d8fb54
commit be701bd
Showing
9 changed files
with
224 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
import lint from '@commitlint/lint'; | ||
import {rules, parserPreset} from '.'; | ||
|
||
const lintMessage = async (message) => { | ||
const parserOpts = parserPreset.parserOpts; | ||
const m = message.replace(/^\s+/, '').trim(); | ||
const result = await lint(m, rules, {parserOpts}); | ||
|
||
if (result.errors.length > 1) { | ||
throw new Error( | ||
'Commit test should only have one error message to validate against' | ||
); | ||
} | ||
|
||
if (result.warnings.length > 1) { | ||
throw new Error( | ||
'Commit test should only have one warning message to validate against' | ||
); | ||
} | ||
|
||
return result; | ||
}; | ||
|
||
test('a valid commit message', async () => { | ||
const result = await lintMessage('test: a valid angular commit'); | ||
expect(result.valid).toBe(true); | ||
expect(result.errors).toStrictEqual([]); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('a valid message with a scope', async () => { | ||
const result = await lintMessage( | ||
'test(scope): a valid angular commit with a scope' | ||
); | ||
expect(result.valid).toBe(true); | ||
expect(result.errors).toStrictEqual([]); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('a valid multi line commit', async () => { | ||
const result = await lintMessage( | ||
`test(scope): a valid angular commit with a scope | ||
Some content in the body` | ||
); | ||
expect(result.valid).toBe(true); | ||
expect(result.errors).toStrictEqual([]); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('a leading blank line after header', async () => { | ||
const result = await lintMessage( | ||
`test(scope): a valid angular commit with a scope | ||
Some content in the body` | ||
); | ||
|
||
expect(result.valid).toBe(true); | ||
expect(result.errors).toStrictEqual([]); | ||
expect(result.warnings[0].message).toBe('body must have leading blank line'); | ||
}); | ||
|
||
test('an invalid scope', async () => { | ||
const result = await lintMessage(`no: no is not not an invalid commit type`); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(result.errors[0].message).toBe( | ||
'type must be one of [build, ci, docs, feat, fix, perf, refactor, revert, style, test]' | ||
); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('a long header', async () => { | ||
const result = await lintMessage( | ||
`test: that its an error when there is ia realllllllllllllllllllllly long header` | ||
); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(result.errors[0].message).toBe( | ||
'header must not be longer than 72 characters, current length is 79' | ||
); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('message header with ! in it', async () => { | ||
const result = await lintMessage(`test!: with a breaking change in the type`); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(result.errors[0].message).toBe( | ||
'subject must not have an exclamation mark in the subject to identify a breaking change' | ||
); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); | ||
|
||
test('message header with ! in it and a scope', async () => { | ||
const result = await lintMessage( | ||
`test(scope)!: with a breaking change in the type` | ||
); | ||
|
||
expect(result.valid).toBe(false); | ||
expect(result.errors[0].message).toBe( | ||
'subject must not have an exclamation mark in the subject to identify a breaking change' | ||
); | ||
expect(result.warnings).toStrictEqual([]); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
import parse from '@commitlint/parse'; | ||
import {subjectExclamationMark} from './subject-exclamation-mark'; | ||
|
||
const preset = require('conventional-changelog-angular'); | ||
|
||
const parseMessage = async (str: string) => { | ||
const {parserOpts} = await preset; | ||
return parse(str, undefined, parserOpts); | ||
}; | ||
|
||
const messages = { | ||
empty: 'test:\n', | ||
with: `test!: subject\n`, | ||
without: `test: subject\n`, | ||
}; | ||
|
||
const parsed = { | ||
empty: parseMessage(messages.empty), | ||
with: parseMessage(messages.with), | ||
without: parseMessage(messages.without), | ||
}; | ||
|
||
test('empty against "always" should fail', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.empty, 'always'); | ||
const expected = false; | ||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
test('empty against "never" should succeed', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.empty, 'never'); | ||
const expected = true; | ||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
test('with against "always" should succeed', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.with, 'always'); | ||
const expected = true; | ||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
test('with against "never" should fail', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.with, 'never'); | ||
const expected = false; | ||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
test('without against "always" should fail', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.without, 'always'); | ||
const expected = false; | ||
expect(actual).toEqual(expected); | ||
}); | ||
|
||
test('without against "never" should succeed', async () => { | ||
const [actual] = subjectExclamationMark(await parsed.without, 'never'); | ||
const expected = true; | ||
expect(actual).toEqual(expected); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import message from '@commitlint/message'; | ||
import {SyncRule} from '@commitlint/types'; | ||
|
||
export const subjectExclamationMark: SyncRule = (parsed, when = 'always') => { | ||
const input = parsed.header; | ||
if (!input) { | ||
return [true, '']; | ||
} | ||
|
||
const negated = when === 'never'; | ||
const hasExclamationMark = /!:/.test(input); | ||
|
||
return [ | ||
negated ? !hasExclamationMark : hasExclamationMark, | ||
message([ | ||
'subject', | ||
negated ? 'must not' : 'must', | ||
'have an exclamation mark in the subject to identify a breaking change', | ||
]), | ||
]; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters