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.
Files changed (77) hide show
  1. package/README.md +48 -20
  2. package/THIRD_PARTY_NOTICES +35 -0
  3. package/dist/cli/main.js +121 -18
  4. package/dist/cli/main.js.map +1 -1
  5. package/dist/config/load.d.ts +8 -1
  6. package/dist/config/load.js +162 -42
  7. package/dist/config/load.js.map +1 -1
  8. package/dist/config/resolve.d.ts +16 -1
  9. package/dist/config/resolve.js +29 -4
  10. package/dist/config/resolve.js.map +1 -1
  11. package/dist/core/engine.js +4 -2
  12. package/dist/core/engine.js.map +1 -1
  13. package/dist/core/types.d.ts +46 -1
  14. package/dist/core/types.js.map +1 -1
  15. package/dist/rules/links.js +51 -10
  16. package/dist/rules/links.js.map +1 -1
  17. package/dist/syntax/math.d.ts +36 -0
  18. package/dist/syntax/math.js +330 -0
  19. package/dist/syntax/math.js.map +1 -0
  20. package/dist/syntax/parse.js +13 -2
  21. package/dist/syntax/parse.js.map +1 -1
  22. package/dist/workspace/access.d.ts +30 -0
  23. package/dist/workspace/access.js +52 -0
  24. package/dist/workspace/access.js.map +1 -0
  25. package/dist/workspace/files.d.ts +27 -1
  26. package/dist/workspace/files.js +135 -21
  27. package/dist/workspace/files.js.map +1 -1
  28. package/dist/workspace/index.d.ts +28 -0
  29. package/dist/workspace/index.js +464 -196
  30. package/dist/workspace/index.js.map +1 -1
  31. package/dist/workspace/selection.js +13 -5
  32. package/dist/workspace/selection.js.map +1 -1
  33. package/dist/workspace/vault.d.ts +51 -0
  34. package/dist/workspace/vault.js +108 -0
  35. package/dist/workspace/vault.js.map +1 -0
  36. package/docs/benchmarks.md +299 -0
  37. package/docs/cli.md +32 -5
  38. package/docs/configuration.md +201 -22
  39. package/docs/contributing.md +37 -11
  40. package/docs/design.md +92 -24
  41. package/docs/examples.md +555 -0
  42. package/docs/index.md +2 -0
  43. package/docs/obsidian.md +71 -8
  44. package/docs/plugins.md +195 -20
  45. package/docs/quick-start.md +16 -7
  46. package/docs/releases/0.2.0-rc.2.md +225 -0
  47. package/docs/releases.md +9 -8
  48. package/docs/testing.md +93 -0
  49. package/examples/README.md +79 -0
  50. package/examples/automation/github-actions.yml +23 -0
  51. package/examples/automation/summarize-report.mjs +34 -0
  52. package/examples/library/format-string.mjs +21 -0
  53. package/examples/library/lint-folder.mjs +43 -0
  54. package/examples/plugin/mdtools.config.jsonc +9 -0
  55. package/examples/plugin/notes/release-checklist.md +9 -0
  56. package/examples/plugin/rules.mjs +94 -0
  57. package/examples/workspaces/obsidian-vault/after/.obsidian/app.json +3 -0
  58. package/examples/workspaces/obsidian-vault/after/Garden/Tomatoes.md +15 -0
  59. package/examples/workspaces/obsidian-vault/after/Garden/Watering schedule.md +11 -0
  60. package/examples/workspaces/obsidian-vault/after/Home.md +16 -0
  61. package/examples/workspaces/obsidian-vault/after/Templates/Plant.md +6 -0
  62. package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +10 -0
  63. package/examples/workspaces/obsidian-vault/before/.obsidian/app.json +3 -0
  64. package/examples/workspaces/obsidian-vault/before/Garden/Tomatoes.md +13 -0
  65. package/examples/workspaces/obsidian-vault/before/Garden/Watering schedule.md +9 -0
  66. package/examples/workspaces/obsidian-vault/before/Home.md +13 -0
  67. package/examples/workspaces/obsidian-vault/before/Templates/Plant.md +6 -0
  68. package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +10 -0
  69. package/examples/workspaces/repository-docs/after/README.md +25 -0
  70. package/examples/workspaces/repository-docs/after/docs/guide.md +19 -0
  71. package/examples/workspaces/repository-docs/after/docs/reference.md +14 -0
  72. package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +10 -0
  73. package/examples/workspaces/repository-docs/before/README.md +21 -0
  74. package/examples/workspaces/repository-docs/before/docs/guide.md +16 -0
  75. package/examples/workspaces/repository-docs/before/docs/reference.md +13 -0
  76. package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +10 -0
  77. 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 read issue and PR descriptions to check the project's
18
- direction, read Markdown documents that agents have processed with the tool, and
19
- assign agents to tasks. Human and agent-authored issues and pull requests are
20
- welcome. Reproducible examples, automated checks, and regression tests guide
21
- maintenance.
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.1
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/` are excluded
109
- from documentation passes.
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
- regression/property/CLI tests. GitHub Actions runs full checks on Linux with
117
- Node.js 24, plus build and test coverage on Linux with Node.js 22 and Windows
118
- with Node.js 24. All three jobs verify the installed package. Changes to
119
- formatting need tests for expected output, idempotence, and preserved semantics;
120
- defects should add a regression example. Keep fixes scoped and describe
121
- remaining limitations in the PR.
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 the limits of an assigned
126
- agent's authority. `test:package` packs and installs a temporary consumer
127
- project, then exercises the installed CLI and library; it can require access to
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 config = resolveConfig(loaded.config, name, loaded.plugins);
68
- if (!indexes.has(config.dialect))
69
- indexes.set(config.dialect, createWorkspace(set.files, {
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
- ...(set.strictLineBreaks !== undefined ? { strictLineBreaks: set.strictLineBreaks } : {}),
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: indexes.get(config.dialect),
137
+ workspace,
138
+ ...(assumed ? { defaultDialect: assumed } : {}),
79
139
  };
80
140
  const value = set.files[name];
81
- const source = typeof value === "function" ? value() : value;
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
- const unsafe = reports.some((report) => report.diagnostics.some((item) => item.rule === "engine/unsafe-format"));
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
- process.stderr.write(`${set.selected.length} file(s) ${mode === "lint" ? "linted" : "processed"}; ${changes.length} ${flags.write && !unsafe ? "written" : "would change"}.\n`);
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 diagnostics = reports.flatMap((report) => report.diagnostics);
122
- const errors = diagnostics.some((item) => item.severity === "error");
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, path.relative(root, path.resolve(file)).split(path.sep).join("/"), loaded.plugins),
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
@@ -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"]}
@@ -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>;