UNPKG

8.84 kBJavaScriptView Raw
1#!/usr/bin/env node
2'use strict';
3
4require('babel-polyfill'); // eslint-disable-line import/no-unassigned-import
5
6const load = require('@commitlint/load');
7const lint = require('@commitlint/lint');
8const read = require('@commitlint/read');
9const meow = require('meow');
10
11var _require = require('lodash');
12
13const merge = _require.merge,
14 pick = _require.pick;
15
16const stdin = require('get-stdin');
17const resolveFrom = require('resolve-from');
18const resolveGlobal = require('resolve-global');
19
20const pkg = require('../package');
21const help = require('./help');
22
23const flags = {
24 color: {
25 alias: 'c',
26 default: true,
27 description: 'toggle colored output',
28 type: 'boolean'
29 },
30 config: {
31 alias: 'g',
32 default: null,
33 description: 'path to the config file',
34 type: 'string'
35 },
36 cwd: {
37 alias: 'd',
38 default: process.cwd(),
39 description: 'directory to execute in',
40 type: 'string'
41 },
42 edit: {
43 alias: 'e',
44 default: false,
45 description: 'read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG',
46 type: 'string'
47 },
48 env: {
49 alias: 'E',
50 default: null,
51 description: 'check message in the file at path given by environment variable value',
52 type: 'string'
53 },
54 extends: {
55 alias: 'x',
56 description: 'array of shareable configurations to extend',
57 type: 'string'
58 },
59 help: {
60 alias: 'h',
61 type: 'boolean',
62 description: 'display this help message'
63 },
64 from: {
65 alias: 'f',
66 default: null,
67 description: 'lower end of the commit range to lint; applies if edit=false',
68 type: 'string'
69 },
70 format: {
71 alias: 'o',
72 default: null,
73 description: 'output format of the results',
74 type: 'string'
75 },
76 'parser-preset': {
77 alias: 'p',
78 description: 'configuration preset to use for conventional-commits-parser',
79 type: 'string'
80 },
81 quiet: {
82 alias: 'q',
83 default: false,
84 description: 'toggle console output',
85 type: 'boolean'
86 },
87 to: {
88 alias: 't',
89 default: null,
90 description: 'upper end of the commit range to lint; applies if edit=false',
91 type: 'string'
92 },
93 version: {
94 alias: 'v',
95 type: 'boolean',
96 description: 'display version information'
97 }
98};
99
100const cli = meow({
101 description: `${pkg.name}@${pkg.version} - ${pkg.description}`,
102 flags,
103 help: `[input] reads from stdin if --edit, --env, --from and --to are omitted\n${help(flags)}`,
104 unknown(arg) {
105 throw new Error(`unknown flags: ${arg}`);
106 }
107});
108
109main(cli).catch(err => setTimeout(() => {
110 if (err.type === pkg.name) {
111 process.exit(1);
112 }
113 throw err;
114}));
115
116function main(options) {
117 return new Promise(function ($return, $error) {
118 var raw, flags, fromStdin, range, input, messages, loadOpts, loaded, parserOpts, opts, format, results, originalInput, report, output;
119 raw = options.input;
120 flags = normalizeFlags(options.flags);
121 fromStdin = checkFromStdin(raw, flags);
122 range = pick(flags, 'edit', 'from', 'to');
123 return Promise.resolve(fromStdin ? stdin() : read(range, { cwd: flags.cwd })).then(function ($await_1) {
124 try {
125 input = $await_1;
126 messages = (Array.isArray(input) ? input : [input]).filter(message => typeof message === 'string').filter(message => message.trim() !== '').filter(Boolean);
127
128
129 if (messages.length === 0 && !checkFromRepository(flags)) {
130 let err;
131 err = new Error('[input] is required: supply via stdin, or --env or --edit or --from and --to');
132
133 err.type = pkg.name;
134 console.log(`${cli.help}\n`);
135 console.log(err.message);
136 return $error(err);
137 }
138
139 loadOpts = { cwd: flags.cwd, file: flags.config };
140 return Promise.resolve(load(getSeed(flags), loadOpts)).then(function ($await_2) {
141 try {
142 loaded = $await_2;
143 parserOpts = selectParserOpts(loaded.parserPreset);
144 opts = parserOpts ? { parserOpts } : { parserOpts: {} };
145 format = loadFormatter(loaded, flags);
146
147
148 // Strip comments if reading from `.git/COMMIT_EDIT_MSG`
149 if (range.edit) {
150 opts.parserOpts.commentChar = '#';
151 }
152
153 return Promise.resolve(Promise.all(messages.map(message => lint(message, loaded.rules, opts)))).then(function ($await_3) {
154 try {
155 results = $await_3;
156
157
158 if (Object.keys(loaded.rules).length === 0) {
159 let input = '';
160
161 if (results.length !== 0) {
162 originalInput = results[0].input;
163
164 input = originalInput;
165 }
166
167 results.splice(0, results.length, {
168 valid: false,
169 errors: [{
170 level: 2,
171 valid: false,
172 name: 'empty-rules',
173 message: ['Please add rules to your `commitlint.config.js`', ' - Getting started guide: https://git.io/fhHij', ' - Example config: https://git.io/fhHip'].join('\n')
174 }],
175 warnings: [],
176 input
177 });
178 }
179
180 report = results.reduce((info, result) => {
181 info.valid = result.valid ? info.valid : false;
182 info.errorCount += result.errors.length;
183 info.warningCount += result.warnings.length;
184 info.results.push(result);
185
186 return info;
187 }, {
188 valid: true,
189 errorCount: 0,
190 warningCount: 0,
191 results: []
192 });
193 output = format(report, { color: flags.color });
194
195
196 if (!flags.quiet) {
197 console.log(output);
198 }
199
200 if (!report.valid) {
201 let err;
202 err = new Error(output);
203
204 err.type = pkg.name;
205 return $error(err);
206 }
207 return $return();
208 } catch ($boundEx) {
209 return $error($boundEx);
210 }
211 }.bind(this), $error);
212 } catch ($boundEx) {
213 return $error($boundEx);
214 }
215 }.bind(this), $error);
216 } catch ($boundEx) {
217 return $error($boundEx);
218 }
219 }.bind(this), $error);
220 }.bind(this));
221}
222
223function checkFromStdin(input, flags) {
224 return input.length === 0 && !checkFromRepository(flags);
225}
226
227function checkFromRepository(flags) {
228 return checkFromHistory(flags) || checkFromEdit(flags);
229}
230
231function checkFromEdit(flags) {
232 return Boolean(flags.edit) || flags.env;
233}
234
235function checkFromHistory(flags) {
236 return typeof flags.from === 'string' || typeof flags.to === 'string';
237}
238
239function normalizeFlags(flags) {
240 const edit = getEditValue(flags);
241 return merge({}, flags, { edit, e: edit });
242}
243
244function getEditValue(flags) {
245 if (flags.env) {
246 if (!(flags.env in process.env)) {
247 throw new Error(`Recieved '${flags.env}' as value for -E | --env, but environment variable '${flags.env}' is not available globally`);
248 }
249 return process.env[flags.env];
250 }
251 const edit = flags.edit;
252 // If the edit flag is set but empty (i.e '-e') we default
253 // to .git/COMMIT_EDITMSG
254
255 if (edit === '') {
256 return true;
257 }
258 if (typeof edit === 'boolean') {
259 return edit;
260 }
261 // The recommended method to specify -e with husky was `commitlint -e $HUSKY_GIT_PARAMS`
262 // This does not work properly with win32 systems, where env variable declarations
263 // use a different syntax
264 // See https://github.com/conventional-changelog/commitlint/issues/103 for details
265 // This has been superceded by the `-E GIT_PARAMS` / `-E HUSKY_GIT_PARAMS`
266 const isGitParams = edit === '$GIT_PARAMS' || edit === '%GIT_PARAMS%';
267 const isHuskyParams = edit === '$HUSKY_GIT_PARAMS' || edit === '%HUSKY_GIT_PARAMS%';
268
269 if (isGitParams || isHuskyParams) {
270 console.warn(`Using environment variable syntax (${edit}) in -e |\
271--edit is deprecated. Use '{-E|--env} HUSKY_GIT_PARAMS instead'`);
272
273 if (isGitParams && 'GIT_PARAMS' in process.env) {
274 return process.env.GIT_PARAMS;
275 }
276 if ('HUSKY_GIT_PARAMS' in process.env) {
277 return process.env.HUSKY_GIT_PARAMS;
278 }
279 throw new Error(`Received ${edit} as value for -e | --edit, but GIT_PARAMS or HUSKY_GIT_PARAMS are not available globally.`);
280 }
281 return edit;
282}
283
284function getSeed(seed) {
285 const e = Array.isArray(seed.extends) ? seed.extends : [seed.extends];
286 const n = e.filter(i => typeof i === 'string');
287 return n.length > 0 ? { extends: n, parserPreset: seed.parserPreset } : { parserPreset: seed.parserPreset };
288}
289
290function selectParserOpts(parserPreset) {
291 if (typeof parserPreset !== 'object') {
292 return undefined;
293 }
294
295 if (typeof parserPreset.parserOpts !== 'object') {
296 return undefined;
297 }
298
299 return parserPreset.parserOpts;
300}
301
302function loadFormatter(config, flags) {
303 const moduleName = flags.format || config.formatter || '@commitlint/format';
304 const modulePath = resolveFrom.silent(__dirname, moduleName) || resolveFrom.silent(flags.cwd, moduleName) || resolveGlobal.silent(moduleName);
305
306 if (modulePath) {
307 return require(modulePath);
308 }
309
310 throw new Error(`Using format ${moduleName}, but cannot find the module.`);
311}
312
313// Catch unhandled rejections globally
314process.on('unhandledRejection', (reason, promise) => {
315 console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
316 throw reason;
317});
318//# sourceMappingURL=cli.js.map
\No newline at end of file