diff --git a/source/index.js b/source/index.js index 4d5b4d22f1..607e0046d2 100644 --- a/source/index.js +++ b/source/index.js @@ -1,4 +1,3 @@ -import {sync as parse} from 'conventional-commits-parser'; import {merge} from 'lodash'; import ruleFunctions from './rules'; @@ -6,6 +5,7 @@ import format from './library/format'; import getConfiguration from './library/get-configuration'; import getMessages from './library/get-messages'; import getPreset from './library/get-preset'; +import parse from './library/parse'; export {format, getConfiguration, getMessages, getPreset}; @@ -21,10 +21,7 @@ export default async (message, options = {}) => { } = options; // parse the commit message - const parsed = merge( - {raw: message}, - parse(message, parserOptions) - ); + const parsed = parse(message); // wildcard matches skip the linting const bails = Object.entries(wildcards) diff --git a/source/library/parse.js b/source/library/parse.js new file mode 100644 index 0000000000..74935d4a7b --- /dev/null +++ b/source/library/parse.js @@ -0,0 +1,9 @@ +import {sync} from 'conventional-commits-parser'; + +export default parse; + +function parse(message, options) { + const parsed = sync(message, options); + parsed.raw = message; + return parsed; +} diff --git a/source/rules/footer-leading-blank.js b/source/rules/footer-leading-blank.js index 1275c3d58f..eaec80b41d 100644 --- a/source/rules/footer-leading-blank.js +++ b/source/rules/footer-leading-blank.js @@ -1,14 +1,20 @@ export default (parsed, when) => { + // flunk if no footer is found + if (!parsed.footer) { + return [true]; + } + const negated = when === 'never'; + // get complete body split into lines - const lines = (parsed.raw || '').split('\n').slice(2); - // check if the first line of body (if any) is empty - const leadingBlank = - lines.length > 0 ? - lines[0].length === 0 : - true; + const lines = (parsed.raw || '').split(/\r|\n/).slice(2); + const [leading] = lines; + + // check if the first line of body is empty + const succeeds = leading === ''; + return [ - negated ? !leadingBlank : leadingBlank, + negated ? !succeeds : succeeds, [ 'footer', negated ? 'may not' : 'must', diff --git a/test/rules/footer-leading-blank.js b/test/rules/footer-leading-blank.js new file mode 100644 index 0000000000..731517ff3c --- /dev/null +++ b/test/rules/footer-leading-blank.js @@ -0,0 +1,109 @@ +import test from 'ava'; +import parse from '../../source/library/parse'; +import footerLeadingBlank from '../../source/rules/footer-leading-blank'; + +const messages = { + simple: 'chore: subject', + body: 'chore: subject\nbody', + trailing: 'chore: subject\nbody\n\n', + without: 'chore: subject\nbody\nBREAKING CHANGE: something important', + with: 'chore: subject\nbody\n\nBREAKING CHANGE: something important' +}; + +const parsed = { + simple: parse(messages.simple), + body: parse(messages.body), + trailing: parse(messages.trailing), + without: parse(messages.without), + with: parse(messages.with) +}; + +test('footer-leading-blank with simple message should succeed for empty keyword', t => { + const [actual] = footerLeadingBlank(parsed.simple); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with simple message should succeed for "never"', t => { + const [actual] = footerLeadingBlank(parsed.simple, 'never'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with simple message should succeed for "always"', t => { + const [actual] = footerLeadingBlank(parsed.simple, 'always'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with body message should succeed for empty keyword', t => { + const [actual] = footerLeadingBlank(parsed.body); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with body message should succeed for "never"', t => { + const [actual] = footerLeadingBlank(parsed.body, 'never'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with body message should succeed for "always"', t => { + const [actual] = footerLeadingBlank(parsed.body, 'always'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with trailing message should succeed for empty keyword', t => { + const [actual] = footerLeadingBlank(parsed.trailing); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with trailing message should succeed for "never"', t => { + const [actual] = footerLeadingBlank(parsed.trailing, 'never'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with trailing message should succeed for "always"', t => { + const [actual] = footerLeadingBlank(parsed.trailing, 'always'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank without blank line before footer should fail for empty keyword', t => { + const [actual] = footerLeadingBlank(parsed.without); + const expected = false; + t.is(actual, expected); +}); + +test('footer-leading-blank without blank line before footer should succeed for "never"', t => { + const [actual] = footerLeadingBlank(parsed.without, 'never'); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank without blank line before footer should fail for "always"', t => { + const [actual] = footerLeadingBlank(parsed.without, 'always'); + const expected = false; + t.is(actual, expected); +}); + +test('footer-leading-blank with blank line before footer should succeed for empty keyword', t => { + const [actual] = footerLeadingBlank(parsed.with); + const expected = true; + t.is(actual, expected); +}); + +test('footer-leading-blank with blank line before footer should fail for "never"', t => { + const [actual] = footerLeadingBlank(parsed.with, 'never'); + const expected = false; + t.is(actual, expected); +}); + +test('footer-leading-blank with blank line before footer should succeed for "always"', t => { + const [actual] = footerLeadingBlank(parsed.with, 'always'); + const expected = true; + t.is(actual, expected); +}); diff --git a/test/rules/scope-empty.js b/test/rules/scope-empty.js index ba5983151a..3ac7a42041 100644 --- a/test/rules/scope-empty.js +++ b/test/rules/scope-empty.js @@ -1,5 +1,5 @@ import test from 'ava'; -import {sync as parse} from 'conventional-commits-parser'; +import parse from '../../source/library/parse'; import scopeEmpty from '../../source/rules/scope-empty'; const messages = { diff --git a/test/rules/scope-enum.js b/test/rules/scope-enum.js index b1a8f7c4a5..f4a2a26e44 100644 --- a/test/rules/scope-enum.js +++ b/test/rules/scope-enum.js @@ -1,5 +1,5 @@ import test from 'ava'; -import {sync as parse} from 'conventional-commits-parser'; +import parse from '../../source/library/parse'; import scopeEnum from '../../source/rules/scope-enum'; const messages = {