mdrefine 0.1.0-beta.1
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/CONTRIBUTING.md +10 -0
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/SECURITY.md +16 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +158 -0
- package/dist/cli/main.js.map +1 -0
- package/dist/config/load.d.ts +9 -0
- package/dist/config/load.js +75 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/resolve.d.ts +93 -0
- package/dist/config/resolve.js +110 -0
- package/dist/config/resolve.js.map +1 -0
- package/dist/core/directives.d.ts +3 -0
- package/dist/core/directives.js +48 -0
- package/dist/core/directives.js.map +1 -0
- package/dist/core/engine.d.ts +8 -0
- package/dist/core/engine.js +192 -0
- package/dist/core/engine.js.map +1 -0
- package/dist/core/types.d.ts +92 -0
- package/dist/core/types.js +2 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/dialects.d.ts +2 -0
- package/dist/rules/dialects.js +105 -0
- package/dist/rules/dialects.js.map +1 -0
- package/dist/rules/links.d.ts +2 -0
- package/dist/rules/links.js +238 -0
- package/dist/rules/links.js.map +1 -0
- package/dist/rules/style.d.ts +12 -0
- package/dist/rules/style.js +244 -0
- package/dist/rules/style.js.map +1 -0
- package/dist/syntax/obsidian.d.ts +33 -0
- package/dist/syntax/obsidian.js +126 -0
- package/dist/syntax/obsidian.js.map +1 -0
- package/dist/syntax/parse.d.ts +5 -0
- package/dist/syntax/parse.js +45 -0
- package/dist/syntax/parse.js.map +1 -0
- package/dist/workspace/files.d.ts +8 -0
- package/dist/workspace/files.js +98 -0
- package/dist/workspace/files.js.map +1 -0
- package/dist/workspace/index.d.ts +10 -0
- package/dist/workspace/index.js +100 -0
- package/dist/workspace/index.js.map +1 -0
- package/docs/cli.md +71 -0
- package/docs/configuration.md +171 -0
- package/docs/contributing.md +103 -0
- package/docs/design.md +79 -0
- package/docs/index.md +48 -0
- package/docs/obsidian.md +69 -0
- package/docs/plugins.md +114 -0
- package/docs/quick-start.md +101 -0
- package/docs/releases/0.1.0-beta.1.md +39 -0
- package/docs/releases.md +90 -0
- package/examples/github.jsonc +7 -0
- package/examples/obsidian.jsonc +8 -0
- package/package.json +101 -0
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Human and agent-authored issues and pull requests are welcome. Read the
|
|
4
|
+
[contribution guide](https://viell-dev.github.io/slop-markdown-tools/contributing.html)
|
|
5
|
+
for defect reports, development, verification, and PR policy. This project is
|
|
6
|
+
agent-maintained, as described in [README.md](README.md).
|
|
7
|
+
|
|
8
|
+
Changes to document rendering need a minimal synthetic regression example.
|
|
9
|
+
Remove private vault content and credentials from all reports. Report security
|
|
10
|
+
vulnerabilities through the channel in [SECURITY.md](SECURITY.md).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 viell
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# mdrefine — Markdown Tools
|
|
2
|
+
|
|
3
|
+
Configurable linting and formatting for CommonMark, GitHub Markdown, and
|
|
4
|
+
Obsidian. Separate `lint` and `format` commands share syntax, style settings,
|
|
5
|
+
and an extensible rule engine.
|
|
6
|
+
|
|
7
|
+
## Maintenance
|
|
8
|
+
|
|
9
|
+
This project is developed and maintained by AI agents. It is
|
|
10
|
+
**not human-maintained**; do not assume that a person has reviewed generated
|
|
11
|
+
changes. Human and agent-authored issues and pull requests are welcome.
|
|
12
|
+
Reproducible examples, automated checks, and regression tests guide maintenance.
|
|
13
|
+
|
|
14
|
+
The project is currently an early prerelease. Report document corruption or
|
|
15
|
+
changed rendering with a minimal input, the configuration, actual output, and
|
|
16
|
+
expected output. Remove private content before posting examples. Agents using
|
|
17
|
+
the tool should report reproducible defects when their operator has authorized
|
|
18
|
+
publishing an issue.
|
|
19
|
+
|
|
20
|
+
## Documentation
|
|
21
|
+
|
|
22
|
+
Read the
|
|
23
|
+
[quick start](https://viell-dev.github.io/slop-markdown-tools/quick-start.html)
|
|
24
|
+
or browse the
|
|
25
|
+
[documentation site](https://viell-dev.github.io/slop-markdown-tools/). It
|
|
26
|
+
covers CLI workflows, configuration and rules, Obsidian vaults, plugins, and
|
|
27
|
+
current limitations. The Markdown source lives in `docs/`.
|
|
28
|
+
|
|
29
|
+
## Getting started
|
|
30
|
+
|
|
31
|
+
The CLI requires Node.js 22.12 or newer. The npm package is `mdrefine`; the
|
|
32
|
+
executable is `mdtools`. To install the beta in your document workspace:
|
|
33
|
+
|
|
34
|
+
```sh
|
|
35
|
+
npm install --save-dev --save-exact mdrefine@0.1.0-beta.1
|
|
36
|
+
npx --no-install mdtools --help
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See the
|
|
40
|
+
[release procedure](https://viell-dev.github.io/slop-markdown-tools/releases.html)
|
|
41
|
+
for registry availability, preparation, and authentication requirements.
|
|
42
|
+
|
|
43
|
+
Inspect your documents from the workspace where you installed the package:
|
|
44
|
+
|
|
45
|
+
```sh
|
|
46
|
+
npx --no-install mdtools lint --root /path/to/documents --json
|
|
47
|
+
npx --no-install mdtools format --root /path/to/documents --diff
|
|
48
|
+
npx --no-install mdtools format --root /path/to/documents --write
|
|
49
|
+
npx --no-install mdtools format --root /path/to/documents --check
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
`lint` never writes documents. `format` previews a diff by default for file
|
|
53
|
+
inputs and writes only with `--write`. For stdin, `format -` returns formatted
|
|
54
|
+
Markdown on stdout. Diagnostics use stderr unless `--json` requests a structured
|
|
55
|
+
report. `--no-install` prevents fetching a different package if the local
|
|
56
|
+
executable is missing.
|
|
57
|
+
|
|
58
|
+
## Build from source
|
|
59
|
+
|
|
60
|
+
For development, use Node.js 22.22.2+ or 24.15.0+ and npm 10 or newer; Node.js
|
|
61
|
+
24 LTS is preferred.
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
git clone https://github.com/viell-dev/slop-markdown-tools.git
|
|
65
|
+
cd slop-markdown-tools
|
|
66
|
+
npm ci
|
|
67
|
+
npm run build
|
|
68
|
+
node dist/cli/main.js --help
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
From the checkout, use `node dist/cli/main.js` in place of
|
|
72
|
+
`npx --no-install mdtools` in the examples above. You can also install the built
|
|
73
|
+
checkout as a local dependency.
|
|
74
|
+
|
|
75
|
+
## Contributions and verification
|
|
76
|
+
|
|
77
|
+
All changes to `main` require a pull request and passing CI, including
|
|
78
|
+
prerelease maintenance. Published npm versions and release tags are immutable;
|
|
79
|
+
corrections get a new version. See [CONTRIBUTING.md](CONTRIBUTING.md) and
|
|
80
|
+
[SECURITY.md](SECURITY.md) for contribution and vulnerability reporting.
|
|
81
|
+
|
|
82
|
+
Issues and PRs from people and agents are welcome. For defects, include the
|
|
83
|
+
version, command, configuration, minimal Markdown input, actual output, and
|
|
84
|
+
expected rendering. A screenshot can help explain a visible defect, but the
|
|
85
|
+
Markdown source is needed to reproduce it. For agents, include the JSON
|
|
86
|
+
diagnostic output when available. Automatic issue reporting is not built into
|
|
87
|
+
the tool.
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
npm run check
|
|
91
|
+
npm run test:package
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The project uses Markdown Tools to lint and format its own Markdown at the
|
|
95
|
+
default 80-column width. `npm run format:docs` formats Markdown;
|
|
96
|
+
`npm run format:docs:check` checks it without writing. Prettier formats
|
|
97
|
+
implementation and configuration files and excludes Markdown. `npm run format`
|
|
98
|
+
runs both formatters. Markdown fixtures under `tests/fixtures/` are excluded
|
|
99
|
+
from documentation passes.
|
|
100
|
+
|
|
101
|
+
`npm-run-all2` coordinates scripts, running independent checks in parallel and
|
|
102
|
+
building the CLI before documentation checks and tests.
|
|
103
|
+
|
|
104
|
+
`check` runs strict TypeScript checks, ESLint, Prettier for non-Markdown files,
|
|
105
|
+
Markdown Tools for documentation, the VitePress site build, and
|
|
106
|
+
regression/property/CLI tests. GitHub Actions runs checks on Linux with Node.js
|
|
107
|
+
22 and 24, plus macOS and Windows with Node.js 24. Changes to formatting need
|
|
108
|
+
tests for expected output, idempotence, and preserved semantics; defects should
|
|
109
|
+
add a regression example. Keep fixes scoped and describe remaining limitations
|
|
110
|
+
in the PR.
|
|
111
|
+
|
|
112
|
+
Agent contributors should read
|
|
113
|
+
[AGENTS.md](https://github.com/viell-dev/slop-markdown-tools/blob/main/AGENTS.md)
|
|
114
|
+
for maintenance-specific instructions. `test:package` packs and installs a
|
|
115
|
+
temporary consumer project, then exercises the installed CLI and library; it can
|
|
116
|
+
require access to the npm registry.
|
|
117
|
+
|
|
118
|
+
## Documentation website
|
|
119
|
+
|
|
120
|
+
```sh
|
|
121
|
+
npm run docs:dev
|
|
122
|
+
npm run docs:build
|
|
123
|
+
npm run docs:preview
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
VitePress renders `docs/`; Markdown Tools formats its Markdown sources. The
|
|
127
|
+
Pages workflow builds from `main` and deploys the generated artifact. No
|
|
128
|
+
publishing branch or committed build output is needed. See the
|
|
129
|
+
[documentation workflow](https://viell-dev.github.io/slop-markdown-tools/contributing.html#write-and-preview-documentation).
|
|
130
|
+
|
|
131
|
+
## License
|
|
132
|
+
|
|
133
|
+
[MIT](LICENSE).
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Security policy
|
|
2
|
+
|
|
3
|
+
Report suspected vulnerabilities through
|
|
4
|
+
[GitHub private vulnerability reporting](https://github.com/viell-dev/slop-markdown-tools/security/advisories/new).
|
|
5
|
+
Include the affected version, configuration, and a minimal synthetic example. Do
|
|
6
|
+
not disclose exploitable details or private documents in a public issue.
|
|
7
|
+
|
|
8
|
+
This project is maintained by agents, without a guaranteed response time.
|
|
9
|
+
Security fixes target the latest prerelease until a stable release exists; older
|
|
10
|
+
prereleases do not receive separate backports.
|
|
11
|
+
|
|
12
|
+
JavaScript configuration and plugins execute with the caller's permissions. Only
|
|
13
|
+
load trusted code. JSON configuration is data, but its plugin entries can still
|
|
14
|
+
load executable code. Link validation does not fetch external URLs. Document
|
|
15
|
+
rendering defects without a security impact belong in ordinary issues; remove
|
|
16
|
+
private content before posting.
|
package/dist/cli/main.js
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from "commander";
|
|
3
|
+
import { createTwoFilesPatch } from "diff";
|
|
4
|
+
import { readFile } from "node:fs/promises";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { format, lint, ruleRegistry } from "../core/engine.js";
|
|
8
|
+
import { loadConfig } from "../config/load.js";
|
|
9
|
+
import { resolveConfig } from "../config/resolve.js";
|
|
10
|
+
import { discover, writeAtomic } from "../workspace/files.js";
|
|
11
|
+
import { createWorkspace } from "../workspace/index.js";
|
|
12
|
+
const manifest = JSON.parse(await readFile(fileURLToPath(new URL("../../package.json", import.meta.url)), "utf8"));
|
|
13
|
+
const program = new Command()
|
|
14
|
+
.name("mdtools")
|
|
15
|
+
.description("Lint and format Markdown with explicit dialects and configurable rules.")
|
|
16
|
+
.version(manifest.version);
|
|
17
|
+
function common(command) {
|
|
18
|
+
return command
|
|
19
|
+
.option("--config <file>", "Explicit JSON, JSONC, or .mjs configuration")
|
|
20
|
+
.option("--root <directory>", "Workspace root (default: configuration directory or cwd)")
|
|
21
|
+
.option("--dialect <dialect>", "commonmark, github, or obsidian")
|
|
22
|
+
.option("--json", "Machine-readable output")
|
|
23
|
+
.option("--stdin-filepath <file>", "Filename context for stdin")
|
|
24
|
+
.option("--max-warnings <count>", "Fail when warning count exceeds this nonnegative integer", (value) => {
|
|
25
|
+
const number = Number(value);
|
|
26
|
+
if (!Number.isInteger(number) || number < 0)
|
|
27
|
+
throw new Error("--max-warnings must be a nonnegative integer.");
|
|
28
|
+
return number;
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
async function run(mode, inputs, flags) {
|
|
32
|
+
if ([flags.check, flags.diff, flags.write].filter(Boolean).length > 1)
|
|
33
|
+
throw new Error("Choose only one of --check, --diff, and --write.");
|
|
34
|
+
const loaded = await loadConfig(path.resolve(flags.root ?? "."), flags.config);
|
|
35
|
+
const root = path.resolve(flags.root ?? loaded.root);
|
|
36
|
+
if (flags.dialect)
|
|
37
|
+
loaded.config.dialect = flags.dialect;
|
|
38
|
+
const resolved = resolveConfig(loaded.config, "document.md", loaded.plugins);
|
|
39
|
+
const stdin = inputs.includes("-");
|
|
40
|
+
if (stdin && (inputs.length !== 1 || flags.write))
|
|
41
|
+
throw new Error("stdin must be the only input and cannot be combined with --write.");
|
|
42
|
+
const set = await discover(root, stdin ? [] : inputs, resolved.ignore);
|
|
43
|
+
if (stdin) {
|
|
44
|
+
const name = path
|
|
45
|
+
.relative(root, path.resolve(flags.stdinFilepath ?? path.join(root, "stdin.md")))
|
|
46
|
+
.split(path.sep)
|
|
47
|
+
.join("/");
|
|
48
|
+
if (name.startsWith("../") || path.isAbsolute(name))
|
|
49
|
+
throw new Error("--stdin-filepath must be within the workspace.");
|
|
50
|
+
let source = "";
|
|
51
|
+
process.stdin.setEncoding("utf8");
|
|
52
|
+
for await (const chunk of process.stdin)
|
|
53
|
+
source += String(chunk);
|
|
54
|
+
set.files[name] = source;
|
|
55
|
+
set.selected = [name];
|
|
56
|
+
}
|
|
57
|
+
const reports = [];
|
|
58
|
+
const changes = [];
|
|
59
|
+
// One index per active dialect; build lazily for mixed documentation workspaces.
|
|
60
|
+
const indexes = new Map();
|
|
61
|
+
for (const name of set.selected) {
|
|
62
|
+
const config = resolveConfig(loaded.config, name, loaded.plugins);
|
|
63
|
+
if (!indexes.has(config.dialect))
|
|
64
|
+
indexes.set(config.dialect, createWorkspace(set.files, {
|
|
65
|
+
dialect: config.dialect,
|
|
66
|
+
...(set.strictLineBreaks !== undefined ? { strictLineBreaks: set.strictLineBreaks } : {}),
|
|
67
|
+
}));
|
|
68
|
+
const options = {
|
|
69
|
+
path: name,
|
|
70
|
+
config: loaded.config,
|
|
71
|
+
plugins: loaded.plugins,
|
|
72
|
+
workspace: indexes.get(config.dialect),
|
|
73
|
+
};
|
|
74
|
+
const source = set.files[name];
|
|
75
|
+
if (source === null)
|
|
76
|
+
continue;
|
|
77
|
+
if (mode === "lint")
|
|
78
|
+
reports.push({ path: name, diagnostics: lint(source, options) });
|
|
79
|
+
else {
|
|
80
|
+
const result = format(source, options);
|
|
81
|
+
reports.push({
|
|
82
|
+
path: name,
|
|
83
|
+
changed: result.changed,
|
|
84
|
+
diagnostics: result.diagnostics,
|
|
85
|
+
...(stdin && !flags.check ? { output: result.output } : {}),
|
|
86
|
+
});
|
|
87
|
+
if (result.changed)
|
|
88
|
+
changes.push({ file: name, before: source, after: result.output });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
const unsafe = reports.some((report) => report.diagnostics.some((item) => item.rule === "engine/unsafe-format"));
|
|
92
|
+
if (flags.write && !unsafe)
|
|
93
|
+
for (const change of changes)
|
|
94
|
+
await writeAtomic(path.join(set.root, change.file), change.before, change.after);
|
|
95
|
+
if (flags.json)
|
|
96
|
+
process.stdout.write(JSON.stringify({
|
|
97
|
+
version: manifest.version,
|
|
98
|
+
mode,
|
|
99
|
+
files: reports,
|
|
100
|
+
written: flags.write === true && !unsafe,
|
|
101
|
+
}, null, 2) + "\n");
|
|
102
|
+
else {
|
|
103
|
+
if (stdin && mode === "format" && !flags.check && !flags.diff)
|
|
104
|
+
process.stdout.write(reports[0]?.output ?? "");
|
|
105
|
+
else if (mode === "format" && !flags.check && !flags.write)
|
|
106
|
+
for (const change of changes)
|
|
107
|
+
process.stdout.write(createTwoFilesPatch(`a/${change.file}`, `b/${change.file}`, change.before, change.after));
|
|
108
|
+
for (const report of reports)
|
|
109
|
+
for (const item of report.diagnostics)
|
|
110
|
+
process.stderr.write(`${report.path}:${item.line}:${item.column}: ${item.severity} ${item.rule}: ${item.message}\n`);
|
|
111
|
+
if (!stdin)
|
|
112
|
+
process.stderr.write(`${set.selected.length} file(s) ${mode === "lint" ? "linted" : "processed"}; ${changes.length} ${flags.write && !unsafe ? "written" : "would change"}.\n`);
|
|
113
|
+
}
|
|
114
|
+
const diagnostics = reports.flatMap((report) => report.diagnostics);
|
|
115
|
+
const errors = diagnostics.some((item) => item.severity === "error");
|
|
116
|
+
const tooManyWarnings = flags.maxWarnings !== undefined &&
|
|
117
|
+
diagnostics.filter((item) => item.severity === "warn").length > flags.maxWarnings;
|
|
118
|
+
process.exitCode = unsafe
|
|
119
|
+
? 2
|
|
120
|
+
: errors || tooManyWarnings || (flags.check && changes.length > 0)
|
|
121
|
+
? 1
|
|
122
|
+
: 0;
|
|
123
|
+
}
|
|
124
|
+
common(program.command("lint").argument("[paths...]", "Markdown files/directories, or - for stdin")).action((inputs, flags) => run("lint", inputs, flags));
|
|
125
|
+
common(program.command("format").argument("[paths...]", "Markdown files/directories, or - for stdin"))
|
|
126
|
+
.option("--check", "Fail if formatting changes would be needed")
|
|
127
|
+
.option("--diff", "Preview the complete diff (default for file inputs)")
|
|
128
|
+
.option("--write", "Write validated formatting changes")
|
|
129
|
+
.action((inputs, flags) => run("format", inputs, flags));
|
|
130
|
+
common(program.command("config").description("Inspect effective configuration"))
|
|
131
|
+
.command("explain <file>")
|
|
132
|
+
.action(async (file, _flags, command) => {
|
|
133
|
+
const flags = command.parent.opts();
|
|
134
|
+
const loaded = await loadConfig(path.resolve(flags.root ?? "."), flags.config);
|
|
135
|
+
if (flags.dialect)
|
|
136
|
+
loaded.config.dialect = flags.dialect;
|
|
137
|
+
const root = path.resolve(flags.root ?? loaded.root);
|
|
138
|
+
process.stdout.write(JSON.stringify({
|
|
139
|
+
root,
|
|
140
|
+
file: loaded.file ?? null,
|
|
141
|
+
effective: resolveConfig(loaded.config, path.relative(root, path.resolve(file)).split(path.sep).join("/"), loaded.plugins),
|
|
142
|
+
}, null, 2) + "\n");
|
|
143
|
+
});
|
|
144
|
+
program
|
|
145
|
+
.command("rules")
|
|
146
|
+
.description("List built-in rules")
|
|
147
|
+
.action(() => {
|
|
148
|
+
for (const [name, rule] of Object.entries(ruleRegistry()))
|
|
149
|
+
process.stdout.write(`${name}\t${rule.kind}\t${rule.description}\n`);
|
|
150
|
+
});
|
|
151
|
+
try {
|
|
152
|
+
await program.parseAsync();
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
process.stderr.write(`mdtools: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
156
|
+
process.exitCode = 2;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=main.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { createTwoFilesPatch } from \"diff\";\nimport { readFile } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { format, lint, ruleRegistry } from \"../core/engine.js\";\nimport { loadConfig } from \"../config/load.js\";\nimport { resolveConfig } from \"../config/resolve.js\";\nimport { discover, writeAtomic } from \"../workspace/files.js\";\nimport { createWorkspace } from \"../workspace/index.js\";\nimport type { Diagnostic, Dialect, ProcessOptions } from \"../core/types.js\";\n\ninterface Flags {\n config?: string;\n root?: string;\n dialect?: Dialect;\n json?: boolean;\n check?: boolean;\n diff?: boolean;\n write?: boolean;\n stdinFilepath?: string;\n maxWarnings?: number;\n}\nconst manifest = JSON.parse(\n await readFile(fileURLToPath(new URL(\"../../package.json\", import.meta.url)), \"utf8\"),\n) as { version: string };\nconst program = new Command()\n .name(\"mdtools\")\n .description(\"Lint and format Markdown with explicit dialects and configurable rules.\")\n .version(manifest.version);\nfunction common(command: Command): Command {\n return command\n .option(\"--config <file>\", \"Explicit JSON, JSONC, or .mjs configuration\")\n .option(\"--root <directory>\", \"Workspace root (default: configuration directory or cwd)\")\n .option(\"--dialect <dialect>\", \"commonmark, github, or obsidian\")\n .option(\"--json\", \"Machine-readable output\")\n .option(\"--stdin-filepath <file>\", \"Filename context for stdin\")\n .option(\n \"--max-warnings <count>\",\n \"Fail when warning count exceeds this nonnegative integer\",\n (value: string) => {\n const number = Number(value);\n if (!Number.isInteger(number) || number < 0)\n throw new Error(\"--max-warnings must be a nonnegative integer.\");\n return number;\n },\n );\n}\ninterface Report {\n path: string;\n changed?: boolean;\n diagnostics: Diagnostic[];\n output?: string;\n}\nasync function run(mode: \"lint\" | \"format\", inputs: string[], flags: Flags) {\n if ([flags.check, flags.diff, flags.write].filter(Boolean).length > 1)\n throw new Error(\"Choose only one of --check, --diff, and --write.\");\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n const root = path.resolve(flags.root ?? loaded.root);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const resolved = resolveConfig(loaded.config, \"document.md\", loaded.plugins);\n const stdin = inputs.includes(\"-\");\n if (stdin && (inputs.length !== 1 || flags.write))\n throw new Error(\"stdin must be the only input and cannot be combined with --write.\");\n const set = await discover(root, stdin ? [] : inputs, resolved.ignore);\n if (stdin) {\n const name = path\n .relative(root, path.resolve(flags.stdinFilepath ?? path.join(root, \"stdin.md\")))\n .split(path.sep)\n .join(\"/\");\n if (name.startsWith(\"../\") || path.isAbsolute(name))\n throw new Error(\"--stdin-filepath must be within the workspace.\");\n let source = \"\";\n process.stdin.setEncoding(\"utf8\");\n for await (const chunk of process.stdin) source += String(chunk);\n set.files[name] = source;\n set.selected = [name];\n }\n const reports: Report[] = [];\n const changes: { file: string; before: string; after: string }[] = [];\n // One index per active dialect; build lazily for mixed documentation workspaces.\n const indexes = new Map<Dialect, ReturnType<typeof createWorkspace>>();\n for (const name of set.selected) {\n const config = resolveConfig(loaded.config, name, loaded.plugins);\n if (!indexes.has(config.dialect))\n indexes.set(\n config.dialect,\n createWorkspace(set.files, {\n dialect: config.dialect,\n ...(set.strictLineBreaks !== undefined ? { strictLineBreaks: set.strictLineBreaks } : {}),\n }),\n );\n const options: ProcessOptions = {\n path: name,\n config: loaded.config,\n plugins: loaded.plugins,\n workspace: indexes.get(config.dialect)!,\n };\n const source = set.files[name]!;\n if (source === null) continue;\n if (mode === \"lint\") reports.push({ path: name, diagnostics: lint(source, options) });\n else {\n const result = format(source, options);\n reports.push({\n path: name,\n changed: result.changed,\n diagnostics: result.diagnostics,\n ...(stdin && !flags.check ? { output: result.output } : {}),\n });\n if (result.changed) changes.push({ file: name, before: source, after: result.output });\n }\n }\n const unsafe = reports.some((report) =>\n report.diagnostics.some((item) => item.rule === \"engine/unsafe-format\"),\n );\n if (flags.write && !unsafe)\n for (const change of changes)\n await writeAtomic(path.join(set.root, change.file), change.before, change.after);\n if (flags.json)\n process.stdout.write(\n JSON.stringify(\n {\n version: manifest.version,\n mode,\n files: reports,\n written: flags.write === true && !unsafe,\n },\n null,\n 2,\n ) + \"\\n\",\n );\n else {\n if (stdin && mode === \"format\" && !flags.check && !flags.diff)\n process.stdout.write(reports[0]?.output ?? \"\");\n else if (mode === \"format\" && !flags.check && !flags.write)\n for (const change of changes)\n process.stdout.write(\n createTwoFilesPatch(`a/${change.file}`, `b/${change.file}`, change.before, change.after),\n );\n for (const report of reports)\n for (const item of report.diagnostics)\n process.stderr.write(\n `${report.path}:${item.line}:${item.column}: ${item.severity} ${item.rule}: ${item.message}\\n`,\n );\n if (!stdin)\n process.stderr.write(\n `${set.selected.length} file(s) ${mode === \"lint\" ? \"linted\" : \"processed\"}; ${changes.length} ${flags.write && !unsafe ? \"written\" : \"would change\"}.\\n`,\n );\n }\n const diagnostics = reports.flatMap((report) => report.diagnostics);\n const errors = diagnostics.some((item) => item.severity === \"error\");\n const tooManyWarnings =\n flags.maxWarnings !== undefined &&\n diagnostics.filter((item) => item.severity === \"warn\").length > flags.maxWarnings;\n process.exitCode = unsafe\n ? 2\n : errors || tooManyWarnings || (flags.check && changes.length > 0)\n ? 1\n : 0;\n}\ncommon(\n program.command(\"lint\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n).action((inputs: string[], flags: Flags) => run(\"lint\", inputs, flags));\ncommon(\n program.command(\"format\").argument(\"[paths...]\", \"Markdown files/directories, or - for stdin\"),\n)\n .option(\"--check\", \"Fail if formatting changes would be needed\")\n .option(\"--diff\", \"Preview the complete diff (default for file inputs)\")\n .option(\"--write\", \"Write validated formatting changes\")\n .action((inputs: string[], flags: Flags) => run(\"format\", inputs, flags));\ncommon(program.command(\"config\").description(\"Inspect effective configuration\"))\n .command(\"explain <file>\")\n .action(async (file: string, _flags: unknown, command: Command) => {\n const flags = command.parent!.opts<Flags>();\n const loaded = await loadConfig(path.resolve(flags.root ?? \".\"), flags.config);\n if (flags.dialect) loaded.config.dialect = flags.dialect;\n const root = path.resolve(flags.root ?? loaded.root);\n process.stdout.write(\n JSON.stringify(\n {\n root,\n file: loaded.file ?? null,\n effective: resolveConfig(\n loaded.config,\n path.relative(root, path.resolve(file)).split(path.sep).join(\"/\"),\n loaded.plugins,\n ),\n },\n null,\n 2,\n ) + \"\\n\",\n );\n });\nprogram\n .command(\"rules\")\n .description(\"List built-in rules\")\n .action(() => {\n for (const [name, rule] of Object.entries(ruleRegistry()))\n process.stdout.write(`${name}\\t${rule.kind}\\t${rule.description}\\n`);\n });\ntry {\n await program.parseAsync();\n} catch (error) {\n process.stderr.write(`mdtools: ${error instanceof Error ? error.message : String(error)}\\n`);\n process.exitCode = 2;\n}\n"]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Config, Plugin } from "../core/types.js";
|
|
2
|
+
export interface LoadedConfig {
|
|
3
|
+
config: Config;
|
|
4
|
+
plugins: Plugin[];
|
|
5
|
+
root: string;
|
|
6
|
+
file?: string;
|
|
7
|
+
}
|
|
8
|
+
export declare function exists(file: string): Promise<boolean>;
|
|
9
|
+
export declare function loadConfig(start: string, explicit?: string): Promise<LoadedConfig>;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { readFile, stat } from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { pathToFileURL } from "node:url";
|
|
4
|
+
import { createRequire } from "node:module";
|
|
5
|
+
import { parse, printParseErrorCode } from "jsonc-parser";
|
|
6
|
+
import { validateConfig } from "./resolve.js";
|
|
7
|
+
export async function exists(file) {
|
|
8
|
+
try {
|
|
9
|
+
await stat(file);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch (error) {
|
|
13
|
+
if (error.code === "ENOENT")
|
|
14
|
+
return false;
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
export async function loadConfig(start, explicit) {
|
|
19
|
+
let file = explicit ? path.resolve(explicit) : undefined;
|
|
20
|
+
let fallbackRoot = path.resolve(start);
|
|
21
|
+
if (!file) {
|
|
22
|
+
let directory = path.resolve(start);
|
|
23
|
+
for (;;) {
|
|
24
|
+
const found = [];
|
|
25
|
+
for (const name of ["mdtools.config.jsonc", "mdtools.config.json", "mdtools.config.mjs"])
|
|
26
|
+
if (await exists(path.join(directory, name)))
|
|
27
|
+
found.push(path.join(directory, name));
|
|
28
|
+
if (found.length > 1)
|
|
29
|
+
throw new Error(`Multiple mdtools configuration files in ${directory}. Use --config.`);
|
|
30
|
+
if (found.length) {
|
|
31
|
+
file = found[0];
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
if ((await exists(path.join(directory, ".git"))) ||
|
|
35
|
+
(await exists(path.join(directory, ".obsidian")))) {
|
|
36
|
+
fallbackRoot = directory;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
if (path.dirname(directory) === directory)
|
|
40
|
+
break;
|
|
41
|
+
directory = path.dirname(directory);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let value = {};
|
|
45
|
+
if (file?.endsWith(".mjs"))
|
|
46
|
+
value = (await import(pathToFileURL(file).href)).default;
|
|
47
|
+
else if (file) {
|
|
48
|
+
const errors = [];
|
|
49
|
+
value = parse(await readFile(file, "utf8"), errors, {
|
|
50
|
+
allowTrailingComma: true,
|
|
51
|
+
disallowComments: file.endsWith(".json"),
|
|
52
|
+
});
|
|
53
|
+
if (errors.length)
|
|
54
|
+
throw new Error(`Invalid JSON configuration: ${errors.map((error) => `${printParseErrorCode(error.error)} at offset ${error.offset}`).join(", ")}`);
|
|
55
|
+
}
|
|
56
|
+
validateConfig(value);
|
|
57
|
+
const root = file ? path.dirname(file) : fallbackRoot;
|
|
58
|
+
const plugins = [];
|
|
59
|
+
const require = createRequire(path.join(root, "mdtools.config.mjs"));
|
|
60
|
+
for (const specifier of value.plugins ?? []) {
|
|
61
|
+
const location = specifier.startsWith(".") || path.isAbsolute(specifier)
|
|
62
|
+
? path.resolve(root, specifier)
|
|
63
|
+
: require.resolve(specifier);
|
|
64
|
+
const plugin = (await import(pathToFileURL(location).href))
|
|
65
|
+
.default;
|
|
66
|
+
if (!plugin ||
|
|
67
|
+
typeof plugin !== "object" ||
|
|
68
|
+
!("name" in plugin) ||
|
|
69
|
+
typeof plugin.name !== "string")
|
|
70
|
+
throw new Error(`Invalid plugin export: ${specifier}`);
|
|
71
|
+
plugins.push(plugin);
|
|
72
|
+
}
|
|
73
|
+
return { config: value, plugins, root, ...(file ? { file } : {}) };
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=load.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/config/load.ts"],"names":[],"mappings":"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","sourcesContent":["import { readFile, stat } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { createRequire } from \"node:module\";\nimport { parse, printParseErrorCode } from \"jsonc-parser\";\nimport type { ParseError } from \"jsonc-parser\";\nimport type { Config, Plugin } from \"../core/types.js\";\nimport { validateConfig } from \"./resolve.js\";\n\nexport interface LoadedConfig {\n config: Config;\n plugins: Plugin[];\n root: string;\n file?: string;\n}\nexport async function exists(file: string): Promise<boolean> {\n try {\n await stat(file);\n return true;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === \"ENOENT\") return false;\n throw error;\n }\n}\nexport async function loadConfig(start: string, explicit?: string): Promise<LoadedConfig> {\n let file = explicit ? path.resolve(explicit) : undefined;\n let fallbackRoot = path.resolve(start);\n if (!file) {\n let directory = path.resolve(start);\n for (;;) {\n const found: string[] = [];\n for (const name of [\"mdtools.config.jsonc\", \"mdtools.config.json\", \"mdtools.config.mjs\"])\n if (await exists(path.join(directory, name))) found.push(path.join(directory, name));\n if (found.length > 1)\n throw new Error(`Multiple mdtools configuration files in ${directory}. Use --config.`);\n if (found.length) {\n file = found[0];\n break;\n }\n if (\n (await exists(path.join(directory, \".git\"))) ||\n (await exists(path.join(directory, \".obsidian\")))\n ) {\n fallbackRoot = directory;\n break;\n }\n if (path.dirname(directory) === directory) break;\n directory = path.dirname(directory);\n }\n }\n let value: unknown = {};\n if (file?.endsWith(\".mjs\"))\n value = ((await import(pathToFileURL(file).href)) as { default: unknown }).default;\n else if (file) {\n const errors: ParseError[] = [];\n value = parse(await readFile(file, \"utf8\"), errors, {\n allowTrailingComma: true,\n disallowComments: file.endsWith(\".json\"),\n });\n if (errors.length)\n throw new Error(\n `Invalid JSON configuration: ${errors.map((error) => `${printParseErrorCode(error.error)} at offset ${error.offset}`).join(\", \")}`,\n );\n }\n validateConfig(value);\n const root = file ? path.dirname(file) : fallbackRoot;\n const plugins: Plugin[] = [];\n const require = createRequire(path.join(root, \"mdtools.config.mjs\"));\n for (const specifier of value.plugins ?? []) {\n const location =\n specifier.startsWith(\".\") || path.isAbsolute(specifier)\n ? path.resolve(root, specifier)\n : require.resolve(specifier);\n const plugin: unknown = ((await import(pathToFileURL(location).href)) as { default: unknown })\n .default;\n if (\n !plugin ||\n typeof plugin !== \"object\" ||\n !(\"name\" in plugin) ||\n typeof plugin.name !== \"string\"\n )\n throw new Error(`Invalid plugin export: ${specifier}`);\n plugins.push(plugin as Plugin);\n }\n return { config: value, plugins, root, ...(file ? { file } : {}) };\n}\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { Config, Plugin, ResolvedConfig, RuleSetting } from "../core/types.js";
|
|
2
|
+
export declare const presets: Record<string, Config>;
|
|
3
|
+
export declare const configSchema: {
|
|
4
|
+
$schema: string;
|
|
5
|
+
type: string;
|
|
6
|
+
additionalProperties: boolean;
|
|
7
|
+
properties: {
|
|
8
|
+
$schema: {
|
|
9
|
+
type: string;
|
|
10
|
+
};
|
|
11
|
+
extends: {
|
|
12
|
+
type: string;
|
|
13
|
+
items: {
|
|
14
|
+
type: string;
|
|
15
|
+
};
|
|
16
|
+
};
|
|
17
|
+
dialect: {
|
|
18
|
+
enum: string[];
|
|
19
|
+
};
|
|
20
|
+
rules: {
|
|
21
|
+
type: string;
|
|
22
|
+
additionalProperties: {
|
|
23
|
+
anyOf: ({
|
|
24
|
+
enum: string[];
|
|
25
|
+
} | {
|
|
26
|
+
type: string;
|
|
27
|
+
items: ({
|
|
28
|
+
enum: string[];
|
|
29
|
+
} | {
|
|
30
|
+
type: string;
|
|
31
|
+
})[];
|
|
32
|
+
minItems: number;
|
|
33
|
+
maxItems: number;
|
|
34
|
+
})[];
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
ignore: {
|
|
38
|
+
type: string;
|
|
39
|
+
items: {
|
|
40
|
+
type: string;
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
plugins: {
|
|
44
|
+
type: string;
|
|
45
|
+
items: {
|
|
46
|
+
type: string;
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
overrides: {
|
|
50
|
+
type: string;
|
|
51
|
+
items: {
|
|
52
|
+
type: string;
|
|
53
|
+
required: string[];
|
|
54
|
+
additionalProperties: boolean;
|
|
55
|
+
properties: {
|
|
56
|
+
files: {
|
|
57
|
+
type: string;
|
|
58
|
+
items: {
|
|
59
|
+
type: string;
|
|
60
|
+
};
|
|
61
|
+
minItems: number;
|
|
62
|
+
};
|
|
63
|
+
dialect: {
|
|
64
|
+
enum: string[];
|
|
65
|
+
};
|
|
66
|
+
rules: {
|
|
67
|
+
type: string;
|
|
68
|
+
additionalProperties: {
|
|
69
|
+
anyOf: ({
|
|
70
|
+
enum: string[];
|
|
71
|
+
} | {
|
|
72
|
+
type: string;
|
|
73
|
+
items: ({
|
|
74
|
+
enum: string[];
|
|
75
|
+
} | {
|
|
76
|
+
type: string;
|
|
77
|
+
})[];
|
|
78
|
+
minItems: number;
|
|
79
|
+
maxItems: number;
|
|
80
|
+
})[];
|
|
81
|
+
};
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
};
|
|
86
|
+
};
|
|
87
|
+
};
|
|
88
|
+
export declare function validateConfig(value: unknown): asserts value is Config;
|
|
89
|
+
export declare function setting(value: RuleSetting): {
|
|
90
|
+
severity: "off" | "warn" | "error";
|
|
91
|
+
options: Record<string, unknown>;
|
|
92
|
+
};
|
|
93
|
+
export declare function resolveConfig(config?: Config, path?: string, plugins?: Plugin[]): ResolvedConfig;
|