Quality Checks#
Documentation rots when its code samples do. These opt-in checks run during the Markdown transform, so a broken snippet fails the build instead of shipping to readers.
| Option | Default | Checks |
|---|---|---|
codeBlockLint |
false |
Fence hygiene: missing languages, trailing spaces. |
codeBlockTypecheck |
false |
TypeScript fences compile, via tsgo. |
docsTests |
false |
Runnable fences pass under Vitest. |
sanitize |
false |
Rendered HTML against an allow list. |
All checks are static — no documentation code is executed during the
transform. docsTests executes code, but only inside the separate Vitest
harness you invoke from CI.
Code Block Lint#
The native scanner reports diagnostics with source positions, and only runs when a file actually contains fences:
import { oxContent } from "@ox-content/vite-plugin";
export default {
plugins: [
oxContent({
codeBlockLint: {
requireLanguage: true,
trailingSpaces: true,
mode: "error",
},
}),
],
};
| Option | Default | Purpose |
|---|---|---|
languages |
all | Restrict which fence languages are linted. |
requireLanguage |
false |
Report fences without a language identifier. |
trailingSpaces |
true |
Report trailing whitespace inside fences. |
mode |
"warn" |
"warn" logs; "error" fails the transform. |
Given a page with an unlabeled fence and a trailing space, the transform reports:
code-block-language at 3:1
Code block is missing a language identifier.
code-block-trailing-spaces at 8:13
Code block line has trailing whitespace.
With mode: "warn" (the default) diagnostics are logged and the build
continues; with mode: "error" the transform throws and the build fails.
Code Block Type Checking#
Type-check TypeScript fences with tsgo, the native TypeScript compiler:
oxContent({
codeBlockTypecheck: {
languages: ["ts", "tsx"],
requireMeta: true,
tsgoCommand: "tsgo",
mode: "error",
},
});
| Option | Default | Purpose |
|---|---|---|
languages |
["ts", "tsx"] |
Fence languages sent to the compiler. |
requireMeta |
true |
Only check fences tagged typecheck/twoslash. |
tsgoCommand |
"tsgo" |
Compiler binary to invoke. |
mode |
"warn" |
"warn" logs; "error" fails the build. |
With the default requireMeta: true, authors opt in per fence, so
intentionally incomplete snippets stay possible:
```ts typecheck
const value: string = "ok";
```
Set requireMeta: false to check every TypeScript fence. Snippets are written
to a temporary directory and compiled with tsgo --noEmit; compiler errors
become build diagnostics pointing at the source fence.
tsgo ships with @typescript/native-preview:
vp install -D @typescript/native-previewpnpm add -D @typescript/native-previewbun add -D @typescript/native-previewnpm install -D @typescript/native-previewyarn add -D @typescript/native-previewDocs Tests#
Extract runnable fences and run them under Vitest — the same "documentation is tested" workflow as Rust doctests. Extraction is native and does not parse the whole document in JavaScript:
import { runDocsTests } from "@ox-content/vite-plugin";
await runDocsTests({
include: ["docs/content/**/*.md"],
vitestCommand: "vitest",
vitestArgs: ["run"],
});
A fence becomes a test when its meta contains test, runnable, vitest, or
docs-test:
```ts docs-test
import { expect } from "vitest";
const result = 1 + 1;
expect(result).toBe(2);
```
Each fence is wrapped in a generated Vitest test(...). Switch
source: "jsdoc" to run @example fences from your source code instead of
Markdown files — the same extractor that powers
generated API docs. See
Vitest Docs Tests for the harness options,
including executionMode: "module" for fences that declare their own tests.
HTML Sanitizer#
Sanitize the final HTML when rendering untrusted or mixed content:
oxContent({
sanitize: {
allowedUrlSchemes: ["http", "https", "mailto"],
},
});
Given this input:
<p onclick="alert(1)">
Hello
<script>
alert(2);
</script>
<a href="javascript:alert(3)">link</a>
</p>
the sanitizer emits:
<p>Hello <a>link</a></p>
Script elements, event-handler attributes, and unsafe URL schemes are removed
in a single native pass. sanitize: true uses safe defaults that keep common
documentation HTML, including media elements (video, audio, source,
track, picture). Passing allowedTags, allowedAttributes, or
allowedUrlSchemes replaces the corresponding built-in list rather than
extending it.
The pass runs at the very end of the pipeline — after embeds expand — so allow lists apply to everything the page actually ships.
Related#
- Code Blocks — highlighting, annotations, and imports for the fences these checks guard.
- Typed Hover — opt-in build-time type overlays for
twoslashfences. - HTML Sanitizer example