Color mode

Diagnostics

Content diagnostics for Obsidian vaults / Riebeckite content: site-wide reference integrity, frontmatter issues, orphan notes, unused assets, and more. Usable as a build plugin, a programmatic API, and a CLI.

日本語

Overview

diagnostics() analyzes the content directory and reports problems as diagnostics during the build. The same checks are available through runDiagnostics() and the riebeckite-diagnostics CLI.

Usage (plugin)

ts
import { defineConfig } from "@riebeckite/core";
import { diagnostics } from "@riebeckite/plugin-diagnostics";
 
export default defineConfig({
  // ...
  plugins: [
    diagnostics({
      reportUnusedAssets: true,
      reportOrphans: true,
      requiredFrontmatter: ["title"],
    }),
  ],
});
  • addDiagnostics — runs the analysis at build time; results go to manifest.diagnostics
  • buildEnd — with failOnError: true, throws DiagnosticsFailure when error-level diagnostics exist

Checks

Code Default severity What it detects
broken-wikilink error Wikilink target or fragment does not resolve
broken-image error Embedded image or image link does not exist
broken-link error Markdown link points to a missing or excluded note/file
unused-asset warning Image never referenced by any note (reportUnusedAssets)
orphan-note info Published note has no incoming links (reportOrphans)
missing-frontmatter warning A required frontmatter field is missing
publish-conflict warning publish: true combined with draft: true / private: true
duplicate-title warning Multiple published notes share a title within the same language
slug-collision error Slugs collide case-insensitively
duplicate-content-id error Published notes share a stable content ID (id)
invalid-content-id error id frontmatter violates the stable content ID contract
excluded-public warning Excluded note is marked publish: true
publish-boundary warning Published content links to or embeds non-published content
analytics-untracked info Published note has no stable id and the analytics plugin will not track it (enabled with reportAnalyticsCoverage, or automatically when the config enables the analytics plugin)
content-integrity:broken-link warning A published page links to a site-local route that is not published or registered
content-integrity:unresolved-wikilink warning A published entry contains a WikiLink that the configured WikiLink/content index resolution did not resolve
content-integrity:broken-asset warning A published page references a local asset that is not in resolved content assets, plugin assets, generated outputs, or public routes
content-integrity:duplicate-public-location error Two published entries or generated routes claim the same final public path
content-integrity:redirect-target-missing warning A public redirect points at content whose public target is unavailable
content-integrity:redirect-cycle error Public redirects form a cycle
content-integrity:redirect-public-location-conflict error A redirect path conflicts with another public route
internal-error error Content analysis failed

Site-wide content integrity

When used as a Riebeckite plugin, diagnostics runs site-wide integrity checks from the resolved manifest instead of rescanning content for link rules. It uses publicEntries, resolved ContentLink metadata, ContentPublicLocation, publicRedirects, plugin page paths, plugin assets, and generated outputs. This keeps permalink, alias, rename redirect, l10n, publish/exclude, and Page System behavior aligned with the build pipeline.

It does not check external HTTP reachability, SEO, spelling, Lighthouse, or automatic repairs. External URLs (http:, https:, mailto:, tel:, data:), protocol-relative URLs, and fragment-only links are ignored. Query strings and fragments are stripped before route existence checks. Plugin authors should register generated routes through Page Types and generated files/assets through the existing plugin output/asset contracts so integrity checks can treat them as valid public targets.

runDiagnostics() and the standalone CLI still use the filesystem/content-source analyzer because no full manifest is available in that mode. As a result, orphan-note and unused-asset are reported only by runDiagnostics() and the standalone CLI; during a build they are omitted because link relationships come from the manifest.

Options

Option Type Default Description
failOnError boolean false Fail the build on error-level diagnostics
reportUnusedAssets boolean false Report unreferenced images
reportOrphans boolean false Report published notes without incoming links
reportAnalyticsCoverage boolean false Report published notes the analytics plugin will not track (auto-enabled when the resolved config enables the analytics plugin)
requiredFrontmatter string[] [] Required frontmatter fields
severity Partial<Record<DiagnosticCode, DiagnosticSeverity>> table above Override severity per code
exclude string[] [] Extra exclude globs
publishStrategy "explicit" | "selective" from config Publication filter strategy

CLI

bash
riebeckite-diagnostics --config riebeckite.config.ts
riebeckite-diagnostics --content ./content --report-orphans
Option Description
--config <path> Path to riebeckite.config.ts (loaded under tsx)
--content <dir> Content directory to analyze (default: .)
--exclude <glob> Extra exclude glob (repeatable)
--publish-strategy <mode> explicit | selective (default: selective)
--report-unused-assets Report images never referenced by any note
--report-orphans Report published notes with no incoming links
--report-analytics-coverage Report published notes without a stable content ID (auto-enabled for --config when the config enables the analytics plugin)
--required-frontmatter <f> Comma-separated required frontmatter fields
--fail-on-error Exit with code 1 when errors are found (default)
--exit-on <severity> Exit with code 1 at/above severity (info | warning | error)
--format <text|json> Output format (default: text)
--no-color Disable ANSI colors
-h, --help Show help

Exit codes: 0 no errors, 1 errors reported (or --exit-on threshold met), 2 invalid arguments.

With --config, options from the config's diagnostics plugin are used as defaults; explicit CLI flags override them, and exclude lists are merged.

Programmatic API

ts
import {
  assertNoErrors,
  formatDiagnostics,
  runDiagnostics,
} from "@riebeckite/plugin-diagnostics";
 
const report = await runDiagnostics("./content", { reportOrphans: true });
console.log(formatDiagnostics(report));
assertNoErrors(report);

runDiagnostics(config | path, options?) returns a DiagnosticsReport with diagnostics, errors, warnings, infos, hasErrors, hasWarnings, summary, and byCode.

Exports

  • diagnostics(options?) / diagnosticsPlugin — plugin factory
  • runDiagnostics(target, options?) — run the analysis directly
  • analyzeContent(config, options?) — raw analysis returning Diagnostic[]
  • hasEnabledAnalyticsPlugin(config?) — whether the resolved config enables the analytics plugin
  • Report helpers: buildReport, formatDiagnostics, groupByCode, summarize, assertNoErrors, DiagnosticsFailure
  • Types: DiagnosticsOptions, DiagnosticsReport, DiagnosticsSummary, AnalyzerContentConfig
  • CLI: riebeckite-diagnostics

See also

History

1 changesCollapseExpand
1 + <!-- Generated from packages/plugins/diagnostics/README.md. Do not edit this page directly; edit the package README and run `pnpm docs:sync`. -->
2 +
3 + # Diagnostics
4 +
5 + Content diagnostics for Obsidian vaults / Riebeckite content: site-wide
6 + reference integrity, frontmatter issues, orphan notes, unused assets, and more.
7 + Usable as a build plugin, a programmatic API, and a CLI.
8 +
9 + [日本語](./diagnostics.md)
10 +
11 + ## Overview
12 +
13 + `diagnostics()` analyzes the content directory and reports problems as
14 + diagnostics during the build. The same checks are available through
15 + `runDiagnostics()` and the `riebeckite-diagnostics` CLI.
16 +
17 + ## Usage (plugin)
18 +
19 + ```ts
20 + import { defineConfig } from "@riebeckite/core";
21 + import { diagnostics } from "@riebeckite/plugin-diagnostics";
22 +
23 + export default defineConfig({
24 + // ...
25 + plugins: [
26 + diagnostics({
27 + reportUnusedAssets: true,
28 + reportOrphans: true,
29 + requiredFrontmatter: ["title"],
30 + }),
31 + ],
32 + });
33 + ```
34 +
35 + - `addDiagnostics` — runs the analysis at build time; results go to
36 + `manifest.diagnostics`
37 + - `buildEnd` — with `failOnError: true`, throws `DiagnosticsFailure` when
38 + error-level diagnostics exist
39 +
40 + ## Checks
41 +
42 + | Code | Default severity | What it detects |
43 + | ---- | ---------------- | --------------- |
44 + | `broken-wikilink` | `error` | Wikilink target or fragment does not resolve |
45 + | `broken-image` | `error` | Embedded image or image link does not exist |
46 + | `broken-link` | `error` | Markdown link points to a missing or excluded note/file |
47 + | `unused-asset` | `warning` | Image never referenced by any note (`reportUnusedAssets`) |
48 + | `orphan-note` | `info` | Published note has no incoming links (`reportOrphans`) |
49 + | `missing-frontmatter` | `warning` | A required frontmatter field is missing |
50 + | `publish-conflict` | `warning` | `publish: true` combined with `draft: true` / `private: true` |
51 + | `duplicate-title` | `warning` | Multiple published notes share a title within the same language |
52 + | `slug-collision` | `error` | Slugs collide case-insensitively |
53 + | `duplicate-content-id` | `error` | Published notes share a stable content ID (`id`) |
54 + | `invalid-content-id` | `error` | `id` frontmatter violates the stable content ID contract |
55 + | `excluded-public` | `warning` | Excluded note is marked `publish: true` |
56 + | `publish-boundary` | `warning` | Published content links to or embeds non-published content |
57 + | `analytics-untracked` | `info` | Published note has no stable `id` and the analytics plugin will not track it (enabled with `reportAnalyticsCoverage`, or automatically when the config enables the analytics plugin) |
58 + | `content-integrity:broken-link` | `warning` | A published page links to a site-local route that is not published or registered |
59 + | `content-integrity:unresolved-wikilink` | `warning` | A published entry contains a WikiLink that the configured WikiLink/content index resolution did not resolve |
60 + | `content-integrity:broken-asset` | `warning` | A published page references a local asset that is not in resolved content assets, plugin assets, generated outputs, or public routes |
61 + | `content-integrity:duplicate-public-location` | `error` | Two published entries or generated routes claim the same final public path |
62 + | `content-integrity:redirect-target-missing` | `warning` | A public redirect points at content whose public target is unavailable |
63 + | `content-integrity:redirect-cycle` | `error` | Public redirects form a cycle |
64 + | `content-integrity:redirect-public-location-conflict` | `error` | A redirect path conflicts with another public route |
65 + | `internal-error` | `error` | Content analysis failed |
66 +
67 + ## Site-wide content integrity
68 +
69 + When used as a Riebeckite plugin, diagnostics runs site-wide integrity checks
70 + from the resolved manifest instead of rescanning content for link rules. It uses
71 + `publicEntries`, resolved `ContentLink` metadata, `ContentPublicLocation`,
72 + `publicRedirects`, plugin page paths, plugin assets, and generated outputs.
73 + This keeps permalink, alias, rename redirect, l10n, publish/exclude, and Page
74 + System behavior aligned with the build pipeline.
75 +
76 + It does not check external HTTP reachability, SEO, spelling, Lighthouse, or
77 + automatic repairs. External URLs (`http:`, `https:`, `mailto:`, `tel:`, `data:`),
78 + protocol-relative URLs, and fragment-only links are ignored. Query strings and
79 + fragments are stripped before route existence checks. Plugin authors should
80 + register generated routes through Page Types and generated files/assets through
81 + the existing plugin output/asset contracts so integrity checks can treat them as
82 + valid public targets.
83 +
84 + `runDiagnostics()` and the standalone CLI still use the filesystem/content-source
85 + analyzer because no full manifest is available in that mode. As a result,
86 + `orphan-note` and `unused-asset` are reported only by `runDiagnostics()` and the
87 + standalone CLI; during a build they are omitted because link relationships come
88 + from the manifest.
89 +
90 + ## Options
91 +
92 + | Option | Type | Default | Description |
93 + | ------ | ---- | ------- | ----------- |
94 + | `failOnError` | `boolean` | `false` | Fail the build on error-level diagnostics |
95 + | `reportUnusedAssets` | `boolean` | `false` | Report unreferenced images |
96 + | `reportOrphans` | `boolean` | `false` | Report published notes without incoming links |
97 + | `reportAnalyticsCoverage` | `boolean` | `false` | Report published notes the analytics plugin will not track (auto-enabled when the resolved config enables the analytics plugin) |
98 + | `requiredFrontmatter` | `string[]` | `[]` | Required frontmatter fields |
99 + | `severity` | `Partial<Record<DiagnosticCode, DiagnosticSeverity>>` | table above | Override severity per code |
100 + | `exclude` | `string[]` | `[]` | Extra exclude globs |
101 + | `publishStrategy` | `"explicit" \| "selective"` | from config | Publication filter strategy |
102 +
103 + ## CLI
104 +
105 + ```bash
106 + riebeckite-diagnostics --config riebeckite.config.ts
107 + riebeckite-diagnostics --content ./content --report-orphans
108 + ```
109 +
110 + | Option | Description |
111 + | ------ | ----------- |
112 + | `--config <path>` | Path to `riebeckite.config.ts` (loaded under tsx) |
113 + | `--content <dir>` | Content directory to analyze (default: `.`) |
114 + | `--exclude <glob>` | Extra exclude glob (repeatable) |
115 + | `--publish-strategy <mode>` | `explicit` \| `selective` (default: `selective`) |
116 + | `--report-unused-assets` | Report images never referenced by any note |
117 + | `--report-orphans` | Report published notes with no incoming links |
118 + | `--report-analytics-coverage` | Report published notes without a stable content ID (auto-enabled for `--config` when the config enables the analytics plugin) |
119 + | `--required-frontmatter <f>` | Comma-separated required frontmatter fields |
120 + | `--fail-on-error` | Exit with code 1 when errors are found (default) |
121 + | `--exit-on <severity>` | Exit with code 1 at/above severity (`info` \| `warning` \| `error`) |
122 + | `--format <text\|json>` | Output format (default: `text`) |
123 + | `--no-color` | Disable ANSI colors |
124 + | `-h`, `--help` | Show help |
125 +
126 + Exit codes: `0` no errors, `1` errors reported (or `--exit-on` threshold met),
127 + `2` invalid arguments.
128 +
129 + With `--config`, options from the config's `diagnostics` plugin are used as
130 + defaults; explicit CLI flags override them, and `exclude` lists are merged.
131 +
132 + ## Programmatic API
133 +
134 + ```ts
135 + import {
136 + assertNoErrors,
137 + formatDiagnostics,
138 + runDiagnostics,
139 + } from "@riebeckite/plugin-diagnostics";
140 +
141 + const report = await runDiagnostics("./content", { reportOrphans: true });
142 + console.log(formatDiagnostics(report));
143 + assertNoErrors(report);
144 + ```
145 +
146 + `runDiagnostics(config | path, options?)` returns a `DiagnosticsReport` with
147 + `diagnostics`, `errors`, `warnings`, `infos`, `hasErrors`, `hasWarnings`,
148 + `summary`, and `byCode`.
149 +
150 + ## Exports
151 +
152 + - `diagnostics(options?)` / `diagnosticsPlugin` — plugin factory
153 + - `runDiagnostics(target, options?)` — run the analysis directly
154 + - `analyzeContent(config, options?)` — raw analysis returning `Diagnostic[]`
155 + - `hasEnabledAnalyticsPlugin(config?)` — whether the resolved config enables the analytics plugin
156 + - Report helpers: `buildReport`, `formatDiagnostics`, `groupByCode`,
157 + `summarize`, `assertNoErrors`, `DiagnosticsFailure`
158 + - Types: `DiagnosticsOptions`, `DiagnosticsReport`, `DiagnosticsSummary`,
159 + `AnalyzerContentConfig`
160 + - CLI: `riebeckite-diagnostics`
161 +
162 + ## See also
163 +
164 + - [Plugin guide](../reference/plugin-api.en.md)
165 +