Skip to content

Quality Checks#

View Markdown

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-preview
pnpm add -D @typescript/native-preview
bun add -D @typescript/native-preview
npm install -D @typescript/native-preview
yarn add -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.

Last updated: