UNPKG

6.64 kBJavaScriptView Raw
1const fs = require('fs');
2const _ = require('lodash');
3const buildCommit = require('./buildCommit');
4const log = require('./logger');
5
6const isNotWip = answers => answers.type.toLowerCase() !== 'wip';
7
8const isValidateTicketNo = (value, config) => {
9 if (!value) {
10 return !config.isTicketNumberRequired;
11 }
12 if (!config.ticketNumberRegExp) {
13 return true;
14 }
15 const reg = new RegExp(config.ticketNumberRegExp);
16 if (value.replace(reg, '') !== '') {
17 return false;
18 }
19 return true;
20};
21
22const getPreparedCommit = context => {
23 let message = null;
24 if (fs.existsSync('./.git/COMMIT_EDITMSG')) {
25 let preparedCommit = fs.readFileSync('./.git/COMMIT_EDITMSG', 'utf-8');
26 preparedCommit = preparedCommit
27 .replace(/^#.*/gm, '')
28 .replace(/^\s*[\r\n]/gm, '')
29 .replace(/[\r\n]$/, '')
30 .split(/\r\n|\r|\n/);
31
32 if (preparedCommit.length && preparedCommit[0]) {
33 if (context === 'subject') [message] = preparedCommit;
34 else if (context === 'body' && preparedCommit.length > 1) {
35 preparedCommit.shift();
36 message = preparedCommit.join('|');
37 }
38 }
39 }
40
41 return message;
42};
43
44module.exports = {
45 getQuestions(config, cz) {
46 // normalize config optional options
47 const scopeOverrides = config.scopeOverrides || {};
48 const messages = config.messages || {};
49 const skipQuestions = config.skipQuestions || [];
50
51 messages.type = messages.type || "Select the type of change that you're committing:";
52 messages.scope = messages.scope || '\nDenote the SCOPE of this change (optional):';
53 messages.customScope = messages.customScope || 'Denote the SCOPE of this change:';
54 if (!messages.ticketNumber) {
55 if (config.ticketNumberRegExp) {
56 messages.ticketNumber =
57 messages.ticketNumberPattern ||
58 `Enter the ticket number following this pattern (${config.ticketNumberRegExp})\n`;
59 } else {
60 messages.ticketNumber = 'Enter the ticket number:\n';
61 }
62 }
63 messages.subject = messages.subject || 'Write a SHORT, IMPERATIVE tense description of the change:\n';
64 messages.body =
65 messages.body || 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n';
66 messages.breaking = messages.breaking || 'List any BREAKING CHANGES (optional):\n';
67 messages.footer = messages.footer || 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n';
68 messages.confirmCommit = messages.confirmCommit || 'Are you sure you want to proceed with the commit above?';
69
70 let questions = [
71 {
72 type: 'list',
73 name: 'type',
74 message: messages.type,
75 choices: config.types,
76 },
77 {
78 type: 'list',
79 name: 'scope',
80 message: messages.scope,
81 choices(answers) {
82 let scopes = [];
83 if (scopeOverrides[answers.type]) {
84 scopes = scopes.concat(scopeOverrides[answers.type]);
85 } else {
86 scopes = scopes.concat(config.scopes);
87 }
88 if (config.allowCustomScopes || scopes.length === 0) {
89 scopes = scopes.concat([
90 new cz.Separator(),
91 { name: 'empty', value: false },
92 { name: 'custom', value: 'custom' },
93 ]);
94 }
95 return scopes;
96 },
97 when(answers) {
98 let hasScope = false;
99 if (scopeOverrides[answers.type]) {
100 hasScope = !!(scopeOverrides[answers.type].length > 0);
101 } else {
102 hasScope = !!(config.scopes && config.scopes.length > 0);
103 }
104 if (!hasScope) {
105 // TODO: Fix when possible
106 // eslint-disable-next-line no-param-reassign
107 answers.scope = 'custom';
108 return false;
109 }
110 return isNotWip(answers);
111 },
112 },
113 {
114 type: 'input',
115 name: 'scope',
116 message: messages.customScope,
117 when(answers) {
118 return answers.scope === 'custom';
119 },
120 },
121 {
122 type: 'input',
123 name: 'ticketNumber',
124 message: messages.ticketNumber,
125 when() {
126 return !!config.allowTicketNumber; // no ticket numbers allowed unless specifed
127 },
128 validate(value) {
129 return isValidateTicketNo(value, config);
130 },
131 },
132 {
133 type: 'input',
134 name: 'subject',
135 message: messages.subject,
136 default: getPreparedCommit('subject'),
137 validate(value) {
138 const limit = config.subjectLimit || 100;
139 if (value.length > limit) {
140 return `Exceed limit: ${limit}`;
141 }
142 return true;
143 },
144 filter(value) {
145 const upperCaseSubject = config.upperCaseSubject || false;
146
147 return (upperCaseSubject ? value.charAt(0).toUpperCase() : value.charAt(0).toLowerCase()) + value.slice(1);
148 },
149 },
150 {
151 type: 'input',
152 name: 'body',
153 message: messages.body,
154 default: getPreparedCommit('body'),
155 },
156 {
157 type: 'input',
158 name: 'breaking',
159 message: messages.breaking,
160 when(answers) {
161 // eslint-disable-next-line max-len
162 if (
163 config.askForBreakingChangeFirst ||
164 (config.allowBreakingChanges && config.allowBreakingChanges.indexOf(answers.type.toLowerCase()) >= 0)
165 ) {
166 return true;
167 }
168 return false; // no breaking changes allowed unless specifed
169 },
170 },
171 {
172 type: 'input',
173 name: 'footer',
174 message: messages.footer,
175 when: isNotWip,
176 },
177 {
178 type: 'expand',
179 name: 'confirmCommit',
180 choices: [
181 { key: 'y', name: 'Yes', value: 'yes' },
182 { key: 'n', name: 'Abort commit', value: 'no' },
183 { key: 'e', name: 'Edit message', value: 'edit' },
184 ],
185 default: 0,
186 message(answers) {
187 const SEP = '###--------------------------------------------------------###';
188 log.info(`\n${SEP}\n${buildCommit(answers, config)}\n${SEP}\n`);
189 return messages.confirmCommit;
190 },
191 },
192 ];
193
194 questions = questions.filter(item => !skipQuestions.includes(item.name));
195
196 if (config.askForBreakingChangeFirst) {
197 const isBreaking = oneQuestion => oneQuestion.name === 'breaking';
198
199 const breakingQuestion = _.filter(questions, isBreaking);
200 const questionWithoutBreaking = _.reject(questions, isBreaking);
201
202 questions = _.concat(breakingQuestion, questionWithoutBreaking);
203 }
204
205 return questions;
206 },
207};