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/CHANGELOG.md ADDED
@@ -0,0 +1,577 @@
1
+ # Changelog
2
+
3
+ Notable changes to mdrefine, newest first. The format follows [Keep a
4
+ Changelog](https://keepachangelog.com/en/1.1.0/), and version numbers follow the
5
+ [versioning
6
+ policy](https://viell-dev.github.io/slop-markdown-tools/versions.html). Each
7
+ release's section is also its [GitHub
8
+ release](https://github.com/viell-dev/slop-markdown-tools/releases)'s notes; the
9
+ prereleases' notes there are longer than their entries here.
10
+
11
+ ## 0.2.0 - 2026-10-18
12
+
13
+ The first stable release. It holds everything in the earlier prereleases, and
14
+ the changes since `0.2.0-rc.3` listed here. From this release on, npm's `latest`
15
+ tag names the newest stable release, and prereleases stay under `rc` and `beta`;
16
+ the [versioning
17
+ policy](https://viell-dev.github.io/slop-markdown-tools/versions.html) says what
18
+ a version number promises.
19
+
20
+ ### Upgrading from a prerelease
21
+
22
+ From `0.2.0-rc.3`:
23
+
24
+ - In the Obsidian dialect, `format` no longer breaks lines inside the text of a
25
+ link, and joins link text that is written across lines, also where
26
+ `0.2.0-rc.3` broke it. Notes that `0.2.0-rc.3` formatted change again: run
27
+ `format --write` once after upgrading.
28
+ - A document from stdin whose `--stdin-filepath` is ignored is no longer
29
+ processed: `lint -` reports nothing and `format -` prints it unchanged, with a
30
+ warning. In `mdrefine/node`, `discoverWorkspace` no longer selects `documents`
31
+ whose paths are ignored but lists them in `skipped`.
32
+
33
+ From an earlier prerelease, also read the upgrade notes of
34
+ [0.2.0-rc.3](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.2.0-rc.3).
35
+
36
+ ### Added
37
+
38
+ - `--no-warn-ignored` leaves out the warning for a file or directory named on
39
+ the command line that the tool skips as ignored, for hooks and scripts that
40
+ pass every changed file. The JSON report still lists it in `skipped`
41
+ ([#311](https://github.com/viell-dev/slop-markdown-tools/pull/311)).
42
+
43
+ ### Fixed
44
+
45
+ - `format --diff`, which `format` also does for files by default, takes about as
46
+ long as `format --check` when formatting changes lines throughout a long
47
+ document. Before, building the diff took time in proportion to the document's
48
+ length times its changed lines: two minutes for a generated document of 8,000
49
+ wrapped paragraphs that took four seconds to check
50
+ ([#315](https://github.com/viell-dev/slop-markdown-tools/pull/315)).
51
+ - A document from stdin whose `--stdin-filepath` the configuration, Git, or
52
+ discovery ignores is left out as a named file is: `lint -` reports nothing,
53
+ `format -` prints the document unchanged, and a warning says why. Before, it
54
+ was linted and formatted, so an editor's format-on-save rewrote a document
55
+ that the configuration says to leave alone. In the library, `documents` that
56
+ ignore rules leave out are no longer selected
57
+ ([#316](https://github.com/viell-dev/slop-markdown-tools/pull/316)).
58
+ - Stdin in a workspace root that Git ignores no longer warns that the root's
59
+ files are not processed
60
+ ([#316](https://github.com/viell-dev/slop-markdown-tools/pull/316)).
61
+ - In the Obsidian dialect, `style/wrap` keeps the text of a link on one line and
62
+ moves the link whole to the next line where it does not fit, and joins link
63
+ text written across lines. Obsidian's Live Preview shows a link whose text
64
+ spans lines as Markdown source. `0.2.0-rc.3` broke lines inside link text in
65
+ every dialect; elsewhere it still does, as GitHub, Forgejo, and Gitea render
66
+ such links
67
+ ([#312](https://github.com/viell-dev/slop-markdown-tools/pull/312)).
68
+ - Lists and block quotes below a first line of `---` or `+++` that no later line
69
+ closes are read as lists and block quotes, as renderers show them. Before, the
70
+ parser read them as paragraphs, and `style/wrap` joined them to the line
71
+ above, which GitHub then showed as text
72
+ ([#309](https://github.com/viell-dev/slop-markdown-tools/pull/309)).
73
+ - `style/thematic-break` and `style/heading` with `setext` no longer write a
74
+ line of `---` alone below such a first line of `---`, which made front matter
75
+ of everything between the two lines. The safety check refused every edit of
76
+ the rule in such a document
77
+ ([#309](https://github.com/viell-dev/slop-markdown-tools/pull/309)).
78
+
79
+ ## 0.2.0-rc.3 - 2026-10-18
80
+
81
+ ### Upgrading from a prerelease
82
+
83
+ From `0.2.0-rc.2`:
84
+
85
+ - `format --write` refuses a read-only file on every platform, where Linux and
86
+ macOS used to replace it: the file gets an `engine/unwritable-file` error and
87
+ stays unchanged. Make the file writable to format it.
88
+ - One document that cannot be read or written, or that a rule was refused or
89
+ failed on, no longer keeps `format --write` from writing the others. Every
90
+ document that formatted is written, that one with the edits that were kept;
91
+ each problem is reported for its own file, and the exit code is still `2`.
92
+ Before, such a document kept every file from being written, and a file that
93
+ failed while the batch was written stopped the run. Pass `--exclude` to leave
94
+ a file alone.
95
+ - In the JSON report, `written` lists the files that were written instead of
96
+ being `true` or `false`. A script that tested it as a boolean must test its
97
+ length, because an empty list counts as true in JavaScript.
98
+ - A document that starts with a byte order mark gets its findings and edits at
99
+ the right positions; before, it could be refused or get findings in the wrong
100
+ places. In the library, `parse` now returns `document.source` without the
101
+ mark, which the tree's positions already skipped.
102
+ - `mdtools-disable-next-line` and line numbers also count a carriage return
103
+ alone as a line ending; before, such a directive could apply to the wrong
104
+ line.
105
+ - A file locked by another program on Windows (`EBUSY`) is handled like one that
106
+ may not be read, instead of stopping every command.
107
+ - A document that is not UTF-8 text gets an `engine/unreadable-file` error, and
108
+ stdin that is not stops the command with exit code `2`. Before, such text was
109
+ decoded with U+FFFD in place of what was not UTF-8, and `format --write` wrote
110
+ those characters into the file. Save such files as UTF-8.
111
+ - `format --write` writes each file into the existing file instead of replacing
112
+ it with a new one, so the file keeps its permissions, owner and group,
113
+ attributes, creation time, and hard links. Before, a replaced file lost what
114
+ Node.js cannot copy: the other names of a hard link kept the old content, and
115
+ on Windows the file's own permissions were dropped, which could let more users
116
+ read it. A crash or power failure during the write can now leave a file
117
+ incomplete; see [what writing
118
+ keeps](https://viell-dev.github.io/slop-markdown-tools/cli.html#what-writing-keeps).
119
+ - When a rule's edits would change a document's meaning, `format` keeps the
120
+ other rules' edits, so `--diff` and the library's output show them, and
121
+ reports that rule with `engine/unsafe-format` at the line where the meaning
122
+ would first differ. Before, the whole document was left unchanged. Edits of
123
+ two rules that overlap are applied in turn instead of refusing the document.
124
+ - A mistake on the command line, such as an unknown option or command or a
125
+ missing option value, exits with code `2` instead of `1`, the code for
126
+ findings, and so does `mdtools` without a command, which prints the help on
127
+ stderr.
128
+ - The text output and the diff name files from the current directory instead of
129
+ the workspace root. The JSON report keeps paths from the root and adds `root`.
130
+ - `config explain` names the configuration file `config` instead of `file`, adds
131
+ the document's `path`, and refuses a document outside the workspace. `config`
132
+ no longer accepts `--exclude`, `--json`, `--stdin-filepath`, and
133
+ `--max-warnings`, which did nothing there.
134
+ - A rule that throws or returns invalid findings is skipped for that document
135
+ with an `engine/rule-error` error, in `lint` as well, and the exit code is
136
+ `2`. Before, `lint` stopped at the first such rule, and `format` reported it
137
+ as `engine/unsafe-format`. A plugin rule with an invalid definition, such as
138
+ `phase: "Inline"`, stops the command with exit code `2` where it used to be
139
+ ignored, and a rule or preset named like a built-in property of every object,
140
+ such as `toString`, is reported as unknown.
141
+ - `recommended` warns about six more problems, listed under Added. A check that
142
+ runs `lint --max-warnings 0` fails on them until the documents are fixed or
143
+ the rules are turned off.
144
+ - `recommended` formats more, so `format --check` and `lint` report documents
145
+ that it used to accept until `format --write` runs once. It removes trailing
146
+ spaces, except hard line breaks, and repeated blank lines; writes code fences
147
+ with backticks and thematic breaks as `---`; turns underlined headings into
148
+ `#` headings and removes closing `#` sequences; and aligns tables. To keep a
149
+ form, turn its rule off or set its option, such as
150
+ `"style/heading": ["warn", { "closing": "keep" }]`.
151
+ - `.gitignore` files decide as in Git: a deeper file can include again what a
152
+ higher one excludes, and patterns match letter case exactly unless the
153
+ repository's `core.ignorecase` is on, as Git sets it on Windows and macOS.
154
+ Files that were left out before can be linted now, and the reverse.
155
+ - Git's other ignore rules apply too: `.gitignore` files above the workspace
156
+ root, `.git/info/exclude`, and the global excludes file, while a file that Git
157
+ tracks is never ignored. With `--root` on a folder of a repository, or with a
158
+ global excludes file, files that were linted before can be left out now, and a
159
+ tracked file that a pattern matches is linted.
160
+ - `.markdown` files are documents, and a file named on the command line is read
161
+ as Markdown whatever its name. A hook that passes every changed file should
162
+ pass only the Markdown ones.
163
+ - An input that does not exist and contains `*`, `?`, `[`, or `{` is a pattern,
164
+ and is an error only when it matches no document.
165
+ - `.npm-cache` is an ordinary directory, linted like any other unless
166
+ `.gitignore` or `ignore` leaves it out.
167
+ - `style/wrap` wraps more: across line breaks in links, emphasis, and code
168
+ spans, inside the text of links and emphasis, in the bodies of GitHub,
169
+ Forgejo, and Gitea alerts, and before words such as `--config` or
170
+ `**strong**`. A check that runs `format --check` fails on documents formatted
171
+ by an earlier version until `format --write` runs once.
172
+ - In the library, `resolveConfig` takes one options object,
173
+ `{ path, config, plugins, defaultDialect }`, as `lint` and `format` do, so
174
+ their options serve as they are. A call with the four separate arguments of
175
+ the prereleases throws an error that says so.
176
+ - `presets`, `builtInRules`, `dialects`, `dialectAliases`, and `configSchema`
177
+ are frozen, and a change to them throws a `TypeError`; change a copy instead.
178
+ - In the library's types, `WikiLink`, `ObsidianComment`, and `ObsidianHighlight`
179
+ replace `ObsidianLiteral`, `Rule.schema` is a `JsonSchema` instead of Ajv's
180
+ schema type, and the lists and records in `Config` are read-only: replace a
181
+ list or the `rules` of a configuration instead of changing it in place.
182
+ - `format` throws an error that the workspace throws, such as a loader's `EIO`,
183
+ as `lint` does, instead of reporting it as `engine/unsafe-format`.
184
+ - A plugin named `engine` is rejected.
185
+ - `recommended` formats lists: `*` and `+` bullets become `-`, `1)` becomes
186
+ `1.`, numbers are made consistent, and each item gets one space after its
187
+ marker and no indentation before it. `format --check` fails on lists written
188
+ otherwise until they are formatted or the list rules are turned off.
189
+ - `style/wrap` no longer joins the lines of a paragraph that has a line starting
190
+ with `:::`, `{%`, or `{{`, but lines that an earlier version joined stay
191
+ joined: split them again where a site generator reads them by line.
192
+ - A document whose front matter would end inside a code block, an HTML block, or
193
+ display math can get `engine/unsafe-format` errors, and exit code `2`, from
194
+ `format`, where the rules used to change it.
195
+
196
+ From `0.2.0-rc.1` or earlier, also:
197
+
198
+ - When nothing names a dialect, documents are read as GitHub Markdown, or as
199
+ Obsidian notes inside a vault, where they used to be read as CommonMark. Name
200
+ `"dialect": "commonmark"` to keep the old reading. The library's `lint`,
201
+ `format`, `resolveConfig`, and `createWorkspace` assume `github` as well.
202
+ - `$…$` is math only under the conditions of each dialect's renderer; before,
203
+ any text between two `$` signs was. Text that was left alone because of that
204
+ is now wrapped and restyled like other prose.
205
+ - A directory that may not be read is skipped with a warning, and a selected
206
+ document that may not be read gets an `engine/unreadable-file` error, where
207
+ both used to stop the command with exit code `2` and no report. An
208
+ `.obsidian/app.json` that cannot be read or is not valid JSON gives a warning,
209
+ and the vault is not wrapped.
210
+ - In a vault below the workspace root, Obsidian links resolve from the vault's
211
+ root and reach only its files. A workspace root inside a vault no longer
212
+ counts as the whole vault, so links that the rest of the vault could affect
213
+ are not rewritten.
214
+ - A plugin package is resolved as an `import` from the configuration file, with
215
+ the conditions given to Node.js, so a package with both an `import` and a
216
+ `require` entry loads through `import`.
217
+ - In the library, a source loader given to `createWorkspace` can be called more
218
+ than once for the same file and must return the same text each time, and one
219
+ that throws `EACCES` or `EPERM` makes the file unreadable instead of stopping
220
+ the run.
221
+ - The `lint` summary counts errors and warnings, and messages about paths,
222
+ configuration files, and plugins are worded differently. Exit codes and the
223
+ JSON report keep their meaning.
224
+
225
+ From every prerelease:
226
+
227
+ - The command line, the configuration, the JSON report, and the library now
228
+ follow the [versioning
229
+ policy](https://viell-dev.github.io/slop-markdown-tools/versions.html). npm's
230
+ `latest` tag names the newest stable release; release candidates no longer
231
+ move it.
232
+
233
+ ### Added
234
+
235
+ - `style/list-marker`, `style/ordered-list`, and `style/list-indent`, in
236
+ `recommended`, write bullets as `-` and numbers as `1.` with consistent
237
+ numbering and one space after each marker, and move each item's lines with its
238
+ text. Their options cover markdownlint's MD004, MD007, MD029, and MD030
239
+ ([#275](https://github.com/viell-dev/slop-markdown-tools/pull/275)).
240
+ - `--quiet` reports errors only, and `rules` lists the rules of the
241
+ configuration's plugins too, also as JSON with `--json`
242
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
243
+ - The CLI finds `mdtools.config.js` and `mdtools.config.cjs`, `extends` accepts
244
+ a single preset name, and a rule's setting can be its severity alone in a
245
+ list, as in `["warn"]`. The package ships the configuration's JSON Schema as
246
+ `config.schema.json`, which the documentation site serves as well, for editors
247
+ to complete and check configuration files
248
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
249
+ - The library loads with `require()`, exports `mdrefine/package.json`, and has a
250
+ doc comment for every export; the package lists `keywords`
251
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
252
+ - Nine problem rules: `links/undefined-reference`, `links/unused-definition`,
253
+ `links/empty`, `structure/heading-increment`, `content/image-alt`, and
254
+ `content/table-columns` in `recommended`, and the optional
255
+ `content/code-language`, `structure/single-title`, and
256
+ `structure/duplicate-heading`
257
+ ([#237](https://github.com/viell-dev/slop-markdown-tools/pull/237)).
258
+ - `style/block-spacing` separates headings and lists from the blocks around them
259
+ with a blank line
260
+ ([#190](https://github.com/viell-dev/slop-markdown-tools/pull/190)).
261
+ - Four style rules in `recommended`: `style/code-fence`, `style/thematic-break`,
262
+ `style/blank-lines`, and `style/trailing-space`, with markdownlint's choices
263
+ as options. `style/heading` also removes closing `#` sequences and can write
264
+ underlined headings (options `closing` and `style`), `style/table` can write
265
+ compact tables (option `style`), and both join `recommended`
266
+ ([#267](https://github.com/viell-dev/slop-markdown-tools/pull/267)).
267
+ - `lint` and `format` accept `ranges` to work on parts of a document, and
268
+ `format` returns their `scope`
269
+ ([#188](https://github.com/viell-dev/slop-markdown-tools/pull/188),
270
+ [#195](https://github.com/viell-dev/slop-markdown-tools/pull/195)).
271
+ - `mdrefine/node` exports `discoverWorkspace()`, the CLI's discovery of files,
272
+ with the example `examples/library/lint-workspace.mjs`
273
+ ([#189](https://github.com/viell-dev/slop-markdown-tools/pull/189)).
274
+ - Glob patterns as inputs, such as `mdtools lint "docs/**/*.md"`, expanded by
275
+ the CLI itself on every system
276
+ ([#239](https://github.com/viell-dev/slop-markdown-tools/pull/239)).
277
+
278
+ ### Changed
279
+
280
+ - A mistake on the command line exits with code `2`, as in ESLint and Prettier
281
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
282
+ - The text output names files from the current directory, and the JSON report
283
+ gives the workspace `root`
284
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
285
+ - `format --check` names the files that would change
286
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
287
+ - The help describes every command and lists only the options that each one
288
+ reads, an unknown dialect gets the list of dialects, `lint --fix` points to
289
+ `format --write`, `config explain` reports `config` and `path` and refuses
290
+ documents outside the workspace, and the `obsidian/strict-line-breaks` message
291
+ speaks to both command-line and library users
292
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
293
+ - Configuration and rule option errors name the setting as it is written, in
294
+ plain words, and list the allowed values, as in
295
+ `"extends[1]" must be a preset name, not 5`; a JSON syntax error gives its
296
+ line and column
297
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
298
+ - `resolveConfig` takes an options object, the exported tables are frozen and
299
+ typed read-only, and the types of Obsidian nodes and rule schemas are their
300
+ own ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
301
+ - `format --write` refuses read-only files on every platform, and reports a file
302
+ that fails with `engine/unwritable-file`
303
+ ([#177](https://github.com/viell-dev/slop-markdown-tools/pull/177)).
304
+ - A file locked by another program on Windows counts as one that may not be
305
+ read, in the CLI and in `createWorkspace` loaders
306
+ ([#177](https://github.com/viell-dev/slop-markdown-tools/pull/177)).
307
+ - Documents and stdin that are not UTF-8 text are refused instead of being
308
+ damaged, and `createWorkspace` treats a loader error with the code
309
+ `ERR_ENCODING_INVALID_ENCODED_DATA` as an unreadable target
310
+ ([#234](https://github.com/viell-dev/slop-markdown-tools/pull/234)).
311
+ - `format --write` writes into the existing file instead of replacing it, so
312
+ that the file keeps its permissions, attributes, owner, creation time, and
313
+ hard links, and writes the original content back when a write fails part way
314
+ ([#234](https://github.com/viell-dev/slop-markdown-tools/pull/234),
315
+ [#255](https://github.com/viell-dev/slop-markdown-tools/pull/255)).
316
+ - `format --write` writes every document that formatted, and reports a document
317
+ that could not be read, formatted as configured, or written for its own file;
318
+ the JSON report's `written` lists the files written
319
+ ([#255](https://github.com/viell-dev/slop-markdown-tools/pull/255)).
320
+ - The documentation is restructured, with new pages for the rules, dialects,
321
+ integrations, the library API, versions, and this changelog, and the release
322
+ notes come from this file
323
+ ([#238](https://github.com/viell-dev/slop-markdown-tools/pull/238)).
324
+ - When one rule's edits would change a document's meaning, `format` leaves out
325
+ only that rule's edits and names the rule and the line, and it applies
326
+ overlapping edits in turn
327
+ ([#241](https://github.com/viell-dev/slop-markdown-tools/pull/241)).
328
+ - Plugin rule option schemas are compiled once instead of for every document
329
+ ([#241](https://github.com/viell-dev/slop-markdown-tools/pull/241)).
330
+ - Many repeated headings, and many emphases on one line, no longer take time
331
+ that grows with the square of their number on Forgejo and Gitea, and `ignore`
332
+ and `overrides` patterns are compiled once instead of for every path
333
+ ([#273](https://github.com/viell-dev/slop-markdown-tools/pull/273)).
334
+ - `.markdown` files are documents and link targets, and a file named on the
335
+ command line is read as Markdown whatever its name
336
+ ([#239](https://github.com/viell-dev/slop-markdown-tools/pull/239)).
337
+ - CI tests Node.js 22.12.0, the lowest supported version, where it tested the
338
+ newest Node.js 22, and also tests Node.js 26
339
+ ([#257](https://github.com/viell-dev/slop-markdown-tools/pull/257)).
340
+ - The documentation site describes the newest stable release instead of the
341
+ unreleased state of `main`
342
+ ([#257](https://github.com/viell-dev/slop-markdown-tools/pull/257)).
343
+ - `style/wrap` reflows a paragraph with a line break inside a link's text, an
344
+ image description, emphasis, strong text, strikethrough, or a code span, and
345
+ can break lines inside the text of links and emphasis
346
+ ([#262](https://github.com/viell-dev/slop-markdown-tools/pull/262)).
347
+ - `style/wrap` wraps the body of a GitHub, Forgejo, or Gitea alert below the
348
+ line with its marker, as it does for Obsidian callouts
349
+ ([#262](https://github.com/viell-dev/slop-markdown-tools/pull/262)).
350
+ - `style/wrap` reflows a paragraph whose code span goes on in a line that starts
351
+ with spaces or tabs, and joins the span without them, as `style/inline-code`
352
+ does ([#288](https://github.com/viell-dev/slop-markdown-tools/pull/288)).
353
+ - `style/wrap` leaves a paragraph as written when one of its lines starts with
354
+ `:::`, `{%`, or `{{`, such as a Docusaurus admonition or a Liquid tag, keeps a
355
+ word that starts with them from starting a line, and keeps a template tag that
356
+ one line holds whole. Site generators read these by line
357
+ ([#298](https://github.com/viell-dev/slop-markdown-tools/pull/298)).
358
+ - The plugin example's style rule writes one space after a heading's `#` marks,
359
+ because `style/thematic-break` now does what it did
360
+ ([#267](https://github.com/viell-dev/slop-markdown-tools/pull/267)).
361
+
362
+ ### Fixed
363
+
364
+ - `style/wrap`, the other rules that leave the line of an alert's or callout's
365
+ marker alone, and the safety check recognize a marker written with an escape
366
+ or a character reference, such as `\[!NOTE]` or `[!TIP]`, which GitHub
367
+ renders as an alert. Before, `style/wrap` joined the body onto such a marker,
368
+ and GitHub showed an ordinary quote
369
+ ([#298](https://github.com/viell-dev/slop-markdown-tools/pull/298)).
370
+ - A document whose front matter would end at a `---` or `+++` inside a code
371
+ block, an HTML block, or display math that a line of the front matter opens is
372
+ read with that block, and formatting keeps its meaning both with and without
373
+ the front matter. Before, rules could change the block's content, such as a
374
+ `___` in code into `---`, which GitHub shows
375
+ ([#298](https://github.com/viell-dev/slop-markdown-tools/pull/298)).
376
+ - `style/wrap` wraps a paragraph that starts with a linked image, such as a
377
+ badge, unless it opens a block quote. Before, it was left as written, as if it
378
+ started with a callout's marker
379
+ ([#298](https://github.com/viell-dev/slop-markdown-tools/pull/298)).
380
+ - A plugin rule's option schema of `false` refuses options, and an asynchronous
381
+ schema (`$async`) is refused when the rule set is built instead of crashing
382
+ Node.js after the run
383
+ ([#297](https://github.com/viell-dev/slop-markdown-tools/pull/297)).
384
+ - A heading whose code span is written across lines gets the anchor that GitHub
385
+ gives it, so a correct link to it is no longer reported missing, and the
386
+ library's `textContent` reads such a code span as renderers show it
387
+ ([#293](https://github.com/viell-dev/slop-markdown-tools/pull/293)).
388
+ - `--dialect` works with an `.mjs` configuration that exports a frozen object
389
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
390
+ - `--stdin-filepath` accepts a document inside a root that is reached through a
391
+ link or a Windows junction
392
+ ([#260](https://github.com/viell-dev/slop-markdown-tools/pull/260)).
393
+ - The configuration and command-line pages say that configuration patterns match
394
+ letter case on every system, while `.gitignore` files and input patterns
395
+ ignore it on Windows and macOS
396
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
397
+ - `format` stops on an error of the workspace, as `lint` does, instead of
398
+ reporting it as `engine/unsafe-format`, and a plugin named `engine`, whose
399
+ findings would pass for the engine's own, is rejected
400
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
401
+ - `parse` reads a document in the dialect that an alias such as `codeberg`
402
+ stands for, so Forgejo's rules apply to it, and the type declarations no
403
+ longer need `@types/mdast` from another package, which a strict installation
404
+ such as pnpm's does not provide
405
+ ([#261](https://github.com/viell-dev/slop-markdown-tools/pull/261)).
406
+ - Documents and JSON configuration files that start with a byte order mark are
407
+ read correctly
408
+ ([#174](https://github.com/viell-dev/slop-markdown-tools/pull/174)).
409
+ - `style/heading` keeps link reference definitions directly above an underlined
410
+ heading, which it used to replace with the heading, so that the document was
411
+ refused ([#267](https://github.com/viell-dev/slop-markdown-tools/pull/267)).
412
+ - `style/final-newline` adds no line ending, and `style/block-spacing` no blank
413
+ line, where it would become part of an unclosed HTML or Obsidian comment, and
414
+ `style/final-newline` none where it would add a line to a code block without
415
+ its closing fence; such documents used to be refused
416
+ ([#267](https://github.com/viell-dev/slop-markdown-tools/pull/267)).
417
+ - `style/wrap` leaves paragraphs as written instead of refusing the document
418
+ when their first word is a bare block marker
419
+ ([#196](https://github.com/viell-dev/slop-markdown-tools/pull/196)), when an
420
+ Obsidian paragraph holds `==`, `[[`, or `]]` outside its highlights and links,
421
+ or when the first line is lazy
422
+ ([#200](https://github.com/viell-dev/slop-markdown-tools/pull/200)).
423
+ - `style/wrap` lets words that only start like a block marker, such as
424
+ `--config`, `#tag`, or `-5`, begin a line, and keeps a table delimiter row
425
+ from beginning one
426
+ ([#262](https://github.com/viell-dev/slop-markdown-tools/pull/262)).
427
+ - `style/wrap` keeps a paragraph's first line from becoming a link reference
428
+ definition, a thematic break, or display math instead of refusing the document
429
+ ([#262](https://github.com/viell-dev/slop-markdown-tools/pull/262)).
430
+ - `style/wrap` counts a tab in a list item's prefix up to the next tab stop
431
+ instead of as no width
432
+ ([#262](https://github.com/viell-dev/slop-markdown-tools/pull/262)).
433
+ - A carriage return alone ends a line for suppression directives, diagnostic
434
+ positions, callout titles, and `style/wrap`
435
+ ([#196](https://github.com/viell-dev/slop-markdown-tools/pull/196),
436
+ [#200](https://github.com/viell-dev/slop-markdown-tools/pull/200)).
437
+ - A rule that throws or returns invalid findings is reported as
438
+ `engine/rule-error` for its document, and invalid plugin rules and names such
439
+ as `toString` are rejected with a clear message
440
+ ([#241](https://github.com/viell-dev/slop-markdown-tools/pull/241)).
441
+ - `.gitignore` files are applied as Git applies them, with negations in deeper
442
+ files and letter case per platform
443
+ ([#239](https://github.com/viell-dev/slop-markdown-tools/pull/239)).
444
+ - Git's ignore rules apply from the repository's top level, also above the
445
+ workspace root, together with `.git/info/exclude` and the global excludes
446
+ file. Inside a nested repository only its own rules apply, and none applies to
447
+ a file that Git tracks
448
+ ([#273](https://github.com/viell-dev/slop-markdown-tools/pull/273)).
449
+ - Git's ignore rules match letter case as each repository's `core.ignorecase`
450
+ says, and each repository, also a nested one, uses the global excludes file
451
+ that its own settings name
452
+ ([#287](https://github.com/viell-dev/slop-markdown-tools/pull/287)).
453
+ - A file or directory named on the command line that is ignored gets a warning
454
+ that says why, instead of being skipped without a word
455
+ ([#239](https://github.com/viell-dev/slop-markdown-tools/pull/239)).
456
+ - A link into `dist`, `coverage`, `node_modules`, or another directory that is
457
+ not processed, or through a symbolic link or junction, is checked on disk
458
+ instead of being reported missing; one that leads outside the workspace root
459
+ cannot be checked
460
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
461
+ - An Obsidian block identifier counts only at the end of a block and holds
462
+ letters, digits, and hyphens, in every rule that reads one
463
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
464
+ - A vault's `.obsidian/app.json` that starts with a byte order mark is read
465
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
466
+ - `links/notation` decodes percent escapes when it makes a wikilink
467
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
468
+ - A link to a file that is not Markdown accepts any fragment, such as an image's
469
+ `#gh-dark-mode-only` or a PDF's `#page=2`
470
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
471
+ - An Obsidian wikilink never resolves to a folder
472
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
473
+ - Obsidian links to a heading below another, `[[Note#Setup#Mobile]]`, are
474
+ resolved ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
475
+ - An Obsidian heading link matches the heading's source text, with its
476
+ backticks, as well as its visible text
477
+ ([#259](https://github.com/viell-dev/slop-markdown-tools/pull/259)).
478
+ - `style/emphasis` and `style/strong` leave the text of a collapsed or shortcut
479
+ reference, such as `[*Title*][]`, as written, because that text is also the
480
+ label that matches the definition, and likewise text in brackets that the new
481
+ markers would make match one. Before, all of the rule's edits in the document
482
+ were refused
483
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
484
+ - `style/emphasis`, `style/strong`, `style/inline-code`, and
485
+ `style/block-spacing` no longer propose edits that change the parse in rare
486
+ places, where their edits were refused: crossed or shared markers, as in
487
+ `*a _b* c_` or `*****a***`, markers next to dollar math or across the line of
488
+ a callout's or alert's marker, code spans whose joined line would form an
489
+ Obsidian highlight or wikilink, change that line, or pair with earlier
490
+ backticks, a table below one that ends in a list item or block quote, and a
491
+ list in a quote that ends in an empty footnote definition
492
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
493
+ - `style/inline-code` leaves out the spaces and tabs that start a code span's
494
+ next line when it joins the span, as CommonMark and GitHub do; before,
495
+ `npm run` continued by an indented `build` became `npm run build`, which
496
+ changed what GitHub shows. The safety check compares code spans the same way
497
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
498
+ - `style/inline-code` and the safety check read a code span as GitHub shows it
499
+ where a line break is at its edge, and on a lazy continuation line, one
500
+ without its block quote's `>` or its list item's indentation, whose spaces and
501
+ tabs GitHub keeps in the span. Before, the rule could change what GitHub
502
+ shows, such as `npm run build` into `npm run build`, and the safety check
503
+ did not notice. Outside the GitHub dialect, a span on such an indented lazy
504
+ line stays split, because commonmark.js leaves those spaces out. `style/wrap`
505
+ now also reflows a paragraph whose code span has a line break at its edge
506
+ ([#291](https://github.com/viell-dev/slop-markdown-tools/pull/291)).
507
+ - `style/wrap` keeps a dollar from the edge of a line where it could begin or
508
+ end math there: in Obsidian a word that ends in `$` stays with the word after
509
+ it, and on GitHub and in Obsidian a word that starts with `$` stays with the
510
+ word before it. Before, such a paragraph was refused in Obsidian
511
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
512
+ - `style/wrap` leaves a paragraph as written when its second line is a table
513
+ delimiter row, in every dialect with tables. Directly below a table that ends
514
+ in a list item or block quote, the parser reads another table as such a
515
+ paragraph, and joining its lines removed a table that GitHub shows
516
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
517
+ - `style/wrap` keeps a run of `=` or `-` that starts the body of an alert or
518
+ callout together with the next word. Alone on a line, it would underline the
519
+ marker's line as a heading, and the paragraph was refused
520
+ ([#274](https://github.com/viell-dev/slop-markdown-tools/pull/274)).
521
+ - The type declaration of `defaultDialect` describes vaults anywhere in a
522
+ workspace ([#184](https://github.com/viell-dev/slop-markdown-tools/pull/184)),
523
+ and the CLI guide explains stdin in PowerShell
524
+ ([#182](https://github.com/viell-dev/slop-markdown-tools/pull/182)).
525
+
526
+ ## 0.2.0-rc.2 - 2026-10-06
527
+
528
+ When nothing names a dialect, GitHub Markdown is assumed, or the Obsidian
529
+ dialect inside a vault. Vaults are recognized anywhere in a workspace, and their
530
+ links resolve within them. Directories and files that may not be read no longer
531
+ stop a run. `$` math follows each renderer. Plugin packages resolve like an
532
+ `import`. Messages are clearer, link checks need about half the memory, and the
533
+ package ships examples. [Release
534
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.2.0-rc.2).
535
+
536
+ ## 0.2.0-rc.1 - 2026-10-03
537
+
538
+ New `forgejo` and `gitea` dialects and presets, with `codeberg` as another name
539
+ for `forgejo`. Fragments are checked against the anchors that each renderer
540
+ generates, Forgejo and Gitea heading attributes are preserved, and
541
+ `forgejo/heading-id` reports ids that break rendering. [Release
542
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.2.0-rc.1).
543
+
544
+ ## 0.1.0-rc.1 - 2026-09-21
545
+
546
+ Five built-in rules no longer propose edits that made formatting refuse ordinary
547
+ documents. Obsidian links match without regard to case, new line breaks follow a
548
+ document's first line ending, and applying many edits takes linear time.
549
+ [Release
550
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.1.0-rc.1).
551
+
552
+ ## 0.1.0-beta.4 - 2026-09-18
553
+
554
+ `style/wrap` wraps an Obsidian callout's body directly below its title, and
555
+ reports a long title with `reportUnbreakable`. [Release
556
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.1.0-beta.4).
557
+
558
+ ## 0.1.0-beta.3 - 2026-09-18
559
+
560
+ `style/wrap`'s report options tell breakable prose from unbreakable atoms, and
561
+ formatting is more than twice as fast. [Release
562
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.1.0-beta.3).
563
+
564
+ ## 0.1.0-beta.2 - 2026-09-18
565
+
566
+ New `style/inline-code` and `structure/initial-heading` rules, the `measure`,
567
+ `reportUnreflowed`, and `reportUnbreakable` options of `style/wrap`, the
568
+ `resolve` settings, and `--exclude`. Link resolution fixes, and workspaces with
569
+ source loaders. [Release
570
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.1.0-beta.2).
571
+
572
+ ## 0.1.0-beta.1 - 2026-09-18
573
+
574
+ The first public release: `lint` and `format` for CommonMark, GitHub Markdown,
575
+ and Obsidian, with presets, overrides, suppression comments, plugins, and link
576
+ checks. [Release
577
+ notes](https://github.com/viell-dev/slop-markdown-tools/releases/tag/0.1.0-beta.1).
@@ -0,0 +1,30 @@
1
+ # Code of conduct
2
+
3
+ People and agents take part in this project through its issues, pull requests,
4
+ and other spaces on GitHub. Everyone who does is expected to:
5
+
6
+ - be respectful, assume good faith, and criticize ideas rather than people;
7
+ - keep to the topic, and keep reports minimal and reproducible;
8
+ - leave out private content, such as other people's documents or personal
9
+ details;
10
+ - not harass, insult, threaten, or demean anyone, and not post sexual content or
11
+ spam.
12
+
13
+ An agent's operator is responsible for what the agent posts. Run agents so that
14
+ they follow this code and the [contribution guide](CONTRIBUTING.md); a stream of
15
+ unreviewed, generated submissions counts as spam.
16
+
17
+ These standards follow the [Contributor
18
+ Covenant](https://www.contributor-covenant.org/) in short form.
19
+
20
+ ## Enforcement
21
+
22
+ The repository's maintainers, who are agents assigned by its owner and the owner
23
+ themselves, may edit or hide comments, lock conversations, and close issues or
24
+ pull requests that break this code, saying why where it helps. The owner can
25
+ block an account. Nobody watches the repository around the clock, so a response
26
+ can take time.
27
+
28
+ To report a violation, use GitHub's "Report content" option on the comment,
29
+ issue, or pull request. It reaches GitHub's staff, who act on abuse across
30
+ GitHub. Do not open a public issue about another person.