diff --git a/markdownlint.js b/markdownlint.js index 402df51b..04555d60 100755 --- a/markdownlint.js +++ b/markdownlint.js @@ -56,6 +56,7 @@ function prepareFileList(files) { function lint(lintFiles, config) { var lintOptions = { + resultVersion: 2, files: lintFiles, config: config }; @@ -63,8 +64,26 @@ function lint(lintFiles, config) { } function printResult(lintResult) { - var lintResultString = lintResult.toString(); - if (lintResultString) { + var results = flatten(Object.keys(lintResult).map(function (file) { + return lintResult[file].map(function (result) { + return { + file: file, + lineNumber: result.lineNumber, + names: result.ruleNames.join('/'), + description: result.ruleDescription + + (result.errorDetail ? ' [' + result.errorDetail + ']' : '') + + (result.errorContext ? ' [Context: "' + result.errorContext + '"]' : '') + }; + }); + })); + if (results.length > 0) { + results.sort(function (a, b) { + return a.file.localeCompare(b.file) || a.lineNumber - b.lineNumber || + a.names.localeCompare(b.names) || a.description.localeCompare(b.description); + }); + var lintResultString = results.map(function (result) { + return result.file + ': ' + result.lineNumber + ': ' + result.names + ' ' + result.description; + }).join('\n'); console.error(lintResultString); // Note: process.exit(1) will end abruptly, interrupting asynchronous IO // streams (e.g., when the output is being piped). Just set the exit code diff --git a/test/test.js b/test/test.js index 4645dd4c..06669a86 100644 --- a/test/test.js +++ b/test/test.js @@ -30,6 +30,7 @@ test('linting of incorrect Markdown file fails', async t => { try { await execa('../markdownlint.js', ['--config', 'test-config.json', 'incorrect.md']); + t.fail(); } catch (err) { t.true(err.stdout === ''); t.true(err.stderr.match(errorPattern).length === 8); @@ -39,6 +40,7 @@ test('linting of incorrect Markdown file fails', async t => { test('linting of incorrect Markdown via npm run file fails with eol', async t => { try { await execa('npm', ['run', 'invalid']); + t.fail(); } catch (err) { t.true(/\nnpm ERR! code ELIFECYCLE/.test(err.stderr)); } @@ -55,6 +57,7 @@ test('glob linting works with failing files', async t => { try { await execa('../markdownlint.js', ['--config', 'test-config.json', '**/*.md']); + t.fail(); } catch (err) { t.true(err.stdout === ''); t.true(err.stderr.match(errorPattern).length === 16); @@ -72,6 +75,7 @@ test('dir linting works with failing .markdown files', async t => { try { await execa('../markdownlint.js', ['--config', 'test-config.json', 'subdir-incorrect']); + t.fail(); } catch (err) { t.true(err.stdout === ''); t.true(err.stderr.match(errorPattern).length === 10); @@ -96,6 +100,7 @@ test('glob linting with failing files has fewer errors when ignored by dir', asy try { await execa('../markdownlint.js', ['--config', 'test-config.json', '**/*.md', '--ignore', 'subdir-incorrect']); + t.fail(); } catch (err) { t.true(err.stdout === ''); t.true(err.stderr.match(errorPattern).length === 8); @@ -106,6 +111,7 @@ test('dir linting with failing files has fewer errors when ignored by file', asy try { await execa('../markdownlint.js', ['--config', 'test-config.json', 'subdir-incorrect', '--ignore', 'subdir-incorrect/incorrect.md']); + t.fail(); } catch (err) { t.true(err.stdout === ''); t.true(err.stderr.match(errorPattern).length === 2); @@ -118,3 +124,25 @@ test('glob linting with failing files passes when ignored by multiple globs', as t.true(result.stdout === ''); t.true(result.stderr === ''); }); + +test('linting results are sorted by file/line/names/description', async t => { + try { + await execa('../markdownlint.js', + ['--config', 'test-config.json', 'incorrect.md']); + t.fail(); + } catch (err) { + const expected = [ + 'incorrect.md: 1: MD002/first-header-h1 First header should be a top level header [Expected: h1; Actual: h2]', + 'incorrect.md: 1: MD022/blanks-around-headers Headers should be surrounded by blank lines [Context: "## header 2"]', + 'incorrect.md: 1: MD041/first-line-h1 First line in file should be a top level header [Context: "## header 2"]', + 'incorrect.md: 2: MD022/blanks-around-headers Headers should be surrounded by blank lines [Context: "# header"]', + 'incorrect.md: 4: MD014/commands-show-output Dollar signs used before commands without showing output [Context: "$ code"]', + 'incorrect.md: 10: MD014/commands-show-output Dollar signs used before commands without showing output [Context: "$ code"]', + 'incorrect.md: 16: MD014/commands-show-output Dollar signs used before commands without showing output [Context: "$ code"]', + 'incorrect.md: 23: MD014/commands-show-output Dollar signs used before commands without showing output [Context: "$ code"]', + '' + ].join('\n'); + t.true(err.stdout === ''); + t.true(err.stderr === expected); + } +});