mdrefine 0.2.0-rc.1 → 0.2.0-rc.2
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 +48 -20
- package/THIRD_PARTY_NOTICES +35 -0
- package/dist/cli/main.js +121 -18
- package/dist/cli/main.js.map +1 -1
- package/dist/config/load.d.ts +8 -1
- package/dist/config/load.js +162 -42
- package/dist/config/load.js.map +1 -1
- package/dist/config/resolve.d.ts +16 -1
- package/dist/config/resolve.js +29 -4
- package/dist/config/resolve.js.map +1 -1
- package/dist/core/engine.js +4 -2
- package/dist/core/engine.js.map +1 -1
- package/dist/core/types.d.ts +46 -1
- package/dist/core/types.js.map +1 -1
- package/dist/rules/links.js +51 -10
- package/dist/rules/links.js.map +1 -1
- package/dist/syntax/math.d.ts +36 -0
- package/dist/syntax/math.js +330 -0
- package/dist/syntax/math.js.map +1 -0
- package/dist/syntax/parse.js +13 -2
- package/dist/syntax/parse.js.map +1 -1
- package/dist/workspace/access.d.ts +30 -0
- package/dist/workspace/access.js +52 -0
- package/dist/workspace/access.js.map +1 -0
- package/dist/workspace/files.d.ts +27 -1
- package/dist/workspace/files.js +135 -21
- package/dist/workspace/files.js.map +1 -1
- package/dist/workspace/index.d.ts +28 -0
- package/dist/workspace/index.js +464 -196
- package/dist/workspace/index.js.map +1 -1
- package/dist/workspace/selection.js +13 -5
- package/dist/workspace/selection.js.map +1 -1
- package/dist/workspace/vault.d.ts +51 -0
- package/dist/workspace/vault.js +108 -0
- package/dist/workspace/vault.js.map +1 -0
- package/docs/benchmarks.md +299 -0
- package/docs/cli.md +32 -5
- package/docs/configuration.md +201 -22
- package/docs/contributing.md +37 -11
- package/docs/design.md +92 -24
- package/docs/examples.md +555 -0
- package/docs/index.md +2 -0
- package/docs/obsidian.md +71 -8
- package/docs/plugins.md +195 -20
- package/docs/quick-start.md +16 -7
- package/docs/releases/0.2.0-rc.2.md +225 -0
- package/docs/releases.md +9 -8
- package/docs/testing.md +93 -0
- package/examples/README.md +79 -0
- package/examples/automation/github-actions.yml +23 -0
- package/examples/automation/summarize-report.mjs +34 -0
- package/examples/library/format-string.mjs +21 -0
- package/examples/library/lint-folder.mjs +43 -0
- package/examples/plugin/mdtools.config.jsonc +9 -0
- package/examples/plugin/notes/release-checklist.md +9 -0
- package/examples/plugin/rules.mjs +94 -0
- package/examples/workspaces/obsidian-vault/after/.obsidian/app.json +3 -0
- package/examples/workspaces/obsidian-vault/after/Garden/Tomatoes.md +15 -0
- package/examples/workspaces/obsidian-vault/after/Garden/Watering schedule.md +11 -0
- package/examples/workspaces/obsidian-vault/after/Home.md +16 -0
- package/examples/workspaces/obsidian-vault/after/Templates/Plant.md +6 -0
- package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +10 -0
- package/examples/workspaces/obsidian-vault/before/.obsidian/app.json +3 -0
- package/examples/workspaces/obsidian-vault/before/Garden/Tomatoes.md +13 -0
- package/examples/workspaces/obsidian-vault/before/Garden/Watering schedule.md +9 -0
- package/examples/workspaces/obsidian-vault/before/Home.md +13 -0
- package/examples/workspaces/obsidian-vault/before/Templates/Plant.md +6 -0
- package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +10 -0
- package/examples/workspaces/repository-docs/after/README.md +25 -0
- package/examples/workspaces/repository-docs/after/docs/guide.md +19 -0
- package/examples/workspaces/repository-docs/after/docs/reference.md +14 -0
- package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +10 -0
- package/examples/workspaces/repository-docs/before/README.md +21 -0
- package/examples/workspaces/repository-docs/before/docs/guide.md +16 -0
- package/examples/workspaces/repository-docs/before/docs/reference.md +13 -0
- package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +10 -0
- package/package.json +14 -6
package/README.md
CHANGED
|
@@ -14,11 +14,13 @@ the code, tests, documentation, CI, and release tooling, and agents implement,
|
|
|
14
14
|
verify, and merge every change. It is **not human-maintained**; do not assume
|
|
15
15
|
that a person has reviewed generated changes. The repository owner does not
|
|
16
16
|
write, read, or review code, does not know the codebase or API, and does not use
|
|
17
|
-
the tool directly. They
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
the tool directly. They guide the project lightly, from what they can see: the
|
|
18
|
+
public documentation, issue and PR descriptions, and Markdown documents that
|
|
19
|
+
agents have processed with the tool. They assign agents to tasks; the assigned
|
|
20
|
+
agents make the architectural and design decisions and keep every part of the
|
|
21
|
+
repository consistent with the rest. Human and agent-authored issues and pull
|
|
22
|
+
requests are welcome. Reproducible examples, automated checks, and regression
|
|
23
|
+
tests guide maintenance.
|
|
22
24
|
|
|
23
25
|
The project is currently an early prerelease. Report document corruption or
|
|
24
26
|
changed rendering with a minimal input, the configuration, actual output, and
|
|
@@ -33,7 +35,10 @@ Read the
|
|
|
33
35
|
or browse the
|
|
34
36
|
[documentation site](https://viell-dev.github.io/slop-markdown-tools/). It
|
|
35
37
|
covers CLI workflows, configuration and rules, Obsidian vaults, plugins, and
|
|
36
|
-
current limitations. The Markdown source lives in `docs/`.
|
|
38
|
+
current limitations. The Markdown source lives in `docs/`. Working
|
|
39
|
+
[examples](https://viell-dev.github.io/slop-markdown-tools/examples.html) live
|
|
40
|
+
in `examples/` and ship with the package: starting configurations, workspaces
|
|
41
|
+
before and after formatting, a plugin, and library scripts.
|
|
37
42
|
|
|
38
43
|
## Getting started
|
|
39
44
|
|
|
@@ -42,7 +47,7 @@ executable is `mdtools`. To install the release candidate in your document
|
|
|
42
47
|
workspace:
|
|
43
48
|
|
|
44
49
|
```sh
|
|
45
|
-
npm install --save-dev --save-exact mdrefine@0.2.0-rc.
|
|
50
|
+
npm install --save-dev --save-exact mdrefine@0.2.0-rc.2
|
|
46
51
|
npx --no-install mdtools --help
|
|
47
52
|
```
|
|
48
53
|
|
|
@@ -65,6 +70,12 @@ Markdown on stdout. Diagnostics use stderr unless `--json` requests a structured
|
|
|
65
70
|
report. `--no-install` prevents fetching a different package if the local
|
|
66
71
|
executable is missing.
|
|
67
72
|
|
|
73
|
+
Without a configuration file, the commands apply the `recommended` preset and
|
|
74
|
+
assume GitHub Markdown, or the Obsidian dialect for documents inside an Obsidian
|
|
75
|
+
vault. Version 0.2.0-rc.1 and earlier assumed CommonMark, in which formatting
|
|
76
|
+
reflows tables and joins the lines of a note. The quick start shows how to
|
|
77
|
+
choose a profile for GitHub, Forgejo, Gitea, Obsidian, or plain CommonMark.
|
|
78
|
+
|
|
68
79
|
## Build from source
|
|
69
80
|
|
|
70
81
|
For development, use Node.js 22.22.2+ or 24.15.0+ and npm 10 or newer; Node.js
|
|
@@ -105,27 +116,44 @@ The project uses Markdown Tools to lint and format its own Markdown at the
|
|
|
105
116
|
default 80-column width. `npm run format:docs` formats Markdown;
|
|
106
117
|
`npm run format:docs:check` checks it without writing. Prettier formats
|
|
107
118
|
implementation and configuration files and excludes Markdown. `npm run format`
|
|
108
|
-
runs both formatters. Markdown fixtures under `tests/fixtures/`
|
|
109
|
-
|
|
119
|
+
runs both formatters. Markdown fixtures under `tests/fixtures/` and the example
|
|
120
|
+
documents under `examples/workspaces/` and `examples/plugin/notes/` are excluded
|
|
121
|
+
from documentation passes, because they are deliberately unformatted.
|
|
110
122
|
|
|
111
123
|
`npm-run-all2` coordinates scripts, running independent checks in parallel and
|
|
112
124
|
building the CLI before documentation checks and tests.
|
|
113
125
|
|
|
114
126
|
`check` runs strict TypeScript checks, ESLint, Prettier for non-Markdown files,
|
|
115
|
-
Markdown Tools for documentation, the VitePress site build, and
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
127
|
+
Markdown Tools for documentation, the VitePress site build, and both test
|
|
128
|
+
suites. Unit tests in `tests/unit/` call the library's source; integration tests
|
|
129
|
+
in `tests/integration/` run the built CLI on real files and check the examples
|
|
130
|
+
and documentation against the tool. `npm run test:unit` and
|
|
131
|
+
`npm run test:integration` run one suite; see
|
|
132
|
+
[testing](https://viell-dev.github.io/slop-markdown-tools/testing.html). GitHub
|
|
133
|
+
Actions runs full checks on Linux with Node.js 24, plus build and test coverage
|
|
134
|
+
on Linux with Node.js 22 and Windows with Node.js 24. All three jobs verify the
|
|
135
|
+
installed package. Changes to formatting need tests for expected output,
|
|
136
|
+
idempotence, and preserved semantics; defects should add a regression example.
|
|
137
|
+
Keep fixes scoped and describe remaining limitations in the PR.
|
|
122
138
|
|
|
123
139
|
Agent contributors should read
|
|
124
140
|
[AGENTS.md](https://github.com/viell-dev/slop-markdown-tools/blob/main/AGENTS.md)
|
|
125
|
-
for maintenance-specific instructions, including
|
|
126
|
-
|
|
127
|
-
project, then exercises the installed CLI and library; it can require
|
|
128
|
-
the npm registry.
|
|
141
|
+
for maintenance-specific instructions, including what an assigned agent decides
|
|
142
|
+
and the limits of that authority. `test:package` packs and installs a temporary
|
|
143
|
+
consumer project, then exercises the installed CLI and library; it can require
|
|
144
|
+
access to the npm registry.
|
|
145
|
+
|
|
146
|
+
## Benchmarks
|
|
147
|
+
|
|
148
|
+
```sh
|
|
149
|
+
npm run benchmark
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The benchmark times parsing, linting, and formatting of synthetic documents
|
|
153
|
+
through the library and the CLI, with Prettier on the same files for comparison.
|
|
154
|
+
It takes a few minutes and is not part of `check`.
|
|
155
|
+
[Benchmarks](https://viell-dev.github.io/slop-markdown-tools/benchmarks.html)
|
|
156
|
+
explains what each number means and whether it is good.
|
|
129
157
|
|
|
130
158
|
## Documentation website
|
|
131
159
|
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
This package includes code adapted from the following project. The notice
|
|
2
|
+
below applies to that code. Everything else in the package is covered by
|
|
3
|
+
the LICENSE file.
|
|
4
|
+
|
|
5
|
+
------------------------------------------------------------------------
|
|
6
|
+
|
|
7
|
+
micromark-extension-math 3.1.0
|
|
8
|
+
https://github.com/micromark/micromark-extension-math
|
|
9
|
+
|
|
10
|
+
The tokenizer for dollar math in dist/syntax/math.js (src/syntax/math.ts in
|
|
11
|
+
the repository) is adapted from this project's lib/math-text.js and
|
|
12
|
+
lib/math-flow.js.
|
|
13
|
+
|
|
14
|
+
(The MIT License)
|
|
15
|
+
|
|
16
|
+
Copyright (c) 2020 Titus Wormer <tituswormer@gmail.com>
|
|
17
|
+
|
|
18
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
19
|
+
a copy of this software and associated documentation files (the
|
|
20
|
+
'Software'), to deal in the Software without restriction, including
|
|
21
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
22
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
23
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
24
|
+
the following conditions:
|
|
25
|
+
|
|
26
|
+
The above copyright notice and this permission notice shall be
|
|
27
|
+
included in all copies or substantial portions of the Software.
|
|
28
|
+
|
|
29
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
|
30
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
31
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
32
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
|
33
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
|
34
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
|
35
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/dist/cli/main.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
import { createTwoFilesPatch } from "diff";
|
|
4
|
-
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { access, constants, readFile, realpath, stat } from "node:fs/promises";
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { format, lint, ruleRegistry } from "../core/engine.js";
|
|
8
8
|
import { loadConfig } from "../config/load.js";
|
|
9
9
|
import { resolveConfig } from "../config/resolve.js";
|
|
10
|
-
import { discover, writeAtomic } from "../workspace/files.js";
|
|
10
|
+
import { discover, vaultFor, writeAtomic } from "../workspace/files.js";
|
|
11
|
+
import { enclosingVault, vaultOf } from "../workspace/vault.js";
|
|
11
12
|
import { excludeSelection } from "../workspace/selection.js";
|
|
12
13
|
import { createWorkspace } from "../workspace/index.js";
|
|
14
|
+
import { pathError, refusal, refusalText } from "../workspace/access.js";
|
|
13
15
|
const manifest = JSON.parse(await readFile(fileURLToPath(new URL("../../package.json", import.meta.url)), "utf8"));
|
|
14
16
|
const program = new Command()
|
|
15
17
|
.name("mdtools")
|
|
@@ -19,7 +21,7 @@ function common(command) {
|
|
|
19
21
|
return command
|
|
20
22
|
.option("--config <file>", "Explicit JSON, JSONC, or .mjs configuration")
|
|
21
23
|
.option("--root <directory>", "Workspace root (default: configuration directory or cwd)")
|
|
22
|
-
.option("--dialect <dialect>", "commonmark, github, forgejo (alias: codeberg), gitea, or obsidian")
|
|
24
|
+
.option("--dialect <dialect>", "commonmark, github, forgejo (alias: codeberg), gitea, or obsidian (when none is named: github, or obsidian inside a vault)")
|
|
23
25
|
.option("--exclude <path>", "Exclude an exact file or directory (repeatable)", (value, previous) => [...previous, value], [])
|
|
24
26
|
.option("--json", "Machine-readable output")
|
|
25
27
|
.option("--stdin-filepath <file>", "Filename context for stdin")
|
|
@@ -30,21 +32,52 @@ function common(command) {
|
|
|
30
32
|
return number;
|
|
31
33
|
});
|
|
32
34
|
}
|
|
35
|
+
/**
|
|
36
|
+
* The dialect to assume for a document when nothing names one, or undefined for
|
|
37
|
+
* the library's own default. An Obsidian vault shows a single line break as a
|
|
38
|
+
* line break and holds wikilinks, both of which any other dialect lets reflow
|
|
39
|
+
* change, so a document that a vault holds is read as a note.
|
|
40
|
+
*/
|
|
41
|
+
function assumedDialect(vault) {
|
|
42
|
+
return vault ? "obsidian" : undefined;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Stops when the folder given as `--root` cannot serve as a workspace root.
|
|
46
|
+
* Without this check the first thing to fail would be the search for a
|
|
47
|
+
* configuration file in it, with a message about a file the user never named.
|
|
48
|
+
*/
|
|
49
|
+
async function checkRoot(given) {
|
|
50
|
+
if (given === undefined)
|
|
51
|
+
return;
|
|
52
|
+
const root = path.resolve(given);
|
|
53
|
+
try {
|
|
54
|
+
if (!(await stat(root)).isDirectory())
|
|
55
|
+
throw new Error(`Workspace root is not a directory: ${root}`);
|
|
56
|
+
await access(root, constants.R_OK | constants.X_OK);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw pathError("Workspace root", root, error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
33
62
|
async function run(mode, inputs, flags) {
|
|
34
63
|
if ([flags.check, flags.diff, flags.write].filter(Boolean).length > 1)
|
|
35
64
|
throw new Error("Choose only one of --check, --diff, and --write.");
|
|
36
65
|
if (inputs.includes("-") && flags.exclude?.length)
|
|
37
66
|
throw new Error("--exclude cannot be combined with stdin.");
|
|
67
|
+
await checkRoot(flags.root);
|
|
38
68
|
const loaded = await loadConfig(path.resolve(flags.root ?? "."), flags.config);
|
|
39
69
|
const root = path.resolve(flags.root ?? loaded.root);
|
|
40
70
|
if (flags.dialect)
|
|
41
71
|
loaded.config.dialect = flags.dialect;
|
|
72
|
+
// Which files to leave out does not depend on a document's dialect.
|
|
42
73
|
const resolved = resolveConfig(loaded.config, "document.md", loaded.plugins);
|
|
43
74
|
const stdin = inputs.includes("-");
|
|
44
75
|
if (stdin && (inputs.length !== 1 || flags.write))
|
|
45
76
|
throw new Error("stdin must be the only input and cannot be combined with --write.");
|
|
46
77
|
const set = await discover(root, stdin ? [] : inputs, resolved.ignore, resolved.resolve);
|
|
47
78
|
set.selected = await excludeSelection(set.root, set.selected, flags.exclude ?? []);
|
|
79
|
+
/** The vault of the document read from stdin, found by a search of its own. */
|
|
80
|
+
let named;
|
|
48
81
|
if (stdin) {
|
|
49
82
|
const name = path
|
|
50
83
|
.relative(root, path.resolve(flags.stdinFilepath ?? path.join(root, "stdin.md")))
|
|
@@ -58,27 +91,79 @@ async function run(mode, inputs, flags) {
|
|
|
58
91
|
source += String(chunk);
|
|
59
92
|
set.files[name] = source;
|
|
60
93
|
set.selected = [name];
|
|
94
|
+
// The name can lead into a folder that discovery did not visit.
|
|
95
|
+
named = await vaultFor(set, name);
|
|
61
96
|
}
|
|
97
|
+
// Links into a directory that could not be read are reported as unchecked, not as missing.
|
|
98
|
+
const unreadable = set.skipped.flatMap((item) => (item.type === "directory" ? [item.path] : []));
|
|
62
99
|
const reports = [];
|
|
63
100
|
const changes = [];
|
|
101
|
+
// Selected files that the system refused to let the tool read.
|
|
102
|
+
const unread = [];
|
|
103
|
+
// The vault that the root is a folder of, if it is one: links may lead out of sight.
|
|
104
|
+
const enclosing = set.vaults.find((item) => item.rootInVault);
|
|
64
105
|
// One index per active dialect; build lazily for mixed documentation workspaces.
|
|
65
106
|
const indexes = new Map();
|
|
107
|
+
// The same index with the line-break setting of each vault that has documents in it.
|
|
108
|
+
const views = new Map();
|
|
66
109
|
for (const name of set.selected) {
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
110
|
+
const vault = stdin ? named : vaultOf(set.vaults, name);
|
|
111
|
+
const assumed = assumedDialect(vault);
|
|
112
|
+
const config = resolveConfig(loaded.config, name, loaded.plugins, assumed);
|
|
113
|
+
let workspace = indexes.get(config.dialect);
|
|
114
|
+
if (!workspace)
|
|
115
|
+
indexes.set(config.dialect, (workspace = createWorkspace(set.files, {
|
|
70
116
|
dialect: config.dialect,
|
|
71
117
|
directories: set.directories,
|
|
72
|
-
|
|
73
|
-
|
|
118
|
+
unreadable,
|
|
119
|
+
// The vault at or above the root holds the whole workspace and needs no entry.
|
|
120
|
+
vaults: set.vaults.flatMap((item) => (item.path ? [item.path] : [])),
|
|
121
|
+
...(enclosing?.rootInVault ? { rootInVault: enclosing.rootInVault } : {}),
|
|
122
|
+
})));
|
|
123
|
+
// Whether a note may be reflowed is a setting of its own vault.
|
|
124
|
+
if (vault?.strictLineBreaks !== undefined) {
|
|
125
|
+
let ofDialect = views.get(config.dialect);
|
|
126
|
+
if (!ofDialect)
|
|
127
|
+
views.set(config.dialect, (ofDialect = new Map()));
|
|
128
|
+
const index = workspace;
|
|
129
|
+
workspace = ofDialect.get(vault);
|
|
130
|
+
if (!workspace)
|
|
131
|
+
ofDialect.set(vault, (workspace = { ...index, strictLineBreaks: vault.strictLineBreaks }));
|
|
132
|
+
}
|
|
74
133
|
const options = {
|
|
75
134
|
path: name,
|
|
76
135
|
config: loaded.config,
|
|
77
136
|
plugins: loaded.plugins,
|
|
78
|
-
workspace
|
|
137
|
+
workspace,
|
|
138
|
+
...(assumed ? { defaultDialect: assumed } : {}),
|
|
79
139
|
};
|
|
80
140
|
const value = set.files[name];
|
|
81
|
-
|
|
141
|
+
let source;
|
|
142
|
+
try {
|
|
143
|
+
source = typeof value === "function" ? value() : value;
|
|
144
|
+
}
|
|
145
|
+
catch (error) {
|
|
146
|
+
// Report the file and carry on with the others; any other error is a fault.
|
|
147
|
+
const code = refusal(error);
|
|
148
|
+
if (!code)
|
|
149
|
+
throw error;
|
|
150
|
+
unread.push(name);
|
|
151
|
+
reports.push({
|
|
152
|
+
path: name,
|
|
153
|
+
...(mode === "format" ? { changed: false } : {}),
|
|
154
|
+
diagnostics: [
|
|
155
|
+
{
|
|
156
|
+
start: 0,
|
|
157
|
+
message: `The file could not be read (${refusalText(code)}) and was not ${mode === "lint" ? "linted" : "formatted"}.`,
|
|
158
|
+
rule: "engine/unreadable-file",
|
|
159
|
+
severity: "error",
|
|
160
|
+
line: 1,
|
|
161
|
+
column: 1,
|
|
162
|
+
},
|
|
163
|
+
],
|
|
164
|
+
});
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
82
167
|
if (source === null)
|
|
83
168
|
continue;
|
|
84
169
|
if (mode === "lint")
|
|
@@ -95,7 +180,12 @@ async function run(mode, inputs, flags) {
|
|
|
95
180
|
changes.push({ file: name, before: source, after: result.output });
|
|
96
181
|
}
|
|
97
182
|
}
|
|
98
|
-
|
|
183
|
+
// A file that could not be formatted safely, or not be read at all, is a failure
|
|
184
|
+
// to operate: nothing of the batch is written, and the run ends with status 2.
|
|
185
|
+
const diagnostics = reports.flatMap((report) => report.diagnostics);
|
|
186
|
+
const count = (severity) => diagnostics.filter((item) => item.severity === severity).length;
|
|
187
|
+
const unsafe = unread.length > 0 ||
|
|
188
|
+
reports.some((report) => report.diagnostics.some((item) => item.rule === "engine/unsafe-format"));
|
|
99
189
|
if (flags.write && !unsafe)
|
|
100
190
|
for (const change of changes)
|
|
101
191
|
await writeAtomic(path.join(set.root, change.file), change.before, change.after);
|
|
@@ -105,8 +195,12 @@ async function run(mode, inputs, flags) {
|
|
|
105
195
|
mode,
|
|
106
196
|
files: reports,
|
|
107
197
|
written: flags.write === true && !unsafe,
|
|
198
|
+
...(set.skipped.length ? { skipped: set.skipped } : {}),
|
|
108
199
|
}, null, 2) + "\n");
|
|
109
200
|
else {
|
|
201
|
+
// Warnings about the run, not about a document: they do not change the exit status.
|
|
202
|
+
for (const item of set.skipped)
|
|
203
|
+
process.stderr.write(`mdtools: warning: ${item.message}\n`);
|
|
110
204
|
if (stdin && mode === "format" && !flags.check && !flags.diff)
|
|
111
205
|
process.stdout.write(reports[0]?.output ?? "");
|
|
112
206
|
else if (mode === "format" && !flags.check && !flags.write)
|
|
@@ -115,13 +209,16 @@ async function run(mode, inputs, flags) {
|
|
|
115
209
|
for (const report of reports)
|
|
116
210
|
for (const item of report.diagnostics)
|
|
117
211
|
process.stderr.write(`${report.path}:${item.line}:${item.column}: ${item.severity} ${item.rule}: ${item.message}\n`);
|
|
118
|
-
if (!stdin)
|
|
119
|
-
|
|
212
|
+
if (!stdin) {
|
|
213
|
+
// Lint changes nothing, so its summary counts what it reported instead.
|
|
214
|
+
const outcome = mode === "lint"
|
|
215
|
+
? `${count("error")} error(s), ${count("warn")} warning(s)`
|
|
216
|
+
: `${changes.length} ${flags.write && !unsafe ? "written" : "would change"}`;
|
|
217
|
+
process.stderr.write(`${set.selected.length - unread.length} file(s) ${mode === "lint" ? "linted" : "processed"}; ${outcome}${unread.length ? `; ${unread.length} could not be read` : ""}.\n`);
|
|
218
|
+
}
|
|
120
219
|
}
|
|
121
|
-
const
|
|
122
|
-
const
|
|
123
|
-
const tooManyWarnings = flags.maxWarnings !== undefined &&
|
|
124
|
-
diagnostics.filter((item) => item.severity === "warn").length > flags.maxWarnings;
|
|
220
|
+
const errors = count("error") > 0;
|
|
221
|
+
const tooManyWarnings = flags.maxWarnings !== undefined && count("warn") > flags.maxWarnings;
|
|
125
222
|
process.exitCode = unsafe
|
|
126
223
|
? 2
|
|
127
224
|
: errors || tooManyWarnings || (flags.check && changes.length > 0)
|
|
@@ -138,14 +235,20 @@ common(program.command("config").description("Inspect effective configuration"))
|
|
|
138
235
|
.command("explain <file>")
|
|
139
236
|
.action(async (file, _flags, command) => {
|
|
140
237
|
const flags = command.parent.opts();
|
|
238
|
+
await checkRoot(flags.root);
|
|
141
239
|
const loaded = await loadConfig(path.resolve(flags.root ?? "."), flags.config);
|
|
142
240
|
if (flags.dialect)
|
|
143
241
|
loaded.config.dialect = flags.dialect;
|
|
144
242
|
const root = path.resolve(flags.root ?? loaded.root);
|
|
243
|
+
const name = path.relative(root, path.resolve(file)).split(path.sep).join("/");
|
|
244
|
+
// The file need not exist. It is located through the root's real path, as
|
|
245
|
+
// discovery locates it, so that a root reached through a link is searched
|
|
246
|
+
// upward from where it really is.
|
|
247
|
+
const actual = await realpath(root).catch(() => root);
|
|
145
248
|
process.stdout.write(JSON.stringify({
|
|
146
249
|
root,
|
|
147
250
|
file: loaded.file ?? null,
|
|
148
|
-
effective: resolveConfig(loaded.config,
|
|
251
|
+
effective: resolveConfig(loaded.config, name, loaded.plugins, assumedDialect(await enclosingVault(path.join(actual, name)))),
|
|
149
252
|
}, null, 2) + "\n");
|
|
150
253
|
});
|
|
151
254
|
program
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +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 { excludeSelection } from \"../workspace/selection.js\";\nimport { createWorkspace } from \"../workspace/index.js\";\nimport type { Diagnostic, Dialect, DialectName, ProcessOptions } from \"../core/types.js\";\n\ninterface Flags {\n config?: string;\n root?: string;\n dialect?: DialectName;\n json?: boolean;\n check?: boolean;\n diff?: boolean;\n write?: boolean;\n stdinFilepath?: string;\n maxWarnings?: number;\n exclude?: string[];\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(\n \"--dialect <dialect>\",\n \"commonmark, github, forgejo (alias: codeberg), gitea, or obsidian\",\n )\n .option(\n \"--exclude <path>\",\n \"Exclude an exact file or directory (repeatable)\",\n (value: string, previous: string[]) => [...previous, value],\n [],\n )\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 if (inputs.includes(\"-\") && flags.exclude?.length)\n throw new Error(\"--exclude cannot be combined with stdin.\");\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, resolved.resolve);\n set.selected = await excludeSelection(set.root, set.selected, flags.exclude ?? []);\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 directories: set.directories,\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 value = set.files[name]!;\n const source = typeof value === \"function\" ? value() : value;\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"]}
|
|
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 { access, constants, readFile, realpath, stat } 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, vaultFor, writeAtomic } from \"../workspace/files.js\";\nimport { enclosingVault, vaultOf } from \"../workspace/vault.js\";\nimport { excludeSelection } from \"../workspace/selection.js\";\nimport { createWorkspace } from \"../workspace/index.js\";\nimport { pathError, refusal, refusalText } from \"../workspace/access.js\";\nimport type { Diagnostic, Dialect, DialectName, ProcessOptions, Workspace } from \"../core/types.js\";\nimport type { Vault } from \"../workspace/vault.js\";\n\ninterface Flags {\n config?: string;\n root?: string;\n dialect?: DialectName;\n json?: boolean;\n check?: boolean;\n diff?: boolean;\n write?: boolean;\n stdinFilepath?: string;\n maxWarnings?: number;\n exclude?: string[];\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(\n \"--dialect <dialect>\",\n \"commonmark, github, forgejo (alias: codeberg), gitea, or obsidian (when none is named: github, or obsidian inside a vault)\",\n )\n .option(\n \"--exclude <path>\",\n \"Exclude an exact file or directory (repeatable)\",\n (value: string, previous: string[]) => [...previous, value],\n [],\n )\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}\n/**\n * The dialect to assume for a document when nothing names one, or undefined for\n * the library's own default. An Obsidian vault shows a single line break as a\n * line break and holds wikilinks, both of which any other dialect lets reflow\n * change, so a document that a vault holds is read as a note.\n */\nfunction assumedDialect(vault: Vault | string | undefined): Dialect | undefined {\n return vault ? \"obsidian\" : undefined;\n}\n/**\n * Stops when the folder given as `--root` cannot serve as a workspace root.\n * Without this check the first thing to fail would be the search for a\n * configuration file in it, with a message about a file the user never named.\n */\nasync function checkRoot(given: string | undefined): Promise<void> {\n if (given === undefined) return;\n const root = path.resolve(given);\n try {\n if (!(await stat(root)).isDirectory())\n throw new Error(`Workspace root is not a directory: ${root}`);\n await access(root, constants.R_OK | constants.X_OK);\n } catch (error) {\n throw pathError(\"Workspace root\", root, error);\n }\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 if (inputs.includes(\"-\") && flags.exclude?.length)\n throw new Error(\"--exclude cannot be combined with stdin.\");\n await checkRoot(flags.root);\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 // Which files to leave out does not depend on a document's 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, resolved.resolve);\n set.selected = await excludeSelection(set.root, set.selected, flags.exclude ?? []);\n /** The vault of the document read from stdin, found by a search of its own. */\n let named: Vault | undefined;\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 // The name can lead into a folder that discovery did not visit.\n named = await vaultFor(set, name);\n }\n // Links into a directory that could not be read are reported as unchecked, not as missing.\n const unreadable = set.skipped.flatMap((item) => (item.type === \"directory\" ? [item.path] : []));\n const reports: Report[] = [];\n const changes: { file: string; before: string; after: string }[] = [];\n // Selected files that the system refused to let the tool read.\n const unread: string[] = [];\n // The vault that the root is a folder of, if it is one: links may lead out of sight.\n const enclosing = set.vaults.find((item) => item.rootInVault);\n // One index per active dialect; build lazily for mixed documentation workspaces.\n const indexes = new Map<Dialect, Workspace>();\n // The same index with the line-break setting of each vault that has documents in it.\n const views = new Map<Dialect, Map<Vault, Workspace>>();\n for (const name of set.selected) {\n const vault = stdin ? named : vaultOf(set.vaults, name);\n const assumed = assumedDialect(vault);\n const config = resolveConfig(loaded.config, name, loaded.plugins, assumed);\n let workspace = indexes.get(config.dialect);\n if (!workspace)\n indexes.set(\n config.dialect,\n (workspace = createWorkspace(set.files, {\n dialect: config.dialect,\n directories: set.directories,\n unreadable,\n // The vault at or above the root holds the whole workspace and needs no entry.\n vaults: set.vaults.flatMap((item) => (item.path ? [item.path] : [])),\n ...(enclosing?.rootInVault ? { rootInVault: enclosing.rootInVault } : {}),\n })),\n );\n // Whether a note may be reflowed is a setting of its own vault.\n if (vault?.strictLineBreaks !== undefined) {\n let ofDialect = views.get(config.dialect);\n if (!ofDialect) views.set(config.dialect, (ofDialect = new Map()));\n const index = workspace;\n workspace = ofDialect.get(vault);\n if (!workspace)\n ofDialect.set(vault, (workspace = { ...index, strictLineBreaks: vault.strictLineBreaks }));\n }\n const options: ProcessOptions = {\n path: name,\n config: loaded.config,\n plugins: loaded.plugins,\n workspace,\n ...(assumed ? { defaultDialect: assumed } : {}),\n };\n const value = set.files[name]!;\n let source: string | null;\n try {\n source = typeof value === \"function\" ? value() : value;\n } catch (error) {\n // Report the file and carry on with the others; any other error is a fault.\n const code = refusal(error);\n if (!code) throw error;\n unread.push(name);\n reports.push({\n path: name,\n ...(mode === \"format\" ? { changed: false } : {}),\n diagnostics: [\n {\n start: 0,\n message: `The file could not be read (${refusalText(code)}) and was not ${mode === \"lint\" ? \"linted\" : \"formatted\"}.`,\n rule: \"engine/unreadable-file\",\n severity: \"error\",\n line: 1,\n column: 1,\n },\n ],\n });\n continue;\n }\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 // A file that could not be formatted safely, or not be read at all, is a failure\n // to operate: nothing of the batch is written, and the run ends with status 2.\n const diagnostics = reports.flatMap((report) => report.diagnostics);\n const count = (severity: Diagnostic[\"severity\"]) =>\n diagnostics.filter((item) => item.severity === severity).length;\n const unsafe =\n unread.length > 0 ||\n 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 ...(set.skipped.length ? { skipped: set.skipped } : {}),\n },\n null,\n 2,\n ) + \"\\n\",\n );\n else {\n // Warnings about the run, not about a document: they do not change the exit status.\n for (const item of set.skipped) process.stderr.write(`mdtools: warning: ${item.message}\\n`);\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 // Lint changes nothing, so its summary counts what it reported instead.\n const outcome =\n mode === \"lint\"\n ? `${count(\"error\")} error(s), ${count(\"warn\")} warning(s)`\n : `${changes.length} ${flags.write && !unsafe ? \"written\" : \"would change\"}`;\n process.stderr.write(\n `${set.selected.length - unread.length} file(s) ${mode === \"lint\" ? \"linted\" : \"processed\"}; ${outcome}${unread.length ? `; ${unread.length} could not be read` : \"\"}.\\n`,\n );\n }\n }\n const errors = count(\"error\") > 0;\n const tooManyWarnings = flags.maxWarnings !== undefined && count(\"warn\") > 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 await checkRoot(flags.root);\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 const name = path.relative(root, path.resolve(file)).split(path.sep).join(\"/\");\n // The file need not exist. It is located through the root's real path, as\n // discovery locates it, so that a root reached through a link is searched\n // upward from where it really is.\n const actual = await realpath(root).catch(() => root);\n process.stdout.write(\n JSON.stringify(\n {\n root,\n file: loaded.file ?? null,\n effective: resolveConfig(\n loaded.config,\n name,\n loaded.plugins,\n assumedDialect(await enclosingVault(path.join(actual, name))),\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"]}
|
package/dist/config/load.d.ts
CHANGED
|
@@ -5,5 +5,12 @@ export interface LoadedConfig {
|
|
|
5
5
|
root: string;
|
|
6
6
|
file?: string;
|
|
7
7
|
}
|
|
8
|
-
export declare function exists(file: string): Promise<boolean>;
|
|
9
8
|
export declare function loadConfig(start: string, explicit?: string): Promise<LoadedConfig>;
|
|
9
|
+
/**
|
|
10
|
+
* The conditions Node.js applies to the `exports` of a package it imports: its defaults, as of
|
|
11
|
+
* Node.js 22.12, changed by the options the process was started with. `--conditions` (`-C`)
|
|
12
|
+
* adds one and `--no-addons` takes `node-addons` away, on the command line or in
|
|
13
|
+
* `NODE_OPTIONS`. Node.js has no supported way to ask for the conditions in effect or to
|
|
14
|
+
* resolve from another file, so they are worked out here.
|
|
15
|
+
*/
|
|
16
|
+
export declare function importConditions(execArgv?: readonly string[], nodeOptions?: string | undefined): Set<string>;
|