UNPKG

13.6 kBJavaScriptView Raw
1"use strict";
2var __asyncValues = (this && this.__asyncValues) || function (o) {
3 if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
4 var m = o[Symbol.asyncIterator], i;
5 return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
6 function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
7 function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
8};
9var __importDefault = (this && this.__importDefault) || function (mod) {
10 return (mod && mod.__esModule) ? mod : { "default": mod };
11};
12Object.defineProperty(exports, "__esModule", { value: true });
13const execa_1 = __importDefault(require("execa"));
14const load_1 = __importDefault(require("@commitlint/load"));
15const lint_1 = __importDefault(require("@commitlint/lint"));
16const read_1 = __importDefault(require("@commitlint/read"));
17const lodash_isfunction_1 = __importDefault(require("lodash.isfunction"));
18const resolve_from_1 = __importDefault(require("resolve-from"));
19const resolve_global_1 = __importDefault(require("resolve-global"));
20const yargs_1 = __importDefault(require("yargs"));
21const util_1 = __importDefault(require("util"));
22const cli_error_1 = require("./cli-error");
23const pkg = require('../package');
24const gitDefaultCommentChar = '#';
25const cli = yargs_1.default
26 .options({
27 color: {
28 alias: 'c',
29 default: true,
30 description: 'toggle colored output',
31 type: 'boolean',
32 },
33 config: {
34 alias: 'g',
35 description: 'path to the config file',
36 type: 'string',
37 },
38 'print-config': {
39 type: 'boolean',
40 default: false,
41 description: 'print resolved config',
42 },
43 cwd: {
44 alias: 'd',
45 default: process.cwd(),
46 defaultDescription: '(Working Directory)',
47 description: 'directory to execute in',
48 type: 'string',
49 },
50 edit: {
51 alias: 'e',
52 description: 'read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG',
53 type: 'string',
54 },
55 env: {
56 alias: 'E',
57 description: 'check message in the file at path given by environment variable value',
58 type: 'string',
59 },
60 extends: {
61 alias: 'x',
62 description: 'array of shareable configurations to extend',
63 type: 'array',
64 },
65 'help-url': {
66 alias: 'H',
67 type: 'string',
68 description: 'help url in error message',
69 },
70 from: {
71 alias: 'f',
72 description: 'lower end of the commit range to lint; applies if edit=false',
73 type: 'string',
74 },
75 'git-log-args': {
76 description: "addditional git log arguments as space separated string, example '--first-parent --cherry-pick'",
77 type: 'string',
78 },
79 format: {
80 alias: 'o',
81 description: 'output format of the results',
82 type: 'string',
83 },
84 'parser-preset': {
85 alias: 'p',
86 description: 'configuration preset to use for conventional-commits-parser',
87 type: 'string',
88 },
89 quiet: {
90 alias: 'q',
91 default: false,
92 description: 'toggle console output',
93 type: 'boolean',
94 },
95 to: {
96 alias: 't',
97 description: 'upper end of the commit range to lint; applies if edit=false',
98 type: 'string',
99 },
100 verbose: {
101 alias: 'V',
102 type: 'boolean',
103 description: 'enable verbose output for reports without problems',
104 },
105 strict: {
106 alias: 's',
107 type: 'boolean',
108 description: 'enable strict mode; result code 2 for warnings, 3 for errors',
109 },
110})
111 .version('version', 'display version information', `${pkg.name}@${pkg.version}`)
112 .alias('v', 'version')
113 .help('help')
114 .alias('h', 'help')
115 .usage(`${pkg.name}@${pkg.version} - ${pkg.description}\n`)
116 .usage(`[input] reads from stdin if --edit, --env, --from and --to are omitted`)
117 .strict();
118main(cli.argv).catch((err) => {
119 setTimeout(() => {
120 if (err.type === pkg.name) {
121 process.exit(err.error_code);
122 }
123 throw err;
124 }, 0);
125});
126async function stdin() {
127 var _a, e_1, _b, _c;
128 let result = '';
129 if (process.stdin.isTTY) {
130 return result;
131 }
132 process.stdin.setEncoding('utf8');
133 try {
134 for (var _d = true, _e = __asyncValues(process.stdin), _f; _f = await _e.next(), _a = _f.done, !_a;) {
135 _c = _f.value;
136 _d = false;
137 try {
138 const chunk = _c;
139 result += chunk;
140 }
141 finally {
142 _d = true;
143 }
144 }
145 }
146 catch (e_1_1) { e_1 = { error: e_1_1 }; }
147 finally {
148 try {
149 if (!_d && !_a && (_b = _e.return)) await _b.call(_e);
150 }
151 finally { if (e_1) throw e_1.error; }
152 }
153 return result;
154}
155async function resolveArgs(args) {
156 return typeof args.then === 'function' ? await args : args;
157}
158async function main(args) {
159 var _a;
160 const options = await resolveArgs(args);
161 if (typeof options.edit === 'undefined') {
162 options.edit = false;
163 }
164 const raw = options._;
165 const flags = normalizeFlags(options);
166 if (flags['print-config']) {
167 const loaded = await (0, load_1.default)(getSeed(flags), {
168 cwd: flags.cwd,
169 file: flags.config,
170 });
171 console.log(util_1.default.inspect(loaded, false, null, options.color));
172 return;
173 }
174 const fromStdin = checkFromStdin(raw, flags);
175 const input = await (fromStdin
176 ? stdin()
177 : (0, read_1.default)({
178 to: flags.to,
179 from: flags.from,
180 edit: flags.edit,
181 cwd: flags.cwd,
182 gitLogArgs: flags['git-log-args'],
183 }));
184 const messages = (Array.isArray(input) ? input : [input])
185 .filter((message) => typeof message === 'string')
186 .filter((message) => message.trim() !== '')
187 .filter(Boolean);
188 if (messages.length === 0 && !checkFromRepository(flags)) {
189 const err = new cli_error_1.CliError('[input] is required: supply via stdin, or --env or --edit or --from and --to', pkg.name);
190 yargs_1.default.showHelp('log');
191 console.log(err.message);
192 throw err;
193 }
194 const loaded = await (0, load_1.default)(getSeed(flags), {
195 cwd: flags.cwd,
196 file: flags.config,
197 });
198 const parserOpts = selectParserOpts(loaded.parserPreset);
199 const opts = {
200 parserOpts: {},
201 plugins: {},
202 ignores: [],
203 defaultIgnores: true,
204 };
205 if (parserOpts) {
206 opts.parserOpts = parserOpts;
207 }
208 if (loaded.plugins) {
209 opts.plugins = loaded.plugins;
210 }
211 if (loaded.ignores) {
212 opts.ignores = loaded.ignores;
213 }
214 if (loaded.defaultIgnores === false) {
215 opts.defaultIgnores = false;
216 }
217 const format = loadFormatter(loaded, flags);
218 // If reading from `.git/COMMIT_EDIT_MSG`, strip comments using
219 // core.commentChar from git configuration, falling back to '#'.
220 if (flags.edit) {
221 try {
222 const { stdout } = await (0, execa_1.default)('git', ['config', 'core.commentChar']);
223 opts.parserOpts.commentChar = stdout.trim() || gitDefaultCommentChar;
224 }
225 catch (e) {
226 const execaError = e;
227 // git config returns exit code 1 when the setting is unset,
228 // don't warn in this case.
229 if (!execaError.failed || execaError.exitCode !== 1) {
230 console.warn('Could not determine core.commentChar git configuration', e);
231 }
232 opts.parserOpts.commentChar = gitDefaultCommentChar;
233 }
234 }
235 const results = await Promise.all(messages.map((message) => (0, lint_1.default)(message, loaded.rules, opts)));
236 if (Object.keys(loaded.rules).length === 0) {
237 let input = '';
238 if (results.length !== 0) {
239 input = results[0].input;
240 }
241 results.splice(0, results.length, {
242 valid: false,
243 errors: [
244 {
245 level: 2,
246 valid: false,
247 name: 'empty-rules',
248 message: [
249 'Please add rules to your `commitlint.config.js`',
250 ' - Getting started guide: https://commitlint.js.org/#/?id=getting-started',
251 ' - Example config: https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/config-conventional/index.js',
252 ].join('\n'),
253 },
254 ],
255 warnings: [],
256 input,
257 });
258 }
259 const report = results.reduce((info, result) => {
260 info.valid = result.valid ? info.valid : false;
261 info.errorCount += result.errors.length;
262 info.warningCount += result.warnings.length;
263 info.results.push(result);
264 return info;
265 }, {
266 valid: true,
267 errorCount: 0,
268 warningCount: 0,
269 results: [],
270 });
271 const helpUrl = ((_a = flags['help-url']) === null || _a === void 0 ? void 0 : _a.trim()) || loaded.helpUrl;
272 const output = format(report, {
273 color: flags.color,
274 verbose: flags.verbose,
275 helpUrl,
276 });
277 if (!flags.quiet && output !== '') {
278 console.log(output);
279 }
280 if (flags.strict) {
281 if (report.errorCount > 0) {
282 throw new cli_error_1.CliError(output, pkg.name, 3);
283 }
284 if (report.warningCount > 0) {
285 throw new cli_error_1.CliError(output, pkg.name, 2);
286 }
287 }
288 if (!report.valid) {
289 throw new cli_error_1.CliError(output, pkg.name);
290 }
291}
292function checkFromStdin(input, flags) {
293 return input.length === 0 && !checkFromRepository(flags);
294}
295function checkFromRepository(flags) {
296 return checkFromHistory(flags) || checkFromEdit(flags);
297}
298function checkFromEdit(flags) {
299 return Boolean(flags.edit) || Boolean(flags.env);
300}
301function checkFromHistory(flags) {
302 return typeof flags.from === 'string' || typeof flags.to === 'string';
303}
304function normalizeFlags(flags) {
305 const edit = getEditValue(flags);
306 return Object.assign(Object.assign({}, flags), { edit });
307}
308function getEditValue(flags) {
309 if (flags.env) {
310 if (!(flags.env in process.env)) {
311 throw new Error(`Received '${flags.env}' as value for -E | --env, but environment variable '${flags.env}' is not available globally`);
312 }
313 return process.env[flags.env];
314 }
315 const { edit } = flags;
316 // If the edit flag is set but empty (i.e '-e') we default
317 // to .git/COMMIT_EDITMSG
318 if (edit === '') {
319 return true;
320 }
321 if (typeof edit === 'boolean') {
322 return edit;
323 }
324 // The recommended method to specify -e with husky was `commitlint -e $HUSKY_GIT_PARAMS`
325 // This does not work properly with win32 systems, where env variable declarations
326 // use a different syntax
327 // See https://github.com/conventional-changelog/commitlint/issues/103 for details
328 // This has been superceded by the `-E GIT_PARAMS` / `-E HUSKY_GIT_PARAMS`
329 const isGitParams = edit === '$GIT_PARAMS' || edit === '%GIT_PARAMS%';
330 const isHuskyParams = edit === '$HUSKY_GIT_PARAMS' || edit === '%HUSKY_GIT_PARAMS%';
331 if (isGitParams || isHuskyParams) {
332 console.warn(`Using environment variable syntax (${edit}) in -e |\
333--edit is deprecated. Use '{-E|--env} HUSKY_GIT_PARAMS instead'`);
334 if (isGitParams && 'GIT_PARAMS' in process.env) {
335 return process.env.GIT_PARAMS;
336 }
337 if ('HUSKY_GIT_PARAMS' in process.env) {
338 return process.env.HUSKY_GIT_PARAMS;
339 }
340 throw new Error(`Received ${edit} as value for -e | --edit, but GIT_PARAMS or HUSKY_GIT_PARAMS are not available globally.`);
341 }
342 return edit;
343}
344function getSeed(flags) {
345 const n = (flags.extends || []).filter((i) => typeof i === 'string');
346 return n.length > 0
347 ? { extends: n, parserPreset: flags['parser-preset'] }
348 : { parserPreset: flags['parser-preset'] };
349}
350function selectParserOpts(parserPreset) {
351 if (typeof parserPreset !== 'object') {
352 return undefined;
353 }
354 if (typeof parserPreset.parserOpts !== 'object') {
355 return undefined;
356 }
357 return parserPreset.parserOpts;
358}
359function loadFormatter(config, flags) {
360 const moduleName = flags.format || config.formatter || '@commitlint/format';
361 const modulePath = resolve_from_1.default.silent(__dirname, moduleName) ||
362 resolve_from_1.default.silent(flags.cwd, moduleName) ||
363 resolve_global_1.default.silent(moduleName);
364 if (modulePath) {
365 const moduleInstance = require(modulePath);
366 if ((0, lodash_isfunction_1.default)(moduleInstance.default)) {
367 return moduleInstance.default;
368 }
369 return moduleInstance;
370 }
371 throw new Error(`Using format ${moduleName}, but cannot find the module.`);
372}
373// Catch unhandled rejections globally
374process.on('unhandledRejection', (reason, promise) => {
375 console.log('Unhandled Rejection at: Promise ', promise, ' reason: ', reason);
376 throw reason;
377});
378//# sourceMappingURL=cli.js.map
\No newline at end of file