1 | #!/usr/bin/env node
|
2 | 'use strict';
|
3 |
|
4 | require('babel-polyfill');
|
5 |
|
6 | const load = require('@commitlint/load');
|
7 | const lint = require('@commitlint/lint');
|
8 | const read = require('@commitlint/read');
|
9 | const meow = require('meow');
|
10 |
|
11 | var _require = require('lodash');
|
12 |
|
13 | const merge = _require.merge,
|
14 | pick = _require.pick;
|
15 |
|
16 | const stdin = require('get-stdin');
|
17 | const resolveFrom = require('resolve-from');
|
18 | const resolveGlobal = require('resolve-global');
|
19 |
|
20 | const pkg = require('../package');
|
21 | const help = require('./help');
|
22 |
|
23 | const 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 |
|
100 | const 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 |
|
109 | main(cli).catch(err => setTimeout(() => {
|
110 | if (err.type === pkg.name) {
|
111 | process.exit(1);
|
112 | }
|
113 | throw err;
|
114 | }));
|
115 |
|
116 | function 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 |
|
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 |
|
223 | function checkFromStdin(input, flags) {
|
224 | return input.length === 0 && !checkFromRepository(flags);
|
225 | }
|
226 |
|
227 | function checkFromRepository(flags) {
|
228 | return checkFromHistory(flags) || checkFromEdit(flags);
|
229 | }
|
230 |
|
231 | function checkFromEdit(flags) {
|
232 | return Boolean(flags.edit) || flags.env;
|
233 | }
|
234 |
|
235 | function checkFromHistory(flags) {
|
236 | return typeof flags.from === 'string' || typeof flags.to === 'string';
|
237 | }
|
238 |
|
239 | function normalizeFlags(flags) {
|
240 | const edit = getEditValue(flags);
|
241 | return merge({}, flags, { edit, e: edit });
|
242 | }
|
243 |
|
244 | function 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 |
|
253 |
|
254 |
|
255 | if (edit === '') {
|
256 | return true;
|
257 | }
|
258 | if (typeof edit === 'boolean') {
|
259 | return edit;
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 |
|
265 |
|
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 |
|
284 | function 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 |
|
290 | function 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 |
|
302 | function 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 |
|
314 | process.on('unhandledRejection', (reason, promise) => {
|
315 | console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
|
316 | throw reason;
|
317 | });
|
318 |
|
\ | No newline at end of file |