Skip to content

demo-vault-coverage

Static coverage/freshness checks that keep a plugin’s in-repo demo-vault/ in sync with its public surface.

A plugin can ship a curated demo vault at demo-vault/ in its repo root. These helpers reflect the real public API/config/docs straight from source and assert — WITHOUT launching Obsidian — that every feature is demonstrated in the notes and that the notes reference no API member that no longer exists (rename drift). The runtime behavior of the plugin is covered by its other integration tests, not by the demo vault; the vault is a learning resource, and these checks only guard that it stays truthful.

They also guard the vault’s own .obsidian/app.json: the settings listed in DEMO_VAULT_APP_JSON_SETTINGS belong to this package, which writes them into the archived vault at release time, so a vault that commits any of them is keeping a second copy nothing reconciles.

They also enforce the demo-vault AUTHORING convention — every note opens with an # H1 and a plain prose paragraph, every note is reachable from the start note, and no note uses a [[wikilink]] or a [Docs](…) line. These checks are unconditional: the demo vault is the published documentation, read in Obsidian AND on GitHub (where a wikilink does not render), so a note that breaks the convention is broken for real readers. authoring only tunes them (which note is the start, which notes sit outside the learning path); it cannot switch them off.

Only rootFolder is required, so a plugin with no settings and no public API to reflect registers the suite with that alone and still gets every authoring check — the checks have nothing to do with what the plugin exposes. Each reflection spec (configInterfaces, interfaces, nonTrivialGuard) adds the checks it describes when supplied. The vault is guarded against being empty either way: with no notes to read, every authoring check would pass by having nothing to look at.

The one exception is a note whose SUBJECT is the wikilink — an embed spelling the reader is being taught to type, a frontmatter property value that exists to contrast with a Markdown link, a fixture that must keep the wikilink a command is supposed to leave alone, a link the reader clicks while it still resolves to nothing. Such a note says so itself, stating why, in one of two forms modelled on ESLint’s.

Per line or per region, written as an HTML comment so it is invisible in Obsidian AND on GitHub — the spelling these vaults already use for markdownlint-disable-next-line:

<!-- obsidian-dev-utils-disable-next-line demo-vault-validation/no-wikilinks -- Clicking a link to a note that does not exist yet IS the feature. -->
2. Click this link: [[Projects/Fresh idea]].
<!-- obsidian-dev-utils-disable demo-vault-validation/no-wikilinks -- The wikilink embed is the syntax this note teaches. -->
![[basic.html|400]]
<!-- obsidian-dev-utils-enable demo-vault-validation/no-wikilinks -->

Per note, in the note’s own frontmatter — the only form that can reach a wikilink INSIDE frontmatter, where a comment cannot go:

---
obsidian-dev-utils:
demo-vault-validation:
allow-wikilinks: The `wikilink` property value is the contrast this note is built around.
---

Either way the declaration travels with the note rather than sitting in a list somewhere else, it carries its justification, and it exempts only the WIKILINK check — the H1, prose, reachability and [Docs] checks still apply. A declaration with no reason, one covering no wikilink, a region never enabled again, or a misspelled directive all fail: an exemption nobody can justify, that nothing needs any more, or that silently does nothing is worse than no exemption at all.

They also check that every Try it section — a heading of any level starting Try it, up to the next heading of the same or a higher level — holds a code-button that performs its setup, because a reader asked to configure things by hand is a reader the demo lost. A section whose steps really cannot be scripted says so with the same directive, naming the demo-vault-validation/try-it-code-button rule on the heading, and with the same checks on the directive itself:

<!-- obsidian-dev-utils-disable-next-line demo-vault-validation/try-it-code-button -- Dragging a file in from outside Obsidian cannot be scripted. -->
## Try it

That check fails the suite by default; DemoVaultAuthoringSpec.tryItCodeButtons set to 'warn' prints the sections instead, for a vault still being brought up to it.

Two layers are exposed: - DemoVaultCoverageChecker — a framework-agnostic core that reads the corpus, parses interface / class / enum members and exported functions, and returns diagnostic arrays (what is undemonstrated / stale / unlinked / off-convention). - registerDemoVaultCoverageSuite — a thin wrapper that registers a vitest suite over the core, so a plugin’s demo-vault.no-app.integration.test.ts is a single declarative call.

Class Description
DemoVaultCoverageChecker Reflects a plugin’s public surface from source and checks its in-repo demo-vault/ stays in sync with it. Every method is a pure query over files under the repo root (the demo corpus is read once and cached), so the checker is trivially unit-testable against a fixture repo and carries no test-framework dependency.
Interface Description
DemoVaultAuthoringSpec Tunes the always-on authoring checks. It cannot disable them — see the file overview.
DemoVaultConfigInterfaceCoverageSpec Reflects a config interface whose options are demonstrated by their bare name.
DemoVaultCoverageCheckerConstructorParams The parameters for the DemoVaultCoverageChecker constructor.
DemoVaultCoverageCheckerFindStaleReferencesParams The parameters for DemoVaultCoverageChecker.findStaleReferences.
DemoVaultCoverageCheckerFindUndemonstratedMembersParams The parameters for DemoVaultCoverageChecker.findUndemonstratedMembers.
DemoVaultCoverageCheckerFindUnlinkedFeatureDocsParams The parameters for DemoVaultCoverageChecker.findUnlinkedFeatureDocs.
DemoVaultCoverageCheckerFindUnreachableNotesParams The parameters for DemoVaultCoverageChecker.findUnreachableNotes.
DemoVaultCoverageCheckerGetExportedFunctionNamesParams The parameters for DemoVaultCoverageChecker.getExportedFunctionNames.
DemoVaultCoverageCheckerGetInterfaceMembersParams The parameters for DemoVaultCoverageChecker.getInterfaceMembers.
DemoVaultDocsCoverageSpec Configures the feature-doc linking check.
DemoVaultFunctionsCoverageSpec Reflects a module’s exported functions, each demonstrated by its bare name in the demo corpus.
DemoVaultInterfaceCoverageSpec Reflects a single interface’s members and demonstrates them via `$\{receiver\}.<member>` references.
DemoVaultNonTrivialGuardSpec Configures the guard that the reflected surface is non-trivial (protects against a parsing regression silently emptying every other check).
DemoVaultNote A single demo note, as read from the demo vault.
DirectiveScan What a note’s inline obsidian-dev-utils-disable… directives allow, rule by rule, and what is wrong with them.
DirectiveScanState The running state of a directive scan, threaded through the per-line helpers.
InterfaceMembers The parsed members of a source interface, class, or enum.
NoteLine A note line paired with the line number it came from, so a check that skips fenced blocks can still report — and an inline directive can still name — the line as the reader sees it.
OpenDirectiveRegion A disable region that has been opened and not yet enabled again.
OpenWikilinkDirectiveRegion A disable region that has been opened and not yet enabled again.
RegisterDemoVaultCoverageSuiteParams The parameters for registerDemoVaultCoverageSuite.
WikilinkDirectiveScan What a note’s inline obsidian-dev-utils-disable… directives allow, and what is wrong with them.
WikilinkDirectiveScanState The running state of a directive scan, threaded through the per-line helpers.
Function Description
registerDemoVaultCoverageSuite Registers a vitest suite that keeps a plugin’s in-repo demo-vault/ in sync with its public surface. Call it once from a plugin’s demo-vault.no-app.integration.test.ts; it registers one test per check, delegating to DemoVaultCoverageChecker.
Type Description
DemoVaultCheckSeverity How a check that is being introduced gradually reports what it finds: 'warn' prints it and passes, 'error' fails.
DemoVaultInterfaceMemberKind Whether an interface’s demonstrated members are its methods or its properties.