Skip to content

Commit

Permalink
Add option to output results in JSON format (#195)
Browse files Browse the repository at this point in the history
  • Loading branch information
EliSchleifer committed Jun 11, 2021
1 parent a350678 commit 4a7f139
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 18 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ markdownlint --help
-d, --dot include files/folders with a dot (for example `.github`)
-f, --fix fix basic errors (does not work with STDIN)
-i, --ignore [file|directory|glob] file(s) to ignore/exclude
-j, --json write issues in json format
-o, --output [outputFile] write issues to file (no console)
-p, --ignore-path [file] path to file with ignore pattern(s)
-r, --rules [file|directory|glob|package] custom rule files
Expand Down
58 changes: 40 additions & 18 deletions markdownlint.js
Original file line number Diff line number Diff line change
Expand Up @@ -131,30 +131,48 @@ function prepareFileList(files, fileExtensions, previousResults) {
}

function printResult(lintResult) {

const results = flatten(Object.keys(lintResult).map(file => {
return lintResult[file].map(result => {
return {
file: file,
lineNumber: result.lineNumber,
column: (result.errorRange && result.errorRange[0]) || 0,
names: result.ruleNames.join('/'),
description: result.ruleDescription +
(result.errorDetail ? ' [' + result.errorDetail + ']' : '') +
(result.errorContext ? ' [Context: "' + result.errorContext + '"]' : '')
};
if (options.json) {
return {
fileName: file,
...result
}
} else {
return {
file: file,
lineNumber: result.lineNumber,
column: (result.errorRange && result.errorRange[0]) || 0,
names: result.ruleNames.join('/'),
description: result.ruleDescription +
(result.errorDetail ? ' [' + result.errorDetail + ']' : '') +
(result.errorContext ? ' [Context: "' + result.errorContext + '"]' : '')
};
}
});
}));

let lintResultString = '';
if (results.length > 0) {
results.sort((a, b) => {
return a.file.localeCompare(b.file) || a.lineNumber - b.lineNumber ||
a.names.localeCompare(b.names) || a.description.localeCompare(b.description);
});
lintResultString = results.map(result => {
const {file, lineNumber, column, names, description} = result;
const columnText = column ? `:${column}` : '';
return `${file}:${lineNumber}${columnText} ${names} ${description}`;
}).join('\n');
if (options.json) {
results.sort((a, b) => {
return a.fileName.localeCompare(b.fileName) || a.lineNumber - b.lineNumber ||
a.ruleDescription.localeCompare(b.ruleDescription);
});
lintResultString = JSON.stringify(results, null, 2);
} else {
results.sort((a, b) => {
return a.file.localeCompare(b.file) || a.lineNumber - b.lineNumber ||
a.names.localeCompare(b.names) || a.description.localeCompare(b.description);
});

lintResultString = results.map(result => {
const {file, lineNumber, column, names, description} = result;
const columnText = column ? `:${column}` : '';
return `${file}:${lineNumber}${columnText} ${names} ${description}`;
}).join('\n');
}
// Note: process.exit(1) will end abruptly, interrupting asynchronous IO
// streams (e.g., when the output is being piped). Just set the exit code
// and let the program terminate normally.
Expand Down Expand Up @@ -191,6 +209,7 @@ program
.option('-d, --dot', 'include files/folders with a dot (for example `.github`)')
.option('-f, --fix', 'fix basic errors (does not work with STDIN)')
.option('-i, --ignore [file|directory|glob]', 'file(s) to ignore/exclude', concatArray, [])
.option('-j, --json', 'write issues in json format')
.option('-o, --output [outputFile]', 'write issues to file (no console)')
.option('-p, --ignore-path [file]', 'path to file with ignore pattern(s)')
.option('-r, --rules [file|directory|glob|package]', 'custom rule files', concatArray, [])
Expand Down Expand Up @@ -278,6 +297,9 @@ function lintAndPrint(stdin, files) {
stdin
};
}
if (options.json) {
lintOptions.resultVersion = 3;
}

if (options.fix) {
const fixOptions = {
Expand Down
54 changes: 54 additions & 0 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,39 @@ test('linting of incorrect Markdown file fails', async t => {
}
});

test('linting of incorrect Markdown file fails prints issues as json', async t => {
try {
await execa('../markdownlint.js',
['--config', 'test-config.json', 'incorrect.md', '--json'],
{stripFinalNewline: false});
t.fail();
} catch (error) {
t.is(error.stdout, '');
const issues = JSON.parse(error.stderr);
t.is(issues.length, 8);
// ruleInformation changes with version so that field just check not null and present
const issue = issues[0];
t.true(issue.ruleInformation.length > 0);
issue.ruleInformation = null;
const expected = {
fileName: "incorrect.md",
lineNumber: 1,
ruleNames: [
"MD002",
"first-heading-h1",
"first-header-h1",
],
ruleDescription: "First heading should be a top-level heading",
ruleInformation: null,
errorContext: null,
errorDetail: "Expected: h1; Actual: h2",
errorRange: null,
fixInfo: null,
};
t.deepEqual(issues[0], expected);
}
});

test('linting of incorrect Markdown file fails with absolute path', async t => {
try {
await execa('../markdownlint.js',
Expand Down Expand Up @@ -343,6 +376,27 @@ test('--output with invalid input outputs violations', async t => {
}
});

test('--output with invalid input and --json outputs issues as json', async t => {
const input = [
'Heading',
'',
'Text ',
''
].join('\n');
const output = '../outputF.json';
try {
await execa('../markdownlint.js',
['--stdin', '--output', output, '--json'],
{input, stripFinalNewline: false});
t.fail();
} catch (error) {
t.is(error.stdout, '');
t.is(error.stderr, '');
t.is(JSON.parse(fs.readFileSync(output, 'utf8')).length, 2);
fs.unlinkSync(output);
}
});

test('--output with invalid path fails', async t => {
const input = '';
const output = 'invalid/outputD.txt';
Expand Down

0 comments on commit 4a7f139

Please sign in to comment.