script-utils/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 obsidian-dev-utils, 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.
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.
Classes
Section titled “Classes”| 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. |
Interfaces
Section titled “Interfaces”| 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. |
| 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. |
| 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. |
Functions
Section titled “Functions”| 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 |
|---|---|
| DemoVaultInterfaceMemberKind | Whether an interface’s demonstrated members are its methods or its properties. |