Audit rules
On this page
kilim check reads the last build (.kilim/manifest.json and the files in dist/) and runs the rules below over every page. Errors make it exit with 1; warnings only do with --max-warnings <n>. Run kilim build first; kilim deploy runs both for you.
The meta rules and links/orphan skip noindex pages, such as the 404 page and drafts shown in kilim dev.
| Rule | Severity | Options | Checks that |
|---|---|---|---|
meta/title-missing | error | every page has a non-empty <title>. | |
meta/title-length | warn | max (60) | titles stay within max characters. |
meta/title-duplicate | warn | no two pages share a title; translations may. | |
meta/description-missing | warn | every page has a meta description. | |
meta/description-length | warn | max (160) | descriptions stay within max characters. |
meta/description-duplicate | warn | no two pages share their own description. | |
meta/canonical-missing | warn | every page has a canonical URL, which needs config url. | |
meta/multiple-h1 | warn | a page has at most one <h1>. | |
links/broken-internal | error | links to the site lead to a page or file, and #anchors to an id. | |
links/broken-resource | error | images, stylesheets, scripts, media and CSS url()s exist. | |
links/duplicate-id | error | raw HTML in Markdown reuses no heading id. | |
links/orphan | warn | every page but the home page has a link from another page. | |
links/redirect-chain | warn | redirects lead straight to a page, and links skip redirects. | |
images/alt-missing | warn | every <img> has alt, and every Markdown image alt text. | |
images/size-missing | warn | every <img> has width and height. | |
budget/page-weight | warn | max in KB (500) | a page's HTML, CSS, images and fonts stay within max. |
budget/image-weight | warn | max in KB (200) | each image stays within max. |
Plugins add rules named after them. The search() plugin adds search/index-size (warn, max in KB, 500 by default) for its index files, and the llmsTxt() plugin adds llms-txt/full-size (warn, max in KB, 500 by default) for each llms-full.txt.
- Lengths count characters as readers see them, so
éand an emoji are one each. - Duplicates compare titles and descriptions without case. The site-wide
descriptionfrom the config does not count as a page's own, and the language versions of one page may share a title. - Anchors:
#topis always fine, as browsers scroll to the top for it. - Duplicate ids: a
## Installheading gets the idinstall, so a<div id="install">in the same Markdown file makes#installambiguous. The error names the content file and the line of the raw HTML; rename one of them, or give the heading its own id with{#id}. - Images:
alt=""marks an image as decoration and passes. Markdown has no way to say that, sois reported on the line of the content file. Kilim addswidthandheightto local images;images/size-missingfinds remote ones written without them. - Budgets: 1 KB is 1000 bytes. An image counts at its largest
srcsetcandidate, and fonts and images that the page's CSS loads count too.
Set a rule to "off", "warn" or "error", or give a severity with options:
import { defineConfig } from "@kilim-dev/core";
export default defineConfig({
audit: {
rules: {
"meta/title-length": ["warn", { max: 70 }],
"meta/canonical-missing": "off",
"links/orphan": "error",
"budget/page-weight": ["warn", { max: 800 }],
},
},
});defineConfig completes the rule names and their options, those of plugins included. An unknown rule name or option stops kilim check with the config file and the valid names.
A rule is a check over the whole build. Write one with defineRule and add it to audit.custom:
import { defineConfig, defineRule, textContent } from "@kilim-dev/core";
const noLoremIpsum = defineRule({
name: "site/no-lorem-ipsum",
description: "Pages do not contain placeholder text.",
severity: "error",
check({ pages, report }) {
for (const page of pages) {
if (textContent(page.tree).includes("Lorem ipsum")) {
report({ page, message: "page still contains placeholder text." });
}
}
},
});
export default defineConfig({
audit: { custom: [noLoremIpsum] },
});Each page has its url, content file, meta (title, description, canonical URL) and the built HTML as a tree. Rules that take options declare an options schema; see Writing plugins for an example.
--format picks how problems are printed:
| Format | For |
|---|---|
text | People, the default. One line per problem and a summary. |
json | Other tools. --json is a shorthand. |
github | GitHub Actions: problems become annotations on the content files of a pull request. |
text names the content file, or the built page and line with the content file it came from:
warning: content/about.md: page has no description; set `description` in its frontmatter or in kilim.config.ts. [meta/description-missing]
error: dist/blog/index.html:14 (from content/blog/index.md): link "/blgo/" leads to no page or file of the build. [links/broken-internal]
Checked 12 pages: 1 error, 1 warning.json prints { pages, errors, warnings, issues }, where each issue has rule, severity ("warn" or "error"), message, file, output, url and line.
github writes workflow commands that GitHub shows as annotations:
::error file=content/blog/index.md,title=links/broken-internal::link "/blgo/" leads to no page or file of the build. (dist/blog/index.html:14)The built HTML is not in the repository, so a problem on a page points at its content file and names the built line in the message. When the site is in a subfolder of the repository, the paths start with it.