mdrefine 0.2.0-rc.2 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/CHANGELOG.md +577 -0
  2. package/CODE_OF_CONDUCT.md +30 -0
  3. package/CONTRIBUTING.md +189 -16
  4. package/README.md +100 -154
  5. package/SECURITY.md +15 -13
  6. package/config.schema.json +91 -0
  7. package/dist/cli/diff.d.ts +10 -0
  8. package/dist/cli/diff.js +247 -0
  9. package/dist/cli/diff.js.map +1 -0
  10. package/dist/cli/main.js +270 -150
  11. package/dist/cli/main.js.map +1 -1
  12. package/dist/config/load.js +67 -6
  13. package/dist/config/load.js.map +1 -1
  14. package/dist/config/problems.d.ts +7 -0
  15. package/dist/config/problems.js +208 -0
  16. package/dist/config/problems.js.map +1 -0
  17. package/dist/config/resolve.d.ts +42 -109
  18. package/dist/config/resolve.js +193 -52
  19. package/dist/config/resolve.js.map +1 -1
  20. package/dist/core/directives.d.ts +7 -0
  21. package/dist/core/directives.js +25 -13
  22. package/dist/core/directives.js.map +1 -1
  23. package/dist/core/engine.d.ts +42 -4
  24. package/dist/core/engine.js +518 -79
  25. package/dist/core/engine.js.map +1 -1
  26. package/dist/core/frozen.d.ts +6 -0
  27. package/dist/core/frozen.js +15 -0
  28. package/dist/core/frozen.js.map +1 -0
  29. package/dist/core/scope.d.ts +30 -0
  30. package/dist/core/scope.js +122 -0
  31. package/dist/core/scope.js.map +1 -0
  32. package/dist/core/types.d.ts +202 -23
  33. package/dist/core/types.js.map +1 -1
  34. package/dist/index.d.ts +2 -2
  35. package/dist/index.js.map +1 -1
  36. package/dist/node.d.ts +3 -0
  37. package/dist/node.js +4 -0
  38. package/dist/node.js.map +1 -0
  39. package/dist/rules/blocks.d.ts +34 -0
  40. package/dist/rules/blocks.js +550 -0
  41. package/dist/rules/blocks.js.map +1 -0
  42. package/dist/rules/content.d.ts +2 -0
  43. package/dist/rules/content.js +85 -0
  44. package/dist/rules/content.js.map +1 -0
  45. package/dist/rules/dialects.js +10 -6
  46. package/dist/rules/dialects.js.map +1 -1
  47. package/dist/rules/links.js +302 -6
  48. package/dist/rules/links.js.map +1 -1
  49. package/dist/rules/lists.d.ts +2 -0
  50. package/dist/rules/lists.js +924 -0
  51. package/dist/rules/lists.js.map +1 -0
  52. package/dist/rules/structure.js +127 -1
  53. package/dist/rules/structure.js.map +1 -1
  54. package/dist/rules/style.d.ts +8 -2
  55. package/dist/rules/style.js +1137 -165
  56. package/dist/rules/style.js.map +1 -1
  57. package/dist/syntax/block-identifier.d.ts +18 -0
  58. package/dist/syntax/block-identifier.js +24 -0
  59. package/dist/syntax/block-identifier.js.map +1 -0
  60. package/dist/syntax/obsidian.d.ts +29 -11
  61. package/dist/syntax/obsidian.js +9 -10
  62. package/dist/syntax/obsidian.js.map +1 -1
  63. package/dist/syntax/parse.d.ts +87 -3
  64. package/dist/syntax/parse.js +247 -19
  65. package/dist/syntax/parse.js.map +1 -1
  66. package/dist/workspace/access.d.ts +12 -3
  67. package/dist/workspace/access.js +33 -5
  68. package/dist/workspace/access.js.map +1 -1
  69. package/dist/workspace/discovery.d.ts +85 -0
  70. package/dist/workspace/discovery.js +155 -0
  71. package/dist/workspace/discovery.js.map +1 -0
  72. package/dist/workspace/encoding.d.ts +24 -0
  73. package/dist/workspace/encoding.js +113 -0
  74. package/dist/workspace/encoding.js.map +1 -0
  75. package/dist/workspace/files.d.ts +92 -6
  76. package/dist/workspace/files.js +715 -59
  77. package/dist/workspace/files.js.map +1 -1
  78. package/dist/workspace/git.d.ts +54 -0
  79. package/dist/workspace/git.js +198 -0
  80. package/dist/workspace/git.js.map +1 -0
  81. package/dist/workspace/index.d.ts +58 -3
  82. package/dist/workspace/index.js +136 -37
  83. package/dist/workspace/index.js.map +1 -1
  84. package/dist/workspace/selection.d.ts +2 -2
  85. package/dist/workspace/selection.js +3 -3
  86. package/dist/workspace/selection.js.map +1 -1
  87. package/dist/workspace/slug.js +8 -1
  88. package/dist/workspace/slug.js.map +1 -1
  89. package/dist/workspace/vault.js +3 -1
  90. package/dist/workspace/vault.js.map +1 -1
  91. package/docs/api.md +239 -0
  92. package/docs/benchmarks.md +140 -200
  93. package/docs/changelog.md +1 -0
  94. package/docs/cli.md +281 -89
  95. package/docs/configuration.md +193 -480
  96. package/docs/design.md +163 -284
  97. package/docs/dialects.md +169 -0
  98. package/docs/examples.md +151 -109
  99. package/docs/index.md +21 -37
  100. package/docs/integrations.md +144 -0
  101. package/docs/obsidian.md +91 -115
  102. package/docs/plugins.md +147 -212
  103. package/docs/quick-start.md +68 -74
  104. package/docs/rules.md +1237 -0
  105. package/docs/versions.md +56 -0
  106. package/examples/README.md +17 -14
  107. package/examples/automation/summarize-report.mjs +5 -2
  108. package/examples/library/lint-folder.mjs +5 -4
  109. package/examples/library/lint-workspace.mjs +32 -0
  110. package/examples/plugin/notes/release-checklist.md +2 -2
  111. package/examples/plugin/rules.mjs +17 -23
  112. package/examples/workspaces/obsidian-vault/after/mdtools.config.jsonc +2 -2
  113. package/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc +2 -2
  114. package/examples/workspaces/repository-docs/after/README.md +2 -2
  115. package/examples/workspaces/repository-docs/after/mdtools.config.jsonc +0 -2
  116. package/examples/workspaces/repository-docs/before/README.md +2 -2
  117. package/examples/workspaces/repository-docs/before/docs/guide.md +7 -7
  118. package/examples/workspaces/repository-docs/before/docs/reference.md +3 -1
  119. package/examples/workspaces/repository-docs/before/mdtools.config.jsonc +0 -2
  120. package/package.json +38 -12
  121. package/docs/contributing.md +0 -218
  122. package/docs/releases/0.1.0-beta.1.md +0 -39
  123. package/docs/releases/0.1.0-beta.2.md +0 -83
  124. package/docs/releases/0.1.0-beta.3.md +0 -61
  125. package/docs/releases/0.1.0-beta.4.md +0 -50
  126. package/docs/releases/0.1.0-rc.1.md +0 -99
  127. package/docs/releases/0.2.0-rc.1.md +0 -141
  128. package/docs/releases/0.2.0-rc.2.md +0 -225
  129. package/docs/releases.md +0 -144
  130. package/docs/testing.md +0 -93
package/docs/examples.md CHANGED
@@ -2,20 +2,19 @@
2
2
 
3
3
  The repository's
4
4
  [`examples/`](https://github.com/viell-dev/slop-markdown-tools/tree/main/examples)
5
- folder holds working examples, and the npm package ships the same files under
6
- `node_modules/mdrefine/examples/`. The test suite compares every file and every
7
- output shown on this page with what the tool produces, so the page cannot drift
8
- from the tool's behavior.
9
-
10
- Commands are written for a
11
- [built checkout](quick-start.md#install-from-the-repository) and run from its
12
- root. With an installed package, use `npx --no-install mdtools` in place of
5
+ directory holds working examples, and the package ships the same files under
6
+ `node_modules/mdrefine/examples/`. The tests compare every file and output on
7
+ this page with the tool.
8
+
9
+ The commands run from the root of a [built
10
+ checkout](https://github.com/viell-dev/slop-markdown-tools/blob/main/CONTRIBUTING.md#set-up).
11
+ With the installed package, use `npx mdtools` in place of
13
12
  `node dist/cli/main.js`.
14
13
 
15
14
  ## Start from a configuration
16
15
 
17
- Four files hold a starting configuration, one for each hosting profile. Copy one
18
- to the root of your documents as `mdtools.config.jsonc`:
16
+ Copy one of four starting configurations to the root of your documents as
17
+ `mdtools.config.jsonc`:
19
18
 
20
19
  - [`examples/github.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/github.jsonc)
21
20
  for repositories rendered by GitHub.
@@ -24,17 +23,16 @@ to the root of your documents as `mdtools.config.jsonc`:
24
23
  - [`examples/gitea.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/gitea.jsonc)
25
24
  for Gitea instances.
26
25
  - [`examples/obsidian.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/obsidian.jsonc)
27
- for Obsidian vaults, with links written as vault-root paths.
26
+ for Obsidian vaults, with links written as vault-relative paths.
28
27
 
29
- Each wraps prose at 80 columns and chooses a link style.
30
- [Configuration and rules](configuration.md) explains the options.
28
+ Each wraps prose at 80 columns and chooses a link style; see
29
+ [configuration](configuration.md).
31
30
 
32
31
  ## Format a repository's documentation
33
32
 
34
33
  `examples/workspaces/repository-docs/` holds a README and two guides twice:
35
34
  `before/` as someone might write them, and `after/` exactly as `format --write`
36
- leaves them. Its configuration selects the GitHub profile and enables two rules
37
- that no preset turns on:
35
+ leaves them. The configuration adds a rule that no preset enables:
38
36
 
39
37
  [`examples/workspaces/repository-docs/before/mdtools.config.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/repository-docs/before/mdtools.config.jsonc):
40
38
 
@@ -43,8 +41,6 @@ that no preset turns on:
43
41
  // Documentation for a repository hosted on GitHub.
44
42
  "extends": ["recommended", "github"],
45
43
  "rules": {
46
- // Not part of a preset: turn underlined headings into "#" headings.
47
- "style/heading": "warn",
48
44
  // Write local links as ./relative paths, which editors can follow too.
49
45
  "links/path": ["warn", { "style": "relative", "leadingDot": true }],
50
46
  },
@@ -55,7 +51,7 @@ The README before formatting:
55
51
 
56
52
  [`examples/workspaces/repository-docs/before/README.md`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/repository-docs/before/README.md):
57
53
 
58
- ````md
54
+ ```md
59
55
  # Weather Station
60
56
 
61
57
  Weather Station collects *temperature*, *humidity*, and *pressure* readings from a sensor board and publishes them as a small web page that updates every minute.
@@ -75,12 +71,12 @@ Follow the [setup guide](docs/guide.md) first, then read the [sensor reference](
75
71
  | Humidity | % | Yes |
76
72
  | Pressure | hPa | Since 1.2 |
77
73
 
78
- - [X] Publish readings every minute
79
- - [ ] Keep a week of history
80
- ````
74
+ * [X] Publish readings every minute
75
+ * [ ] Keep a week of history
76
+ ```
81
77
 
82
- Preview the changes with `--diff`. To apply them with `--write`, copy `before/`
83
- somewhere else first so that the example stays as it is.
78
+ Preview the changes with `--diff`. To try `--write`, copy `before/` elsewhere
79
+ first.
84
80
 
85
81
  ```sh
86
82
  node dist/cli/main.js format --root examples/workspaces/repository-docs/before --diff
@@ -90,7 +86,7 @@ The same README afterwards:
90
86
 
91
87
  [`examples/workspaces/repository-docs/after/README.md`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/repository-docs/after/README.md):
92
88
 
93
- ````md
89
+ ```md
94
90
  # Weather Station
95
91
 
96
92
  Weather Station collects _temperature_, _humidity_, and _pressure_ readings from
@@ -101,8 +97,8 @@ a sensor board and publishes them as a small web page that updates every minute.
101
97
 
102
98
  ## Usage
103
99
 
104
- Follow the [setup guide](./docs/guide.md) first, then read the
105
- [sensor reference](./docs/reference.md#pressure) to interpret the readings. The
100
+ Follow the [setup guide](./docs/guide.md) first, then read the [sensor
101
+ reference](./docs/reference.md#pressure) to interpret the readings. The
106
102
  [calibration notes](docs/calibration.md) explain how to correct a drifting
107
103
  sensor.
108
104
 
@@ -116,7 +112,7 @@ sensor.
116
112
 
117
113
  - [x] Publish readings every minute
118
114
  - [ ] Keep a week of history
119
- ````
115
+ ```
120
116
 
121
117
  Each change comes from one rule of the configuration:
122
118
 
@@ -124,41 +120,48 @@ Each change comes from one rule of the configuration:
124
120
  keep their lines.
125
121
  - `style/emphasis` and `style/strong` chose `_` and `**`.
126
122
  - `github/alert-marker` and `github/task-marker` normalized `[!note]` and `[X]`.
123
+ - `style/list-marker` wrote the task list with `-`.
127
124
  - `style/table` aligned the table.
128
125
  - `links/path` put `./` in front of the links it could resolve.
129
126
  - In `docs/guide.md`, `style/heading` turned an underlined heading into a `#`
130
- heading, and `docs/reference.md` gained its missing final newline.
131
-
132
- One thing did not change: the link to `docs/calibration.md`, a file that does
133
- not exist. Formatting never guesses a target, so linting the formatted tree
134
- still reports it, and exits with status `1`:
127
+ heading, `style/ordered-list` wrote `1)` as `1.`, `style/list-indent` left one
128
+ space after each number and moved the code block in the second step with the
129
+ step's text, and `style/code-fence` wrote the block's `~~~` fence with
130
+ backticks.
131
+ - In `docs/reference.md`, `style/heading` removed the closing `##` of a heading,
132
+ `style/blank-lines` removed two repeated blank lines, and
133
+ `style/final-newline` added the missing final newline.
134
+
135
+ The link to `docs/calibration.md`, a file that does not exist, did not change.
136
+ Formatting never guesses a target, so `lint` still reports it and exits with
137
+ code `1`:
135
138
 
136
139
  ```sh
137
140
  node dist/cli/main.js lint --root examples/workspaces/repository-docs/after
138
141
  ```
139
142
 
140
143
  ```text
141
- README.md:13:1: error links/valid: Missing local target: docs/calibration.md.
144
+ examples/workspaces/repository-docs/after/README.md:13:1: error links/valid: Missing local target: docs/calibration.md.
142
145
  3 file(s) linted; 1 error(s), 0 warning(s).
143
146
  ```
144
147
 
145
148
  ## Format an Obsidian vault
146
149
 
147
150
  `examples/workspaces/obsidian-vault/` is a small vault, again as `before/` and
148
- `after/`. The configuration sits at the vault root, next to `.obsidian/`, whose
149
- `app.json` sets `"strictLineBreaks": true`; without that setting the tool
150
- [does not reflow prose](obsidian.md#enable-source-wrapping).
151
+ `after/`. The configuration sits at the vault's root, next to `.obsidian/`,
152
+ whose `app.json` sets `"strictLineBreaks": true`, which [wrapping
153
+ needs](obsidian.md#enable-wrapping).
151
154
 
152
155
  [`examples/workspaces/obsidian-vault/before/mdtools.config.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/obsidian-vault/before/mdtools.config.jsonc):
153
156
 
154
157
  ```jsonc
155
158
  {
156
- // An Obsidian vault; this file sits at the vault root, next to .obsidian/.
159
+ // An Obsidian vault; this file sits at the vault's root, next to .obsidian/.
157
160
  "extends": ["recommended", "obsidian"],
158
161
  // Templates contain placeholders, not prose. They stay valid link targets.
159
162
  "ignore": ["Templates/**"],
160
163
  "rules": {
161
- // Spell Markdown links as vault-root paths in angle brackets.
164
+ // Spell Markdown links as vault-relative paths in angle brackets.
162
165
  "links/path": ["warn", { "style": "root", "brackets": "angle" }],
163
166
  },
164
167
  }
@@ -168,7 +171,7 @@ The vault's home note before formatting:
168
171
 
169
172
  [`examples/workspaces/obsidian-vault/before/Home.md`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/obsidian-vault/before/Home.md):
170
173
 
171
- ````md
174
+ ```md
172
175
  # Home
173
176
 
174
177
  This vault tracks the kitchen garden. Start with [[Tomatoes]] or jump straight to the [[Watering schedule#Summer|summer watering plan]].
@@ -182,13 +185,13 @@ This vault tracks the kitchen garden. Start with [[Tomatoes]] or jump straight t
182
185
  - [ ] Order *more* seed trays
183
186
 
184
187
  ![[Tomatoes#^harvest]]
185
- ````
188
+ ```
186
189
 
187
190
  And afterwards:
188
191
 
189
192
  [`examples/workspaces/obsidian-vault/after/Home.md`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/workspaces/obsidian-vault/after/Home.md):
190
193
 
191
- ````md
194
+ ```md
192
195
  # Home
193
196
 
194
197
  This vault tracks the kitchen garden. Start with [[Tomatoes]] or jump straight
@@ -205,30 +208,28 @@ to the [[Garden/Watering schedule#Summer|summer watering plan]].
205
208
  - [ ] Order _more_ seed trays
206
209
 
207
210
  ![[Garden/Tomatoes#^harvest]]
208
- ````
211
+ ```
209
212
 
210
213
  What to notice:
211
214
 
212
215
  - The callout type is lowercase, its title stays on its line, and its body is
213
216
  wrapped inside the quote.
214
- - `[[Tomatoes]]` is unchanged, because rewriting an unaliased wikilink would
215
- change the text a reader sees. The aliased wikilink and the embed show no
216
- target, so both now carry the vault-root path.
217
- - The Markdown link to the template uses a vault-root path in angle brackets, as
218
- `links/path` is configured. The template itself is ignored: it is never linted
219
- or formatted, but links to it still resolve.
217
+ - `[[Tomatoes]]` is unchanged, because its target is its visible text. The
218
+ labeled wikilink and the embed do not show their target, so both now carry the
219
+ vault-relative path.
220
+ - The Markdown link to the template is vault-relative, in angle brackets. The
221
+ template itself is ignored: never linted or formatted, but still a link
222
+ target.
220
223
  - In `Garden/Tomatoes.md`, the paragraph ending in `^harvest` is not reflowed,
221
224
  because a block identifier belongs to its paragraph's last line.
222
- - `Garden/Watering schedule.md` links to a note named `Compost` that does not
223
- exist. As in the repository example, `lint` reports it before and after
224
- formatting.
225
+ - `Garden/Watering schedule.md` links to a note `Compost` that does not exist,
226
+ which `lint` reports before and after formatting.
225
227
 
226
228
  ## Write a plugin
227
229
 
228
- `examples/plugin/` adds two house rules to the built-in ones. A problem rule
229
- reports placeholder words and never edits. A style rule rewrites thematic breaks
230
- as `---`, but only where that cannot change what the document means.
231
- [Plugins and library API](plugins.md) describes the contract.
230
+ `examples/plugin/` adds two rules. A problem rule reports placeholder words. A
231
+ style rule writes one space between a heading's `#` marks and its text, and
232
+ leaves underlined headings alone. [Plugins](plugins.md) describes the contract.
232
233
 
233
234
  [`examples/plugin/rules.mjs`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/plugin/rules.mjs):
234
235
 
@@ -250,7 +251,7 @@ export default {
250
251
  name: "house",
251
252
  presets: {
252
253
  recommended: {
253
- rules: { "house/no-placeholder": "error", "house/thematic-break": "warn" },
254
+ rules: { "house/no-placeholder": "error", "house/heading-space": "warn" },
254
255
  },
255
256
  },
256
257
  rules: {
@@ -292,36 +293,30 @@ export default {
292
293
  return findings;
293
294
  },
294
295
  },
295
- "thematic-break": {
296
- description: "Write thematic breaks as three hyphens.",
296
+ "heading-space": {
297
+ description: "Write one space between a heading's # marks and its text.",
297
298
  kind: "style",
298
299
  phase: "block",
299
300
  check({ document }) {
300
301
  const findings = [];
301
- const blocks = document.tree.children;
302
- // In a document whose first line is the thematic break "---", a later
303
- // "---" would close it as front matter, so no break is rewritten there.
304
- const opensFrontMatter =
305
- blocks[0]?.type === "thematicBreak" && document.source.startsWith("---");
306
- for (const [index, node] of blocks.entries()) {
307
- if (node.type !== "thematicBreak") continue;
302
+ visit(document.tree, (node) => {
303
+ if (node.type !== "heading" || !node.children.length) return;
308
304
  const start = node.position.start.offset;
309
- const end = node.position.end.offset;
310
- if (document.source.slice(start, end) === "---") continue;
311
- // "---" directly below a paragraph would turn that paragraph into a
312
- // heading. Only propose the edit after a blank line, where it cannot.
313
- const previous = blocks[index - 1];
314
- const safe =
315
- !opensFrontMatter &&
316
- previous &&
317
- previous.position.end.line < node.position.start.line - 1;
305
+ const text = node.children[0].position.start.offset;
306
+ const marks = document.source.slice(start, text);
307
+ const wanted = `${"#".repeat(node.depth)} `;
308
+ // Only a "#" heading starts with its marks: an underlined heading
309
+ // starts with its text, and so the check below leaves it alone. The
310
+ // parser drops the blank space before a heading's text, so changing
311
+ // it cannot change what the heading says.
312
+ if (!/^#+[ \t]+$/.test(marks) || marks === wanted) return;
318
313
  findings.push({
319
314
  start,
320
- end,
321
- message: "Write this thematic break as ---.",
322
- ...(safe ? { edit: { start, end, text: "---" } } : {}),
315
+ end: text,
316
+ message: "Write one space after the heading's # marks.",
317
+ edit: { start, end: text, text: wanted },
323
318
  });
324
- }
319
+ });
325
320
  return findings;
326
321
  },
327
322
  },
@@ -329,8 +324,8 @@ export default {
329
324
  };
330
325
  ```
331
326
 
332
- The configuration loads the plugin, enables its preset, and passes options to
333
- one rule:
327
+ The configuration loads the plugin, enables its preset, and sets one rule's
328
+ options:
334
329
 
335
330
  [`examples/plugin/mdtools.config.jsonc`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/plugin/mdtools.config.jsonc):
336
331
 
@@ -350,32 +345,31 @@ A note that triggers both rules:
350
345
 
351
346
  [`examples/plugin/notes/release-checklist.md`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/plugin/notes/release-checklist.md):
352
347
 
353
- ````md
348
+ ```md
354
349
  # Release checklist
355
350
 
356
351
  Draft the announcement. TODO: add the download link.
357
352
 
358
353
  Searching the notes with `grep TODO` is not reported: code is not prose.
359
354
 
360
- ***
355
+ ## Before publishing
361
356
 
362
- Ask for a review before publishing.
363
- ````
357
+ Ask for a review.
358
+ ```
364
359
 
365
- Linting reports both, and does not report the `TODO` inside the code span:
360
+ Linting reports both, but not the `TODO` inside the code span:
366
361
 
367
362
  ```sh
368
363
  node dist/cli/main.js lint --root examples/plugin
369
364
  ```
370
365
 
371
366
  ```text
372
- notes/release-checklist.md:3:25: error house/no-placeholder: Replace the placeholder "TODO" before publishing.
373
- notes/release-checklist.md:7:1: warn house/thematic-break: Write this thematic break as ---.
367
+ examples/plugin/notes/release-checklist.md:3:25: error house/no-placeholder: Replace the placeholder "TODO" before publishing.
368
+ examples/plugin/notes/release-checklist.md:7:1: warn house/heading-space: Write one space after the heading's # marks.
374
369
  1 file(s) linted; 1 error(s), 1 warning(s).
375
370
  ```
376
371
 
377
- Formatting applies the style rule's edit and leaves the placeholder for a
378
- person:
372
+ Formatting applies the style rule's edit and leaves the placeholder to a person:
379
373
 
380
374
  ```sh
381
375
  node dist/cli/main.js format --root examples/plugin --diff
@@ -383,24 +377,24 @@ node dist/cli/main.js format --root examples/plugin --diff
383
377
 
384
378
  ```text
385
379
  ===================================================================
386
- --- a/notes/release-checklist.md
387
- +++ b/notes/release-checklist.md
380
+ --- a/examples/plugin/notes/release-checklist.md
381
+ +++ b/examples/plugin/notes/release-checklist.md
388
382
  @@ -3,7 +3,7 @@
389
383
  Draft the announcement. TODO: add the download link.
390
384
 
391
385
  Searching the notes with `grep TODO` is not reported: code is not prose.
392
386
 
393
- -***
394
- +---
387
+ -## Before publishing
388
+ +## Before publishing
395
389
 
396
- Ask for a review before publishing.
397
- notes/release-checklist.md:3:25: error house/no-placeholder: Replace the placeholder "TODO" before publishing.
390
+ Ask for a review.
391
+ examples/plugin/notes/release-checklist.md:3:25: error house/no-placeholder: Replace the placeholder "TODO" before publishing.
398
392
  1 file(s) processed; 1 would change.
399
393
  ```
400
394
 
401
395
  ## Call the library
402
396
 
403
- The library takes strings and returns strings; it never reads or writes files.
397
+ The [library](api.md) takes and returns strings; it never reads or writes files.
404
398
  The scripts import `mdrefine` by name, which works in a checkout and wherever
405
399
  the package is installed.
406
400
 
@@ -441,7 +435,7 @@ no final newline.
441
435
  changed: true; remaining diagnostics: 0
442
436
  ```
443
437
 
444
- To check links between documents, the host reads the files and passes them to
438
+ To check links between documents, the script reads the files and passes them to
445
439
  `createWorkspace()`:
446
440
 
447
441
  [`examples/library/lint-folder.mjs`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/library/lint-folder.mjs):
@@ -452,7 +446,8 @@ To check links between documents, the host reads the files and passes them to
452
446
  // The library never reads or writes files. The host reads them and hands them
453
447
  // to createWorkspace(), which is what lets rules check links between documents.
454
448
  // This script reads everything below the folder; a real host would skip folders
455
- // such as .git and node_modules, as the CLI does.
449
+ // such as .git and node_modules, as the CLI does. lint-workspace.mjs leaves that
450
+ // to discoverWorkspace() from "mdrefine/node".
456
451
  //
457
452
  // node examples/library/lint-folder.mjs [folder]
458
453
  import { readdir, readFile } from "node:fs/promises";
@@ -466,14 +461,14 @@ const folder = path.resolve(
466
461
  const config = { extends: ["recommended", "github"] };
467
462
 
468
463
  // Workspace keys are forward-slash paths relative to the folder. A Markdown
469
- // file maps to its text; any other file maps to null, so that links to it
470
- // still resolve.
464
+ // file, named .md or .markdown as the CLI finds them, maps to its text; any
465
+ // other file maps to null, so that links to it still resolve.
471
466
  const files = {};
472
467
  for (const entry of await readdir(folder, { recursive: true, withFileTypes: true })) {
473
468
  if (!entry.isFile()) continue;
474
469
  const absolute = path.join(entry.parentPath, entry.name);
475
470
  const name = path.relative(folder, absolute).split(path.sep).join("/");
476
- files[name] = /\.md$/i.test(name) ? await readFile(absolute, "utf8") : null;
471
+ files[name] = /\.(?:md|markdown)$/i.test(name) ? await readFile(absolute, "utf8") : null;
477
472
  }
478
473
  const workspace = createWorkspace(files, { dialect: "github" });
479
474
 
@@ -504,10 +499,60 @@ README.md:13:1 error links/valid: Missing local target: docs/calibration.md.
504
499
  1 error(s)
505
500
  ```
506
501
 
502
+ `mdrefine/node` finds and selects the files as the CLI does, and prepares each
503
+ document's options:
504
+
505
+ [`examples/library/lint-workspace.mjs`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/library/lint-workspace.mjs):
506
+
507
+ ```js
508
+ // Lint a workspace on disk through the library, choosing files as the CLI does.
509
+ //
510
+ // discoverWorkspace() from "mdrefine/node" finds the files, honors .gitignore
511
+ // files and the configuration's ignore patterns, leaves out nested
512
+ // repositories and what it may not read, and recognizes Obsidian vaults.
513
+ // options() then gives each document what the CLI passes to lint() and
514
+ // format(). Configuration files are not read: pass the configuration.
515
+ //
516
+ // node examples/library/lint-workspace.mjs [folder]
517
+ import path from "node:path";
518
+ import { fileURLToPath } from "node:url";
519
+ import { lint } from "mdrefine";
520
+ import { discoverWorkspace } from "mdrefine/node";
521
+
522
+ const root = path.resolve(
523
+ process.argv[2] ?? fileURLToPath(new URL("../workspaces/repository-docs/after", import.meta.url)),
524
+ );
525
+ const config = { extends: ["recommended", "github"] };
526
+
527
+ const discovery = await discoverWorkspace({ root, config });
528
+ for (const item of discovery.skipped) console.log(`skipped: ${item.message}`);
529
+ let errors = 0;
530
+ for (const name of discovery.selected) {
531
+ for (const item of lint(discovery.read(name), discovery.options(name))) {
532
+ if (item.severity === "error") errors += 1;
533
+ console.log(
534
+ `${name}:${item.line}:${item.column} ${item.severity} ${item.rule}: ${item.message}`,
535
+ );
536
+ }
537
+ }
538
+ console.log(`${errors} error(s) in ${discovery.selected.length} document(s)`);
539
+ process.exitCode = errors > 0 ? 1 : 0;
540
+ ```
541
+
542
+ ```sh
543
+ node examples/library/lint-workspace.mjs
544
+ ```
545
+
546
+ ```text
547
+ README.md:13:1 error links/valid: Missing local target: docs/calibration.md.
548
+ 1 error(s) in 3 document(s)
549
+ ```
550
+
507
551
  ## Automate checks
508
552
 
509
- A workflow for a repository that lists `mdrefine` in its `devDependencies` and
510
- has an `mdtools.config.jsonc` at its root:
553
+ A GitHub Actions workflow for a repository that lists `mdrefine` in its
554
+ `devDependencies`; [integrations](integrations.md#continuous-integration) covers
555
+ Forgejo:
511
556
 
512
557
  [`examples/automation/github-actions.yml`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/automation/github-actions.yml):
513
558
 
@@ -537,9 +582,9 @@ jobs:
537
582
  - run: npx --no-install mdtools format --check
538
583
  ```
539
584
 
540
- For scripts and agents, `--json` prints one report object instead of text.
585
+ The script
541
586
  [`summarize-report.mjs`](https://github.com/viell-dev/slop-markdown-tools/blob/main/examples/automation/summarize-report.mjs)
542
- reads that report and counts diagnostics per rule:
587
+ reads a `--json` report and counts diagnostics per rule:
543
588
 
544
589
  ```sh
545
590
  node dist/cli/main.js lint --root examples/plugin --json | node examples/automation/summarize-report.mjs
@@ -548,8 +593,5 @@ node dist/cli/main.js lint --root examples/plugin --json | node examples/automat
548
593
  ```text
549
594
  lint: 1 file(s), 2 diagnostic(s)
550
595
  1 error house/no-placeholder in 1 file(s)
551
- 1 warn house/thematic-break in 1 file(s)
596
+ 1 warn house/heading-space in 1 file(s)
552
597
  ```
553
-
554
- [CLI workflows](cli.md) covers file selection, exit codes, and stdin;
555
- [configuration and rules](configuration.md) lists every rule and option.
package/docs/index.md CHANGED
@@ -1,52 +1,36 @@
1
1
  ---
2
2
  layout: home
3
3
  hero:
4
- name: Markdown Tools
5
- text: Your Markdown. Your rules.
6
- tagline: Separate lint and format commands. Shared configuration. Built for CommonMark, GitHub, Forgejo, Gitea, and Obsidian.
4
+ name: mdrefine
5
+ text: Lint and format Markdown
6
+ tagline: One configuration for checks and formatting, for documents rendered by GitHub, Forgejo, Gitea, Obsidian, or any CommonMark renderer.
7
7
  actions:
8
8
  - theme: brand
9
- text: Quick start
9
+ text: Get started
10
10
  link: /quick-start
11
11
  - theme: alt
12
- text: Explore the rules
13
- link: /configuration
12
+ text: Rules
13
+ link: /rules
14
14
  features:
15
15
  - title: Check, preview, then write
16
- details: Lint without changing files, inspect formatting diffs, and write when ready. JSON diagnostics work with agents and automation.
17
- - title: Format what you configure
18
- details: Wrap at 80 columns by default. Choose link paths, delimiters, and dialect rules. Disabled rules preserve their syntax choices.
19
- - title: Extend the shared core
20
- details: Add JavaScript rules, presets, and syntax extensions. Use the same engine through the CLI or the TypeScript library.
16
+ details: Lint without changing files, read the diff, and write when ready. A JSON report and exit codes serve CI, scripts, and agents.
17
+ - title: Changes only what its rules target
18
+ details: Formatting edits the source in place, keeps everything else byte for byte, and refuses an edit that would change what a document means.
19
+ - title: Knows where it renders
20
+ details: Tables, math, wikilinks, callouts, and heading anchors are read as GitHub, Forgejo, Gitea, or Obsidian read them, and local links are checked the same way.
21
21
  ---
22
22
 
23
- ## A small, reviewable workflow
24
-
25
- From a [built checkout](quick-start.md), point the CLI at your documents:
23
+ mdrefine is meant to replace a general-purpose linter and formatter, such as
24
+ markdownlint and Prettier, for Markdown documents.
25
+ [Integrations](integrations.md#coming-from-prettier-or-markdownlint) shows which
26
+ of their checks have a rule here so far.
26
27
 
27
28
  ```sh
28
- node dist/cli/main.js lint --root /path/to/documents
29
- node dist/cli/main.js format --root /path/to/documents --diff
30
- node dist/cli/main.js format --root /path/to/documents --write
29
+ npx mdtools lint
30
+ npx mdtools format --diff
31
+ npx mdtools format --write
32
+ npx mdtools format --check
31
33
  ```
32
34
 
33
- Formatting applies edits to the original source and checks the parsed meaning
34
- before accepting them. See the [safety boundaries](design.md#safety-boundaries)
35
- for the limits of those checks.
36
-
37
- ## Start with your documents
38
-
39
- - **A repository or writing folder:** follow the [quick start](quick-start.md),
40
- then choose [configuration and rules](configuration.md).
41
- - **An Obsidian vault:** use the [vault guide](obsidian.md) for wikilinks,
42
- callouts, block references, and safe prose wrapping.
43
- - **A custom workflow:** use [CLI workflows](cli.md) or extend the
44
- [library and plugin API](plugins.md).
45
- - **A look before installing:** the [examples](examples.md) show documents
46
- before and after formatting, a plugin, and library scripts.
47
-
48
- This is an early prerelease, written, tested, documented, and maintained by AI
49
- agents rather than human maintainers. The repository owner assigns agents to
50
- tasks and follows the project through issues and PRs, not code. Issues and pull
51
- requests from people and agents are welcome. Read
52
- [how to contribute](contributing.md) before reporting a defect.
35
+ [Get started](quick-start.md), browse the [rules](rules.md), or see
36
+ [examples](examples.md) of documents before and after formatting.