# MD057 - Check that file links work

Aliases: `existing-relative-links`

## What this rule does

Verifies that relative links to other files in your documentation actually point to files that exist.
This includes both inline links and reference-style link definitions.

## Why this matters

- **Prevents frustration**: Broken links waste readers' time and damage trust
- **Maintains quality**: Working links show your documentation is well-maintained
- **Aids navigation**: Readers can confidently explore your documentation
- **Catches typos**: Common mistakes in file paths are caught early

## Examples

<!-- rumdl-disable MD057 MD031 MD040 -->

### ✅ Correct

```markdown
[Installation Guide](install.md)          <!-- File exists -->
[Contributing](../CONTRIBUTING.md)        <!-- File exists -->
[GitHub Repo](https://github.com/org/repo) <!-- External URL -->
[Email Us](mailto:help@example.com)       <!-- Email link -->
[Jump to Section](#configuration)         <!-- Same-file anchor -->

<!-- Reference-style links -->
[readme]: ./README.md                     <!-- File exists -->
[external]: https://example.com           <!-- External URL -->
```

### ❌ Incorrect

```markdown
[Missing Doc](does-not-exist.md)           <!-- File doesn't exist -->
[Bad Path](../missing/guide.md)            <!-- Path doesn't exist -->
[Typo in Name](READNE.md)                 <!-- Should be README.md -->
[Wrong Extension](setup.markdown)          <!-- File is setup.md -->

<!-- Reference-style links with missing targets -->
[missing]: ./does-not-exist.md            <!-- File doesn't exist -->
[bad-path]: ../missing/doc.md             <!-- Path doesn't exist -->
```

### 🔧 Fixed

This rule cannot automatically fix broken links because it can't know which file you intended to link to. You must manually:

1. Correct the file path
2. Create the missing file
3. Or remove the broken link

<!-- rumdl-enable MD057 MD031 MD040 -->

## Configuration

### `absolute-links`

Controls how absolute links (paths starting with `/`) are handled.

| Value | Behavior |
|-------|----------|
| `ignore` (default) | Skip validation for absolute links |
| `warn` | Report a warning for absolute links |
| `relative_to_docs` | Resolve absolute links relative to MkDocs `docs_dir` and validate |

Absolute links like `/api/docs` or `/blog/post.html` are typically routes for published
documentation sites, not filesystem paths. By default, MD057 ignores these because they
can't be validated locally.

If you want to be notified about absolute links in your documentation (perhaps to convert
them to relative links), set `absolute-links = "warn"`.

```toml
# .rumdl.toml
[MD057]
absolute-links = "warn"
```

For MkDocs projects, use `relative_to_docs` to validate absolute links by resolving them
relative to the `docs_dir` configured in `mkdocs.yml`. This finds the `mkdocs.yml` by
walking up from the file being checked, reads the `docs_dir` setting (default: `docs`),
and checks that the linked file exists there.

```toml
# .rumdl.toml
[global]
flavor = "mkdocs"

[MD057]
absolute-links = "relative_to_docs"
```

With this configuration, a link like `[Guide](/getting-started/)` will be validated by
checking if `docs/getting-started/index.md` exists. Extensionless links like
`/getting-started` will also try markdown extensions (e.g., `docs/getting-started.md`).

If no `mkdocs.yml` is found, the behavior falls back to `warn`.

### `compact-paths`

When enabled, warns about relative links that contain unnecessary path traversal.
Disabled by default.

| Value | Behavior |
|-------|----------|
| `false` (default) | No compact-paths warnings |
| `true` | Warn when a shorter equivalent path exists |

For example, in `doc/sub_dir/file2.md`, the link `[text](../sub_dir/file1.md)` goes up
to `doc/` then back into `sub_dir/` — the same directory the file is already in. The
shorter equivalent is just `file1.md`.

```toml
# .rumdl.toml
[MD057]
compact-paths = true
```

When enabled, this rule also provides auto-fix support: running `rumdl check --fix` will
replace unnecessarily long paths with their shorter equivalents.

| Original | Simplified |
|----------|-----------|
| `../sub_dir/file.md` (from `sub_dir/`) | `file.md` |
| `./file.md` | `file.md` |
| `./sub/../file.md` | `file.md` |
| `../../a/sub/file.md` (from `a/sub/`) | `file.md` |

Paths that are already optimal are not flagged:

| Link | Why it's already optimal |
|------|------------------------|
| `file.md` | No traversal |
| `../sibling/file.md` (from `other/`) | Cannot be shortened |
| `../../file.md` (from `a/b/`) | Necessary parent traversal |

Fragment (`#section`) and query (`?v=1`) suffixes are preserved in the suggested fix.

### `search-paths`

Additional directories to search when a relative link target is not found relative to the
file's directory. Paths are resolved relative to the project root (where `.rumdl.toml` or
`pyproject.toml` is found), or relative to the current working directory.

```toml
# .rumdl.toml
[MD057]
search-paths = ["assets", "images", "attachments"]
```

With this configuration, a link like `![photo](photo.png)` will first be checked relative
to the markdown file's directory. If not found there, MD057 will also look in `assets/photo.png`,
`images/photo.png`, and `attachments/photo.png`.

**Obsidian users:** When `flavor = "obsidian"` is set in the global config, the attachment
folder is auto-detected from `.obsidian/app.json`, so this option is typically not needed.
Use it for custom setups or non-Obsidian tools with similar asset directory conventions.

```toml
# .rumdl.toml — Obsidian auto-detection (no search-paths needed)
[global]
flavor = "obsidian"
```

Obsidian supports 4 attachment location modes configured via `attachmentFolderPath` in
`.obsidian/app.json`:

| Setting | `attachmentFolderPath` | Resolution |
|---------|----------------------|------------|
| Vault folder (root) | `""` (empty) | `<vault-root>/` |
| Specified folder | `"Attachments"` | `<vault-root>/Attachments/` |
| Same folder as file | `"./"` | Same directory as the markdown file |
| Subfolder under file | `"./assets"` | `<file-dir>/assets/` |

### Build-Generated Files

Documentation sites often compile markdown files to HTML during build. MD057 automatically
checks if a corresponding markdown source file exists when a link points to a `.html` or
`.htm` file.

For example, `[Guide](guide.html)` will pass if `guide.md` exists, even though `guide.html`
doesn't exist in your source tree.

### Handling Complex Generator Patterns

For documentation generators that place source files in different locations (e.g., mdBook's
`src/` directory), MD057 checks for markdown sources in the same directory as the HTML file.
If your generator uses a different structure, you can disable MD057 for affected directories
using `per-file-ignores`:

```toml
[per-file-ignores]

## mdBook projects - HTML links in book/ point to book/src/*.md sources

"book/**/*.md" = ["MD057"]

## Jekyll projects - HTML links in _posts/ point to generated files

"_posts/**/*.md" = ["MD057"]
"_docs/**/*.md" = ["MD057"]

## Hugo projects - HTML links in content/ point to generated files

"content/**/*.md" = ["MD057"]
```

MD057 will still check for markdown sources in the same directory automatically.
Use `per-file-ignores` only when sources are in different locations.

## Automatic fixes

Broken links cannot be automatically fixed because the rule cannot determine which file
you intended to link to. They must be corrected manually.

When `compact-paths = true`, unnecessary path traversal can be auto-fixed with
`rumdl check --fix`. The fix replaces long paths with their shorter equivalents while
preserving any fragment or query suffix.

## Learn more

- [Markdown Guide: Links](https://www.markdownguide.org/basic-syntax/#links)
- [Writing good documentation](https://www.writethedocs.org/guide/writing/beginners-guide-to-docs/)

## Related rules

- [MD051 - Fix broken link fragments](md051.md)
- [MD042 - Ensure links have content](md042.md)
- [MD034 - Format bare URLs properly](md034.md)
