cz-ghostwriter
Version:
A configurable commitizen adapter
167 lines (166 loc) • 7.42 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.run = void 0;
const chalk = require("chalk");
const child_process_1 = require("child_process");
const fs_1 = require("fs");
const path_1 = require("path");
const wordWrap = require("word-wrap");
const configuration_1 = require("./configuration");
const maxLineLength = 120;
const getLongestString = (array) => {
if (array.length === 0) {
return '';
}
let longest = array[0];
let longestLength = longest.length;
for (let idx = 0; idx < array.length; idx += 1) {
const element = array[idx];
const elementLength = element.length;
if (elementLength > longestLength) {
longest = element;
longestLength = elementLength;
}
}
return longest;
};
const getSubjectMaxLength = (scope, type) => {
return maxLineLength - (type.length + 2 + (scope ? scope.length + 2 : 0));
};
const sanitizeSubject = (subject) => {
let sanitizedSubject = subject.trim();
const lowerCasedFirstCharacter = sanitizedSubject.charAt(0).toLowerCase();
if (lowerCasedFirstCharacter !== sanitizedSubject.charAt(0)) {
sanitizedSubject =
lowerCasedFirstCharacter + sanitizedSubject.slice(1, sanitizedSubject.length);
}
while (sanitizedSubject.endsWith('.')) {
sanitizedSubject = sanitizedSubject.slice(0, sanitizedSubject.length - 1);
}
return sanitizedSubject;
};
const skipWhenUsingMergeMessage = ({ useMergeMessage }) => !useMergeMessage;
const run = () => {
var _a, _b, _c;
const config = (0, configuration_1.getConfiguration)();
const typeLength = getLongestString(config.types.map(({ type }) => type)).length + 1;
const typeChoices = config.types.map(({ description, type }) => {
const prefix = `${type}:`.padEnd(typeLength);
return { name: `${prefix} ${description}`, value: type };
});
const scopeLength = getLongestString((_b = (_a = config.scopes) === null || _a === void 0 ? void 0 : _a.map(({ type }) => type)) !== null && _b !== void 0 ? _b : []).length + 1;
const scopeChoices = (_c = config.scopes) === null || _c === void 0 ? void 0 : _c.map(({ description, type }) => {
const prefix = `${type}:`.padEnd(scopeLength);
return { name: `${prefix} ${description}`, value: type };
});
const scopePrompt = scopeChoices
? {
choices: scopeChoices,
message: 'What is the scope of this commit? (press enter to skip)\n',
name: 'scope',
type: 'list',
when: skipWhenUsingMergeMessage,
}
: {
filter: (scope) => scope.trim().toLowerCase(),
message: 'What is the scope of this commit? (press enter to skip)\n',
name: 'scope',
type: 'input',
when: skipWhenUsingMergeMessage,
};
const mergeMsgPath = (0, path_1.resolve)((0, child_process_1.execSync)('git rev-parse --git-dir').toString().trim(), 'MERGE_MSG');
const isMerging = (0, fs_1.existsSync)(mergeMsgPath);
const defaultMergeMsg = isMerging ? (0, fs_1.readFileSync)(mergeMsgPath).toString().trim() : '';
const hasDefaultMergeMsg = defaultMergeMsg.length > 0;
return {
prompter: (cz, commit) => {
cz.prompt([
{
message: 'An ongoing merge was detected. Would you like to use the default merge commit message?',
name: 'useMergeMessage',
type: 'confirm',
when: hasDefaultMergeMsg,
},
{
choices: typeChoices,
message: 'What type of change are you committing?',
name: 'type',
when: skipWhenUsingMergeMessage,
type: 'list',
},
scopePrompt,
{
filter: sanitizeSubject,
message: (answers) => {
return `Write a short, imperative tense description of the commit (max: ${getSubjectMaxLength(answers.scope, answers.type)} chars):\n`;
},
name: 'subject',
transformer: (subject, answers) => {
const color = subject.length <= getSubjectMaxLength(answers.scope, answers.type)
? chalk.green
: chalk.red;
return color(`(${subject.length}) ${subject}`);
},
type: 'input',
validate: (subject, answers) => {
const sanitizedSubject = sanitizeSubject(subject);
if (sanitizedSubject.length === 0) {
return 'A subject is required';
}
const subjectMaxLength = getSubjectMaxLength(answers.scope, answers.type);
if (sanitizedSubject.length <= subjectMaxLength) {
return true;
}
return `The subject length must be less than or equal to ${subjectMaxLength} characters. The current length is ${subject.length} characters.`;
},
when: skipWhenUsingMergeMessage,
},
{
filter: (breaking) => breaking.trim(),
message: 'If any, describe the breaking change(s) contained within the commit: (press enter to skip)\n',
name: 'breaking',
type: 'input',
when: skipWhenUsingMergeMessage,
},
{
filter: (issues) => issues.trim(),
message: `If any, enter the issues, separated by a space, that are related to this commit: (press enter to skip)\n`,
name: 'issues',
type: 'input',
when: skipWhenUsingMergeMessage,
},
]).then((answers) => {
const useMergeMessage = Boolean(answers.useMergeMessage);
if (useMergeMessage) {
commit(wordWrap(defaultMergeMsg, {
indent: '',
trim: true,
width: maxLineLength,
}));
return;
}
let message = `${answers.type}`;
if (answers.scope && answers.scope !== '*') {
message += `(${answers.scope})`;
}
message += `: ${answers.subject}`;
if (answers.breaking) {
message += `\n\nBREAKING CHANGE: ${answers.breaking}`;
}
if (answers.issues) {
const issues = answers.issues
.split(' ')
.map((issue) => (issue.charAt(0) === '#' ? issue : `#${issue}`))
.join(' ');
message += `\n\n${config.issueReferencesPrefix} ${issues}`;
}
commit(wordWrap(message, {
indent: '',
trim: true,
width: maxLineLength,
}));
});
},
};
};
exports.run = run;