Skip to content

Commit 1da26d4

Browse files
committed
Add --format command-line parameter to support editors that invoke tools to format via stdin/stdout (fixes #603).
1 parent 84e8cf4 commit 1da26d4

9 files changed

+245
-71
lines changed

README.md

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ As a [GitHub Action][github-action] via
6767
markdownlint-cli2 vX.Y.Z (markdownlint vX.Y.Z)
6868
https://github.com/DavidAnson/markdownlint-cli2
6969
70-
Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN] [--config file] [--fix] [--help] [--no-globs]
70+
Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN] [--config file] [--fix] [--format] [--help] [--no-globs]
7171
7272
Glob expressions (from the globby library):
7373
- * matches any number of characters, but not /
@@ -86,6 +86,7 @@ Dot-only glob:
8686
Optional parameters:
8787
- --config specifies the path to a configuration file to define the base configuration
8888
- --fix updates files to resolve fixable issues (can be overridden in configuration)
89+
- --format reads standard input (stdin), applies fixes, writes standard output (stdout)
8990
- --help writes this message to the console and exits without doing anything else
9091
- --no-globs ignores the "globs" property if present in the top-level options object
9192
@@ -213,6 +214,17 @@ parameter below.
213214
- `1`: Linting was successful and there were errors (and possibly warnings)
214215
- `2`: Linting was not successful due to a problem or failure
215216

217+
### Formatting
218+
219+
Some editors implement document formatting by invoking an external program,
220+
passing the text of the current document on standard input (`stdin`), and
221+
reading the formatted result from standard output (`stdout`). This scenario is
222+
supported by the `--format` command-line parameter. When `--format` is set:
223+
224+
- Globs and other input sources are ignored
225+
- The `--fix` parameter is implicitly set
226+
- The exit code `1` is not used
227+
216228
## Rule List
217229

218230
- See the [Rules / Aliases][markdownlint-rules-aliases] and
@@ -433,10 +445,6 @@ parameter below.
433445
- The `INI` config format, `.markdownlintrc`, and `.markdownlintignore` are not
434446
supported.
435447

436-
### `vscode-markdownlint`
437-
438-
- `.markdownlintignore` is not supported.
439-
440448
## pre-commit
441449

442450
To run `markdownlint-cli2` as part of a [pre-commit][pre-commit] workflow, add a

markdownlint-cli2.mjs

Lines changed: 48 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ const showHelp = (/** @type {Logger} */ logMessage, /** @type {boolean} */ showB
229229
}
230230
logMessage(`https://github.com/DavidAnson/markdownlint-cli2
231231
232-
Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN] [--config file] [--fix] [--help] [--no-globs]
232+
Syntax: markdownlint-cli2 glob0 [glob1] [...] [globN] [--config file] [--fix] [--format] [--help] [--no-globs]
233233
234234
Glob expressions (from the globby library):
235235
- * matches any number of characters, but not /
@@ -248,6 +248,7 @@ Dot-only glob:
248248
Optional parameters:
249249
- --config specifies the path to a configuration file to define the base configuration
250250
- --fix updates files to resolve fixable issues (can be overridden in configuration)
251+
- --format reads standard input (stdin), applies fixes, writes standard output (stdout)
251252
- --help writes this message to the console and exits without doing anything else
252253
- --no-globs ignores the "globs" property if present in the top-level options object
253254
@@ -734,7 +735,7 @@ const createDirInfos = async (
734735
};
735736

736737
// Lint files in groups by shared configuration
737-
const lintFiles = (/** @type {FsLike} */ fs, /** @type {DirInfo[]} */ dirInfos, /** @type {Record<string, string>} */ fileContents) => {
738+
const lintFiles = (/** @type {FsLike} */ fs, /** @type {DirInfo[]} */ dirInfos, /** @type {Record<string, string>} */ fileContents, /** @type {FormattingContext} */ formattingContext) => {
738739
const tasks = [];
739740
// For each dirInfo
740741
for (const dirInfo of dirInfos) {
@@ -790,8 +791,16 @@ const lintFiles = (/** @type {FsLike} */ fs, /** @type {DirInfo[]} */ dirInfos,
790791
};
791792
// Invoke markdownlint
792793
let task = lint(options);
793-
// For any fixable errors, read file, apply fixes, and write it back
794-
if (markdownlintOptions.fix) {
794+
if (formattingContext.formatting) {
795+
// Apply fixes to stdin input
796+
task = task.then((results) => {
797+
const [ [ id, original ] ] = Object.entries(filteredStrings);
798+
const errorInfos = results[id];
799+
formattingContext.formatted = applyFixes(original, errorInfos);
800+
return {};
801+
});
802+
} else if (markdownlintOptions.fix) {
803+
// For any fixable errors, read file, apply fixes, write it back, and re-lint
795804
task = task.then((results) => {
796805
options.files = [];
797806
const subTasks = [];
@@ -912,6 +921,8 @@ export const main = async (/** @type {Parameters} */ params) => {
912921
(directory && pathDefault.resolve(directory)) ||
913922
process.cwd();
914923
const baseDir = posixPath(baseDirSystem);
924+
/** @type {FormattingContext} */
925+
const formattingContext = {};
915926
// Merge and process args/argv
916927
let fixDefault = false;
917928
/** @type {undefined | null | string} */
@@ -934,6 +945,9 @@ export const main = async (/** @type {Parameters} */ params) => {
934945
configPath = null;
935946
} else if (arg === "--fix") {
936947
fixDefault = true;
948+
} else if (arg === "--format") {
949+
formattingContext.formatting = true;
950+
useStdin = true;
937951
} else if (arg === "--help") {
938952
shouldShowHelp = true;
939953
} else if (arg === "--no-globs") {
@@ -972,7 +986,7 @@ export const main = async (/** @type {Parameters} */ params) => {
972986
Boolean(noImport)
973987
);
974988
} finally {
975-
if (!baseOptions?.baseMarkdownlintOptions.noBanner) {
989+
if (!baseOptions?.baseMarkdownlintOptions.noBanner && !formattingContext.formatting) {
976990
logMessage(bannerMessage);
977991
}
978992
}
@@ -1007,7 +1021,7 @@ export const main = async (/** @type {Parameters} */ params) => {
10071021
Object.keys(nonFileContents || {})
10081022
);
10091023
// Output finding status
1010-
const showProgress = !baseMarkdownlintOptions.noProgress;
1024+
const showProgress = !baseMarkdownlintOptions.noProgress && !formattingContext.formatting;
10111025
if (showProgress) {
10121026
logMessage(`Finding: ${globPatterns.join(" ")}`);
10131027
}
@@ -1046,29 +1060,33 @@ export const main = async (/** @type {Parameters} */ params) => {
10461060
logMessage(`Linting: ${fileCount} file(s)`);
10471061
}
10481062
// Lint files
1049-
const lintResults = await lintFiles(fs, dirInfos, resolvedFileContents);
1063+
const lintResults = await lintFiles(fs, dirInfos, resolvedFileContents, formattingContext);
10501064
// Output summary
10511065
const results = createResults(baseDir, lintResults);
10521066
if (showProgress) {
10531067
logMessage(`Summary: ${results.length} error(s)`);
10541068
}
1055-
const outputFormatters =
1056-
(optionsOverride && optionsOverride.outputFormatters) ||
1057-
baseMarkdownlintOptions.outputFormatters;
1058-
const modulePaths = resolveModulePaths(
1059-
baseDir,
1060-
baseMarkdownlintOptions.modulePaths || []
1061-
);
1062-
await outputResults(
1063-
baseDir,
1064-
relativeDir,
1065-
results,
1066-
outputFormatters,
1067-
modulePaths,
1068-
logMessage,
1069-
logError,
1070-
Boolean(noImport)
1071-
);
1069+
if (formattingContext.formatting) {
1070+
console.log(formattingContext.formatted);
1071+
} else {
1072+
const outputFormatters =
1073+
(optionsOverride && optionsOverride.outputFormatters) ||
1074+
baseMarkdownlintOptions.outputFormatters;
1075+
const modulePaths = resolveModulePaths(
1076+
baseDir,
1077+
baseMarkdownlintOptions.modulePaths || []
1078+
);
1079+
await outputResults(
1080+
baseDir,
1081+
relativeDir,
1082+
results,
1083+
outputFormatters,
1084+
modulePaths,
1085+
logMessage,
1086+
logError,
1087+
Boolean(noImport)
1088+
);
1089+
}
10721090
// Return result
10731091
const errorsPresent = lintResults.flatMap(
10741092
(lintResult) => Object.values(lintResult).flatMap(
@@ -1141,6 +1159,12 @@ export const main = async (/** @type {Parameters} */ params) => {
11411159

11421160
/** @typedef {import("markdownlint").LintError & LintContext} LintResult */
11431161

1162+
/**
1163+
* @typedef FormattingContext
1164+
* @property {boolean} [formatting] True iff formatting.
1165+
* @property {string} [formatted] Formatted content.
1166+
*/
1167+
11441168
/**
11451169
* @callback Logger
11461170
* @param {string} msg Message.

0 commit comments

Comments
 (0)