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:

npm install -D @typescript/native-preview
pnpm add -D @typescript/native-preview
yarn add -D @typescript/native-preview
bun add -D @typescript/native-preview
vp install -D @typescript/native-preview

Docs 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.