@packages/parse-commit-message/src/main.ts
import { checkCommit, parseCommit, stringifyCommit } from './commit.ts';
import type { Commit, Options, PossibleCommit, Result } from './types.ts';
import { errorMsg, toArray } from './utils.ts';
/**
* Receives and parses a single or multiple commit message(s) in form of string,
* object, array of strings, array of objects or mixed.
*
* @example
* import { parse } from 'parse-commit-message';
*
* const commits = [
* 'fix(ci): tweaks for @circleci config',
* 'chore: bar qux'
* ];
* const result = parse(commits);
* console.log(result);
* // => [{
* // header: { type: 'fix', scope: 'ci', subject: 'tweaks for @circleci config' },
* // body: null,
* // footer: null,
* // }, {
* // header: { type: 'chore', scope: null, subject: 'bar qux' },
* // body: null,
* // footer: null,
* // }]
*
* const commitMessage = `feat: awesome yeah
*
* Awesome body!
* resolves #123
*
* Signed-off-by: And Footer <abc@exam.pl>`;
*
* const res = parse(commitMessage);
*
* console.log(res);
* // => {
* // header: { type: 'feat', scope: null, subject: 'awesome yeah' },
* // body: 'Awesome body!\nresolves #123',
* // footer: 'Signed-off-by: And Footer <abc@exam.pl>',
* // }
*
* @name .parse
* @param {PossibleCommit} commits a value to be parsed into an object like `Commit` type
* @param {object} options options to control the header regex and case sensitivity
* @param {RegExp|string} options.headerRegex string regular expression or instance of RegExp
* @param {boolean} options.caseSensitive whether or not to be case sensitive, defaults to `false`
* @returns {Array<Commit>} if array of commit objects
* @public
*/
export function parse(commits: PossibleCommit, options?: Options): Commit[] {
const result = toArray(commits)
.filter(Boolean)
.flat()
.reduce((acc: any[], val) => {
if (typeof val === 'string') {
return acc.concat(parseCommit(val, options));
}
if (typeof val === 'object' && !Array.isArray(val)) {
return acc.concat(val);
}
return acc.concat(parse(val, options));
}, []) as Commit[];
return result as Commit[];
}
/**
* Receives a `Commit` object, validates it using `validate`,
* builds a "commit" message string and returns it.
*
* This method does checking and validation too,
* so if you pass a string, it will be parsed and validated,
* and after that turned again to string.
*
* @example
* import { parse, stringify } from 'parse-commit-message';
*
* const commitMessage = `feat: awesome yeah
*
* Awesome body!
* resolves #123
*
* Signed-off-by: And Footer <abc@exam.pl>`;
*
* const flat = true;
* const res = parse(commitMessage, flat);
*
* const str = stringify(res, flat);
* console.log(str);
* console.log(str === commitMessage);
*
* @name .stringify
* @param {PossibleCommit} commits a `Commit` object, or anything that can be passed to `check`
* @param {object} options options to control the header regex and case sensitivity
* @param {RegExp|string} options.headerRegex string regular expression or instance of RegExp
* @param {boolean} options.caseSensitive whether or not to be case sensitive, defaults to `false`
* @returns {Array<string>} an array of commit strings like `'fix(foo): bar baz'`
* @public
*/
export function stringify(commits: PossibleCommit, options?: Options): string[] {
const result = toArray(commits)
.filter(Boolean)
.flat()
.reduce((acc, val) => {
const xxx = typeof val === 'string' ? { header: { value: val } } : val;
const foo = check(xxx, options);
const bar = toArray<Commit>(foo as any).map((x) => stringifyCommit(x, options));
return acc.concat(bar) as string[];
}, [] as string[]);
return result as string[];
}
/**
* Validates a single or multiple commit message(s) in form of string,
* object, array of strings, array of objects or mixed.
*
* @example
* import { validate } from 'parse-commit-message';
*
* console.log(validate('foo bar qux')); // false
* console.log(validate('foo: bar qux')); // true
* console.log(validate('fix(ci): bar qux')); // true
*
* console.log(validate(['a bc cqux', 'foo bar qux'])); // false
*
* console.log(validate({ qux: 1 })); // false
* console.log(validate({ header: { type: 'fix' } })); // false
* console.log(validate({ header: { type: 'fix', subject: 'ok' } })); // true
*
* const commitObject = {
* header: { type: 'test', subject: 'updating tests' },
* foo: 'bar',
* isBreaking: false,
* body: 'oh ah',
* };
* console.log(validate(commitObject)); // true
*
* const result = validate('foo bar qux');
* console.log(result.error);
* // => Error: expect \`commit\` to follow:
* // <type>[optional scope]: <description>
* //
* // [optional body]
* //
* // [optional footer]
*
* const res = validate('fix(ci): okey barry');
* console.log(result.value);
* // => [{
* // header: { type: 'fix', scope: 'ci', subject: 'okey barry' },
* // body: null,
* // footer: null,
* // }]
*
* const commit = { header: { type: 'fix' } };
* const { error } = validate(commit);
* console.log(error);
* // => TypeError: header.subject should be non empty string
*
*
* const commit = { header: { type: 'fix', scope: 123, subject: 'okk' } };
* const { error } = validate(commit);
* console.log(error);
* // => TypeError: header.scope should be non empty string when given
*
* @name .validate
* @param {PossibleCommit} commits a value to be parsed & validated into an object like `Commit` type
* @param {object} options options to control the header regex and case sensitivity
* @param {RegExp|string} options.headerRegex string regular expression or instance of RegExp
* @param {boolean} options.caseSensitive whether or not to be case sensitive, defaults to `false`
* @returns {Result} an object like `{ value: Array<Commit>, error: Error }`
* @public
*/
export function validate(commits: PossibleCommit, options?: Options): Result<Commit[]> {
const result = {} as Result<Commit[]>;
try {
result.value = check(commits, options) as Commit[];
} catch (err) {
return { error: err } as Result<any>;
}
return result as Result<Commit[]>;
}
/**
* Receives a single or multiple commit message(s) in form of string,
* object, array of strings, array of objects or mixed.
* Throws if find some error. Think of it as "assert", it's basically that.
*
* @example
* import { check } from 'parse-commit-message';
*
* try {
* check({ header: { type: 'fix' } });
* } catch(err) {
* console.log(err);
* // => TypeError: header.subject should be non empty string
* }
*
* // Can also validate/check a strings, array of strings,
* // or even mixed - array of strings and objects
* try {
* check('fix(): invalid scope, it cannot be empty')
* } catch(err) {
* console.log(err);
* // => TypeError: header.scope should be non empty string when given
* }
*
* @name .check
* @param {PossibleCommit} commits a value to be parsed & validated into an object like `Commit` type
* @param {object} options options to control the header regex and case sensitivity
* @param {RegExp|string} options.headerRegex string regular expression or instance of RegExp
* @param {boolean} options.caseSensitive whether or not to be case sensitive, defaults to `false`
* @returns {Array<Commit>} returns the same as given if no problems, otherwise it will throw;
* @public
*/
export function check(commits: PossibleCommit, options?: Options): Commit[] {
const result = toArray<any>(commits)
.filter(Boolean)
.flat()
.reduce((acc, commit) => {
if (typeof commit === 'string') {
commit = parseCommit(commit, options);
}
return acc.concat(checkCommit(commit as Commit, options));
}, []);
if (result.length === 0) {
throw new Error(errorMsg);
}
return result as Commit[];
}