astro-post-auditv0.10.0

Integration options

Every top-level option of postAudit(). All are optional. Your editor autocompletes them from the shipped types, and the package exports a JSON Schema at @casoon/astro-post-audit/schema.

functionpostAudit
import postAudit from '@casoon/astro-post-audit';

postAudit(options?: PostAuditOptions): AstroIntegration

Registers an astro:build:done hook that passes the options as JSON to the audit binary and runs it against the build output.

Top-level options

Option Type Default Purpose
preset 'standard' | 'strict' | 'production' | 'seo' | 'accessibility' | 'performance' | 'relaxed' | 'editorial' – Predefined configuration applied before rules. See Presets.
mode 'fast' | 'full' 'full' 'fast' disables rules.html_validation.
strict boolean false Treat warnings as errors.
failOn 'errors' | 'warnings' | 'never' – When to fail the build. Takes precedence over throwOnError.
throwOnError boolean false Fail the build on findings. Ignored when failOn is set.
maxErrors number – Stop after this many errors; the report is then marked truncated. Errors known from the baseline do not count, and writeBaseline ignores the limit.
maxWarnings number – Fail when the warning count exceeds this number.
site string Astro site Base URL. Astro’s base is applied too: links, canonicals and sitemap URLs carry it, files in dist/ do not.
reports { json?, markdown?, sarif?, html? } – Report files. See Output and CI.
output string – Legacy alias for reports.json.
outputMarkdown string – Legacy alias for reports.markdown.
baseline string – Baseline file; only new findings are reported.
writeBaseline boolean false Write the current findings as baseline and exit 0.
hints.sourceFiles boolean false Show likely source files next to dist/ findings.
groups { seo?, a11y?, links?, performance?, privacy?, viz_alternatives? } – true enables a group, 'warn' enables it as warnings. viz_alternatives checks the display-mode convention (figure[data-viz], html[data-display]).
goLive { enabled, expectedSite?, forbiddenDomains?, imageHosts? } – Production gate. See Rolling out.
pageOverview boolean false Print a page properties table instead of running checks.
benchmark boolean false Per-check timing breakdown.
progress boolean | 'verbose' auto Progress bar on stderr.
color 'auto' | 'always' | 'never' 'auto' Colour for the text report and progress output.
debug boolean false Verbose diagnostics on stderr.
aiVisibility boolean false Enable the AI visibility module.
uxHeuristics boolean | { maxLinksPerPage?, minCtaPerPage? } false Enable UX heuristics (defaults 80 and 1). Settings in rules.ux_heuristics are kept; false switches the check off.
contentStyle boolean false Enable content style heuristics.
disable boolean false Switch the integration off.
rules RulesConfig – Per-check configuration, see below.

Rules

rules configures each check module by its snake_case name, plus filters and severity:

postAudit({
  rules: {
    filters: { exclude: ['404.html', 'drafts/**'] },
    canonical: { self_reference: true },
    headings: { no_skip: true },
    external_links: { enabled: true, timeout_ms: 5000 },
    severity: {
      'html/title-too-long': 'off',
      'images/alt-missing': 'error',
    },
  },
});

severity sets single rule ids to 'error', 'warning', 'info' or 'off'. The complete list of fields with their defaults follows below and is also in the shipped JSON Schema.

Full rules reference

All fields are optional — shown here with their defaults.

rules: {
  // Site settings
  site: {
    base_url: undefined,               // Auto-detected from Astro's `site` config
  },

  // File filters
  filters: {
    include: [],                        // Glob patterns to include
    exclude: [],                        // Glob patterns to exclude (e.g. ["404.html", "drafts/**"])
  },

  // URL normalization
  url_normalization: {
    trailing_slash: 'always',           // 'always' | 'never' | 'ignore'
    index_html: 'forbid',              // 'forbid' | 'allow'
  },

  // Canonical tag checks
  canonical: {
    require: true,                      // Every page must have a canonical tag
    absolute: true,                     // Canonical URL must be absolute
    same_origin: true,                  // Must point to same origin as site
    self_reference: false,              // Must be a self-referencing canonical
    detect_clusters: true,              // Warn when multiple pages share the same canonical
  },

  // Robots meta
  robots_meta: {
    allow_noindex: true,                // Don't warn on noindex pages
    fail_if_noindex: false,             // Treat noindex as error
  },

  // Internal link checks
  links: {
    check_internal: true,               // Verify internal links resolve
    fail_on_broken: true,               // Broken links are errors (not warnings)
    forbid_query_params_internal: true,  // Warn on ?query in internal links
    check_fragments: false,             // Validate #fragment targets exist
    detect_orphan_pages: false,         // Warn about pages with no incoming links
    check_mixed_content: true,          // Warn on http:// in internal links
    max_url_depth: undefined,           // Warn when URL nesting depth exceeds this (e.g. 3)
    known_routes: [],                   // Glob patterns for known SSR/dynamic routes (e.g. ['/dashboard/'])
                                         // excluded from links/broken and sitemap/entry-not-in-dist
  },

  // Sitemap cross-reference
  sitemap: {
    require: false,                     // sitemap.xml must exist
    canonical_must_be_in_sitemap: true,  // Canonical URLs should appear in sitemap
    forbid_noncanonical_in_sitemap: false, // Sitemap must not contain non-canonical URLs
    entries_must_exist_in_dist: true,    // Sitemap URLs must correspond to pages
  },

  // robots.txt
  robots_txt: {
    require: false,                     // robots.txt must exist
    require_sitemap_link: false,        // Must contain a sitemap link
    check_disallow_all: true,           // Error when User-agent: * blocks everything
    max_crawl_delay: 10,                // Warn when Crawl-delay exceeds this value (seconds)
    ai_bot_policy: false,               // Warn on blocked AI citation bots, note allowed training bots
    check_noindex_contradiction: false, // Error when a Disallow'd page also has noindex
    check_sitemap_blocked: false,       // Warn when a sitemap URL is blocked by robots.txt
  },

  // HTML basics
  html_basics: {
    lang_attr_required: true,           // <html lang="..."> required
    title_required: true,               // <title> required and non-empty
    meta_description_required: false,   // <meta name="description"> required
    viewport_required: true,            // <meta name="viewport"> required
    title_max_length: 60,               // Warn if title exceeds this length
    meta_description_max_length: 160,   // Warn if description exceeds this length
  },

  // Heading hierarchy
  headings: {
    require_h1: true,                   // Page must have at least one <h1>
    single_h1: true,                    // Only one <h1> per page
    no_skip: false,                     // No heading level gaps (h2 → h4)
  },

  // Accessibility
  a11y: {
    img_alt_required: true,             // <img> must have alt attribute
    allow_decorative_images: true,      // Deprecated, no effect: decorative images are always recognised
    a_accessible_name_required: true,   // <a> must have accessible name
    button_name_required: true,         // <button> must have accessible name
    label_for_required: true,           // Form controls need associated <label>
    warn_generic_link_text: true,       // Warn on "click here", "mehr", "weiter"
    aria_hidden_focusable_check: true,  // Warn on aria-hidden on focusable elements
    require_skip_link: false,           // Require skip navigation link
    check_landmarks: true,              // Require proper landmark structure (<main>, <nav>, …)
    check_duplicate_ids: true,          // Error on a duplicate id that an IDREF points at
    check_aria_roles: true,             // Validate role= values against WAI-ARIA spec
    check_alt_quality: true,            // Warn on filename/placeholder/too-short alt text
    manual_checklist: false,            // List manual/* items to check by hand (counted by severity)
    viz_alternatives: false,            // Display-mode convention rules (viz/*, display/*)
    contrast: false,                    // Compute styles from dist/ stylesheets: contrast/*, focus/outline-removed, text/* …
  },

  // Asset checks
  assets: {
    check_broken_assets: false,         // Verify img/script/link references
    check_image_dimensions: false,      // Warn on missing width/height (CLS)
    max_image_size_kb: undefined,       // Warn if image exceeds size in KB
    max_js_size_kb: undefined,          // Warn if JS file exceeds size in KB
    max_css_size_kb: undefined,         // Warn if CSS file exceeds size in KB
    require_hashed_filenames: false,    // Warn if filenames lack cache-busting hash
  },

  // Open Graph & Twitter Cards
  opengraph: {
    require_og_title: false,            // Require og:title
    require_og_description: false,      // Require og:description
    require_og_image: false,            // Require og:image
    require_twitter_card: false,        // Require twitter:card
    require_og_type: false,             // Require og:type
    require_og_url: false,              // Require og:url
    og_image_absolute_url: true,        // og:image must be an absolute URL
    require_twitter_image: false,       // Require twitter:image
    twitter_card_valid_values: true,    // Validate twitter:card value against the spec
    og_title_consistency: false,        // Warn when og:title and <title> differ significantly
    check_image_exists: false,          // Verify a local og:image exists in dist
    check_image_dimensions: false,      // Warn if local og:image is below 1200×630
    og_image_max_size_kb: undefined,    // Warn if local og:image exceeds this size in KB
  },

  // Structured data (JSON-LD)
  structured_data: {
    check_json_ld: false,               // Validate JSON-LD syntax and semantics
    require_json_ld: false,             // Every page must have JSON-LD
    detect_duplicate_types: false,      // Warn on duplicate @type per page
  },

  // Hreflang (multilingual sites)
  hreflang: {
    check_hreflang: false,              // Enable hreflang checks
    require_x_default: false,           // Require x-default entry
    require_self_reference: false,      // Must include self-referencing entry
    require_reciprocal: false,          // Links must be reciprocal (A→B and B→A)
    require_target_exists: false,       // Warn when an internal hreflang target is missing
    require_valid_code: true,           // Warn on values like "de_DE" or "deutsch"
  },

  // Security
  security: {
    check_target_blank: true,           // Warn on target="_blank" without rel="noopener"
    check_mixed_content: true,          // Warn on http:// resource URLs
    warn_inline_scripts: false,         // Warn on inline <script> tags
  },

  // Content quality
  content_quality: {
    detect_duplicate_titles: false,     // Warn on duplicate <title> across pages
    detect_duplicate_descriptions: false, // Warn on duplicate meta descriptions
    detect_duplicate_h1: false,         // Warn on duplicate <h1> across pages
    detect_duplicate_pages: false,      // Warn on identical page content
  },

  // External link checking (network requests)
  external_links: {
    enabled: false,                     // Enable external link checking via HEAD requests
    timeout_ms: 3000,                   // Timeout per request in milliseconds
    max_concurrent: 10,                 // Maximum concurrent requests
    fail_on_broken: false,              // Broken external links are errors (not just warnings)
    allow_domains: [],                  // Only check links to these domains (empty = all)
    block_domains: [],                  // Skip links to these domains
  },

  // Image checks (HTML attribute-level)
  images: {
    check_missing_dimensions: true,     // Error on <img> without width/height (CLS risk)
    warn_missing_lazy: true,            // Warn when images below the fold lack loading="lazy"
    info_missing_srcset: true,          // Info for missing srcset above 1,000 px (unknown width: above 100 KiB)
    format_hints: false,                // Info hint when JPEG/PNG/GIF could use a modern format
  },

  // C2PA Content Credentials — opt-in module
  c2pa: {
    enabled: false,                     // Validate embedded Content Credentials in local JPEG/PNG/WebP assets
    require_for: [],                    // Dist-relative image globs for which credentials are expected, e.g. ["blog/**/*.jpg"]
    require_trusted: false,             // Also flag Valid-but-not-Trusted manifests (e.g. self-signed certs)
  },

  // AI Visibility — opt-in module
  // Enable via top-level aiVisibility option or set enabled: true here
  ai_visibility: {
    enabled: false,                     // Enable AI visibility checks
    require_llms_txt: true,             // Require llms.txt or llms-full.txt and validate internal links
  },

  // UX Heuristics — opt-in module
  // Enable via top-level uxHeuristics option or set enabled: true here
  ux_heuristics: {
    enabled: false,                     // Enable UX heuristic checks
    max_links_per_page: 80,             // Info when a page exceeds this link count
    min_cta_per_page: 1,                // Warn when a page has fewer CTAs than this
  },

  // Content Style — opt-in module, heuristics for recurring "reads like AI" writing patterns
  // Enable via top-level contentStyle option or set enabled: true here
  content_style: {
    enabled: false,                     // Enable content style checks
    content_selector: "article, main, .prose", // Default: prefer article, then main, then .prose
    exclude: [],                        // Dist-relative globs to skip only this check, e.g. ["tags/**"]
    // rules: [...],                    // Replaces the built-in ruleset entirely when set (even to [])
    // extra_rules: [                   // Always appended to whichever ruleset is in effect
    //   { id: "custom-word", type: "presence", pattern: "unleash", level: "warning" },
    // ],
    disabled_rules: [],                 // Disable built-in/custom rules by short ID
    language_detection: {
      enabled: true,                    // Compare German/English text signals with html[lang]
      min_signal_words: 12,             // Require this many signals for the other language
      mismatch_ratio: 2,                // Other-language signals must be this many times higher
    },
  },

  // Font loading checks — opt-in
  fonts: {
    enabled: false,
    check_font_display: true,           // Warn when an @font-face block has no font-display
    require_font_preload: true,         // Info when self-hosted fonts have no preload hint
  },

  // Astro View Transitions checks — opt-in
  view_transitions: {
    enabled: false,
    check_duplicate_names: true,        // Error for duplicate transition:name values on one page
    check_external_reload: true,        // Info for external links missing data-astro-reload
  },

  // Static Astro/Tailwind source analysis — opt-in (needs project root, passed automatically)
  source_analysis: {
    enabled: false,
    extensions: [],                     // Additional source file extensions to inspect (without leading dot)
    exclude: [],                        // Additional globs; build/dependency directories are always excluded
    tailwind_inventory: true,           // Emit a compact Tailwind utility inventory
    duplicate_signatures: true,         // Report exact repeated static class signatures
    utility_conflicts: true,            // Report duplicate/mutually exclusive utility tokens
    component_complexity: true,         // Report components crossing complexity thresholds
    min_duplicate_occurrences: 3,       // Minimum occurrences before a repeated signature is reported
    max_component_lines: 300,           // Advisory source-line threshold
    max_component_props: 12,            // Advisory declared Props member threshold
    max_component_slots: 6,             // Advisory named-slot threshold
  },

  // Innovative dist-only audits
  i18n_audit: {
    enabled: false,                     // lang/hreflang/canonical consistency by locale route
  },
  crawl_budget: {
    enabled: false,                     // URL variants, duplicate clusters, indexability mismatches
  },
  render_blocking: {
    enabled: false,                     // Sync head scripts and missing preconnect hints
  },
  css_architecture: {
    enabled: false,                     // Per-route local and inline CSS payload
    max_route_kb: 50,                   // Warning threshold per route
    detect_route_outliers: true,        // Compare route payloads with the site median
    outlier_factor: 2,                  // Required multiple of the median
    min_outlier_kb: 20,                 // Ignore small absolute differences
  },
  privacy_security: {
    enabled: false,                     // Third-party domains, SRI/CSP readiness, consent indicators
    gdpr: false,                        // GDPR/DSGVO transfers: Google Fonts, YouTube, Maps, CDNs, external images
  },
  structured_data_graph: {
    enabled: false,                     // Cross-page JSON-LD entity consistency and missing internal URLs
  },

  // Static meta-refresh redirect analysis
  redirects: {
    enabled: false,                     // Links to redirect pages, redirect chains, loops
  },
  // Client-side JS bloat per route
  js_bloat: {
    enabled: false,                     // Warn when a route's total local JS is too large
    max_kb: 100,                        // Threshold in KB
  },
  // Content collection ↔ generated page sync (needs project root, passed automatically)
  content_sync: {
    enabled: false,                     // Warn about src/content items with no build page
  },
  // Native HTML5 conformance validation (offline, via html-conform, vnu-comparable)
  html_validation: {
    enabled: false,                     // Report HTML5 conformance findings
    max_per_page: 20,                   // Cap distinct findings reported per page
    cache: true,                        // Reuse results for pages whose HTML is unchanged
  },

  // Override severity per rule ID
  severity: {
    // 'rule-id': 'error' | 'warning' | 'info' | 'off'
  },
}

Edit this page on GitHub · Docs for v0.10.0