@commitlint/cli
Advanced tools
Comparing version 5.2.0 to 5.3.0-0
224
lib/cli.js
#!/usr/bin/env node | ||
'use strict'; | ||
var _slicedToArray2 = require('babel-runtime/helpers/slicedToArray'); | ||
var _slicedToArray3 = _interopRequireDefault(_slicedToArray2); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
require('babel-polyfill'); // eslint-disable-line import/no-unassigned-import | ||
var core = require('@commitlint/core'); | ||
var chalk = require('chalk'); | ||
var meow = require('meow'); | ||
var merge = require('lodash/merge'); | ||
var _require = require('lodash'), | ||
merge = _require.merge, | ||
pick = _require.pick; | ||
var pkg = require('../package'); | ||
var commands = require('./commands'); | ||
var stdin = require('get-stdin'); | ||
var FORMATS = ['commitlint', 'json']; | ||
var COMMANDS = ['config']; | ||
var pkg = require('../package'); | ||
var help = require('./help'); | ||
var HELP = ` | ||
Commands | ||
commitlint lint commits, [input] reads from stdin if --edit, --from and --to are omitted | ||
var configuration = { | ||
string: ['cwd', 'from', 'to', 'edit', 'extends', 'parser-preset'], | ||
Options | ||
--cwd, -d directory to execute in, defaults to: process.cwd() | ||
--extends, -x array of shareable configurations to extend | ||
--format, -o format to use, defaults to "commitlint". available: "commitlint", "json" | ||
--parser-preset, -p configuration preset to use for conventional-commits-parser | ||
--quiet, -q toggle console output | ||
commitlint | ||
--color, -c toggle colored output, defaults to: true | ||
--edit, -e read last commit message from the specified file or falls back to ./.git/COMMIT_EDITMSG | ||
--from, -f lower end of the commit range to lint; applies if edit=false | ||
--to, -t upper end of the commit range to lint; applies if edit=false | ||
Usage | ||
$ echo "some commit" | commitlint | ||
$ commitlint --to=master | ||
$ commitlint --from=HEAD~1 | ||
`; | ||
var cli = meow({ | ||
help: HELP, | ||
description: `${pkg.name}@${pkg.version} - ${pkg.description}` | ||
}, { | ||
string: ['cwd', 'format', 'from', 'to', 'edit', 'extends', 'parser-preset'], | ||
boolean: ['help', 'version', 'quiet', 'color'], | ||
@@ -27,19 +55,10 @@ alias: { | ||
f: 'from', | ||
h: 'help', | ||
o: 'format', | ||
p: 'parser-preset', | ||
q: 'quiet', | ||
t: 'to', | ||
q: 'quiet', | ||
h: 'help', | ||
v: 'version', | ||
x: 'extends', | ||
p: 'parser-preset' | ||
x: 'extends' | ||
}, | ||
description: { | ||
color: 'toggle colored output', | ||
cwd: 'directory to execute in', | ||
edit: 'read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG', | ||
extends: 'array of shareable configurations to extend', | ||
from: 'lower end of the commit range to lint; applies if edit=false', | ||
to: 'upper end of the commit range to lint; applies if edit=false', | ||
quiet: 'toggle console output', | ||
'parser-preset': 'configuration preset to use for conventional-commits-parser' | ||
}, | ||
default: { | ||
@@ -54,14 +73,28 @@ color: true, | ||
unknown(arg) { | ||
throw new Error(`unknown flags: ${arg}`); | ||
if (COMMANDS.includes(arg)) { | ||
return; | ||
} | ||
console.log(HELP); | ||
if (!arg.startsWith('-') && !COMMANDS.includes(arg)) { | ||
console.log(`<command> must be on of: [config], received "${arg}"`); | ||
} else { | ||
console.log(`unknown flags: ${arg}`); | ||
} | ||
process.exit(1); | ||
} | ||
}; | ||
}); | ||
var cli = meow({ | ||
help: `[input] reads from stdin if --edit, --from and --to are omitted\n${help(configuration)}`, | ||
description: `${pkg.name}@${pkg.version} - ${pkg.description}` | ||
}, configuration); | ||
main(cli).catch(function (err) { | ||
return setTimeout(function () { | ||
if (err.quiet) { | ||
process.exit(1); | ||
} | ||
if (err.help) { | ||
console.log(`${cli.help}\n`); | ||
} | ||
if (err.type === pkg.name) { | ||
console.log(err.message); | ||
process.exit(1); | ||
@@ -75,115 +108,54 @@ } | ||
return new Promise(function ($return, $error) { | ||
var raw, flags, fromStdin, range, fmt, input, messages, err; | ||
var raw = Array.isArray(options.input) ? options.input : []; | ||
raw = options.input; | ||
flags = normalizeFlags(options.flags); | ||
fromStdin = checkFromStdin(raw, flags); | ||
var _raw = (0, _slicedToArray3.default)(raw, 1), | ||
command = _raw[0]; | ||
range = pick(flags, 'edit', 'from', 'to'); | ||
fmt = new chalk.constructor({ enabled: flags.color }); | ||
var flags = normalizeFlags(options.flags); | ||
return Promise.resolve(fromStdin ? stdin() : core.read(range, { cwd: flags.cwd })).then(function ($await_1) { | ||
try { | ||
input = $await_1; | ||
if (!command) { | ||
return $return(commands.lint(raw, flags)); | ||
} | ||
messages = (Array.isArray(input) ? input : [input]).filter(function (message) { | ||
return typeof message === 'string'; | ||
}).filter(Boolean); | ||
switch (command) { | ||
case 'config': | ||
return $return(commands.config(raw, { | ||
cwd: flags.cwd, | ||
extends: flags.extends, | ||
format: flags.format, | ||
parserPreset: flags.parserPreset | ||
})); | ||
default: | ||
var err = new Error(`<command> must be on of: [config], received "${command}"`); | ||
err.help = true; | ||
err.type = pkg.name; | ||
return $error(err); | ||
if (messages.length === 0 && !checkFromRepository(flags)) { | ||
err = new Error('[input] is required: supply via stdin, or --edit or --from and --to'); | ||
err.type = pkg.name; | ||
console.log(`${cli.help}\n`); | ||
console.log(err.message); | ||
return $error(err); | ||
} | ||
return $return(Promise.all(messages.map(function (message) { | ||
return new Promise(function ($return, $error) { | ||
var loaded, parserOpts, opts, report, formatted, error; | ||
return Promise.resolve(core.load(getSeed(flags), { cwd: flags.cwd })).then(function ($await_2) { | ||
try { | ||
loaded = $await_2; | ||
parserOpts = selectParserOpts(loaded.parserPreset); | ||
opts = parserOpts ? { parserOpts } : undefined; | ||
return Promise.resolve(core.lint(message, loaded.rules, opts)).then(function ($await_3) { | ||
try { | ||
report = $await_3; | ||
formatted = core.format(report, { color: flags.color }); | ||
if (!flags.quiet) { | ||
console.log(`${fmt.grey('⧗')} input: ${fmt.bold(message.split('\n')[0])}`); | ||
console.log(formatted.join('\n')); | ||
} | ||
if (report.errors.length > 0) { | ||
error = new Error(formatted[formatted.length - 1]); | ||
error.type = pkg.name; | ||
return $error(error); | ||
} | ||
console.log(''); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}))); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} | ||
return $return(); | ||
}.bind(this)); | ||
} | ||
function checkFromStdin(input, flags) { | ||
return input.length === 0 && !checkFromRepository(flags); | ||
} | ||
function normalizeFlags(raw) { | ||
var flags = merge({}, raw); | ||
function checkFromRepository(flags) { | ||
return checkFromHistory(flags) || checkFromEdit(flags); | ||
} | ||
function checkFromEdit(flags) { | ||
return Boolean(flags.edit); | ||
} | ||
function checkFromHistory(flags) { | ||
return typeof flags.from === 'string' || typeof flags.to === 'string'; | ||
} | ||
function normalizeFlags(flags) { | ||
// The `edit` flag is either a boolean or a string but we are only allowed | ||
// to specify one of them in minimist | ||
if (flags.edit === '') { | ||
return merge({}, flags, { edit: true, e: true }); | ||
merge(flags, { edit: true, e: true }); | ||
} | ||
return flags; | ||
} | ||
function getSeed(seed) { | ||
var e = Array.isArray(seed.extends) ? seed.extends : [seed.extends]; | ||
var n = e.filter(function (i) { | ||
return typeof i === 'string'; | ||
}); | ||
return n.length > 0 ? { extends: n, parserPreset: seed.parserPreset } : { parserPreset: seed.parserPreset }; | ||
} | ||
function selectParserOpts(parserPreset) { | ||
if (typeof parserPreset !== 'object') { | ||
return undefined; | ||
if (!('format' in flags)) { | ||
flags.format = 'commitlint'; | ||
} | ||
var opts = parserPreset.opts; | ||
if (typeof opts !== 'object') { | ||
return undefined; | ||
if (!FORMATS.includes(flags.format)) { | ||
var err = new Error(`--format must be on of: [${FORMATS.join(',')}], received "${flags.format}".`); | ||
err.quiet = flags.quiet; | ||
err.help = true; | ||
err.type = pkg.name; | ||
throw err; | ||
} | ||
return opts.parserOpts; | ||
return flags; | ||
} | ||
@@ -190,0 +162,0 @@ |
@@ -93,3 +93,3 @@ 'use strict'; | ||
(0, _ava2.default)('should produce no success output with --quiet flag', function (t) { | ||
(0, _ava2.default)('should throw for unknown command', function (t) { | ||
return new Promise(function ($return, $error) { | ||
@@ -100,7 +100,7 @@ var cwd, actual; | ||
cwd = $await_5; | ||
return Promise.resolve(cli(['--quiet'], { cwd })('foo: bar')).then(function ($await_6) { | ||
return Promise.resolve(cli(['foo'], { cwd })()).then(function ($await_6) { | ||
try { | ||
actual = $await_6; | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
t.is(actual.code, 1); | ||
t.true(actual.stdout.includes('<command> must be on of: [config], received "foo"')); | ||
return $return(); | ||
@@ -118,3 +118,3 @@ } catch ($boundEx) { | ||
(0, _ava2.default)('should produce no success output with -q flag', function (t) { | ||
(0, _ava2.default)('should produce no success output with --quiet flag', function (t) { | ||
return new Promise(function ($return, $error) { | ||
@@ -125,3 +125,3 @@ var cwd, actual; | ||
cwd = $await_7; | ||
return Promise.resolve(cli(['-q'], { cwd })('foo: bar')).then(function ($await_8) { | ||
return Promise.resolve(cli(['--quiet'], { cwd })('foo: bar')).then(function ($await_8) { | ||
try { | ||
@@ -143,3 +143,3 @@ actual = $await_8; | ||
(0, _ava2.default)('should succeed for input from stdin without rules', function (t) { | ||
(0, _ava2.default)('should produce no success output with -q flag', function (t) { | ||
return new Promise(function ($return, $error) { | ||
@@ -150,6 +150,7 @@ var cwd, actual; | ||
cwd = $await_9; | ||
return Promise.resolve(cli([], { cwd })('foo: bar')).then(function ($await_10) { | ||
return Promise.resolve(cli(['-q'], { cwd })('foo: bar')).then(function ($await_10) { | ||
try { | ||
actual = $await_10; | ||
t.is(actual.code, 0); | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
return $return(); | ||
@@ -167,6 +168,6 @@ } catch ($boundEx) { | ||
(0, _ava2.default)('should fail for input from stdin with rule from rc', function (t) { | ||
(0, _ava2.default)('should succeed for input from stdin without rules', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/simple')).then(function ($await_11) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_11) { | ||
try { | ||
@@ -177,4 +178,3 @@ cwd = $await_11; | ||
actual = $await_12; | ||
t.true(actual.stdout.includes('type must not be one of [foo]')); | ||
t.is(actual.code, 1); | ||
t.is(actual.code, 0); | ||
return $return(); | ||
@@ -192,9 +192,9 @@ } catch ($boundEx) { | ||
(0, _ava2.default)('should fail for input from stdin with rule from js', function (t) { | ||
(0, _ava2.default)('should fail for input from stdin with rule from rc', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/extends-root')).then(function ($await_13) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/simple')).then(function ($await_13) { | ||
try { | ||
cwd = $await_13; | ||
return Promise.resolve(cli(['--extends', './extended'], { cwd })('foo: bar')).then(function ($await_14) { | ||
return Promise.resolve(cli([], { cwd })('foo: bar')).then(function ($await_14) { | ||
try { | ||
@@ -216,13 +216,12 @@ actual = $await_14; | ||
(0, _ava2.default)('should produce no error output with --quiet flag', function (t) { | ||
(0, _ava2.default)('should fail for input from stdin with rule from js', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/simple')).then(function ($await_15) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/extends-root')).then(function ($await_15) { | ||
try { | ||
cwd = $await_15; | ||
return Promise.resolve(cli(['--quiet'], { cwd })('foo: bar')).then(function ($await_16) { | ||
return Promise.resolve(cli(['--extends', './extended'], { cwd })('foo: bar')).then(function ($await_16) { | ||
try { | ||
actual = $await_16; | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
t.true(actual.stdout.includes('type must not be one of [foo]')); | ||
t.is(actual.code, 1); | ||
@@ -241,3 +240,3 @@ return $return(); | ||
(0, _ava2.default)('should produce no error output with -q flag', function (t) { | ||
(0, _ava2.default)('should produce no error output with --quiet flag', function (t) { | ||
return new Promise(function ($return, $error) { | ||
@@ -248,3 +247,3 @@ var cwd, actual; | ||
cwd = $await_17; | ||
return Promise.resolve(cli(['-q'], { cwd })('foo: bar')).then(function ($await_18) { | ||
return Promise.resolve(cli(['--quiet'], { cwd })('foo: bar')).then(function ($await_18) { | ||
try { | ||
@@ -267,15 +266,226 @@ actual = $await_18; | ||
(0, _ava2.default)('should produce no error output with -q flag', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/simple')).then(function ($await_19) { | ||
try { | ||
cwd = $await_19; | ||
return Promise.resolve(cli(['-q'], { cwd })('foo: bar')).then(function ($await_20) { | ||
try { | ||
actual = $await_20; | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
t.is(actual.code, 1); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should throw for unknown --format', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_21) { | ||
try { | ||
cwd = $await_21; | ||
return Promise.resolve(cli(['--format=foo'], { cwd })('foo: bar')).then(function ($await_22) { | ||
try { | ||
actual = $await_22; | ||
t.is(actual.code, 1); | ||
t.true(actual.stdout.includes('--format must be on of: [commitlint,json], received "foo"')); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should produce json output with --format=json', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_23) { | ||
try { | ||
cwd = $await_23; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_24) { | ||
try { | ||
actual = $await_24; | ||
t.notThrows(function () { | ||
return JSON.parse(actual.stdout); | ||
}); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should produce no output when --quiet and --format=json', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_25) { | ||
try { | ||
cwd = $await_25; | ||
return Promise.resolve(cli(['--format=json', '--quiet'], { cwd })('foo: bar')).then(function ($await_26) { | ||
try { | ||
actual = $await_26; | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should produce json with expected truthy valid key', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual, data; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_27) { | ||
try { | ||
cwd = $await_27; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_28) { | ||
try { | ||
actual = $await_28; | ||
data = JSON.parse(actual.stdout); | ||
t.is(data.valid, true); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should produce json with expected falsy valid key', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual, data; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/simple')).then(function ($await_29) { | ||
try { | ||
cwd = $await_29; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_30) { | ||
try { | ||
actual = $await_30; | ||
data = JSON.parse(actual.stdout); | ||
t.is(data.valid, false); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should produce json with results array', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual, data; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_31) { | ||
try { | ||
cwd = $await_31; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_32) { | ||
try { | ||
actual = $await_32; | ||
data = JSON.parse(actual.stdout); | ||
t.is(Array.isArray(data.results), true); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('results array has one report for one message', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual, data; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_33) { | ||
try { | ||
cwd = $await_33; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_34) { | ||
try { | ||
actual = $await_34; | ||
data = JSON.parse(actual.stdout); | ||
t.is(data.results.length, 1); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('report has expected schema', function (t) { | ||
return new Promise(function ($return, $error) { | ||
var cwd, actual, data, result; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/empty')).then(function ($await_35) { | ||
try { | ||
cwd = $await_35; | ||
return Promise.resolve(cli(['--format=json'], { cwd })('foo: bar')).then(function ($await_36) { | ||
try { | ||
actual = $await_36; | ||
data = JSON.parse(actual.stdout); | ||
result = data.results[0]; | ||
t.is('report' in result, true); | ||
t.is('input' in result, true); | ||
return $return(); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
} catch ($boundEx) { | ||
return $error($boundEx); | ||
} | ||
}.bind(this), $error); | ||
}.bind(this)); | ||
}); | ||
(0, _ava2.default)('should work with husky commitmsg hook and git commit', function () { | ||
return new Promise(function ($return, $error) { | ||
var cwd; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/husky/integration')).then(function ($await_19) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/husky/integration')).then(function ($await_37) { | ||
try { | ||
cwd = $await_19; | ||
return Promise.resolve(writePkg({ scripts: { commitmsg: `${bin} -e` } }, { cwd })).then(function ($await_20) { | ||
cwd = $await_37; | ||
return Promise.resolve(writePkg({ scripts: { commitmsg: `${bin} -e` } }, { cwd })).then(function ($await_38) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('npm', ['install'], { cwd })).then(function ($await_21) { | ||
return Promise.resolve((0, _execa2.default)('npm', ['install'], { cwd })).then(function ($await_39) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('git', ['add', 'package.json'], { cwd })).then(function ($await_22) { | ||
return Promise.resolve((0, _execa2.default)('git', ['add', 'package.json'], { cwd })).then(function ($await_40) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('git', ['commit', '-m', '"test: this should work"'], { cwd })).then(function ($await_23) { | ||
return Promise.resolve((0, _execa2.default)('git', ['commit', '-m', '"test: this should work"'], { cwd })).then(function ($await_41) { | ||
try { | ||
@@ -309,13 +519,13 @@ return $return(); | ||
var upper, cwd; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/husky')).then(function ($await_24) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/husky')).then(function ($await_42) { | ||
try { | ||
upper = $await_24; | ||
upper = $await_42; | ||
cwd = _path2.default.join(upper, 'integration'); | ||
return Promise.resolve(writePkg({ scripts: { commitmsg: `${bin} -e` } }, { cwd: upper })).then(function ($await_25) { | ||
return Promise.resolve(writePkg({ scripts: { commitmsg: `${bin} -e` } }, { cwd: upper })).then(function ($await_43) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('npm', ['install'], { cwd })).then(function ($await_26) { | ||
return Promise.resolve((0, _execa2.default)('npm', ['install'], { cwd })).then(function ($await_44) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('git', ['add', 'package.json'], { cwd })).then(function ($await_27) { | ||
return Promise.resolve((0, _execa2.default)('git', ['add', 'package.json'], { cwd })).then(function ($await_45) { | ||
try { | ||
return Promise.resolve((0, _execa2.default)('git', ['commit', '-m', '"test: this should work"'], { cwd })).then(function ($await_28) { | ||
return Promise.resolve((0, _execa2.default)('git', ['commit', '-m', '"test: this should work"'], { cwd })).then(function ($await_46) { | ||
try { | ||
@@ -349,8 +559,8 @@ return $return(); | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/parser-preset')).then(function ($await_29) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/parser-preset')).then(function ($await_47) { | ||
try { | ||
cwd = $await_29; | ||
return Promise.resolve(cli(['--parser-preset', './parser-preset'], { cwd })('type(scope): subject')).then(function ($await_30) { | ||
cwd = $await_47; | ||
return Promise.resolve(cli(['--parser-preset', './parser-preset'], { cwd })('type(scope): subject')).then(function ($await_48) { | ||
try { | ||
actual = $await_30; | ||
actual = $await_48; | ||
t.is(actual.code, 1); | ||
@@ -373,8 +583,8 @@ t.true(actual.stdout.includes('message may not be empty [subject-empty]')); | ||
var cwd, actual; | ||
return Promise.resolve(_test.git.bootstrap('fixtures/parser-preset')).then(function ($await_31) { | ||
return Promise.resolve(_test.git.bootstrap('fixtures/parser-preset')).then(function ($await_49) { | ||
try { | ||
cwd = $await_31; | ||
return Promise.resolve(cli(['--parser-preset', './parser-preset'], { cwd })('----type(scope): subject')).then(function ($await_32) { | ||
cwd = $await_49; | ||
return Promise.resolve(cli(['--parser-preset', './parser-preset'], { cwd })('----type(scope): subject')).then(function ($await_50) { | ||
try { | ||
actual = $await_32; | ||
actual = $await_50; | ||
t.is(actual.code, 0); | ||
@@ -396,12 +606,12 @@ return $return(); | ||
var outer, cwd, actual; | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/outer-scope')).then(function ($await_33) { | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/outer-scope')).then(function ($await_51) { | ||
try { | ||
outer = $await_33; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_34) { | ||
outer = $await_51; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_52) { | ||
try { | ||
cwd = $await_34; | ||
cwd = $await_52; | ||
return Promise.resolve(cli([], { cwd })('inner: bar')).then(function ($await_35) { | ||
return Promise.resolve(cli([], { cwd })('inner: bar')).then(function ($await_53) { | ||
try { | ||
actual = $await_35; | ||
actual = $await_53; | ||
t.is(actual.code, 1); | ||
@@ -427,12 +637,12 @@ return $return(); | ||
var outer, cwd, actual; | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/outer-scope')).then(function ($await_36) { | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/outer-scope')).then(function ($await_54) { | ||
try { | ||
outer = $await_36; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_37) { | ||
outer = $await_54; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_55) { | ||
try { | ||
cwd = $await_37; | ||
cwd = $await_55; | ||
return Promise.resolve(cli([], { cwd })('outer: bar')).then(function ($await_38) { | ||
return Promise.resolve(cli([], { cwd })('outer: bar')).then(function ($await_56) { | ||
try { | ||
actual = $await_38; | ||
actual = $await_56; | ||
t.is(actual.code, 0); | ||
@@ -458,12 +668,12 @@ return $return(); | ||
var outer, cwd, actual; | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/inner-scope')).then(function ($await_39) { | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/inner-scope')).then(function ($await_57) { | ||
try { | ||
outer = $await_39; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_40) { | ||
outer = $await_57; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_58) { | ||
try { | ||
cwd = $await_40; | ||
cwd = $await_58; | ||
return Promise.resolve(cli([], { cwd })('inner: bar')).then(function ($await_41) { | ||
return Promise.resolve(cli([], { cwd })('inner: bar')).then(function ($await_59) { | ||
try { | ||
actual = $await_41; | ||
actual = $await_59; | ||
t.is(actual.code, 0); | ||
@@ -489,12 +699,12 @@ return $return(); | ||
var outer, cwd, actual; | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/inner-scope')).then(function ($await_42) { | ||
return Promise.resolve(_test.fix.bootstrap('fixtures/inner-scope')).then(function ($await_60) { | ||
try { | ||
outer = $await_42; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_43) { | ||
outer = $await_60; | ||
return Promise.resolve(_test.git.init(_path2.default.join(outer, 'inner-scope'))).then(function ($await_61) { | ||
try { | ||
cwd = $await_43; | ||
cwd = $await_61; | ||
return Promise.resolve(cli([], { cwd })('outer: bar')).then(function ($await_44) { | ||
return Promise.resolve(cli([], { cwd })('outer: bar')).then(function ($await_62) { | ||
try { | ||
actual = $await_44; | ||
actual = $await_62; | ||
t.is(actual.code, 1); | ||
@@ -522,7 +732,7 @@ return $return(); | ||
pkgPath = _path2.default.join(options.cwd, 'package.json'); | ||
return Promise.resolve(sander.readFile(pkgPath)).then(function ($await_45) { | ||
return Promise.resolve(sander.readFile(pkgPath)).then(function ($await_63) { | ||
try { | ||
pkg = JSON.parse($await_45); | ||
pkg = JSON.parse($await_63); | ||
result = (0, _lodash.merge)(pkg, payload); | ||
return Promise.resolve(sander.writeFile(pkgPath, JSON.stringify(result, null, ' '))).then(function ($await_46) { | ||
return Promise.resolve(sander.writeFile(pkgPath, JSON.stringify(result, null, ' '))).then(function ($await_64) { | ||
try { | ||
@@ -529,0 +739,0 @@ return $return(); |
{ | ||
"name": "@commitlint/cli", | ||
"version": "5.2.0", | ||
"version": "5.3.0-0", | ||
"description": "Lint your commit messages", | ||
@@ -72,4 +72,5 @@ "bin": { | ||
"dependencies": { | ||
"@commitlint/core": "^5.2.0", | ||
"@commitlint/core": "^5.3.0-0", | ||
"babel-polyfill": "6.26.0", | ||
"babel-runtime": "^6.26.0", | ||
"chalk": "2.3.0", | ||
@@ -76,0 +77,0 @@ "get-stdin": "5.0.1", |
241
src/cli.js
#!/usr/bin/env node | ||
require('babel-polyfill'); // eslint-disable-line import/no-unassigned-import | ||
const core = require('@commitlint/core'); | ||
const chalk = require('chalk'); | ||
const meow = require('meow'); | ||
const {merge, pick} = require('lodash'); | ||
const stdin = require('get-stdin'); | ||
const merge = require('lodash/merge'); | ||
const pkg = require('../package'); | ||
const help = require('./help'); | ||
const commands = require('./commands'); | ||
const configuration = { | ||
string: ['cwd', 'from', 'to', 'edit', 'extends', 'parser-preset'], | ||
boolean: ['help', 'version', 'quiet', 'color'], | ||
alias: { | ||
c: 'color', | ||
d: 'cwd', | ||
e: 'edit', | ||
f: 'from', | ||
t: 'to', | ||
q: 'quiet', | ||
h: 'help', | ||
v: 'version', | ||
x: 'extends', | ||
p: 'parser-preset' | ||
}, | ||
description: { | ||
color: 'toggle colored output', | ||
cwd: 'directory to execute in', | ||
edit: | ||
'read last commit message from the specified file or fallbacks to ./.git/COMMIT_EDITMSG', | ||
extends: 'array of shareable configurations to extend', | ||
from: 'lower end of the commit range to lint; applies if edit=false', | ||
to: 'upper end of the commit range to lint; applies if edit=false', | ||
quiet: 'toggle console output', | ||
'parser-preset': | ||
'configuration preset to use for conventional-commits-parser' | ||
}, | ||
default: { | ||
color: true, | ||
cwd: process.cwd(), | ||
edit: false, | ||
from: null, | ||
to: null, | ||
quiet: false | ||
}, | ||
unknown(arg) { | ||
throw new Error(`unknown flags: ${arg}`); | ||
} | ||
}; | ||
const FORMATS = ['commitlint', 'json']; | ||
const COMMANDS = ['config']; | ||
const HELP = ` | ||
Commands | ||
commitlint lint commits, [input] reads from stdin if --edit, --from and --to are omitted | ||
Options | ||
--cwd, -d directory to execute in, defaults to: process.cwd() | ||
--extends, -x array of shareable configurations to extend | ||
--format, -o format to use, defaults to "commitlint". available: "commitlint", "json" | ||
--parser-preset, -p configuration preset to use for conventional-commits-parser | ||
--quiet, -q toggle console output | ||
commitlint | ||
--color, -c toggle colored output, defaults to: true | ||
--edit, -e read last commit message from the specified file or falls back to ./.git/COMMIT_EDITMSG | ||
--from, -f lower end of the commit range to lint; applies if edit=false | ||
--to, -t upper end of the commit range to lint; applies if edit=false | ||
Usage | ||
$ echo "some commit" | commitlint | ||
$ commitlint --to=master | ||
$ commitlint --from=HEAD~1 | ||
`; | ||
const cli = meow( | ||
{ | ||
help: `[input] reads from stdin if --edit, --from and --to are omitted\n${help( | ||
configuration | ||
)}`, | ||
help: HELP, | ||
description: `${pkg.name}@${pkg.version} - ${pkg.description}` | ||
}, | ||
configuration | ||
{ | ||
string: ['cwd', 'format', 'from', 'to', 'edit', 'extends', 'parser-preset'], | ||
boolean: ['help', 'version', 'quiet', 'color'], | ||
alias: { | ||
c: 'color', | ||
d: 'cwd', | ||
e: 'edit', | ||
f: 'from', | ||
h: 'help', | ||
o: 'format', | ||
p: 'parser-preset', | ||
q: 'quiet', | ||
t: 'to', | ||
v: 'version', | ||
x: 'extends' | ||
}, | ||
default: { | ||
color: true, | ||
cwd: process.cwd(), | ||
edit: false, | ||
from: null, | ||
to: null, | ||
quiet: false | ||
}, | ||
unknown(arg) { | ||
if (COMMANDS.includes(arg)) { | ||
return; | ||
} | ||
console.log(HELP); | ||
if (!arg.startsWith('-') && !COMMANDS.includes(arg)) { | ||
console.log(`<command> must be on of: [config], received "${arg}"`); | ||
} else { | ||
console.log(`unknown flags: ${arg}`); | ||
} | ||
process.exit(1); | ||
} | ||
} | ||
); | ||
@@ -65,3 +85,10 @@ | ||
setTimeout(() => { | ||
if (err.quiet) { | ||
process.exit(1); | ||
} | ||
if (err.help) { | ||
console.log(`${cli.help}\n`); | ||
} | ||
if (err.type === pkg.name) { | ||
console.log(err.message); | ||
process.exit(1); | ||
@@ -74,98 +101,56 @@ } | ||
async function main(options) { | ||
const raw = options.input; | ||
const raw = Array.isArray(options.input) ? options.input : []; | ||
const [command] = raw; | ||
const flags = normalizeFlags(options.flags); | ||
const fromStdin = checkFromStdin(raw, flags); | ||
const range = pick(flags, 'edit', 'from', 'to'); | ||
const fmt = new chalk.constructor({enabled: flags.color}); | ||
if (!command) { | ||
return commands.lint(raw, flags); | ||
} | ||
const input = await (fromStdin | ||
? stdin() | ||
: core.read(range, {cwd: flags.cwd})); | ||
const messages = (Array.isArray(input) ? input : [input]) | ||
.filter(message => typeof message === 'string') | ||
.filter(Boolean); | ||
if (messages.length === 0 && !checkFromRepository(flags)) { | ||
const err = new Error( | ||
'[input] is required: supply via stdin, or --edit or --from and --to' | ||
); | ||
err.type = pkg.name; | ||
console.log(`${cli.help}\n`); | ||
console.log(err.message); | ||
throw err; | ||
switch (command) { | ||
case 'config': | ||
return commands.config(raw, { | ||
cwd: flags.cwd, | ||
extends: flags.extends, | ||
format: flags.format, | ||
parserPreset: flags.parserPreset | ||
}); | ||
default: { | ||
const err = new Error( | ||
`<command> must be on of: [config], received "${command}"` | ||
); | ||
err.help = true; | ||
err.type = pkg.name; | ||
throw err; | ||
} | ||
} | ||
return Promise.all( | ||
messages.map(async message => { | ||
const loaded = await core.load(getSeed(flags), {cwd: flags.cwd}); | ||
const parserOpts = selectParserOpts(loaded.parserPreset); | ||
const opts = parserOpts ? {parserOpts} : undefined; | ||
const report = await core.lint(message, loaded.rules, opts); | ||
const formatted = core.format(report, {color: flags.color}); | ||
if (!flags.quiet) { | ||
console.log( | ||
`${fmt.grey('⧗')} input: ${fmt.bold(message.split('\n')[0])}` | ||
); | ||
console.log(formatted.join('\n')); | ||
} | ||
if (report.errors.length > 0) { | ||
const error = new Error(formatted[formatted.length - 1]); | ||
error.type = pkg.name; | ||
throw error; | ||
} | ||
console.log(''); | ||
}) | ||
); | ||
} | ||
function checkFromStdin(input, flags) { | ||
return input.length === 0 && !checkFromRepository(flags); | ||
} | ||
function normalizeFlags(raw) { | ||
const flags = merge({}, raw); | ||
function checkFromRepository(flags) { | ||
return checkFromHistory(flags) || checkFromEdit(flags); | ||
} | ||
function checkFromEdit(flags) { | ||
return Boolean(flags.edit); | ||
} | ||
function checkFromHistory(flags) { | ||
return typeof flags.from === 'string' || typeof flags.to === 'string'; | ||
} | ||
function normalizeFlags(flags) { | ||
// The `edit` flag is either a boolean or a string but we are only allowed | ||
// to specify one of them in minimist | ||
if (flags.edit === '') { | ||
return merge({}, flags, {edit: true, e: true}); | ||
merge(flags, {edit: true, e: true}); | ||
} | ||
return flags; | ||
} | ||
function getSeed(seed) { | ||
const e = Array.isArray(seed.extends) ? seed.extends : [seed.extends]; | ||
const n = e.filter(i => typeof i === 'string'); | ||
return n.length > 0 | ||
? {extends: n, parserPreset: seed.parserPreset} | ||
: {parserPreset: seed.parserPreset}; | ||
} | ||
function selectParserOpts(parserPreset) { | ||
if (typeof parserPreset !== 'object') { | ||
return undefined; | ||
if (!('format' in flags)) { | ||
flags.format = 'commitlint'; | ||
} | ||
const opts = parserPreset.opts; | ||
if (typeof opts !== 'object') { | ||
return undefined; | ||
if (!FORMATS.includes(flags.format)) { | ||
const err = new Error( | ||
`--format must be on of: [${FORMATS.join(',')}], received "${ | ||
flags.format | ||
}".` | ||
); | ||
err.quiet = flags.quiet; | ||
err.help = true; | ||
err.type = pkg.name; | ||
throw err; | ||
} | ||
return opts.parserOpts; | ||
return flags; | ||
} | ||
@@ -172,0 +157,0 @@ |
@@ -35,2 +35,11 @@ import path from 'path'; | ||
test('should throw for unknown command', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['foo'], {cwd})(); | ||
t.is(actual.code, 1); | ||
t.true( | ||
actual.stdout.includes('<command> must be on of: [config], received "foo"') | ||
); | ||
}); | ||
test('should produce no success output with --quiet flag', async t => { | ||
@@ -86,2 +95,63 @@ const cwd = await git.bootstrap('fixtures/empty'); | ||
test('should throw for unknown --format', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=foo'], {cwd})('foo: bar'); | ||
t.is(actual.code, 1); | ||
t.true( | ||
actual.stdout.includes( | ||
'--format must be on of: [commitlint,json], received "foo"' | ||
) | ||
); | ||
}); | ||
test('should produce json output with --format=json', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
t.notThrows(() => JSON.parse(actual.stdout)); | ||
}); | ||
test('should produce no output when --quiet and --format=json', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json', '--quiet'], {cwd})('foo: bar'); | ||
t.is(actual.stdout, ''); | ||
t.is(actual.stderr, ''); | ||
}); | ||
test('should produce json with expected truthy valid key', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
const data = JSON.parse(actual.stdout); | ||
t.is(data.valid, true); | ||
}); | ||
test('should produce json with expected falsy valid key', async t => { | ||
const cwd = await git.bootstrap('fixtures/simple'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
const data = JSON.parse(actual.stdout); | ||
t.is(data.valid, false); | ||
}); | ||
test('should produce json with results array', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
const data = JSON.parse(actual.stdout); | ||
t.is(Array.isArray(data.results), true); | ||
}); | ||
test('results array has one report for one message', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
const data = JSON.parse(actual.stdout); | ||
t.is(data.results.length, 1); | ||
}); | ||
test('report has expected schema', async t => { | ||
const cwd = await git.bootstrap('fixtures/empty'); | ||
const actual = await cli(['--format=json'], {cwd})('foo: bar'); | ||
const data = JSON.parse(actual.stdout); | ||
const result = data.results[0]; | ||
t.is('report' in result, true); | ||
t.is('input' in result, true); | ||
}); | ||
test('should work with husky commitmsg hook and git commit', async () => { | ||
@@ -88,0 +158,0 @@ const cwd = await git.bootstrap('fixtures/husky/integration'); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
402359
71
1613
7
1
+ Addedbabel-runtime@^6.26.0
+ Added@commitlint/core@5.3.0-1(transitive)
+ Addedsemver@5.7.2(transitive)
- Removed@commitlint/core@5.2.8(transitive)
- Removed@commitlint/ensure@5.2.8(transitive)
- Removed@commitlint/execute-rule@5.2.8(transitive)
- Removed@commitlint/is-ignored@5.2.8(transitive)
- Removed@commitlint/message@5.2.8(transitive)
- Removed@commitlint/parse@5.2.8(transitive)
- Removed@commitlint/resolve-extends@5.2.8(transitive)
- Removed@commitlint/rules@5.2.8(transitive)
- Removed@commitlint/to-lines@5.2.8(transitive)
- Removed@commitlint/top-level@5.2.8(transitive)
- Removedlodash.camelcase@4.3.0(transitive)
- Removedlodash.kebabcase@4.1.1(transitive)
- Removedlodash.merge@4.6.0(transitive)
- Removedlodash.mergewith@4.6.0(transitive)
- Removedlodash.omit@4.5.0(transitive)
- Removedlodash.pick@4.4.0(transitive)
- Removedlodash.snakecase@4.1.1(transitive)
- Removedlodash.startcase@4.4.0(transitive)
- Removedlodash.topairs@4.3.0(transitive)
- Removedlodash.upperfirst@4.3.1(transitive)
- Removedsemver@5.4.1(transitive)
Updated@commitlint/core@^5.3.0-0