markdownlint-obsidian-cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -0
- package/bin/markdownlint-obsidian.js +7 -0
- package/dist/bin.mjs +4 -0
- package/dist/package.json +4 -0
- package/dist/src/args.d.ts +34 -0
- package/dist/src/args.d.ts.map +1 -0
- package/dist/src/args.js +32 -0
- package/dist/src/args.js.map +1 -0
- package/dist/src/main.d.ts +21 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +136 -0
- package/dist/src/main.js.map +1 -0
- package/package.json +55 -0
- package/src/args.ts +60 -0
- package/src/main.ts +183 -0
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# markdownlint-obsidian-cli
|
|
2
|
+
|
|
3
|
+
Command-line interface for linting Obsidian Flavored Markdown. Wraps the
|
|
4
|
+
`markdownlint-obsidian` library with a `commander`-based CLI.
|
|
5
|
+
|
|
6
|
+
## Install
|
|
7
|
+
|
|
8
|
+
```bash
|
|
9
|
+
# Global install
|
|
10
|
+
npm install -g markdownlint-obsidian-cli
|
|
11
|
+
bun add -g markdownlint-obsidian-cli
|
|
12
|
+
|
|
13
|
+
# Project install (use via npx / bunx)
|
|
14
|
+
npm install -D markdownlint-obsidian-cli
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Usage
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Lint all markdown files
|
|
21
|
+
markdownlint-obsidian "**/*.md"
|
|
22
|
+
|
|
23
|
+
# Fix auto-fixable issues in place
|
|
24
|
+
markdownlint-obsidian --fix "**/*.md"
|
|
25
|
+
|
|
26
|
+
# Check what would be fixed (no writes)
|
|
27
|
+
markdownlint-obsidian --fix-check "**/*.md"
|
|
28
|
+
|
|
29
|
+
# Machine-readable output
|
|
30
|
+
markdownlint-obsidian --output-formatter sarif "**/*.md" > report.sarif
|
|
31
|
+
markdownlint-obsidian --output-formatter junit "**/*.md" > junit.xml
|
|
32
|
+
|
|
33
|
+
# Custom config location
|
|
34
|
+
markdownlint-obsidian --config /path/to/project "**/*.md"
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Exit codes
|
|
38
|
+
|
|
39
|
+
| Code | Meaning |
|
|
40
|
+
| --- | --- |
|
|
41
|
+
| `0` | Clean (no errors; warnings do not raise exit code) |
|
|
42
|
+
| `1` | One or more lint errors found |
|
|
43
|
+
| `2` | Tool or configuration failure |
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
MIT
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
// Dev entry: Bun executes the TypeScript source directly — no compilation step.
|
|
3
|
+
// The published hermetic entry is dist/bin.mjs (Node-compatible shebang,
|
|
4
|
+
// imports compiled dist/src/cli/main.js), generated by `bun run build`.
|
|
5
|
+
import { main } from "../src/main.ts";
|
|
6
|
+
const code = await main(process.argv);
|
|
7
|
+
process.exit(code);
|
package/dist/bin.mjs
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
/**
|
|
3
|
+
* Parsed CLI options returned by commander.
|
|
4
|
+
*
|
|
5
|
+
* Fields mirror markdownlint-cli2's flag set so downstream users can reuse
|
|
6
|
+
* existing muscle memory and automation.
|
|
7
|
+
*/
|
|
8
|
+
export interface CLIArgs {
|
|
9
|
+
readonly globs: string[];
|
|
10
|
+
readonly config?: string;
|
|
11
|
+
readonly configPointer?: string;
|
|
12
|
+
readonly fix: boolean;
|
|
13
|
+
readonly fixCheck: boolean;
|
|
14
|
+
readonly format: boolean;
|
|
15
|
+
readonly noGlobs: boolean;
|
|
16
|
+
readonly vaultRoot?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Commander maps `--no-resolve` to `resolve: false`. Left `undefined`
|
|
19
|
+
* when the flag is not supplied so config-level `resolve` can win.
|
|
20
|
+
*/
|
|
21
|
+
readonly resolve?: boolean;
|
|
22
|
+
readonly outputFormatter: string;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Build the top-level commander {@link Command} for the CLI.
|
|
26
|
+
*
|
|
27
|
+
* The returned program has `exitOverride()` configured by the caller so
|
|
28
|
+
* `--help` and `--version` can be caught in tests without terminating the
|
|
29
|
+
* process.
|
|
30
|
+
*
|
|
31
|
+
* @returns A fully wired commander `Command`.
|
|
32
|
+
*/
|
|
33
|
+
export declare function buildProgram(): Command;
|
|
34
|
+
//# sourceMappingURL=args.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAMpC;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,EAAE,CAAC;IACzB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;CAClC;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,IAAI,OAAO,CAqBtC"}
|
package/dist/src/args.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
const require = createRequire(import.meta.url);
|
|
4
|
+
const { version } = require("../package.json");
|
|
5
|
+
/**
|
|
6
|
+
* Build the top-level commander {@link Command} for the CLI.
|
|
7
|
+
*
|
|
8
|
+
* The returned program has `exitOverride()` configured by the caller so
|
|
9
|
+
* `--help` and `--version` can be caught in tests without terminating the
|
|
10
|
+
* process.
|
|
11
|
+
*
|
|
12
|
+
* @returns A fully wired commander `Command`.
|
|
13
|
+
*/
|
|
14
|
+
export function buildProgram() {
|
|
15
|
+
const program = new Command();
|
|
16
|
+
program
|
|
17
|
+
.name("markdownlint-obsidian")
|
|
18
|
+
.description("Obsidian Flavored Markdown linter for CI pipelines")
|
|
19
|
+
.version(version)
|
|
20
|
+
.argument("[globs...]", "Glob patterns for files to lint")
|
|
21
|
+
.option("--config <path>", "Explicit config file path")
|
|
22
|
+
.option("--config-pointer <ptr>", "JSON Pointer into config (e.g. #/markdownlint)")
|
|
23
|
+
.option("--fix", "Auto-fix fixable errors in-place", false)
|
|
24
|
+
.option("--fix-check", "Check if fixes are needed without writing files", false)
|
|
25
|
+
.option("--format", "Read stdin, write linted content to stdout", false)
|
|
26
|
+
.option("--no-globs", "Ignore globs property in config file")
|
|
27
|
+
.option("--vault-root <path>", "Override auto-detected vault root")
|
|
28
|
+
.option("--no-resolve", "Disable wikilink resolution")
|
|
29
|
+
.option("--output-formatter <name>", "Output formatter (default, json, junit, sarif)", "default");
|
|
30
|
+
return program;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=args.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/args.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE5C,MAAM,OAAO,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC/C,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,iBAAiB,CAAwB,CAAC;AAyBtE;;;;;;;;GAQG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;IAC9B,OAAO;SACJ,IAAI,CAAC,uBAAuB,CAAC;SAC7B,WAAW,CAAC,oDAAoD,CAAC;SACjE,OAAO,CAAC,OAAO,CAAC;SAChB,QAAQ,CAAC,YAAY,EAAE,iCAAiC,CAAC;SACzD,MAAM,CAAC,iBAAiB,EAAE,2BAA2B,CAAC;SACtD,MAAM,CAAC,wBAAwB,EAAE,gDAAgD,CAAC;SAClF,MAAM,CAAC,OAAO,EAAE,kCAAkC,EAAE,KAAK,CAAC;SAC1D,MAAM,CAAC,aAAa,EAAE,iDAAiD,EAAE,KAAK,CAAC;SAC/E,MAAM,CAAC,UAAU,EAAE,4CAA4C,EAAE,KAAK,CAAC;SACvE,MAAM,CAAC,YAAY,EAAE,sCAAsC,CAAC;SAC5D,MAAM,CAAC,qBAAqB,EAAE,mCAAmC,CAAC;SAClE,MAAM,CAAC,cAAc,EAAE,6BAA6B,CAAC;SACrD,MAAM,CACL,2BAA2B,EAC3B,gDAAgD,EAChD,SAAS,CACV,CAAC;IACJ,OAAO,OAAO,CAAC;AACjB,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Exit codes used by every CLI path.
|
|
3
|
+
*
|
|
4
|
+
* 0 = clean, 1 = lint errors, 2 = tool or config failure.
|
|
5
|
+
*/
|
|
6
|
+
export declare const EXIT_CODES: Readonly<{
|
|
7
|
+
readonly CLEAN: 0;
|
|
8
|
+
readonly LINT_ERRORS: 1;
|
|
9
|
+
readonly TOOL_FAILURE: 2;
|
|
10
|
+
}>;
|
|
11
|
+
/**
|
|
12
|
+
* Entry point called by `bin/markdownlint-obsidian.js`.
|
|
13
|
+
*
|
|
14
|
+
* Parses arguments, runs the linting pipeline via the engine API,
|
|
15
|
+
* prints formatter output, and returns the appropriate exit code.
|
|
16
|
+
*
|
|
17
|
+
* @param argv - Argument vector, typically `process.argv`.
|
|
18
|
+
* @returns Resolved exit code (0, 1, or 2).
|
|
19
|
+
*/
|
|
20
|
+
export declare function main(argv: string[]): Promise<number>;
|
|
21
|
+
//# sourceMappingURL=main.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AA2BA;;;;GAIG;AACH,eAAO,MAAM,UAAU;;;;EAIZ,CAAC;AAEZ;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAY1D"}
|
package/dist/src/main.js
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { buildProgram } from "./args.js";
|
|
2
|
+
import { lint, fix, getFormatter, loadConfig, } from "markdownlint-obsidian/engine";
|
|
3
|
+
/**
|
|
4
|
+
* Exit codes used by every CLI path.
|
|
5
|
+
*
|
|
6
|
+
* 0 = clean, 1 = lint errors, 2 = tool or config failure.
|
|
7
|
+
*/
|
|
8
|
+
export const EXIT_CODES = Object.freeze({
|
|
9
|
+
CLEAN: 0,
|
|
10
|
+
LINT_ERRORS: 1,
|
|
11
|
+
TOOL_FAILURE: 2,
|
|
12
|
+
});
|
|
13
|
+
/**
|
|
14
|
+
* Entry point called by `bin/markdownlint-obsidian.js`.
|
|
15
|
+
*
|
|
16
|
+
* Parses arguments, runs the linting pipeline via the engine API,
|
|
17
|
+
* prints formatter output, and returns the appropriate exit code.
|
|
18
|
+
*
|
|
19
|
+
* @param argv - Argument vector, typically `process.argv`.
|
|
20
|
+
* @returns Resolved exit code (0, 1, or 2).
|
|
21
|
+
*/
|
|
22
|
+
export async function main(argv) {
|
|
23
|
+
const program = buildProgram();
|
|
24
|
+
program.exitOverride();
|
|
25
|
+
const parsed = parseArgv(program, argv);
|
|
26
|
+
if (parsed.terminal !== null)
|
|
27
|
+
return parsed.terminal;
|
|
28
|
+
const opts = program.opts();
|
|
29
|
+
const cwd = process.cwd();
|
|
30
|
+
const globs = program.args;
|
|
31
|
+
return runPipeline(globs, opts, cwd);
|
|
32
|
+
}
|
|
33
|
+
function parseArgv(program, argv) {
|
|
34
|
+
try {
|
|
35
|
+
program.parse(argv);
|
|
36
|
+
return { terminal: null };
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
const e = err;
|
|
40
|
+
if (e.code === "commander.helpDisplayed" || e.code === "commander.version") {
|
|
41
|
+
return { terminal: EXIT_CODES.CLEAN };
|
|
42
|
+
}
|
|
43
|
+
return { terminal: EXIT_CODES.TOOL_FAILURE };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
function resolveFormatter(name) {
|
|
47
|
+
try {
|
|
48
|
+
return getFormatter(name);
|
|
49
|
+
}
|
|
50
|
+
catch (err) {
|
|
51
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
function emitAndExit(results, formatterName) {
|
|
56
|
+
const formatter = resolveFormatter(formatterName);
|
|
57
|
+
if (formatter === null)
|
|
58
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
59
|
+
const output = formatter(results);
|
|
60
|
+
if (output)
|
|
61
|
+
process.stdout.write(output + "\n");
|
|
62
|
+
return results.some((r) => r.hasErrors) ? EXIT_CODES.LINT_ERRORS : EXIT_CODES.CLEAN;
|
|
63
|
+
}
|
|
64
|
+
function fmtRange(col, del) {
|
|
65
|
+
return del === 0 ? `col ${col}` : `col ${col}–${col + del - 1}`;
|
|
66
|
+
}
|
|
67
|
+
function onCustomRuleError(modulePath, message) {
|
|
68
|
+
process.stderr.write(`OFM905: failed to load custom rule module "${modulePath}": ${message}\n`);
|
|
69
|
+
}
|
|
70
|
+
function buildEngineOptions(globArgs, config, opts, cwd) {
|
|
71
|
+
const effectiveGlobs = globArgs.length > 0 ? [...globArgs] : config.globs;
|
|
72
|
+
return {
|
|
73
|
+
globs: effectiveGlobs,
|
|
74
|
+
cwd,
|
|
75
|
+
...(opts.vaultRoot !== undefined && { vaultRoot: opts.vaultRoot }),
|
|
76
|
+
...(opts.resolve === false && { resolve: false }),
|
|
77
|
+
...(opts.config !== undefined && { config: opts.config }),
|
|
78
|
+
onCustomRuleError,
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
async function runFixBranch(engineOptions, opts) {
|
|
82
|
+
try {
|
|
83
|
+
const outcome = await fix({
|
|
84
|
+
...engineOptions,
|
|
85
|
+
check: opts.fixCheck,
|
|
86
|
+
});
|
|
87
|
+
if (outcome.filesFixed.length > 0) {
|
|
88
|
+
const verb = opts.fixCheck ? "Would fix" : "Fixed";
|
|
89
|
+
process.stderr.write(`${verb} ${outcome.filesFixed.length} file(s)\n`);
|
|
90
|
+
}
|
|
91
|
+
for (const conflict of outcome.conflicts) {
|
|
92
|
+
const colA = fmtRange(conflict.first.editColumn, conflict.first.deleteCount);
|
|
93
|
+
const colB = fmtRange(conflict.second.editColumn, conflict.second.deleteCount);
|
|
94
|
+
process.stderr.write(`[fix-conflict] ${conflict.filePath}: ${conflict.reason} (${colA} vs ${colB})\n`);
|
|
95
|
+
}
|
|
96
|
+
return emitAndExit(outcome.finalPass, opts.outputFormatter);
|
|
97
|
+
}
|
|
98
|
+
catch (err) {
|
|
99
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
100
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
async function runLintBranch(engineOptions, opts) {
|
|
104
|
+
try {
|
|
105
|
+
const results = await lint(engineOptions);
|
|
106
|
+
return emitAndExit(results, opts.outputFormatter);
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
110
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function checkMutualExclusion(opts) {
|
|
114
|
+
if (opts.fix && opts.fixCheck) {
|
|
115
|
+
process.stderr.write("OFM902: --fix and --fix-check are mutually exclusive\n");
|
|
116
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
117
|
+
}
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
async function runPipeline(globArgs, opts, cwd) {
|
|
121
|
+
const exclusionCode = checkMutualExclusion(opts);
|
|
122
|
+
if (exclusionCode !== null)
|
|
123
|
+
return exclusionCode;
|
|
124
|
+
if (resolveFormatter(opts.outputFormatter) === null)
|
|
125
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
126
|
+
const config = await loadConfig(opts.config ?? cwd).catch(() => null);
|
|
127
|
+
if (!config) {
|
|
128
|
+
process.stderr.write("OFM901: failed to load configuration\n");
|
|
129
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
130
|
+
}
|
|
131
|
+
const engineOptions = buildEngineOptions(globArgs, config, opts, cwd);
|
|
132
|
+
return opts.fix || opts.fixCheck
|
|
133
|
+
? runFixBranch(engineOptions, opts)
|
|
134
|
+
: runLintBranch(engineOptions, opts);
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "markdownlint-obsidian-cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "CLI for markdownlint-obsidian — lint Obsidian Flavored Markdown from the command line",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"markdownlint-obsidian": "./bin/markdownlint-obsidian.js"
|
|
8
|
+
},
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/src/main.d.ts",
|
|
12
|
+
"default": "./dist/src/main.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist/",
|
|
17
|
+
"src/",
|
|
18
|
+
"bin/",
|
|
19
|
+
"README.md",
|
|
20
|
+
"CHANGELOG.md",
|
|
21
|
+
"LICENSE"
|
|
22
|
+
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsc -p tsconfig.build.json && node scripts/gen-dist-bin.mjs",
|
|
25
|
+
"typecheck": "tsc --noEmit",
|
|
26
|
+
"test": "bun test --timeout 30000",
|
|
27
|
+
"test:watch": "bun test --watch --timeout 30000",
|
|
28
|
+
"prepublishOnly": "bun run build && bun run test"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"markdownlint-obsidian": "workspace:*",
|
|
32
|
+
"commander": "^12.0.0"
|
|
33
|
+
},
|
|
34
|
+
"engines": {
|
|
35
|
+
"node": ">=20.0.0",
|
|
36
|
+
"bun": ">=1.1.30"
|
|
37
|
+
},
|
|
38
|
+
"license": "MIT",
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/alisonaquinas/markdownlint-obsidian.git",
|
|
42
|
+
"directory": "packages/cli"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://github.com/alisonaquinas/markdownlint-obsidian#readme",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/alisonaquinas/markdownlint-obsidian/issues"
|
|
47
|
+
},
|
|
48
|
+
"keywords": [
|
|
49
|
+
"markdown",
|
|
50
|
+
"markdownlint",
|
|
51
|
+
"obsidian",
|
|
52
|
+
"linter",
|
|
53
|
+
"cli"
|
|
54
|
+
]
|
|
55
|
+
}
|
package/src/args.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { createRequire } from "node:module";
|
|
3
|
+
|
|
4
|
+
const require = createRequire(import.meta.url);
|
|
5
|
+
const { version } = require("../package.json") as { version: string };
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parsed CLI options returned by commander.
|
|
9
|
+
*
|
|
10
|
+
* Fields mirror markdownlint-cli2's flag set so downstream users can reuse
|
|
11
|
+
* existing muscle memory and automation.
|
|
12
|
+
*/
|
|
13
|
+
export interface CLIArgs {
|
|
14
|
+
readonly globs: string[];
|
|
15
|
+
readonly config?: string;
|
|
16
|
+
readonly configPointer?: string;
|
|
17
|
+
readonly fix: boolean;
|
|
18
|
+
readonly fixCheck: boolean;
|
|
19
|
+
readonly format: boolean;
|
|
20
|
+
readonly noGlobs: boolean;
|
|
21
|
+
readonly vaultRoot?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Commander maps `--no-resolve` to `resolve: false`. Left `undefined`
|
|
24
|
+
* when the flag is not supplied so config-level `resolve` can win.
|
|
25
|
+
*/
|
|
26
|
+
readonly resolve?: boolean;
|
|
27
|
+
readonly outputFormatter: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Build the top-level commander {@link Command} for the CLI.
|
|
32
|
+
*
|
|
33
|
+
* The returned program has `exitOverride()` configured by the caller so
|
|
34
|
+
* `--help` and `--version` can be caught in tests without terminating the
|
|
35
|
+
* process.
|
|
36
|
+
*
|
|
37
|
+
* @returns A fully wired commander `Command`.
|
|
38
|
+
*/
|
|
39
|
+
export function buildProgram(): Command {
|
|
40
|
+
const program = new Command();
|
|
41
|
+
program
|
|
42
|
+
.name("markdownlint-obsidian")
|
|
43
|
+
.description("Obsidian Flavored Markdown linter for CI pipelines")
|
|
44
|
+
.version(version)
|
|
45
|
+
.argument("[globs...]", "Glob patterns for files to lint")
|
|
46
|
+
.option("--config <path>", "Explicit config file path")
|
|
47
|
+
.option("--config-pointer <ptr>", "JSON Pointer into config (e.g. #/markdownlint)")
|
|
48
|
+
.option("--fix", "Auto-fix fixable errors in-place", false)
|
|
49
|
+
.option("--fix-check", "Check if fixes are needed without writing files", false)
|
|
50
|
+
.option("--format", "Read stdin, write linted content to stdout", false)
|
|
51
|
+
.option("--no-globs", "Ignore globs property in config file")
|
|
52
|
+
.option("--vault-root <path>", "Override auto-detected vault root")
|
|
53
|
+
.option("--no-resolve", "Disable wikilink resolution")
|
|
54
|
+
.option(
|
|
55
|
+
"--output-formatter <name>",
|
|
56
|
+
"Output formatter (default, json, junit, sarif)",
|
|
57
|
+
"default",
|
|
58
|
+
);
|
|
59
|
+
return program;
|
|
60
|
+
}
|
package/src/main.ts
ADDED
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import { buildProgram } from "./args.js";
|
|
3
|
+
import {
|
|
4
|
+
lint,
|
|
5
|
+
fix,
|
|
6
|
+
getFormatter,
|
|
7
|
+
loadConfig,
|
|
8
|
+
type Formatter,
|
|
9
|
+
type LintResult,
|
|
10
|
+
} from "markdownlint-obsidian/engine";
|
|
11
|
+
|
|
12
|
+
interface ParsedOptions {
|
|
13
|
+
readonly fix: boolean;
|
|
14
|
+
readonly fixCheck: boolean;
|
|
15
|
+
readonly format: boolean;
|
|
16
|
+
readonly vaultRoot?: string;
|
|
17
|
+
/**
|
|
18
|
+
* Commander maps `--no-resolve` to `resolve: false` and leaves the default
|
|
19
|
+
* at `true`. This is a tri-state in practice: `undefined` means the flag
|
|
20
|
+
* was never touched (fall back to config), `false` means `--no-resolve`
|
|
21
|
+
* was supplied.
|
|
22
|
+
*/
|
|
23
|
+
readonly resolve?: boolean;
|
|
24
|
+
readonly outputFormatter: string;
|
|
25
|
+
readonly config?: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Exit codes used by every CLI path.
|
|
30
|
+
*
|
|
31
|
+
* 0 = clean, 1 = lint errors, 2 = tool or config failure.
|
|
32
|
+
*/
|
|
33
|
+
export const EXIT_CODES = Object.freeze({
|
|
34
|
+
CLEAN: 0,
|
|
35
|
+
LINT_ERRORS: 1,
|
|
36
|
+
TOOL_FAILURE: 2,
|
|
37
|
+
} as const);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Entry point called by `bin/markdownlint-obsidian.js`.
|
|
41
|
+
*
|
|
42
|
+
* Parses arguments, runs the linting pipeline via the engine API,
|
|
43
|
+
* prints formatter output, and returns the appropriate exit code.
|
|
44
|
+
*
|
|
45
|
+
* @param argv - Argument vector, typically `process.argv`.
|
|
46
|
+
* @returns Resolved exit code (0, 1, or 2).
|
|
47
|
+
*/
|
|
48
|
+
export async function main(argv: string[]): Promise<number> {
|
|
49
|
+
const program = buildProgram();
|
|
50
|
+
program.exitOverride();
|
|
51
|
+
|
|
52
|
+
const parsed = parseArgv(program, argv);
|
|
53
|
+
if (parsed.terminal !== null) return parsed.terminal;
|
|
54
|
+
|
|
55
|
+
const opts = program.opts<ParsedOptions>();
|
|
56
|
+
const cwd = process.cwd();
|
|
57
|
+
const globs = program.args as string[];
|
|
58
|
+
|
|
59
|
+
return runPipeline(globs, opts, cwd);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
interface ParseResult {
|
|
63
|
+
readonly terminal: number | null;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function parseArgv(program: Command, argv: string[]): ParseResult {
|
|
67
|
+
try {
|
|
68
|
+
program.parse(argv);
|
|
69
|
+
return { terminal: null };
|
|
70
|
+
} catch (err: unknown) {
|
|
71
|
+
const e = err as { code?: string };
|
|
72
|
+
if (e.code === "commander.helpDisplayed" || e.code === "commander.version") {
|
|
73
|
+
return { terminal: EXIT_CODES.CLEAN };
|
|
74
|
+
}
|
|
75
|
+
return { terminal: EXIT_CODES.TOOL_FAILURE };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function resolveFormatter(name: string): Formatter | null {
|
|
80
|
+
try {
|
|
81
|
+
return getFormatter(name);
|
|
82
|
+
} catch (err) {
|
|
83
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function emitAndExit(results: readonly LintResult[], formatterName: string): number {
|
|
89
|
+
const formatter = resolveFormatter(formatterName);
|
|
90
|
+
if (formatter === null) return EXIT_CODES.TOOL_FAILURE;
|
|
91
|
+
const output = formatter(results);
|
|
92
|
+
if (output) process.stdout.write(output + "\n");
|
|
93
|
+
return results.some((r) => r.hasErrors) ? EXIT_CODES.LINT_ERRORS : EXIT_CODES.CLEAN;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function fmtRange(col: number, del: number): string {
|
|
97
|
+
return del === 0 ? `col ${col}` : `col ${col}–${col + del - 1}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function onCustomRuleError(modulePath: string, message: string): void {
|
|
101
|
+
process.stderr.write(`OFM905: failed to load custom rule module "${modulePath}": ${message}\n`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function buildEngineOptions(
|
|
105
|
+
globArgs: readonly string[],
|
|
106
|
+
config: Awaited<ReturnType<typeof loadConfig>>,
|
|
107
|
+
opts: ParsedOptions,
|
|
108
|
+
cwd: string,
|
|
109
|
+
): object {
|
|
110
|
+
const effectiveGlobs = globArgs.length > 0 ? [...globArgs] : config.globs;
|
|
111
|
+
return {
|
|
112
|
+
globs: effectiveGlobs,
|
|
113
|
+
cwd,
|
|
114
|
+
...(opts.vaultRoot !== undefined && { vaultRoot: opts.vaultRoot }),
|
|
115
|
+
...(opts.resolve === false && { resolve: false }),
|
|
116
|
+
...(opts.config !== undefined && { config: opts.config }),
|
|
117
|
+
onCustomRuleError,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function runFixBranch(engineOptions: object, opts: ParsedOptions): Promise<number> {
|
|
122
|
+
try {
|
|
123
|
+
const outcome = await fix({
|
|
124
|
+
...(engineOptions as Parameters<typeof fix>[0]),
|
|
125
|
+
check: opts.fixCheck,
|
|
126
|
+
});
|
|
127
|
+
if (outcome.filesFixed.length > 0) {
|
|
128
|
+
const verb = opts.fixCheck ? "Would fix" : "Fixed";
|
|
129
|
+
process.stderr.write(`${verb} ${outcome.filesFixed.length} file(s)\n`);
|
|
130
|
+
}
|
|
131
|
+
for (const conflict of outcome.conflicts) {
|
|
132
|
+
const colA = fmtRange(conflict.first.editColumn, conflict.first.deleteCount);
|
|
133
|
+
const colB = fmtRange(conflict.second.editColumn, conflict.second.deleteCount);
|
|
134
|
+
process.stderr.write(
|
|
135
|
+
`[fix-conflict] ${conflict.filePath}: ${conflict.reason} (${colA} vs ${colB})\n`,
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
return emitAndExit(outcome.finalPass, opts.outputFormatter);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
141
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
async function runLintBranch(engineOptions: object, opts: ParsedOptions): Promise<number> {
|
|
146
|
+
try {
|
|
147
|
+
const results = await lint(engineOptions as Parameters<typeof lint>[0]);
|
|
148
|
+
return emitAndExit(results, opts.outputFormatter);
|
|
149
|
+
} catch (err) {
|
|
150
|
+
process.stderr.write(`${err instanceof Error ? err.message : String(err)}\n`);
|
|
151
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function checkMutualExclusion(opts: ParsedOptions): number | null {
|
|
156
|
+
if (opts.fix && opts.fixCheck) {
|
|
157
|
+
process.stderr.write("OFM902: --fix and --fix-check are mutually exclusive\n");
|
|
158
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
async function runPipeline(
|
|
164
|
+
globArgs: readonly string[],
|
|
165
|
+
opts: ParsedOptions,
|
|
166
|
+
cwd: string,
|
|
167
|
+
): Promise<number> {
|
|
168
|
+
const exclusionCode = checkMutualExclusion(opts);
|
|
169
|
+
if (exclusionCode !== null) return exclusionCode;
|
|
170
|
+
|
|
171
|
+
if (resolveFormatter(opts.outputFormatter) === null) return EXIT_CODES.TOOL_FAILURE;
|
|
172
|
+
|
|
173
|
+
const config = await loadConfig(opts.config ?? cwd).catch(() => null);
|
|
174
|
+
if (!config) {
|
|
175
|
+
process.stderr.write("OFM901: failed to load configuration\n");
|
|
176
|
+
return EXIT_CODES.TOOL_FAILURE;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const engineOptions = buildEngineOptions(globArgs, config, opts, cwd);
|
|
180
|
+
return opts.fix || opts.fixCheck
|
|
181
|
+
? runFixBranch(engineOptions, opts)
|
|
182
|
+
: runLintBranch(engineOptions, opts);
|
|
183
|
+
}
|