1 | const fs = require('fs');
|
2 | const _ = require('lodash');
|
3 | const buildCommit = require('./build-commit');
|
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 | const skipEmptyScopes = config.skipEmptyScopes || false;
|
51 |
|
52 | messages.type = messages.type || "Select the type of change that you're committing:";
|
53 | messages.scope = messages.scope || '\nDenote the SCOPE of this change (optional):';
|
54 | messages.customScope = messages.customScope || 'Denote the SCOPE of this change:';
|
55 | if (!messages.ticketNumber) {
|
56 | if (config.ticketNumberRegExp) {
|
57 | messages.ticketNumber =
|
58 | messages.ticketNumberPattern ||
|
59 | `Enter the ticket number following this pattern (${config.ticketNumberRegExp})\n`;
|
60 | } else {
|
61 | messages.ticketNumber = 'Enter the ticket number:\n';
|
62 | }
|
63 | }
|
64 | messages.subject = messages.subject || 'Write a SHORT, IMPERATIVE tense description of the change:\n';
|
65 | messages.body =
|
66 | messages.body || 'Provide a LONGER description of the change (optional). Use "|" to break new line:\n';
|
67 | messages.breaking = messages.breaking || 'List any BREAKING CHANGES (optional):\n';
|
68 | messages.footer = messages.footer || 'List any ISSUES CLOSED by this change (optional). E.g.: #31, #34:\n';
|
69 | messages.confirmCommit = messages.confirmCommit || 'Are you sure you want to proceed with the commit above?';
|
70 |
|
71 | let questions = [
|
72 | {
|
73 | type: 'list',
|
74 | name: 'type',
|
75 | message: messages.type,
|
76 | choices: config.types,
|
77 | },
|
78 | {
|
79 | type: 'list',
|
80 | name: 'scope',
|
81 | message: messages.scope,
|
82 | choices(answers) {
|
83 | let scopes = [];
|
84 | if (scopeOverrides[answers.type]) {
|
85 | scopes = scopes.concat(scopeOverrides[answers.type]);
|
86 | } else {
|
87 | scopes = scopes.concat(config.scopes);
|
88 | }
|
89 | if (config.allowCustomScopes || scopes.length === 0) {
|
90 | scopes = scopes.concat([
|
91 | new cz.Separator(),
|
92 | { name: 'empty', value: false },
|
93 | { name: 'custom', value: 'custom' },
|
94 | ]);
|
95 | }
|
96 | return scopes;
|
97 | },
|
98 | when(answers) {
|
99 | let hasScope = false;
|
100 | if (scopeOverrides[answers.type]) {
|
101 | hasScope = !!(scopeOverrides[answers.type].length > 0);
|
102 | } else {
|
103 | hasScope = !!(config.scopes && config.scopes.length > 0);
|
104 | }
|
105 | if (!hasScope) {
|
106 |
|
107 |
|
108 | answers.scope = skipEmptyScopes ? '' : 'custom';
|
109 | return false;
|
110 | }
|
111 | return isNotWip(answers);
|
112 | },
|
113 | },
|
114 | {
|
115 | type: 'input',
|
116 | name: 'scope',
|
117 | message: messages.customScope,
|
118 | when(answers) {
|
119 | return answers.scope === 'custom';
|
120 | },
|
121 | },
|
122 | {
|
123 | type: 'input',
|
124 | name: 'ticketNumber',
|
125 | message: messages.ticketNumber,
|
126 | when() {
|
127 | return !!config.allowTicketNumber;
|
128 | },
|
129 | validate(value) {
|
130 | return isValidateTicketNo(value, config);
|
131 | },
|
132 | },
|
133 | {
|
134 | type: 'input',
|
135 | name: 'subject',
|
136 | message: messages.subject,
|
137 | default: getPreparedCommit('subject'),
|
138 | validate(value) {
|
139 | const limit = config.subjectLimit || 100;
|
140 | if (value.length > limit) {
|
141 | return `Exceed limit: ${limit}`;
|
142 | }
|
143 | return true;
|
144 | },
|
145 | filter(value) {
|
146 | const upperCaseSubject = config.upperCaseSubject || false;
|
147 |
|
148 | return (upperCaseSubject ? value.charAt(0).toUpperCase() : value.charAt(0).toLowerCase()) + value.slice(1);
|
149 | },
|
150 | },
|
151 | {
|
152 | type: 'input',
|
153 | name: 'body',
|
154 | message: messages.body,
|
155 | default: getPreparedCommit('body'),
|
156 | },
|
157 | {
|
158 | type: 'input',
|
159 | name: 'breaking',
|
160 | message: messages.breaking,
|
161 | when(answers) {
|
162 |
|
163 | if (
|
164 | config.askForBreakingChangeFirst ||
|
165 | (config.allowBreakingChanges && config.allowBreakingChanges.indexOf(answers.type.toLowerCase()) >= 0)
|
166 | ) {
|
167 | return true;
|
168 | }
|
169 | return false;
|
170 | },
|
171 | },
|
172 | {
|
173 | type: 'input',
|
174 | name: 'footer',
|
175 | message: messages.footer,
|
176 | when: isNotWip,
|
177 | },
|
178 | {
|
179 | type: 'expand',
|
180 | name: 'confirmCommit',
|
181 | choices: [
|
182 | { key: 'y', name: 'Yes', value: 'yes' },
|
183 | { key: 'n', name: 'Abort commit', value: 'no' },
|
184 | { key: 'e', name: 'Edit message', value: 'edit' },
|
185 | ],
|
186 | default: 0,
|
187 | message(answers) {
|
188 | const SEP = '###--------------------------------------------------------###';
|
189 | log.info(`\n${SEP}\n${buildCommit(answers, config)}\n${SEP}\n`);
|
190 | return messages.confirmCommit;
|
191 | },
|
192 | },
|
193 | ];
|
194 |
|
195 | questions = questions.filter(item => !skipQuestions.includes(item.name));
|
196 |
|
197 | if (config.askForBreakingChangeFirst) {
|
198 | const isBreaking = oneQuestion => oneQuestion.name === 'breaking';
|
199 |
|
200 | const breakingQuestion = _.filter(questions, isBreaking);
|
201 | const questionWithoutBreaking = _.reject(questions, isBreaking);
|
202 |
|
203 | questions = _.concat(breakingQuestion, questionWithoutBreaking);
|
204 | }
|
205 |
|
206 | return questions;
|
207 | },
|
208 | };
|