From 0262ca67beadd0f692fb3e40cb9a9c8100f7f7ff Mon Sep 17 00:00:00 2001 From: Dan Rivett Date: Sat, 15 Feb 2020 07:20:30 -0800 Subject: [PATCH] (#1) Initial changes to commitlint-github-utils. --- .../commitlint-github-utils/@types/index.d.ts | 53 +-- .../src/commitlintGitHubConstants.ts | 19 +- packages/commitlint-github-utils/src/index.ts | 12 +- .../src/parseCommitMessage.ts | 97 +++--- .../src/tests/parseCommitMessage.test.ts | 303 +++++++++++++++--- 5 files changed, 338 insertions(+), 146 deletions(-) diff --git a/packages/commitlint-github-utils/@types/index.d.ts b/packages/commitlint-github-utils/@types/index.d.ts index 14da381..9e13352 100644 --- a/packages/commitlint-github-utils/@types/index.d.ts +++ b/packages/commitlint-github-utils/@types/index.d.ts @@ -1,37 +1,38 @@ export interface Rules { - taskIdEmpty: string - taskIdSeparator: string - taskIdCase: string - taskIdMaxLength: string - taskIdMinLength: string - commitMessageSeparator: string + issueNumberMissing: string; + issueNumberFormat: string; + typeOrWip: string; + subjectCase: string; } export interface TCommitlintGitHubConstants { - GITHUB_RULES: Rules - COMMIT_MESSAGE_SEPARATOR: string - COMMIT_TASK_IDS_SEPARATOR: string - TASK_ID_SEPARATOR: string - UPPERCASE: string - LOWERCASE: string - COMMIT_DESCRIPTION_SEPARATOR: string + GITHUB_RULES: Rules; + ISSUE_NUMBER_PREFIX: string; + ISSUE_NUMBERS_SEPARATOR: string; + ISSUE_NUMBERS_PATTERN: RegExp; + TYPE_SEPARATOR: string; + COMMIT_DESCRIPTION_SEPARATOR: string; } -export type TParseCommitMessage = ( - commitMessage: string, -) => { - commitTaskIds: string[] - commitHeader: string - commitFooter: string -} +export type ParsedCommitMessage = { + issueNumbers: number[]; + isWip: boolean; + type?: string; + subject?: string; + body: string[]; +}; + +export type CommitParser = ( + unparsedCommitMessage: string, +) => ParsedCommitMessage; export interface CommitlintGitHubUtils { - parseCommitMessage: TParseCommitMessage - commitlintGitHubConstants: TCommitlintGitHubConstants + parseCommitMessage: CommitParser; + commitlintGitHubConstants: TCommitlintGitHubConstants; } -export const commitlintGitHubConstants: TCommitlintGitHubConstants -export const parseCommitMessage: TParseCommitMessage +export const commitlintGitHubConstants: TCommitlintGitHubConstants; +export const parseCommitMessage: CommitParser; -declare const commitlintGitHubUtils: CommitlintGitHubUtils -export default commitlintGitHubUtils +declare const commitlintGitHubUtils: CommitlintGitHubUtils; +export default commitlintGitHubUtils; diff --git a/packages/commitlint-github-utils/src/commitlintGitHubConstants.ts b/packages/commitlint-github-utils/src/commitlintGitHubConstants.ts index ead6241..5cf5731 100644 --- a/packages/commitlint-github-utils/src/commitlintGitHubConstants.ts +++ b/packages/commitlint-github-utils/src/commitlintGitHubConstants.ts @@ -1,17 +1,12 @@ export const GITHUB_RULES = { issueNumberMissing: 'github-issue-number-missing', issueNumberFormat: 'github-issue-number-format', - issueNumberBrackets: 'github-issue-number-brackets', typeOrWip: 'github-type-or-wip', - typeMessageSeparator: 'github-type-message-separator', -} + subjectCase: 'github-subject-case', +}; -export const ISSUE_NUMBER_PREFIX = '#' -export const ISSUE_NUMBERS_SEPARATOR = ',' -export const ISSUE_NUMBERS_PATTERN = /^\(#.+\) / -export const ISSUE_NUMBERS_WITH_TYPE_PATTERN = /\(#.+\) \w+: / -export const TYPE_SEPARATOR = ':' -export const PARENTHESES = '()' -export const SQUARE_BRACKETS = '[]' -export const ANGLE_BRACKETS = '<>' -export const COMMIT_DESCRIPTION_SEPARATOR = '\n' +export const ISSUE_NUMBER_PREFIX = '#'; +export const ISSUE_NUMBERS_SEPARATOR = ','; +export const ISSUE_NUMBERS_PATTERN = /^\((?.+)\) (?:(?\w+): )?(?.*)/; +export const TYPE_SEPARATOR = ':'; +export const COMMIT_DESCRIPTION_SEPARATOR = '\n'; diff --git a/packages/commitlint-github-utils/src/index.ts b/packages/commitlint-github-utils/src/index.ts index 757eec3..192eb98 100644 --- a/packages/commitlint-github-utils/src/index.ts +++ b/packages/commitlint-github-utils/src/index.ts @@ -1,11 +1,11 @@ -import * as commitlintGitHubConstants from './commitlintGitHubConstants' -import parseCommitMessage from './parseCommitMessage' -import { CommitlintGitHubUtils } from '../@types' +import * as commitlintGitHubConstants from './commitlintGitHubConstants'; +import parseCommitMessage from './parseCommitMessage'; +import { CommitlintGitHubUtils } from '../@types'; const commitlintGitHubUtils: CommitlintGitHubUtils = { commitlintGitHubConstants, parseCommitMessage, -} +}; -export { commitlintGitHubConstants, parseCommitMessage } -export default commitlintGitHubUtils +export { commitlintGitHubConstants, parseCommitMessage }; +export default commitlintGitHubUtils; diff --git a/packages/commitlint-github-utils/src/parseCommitMessage.ts b/packages/commitlint-github-utils/src/parseCommitMessage.ts index 5ad7822..0e85b93 100644 --- a/packages/commitlint-github-utils/src/parseCommitMessage.ts +++ b/packages/commitlint-github-utils/src/parseCommitMessage.ts @@ -1,54 +1,53 @@ -import { TParseCommitMessage } from '../@types' +import { CommitParser, ParsedCommitMessage } from '../@types'; import { - COMMIT_MESSAGE_SEPARATOR, - COMMIT_TASK_IDS_SEPARATOR, + ISSUE_NUMBERS_PATTERN, COMMIT_DESCRIPTION_SEPARATOR, -} from './commitlintGitHubConstants' - -const parseCommitMessage: TParseCommitMessage = rawCommitMessage => { - /** - * Description separator is used to separe commit parts without description - * Read more about this issue: https://github.com/Gherciu/commitlint-github/issues/6 - */ - const commitMessage = rawCommitMessage - .split(COMMIT_DESCRIPTION_SEPARATOR) - .filter(commitMessageSeparatedPart => commitMessageSeparatedPart)[0] - const commitMessageParts = commitMessage.split(COMMIT_MESSAGE_SEPARATOR) - - /** - * if commit parts length is greater or equal with 2 return part one else - * commit parts length is less than 2 it means that task ids is not provided - * or is not separated corectly - */ - const rawCommitHeader = - commitMessageParts.length >= 2 ? commitMessageParts[0] : '' - const commitHeader = rawCommitHeader.trim() - /** - * if commit parts length is greater than 2 return all parts without first part - * because first part is commit header - * Note: rest of parts should be joined with COMMIT_MESSAGE_SEPARATOR - * because is posible that the commit message footer to contain symbols equal - * with COMMIT_MESSAGE_SEPARATOR then the commit footer will be resolved incorect - * More info about this issue: https://github.com/Gherciu/commitlint-github/issues/7 - */ - const commitFooter = - commitMessageParts.length > 2 - ? commitMessageParts - .filter((_value, index) => index > 0) - .join(COMMIT_MESSAGE_SEPARATOR) - .trim() - : commitMessageParts[commitMessageParts.length - 1].trim() - - const commitTaskIds = commitHeader - .split(COMMIT_TASK_IDS_SEPARATOR) - .map(taskId => taskId.trim()) - .filter(taskId => taskId) +} from './commitlintGitHubConstants'; - return { - commitTaskIds, - commitFooter, - commitHeader, +const parseIssues = (issuesString: string): number[] => { + // TODO: Implement + return [0]; +}; + +const parseCommitMessage: CommitParser = ( + rawCommitMessage: string, +): ParsedCommitMessage => { + let issueNumbers: number[] = []; + let type: string | undefined; + let isWip = false; + let subject: string | undefined; + let body: string[] = []; + + const issueNumbersWithPossibleType = ISSUE_NUMBERS_PATTERN.exec( + rawCommitMessage, + ); + + const issueNumbersWithPossibleTypeGroups = + issueNumbersWithPossibleType && issueNumbersWithPossibleType.groups; + + if (issueNumbersWithPossibleTypeGroups) { + issueNumbers = parseIssues(issueNumbersWithPossibleTypeGroups.issue); + + // eslint-disable-next-line prefer-destructuring + type = issueNumbersWithPossibleTypeGroups.type; + isWip = type === 'WIP'; + + const descriptionLines = issueNumbersWithPossibleTypeGroups.description.split( + COMMIT_DESCRIPTION_SEPARATOR, + ); + + const [description] = descriptionLines; + [subject] = description; + body = descriptionLines.slice(1); } -} -export default parseCommitMessage + return { + issueNumbers, + isWip, + type, + subject, + body, + }; +}; + +export default parseCommitMessage; diff --git a/packages/commitlint-github-utils/src/tests/parseCommitMessage.test.ts b/packages/commitlint-github-utils/src/tests/parseCommitMessage.test.ts index d34196a..e07d08d 100644 --- a/packages/commitlint-github-utils/src/tests/parseCommitMessage.test.ts +++ b/packages/commitlint-github-utils/src/tests/parseCommitMessage.test.ts @@ -1,22 +1,10 @@ -import parseCommitMessage from '../parseCommitMessage' +/* eslint-disable prettier/prettier */ +import parseCommitMessage from '../parseCommitMessage'; -const COMMIT_MESSAGE = 'Test commit message' +const COMMIT_MESSAGE = 'Test commit message'; describe('commitlintPluginGitHubTests', () => { const testCommitMessages = { - singleScope: `(#1) ${COMMIT_MESSAGE}`, - singleScopeMultipleDigits: `(#1234) ${COMMIT_MESSAGE}`, - multiScope: `(#1, #2) ${COMMIT_MESSAGE}`, - multiScopeMultipleDigits: `(#1, #34) ${COMMIT_MESSAGE}`, - singleScopeTypedTask: `(#123) chore: ${COMMIT_MESSAGE}`, - multiScopeTypedTask: `(#123, #45) chore: ${COMMIT_MESSAGE}`, - singleScopeWipTask: `(#123) WIP: ${COMMIT_MESSAGE}`, - multiScopeWipTask: `(#123, #45) WIP: ${COMMIT_MESSAGE}`, - issueNumberMissing: 'My commit message', - issueNumberEmpty: '() My commit message', - issueNumberMissingHash: '(123) My commit message', - issueNumberNonNumeric: '(#bob) My commit message', - multiCommitPartsSeparator: `(#42) ${COMMIT_MESSAGE} http://gherciu.github.io`, multiLineCommit: ` (#123) ${COMMIT_MESSAGE} @@ -24,55 +12,264 @@ describe('commitlintPluginGitHubTests', () => { - SUBTASK-1: I added a new feature * SUBTASK-2: I fixed a issue `, - } + }; - it('should return correct commitTaskIds', () => { + it('should return correct issue numbers', () => { expect( - parseCommitMessage(testCommitMessages.singleScope).commitTaskIds, - ).toEqual(['IB-2121']) - // expect( - // parseCommitMessage(testCommitMessages.singleScopeWipTask).commitTaskIds, - // ).toEqual(['IB-2121']) + parseCommitMessage(`(#1) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1]); + + expect( + parseCommitMessage(`(#1234) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1234]); + + expect( + parseCommitMessage(`(#1, #2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1, 2]); + + expect( + parseCommitMessage(`(#1,#2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1, 2]); + + expect( + parseCommitMessage(`(#1, #34) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1, 34]); + + expect( + parseCommitMessage(`(#1,#34) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([1, 34]); + + expect( + parseCommitMessage(`(#123) chore: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123]); + + expect( + parseCommitMessage(`(#123, #45) chore: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123, 45]); + + expect( + parseCommitMessage(`(#123,#45) chore: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123, 45]); + + expect( + parseCommitMessage(`(#123) WIP: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123]); + + expect( + parseCommitMessage(`(#123, #45) WIP: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123, 45]); + + expect( + parseCommitMessage(`(#123,#45) WIP: ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([123, 45]); + }); + + it('should return no issue numbers', () => { + // Missing issue numbers prefix entirely + expect( + parseCommitMessage('My commit message').issueNumbers + ).toEqual([]); + + // Empty issue numbers prefix + expect( + parseCommitMessage(`() ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + // Non-numeric issue numbers prefix + expect( + parseCommitMessage(`(#2bob) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#2, #1bob) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#2,#1bob) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#2, 1bob) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#2,1bob) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#bob, #2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#1bob,#2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#1bob, 2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#1bob,2) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + // Missing issue number hash + expect( + parseCommitMessage(`(123) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#1, 123) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(#1,123) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(1, #123) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + + expect( + parseCommitMessage(`(1,#123) ${COMMIT_MESSAGE}`).issueNumbers + ).toEqual([]); + }); + + it('should return correct type', () => { + expect( + parseCommitMessage(`(#1) ${COMMIT_MESSAGE}`).type + ).toEqual(null); + + expect( + parseCommitMessage(`(#1, #2) ${COMMIT_MESSAGE}`).type + ).toEqual(null); + + expect( + parseCommitMessage(`(#1,#2) ${COMMIT_MESSAGE}`).type + ).toEqual(null); + + expect( + parseCommitMessage(`(#123) chore: ${COMMIT_MESSAGE}`).type + ).toEqual('chore'); + + expect( + parseCommitMessage(`(#123, #45) chore: ${COMMIT_MESSAGE}`).type + ).toEqual('chore'); + + expect( + parseCommitMessage(`(#123,#45) chore: ${COMMIT_MESSAGE}`).type + ).toEqual('chore'); + + expect( + parseCommitMessage(`(#123) WIP: ${COMMIT_MESSAGE}`).type + ).toEqual('WIP'); + + expect( + parseCommitMessage(`(#123, #45) WIP: ${COMMIT_MESSAGE}`).type + ).toEqual('WIP'); + + expect( + parseCommitMessage(`(#123,#45) WIP: ${COMMIT_MESSAGE}`).type + ).toEqual('WIP'); + }); + + it('should return correct WIP status', () => { + expect( + parseCommitMessage(`(#1) ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + + expect( + parseCommitMessage(`(#1, #2) ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + + expect( + parseCommitMessage(`(#1,#2) ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + + expect( + parseCommitMessage(`(#123) chore: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + expect( - parseCommitMessage(testCommitMessages.multiScope).commitTaskIds, - ).toEqual(['IB-2121', 'IB-21']) - // expect( - // parseCommitMessage(testCommitMessages.multiScopeWipTask).commitTaskIds, - // ).toEqual(['IB-2121', 'IB-21']) - }) + parseCommitMessage(`(#123, #45) chore: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); - it('should return correct commitFooter', () => { expect( - parseCommitMessage(testCommitMessages.singleScope).commitFooter, - ).toEqual('test commit message') - }) + parseCommitMessage(`(#123,#45) chore: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); - it('should return empty array of taskIds', () => { expect( - parseCommitMessage(testCommitMessages.emptyTaskIds).commitTaskIds, - ).toEqual([]) + parseCommitMessage(`(#123) WIP: ${COMMIT_MESSAGE}`).isWip + ).toEqual(true); + + expect( + parseCommitMessage(`(#123, #45) WIP: ${COMMIT_MESSAGE}`).isWip + ).toEqual(true); + + expect( + parseCommitMessage(`(#123,#45) WIP: ${COMMIT_MESSAGE}`).isWip + ).toEqual(true); + + // WIP must be all uppercase to match + expect( + parseCommitMessage(`(#123) wip: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + expect( - parseCommitMessage(testCommitMessages.missingSeparator).commitTaskIds, - ).toEqual([]) - }) + parseCommitMessage(`(#123) Wip: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); - it('should return corect taskIds and commit footer if a url is added in commit message', () => { expect( - parseCommitMessage(testCommitMessages.multiCommitPartsSeparator) - .commitTaskIds, - ).toEqual(['IB-2121']) + parseCommitMessage(`(#123) WiP: ${COMMIT_MESSAGE}`).isWip + ).toEqual(false); + }); + + it('should return correct subject', () => { + expect( + parseCommitMessage(`(#1) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#1234) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#1, #2) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + expect( - parseCommitMessage(testCommitMessages.multiCommitPartsSeparator) - .commitFooter, - ).toEqual('test commit message http://gherciu.github.io') - }) + parseCommitMessage(`(#1,#2) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); - it('should return corect taskIds and commit footer if is provided a multiline commit message used for description', () => { expect( - parseCommitMessage(testCommitMessages.multiLineCommit).commitTaskIds, - ).toEqual(['IB-2121']) + parseCommitMessage(`(#1, #34) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#1,#34) ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#123) chore: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#123, #45) chore: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#123,#45) chore: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#123) WIP: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + + expect( + parseCommitMessage(`(#123, #45) WIP: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + expect( - parseCommitMessage(testCommitMessages.multiLineCommit).commitFooter, - ).toEqual('test commit message') - }) -}) + parseCommitMessage(`(#123,#45) WIP: ${COMMIT_MESSAGE}`).subject + ).toEqual(COMMIT_MESSAGE); + }); +});