1 | const fs = require('fs');
|
2 | const _ = require('lodash');
|
3 | const buildCommit = require('./buildCommit');
|
4 | const log = require('./logger');
|
5 |
|
6 | const isNotWip = answers => answers.type.toLowerCase() !== 'wip';
|
7 |
|
8 | const 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 |
|
22 | const 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 |
|
44 | module.exports = {
|
45 | getQuestions(config, cz) {
|
46 |
|
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 |
|
106 |
|
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;
|
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 |
|
162 | if (
|
163 | config.askForBreakingChangeFirst ||
|
164 | (config.allowBreakingChanges && config.allowBreakingChanges.indexOf(answers.type.toLowerCase()) >= 0)
|
165 | ) {
|
166 | return true;
|
167 | }
|
168 | return false;
|
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 | };
|