cli.js 12 KB

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