Skip to content

Conversation

kaichen
Copy link

@kaichen kaichen commented Jul 11, 2025

This PR enables the --stdin and --fix options to work together, allowing users to pipe markdown content through markdownlint-cli and receive fixed output. Previously, using these options together would show the help message. The implementation applies fixes to stdin content and outputs to stdout by default, with support for --output and --quiet options.

Copy link
Collaborator

@DavidAnson DavidAnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks great, very thorough!! Just a few comments and questions in the first pass.

@@ -208,7 +208,7 @@ program
.option('-c, --config <configFile>', 'configuration file (JSON, JSONC, JS, YAML, or TOML)')
.option('--configPointer <pointer>', 'JSON Pointer to object within configuration file', '')
.option('-d, --dot', 'include files/folders with a dot (for example `.github`)')
.option('-f, --fix', 'fix basic errors (does not work with STDIN)')
.option('-f, --fix', 'fix basic errors')
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks. Please update README.md with the same change.

markdownlint.js Outdated
const fixResult = lint(fixOptions);
const fixes = fixResult.stdin.filter(error => error.fixInfo);
let outputText = stdin;
if (fixes.length > 0) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary check? Should be fine to remove.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch!

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed 96ef796

markdownlint.js Outdated

if (options.output) {
// Write content to output file
try {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not easy to tell on my phone, but this feels duplicated? Is there a clean way to reuse? Maybe not, but worth looking.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed at a66ffaa

markdownlint.js Outdated
console.warn('Cannot write to output file ' + options.output + ': ' + error.message);
process.exitCode = exitCodes.failedToWriteOutputFile;
}
} else if (!options.quiet) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this a scenario anyone would use? stdin with quiet? I feel like maybe it should not care about quiet?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, stdin definitely conflicts with quiet, they can't be used together. It's just that the options earlier didn't check for that. Logically, this part can be removed.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed 96ef796

markdownlint.js Outdated
process.stdout.write(outputText);
}

return; // Exit early when fixing stdin
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, I don't love this pattern, but maybe the alternative is worse?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we don't do an early return here, then the code that handles the file below would have to be wrapped inside an else block.

test/test.js Outdated
const stdin = {string: ['## Not a first level heading', '', 'Text content'].join('\n')};
const result = await spawn('../markdownlint.js', ['--stdin', '--fix', '--config', 'test-config.json'], {stdin});
t.is(result.stdout, stdin.string);
t.is(result.stderr, '');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't stderr still contain the unfixable errors in this case?

If so, I'd ask for one more test that fixes some of the total errors and stderr outputs the unfixed ones.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added testcases for unfixable errors. 111fe3a

@kaichen kaichen requested a review from DavidAnson July 14, 2025 13:34
Copy link
Collaborator

@DavidAnson DavidAnson left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the updates! Good catch adding the third parameter to writeFileSync. :)

There is one area of simplification and I think the meaning of the output argument may be wrong?

if (stdin) {
// Handle fix for stdin
const fixResult = lint(fixOptions);
const fixes = fixResult.stdin.filter(error => error.fixInfo);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to filter here, I think - pass the full list and it will ignore what it cannot do.

outputText = applyFixes(stdin, fixes);

if (options.output) {
writeOutputFile(options.output, outputText);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The README says this file contains the issues, not the file content - I do not think this should be used for both purposes?

const checkOptions = {...lintOptions};
checkOptions.strings = {stdin: outputText};
const checkResult = lint(checkOptions);
if (Object.keys(checkResult).some(file => checkResult[file].length > 0)) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is only one input just like line 321, so it can be used directly here.


// Check for remaining errors after fix
const remainingErrors = lint(lintOptions);
const hasErrors = Object.keys(remainingErrors).some(file => remainingErrors[file].length > 0);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment as above, only one key will be present.

const remainingErrors = lint(lintOptions);
const hasErrors = Object.keys(remainingErrors).some(file => remainingErrors[file].length > 0);

process.stdout.write(outputText);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move this line above the previous lines setting remainingErrors/hasErrors.

t.is(result.exitCode, 0);
});

test('--stdin --fix --output writes fixed content to file', async t => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See above for why I think this is testing the wrong behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants