const { formatMarkdown, formatFiles, checkFiles } = require("../index.js");
const args = process.argv.slice(2);
let width = 80;
let wrap = "preserve";
let orderedList = "ascending";
let check = false;
let write = false;
let stdin = false;
let patterns = [];
let exclude = [];
let noDefaultExcludes = false;
function printHelp() {
console.log(`mdfmt - A fast, opinionated Markdown formatter
Usage: mdfmt [OPTIONS] [FILES...]
Arguments:
[FILES...] Markdown files, directories, or glob patterns to format
Options:
-w, --write Write formatted output back to file
-c, --check Check if files are formatted (exit 1 if not)
--stdin Read from standard input
--width <NUMBER> Maximum line width [default: 80]
--wrap <MODE> Prose wrapping: always, never, preserve [default: preserve]
--ordered-list <MODE> Ordered list style: ascending, one [default: ascending]
--exclude <DIR> Additional directories to exclude (can be used multiple times)
--no-default-excludes Don't exclude node_modules, .git, etc. by default
-h, --help Print help
-V, --version Print version`);
}
function printVersion() {
const pkg = require("../package.json");
console.log(`mdfmt ${pkg.version}`);
}
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === "-h" || arg === "--help") {
printHelp();
process.exit(0);
} else if (arg === "-V" || arg === "--version") {
printVersion();
process.exit(0);
} else if (arg === "-w" || arg === "--write") {
write = true;
} else if (arg === "-c" || arg === "--check") {
check = true;
} else if (arg === "--stdin") {
stdin = true;
} else if (arg === "--width") {
width = parseInt(args[++i], 10);
if (isNaN(width)) {
console.error("Error: --width requires a number");
process.exit(2);
}
} else if (arg === "--wrap") {
wrap = args[++i];
if (!["always", "never", "preserve"].includes(wrap)) {
console.error("Error: --wrap must be always, never, or preserve");
process.exit(2);
}
} else if (arg === "--ordered-list") {
orderedList = args[++i];
if (!["ascending", "one"].includes(orderedList)) {
console.error("Error: --ordered-list must be ascending or one");
process.exit(2);
}
} else if (arg === "--exclude") {
exclude.push(args[++i]);
} else if (arg === "--no-default-excludes") {
noDefaultExcludes = true;
} else if (arg.startsWith("-")) {
console.error(`Error: Unknown option: ${arg}`);
process.exit(2);
} else {
patterns.push(arg);
}
}
const options = {
width,
wrap,
orderedList,
exclude: exclude.length > 0 ? exclude : undefined,
noDefaultExcludes: noDefaultExcludes || undefined,
};
async function readStdin() {
return new Promise((resolve) => {
let data = "";
process.stdin.setEncoding("utf8");
process.stdin.on("data", (chunk) => (data += chunk));
process.stdin.on("end", () => resolve(data));
});
}
async function main() {
if (stdin) {
const input = await readStdin();
const output = formatMarkdown(input, options);
process.stdout.write(output);
process.exit(0);
}
if (patterns.length === 0) {
printHelp();
process.exit(2);
}
let hasErrors = false;
let needsFormatting = false;
if (check) {
const results = checkFiles(patterns, options);
if (results.length === 0) {
console.error("No markdown files found.");
process.exit(2);
}
for (const result of results) {
if (result.error) {
console.error(`Error: ${result.path}: ${result.error}`);
hasErrors = true;
} else if (result.changed) {
console.log(`Would reformat: ${result.path}`);
needsFormatting = true;
}
}
const wouldChange = results.filter((r) => r.changed && !r.error).length;
const total = results.filter((r) => !r.error).length;
if (wouldChange > 0) {
console.error(`${wouldChange} file(s) would be reformatted`);
} else {
console.error(`All ${total} file(s) are formatted correctly`);
}
} else if (write) {
const results = formatFiles(patterns, options);
if (results.length === 0) {
console.error("No markdown files found.");
process.exit(2);
}
for (const result of results) {
if (result.error) {
console.error(`Error: ${result.path}: ${result.error}`);
hasErrors = true;
} else if (result.changed) {
console.log(`Formatted: ${result.path}`);
}
}
} else {
if (patterns.length > 1) {
console.error(
"Error: Cannot output multiple files to stdout. Use --write to format in-place."
);
process.exit(2);
}
const results = checkFiles(patterns, options);
if (results.length === 0) {
console.error("No markdown files found.");
process.exit(2);
}
if (results.length > 1) {
console.error(
"Error: Pattern matches multiple files. Use --write to format in-place."
);
process.exit(2);
}
const result = results[0];
if (result.error) {
console.error(`Error: ${result.path}: ${result.error}`);
process.exit(2);
}
const fs = require("fs");
const content = fs.readFileSync(result.path, "utf8");
const formatted = formatMarkdown(content, options);
process.stdout.write(formatted);
}
if (hasErrors) {
process.exit(2);
} else if (check && needsFormatting) {
process.exit(1);
} else {
process.exit(0);
}
}
main();