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
@@ -0,0 +1,144 @@
1
+ # Integrations
2
+
3
+ ## Continuous integration
4
+
5
+ Two commands check a workspace without writing anything. Each fails with exit
6
+ code `1` when it finds something, and with `2` when it cannot do its job:
7
+
8
+ ```sh
9
+ npx mdtools lint --max-warnings 0
10
+ npx mdtools format --check
11
+ ```
12
+
13
+ The [examples](examples.md#automate-checks) include a complete GitHub Actions
14
+ workflow that runs both on every pull request; copy it to
15
+ `.github/workflows/markdown.yml`. Forgejo Actions, on Codeberg and other Forgejo
16
+ instances, runs the same workflow from `.forgejo/workflows/`: set `runs-on` to a
17
+ label that the instance's runners offer, and check that the actions it uses are
18
+ available there.
19
+
20
+ ## Git hooks
21
+
22
+ The CLI reads files from the working tree, not the staged content of the index.
23
+ Pass it the files that a commit changes, and never run it with an empty list,
24
+ which would check the whole workspace.
25
+
26
+ With [lint-staged](https://github.com/lint-staged/lint-staged), which passes the
27
+ staged files that match a pattern, keeps unstaged changes out of the way while
28
+ it runs, and adds what the commands changed to the commit, configure it in
29
+ `package.json`:
30
+
31
+ ```json
32
+ {
33
+ "lint-staged": {
34
+ "*.{md,markdown}": [
35
+ "mdtools format --write --no-warn-ignored",
36
+ "mdtools lint --max-warnings 0 --no-warn-ignored"
37
+ ]
38
+ }
39
+ }
40
+ ```
41
+
42
+ and run `npx lint-staged` from a pre-commit hook, for example through husky.
43
+ Without it, a `.git/hooks/pre-commit` script can lint the Markdown files that
44
+ the commit adds or changes:
45
+
46
+ ```sh
47
+ #!/bin/sh
48
+ git diff --cached --name-only -z --diff-filter=ACMR -- '*.md' '*.markdown' |
49
+ xargs -0 -r npx mdtools lint --max-warnings 0 --no-warn-ignored --
50
+ ```
51
+
52
+ In both, `--no-warn-ignored` leaves out the
53
+ [warning](cli.md#which-files-are-processed) for a changed file that the tool
54
+ skips, such as one that the configuration's `ignore` patterns match.
55
+ `--diff-filter=ACMR` leaves out deleted files, which would be an error as paths.
56
+ `xargs -r` skips the command when no file changed. `--` keeps a file name that
57
+ starts with `-` from being read as an option. This hook checks each file as it
58
+ is on disk, including changes that are not staged.
59
+
60
+ ## Editors
61
+
62
+ There is no editor extension or language server. An editor that can format a
63
+ buffer through an external command can run
64
+ `npx mdtools format - --stdin-filepath path/of/the/file.md`, which reads the
65
+ buffer from stdin and prints the formatted text; the path gives the buffer its
66
+ file's configuration, dialect, and link targets. Exit code `1` means that
67
+ findings remain, and the output is still the formatted text; with `2`, keep the
68
+ buffer as it was.
69
+ `npx mdtools lint - --stdin-filepath path/of/the/file.md --json` returns the
70
+ findings with their positions. An editor that formats a selection can use the
71
+ library's [`ranges`](api.md#part-of-a-document). An editor that reads JSON
72
+ Schemas completes and checks the configuration file with the package's
73
+ [schema](configuration.md#json-schema).
74
+
75
+ ## Next to Prettier or markdownlint
76
+
77
+ Prettier formats Markdown too, and two formatters undo each other's changes.
78
+ Leave Markdown to mdrefine by adding this line to `.prettierignore`:
79
+
80
+ ```text
81
+ **/*.md
82
+ ```
83
+
84
+ markdownlint and mdrefine can run side by side, but rules that overlap can
85
+ disagree, such as markdownlint's emphasis style (MD049) and `style/emphasis`.
86
+ Turn off in one tool what the other covers; the table below lists the overlaps.
87
+
88
+ ## Coming from Prettier or markdownlint
89
+
90
+ mdrefine is meant to replace these tools for Markdown, but it does not yet cover
91
+ everything they do. This table lists what has a rule today. Where it says "None
92
+ yet", keep the other tool for that check.
93
+
94
+ | Prettier or markdownlint | mdrefine |
95
+ | -------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- |
96
+ | `proseWrap: "always"` and `printWidth`; MD013 (line length) | [`style/wrap`](rules.md#stylewrap), which wraps instead of only reporting |
97
+ | `proseWrap: "preserve"` | Turn `style/wrap` off |
98
+ | `proseWrap: "never"` | None yet |
99
+ | `_` for emphasis; MD049 (emphasis style) | [`style/emphasis`](rules.md#styleemphasis) |
100
+ | `**` for strong text; MD050 (strong style) | [`style/strong`](rules.md#stylestrong) |
101
+ | `#` headings without closing `#`; MD003 (heading style) | [`style/heading`](rules.md#styleheading), whose default converts underlined ones, unlike Prettier |
102
+ | Aligned or compact tables; MD055, MD060 (table style) | [`style/table`](rules.md#styletable), for tables outside lists and quotes |
103
+ | Blank lines between blocks; MD022, MD032 (blank lines around headings and lists) | [`style/block-spacing`](rules.md#styleblock-spacing), for headings and lists |
104
+ | Backtick code fences; MD046, MD048 (code block and fence style) | [`style/code-fence`](rules.md#stylecode-fence), which leaves indented code blocks as written |
105
+ | `---` for thematic breaks; MD035 | [`style/thematic-break`](rules.md#stylethematic-break) |
106
+ | Trailing spaces; MD009 | [`style/trailing-space`](rules.md#styletrailing-space), which keeps hard line breaks |
107
+ | Repeated blank lines; MD012 | [`style/blank-lines`](rules.md#styleblank-lines), with a `maximum` |
108
+ | Final newline; MD047 | [`style/final-newline`](rules.md#stylefinal-newline) |
109
+ | MD041 (first line is a heading) | [`structure/initial-heading`](rules.md#structureinitial-heading) |
110
+ | MD051 (link fragments) | [`links/valid`](rules.md#linksvalid), which also checks that local files exist |
111
+ | MD052 (reference labels are defined) | [`links/undefined-reference`](rules.md#linksundefined-reference), which also checks footnotes |
112
+ | MD053 (definitions are used) | [`links/unused-definition`](rules.md#linksunused-definition), which also checks footnotes |
113
+ | MD042 (no empty links) | [`links/empty`](rules.md#linksempty) |
114
+ | MD001 (heading levels go up by one) | [`structure/heading-increment`](rules.md#structureheading-increment), with block quotes checked apart |
115
+ | MD025 (one top-level heading) | [`structure/single-title`](rules.md#structuresingle-title) |
116
+ | MD024 (repeated headings) with `siblings_only` | [`structure/duplicate-heading`](rules.md#structureduplicate-heading) |
117
+ | MD045 (image alternative text) | [`content/image-alt`](rules.md#contentimage-alt) |
118
+ | MD056 (table column count) | [`content/table-columns`](rules.md#contenttable-columns) |
119
+ | MD040 (code block language) | [`content/code-language`](rules.md#contentcode-language) |
120
+ | `-` for bullets; MD004 (list style) | [`style/list-marker`](rules.md#stylelist-marker), with MD004's `consistent` and `sublist` |
121
+ | `1.` and numbering; MD029 (ordered list prefix) | [`style/ordered-list`](rules.md#styleordered-list); `same` stands for MD029's `one` and `zero` |
122
+ | One space after list markers, list indentation; MD005, MD007, MD030 | [`style/list-indent`](rules.md#stylelist-indent), with MD030's spacing and MD007's `indent` |
123
+ | Inline HTML, bare URLs; MD033, MD034 | None yet |
124
+ | Hard tabs; MD010 | None yet |
125
+ | Formatting the code inside code blocks | None yet |
126
+ | `<!-- prettier-ignore -->`, `<!-- markdownlint-disable -->` | [`<!-- mdtools-disable -->`](configuration.md#suppression-comments) |
127
+ | Custom rules | [Plugins](plugins.md) |
128
+
129
+ Prettier keeps line breaks by default (`proseWrap: "preserve"`), while
130
+ `recommended` wraps at 80 columns. `style/wrap` keeps the lines of
131
+ [site-generator syntax](dialects.md#what-each-dialect-reads) such as `:::note`
132
+ and `{% tab %}`; for other syntax that depends on its line breaks, turn the rule
133
+ off or use a suppression comment.
134
+
135
+ ## Agents and scripts
136
+
137
+ Read the [JSON report](cli.md#json-report) and the exit code, not the text
138
+ output, whose wording can change in any release. A diagnostic names its rule,
139
+ its position, and, for a style rule, the edit it proposes. A safe loop is:
140
+ `format --diff` to see the changes, `format --write` to apply them, then `lint`
141
+ again for what needs a person. The [examples](examples.md#automate-checks)
142
+ include a script that summarizes a report. To report a defect of the tool,
143
+ follow the [contribution
144
+ guide](https://github.com/viell-dev/slop-markdown-tools/blob/main/CONTRIBUTING.md#report-a-defect).
package/docs/obsidian.md CHANGED
@@ -1,12 +1,12 @@
1
1
  # Obsidian vaults
2
2
 
3
- The Obsidian profile understands wikilinks, embeds, callouts, highlights,
4
- comments, heading references, and block references. Its rules share the same
5
- lint and format engine used for ordinary Markdown.
3
+ The `obsidian` dialect reads wikilinks, embeds, callouts, highlights, comments,
4
+ and heading and block references, and checks links the way Obsidian resolves
5
+ them.
6
6
 
7
7
  ## Configure the vault
8
8
 
9
- Create `mdtools.config.jsonc` at the vault root:
9
+ Create `mdtools.config.jsonc` at the vault's root, next to `.obsidian/`:
10
10
 
11
11
  ```jsonc
12
12
  {
@@ -15,102 +15,83 @@ Create `mdtools.config.jsonc` at the vault root:
15
15
  }
16
16
  ```
17
17
 
18
- Use the vault root as `--root` so the CLI can resolve links across all notes and
19
- from the vault's folder, as Obsidian does. Run these commands from a
20
- [built checkout](quick-start.md):
18
+ Run the commands from the vault's root, or pass it with `--root`, so that links
19
+ are resolved across the whole vault:
21
20
 
22
21
  ```sh
23
- node dist/cli/main.js lint --root "/path/to/vault" --json
24
- node dist/cli/main.js format --root "/path/to/vault" --diff
22
+ npx mdtools lint --root "/path/to/vault"
23
+ npx mdtools format --root "/path/to/vault" --diff
25
24
  ```
26
25
 
27
- Before that file exists, or when it names no dialect, the CLI still reads the
28
- notes in the Obsidian dialect: a folder that contains a `.obsidian` folder is
29
- recognized as a vault. Line breaks and wikilinks are then safe on a first run,
30
- but only the dialect is assumed. The rules of the `obsidian` preset, including
31
- the report that reflow is disabled, need the configuration above.
32
- Version 0.2.0-rc.1 and earlier read such a vault as CommonMark, joined the lines
33
- of its paragraphs, and could split a wikilink; with those versions, create the
34
- configuration first.
35
-
36
- ## Vaults that are not the workspace root
37
-
38
- A note belongs to the nearest vault at or above its folder, wherever the
39
- workspace root is. Its assumed dialect and its line-break setting come from that
40
- vault:
41
-
42
- - **A vault in a folder of a larger workspace**, such as `docs/vault/` in a
43
- repository: its notes are read in the Obsidian dialect and the documents
44
- around it as GitHub Markdown. Each vault in a workspace follows its own
45
- `.obsidian/app.json`.
46
- - **A folder of a vault used as the root**, with `--root` or through a
47
- configuration file in that folder: the vault is found above the root.
48
- - **A repository inside a vault** is not part of the vault. The tool leaves a
49
- repository inside a workspace alone, and when it runs in that repository it
50
- reads the documents as GitHub Markdown.
51
-
52
- A dialect named by the configuration or by `--dialect` still takes precedence
53
- over all of this.
54
-
55
- Links follow the vault as well. In a vault below the workspace root, a link in a
56
- note reaches only the vault's files, as it does in Obsidian: a path is counted
57
- from the vault's folder, a search by note name looks inside the vault, and
58
- `links/path` writes `"style": "root"` paths from the vault's folder. The
59
- findings and edits are the ones a run with the vault as the root produces. A
60
- path that leads out of the vault to a file that exists, such as
61
- `[the project](../../README.md)`, is reported as
26
+ Without a configuration file, the notes are still read in the Obsidian dialect,
27
+ because the vault is recognized by its `.obsidian` directory; see [the assumed
28
+ dialect](dialects.md#the-assumed-dialect). The rules of the `obsidian` preset,
29
+ among them the report that wrapping is off, still need the configuration.
30
+
31
+ ## Enable wrapping
32
+
33
+ Obsidian shows a single line break inside a paragraph as a line break unless the
34
+ vault's "Strict line breaks" setting is on. `style/wrap` therefore wraps notes
35
+ only when the vault's `.obsidian/app.json` contains `"strictLineBreaks": true`,
36
+ and [`obsidian/strict-line-breaks`](rules.md#obsidianstrict-line-breaks) reports
37
+ when it does not. The tool reads the file and never changes it; when you change
38
+ the setting, keep the file's other settings. If the file cannot be read or is
39
+ not valid JSON, the tool warns and leaves paragraphs as written; a byte order
40
+ mark at its start is ignored. If strict line breaks do not suit your vault,
41
+ disable `style/wrap`; the other rules still run.
42
+
43
+ The tool reads highlights (`==text==`) and wikilinks within one line. A
44
+ paragraph with `==`, `[[`, or `]]` outside them, such as a highlight written
45
+ across two lines, is left as written, because joining its lines could create a
46
+ highlight or a link.
47
+
48
+ In a callout, the title line stays as written, with its folding marker, and the
49
+ body is wrapped inside the quote, also directly below the title. A body
50
+ paragraph is left as written in the cases that
51
+ [`style/wrap`](rules.md#stylewrap) lists, and so is a body whose quote markers
52
+ are not consistent, or inline syntax that runs from the title into the body. A
53
+ title that is too long is reported by `reportUnbreakable`.
54
+
55
+ ## Vaults inside a larger workspace
56
+
57
+ A note belongs to the nearest vault at or above its directory, wherever the
58
+ workspace root is, and its dialect and its line-break setting come from that
59
+ vault. A vault in a directory of a repository, such as `docs/vault/`, is read in
60
+ the Obsidian dialect while the documents around it are read as GitHub Markdown.
61
+ Each vault in a workspace follows its own `.obsidian/app.json`. A repository
62
+ inside a vault is not part of the vault.
63
+
64
+ Links follow the vault too. In a vault below the workspace root, a link reaches
65
+ only the vault's files, as in Obsidian: a path counts from the vault's root, a
66
+ search by note name looks only inside the vault, and `links/path` writes
67
+ `"style": "root"` paths from the vault's root. A path that leaves the vault for
68
+ a file that exists, such as `[the project](../../README.md)`, is reported as
62
69
  `Local target is outside the vault`, because Obsidian cannot follow it.
63
- Documents outside the vault link into it as into any other folder. Versions up
64
- to `0.2.0-rc.1` resolved such a vault's links from the workspace root.
70
+ Documents outside the vault link into it as into any other directory.
65
71
 
66
- In the opposite layout, a root that is a folder of a vault, the tool sees only a
67
- part of the vault and reads nothing outside the root. It therefore treats a link
68
- as uncertain whenever the rest of the vault could matter:
72
+ ## A workspace root inside a vault
73
+
74
+ When the workspace root is a directory inside a vault, through `--root` or a
75
+ configuration file there, the tool finds the vault above it but reads nothing
76
+ outside the root. Where the rest of the vault could matter, a link is uncertain:
69
77
 
70
78
  - A link with no match below the root is reported as
71
- `Local target could not be checked`, with the reason that the workspace root
72
- is only a part of the vault. It is not called missing.
73
- - A link found by note name, or by a path that Obsidian would first look for at
74
- the top of the vault, is accepted but never rewritten by `links/path` or
75
- `links/notation`: another note outside the root could be the one Obsidian
76
- chooses.
77
- - A link written as an explicit relative path (`./Sub/Note.md`), or as a path
78
- from the vault's folder that leads through the root, is certain and is
79
- rewritten as usual. `"style": "root"` writes it from the vault's folder.
80
-
81
- For complete link checks and shortest-path rewriting, run with the vault as the
82
- root. Versions up to `0.2.0-rc.1` took the folder for the whole vault, which
83
- could shorten a link until it named a different note.
84
-
85
- ## Enable source wrapping
86
-
87
- Obsidian reflow requires `"strictLineBreaks": true` in the vault's
88
- `.obsidian/app.json` so soft line breaks in source do not become visible line
89
- breaks in Reading view. Preserve the file's other settings when changing this
90
- value. The CLI checks it and never edits the settings file itself. If it is not
91
- permitted to read the file, or the file is not valid JSON, it warns and leaves
92
- paragraphs as written; see
93
- [paths that cannot be read](configuration.md#paths-that-cannot-be-read).
94
-
95
- If that setting is not appropriate for your vault, disable `style/wrap`. Other
96
- enabled rules can still run.
97
-
98
- ## Wrap callout bodies
99
-
100
- With strict line breaks verified, `style/wrap` reflows supported body prose even
101
- when it directly follows the callout header without a blank quote line. The
102
- header stays on its original physical line, including its title and folding
103
- marker. Existing quote and list-container prefixes are retained.
104
-
105
- Hard breaks, block IDs, inline HTML, and unsupported multiline syntax still
106
- protect body paragraphs. Lazy or inconsistent quote continuations and inline
107
- syntax spanning the title/body boundary remain untouched. Long headers follow
108
- `reportUnbreakable`; skipped breakable body prose follows `reportUnreflowed`.
109
-
110
- ## Choose link policies deliberately
111
-
112
- The recommended preset validates local links. To normalize verified destinations
113
- to vault-relative paths wrapped in angle brackets:
79
+ `Local target could not be checked`, because the workspace root is only part
80
+ of the vault. It is not called missing.
81
+ - A link found by note name, or by a path that Obsidian would look for at the
82
+ vault's root first, is accepted but never rewritten by `links/path` or
83
+ `links/notation`: a note outside the root could be the one Obsidian chooses.
84
+ - A link written as an explicit relative path (`./Sub/Note.md`), or as a
85
+ vault-relative path that leads into the root, is certain and is rewritten as
86
+ usual; `"style": "root"` writes it from the vault's root.
87
+
88
+ For complete link checks and rewriting, use the vault's root as the workspace
89
+ root.
90
+
91
+ ## Choose link policies
92
+
93
+ The `recommended` preset checks links. To also write verified Markdown links as
94
+ vault-relative paths in angle brackets, such as `[Note](<Folder/Note.md>)`:
114
95
 
115
96
  ```jsonc
116
97
  {
@@ -121,26 +102,21 @@ to vault-relative paths wrapped in angle brackets:
121
102
  }
122
103
  ```
123
104
 
124
- For example, a resolved Markdown link can become `[Note](<Folder/Note.md>)`.
125
- Note names and heading subpaths match case-insensitively, as in Obsidian, while
126
- block identifiers match exactly; notes whose paths differ only by case are
127
- reported as ambiguous. Missing or ambiguous destinations are never guessed.
128
- Unaliased wikilinks retain their target spelling because changing it may change
129
- their visible label. Note embeds remain embeds.
130
-
131
- See [link policies](configuration.md#link-policies) for relative paths,
132
- extension handling, and eligible Markdown/wikilink conversion. Path rewriting
133
- and notation conversion should be run in separate passes.
134
-
135
- ## Review visible results
136
-
137
- Preview changes with `--diff`, then use `--write` to apply them. Inspect complex
138
- notes in Obsidian, especially when using additional plugins. The resolver does
139
- not reproduce every Obsidian link-resolution behavior, and unsupported syntax
140
- may need an ignore pattern or a
141
- [suppression comment](configuration.md#suppressions).
142
-
143
- The [vault example](examples.md#format-an-obsidian-vault) shows a small vault
144
- before and after formatting. Read the
145
- [current limitations](design.md#safety-boundaries) before a broad vault-wide
146
- formatting pass.
105
+ Note names and headings match without regard to case, as in Obsidian, and block
106
+ identifiers match exactly; notes whose paths differ only by case are ambiguous.
107
+ A heading also matches as its source writes it, and `[[Note#Setup#Mobile]]`
108
+ reaches a heading below another; a wikilink never names a folder. A missing or
109
+ ambiguous target is never guessed. Wikilinks without a label keep their target
110
+ as written, because it is their visible text, and embeds stay embeds, with any
111
+ image size in their label. [`links/path`](rules.md#linkspath) and
112
+ [`links/notation`](rules.md#linksnotation) describe the options.
113
+
114
+ ## Review the result
115
+
116
+ Preview with `--diff` before `--write`, and look at complex notes in Obsidian
117
+ afterwards, especially with community plugins whose syntax the tool does not
118
+ know. The link resolver does not reproduce every behavior of Obsidian's, and
119
+ syntax it does not know may need an `ignore` pattern or a [suppression
120
+ comment](configuration.md#suppression-comments). The [vault
121
+ example](examples.md#format-an-obsidian-vault) shows a small vault before and
122
+ after formatting, and [limits](design.md#limits) lists what is not modeled.